block: avoid to touch unloaded module instance when opening bdev
authorMing Lei <ming.lei@redhat.com>
Thu, 11 Nov 2021 02:03:43 +0000 (10:03 +0800)
committerJens Axboe <axboe@kernel.dk>
Tue, 23 Nov 2021 01:35:37 +0000 (18:35 -0700)
disk->fops->owner is grabbed in blkdev_get_no_open() after the disk
kobject refcount is increased. This way can't make sure that
disk->fops->owner is still alive since del_gendisk() still can move
on if the kobject refcount of disk is grabbed by open() and
disk->fops->open() isn't called yet.

Fixes the issue by moving try_module_get() into blkdev_get_by_dev()
with ->open_mutex() held, then we can drain the in-progress open()
in del_gendisk(). Meantime new open() won't succeed because disk
becomes not alive.

This way is reasonable because blkdev_get_no_open() needn't to touch
disk->fops or defined callbacks.

Cc: Christoph Hellwig <hch@lst.de>
Cc: czhong@redhat.com
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/r/20211111020343.316126-1-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/bdev.c

index b4dab2fb6a74690c655fb0e712df5de8332abd19..b1d087e5e205f6afb0f48393c1bbc452f95c3e6c 100644 (file)
@@ -753,8 +753,7 @@ struct block_device *blkdev_get_no_open(dev_t dev)
 
        if (!bdev)
                return NULL;
-       if ((bdev->bd_disk->flags & GENHD_FL_HIDDEN) ||
-           !try_module_get(bdev->bd_disk->fops->owner)) {
+       if ((bdev->bd_disk->flags & GENHD_FL_HIDDEN)) {
                put_device(&bdev->bd_device);
                return NULL;
        }
@@ -764,7 +763,6 @@ struct block_device *blkdev_get_no_open(dev_t dev)
 
 void blkdev_put_no_open(struct block_device *bdev)
 {
-       module_put(bdev->bd_disk->fops->owner);
        put_device(&bdev->bd_device);
 }
 
@@ -820,12 +818,14 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
        ret = -ENXIO;
        if (!disk_live(disk))
                goto abort_claiming;
+       if (!try_module_get(disk->fops->owner))
+               goto abort_claiming;
        if (bdev_is_partition(bdev))
                ret = blkdev_get_part(bdev, mode);
        else
                ret = blkdev_get_whole(bdev, mode);
        if (ret)
-               goto abort_claiming;
+               goto put_module;
        if (mode & FMODE_EXCL) {
                bd_finish_claiming(bdev, holder);
 
@@ -847,7 +847,8 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
        if (unblock_events)
                disk_unblock_events(disk);
        return bdev;
-
+put_module:
+       module_put(disk->fops->owner);
 abort_claiming:
        if (mode & FMODE_EXCL)
                bd_abort_claiming(bdev, holder);
@@ -956,6 +957,7 @@ void blkdev_put(struct block_device *bdev, fmode_t mode)
                blkdev_put_whole(bdev, mode);
        mutex_unlock(&disk->open_mutex);
 
+       module_put(disk->fops->owner);
        blkdev_put_no_open(bdev);
 }
 EXPORT_SYMBOL(blkdev_put);