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