Skip to content

Commit 7f201ca

Browse files
maurerDanilo Krummrich
authored andcommitted
rust: debugfs: Add initial support for directories
Adds a `debugfs::Dir` type that can be used to create and remove DebugFS directories. The `Dir` handle automatically cleans up the directory on `Drop`. Signed-off-by: Matthew Maurer <mmaurer@google.com> Tested-by: Dirk Behme <dirk.behme@de.bosch.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Link: https://lore.kernel.org/r/20250904-debugfs-rust-v11-1-7d12a165685a@google.com Signed-off-by: Danilo Krummrich <dakr@kernel.org>
1 parent 4c48aed commit 7f201ca

5 files changed

Lines changed: 147 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7487,6 +7487,8 @@ F: include/linux/kobj*
74877487
F: include/linux/property.h
74887488
F: include/linux/sysfs.h
74897489
F: lib/kobj*
7490+
F: rust/kernel/debugfs.rs
7491+
F: rust/kernel/debugfs/
74907492
F: rust/kernel/device.rs
74917493
F: rust/kernel/device/
74927494
F: rust/kernel/device_id.rs

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <linux/cpufreq.h>
4747
#include <linux/cpumask.h>
4848
#include <linux/cred.h>
49+
#include <linux/debugfs.h>
4950
#include <linux/device/faux.h>
5051
#include <linux/dma-mapping.h>
5152
#include <linux/errname.h>

rust/kernel/debugfs.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (C) 2025 Google LLC.
3+
4+
//! DebugFS Abstraction
5+
//!
6+
//! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h)
7+
8+
// When DebugFS is disabled, many parameters are dead. Linting for this isn't helpful.
9+
#![cfg_attr(not(CONFIG_DEBUG_FS), allow(unused_variables))]
10+
11+
#[cfg(CONFIG_DEBUG_FS)]
12+
use crate::prelude::*;
13+
use crate::str::CStr;
14+
#[cfg(CONFIG_DEBUG_FS)]
15+
use crate::sync::Arc;
16+
17+
#[cfg(CONFIG_DEBUG_FS)]
18+
mod entry;
19+
#[cfg(CONFIG_DEBUG_FS)]
20+
use entry::Entry;
21+
22+
/// Owning handle to a DebugFS directory.
23+
///
24+
/// The directory in the filesystem represented by [`Dir`] will be removed when handle has been
25+
/// dropped *and* all children have been removed.
26+
// If we have a parent, we hold a reference to it in the `Entry`. This prevents the `dentry`
27+
// we point to from being cleaned up if our parent `Dir`/`Entry` is dropped before us.
28+
//
29+
// The `None` option indicates that the `Arc` could not be allocated, so our children would not be
30+
// able to refer to us. In this case, we need to silently fail. All future child directories/files
31+
// will silently fail as well.
32+
#[derive(Clone)]
33+
pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry>>);
34+
35+
impl Dir {
36+
/// Create a new directory in DebugFS. If `parent` is [`None`], it will be created at the root.
37+
fn create(name: &CStr, parent: Option<&Dir>) -> Self {
38+
#[cfg(CONFIG_DEBUG_FS)]
39+
{
40+
let parent_entry = match parent {
41+
// If the parent couldn't be allocated, just early-return
42+
Some(Dir(None)) => return Self(None),
43+
Some(Dir(Some(entry))) => Some(entry.clone()),
44+
None => None,
45+
};
46+
Self(
47+
// If Arc creation fails, the `Entry` will be dropped, so the directory will be
48+
// cleaned up.
49+
Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(),
50+
)
51+
}
52+
#[cfg(not(CONFIG_DEBUG_FS))]
53+
Self()
54+
}
55+
56+
/// Create a new directory in DebugFS at the root.
57+
///
58+
/// # Examples
59+
///
60+
/// ```
61+
/// # use kernel::c_str;
62+
/// # use kernel::debugfs::Dir;
63+
/// let debugfs = Dir::new(c_str!("parent"));
64+
/// ```
65+
pub fn new(name: &CStr) -> Self {
66+
Dir::create(name, None)
67+
}
68+
69+
/// Creates a subdirectory within this directory.
70+
///
71+
/// # Examples
72+
///
73+
/// ```
74+
/// # use kernel::c_str;
75+
/// # use kernel::debugfs::Dir;
76+
/// let parent = Dir::new(c_str!("parent"));
77+
/// let child = parent.subdir(c_str!("child"));
78+
/// ```
79+
pub fn subdir(&self, name: &CStr) -> Self {
80+
Dir::create(name, Some(self))
81+
}
82+
}

rust/kernel/debugfs/entry.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (C) 2025 Google LLC.
3+
4+
use crate::str::CStr;
5+
use crate::sync::Arc;
6+
7+
/// Owning handle to a DebugFS entry.
8+
///
9+
/// # Invariants
10+
///
11+
/// The wrapped pointer will always be `NULL`, an error, or an owned DebugFS `dentry`.
12+
pub(crate) struct Entry {
13+
entry: *mut bindings::dentry,
14+
// If we were created with an owning parent, this is the keep-alive
15+
_parent: Option<Arc<Entry>>,
16+
}
17+
18+
// SAFETY: [`Entry`] is just a `dentry` under the hood, which the API promises can be transferred
19+
// between threads.
20+
unsafe impl Send for Entry {}
21+
22+
// SAFETY: All the C functions we call on the `dentry` pointer are threadsafe.
23+
unsafe impl Sync for Entry {}
24+
25+
impl Entry {
26+
pub(crate) fn dynamic_dir(name: &CStr, parent: Option<Arc<Self>>) -> Self {
27+
let parent_ptr = match &parent {
28+
Some(entry) => entry.as_ptr(),
29+
None => core::ptr::null_mut(),
30+
};
31+
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
32+
// * `name` is a valid C string by the invariants of `&CStr`.
33+
// * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
34+
// `dentry` by our invariant. `debugfs_create_dir` handles `NULL` pointers correctly.
35+
let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };
36+
37+
Entry {
38+
entry,
39+
_parent: parent,
40+
}
41+
}
42+
43+
/// Returns the pointer representation of the DebugFS directory.
44+
///
45+
/// # Guarantees
46+
///
47+
/// Due to the type invariant, the value returned from this function will always be an error
48+
/// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as
49+
/// long as this entry lives.
50+
pub(crate) fn as_ptr(&self) -> *mut bindings::dentry {
51+
self.entry
52+
}
53+
}
54+
55+
impl Drop for Entry {
56+
fn drop(&mut self) {
57+
// SAFETY: `debugfs_remove` can take `NULL`, error values, and legal DebugFS dentries.
58+
// `as_ptr` guarantees that the pointer is of this form.
59+
unsafe { bindings::debugfs_remove(self.as_ptr()) }
60+
}
61+
}

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub mod cpu;
7676
pub mod cpufreq;
7777
pub mod cpumask;
7878
pub mod cred;
79+
pub mod debugfs;
7980
pub mod device;
8081
pub mod device_id;
8182
pub mod devres;

0 commit comments

Comments
 (0)