Skip to contentSkip to conversation

rust: DebugFS Bindings

PATCH Superseded Matthew Maurer 4 participants 15 messages Jun 18, 2025 – Jun 24, 2025

Subsystems (2)

v6 of 11 Superseded Latest is v11 →

lore
Share & tools
lore
#0 lore raw

v6 cover rust: DebugFS Bindings

This series provides safe DebugFS bindings for Rust, with a sample driver using them.

---
Changes in v6:
- Replaced explicit lifetimes with children keeping their parents alive.
- Added support for attaching owned data.
- Removed recomendation to only keep root handles and handles you want
to delete around.
- Refactored some code into separate files to improve clarity.
- Link to v5: https://lore.kernel.org/r/[email protected]

Changes in v5:
- Made Dir + File wrappers around Entry
- All functions return owning handles. To discard without drop, use forget. To keep a handle without drop, use ManuallyDrop.
- Fixed bugs around not(CONFIG_DEBUG_FS)
- Removed unnecessary addr_of!
- Link to v4: https://lore.kernel.org/r/[email protected]

Changes in v4:
- Remove SubDir, replace with type-level constant.
- Add lifetime to Dir to prevent subdirectories and files from outliving their parents and triggering an Oops when accessed.
- Split unsafe blocks with two calls into two blocks
- Access private field through direct pointer dereference, avoiding creation of a reference to it.
- Notably not changed - owning/non-owning handle defaults. The best read
I had from the thread was to continue with this mode, but I'm willing
to change if need be.
- Comment changes
- More comment markdown
- Remove scopes from examples
- Put as_ptr properties into a # Guarantees section.
- Link to v3: https://lore.kernel.org/r/[email protected]

Changes in v3:
- Split Dir into Dir/SubDir/File to improve API.
- Add "." to end of all comments.
- Convert INVARIANT to # Invariants on types.
- Add backticks everywhere I found variables/types in my comments.
- Promoted invariant comment to doc comment.
- Extended sample commenting to make it clearer what is happening.
- Link to v2: https://lore.kernel.org/r/[email protected]

Changes in v2:
- Drop support for builder / pinned bindings in initial series
- Remove ARef usage to abstract the dentry nature of handles
- Remove error handling to discourage users from caring whether DebugFS
is enabled.
- Support CONFIG_DEBUG_FS=n while leaving the API available
- Fixed mistaken decimal/octal mixup
- Doc/comment cleanup
- Link to v1: https://lore.kernel.org/r/[email protected]

---
Matthew Maurer (5):
rust: debugfs: Bind DebugFS directory creation
rust: debugfs: Bind file creation for long-lived Display
rust: debugfs: Support arbitrary owned backing for File
rust: debugfs: Support format hooks
rust: samples: Add debugfs sample

 MAINTAINERS                         |   3 +
 rust/bindings/bindings_helper.h     |   1 +
 rust/kernel/debugfs.rs              | 218 ++++++++++++++++++++++++++++++++++++
 rust/kernel/debugfs/display_file.rs | 115 +++++++++++++++++++
 rust/kernel/debugfs/entry.rs        |  66 +++++++++++
 rust/kernel/lib.rs                  |   1 +
 samples/rust/Kconfig                |  11 ++
 samples/rust/Makefile               |   1 +
 samples/rust/rust_debugfs.rs        |  76 +++++++++++++
 9 files changed, 492 insertions(+)

---
base-commit: 6a7635cd013da3b41bf1e66bbdb9ae4a570d7449
change-id: 20250428-debugfs-rust-3cd5c97eb7d1

Best regards,

Signature · 1 line
Matthew Maurer <[email protected]>
#1 lore raw

PATCH v6 1/5 rust: debugfs: Bind DebugFS directory creation

Support creating DebugFS directories and subdirectories. Similar to the original DebugFS API, errors are hidden.

Directories persist until their handle and the handles of any child objects have been dropped.

---
 MAINTAINERS                     |  2 +
 rust/bindings/bindings_helper.h |  1 +
 rust/kernel/debugfs.rs          | 88 +++++++++++++++++++++++++++++++++++++++++
 rust/kernel/debugfs/entry.rs    | 58 +++++++++++++++++++++++++++
 rust/kernel/lib.rs              |  1 +
 5 files changed, 150 insertions(+)
MAINTAINERS +2 -0
index ec49e19734256f0c0c0960b722293edeb851562a..fda422023e30fe8821e4cd79b503693ecc6eb48c 100644
@@ -7373,6 +7373,8 @@ F:	include/linux/kobj*
 F:	include/linux/property.h
 F:	include/linux/sysfs.h
 F:	lib/kobj*
+F:	rust/kernel/debugfs.rs
+F:	rust/kernel/debugfs/
 F:	rust/kernel/device.rs
 F:	rust/kernel/device/
 F:	rust/kernel/device_id.rs
rust/bindings/bindings_helper.h +1 -0
index 8cbb660e2ec218021d16e6e0144acf6f4d7cca13..655d88b8e7fe717190ccfb7b8173e84213bf8331 100644
@@ -45,6 +45,7 @@
 #include <linux/cpufreq.h>
 #include <linux/cpumask.h>
 #include <linux/cred.h>
+#include <linux/debugfs.h>
 #include <linux/device/faux.h>
 #include <linux/dma-mapping.h>
 #include <linux/errname.h>
rust/kernel/debugfs.rs new file +88 -0

88 lines not on this page · raw message

rust/kernel/debugfs/entry.rs new file +58 -0

58 lines not on this page · raw message

rust/kernel/lib.rs +1 -0
index 6b4774b2b1c37f4da1866e993be6230bc6715841..43f40b1baa9717ea71e4586042e9e6979491ad37 100644
@@ -66,6 +66,7 @@
 pub mod cpufreq;
 pub mod cpumask;
 pub mod cred;
+pub mod debugfs;
 pub mod device;
 pub mod device_id;
 pub mod devres;
Signature · 1 line
2.50.0.rc2.696.g1fc2a0284f-goog
maintainer
#2 lore raw

Re: [PATCH v6 1/5] rust: debugfs: Bind DebugFS directory creation

rust/kernel/debugfs.rs line 23

Quoted 10 lines · Matthew Maurer
rust/kernel/debugfs.rs new file +10 -0
+#[cfg(CONFIG_DEBUG_FS)]
+mod entry;
+
+/// Owning handle to a DebugFS directory.
+///
+/// This directory will be cleaned up when the handle and all child directory/file handles have
+/// been dropped.
+// We hold a reference to our parent if it exists to prevent the dentry we point to from being
+// cleaned up when our parent is removed.
+pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<entry::Entry>>);

NIT: Can we import Entry, such that we don't have to refer to it as entry::Entry all the time?

#3 lore raw

PATCH v6 2/5 rust: debugfs: Bind file creation for long-lived Display

In reply to Matthew Maurer

Allows creation of files for references that live forever and lack metadata through the Display implementation.

The reference must live forever because we do not have a maximum lifetime for the file we are creating.

The Display implementation is used because seq_printf needs to route through %pA, which in turn routes through Arguments.

---
 rust/kernel/debugfs.rs              | 62 +++++++++++++++++++++++++++++++++++++
 rust/kernel/debugfs/display_file.rs | 60 +++++++++++++++++++++++++++++++++++
 rust/kernel/debugfs/entry.rs        |  8 +++++
 3 files changed, 130 insertions(+)
rust/kernel/debugfs.rs +62 -0
index e390278b8b7384f1d9e2b3d7a4f5f365f6447d0f..6a89557d8cf49327d2984d15741ffb6640defd70 100644
@@ -10,7 +10,10 @@
 use crate::str::CStr;
 #[cfg(CONFIG_DEBUG_FS)]
 use crate::sync::Arc;
+use core::fmt::Display;
 
+#[cfg(CONFIG_DEBUG_FS)]
+mod display_file;
 #[cfg(CONFIG_DEBUG_FS)]
 mod entry;
 
@@ -57,6 +60,43 @@ fn create(_name: &CStr, _parent: Option<&Dir>) -> Self {
         Self()
     }
 
+    #[cfg(CONFIG_DEBUG_FS)]
+    fn create_file<T: Display + Sized>(&self, name: &CStr, data: &'static T) -> File {
+        let Some(parent) = &self.0 else {
+            return File {
+                _entry: entry::Entry::empty(),
+            };
+        };
+        // SAFETY:
+        // * `name` is a NUL-terminated C string, living across the call, by `CStr` invariant.
+        // * `parent` is a live `dentry` since we have a reference to it.
+        // * `vtable` is all stock `seq_file` implementations except for `open`.
+        //   `open`'s only requirement beyond what is provided to all open functions is that the
+        //   inode's data pointer must point to a `T` that will outlive it, which we know because
+        //   we have a static reference.
+        let ptr = unsafe {
+            bindings::debugfs_create_file_full(
+                name.as_char_ptr(),
+                0o444,
+                parent.as_ptr(),
+                data as *const _ as *mut _,
+                core::ptr::null(),
+                &<T as display_file::DisplayFile>::VTABLE,
+            )
+        };
+
+        // SAFETY: `debugfs_create_file_full` either returns an error code or a legal
+        // dentry pointer, so `Entry::new` is safe to call here.
+        let entry = unsafe { entry::Entry::new(ptr, Some(parent.clone())) };
+
+        File { _entry: entry }
+    }
+
+    #[cfg(not(CONFIG_DEBUG_FS))]
+    fn create_file<T: Display + Sized>(&self, _name: &CStr, _data: &'static T) -> File {
+        File {}
+    }
+
     /// Create a DebugFS subdirectory.
     ///
     /// Subdirectory handles cannot outlive the directory handle they were created from.
@@ -73,6 +113,22 @@ pub fn subdir(&self, name: &CStr) -> Self {
         Dir::create(name, Some(self))
     }
 
+    /// Create a file in a DebugFS directory with the provided name, and contents from invoking
+    /// [`Display::fmt`] on the provided reference.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use kernel::c_str;
+    /// # use kernel::debugfs::Dir;
+    /// let dir = Dir::new(c_str!("my_debugfs_dir"));
+    /// dir.display_file(c_str!("foo"), &200);
+    /// // "my_debugfs_dir/foo" now contains the number 200.
+    /// ```
+    pub fn display_file<T: Display + Sized>(&self, name: &CStr, data: &'static T) -> File {
+        self.create_file(name, data)
+    }
+
     /// Create a new directory in DebugFS at the root.
     ///
     /// # Examples
@@ -86,3 +142,9 @@ pub fn new(name: &CStr) -> Self {
         Dir::create(name, None)
     }
 }
+
+/// Handle to a DebugFS file.
+pub struct File {
+    #[cfg(CONFIG_DEBUG_FS)]
+    _entry: entry::Entry,
+}
rust/kernel/debugfs/display_file.rs new file +60 -0

60 lines not on this page · raw message

rust/kernel/debugfs/entry.rs +8 -0
index ae0e2c4e1d58e878ebb081a71e4ac0f4a7d99b91..2baaf31c326c3071b92b5bc37552907fa1102246 100644
@@ -38,6 +38,14 @@ pub(crate) unsafe fn new(entry: *mut bindings::dentry, parent: Option<Arc<Entry>
         }
     }
 
+    /// Constructs a placeholder DebugFS [`Entry`].
+    pub(crate) fn empty() -> Self {
+        Self {
+            entry: core::ptr::null_mut(),
+            _parent: None,
+        }
+    }
+
     /// Returns the pointer representation of the DebugFS directory.
     ///
     /// # Guarantees
Signature · 1 line
2.50.0.rc2.696.g1fc2a0284f-goog
maintainer
#4 lore raw

Re: [PATCH v6 2/5] rust: debugfs: Bind file creation for long-lived Display

Quoted 14 lines · Matthew Maurer
rust/kernel/debugfs/display_file.rs new file +8 -0
new file mode 100644
index 0000000000000000000000000000000000000000..65e37e34d7b587482492dc296637a3bc517b9fe3
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Google LLC.
+
+use crate::seq_file::SeqFile;
+use crate::seq_print;
+use core::fmt::Display;
+
+/// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.

rust/kernel/debugfs/display_file.rs line 18

Quoted 10 lines
rust/kernel/debugfs/display_file.rs new file +10 -0
+///
+/// # Safety
+///
+/// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode`
+///   and will not be mutated during this call.
+/// * `file` must point to a live, not-yet-initialized file object.
+pub(crate) unsafe extern "C" fn display_open<T: Display>(
+    inode: *mut bindings::inode,
+    file: *mut bindings::file,
+) -> i32 {

Please use kernel::ffi::c_int instead.

Quoted 9 lines
rust/kernel/debugfs/display_file.rs new file +9 -0
+    // SAFETY:
+    // * `file` is acceptable by caller precondition.
+    // * `print_act` will be called on a `seq_file` with private data set to the third argument,
+    //   so we meet its safety requirements.
+    // * The `data` pointer passed in the third argument is a valid `T` pointer that outlives
+    //   this call by caller preconditions.
+    unsafe { bindings::single_open(file, Some(display_act::<T>), (*inode).i_private) }
+}
+

rust/kernel/debugfs/display_file.rs line 37

Quoted 10 lines
rust/kernel/debugfs/display_file.rs new file +10 -0
+/// Prints private data stashed in a seq_file to that seq file.
+///
+/// # Safety
+///
+/// `seq` must point to a live `seq_file` whose private data is a live pointer to a `T` which is
+/// not being mutated.
+pub(crate) unsafe extern "C" fn display_act<T: Display>(
+    seq: *mut bindings::seq_file,
+    _: *mut core::ffi::c_void,
+) -> i32 {

kernel::ffi::c_int

Quoted 7 lines
rust/kernel/debugfs/display_file.rs new file +7 -0
+    // SAFETY: By caller precondition, this pointer is live, points to a value of type `T`, and
+    // is not being mutated.
+    let data = unsafe { &*((*seq).private as *mut T) };
+    // SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift
+    // it.
+    let seq_file = unsafe { SeqFile::from_raw(seq) };
+    seq_print!(seq_file, "{}", data);

rust/kernel/debugfs/display_file.rs line 54

Quoted 10 lines
rust/kernel/debugfs/display_file.rs new file +10 -0
+    0
+}
+
+// Work around lack of generic const items.
+pub(crate) trait DisplayFile: Display + Sized {
+    const VTABLE: bindings::file_operations = bindings::file_operations {
+        read: Some(bindings::seq_read),
+        llseek: Some(bindings::seq_lseek),
+        release: Some(bindings::single_release),
+        open: Some(display_open::<Self> as _),

Why the cast? That shouldn't be needed.

Quoted 4 lines
rust/kernel/debugfs/display_file.rs new file +4 -0
+        // SAFETY: `file_operations` supports zeroes in all fields.
+        ..unsafe { core::mem::zeroed() }
+    };
+}
reviewer
#5 lore raw

Re: [PATCH v6 2/5] rust: debugfs: Bind file creation for long-lived Display

Quoted 20 lines · Danilo Krummrich
Quoted 18 lines · Matthew Maurer
rust/kernel/debugfs/display_file.rs new file +12 -0
new file mode 100644
index 0000000000000000000000000000000000000000..65e37e34d7b587482492dc296637a3bc517b9fe3
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Google LLC.
+
+use crate::seq_file::SeqFile;
+use crate::seq_print;
+use core::fmt::Display;
+
+/// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.
+///
+/// # Safety
+///
+/// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode`
Quoted 8 lines

rust/kernel/debugfs/display_file.rs line 18

Quoted 6 lines
rust/kernel/debugfs/display_file.rs new file +6 -0
+///   and will not be mutated during this call.
+/// * `file` must point to a live, not-yet-initialized file object.
+pub(crate) unsafe extern "C" fn display_open<T: Display>(
+    inode: *mut bindings::inode,
+    file: *mut bindings::file,
+) -> i32 {

Please use kernel::ffi::c_int instead.

And please use it without the fully qualified path. The c_int typedef is available in the prelude, so if you import kernel::prelude::* it should be available.

Alice

#6 lore raw

PATCH v6 3/5 rust: debugfs: Support arbitrary owned backing for File

In reply to Matthew Maurer

This allows Files to be backed by Deref<Target=T> rather than just &'static T. This means that dynamically allocated objects can be attached to Files without needing to take extra steps to create a pinned reference that's guaranteed to live long enough.

---
 rust/kernel/debugfs.rs | 51 ++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 39 insertions(+), 12 deletions(-)
rust/kernel/debugfs.rs +39 -12
index 6a89557d8cf49327d2984d15741ffb6640defd70..cd83f21cf2818f406575941ebbc6c426575643e4 100644
@@ -5,12 +5,13 @@
 //!
 //! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h)
 
-#[cfg(CONFIG_DEBUG_FS)]
+use crate::alloc::KBox;
 use crate::prelude::GFP_KERNEL;
 use crate::str::CStr;
 #[cfg(CONFIG_DEBUG_FS)]
 use crate::sync::Arc;
 use core::fmt::Display;
+use core::ops::Deref;
 
 #[cfg(CONFIG_DEBUG_FS)]
 mod display_file;
@@ -61,40 +62,59 @@ fn create(_name: &CStr, _parent: Option<&Dir>) -> Self {
     }
 
     #[cfg(CONFIG_DEBUG_FS)]
-    fn create_file<T: Display + Sized>(&self, name: &CStr, data: &'static T) -> File {
+    fn create_file<D: Deref<Target = T> + 'static + Send + Sync, T: Display>(
+        &self,
+        name: &CStr,
+        data: D,
+    ) -> File {
+        let mut file = File {
+            _entry: entry::Entry::empty(),
+            _data: None,
+        };
+        let Some(data) = KBox::new(data, GFP_KERNEL).ok() else {
+            return file;
+        };
+
         let Some(parent) = &self.0 else {
-            return File {
-                _entry: entry::Entry::empty(),
-            };
+            return file;
         };
+
         // SAFETY:
         // * `name` is a NUL-terminated C string, living across the call, by `CStr` invariant.
         // * `parent` is a live `dentry` since we have a reference to it.
         // * `vtable` is all stock `seq_file` implementations except for `open`.
         //   `open`'s only requirement beyond what is provided to all open functions is that the
         //   inode's data pointer must point to a `T` that will outlive it, which we know because
-        //   we have a static reference.
+        //   we have an owning `D` in the `File`, and we tear down the file during `Drop`.
         let ptr = unsafe {
             bindings::debugfs_create_file_full(
                 name.as_char_ptr(),
                 0o444,
                 parent.as_ptr(),
-                data as *const _ as *mut _,
+                data.deref() as *const _ as *mut _,
                 core::ptr::null(),
                 &<T as display_file::DisplayFile>::VTABLE,
             )
         };
 
+        file._data = Some(data);
+
         // SAFETY: `debugfs_create_file_full` either returns an error code or a legal
         // dentry pointer, so `Entry::new` is safe to call here.
-        let entry = unsafe { entry::Entry::new(ptr, Some(parent.clone())) };
+        file._entry = unsafe { entry::Entry::new(ptr, Some(parent.clone())) };
 
-        File { _entry: entry }
+        file
     }
 
     #[cfg(not(CONFIG_DEBUG_FS))]
-    fn create_file<T: Display + Sized>(&self, _name: &CStr, _data: &'static T) -> File {
-        File {}
+    fn create_file<D: Deref<Target = T> + 'static + Send + Sync, T: Display>(
+        &self,
+        _name: &CStr,
+        data: D,
+    ) -> File {
+        File {
+            _data: KBox::new(data, GFP_KERNEL).ok().map(|x| x as _),
+        }
     }
 
     /// Create a DebugFS subdirectory.
@@ -125,7 +145,11 @@ pub fn subdir(&self, name: &CStr) -> Self {
     /// dir.display_file(c_str!("foo"), &200);
     /// // "my_debugfs_dir/foo" now contains the number 200.
     /// ```
-    pub fn display_file<T: Display + Sized>(&self, name: &CStr, data: &'static T) -> File {
+    pub fn display_file<D: Deref<Target = T> + 'static + Send + Sync, T: Display>(
+        &self,
+        name: &CStr,
+        data: D,
+    ) -> File {
         self.create_file(name, data)
     }
 
@@ -147,4 +171,7 @@ pub fn new(name: &CStr) -> Self {
 pub struct File {
     #[cfg(CONFIG_DEBUG_FS)]
     _entry: entry::Entry,
+    // The data needs to be kept in a `Box` to prevent it from moving when the file does, as
+    // this might invalidate the pointer that's been passed to debugfs.
+    _data: Option<KBox<dyn Send + Sync>>,
 }
Signature · 1 line
2.50.0.rc2.696.g1fc2a0284f-goog
reviewer
#7 lore raw

Re: [PATCH v6 3/5] rust: debugfs: Support arbitrary owned backing for File

Quoted 36 lines · Matthew Maurer

This allows Files to be backed by Deref<Target=T> rather than just &'static T. This means that dynamically allocated objects can be attached to Files without needing to take extra steps to create a pinned reference that's guaranteed to live long enough.

---
 rust/kernel/debugfs.rs | 51 ++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 39 insertions(+), 12 deletions(-)
rust/kernel/debugfs.rs +4 -2
index 6a89557d8cf49327d2984d15741ffb6640defd70..cd83f21cf2818f406575941ebbc6c426575643e4 100644
@@ -5,12 +5,13 @@
 //!
 //! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h)
 
-#[cfg(CONFIG_DEBUG_FS)]
+use crate::alloc::KBox;
 use crate::prelude::GFP_KERNEL;
 use crate::str::CStr;
 #[cfg(CONFIG_DEBUG_FS)]
 use crate::sync::Arc;
 use core::fmt::Display;
+use core::ops::Deref;
 
 #[cfg(CONFIG_DEBUG_FS)]
 mod display_file;
@@ -61,40 +62,59 @@ fn create(_name: &CStr, _parent: Option<&Dir>) -> Self {
     }
 
     #[cfg(CONFIG_DEBUG_FS)]
-    fn create_file<T: Display + Sized>(&self, name: &CStr, data: &'static T) -> File {
+    fn create_file<D: Deref<Target = T> + 'static + Send + Sync, T: Display>(
+        &self,

rust/kernel/debugfs.rs fn create(_name: &CStr, _parent: Option<&Dir>) -> Self { · line 76

Quoted 10 lines
rust/kernel/debugfs.rs +10 -0
+        name: &CStr,
+        data: D,
+    ) -> File {
+        let mut file = File {
+            _entry: entry::Entry::empty(),
+            _data: None,
+        };
+        let Some(data) = KBox::new(data, GFP_KERNEL).ok() else {
+            return file;
+        };

We may want to consider using the ForeignOwnable trait here instead. The trait is implemented by anything that can be converted to/from a void pointer, so you can:

* When creating the file, convert it to a void pointer that you store in File and pass to debugfs_create_file_full.
* When displaying the file, create a borrowed version of the void pointer and display that.
* When freeing the File, convert the void pointer back into an owned value and drop it.

For cases where a box really is necessary, the user can create a box and pass it themselves. But if the user already has a pointer type (e.g. and Arc<T> or &'static T) then they can pass that pointer directly and the pointer is stored as a void pointer without the Box indirection.

Alice

#8 lore raw

Re: [PATCH v6 3/5] rust: debugfs: Support arbitrary owned backing for File

Quoted 1 line

We may want to consider using the ForeignOwnable trait here instead. The

I was considering trying to switch over to StableDeref-like trait
[1] in a follow-up patchset. The core property I need is that moving the D cannot result in the pointer it would deref to changing.

The problem with ForeignOwnable is that it forbids the user from passing in a Box<dyn Foo>, because that doesn't fit in a void* A StableDeref version would not have this issue. I agree that ForeignOwnable would be a strict upgrade to what I have now, since a user can still pass in a Box<Box<dyn Foo>> and have it work with ForeignOwnable, and if we ever added StableDeref, then ForeignOwnable would have a blanket impl for it.

I'll send a new version using ForeignOwnable, and we can consider the StableDeref version in the future.

[1]: https://docs.rs/gimli/latest/gimli/trait.StableDeref.html

Quoted 16 lines

trait is implemented by anything that can be converted to/from a void pointer, so you can:

* When creating the file, convert it to a void pointer that you store in File and pass to debugfs_create_file_full.
* When displaying the file, create a borrowed version of the void pointer and display that.
* When freeing the File, convert the void pointer back into an owned value and drop it.

For cases where a box really is necessary, the user can create a box and pass it themselves. But if the user already has a pointer type (e.g. and Arc<T> or &'static T) then they can pass that pointer directly and the pointer is stored as a void pointer without the Box indirection.

Alice

maintainer
#9 lore raw

Re: [PATCH v6 3/5] rust: debugfs: Support arbitrary owned backing for File

Quoted 14 lines · Matthew Maurer
Quoted 1 line

We may want to consider using the ForeignOwnable trait here instead. The

I was considering trying to switch over to StableDeref-like trait
[1] in a follow-up patchset. The core property I need is that moving the D cannot result in the pointer it would deref to changing.

The problem with ForeignOwnable is that it forbids the user from passing in a Box<dyn Foo>, because that doesn't fit in a void* A StableDeref version would not have this issue. I agree that ForeignOwnable would be a strict upgrade to what I have now, since a user can still pass in a Box<Box<dyn Foo>> and have it work with ForeignOwnable, and if we ever added StableDeref, then ForeignOwnable would have a blanket impl for it.

Quoted 2 lines

I'll send a new version using ForeignOwnable, and we can consider the StableDeref version in the future.

Yes, please do that for now. It's rather common case that drivers want to expose reference counted data, i.e. an Arc, through debugfs and having to go through the indirection with an additional Box isn't quite nice.

Quoted 19 lines

[1]: https://docs.rs/gimli/latest/gimli/trait.StableDeref.html

Quoted 16 lines

trait is implemented by anything that can be converted to/from a void pointer, so you can:

* When creating the file, convert it to a void pointer that you store in File and pass to debugfs_create_file_full.
* When displaying the file, create a borrowed version of the void pointer and display that.
* When freeing the File, convert the void pointer back into an owned value and drop it.

For cases where a box really is necessary, the user can create a box and pass it themselves. But if the user already has a pointer type (e.g. and Arc<T> or &'static T) then they can pass that pointer directly and the pointer is stored as a void pointer without the Box indirection.

Alice

#10 lore raw

PATCH v6 4/5 rust: debugfs: Support format hooks

In reply to Matthew Maurer

Rather than always using Display, allow hooking arbitrary functions to arbitrary files. Display technically has the expressiveness to do this, but requires a new type be declared for every different way to render things, which can be very clumsy.

---
 rust/kernel/debugfs.rs              | 41 ++++++++++++++++++++++++++
 rust/kernel/debugfs/display_file.rs | 57 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 97 insertions(+), 1 deletion(-)
rust/kernel/debugfs.rs +41 -0
index cd83f21cf2818f406575941ebbc6c426575643e4..78544653b46d959c1520b1210170d22a0e973989 100644
@@ -10,6 +10,7 @@
 use crate::str::CStr;
 #[cfg(CONFIG_DEBUG_FS)]
 use crate::sync::Arc;
+use core::fmt;
 use core::fmt::Display;
 use core::ops::Deref;
 
@@ -153,6 +154,46 @@ pub fn display_file<D: Deref<Target = T> + 'static + Send + Sync, T: Display>(
         self.create_file(name, data)
     }
 
+    /// Create a file in a DebugFS directory with the provided name, and contents from invoking `f`
+    /// on the provided reference.
+    ///
+    /// `f` must be a function item or a non-capturing closure, or this will fail to compile.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # use core::sync::atomic::{AtomicU32, Ordering};
+    /// # use kernel::c_str;
+    /// # use kernel::debugfs::Dir;
+    /// let dir = Dir::new(c_str!("foo"));
+    /// static MY_ATOMIC: AtomicU32 = AtomicU32::new(3);
+    /// let file = dir.fmt_file(c_str!("bar"), &MY_ATOMIC, &|val, f| {
+    ///   let out = val.load(Ordering::Relaxed);
+    ///   writeln!(f, "{out:#010x}")
+    /// });
+    /// MY_ATOMIC.store(10, Ordering::Relaxed);
+    /// ```
+    pub fn fmt_file<
+        D: Deref<Target = T> + 'static + Send + Sync,
+        T: Sync,
+        F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
+    >(
+        &self,
+        name: &CStr,
+        data: D,
+        f: &'static F,
+    ) -> File {
+        #[cfg(CONFIG_DEBUG_FS)]
+        let data_adapted = display_file::FormatAdapter::new(data, f);
+        #[cfg(not(CONFIG_DEBUG_FS))]
+        let data_adapted = {
+            // Mark used
+            let (_, _) = (data, f);
+            &0
+        };
+        self.display_file(name, data_adapted)
+    }
+
     /// Create a new directory in DebugFS at the root.
     ///
     /// # Examples
rust/kernel/debugfs/display_file.rs +56 -1

66 lines not on this page · raw message

Signature · 1 line
2.50.0.rc2.696.g1fc2a0284f-goog
#11 lore raw

PATCH v6 5/5 rust: samples: Add debugfs sample

In reply to Matthew Maurer

Provides an example of using the Rust DebugFS bindings.

---
 MAINTAINERS                  |  1 +
 samples/rust/Kconfig         | 11 +++++++
 samples/rust/Makefile        |  1 +
 samples/rust/rust_debugfs.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 89 insertions(+)
MAINTAINERS +1 -0
index fda422023e30fe8821e4cd79b503693ecc6eb48c..7faa088cc08514acfec371b043d324bb52d2e7ba 100644
@@ -7382,6 +7382,7 @@ F:	rust/kernel/devres.rs
 F:	rust/kernel/driver.rs
 F:	rust/kernel/faux.rs
 F:	rust/kernel/platform.rs
+F:	samples/rust/rust_debugfs.rs
 F:	samples/rust/rust_driver_platform.rs
 F:	samples/rust/rust_driver_faux.rs
 
samples/rust/Kconfig +11 -0
index 7f7371a004ee0a8f67dca99c836596709a70c4fa..8eb2088b66b0646c7a1fba9cdccc9a6bb328defb 100644
@@ -62,6 +62,17 @@ config SAMPLE_RUST_DMA
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DEBUGFS
+	tristate "DebugFS Test Driver"
+	depends on DEBUG_FS
+	help
+	  This option builds the Rust DebugFS Test driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_debugfs.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_DRIVER_PCI
 	tristate "PCI Driver"
 	depends on PCI
samples/rust/Makefile +1 -0
index bd2faad63b4f3befe7d1ed5139fe25c7a8b6d7f6..61276222a99f8cc6d7f84c26d0533b30815ebadd 100644
@@ -4,6 +4,7 @@ ccflags-y += -I$(src)				# needed for trace events
 obj-$(CONFIG_SAMPLE_RUST_MINIMAL)		+= rust_minimal.o
 obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE)		+= rust_misc_device.o
 obj-$(CONFIG_SAMPLE_RUST_PRINT)			+= rust_print.o
+obj-$(CONFIG_SAMPLE_RUST_DEBUGFS)		+= rust_debugfs.o
 obj-$(CONFIG_SAMPLE_RUST_DMA)			+= rust_dma.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
samples/rust/rust_debugfs.rs new file +76 -0

76 lines not on this page · raw message

Signature · 1 line
2.50.0.rc2.696.g1fc2a0284f-goog
maintainer
#12 lore raw

Re: [PATCH v6 5/5] rust: samples: Add debugfs sample

samples/rust/Kconfig config SAMPLE_RUST_DMA · line 66

Quoted 2 lines · Matthew Maurer
samples/rust/Kconfig +2 -0
+config SAMPLE_RUST_DEBUGFS
+	tristate "DebugFS Test Driver"

s/driver/module/

samples/rust/Kconfig config SAMPLE_RUST_DMA · line 69

Quoted 3 lines
samples/rust/Kconfig +3 -0
+	depends on DEBUG_FS
+	help
+	  This option builds the Rust DebugFS Test driver sample.

s/driver/module/

Tested-by on the v6 series
#14 lore raw
In reply to Matthew Maurer
Quoted 4 lines · Matthew Maurer

This series provides safe DebugFS bindings for Rust, with a sample driver using them.

Having this series applied on top of v6.16-rc1 and adapting a proprietary driver exposing some simple u64 values via debugfs on ARM64 looks good.

Thanks!

Dirk

Quoted 75 lines

---
Changes in v6:
- Replaced explicit lifetimes with children keeping their parents alive.
- Added support for attaching owned data.
- Removed recomendation to only keep root handles and handles you want
to delete around.
- Refactored some code into separate files to improve clarity.
- Link to v5: https://lore.kernel.org/r/[email protected]

Changes in v5:
- Made Dir + File wrappers around Entry
- All functions return owning handles. To discard without drop, use forget. To keep a handle without drop, use ManuallyDrop.
- Fixed bugs around not(CONFIG_DEBUG_FS)
- Removed unnecessary addr_of!
- Link to v4: https://lore.kernel.org/r/[email protected]

Changes in v4:
- Remove SubDir, replace with type-level constant.
- Add lifetime to Dir to prevent subdirectories and files from outliving their parents and triggering an Oops when accessed.
- Split unsafe blocks with two calls into two blocks
- Access private field through direct pointer dereference, avoiding creation of a reference to it.
- Notably not changed - owning/non-owning handle defaults. The best read
I had from the thread was to continue with this mode, but I'm willing
to change if need be.
- Comment changes
- More comment markdown
- Remove scopes from examples
- Put as_ptr properties into a # Guarantees section.
- Link to v3: https://lore.kernel.org/r/[email protected]

Changes in v3:
- Split Dir into Dir/SubDir/File to improve API.
- Add "." to end of all comments.
- Convert INVARIANT to # Invariants on types.
- Add backticks everywhere I found variables/types in my comments.
- Promoted invariant comment to doc comment.
- Extended sample commenting to make it clearer what is happening.
- Link to v2: https://lore.kernel.org/r/[email protected]

Changes in v2:
- Drop support for builder / pinned bindings in initial series
- Remove ARef usage to abstract the dentry nature of handles
- Remove error handling to discourage users from caring whether DebugFS
is enabled.
- Support CONFIG_DEBUG_FS=n while leaving the API available
- Fixed mistaken decimal/octal mixup
- Doc/comment cleanup
- Link to v1: https://lore.kernel.org/r/[email protected]

---
Matthew Maurer (5):
rust: debugfs: Bind DebugFS directory creation
rust: debugfs: Bind file creation for long-lived Display
rust: debugfs: Support arbitrary owned backing for File
rust: debugfs: Support format hooks
rust: samples: Add debugfs sample

 MAINTAINERS                         |   3 +
 rust/bindings/bindings_helper.h     |   1 +
 rust/kernel/debugfs.rs              | 218 ++++++++++++++++++++++++++++++++++++
 rust/kernel/debugfs/display_file.rs | 115 +++++++++++++++++++
 rust/kernel/debugfs/entry.rs        |  66 +++++++++++
 rust/kernel/lib.rs                  |   1 +
 samples/rust/Kconfig                |  11 ++
 samples/rust/Makefile               |   1 +
 samples/rust/rust_debugfs.rs        |  76 +++++++++++++
 9 files changed, 492 insertions(+)

---
base-commit: 6a7635cd013da3b41bf1e66bbdb9ae4a570d7449
change-id: 20250428-debugfs-rust-3cd5c97eb7d1

Best regards,

Signature · 17 lines
======================================================================
Dirk Behme                      Robert Bosch Car Multimedia GmbH
                                CM/ESO2
Phone: +49 5121 49-3274         Dirk Behme
Fax:   +49 711 811 5053274      PO Box 77 77 77
mailto:[email protected]  D-31132 Hildesheim - Germany

Bosch Group, Car Multimedia (CM)
             Engineering SW Operating Systems 2 (ESO2)

Robert Bosch Car Multimedia GmbH - Ein Unternehmen der Bosch Gruppe
Sitz: Hildesheim
Registergericht: Amtsgericht Hildesheim HRB 201334
Aufsichtsratsvorsitzender: Dr. Dirk Hoheisel
Geschäftsführung: Dr. Steffen Berns;
                  Dr. Sven Ost, Jörg Pollak, Dr. Walter Schirm
======================================================================