xref: /linux/rust/kernel/debugfs.rs (revision 839dc1d15b9ba5318ed145b20efffcfa91c02a3d)
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::{Reader, Writer};
20 
21 mod file_ops;
22 use file_ops::{FileOps, ReadFile, ReadWriteFile, WriteFile};
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     /// Creates a read-write file in this directory.
147     ///
148     /// Reading the file uses the [`Writer`] implementation.
149     /// Writing to the file uses the [`Reader`] implementation.
150     pub fn read_write_file<'a, T, E: 'a>(
151         &'a self,
152         name: &'a CStr,
153         data: impl PinInit<T, E> + 'a,
154     ) -> impl PinInit<File<T>, E> + 'a
155     where
156         T: Writer + Reader + Send + Sync + 'static,
157     {
158         let file_ops = &<T as ReadWriteFile<_>>::FILE_OPS;
159         self.create_file(name, data, file_ops)
160     }
161 
162     /// Creates a write-only file in this directory.
163     ///
164     /// The file owns its backing data. Writing to the file uses the [`Reader`]
165     /// implementation.
166     ///
167     /// The file is removed when the returned [`File`] is dropped.
168     pub fn write_only_file<'a, T, E: 'a>(
169         &'a self,
170         name: &'a CStr,
171         data: impl PinInit<T, E> + 'a,
172     ) -> impl PinInit<File<T>, E> + 'a
173     where
174         T: Reader + Send + Sync + 'static,
175     {
176         self.create_file(name, data, &T::FILE_OPS)
177     }
178 }
179 
180 #[pin_data]
181 /// Handle to a DebugFS scope, which ensures that attached `data` will outlive the provided
182 /// [`Entry`] without moving.
183 /// Currently, this is used to back [`File`] so that its `read` and/or `write` implementations
184 /// can assume that their backing data is still alive.
185 struct Scope<T> {
186     // This order is load-bearing for drops - `_entry` must be dropped before `data`.
187     #[cfg(CONFIG_DEBUG_FS)]
188     _entry: Entry,
189     #[pin]
190     data: T,
191     // Even if `T` is `Unpin`, we still can't allow it to be moved.
192     #[pin]
193     _pin: PhantomPinned,
194 }
195 
196 #[pin_data]
197 /// Handle to a DebugFS file, owning its backing data.
198 ///
199 /// When dropped, the DebugFS file will be removed and the attached data will be dropped.
200 pub struct File<T> {
201     #[pin]
202     scope: Scope<T>,
203 }
204 
205 #[cfg(not(CONFIG_DEBUG_FS))]
206 impl<'b, T: 'b> Scope<T> {
207     fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
208     where
209         F: for<'a> FnOnce(&'a T) + 'b,
210     {
211         try_pin_init! {
212             Self {
213                 data <- data,
214                 _pin: PhantomPinned
215             } ? E
216         }
217         .pin_chain(|scope| {
218             init(&scope.data);
219             Ok(())
220         })
221     }
222 }
223 
224 #[cfg(CONFIG_DEBUG_FS)]
225 impl<'b, T: 'b> Scope<T> {
226     fn entry_mut(self: Pin<&mut Self>) -> &mut Entry {
227         // SAFETY: _entry is not structurally pinned.
228         unsafe { &mut Pin::into_inner_unchecked(self)._entry }
229     }
230 
231     fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b
232     where
233         F: for<'a> FnOnce(&'a T) -> Entry + 'b,
234     {
235         try_pin_init! {
236             Self {
237                 _entry: Entry::empty(),
238                 data <- data,
239                 _pin: PhantomPinned
240             } ? E
241         }
242         .pin_chain(|scope| {
243             *scope.entry_mut() = init(&scope.data);
244             Ok(())
245         })
246     }
247 }
248 
249 impl<T> Deref for Scope<T> {
250     type Target = T;
251     fn deref(&self) -> &T {
252         &self.data
253     }
254 }
255 
256 impl<T> Deref for File<T> {
257     type Target = T;
258     fn deref(&self) -> &T {
259         &self.scope
260     }
261 }
262