[PATCH v2.1 1/2] block: Add post_release() operation
From: Tetsuo Handa
Date: Fri Sep 18 2026 - 08:49:18 EST
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 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 <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
---
block/bdev.c | 2 ++
include/linux/blkdev.h | 8 ++++++++
rust/kernel/block/mq/gen_disk.rs | 1 +
3 files changed, 11 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..d13d703a7bea 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1577,6 +1577,14 @@ struct block_device_operations {
unsigned int flags);
int (*open)(struct gendisk *disk, blk_mode_t mode);
void (*release)(struct gendisk *disk);
+ /*
+ * This operation is called after returned from release() and
+ * disk->open_mutex was released. But be careful that this operation
+ * is not called after an initialization open() has succeeded but
+ * something went wrong and an error-unwinding release() was called.
+ * This operation might sleep.
+ */
+ void (*post_release)(struct gendisk *disk);
int (*ioctl)(struct block_device *bdev, blk_mode_t mode,
unsigned cmd, unsigned long arg);
int (*compat_ioctl)(struct block_device *bdev, blk_mode_t mode,
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<T: Operations>(
submit_bio: None,
open: None,
release: None,
+ post_release: None,
ioctl: None,
compat_ioctl: None,
check_events: None,
--
2.52.0