[PATCH 15/79] block: rnull: add `use_per_node_hctx` config option

From: Andreas Hindborg

Date: Sun Feb 15 2026 - 18:58:53 EST


Add a configfs attribute to enable per-NUMA-node hardware contexts.
When enabled, the driver creates one hardware queue per NUMA node
instead of the default configuration.

Signed-off-by: Andreas Hindborg <a.hindborg@xxxxxxxxxx>
---
drivers/block/rnull/configfs.rs | 26 ++++++++++++++++++++++++--
drivers/block/rnull/rnull.rs | 28 ++++++++++++++++++++++------
2 files changed, 46 insertions(+), 8 deletions(-)

diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index fd3cbf7aa012e..b05bfc23090c2 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -30,8 +30,11 @@
time, //
};
use macros::{
+ configfs_attribute,
configfs_simple_bool_field,
- configfs_simple_field, //
+ configfs_simple_field,
+ show_field,
+ store_with_power_check, //
};
use pin_init::PinInit;

@@ -61,7 +64,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\n",
+ submit_queues,use_per_node_hctx\n",
)?;
Ok(writer.bytes_written())
}
@@ -88,6 +91,7 @@ fn make_group(
completion_nsec: 5,
memory_backed: 6,
submit_queues: 7,
+ use_per_node_hctx: 8,
],
};

@@ -280,3 +284,21 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
Ok(())
}
}
+
+configfs_attribute!(DeviceConfig, 8,
+ show: |this, page| show_field(
+ this.data.lock().submit_queues == kernel::num_online_nodes(), page
+ ),
+ store: |this, page| store_with_power_check(this, page, |this, page| {
+ let value = core::str::from_utf8(page)?
+ .trim()
+ .parse::<u8>()
+ .map_err(|_| kernel::error::code::EINVAL)?
+ != 0;
+
+ if value {
+ this.data.lock().submit_queues *= kernel::num_online_nodes();
+ }
+ Ok(())
+ })
+);
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index 55ee5165b90b3..990dfcf95c9de 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -21,12 +21,17 @@
},
},
error::Result,
- new_mutex, new_xarray,
+ new_mutex,
+ new_xarray,
page::SafePage,
pr_info,
prelude::*,
str::CString,
- sync::{aref::ARef, Arc, Mutex},
+ sync::{
+ aref::ARef,
+ Arc,
+ Mutex, //
+ },
time::{
hrtimer::{
HrTimerCallback,
@@ -40,7 +45,7 @@
OwnableRefCounted,
Owned, //
},
- xarray::XArray,
+ xarray::XArray, //
};
use pin_init::PinInit;

@@ -73,8 +78,9 @@
description: "IRQ completion handler. 0-none, 1-softirq, 2-timer",
},
completion_nsec: u64 {
- default: 10_000,
- description: "Time in ns to complete a request in hardware. Default: 10,000ns",
+ default: 10_000,
+ description:
+ "Time in ns to complete a request in hardware. Default: 10,000ns",
},
memory_backed: u8 {
default: 0,
@@ -84,6 +90,11 @@
default: 1,
description: "Number of submission queues",
},
+ use_per_node_hctx: u8 {
+ default: 0,
+ description:
+ "Use per-node allocation for hardware context queues, 0-false, 1-true. Default: 0-false",
+ },
},
}

@@ -106,6 +117,11 @@ 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 submit_queues = if *module_parameters::use_per_node_hctx.value() != 0 {
+ kernel::num_online_nodes()
+ } else {
+ *module_parameters::submit_queues.value()
+ };
let disk = NullBlkDevice::new(NullBlkOptions {
name: &name,
block_size: *module_parameters::bs.value(),
@@ -114,7 +130,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
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(),
+ submit_queues,
})?;
disks.push(disk, GFP_KERNEL)?;
}

--
2.51.2