__find_get_block_slow() and grow_buffers() use different methods to compute a page index for a given block: __find_get_block_slow() computes it from bd_inode->i_blkbits, while grow_buffers() computes it from the block size argument. The two can get out of sync, for example if bd_inode->i_blkbits is modified while a isofs_fill_super() (in mount) is between sb_min_blocksize() and sb_read/__bread_gfp/__getblk_gfp/__getblk_slow. When this happens this can result in an infinite loop in __getblk_slow(), as grow_buffers() allocates a page but __find_get_block() keeps looking for a different one. I can often, but not consistently, reproduce this scenario with the script below. This patch changes __find_get_block_slow() to also use the block size. It implicitly relies on size being a power of 2 in the 256..PAGE_SIZE range. FILE=/tmp/fsfile MNT=/tmp/fsmnt LOOP=`losetup -f` rm -rf $FILE $MNT mkdir $MNT dd if=/dev/zero of=$FILE count=32 bs=1MiB losetup $LOOP $FILE mkfs -t ext4 $LOOP while true; do losetup -D $LOOP; losetup $LOOP $FILE;done 2>&1 >/dev/null & for ((i=0; i<100; i++)); do echo ================== $i; \ mount $LOOP $MNT; umount $MNT; done Signed-off-by: Francesco Ruggeri <fruggeri@xxxxxxxxxx> --- fs/buffer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/buffer.c b/fs/buffer.c index 48318fb74938..447e8db2ff5f 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -190,7 +190,8 @@ EXPORT_SYMBOL(end_buffer_write_sync); * succeeds, there is no need to take private_lock. */ static struct buffer_head * -__find_get_block_slow(struct block_device *bdev, sector_t block) +__find_get_block_slow(struct block_device *bdev, sector_t block, + unsigned int size) { struct inode *bd_inode = bdev->bd_inode; struct address_space *bd_mapping = bd_inode->i_mapping; @@ -202,7 +203,7 @@ __find_get_block_slow(struct block_device *bdev, sector_t block) int all_mapped = 1; static DEFINE_RATELIMIT_STATE(last_warned, HZ, 1); - index = block >> (PAGE_SHIFT - bd_inode->i_blkbits); + index = block >> (PAGE_SHIFT - blksize_bits(size)); page = find_get_page_flags(bd_mapping, index, FGP_ACCESSED); if (!page) goto out; @@ -1292,7 +1293,7 @@ __find_get_block(struct block_device *bdev, sector_t block, unsigned size) if (bh == NULL) { /* __find_get_block_slow will mark the page accessed */ - bh = __find_get_block_slow(bdev, block); + bh = __find_get_block_slow(bdev, block, size); if (bh) bh_lru_install(bh); } else -- 2.19.1