[PATCH v2 20/83] block: rnull: allow specifying the home numa node

From: Andreas Hindborg

Date: Tue Jun 09 2026 - 15:16:24 EST


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@xxxxxxxxxx>
---
drivers/block/rnull/configfs.rs | 19 +++++++++++++++++++
drivers/block/rnull/rnull.rs | 30 ++++++++++++++++++++++--------
2 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index 71b38373be33..2f3fa81ea121 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, //
@@ -91,6 +92,7 @@ fn make_group(
memory_backed: 6,
submit_queues: 7,
use_per_node_hctx: 8,
+ home_node: 9,
],
};

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

#[vtable]
@@ -208,6 +212,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 {
@@ -288,3 +293,17 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
Ok(())
})
);
+
+configfs_simple_field!(
+ DeviceConfig,
+ 9,
+ home_node,
+ i32,
+ check(|value| {
+ if value == 0 || value >= kernel::numa::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 6323327d4a5a..1d0faf524f5c 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -20,7 +20,10 @@
TagSet, //
},
},
- error::Result,
+ error::{
+ code,
+ Result, //
+ },
memalloc_scope,
new_mutex,
new_xarray,
@@ -93,6 +96,10 @@
default: false,
description: "Use per-node allocation for hardware context queues.",
},
+ home_node: i32 {
+ default: -1,
+ description: "Home node for the device. Default: -1 (no node)",
+ },
},
}

@@ -129,6 +136,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
completion_time: Delta::from_nanos(completion_time),
memory_backed: module_parameters::memory_backed.value(),
submit_queues,
+ home_node: module_parameters::home_node.value(),
})?;
disks.push(disk, GFP_KERNEL)?;
}
@@ -152,6 +160,7 @@ struct NullBlkOptions<'a> {
completion_time: Delta,
memory_backed: bool,
submit_queues: u32,
+ home_node: i32,
}
struct NullBlkDevice;

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

let flags = if memory_backed {
@@ -174,14 +184,18 @@ fn new(options: NullBlkOptions<'_>) -> Result<GenDisk<Self>> {
mq::tag_set::Flags::default()
};

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


--
2.51.2