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