From: Alice Ryhl <aliceryhl@google.com>
To: Andreas Hindborg <a.hindborg@kernel.org>
Cc: "Boqun Feng" <boqun.feng@gmail.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Jens Axboe" <axboe@kernel.dk>,
linux-block@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 12/16] rnull: enable configuration via `configfs`
Date: Tue, 15 Jul 2025 09:47:25 +0000 [thread overview]
Message-ID: <aHYjrSPttj0VQ7sg@google.com> (raw)
In-Reply-To: <20250711-rnull-up-v6-16-v3-12-3a262b4e2921@kernel.org>
On Fri, Jul 11, 2025 at 01:43:13PM +0200, Andreas Hindborg wrote:
> 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..6c0e3bbb36ec
> --- /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::Formatter::new(page);
> + writer.write_str("blocksize,size,rotational\n")?;
> + Ok(writer.bytes_written())
> + }
> +}
> +
> +#[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::Formatter::new(page);
> +
> + if this.data.lock().powered {
> + writer.write_fmt(fmt!("1\n"))?;
> + } else {
> + writer.write_fmt(fmt!("0\n"))?;
I think these can just be
writer.write_str("1\n")?;
> + }
> +
> + Ok(writer.bytes_written())
> + }
> +
> + 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;
So if I write 27, that's treated as true, but if I write 300, that's an
EINVAL?
> + 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::Formatter::new(page);
> + writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?;
> + Ok(writer.bytes_written())
> + }
> +
> + 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::Formatter::new(page);
> +
> + if this.data.lock().rotational {
> + writer.write_fmt(fmt!("1\n"))?;
> + } else {
> + writer.write_fmt(fmt!("0\n"))?;
> + }
> +
> + Ok(writer.bytes_written())
> + }
> +
> + 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::Formatter::new(page);
> + writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?;
> + Ok(writer.bytes_written())
> + }
> +
> + 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 39be2a31337f..7ab049ec591b 100644
> --- a/rust/kernel/block/mq/gen_disk.rs
> +++ b/rust/kernel/block/mq/gen_disk.rs
> @@ -50,7 +50,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
>
>
next prev parent reply other threads:[~2025-07-15 9:47 UTC|newest]
Thread overview: 56+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-11 11:43 [PATCH v3 00/16] rnull: add configfs, remote completion to rnull Andreas Hindborg
2025-07-11 11:43 ` [PATCH v3 01/16] rust: str: normalize imports in `str.rs` Andreas Hindborg
2025-08-06 12:59 ` Daniel Almeida
2025-07-11 11:43 ` [PATCH v3 02/16] rust: str: allow `str::Formatter` to format into `&mut [u8]` Andreas Hindborg
2025-07-15 9:34 ` Alice Ryhl
2025-08-06 13:05 ` Daniel Almeida
2025-08-06 14:32 ` Andreas Hindborg
2025-07-11 11:43 ` [PATCH v3 03/16] rust: str: expose `str::Formatter::new` publicly Andreas Hindborg
2025-07-15 9:35 ` Alice Ryhl
2025-08-06 13:06 ` Daniel Almeida
2025-07-11 11:43 ` [PATCH v3 04/16] rust: str: make `RawFormatter::bytes_written` public Andreas Hindborg
2025-07-15 9:36 ` Alice Ryhl
2025-08-06 9:43 ` Andreas Hindborg
2025-08-06 13:07 ` Daniel Almeida
2025-07-11 11:43 ` [PATCH v3 05/16] rust: str: introduce `NullTerminatedFormatter` Andreas Hindborg
2025-07-15 9:40 ` Alice Ryhl
2025-08-06 10:07 ` Andreas Hindborg
2025-08-06 13:15 ` Daniel Almeida
2025-08-06 14:47 ` Andreas Hindborg
2025-07-11 11:43 ` [PATCH v3 06/16] rust: block: normalize imports for `gen_disk.rs` Andreas Hindborg
2025-08-06 13:18 ` Daniel Almeida
2025-08-06 14:51 ` Andreas Hindborg
2025-08-06 15:31 ` Daniel Almeida
2025-08-07 7:12 ` Andreas Hindborg
2025-07-11 11:43 ` [PATCH v3 07/16] rust: block: use `NullTerminatedFormatter` Andreas Hindborg
2025-07-15 9:41 ` Alice Ryhl
2025-08-06 13:22 ` Daniel Almeida
2025-08-06 13:24 ` Daniel Almeida
2025-08-06 14:54 ` Andreas Hindborg
2025-07-11 11:43 ` [PATCH v3 08/16] rust: block: remove `RawWriter` Andreas Hindborg
2025-07-15 9:42 ` Alice Ryhl
2025-08-06 13:25 ` Daniel Almeida
2025-07-11 11:43 ` [PATCH v3 09/16] rust: block: remove trait bound from `mq::Request` definition Andreas Hindborg
2025-08-06 17:20 ` Daniel Almeida
2025-07-11 11:43 ` [PATCH v3 10/16] rust: block: add block related constants Andreas Hindborg
2025-07-15 9:44 ` Alice Ryhl
2025-08-06 10:28 ` Andreas Hindborg
2025-08-06 17:27 ` Daniel Almeida
2025-08-07 7:26 ` Andreas Hindborg
2025-07-11 11:43 ` [PATCH v3 11/16] rnull: move driver to separate directory Andreas Hindborg
2025-07-15 9:44 ` Alice Ryhl
2025-08-06 17:28 ` Daniel Almeida
2025-07-11 11:43 ` [PATCH v3 12/16] rnull: enable configuration via `configfs` Andreas Hindborg
2025-07-15 9:47 ` Alice Ryhl [this message]
2025-08-07 9:50 ` Andreas Hindborg
2025-08-06 19:35 ` Daniel Almeida
2025-08-07 8:02 ` Andreas Hindborg
2025-07-11 11:43 ` [PATCH v3 13/16] rust: block: add `GenDisk` private data support Andreas Hindborg
2025-07-15 9:51 ` Alice Ryhl
2025-08-07 9:57 ` Andreas Hindborg
2025-07-11 11:43 ` [PATCH v3 14/16] rust: block: mq: fix spelling in a safety comment Andreas Hindborg
2025-07-15 9:51 ` Alice Ryhl
2025-07-11 11:43 ` [PATCH v3 15/16] rust: block: add remote completion to `Request` Andreas Hindborg
2025-07-15 9:52 ` Alice Ryhl
2025-07-11 11:43 ` [PATCH v3 16/16] rnull: add soft-irq completion support Andreas Hindborg
2025-07-15 9:54 ` Alice Ryhl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aHYjrSPttj0VQ7sg@google.com \
--to=aliceryhl@google.com \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=axboe@kernel.dk \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.