Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/samples/rust/rust_debugfs_scoped.rs
29266 views
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
9
use core::sync::atomic::AtomicUsize;
10
use kernel::debugfs::{Dir, Scope};
11
use kernel::prelude::*;
12
use kernel::sync::Mutex;
13
use kernel::{c_str, new_mutex, str::CString};
14
15
module! {
16
type: RustScopedDebugFs,
17
name: "rust_debugfs_scoped",
18
authors: ["Matthew Maurer"],
19
description: "Rust Scoped DebugFS usage sample",
20
license: "GPL",
21
}
22
23
fn 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
45
fn 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
88
struct RustScopedDebugFs {
89
_data: Pin<KBox<Scope<ModuleData>>>,
90
}
91
92
#[pin_data]
93
struct ModuleData {
94
device_dir: Dir,
95
#[pin]
96
devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,
97
}
98
99
impl 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
110
struct DeviceData {
111
name: CString,
112
nums: KVec<AtomicUsize>,
113
}
114
115
fn 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
126
impl 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
}
135
136