All of lore.kernel.org
 help / color / mirror / Atom feed
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>,
	"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>,
	"Benno Lossin" <lossin@kernel.org>
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
	 Matthew Maurer <mmaurer@google.com>
Subject: [PATCH v9 4/5] rust: debugfs: Support format hooks
Date: Wed, 09 Jul 2025 19:09:31 +0000	[thread overview]
Message-ID: <20250709-debugfs-rust-v9-4-92b9eab5a951@google.com> (raw)
In-Reply-To: <20250709-debugfs-rust-v9-0-92b9eab5a951@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              | 49 +++++++++++++++++++++++++++++++++++++
 rust/kernel/debugfs/display_file.rs | 39 ++++++++++++++++++++++++++++-
 2 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index a1a84dd309216f455ae8fe3d3c0fd00f957f82a9..083c49007cd7ae5b3d7954bf859c24b7eb62d557 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -9,6 +9,7 @@
 use crate::str::CStr;
 #[cfg(CONFIG_DEBUG_FS)]
 use crate::sync::Arc;
+use core::fmt;
 use core::fmt::Display;
 use core::marker::PhantomPinned;
 use core::ops::Deref;
@@ -194,6 +195,54 @@ pub fn display_file<'b, T: Display + Send + Sync, E, TI: PinInit<T, E>>(
         unsafe { self.create_file(name, data, vtable) }
     }
 
+    /// 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: Send + Sync,
+        E,
+        TI: PinInit<T, E>,
+        F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
+    >(
+        &self,
+        name: &'b CStr,
+        data: TI,
+        _f: &'static F,
+    ) -> impl PinInit<File<T>, E> + use<'_, 'b, T, TI, E, F> {
+        #[cfg(CONFIG_DEBUG_FS)]
+        let vtable = &<display_file::FormatAdapter<T, F> as display_file::DisplayFile>::VTABLE;
+        #[cfg(not(CONFIG_DEBUG_FS))]
+        let vtable = ();
+
+        // SAFETY: `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 `FormatAdapter<T, F>` that will outlive it.
+        // `create_file`'s safety requirements provide the lifetime aspect of this, but we are
+        // using a private `T` pointer. This is legal because:
+        // 1. `FormatAdapter<T, F>` is a `#[repr(transparent)]` wrapper around `T`, so the
+        //    implicit transmute is legal.
+        // 2. The invariant in `FormatAdapter` that `F` is inhabited is upheld because we have
+        //    `_f`, so constructing a `FormatAdapter<T, F> is legal.
+        unsafe { self.create_file(name, data, vtable) }
+    }
+
     /// Create a new directory in DebugFS at the root.
     ///
     /// # Examples
diff --git a/rust/kernel/debugfs/display_file.rs b/rust/kernel/debugfs/display_file.rs
index 2a58ca2685258b050089e4cfd62188885f7f5f04..6275283b9dabd8dae84a9335c8832e7943707d56 100644
--- a/rust/kernel/debugfs/display_file.rs
+++ b/rust/kernel/debugfs/display_file.rs
@@ -4,7 +4,8 @@
 use crate::prelude::*;
 use crate::seq_file::SeqFile;
 use crate::seq_print;
-use core::fmt::Display;
+use core::fmt::{Display, Formatter, Result};
+use core::marker::PhantomData;
 
 /// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.
 ///
@@ -61,3 +62,39 @@ impl<T: Display + Sync> DisplayFile for T {
         ..unsafe { core::mem::zeroed() }
     };
 }
+
+/// 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<D, F> {
+    inner: D,
+    _formatter: PhantomData<F>,
+}
+
+impl<D, F> Display for FormatAdapter<D, F>
+where
+    F: Fn(&D, &mut Formatter<'_>) -> Result + 'static,
+{
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> 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() }
+}

-- 
2.50.0.727.gbf7dc18ff4-goog


  parent reply	other threads:[~2025-07-09 19:09 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-09 19:09 [PATCH v9 0/5] rust: DebugFS Bindings Matthew Maurer
2025-07-09 19:09 ` [PATCH v9 1/5] rust: debugfs: Bind DebugFS directory creation Matthew Maurer
2025-07-09 19:09 ` [PATCH v9 2/5] rust: debugfs: Bind file creation for long-lived Display Matthew Maurer
2025-07-09 19:09 ` [PATCH v9 3/5] rust: debugfs: Support `PinInit` backing for `File`s Matthew Maurer
2025-08-19  5:51   ` Dirk Behme
2025-08-19 14:33     ` Matthew Maurer
2025-08-19 14:47       ` Miguel Ojeda
2025-08-19 23:22         ` Matthew Maurer
2025-07-09 19:09 ` Matthew Maurer [this message]
2025-07-09 19:09 ` [PATCH v9 5/5] rust: samples: Add debugfs sample Matthew Maurer
2025-07-09 21:56   ` Danilo Krummrich
2025-07-09 23:35     ` Matthew Maurer
2025-07-09 21:47 ` [PATCH v9 0/5] rust: DebugFS Bindings Danilo Krummrich
2025-07-09 21:53   ` Matthew Maurer
2025-07-09 21:59     ` Danilo Krummrich
2025-07-09 22:04       ` Matthew Maurer
2025-07-09 22:12         ` Danilo Krummrich
2025-07-09 22:21           ` Matthew Maurer
2025-07-09 22:33             ` Danilo Krummrich
2025-07-10  5:27           ` Greg Kroah-Hartman
2025-07-10  9:36             ` Danilo Krummrich
2025-07-10 11:09               ` Benno Lossin
2025-07-10 11:11                 ` Benno Lossin

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=20250709-debugfs-rust-v9-4-92b9eab5a951@google.com \
    --to=mmaurer@google.com \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --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=lossin@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.