xref: /linux/rust/kernel/debugfs.rs (revision 5e40b591cb46c0379d5406fa5548c9b2a3801353)
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 use crate::prelude::*;
12 use crate::str::CStr;
13 #[cfg(CONFIG_DEBUG_FS)]
14 use crate::sync::Arc;
15 use core::marker::PhantomPinned;
16 use core::ops::Deref;
17 
18 mod traits;
19 pub use traits::Writer;
20 
21 mod file_ops;
22 use file_ops::{FileOps, ReadFile};
23 #[cfg(CONFIG_DEBUG_FS)]
24 mod entry;
25 #[cfg(CONFIG_DEBUG_FS)]
26 use entry::Entry;
27 
28 /// Owning handle to a DebugFS directory.
29 ///
30 /// The directory in the filesystem represented by [`Dir`] will be removed when handle has been
31 /// dropped *and* all children have been removed.
32 // If we have a parent, we hold a reference to it in the `Entry`. This prevents the `dentry`
33 // we point to from being cleaned up if our parent `Dir`/`Entry` is dropped before us.
34 //
35 // The `None` option indicates that the `Arc` could not be allocated, so our children would not be
36 // able to refer to us. In this case, we need to silently fail. All future child directories/files
37 // will silently fail as well.
38 #[derive(Clone)]
39 pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry>>);
40 
41 impl Dir {
42     /// Create a new directory in DebugFS. If `parent` is [`None`], it will be created at the root.
43     fn create(name: &CStr, parent: Option<&Dir>) -> Self {
44         #[cfg(CONFIG_DEBUG_FS)]
45         {
46             let parent_entry = match parent {
47                 // If the parent couldn't be allocated, just early-return
48                 Some(Dir(None)) => return Self(None),
49                 Some(Dir(Some(entry))) => Some(entry.clone()),
50                 None => None,
51             };
52             Self(
53                 // If Arc creation fails, the `Entry` will be dropped, so the directory will be
54                 // cleaned up.
55                 Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(),
56             )
57         }
58         #[cfg(not(CONFIG_DEBUG_FS))]
59         Self()
60     }
61 
62     /// Creates a DebugFS file which will own the data produced by the initializer provided in
63     /// `data`.
64     fn create_file<'a, T, E: 'a>(
65         &'a self,
66         name: &'a CStr,
67         data: impl PinInit<T, E> + 'a,
68         file_ops: &'static FileOps<T>,
69     ) -> impl PinInit<File<T>, E> + 'a
70     where
71         T: Sync + 'static,
72     {
73         let scope = Scope::<T>::new(data, move |data| {
74             #[cfg(CONFIG_DEBUG_FS)]
75             if let Some(parent) = &self.0 {
76                 // SAFETY: Because data derives from a scope, and our entry will be dropped before
77                 // the data is dropped, it is guaranteed to outlive the entry we return.
78                 unsafe { Entry::dynamic_file(name, parent.clone(), data, file_ops) }
79             } else {
80                 Entry::empty()
81             }
82         });
83         try_pin_init! {
84             File {
85                 scope <- scope
86             } ? E
87         }
88     }
89 
90     /// Create a new directory in DebugFS at the root.
91     ///
92     /// # Examples
93     ///
94     /// ```
95     /// # use kernel::c_str;
96     /// # use kernel::debugfs::Dir;
97     /// let debugfs = Dir::new(c_str!("parent"));
98     /// ```
99     pub fn new(name: &CStr) -> Self {
100         Dir::create(name, None)
101     }
102 
103     /// Creates a subdirectory within this directory.
104     ///
105     /// # Examples
106     ///
107     /// ```
108     /// # use kernel::c_str;
109     /// # use kernel::debugfs::Dir;
110     /// let parent = Dir::new(c_str!("parent"));
111     /// let child = parent.subdir(c_str!("child"));
112     /// ```
113     pub fn subdir(&self, name: &CStr) -> Self {
114         Dir::create(name, Some(self))
115     }
116 
117     /// Creates a read-only file in this directory.
118     ///
119     /// The file's contents are produced by invoking [`Writer::write`] on the value initialized by
120     /// `data`.
121     ///
122     /// # Examples
123     ///
124     /// ```
125     /// # use kernel::c_str;
126     /// # use kernel::debugfs::Dir;
127     /// # use kernel::prelude::*;
128     /// # let dir = Dir::new(c_str!("my_debugfs_dir"));
129     /// let file = KBox::pin_init(dir.read_only_file(c_str!("foo"), 200), GFP_KERNEL)?;
130     /// // "my_debugfs_dir/foo" now contains the number 200.
131     /// // The file is removed when `file` is dropped.
132     /// # Ok::<(), Error>(())
133     /// ```
134     pub fn read_only_file<'a, T, E: 'a>(
135         &'a self,
136         name: &'a CStr,
137         data: impl PinInit<T, E> + 'a,
138     ) -> impl PinInit<File<T>, E> + 'a
139     where
140         T: Writer + Send + Sync + 'static,
141     {
142         let file_ops = &<T as ReadFile<_>>::FILE_OPS;
143         self.create_file(name, data, file_ops)
144     }
145 }
146 
147 #[pin_data]
148 /// Handle to a DebugFS scope, which ensures that attached `data` will outlive the provided
149 /// [`Entry`] without moving.
150 /// Currently, this is used to back [`File`] so that its `read` and/or `write` implementations
151 /// can assume that their backing data is still alive.
152 struct Scope<T> {
153     // This order is load-bearing for drops - `_entry` must be dropped before `data`.
154     #[cfg(CONFIG_DEBUG_FS)]
155     _entry: Entry,
156     #[pin]
157     data: T,
158     // Even if `T` is `Unpin`, we still can't allow it to be moved.
159     #[pin]
160     _pin: PhantomPinned,
161 }
162 
163 #[pin_data]
164 /// Handle to a DebugFS file, owning its backing data.
165 ///
166 /// When dropped, the DebugFS file will be removed and the attached data will be dropped.
167 pub struct File<T> {
168     #[pin]
169     scope: Scope<T>,
170 }
171 
172 #[cfg(not(CONFIG_DEBUG_FS))]
173 impl<'b, T: 'b> Scope<T> {
174     fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
175     where
176         F: for<'a> FnOnce(&'a T) + 'b,
177     {
178         try_pin_init! {
179             Self {
180                 data <- data,
181                 _pin: PhantomPinned
182             } ? E
183         }
184         .pin_chain(|scope| {
185             init(&scope.data);
186             Ok(())
187         })
188     }
189 }
190 
191 #[cfg(CONFIG_DEBUG_FS)]
192 impl<'b, T: 'b> Scope<T> {
193     fn entry_mut(self: Pin<&mut Self>) -> &mut Entry {
194         // SAFETY: _entry is not structurally pinned.
195         unsafe { &mut Pin::into_inner_unchecked(self)._entry }
196     }
197 
198     fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
199     where
200         F: for<'a> FnOnce(&'a T) -> Entry + 'b,
201     {
202         try_pin_init! {
203             Self {
204                 _entry: Entry::empty(),
205                 data <- data,
206                 _pin: PhantomPinned
207             } ? E
208         }
209         .pin_chain(|scope| {
210             *scope.entry_mut() = init(&scope.data);
211             Ok(())
212         })
213     }
214 }
215 
216 impl<T> Deref for Scope<T> {
217     type Target = T;
218     fn deref(&self) -> &T {
219         &self.data
220     }
221 }
222 
223 impl<T> Deref for File<T> {
224     type Target = T;
225     fn deref(&self) -> &T {
226         &self.scope
227     }
228 }
229