Provides an example of using the Rust DebugFS bindings.
Signed-off-by: Matthew Maurer <mmaurer@google.com>
---
MAINTAINERS | 1 +
samples/rust/Kconfig | 11 +++
samples/rust/Makefile | 1 +
samples/rust/rust_debugfs.rs | 182 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 195 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 1427af9d9779b1f6463409f4392e2900438bdc2a..0d9cb77b54b4608a0e1006ae45761ed7440495ba 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7376,6 +7376,7 @@ F: rust/kernel/devres.rs
F: rust/kernel/driver.rs
F: rust/kernel/faux.rs
F: rust/kernel/platform.rs
+F: samples/rust/rust_debugfs.rs
F: samples/rust/rust_driver_platform.rs
F: samples/rust/rust_driver_faux.rs
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 7f7371a004ee0a8f67dca99c836596709a70c4fa..01101db41ae31b08a86d048cdd27da8ef9bb23a2 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -62,6 +62,17 @@ config SAMPLE_RUST_DMA
If unsure, say N.
+config SAMPLE_RUST_DEBUGFS
+ tristate "DebugFS Test Module"
+ depends on DEBUG_FS
+ help
+ This option builds the Rust DebugFS Test module sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_debugfs.
+
+ If unsure, say N.
+
config SAMPLE_RUST_DRIVER_PCI
tristate "PCI Driver"
depends on PCI
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index bd2faad63b4f3befe7d1ed5139fe25c7a8b6d7f6..61276222a99f8cc6d7f84c26d0533b30815ebadd 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -4,6 +4,7 @@ ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
+obj-$(CONFIG_SAMPLE_RUST_DEBUGFS) += rust_debugfs.o
obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o
diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs
new file mode 100644
index 0000000000000000000000000000000000000000..21fbf26f7ec07fabad782915046da3cdf52b03b3
--- /dev/null
+++ b/samples/rust/rust_debugfs.rs
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! Sample DebugFS exporting platform driver
+//!
+//! To successfully probe this driver with ACPI, use an ssdt that looks like
+//!
+//! ```dsl
+//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001)
+//!{
+//! Scope (\_SB)
+//! {
+//! Device (T432)
+//! {
+//! Name (_HID, "LNUXDEBF") // ACPI hardware ID to match
+//! Name (_UID, 1)
+//! Name (_STA, 0x0F) // Device present, enabled
+//! Name (_DSD, Package () { // Sample attribute
+//! ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+//! Package() {
+//! Package(2) {"compatible", "sample-debugfs"}
+//! }
+//! })
+//! Name (_CRS, ResourceTemplate ()
+//! {
+//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
+//! })
+//! }
+//! }
+//!}
+//! ```
+
+use core::sync::atomic::AtomicUsize;
+use core::sync::atomic::Ordering;
+use kernel::c_str;
+use kernel::debugfs::{Dir, File};
+use kernel::new_mutex;
+use kernel::prelude::*;
+use kernel::sync::Mutex;
+
+use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef};
+
+kernel::module_platform_driver! {
+ type: Wrapper,
+ name: "rust_debugfs",
+ authors: ["Matthew Maurer"],
+ description: "Rust DebugFS usage sample",
+ license: "GPL",
+}
+
+// This data structure would be unlikely to be there in a real driver - it's to hook up mutation
+// that would normally be driven by whatever the driver was actually servicing and show how that
+// would work. We're assuming here that those methods would have access to a `&RustDebugFs`.
+#[pin_data]
+struct Wrapper {
+ _dir: Dir,
+ #[pin]
+ _wrapped: File<File<RustDebugFs>>,
+}
+
+#[pin_data]
+struct RustDebugFs {
+ pdev: ARef<platform::Device>,
+ // As we only hold these for drop effect (to remove the directory/files) we have a leading
+ // underscore to indicate to the compiler that we don't expect to use this field directly.
+ _debugfs: Dir,
+ #[pin]
+ _compatible: File<CString>,
+ #[pin]
+ counter: File<File<AtomicUsize>>,
+ #[pin]
+ inner: File<Mutex<Inner>>,
+}
+
+#[derive(Debug)]
+struct Inner {
+ x: u32,
+ y: u32,
+}
+
+kernel::of_device_table!(
+ OF_TABLE,
+ MODULE_OF_TABLE,
+ <Wrapper as platform::Driver>::IdInfo,
+ [(of::DeviceId::new(c_str!("test,rust-debugfs-device")), ())]
+);
+
+kernel::acpi_device_table!(
+ ACPI_TABLE,
+ MODULE_ACPI_TABLE,
+ <Wrapper as platform::Driver>::IdInfo,
+ [(acpi::DeviceId::new(c_str!("LNUXDEBF")), ())]
+);
+
+impl platform::Driver for Wrapper {
+ type IdInfo = ();
+ const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+ const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+
+ fn probe(
+ pdev: &platform::Device<Core>,
+ _info: Option<&Self::IdInfo>,
+ ) -> Result<Pin<KBox<Self>>> {
+ KBox::try_pin_init(Wrapper::new(RustDebugFs::new(pdev)), GFP_KERNEL)
+ }
+}
+
+impl Wrapper {
+ /// This builds two debugfs files that would be unusual to exist in the real world to emulate
+ /// actions taken servicing the device. They trigger their action when the debugfs file is
+ /// opened.
+ fn build_control<I: PinInit<RustDebugFs, Error>>(
+ dir: &Dir,
+ init: I,
+ ) -> impl PinInit<File<File<RustDebugFs>>, Error> + use<'_, I> {
+ let swap = dir.fmt_file(c_str!("swap"), init, &|sample, fmt| {
+ let mut guard = sample.inner.lock();
+ let x = guard.x;
+ guard.x = guard.y;
+ guard.y = x;
+ writeln!(fmt, "Swapped!")
+ });
+
+ dir.fmt_file(c_str!("add_counter"), swap, &|sample, fmt| {
+ let mut inner = sample.inner.lock();
+ inner.x += sample.counter.load(Ordering::Relaxed) as u32;
+ writeln!(fmt, "Counter added!")
+ })
+ }
+
+ fn new<I: PinInit<RustDebugFs, Error>>(init: I) -> impl PinInit<Self, Error> + use<I> {
+ let dir = Dir::new(c_str!("sample_control"));
+ try_pin_init! {
+ Self {
+ _wrapped <- Wrapper::build_control(&dir, init),
+ _dir: dir,
+ } ? Error
+ }
+ }
+}
+
+impl RustDebugFs {
+ fn build_counter(dir: &Dir) -> impl PinInit<File<File<AtomicUsize>>> + use<'_> {
+ let counter = dir.fmt_file(c_str!("counter"), AtomicUsize::new(0), &|counter, fmt| {
+ writeln!(fmt, "{}", counter.load(Ordering::Relaxed))
+ });
+ dir.fmt_file(c_str!("inc_counter"), counter, &|counter, fmt| {
+ writeln!(fmt, "{}", counter.fetch_add(1, Ordering::Relaxed))
+ })
+ }
+
+ fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + use<'_> {
+ dir.fmt_file(
+ c_str!("pair"),
+ new_mutex!(Inner { x: 3, y: 10 }),
+ &|i, fmt| writeln!(fmt, "{:?}", *i.lock()),
+ )
+ }
+
+ fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + use<'_> {
+ let debugfs = Dir::new(c_str!("sample_debugfs"));
+ let dev = pdev.as_ref();
+
+ try_pin_init! {
+ Self {
+ _compatible <- debugfs.fmt_file(
+ c_str!("compatible"),
+ dev.fwnode()
+ .ok_or(ENOENT)?
+ .property_read::<CString>(c_str!("compatible"))
+ .required_by(dev)?,
+ &|cs, w| writeln!(w, "{cs:?}"),
+ ),
+ counter <- Self::build_counter(&debugfs),
+ inner <- Self::build_inner(&debugfs),
+ _debugfs: debugfs,
+ pdev: pdev.into(),
+ }
+ }
+ }
+}
--
2.50.0.727.gbf7dc18ff4-goog
On Wed Jul 9, 2025 at 9:09 PM CEST, Matthew Maurer wrote: > +// This data structure would be unlikely to be there in a real driver - it's to hook up mutation > +// that would normally be driven by whatever the driver was actually servicing and show how that > +// would work. We're assuming here that those methods would have access to a `&RustDebugFs`. Please see also [1]. I think you're making this too complicated, and due to the missing write() support you have to create workarounds because of that, which serve as a bad reference. Keep it simple, Create some driver private data in probe() and export a couple of fields of this driver private data through debugfs. If you really want to showcase that those values can change, queue some work and modify the counter and / or the Inner type that is protected by a mutex. [1] https://lore.kernel.org/lkml/DB7US8G7ISG0.20430M3P7I0K0@kernel.org/ > +#[pin_data] > +struct Wrapper { > + _dir: Dir, > + #[pin] > + _wrapped: File<File<RustDebugFs>>, > +} > + > +#[pin_data] > +struct RustDebugFs { > + pdev: ARef<platform::Device>, > + // As we only hold these for drop effect (to remove the directory/files) we have a leading > + // underscore to indicate to the compiler that we don't expect to use this field directly. > + _debugfs: Dir, > + #[pin] > + _compatible: File<CString>, > + #[pin] > + counter: File<File<AtomicUsize>>, > + #[pin] > + inner: File<Mutex<Inner>>, > +} > + > +#[derive(Debug)] > +struct Inner { > + x: u32, > + y: u32, > +} > + > +kernel::of_device_table!( > + OF_TABLE, > + MODULE_OF_TABLE, > + <Wrapper as platform::Driver>::IdInfo, > + [(of::DeviceId::new(c_str!("test,rust-debugfs-device")), ())] > +); I don't think we need both, ACPI should be much simpler with QEMU. > +kernel::acpi_device_table!( > + ACPI_TABLE, > + MODULE_ACPI_TABLE, > + <Wrapper as platform::Driver>::IdInfo, > + [(acpi::DeviceId::new(c_str!("LNUXDEBF")), ())] > +); > + > +impl platform::Driver for Wrapper { > + type IdInfo = (); > + const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); > + const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); > + > + fn probe( > + pdev: &platform::Device<Core>, > + _info: Option<&Self::IdInfo>, > + ) -> Result<Pin<KBox<Self>>> { > + KBox::try_pin_init(Wrapper::new(RustDebugFs::new(pdev)), GFP_KERNEL) > + } > +}
> Keep it simple, Create some driver private data in probe() and export a couple > of fields of this driver private data through debugfs. > > If you really want to showcase that those values can change, queue some work and > modify the counter and / or the Inner type that is protected by a mutex. I've included an example of what a simplified sample of the sort you asked for might look like below. I'll include it in the updated series unless someone wants the original functionality. Provides an example of using the Rust DebugFS bindings. Signed-off-by: Matthew Maurer <mmaurer@google.com> --- MAINTAINERS | 1 + samples/rust/Kconfig | 11 +++ samples/rust/Makefile | 1 + samples/rust/rust_debugfs.rs | 136 +++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 samples/rust/rust_debugfs.rs diff --git a/MAINTAINERS b/MAINTAINERS index 1427af9d9779..0d9cb77b54b4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7376,6 +7376,7 @@ F: rust/kernel/devres.rs F: rust/kernel/driver.rs F: rust/kernel/faux.rs F: rust/kernel/platform.rs +F: samples/rust/rust_debugfs.rs F: samples/rust/rust_driver_platform.rs F: samples/rust/rust_driver_faux.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 7f7371a004ee..01101db41ae3 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -62,6 +62,17 @@ config SAMPLE_RUST_DMA If unsure, say N. +config SAMPLE_RUST_DEBUGFS + tristate "DebugFS Test Module" + depends on DEBUG_FS + help + This option builds the Rust DebugFS Test module sample. + + To compile this as a module, choose M here: + the module will be called rust_debugfs. + + If unsure, say N. + config SAMPLE_RUST_DRIVER_PCI tristate "PCI Driver" depends on PCI diff --git a/samples/rust/Makefile b/samples/rust/Makefile index bd2faad63b4f..61276222a99f 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -4,6 +4,7 @@ ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o +obj-$(CONFIG_SAMPLE_RUST_DEBUGFS) += rust_debugfs.o obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs new file mode 100644 index 000000000000..47b337f2e960 --- /dev/null +++ b/samples/rust/rust_debugfs.rs @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2025 Google LLC. + +//! Sample DebugFS exporting platform driver +//! +//! To successfully probe this driver with ACPI, use an ssdt that looks like +//! +//! ```dsl +//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001) +//! { +//! Scope (\_SB) +//! { +//! Device (T432) +//! { +//! Name (_HID, "LNUXDEBF") // ACPI hardware ID to match +//! Name (_UID, 1) +//! Name (_STA, 0x0F) // Device present, enabled +//! Name (_DSD, Package () { // Sample attribute +//! ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), +//! Package() { +//! Package(2) {"compatible", "sample-debugfs"} +//! } +//! }) +//! Name (_CRS, ResourceTemplate () +//! { +//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000) +//! }) +//! } +//! } +//! } +//! ``` + +use core::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering; +use kernel::c_str; +use kernel::debugfs::{Dir, File}; +use kernel::new_mutex; +use kernel::prelude::*; +use kernel::sync::Mutex; + +use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef}; + +kernel::module_platform_driver! { + type: RustDebugFs, + name: "rust_debugfs", + authors: ["Matthew Maurer"], + description: "Rust DebugFS usage sample", + license: "GPL", +} + +#[pin_data] +struct RustDebugFs { + pdev: ARef<platform::Device>, + // As we only hold these for drop effect (to remove the directory/files) we have a leading + // underscore to indicate to the compiler that we don't expect to use this field directly. + _debugfs: Dir, + #[pin] + _compatible: File<CString>, + #[pin] + counter: File<AtomicUsize>, + #[pin] + inner: File<Mutex<Inner>>, +} + +#[derive(Debug)] +struct Inner { + x: u32, + y: u32, +} + +kernel::acpi_device_table!( + ACPI_TABLE, + MODULE_ACPI_TABLE, + <RustDebugFs as platform::Driver>::IdInfo, + [(acpi::DeviceId::new(c_str!("LNUXDEBF")), ())] +); + +impl platform::Driver for RustDebugFs { + type IdInfo = (); + const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None; + const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); + + fn probe( + pdev: &platform::Device<Core>, + _info: Option<&Self::IdInfo>, + ) -> Result<Pin<KBox<Self>>> { + let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?; + // We can still mutate fields through the files which are atomic or mutexed: + result.counter.store(91, Ordering::Relaxed); + { + let mut guard = result.inner.lock(); + guard.x = guard.y; + guard.y = 42; + } + Ok(result) + } +} + +impl RustDebugFs { + fn build_counter(dir: &Dir) -> impl PinInit<File<AtomicUsize>> + use<'_> { + dir.fmt_file(c_str!("counter"), AtomicUsize::new(0), &|counter, fmt| { + writeln!(fmt, "{}", counter.load(Ordering::Relaxed)) + }) + } + + fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + use<'_> { + dir.fmt_file( + c_str!("pair"), + new_mutex!(Inner { x: 3, y: 10 }), + &|i, fmt| writeln!(fmt, "{:?}", *i.lock()), + ) + } + + fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + use<'_> { + let debugfs = Dir::new(c_str!("sample_debugfs")); + let dev = pdev.as_ref(); + + try_pin_init! { + Self { + _compatible <- debugfs.fmt_file( + c_str!("compatible"), + dev.fwnode() + .ok_or(ENOENT)? + .property_read::<CString>(c_str!("compatible")) + .required_by(dev)?, + &|cs, w| writeln!(w, "{cs:?}"), + ), + counter <- Self::build_counter(&debugfs), + inner <- Self::build_inner(&debugfs), + _debugfs: debugfs, + pdev: pdev.into(), + } + } + } +} -- 2.50.0.727.gbf7dc18ff4-goog
© 2016 - 2025 Red Hat, Inc.