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