[PATCH AUTOSEL 5.15 32/68] block: avoid to touch unloaded module instance when opening bdev

From: Sasha Levin
Date: Tue Nov 30 2021 - 09:52:07 EST


From: Ming Lei <ming.lei@xxxxxxxxxx>

[ Upstream commit efcf5932230b9472cfdbe01c858726f29ac5ec7d ]

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@xxxxxx>
Cc: czhong@xxxxxxxxxx
Signed-off-by: Ming Lei <ming.lei@xxxxxxxxxx>
Reviewed-by: Christoph Hellwig <hch@xxxxxx>
Link: https://lore.kernel.org/r/20211111020343.316126-1-ming.lei@xxxxxxxxxx
Signed-off-by: Jens Axboe <axboe@xxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
block/bdev.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index 485a258b0ab37..e52af269004bc 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -750,8 +750,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;
}
@@ -761,7 +760,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);
}

@@ -817,12 +815,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);

@@ -844,7 +844,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);
@@ -953,6 +954,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);
--
2.33.0