block: prevent block device lookups at the beginning of del_gendisk

As an artifact of how gendisk lookup used to work in earlier kernels,
GENHD_FL_UP is only cleared very late in del_gendisk, and a global lock
is used to prevent opens from succeeding while del_gendisk is tearing
down the gendisk.  Switch to clearing the flag early and under bd_mutex
so that callers can use bd_mutex to stabilize the flag, which removes
the need for the global mutex.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20210514131842.1600568-2-hch@lst.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Christoph Hellwig 2021-05-14 15:18:41 +02:00 committed by Jens Axboe
parent 9a66e6bd7e
commit 6c60ff048c
3 changed files with 6 additions and 22 deletions

View File

@ -29,8 +29,6 @@
static struct kobject *block_depr; static struct kobject *block_depr;
DECLARE_RWSEM(bdev_lookup_sem);
/* for extended dynamic devt allocation, currently only one major is used */ /* for extended dynamic devt allocation, currently only one major is used */
#define NR_EXT_DEVT (1 << MINORBITS) #define NR_EXT_DEVT (1 << MINORBITS)
static DEFINE_IDA(ext_devt_ida); static DEFINE_IDA(ext_devt_ida);
@ -609,13 +607,8 @@ void del_gendisk(struct gendisk *disk)
blk_integrity_del(disk); blk_integrity_del(disk);
disk_del_events(disk); disk_del_events(disk);
/*
* Block lookups of the disk until all bdevs are unhashed and the
* disk is marked as dead (GENHD_FL_UP cleared).
*/
down_write(&bdev_lookup_sem);
mutex_lock(&disk->part0->bd_mutex); mutex_lock(&disk->part0->bd_mutex);
disk->flags &= ~GENHD_FL_UP;
blk_drop_partitions(disk); blk_drop_partitions(disk);
mutex_unlock(&disk->part0->bd_mutex); mutex_unlock(&disk->part0->bd_mutex);
@ -629,8 +622,6 @@ void del_gendisk(struct gendisk *disk)
remove_inode_hash(disk->part0->bd_inode); remove_inode_hash(disk->part0->bd_inode);
set_capacity(disk, 0); set_capacity(disk, 0);
disk->flags &= ~GENHD_FL_UP;
up_write(&bdev_lookup_sem);
if (!(disk->flags & GENHD_FL_HIDDEN)) { if (!(disk->flags & GENHD_FL_HIDDEN)) {
sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");

View File

@ -1298,6 +1298,9 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode)
struct gendisk *disk = bdev->bd_disk; struct gendisk *disk = bdev->bd_disk;
int ret = 0; int ret = 0;
if (!(disk->flags & GENHD_FL_UP))
return -ENXIO;
if (!bdev->bd_openers) { if (!bdev->bd_openers) {
if (!bdev_is_partition(bdev)) { if (!bdev_is_partition(bdev)) {
ret = 0; ret = 0;
@ -1332,8 +1335,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode)
whole->bd_part_count++; whole->bd_part_count++;
mutex_unlock(&whole->bd_mutex); mutex_unlock(&whole->bd_mutex);
if (!(disk->flags & GENHD_FL_UP) || if (!bdev_nr_sectors(bdev)) {
!bdev_nr_sectors(bdev)) {
__blkdev_put(whole, mode, 1); __blkdev_put(whole, mode, 1);
bdput(whole); bdput(whole);
return -ENXIO; return -ENXIO;
@ -1364,16 +1366,12 @@ struct block_device *blkdev_get_no_open(dev_t dev)
struct block_device *bdev; struct block_device *bdev;
struct gendisk *disk; struct gendisk *disk;
down_read(&bdev_lookup_sem);
bdev = bdget(dev); bdev = bdget(dev);
if (!bdev) { if (!bdev) {
up_read(&bdev_lookup_sem);
blk_request_module(dev); blk_request_module(dev);
down_read(&bdev_lookup_sem);
bdev = bdget(dev); bdev = bdget(dev);
if (!bdev) if (!bdev)
goto unlock; return NULL;
} }
disk = bdev->bd_disk; disk = bdev->bd_disk;
@ -1383,14 +1381,11 @@ struct block_device *blkdev_get_no_open(dev_t dev)
goto put_disk; goto put_disk;
if (!try_module_get(bdev->bd_disk->fops->owner)) if (!try_module_get(bdev->bd_disk->fops->owner))
goto put_disk; goto put_disk;
up_read(&bdev_lookup_sem);
return bdev; return bdev;
put_disk: put_disk:
put_disk(disk); put_disk(disk);
bdput: bdput:
bdput(bdev); bdput(bdev);
unlock:
up_read(&bdev_lookup_sem);
return NULL; return NULL;
} }

View File

@ -306,8 +306,6 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev,
} }
#endif /* CONFIG_SYSFS */ #endif /* CONFIG_SYSFS */
extern struct rw_semaphore bdev_lookup_sem;
dev_t blk_lookup_devt(const char *name, int partno); dev_t blk_lookup_devt(const char *name, int partno);
void blk_request_module(dev_t devt); void blk_request_module(dev_t devt);
#ifdef CONFIG_BLOCK #ifdef CONFIG_BLOCK