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>,
	Dirk Behme <dirk.behme@de.bosch.com>
Subject: [PATCH v8 5/6] rust: debugfs: Support format hooks
Date: Fri, 27 Jun 2025 23:18:28 +0000	[thread overview]
Message-ID: <20250627-debugfs-rust-v8-5-c6526e413d40@google.com> (raw)
In-Reply-To: <20250627-debugfs-rust-v8-0-c6526e413d40@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.

Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
 rust/kernel/debugfs.rs              | 45 ++++++++++++++++++++
 rust/kernel/debugfs/display_file.rs | 85 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 129 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index 929e55ee5629f6888edf29997b9ed77d274e11c8..d74b599e8534536b10502e6db8c6f3197f7ab4a5 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -10,7 +10,9 @@
 #[cfg(CONFIG_DEBUG_FS)]
 use crate::sync::Arc;
 use crate::types::ForeignOwnable;
+use core::fmt;
 use core::fmt::Display;
+use core::ops::Deref;
 
 #[cfg(CONFIG_DEBUG_FS)]
 mod display_file;
@@ -157,6 +159,49 @@ pub fn display_file<D: ForeignOwnable + Send + Sync>(&self, name: &CStr, data: D
         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: ForeignOwnable + Send + Sync,
+        T,
+        F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
+    >(
+        &self,
+        name: &CStr,
+        data: D,
+        f: &'static F,
+    ) -> File
+    where
+        for<'a> D::Borrowed<'a>: Deref<Target = T>,
+    {
+        #[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
diff --git a/rust/kernel/debugfs/display_file.rs b/rust/kernel/debugfs/display_file.rs
index 0c2dd756fa866425d1b7771beceaa2fb43bf11e5..b38675a90e1b2e359fb54afd91062b26d1c32ba2 100644
--- a/rust/kernel/debugfs/display_file.rs
+++ b/rust/kernel/debugfs/display_file.rs
@@ -5,7 +5,9 @@
 use crate::seq_file::SeqFile;
 use crate::seq_print;
 use crate::types::ForeignOwnable;
-use core::fmt::Display;
+use core::fmt::{Display, Formatter, Result};
+use core::marker::PhantomData;
+use core::ops::Deref;
 
 /// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.
 ///
@@ -70,3 +72,84 @@ impl<D: ForeignOwnable + Sync> DisplayFile for D
         ..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> FormatAdapter<D, F> {
+    pub(crate) fn new(inner: D, _f: &'static F) -> Self {
+        // INVARIANT: We were passed a reference to F, so it is inhabited.
+        FormatAdapter {
+            inner,
+            _formatter: PhantomData,
+        }
+    }
+}
+
+pub(crate) struct BorrowedAdapter<'a, D: ForeignOwnable, F> {
+    borrowed: D::Borrowed<'a>,
+    _formatter: PhantomData<F>,
+}
+
+// SAFETY: We delegate to D's implementation of `ForeignOwnable`, so `into_foreign` produced aligned
+// pointers.
+unsafe impl<D: ForeignOwnable, F> ForeignOwnable for FormatAdapter<D, F> {
+    type PointedTo = D::PointedTo;
+    type Borrowed<'a> = BorrowedAdapter<'a, D, F>;
+    type BorrowedMut<'a> = Self::Borrowed<'a>;
+    fn into_foreign(self) -> *mut Self::PointedTo {
+        self.inner.into_foreign()
+    }
+    unsafe fn from_foreign(foreign: *mut Self::PointedTo) -> Self {
+        Self {
+            // SAFETY: `into_foreign` is delegated, so a delegated `from_foreign` is safe.
+            inner: unsafe { D::from_foreign(foreign) },
+            _formatter: PhantomData,
+        }
+    }
+    unsafe fn borrow<'a>(foreign: *mut Self::PointedTo) -> Self::Borrowed<'a> {
+        BorrowedAdapter {
+            // SAFETY: `into_foreign` is delegated, so a delegated `borrow` is safe.
+            borrowed: unsafe { D::borrow(foreign) },
+            _formatter: PhantomData,
+        }
+    }
+    unsafe fn borrow_mut<'a>(foreign: *mut Self::PointedTo) -> Self::BorrowedMut<'a> {
+        // SAFETY: `borrow_mut` has stricter requirements than `borrow`
+        unsafe { Self::borrow(foreign) }
+    }
+}
+
+impl<'a, D: ForeignOwnable<Borrowed<'a>: Deref<Target = T>>, T, F> Display
+    for BorrowedAdapter<'a, D, F>
+where
+    F: Fn(&T, &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.borrowed, 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-06-27 23:19 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-27 23:18 [PATCH v8 0/6] rust: DebugFS Bindings Matthew Maurer
2025-06-27 23:18 ` [PATCH v8 1/6] rust: debugfs: Bind DebugFS directory creation Matthew Maurer
2025-06-27 23:18 ` [PATCH v8 2/6] rust: debugfs: Bind file creation for long-lived Display Matthew Maurer
2025-06-27 23:18 ` [PATCH v8 3/6] rust: types: Support &'static and &'static mut ForeignOwnable Matthew Maurer
2025-07-01 11:41   ` Dirk Behme
2025-07-01 11:46     ` Danilo Krummrich
2025-06-27 23:18 ` [PATCH v8 4/6] rust: debugfs: Support arbitrary owned backing for File Matthew Maurer
2025-06-30 17:29   ` Danilo Krummrich
2025-06-30 17:34     ` Matthew Maurer
2025-06-30 17:36       ` Matthew Maurer
2025-06-30 17:39       ` Danilo Krummrich
2025-06-30 17:49         ` Matthew Maurer
2025-06-30 18:16           ` Danilo Krummrich
2025-07-01 13:58             ` Greg Kroah-Hartman
2025-07-01 14:13               ` Danilo Krummrich
2025-07-01 14:21                 ` Greg Kroah-Hartman
2025-07-01 15:10                   ` Danilo Krummrich
2025-07-01 18:11                     ` Matthew Maurer
2025-07-01 19:21                       ` Danilo Krummrich
2025-07-01 19:46                         ` Benno Lossin
2025-07-01 19:58                           ` Danilo Krummrich
2025-07-01 20:03                             ` Benno Lossin
2025-07-01 20:09                               ` Benno Lossin
2025-07-01 20:16                                 ` Danilo Krummrich
2025-07-01 21:53                                   ` Matthew Maurer
2025-07-01 22:26                                     ` Danilo Krummrich
2025-07-01 20:07                     ` Benno Lossin
2025-07-03 10:02                     ` Alice Ryhl
2025-07-03 10:33                       ` Benno Lossin
2025-07-03 10:54                         ` Alice Ryhl
2025-07-03 11:41                           ` Greg Kroah-Hartman
2025-07-03 12:29                             ` Danilo Krummrich
2025-07-03 12:50                               ` Greg Kroah-Hartman
2025-07-03 14:00                                 ` Danilo Krummrich
2025-07-03 13:34                               ` Benno Lossin
2025-07-03 14:04                                 ` Danilo Krummrich
2025-07-03 13:35                               ` Benno Lossin
2025-07-03 13:38                                 ` Alice Ryhl
2025-07-03 12:34                             ` Benno Lossin
2025-07-03 12:45                               ` Greg Kroah-Hartman
2025-07-03 11:00                       ` Danilo Krummrich
2025-06-27 23:18 ` Matthew Maurer [this message]
2025-06-27 23:18 ` [PATCH v8 6/6] rust: samples: Add debugfs sample Matthew Maurer
2025-07-01 14:03   ` Greg Kroah-Hartman
2025-07-01 17:24     ` Matthew Maurer
2025-07-01 17:34       ` Danilo Krummrich
2025-07-01 18:32         ` Matthew Maurer
2025-07-01 19:40           ` Danilo Krummrich
2025-07-01 10:57 ` [PATCH v8 0/6] rust: DebugFS Bindings Alice Ryhl

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=20250627-debugfs-rust-v8-5-c6526e413d40@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=dirk.behme@de.bosch.com \
    --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.