Skip to content

Commit 8ac6eab

Browse files
Andreas Hindborgkawasaki
authored andcommitted
rnull: enable configuration via configfs
Allow rust null block devices to be configured and instantiated via `configfs`. Signed-off-by: Andreas Hindborg <[email protected]>
1 parent f2cfe92 commit 8ac6eab

4 files changed

Lines changed: 253 additions & 29 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: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
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::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::BorrowFormatter::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::BorrowFormatter::new(page)?;
104+
105+
if this.data.lock().powered {
106+
writer.write_fmt(fmt!("1\n"))?;
107+
} else {
108+
writer.write_fmt(fmt!("0\n"))?;
109+
}
110+
111+
Ok(writer.bytes_written())
112+
}
113+
114+
fn store(this: &DeviceConfig, page: &[u8]) -> Result {
115+
let power_op: bool = core::str::from_utf8(page)?
116+
.trim()
117+
.parse::<u8>()
118+
.map_err(|_| kernel::error::code::EINVAL)?
119+
!= 0;
120+
121+
let mut guard = this.data.lock();
122+
123+
if !guard.powered && power_op {
124+
guard.disk = Some(NullBlkDevice::new(
125+
&guard.name,
126+
guard.block_size,
127+
guard.rotational,
128+
guard.capacity_mib,
129+
)?);
130+
guard.powered = true;
131+
} else if guard.powered && !power_op {
132+
drop(guard.disk.take());
133+
guard.powered = false;
134+
}
135+
136+
Ok(())
137+
}
138+
}
139+
140+
#[vtable]
141+
impl configfs::AttributeOperations<1> for DeviceConfig {
142+
type Data = DeviceConfig;
143+
144+
fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
145+
let mut writer = kernel::str::BorrowFormatter::new(page)?;
146+
writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?;
147+
Ok(writer.bytes_written())
148+
}
149+
150+
fn store(this: &DeviceConfig, page: &[u8]) -> Result {
151+
if this.data.lock().powered {
152+
return Err(EBUSY);
153+
}
154+
155+
let text = core::str::from_utf8(page)?.trim();
156+
let value = text
157+
.parse::<u32>()
158+
.map_err(|_| kernel::error::code::EINVAL)?;
159+
160+
GenDiskBuilder::validate_block_size(value)?;
161+
this.data.lock().block_size = value;
162+
Ok(())
163+
}
164+
}
165+
166+
#[vtable]
167+
impl configfs::AttributeOperations<2> for DeviceConfig {
168+
type Data = DeviceConfig;
169+
170+
fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
171+
let mut writer = kernel::str::BorrowFormatter::new(page)?;
172+
173+
if this.data.lock().rotational {
174+
writer.write_fmt(fmt!("1\n"))?;
175+
} else {
176+
writer.write_fmt(fmt!("0\n"))?;
177+
}
178+
179+
Ok(writer.bytes_written())
180+
}
181+
182+
fn store(this: &DeviceConfig, page: &[u8]) -> Result {
183+
if this.data.lock().powered {
184+
return Err(EBUSY);
185+
}
186+
187+
this.data.lock().rotational = core::str::from_utf8(page)?
188+
.trim()
189+
.parse::<u8>()
190+
.map_err(|_| kernel::error::code::EINVAL)?
191+
!= 0;
192+
193+
Ok(())
194+
}
195+
}
196+
197+
#[vtable]
198+
impl configfs::AttributeOperations<3> for DeviceConfig {
199+
type Data = DeviceConfig;
200+
201+
fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
202+
let mut writer = kernel::str::BorrowFormatter::new(page)?;
203+
writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?;
204+
Ok(writer.bytes_written())
205+
}
206+
207+
fn store(this: &DeviceConfig, page: &[u8]) -> Result {
208+
if this.data.lock().powered {
209+
return Err(EBUSY);
210+
}
211+
212+
let text = core::str::from_utf8(page)?.trim();
213+
let value = text
214+
.parse::<u64>()
215+
.map_err(|_| kernel::error::code::EINVAL)?;
216+
217+
this.data.lock().capacity_mib = value;
218+
Ok(())
219+
}
220+
}

drivers/block/rnull/rnull.rs

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
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::{
148
alloc::flags,
15-
block::mq::{
9+
block::{
1610
self,
17-
gen_disk::{self, GenDisk},
18-
Operations, TagSet,
11+
mq::{
12+
self,
13+
gen_disk::{self, GenDisk},
14+
Operations, TagSet,
15+
},
1916
},
2017
error::Result,
21-
new_mutex, pr_info,
18+
pr_info,
2219
prelude::*,
23-
sync::{Arc, Mutex},
20+
sync::Arc,
2421
types::ARef,
2522
};
23+
use pin_init::PinInit;
2624

2725
module! {
2826
type: NullBlkModule,
@@ -35,33 +33,39 @@ module! {
3533
#[pin_data]
3634
struct NullBlkModule {
3735
#[pin]
38-
_disk: Mutex<GenDisk<NullBlkDevice>>,
36+
configfs_subsystem: kernel::configfs::Subsystem<configfs::Config>,
3937
}
4038

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

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-
5743
try_pin_init!(Self {
58-
_disk <- new_mutex!(disk?, "nullb:disk"),
44+
configfs_subsystem <- configfs::subsystem(),
5945
})
6046
}
6147
}
6248

6349
struct NullBlkDevice;
6450

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

rust/kernel/block/mq/gen_disk.rs

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

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

0 commit comments

Comments
 (0)