[PATCH 17/79] block: rnull: allow specifying the home numa node

Andreas Hindborg
2026-02-15T23:43:46Z

Add a configfs attribute to specify the NUMA node for rnull tag set
and CPU map allocations. This allows testing NUMA-aware block device
behavior and optimizing memory placement for specific hardware
configurations.

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

diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index b05bfc23090c2..fe00617d2b679 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -5,6 +5,7 @@
THIS_MODULE, //
};
use kernel::{
+ bindings,
block::mq::gen_disk::{
GenDisk,
GenDiskBuilder, //
@@ -92,6 +93,7 @@ fn make_group(
memory_backed: 6,
submit_queues: 7,
use_per_node_hctx: 8,
+ home_node: 9,
],
};

@@ -111,6 +113,7 @@ fn make_group(
name: name.try_into()?,
memory_backed: false,
submit_queues: 1,
+ home_node: bindings::NUMA_NO_NODE,
}),
}),
core::iter::empty(),
@@ -167,6 +170,7 @@ struct DeviceConfigInner {
disk: Option<GenDisk<NullBlkDevice>>,
memory_backed: bool,
submit_queues: u32,
+ home_node: i32,
}

#[vtable]
@@ -199,6 +203,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
completion_time: guard.completion_time,
memory_backed: guard.memory_backed,
submit_queues: guard.submit_queues,
+ home_node: guard.home_node,
})?);
guard.powered = true;
} else if guard.powered && !power_op {
@@ -302,3 +307,17 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
Ok(())
})
);
+
+configfs_simple_field!(
+ DeviceConfig,
+ 9,
+ home_node,
+ i32,
+ check | value | {
+ if value == 0 || value >= kernel::num_online_nodes().try_into()? {
+ Err(kernel::error::code::EINVAL)
+ } else {
+ Ok(())
+ }
+ }
+);
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index 278f75d886fd9..fb3ce272d5bec 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -20,7 +20,10 @@
TagSet, //
},
},
- error::Result,
+ error::{
+ code,
+ Result, //
+ },
new_mutex,
new_xarray,
page::SafePage,
@@ -95,6 +98,10 @@
description:
"Use per-node allocation for hardware context queues, 0-false, 1-true. Default: 0-false",
},
+ home_node: i32 {
+ default: -1,
+ description: "Home node for the device. Default: -1 (no node)",
+ },
},
}

@@ -131,6 +138,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
completion_time: Delta::from_nanos(completion_time),
memory_backed: *module_parameters::memory_backed.value() != 0,
submit_queues,
+ home_node: *module_parameters::home_node.value(),
})?;
disks.push(disk, GFP_KERNEL)?;
}
@@ -154,6 +162,7 @@ struct NullBlkOptions<'a> {
completion_time: Delta,
memory_backed: bool,
submit_queues: u32,
+ home_node: i32,
}
struct NullBlkDevice;

@@ -168,6 +177,7 @@ fn new(options: NullBlkOptions<'_>) -> Result<GenDisk<Self>> {
completion_time,
memory_backed,
submit_queues,
+ home_node,
} = options;

let flags = if memory_backed {
@@ -176,8 +186,12 @@ fn new(options: NullBlkOptions<'_>) -> Result<GenDisk<Self>> {
mq::tag_set::Flags::default()
};

+ if home_node > kernel::num_online_nodes().try_into()? {
+ return Err(code::EINVAL);
+ }
+
let tagset = Arc::pin_init(
- TagSet::new(submit_queues, 256, 1, bindings::NUMA_NO_NODE, flags),
+ TagSet::new(submit_queues, 256, 1, home_node, flags),
GFP_KERNEL,
)?;

--
2.51.2