[PATCH 33/79] block: rust: add a back reference feature to `GenDisk`

Andreas Hindborg
2026-02-15T23:45:31Z

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@kernel.org>
---
drivers/block/rnull/configfs.rs | 2 +-
drivers/block/rnull/rnull.rs | 4 +--
rust/kernel/block/mq/gen_disk.rs | 62 ++++++++++++++++++++++++++++++++++++----
3 files changed, 59 insertions(+), 9 deletions(-)

diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index d679f12ee6749..e365eb06be6de 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -189,7 +189,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 cca497aef40df..aa59ede72e495 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -136,7 +136,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 {
@@ -218,7 +218,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 72bbf3cadfe82..8d39bb70725b0 100644
--- a/rust/kernel/block/mq/gen_disk.rs
+++ b/rust/kernel/block/mq/gen_disk.rs
@@ -11,11 +11,13 @@
error::{self, from_err_ptr, Result},
fmt::{self, 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`].
///
@@ -112,7 +114,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
@@ -194,10 +196,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(disk.as_ptr().cast_mut()).expect("Should not be null"),
+ )),
+ GFP_KERNEL,
+ )?;
+
+ Ok(disk.into())
}
}

@@ -212,6 +232,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
@@ -241,3 +269,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