xref: /linux/rust/kernel/debugfs.rs (revision 839dc1d15b9ba5318ed145b20efffcfa91c02a3d)
17f201ca1SMatthew Maurer // SPDX-License-Identifier: GPL-2.0
27f201ca1SMatthew Maurer // Copyright (C) 2025 Google LLC.
37f201ca1SMatthew Maurer 
47f201ca1SMatthew Maurer //! DebugFS Abstraction
57f201ca1SMatthew Maurer //!
67f201ca1SMatthew Maurer //! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h)
77f201ca1SMatthew Maurer 
87f201ca1SMatthew Maurer // When DebugFS is disabled, many parameters are dead. Linting for this isn't helpful.
97f201ca1SMatthew Maurer #![cfg_attr(not(CONFIG_DEBUG_FS), allow(unused_variables))]
107f201ca1SMatthew Maurer 
117f201ca1SMatthew Maurer use crate::prelude::*;
127f201ca1SMatthew Maurer use crate::str::CStr;
137f201ca1SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)]
147f201ca1SMatthew Maurer use crate::sync::Arc;
155e40b591SMatthew Maurer use core::marker::PhantomPinned;
165e40b591SMatthew Maurer use core::ops::Deref;
177f201ca1SMatthew Maurer 
185e40b591SMatthew Maurer mod traits;
19*839dc1d1SMatthew Maurer pub use traits::{Reader, Writer};
205e40b591SMatthew Maurer 
215e40b591SMatthew Maurer mod file_ops;
22*839dc1d1SMatthew Maurer use file_ops::{FileOps, ReadFile, ReadWriteFile, WriteFile};
237f201ca1SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)]
247f201ca1SMatthew Maurer mod entry;
257f201ca1SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)]
267f201ca1SMatthew Maurer use entry::Entry;
277f201ca1SMatthew Maurer 
287f201ca1SMatthew Maurer /// Owning handle to a DebugFS directory.
297f201ca1SMatthew Maurer ///
307f201ca1SMatthew Maurer /// The directory in the filesystem represented by [`Dir`] will be removed when handle has been
317f201ca1SMatthew Maurer /// dropped *and* all children have been removed.
327f201ca1SMatthew Maurer // If we have a parent, we hold a reference to it in the `Entry`. This prevents the `dentry`
337f201ca1SMatthew Maurer // we point to from being cleaned up if our parent `Dir`/`Entry` is dropped before us.
347f201ca1SMatthew Maurer //
357f201ca1SMatthew Maurer // The `None` option indicates that the `Arc` could not be allocated, so our children would not be
367f201ca1SMatthew Maurer // able to refer to us. In this case, we need to silently fail. All future child directories/files
377f201ca1SMatthew Maurer // will silently fail as well.
387f201ca1SMatthew Maurer #[derive(Clone)]
397f201ca1SMatthew Maurer pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry>>);
407f201ca1SMatthew Maurer 
417f201ca1SMatthew Maurer impl Dir {
427f201ca1SMatthew Maurer     /// Create a new directory in DebugFS. If `parent` is [`None`], it will be created at the root.
437f201ca1SMatthew Maurer     fn create(name: &CStr, parent: Option<&Dir>) -> Self {
447f201ca1SMatthew Maurer         #[cfg(CONFIG_DEBUG_FS)]
457f201ca1SMatthew Maurer         {
467f201ca1SMatthew Maurer             let parent_entry = match parent {
477f201ca1SMatthew Maurer                 // If the parent couldn't be allocated, just early-return
487f201ca1SMatthew Maurer                 Some(Dir(None)) => return Self(None),
497f201ca1SMatthew Maurer                 Some(Dir(Some(entry))) => Some(entry.clone()),
507f201ca1SMatthew Maurer                 None => None,
517f201ca1SMatthew Maurer             };
527f201ca1SMatthew Maurer             Self(
537f201ca1SMatthew Maurer                 // If Arc creation fails, the `Entry` will be dropped, so the directory will be
547f201ca1SMatthew Maurer                 // cleaned up.
557f201ca1SMatthew Maurer                 Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(),
567f201ca1SMatthew Maurer             )
577f201ca1SMatthew Maurer         }
587f201ca1SMatthew Maurer         #[cfg(not(CONFIG_DEBUG_FS))]
597f201ca1SMatthew Maurer         Self()
607f201ca1SMatthew Maurer     }
617f201ca1SMatthew Maurer 
625e40b591SMatthew Maurer     /// Creates a DebugFS file which will own the data produced by the initializer provided in
635e40b591SMatthew Maurer     /// `data`.
645e40b591SMatthew Maurer     fn create_file<'a, T, E: 'a>(
655e40b591SMatthew Maurer         &'a self,
665e40b591SMatthew Maurer         name: &'a CStr,
675e40b591SMatthew Maurer         data: impl PinInit<T, E> + 'a,
685e40b591SMatthew Maurer         file_ops: &'static FileOps<T>,
695e40b591SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
705e40b591SMatthew Maurer     where
715e40b591SMatthew Maurer         T: Sync + 'static,
725e40b591SMatthew Maurer     {
735e40b591SMatthew Maurer         let scope = Scope::<T>::new(data, move |data| {
745e40b591SMatthew Maurer             #[cfg(CONFIG_DEBUG_FS)]
755e40b591SMatthew Maurer             if let Some(parent) = &self.0 {
765e40b591SMatthew Maurer                 // SAFETY: Because data derives from a scope, and our entry will be dropped before
775e40b591SMatthew Maurer                 // the data is dropped, it is guaranteed to outlive the entry we return.
785e40b591SMatthew Maurer                 unsafe { Entry::dynamic_file(name, parent.clone(), data, file_ops) }
795e40b591SMatthew Maurer             } else {
805e40b591SMatthew Maurer                 Entry::empty()
815e40b591SMatthew Maurer             }
825e40b591SMatthew Maurer         });
835e40b591SMatthew Maurer         try_pin_init! {
845e40b591SMatthew Maurer             File {
855e40b591SMatthew Maurer                 scope <- scope
865e40b591SMatthew Maurer             } ? E
875e40b591SMatthew Maurer         }
885e40b591SMatthew Maurer     }
895e40b591SMatthew Maurer 
907f201ca1SMatthew Maurer     /// Create a new directory in DebugFS at the root.
917f201ca1SMatthew Maurer     ///
927f201ca1SMatthew Maurer     /// # Examples
937f201ca1SMatthew Maurer     ///
947f201ca1SMatthew Maurer     /// ```
957f201ca1SMatthew Maurer     /// # use kernel::c_str;
967f201ca1SMatthew Maurer     /// # use kernel::debugfs::Dir;
977f201ca1SMatthew Maurer     /// let debugfs = Dir::new(c_str!("parent"));
987f201ca1SMatthew Maurer     /// ```
997f201ca1SMatthew Maurer     pub fn new(name: &CStr) -> Self {
1007f201ca1SMatthew Maurer         Dir::create(name, None)
1017f201ca1SMatthew Maurer     }
1027f201ca1SMatthew Maurer 
1037f201ca1SMatthew Maurer     /// Creates a subdirectory within this directory.
1047f201ca1SMatthew Maurer     ///
1057f201ca1SMatthew Maurer     /// # Examples
1067f201ca1SMatthew Maurer     ///
1077f201ca1SMatthew Maurer     /// ```
1087f201ca1SMatthew Maurer     /// # use kernel::c_str;
1097f201ca1SMatthew Maurer     /// # use kernel::debugfs::Dir;
1107f201ca1SMatthew Maurer     /// let parent = Dir::new(c_str!("parent"));
1117f201ca1SMatthew Maurer     /// let child = parent.subdir(c_str!("child"));
1127f201ca1SMatthew Maurer     /// ```
1137f201ca1SMatthew Maurer     pub fn subdir(&self, name: &CStr) -> Self {
1147f201ca1SMatthew Maurer         Dir::create(name, Some(self))
1157f201ca1SMatthew Maurer     }
1165e40b591SMatthew Maurer 
1175e40b591SMatthew Maurer     /// Creates a read-only file in this directory.
1185e40b591SMatthew Maurer     ///
1195e40b591SMatthew Maurer     /// The file's contents are produced by invoking [`Writer::write`] on the value initialized by
1205e40b591SMatthew Maurer     /// `data`.
1215e40b591SMatthew Maurer     ///
1225e40b591SMatthew Maurer     /// # Examples
1235e40b591SMatthew Maurer     ///
1245e40b591SMatthew Maurer     /// ```
1255e40b591SMatthew Maurer     /// # use kernel::c_str;
1265e40b591SMatthew Maurer     /// # use kernel::debugfs::Dir;
1275e40b591SMatthew Maurer     /// # use kernel::prelude::*;
1285e40b591SMatthew Maurer     /// # let dir = Dir::new(c_str!("my_debugfs_dir"));
1295e40b591SMatthew Maurer     /// let file = KBox::pin_init(dir.read_only_file(c_str!("foo"), 200), GFP_KERNEL)?;
1305e40b591SMatthew Maurer     /// // "my_debugfs_dir/foo" now contains the number 200.
1315e40b591SMatthew Maurer     /// // The file is removed when `file` is dropped.
1325e40b591SMatthew Maurer     /// # Ok::<(), Error>(())
1335e40b591SMatthew Maurer     /// ```
1345e40b591SMatthew Maurer     pub fn read_only_file<'a, T, E: 'a>(
1355e40b591SMatthew Maurer         &'a self,
1365e40b591SMatthew Maurer         name: &'a CStr,
1375e40b591SMatthew Maurer         data: impl PinInit<T, E> + 'a,
1385e40b591SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
1395e40b591SMatthew Maurer     where
1405e40b591SMatthew Maurer         T: Writer + Send + Sync + 'static,
1415e40b591SMatthew Maurer     {
1425e40b591SMatthew Maurer         let file_ops = &<T as ReadFile<_>>::FILE_OPS;
1435e40b591SMatthew Maurer         self.create_file(name, data, file_ops)
1445e40b591SMatthew Maurer     }
145*839dc1d1SMatthew Maurer 
146*839dc1d1SMatthew Maurer     /// Creates a read-write file in this directory.
147*839dc1d1SMatthew Maurer     ///
148*839dc1d1SMatthew Maurer     /// Reading the file uses the [`Writer`] implementation.
149*839dc1d1SMatthew Maurer     /// Writing to the file uses the [`Reader`] implementation.
150*839dc1d1SMatthew Maurer     pub fn read_write_file<'a, T, E: 'a>(
151*839dc1d1SMatthew Maurer         &'a self,
152*839dc1d1SMatthew Maurer         name: &'a CStr,
153*839dc1d1SMatthew Maurer         data: impl PinInit<T, E> + 'a,
154*839dc1d1SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
155*839dc1d1SMatthew Maurer     where
156*839dc1d1SMatthew Maurer         T: Writer + Reader + Send + Sync + 'static,
157*839dc1d1SMatthew Maurer     {
158*839dc1d1SMatthew Maurer         let file_ops = &<T as ReadWriteFile<_>>::FILE_OPS;
159*839dc1d1SMatthew Maurer         self.create_file(name, data, file_ops)
160*839dc1d1SMatthew Maurer     }
161*839dc1d1SMatthew Maurer 
162*839dc1d1SMatthew Maurer     /// Creates a write-only file in this directory.
163*839dc1d1SMatthew Maurer     ///
164*839dc1d1SMatthew Maurer     /// The file owns its backing data. Writing to the file uses the [`Reader`]
165*839dc1d1SMatthew Maurer     /// implementation.
166*839dc1d1SMatthew Maurer     ///
167*839dc1d1SMatthew Maurer     /// The file is removed when the returned [`File`] is dropped.
168*839dc1d1SMatthew Maurer     pub fn write_only_file<'a, T, E: 'a>(
169*839dc1d1SMatthew Maurer         &'a self,
170*839dc1d1SMatthew Maurer         name: &'a CStr,
171*839dc1d1SMatthew Maurer         data: impl PinInit<T, E> + 'a,
172*839dc1d1SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
173*839dc1d1SMatthew Maurer     where
174*839dc1d1SMatthew Maurer         T: Reader + Send + Sync + 'static,
175*839dc1d1SMatthew Maurer     {
176*839dc1d1SMatthew Maurer         self.create_file(name, data, &T::FILE_OPS)
177*839dc1d1SMatthew Maurer     }
1785e40b591SMatthew Maurer }
1795e40b591SMatthew Maurer 
1805e40b591SMatthew Maurer #[pin_data]
1815e40b591SMatthew Maurer /// Handle to a DebugFS scope, which ensures that attached `data` will outlive the provided
1825e40b591SMatthew Maurer /// [`Entry`] without moving.
1835e40b591SMatthew Maurer /// Currently, this is used to back [`File`] so that its `read` and/or `write` implementations
1845e40b591SMatthew Maurer /// can assume that their backing data is still alive.
1855e40b591SMatthew Maurer struct Scope<T> {
1865e40b591SMatthew Maurer     // This order is load-bearing for drops - `_entry` must be dropped before `data`.
1875e40b591SMatthew Maurer     #[cfg(CONFIG_DEBUG_FS)]
1885e40b591SMatthew Maurer     _entry: Entry,
1895e40b591SMatthew Maurer     #[pin]
1905e40b591SMatthew Maurer     data: T,
1915e40b591SMatthew Maurer     // Even if `T` is `Unpin`, we still can't allow it to be moved.
1925e40b591SMatthew Maurer     #[pin]
1935e40b591SMatthew Maurer     _pin: PhantomPinned,
1945e40b591SMatthew Maurer }
1955e40b591SMatthew Maurer 
1965e40b591SMatthew Maurer #[pin_data]
1975e40b591SMatthew Maurer /// Handle to a DebugFS file, owning its backing data.
1985e40b591SMatthew Maurer ///
1995e40b591SMatthew Maurer /// When dropped, the DebugFS file will be removed and the attached data will be dropped.
2005e40b591SMatthew Maurer pub struct File<T> {
2015e40b591SMatthew Maurer     #[pin]
2025e40b591SMatthew Maurer     scope: Scope<T>,
2035e40b591SMatthew Maurer }
2045e40b591SMatthew Maurer 
2055e40b591SMatthew Maurer #[cfg(not(CONFIG_DEBUG_FS))]
2065e40b591SMatthew Maurer impl<'b, T: 'b> Scope<T> {
2075e40b591SMatthew Maurer     fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
2085e40b591SMatthew Maurer     where
2095e40b591SMatthew Maurer         F: for<'a> FnOnce(&'a T) + 'b,
2105e40b591SMatthew Maurer     {
2115e40b591SMatthew Maurer         try_pin_init! {
2125e40b591SMatthew Maurer             Self {
2135e40b591SMatthew Maurer                 data <- data,
2145e40b591SMatthew Maurer                 _pin: PhantomPinned
2155e40b591SMatthew Maurer             } ? E
2165e40b591SMatthew Maurer         }
2175e40b591SMatthew Maurer         .pin_chain(|scope| {
2185e40b591SMatthew Maurer             init(&scope.data);
2195e40b591SMatthew Maurer             Ok(())
2205e40b591SMatthew Maurer         })
2215e40b591SMatthew Maurer     }
2225e40b591SMatthew Maurer }
2235e40b591SMatthew Maurer 
2245e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)]
2255e40b591SMatthew Maurer impl<'b, T: 'b> Scope<T> {
2265e40b591SMatthew Maurer     fn entry_mut(self: Pin<&mut Self>) -> &mut Entry {
2275e40b591SMatthew Maurer         // SAFETY: _entry is not structurally pinned.
2285e40b591SMatthew Maurer         unsafe { &mut Pin::into_inner_unchecked(self)._entry }
2295e40b591SMatthew Maurer     }
2305e40b591SMatthew Maurer 
2315e40b591SMatthew Maurer     fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
2325e40b591SMatthew Maurer     where
2335e40b591SMatthew Maurer         F: for<'a> FnOnce(&'a T) -> Entry + 'b,
2345e40b591SMatthew Maurer     {
2355e40b591SMatthew Maurer         try_pin_init! {
2365e40b591SMatthew Maurer             Self {
2375e40b591SMatthew Maurer                 _entry: Entry::empty(),
2385e40b591SMatthew Maurer                 data <- data,
2395e40b591SMatthew Maurer                 _pin: PhantomPinned
2405e40b591SMatthew Maurer             } ? E
2415e40b591SMatthew Maurer         }
2425e40b591SMatthew Maurer         .pin_chain(|scope| {
2435e40b591SMatthew Maurer             *scope.entry_mut() = init(&scope.data);
2445e40b591SMatthew Maurer             Ok(())
2455e40b591SMatthew Maurer         })
2465e40b591SMatthew Maurer     }
2475e40b591SMatthew Maurer }
2485e40b591SMatthew Maurer 
2495e40b591SMatthew Maurer impl<T> Deref for Scope<T> {
2505e40b591SMatthew Maurer     type Target = T;
2515e40b591SMatthew Maurer     fn deref(&self) -> &T {
2525e40b591SMatthew Maurer         &self.data
2535e40b591SMatthew Maurer     }
2545e40b591SMatthew Maurer }
2555e40b591SMatthew Maurer 
2565e40b591SMatthew Maurer impl<T> Deref for File<T> {
2575e40b591SMatthew Maurer     type Target = T;
2585e40b591SMatthew Maurer     fn deref(&self) -> &T {
2595e40b591SMatthew Maurer         &self.scope
2605e40b591SMatthew Maurer     }
2617f201ca1SMatthew Maurer }
262