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