Add post_release() block device operation which provides a hook for performing synchronous cleanup without disk->open_mutex held, which is needed by the loop devices. Real-world container engines, test suites, and system utilities rely on fput() from __loop_clr_fd() being completed when lo_release() returns. But changes which went to the v7.1 merge window broke an assumption that there is no outstanding I/O when __loop_clr_fd() is called, causing NULL pointer dereference problem in lo_rw_aio(). In order to fix this regression, we want to allow __loop_clr_fd() to flush outstanding I/O. But calling drain_workqueue() from __loop_clr_fd() with disk->open_mutex held causes lockdep warnings. We need a mechanism which can flush outstanding I/O without disk->open_mutex held. This post_release() operation is intended for performing only idempotent actions such as flush_work(), for nothing prevents multiple threads from concurrently calling this operation. That is, the loop device schedules a work_struct for calling __loop_clr_fd() from lo_release() where disk->open_mutex is held, and waits for completion of that work_struct using post_release() operation where disk->open_mutex is not held. Also, this post_release() operation is called from only bdev_release() path. This is because loop_configure() is not yet called (there is nothing to clear) if something went wrong between an initialization lo_open() and an error-unwinding lo_release() within the bdev_open() path. Signed-off-by: Tetsuo Handa --- block/bdev.c | 2 ++ include/linux/blkdev.h | 6 ++++++ rust/kernel/block/mq/gen_disk.rs | 1 + 3 files changed, 9 insertions(+) diff --git a/block/bdev.c b/block/bdev.c index cd8323083740..7ce5acaacf43 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -1188,6 +1188,8 @@ void bdev_release(struct file *bdev_file) else blkdev_put_whole(bdev); mutex_unlock(&disk->open_mutex); + if (bdev->bd_disk->fops->post_release) + bdev->bd_disk->fops->post_release(bdev->bd_disk); module_put(disk->fops->owner); put_no_open: diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4f7905c3412b..f05dba1b5962 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1605,6 +1605,12 @@ struct block_device_operations { * driver. */ int (*alternative_gpt_sector)(struct gendisk *disk, sector_t *sector); + /* + * Called after disk->open_mutex is released in the bdev_release() path. + * Used by loop devices that need to perform synchronization without + * holding disk->open_mutex. This operation has to be idempotent. + */ + void (*post_release)(struct gendisk *disk); }; #ifdef CONFIG_COMPAT diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs index fc97dd873974..2ff77ef49781 100644 --- a/rust/kernel/block/mq/gen_disk.rs +++ b/rust/kernel/block/mq/gen_disk.rs @@ -129,6 +129,7 @@ pub fn build( submit_bio: None, open: None, release: None, + post_release: None, ioctl: None, compat_ioctl: None, check_events: None, -- 2.55.0