[PATCH 42/79] block: rnull: add shared tags

Andreas Hindborg
2026-02-15T23:44:26Z

Add support for sharing tags between multiple rnull devices. When
enabled via the `shared_tags` configfs attribute, all devices in the
group share a single tag set, reducing memory usage.

This feature requires creating a shared `TagSet` that can be referenced
by multiple devices.

Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
drivers/block/rnull/configfs.rs | 7 ++++++-
drivers/block/rnull/rnull.rs | 25 +++++++++++++++++++++----
rust/kernel/block/mq/tag_set.rs | 6 ++++++
3 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index 640e3de230c0a..d1a70fe030cb2 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -70,7 +70,7 @@ impl AttributeOperations<0> for Config {
let mut writer = kernel::str::Formatter::new(page);
writer.write_str(
"blocksize,size,rotational,irqmode,completion_nsec,memory_backed\
- submit_queues,use_per_node_hctx,discard,blocking\n",
+ submit_queues,use_per_node_hctx,discard,blocking,shared_tags\n",
)?;
Ok(writer.bytes_written())
}
@@ -107,6 +107,7 @@ fn make_group(
cache_size_mib: 15,
mbps: 16,
blocking: 17,
+ shared_tags: 18,
],
};

@@ -140,6 +141,7 @@ fn make_group(
cache_size_mib: 0,
mbps: 0,
blocking: false,
+ shared_tags: false,
}),
}),
core::iter::empty(),
@@ -206,6 +208,7 @@ struct DeviceConfigInner {
disk_storage: Arc<DiskStorage>,
mbps: u32,
blocking: bool,
+ shared_tags: bool,
}

#[vtable]
@@ -247,6 +250,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
storage: guard.disk_storage.clone(),
bandwidth_limit: u64::from(guard.mbps) * 2u64.pow(20),
blocking: guard.blocking,
+ shared_tags: guard.shared_tags,
})?);
guard.powered = true;
} else if guard.powered && !power_op {
@@ -467,3 +471,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {

configfs_simple_field!(DeviceConfig, 16, mbps, u32);
configfs_simple_bool_field!(DeviceConfig, 17, blocking);
+configfs_simple_bool_field!(DeviceConfig, 18, shared_tags);
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index cfc00c104d9ca..84e75a7042214 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -145,6 +145,10 @@
default: 0,
description: "Register as a blocking blk-mq driver device",
},
+ shared_tags: u8 {
+ default: 0,
+ description: "Share tag set between devices for blk-mq",
+ },
},
}

@@ -192,6 +196,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
storage: Arc::pin_init(DiskStorage::new(0, block_size as usize), GFP_KERNEL)?,
bandwidth_limit: u64::from(*module_parameters::mbps.value()) * 2u64.pow(20),
blocking: *module_parameters::blocking.value() != 0,
+ shared_tags: *module_parameters::shared_tags.value() != 0,
})?;
disks.push(disk, GFP_KERNEL)?;
}
@@ -224,8 +229,11 @@ struct NullBlkOptions<'a> {
storage: Arc<DiskStorage>,
bandwidth_limit: u64,
blocking: bool,
+ shared_tags: bool,
}

+static SHARED_TAG_SET: SetOnce<Arc<TagSet<NullBlkDevice>>> = SetOnce::new();
+
#[pin_data]
struct NullBlkDevice {
storage: Arc<DiskStorage>,
@@ -267,6 +275,7 @@ fn new(options: NullBlkOptions<'_>) -> Result<Arc<GenDisk<Self>>> {
storage,
bandwidth_limit,
blocking,
+ shared_tags,
} = options;

let mut flags = mq::tag_set::Flags::default();
@@ -286,10 +295,18 @@ fn new(options: NullBlkOptions<'_>) -> Result<Arc<GenDisk<Self>>> {
return Err(code::EINVAL);
}

- let tagset = Arc::pin_init(
- TagSet::new(submit_queues, (), 256, 1, home_node, flags),
- GFP_KERNEL,
- )?;
+ let tagset_ctor = || -> Result<Arc<_>> {
+ Arc::pin_init(
+ TagSet::new(submit_queues, (), 256, 1, home_node, flags),
+ GFP_KERNEL,
+ )
+ };
+
+ let tagset = if shared_tags {
+ SHARED_TAG_SET.as_ref_or_populate_with(tagset_ctor)?.clone()
+ } else {
+ tagset_ctor()?
+ };

let queue_data = Arc::try_pin_init(
try_pin_init!(Self {
diff --git a/rust/kernel/block/mq/tag_set.rs b/rust/kernel/block/mq/tag_set.rs
index f18b51e5217fe..600c9c6249123 100644
--- a/rust/kernel/block/mq/tag_set.rs
+++ b/rust/kernel/block/mq/tag_set.rs
@@ -106,3 +106,9 @@ fn drop(self: Pin<&mut Self>) {
unsafe { T::TagSetData::from_foreign(tagset_data) };
}
}
+
+// SAFETY: It is safe to transfer ownership of `TagSet` across thread boundaries.
+unsafe impl<T: Operations> Sync for TagSet<T> {}
+
+// SAFETY: It is safe to share references to `TagSet` across thread boundaries.
+unsafe impl<T: Operations> Send for TagSet<T> {}

--
2.51.2