|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +// Copyright (C) 2025 Google LLC. |
| 3 | + |
| 4 | +//! DebugFS Abstraction |
| 5 | +//! |
| 6 | +//! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h) |
| 7 | +
|
| 8 | +// When DebugFS is disabled, many parameters are dead. Linting for this isn't helpful. |
| 9 | +#![cfg_attr(not(CONFIG_DEBUG_FS), allow(unused_variables))] |
| 10 | + |
| 11 | +#[cfg(CONFIG_DEBUG_FS)] |
| 12 | +use crate::prelude::*; |
| 13 | +use crate::str::CStr; |
| 14 | +#[cfg(CONFIG_DEBUG_FS)] |
| 15 | +use crate::sync::Arc; |
| 16 | + |
| 17 | +#[cfg(CONFIG_DEBUG_FS)] |
| 18 | +mod entry; |
| 19 | +#[cfg(CONFIG_DEBUG_FS)] |
| 20 | +use entry::Entry; |
| 21 | + |
| 22 | +/// Owning handle to a DebugFS directory. |
| 23 | +/// |
| 24 | +/// The directory in the filesystem represented by [`Dir`] will be removed when handle has been |
| 25 | +/// dropped *and* all children have been removed. |
| 26 | +// If we have a parent, we hold a reference to it in the `Entry`. This prevents the `dentry` |
| 27 | +// we point to from being cleaned up if our parent `Dir`/`Entry` is dropped before us. |
| 28 | +// |
| 29 | +// The `None` option indicates that the `Arc` could not be allocated, so our children would not be |
| 30 | +// able to refer to us. In this case, we need to silently fail. All future child directories/files |
| 31 | +// will silently fail as well. |
| 32 | +#[derive(Clone)] |
| 33 | +pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry>>); |
| 34 | + |
| 35 | +impl Dir { |
| 36 | + /// Create a new directory in DebugFS. If `parent` is [`None`], it will be created at the root. |
| 37 | + fn create(name: &CStr, parent: Option<&Dir>) -> Self { |
| 38 | + #[cfg(CONFIG_DEBUG_FS)] |
| 39 | + { |
| 40 | + let parent_entry = match parent { |
| 41 | + // If the parent couldn't be allocated, just early-return |
| 42 | + Some(Dir(None)) => return Self(None), |
| 43 | + Some(Dir(Some(entry))) => Some(entry.clone()), |
| 44 | + None => None, |
| 45 | + }; |
| 46 | + Self( |
| 47 | + // If Arc creation fails, the `Entry` will be dropped, so the directory will be |
| 48 | + // cleaned up. |
| 49 | + Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(), |
| 50 | + ) |
| 51 | + } |
| 52 | + #[cfg(not(CONFIG_DEBUG_FS))] |
| 53 | + Self() |
| 54 | + } |
| 55 | + |
| 56 | + /// Create a new directory in DebugFS at the root. |
| 57 | + /// |
| 58 | + /// # Examples |
| 59 | + /// |
| 60 | + /// ``` |
| 61 | + /// # use kernel::c_str; |
| 62 | + /// # use kernel::debugfs::Dir; |
| 63 | + /// let debugfs = Dir::new(c_str!("parent")); |
| 64 | + /// ``` |
| 65 | + pub fn new(name: &CStr) -> Self { |
| 66 | + Dir::create(name, None) |
| 67 | + } |
| 68 | + |
| 69 | + /// Creates a subdirectory within this directory. |
| 70 | + /// |
| 71 | + /// # Examples |
| 72 | + /// |
| 73 | + /// ``` |
| 74 | + /// # use kernel::c_str; |
| 75 | + /// # use kernel::debugfs::Dir; |
| 76 | + /// let parent = Dir::new(c_str!("parent")); |
| 77 | + /// let child = parent.subdir(c_str!("child")); |
| 78 | + /// ``` |
| 79 | + pub fn subdir(&self, name: &CStr) -> Self { |
| 80 | + Dir::create(name, Some(self)) |
| 81 | + } |
| 82 | +} |
0 commit comments