mirror of https://github.com/torvalds/linux.git
samples: rust: Add debugfs sample driver
Adds a new sample driver that demonstrates the debugfs APIs. The driver creates a directory in debugfs and populates it with a few files: - A read-only file that displays a fwnode property. - A read-write file that exposes an atomic counter. - A read-write file that exposes a custom struct. This sample serves as a basic example of how to use the `debugfs::Dir` and `debugfs::File` APIs to create and manage debugfs entries. Signed-off-by: Matthew Maurer <mmaurer@google.com> Tested-by: Dirk Behme <dirk.behme@de.bosch.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Link: https://lore.kernel.org/r/20250904-debugfs-rust-v11-5-7d12a165685a@google.com [ Change ACPI ID "LNUXDEBF" to "LNUXBEEF". - Danilo ] Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
parent
40ecc49466
commit
6f227d2137
|
|
@ -7496,6 +7496,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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,151 @@
|
|||
// 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, "LNUXBEEF") // 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::str::FromStr;
|
||||
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,
|
||||
}
|
||||
|
||||
impl FromStr for Inner {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
let mut parts = s.split_whitespace();
|
||||
let x = parts
|
||||
.next()
|
||||
.ok_or(EINVAL)?
|
||||
.parse::<u32>()
|
||||
.map_err(|_| EINVAL)?;
|
||||
let y = parts
|
||||
.next()
|
||||
.ok_or(EINVAL)?
|
||||
.parse::<u32>()
|
||||
.map_err(|_| EINVAL)?;
|
||||
if parts.next().is_some() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
Ok(Inner { x, y })
|
||||
}
|
||||
}
|
||||
|
||||
kernel::acpi_device_table!(
|
||||
ACPI_TABLE,
|
||||
MODULE_ACPI_TABLE,
|
||||
<RustDebugFs as platform::Driver>::IdInfo,
|
||||
[(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())]
|
||||
);
|
||||
|
||||
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>> + '_ {
|
||||
dir.read_write_file(c_str!("counter"), AtomicUsize::new(0))
|
||||
}
|
||||
|
||||
fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + '_ {
|
||||
dir.read_write_file(c_str!("pair"), new_mutex!(Inner { x: 3, y: 10 }))
|
||||
}
|
||||
|
||||
fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + '_ {
|
||||
let debugfs = Dir::new(c_str!("sample_debugfs"));
|
||||
let dev = pdev.as_ref();
|
||||
|
||||
try_pin_init! {
|
||||
Self {
|
||||
_compatible <- debugfs.read_only_file(
|
||||
c_str!("compatible"),
|
||||
dev.fwnode()
|
||||
.ok_or(ENOENT)?
|
||||
.property_read::<CString>(c_str!("compatible"))
|
||||
.required_by(dev)?,
|
||||
),
|
||||
counter <- Self::build_counter(&debugfs),
|
||||
inner <- Self::build_inner(&debugfs),
|
||||
_debugfs: debugfs,
|
||||
pdev: pdev.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue