[PATCH 29/79] block: rust: add `TagSet` private data support

Andreas Hindborg
2026-02-15T23:41:39Z

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@kernel.org>
---
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 | 25 ++++++++++++++++++++-----
4 files changed, 30 insertions(+), 8 deletions(-)

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

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

@@ -505,6 +505,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 d3957f2fb1a66..415be31e9a777 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, bindings::NUMA_NO_NODE, mq::tag_set::Flags::default()),
-//! GFP_KERNEL)?;
+//! TagSet::new(1, (), 256, 1, bindings::NUMA_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 cd37b939bbf30..9aab6240428cc 100644
--- a/rust/kernel/block/mq/operations.rs
+++ b/rust/kernel/block/mq/operations.rs
@@ -44,6 +44,10 @@ pub trait Operations: Sized {
/// the `GenDisk` associated with this `Operations` implementation.
type QueueData: ForeignOwnable;

+ /// Data associated with a `TagSet`. This is stored as a pointer in `struct
+ /// blk_mq_tag_set`.
+ type TagSetData: ForeignOwnable;
+
/// 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 e9b36d6329b9b..f18b51e5217fe 100644
--- a/rust/kernel/block/mq/tag_set.rs
+++ b/rust/kernel/block/mq/tag_set.rs
@@ -10,8 +10,8 @@
bindings,
block::mq::{operations::OperationsVTable, request::RequestDataWrapper, Operations},
error::{self, Result},
- prelude::try_pin_init,
- types::Opaque,
+ try_pin_init,
+ types::{ForeignOwnable, Opaque},
};
use core::{convert::TryInto, marker::PhantomData};
use pin_init::{pin_data, pinned_drop, PinInit};
@@ -39,6 +39,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: i32,
@@ -58,7 +59,7 @@ pub fn new(
queue_depth: num_tags,
cmd_size,
flags: flags.into_inner(),
- driver_data: core::ptr::null_mut::<crate::ffi::c_void>(),
+ driver_data: tagset_data.into_foreign(),
nr_maps: num_maps,
..tag_set
}
@@ -71,7 +72,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,
})
@@ -87,7 +95,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