xref: /linux/drivers/block/rnull/configfs.rs (revision 69050f8d6d075dc01af7a5f2f550a8067510366f)
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