summaryrefslogtreecommitdiff
path: root/rust/kernel/debugfs.rs
diff options
context:
space:
mode:
authorMatthew Maurer <mmaurer@google.com>2025-09-04 21:13:55 +0000
committerDanilo Krummrich <dakr@kernel.org>2025-09-10 18:58:21 +0200
commit40ecc49466c8b7f9518c5fbbcfb24cb7e26c36c7 (patch)
tree6cf8fe2e5cfcd1c7c4e2d93df75cfb110b44586a /rust/kernel/debugfs.rs
parent839dc1d15b9ba5318ed145b20efffcfa91c02a3d (diff)
rust: debugfs: Add support for callback-based files
Extends the `debugfs` API to support creating files with content generated and updated by callbacks. This is done via the `read_callback_file`, `write_callback_file`, and `read_write_callback_file` methods. These methods allow for more flexible file definition, either because the type already has a `Writer` or `Reader` method that doesn't do what you'd like, or because you cannot implement it (e.g. because it's a type defined in another crate or a primitive type). Signed-off-by: Matthew Maurer <mmaurer@google.com> Tested-by: Dirk Behme <dirk.behme@de.bosch.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Link: https://lore.kernel.org/r/20250904-debugfs-rust-v11-4-7d12a165685a@google.com [ Fix up Result<(), Error> -> Result. - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Diffstat (limited to 'rust/kernel/debugfs.rs')
-rw-r--r--rust/kernel/debugfs.rs89
1 files changed, 89 insertions, 0 deletions
diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index 5d77e0cd393c..34c991d138a7 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -12,12 +12,16 @@ use crate::prelude::*;
use crate::str::CStr;
#[cfg(CONFIG_DEBUG_FS)]
use crate::sync::Arc;
+use crate::uaccess::UserSliceReader;
+use core::fmt;
use core::marker::PhantomPinned;
use core::ops::Deref;
mod traits;
pub use traits::{Reader, Writer};
+mod callback_adapters;
+use callback_adapters::{FormatAdapter, NoWriter, WritableAdapter};
mod file_ops;
use file_ops::{FileOps, ReadFile, ReadWriteFile, WriteFile};
#[cfg(CONFIG_DEBUG_FS)]
@@ -143,6 +147,46 @@ impl Dir {
self.create_file(name, data, file_ops)
}
+ /// Creates a read-only file in this directory, with contents from a callback.
+ ///
+ /// `f` must be a function item or a non-capturing closure.
+ /// This is statically asserted and not a safety requirement.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use core::sync::atomic::{AtomicU32, Ordering};
+ /// # use kernel::c_str;
+ /// # use kernel::debugfs::Dir;
+ /// # use kernel::prelude::*;
+ /// # let dir = Dir::new(c_str!("foo"));
+ /// let file = KBox::pin_init(
+ /// dir.read_callback_file(c_str!("bar"),
+ /// AtomicU32::new(3),
+ /// &|val, f| {
+ /// let out = val.load(Ordering::Relaxed);
+ /// writeln!(f, "{out:#010x}")
+ /// }),
+ /// GFP_KERNEL)?;
+ /// // Reading "foo/bar" will show "0x00000003".
+ /// file.store(10, Ordering::Relaxed);
+ /// // Reading "foo/bar" will now show "0x0000000a".
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn read_callback_file<'a, T, E: 'a, F>(
+ &'a self,
+ name: &'a CStr,
+ data: impl PinInit<T, E> + 'a,
+ _f: &'static F,
+ ) -> impl PinInit<File<T>, E> + 'a
+ where
+ T: Send + Sync + 'static,
+ F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
+ {
+ let file_ops = <FormatAdapter<T, F>>::FILE_OPS.adapt();
+ self.create_file(name, data, file_ops)
+ }
+
/// Creates a read-write file in this directory.
///
/// Reading the file uses the [`Writer`] implementation.
@@ -159,6 +203,31 @@ impl Dir {
self.create_file(name, data, file_ops)
}
+ /// Creates a read-write file in this directory, with logic from callbacks.
+ ///
+ /// Reading from the file is handled by `f`. Writing to the file is handled by `w`.
+ ///
+ /// `f` and `w` must be function items or non-capturing closures.
+ /// This is statically asserted and not a safety requirement.
+ pub fn read_write_callback_file<'a, T, E: 'a, F, W>(
+ &'a self,
+ name: &'a CStr,
+ data: impl PinInit<T, E> + 'a,
+ _f: &'static F,
+ _w: &'static W,
+ ) -> impl PinInit<File<T>, E> + 'a
+ where
+ T: Send + Sync + 'static,
+ F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
+ W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
+ {
+ let file_ops =
+ <WritableAdapter<FormatAdapter<T, F>, W> as file_ops::ReadWriteFile<_>>::FILE_OPS
+ .adapt()
+ .adapt();
+ self.create_file(name, data, file_ops)
+ }
+
/// Creates a write-only file in this directory.
///
/// The file owns its backing data. Writing to the file uses the [`Reader`]
@@ -175,6 +244,26 @@ impl Dir {
{
self.create_file(name, data, &T::FILE_OPS)
}
+
+ /// Creates a write-only file in this directory, with write logic from a callback.
+ ///
+ /// `w` must be a function item or a non-capturing closure.
+ /// This is statically asserted and not a safety requirement.
+ pub fn write_callback_file<'a, T, E: 'a, W>(
+ &'a self,
+ name: &'a CStr,
+ data: impl PinInit<T, E> + 'a,
+ _w: &'static W,
+ ) -> impl PinInit<File<T>, E> + 'a
+ where
+ T: Send + Sync + 'static,
+ W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
+ {
+ let file_ops = <WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS
+ .adapt()
+ .adapt();
+ self.create_file(name, data, file_ops)
+ }
}
#[pin_data]