Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Andreas Hindborg and committed by
Jens Axboe
d969d504 edd86506

+240 -30
+1 -1
drivers/block/rnull/Kconfig
··· 4 4 5 5 config BLK_DEV_RUST_NULL 6 6 tristate "Rust null block driver (Experimental)" 7 - depends on RUST 7 + depends on RUST && CONFIGFS_FS 8 8 help 9 9 This is the Rust implementation of the null block driver. Like 10 10 the C version, the driver allows the user to create virutal block
+207
drivers/block/rnull/configfs.rs
··· 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 + }
+31 -28
drivers/block/rnull/rnull.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 3 //! 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; 12 6 13 7 use kernel::{ 14 - alloc::flags, 15 - block::mq::{ 8 + block::{ 16 9 self, 17 - gen_disk::{self, GenDisk}, 18 - Operations, TagSet, 10 + mq::{ 11 + self, 12 + gen_disk::{self, GenDisk}, 13 + Operations, TagSet, 14 + }, 19 15 }, 20 16 error::Result, 21 - new_mutex, pr_info, 17 + pr_info, 22 18 prelude::*, 23 - sync::{Arc, Mutex}, 19 + sync::Arc, 24 20 types::ARef, 25 21 }; 22 + use pin_init::PinInit; 26 23 27 24 module! { 28 25 type: NullBlkModule, ··· 32 35 #[pin_data] 33 36 struct NullBlkModule { 34 37 #[pin] 35 - _disk: Mutex<GenDisk<NullBlkDevice>>, 38 + configfs_subsystem: kernel::configfs::Subsystem<configfs::Config>, 36 39 } 37 40 38 41 impl kernel::InPlaceModule for NullBlkModule { 39 42 fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> { 40 43 pr_info!("Rust null_blk loaded\n"); 41 44 42 - // Use a immediately-called closure as a stable `try` block 43 - let disk = /* try */ (|| { 44 - let tagset = Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?; 45 - 46 - gen_disk::GenDiskBuilder::new() 47 - .capacity_sectors(4096 << 11) 48 - .logical_block_size(4096)? 49 - .physical_block_size(4096)? 50 - .rotational(false) 51 - .build(format_args!("rnullb{}", 0), tagset) 52 - })(); 53 - 54 45 try_pin_init!(Self { 55 - _disk <- new_mutex!(disk?, "nullb:disk"), 46 + configfs_subsystem <- configfs::subsystem(), 56 47 }) 57 48 } 58 49 } 59 50 60 51 struct NullBlkDevice; 52 + 53 + impl NullBlkDevice { 54 + fn new( 55 + name: &CStr, 56 + block_size: u32, 57 + rotational: bool, 58 + capacity_mib: u64, 59 + ) -> Result<GenDisk<Self>> { 60 + let tagset = Arc::pin_init(TagSet::new(1, 256, 1), GFP_KERNEL)?; 61 + 62 + gen_disk::GenDiskBuilder::new() 63 + .capacity_sectors(capacity_mib << (20 - block::SECTOR_SHIFT)) 64 + .logical_block_size(block_size)? 65 + .physical_block_size(block_size)? 66 + .rotational(rotational) 67 + .build(fmt!("{}", name.to_str()?), tagset) 68 + } 69 + } 61 70 62 71 #[vtable] 63 72 impl Operations for NullBlkDevice {
+1 -1
rust/kernel/block/mq/gen_disk.rs
··· 51 51 52 52 /// Validate block size by verifying that it is between 512 and `PAGE_SIZE`, 53 53 /// 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 { 55 55 if !(512..=bindings::PAGE_SIZE as u32).contains(&size) || !size.is_power_of_two() { 56 56 Err(error::code::EINVAL) 57 57 } else {