xref: /linux/rust/kernel/debugfs.rs (revision 5f0942581dd0218c4449dac0639cf362e943c302)
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;
1540ecc494SMatthew Maurer use crate::uaccess::UserSliceReader;
1640ecc494SMatthew Maurer use core::fmt;
17*5f094258SMatthew Maurer use core::marker::PhantomData;
185e40b591SMatthew Maurer use core::marker::PhantomPinned;
19*5f094258SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)]
20*5f094258SMatthew Maurer use core::mem::ManuallyDrop;
215e40b591SMatthew Maurer use core::ops::Deref;
227f201ca1SMatthew Maurer 
235e40b591SMatthew Maurer mod traits;
24839dc1d1SMatthew Maurer pub use traits::{Reader, Writer};
255e40b591SMatthew Maurer 
2640ecc494SMatthew Maurer mod callback_adapters;
2740ecc494SMatthew Maurer use callback_adapters::{FormatAdapter, NoWriter, WritableAdapter};
285e40b591SMatthew Maurer mod file_ops;
29839dc1d1SMatthew Maurer use file_ops::{FileOps, ReadFile, ReadWriteFile, WriteFile};
307f201ca1SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)]
317f201ca1SMatthew Maurer mod entry;
327f201ca1SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)]
337f201ca1SMatthew Maurer use entry::Entry;
347f201ca1SMatthew Maurer 
357f201ca1SMatthew Maurer /// Owning handle to a DebugFS directory.
367f201ca1SMatthew Maurer ///
377f201ca1SMatthew Maurer /// The directory in the filesystem represented by [`Dir`] will be removed when handle has been
387f201ca1SMatthew Maurer /// dropped *and* all children have been removed.
397f201ca1SMatthew Maurer // If we have a parent, we hold a reference to it in the `Entry`. This prevents the `dentry`
407f201ca1SMatthew Maurer // we point to from being cleaned up if our parent `Dir`/`Entry` is dropped before us.
417f201ca1SMatthew Maurer //
427f201ca1SMatthew Maurer // The `None` option indicates that the `Arc` could not be allocated, so our children would not be
437f201ca1SMatthew Maurer // able to refer to us. In this case, we need to silently fail. All future child directories/files
447f201ca1SMatthew Maurer // will silently fail as well.
457f201ca1SMatthew Maurer #[derive(Clone)]
46*5f094258SMatthew Maurer pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry<'static>>>);
477f201ca1SMatthew Maurer 
487f201ca1SMatthew Maurer impl Dir {
497f201ca1SMatthew Maurer     /// Create a new directory in DebugFS. If `parent` is [`None`], it will be created at the root.
507f201ca1SMatthew Maurer     fn create(name: &CStr, parent: Option<&Dir>) -> Self {
517f201ca1SMatthew Maurer         #[cfg(CONFIG_DEBUG_FS)]
527f201ca1SMatthew Maurer         {
537f201ca1SMatthew Maurer             let parent_entry = match parent {
547f201ca1SMatthew Maurer                 // If the parent couldn't be allocated, just early-return
557f201ca1SMatthew Maurer                 Some(Dir(None)) => return Self(None),
567f201ca1SMatthew Maurer                 Some(Dir(Some(entry))) => Some(entry.clone()),
577f201ca1SMatthew Maurer                 None => None,
587f201ca1SMatthew Maurer             };
597f201ca1SMatthew Maurer             Self(
607f201ca1SMatthew Maurer                 // If Arc creation fails, the `Entry` will be dropped, so the directory will be
617f201ca1SMatthew Maurer                 // cleaned up.
627f201ca1SMatthew Maurer                 Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(),
637f201ca1SMatthew Maurer             )
647f201ca1SMatthew Maurer         }
657f201ca1SMatthew Maurer         #[cfg(not(CONFIG_DEBUG_FS))]
667f201ca1SMatthew Maurer         Self()
677f201ca1SMatthew Maurer     }
687f201ca1SMatthew Maurer 
695e40b591SMatthew Maurer     /// Creates a DebugFS file which will own the data produced by the initializer provided in
705e40b591SMatthew Maurer     /// `data`.
715e40b591SMatthew Maurer     fn create_file<'a, T, E: 'a>(
725e40b591SMatthew Maurer         &'a self,
735e40b591SMatthew Maurer         name: &'a CStr,
745e40b591SMatthew Maurer         data: impl PinInit<T, E> + 'a,
755e40b591SMatthew Maurer         file_ops: &'static FileOps<T>,
765e40b591SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
775e40b591SMatthew Maurer     where
785e40b591SMatthew Maurer         T: Sync + 'static,
795e40b591SMatthew Maurer     {
805e40b591SMatthew Maurer         let scope = Scope::<T>::new(data, move |data| {
815e40b591SMatthew Maurer             #[cfg(CONFIG_DEBUG_FS)]
825e40b591SMatthew Maurer             if let Some(parent) = &self.0 {
835e40b591SMatthew Maurer                 // SAFETY: Because data derives from a scope, and our entry will be dropped before
845e40b591SMatthew Maurer                 // the data is dropped, it is guaranteed to outlive the entry we return.
855e40b591SMatthew Maurer                 unsafe { Entry::dynamic_file(name, parent.clone(), data, file_ops) }
865e40b591SMatthew Maurer             } else {
875e40b591SMatthew Maurer                 Entry::empty()
885e40b591SMatthew Maurer             }
895e40b591SMatthew Maurer         });
905e40b591SMatthew Maurer         try_pin_init! {
915e40b591SMatthew Maurer             File {
925e40b591SMatthew Maurer                 scope <- scope
935e40b591SMatthew Maurer             } ? E
945e40b591SMatthew Maurer         }
955e40b591SMatthew Maurer     }
965e40b591SMatthew Maurer 
977f201ca1SMatthew Maurer     /// Create a new directory in DebugFS at the root.
987f201ca1SMatthew Maurer     ///
997f201ca1SMatthew Maurer     /// # Examples
1007f201ca1SMatthew Maurer     ///
1017f201ca1SMatthew Maurer     /// ```
1027f201ca1SMatthew Maurer     /// # use kernel::c_str;
1037f201ca1SMatthew Maurer     /// # use kernel::debugfs::Dir;
1047f201ca1SMatthew Maurer     /// let debugfs = Dir::new(c_str!("parent"));
1057f201ca1SMatthew Maurer     /// ```
1067f201ca1SMatthew Maurer     pub fn new(name: &CStr) -> Self {
1077f201ca1SMatthew Maurer         Dir::create(name, None)
1087f201ca1SMatthew Maurer     }
1097f201ca1SMatthew Maurer 
1107f201ca1SMatthew Maurer     /// Creates a subdirectory within this directory.
1117f201ca1SMatthew Maurer     ///
1127f201ca1SMatthew Maurer     /// # Examples
1137f201ca1SMatthew Maurer     ///
1147f201ca1SMatthew Maurer     /// ```
1157f201ca1SMatthew Maurer     /// # use kernel::c_str;
1167f201ca1SMatthew Maurer     /// # use kernel::debugfs::Dir;
1177f201ca1SMatthew Maurer     /// let parent = Dir::new(c_str!("parent"));
1187f201ca1SMatthew Maurer     /// let child = parent.subdir(c_str!("child"));
1197f201ca1SMatthew Maurer     /// ```
1207f201ca1SMatthew Maurer     pub fn subdir(&self, name: &CStr) -> Self {
1217f201ca1SMatthew Maurer         Dir::create(name, Some(self))
1227f201ca1SMatthew Maurer     }
1235e40b591SMatthew Maurer 
1245e40b591SMatthew Maurer     /// Creates a read-only file in this directory.
1255e40b591SMatthew Maurer     ///
1265e40b591SMatthew Maurer     /// The file's contents are produced by invoking [`Writer::write`] on the value initialized by
1275e40b591SMatthew Maurer     /// `data`.
1285e40b591SMatthew Maurer     ///
1295e40b591SMatthew Maurer     /// # Examples
1305e40b591SMatthew Maurer     ///
1315e40b591SMatthew Maurer     /// ```
1325e40b591SMatthew Maurer     /// # use kernel::c_str;
1335e40b591SMatthew Maurer     /// # use kernel::debugfs::Dir;
1345e40b591SMatthew Maurer     /// # use kernel::prelude::*;
1355e40b591SMatthew Maurer     /// # let dir = Dir::new(c_str!("my_debugfs_dir"));
1365e40b591SMatthew Maurer     /// let file = KBox::pin_init(dir.read_only_file(c_str!("foo"), 200), GFP_KERNEL)?;
1375e40b591SMatthew Maurer     /// // "my_debugfs_dir/foo" now contains the number 200.
1385e40b591SMatthew Maurer     /// // The file is removed when `file` is dropped.
1395e40b591SMatthew Maurer     /// # Ok::<(), Error>(())
1405e40b591SMatthew Maurer     /// ```
1415e40b591SMatthew Maurer     pub fn read_only_file<'a, T, E: 'a>(
1425e40b591SMatthew Maurer         &'a self,
1435e40b591SMatthew Maurer         name: &'a CStr,
1445e40b591SMatthew Maurer         data: impl PinInit<T, E> + 'a,
1455e40b591SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
1465e40b591SMatthew Maurer     where
1475e40b591SMatthew Maurer         T: Writer + Send + Sync + 'static,
1485e40b591SMatthew Maurer     {
1495e40b591SMatthew Maurer         let file_ops = &<T as ReadFile<_>>::FILE_OPS;
1505e40b591SMatthew Maurer         self.create_file(name, data, file_ops)
1515e40b591SMatthew Maurer     }
152839dc1d1SMatthew Maurer 
15340ecc494SMatthew Maurer     /// Creates a read-only file in this directory, with contents from a callback.
15440ecc494SMatthew Maurer     ///
15540ecc494SMatthew Maurer     /// `f` must be a function item or a non-capturing closure.
15640ecc494SMatthew Maurer     /// This is statically asserted and not a safety requirement.
15740ecc494SMatthew Maurer     ///
15840ecc494SMatthew Maurer     /// # Examples
15940ecc494SMatthew Maurer     ///
16040ecc494SMatthew Maurer     /// ```
16140ecc494SMatthew Maurer     /// # use core::sync::atomic::{AtomicU32, Ordering};
16240ecc494SMatthew Maurer     /// # use kernel::c_str;
16340ecc494SMatthew Maurer     /// # use kernel::debugfs::Dir;
16440ecc494SMatthew Maurer     /// # use kernel::prelude::*;
16540ecc494SMatthew Maurer     /// # let dir = Dir::new(c_str!("foo"));
16640ecc494SMatthew Maurer     /// let file = KBox::pin_init(
16740ecc494SMatthew Maurer     ///     dir.read_callback_file(c_str!("bar"),
16840ecc494SMatthew Maurer     ///     AtomicU32::new(3),
16940ecc494SMatthew Maurer     ///     &|val, f| {
17040ecc494SMatthew Maurer     ///       let out = val.load(Ordering::Relaxed);
17140ecc494SMatthew Maurer     ///       writeln!(f, "{out:#010x}")
17240ecc494SMatthew Maurer     ///     }),
17340ecc494SMatthew Maurer     ///     GFP_KERNEL)?;
17440ecc494SMatthew Maurer     /// // Reading "foo/bar" will show "0x00000003".
17540ecc494SMatthew Maurer     /// file.store(10, Ordering::Relaxed);
17640ecc494SMatthew Maurer     /// // Reading "foo/bar" will now show "0x0000000a".
17740ecc494SMatthew Maurer     /// # Ok::<(), Error>(())
17840ecc494SMatthew Maurer     /// ```
17940ecc494SMatthew Maurer     pub fn read_callback_file<'a, T, E: 'a, F>(
18040ecc494SMatthew Maurer         &'a self,
18140ecc494SMatthew Maurer         name: &'a CStr,
18240ecc494SMatthew Maurer         data: impl PinInit<T, E> + 'a,
18340ecc494SMatthew Maurer         _f: &'static F,
18440ecc494SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
18540ecc494SMatthew Maurer     where
18640ecc494SMatthew Maurer         T: Send + Sync + 'static,
18740ecc494SMatthew Maurer         F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
18840ecc494SMatthew Maurer     {
18940ecc494SMatthew Maurer         let file_ops = <FormatAdapter<T, F>>::FILE_OPS.adapt();
19040ecc494SMatthew Maurer         self.create_file(name, data, file_ops)
19140ecc494SMatthew Maurer     }
19240ecc494SMatthew Maurer 
193839dc1d1SMatthew Maurer     /// Creates a read-write file in this directory.
194839dc1d1SMatthew Maurer     ///
195839dc1d1SMatthew Maurer     /// Reading the file uses the [`Writer`] implementation.
196839dc1d1SMatthew Maurer     /// Writing to the file uses the [`Reader`] implementation.
197839dc1d1SMatthew Maurer     pub fn read_write_file<'a, T, E: 'a>(
198839dc1d1SMatthew Maurer         &'a self,
199839dc1d1SMatthew Maurer         name: &'a CStr,
200839dc1d1SMatthew Maurer         data: impl PinInit<T, E> + 'a,
201839dc1d1SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
202839dc1d1SMatthew Maurer     where
203839dc1d1SMatthew Maurer         T: Writer + Reader + Send + Sync + 'static,
204839dc1d1SMatthew Maurer     {
205839dc1d1SMatthew Maurer         let file_ops = &<T as ReadWriteFile<_>>::FILE_OPS;
206839dc1d1SMatthew Maurer         self.create_file(name, data, file_ops)
207839dc1d1SMatthew Maurer     }
208839dc1d1SMatthew Maurer 
20940ecc494SMatthew Maurer     /// Creates a read-write file in this directory, with logic from callbacks.
21040ecc494SMatthew Maurer     ///
21140ecc494SMatthew Maurer     /// Reading from the file is handled by `f`. Writing to the file is handled by `w`.
21240ecc494SMatthew Maurer     ///
21340ecc494SMatthew Maurer     /// `f` and `w` must be function items or non-capturing closures.
21440ecc494SMatthew Maurer     /// This is statically asserted and not a safety requirement.
21540ecc494SMatthew Maurer     pub fn read_write_callback_file<'a, T, E: 'a, F, W>(
21640ecc494SMatthew Maurer         &'a self,
21740ecc494SMatthew Maurer         name: &'a CStr,
21840ecc494SMatthew Maurer         data: impl PinInit<T, E> + 'a,
21940ecc494SMatthew Maurer         _f: &'static F,
22040ecc494SMatthew Maurer         _w: &'static W,
22140ecc494SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
22240ecc494SMatthew Maurer     where
22340ecc494SMatthew Maurer         T: Send + Sync + 'static,
22440ecc494SMatthew Maurer         F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
22540ecc494SMatthew Maurer         W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
22640ecc494SMatthew Maurer     {
22740ecc494SMatthew Maurer         let file_ops =
22840ecc494SMatthew Maurer             <WritableAdapter<FormatAdapter<T, F>, W> as file_ops::ReadWriteFile<_>>::FILE_OPS
22940ecc494SMatthew Maurer                 .adapt()
23040ecc494SMatthew Maurer                 .adapt();
23140ecc494SMatthew Maurer         self.create_file(name, data, file_ops)
23240ecc494SMatthew Maurer     }
23340ecc494SMatthew Maurer 
234839dc1d1SMatthew Maurer     /// Creates a write-only file in this directory.
235839dc1d1SMatthew Maurer     ///
236839dc1d1SMatthew Maurer     /// The file owns its backing data. Writing to the file uses the [`Reader`]
237839dc1d1SMatthew Maurer     /// implementation.
238839dc1d1SMatthew Maurer     ///
239839dc1d1SMatthew Maurer     /// The file is removed when the returned [`File`] is dropped.
240839dc1d1SMatthew Maurer     pub fn write_only_file<'a, T, E: 'a>(
241839dc1d1SMatthew Maurer         &'a self,
242839dc1d1SMatthew Maurer         name: &'a CStr,
243839dc1d1SMatthew Maurer         data: impl PinInit<T, E> + 'a,
244839dc1d1SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
245839dc1d1SMatthew Maurer     where
246839dc1d1SMatthew Maurer         T: Reader + Send + Sync + 'static,
247839dc1d1SMatthew Maurer     {
248839dc1d1SMatthew Maurer         self.create_file(name, data, &T::FILE_OPS)
249839dc1d1SMatthew Maurer     }
25040ecc494SMatthew Maurer 
25140ecc494SMatthew Maurer     /// Creates a write-only file in this directory, with write logic from a callback.
25240ecc494SMatthew Maurer     ///
25340ecc494SMatthew Maurer     /// `w` must be a function item or a non-capturing closure.
25440ecc494SMatthew Maurer     /// This is statically asserted and not a safety requirement.
25540ecc494SMatthew Maurer     pub fn write_callback_file<'a, T, E: 'a, W>(
25640ecc494SMatthew Maurer         &'a self,
25740ecc494SMatthew Maurer         name: &'a CStr,
25840ecc494SMatthew Maurer         data: impl PinInit<T, E> + 'a,
25940ecc494SMatthew Maurer         _w: &'static W,
26040ecc494SMatthew Maurer     ) -> impl PinInit<File<T>, E> + 'a
26140ecc494SMatthew Maurer     where
26240ecc494SMatthew Maurer         T: Send + Sync + 'static,
26340ecc494SMatthew Maurer         W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
26440ecc494SMatthew Maurer     {
26540ecc494SMatthew Maurer         let file_ops = <WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS
26640ecc494SMatthew Maurer             .adapt()
26740ecc494SMatthew Maurer             .adapt();
26840ecc494SMatthew Maurer         self.create_file(name, data, file_ops)
26940ecc494SMatthew Maurer     }
270*5f094258SMatthew Maurer 
271*5f094258SMatthew Maurer     // While this function is safe, it is intentionally not public because it's a bit of a
272*5f094258SMatthew Maurer     // footgun.
273*5f094258SMatthew Maurer     //
274*5f094258SMatthew Maurer     // Unless you also extract the `entry` later and schedule it for `Drop` at the appropriate
275*5f094258SMatthew Maurer     // time, a `ScopedDir` with a `Dir` parent will never be deleted.
276*5f094258SMatthew Maurer     fn scoped_dir<'data>(&self, name: &CStr) -> ScopedDir<'data, 'static> {
277*5f094258SMatthew Maurer         #[cfg(CONFIG_DEBUG_FS)]
278*5f094258SMatthew Maurer         {
279*5f094258SMatthew Maurer             let parent_entry = match &self.0 {
280*5f094258SMatthew Maurer                 None => return ScopedDir::empty(),
281*5f094258SMatthew Maurer                 Some(entry) => entry.clone(),
282*5f094258SMatthew Maurer             };
283*5f094258SMatthew Maurer             ScopedDir {
284*5f094258SMatthew Maurer                 entry: ManuallyDrop::new(Entry::dynamic_dir(name, Some(parent_entry))),
285*5f094258SMatthew Maurer                 _phantom: PhantomData,
286*5f094258SMatthew Maurer             }
287*5f094258SMatthew Maurer         }
288*5f094258SMatthew Maurer         #[cfg(not(CONFIG_DEBUG_FS))]
289*5f094258SMatthew Maurer         ScopedDir::empty()
290*5f094258SMatthew Maurer     }
291*5f094258SMatthew Maurer 
292*5f094258SMatthew Maurer     /// Creates a new scope, which is a directory associated with some data `T`.
293*5f094258SMatthew Maurer     ///
294*5f094258SMatthew Maurer     /// The created directory will be a subdirectory of `self`. The `init` closure is called to
295*5f094258SMatthew Maurer     /// populate the directory with files and subdirectories. These files can reference the data
296*5f094258SMatthew Maurer     /// stored in the scope.
297*5f094258SMatthew Maurer     ///
298*5f094258SMatthew Maurer     /// The entire directory tree created within the scope will be removed when the returned
299*5f094258SMatthew Maurer     /// `Scope` handle is dropped.
300*5f094258SMatthew Maurer     pub fn scope<'a, T: 'a, E: 'a, F>(
301*5f094258SMatthew Maurer         &'a self,
302*5f094258SMatthew Maurer         data: impl PinInit<T, E> + 'a,
303*5f094258SMatthew Maurer         name: &'a CStr,
304*5f094258SMatthew Maurer         init: F,
305*5f094258SMatthew Maurer     ) -> impl PinInit<Scope<T>, E> + 'a
306*5f094258SMatthew Maurer     where
307*5f094258SMatthew Maurer         F: for<'data, 'dir> FnOnce(&'data T, &'dir ScopedDir<'data, 'dir>) + 'a,
308*5f094258SMatthew Maurer     {
309*5f094258SMatthew Maurer         Scope::new(data, |data| {
310*5f094258SMatthew Maurer             let scoped = self.scoped_dir(name);
311*5f094258SMatthew Maurer             init(data, &scoped);
312*5f094258SMatthew Maurer             scoped.into_entry()
313*5f094258SMatthew Maurer         })
314*5f094258SMatthew Maurer     }
3155e40b591SMatthew Maurer }
3165e40b591SMatthew Maurer 
3175e40b591SMatthew Maurer #[pin_data]
318*5f094258SMatthew Maurer /// Handle to a DebugFS scope, which ensures that attached `data` will outlive the DebugFS entry
319*5f094258SMatthew Maurer /// without moving.
320*5f094258SMatthew Maurer ///
321*5f094258SMatthew Maurer /// This is internally used to back [`File`], and used in the API to represent the attachment
322*5f094258SMatthew Maurer /// of a directory lifetime to a data structure which may be jointly accessed by a number of
323*5f094258SMatthew Maurer /// different files.
324*5f094258SMatthew Maurer ///
325*5f094258SMatthew Maurer /// When dropped, a `Scope` will remove all directories and files in the filesystem backed by the
326*5f094258SMatthew Maurer /// attached data structure prior to releasing the attached data.
327*5f094258SMatthew Maurer pub struct Scope<T> {
3285e40b591SMatthew Maurer     // This order is load-bearing for drops - `_entry` must be dropped before `data`.
3295e40b591SMatthew Maurer     #[cfg(CONFIG_DEBUG_FS)]
330*5f094258SMatthew Maurer     _entry: Entry<'static>,
3315e40b591SMatthew Maurer     #[pin]
3325e40b591SMatthew Maurer     data: T,
3335e40b591SMatthew Maurer     // Even if `T` is `Unpin`, we still can't allow it to be moved.
3345e40b591SMatthew Maurer     #[pin]
3355e40b591SMatthew Maurer     _pin: PhantomPinned,
3365e40b591SMatthew Maurer }
3375e40b591SMatthew Maurer 
3385e40b591SMatthew Maurer #[pin_data]
3395e40b591SMatthew Maurer /// Handle to a DebugFS file, owning its backing data.
3405e40b591SMatthew Maurer ///
3415e40b591SMatthew Maurer /// When dropped, the DebugFS file will be removed and the attached data will be dropped.
3425e40b591SMatthew Maurer pub struct File<T> {
3435e40b591SMatthew Maurer     #[pin]
3445e40b591SMatthew Maurer     scope: Scope<T>,
3455e40b591SMatthew Maurer }
3465e40b591SMatthew Maurer 
3475e40b591SMatthew Maurer #[cfg(not(CONFIG_DEBUG_FS))]
3485e40b591SMatthew Maurer impl<'b, T: 'b> Scope<T> {
3495e40b591SMatthew Maurer     fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
3505e40b591SMatthew Maurer     where
3515e40b591SMatthew Maurer         F: for<'a> FnOnce(&'a T) + 'b,
3525e40b591SMatthew Maurer     {
3535e40b591SMatthew Maurer         try_pin_init! {
3545e40b591SMatthew Maurer             Self {
3555e40b591SMatthew Maurer                 data <- data,
3565e40b591SMatthew Maurer                 _pin: PhantomPinned
3575e40b591SMatthew Maurer             } ? E
3585e40b591SMatthew Maurer         }
3595e40b591SMatthew Maurer         .pin_chain(|scope| {
3605e40b591SMatthew Maurer             init(&scope.data);
3615e40b591SMatthew Maurer             Ok(())
3625e40b591SMatthew Maurer         })
3635e40b591SMatthew Maurer     }
3645e40b591SMatthew Maurer }
3655e40b591SMatthew Maurer 
3665e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)]
3675e40b591SMatthew Maurer impl<'b, T: 'b> Scope<T> {
368*5f094258SMatthew Maurer     fn entry_mut(self: Pin<&mut Self>) -> &mut Entry<'static> {
3695e40b591SMatthew Maurer         // SAFETY: _entry is not structurally pinned.
3705e40b591SMatthew Maurer         unsafe { &mut Pin::into_inner_unchecked(self)._entry }
3715e40b591SMatthew Maurer     }
3725e40b591SMatthew Maurer 
3735e40b591SMatthew Maurer     fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
3745e40b591SMatthew Maurer     where
375*5f094258SMatthew Maurer         F: for<'a> FnOnce(&'a T) -> Entry<'static> + 'b,
3765e40b591SMatthew Maurer     {
3775e40b591SMatthew Maurer         try_pin_init! {
3785e40b591SMatthew Maurer             Self {
3795e40b591SMatthew Maurer                 _entry: Entry::empty(),
3805e40b591SMatthew Maurer                 data <- data,
3815e40b591SMatthew Maurer                 _pin: PhantomPinned
3825e40b591SMatthew Maurer             } ? E
3835e40b591SMatthew Maurer         }
3845e40b591SMatthew Maurer         .pin_chain(|scope| {
3855e40b591SMatthew Maurer             *scope.entry_mut() = init(&scope.data);
3865e40b591SMatthew Maurer             Ok(())
3875e40b591SMatthew Maurer         })
3885e40b591SMatthew Maurer     }
3895e40b591SMatthew Maurer }
3905e40b591SMatthew Maurer 
391*5f094258SMatthew Maurer impl<'a, T: 'a> Scope<T> {
392*5f094258SMatthew Maurer     /// Creates a new scope, which is a directory at the root of the debugfs filesystem,
393*5f094258SMatthew Maurer     /// associated with some data `T`.
394*5f094258SMatthew Maurer     ///
395*5f094258SMatthew Maurer     /// The `init` closure is called to populate the directory with files and subdirectories. These
396*5f094258SMatthew Maurer     /// files can reference the data stored in the scope.
397*5f094258SMatthew Maurer     ///
398*5f094258SMatthew Maurer     /// The entire directory tree created within the scope will be removed when the returned
399*5f094258SMatthew Maurer     /// `Scope` handle is dropped.
400*5f094258SMatthew Maurer     pub fn dir<E: 'a, F>(
401*5f094258SMatthew Maurer         data: impl PinInit<T, E> + 'a,
402*5f094258SMatthew Maurer         name: &'a CStr,
403*5f094258SMatthew Maurer         init: F,
404*5f094258SMatthew Maurer     ) -> impl PinInit<Self, E> + 'a
405*5f094258SMatthew Maurer     where
406*5f094258SMatthew Maurer         F: for<'data, 'dir> FnOnce(&'data T, &'dir ScopedDir<'data, 'dir>) + 'a,
407*5f094258SMatthew Maurer     {
408*5f094258SMatthew Maurer         Scope::new(data, |data| {
409*5f094258SMatthew Maurer             let scoped = ScopedDir::new(name);
410*5f094258SMatthew Maurer             init(data, &scoped);
411*5f094258SMatthew Maurer             scoped.into_entry()
412*5f094258SMatthew Maurer         })
413*5f094258SMatthew Maurer     }
414*5f094258SMatthew Maurer }
415*5f094258SMatthew Maurer 
4165e40b591SMatthew Maurer impl<T> Deref for Scope<T> {
4175e40b591SMatthew Maurer     type Target = T;
4185e40b591SMatthew Maurer     fn deref(&self) -> &T {
4195e40b591SMatthew Maurer         &self.data
4205e40b591SMatthew Maurer     }
4215e40b591SMatthew Maurer }
4225e40b591SMatthew Maurer 
4235e40b591SMatthew Maurer impl<T> Deref for File<T> {
4245e40b591SMatthew Maurer     type Target = T;
4255e40b591SMatthew Maurer     fn deref(&self) -> &T {
4265e40b591SMatthew Maurer         &self.scope
4275e40b591SMatthew Maurer     }
4287f201ca1SMatthew Maurer }
429*5f094258SMatthew Maurer 
430*5f094258SMatthew Maurer /// A handle to a directory which will live at most `'dir`, accessing data that will live for at
431*5f094258SMatthew Maurer /// least `'data`.
432*5f094258SMatthew Maurer ///
433*5f094258SMatthew Maurer /// Dropping a ScopedDir will not delete or clean it up, this is expected to occur through dropping
434*5f094258SMatthew Maurer /// the `Scope` that created it.
435*5f094258SMatthew Maurer pub struct ScopedDir<'data, 'dir> {
436*5f094258SMatthew Maurer     #[cfg(CONFIG_DEBUG_FS)]
437*5f094258SMatthew Maurer     entry: ManuallyDrop<Entry<'dir>>,
438*5f094258SMatthew Maurer     _phantom: PhantomData<fn(&'data ()) -> &'dir ()>,
439*5f094258SMatthew Maurer }
440*5f094258SMatthew Maurer 
441*5f094258SMatthew Maurer impl<'data, 'dir> ScopedDir<'data, 'dir> {
442*5f094258SMatthew Maurer     /// Creates a subdirectory inside this `ScopedDir`.
443*5f094258SMatthew Maurer     ///
444*5f094258SMatthew Maurer     /// The returned directory handle cannot outlive this one.
445*5f094258SMatthew Maurer     pub fn dir<'dir2>(&'dir2 self, name: &CStr) -> ScopedDir<'data, 'dir2> {
446*5f094258SMatthew Maurer         #[cfg(not(CONFIG_DEBUG_FS))]
447*5f094258SMatthew Maurer         let _ = name;
448*5f094258SMatthew Maurer         ScopedDir {
449*5f094258SMatthew Maurer             #[cfg(CONFIG_DEBUG_FS)]
450*5f094258SMatthew Maurer             entry: ManuallyDrop::new(Entry::dir(name, Some(&*self.entry))),
451*5f094258SMatthew Maurer             _phantom: PhantomData,
452*5f094258SMatthew Maurer         }
453*5f094258SMatthew Maurer     }
454*5f094258SMatthew Maurer 
455*5f094258SMatthew Maurer     fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &'static FileOps<T>) {
456*5f094258SMatthew Maurer         #[cfg(CONFIG_DEBUG_FS)]
457*5f094258SMatthew Maurer         core::mem::forget(Entry::file(name, &self.entry, data, vtable));
458*5f094258SMatthew Maurer     }
459*5f094258SMatthew Maurer 
460*5f094258SMatthew Maurer     /// Creates a read-only file in this directory.
461*5f094258SMatthew Maurer     ///
462*5f094258SMatthew Maurer     /// The file's contents are produced by invoking [`Writer::write`].
463*5f094258SMatthew Maurer     ///
464*5f094258SMatthew Maurer     /// This function does not produce an owning handle to the file. The created
465*5f094258SMatthew Maurer     /// file is removed when the [`Scope`] that this directory belongs
466*5f094258SMatthew Maurer     /// to is dropped.
467*5f094258SMatthew Maurer     pub fn read_only_file<T: Writer + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) {
468*5f094258SMatthew Maurer         self.create_file(name, data, &T::FILE_OPS)
469*5f094258SMatthew Maurer     }
470*5f094258SMatthew Maurer 
471*5f094258SMatthew Maurer     /// Creates a read-only file in this directory, with contents from a callback.
472*5f094258SMatthew Maurer     ///
473*5f094258SMatthew Maurer     /// The file contents are generated by calling `f` with `data`.
474*5f094258SMatthew Maurer     ///
475*5f094258SMatthew Maurer     ///
476*5f094258SMatthew Maurer     /// `f` must be a function item or a non-capturing closure.
477*5f094258SMatthew Maurer     /// This is statically asserted and not a safety requirement.
478*5f094258SMatthew Maurer     ///
479*5f094258SMatthew Maurer     /// This function does not produce an owning handle to the file. The created
480*5f094258SMatthew Maurer     /// file is removed when the [`Scope`] that this directory belongs
481*5f094258SMatthew Maurer     /// to is dropped.
482*5f094258SMatthew Maurer     pub fn read_callback_file<T, F>(&self, name: &CStr, data: &'data T, _f: &'static F)
483*5f094258SMatthew Maurer     where
484*5f094258SMatthew Maurer         T: Send + Sync + 'static,
485*5f094258SMatthew Maurer         F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
486*5f094258SMatthew Maurer     {
487*5f094258SMatthew Maurer         let vtable = <FormatAdapter<T, F> as ReadFile<_>>::FILE_OPS.adapt();
488*5f094258SMatthew Maurer         self.create_file(name, data, vtable)
489*5f094258SMatthew Maurer     }
490*5f094258SMatthew Maurer 
491*5f094258SMatthew Maurer     /// Creates a read-write file in this directory.
492*5f094258SMatthew Maurer     ///
493*5f094258SMatthew Maurer     /// Reading the file uses the [`Writer`] implementation on `data`. Writing to the file uses
494*5f094258SMatthew Maurer     /// the [`Reader`] implementation on `data`.
495*5f094258SMatthew Maurer     ///
496*5f094258SMatthew Maurer     /// This function does not produce an owning handle to the file. The created
497*5f094258SMatthew Maurer     /// file is removed when the [`Scope`] that this directory belongs
498*5f094258SMatthew Maurer     /// to is dropped.
499*5f094258SMatthew Maurer     pub fn read_write_file<T: Writer + Reader + Send + Sync + 'static>(
500*5f094258SMatthew Maurer         &self,
501*5f094258SMatthew Maurer         name: &CStr,
502*5f094258SMatthew Maurer         data: &'data T,
503*5f094258SMatthew Maurer     ) {
504*5f094258SMatthew Maurer         let vtable = &<T as ReadWriteFile<_>>::FILE_OPS;
505*5f094258SMatthew Maurer         self.create_file(name, data, vtable)
506*5f094258SMatthew Maurer     }
507*5f094258SMatthew Maurer 
508*5f094258SMatthew Maurer     /// Creates a read-write file in this directory, with logic from callbacks.
509*5f094258SMatthew Maurer     ///
510*5f094258SMatthew Maurer     /// Reading from the file is handled by `f`. Writing to the file is handled by `w`.
511*5f094258SMatthew Maurer     ///
512*5f094258SMatthew Maurer     /// `f` and `w` must be function items or non-capturing closures.
513*5f094258SMatthew Maurer     /// This is statically asserted and not a safety requirement.
514*5f094258SMatthew Maurer     ///
515*5f094258SMatthew Maurer     /// This function does not produce an owning handle to the file. The created
516*5f094258SMatthew Maurer     /// file is removed when the [`Scope`] that this directory belongs
517*5f094258SMatthew Maurer     /// to is dropped.
518*5f094258SMatthew Maurer     pub fn read_write_callback_file<T, F, W>(
519*5f094258SMatthew Maurer         &self,
520*5f094258SMatthew Maurer         name: &CStr,
521*5f094258SMatthew Maurer         data: &'data T,
522*5f094258SMatthew Maurer         _f: &'static F,
523*5f094258SMatthew Maurer         _w: &'static W,
524*5f094258SMatthew Maurer     ) where
525*5f094258SMatthew Maurer         T: Send + Sync + 'static,
526*5f094258SMatthew Maurer         F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync,
527*5f094258SMatthew Maurer         W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
528*5f094258SMatthew Maurer     {
529*5f094258SMatthew Maurer         let vtable = <WritableAdapter<FormatAdapter<T, F>, W> as ReadWriteFile<_>>::FILE_OPS
530*5f094258SMatthew Maurer             .adapt()
531*5f094258SMatthew Maurer             .adapt();
532*5f094258SMatthew Maurer         self.create_file(name, data, vtable)
533*5f094258SMatthew Maurer     }
534*5f094258SMatthew Maurer 
535*5f094258SMatthew Maurer     /// Creates a write-only file in this directory.
536*5f094258SMatthew Maurer     ///
537*5f094258SMatthew Maurer     /// Writing to the file uses the [`Reader`] implementation on `data`.
538*5f094258SMatthew Maurer     ///
539*5f094258SMatthew Maurer     /// This function does not produce an owning handle to the file. The created
540*5f094258SMatthew Maurer     /// file is removed when the [`Scope`] that this directory belongs
541*5f094258SMatthew Maurer     /// to is dropped.
542*5f094258SMatthew Maurer     pub fn write_only_file<T: Reader + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) {
543*5f094258SMatthew Maurer         let vtable = &<T as WriteFile<_>>::FILE_OPS;
544*5f094258SMatthew Maurer         self.create_file(name, data, vtable)
545*5f094258SMatthew Maurer     }
546*5f094258SMatthew Maurer 
547*5f094258SMatthew Maurer     /// Creates a write-only file in this directory, with write logic from a callback.
548*5f094258SMatthew Maurer     ///
549*5f094258SMatthew Maurer     /// Writing to the file is handled by `w`.
550*5f094258SMatthew Maurer     ///
551*5f094258SMatthew Maurer     /// `w` must be a function item or a non-capturing closure.
552*5f094258SMatthew Maurer     /// This is statically asserted and not a safety requirement.
553*5f094258SMatthew Maurer     ///
554*5f094258SMatthew Maurer     /// This function does not produce an owning handle to the file. The created
555*5f094258SMatthew Maurer     /// file is removed when the [`Scope`] that this directory belongs
556*5f094258SMatthew Maurer     /// to is dropped.
557*5f094258SMatthew Maurer     pub fn write_only_callback_file<T, W>(&self, name: &CStr, data: &'data T, _w: &'static W)
558*5f094258SMatthew Maurer     where
559*5f094258SMatthew Maurer         T: Send + Sync + 'static,
560*5f094258SMatthew Maurer         W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync,
561*5f094258SMatthew Maurer     {
562*5f094258SMatthew Maurer         let vtable = &<WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS
563*5f094258SMatthew Maurer             .adapt()
564*5f094258SMatthew Maurer             .adapt();
565*5f094258SMatthew Maurer         self.create_file(name, data, vtable)
566*5f094258SMatthew Maurer     }
567*5f094258SMatthew Maurer 
568*5f094258SMatthew Maurer     fn empty() -> Self {
569*5f094258SMatthew Maurer         ScopedDir {
570*5f094258SMatthew Maurer             #[cfg(CONFIG_DEBUG_FS)]
571*5f094258SMatthew Maurer             entry: ManuallyDrop::new(Entry::empty()),
572*5f094258SMatthew Maurer             _phantom: PhantomData,
573*5f094258SMatthew Maurer         }
574*5f094258SMatthew Maurer     }
575*5f094258SMatthew Maurer     #[cfg(CONFIG_DEBUG_FS)]
576*5f094258SMatthew Maurer     fn into_entry(self) -> Entry<'dir> {
577*5f094258SMatthew Maurer         ManuallyDrop::into_inner(self.entry)
578*5f094258SMatthew Maurer     }
579*5f094258SMatthew Maurer     #[cfg(not(CONFIG_DEBUG_FS))]
580*5f094258SMatthew Maurer     fn into_entry(self) {}
581*5f094258SMatthew Maurer }
582*5f094258SMatthew Maurer 
583*5f094258SMatthew Maurer impl<'data> ScopedDir<'data, 'static> {
584*5f094258SMatthew Maurer     // This is safe, but intentionally not exported due to footgun status. A ScopedDir with no
585*5f094258SMatthew Maurer     // parent will never be released by default, and needs to have its entry extracted and used
586*5f094258SMatthew Maurer     // somewhere.
587*5f094258SMatthew Maurer     fn new(name: &CStr) -> ScopedDir<'data, 'static> {
588*5f094258SMatthew Maurer         ScopedDir {
589*5f094258SMatthew Maurer             #[cfg(CONFIG_DEBUG_FS)]
590*5f094258SMatthew Maurer             entry: ManuallyDrop::new(Entry::dir(name, None)),
591*5f094258SMatthew Maurer             _phantom: PhantomData,
592*5f094258SMatthew Maurer         }
593*5f094258SMatthew Maurer     }
594*5f094258SMatthew Maurer }
595