Skip to content

Commit 560e7cb

Browse files
jankaraaxboe
authored andcommitted
blockdev: Avoid two active bdev inodes for one device
When blkdev_open() races with device removal and creation it can happen that unhashed bdev inode gets associated with newly created gendisk like: CPU0 CPU1 blkdev_open() bdev = bd_acquire() del_gendisk() bdev_unhash_inode(bdev); remove device create new device with the same number __blkdev_get() disk = get_gendisk() - gets reference to gendisk of the new device Now another blkdev_open() will not find original 'bdev' as it got unhashed, create a new one and associate it with the same 'disk' at which point problems start as we have two independent page caches for one device. Fix the problem by verifying that the bdev inode didn't get unhashed before we acquired gendisk reference. That way we make sure gendisk can get associated only with visible bdev inodes. Tested-by: Hou Tao <[email protected]> Signed-off-by: Jan Kara <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 56c0908 commit 560e7cb

File tree

1 file changed

+23
-2
lines changed

1 file changed

+23
-2
lines changed

fs/block_dev.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,27 @@ static int bd_prepare_to_claim(struct block_device *bdev,
10581058
return 0;
10591059
}
10601060

1061+
static struct gendisk *bdev_get_gendisk(struct block_device *bdev, int *partno)
1062+
{
1063+
struct gendisk *disk = get_gendisk(bdev->bd_dev, partno);
1064+
1065+
if (!disk)
1066+
return NULL;
1067+
/*
1068+
* Now that we hold gendisk reference we make sure bdev we looked up is
1069+
* not stale. If it is, it means device got removed and created before
1070+
* we looked up gendisk and we fail open in such case. Associating
1071+
* unhashed bdev with newly created gendisk could lead to two bdevs
1072+
* (and thus two independent caches) being associated with one device
1073+
* which is bad.
1074+
*/
1075+
if (inode_unhashed(bdev->bd_inode)) {
1076+
put_disk_and_module(disk);
1077+
return NULL;
1078+
}
1079+
return disk;
1080+
}
1081+
10611082
/**
10621083
* bd_start_claiming - start claiming a block device
10631084
* @bdev: block device of interest
@@ -1094,7 +1115,7 @@ static struct block_device *bd_start_claiming(struct block_device *bdev,
10941115
* @bdev might not have been initialized properly yet, look up
10951116
* and grab the outer block device the hard way.
10961117
*/
1097-
disk = get_gendisk(bdev->bd_dev, &partno);
1118+
disk = bdev_get_gendisk(bdev, &partno);
10981119
if (!disk)
10991120
return ERR_PTR(-ENXIO);
11001121

@@ -1429,7 +1450,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)
14291450
restart:
14301451

14311452
ret = -ENXIO;
1432-
disk = get_gendisk(bdev->bd_dev, &partno);
1453+
disk = bdev_get_gendisk(bdev, &partno);
14331454
if (!disk)
14341455
goto out;
14351456

0 commit comments

Comments
 (0)