diff options
author | Jan Kara <jack@suse.cz> | 2019-01-14 03:48:10 -0500 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2019-01-15 09:30:56 -0500 |
commit | 04906b2f542c23626b0ef6219b808406f8dddbe9 (patch) | |
tree | a0106c5a12f484aa8ae4e2e057a4da1bd8b27523 /fs/block_dev.c | |
parent | c8a83a6b54d0ca078de036aafb3f6af58c1dc5eb (diff) |
blockdev: Fix livelocks on loop device
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 <penguin-kernel@I-love.SAKURA.ne.jp> for help with
debugging the problem.
Reported-by: syzbot+9933e4476f365f5d5a1b@syzkaller.appspotmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'fs/block_dev.c')
-rw-r--r-- | fs/block_dev.c | 28 |
1 files changed, 18 insertions, 10 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index c546cdce77e6..58a4c1217fa8 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -104,6 +104,20 @@ void invalidate_bdev(struct block_device *bdev) | |||
104 | } | 104 | } |
105 | EXPORT_SYMBOL(invalidate_bdev); | 105 | EXPORT_SYMBOL(invalidate_bdev); |
106 | 106 | ||
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 | |||
107 | int set_blocksize(struct block_device *bdev, int size) | 121 | int set_blocksize(struct block_device *bdev, int size) |
108 | { | 122 | { |
109 | /* Size must be a power of two, and between 512 and PAGE_SIZE */ | 123 | /* Size must be a power of two, and between 512 and PAGE_SIZE */ |
@@ -1431,18 +1445,9 @@ EXPORT_SYMBOL(check_disk_change); | |||
1431 | 1445 | ||
1432 | void bd_set_size(struct block_device *bdev, loff_t size) | 1446 | void bd_set_size(struct block_device *bdev, loff_t size) |
1433 | { | 1447 | { |
1434 | unsigned bsize = bdev_logical_block_size(bdev); | ||
1435 | |||
1436 | inode_lock(bdev->bd_inode); | 1448 | inode_lock(bdev->bd_inode); |
1437 | i_size_write(bdev->bd_inode, size); | 1449 | i_size_write(bdev->bd_inode, size); |
1438 | inode_unlock(bdev->bd_inode); | 1450 | inode_unlock(bdev->bd_inode); |
1439 | while (bsize < PAGE_SIZE) { | ||
1440 | if (size & bsize) | ||
1441 | break; | ||
1442 | bsize <<= 1; | ||
1443 | } | ||
1444 | bdev->bd_block_size = bsize; | ||
1445 | bdev->bd_inode->i_blkbits = blksize_bits(bsize); | ||
1446 | } | 1451 | } |
1447 | EXPORT_SYMBOL(bd_set_size); | 1452 | EXPORT_SYMBOL(bd_set_size); |
1448 | 1453 | ||
@@ -1519,8 +1524,10 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) | |||
1519 | } | 1524 | } |
1520 | } | 1525 | } |
1521 | 1526 | ||
1522 | if (!ret) | 1527 | if (!ret) { |
1523 | bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); | 1528 | bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); |
1529 | set_init_blocksize(bdev); | ||
1530 | } | ||
1524 | 1531 | ||
1525 | /* | 1532 | /* |
1526 | * If the device is invalidated, rescan partition | 1533 | * If the device is invalidated, rescan partition |
@@ -1555,6 +1562,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) | |||
1555 | goto out_clear; | 1562 | goto out_clear; |
1556 | } | 1563 | } |
1557 | bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9); | 1564 | bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9); |
1565 | set_init_blocksize(bdev); | ||
1558 | } | 1566 | } |
1559 | 1567 | ||
1560 | if (bdev->bd_bdi == &noop_backing_dev_info) | 1568 | if (bdev->bd_bdi == &noop_backing_dev_info) |