[PATCH v2 37/83] block: rust: add a back reference feature to `GenDisk`

From: Andreas Hindborg

Date: Tue Jun 09 2026 - 15:13:54 EST


During certain block layer callbacks, drivers may need access to the Rust
`GenDisk` representing a disk the driver is managing. In some situations it
is only possible to obtain a pointer to the C `struct gendisk`. With the
current setup, it is not possible to obtain the `GenDisk` for this C
`gendisk`. To circumvent this, we add a back reference feature to the
`GenDisk` so that we can store a reference counted reference to the
`GenDisk` somewhere easily accessible.

Signed-off-by: Andreas Hindborg <a.hindborg@xxxxxxxxxx>
---
drivers/block/rnull/configfs.rs | 2 +-
drivers/block/rnull/rnull.rs | 4 +--
rust/kernel/block/mq/gen_disk.rs | 65 ++++++++++++++++++++++++++++++++++++----
3 files changed, 62 insertions(+), 9 deletions(-)

diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index 504bb477c2d0..4df0b748596a 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -198,7 +198,7 @@ struct DeviceConfigInner {
capacity_mib: u64,
irq_mode: IRQMode,
completion_time: time::Delta,
- disk: Option<GenDisk<NullBlkDevice>>,
+ disk: Option<Arc<GenDisk<NullBlkDevice>>>,
memory_backed: bool,
submit_queues: u32,
home_node: i32,
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index 877683dba0ac..fd9b770965a6 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -134,7 +134,7 @@ struct NullBlkModule {
#[pin]
configfs_subsystem: kernel::configfs::Subsystem<configfs::Config>,
#[pin]
- param_disks: Mutex<KVec<GenDisk<NullBlkDevice>>>,
+ param_disks: Mutex<KVec<Arc<GenDisk<NullBlkDevice>>>>,
}

impl kernel::InPlaceModule for NullBlkModule {
@@ -216,7 +216,7 @@ struct NullBlkDevice {
}

impl NullBlkDevice {
- fn new(options: NullBlkOptions<'_>) -> Result<GenDisk<Self>> {
+ fn new(options: NullBlkOptions<'_>) -> Result<Arc<GenDisk<Self>>> {
let NullBlkOptions {
name,
block_size,
diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs
index 94af85fe1716..f51bccb0d2ef 100644
--- a/rust/kernel/block/mq/gen_disk.rs
+++ b/rust/kernel/block/mq/gen_disk.rs
@@ -21,14 +21,19 @@
Write, //
},
prelude::*,
+ revocable::Revocable,
static_lock_class,
str::NullTerminatedFormatter,
- sync::Arc,
+ sync::{
+ Arc,
+ UniqueArc, //
+ },
types::{
ForeignOwnable,
ScopeGuard, //
},
};
+use core::ptr::NonNull;

/// A builder for [`GenDisk`].
///
@@ -125,7 +130,7 @@ pub fn build<T: Operations>(
name: fmt::Arguments<'_>,
tagset: Arc<TagSet<T>>,
queue_data: T::QueueData,
- ) -> Result<GenDisk<T>> {
+ ) -> Result<Arc<GenDisk<T>>> {
let data = queue_data.into_foreign();
let recover_data = ScopeGuard::new(|| {
// SAFETY: T::QueueData was created by the call to `into_foreign()` above
@@ -204,10 +209,28 @@ pub fn build<T: Operations>(
// INVARIANT: `gendisk` was added to the VFS via `device_add_disk` above.
// INVARIANT: `gendisk.queue.queue_data` is set to `data` in the call to
// `__blk_mq_alloc_disk` above.
- Ok(GenDisk {
- _tagset: tagset,
- gendisk,
- })
+ let mut disk = UniqueArc::new(
+ GenDisk {
+ _tagset: tagset,
+ gendisk,
+ backref: Arc::pin_init(
+ // INVARIANT: We break `GenDiskRef` invariant here, but we restore it below.
+ Revocable::new(GenDiskRef(NonNull::dangling())),
+ GFP_KERNEL,
+ )?,
+ },
+ GFP_KERNEL,
+ )?;
+
+ disk.backref = Arc::pin_init(
+ // INVARIANT: The `GenDisk` in `disk` is a valid for use as a reference.
+ Revocable::new(GenDiskRef(
+ NonNull::new(UniqueArc::as_ptr(&disk).cast_mut()).expect("Should not be null"),
+ )),
+ GFP_KERNEL,
+ )?;
+
+ Ok(disk.into())
}
}

@@ -222,6 +245,14 @@ pub fn build<T: Operations>(
pub struct GenDisk<T: Operations> {
_tagset: Arc<TagSet<T>>,
gendisk: *mut bindings::gendisk,
+ backref: Arc<Revocable<GenDiskRef<T>>>,
+}
+
+impl<T: Operations> GenDisk<T> {
+ /// Get a `GenDiskRef` referencing this `GenDisk`.
+ pub fn get_ref(&self) -> Arc<Revocable<GenDiskRef<T>>> {
+ self.backref.clone()
+ }
}

// SAFETY: `GenDisk` is an owned pointer to a `struct gendisk` and an `Arc` to a
@@ -264,3 +295,25 @@ fn drop(&mut self) {
drop(unsafe { T::QueueData::from_foreign(queue_data) });
}
}
+
+/// A reference to a `GenDisk`.
+///
+/// # Invariants
+///
+/// `self.0` is valid for use as a reference.
+pub struct GenDiskRef<T: Operations>(NonNull<GenDisk<T>>);
+
+// SAFETY: It is safe to transfer ownership of `GenDiskRef` across thread boundaries.
+unsafe impl<T: Operations> Send for GenDiskRef<T> {}
+
+// SAFETY: It is safe to share references to `GenDiskRef` across thread boundaries.
+unsafe impl<T: Operations> Sync for GenDiskRef<T> {}
+
+impl<T: Operations> core::ops::Deref for GenDiskRef<T> {
+ type Target = GenDisk<T>;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: By type invariant, `self.0` is valid for use as a reference.
+ unsafe { self.0.as_ref() }
+ }
+}

--
2.51.2