Skip to content

Commit a6ba5e0

Browse files
committed
rust: add debugfs wrapper
Signed-off-by: Fabien Parent <[email protected]>
1 parent 54a3c8e commit a6ba5e0

File tree

3 files changed

+370
-0
lines changed

3 files changed

+370
-0
lines changed

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <kunit/test.h>
1010
#include <linux/cred.h>
11+
#include <linux/debugfs.h>
1112
#include <linux/errname.h>
1213
#include <linux/fdtable.h>
1314
#include <linux/file.h>

rust/kernel/debugfs.rs

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
#![allow(missing_docs)]
2+
// SPDX-License-Identifier: GPL-2.0
3+
4+
//! API to add files to debugfs.
5+
//!
6+
//! C header: [`include/linux/debugfs.h`](../../../../include/linux/debugfs.h)
7+
//!
8+
//! Reference: <https://www.kernel.org/doc/html/next/filesystems/debugfs.html>
9+
10+
use crate::error::{from_err_ptr, Result};
11+
use crate::file;
12+
use crate::prelude::*;
13+
use crate::str::CStr;
14+
use crate::sync::Arc;
15+
use crate::types::Mode;
16+
use core::ffi::c_void;
17+
18+
pub type PinnedRegistration<T = ()> = Pin<Box<Registration<T>>>;
19+
20+
/// A registration of a debugfs directory or file
21+
pub struct Registration<T> {
22+
open_data: T,
23+
dentry: *mut bindings::dentry,
24+
_parent: Option<Arc<Registration<()>>>, // Store parent to prevent it from being dropped
25+
}
26+
27+
// SAFETY: dentry is only being held by the struct and is not shared with anyone else, so if T is
28+
// Send, it is safe to send this struct to another thread.
29+
unsafe impl<T: Send> Send for Registration<T> {}
30+
31+
// SAFETY: dentry is never accessed except in Registration::drop. As long as T is Sync, then
32+
// it is safe for Registration to be Sync
33+
unsafe impl<T: Sync> Sync for Registration<T> {}
34+
35+
impl<T> Drop for Registration<T> {
36+
fn drop(&mut self) {
37+
// SAFETY: self.dentry is valid by the type invariant.
38+
unsafe {
39+
bindings::debugfs_remove(self.dentry);
40+
}
41+
}
42+
}
43+
44+
impl Registration<()> {
45+
pub fn register_symlink(
46+
name: &'static CStr,
47+
parent: Option<Arc<Registration<()>>>,
48+
dest: &'static CStr,
49+
) -> Result<Registration<()>> {
50+
let parent_dentry = parent.as_ref().map_or(core::ptr::null_mut(), |r| r.dentry);
51+
52+
let dentry = from_err_ptr(unsafe {
53+
bindings::debugfs_create_symlink(name.as_char_ptr(), parent_dentry, dest.as_char_ptr())
54+
})?;
55+
56+
Ok(Self {
57+
dentry,
58+
open_data: (),
59+
_parent: parent,
60+
})
61+
}
62+
63+
pub fn register_dir(
64+
name: &'static CStr,
65+
parent: Option<Arc<Registration<()>>>,
66+
) -> Result<Registration<()>> {
67+
let parent_dentry = parent.as_ref().map_or(core::ptr::null_mut(), |r| r.dentry);
68+
69+
// SAFETY: name.as_char_ptr() cannot be null. The type invariant ensure that
70+
// self.dentry is always a valid pointer, so p will always be a NULL pointer or a valid
71+
// pointer.
72+
let dentry = from_err_ptr(unsafe {
73+
bindings::debugfs_create_dir(name.as_char_ptr(), parent_dentry)
74+
})?;
75+
76+
Ok(Self {
77+
dentry,
78+
open_data: (),
79+
_parent: parent,
80+
})
81+
}
82+
}
83+
84+
impl<T: Sync> Registration<T> {
85+
pub fn register_file<U>(
86+
name: &'static CStr,
87+
mode: Mode,
88+
open_data: T,
89+
parent: Option<Arc<Registration<()>>>,
90+
) -> Result<PinnedRegistration<T>>
91+
where
92+
Self: file::OpenAdapter<T>,
93+
U: file::Operations<OpenData = T>,
94+
{
95+
let fops = unsafe { file::OperationsVtable::<Self, U>::build() };
96+
let parent_dentry = parent.as_ref().map_or(core::ptr::null_mut(), |r| r.dentry);
97+
98+
let mut registration = Pin::from(Box::try_new(Self {
99+
dentry: core::ptr::null_mut(),
100+
open_data,
101+
_parent: parent,
102+
})?);
103+
// SAFETY: The function never moves `this` hence the call is safe.
104+
let this = unsafe { registration.as_mut().get_unchecked_mut() };
105+
this.dentry = from_err_ptr(unsafe {
106+
bindings::debugfs_create_file(
107+
name.as_char_ptr(),
108+
mode.as_int(),
109+
parent_dentry,
110+
this as *mut _ as *mut c_void,
111+
fops,
112+
)
113+
})?;
114+
115+
Ok(registration)
116+
}
117+
}
118+
119+
impl<T: Sync> file::OpenAdapter<T> for Registration<T> {
120+
// WIP: Returns a valid pointer that lives longer than the call to the open function
121+
unsafe fn convert(inode: *mut bindings::inode, _file: *mut bindings::file) -> *const T {
122+
// SAFETY: debugfs_create_file is called with self as private data. The C debugfs API
123+
// stores it into the inode.i_private field.
124+
let this: &Self = unsafe { &*((*inode).i_private as *const Self) };
125+
&this.open_data
126+
}
127+
}
128+
129+
pub mod attr {
130+
use super::Registration;
131+
use crate::error::Result;
132+
use crate::file;
133+
use crate::prelude::*;
134+
use crate::str::CStr;
135+
use crate::sync::{Arc, ArcBorrow};
136+
use core::ffi::{c_int, c_void};
137+
138+
pub trait Attribute<T> {
139+
fn get(&self) -> Result<T>;
140+
fn set(&self, val: T) -> Result;
141+
}
142+
143+
pub struct AttributeData {
144+
private_data: *mut c_void,
145+
}
146+
147+
unsafe impl Sync for AttributeData {}
148+
unsafe impl Send for AttributeData {}
149+
150+
pub fn open(
151+
file: &file::File,
152+
get: Option<unsafe extern "C" fn(_: *mut c_void, _: *mut u64) -> c_int>,
153+
set: Option<unsafe extern "C" fn(_: *mut c_void, _: u64) -> c_int>,
154+
fmt: &CStr,
155+
) -> Result<Arc<AttributeData>> {
156+
let file = file.as_ptr();
157+
158+
let private_data = unsafe {
159+
bindings::simple_attr_open((*file).f_inode, file, get, set, fmt.as_char_ptr());
160+
(*file).private_data
161+
};
162+
163+
Ok(Arc::try_new(AttributeData { private_data })?)
164+
}
165+
166+
pub fn release(_data: Arc<AttributeData>, file: &file::File) {
167+
let file = file.as_ptr();
168+
unsafe { bindings::simple_attr_release((*file).f_inode, file) };
169+
}
170+
171+
pub fn read(
172+
data: ArcBorrow<'_, AttributeData>,
173+
file: &file::File,
174+
writer: &mut impl crate::io_buffer::IoBufferWriter,
175+
offset: u64,
176+
) -> Result<usize> {
177+
let mut ppos = offset as bindings::loff_t;
178+
let file = file.as_ptr();
179+
let buf = writer.buffer().unwrap() as *mut i8;
180+
181+
let ret = unsafe {
182+
let private_data = (*file).private_data;
183+
(*file).private_data = data.private_data;
184+
let ret = bindings::debugfs_attr_read(file, buf, writer.len(), &mut ppos);
185+
(*file).private_data = private_data;
186+
ret
187+
};
188+
189+
if ret < 0 {
190+
Err(Error::from_errno(ret as i32))
191+
} else {
192+
Ok(ret as usize)
193+
}
194+
}
195+
196+
pub fn write(
197+
data: ArcBorrow<'_, AttributeData>,
198+
file: &file::File,
199+
reader: &mut impl crate::io_buffer::IoBufferReader,
200+
offset: u64,
201+
signed: bool,
202+
) -> Result<usize> {
203+
let mut ppos = offset as bindings::loff_t;
204+
let file = file.as_ptr();
205+
let buf = reader.buffer().unwrap() as *mut i8;
206+
207+
let ret = unsafe {
208+
let private_data = (*file).private_data;
209+
(*file).private_data = data.private_data;
210+
let ret = match signed {
211+
true => bindings::debugfs_attr_write_signed(file, buf, reader.len(), &mut ppos),
212+
false => bindings::debugfs_attr_write(file, buf, reader.len(), &mut ppos),
213+
};
214+
(*file).private_data = private_data;
215+
ret
216+
};
217+
218+
if ret < 0 {
219+
Err(Error::from_errno(ret as i32))
220+
} else {
221+
Ok(ret as usize)
222+
}
223+
}
224+
225+
pub extern "C" fn _get_callback_unsigned<T: Attribute<u64>>(
226+
this: *mut core::ffi::c_void,
227+
val: *mut u64,
228+
) -> core::ffi::c_int {
229+
let this: &Registration<Arc<T>> = unsafe { &mut *(this as *mut _) };
230+
match this.open_data.get() {
231+
Ok(v) => {
232+
unsafe { *val = v };
233+
0
234+
}
235+
Err(e) => e.to_errno(),
236+
}
237+
}
238+
239+
pub extern "C" fn _get_callback_signed<T: Attribute<i64>>(
240+
this: *mut core::ffi::c_void,
241+
val: *mut u64,
242+
) -> core::ffi::c_int {
243+
let this: &Registration<Arc<T>> = unsafe { &mut *(this as *mut _) };
244+
match this.open_data.get() {
245+
Ok(v) => {
246+
unsafe { *val = v as u64 };
247+
0
248+
}
249+
Err(e) => e.to_errno(),
250+
}
251+
}
252+
253+
pub extern "C" fn _set_callback_unsigned<T: Attribute<u64>>(
254+
this: *mut core::ffi::c_void,
255+
val: u64,
256+
) -> core::ffi::c_int {
257+
let this: &Registration<Arc<T>> = unsafe { &mut *(this as *mut _) };
258+
match this.open_data.set(val) {
259+
Ok(_) => 0,
260+
Err(e) => e.to_errno(),
261+
}
262+
}
263+
264+
pub extern "C" fn _set_callback_signed<T: Attribute<i64>>(
265+
this: *mut core::ffi::c_void,
266+
val: u64,
267+
) -> core::ffi::c_int {
268+
let this: &Registration<Arc<T>> = unsafe { &mut *(this as *mut _) };
269+
match this.open_data.set(val as i64) {
270+
Ok(_) => 0,
271+
Err(e) => e.to_errno(),
272+
}
273+
}
274+
}
275+
276+
#[macro_export]
277+
macro_rules! attribute {
278+
($attribute_type:ty, $fmt:literal, $is_signed:literal, $getter:expr, $setter:expr) => {
279+
impl $attribute_type {
280+
fn register(
281+
self: $crate::sync::Arc<Self>,
282+
name: &'static $crate::str::CStr,
283+
mode: $crate::types::Mode,
284+
parent: ::core::option::Option<
285+
$crate::sync::Arc<$crate::debugfs::Registration<()>>,
286+
>,
287+
) -> $crate::error::Result<$crate::debugfs::PinnedRegistration<$crate::sync::Arc<Self>>>
288+
{
289+
$crate::debugfs::Registration::<$crate::sync::Arc<Self>>::register_file::<Self>(
290+
name, mode, self, parent,
291+
)
292+
}
293+
}
294+
295+
#[vtable]
296+
impl $crate::file::Operations for $attribute_type {
297+
type OpenData = $crate::sync::Arc<Self>;
298+
type Data = $crate::sync::Arc<$crate::debugfs::attr::AttributeData>;
299+
300+
fn open(
301+
data: &Self::OpenData,
302+
file: &$crate::file::File,
303+
) -> $crate::error::Result<Self::Data> {
304+
use ::core::option::Option::Some;
305+
306+
$crate::debugfs::attr::open(
307+
file,
308+
Some($getter),
309+
Some($setter),
310+
$crate::c_str!($fmt),
311+
)
312+
}
313+
314+
fn release(data: Self::Data, file: &$crate::file::File) {
315+
$crate::debugfs::attr::release(data, file);
316+
}
317+
318+
fn read(
319+
data: $crate::sync::ArcBorrow<'_, $crate::debugfs::attr::AttributeData>,
320+
file: &$crate::file::File,
321+
writer: &mut impl $crate::io_buffer::IoBufferWriter,
322+
offset: u64,
323+
) -> $crate::error::Result<usize> {
324+
$crate::debugfs::attr::read(data, file, writer, offset)
325+
}
326+
327+
fn write(
328+
data: $crate::sync::ArcBorrow<'_, $crate::debugfs::attr::AttributeData>,
329+
file: &$crate::file::File,
330+
reader: &mut impl $crate::io_buffer::IoBufferReader,
331+
offset: u64,
332+
) -> $crate::error::Result<usize> {
333+
$crate::debugfs::attr::write(data, file, reader, offset, $is_signed)
334+
}
335+
}
336+
};
337+
}
338+
339+
#[macro_export]
340+
macro_rules! attribute_unsigned {
341+
($attribute_type:ty, $fmt:literal) => {
342+
$crate::debugfs::attribute!(
343+
$attribute_type,
344+
$fmt,
345+
false,
346+
$crate::debugfs::attr::_get_callback_unsigned::<Self>,
347+
$crate::debugfs::attr::_set_callback_unsigned::<Self>
348+
);
349+
};
350+
}
351+
352+
#[macro_export]
353+
macro_rules! attribute_signed {
354+
($attribute_type:ty, $fmt:literal) => {
355+
$crate::debugfs::attribute!(
356+
$attribute_type,
357+
$fmt,
358+
true,
359+
$crate::debugfs::attr::_get_callback_signed::<Self>,
360+
$crate::debugfs::attr::_set_callback_signed::<Self>
361+
);
362+
};
363+
}
364+
365+
pub use attribute;
366+
pub use attribute_signed;
367+
pub use attribute_unsigned;

rust/kernel/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ extern crate self as kernel;
3636
mod allocator;
3737
mod build_assert;
3838
pub mod cred;
39+
#[cfg(CONFIG_DEBUG_FS)]
40+
pub mod debugfs;
3941
pub mod error;
4042
pub mod file;
4143
pub mod init;

0 commit comments

Comments
 (0)