Add SeqShow trait for seq_file-backed debugfs files. Needed by DRM and other subsystems to expose readable debugfs state via seq_file.
Signed-off-by: Alvin Sun <[email protected]> --- rust/kernel/debugfs/file_ops.rs | 60 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/debugfs/traits.rs | 11 ++++++++ 2 files changed, 71 insertions(+) diff --git a/rust/kernel/debugfs/file_ops.rs b/rust/kernel/debugfs/file_ops.rs index f15908f71c4a2..ee8c85360e924 100644 --- a/rust/kernel/debugfs/file_ops.rs +++ b/rust/kernel/debugfs/file_ops.rs @@ -5,11 +5,13 @@ BinaryReader, BinaryWriter, Reader, + SeqShow, Writer, // }; use crate::{ debugfs::callback_adapters::Adapter, + error::from_result, fmt, fs::file, prelude::*, @@ -123,6 +125,64 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 0 } +/// Show callback for `SeqShow` types. +/// +/// # Safety +/// +/// The seq_file core guarantees that `seq` points to a live `seq_file` +/// whose private data is a valid pointer to a `T` with no outstanding +/// mutable references. +unsafe extern "C" fn seq_file_show<S: SeqShow<T>, T: Sync>( + seq: *mut bindings::seq_file, + _: *mut c_void, +) -> c_int { + // SAFETY: `seq->private` is a valid `T` pointer with no outstanding + // mutable references. + let data = unsafe { &*((*seq).private.cast::<T>()) }; + // SAFETY: `seq` points to a live `seq_file`. + let m = unsafe { SeqFile::from_raw(seq) }; + from_result(|| S::show(data, m).map(|()| 0)) +} + +/// Open callback for `SeqShow` types. +/// +/// # Safety +/// +/// The VFS guarantees that `inode` is valid with `i_private` pointing to a +/// valid `T` that remains valid for the duration of the call, and that `file` +/// points to a live, uninitialized file object. +unsafe extern "C" fn seq_file_open<S: SeqShow<T>, T: Sync>( + inode: *mut bindings::inode, + file: *mut bindings::file, +) -> c_int { + // SAFETY: `inode` is valid per VFS. `i_private` points to a valid `T` + // (guaranteed by the `FileOps<T>` invariants). + let data = unsafe { (*inode).i_private.cast::<T>() }; + + // SAFETY: `file` is valid per VFS; `data` matches `seq_file_show`'s contract. + unsafe { bindings::single_open(file, Some(seq_file_show::<S, T>), data.cast()) } +} + +pub(crate) trait SeqReadFile<T> { + const FILE_OPS: FileOps<T>; +} + +impl<S: SeqShow<T>, T: Sync> SeqReadFile<T> for S { + const FILE_OPS: FileOps<T> = { + let operations = bindings::file_operations { + read: Some(bindings::seq_read), + llseek: Some(bindings::seq_lseek), + release: Some(bindings::single_release), + open: Some(seq_file_open::<Self, T>), + ..pin_init::zeroed() + }; + // SAFETY: `read` and `llseek` are stock `seq_file` implementations. + // `seq_file_open` treats `inode->i_private` as a valid `&T` reference, + // satisfying the `FileOps::new` contract. + unsafe { FileOps::new(operations, 0o400) } + }; +} + // Work around lack of generic const items. pub(crate) trait ReadFile<T> { const FILE_OPS: FileOps<T>; diff --git a/rust/kernel/debugfs/traits.rs b/rust/kernel/debugfs/traits.rs index 8c39524b6a990..02782cd817c4b 100644 --- a/rust/kernel/debugfs/traits.rs +++ b/rust/kernel/debugfs/traits.rs @@ -8,6 +8,7 @@ fmt, fs::file, prelude::*, + seq_file::SeqFile, sync::{ atomic::{ Atomic, @@ -338,3 +339,13 @@ fn read_from_slice( self.deref().read_from_slice(reader, offset) } } + +/// Renders `data` into a seq_file. +/// +/// `data` is the value stashed as the debugfs file's `i_private` at creation +/// time. `show` is invoked on each read to produce the file's contents. +/// `data` must remain valid while the debugfs file is registered. +pub trait SeqShow<T> { + /// Writes debugfs output for the file. + fn show(data: &T, m: &SeqFile) -> Result; +} -- 2.43.0
