Skip to content

Commit d969d50

Browse files
Andreas Hindborgaxboe
authored andcommitted
rnull: enable configuration via configfs
Allow rust null block devices to be configured and instantiated via `configfs`. Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org> Link: https://lore.kernel.org/r/20250902-rnull-up-v6-16-v7-13-b5212cc89b98@kernel.org Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent edd8650 commit d969d50

4 files changed

Lines changed: 240 additions & 30 deletions

File tree

drivers/block/rnull/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
config BLK_DEV_RUST_NULL
66
tristate "Rust null block driver (Experimental)"
7-
depends on RUST
7+
depends on RUST && CONFIGFS_FS
88
help
99
This is the Rust implementation of the null block driver. Like
1010
the C version, the driver allows the user to create virutal block

drivers/block/rnull/configfs.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use super::{NullBlkDevice, THIS_MODULE};
4+
use core::fmt::Write;
5+
use kernel::{
6+
block::mq::gen_disk::{GenDisk, GenDiskBuilder},
7+
c_str,
8+
configfs::{self, AttributeOperations},
9+
configfs_attrs, new_mutex,
10+
page::PAGE_SIZE,
11+
prelude::*,
12+
str::{kstrtobool_bytes, CString},
13+
sync::Mutex,
14+
};
15+
use pin_init::PinInit;
16+
17+
pub(crate) fn subsystem() -> impl PinInit<kernel::configfs::Subsystem<Config>, Error> {
18+
let item_type = configfs_attrs! {
19+
container: configfs::Subsystem<Config>,
20+
data: Config,
21+
child: DeviceConfig,
22+
attributes: [
23+
features: 0,
24+
],
25+
};
26+
27+
kernel::configfs::Subsystem::new(c_str!("rnull"), item_type, try_pin_init!(Config {}))
28+
}
29+
30+
#[pin_data]
31+
pub(crate) struct Config {}
32+
33+
#[vtable]
34+
impl AttributeOperations<0> for Config {
35+
type Data = Config;
36+
37+
fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
38+
let mut writer = kernel::str::Formatter::new(page);
39+
writer.write_str("blocksize,size,rotational\n")?;
40+
Ok(writer.bytes_written())
41+
}
42+
}
43+
44+
#[vtable]
45+
impl configfs::GroupOperations for Config {
46+
type Child = DeviceConfig;
47+
48+
fn make_group(
49+
&self,
50+
name: &CStr,
51+
) -> Result<impl PinInit<configfs::Group<DeviceConfig>, Error>> {
52+
let item_type = configfs_attrs! {
53+
container: configfs::Group<DeviceConfig>,
54+
data: DeviceConfig,
55+
attributes: [
56+
// Named for compatibility with C null_blk
57+
power: 0,
58+
blocksize: 1,
59+
rotational: 2,
60+
size: 3,
61+
],
62+
};
63+
64+
Ok(configfs::Group::new(
65+
name.try_into()?,
66+
item_type,
67+
// TODO: cannot coerce new_mutex!() to impl PinInit<_, Error>, so put mutex inside
68+
try_pin_init!( DeviceConfig {
69+
data <- new_mutex!(DeviceConfigInner {
70+
powered: false,
71+
block_size: 4096,
72+
rotational: false,
73+
disk: None,
74+
capacity_mib: 4096,
75+
name: name.try_into()?,
76+
}),
77+
}),
78+
))
79+
}
80+
}
81+
82+
#[pin_data]
83+
pub(crate) struct DeviceConfig {
84+
#[pin]
85+
data: Mutex<DeviceConfigInner>,
86+
}
87+
88+
#[pin_data]
89+
struct DeviceConfigInner {
90+
powered: bool,
91+
name: CString,
92+
block_size: u32,
93+
rotational: bool,
94+
capacity_mib: u64,
95+
disk: Option<GenDisk<NullBlkDevice>>,
96+
}
97+
98+
#[vtable]
99+
impl configfs::AttributeOperations<0> for DeviceConfig {
100+
type Data = DeviceConfig;
101+
102+
fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
103+
let mut writer = kernel::str::Formatter::new(page);
104+
105+
if this.data.lock().powered {
106+
writer.write_str("1\n")?;
107+
} else {
108+
writer.write_str("0\n")?;
109+
}
110+
111+
Ok(writer.bytes_written())
112+
}
113+
114+
fn store(this: &DeviceConfig, page: &[u8]) -> Result {
115+
let power_op = kstrtobool_bytes(page)?;
116+
let mut guard = this.data.lock();
117+
118+
if !guard.powered && power_op {
119+
guard.disk = Some(NullBlkDevice::new(
120+
&guard.name,
121+
guard.block_size,
122+
guard.rotational,
123+
guard.capacity_mib,
124+
)?);
125+
guard.powered = true;
126+
} else if guard.powered && !power_op {
127+
drop(guard.disk.take());
128+
guard.powered = false;
129+
}
130+
131+
Ok(())
132+
}
133+
}
134+
135+
#[vtable]
136+
impl configfs::AttributeOperations<1> for DeviceConfig {
137+
type Data = DeviceConfig;
138+
139+
fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
140+
let mut writer = kernel::str::Formatter::new(page);
141+
writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?;
142+
Ok(writer.bytes_written())
143+
}
144+
145+
fn store(this: &DeviceConfig, page: &[u8]) -> Result {
146+
if this.data.lock().powered {
147+
return Err(EBUSY);
148+
}
149+
150+
let text = core::str::from_utf8(page)?.trim();
151+
let value = text.parse::<u32>().map_err(|_| EINVAL)?;
152+
153+
GenDiskBuilder::validate_block_size(value)?;
154+
this.data.lock().block_size = value;
155+
Ok(())
156+
}
157+
}
158+
159+
#[vtable]
160+
impl configfs::AttributeOperations<2> for DeviceConfig {
161+
type Data = DeviceConfig;
162+
163+
fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
164+
let mut writer = kernel::str::Formatter::new(page);
165+
166+
if this.data.lock().rotational {
167+
writer.write_str("1\n")?;
168+
} else {
169+
writer.write_str("0\n")?;
170+
}
171+
172+
Ok(writer.bytes_written())
173+
}
174+
175+
fn store(this: &DeviceConfig, page: &[u8]) -> Result {
176+
if this.data.lock().powered {
177+
return Err(EBUSY);
178+
}
179+
180+
this.data.lock().rotational = kstrtobool_bytes(page)?;
181+
182+
Ok(())
183+
}
184+
}
185+
186+
#[vtable]
187+
impl configfs::AttributeOperations<3> for DeviceConfig {
188+
type Data = DeviceConfig;
189+
190+
fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
191+
let mut writer = kernel::str::Formatter::new(page);
192+
writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?;
193+
Ok(writer.bytes_written())
194+
}
195+
196+
fn store(this: &DeviceConfig, page: &[u8]) -> Result {
197+
if this.data.lock().powered {
198+
return Err(EBUSY);
199+
}
200+
201+
let text = core::str::from_utf8(page)?.trim();
202+
let value = text.parse::<u64>().map_err(|_| EINVAL)?;
203+
204+
this.data.lock().capacity_mib = value;
205+
Ok(())
206+
}
207+
}

drivers/block/rnull/rnull.rs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
//! This is a Rust implementation of the C null block driver.
4-
//!
5-
//! Supported features:
6-
//!
7-
//! - blk-mq interface
8-
//! - direct completion
9-
//! - block size 4k
10-
//!
11-
//! The driver is not configurable.
4+
5+
mod configfs;
126

137
use kernel::{
14-
alloc::flags,
15-
block::mq::{
8+
block::{
169
self,
17-
gen_disk::{self, GenDisk},
18-
Operations, TagSet,
10+
mq::{
11+
self,
12+
gen_disk::{self, GenDisk},
13+
Operations, TagSet,
14+
},
1915
},
2016
error::Result,
21-
new_mutex, pr_info,
17+
pr_info,
2218
prelude::*,
23-
sync::{Arc, Mutex},
19+
sync::Arc,
2420
types::ARef,
2521
};
22+
use pin_init::PinInit;
2623

2724
module! {
2825
type: NullBlkModule,
@@ -35,33 +32,39 @@ module! {
3532
#[pin_data]
3633
struct NullBlkModule {
3734
#[pin]
38-
_disk: Mutex<GenDisk<NullBlkDevice>>,
35+
configfs_subsystem: kernel::configfs::Subsystem<configfs::Config>,
3936
}
4037

4138
impl kernel::InPlaceModule for NullBlkModule {
4239
fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
4340
pr_info!("Rust null_blk loaded\n");
4441

45-
// Use a immediately-called closure as a stable `try` block
46-
let disk = /* try */ (|| {
47-
let tagset = Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?;
48-
49-
gen_disk::GenDiskBuilder::new()
50-
.capacity_sectors(4096 << 11)
51-
.logical_block_size(4096)?
52-
.physical_block_size(4096)?
53-
.rotational(false)
54-
.build(format_args!("rnullb{}", 0), tagset)
55-
})();
56-
5742
try_pin_init!(Self {
58-
_disk <- new_mutex!(disk?, "nullb:disk"),
43+
configfs_subsystem <- configfs::subsystem(),
5944
})
6045
}
6146
}
6247

6348
struct NullBlkDevice;
6449

50+
impl NullBlkDevice {
51+
fn new(
52+
name: &CStr,
53+
block_size: u32,
54+
rotational: bool,
55+
capacity_mib: u64,
56+
) -> Result<GenDisk<Self>> {
57+
let tagset = Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?;
58+
59+
gen_disk::GenDiskBuilder::new()
60+
.capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT))
61+
.logical_block_size(block_size)?
62+
.physical_block_size(block_size)?
63+
.rotational(rotational)
64+
.build(fmt!("{}", name.to_str()?), tagset)
65+
}
66+
}
67+
6568
#[vtable]
6669
impl Operations for NullBlkDevice {
6770
#[inline(always)]

rust/kernel/block/mq/gen_disk.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl GenDiskBuilder {
5151

5252
/// Validate block size by verifying that it is between 512 and `PAGE_SIZE`,
5353
/// and that it is a power of two.
54-
fn validate_block_size(size: u32) -> Result {
54+
pub fn validate_block_size(size: u32) -> Result {
5555
if !(512..=bindings::PAGE_SIZE as u32).contains(&size) || !size.is_power_of_two() {
5656
Err(error::code::EINVAL)
5757
} else {

0 commit comments

Comments
 (0)