Skip to content

Commit 00199a4

Browse files
jankaragregkh
authored andcommitted
blockdev: Fix livelocks on loop device
commit 04906b2 upstream. bd_set_size() updates also block device's block size. This is somewhat unexpected from its name and at this point, only blkdev_open() uses this functionality. Furthermore, this can result in changing block size under a filesystem mounted on a loop device which leads to livelocks inside __getblk_gfp() like: Sending NMI from CPU 0 to CPUs 1: NMI backtrace for cpu 1 CPU: 1 PID: 10863 Comm: syz-executor0 Not tainted 4.18.0-rc5+ #151 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 RIP: 0010:__sanitizer_cov_trace_pc+0x3f/0x50 kernel/kcov.c:106 ... Call Trace: init_page_buffers+0x3e2/0x530 fs/buffer.c:904 grow_dev_page fs/buffer.c:947 [inline] grow_buffers fs/buffer.c:1009 [inline] __getblk_slow fs/buffer.c:1036 [inline] __getblk_gfp+0x906/0xb10 fs/buffer.c:1313 __bread_gfp+0x2d/0x310 fs/buffer.c:1347 sb_bread include/linux/buffer_head.h:307 [inline] fat12_ent_bread+0x14e/0x3d0 fs/fat/fatent.c:75 fat_ent_read_block fs/fat/fatent.c:441 [inline] fat_alloc_clusters+0x8ce/0x16e0 fs/fat/fatent.c:489 fat_add_cluster+0x7a/0x150 fs/fat/inode.c:101 __fat_get_block fs/fat/inode.c:148 [inline] ... Trivial reproducer for the problem looks like: truncate -s 1G /tmp/image losetup /dev/loop0 /tmp/image mkfs.ext4 -b 1024 /dev/loop0 mount -t ext4 /dev/loop0 /mnt losetup -c /dev/loop0 l /mnt Fix the problem by moving initialization of a block device block size into a separate function and call it when needed. Thanks to Tetsuo Handa <[email protected]> for help with debugging the problem. Reported-by: [email protected] Signed-off-by: Jan Kara <[email protected]> Signed-off-by: Jens Axboe <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 081aee5 commit 00199a4

File tree

1 file changed

+18
-10
lines changed

1 file changed

+18
-10
lines changed

fs/block_dev.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,20 @@ void invalidate_bdev(struct block_device *bdev)
104104
}
105105
EXPORT_SYMBOL(invalidate_bdev);
106106

107+
static void set_init_blocksize(struct block_device *bdev)
108+
{
109+
unsigned bsize = bdev_logical_block_size(bdev);
110+
loff_t size = i_size_read(bdev->bd_inode);
111+
112+
while (bsize < PAGE_SIZE) {
113+
if (size & bsize)
114+
break;
115+
bsize <<= 1;
116+
}
117+
bdev->bd_block_size = bsize;
118+
bdev->bd_inode->i_blkbits = blksize_bits(bsize);
119+
}
120+
107121
int set_blocksize(struct block_device *bdev, int size)
108122
{
109123
/* Size must be a power of two, and between 512 and PAGE_SIZE */
@@ -1408,18 +1422,9 @@ EXPORT_SYMBOL(check_disk_change);
14081422

14091423
void bd_set_size(struct block_device *bdev, loff_t size)
14101424
{
1411-
unsigned bsize = bdev_logical_block_size(bdev);
1412-
14131425
inode_lock(bdev->bd_inode);
14141426
i_size_write(bdev->bd_inode, size);
14151427
inode_unlock(bdev->bd_inode);
1416-
while (bsize < PAGE_SIZE) {
1417-
if (size & bsize)
1418-
break;
1419-
bsize <<= 1;
1420-
}
1421-
bdev->bd_block_size = bsize;
1422-
bdev->bd_inode->i_blkbits = blksize_bits(bsize);
14231428
}
14241429
EXPORT_SYMBOL(bd_set_size);
14251430

@@ -1496,8 +1501,10 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
14961501
}
14971502
}
14981503

1499-
if (!ret)
1504+
if (!ret) {
15001505
bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
1506+
set_init_blocksize(bdev);
1507+
}
15011508

15021509
/*
15031510
* If the device is invalidated, rescan partition
@@ -1532,6 +1539,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
15321539
goto out_clear;
15331540
}
15341541
bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9);
1542+
set_init_blocksize(bdev);
15351543
}
15361544

15371545
if (bdev->bd_bdi == &noop_backing_dev_info)

0 commit comments

Comments
 (0)