From: Andreas Hindborg <a.hindborg@kernel.org>
To: Daniel Almeida <daniel.almeida@collabora.com>
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>,
"Alice Ryhl" <aliceryhl@google.com>,
"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: Thu, 07 Aug 2025 10:02:52 +0200 [thread overview]
Message-ID: <87v7mz7e4z.fsf@kernel.org> (raw)
In-Reply-To: <03D084B5-5844-4BC5-902C-14E53AC13DC9@collabora.com>
"Daniel Almeida" <daniel.almeida@collabora.com> writes:
>> On 11 Jul 2025, at 08:43, Andreas Hindborg <a.hindborg@kernel.org> 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
>
> Should this really be a dependency? IIUC, the driver still works with this
> unset, it just doesn’t have this feature?
It does not and I do not intend for it to operate without configfs.
I did not try to build without configfs enabled, but the rnull driver
has calls to symbols provided by the configfs subsystem, so it really
should not work without configfs loaded.
>
>> 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 {}
>
> This still builds:
>
> diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
> index 3ae84dfc8d62..2e5ffa82e679 100644
> --- a/drivers/block/rnull/configfs.rs
> +++ b/drivers/block/rnull/configfs.rs
> @@ -24,10 +24,9 @@ pub(crate) fn subsystem() -> impl PinInit<kernel::configfs::Subsystem<Config>, E
> ],
> };
>
> - kernel::configfs::Subsystem::new(c_str!("rnull"), item_type, try_pin_init!(Config {}))
> + kernel::configfs::Subsystem::new(c_str!("rnull"), item_type, Config {})
> }
>
> -#[pin_data]
> pub(crate) struct Config {}
>
> Perhaps due to:
>
> // SAFETY: the `__pinned_init` function always returns `Ok(())` and initializes every field of
> // `slot`. Additionally, all pinning invariants of `T` are upheld.
> unsafe impl<T> PinInit<T> for T {
> unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), Infallible> {
> // SAFETY: `slot` is valid for writes by the safety requirements of this function.
> unsafe { slot.write(self) };
> Ok(())
> }
> }
Hmm, when I apply this change it does not work out for me:
RUSTC [M] drivers/block/rnull/rnull.o
error[E0277]: the trait bound `Config: PinInit<Config, kernel::error::Error>` is not satisfied
--> /home/aeh/src/linux-rust/rnull-up-v6.16-rc1/drivers/block/rnull/configfs.rs:27:66
|
27 | kernel::configfs::Subsystem::new(c_str!("rnull"), item_type, Config {})
| -------------------------------- ^^^^^^^^^ the trait `PinInit<Config, kernel::error::Error>` is not implemented for `Config`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `PinInit<T, E>`:
<AlwaysFail<T> as PinInit<T, ()>>
<ChainPinInit<I, F, T, E> as PinInit<T, E>>
<ChainInit<I, F, T, E> as PinInit<T, E>>
<core::result::Result<T, E> as PinInit<T, E>>
note: required by a bound in `Subsystem::<Data>::new`
--> /home/aeh/src/linux-rust/rnull-up-v6.16-rc1/rust/kernel/configfs.rs:151:20
|
148 | pub fn new(
| --- required by a bound in this associated function
...
151 | data: impl PinInit<Data, Error>,
| ^^^^^^^^^^^^^^^^^^^^ required by this bound in `Subsystem::<Data>::new`
error[E0277]: the trait bound `Config: PinInit<Config, kernel::error::Error>` is not satisfied
--> /home/aeh/src/linux-rust/rnull-up-v6.16-rc1/drivers/block/rnull/configfs.rs:17:30
|
17 | pub(crate) fn subsystem() -> impl PinInit<kernel::configfs::Subsystem<Config>, Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `PinInit<Config, kernel::error::Error>` is not implemented for `Config`
|
= help: the following other types implement trait `PinInit<T, E>`:
<AlwaysFail<T> as PinInit<T, ()>>
<ChainPinInit<I, F, T, E> as PinInit<T, E>>
<ChainInit<I, F, T, E> as PinInit<T, E>>
<core::result::Result<T, E> as PinInit<T, E>>
note: required by a bound in `Subsystem::<Data>::new`
--> /home/aeh/src/linux-rust/rnull-up-v6.16-rc1/rust/kernel/configfs.rs:151:20
|
148 | pub fn new(
| --- required by a bound in this associated function
...
151 | data: impl PinInit<Data, Error>,
| ^^^^^^^^^^^^^^^^^^^^ required by this bound in `Subsystem::<Data>::new`
error: aborting due to 2 previous errors
I rebased on rust-6.17. What did you apply this series to?
>
>
>> +
>> +#[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
>
> Isn’t this related to [0] ?
No, I think this is a type inference problem.
>
>
>> + 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"))?;
>> + }
>> +
>> + 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)?
>
> nit: I’d import that if I were you, but it’s your call.
OK.
>
>> + != 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;
>> + }
>
> nit: the guard is not used here, but it is still alive. This is harmless in
> this case, but as I general pattern, I find that using closures cuts back on
> the scope, i.e.:
>
> this.with_locked_data(|data| {
> // use the guard
> });
>
> // Guard is already free here, no surprises.
I don't see `with_locked_data` anywhere in the kernel crate? It would be
a method on `Mutex`? Or would you add the method to `DeviceConfig`?
>
>> +
>> + 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
>
> Why are these three removed?
Because the list is stale and I did not want to maintain it.
Best regards,
Andreas Hindborg
next prev parent reply other threads:[~2025-08-07 8:03 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
2025-08-07 9:50 ` Andreas Hindborg
2025-08-06 19:35 ` Daniel Almeida
2025-08-07 8:02 ` Andreas Hindborg [this message]
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=87v7mz7e4z.fsf@kernel.org \
--to=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=axboe@kernel.dk \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--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.