1 // SPDX-License-Identifier: GPL-2.0
2
3 use super::{NullBlkDevice, THIS_MODULE};
4 use core::fmt::{Display, 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
subsystem() -> impl PinInit<kernel::configfs::Subsystem<Config>, Error>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
show(_this: &Config, page: &mut [u8; PAGE_SIZE]) -> Result<usize>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,irqmode\n")?;
40 Ok(writer.bytes_written())
41 }
42 }
43
44 #[vtable]
45 impl configfs::GroupOperations for Config {
46 type Child = DeviceConfig;
47
make_group( &self, name: &CStr, ) -> Result<impl PinInit<configfs::Group<DeviceConfig>, Error>>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 irqmode: 4,
62 ],
63 };
64
65 Ok(configfs::Group::new(
66 name.try_into()?,
67 item_type,
68 // TODO: cannot coerce new_mutex!() to impl PinInit<_, Error>, so put mutex inside
69 try_pin_init!( DeviceConfig {
70 data <- new_mutex!(DeviceConfigInner {
71 powered: false,
72 block_size: 4096,
73 rotational: false,
74 disk: None,
75 capacity_mib: 4096,
76 irq_mode: IRQMode::None,
77 name: name.try_into()?,
78 }),
79 }),
80 ))
81 }
82 }
83
84 #[derive(Debug, Clone, Copy)]
85 pub(crate) enum IRQMode {
86 None,
87 Soft,
88 }
89
90 impl TryFrom<u8> for IRQMode {
91 type Error = kernel::error::Error;
92
try_from(value: u8) -> Result<Self>93 fn try_from(value: u8) -> Result<Self> {
94 match value {
95 0 => Ok(Self::None),
96 1 => Ok(Self::Soft),
97 _ => Err(EINVAL),
98 }
99 }
100 }
101
102 impl Display for IRQMode {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result103 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104 match self {
105 Self::None => f.write_str("0")?,
106 Self::Soft => f.write_str("1")?,
107 }
108 Ok(())
109 }
110 }
111
112 #[pin_data]
113 pub(crate) struct DeviceConfig {
114 #[pin]
115 data: Mutex<DeviceConfigInner>,
116 }
117
118 #[pin_data]
119 struct DeviceConfigInner {
120 powered: bool,
121 name: CString,
122 block_size: u32,
123 rotational: bool,
124 capacity_mib: u64,
125 irq_mode: IRQMode,
126 disk: Option<GenDisk<NullBlkDevice>>,
127 }
128
129 #[vtable]
130 impl configfs::AttributeOperations<0> for DeviceConfig {
131 type Data = DeviceConfig;
132
show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize>133 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
134 let mut writer = kernel::str::Formatter::new(page);
135
136 if this.data.lock().powered {
137 writer.write_str("1\n")?;
138 } else {
139 writer.write_str("0\n")?;
140 }
141
142 Ok(writer.bytes_written())
143 }
144
store(this: &DeviceConfig, page: &[u8]) -> Result145 fn store(this: &DeviceConfig, page: &[u8]) -> Result {
146 let power_op = kstrtobool_bytes(page)?;
147 let mut guard = this.data.lock();
148
149 if !guard.powered && power_op {
150 guard.disk = Some(NullBlkDevice::new(
151 &guard.name,
152 guard.block_size,
153 guard.rotational,
154 guard.capacity_mib,
155 guard.irq_mode,
156 )?);
157 guard.powered = true;
158 } else if guard.powered && !power_op {
159 drop(guard.disk.take());
160 guard.powered = false;
161 }
162
163 Ok(())
164 }
165 }
166
167 #[vtable]
168 impl configfs::AttributeOperations<1> for DeviceConfig {
169 type Data = DeviceConfig;
170
show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize>171 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
172 let mut writer = kernel::str::Formatter::new(page);
173 writer.write_fmt(fmt!("{}\n", this.data.lock().block_size))?;
174 Ok(writer.bytes_written())
175 }
176
store(this: &DeviceConfig, page: &[u8]) -> Result177 fn store(this: &DeviceConfig, page: &[u8]) -> Result {
178 if this.data.lock().powered {
179 return Err(EBUSY);
180 }
181
182 let text = core::str::from_utf8(page)?.trim();
183 let value = text.parse::<u32>().map_err(|_| EINVAL)?;
184
185 GenDiskBuilder::validate_block_size(value)?;
186 this.data.lock().block_size = value;
187 Ok(())
188 }
189 }
190
191 #[vtable]
192 impl configfs::AttributeOperations<2> for DeviceConfig {
193 type Data = DeviceConfig;
194
show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize>195 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
196 let mut writer = kernel::str::Formatter::new(page);
197
198 if this.data.lock().rotational {
199 writer.write_str("1\n")?;
200 } else {
201 writer.write_str("0\n")?;
202 }
203
204 Ok(writer.bytes_written())
205 }
206
store(this: &DeviceConfig, page: &[u8]) -> Result207 fn store(this: &DeviceConfig, page: &[u8]) -> Result {
208 if this.data.lock().powered {
209 return Err(EBUSY);
210 }
211
212 this.data.lock().rotational = kstrtobool_bytes(page)?;
213
214 Ok(())
215 }
216 }
217
218 #[vtable]
219 impl configfs::AttributeOperations<3> for DeviceConfig {
220 type Data = DeviceConfig;
221
show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize>222 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
223 let mut writer = kernel::str::Formatter::new(page);
224 writer.write_fmt(fmt!("{}\n", this.data.lock().capacity_mib))?;
225 Ok(writer.bytes_written())
226 }
227
store(this: &DeviceConfig, page: &[u8]) -> Result228 fn store(this: &DeviceConfig, page: &[u8]) -> Result {
229 if this.data.lock().powered {
230 return Err(EBUSY);
231 }
232
233 let text = core::str::from_utf8(page)?.trim();
234 let value = text.parse::<u64>().map_err(|_| EINVAL)?;
235
236 this.data.lock().capacity_mib = value;
237 Ok(())
238 }
239 }
240
241 #[vtable]
242 impl configfs::AttributeOperations<4> for DeviceConfig {
243 type Data = DeviceConfig;
244
show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize>245 fn show(this: &DeviceConfig, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
246 let mut writer = kernel::str::Formatter::new(page);
247 writer.write_fmt(fmt!("{}\n", this.data.lock().irq_mode))?;
248 Ok(writer.bytes_written())
249 }
250
store(this: &DeviceConfig, page: &[u8]) -> Result251 fn store(this: &DeviceConfig, page: &[u8]) -> Result {
252 if this.data.lock().powered {
253 return Err(EBUSY);
254 }
255
256 let text = core::str::from_utf8(page)?.trim();
257 let value = text.parse::<u8>().map_err(|_| EINVAL)?;
258
259 this.data.lock().irq_mode = IRQMode::try_from(value)?;
260 Ok(())
261 }
262 }
263