[PATCH v2 81/83] block: rnull: add `virt_boundary` option
From: Andreas Hindborg
Date: Tue Jun 09 2026 - 15:43:51 EST
Add a configfs attribute to configure the virtual memory boundary mask
for the rnull block device. This allows testing how drivers and
filesystems handle devices with specific alignment requirements.
Signed-off-by: Andreas Hindborg <a.hindborg@xxxxxxxxxx>
---
drivers/block/rnull/configfs.rs | 5 +++++
drivers/block/rnull/rnull.rs | 17 ++++++++++++++++-
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index 5ab217e43e2b..3e054339226c 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -133,6 +133,7 @@ fn make_group(
poll_queues: 27,
fua: 28,
max_sectors: 29,
+ virt_boundary: 30,
],
};
@@ -221,6 +222,7 @@ fn make_group(
#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)]
init_hctx_inject,
max_sectors: 0,
+ virt_boundary: false,
}),
}),
default_groups,
@@ -315,6 +317,7 @@ struct DeviceConfigInner {
#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)]
init_hctx_inject: Arc<FaultConfig>,
max_sectors: u32,
+ virt_boundary: bool,
}
#[vtable]
@@ -388,6 +391,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)]
timeout_inject: guard.timeout_inject.clone(),
max_sectors: guard.max_sectors,
+ virt_boundary: guard.virt_boundary,
})?);
guard.powered = true;
} else if guard.powered && !power_op {
@@ -617,3 +621,4 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
}
configfs_simple_bool_field!(DeviceConfig, 28, fua);
configfs_simple_field!(DeviceConfig, 29, max_sectors, u32);
+configfs_simple_bool_field!(DeviceConfig, 30, virt_boundary);
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index 15b8c365b9fa..147dc8498c3a 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -28,7 +28,10 @@
BadBlocks, //
},
bio::Segment,
- error::{BlkError, BlkResult},
+ error::{
+ BlkError,
+ BlkResult, //
+ },
mq::{
self,
gen_disk::{
@@ -54,6 +57,7 @@
memalloc_scope,
new_mutex,
new_spinlock,
+ page::PAGE_SIZE,
pr_info,
prelude::*,
revocable::Revocable,
@@ -208,6 +212,10 @@
default: 0,
description: "Maximum size of a command (in 512B sectors)",
},
+ virt_boundary: bool {
+ default: false,
+ description: "Set alignment requirement for IO buffers to be page size.",
+ },
},
}
@@ -312,6 +320,7 @@ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)]
timeout_inject: Arc::pin_init(FaultConfig::new(c"timeout_inject"), GFP_KERNEL)?,
max_sectors: module_parameters::max_sectors.value(),
+ virt_boundary: module_parameters::virt_boundary.value(),
})?;
disks.push(disk, GFP_KERNEL)?;
}
@@ -358,6 +367,7 @@ struct NullBlkOptions<'a> {
#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)]
timeout_inject: Arc<FaultConfig>,
max_sectors: u32,
+ virt_boundary: bool,
}
#[pin_data]
@@ -494,6 +504,7 @@ fn new(options: NullBlkOptions<'_>) -> Result<Arc<GenDisk<Self>>> {
#[cfg(CONFIG_BLK_DEV_RUST_NULL_FAULT_INJECTION)]
timeout_inject,
max_sectors,
+ virt_boundary,
} = options;
let memory_backed = tag_set.memory_backed;
@@ -558,6 +569,10 @@ fn new(options: NullBlkOptions<'_>) -> Result<Arc<GenDisk<Self>>> {
.forced_unit_access(forced_unit_access && storage.cache_enabled())
.max_sectors(max_sectors);
+ if virt_boundary {
+ builder = builder.virt_boundary_mask(PAGE_SIZE - 1);
+ }
+
#[cfg(CONFIG_BLK_DEV_ZONED)]
{
builder = builder
--
2.51.2