[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250429-debugfs-rust-v1-3-6b6e7cb7929f@google.com>
Date: Tue, 29 Apr 2025 23:15:57 +0000
From: Matthew Maurer <mmaurer@...gle.com>
To: Miguel Ojeda <ojeda@...nel.org>, Alex Gaynor <alex.gaynor@...il.com>,
Boqun Feng <boqun.feng@...il.com>, Gary Guo <gary@...yguo.net>,
"Björn Roy Baron" <bjorn3_gh@...tonmail.com>, Benno Lossin <benno.lossin@...ton.me>,
Andreas Hindborg <a.hindborg@...nel.org>, Alice Ryhl <aliceryhl@...gle.com>,
Trevor Gross <tmgross@...ch.edu>, Danilo Krummrich <dakr@...nel.org>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>, "Rafael J. Wysocki" <rafael@...nel.org>,
Sami Tolvanen <samitolvanen@...gle.com>
Cc: linux-kernel@...r.kernel.org, rust-for-linux@...r.kernel.org,
Matthew Maurer <mmaurer@...gle.com>
Subject: [PATCH 3/8] rust: debugfs: Add scoped builder interface
This adds an interface which allows access to references which may not
live indefinitely by forcing them to live at least as long as the
DebugFS directory itself.
Signed-off-by: Matthew Maurer <mmaurer@...gle.com>
---
rust/kernel/debugfs.rs | 163 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 163 insertions(+)
diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index b20df5fce692b3047c804f7ad5af90fc4248979b..f6240fd056f8598d5ef06bdaf61c5c33eab5b734 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -12,7 +12,12 @@
use crate::str::CStr;
use crate::types::{ARef, AlwaysRefCounted, Opaque};
use core::fmt::Display;
+use core::marker::{PhantomData, PhantomPinned};
+use core::mem::ManuallyDrop;
+use core::ops::Deref;
+use core::pin::Pin;
use core::ptr::NonNull;
+use pin_init::{pin_data, pinned_drop, PinInit};
/// Handle to a DebugFS directory.
pub struct Dir {
@@ -117,6 +122,22 @@ pub fn display_file<T: Display + Sized>(
&self,
name: &CStr,
data: &'static T,
+ ) -> Result<ARef<Self>> {
+ // SAFETY: As `data` lives for the static lifetime, it outlives the file.
+ unsafe { self.display_file_raw(name, data) }
+ }
+
+ /// 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<T: Display + Sized>(
+ &self,
+ name: &CStr,
+ data: *const T,
) -> Result<ARef<Self>> {
// SAFETY:
// * `name` is a NUL-terminated C string, living across the call, by CStr invariant
@@ -192,3 +213,145 @@ trait DisplayFile: Display + Sized {
}
impl<T: Display + Sized> DisplayFile for T {}
+
+#[pin_data(PinnedDrop)]
+/// A DebugFS directory combined with a backing store for data to implement it
+pub struct Values<T> {
+ #[pin]
+ backing: T,
+ // Calling `debugfs_remove`, as we will do in our `Drop` impl, will consume the refcount we
+ // hold after cleaning up all the child directories.
+ dir: ManuallyDrop<ARef<Dir>>,
+ // Since the files present under our directory may point into backing, we are !Unpin
+ #[pin]
+ _pin: PhantomPinned,
+}
+
+impl<T> Deref for Values<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.backing
+ }
+}
+
+impl<T> Values<T> {
+ /// Attach backing data to a DebugFS directory. When the resulting object is destroyed, the
+ /// DebugFS directory will be recursively removed as well.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use kernel::c_str;
+ /// # use kernel::debugfs::{Dir, Values};
+ /// let dir = Dir::new(c_str!("foo"), None)?;
+ /// let _foo = KBox::pin_init(Values::attach(0, dir), GFP_KERNEL)?;
+ /// // foo can now be used with `Values::build` to allow access to the attached value when
+ /// // printing
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn attach(backing: impl PinInit<T>, dir: ARef<Dir>) -> impl PinInit<Self> {
+ pin_init::pin_init! { Self {
+ backing <- backing,
+ dir: ManuallyDrop::new(dir),
+ _pin: PhantomPinned,
+ } }
+ }
+
+ /// Runs a closure which has access to the backing data and a builder that will allow you to
+ /// build a DebugFS structure off the backing data using its methods.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use kernel::c_str;
+ /// # use kernel::debugfs::{Dir, Values};
+ /// let dir = Dir::new(c_str!("foo"), None)?;
+ /// let foo = KBox::pin_init(Values::attach(0, dir), GFP_KERNEL)?;
+ /// foo.as_ref().build(|_value, _builder| {
+ /// // Construct debugfs with access to the attached value here
+ /// });
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn build<U, F: for<'a> FnOnce(&'a T, Builder<'a>) -> U>(self: Pin<&Self>, f: F) -> U {
+ // SAFETY: The Builder produced here is technically at the lifetime of self, but is
+ // being used only in a universal context, so that information is immediately erased and
+ // replaced with the universally quantified 'a. By taking a Pin<&Self>, we enforce that
+ // self.backing remains alive for any 'a less than the lifetime of the struct. By not
+ // providing any mutable access to self.backing, we ensure that it's always safe to
+ // materialize a read-only reference to &self.backing for any 'a less than the lifetime of
+ // the struct.
+ f(&self.backing, unsafe { Builder::new(&self.dir) })
+ }
+}
+
+#[pinned_drop]
+impl<T> PinnedDrop for Values<T> {
+ fn drop(self: Pin<&mut Self>) {
+ // SAFETY: Our internal dir holds its own reference count, so it's always valid.
+ unsafe { kernel::bindings::debugfs_remove(self.dir.as_ptr()) }
+ }
+}
+
+/// A Dir, scoped to the lifetime for which it will exist. Unlike `&'a Dir`, this is equivariant,
+/// preventing the shortening of the lifetime.
+///
+/// # Invariants
+/// Builder will only ever be used with 'static or a universally quantified lifetime that is
+/// unified only with the lifetime of data structures guaranteed to outlive it and not have mutable
+/// references taken.
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+pub struct Builder<'a> {
+ inner: &'a Dir,
+ _equivariant: PhantomData<fn(&'a ()) -> &'a ()>,
+}
+
+impl<'a> Builder<'a> {
+ /// # Safety
+ /// Caller must promise to use this function at static lifetime or only expose it to
+ /// universally quantified functions, unified only with lifetimes guaranteed to extend beyond
+ /// when the directory will be rendered inaccessible.
+ unsafe fn new(inner: &'a Dir) -> Self {
+ Self {
+ inner,
+ _equivariant: PhantomData,
+ }
+ }
+
+ /// Create a file in a DebugFS directory with the provided name, and contents from invoking
+ /// [`Display::fmt`] on the provided reference.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use kernel::{try_pin_init, c_str};
+ /// # use pin_init::stack_try_pin_init;
+ /// # use kernel::debugfs::{Dir, Values};
+ /// let dir = Dir::new(c_str!("foo"), None)?;
+ /// stack_try_pin_init!(let foo =? Values::attach((1, 2), dir));
+ /// foo.as_ref().build(|value, builder| {
+ /// builder.display_file(c_str!("bar"), &value.0)?;
+ /// builder.display_file(c_str!("baz"), &value.1)
+ /// })?;
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn display_file<T: Display + Sized>(&self, name: &CStr, data: &'a T) -> Result<()> {
+ // We forget the reference because its reference count is implicitly "owned" by the root
+ // builder, which we know will use `debugfs_remove` to clean this up. If we release the
+ // file here, it will be immediately deleted.
+ // SAFETY:
+ // Because `Builder`'s invariant says that our lifetime is how long the directory will
+ // be available, and is equivariant, `'a` will outlive the base directory, which will be
+ // torn down by `debugfs_remove` to prevent access even if an extra refcount is held
+ // somewhere.
+ core::mem::forget(unsafe { self.inner.display_file_raw(name, data)? });
+ Ok(())
+ }
+}
+
+impl<'a> Deref for Builder<'a> {
+ type Target = Dir;
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
--
2.49.0.901.g37484f566f-goog
Powered by blists - more mailing lists