[PATCH v2 33/83] block: rust: add `TagSet` private data support

From: Andreas Hindborg

Date: Tue Jun 09 2026 - 15:19:44 EST


From: Andreas Hindborg <a.hindborg@xxxxxxxxxxx>

C block device drivers can attach private data to a `struct
blk_mq_tag_set`. Add support for this feature for Rust block device
drivers via the `Operations::TagSetData` associated type.

The private data is passed to `TagSet::new` and is stored in the
`driver_data` field of the underlying `struct blk_mq_tag_set`. It is
released when the `TagSet` is dropped.

Signed-off-by: Andreas Hindborg <a.hindborg@xxxxxxxxxx>
---
drivers/block/rnull/rnull.rs | 3 ++-
rust/kernel/block/mq.rs | 6 ++++--
rust/kernel/block/mq/operations.rs | 4 ++++
rust/kernel/block/mq/tag_set.rs | 26 ++++++++++++++++++++++----
4 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index be0b4bd25e53..ad26a4a8dbbe 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -240,7 +240,7 @@ fn new(options: NullBlkOptions<'_>) -> Result<GenDisk<Self>> {
}

let tagset = Arc::pin_init(
- TagSet::new(submit_queues, 256, 1, numa_node, flags),
+ TagSet::new(submit_queues, (), 256, 1, numa_node, flags),
GFP_KERNEL,
)?;

@@ -533,6 +533,7 @@ fn align_down<T>(value: T, to: T) -> T
impl Operations for NullBlkDevice {
type QueueData = Pin<KBox<QueueData>>;
type RequestData = Pdu;
+ type TagSetData = ();

fn new_request_data() -> impl PinInit<Self::RequestData> {
pin_init!(Pdu {
diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs
index bac15b509d90..28cee0d60846 100644
--- a/rust/kernel/block/mq.rs
+++ b/rust/kernel/block/mq.rs
@@ -71,6 +71,7 @@
//! impl Operations for MyBlkDevice {
//! type RequestData = ();
//! type QueueData = ();
+//! type TagSetData = ();
//!
//! fn new_request_data(
//! ) -> impl PinInit<()> {
@@ -94,8 +95,9 @@
//!
//! let tagset: Arc<TagSet<MyBlkDevice>> =
//! Arc::pin_init(
-//! TagSet::new(1, 256, 1, NumaNode::NO_NODE, mq::tag_set::Flags::default()),
-//! GFP_KERNEL)?;
+//! TagSet::new(1, (), 256, 1, NumaNode::NO_NODE, mq::tag_set::Flags::default()),
+//! GFP_KERNEL
+//! )?;
//! let mut disk = gen_disk::GenDiskBuilder::new()
//! .capacity_sectors(4096)
//! .build(fmt!("myblk"), tagset, ())?;
diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/operations.rs
index c49ca2e8bbb2..093bb21fa1b2 100644
--- a/rust/kernel/block/mq/operations.rs
+++ b/rust/kernel/block/mq/operations.rs
@@ -63,6 +63,10 @@ pub trait Operations: Sized {
/// the `GenDisk` associated with this `Operations` implementation.
type QueueData: ForeignOwnable + Sync;

+ /// Data associated with a `TagSet`. This is stored as a pointer in `struct
+ /// blk_mq_tag_set`.
+ type TagSetData: ForeignOwnable + Sync;
+
/// Called by the kernel to get an initializer for a `Pin<&mut RequestData>`.
fn new_request_data() -> impl PinInit<Self::RequestData>;

diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set.rs
index d6d104adf4aa..bfb8f8af4ee1 100644
--- a/rust/kernel/block/mq/tag_set.rs
+++ b/rust/kernel/block/mq/tag_set.rs
@@ -19,7 +19,10 @@
Result, //
},
prelude::*,
- types::Opaque,
+ types::{
+ ForeignOwnable,
+ Opaque, //
+ },
};
use core::{
convert::TryInto,
@@ -56,6 +59,7 @@ impl<T: Operations> TagSet<T> {
/// Try to create a new tag set
pub fn new(
nr_hw_queues: u32,
+ tagset_data: T::TagSetData,
num_tags: u32,
num_maps: u32,
numa_node: NumaNode,
@@ -73,7 +77,7 @@ pub fn new(
queue_depth: num_tags,
cmd_size,
flags: flags.into(),
- driver_data: core::ptr::null_mut::<c_void>(),
+ driver_data: tagset_data.into_foreign(),
nr_maps: num_maps,
..tag_set
}
@@ -86,7 +90,14 @@ pub fn new(
// SAFETY: we do not move out of `tag_set`.
let tag_set: &mut Opaque<_> = unsafe { Pin::get_unchecked_mut(tag_set) };
// SAFETY: `tag_set` is a reference to an initialized `blk_mq_tag_set`.
- error::to_result( unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())})
+ let status = error::to_result(
+ unsafe { bindings::blk_mq_alloc_tag_set(tag_set.get())}
+ );
+ if status.is_err() {
+ // SAFETY: We created `driver_data` above with `into_foreign`
+ unsafe { T::TagSetData::from_foreign((*tag_set.get()).driver_data) };
+ }
+ status
}),
_p: PhantomData,
})
@@ -102,7 +113,14 @@ pub(crate) fn raw_tag_set(&self) -> *mut bindings::blk_mq_tag_set {
impl<T: Operations> PinnedDrop for TagSet<T> {
fn drop(self: Pin<&mut Self>) {
// SAFETY: By type invariant `inner` is valid and has been properly
- // initialized during construction.
+ // initialised during construction.
+ let tagset_data = unsafe { (*self.inner.get()).driver_data };
+
+ // SAFETY: `inner` is valid and has been properly initialised during construction.
unsafe { bindings::blk_mq_free_tag_set(self.inner.get()) };
+
+ // SAFETY: `tagset_data` was created by a call to
+ // `ForeignOwnable::into_foreign` in `TagSet::try_new()`
+ unsafe { T::TagSetData::from_foreign(tagset_data) };
}
}

--
2.51.2