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
