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 --- 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>, + disk: Option>>, 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, #[pin] - param_disks: Mutex>>, + param_disks: Mutex>>>, } impl kernel::InPlaceModule for NullBlkModule { @@ -218,7 +218,7 @@ struct NullBlkDevice { } impl NullBlkDevice { - fn new(options: NullBlkOptions<'_>) -> Result> { + fn new(options: NullBlkOptions<'_>) -> Result>> { 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( name: fmt::Arguments<'_>, tagset: Arc>, queue_data: T::QueueData, - ) -> Result> { + ) -> Result>> { 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( // 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( pub struct GenDisk { _tagset: Arc>, gendisk: *mut bindings::gendisk, + backref: Arc>>, +} + +impl GenDisk { + /// Get a `GenDiskRef` referencing this `GenDisk`. + pub fn get_ref(&self) -> Arc>> { + 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(NonNull>); + +// SAFETY: It is safe to transfer ownership of `GenDiskRef` across thread boundaries. +unsafe impl Send for GenDiskRef {} + +// SAFETY: It is safe to share references to `GenDiskRef` across thread boundaries. +unsafe impl Sync for GenDiskRef {} + +impl core::ops::Deref for GenDiskRef { + type Target = GenDisk; + + 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