* [PATCH 0/9] rnull: add configfs, remote completion to rnull
@ 2025-06-16 13:23 Andreas Hindborg
2025-06-16 13:23 ` [PATCH 1/9] rust: block: remove trait bound from `mq::Request` definition Andreas Hindborg
` (8 more replies)
0 siblings, 9 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
This series adds configuration via configfs and remote completion to
the rnull driver. The series also includes a set of changes to the
rust block device driver API: a few cleanup patches, and a few features
supporting the rnull changes.
This series is a smaller subset of the patches available in the
downstream rnull tree. I hope to minimize the delta between upstream
and downstream over the next few kernel releases.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
Andreas Hindborg (9):
rust: block: remove trait bound from `mq::Request` definition
rust: block: add block related constants
rust: block,core: rename `RawWriter` to `BufferWriter`
rnull: move driver to separate directory
rnull: enable configuration via `configfs`
rust: block: add `GenDisk` private data support
rust: block: mq: fix spelling in a safety comment
rust: block: add remote completion to `Request`
rnull: add soft-irq completion support
MAINTAINERS | 2 +-
drivers/block/Kconfig | 10 +-
drivers/block/Makefile | 4 +-
drivers/block/rnull.rs | 80 ------
drivers/block/rnull/Kconfig | 13 +
drivers/block/rnull/Makefile | 3 +
drivers/block/rnull/configfs.rs | 277 +++++++++++++++++++++
drivers/block/rnull/rnull.rs | 105 ++++++++
rust/kernel/block.rs | 12 +
rust/kernel/block/mq.rs | 14 +-
rust/kernel/block/mq/gen_disk.rs | 53 +++-
rust/kernel/block/mq/operations.rs | 65 ++++-
rust/kernel/block/mq/request.rs | 21 +-
rust/kernel/str.rs | 3 +
.../mq/raw_writer.rs => str/buffer_writer.rs} | 17 +-
15 files changed, 550 insertions(+), 129 deletions(-)
---
base-commit: e04c78d86a9699d136910cfc0bdcf01087e3267e
change-id: 20250616-rnull-up-v6-16-b4571e28feee
Best regards,
--
Andreas Hindborg <a.hindborg@kernel.org>
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/9] rust: block: remove trait bound from `mq::Request` definition
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 2/9] rust: block: add block related constants Andreas Hindborg
` (7 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
Remove the trait bound `T:Operations` from `mq::Request`. The bound is not
required, so remove it to reduce complexity.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/block/mq/request.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs
index 4a5b7ec914ef..2d14a6261a31 100644
--- a/rust/kernel/block/mq/request.rs
+++ b/rust/kernel/block/mq/request.rs
@@ -53,7 +53,7 @@
/// [`struct request`]: srctree/include/linux/blk-mq.h
///
#[repr(transparent)]
-pub struct Request<T: Operations>(Opaque<bindings::request>, PhantomData<T>);
+pub struct Request<T>(Opaque<bindings::request>, PhantomData<T>);
impl<T: Operations> Request<T> {
/// Create an [`ARef<Request>`] from a [`struct request`] pointer.
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 2/9] rust: block: add block related constants
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
2025-06-16 13:23 ` [PATCH 1/9] rust: block: remove trait bound from `mq::Request` definition Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter` Andreas Hindborg
` (6 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
Add a few block subsystem constants to the rust `kernel::block` name space.
This makes it easier to access the constants from rust code.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/block.rs | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs
index 150f710efe5b..7461adf4d7e0 100644
--- a/rust/kernel/block.rs
+++ b/rust/kernel/block.rs
@@ -3,3 +3,15 @@
//! Types for working with the block layer.
pub mod mq;
+
+/// Bit mask for masking out [`SECTOR_SIZE`]
+pub const SECTOR_MASK: u32 = bindings::SECTOR_MASK;
+
+/// Sectors are size `1 << SECTOR_SHIFT`.
+pub const SECTOR_SHIFT: u32 = bindings::SECTOR_SHIFT;
+
+/// Size of a sector.
+pub const SECTOR_SIZE: u32 = bindings::SECTOR_SIZE;
+
+/// Power of two difference in size of a page and size of a sector.
+pub const PAGE_SECTORS_SHIFT: u32 = bindings::PAGE_SECTORS_SHIFT;
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter`
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
2025-06-16 13:23 ` [PATCH 1/9] rust: block: remove trait bound from `mq::Request` definition Andreas Hindborg
2025-06-16 13:23 ` [PATCH 2/9] rust: block: add block related constants Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-07-07 14:18 ` Miguel Ojeda
2025-06-16 13:23 ` [PATCH 4/9] rnull: move driver to separate directory Andreas Hindborg
` (5 subsequent siblings)
8 siblings, 1 reply; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
Rename the `RawWriter` to `BufferWriter`, wihich is a more suitable name.
Also move the module from `block` to `str`.
The ability to format a string to a byte buffer is something that is not
specific to `block`, so there is no reason this code should live in
`block`.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
`BufferWriter` is used in `rnull` for interacting with `configfs`.
---
rust/kernel/block/mq.rs | 1 -
rust/kernel/block/mq/gen_disk.rs | 9 +++++----
rust/kernel/str.rs | 3 +++
.../{block/mq/raw_writer.rs => str/buffer_writer.rs} | 17 +++++++++++------
4 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs
index fb0f393c1cea..faa3ccb5a49a 100644
--- a/rust/kernel/block/mq.rs
+++ b/rust/kernel/block/mq.rs
@@ -89,7 +89,6 @@
pub mod gen_disk;
mod operations;
-mod raw_writer;
mod request;
mod tag_set;
diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs
index cd54cd64ea88..a04b709514ac 100644
--- a/rust/kernel/block/mq/gen_disk.rs
+++ b/rust/kernel/block/mq/gen_disk.rs
@@ -5,10 +5,11 @@
//! C header: [`include/linux/blkdev.h`](srctree/include/linux/blkdev.h)
//! C header: [`include/linux/blk_mq.h`](srctree/include/linux/blk_mq.h)
-use crate::block::mq::{raw_writer::RawWriter, Operations, TagSet};
+use crate::block::mq::{Operations, TagSet};
use crate::{bindings, error::from_err_ptr, error::Result, sync::Arc};
use crate::{error, static_lock_class};
use core::fmt::{self, Write};
+use kernel::str::BufferWriter;
/// A builder for [`GenDisk`].
///
@@ -139,14 +140,14 @@ pub fn build<T: Operations>(
// SAFETY: `gendisk` is a valid pointer as we initialized it above
unsafe { (*gendisk).fops = &TABLE };
- let mut raw_writer = RawWriter::from_array(
+ let mut writer = BufferWriter::from_array(
// SAFETY: `gendisk` points to a valid and initialized instance. We
// have exclusive access, since the disk is not added to the VFS
// yet.
unsafe { &mut (*gendisk).disk_name },
)?;
- raw_writer.write_fmt(name)?;
- raw_writer.write_char('\0')?;
+ writer.write_fmt(name)?;
+ writer.write_char('\0')?;
// SAFETY: `gendisk` points to a valid and initialized instance of
// `struct gendisk`. `set_capacity` takes a lock to synchronize this
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index a927db8e079c..050793fb7d3a 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -936,3 +936,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
macro_rules! fmt {
($($f:tt)*) => ( ::core::format_args!($($f)*) )
}
+
+mod buffer_writer;
+pub use buffer_writer::BufferWriter;
diff --git a/rust/kernel/block/mq/raw_writer.rs b/rust/kernel/str/buffer_writer.rs
similarity index 77%
rename from rust/kernel/block/mq/raw_writer.rs
rename to rust/kernel/str/buffer_writer.rs
index 7e2159e4f6a6..364842a6cff8 100644
--- a/rust/kernel/block/mq/raw_writer.rs
+++ b/rust/kernel/str/buffer_writer.rs
@@ -10,14 +10,14 @@
/// # Invariants
///
/// `buffer` is always null terminated.
-pub(crate) struct RawWriter<'a> {
+pub struct BufferWriter<'a> {
buffer: &'a mut [u8],
pos: usize,
}
-impl<'a> RawWriter<'a> {
- /// Create a new `RawWriter` instance.
- fn new(buffer: &'a mut [u8]) -> Result<RawWriter<'a>> {
+impl<'a> BufferWriter<'a> {
+ /// Create a new [`Self`] instance.
+ pub fn new(buffer: &'a mut [u8]) -> Result<BufferWriter<'a>> {
*(buffer.last_mut().ok_or(EINVAL)?) = 0;
// INVARIANT: We null terminated the buffer above.
@@ -26,16 +26,21 @@ fn new(buffer: &'a mut [u8]) -> Result<RawWriter<'a>> {
pub(crate) fn from_array<const N: usize>(
a: &'a mut [crate::ffi::c_char; N],
- ) -> Result<RawWriter<'a>> {
+ ) -> Result<BufferWriter<'a>> {
Self::new(
// SAFETY: the buffer of `a` is valid for read and write as `u8` for
// at least `N` bytes.
unsafe { core::slice::from_raw_parts_mut(a.as_mut_ptr().cast::<u8>(), N) },
)
}
+
+ /// Return the position of the write pointer in the underlying buffer.
+ pub fn pos(&self) -> usize {
+ self.pos
+ }
}
-impl Write for RawWriter<'_> {
+impl Write for BufferWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
let len = bytes.len();
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 4/9] rnull: move driver to separate directory
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
` (2 preceding siblings ...)
2025-06-16 13:23 ` [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter` Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 5/9] rnull: enable configuration via `configfs` Andreas Hindborg
` (4 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
The rust null block driver is about to gain some additional modules. Rather
than pollute the current directory, move the driver to a subdirectory.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
MAINTAINERS | 2 +-
drivers/block/Kconfig | 10 +---------
drivers/block/Makefile | 4 +---
drivers/block/rnull/Kconfig | 13 +++++++++++++
drivers/block/rnull/Makefile | 3 +++
drivers/block/{ => rnull}/rnull.rs | 0
6 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0c1d245bf7b8..29b14aec3559 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4246,7 +4246,7 @@ W: https://rust-for-linux.com
B: https://github.com/Rust-for-Linux/linux/issues
C: https://rust-for-linux.zulipchat.com/#narrow/stream/Block
T: git https://github.com/Rust-for-Linux/linux.git rust-block-next
-F: drivers/block/rnull.rs
+F: drivers/block/rnull/
F: rust/kernel/block.rs
F: rust/kernel/block/
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 0f70e2374e7f..6b50dbc0495b 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -17,6 +17,7 @@ menuconfig BLK_DEV
if BLK_DEV
source "drivers/block/null_blk/Kconfig"
+source "drivers/block/rnull/Kconfig"
config BLK_DEV_FD
tristate "Normal floppy disk support"
@@ -354,15 +355,6 @@ config VIRTIO_BLK
This is the virtual block driver for virtio. It can be used with
QEMU based VMMs (like KVM or Xen). Say Y or M.
-config BLK_DEV_RUST_NULL
- tristate "Rust null block driver (Experimental)"
- depends on RUST
- help
- This is the Rust implementation of the null block driver. For now it
- is only a minimal stub.
-
- If unsure, say N.
-
config BLK_DEV_RBD
tristate "Rados block device (RBD)"
depends on INET && BLOCK
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 097707aca725..aba3e93d5014 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -9,9 +9,6 @@
# needed for trace events
ccflags-y += -I$(src)
-obj-$(CONFIG_BLK_DEV_RUST_NULL) += rnull_mod.o
-rnull_mod-y := rnull.o
-
obj-$(CONFIG_MAC_FLOPPY) += swim3.o
obj-$(CONFIG_BLK_DEV_SWIM) += swim_mod.o
obj-$(CONFIG_BLK_DEV_FD) += floppy.o
@@ -39,6 +36,7 @@ obj-$(CONFIG_ZRAM) += zram/
obj-$(CONFIG_BLK_DEV_RNBD) += rnbd/
obj-$(CONFIG_BLK_DEV_NULL_BLK) += null_blk/
+obj-$(CONFIG_BLK_DEV_RUST_NULL) += rnull/
obj-$(CONFIG_BLK_DEV_UBLK) += ublk_drv.o
obj-$(CONFIG_BLK_DEV_ZONED_LOOP) += zloop.o
diff --git a/drivers/block/rnull/Kconfig b/drivers/block/rnull/Kconfig
new file mode 100644
index 000000000000..6dc5aff96bf4
--- /dev/null
+++ b/drivers/block/rnull/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Rust null block device driver configuration
+
+config BLK_DEV_RUST_NULL
+ tristate "Rust null block driver (Experimental)"
+ depends on RUST
+ help
+ This is the Rust implementation of the null block driver. Like
+ the C version, the driver allows the user to create virutal block
+ devices that can be configured via various configuration options.
+
+ If unsure, say N.
diff --git a/drivers/block/rnull/Makefile b/drivers/block/rnull/Makefile
new file mode 100644
index 000000000000..11cfa5e615dc
--- /dev/null
+++ b/drivers/block/rnull/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_BLK_DEV_RUST_NULL) += rnull_mod.o
+rnull_mod-y := rnull.o
diff --git a/drivers/block/rnull.rs b/drivers/block/rnull/rnull.rs
similarity index 100%
rename from drivers/block/rnull.rs
rename to drivers/block/rnull/rnull.rs
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 5/9] rnull: enable configuration via `configfs`
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
` (3 preceding siblings ...)
2025-06-16 13:23 ` [PATCH 4/9] rnull: move driver to separate directory Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 6/9] rust: block: add `GenDisk` private data support Andreas Hindborg
` (3 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
Allow rust null block devices to be configured and instantiated via
`configfs`.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
drivers/block/rnull/Kconfig | 2 +-
drivers/block/rnull/configfs.rs | 220 +++++++++++++++++++++++++++++++++++++++
drivers/block/rnull/rnull.rs | 58 ++++++-----
rust/kernel/block/mq/gen_disk.rs | 2 +-
4 files changed, 253 insertions(+), 29 deletions(-)
diff --git a/drivers/block/rnull/Kconfig b/drivers/block/rnull/Kconfig
index 6dc5aff96bf4..7bc5b376c128 100644
--- a/drivers/block/rnull/Kconfig
+++ b/drivers/block/rnull/Kconfig
@@ -4,7 +4,7 @@
config BLK_DEV_RUST_NULL
tristate "Rust null block driver (Experimental)"
- depends on RUST
+ depends on RUST && CONFIGFS_FS
help
This is the Rust implementation of the null block driver. Like
the C version, the driver allows the user to create virutal block
diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
new file mode 100644
index 000000000000..315e04fbf14f
--- /dev/null
+++ b/drivers/block/rnull/configfs.rs
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use super::{NullBlkDevice, THIS_MODULE};
+use core::fmt::Write;
+use kernel::{
+ block::mq::gen_disk::{GenDisk, GenDiskBuilder},
+ c_str,
+ configfs::{self, AttributeOperations},
+ configfs_attrs, new_mutex,
+ page::PAGE_SIZE,
+ prelude::*,
+ str::CString,
+ sync::Mutex,
+};
+use pin_init::PinInit;
+
+pub(crate) fn subsystem() -> impl PinInit<kernel::configfs::Subsystem<Config>, Error> {
+ let item_type = configfs_attrs! {
+ container: configfs::Subsystem<Config>,
+ data: Config,
+ child: DeviceConfig,
+ attributes: [
+ features: 0,
+ ],
+ };
+
+ kernel::configfs::Subsystem::new(c_str!("rnull"), item_type, try_pin_init!(Config {}))
+}
+
+#[pin_data]
+pub(crate) struct Config {}
+
+#[vtable]
+impl AttributeOperations<0> for Config {
+ type Data = Config;
+
+ fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::BufferWriter::new(page)?;
+ writer.write_str("blocksize,size,rotational\n")?;
+ Ok(writer.pos())
+ }
+}
+
+#[vtable]
+impl configfs::GroupOperations for Config {
+ type Child = DeviceConfig;
+
+ fn make_group(
+ &self,
+ name: &CStr,
+ ) -> Result<impl PinInit<configfs::Group<DeviceConfig>, Error>> {
+ let item_type = configfs_attrs! {
+ container: configfs::Group<DeviceConfig>,
+ data: DeviceConfig,
+ attributes: [
+ // Named for compatibility with C null_blk
+ power: 0,
+ blocksize: 1,
+ rotational: 2,
+ size: 3,
+ ],
+ };
+
+ Ok(configfs::Group::new(
+ name.try_into()?,
+ item_type,
+ // TODO: cannot coerce new_mutex!() to impl PinInit<_, Error>, so put mutex inside
+ try_pin_init!( DeviceConfig {
+ data <- new_mutex!( DeviceConfigInner {
+ powered: false,
+ block_size: 4096,
+ rotational: false,
+ disk: None,
+ capacity_mib: 4096,
+ name: name.try_into()?,
+ }),
+ }),
+ ))
+ }
+}
+
+#[pin_data]
+pub(crate) struct DeviceConfig {
+ #[pin]
+ data: Mutex<DeviceConfigInner>,
+}
+
+#[pin_data]
+struct DeviceConfigInner {
+ powered: bool,
+ name: CString,
+ block_size: u32,
+ rotational: bool,
+ capacity_mib: u64,
+ disk: Option<GenDisk<NullBlkDevice>>,
+}
+
+#[vtable]
+impl configfs::AttributeOperations<0> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::BufferWriter::new(page)?;
+
+ if this.data.lock().powered {
+ writer.write_fmt(fmt!("1\n"))?;
+ } else {
+ writer.write_fmt(fmt!("0\n"))?;
+ }
+
+ Ok(writer.pos())
+ }
+
+ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
+ let power_op: bool = core::str::from_utf8(page)?
+ .trim()
+ .parse::<u8>()
+ .map_err(|_| kernel::error::code::EINVAL)?
+ != 0;
+
+ 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.powered = true;
+ } else if guard.powered && !power_op {
+ drop(guard.disk.take());
+ guard.powered = false;
+ }
+
+ Ok(())
+ }
+}
+
+#[vtable]
+impl configfs::AttributeOperations<1> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::BufferWriter::new(page)?;
+ writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?;
+ Ok(writer.pos())
+ }
+
+ 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)?;
+
+ GenDiskBuilder::validate_block_size(value)?;
+ this.data.lock().block_size = value;
+ Ok(())
+ }
+}
+
+#[vtable]
+impl configfs::AttributeOperations<2> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::BufferWriter::new(page)?;
+
+ if this.data.lock().rotational {
+ writer.write_fmt(fmt!("1\n"))?;
+ } else {
+ writer.write_fmt(fmt!("0\n"))?;
+ }
+
+ Ok(writer.pos())
+ }
+
+ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
+ if this.data.lock().powered {
+ return Err(EBUSY);
+ }
+
+ this.data.lock().rotational = core::str::from_utf8(page)?
+ .trim()
+ .parse::<u8>()
+ .map_err(|_| kernel::error::code::EINVAL)?
+ != 0;
+
+ Ok(())
+ }
+}
+
+#[vtable]
+impl configfs::AttributeOperations<3> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::BufferWriter::new(page)?;
+ writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?;
+ Ok(writer.pos())
+ }
+
+ 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::<u64>()
+ .map_err(|_| kernel::error::code::EINVAL)?;
+
+ this.data.lock().capacity_mib = value;
+ Ok(())
+ }
+}
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index d07e76ae2c13..d09bc77861e4 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -1,28 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
//! This is a Rust implementation of the C null block driver.
-//!
-//! Supported features:
-//!
-//! - blk-mq interface
-//! - direct completion
-//! - block size 4k
-//!
-//! The driver is not configurable.
+
+mod configfs;
use kernel::{
alloc::flags,
- block::mq::{
+ block::{
self,
- gen_disk::{self, GenDisk},
- Operations, TagSet,
+ mq::{
+ self,
+ gen_disk::{self, GenDisk},
+ Operations, TagSet,
+ },
},
error::Result,
- new_mutex, pr_info,
+ pr_info,
prelude::*,
- sync::{Arc, Mutex},
+ sync::Arc,
types::ARef,
};
+use pin_init::PinInit;
module! {
type: NullBlkModule,
@@ -35,33 +33,39 @@
#[pin_data]
struct NullBlkModule {
#[pin]
- _disk: Mutex<GenDisk<NullBlkDevice>>,
+ configfs_subsystem: kernel::configfs::Subsystem<configfs::Config>,
}
impl kernel::InPlaceModule for NullBlkModule {
fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
pr_info!("Rust null_blk loaded\n");
- // Use a immediately-called closure as a stable `try` block
- let disk = /* try */ (|| {
- let tagset = Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?;
-
- gen_disk::GenDiskBuilder::new()
- .capacity_sectors(4096 << 11)
- .logical_block_size(4096)?
- .physical_block_size(4096)?
- .rotational(false)
- .build(format_args!("rnullb{}", 0), tagset)
- })();
-
try_pin_init!(Self {
- _disk <- new_mutex!(disk?, "nullb:disk"),
+ configfs_subsystem <- configfs::subsystem(),
})
}
}
struct NullBlkDevice;
+impl NullBlkDevice {
+ fn new(
+ name: &CStr,
+ block_size: u32,
+ rotational: bool,
+ capacity_mib: u64,
+ ) -> Result<GenDisk<Self>> {
+ let tagset = Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?;
+
+ gen_disk::GenDiskBuilder::new()
+ .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT))
+ .logical_block_size(block_size)?
+ .physical_block_size(block_size)?
+ .rotational(rotational)
+ .build(fmt!("{}", name.to_str()?), tagset)
+ }
+}
+
#[vtable]
impl Operations for NullBlkDevice {
#[inline(always)]
diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs
index a04b709514ac..34f13e9b286f 100644
--- a/rust/kernel/block/mq/gen_disk.rs
+++ b/rust/kernel/block/mq/gen_disk.rs
@@ -46,7 +46,7 @@ pub fn rotational(mut self, rotational: bool) -> Self {
/// Validate block size by verifying that it is between 512 and `PAGE_SIZE`,
/// and that it is a power of two.
- fn validate_block_size(size: u32) -> Result {
+ pub fn validate_block_size(size: u32) -> Result {
if !(512..=bindings::PAGE_SIZE as u32).contains(&size) || !size.is_power_of_two() {
Err(error::code::EINVAL)
} else {
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 6/9] rust: block: add `GenDisk` private data support
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
` (4 preceding siblings ...)
2025-06-16 13:23 ` [PATCH 5/9] rnull: enable configuration via `configfs` Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 7/9] rust: block: mq: fix spelling in a safety comment Andreas Hindborg
` (2 subsequent siblings)
8 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
Allow users of the rust block device driver API to install private data in
the `GenDisk` structure.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
drivers/block/rnull/rnull.rs | 8 ++++---
rust/kernel/block/mq.rs | 7 +++---
rust/kernel/block/mq/gen_disk.rs | 46 +++++++++++++++++++++++++++++++-------
rust/kernel/block/mq/operations.rs | 46 ++++++++++++++++++++++++++++++--------
4 files changed, 84 insertions(+), 23 deletions(-)
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index d09bc77861e4..a012c59ecb3c 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -62,14 +62,16 @@ fn new(
.logical_block_size(block_size)?
.physical_block_size(block_size)?
.rotational(rotational)
- .build(fmt!("{}", name.to_str()?), tagset)
+ .build(fmt!("{}", name.to_str()?), tagset, ())
}
}
#[vtable]
impl Operations for NullBlkDevice {
+ type QueueData = ();
+
#[inline(always)]
- fn queue_rq(rq: ARef<mq::Request<Self>>, _is_last: bool) -> Result {
+ fn queue_rq(_queue_data: (), rq: ARef<mq::Request<Self>>, _is_last: bool) -> Result {
mq::Request::end_ok(rq)
.map_err(|_e| kernel::error::code::EIO)
// We take no refcounts on the request, so we expect to be able to
@@ -80,5 +82,5 @@ fn queue_rq(rq: ARef<mq::Request<Self>>, _is_last: bool) -> Result {
Ok(())
}
- fn commit_rqs() {}
+ fn commit_rqs(_queue_data: ()) {}
}
diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs
index faa3ccb5a49a..34b7425fa94d 100644
--- a/rust/kernel/block/mq.rs
+++ b/rust/kernel/block/mq.rs
@@ -69,20 +69,21 @@
//!
//! #[vtable]
//! impl Operations for MyBlkDevice {
+//! type QueueData = ();
//!
-//! fn queue_rq(rq: ARef<Request<Self>>, _is_last: bool) -> Result {
+//! fn queue_rq(_queue_data: (), rq: ARef<Request<Self>>, _is_last: bool) -> Result {
//! Request::end_ok(rq);
//! Ok(())
//! }
//!
-//! fn commit_rqs() {}
+//! fn commit_rqs(_queue_data: ()) {}
//! }
//!
//! let tagset: Arc<TagSet<MyBlkDevice>> =
//! Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?;
//! let mut disk = gen_disk::GenDiskBuilder::new()
//! .capacity_sectors(4096)
-//! .build(format_args!("myblk"), tagset)?;
+//! .build(format_args!("myblk"), tagset, ())?;
//!
//! # Ok::<(), kernel::error::Error>(())
//! ```
diff --git a/rust/kernel/block/mq/gen_disk.rs b/rust/kernel/block/mq/gen_disk.rs
index 34f13e9b286f..3a0fd9e47049 100644
--- a/rust/kernel/block/mq/gen_disk.rs
+++ b/rust/kernel/block/mq/gen_disk.rs
@@ -5,11 +5,18 @@
//! C header: [`include/linux/blkdev.h`](srctree/include/linux/blkdev.h)
//! C header: [`include/linux/blk_mq.h`](srctree/include/linux/blk_mq.h)
-use crate::block::mq::{Operations, TagSet};
-use crate::{bindings, error::from_err_ptr, error::Result, sync::Arc};
-use crate::{error, static_lock_class};
+use crate::{
+ bindings,
+ block::mq::{Operations, TagSet},
+ error::Result,
+ error::{self, from_err_ptr},
+ static_lock_class,
+ str::BufferWriter,
+ sync::Arc,
+ types::ForeignOwnable,
+ types::ScopeGuard,
+};
use core::fmt::{self, Write};
-use kernel::str::BufferWriter;
/// A builder for [`GenDisk`].
///
@@ -93,7 +100,14 @@ pub fn build<T: Operations>(
self,
name: fmt::Arguments<'_>,
tagset: Arc<TagSet<T>>,
+ queue_data: T::QueueData,
) -> Result<GenDisk<T>> {
+ let data = queue_data.into_foreign();
+ let recover_data = ScopeGuard::new(|| {
+ // SAFETY: T::QueueData was created by the call to `into_foreign()` above
+ unsafe { T::QueueData::from_foreign(data) };
+ });
+
// SAFETY: `bindings::queue_limits` contain only fields that are valid when zeroed.
let mut lim: bindings::queue_limits = unsafe { core::mem::zeroed() };
@@ -108,7 +122,7 @@ pub fn build<T: Operations>(
bindings::__blk_mq_alloc_disk(
tagset.raw_tag_set(),
&mut lim,
- core::ptr::null_mut(),
+ data.cast(),
static_lock_class!().as_ptr(),
)
})?;
@@ -162,8 +176,12 @@ pub fn build<T: Operations>(
},
)?;
+ recover_data.dismiss();
+
// INVARIANT: `gendisk` was initialized above.
// INVARIANT: `gendisk` was added to the VFS via `device_add_disk` above.
+ // INVARIANT: `gendisk.queue.queue_data` is set to `data` in the call to
+ // `__blk_mq_alloc_disk` above.
Ok(GenDisk {
_tagset: tagset,
gendisk,
@@ -175,9 +193,10 @@ pub fn build<T: Operations>(
///
/// # Invariants
///
-/// - `gendisk` must always point to an initialized and valid `struct gendisk`.
-/// - `gendisk` was added to the VFS through a call to
-/// `bindings::device_add_disk`.
+/// - `gendisk` must always point to an initialized and valid `struct gendisk`.
+/// - `gendisk` was added to the VFS through a call to
+/// `bindings::device_add_disk`.
+/// - `self.gendisk.queue.queuedata` is initialized by a call to `ForeignOwnable::into_foreign`.
pub struct GenDisk<T: Operations> {
_tagset: Arc<TagSet<T>>,
gendisk: *mut bindings::gendisk,
@@ -189,9 +208,20 @@ unsafe impl<T: Operations + Send> Send for GenDisk<T> {}
impl<T: Operations> Drop for GenDisk<T> {
fn drop(&mut self) {
+ // SAFETY: By type invariant of `Self`, `self.gendisk` points to a valid
+ // and initialized instance of `struct gendisk`, and, `queuedata` was
+ // initialized with the result of a call to
+ // `ForeignOwnable::into_foreign`.
+ let queue_data = unsafe { (*(*self.gendisk).queue).queuedata };
+
// SAFETY: By type invariant, `self.gendisk` points to a valid and
// initialized instance of `struct gendisk`, and it was previously added
// to the VFS.
unsafe { bindings::del_gendisk(self.gendisk) };
+
+ // SAFETY: `queue.queuedata` was created by `GenDiskBuilder::build` with
+ // a call to `ForeignOwnable::into_foreign` to create `queuedata`.
+ // `ForeignOwnable::from_foreign` is only called here.
+ let _queue_data = unsafe { T::QueueData::from_foreign(queue_data.cast()) };
}
}
diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/operations.rs
index 864ff379dc91..c50959d5517b 100644
--- a/rust/kernel/block/mq/operations.rs
+++ b/rust/kernel/block/mq/operations.rs
@@ -6,14 +6,15 @@
use crate::{
bindings,
- block::mq::request::RequestDataWrapper,
- block::mq::Request,
+ block::mq::{request::RequestDataWrapper, Request},
error::{from_result, Result},
prelude::*,
- types::ARef,
+ types::{ARef, ForeignOwnable},
};
use core::{marker::PhantomData, sync::atomic::AtomicU64, sync::atomic::Ordering};
+type ForeignBorrowed<'a, T> = <T as ForeignOwnable>::Borrowed<'a>;
+
/// Implement this trait to interface blk-mq as block devices.
///
/// To implement a block device driver, implement this trait as described in the
@@ -26,12 +27,20 @@
/// [module level documentation]: kernel::block::mq
#[macros::vtable]
pub trait Operations: Sized {
+ /// Data associated with the `struct request_queue` that is allocated for
+ /// the `GenDisk` associated with this `Operations` implementation.
+ type QueueData: ForeignOwnable;
+
/// Called by the kernel to queue a request with the driver. If `is_last` is
/// `false`, the driver is allowed to defer committing the request.
- fn queue_rq(rq: ARef<Request<Self>>, is_last: bool) -> Result;
+ fn queue_rq(
+ queue_data: ForeignBorrowed<'_, Self::QueueData>,
+ rq: ARef<Request<Self>>,
+ is_last: bool,
+ ) -> Result;
/// Called by the kernel to indicate that queued requests should be submitted.
- fn commit_rqs();
+ fn commit_rqs(queue_data: ForeignBorrowed<'_, Self::QueueData>);
/// Called by the kernel to poll the device for completed requests. Only
/// used for poll queues.
@@ -70,7 +79,7 @@ impl<T: Operations> OperationsVTable<T> {
/// promise to not access the request until the driver calls
/// `bindings::blk_mq_end_request` for the request.
unsafe extern "C" fn queue_rq_callback(
- _hctx: *mut bindings::blk_mq_hw_ctx,
+ hctx: *mut bindings::blk_mq_hw_ctx,
bd: *const bindings::blk_mq_queue_data,
) -> bindings::blk_status_t {
// SAFETY: `bd.rq` is valid as required by the safety requirement for
@@ -88,10 +97,20 @@ impl<T: Operations> OperationsVTable<T> {
// reference counted by `ARef` until then.
let rq = unsafe { Request::aref_from_raw((*bd).rq) };
+ // SAFETY: `hctx` is valid as required by this function.
+ let queue_data = unsafe { (*(*hctx).queue).queuedata };
+
+ // SAFETY: `queue.queuedata` was created by `GenDisk::try_new()` with a
+ // call to `ForeignOwnable::into_pointer()` to create `queuedata`.
+ // `ForeignOwnable::from_foreign()` is only called when the tagset is
+ // dropped, which happens after we are dropped.
+ let queue_data = unsafe { T::QueueData::borrow(queue_data.cast()) };
+
// SAFETY: We have exclusive access and we just set the refcount above.
unsafe { Request::start_unchecked(&rq) };
let ret = T::queue_rq(
+ queue_data,
rq,
// SAFETY: `bd` is valid as required by the safety requirement for
// this function.
@@ -110,9 +129,18 @@ impl<T: Operations> OperationsVTable<T> {
///
/// # Safety
///
- /// This function may only be called by blk-mq C infrastructure.
- unsafe extern "C" fn commit_rqs_callback(_hctx: *mut bindings::blk_mq_hw_ctx) {
- T::commit_rqs()
+ /// This function may only be called by blk-mq C infrastructure. The caller
+ /// must ensure that `hctx` is valid.
+ unsafe extern "C" fn commit_rqs_callback(hctx: *mut bindings::blk_mq_hw_ctx) {
+ // SAFETY: `hctx` is valid as required by this function.
+ let queue_data = unsafe { (*(*hctx).queue).queuedata };
+
+ // SAFETY: `queue.queuedata` was created by `GenDisk::try_new()` with a
+ // call to `ForeignOwnable::into_pointer()` to create `queuedata`.
+ // `ForeignOwnable::from_foreign()` is only called when the tagset is
+ // dropped, which happens after we are dropped.
+ let queue_data = unsafe { T::QueueData::borrow(queue_data.cast()) };
+ T::commit_rqs(queue_data)
}
/// This function is called by the C kernel. It is not currently
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 7/9] rust: block: mq: fix spelling in a safety comment
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
` (5 preceding siblings ...)
2025-06-16 13:23 ` [PATCH 6/9] rust: block: add `GenDisk` private data support Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 8/9] rust: block: add remote completion to `Request` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 9/9] rnull: add soft-irq completion support Andreas Hindborg
8 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
Add code block quotes to a safety comment.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
rust/kernel/block/mq/request.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs
index 2d14a6261a31..873d00db74dd 100644
--- a/rust/kernel/block/mq/request.rs
+++ b/rust/kernel/block/mq/request.rs
@@ -143,7 +143,7 @@ pub(crate) unsafe fn wrapper_ptr(this: *mut Self) -> NonNull<RequestDataWrapper>
// valid allocation.
let wrapper_ptr =
unsafe { bindings::blk_mq_rq_to_pdu(request_ptr).cast::<RequestDataWrapper>() };
- // SAFETY: By C API contract, wrapper_ptr points to a valid allocation
+ // SAFETY: By C API contract, `wrapper_ptr` points to a valid allocation
// and is not null.
unsafe { NonNull::new_unchecked(wrapper_ptr) }
}
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 8/9] rust: block: add remote completion to `Request`
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
` (6 preceding siblings ...)
2025-06-16 13:23 ` [PATCH 7/9] rust: block: mq: fix spelling in a safety comment Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 9/9] rnull: add soft-irq completion support Andreas Hindborg
8 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
Allow users of rust block device driver API to schedule completion of
requests via `blk_mq_complete_request_remote`.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
drivers/block/rnull/rnull.rs | 9 +++++++++
rust/kernel/block/mq.rs | 6 ++++++
rust/kernel/block/mq/operations.rs | 19 +++++++++++++++----
rust/kernel/block/mq/request.rs | 17 +++++++++++++++++
4 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index a012c59ecb3c..371786be7f47 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -83,4 +83,13 @@ fn queue_rq(_queue_data: (), rq: ARef<mq::Request<Self>>, _is_last: bool) -> Res
}
fn commit_rqs(_queue_data: ()) {}
+
+ fn complete(rq: ARef<mq::Request<Self>>) {
+ mq::Request::end_ok(rq)
+ .map_err(|_e| kernel::error::code::EIO)
+ // We take no refcounts on the request, so we expect to be able to
+ // end the request. The request reference must be unique at this
+ // point, and so `end_ok` cannot fail.
+ .expect("Fatal error - expected to be able to end request");
+ }
}
diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs
index 34b7425fa94d..551ef38efea2 100644
--- a/rust/kernel/block/mq.rs
+++ b/rust/kernel/block/mq.rs
@@ -77,6 +77,12 @@
//! }
//!
//! fn commit_rqs(_queue_data: ()) {}
+//!
+//! fn complete(rq: ARef<Request<Self>>) {
+//! Request::end_ok(rq)
+//! .map_err(|_e| kernel::error::code::EIO)
+//! .expect("Fatal error - expected to be able to end request");
+//! }
//! }
//!
//! let tagset: Arc<TagSet<MyBlkDevice>> =
diff --git a/rust/kernel/block/mq/operations.rs b/rust/kernel/block/mq/operations.rs
index c50959d5517b..b6b26cebd4f5 100644
--- a/rust/kernel/block/mq/operations.rs
+++ b/rust/kernel/block/mq/operations.rs
@@ -42,6 +42,9 @@ fn queue_rq(
/// Called by the kernel to indicate that queued requests should be submitted.
fn commit_rqs(queue_data: ForeignBorrowed<'_, Self::QueueData>);
+ /// Called by the kernel when the request is completed.
+ fn complete(rq: ARef<Request<Self>>);
+
/// Called by the kernel to poll the device for completed requests. Only
/// used for poll queues.
fn poll() -> bool {
@@ -143,13 +146,21 @@ impl<T: Operations> OperationsVTable<T> {
T::commit_rqs(queue_data)
}
- /// This function is called by the C kernel. It is not currently
- /// implemented, and there is no way to exercise this code path.
+ /// This function is called by the C kernel. A pointer to this function is
+ /// installed in the `blk_mq_ops` vtable for the driver.
///
/// # Safety
///
- /// This function may only be called by blk-mq C infrastructure.
- unsafe extern "C" fn complete_callback(_rq: *mut bindings::request) {}
+ /// This function may only be called by blk-mq C infrastructure. `rq` must
+ /// point to a valid request that has been marked as completed. The pointee
+ /// of `rq` must be valid for write for the duration of this function.
+ unsafe extern "C" fn complete_callback(rq: *mut bindings::request) {
+ // SAFETY: This function can only be dispatched through
+ // `Request::complete`. We leaked a refcount then which we pick back up
+ // now.
+ let aref = unsafe { Request::aref_from_raw(rq) };
+ T::complete(aref);
+ }
/// This function is called by the C kernel. A pointer to this function is
/// installed in the `blk_mq_ops` vtable for the driver.
diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs
index 873d00db74dd..244578a802ce 100644
--- a/rust/kernel/block/mq/request.rs
+++ b/rust/kernel/block/mq/request.rs
@@ -130,6 +130,23 @@ pub fn end_ok(this: ARef<Self>) -> Result<(), ARef<Self>> {
Ok(())
}
+ /// Complete the request by scheduling `Operations::complete` for
+ /// execution.
+ ///
+ /// The function may be scheduled locally, via SoftIRQ or remotely via IPMI.
+ /// See `blk_mq_complete_request_remote` in [`blk-mq.c`] for details.
+ ///
+ /// [`blk-mq.c`]: srctree/block/blk-mq.c
+ pub fn complete(this: ARef<Self>) {
+ let ptr = ARef::into_raw(this).cast::<bindings::request>().as_ptr();
+ // SAFETY: By type invariant, `self.0` is a valid `struct request`
+ if !unsafe { bindings::blk_mq_complete_request_remote(ptr) } {
+ // SAFETY: We released a refcount above that we can reclaim here.
+ let this = unsafe { Request::aref_from_raw(ptr) };
+ T::complete(this);
+ }
+ }
+
/// Return a pointer to the [`RequestDataWrapper`] stored in the private area
/// of the request structure.
///
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 9/9] rnull: add soft-irq completion support
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
` (7 preceding siblings ...)
2025-06-16 13:23 ` [PATCH 8/9] rust: block: add remote completion to `Request` Andreas Hindborg
@ 2025-06-16 13:23 ` Andreas Hindborg
2025-06-16 13:56 ` Johannes Thumshirn
8 siblings, 1 reply; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-16 13:23 UTC (permalink / raw)
To: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block, rust-for-linux, linux-kernel, Andreas Hindborg
rnull currently only supports direct completion. Add option for completing
requests across CPU nodes via soft IRQ or IPMI.
Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
drivers/block/rnull/configfs.rs | 61 +++++++++++++++++++++++++++++++++++++++--
drivers/block/rnull/rnull.rs | 32 +++++++++++++--------
2 files changed, 80 insertions(+), 13 deletions(-)
diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index 315e04fbf14f..22a7ec26cb36 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
use super::{NullBlkDevice, THIS_MODULE};
-use core::fmt::Write;
+use core::fmt::{Display, Write};
use kernel::{
block::mq::gen_disk::{GenDisk, GenDiskBuilder},
c_str,
@@ -36,7 +36,7 @@ impl AttributeOperations<0> for Config {
fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
let mut writer = kernel::str::BufferWriter::new(page)?;
- writer.write_str("blocksize,size,rotational\n")?;
+ writer.write_str("blocksize,size,rotational,irqmode\n")?;
Ok(writer.pos())
}
}
@@ -58,6 +58,7 @@ fn make_group(
blocksize: 1,
rotational: 2,
size: 3,
+ irqmode: 4,
],
};
@@ -72,6 +73,7 @@ fn make_group(
rotational: false,
disk: None,
capacity_mib: 4096,
+ irq_mode: IRQMode::None,
name: name.try_into()?,
}),
}),
@@ -79,6 +81,34 @@ fn make_group(
}
}
+#[derive(Debug, Clone, Copy)]
+pub(crate) enum IRQMode {
+ None,
+ Soft,
+}
+
+impl TryFrom<u8> for IRQMode {
+ type Error = kernel::error::Error;
+
+ fn try_from(value: u8) -> Result<Self> {
+ match value {
+ 0 => Ok(Self::None),
+ 1 => Ok(Self::Soft),
+ _ => Err(kernel::error::code::EINVAL),
+ }
+ }
+}
+
+impl Display for IRQMode {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ match self {
+ Self::None => f.write_str("0")?,
+ Self::Soft => f.write_str("1")?,
+ }
+ Ok(())
+ }
+}
+
#[pin_data]
pub(crate) struct DeviceConfig {
#[pin]
@@ -92,6 +122,7 @@ struct DeviceConfigInner {
block_size: u32,
rotational: bool,
capacity_mib: u64,
+ irq_mode: IRQMode,
disk: Option<GenDisk<NullBlkDevice>>,
}
@@ -126,6 +157,7 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
guard.block_size,
guard.rotational,
guard.capacity_mib,
+ guard.irq_mode,
)?);
guard.powered = true;
} else if guard.powered && !power_op {
@@ -218,3 +250,28 @@ fn store(this: &DeviceConfig, page: &[u8]) -> Result {
Ok(())
}
}
+
+#[vtable]
+impl configfs::AttributeOperations<4> for DeviceConfig {
+ type Data = DeviceConfig;
+
+ fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
+ let mut writer = kernel::str::BufferWriter::new(page)?;
+ writer.write_fmt(fmt!("{}\n", this.data.lock().irq_mode))?;
+ Ok(writer.pos())
+ }
+
+ 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::<u8>()
+ .map_err(|_| kernel::error::code::EINVAL)?;
+
+ this.data.lock().irq_mode = IRQMode::try_from(value)?;
+ Ok(())
+ }
+}
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index 371786be7f47..85b1509a3106 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -4,6 +4,7 @@
mod configfs;
+use configfs::IRQMode;
use kernel::{
alloc::flags,
block::{
@@ -54,35 +55,44 @@ fn new(
block_size: u32,
rotational: bool,
capacity_mib: u64,
+ irq_mode: IRQMode,
) -> Result<GenDisk<Self>> {
let tagset = Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?;
+ let queue_data = Box::new(QueueData { irq_mode }, flags::GFP_KERNEL)?;
+
gen_disk::GenDiskBuilder::new()
.capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT))
.logical_block_size(block_size)?
.physical_block_size(block_size)?
.rotational(rotational)
- .build(fmt!("{}", name.to_str()?), tagset, ())
+ .build(fmt!("{}", name.to_str()?), tagset, queue_data)
}
}
+struct QueueData {
+ irq_mode: IRQMode,
+}
+
#[vtable]
impl Operations for NullBlkDevice {
- type QueueData = ();
+ type QueueData = KBox<QueueData>;
#[inline(always)]
- fn queue_rq(_queue_data: (), rq: ARef<mq::Request<Self>>, _is_last: bool) -> Result {
- mq::Request::end_ok(rq)
- .map_err(|_e| kernel::error::code::EIO)
- // We take no refcounts on the request, so we expect to be able to
- // end the request. The request reference must be unique at this
- // point, and so `end_ok` cannot fail.
- .expect("Fatal error - expected to be able to end request");
-
+ fn queue_rq(queue_data: &QueueData, rq: ARef<mq::Request<Self>>, _is_last: bool) -> Result {
+ match queue_data.irq_mode {
+ IRQMode::None => mq::Request::end_ok(rq)
+ .map_err(|_e| kernel::error::code::EIO)
+ // We take no refcounts on the request, so we expect to be able to
+ // end the request. The request reference must be unique at this
+ // point, and so `end_ok` cannot fail.
+ .expect("Fatal error - expected to be able to end request"),
+ IRQMode::Soft => mq::Request::complete(rq),
+ }
Ok(())
}
- fn commit_rqs(_queue_data: ()) {}
+ fn commit_rqs(_queue_data: &QueueData) {}
fn complete(rq: ARef<mq::Request<Self>>) {
mq::Request::end_ok(rq)
--
2.47.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 9/9] rnull: add soft-irq completion support
2025-06-16 13:23 ` [PATCH 9/9] rnull: add soft-irq completion support Andreas Hindborg
@ 2025-06-16 13:56 ` Johannes Thumshirn
2025-06-17 8:08 ` Andreas Hindborg
0 siblings, 1 reply; 16+ messages in thread
From: Johannes Thumshirn @ 2025-06-16 13:56 UTC (permalink / raw)
To: Andreas Hindborg, Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe
Cc: linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-kernel@vger.kernel.org
On 16.06.25 15:28, Andreas Hindborg wrote:
> rnull currently only supports direct completion. Add option for completing
> requests across CPU nodes via soft IRQ or IPMI.
No idea about the Rust stuff but I think you ment IPI not IPMI.
Byte,
Johannes
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 9/9] rnull: add soft-irq completion support
2025-06-16 13:56 ` Johannes Thumshirn
@ 2025-06-17 8:08 ` Andreas Hindborg
0 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-06-17 8:08 UTC (permalink / raw)
To: Johannes Thumshirn
Cc: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe, linux-block@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
"Johannes Thumshirn" <Johannes.Thumshirn@wdc.com> writes:
> On 16.06.25 15:28, Andreas Hindborg wrote:
>> rnull currently only supports direct completion. Add option for completing
>> requests across CPU nodes via soft IRQ or IPMI.
>
> No idea about the Rust stuff but I think you ment IPI not IPMI.
Absolutely, thanks! Sneaky little M got in there.
Best regards,
Andreas Hindborg
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter`
2025-06-16 13:23 ` [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter` Andreas Hindborg
@ 2025-07-07 14:18 ` Miguel Ojeda
2025-07-07 14:58 ` Andreas Hindborg
0 siblings, 1 reply; 16+ messages in thread
From: Miguel Ojeda @ 2025-07-07 14:18 UTC (permalink / raw)
To: Andreas Hindborg
Cc: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe, linux-block, rust-for-linux,
linux-kernel
On Mon, Jun 16, 2025 at 3:26 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
> Rename the `RawWriter` to `BufferWriter`, wihich is a more suitable name.
> Also move the module from `block` to `str`.
The prefix should probably be "rust: block,str", or just "rust:".
(This patch would be ideally first in the series rather than in the
middle, by the way)
> -pub(crate) struct RawWriter<'a> {
> +pub struct BufferWriter<'a> {
Since you are re-exporting, can this be kept for the crate?
> + /// Create a new [`Self`] instance.
It is not a big deal here, but when you have a "move" commit, please
try to keep changes to the minimum, e.g. type renaming could be done
before or after.
> + /// Return the position of the write pointer in the underlying buffer.
> + pub fn pos(&self) -> usize {
> + self.pos
> + }
This is not mentioned in the commit message (and should have been a
different patch too).
By the way, cannot you use `{Raw,}Formatter`? You could add a
formatter that null-terminates automatically and/or that tracks the
lifetime, but we add the null manually elsewhere.
Speaking of which, aren't you null-terminating manually anyway in `gen_disk.rs`?
Ah, no, you are adding two nulls -- one at the end of the buffer, and
one in the middle. So the current code will fail if it needs one final
byte (i.e. when the last null would have been enough).
Given all that, I would say just drop this one, and use the one we
have. Then we can add a fancier formatter later on independently if
needed.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter`
2025-07-07 14:18 ` Miguel Ojeda
@ 2025-07-07 14:58 ` Andreas Hindborg
2025-07-07 18:25 ` Miguel Ojeda
0 siblings, 1 reply; 16+ messages in thread
From: Andreas Hindborg @ 2025-07-07 14:58 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe, linux-block, rust-for-linux,
linux-kernel
"Miguel Ojeda" <miguel.ojeda.sandonis@gmail.com> writes:
> On Mon, Jun 16, 2025 at 3:26 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>>
>> Rename the `RawWriter` to `BufferWriter`, wihich is a more suitable name.
>> Also move the module from `block` to `str`.
>
> The prefix should probably be "rust: block,str", or just "rust:".
OK 👍
>
> (This patch would be ideally first in the series rather than in the
> middle, by the way)
I'll move it.
>
>> -pub(crate) struct RawWriter<'a> {
>> +pub struct BufferWriter<'a> {
>
> Since you are re-exporting, can this be kept for the crate?
Yep.
>
>> + /// Create a new [`Self`] instance.
>
> It is not a big deal here, but when you have a "move" commit, please
> try to keep changes to the minimum, e.g. type renaming could be done
> before or after.
Will do.
>
>> + /// Return the position of the write pointer in the underlying buffer.
>> + pub fn pos(&self) -> usize {
>> + self.pos
>> + }
>
> This is not mentioned in the commit message (and should have been a
> different patch too).
Right.
> By the way, cannot you use `{Raw,}Formatter`? You could add a
> formatter that null-terminates automatically and/or that tracks the
> lifetime, but we add the null manually elsewhere.
I'll look into that. It looks like I could just use `RawFormatter`. I
don't recall the reason for `RawWriter`, it's been years and it was
Wedson who introduced it originally.
>
> Speaking of which, aren't you null-terminating manually anyway in `gen_disk.rs`?
>
> Ah, no, you are adding two nulls -- one at the end of the buffer, and
> one in the middle. So the current code will fail if it needs one final
> byte (i.e. when the last null would have been enough).
The null insertion at the call site should be removed, it's a leftover
from before `BufferWriter` handled that.
>
> Given all that, I would say just drop this one, and use the one we
> have. Then we can add a fancier formatter later on independently if
> needed.
Will try that. Thanks for taking a look.
Best regards,
Andreas Hindborg
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter`
2025-07-07 14:58 ` Andreas Hindborg
@ 2025-07-07 18:25 ` Miguel Ojeda
2025-07-08 8:44 ` Andreas Hindborg
0 siblings, 1 reply; 16+ messages in thread
From: Miguel Ojeda @ 2025-07-07 18:25 UTC (permalink / raw)
To: Andreas Hindborg
Cc: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe, linux-block, rust-for-linux,
linux-kernel
On Mon, Jul 7, 2025 at 4:58 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>
> The null insertion at the call site should be removed, it's a leftover
> from before `BufferWriter` handled that.
Just in case -- I think that if you do that then you would only get
the null terminator at the end of the buffer.
i.e. the one your formatter (`RawWriter`) places is really at the end
of the buffer, rather than immediately after your formatted text, so
if you don't have the one at the call site, you would end with garbage
in the middle unless the disk name fits perfectly into the buffer.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter`
2025-07-07 18:25 ` Miguel Ojeda
@ 2025-07-08 8:44 ` Andreas Hindborg
0 siblings, 0 replies; 16+ messages in thread
From: Andreas Hindborg @ 2025-07-08 8:44 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Boqun Feng, Miguel Ojeda, Alex Gaynor, Gary Guo,
Björn Roy Baron, Benno Lossin, Alice Ryhl, Trevor Gross,
Danilo Krummrich, Jens Axboe, linux-block, rust-for-linux,
linux-kernel
"Miguel Ojeda" <miguel.ojeda.sandonis@gmail.com> writes:
> On Mon, Jul 7, 2025 at 4:58 PM Andreas Hindborg <a.hindborg@kernel.org> wrote:
>>
>> The null insertion at the call site should be removed, it's a leftover
>> from before `BufferWriter` handled that.
>
> Just in case -- I think that if you do that then you would only get
> the null terminator at the end of the buffer.
>
> i.e. the one your formatter (`RawWriter`) places is really at the end
> of the buffer, rather than immediately after your formatted text, so
> if you don't have the one at the call site, you would end with garbage
> in the middle unless the disk name fits perfectly into the buffer.
I'll be sure to thoroughly check everything. Thanks for taking a look.
Best regards,
Andreas Hindborg
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2025-07-08 8:44 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-16 13:23 [PATCH 0/9] rnull: add configfs, remote completion to rnull Andreas Hindborg
2025-06-16 13:23 ` [PATCH 1/9] rust: block: remove trait bound from `mq::Request` definition Andreas Hindborg
2025-06-16 13:23 ` [PATCH 2/9] rust: block: add block related constants Andreas Hindborg
2025-06-16 13:23 ` [PATCH 3/9] rust: block,core: rename `RawWriter` to `BufferWriter` Andreas Hindborg
2025-07-07 14:18 ` Miguel Ojeda
2025-07-07 14:58 ` Andreas Hindborg
2025-07-07 18:25 ` Miguel Ojeda
2025-07-08 8:44 ` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 4/9] rnull: move driver to separate directory Andreas Hindborg
2025-06-16 13:23 ` [PATCH 5/9] rnull: enable configuration via `configfs` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 6/9] rust: block: add `GenDisk` private data support Andreas Hindborg
2025-06-16 13:23 ` [PATCH 7/9] rust: block: mq: fix spelling in a safety comment Andreas Hindborg
2025-06-16 13:23 ` [PATCH 8/9] rust: block: add remote completion to `Request` Andreas Hindborg
2025-06-16 13:23 ` [PATCH 9/9] rnull: add soft-irq completion support Andreas Hindborg
2025-06-16 13:56 ` Johannes Thumshirn
2025-06-17 8:08 ` Andreas Hindborg
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).