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@google.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
On Tue, Apr 29, 2025 at 11:15:57PM +0000, Matthew Maurer wrote:
> 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.
They can ONLY live as long as the debugfs directory lives, is that what
you mean here?
>
> Signed-off-by: Matthew Maurer <mmaurer@google.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`
Perhaps the refcount should be the thing driving debugfs_remove()? That
might save you lots of mess overall I think.
thanks,
greg k-h
© 2016 - 2026 Red Hat, Inc.