Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 core::sync::atomic::AtomicUsize;
10use kernel::debugfs::{Dir, Scope};
11use kernel::prelude::*;
12use kernel::sync::Mutex;
13use kernel::{c_str, new_mutex, str::CString};
14
15module! {
16 type: RustScopedDebugFs,
17 name: "rust_debugfs_scoped",
18 authors: ["Matthew Maurer"],
19 description: "Rust Scoped DebugFS usage sample",
20 license: "GPL",
21}
22
23fn remove_file_write(
24 mod_data: &ModuleData,
25 reader: &mut kernel::uaccess::UserSliceReader,
26) -> Result {
27 let mut buf = [0u8; 128];
28 if reader.len() >= buf.len() {
29 return Err(EINVAL);
30 }
31 let n = reader.len();
32 reader.read_slice(&mut buf[..n])?;
33
34 let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
35 let nul_idx = s.len();
36 buf[nul_idx] = 0;
37 let to_remove = CStr::from_bytes_with_nul(&buf[..nul_idx + 1]).map_err(|_| EINVAL)?;
38 mod_data
39 .devices
40 .lock()
41 .retain(|device| device.name.as_bytes() != to_remove.as_bytes());
42 Ok(())
43}
44
45fn create_file_write(
46 mod_data: &ModuleData,
47 reader: &mut kernel::uaccess::UserSliceReader,
48) -> Result {
49 let mut buf = [0u8; 128];
50 if reader.len() > buf.len() {
51 return Err(EINVAL);
52 }
53 let n = reader.len();
54 reader.read_slice(&mut buf[..n])?;
55
56 let mut nums = KVec::new();
57
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 AtomicUsize::new(sub.parse().map_err(|_| EINVAL)?),
66 GFP_KERNEL,
67 )?;
68 }
69
70 let scope = KBox::pin_init(
71 mod_data
72 .device_dir
73 .scope(DeviceData { name, nums }, &file_name, |dev_data, dir| {
74 for (idx, val) in dev_data.nums.iter().enumerate() {
75 let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else {
76 return;
77 };
78 dir.read_write_file(&name, val);
79 }
80 }),
81 GFP_KERNEL,
82 )?;
83 (*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;
84
85 Ok(())
86}
87
88struct RustScopedDebugFs {
89 _data: Pin<KBox<Scope<ModuleData>>>,
90}
91
92#[pin_data]
93struct ModuleData {
94 device_dir: Dir,
95 #[pin]
96 devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,
97}
98
99impl ModuleData {
100 fn init(device_dir: Dir) -> impl PinInit<Self> {
101 pin_init! {
102 Self {
103 device_dir: device_dir,
104 devices <- new_mutex!(KVec::new())
105 }
106 }
107 }
108}
109
110struct DeviceData {
111 name: CString,
112 nums: KVec<AtomicUsize>,
113}
114
115fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ {
116 base_dir.scope(
117 ModuleData::init(dyn_dirs),
118 c_str!("control"),
119 |data, dir| {
120 dir.write_only_callback_file(c_str!("create"), data, &create_file_write);
121 dir.write_only_callback_file(c_str!("remove"), data, &remove_file_write);
122 },
123 )
124}
125
126impl kernel::Module for RustScopedDebugFs {
127 fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
128 let base_dir = Dir::new(c_str!("rust_scoped_debugfs"));
129 let dyn_dirs = base_dir.subdir(c_str!("dynamic"));
130 Ok(Self {
131 _data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?,
132 })
133 }
134}