configfs.rs 6.8 KB

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