rust_debugfs_scoped.rs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (C) 2025 Google LLC.
  3. //! Sample DebugFS exporting platform driver that demonstrates the use of
  4. //! `Scope::dir` to create a variety of files without the need to separately
  5. //! track them all.
  6. use kernel::{
  7. debugfs::{
  8. Dir,
  9. Scope, //
  10. },
  11. new_mutex,
  12. prelude::*,
  13. sizes::*,
  14. str::CString,
  15. sync::{
  16. atomic::Atomic,
  17. Mutex, //
  18. },
  19. };
  20. module! {
  21. type: RustScopedDebugFs,
  22. name: "rust_debugfs_scoped",
  23. authors: ["Matthew Maurer"],
  24. description: "Rust Scoped DebugFS usage sample",
  25. license: "GPL",
  26. }
  27. fn remove_file_write(
  28. mod_data: &ModuleData,
  29. reader: &mut kernel::uaccess::UserSliceReader,
  30. ) -> Result {
  31. let mut buf = [0u8; 128];
  32. if reader.len() >= buf.len() {
  33. return Err(EINVAL);
  34. }
  35. let n = reader.len();
  36. reader.read_slice(&mut buf[..n])?;
  37. let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
  38. let nul_idx = s.len();
  39. buf[nul_idx] = 0;
  40. let to_remove = CStr::from_bytes_with_nul(&buf[..nul_idx + 1]).map_err(|_| EINVAL)?;
  41. mod_data
  42. .devices
  43. .lock()
  44. .retain(|device| device.name.to_bytes() != to_remove.to_bytes());
  45. Ok(())
  46. }
  47. fn create_file_write(
  48. mod_data: &ModuleData,
  49. reader: &mut kernel::uaccess::UserSliceReader,
  50. ) -> Result {
  51. let mut buf = [0u8; 128];
  52. if reader.len() > buf.len() {
  53. return Err(EINVAL);
  54. }
  55. let n = reader.len();
  56. reader.read_slice(&mut buf[..n])?;
  57. let mut nums = KVec::new();
  58. let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
  59. let mut items = s.split_whitespace();
  60. let name_str = items.next().ok_or(EINVAL)?;
  61. let name = CString::try_from_fmt(fmt!("{name_str}"))?;
  62. let file_name = CString::try_from_fmt(fmt!("{name_str}"))?;
  63. for sub in items {
  64. nums.push(
  65. Atomic::<usize>::new(sub.parse().map_err(|_| EINVAL)?),
  66. GFP_KERNEL,
  67. )?;
  68. }
  69. let blob = KBox::pin_init(new_mutex!([0x42; SZ_4K]), GFP_KERNEL)?;
  70. let scope = KBox::pin_init(
  71. mod_data.device_dir.scope(
  72. DeviceData { name, nums, blob },
  73. &file_name,
  74. |dev_data, dir| {
  75. for (idx, val) in dev_data.nums.iter().enumerate() {
  76. let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else {
  77. return;
  78. };
  79. dir.read_write_file(&name, val);
  80. }
  81. dir.read_write_binary_file(c"blob", &dev_data.blob);
  82. },
  83. ),
  84. GFP_KERNEL,
  85. )?;
  86. (*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;
  87. Ok(())
  88. }
  89. struct RustScopedDebugFs {
  90. _data: Pin<KBox<Scope<ModuleData>>>,
  91. }
  92. #[pin_data]
  93. struct ModuleData {
  94. device_dir: Dir,
  95. #[pin]
  96. devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,
  97. }
  98. impl ModuleData {
  99. fn init(device_dir: Dir) -> impl PinInit<Self> {
  100. pin_init! {
  101. Self {
  102. device_dir: device_dir,
  103. devices <- new_mutex!(KVec::new())
  104. }
  105. }
  106. }
  107. }
  108. struct DeviceData {
  109. name: CString,
  110. nums: KVec<Atomic<usize>>,
  111. blob: Pin<KBox<Mutex<[u8; SZ_4K]>>>,
  112. }
  113. fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ {
  114. base_dir.scope(ModuleData::init(dyn_dirs), c"control", |data, dir| {
  115. dir.write_only_callback_file(c"create", data, &create_file_write);
  116. dir.write_only_callback_file(c"remove", data, &remove_file_write);
  117. })
  118. }
  119. impl kernel::Module for RustScopedDebugFs {
  120. fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
  121. let base_dir = Dir::new(c"rust_scoped_debugfs");
  122. let dyn_dirs = base_dir.subdir(c"dynamic");
  123. Ok(Self {
  124. _data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?,
  125. })
  126. }
  127. }