kernel/debugfs/
traits.rs

1// SPDX-License-Identifier: GPL-2.0
2// Copyright (C) 2025 Google LLC.
3
4//! Traits for rendering or updating values exported to DebugFS.
5
6use crate::{
7    alloc::Allocator,
8    fmt,
9    fs::file,
10    prelude::*,
11    sync::{
12        atomic::{
13            Atomic,
14            AtomicBasicOps,
15            AtomicType,
16            Relaxed, //
17        },
18        Arc,
19        Mutex, //
20    },
21    transmute::{
22        AsBytes,
23        FromBytes, //
24    },
25    uaccess::{
26        UserSliceReader,
27        UserSliceWriter, //
28    },
29};
30
31use core::{
32    ops::{
33        Deref,
34        DerefMut, //
35    },
36    str::FromStr,
37};
38
39/// A trait for types that can be written into a string.
40///
41/// This works very similarly to `Debug`, and is automatically implemented if `Debug` is
42/// implemented for a type. It is also implemented for any writable type inside a `Mutex`.
43///
44/// The derived implementation of `Debug` [may
45/// change](https://doc.rust-lang.org/std/fmt/trait.Debug.html#stability)
46/// between Rust versions, so if stability is key for your use case, please implement `Writer`
47/// explicitly instead.
48pub trait Writer {
49    /// Formats the value using the given formatter.
50    fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
51}
52
53impl<T: Writer> Writer for Mutex<T> {
54    fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        self.lock().write(f)
56    }
57}
58
59impl<T: fmt::Debug> Writer for T {
60    fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        writeln!(f, "{self:?}")
62    }
63}
64
65/// Trait for types that can be written out as binary.
66pub trait BinaryWriter {
67    /// Writes the binary form of `self` into `writer`.
68    ///
69    /// `offset` is the requested offset into the binary representation of `self`.
70    ///
71    /// On success, returns the number of bytes written in to `writer`.
72    fn write_to_slice(
73        &self,
74        writer: &mut UserSliceWriter,
75        offset: &mut file::Offset,
76    ) -> Result<usize>;
77}
78
79// Base implementation for any `T: AsBytes`.
80impl<T: AsBytes> BinaryWriter for T {
81    fn write_to_slice(
82        &self,
83        writer: &mut UserSliceWriter,
84        offset: &mut file::Offset,
85    ) -> Result<usize> {
86        writer.write_slice_file(self.as_bytes(), offset)
87    }
88}
89
90// Delegate for `Mutex<T>`: Support a `T` with an outer mutex.
91impl<T: BinaryWriter> BinaryWriter for Mutex<T> {
92    fn write_to_slice(
93        &self,
94        writer: &mut UserSliceWriter,
95        offset: &mut file::Offset,
96    ) -> Result<usize> {
97        let guard = self.lock();
98
99        guard.write_to_slice(writer, offset)
100    }
101}
102
103// Delegate for `Box<T, A>`: Support a `Box<T, A>` with no lock or an inner lock.
104impl<T, A> BinaryWriter for Box<T, A>
105where
106    T: BinaryWriter,
107    A: Allocator,
108{
109    fn write_to_slice(
110        &self,
111        writer: &mut UserSliceWriter,
112        offset: &mut file::Offset,
113    ) -> Result<usize> {
114        self.deref().write_to_slice(writer, offset)
115    }
116}
117
118// Delegate for `Pin<Box<T, A>>`: Support a `Pin<Box<T, A>>` with no lock or an inner lock.
119impl<T, A> BinaryWriter for Pin<Box<T, A>>
120where
121    T: BinaryWriter,
122    A: Allocator,
123{
124    fn write_to_slice(
125        &self,
126        writer: &mut UserSliceWriter,
127        offset: &mut file::Offset,
128    ) -> Result<usize> {
129        self.deref().write_to_slice(writer, offset)
130    }
131}
132
133// Delegate for `Arc<T>`: Support a `Arc<T>` with no lock or an inner lock.
134impl<T> BinaryWriter for Arc<T>
135where
136    T: BinaryWriter,
137{
138    fn write_to_slice(
139        &self,
140        writer: &mut UserSliceWriter,
141        offset: &mut file::Offset,
142    ) -> Result<usize> {
143        self.deref().write_to_slice(writer, offset)
144    }
145}
146
147// Delegate for `Vec<T, A>`.
148impl<T, A> BinaryWriter for Vec<T, A>
149where
150    T: AsBytes,
151    A: Allocator,
152{
153    fn write_to_slice(
154        &self,
155        writer: &mut UserSliceWriter,
156        offset: &mut file::Offset,
157    ) -> Result<usize> {
158        let slice = self.as_slice();
159
160        // SAFETY: `T: AsBytes` allows us to treat `&[T]` as `&[u8]`.
161        let buffer = unsafe {
162            core::slice::from_raw_parts(slice.as_ptr().cast(), core::mem::size_of_val(slice))
163        };
164
165        writer.write_slice_file(buffer, offset)
166    }
167}
168
169/// A trait for types that can be updated from a user slice.
170///
171/// This works similarly to `FromStr`, but operates on a `UserSliceReader` rather than a &str.
172///
173/// It is automatically implemented for all atomic integers, or any type that implements `FromStr`
174/// wrapped in a `Mutex`.
175pub trait Reader {
176    /// Updates the value from the given user slice.
177    fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result;
178}
179
180impl<T: FromStr + Unpin> Reader for Mutex<T> {
181    fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result {
182        let mut buf = [0u8; 128];
183        if reader.len() > buf.len() {
184            return Err(EINVAL);
185        }
186        let n = reader.len();
187        reader.read_slice(&mut buf[..n])?;
188
189        let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?;
190        let val = s.trim().parse::<T>().map_err(|_| EINVAL)?;
191        *self.lock() = val;
192        Ok(())
193    }
194}
195
196impl<T: AtomicType + FromStr> Reader for Atomic<T>
197where
198    T::Repr: AtomicBasicOps,
199{
200    fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result {
201        let mut buf = [0u8; 21]; // Enough for a 64-bit number.
202        if reader.len() > buf.len() {
203            return Err(EINVAL);
204        }
205        let n = reader.len();
206        reader.read_slice(&mut buf[..n])?;
207
208        let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?;
209        let val = s.trim().parse::<T>().map_err(|_| EINVAL)?;
210        self.store(val, Relaxed);
211        Ok(())
212    }
213}
214
215/// Trait for types that can be constructed from a binary representation.
216///
217/// See also [`BinaryReader`] for interior mutability.
218pub trait BinaryReaderMut {
219    /// Reads the binary form of `self` from `reader`.
220    ///
221    /// Same as [`BinaryReader::read_from_slice`], but takes a mutable reference.
222    ///
223    /// `offset` is the requested offset into the binary representation of `self`.
224    ///
225    /// On success, returns the number of bytes read from `reader`.
226    fn read_from_slice_mut(
227        &mut self,
228        reader: &mut UserSliceReader,
229        offset: &mut file::Offset,
230    ) -> Result<usize>;
231}
232
233// Base implementation for any `T: AsBytes + FromBytes`.
234impl<T: AsBytes + FromBytes> BinaryReaderMut for T {
235    fn read_from_slice_mut(
236        &mut self,
237        reader: &mut UserSliceReader,
238        offset: &mut file::Offset,
239    ) -> Result<usize> {
240        reader.read_slice_file(self.as_bytes_mut(), offset)
241    }
242}
243
244// Delegate for `Box<T, A>`: Support a `Box<T, A>` with an outer lock.
245impl<T: ?Sized + BinaryReaderMut, A: Allocator> BinaryReaderMut for Box<T, A> {
246    fn read_from_slice_mut(
247        &mut self,
248        reader: &mut UserSliceReader,
249        offset: &mut file::Offset,
250    ) -> Result<usize> {
251        self.deref_mut().read_from_slice_mut(reader, offset)
252    }
253}
254
255// Delegate for `Vec<T, A>`: Support a `Vec<T, A>` with an outer lock.
256impl<T, A> BinaryReaderMut for Vec<T, A>
257where
258    T: AsBytes + FromBytes,
259    A: Allocator,
260{
261    fn read_from_slice_mut(
262        &mut self,
263        reader: &mut UserSliceReader,
264        offset: &mut file::Offset,
265    ) -> Result<usize> {
266        let slice = self.as_mut_slice();
267
268        // SAFETY: `T: AsBytes + FromBytes` allows us to treat `&mut [T]` as `&mut [u8]`.
269        let buffer = unsafe {
270            core::slice::from_raw_parts_mut(
271                slice.as_mut_ptr().cast(),
272                core::mem::size_of_val(slice),
273            )
274        };
275
276        reader.read_slice_file(buffer, offset)
277    }
278}
279
280/// Trait for types that can be constructed from a binary representation.
281///
282/// See also [`BinaryReaderMut`] for the mutable version.
283pub trait BinaryReader {
284    /// Reads the binary form of `self` from `reader`.
285    ///
286    /// `offset` is the requested offset into the binary representation of `self`.
287    ///
288    /// On success, returns the number of bytes read from `reader`.
289    fn read_from_slice(
290        &self,
291        reader: &mut UserSliceReader,
292        offset: &mut file::Offset,
293    ) -> Result<usize>;
294}
295
296// Delegate for `Mutex<T>`: Support a `T` with an outer `Mutex`.
297impl<T: BinaryReaderMut + Unpin> BinaryReader for Mutex<T> {
298    fn read_from_slice(
299        &self,
300        reader: &mut UserSliceReader,
301        offset: &mut file::Offset,
302    ) -> Result<usize> {
303        let mut this = self.lock();
304
305        this.read_from_slice_mut(reader, offset)
306    }
307}
308
309// Delegate for `Box<T, A>`: Support a `Box<T, A>` with an inner lock.
310impl<T: ?Sized + BinaryReader, A: Allocator> BinaryReader for Box<T, A> {
311    fn read_from_slice(
312        &self,
313        reader: &mut UserSliceReader,
314        offset: &mut file::Offset,
315    ) -> Result<usize> {
316        self.deref().read_from_slice(reader, offset)
317    }
318}
319
320// Delegate for `Pin<Box<T, A>>`: Support a `Pin<Box<T, A>>` with an inner lock.
321impl<T: ?Sized + BinaryReader, A: Allocator> BinaryReader for Pin<Box<T, A>> {
322    fn read_from_slice(
323        &self,
324        reader: &mut UserSliceReader,
325        offset: &mut file::Offset,
326    ) -> Result<usize> {
327        self.deref().read_from_slice(reader, offset)
328    }
329}
330
331// Delegate for `Arc<T>`: Support an `Arc<T>` with an inner lock.
332impl<T: ?Sized + BinaryReader> BinaryReader for Arc<T> {
333    fn read_from_slice(
334        &self,
335        reader: &mut UserSliceReader,
336        offset: &mut file::Offset,
337    ) -> Result<usize> {
338        self.deref().read_from_slice(reader, offset)
339    }
340}