Re: [PATCH 4.20 085/111] blockdev: Fix livelocks on loop device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon 21-01-19 14:43:19, Greg Kroah-Hartman wrote:
> 4.20-stable review patch.  If anyone has any objections, please let me know.

Greg, when applying this, you should also apply commit c8a83a6b54d0 "nbd:
Use set_blocksize() to set device blocksize". Otherwise some nbd
functionality would regress.

								Honza

> 
> ------------------
> 
> From: Jan Kara <jack@xxxxxxx>
> 
> commit 04906b2f542c23626b0ef6219b808406f8dddbe9 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 <penguin-kernel@xxxxxxxxxxxxxxxxxxx> for help with
> debugging the problem.
> 
> Reported-by: syzbot+9933e4476f365f5d5a1b@xxxxxxxxxxxxxxxxxxxxxxxxx
> Signed-off-by: Jan Kara <jack@xxxxxxx>
> Signed-off-by: Jens Axboe <axboe@xxxxxxxxx>
> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
> 
> ---
>  fs/block_dev.c |   28 ++++++++++++++++++----------
>  1 file changed, 18 insertions(+), 10 deletions(-)
> 
> --- a/fs/block_dev.c
> +++ b/fs/block_dev.c
> @@ -104,6 +104,20 @@ void invalidate_bdev(struct block_device
>  }
>  EXPORT_SYMBOL(invalidate_bdev);
>  
> +static void set_init_blocksize(struct block_device *bdev)
> +{
> +	unsigned bsize = bdev_logical_block_size(bdev);
> +	loff_t size = i_size_read(bdev->bd_inode);
> +
> +	while (bsize < PAGE_SIZE) {
> +		if (size & bsize)
> +			break;
> +		bsize <<= 1;
> +	}
> +	bdev->bd_block_size = bsize;
> +	bdev->bd_inode->i_blkbits = blksize_bits(bsize);
> +}
> +
>  int set_blocksize(struct block_device *bdev, int size)
>  {
>  	/* Size must be a power of two, and between 512 and PAGE_SIZE */
> @@ -1408,18 +1422,9 @@ EXPORT_SYMBOL(check_disk_change);
>  
>  void bd_set_size(struct block_device *bdev, loff_t size)
>  {
> -	unsigned bsize = bdev_logical_block_size(bdev);
> -
>  	inode_lock(bdev->bd_inode);
>  	i_size_write(bdev->bd_inode, size);
>  	inode_unlock(bdev->bd_inode);
> -	while (bsize < PAGE_SIZE) {
> -		if (size & bsize)
> -			break;
> -		bsize <<= 1;
> -	}
> -	bdev->bd_block_size = bsize;
> -	bdev->bd_inode->i_blkbits = blksize_bits(bsize);
>  }
>  EXPORT_SYMBOL(bd_set_size);
>  
> @@ -1496,8 +1501,10 @@ static int __blkdev_get(struct block_dev
>  				}
>  			}
>  
> -			if (!ret)
> +			if (!ret) {
>  				bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
> +				set_init_blocksize(bdev);
> +			}
>  
>  			/*
>  			 * If the device is invalidated, rescan partition
> @@ -1532,6 +1539,7 @@ static int __blkdev_get(struct block_dev
>  				goto out_clear;
>  			}
>  			bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9);
> +			set_init_blocksize(bdev);
>  		}
>  
>  		if (bdev->bd_bdi == &noop_backing_dev_info)
> 
> 
-- 
Jan Kara <jack@xxxxxxxx>
SUSE Labs, CR



[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux