[PATCH v3 3/8] rust: debugfs: add ScopeRef for existing dentries

Alvin Sun <[email protected]>
Newsgroups org.kernel.vger.rust-for-linux,dev.linux.lists.driver-core,org.freedesktop.lists.dri-devel,org.kernel.feeds.b4-sent
Message-ID <[email protected]>
Add methods to construct debugfs abstractions from raw C dentry
pointers. Needed by DRM debugfs_init callback to create ScopedDir
from an existing dentry.

Add ScopeRef<'a, T>, a debugfs directory handle that carries a
reference to associated data of type T.

Signed-off-by: Alvin Sun <[email protected]>
---
 rust/kernel/debugfs.rs       | 84 ++++++++++++++++++++++++++++++++++++++++++--
 rust/kernel/debugfs/entry.rs | 15 +++++++-
 2 files changed, 96 insertions(+), 3 deletions(-)

diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index d7b8014a64746..831d6750a34ba 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -24,7 +24,7 @@
         PhantomData,
         PhantomPinned, //
     },
-    ops::Deref,
+    ops::Deref, //
 };
 
 mod traits;
@@ -33,6 +33,7 @@
     BinaryReaderMut,
     BinaryWriter,
     Reader,
+    SeqShow,
     Writer, //
 };
 
@@ -51,6 +52,7 @@
     FileOps,
     ReadFile,
     ReadWriteFile,
+    SeqReadFile,
     WriteFile, //
 };
 
@@ -538,7 +540,7 @@ pub fn dir<'dir2>(&'dir2 self, name: &CStr) -> ScopedDir<'data, 'dir2> {
         }
     }
 
-    fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &'static FileOps<T>) {
+    fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &FileOps<T>) {
         #[cfg(CONFIG_DEBUG_FS)]
         core::mem::forget(Entry::file(name, &self.entry, data, vtable));
     }
@@ -588,6 +590,14 @@ pub fn read_callback_file<T, F>(&self, name: &CStr, data: &'data T, _f: &'static
         self.create_file(name, data, vtable)
     }
 
+    /// Creates a seq_file debugfs file in this directory.
+    ///
+    /// The file's contents are produced by invoking [`SeqShow::show`] with
+    /// `data` on each read.
+    pub fn seq_file<S: SeqShow<U>, U: Sync>(&self, name: &CStr, data: &'data U) {
+        self.create_file(name, data, &<S as SeqReadFile<U>>::FILE_OPS)
+    }
+
     /// Creates a read-write file in this directory.
     ///
     /// Reading the file uses the [`Writer`] implementation on `data`. Writing to the file uses
@@ -721,4 +731,74 @@ fn new(name: &CStr) -> ScopedDir<'data, 'static> {
             _phantom: PhantomData,
         }
     }
+
+    /// Creates a [`ScopedDir`] wrapping an existing debugfs dentry.
+    ///
+    /// Files created under this directory are not automatically removed on drop;
+    /// their lifetime is tied to the dentry owner.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure the dentry remains valid for the lifetime of the
+    /// returned `ScopedDir`.
+    pub unsafe fn from_dentry(dentry: *mut bindings::dentry) -> Self {
+        let _ = dentry;
+        ScopedDir {
+            #[cfg(CONFIG_DEBUG_FS)]
+            // SAFETY: The caller guarantees the dentry is valid and outlives this `ScopedDir`.
+            entry: ManuallyDrop::new(unsafe { Entry::from_raw(dentry) }),
+            _phantom: PhantomData,
+        }
+    }
+}
+
+/// A reference to a debugfs directory that also holds a reference to
+/// associated data of type `T`.
+///
+/// Created from an existing debugfs dentry (e.g. the DRM debugfs root).
+/// `data` must remain valid while the debugfs files created under this
+/// scope may be accessed.
+pub struct ScopeRef<'a, T> {
+    #[cfg(CONFIG_DEBUG_FS)]
+    inner: ScopedDir<'a, 'a>,
+    data: &'a T,
+}
+
+impl<'a, T> ScopeRef<'a, T> {
+    /// Creates a [`ScopeRef`] from an existing debugfs dentry and a data reference.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that `dentry` remains valid for the lifetime of
+    /// the returned [`ScopeRef`] and that `data` remains valid while the
+    /// debugfs files created under this scope may be accessed.
+    pub unsafe fn new(dentry: *mut bindings::dentry, data: &'a T) -> Self {
+        let _ = dentry;
+        ScopeRef {
+            #[cfg(CONFIG_DEBUG_FS)]
+            // SAFETY: By the safety preconditions of `new`, `dentry` is valid
+            // and remains valid for the lifetime of the returned `ScopeRef`.
+            inner: unsafe { ScopedDir::from_dentry(dentry) },
+            data,
+        }
+    }
+
+    /// Creates a seq_file debugfs file in this directory.
+    ///
+    /// The file's contents are produced by invoking [`SeqShow::show`] with
+    /// `data` on each read.
+    #[cfg(CONFIG_DEBUG_FS)]
+    pub fn seq_file<S: SeqShow<T>>(&self, name: &CStr)
+    where
+        T: Sync,
+    {
+        self.inner.seq_file::<S, T>(name, self.data);
+    }
+
+    #[cfg(not(CONFIG_DEBUG_FS))]
+    pub fn seq_file<S: SeqShow<T>>(&self, _name: &CStr)
+    where
+        T: Sync,
+    {
+    }
 }
diff --git a/rust/kernel/debugfs/entry.rs b/rust/kernel/debugfs/entry.rs
index 46aad64896ecb..7643ff0fa6093 100644
--- a/rust/kernel/debugfs/entry.rs
+++ b/rust/kernel/debugfs/entry.rs
@@ -8,7 +8,7 @@
         CStr,
         CStrExt as _, //
     },
-    sync::Arc,
+    sync::Arc, //
 };
 
 use core::marker::PhantomData;
@@ -87,6 +87,19 @@ pub(crate) unsafe fn dynamic_file<T>(
 }
 
 impl<'a> Entry<'a> {
+    /// Wraps a raw dentry pointer.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure the dentry is valid and outlives this `Entry`.
+    pub(crate) unsafe fn from_raw(entry: *mut bindings::dentry) -> Self {
+        Self {
+            entry,
+            _parent: None,
+            _phantom: PhantomData,
+        }
+    }
+
     pub(crate) fn dir(name: &CStr, parent: Option<&'a Entry<'_>>) -> Self {
         let parent_ptr = match &parent {
             Some(entry) => entry.as_ptr(),

-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.