From: Matthew Maurer <mmaurer@google.com>
To: "Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Rafael J. Wysocki" <rafael@kernel.org>,
"Sami Tolvanen" <samitolvanen@google.com>,
"Timur Tabi" <ttabi@nvidia.com>
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
Matthew Maurer <mmaurer@google.com>
Subject: [PATCH v5 3/4] rust: debugfs: Support format hooks
Date: Mon, 05 May 2025 23:51:36 +0000 [thread overview]
Message-ID: <20250505-debugfs-rust-v5-3-3e93ce7bb76e@google.com> (raw)
In-Reply-To: <20250505-debugfs-rust-v5-0-3e93ce7bb76e@google.com>
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.
Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
rust/kernel/debugfs.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 122 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index 4a138717bd0fdb320033d07446a192c9f520a17b..e2f5960bb87f24607780b3f4e67039e379f3bda6 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -6,6 +6,7 @@
//! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h)
use crate::str::CStr;
+use core::fmt;
use core::fmt::Display;
use core::marker::PhantomData;
@@ -154,6 +155,52 @@ pub fn display_file<'b, T: Display + Sized>(
&'b self,
name: &CStr,
data: &'static T,
+ ) -> File<'b> {
+ // SAFETY: As `data` lives for the static lifetime, it outlives the file.
+ unsafe { self.display_file_raw(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<'b, T, F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result>(
+ &'b self,
+ name: &CStr,
+ data: &'static T,
+ f: &'static F,
+ ) -> File<'b> {
+ // SAFETY: As `data` lives for the static lifetime, it outlives the file
+ unsafe { self.fmt_file_raw(name, data, f) }
+ }
+
+ /// Creates a DebugFS file backed by the display implementation of the provided pointer.
+ ///
+ /// # Safety
+ ///
+ /// The pointee of `data` must outlive the accessibility of the `Dir` returned by this function.
+ /// This means that before `data` may become invalid, either:
+ /// * The refcount must go to zero.
+ /// * The file must be rendered inaccessible, e.g. via `debugfs_remove`.
+ unsafe fn display_file_raw<'b, T: Display + Sized>(
+ &'b self,
+ name: &CStr,
+ data: *const T,
) -> File<'b> {
// SAFETY:
// * `name` is a NUL-terminated C string, living across the call, by `CStr` invariant.
@@ -189,6 +236,32 @@ pub fn display_file<'b, T: Display + Sized>(
File(entry)
}
+ /// Create a file in a DebugFS directory with the provided name, and contents from invoking the
+ /// fomatter on the attached data.
+ ///
+ /// The attached function must be a ZST, and will cause a compilation error if it is not.
+ ///
+ /// # Safety
+ ///
+ /// `data` must outlive the resulting file's accessibility
+ unsafe fn fmt_file_raw<'b, T, F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result>(
+ &'b self,
+ name: &CStr,
+ data: &T,
+ f: &'static F,
+ ) -> File<'b> {
+ #[cfg(CONFIG_DEBUG_FS)]
+ let data_adapted = FormatAdapter::new(data, f);
+ #[cfg(not(CONFIG_DEBUG_FS))]
+ let data_adapted = {
+ // Mark used
+ let (_, _) = (data, f);
+ &0
+ };
+ // SAFETY: data outlives the file's accessibility, so data_adapted does too
+ unsafe { self.display_file_raw(name, data_adapted) }
+ }
+
/// Create a new directory in DebugFS at the root.
///
/// # Examples
@@ -202,6 +275,7 @@ pub fn new(name: &CStr) -> Self {
Dir::create(name, None)
}
}
+
/// Handle to a DebugFS file.
#[repr(transparent)]
pub struct File<'a>(Entry<'a>);
@@ -210,7 +284,9 @@ pub fn new(name: &CStr) -> Self {
mod helpers {
use crate::seq_file::SeqFile;
use crate::seq_print;
- use core::fmt::Display;
+ use core::fmt;
+ use core::fmt::{Display, Formatter};
+ use core::marker::PhantomData;
/// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.
///
@@ -265,6 +341,51 @@ pub(crate) trait DisplayFile: Display + Sized {
}
impl<T: Display + Sized> DisplayFile for T {}
+
+ /// Adapter to implement `Display` via a callback with the same representation as `T`.
+ ///
+ /// # Invariants
+ ///
+ /// If an instance for `FormatAdapter<_, F>` is constructed, `F` is inhabited.
+ #[repr(transparent)]
+ pub(crate) struct FormatAdapter<T, F> {
+ inner: T,
+ _formatter: PhantomData<F>,
+ }
+
+ impl<T, F> FormatAdapter<T, F> {
+ pub(crate) fn new<'a>(inner: &'a T, _f: &'static F) -> &'a Self {
+ // SAFETY: FormatAdapater is a repr(transparent) wrapper around T, so
+ // casting a reference is legal
+ // INVARIANT: We were passed a reference to F, so it is inhabited.
+ unsafe { core::mem::transmute(inner) }
+ }
+ }
+
+ impl<T, F> Display for FormatAdapter<T, F>
+ where
+ F: Fn(&T, &mut Formatter<'_>) -> fmt::Result + 'static,
+ {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ // SAFETY: FormatAdapter<_, F> can only be constructed if F is inhabited
+ let f: &F = unsafe { materialize_zst_fmt() };
+ f(&self.inner, fmt)
+ }
+ }
+
+ /// For types with a unique value, produce a static reference to it.
+ ///
+ /// # Safety
+ ///
+ /// The caller asserts that F is inhabited
+ unsafe fn materialize_zst_fmt<F>() -> &'static F {
+ const { assert!(core::mem::size_of::<F>() == 0) };
+ let zst_dangle: core::ptr::NonNull<F> = core::ptr::NonNull::dangling();
+ // SAFETY: While the pointer is dangling, it is a dangling pointer to a ZST, based on the
+ // assertion above. The type is also inhabited, by the caller's assertion. This means
+ // we can materialize it.
+ unsafe { zst_dangle.as_ref() }
+ }
}
#[cfg(CONFIG_DEBUG_FS)]
--
2.49.0.967.g6a0df3ecc3-goog
next prev parent reply other threads:[~2025-05-05 23:51 UTC|newest]
Thread overview: 59+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-05 23:51 [PATCH v5 0/4] rust: DebugFS Bindings Matthew Maurer
2025-05-05 23:51 ` [PATCH v5 1/4] rust: debugfs: Bind DebugFS directory creation Matthew Maurer
2025-05-07 18:46 ` Timur Tabi
2025-05-14 22:26 ` Matthew Maurer
2025-05-14 7:33 ` Benno Lossin
2025-05-14 8:49 ` Greg Kroah-Hartman
2025-05-14 9:38 ` Benno Lossin
2025-05-05 23:51 ` [PATCH v5 2/4] rust: debugfs: Bind file creation for long-lived Display Matthew Maurer
2025-05-07 19:04 ` Timur Tabi
2025-05-07 19:41 ` Timur Tabi
2025-05-09 12:56 ` Alice Ryhl
2025-05-12 20:51 ` Timur Tabi
2025-05-14 8:06 ` Benno Lossin
2025-05-05 23:51 ` Matthew Maurer [this message]
2025-05-05 23:51 ` [PATCH v5 4/4] rust: samples: Add debugfs sample Matthew Maurer
2025-05-14 7:20 ` Benno Lossin
2025-05-14 9:07 ` Danilo Krummrich
2025-05-14 9:54 ` Benno Lossin
2025-05-14 11:24 ` Danilo Krummrich
2025-05-14 12:21 ` Benno Lossin
2025-05-14 13:04 ` Danilo Krummrich
2025-05-14 22:14 ` Matthew Maurer
2025-05-14 22:08 ` Matthew Maurer
2025-05-14 22:14 ` Danilo Krummrich
2025-05-14 22:23 ` Matthew Maurer
2025-05-14 22:32 ` Matthew Maurer
2025-05-14 22:40 ` Timur Tabi
2025-05-14 22:42 ` Matthew Maurer
2025-05-15 7:43 ` gregkh
2025-05-15 8:50 ` Benno Lossin
2025-05-14 21:55 ` Matthew Maurer
2025-05-14 22:18 ` Danilo Krummrich
2025-05-15 8:59 ` Benno Lossin
2025-05-15 11:43 ` Greg Kroah-Hartman
2025-05-15 12:37 ` Danilo Krummrich
2025-05-15 12:55 ` Benno Lossin
2025-05-20 21:24 ` Alice Ryhl
2025-05-21 4:47 ` Greg Kroah-Hartman
2025-05-21 22:40 ` Alice Ryhl
2025-05-21 7:57 ` Danilo Krummrich
2025-05-21 22:43 ` Alice Ryhl
2025-05-22 6:25 ` Danilo Krummrich
2025-05-22 8:28 ` Greg Kroah-Hartman
2025-05-22 14:01 ` Alice Ryhl
2025-05-22 14:15 ` Greg Kroah-Hartman
2025-05-22 17:40 ` Alice Ryhl
2025-05-22 20:26 ` Benno Lossin
2025-05-23 9:15 ` Greg Kroah-Hartman
2025-05-22 17:53 ` Danilo Krummrich
2025-05-23 9:14 ` Greg Kroah-Hartman
2025-05-23 9:42 ` Danilo Krummrich
2025-05-23 10:22 ` Greg Kroah-Hartman
2025-05-23 17:09 ` Alice Ryhl
2025-05-24 12:25 ` Danilo Krummrich
2025-05-27 11:38 ` Alice Ryhl
2025-05-27 11:50 ` Danilo Krummrich
2025-06-10 17:54 ` Matthew Maurer
2025-05-23 17:06 ` Alice Ryhl
2025-05-07 16:49 ` [PATCH v5 0/4] rust: DebugFS Bindings Danilo Krummrich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250505-debugfs-rust-v5-3-3e93ce7bb76e@google.com \
--to=mmaurer@google.com \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=samitolvanen@google.com \
--cc=tmgross@umich.edu \
--cc=ttabi@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.