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 3/5] rust: debugfs: Support `PinInit` backing for `File`s.
Date: Wed, 09 Jul 2025 19:09:30 +0000 [thread overview]
Message-ID: <20250709-debugfs-rust-v9-3-92b9eab5a951@google.com> (raw)
In-Reply-To: <20250709-debugfs-rust-v9-0-92b9eab5a951@google.com>
This allows `File`s to own their data, allowing DebugFS files to be
managed in sync with the data that backs them. Because DebugFS files are
intended to actually own data and provide access, `File`s still maintain
the same lifecycle for provided data when `CONFIG_DEBUG_FS` is disabled.
Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
rust/kernel/debugfs.rs | 149 ++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 117 insertions(+), 32 deletions(-)
diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index e5b6497d1deb67671d22ffd90cd5baa855bb9257..a1a84dd309216f455ae8fe3d3c0fd00f957f82a9 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -5,12 +5,13 @@
//!
//! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h)
-#[cfg(CONFIG_DEBUG_FS)]
-use crate::prelude::GFP_KERNEL;
+use crate::prelude::*;
use crate::str::CStr;
#[cfg(CONFIG_DEBUG_FS)]
use crate::sync::Arc;
use core::fmt::Display;
+use core::marker::PhantomPinned;
+use core::ops::Deref;
#[cfg(CONFIG_DEBUG_FS)]
mod display_file;
@@ -63,40 +64,78 @@ fn create(_name: &CStr, _parent: Option<&Dir>) -> Self {
}
#[cfg(CONFIG_DEBUG_FS)]
- fn create_file<T: Display + Sized + Sync>(&self, name: &CStr, data: &'static T) -> File {
- let Some(parent) = &self.0 else {
- return File {
+ /// Creates a DebugFS file which will own the data produced by the initializer provided in
+ /// `data`.
+ ///
+ /// # Safety
+ ///
+ /// The provided vtable must be appropriate for implementing a seq_file if provided
+ /// with a private data pointer which provides shared access to a `T`.
+ unsafe fn create_file<'a, T: Sync, E, TI: PinInit<T, E>>(
+ &self,
+ name: &'a CStr,
+ data: TI,
+ vtable: &'static bindings::file_operations,
+ ) -> impl PinInit<File<T>, E> + use<'_, 'a, T, E, TI> {
+ try_pin_init! {
+ File {
_entry: Entry::empty(),
+ data <- data,
+ _pin: PhantomPinned,
+ } ? E
+ }
+ .pin_chain(|file| {
+ let Some(parent) = &self.0 else {
+ return Ok(());
};
- };
- // SAFETY:
- // * `name` is a NUL-terminated C string, living across the call, by `CStr` invariant.
- // * `parent` is a live `dentry` since we have a reference to it.
- // * `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 `T` that will outlive it, which we know because
- // we have a static reference.
- let ptr = unsafe {
- bindings::debugfs_create_file_full(
- name.as_char_ptr(),
- 0o444,
- parent.as_ptr(),
- data as *const _ as *mut _,
- core::ptr::null(),
- &<T as display_file::DisplayFile>::VTABLE,
- )
- };
- // SAFETY: `debugfs_create_file_full` either returns an error code or a legal
- // dentry pointer, so `Entry::new` is safe to call here.
- let entry = unsafe { Entry::new(ptr, Some(parent.clone())) };
+ // SAFETY:
+ // * `name` is a NUL-terminated C string, living across the call, by `CStr` invariant.
+ // * `parent` is a live `dentry` since we have a reference to it.
+ // * Since the file owns the `T` and it is pinned, we can safely assume the pointer
+ // lives and is valid as long as we are.
+ // * Since the `Entry` will live in the `File`, it will be dropped before the pointer
+ // is invalidated. Dropping the `Entry` will remove the DebugFS file and avoid
+ // further access.
+ let ptr = unsafe {
+ bindings::debugfs_create_file_full(
+ name.as_char_ptr(),
+ 0o444,
+ parent.as_ptr(),
+ &file.data as *const _ as *mut c_void,
+ core::ptr::null(),
+ vtable,
+ )
+ };
+
+ // SAFETY: `debugfs_create_file_full` either returns an error code or a legal
+ // dentry pointer, so `Entry::new` is safe to call here.
+ *file.entry_mut() = unsafe { Entry::new(ptr, Some(parent.clone())) };
- File { _entry: entry }
+ Ok(())
+ })
}
#[cfg(not(CONFIG_DEBUG_FS))]
- fn create_file<T: Display + Sized + Sync>(&self, _name: &CStr, _data: &'static T) -> File {
- File {}
+ /// Creates a DebugFS file which will own the data produced by the initializer provided in
+ /// `data`.
+ ///
+ /// # Safety
+ ///
+ /// As DebugFS is disabled, this is actually entirely safe. It is marked unsafe for code
+ /// compatibility with the DebugFS-enabled variant.
+ unsafe fn create_file<'a, T: Sync, E, TI: PinInit<T, E>>(
+ &self,
+ _name: &'a CStr,
+ data: TI,
+ _vtable: (),
+ ) -> impl PinInit<File<T>, E> + use<'_, 'a, T, E, TI> {
+ try_pin_init! {
+ File {
+ data <- data,
+ _pin: PhantomPinned,
+ } ? E
+ }
}
/// Create a DebugFS subdirectory.
@@ -127,8 +166,32 @@ pub fn subdir(&self, name: &CStr) -> Self {
/// dir.display_file(c_str!("foo"), &200);
/// // "my_debugfs_dir/foo" now contains the number 200.
/// ```
- pub fn display_file<T: Display + Sized + Sync>(&self, name: &CStr, data: &'static T) -> File {
- self.create_file(name, data)
+ ///
+ /// ```
+ /// # use kernel::c_str;
+ /// # use kernel::debugfs::Dir;
+ /// # use kernel::prelude::*;
+ /// let val = KBox::new(300, GFP_KERNEL)?;
+ /// let dir = Dir::new(c_str!("my_debugfs_dir"));
+ /// dir.display_file(c_str!("foo"), val);
+ /// // "my_debugfs_dir/foo" now contains the number 300.
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn display_file<'b, T: Display + Send + Sync, E, TI: PinInit<T, E>>(
+ &self,
+ name: &'b CStr,
+ data: TI,
+ ) -> impl PinInit<File<T>, E> + use<'_, 'b, T, E, TI> {
+ #[cfg(CONFIG_DEBUG_FS)]
+ let vtable = &<T 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 `T` that will outlive it, which is provided by
+ // `create_file`'s safety requirements.
+ unsafe { self.create_file(name, data, vtable) }
}
/// Create a new directory in DebugFS at the root.
@@ -146,7 +209,29 @@ pub fn new(name: &CStr) -> Self {
}
/// Handle to a DebugFS file.
-pub struct File {
+#[pin_data]
+pub struct File<T> {
+ // This order is load-bearing for drops - `_entry` must be dropped before `data`.
#[cfg(CONFIG_DEBUG_FS)]
_entry: Entry,
+ #[pin]
+ data: T,
+ // Even if `T` is `Unpin`, we still can't allow it to be moved.
+ #[pin]
+ _pin: PhantomPinned,
+}
+
+#[cfg(CONFIG_DEBUG_FS)]
+impl<T> File<T> {
+ fn entry_mut(self: Pin<&mut Self>) -> &mut Entry {
+ // SAFETY: _entry is not structurally pinned
+ unsafe { &mut Pin::into_inner_unchecked(self)._entry }
+ }
+}
+
+impl<T> Deref for File<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.data
+ }
}
--
2.50.0.727.gbf7dc18ff4-goog
next prev 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 ` Matthew Maurer [this message]
2025-08-19 5:51 ` [PATCH v9 3/5] rust: debugfs: Support `PinInit` backing for `File`s 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 ` [PATCH v9 4/5] rust: debugfs: Support format hooks Matthew Maurer
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-3-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.