diff options
| author | Jan Kara <jack@suse.cz> | 2017-03-02 10:50:13 -0500 |
|---|---|---|
| committer | Jens Axboe <axboe@fb.com> | 2017-03-02 10:56:59 -0500 |
| commit | a5a79d00017c9eee68a9bcb40d5dfd6f45f17461 (patch) | |
| tree | dc1803bee107865dbea5c4434bad158a15a66ced /fs | |
| parent | e02898b423802b1f3a3aaa7f16e896da069ba8f7 (diff) | |
block: Initialize bd_bdi on inode initialization
So far we initialized bd_bdi only in bdget(). That is fine for normal
bdev inodes however for the special case of the root inode of
blockdev_superblock that function is never called and thus bd_bdi is
left uninitialized. As a result bdev_evict_inode() may oops doing
bdi_put(root->bd_bdi) on that inode as can be seen when doing:
mount -t bdev none /mnt
Fix the problem by initializing bd_bdi when first allocating the inode
and then reinitializing bd_bdi in bdev_evict_inode().
Thanks to syzkaller team for finding the problem.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Fixes: b1d2dc5659b4 ("block: Make blk_get_backing_dev_info() safe without open bdev")
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/block_dev.c | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index 77c30f15a02c..2eca00ec4370 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
| @@ -870,6 +870,7 @@ static void init_once(void *foo) | |||
| 870 | #ifdef CONFIG_SYSFS | 870 | #ifdef CONFIG_SYSFS |
| 871 | INIT_LIST_HEAD(&bdev->bd_holder_disks); | 871 | INIT_LIST_HEAD(&bdev->bd_holder_disks); |
| 872 | #endif | 872 | #endif |
| 873 | bdev->bd_bdi = &noop_backing_dev_info; | ||
| 873 | inode_init_once(&ei->vfs_inode); | 874 | inode_init_once(&ei->vfs_inode); |
| 874 | /* Initialize mutex for freeze. */ | 875 | /* Initialize mutex for freeze. */ |
| 875 | mutex_init(&bdev->bd_fsfreeze_mutex); | 876 | mutex_init(&bdev->bd_fsfreeze_mutex); |
| @@ -884,8 +885,10 @@ static void bdev_evict_inode(struct inode *inode) | |||
| 884 | spin_lock(&bdev_lock); | 885 | spin_lock(&bdev_lock); |
| 885 | list_del_init(&bdev->bd_list); | 886 | list_del_init(&bdev->bd_list); |
| 886 | spin_unlock(&bdev_lock); | 887 | spin_unlock(&bdev_lock); |
| 887 | if (bdev->bd_bdi != &noop_backing_dev_info) | 888 | if (bdev->bd_bdi != &noop_backing_dev_info) { |
| 888 | bdi_put(bdev->bd_bdi); | 889 | bdi_put(bdev->bd_bdi); |
| 890 | bdev->bd_bdi = &noop_backing_dev_info; | ||
| 891 | } | ||
| 889 | } | 892 | } |
| 890 | 893 | ||
| 891 | static const struct super_operations bdev_sops = { | 894 | static const struct super_operations bdev_sops = { |
| @@ -988,7 +991,6 @@ struct block_device *bdget(dev_t dev) | |||
| 988 | bdev->bd_contains = NULL; | 991 | bdev->bd_contains = NULL; |
| 989 | bdev->bd_super = NULL; | 992 | bdev->bd_super = NULL; |
| 990 | bdev->bd_inode = inode; | 993 | bdev->bd_inode = inode; |
| 991 | bdev->bd_bdi = &noop_backing_dev_info; | ||
| 992 | bdev->bd_block_size = i_blocksize(inode); | 994 | bdev->bd_block_size = i_blocksize(inode); |
| 993 | bdev->bd_part_count = 0; | 995 | bdev->bd_part_count = 0; |
| 994 | bdev->bd_invalidated = 0; | 996 | bdev->bd_invalidated = 0; |
