[PATCH 14/79] block: rnull: add submit queue count config option

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

Allow user space to control the number of submission queues when creating
null block devices.

Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
drivers/block/rnull/configfs.rs | 56 +++++++++++++++++++++++++++++++++--------
drivers/block/rnull/rnull.rs | 56 +++++++++++++++++++++++++++--------------
2 files changed, 83 insertions(+), 29 deletions(-)

diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index b5dc30c5d3e20..fd3cbf7aa012e 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -59,7 +59,10 @@ impl AttributeOperations<0> for Config {

fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
let mut writer = kernel::str::Formatter::new(page);
- writer.write_str("blocksize,size,rotational,irqmode,completion_nsec,memory_backed\n")?;
+ writer.write_str(
+ "blocksize,size,rotational,irqmode,completion_nsec,memory_backed\
+ submit_queues\n",
+ )?;
Ok(writer.bytes_written())
}
}
@@ -84,6 +87,7 @@ fn make_group(
irqmode: 4,
completion_nsec: 5,
memory_backed: 6,
+ submit_queues: 7,
],
};

@@ -102,6 +106,7 @@ fn make_group(
completion_time: time::Delta::ZERO,
name: name.try_into()?,
memory_backed: false,
+ submit_queues: 1,
}),
}),
core::iter::empty(),
@@ -157,6 +162,7 @@ struct DeviceConfigInner {
completion_time: time::Delta,
disk: Option<GenDisk<NullBlkDevice>>,
memory_backed: bool,
+ submit_queues: u32,
}

#[vtable]
@@ -180,15 +186,16 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
let mut guard = this.data.lock();

if !guard.powered && power_op {
- guard.disk = Some(NullBlkDevice::new(
- &guard.name,
- guard.block_size,
- guard.rotational,
- guard.capacity_mib,
- guard.irq_mode,
- guard.completion_time,
- guard.memory_backed,
- )?);
+ guard.disk = Some(NullBlkDevice::new(crate::NullBlkOptions {
+ name: &guard.name,
+ block_size: guard.block_size,
+ rotational: guard.rotational,
+ capacity_mib: guard.capacity_mib,
+ irq_mode: guard.irq_mode,
+ completion_time: guard.completion_time,
+ memory_backed: guard.memory_backed,
+ submit_queues: guard.submit_queues,
+ })?);
guard.powered = true;
} else if guard.powered && !power_op {
drop(guard.disk.take());
@@ -244,3 +251,32 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
Ok(())
}
}
+
+#[vtable]
+impl configfs::AttributeOperations<7> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::Formatter::new(page);
+ writer.write_fmt(fmt!("{}\n", this.data.lock().submit_queues))?;
+ Ok(writer.bytes_written())
+ }
+
+ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
+ if this.data.lock().powered {
+ return Err(EBUSY);
+ }
+
+ let text = core::str::from_utf8(page)?.trim();
+ let value = text
+ .parse::<u32>()
+ .map_err(|_| kernel::error::code::EINVAL)?;
+
+ if value == 0 || value > kernel::num_possible_cpus() {
+ return Err(kernel::error::code::EINVAL);
+ }
+
+ this.data.lock().submit_queues = value;
+ Ok(())
+ }
+}
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index a1156a368c467..55ee5165b90b3 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -80,6 +80,10 @@
default: 0,
description: "Create a memory-backed block device. 0-false, 1-true. Default: 0",
},
+ submit_queues: u32 {
+ default: 1,
+ description: "Number of submission queues",
+ },
},
}

@@ -102,15 +106,16 @@ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
for i in 0..(*module_parameters::nr_devices.value()) {
let name = CString::try_from_fmt(fmt!("rnullb{}", i))?;

- let disk = NullBlkDevice::new(
- &name,
- *module_parameters::bs.value(),
- *module_parameters::rotational.value() != 0,
- *module_parameters::gb.value() * 1024,
- (*module_parameters::irqmode.value()).try_into()?,
- Delta::from_nanos(completion_time),
- *module_parameters::memory_backed.value() != 0,
- )?;
+ let disk = NullBlkDevice::new(NullBlkOptions {
+ name: &name,
+ block_size: *module_parameters::bs.value(),
+ rotational: *module_parameters::rotational.value() != 0,
+ capacity_mib: *module_parameters::gb.value() * 1024,
+ irq_mode: (*module_parameters::irqmode.value()).try_into()?,
+ completion_time: Delta::from_nanos(completion_time),
+ memory_backed: *module_parameters::memory_backed.value() != 0,
+ submit_queues: *module_parameters::submit_queues.value(),
+ })?;
disks.push(disk, GFP_KERNEL)?;
}

@@ -124,25 +129,38 @@ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
}
}

+struct NullBlkOptions<'a> {
+ name: &'a CStr,
+ block_size: u32,
+ rotational: bool,
+ capacity_mib: u64,
+ irq_mode: IRQMode,
+ completion_time: Delta,
+ memory_backed: bool,
+ submit_queues: u32,
+}
struct NullBlkDevice;

impl NullBlkDevice {
- fn new(
- name: &CStr,
- block_size: u32,
- rotational: bool,
- capacity_mib: u64,
- irq_mode: IRQMode,
- completion_time: Delta,
- memory_backed: bool,
- ) -> Result<GenDisk<Self>> {
+ fn new(options: NullBlkOptions<'_>) -> Result<GenDisk<Self>> {
+ let NullBlkOptions {
+ name,
+ block_size,
+ rotational,
+ capacity_mib,
+ irq_mode,
+ completion_time,
+ memory_backed,
+ submit_queues,
+ } = options;
+
let flags = if memory_backed {
mq::tag_set::Flag::Blocking.into()
} else {
mq::tag_set::Flags::default()
};

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

let queue_data = Box::pin_init(
pin_init!(QueueData {

--
2.51.2