projects/linux·blob master · refs

147 lines · 3.9 KiB

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