1 // SPDX-License-Identifier: GPL-2.0 2 3 use super::{NullBlkDevice, THIS_MODULE}; 4 use kernel::{ 5 block::mq::gen_disk::{GenDisk, GenDiskBuilder}, 6 configfs::{self, AttributeOperations}, 7 configfs_attrs, 8 fmt::{self, Write as _}, 9 new_mutex, 10 page::PAGE_SIZE, 11 prelude::*, 12 str::{kstrtobool_bytes, CString}, 13 sync::Mutex, 14 }; 15 16 pub(crate) fn subsystem() -> impl PinInit<kernel::configfs::Subsystem<Config>, Error> { 17 let item_type = configfs_attrs! { 18 container: configfs::Subsystem<Config>, 19 data: Config, 20 child: DeviceConfig, 21 attributes: [ 22 features: 0, 23 ], 24 }; 25 26 kernel::configfs::Subsystem::new(c"rnull", item_type, try_pin_init!(Config {})) 27 } 28 29 #[pin_data] 30 pub(crate) struct Config {} 31 32 #[vtable] 33 impl AttributeOperations<0> for Config { 34 type Data = Config; 35 36 fn show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 37 let mut writer = kernel::str::Formatter::new(page); 38 writer.write_str("blocksize,size,rotational,irqmode\n")?; 39 Ok(writer.bytes_written()) 40 } 41 } 42 43 #[vtable] 44 impl configfs::GroupOperations for Config { 45 type Child = DeviceConfig; 46 47 fn make_group( 48 &self, 49 name: &CStr, 50 ) -> Result<impl PinInit<configfs::Group<DeviceConfig>, Error>> { 51 let item_type = configfs_attrs! { 52 container: configfs::Group<DeviceConfig>, 53 data: DeviceConfig, 54 attributes: [ 55 // Named for compatibility with C null_blk 56 power: 0, 57 blocksize: 1, 58 rotational: 2, 59 size: 3, 60 irqmode: 4, 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 irq_mode: IRQMode::None, 76 name: name.try_into()?, 77 }), 78 }), 79 )) 80 } 81 } 82 83 #[derive(Debug, Clone, Copy)] 84 pub(crate) enum IRQMode { 85 None, 86 Soft, 87 } 88 89 impl TryFrom<u8> for IRQMode { 90 type Error = kernel::error::Error; 91 92 fn try_from(value: u8) -> Result<Self> { 93 match value { 94 0 => Ok(Self::None), 95 1 => Ok(Self::Soft), 96 _ => Err(EINVAL), 97 } 98 } 99 } 100 101 impl fmt::Display for IRQMode { 102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 103 match self { 104 Self::None => f.write_str("0")?, 105 Self::Soft => f.write_str("1")?, 106 } 107 Ok(()) 108 } 109 } 110 111 #[pin_data] 112 pub(crate) struct DeviceConfig { 113 #[pin] 114 data: Mutex<DeviceConfigInner>, 115 } 116 117 #[pin_data] 118 struct DeviceConfigInner { 119 powered: bool, 120 name: CString, 121 block_size: u32, 122 rotational: bool, 123 capacity_mib: u64, 124 irq_mode: IRQMode, 125 disk: Option<GenDisk<NullBlkDevice>>, 126 } 127 128 #[vtable] 129 impl configfs::AttributeOperations<0> for DeviceConfig { 130 type Data = DeviceConfig; 131 132 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 133 let mut writer = kernel::str::Formatter::new(page); 134 135 if this.data.lock().powered { 136 writer.write_str("1\n")?; 137 } else { 138 writer.write_str("0\n")?; 139 } 140 141 Ok(writer.bytes_written()) 142 } 143 144 fn store(this: &DeviceConfig, page: &[u8]) -> Result { 145 let power_op = kstrtobool_bytes(page)?; 146 let mut guard = this.data.lock(); 147 148 if !guard.powered && power_op { 149 guard.disk = Some(NullBlkDevice::new( 150 &guard.name, 151 guard.block_size, 152 guard.rotational, 153 guard.capacity_mib, 154 guard.irq_mode, 155 )?); 156 guard.powered = true; 157 } else if guard.powered && !power_op { 158 drop(guard.disk.take()); 159 guard.powered = false; 160 } 161 162 Ok(()) 163 } 164 } 165 166 #[vtable] 167 impl configfs::AttributeOperations<1> 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::Formatter::new(page); 172 writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?; 173 Ok(writer.bytes_written()) 174 } 175 176 fn store(this: &DeviceConfig, page: &[u8]) -> Result { 177 if this.data.lock().powered { 178 return Err(EBUSY); 179 } 180 181 let text = core::str::from_utf8(page)?.trim(); 182 let value = text.parse::<u32>().map_err(|_| EINVAL)?; 183 184 GenDiskBuilder::validate_block_size(value)?; 185 this.data.lock().block_size = value; 186 Ok(()) 187 } 188 } 189 190 #[vtable] 191 impl configfs::AttributeOperations<2> for DeviceConfig { 192 type Data = DeviceConfig; 193 194 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 195 let mut writer = kernel::str::Formatter::new(page); 196 197 if this.data.lock().rotational { 198 writer.write_str("1\n")?; 199 } else { 200 writer.write_str("0\n")?; 201 } 202 203 Ok(writer.bytes_written()) 204 } 205 206 fn store(this: &DeviceConfig, page: &[u8]) -> Result { 207 if this.data.lock().powered { 208 return Err(EBUSY); 209 } 210 211 this.data.lock().rotational = kstrtobool_bytes(page)?; 212 213 Ok(()) 214 } 215 } 216 217 #[vtable] 218 impl configfs::AttributeOperations<3> for DeviceConfig { 219 type Data = DeviceConfig; 220 221 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 222 let mut writer = kernel::str::Formatter::new(page); 223 writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?; 224 Ok(writer.bytes_written()) 225 } 226 227 fn store(this: &DeviceConfig, page: &[u8]) -> Result { 228 if this.data.lock().powered { 229 return Err(EBUSY); 230 } 231 232 let text = core::str::from_utf8(page)?.trim(); 233 let value = text.parse::<u64>().map_err(|_| EINVAL)?; 234 235 this.data.lock().capacity_mib = value; 236 Ok(()) 237 } 238 } 239 240 #[vtable] 241 impl configfs::AttributeOperations<4> for DeviceConfig { 242 type Data = DeviceConfig; 243 244 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> { 245 let mut writer = kernel::str::Formatter::new(page); 246 writer.write_fmt(fmt!("{}\n", this.data.lock().irq_mode))?; 247 Ok(writer.bytes_written()) 248 } 249 250 fn store(this: &DeviceConfig, page: &[u8]) -> Result { 251 if this.data.lock().powered { 252 return Err(EBUSY); 253 } 254 255 let text = core::str::from_utf8(page)?.trim(); 256 let value = text.parse::<u8>().map_err(|_| EINVAL)?; 257 258 this.data.lock().irq_mode = IRQMode::try_from(value)?; 259 Ok(()) 260 } 261 } 262