mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge tag 'driver-core-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core
Pull driver core updates from Danilo Krummrich:
"Auxiliary:
- Drop call to dev_pm_domain_detach() in auxiliary_bus_probe()
- Optimize logic of auxiliary_match_id()
Rust:
- Auxiliary:
- Use primitive C types from prelude
- DebugFs:
- Add debugfs support for simple read/write files and custom
callbacks through a File-type-based and directory-scope-based
API
- Sample driver code for the File-type-based API
- Sample module code for the directory-scope-based API
- I/O:
- Add io::poll module and implement Rust specific
read_poll_timeout() helper
- IRQ:
- Implement support for threaded and non-threaded device IRQs
based on (&Device<Bound>, IRQ number) tuples (IrqRequest)
- Provide &Device<Bound> cookie in IRQ handlers
- PCI:
- Support IRQ requests from IRQ vectors for a specific
pci::Device<Bound>
- Implement accessors for subsystem IDs, revision, devid and
resource start
- Provide dedicated pci::Vendor and pci::Class types for vendor
and class ID numbers
- Implement Display to print actual vendor and class names; Debug
to print the raw ID numbers
- Add pci::DeviceId::from_class_and_vendor() helper
- Use primitive C types from prelude
- Various minor inline and (safety) comment improvements
- Platform:
- Support IRQ requests from IRQ vectors for a specific
platform::Device<Bound>
- Nova:
- Use pci::DeviceId::from_class_and_vendor() to avoid probing
non-display/compute PCI functions
- Misc:
- Add helper for cpu_relax()
- Update ARef import from sync::aref
sysfs:
- Remove bin_attrs_new field from struct attribute_group
- Remove read_new() and write_new() from struct bin_attribute
Misc:
- Document potential race condition in get_dev_from_fwnode()
- Constify node_group argument in software node registration
functions
- Fix order of kernel-doc parameters in various functions
- Set power.no_pm flag for faux devices
- Set power.no_callbacks flag along with the power.no_pm flag
- Constify the pmu_bus bus type
- Minor spelling fixes"
* tag 'driver-core-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: (43 commits)
rust: pci: display symbolic PCI vendor names
rust: pci: display symbolic PCI class names
rust: pci: fix incorrect platform reference in PCI driver probe doc comment
rust: pci: fix incorrect platform reference in PCI driver unbind doc comment
perf: make pmu_bus const
samples: rust: Add scoped debugfs sample driver
rust: debugfs: Add support for scoped directories
samples: rust: Add debugfs sample driver
rust: debugfs: Add support for callback-based files
rust: debugfs: Add support for writable files
rust: debugfs: Add support for read-only files
rust: debugfs: Add initial support for directories
driver core: auxiliary bus: Optimize logic of auxiliary_match_id()
driver core: auxiliary bus: Drop dev_pm_domain_detach() call
driver core: Fix order of the kernel-doc parameters
driver core: get_dev_from_fwnode(): document potential race
drivers: base: fix "publically"->"publicly"
driver core/PM: Set power.no_callbacks along with power.no_pm
driver core: faux: Set power.no_pm for faux devices
rust: pci: inline several tiny functions
...
This commit is contained in:
@@ -62,6 +62,28 @@ 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_DEBUGFS_SCOPED
|
||||
tristate "Scoped DebugFS Test Module"
|
||||
depends on DEBUG_FS
|
||||
help
|
||||
This option builds the Rust Scoped DebugFS Test module sample.
|
||||
|
||||
To compile this as a module, choose M here:
|
||||
the module will be called rust_debugfs_scoped.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SAMPLE_RUST_DRIVER_PCI
|
||||
tristate "PCI Driver"
|
||||
depends on PCI
|
||||
|
||||
@@ -4,6 +4,8 @@ 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_DEBUGFS_SCOPED) += rust_debugfs_scoped.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
|
||||
|
||||
151
samples/rust/rust_debugfs.rs
Normal file
151
samples/rust/rust_debugfs.rs
Normal file
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
134
samples/rust/rust_debugfs_scoped.rs
Normal file
134
samples/rust/rust_debugfs_scoped.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
// Copyright (C) 2025 Google LLC.
|
||||
|
||||
//! Sample DebugFS exporting platform driver that demonstrates the use of
|
||||
//! `Scope::dir` to create a variety of files without the need to separately
|
||||
//! track them all.
|
||||
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
use kernel::debugfs::{Dir, Scope};
|
||||
use kernel::prelude::*;
|
||||
use kernel::sync::Mutex;
|
||||
use kernel::{c_str, new_mutex, str::CString};
|
||||
|
||||
module! {
|
||||
type: RustScopedDebugFs,
|
||||
name: "rust_debugfs_scoped",
|
||||
authors: ["Matthew Maurer"],
|
||||
description: "Rust Scoped DebugFS usage sample",
|
||||
license: "GPL",
|
||||
}
|
||||
|
||||
fn remove_file_write(
|
||||
mod_data: &ModuleData,
|
||||
reader: &mut kernel::uaccess::UserSliceReader,
|
||||
) -> Result {
|
||||
let mut buf = [0u8; 128];
|
||||
if reader.len() >= buf.len() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
let n = reader.len();
|
||||
reader.read_slice(&mut buf[..n])?;
|
||||
|
||||
let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
|
||||
let nul_idx = s.len();
|
||||
buf[nul_idx] = 0;
|
||||
let to_remove = CStr::from_bytes_with_nul(&buf[..nul_idx + 1]).map_err(|_| EINVAL)?;
|
||||
mod_data
|
||||
.devices
|
||||
.lock()
|
||||
.retain(|device| device.name.as_bytes() != to_remove.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_file_write(
|
||||
mod_data: &ModuleData,
|
||||
reader: &mut kernel::uaccess::UserSliceReader,
|
||||
) -> Result {
|
||||
let mut buf = [0u8; 128];
|
||||
if reader.len() > buf.len() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
let n = reader.len();
|
||||
reader.read_slice(&mut buf[..n])?;
|
||||
|
||||
let mut nums = KVec::new();
|
||||
|
||||
let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
|
||||
let mut items = s.split_whitespace();
|
||||
let name_str = items.next().ok_or(EINVAL)?;
|
||||
let name = CString::try_from_fmt(fmt!("{name_str}"))?;
|
||||
let file_name = CString::try_from_fmt(fmt!("{name_str}"))?;
|
||||
for sub in items {
|
||||
nums.push(
|
||||
AtomicUsize::new(sub.parse().map_err(|_| EINVAL)?),
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
}
|
||||
|
||||
let scope = KBox::pin_init(
|
||||
mod_data
|
||||
.device_dir
|
||||
.scope(DeviceData { name, nums }, &file_name, |dev_data, dir| {
|
||||
for (idx, val) in dev_data.nums.iter().enumerate() {
|
||||
let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else {
|
||||
return;
|
||||
};
|
||||
dir.read_write_file(&name, val);
|
||||
}
|
||||
}),
|
||||
GFP_KERNEL,
|
||||
)?;
|
||||
(*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct RustScopedDebugFs {
|
||||
_data: Pin<KBox<Scope<ModuleData>>>,
|
||||
}
|
||||
|
||||
#[pin_data]
|
||||
struct ModuleData {
|
||||
device_dir: Dir,
|
||||
#[pin]
|
||||
devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,
|
||||
}
|
||||
|
||||
impl ModuleData {
|
||||
fn init(device_dir: Dir) -> impl PinInit<Self> {
|
||||
pin_init! {
|
||||
Self {
|
||||
device_dir: device_dir,
|
||||
devices <- new_mutex!(KVec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceData {
|
||||
name: CString,
|
||||
nums: KVec<AtomicUsize>,
|
||||
}
|
||||
|
||||
fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ {
|
||||
base_dir.scope(
|
||||
ModuleData::init(dyn_dirs),
|
||||
c_str!("control"),
|
||||
|data, dir| {
|
||||
dir.write_only_callback_file(c_str!("create"), data, &create_file_write);
|
||||
dir.write_only_callback_file(c_str!("remove"), data, &remove_file_write);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
impl kernel::Module for RustScopedDebugFs {
|
||||
fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
|
||||
let base_dir = Dir::new(c_str!("rust_scoped_debugfs"));
|
||||
let dyn_dirs = base_dir.subdir(c_str!("dynamic"));
|
||||
Ok(Self {
|
||||
_data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
|
||||
|
||||
use kernel::{
|
||||
bindings,
|
||||
device::Core,
|
||||
dma::{CoherentAllocation, Device, DmaMask},
|
||||
pci,
|
||||
@@ -45,10 +44,7 @@ kernel::pci_device_table!(
|
||||
PCI_TABLE,
|
||||
MODULE_PCI_TABLE,
|
||||
<DmaSampleDriver as pci::Driver>::IdInfo,
|
||||
[(
|
||||
pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
|
||||
()
|
||||
)]
|
||||
[(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
|
||||
);
|
||||
|
||||
impl pci::Driver for DmaSampleDriver {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
|
||||
|
||||
use kernel::{
|
||||
auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, InPlaceModule,
|
||||
auxiliary, c_str, device::Core, driver, error::Error, pci, prelude::*, InPlaceModule,
|
||||
};
|
||||
|
||||
use pin_init::PinInit;
|
||||
@@ -50,10 +50,7 @@ kernel::pci_device_table!(
|
||||
PCI_TABLE,
|
||||
MODULE_PCI_TABLE,
|
||||
<ParentDriver as pci::Driver>::IdInfo,
|
||||
[(
|
||||
pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
|
||||
()
|
||||
)]
|
||||
[(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
|
||||
);
|
||||
|
||||
impl pci::Driver for ParentDriver {
|
||||
@@ -81,11 +78,12 @@ impl ParentDriver {
|
||||
let parent = adev.parent().ok_or(EINVAL)?;
|
||||
let pdev: &pci::Device = parent.try_into()?;
|
||||
|
||||
let vendor = pdev.vendor_id();
|
||||
dev_info!(
|
||||
adev.as_ref(),
|
||||
"Connect auxiliary {} with parent: VendorID={:#x}, DeviceID={:#x}\n",
|
||||
"Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n",
|
||||
adev.id(),
|
||||
pdev.vendor_id(),
|
||||
vendor,
|
||||
pdev.device_id()
|
||||
);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//!
|
||||
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
|
||||
|
||||
use kernel::{bindings, c_str, device::Core, devres::Devres, pci, prelude::*, types::ARef};
|
||||
use kernel::{c_str, device::Core, devres::Devres, pci, prelude::*, sync::aref::ARef};
|
||||
|
||||
struct Regs;
|
||||
|
||||
@@ -38,7 +38,7 @@ kernel::pci_device_table!(
|
||||
MODULE_PCI_TABLE,
|
||||
<SampleDriver as pci::Driver>::IdInfo,
|
||||
[(
|
||||
pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
|
||||
pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5),
|
||||
TestIndex::NO_EVENTFD
|
||||
)]
|
||||
);
|
||||
@@ -66,10 +66,11 @@ impl pci::Driver for SampleDriver {
|
||||
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
|
||||
|
||||
fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
|
||||
let vendor = pdev.vendor_id();
|
||||
dev_dbg!(
|
||||
pdev.as_ref(),
|
||||
"Probe Rust PCI driver sample (PCI ID: 0x{:x}, 0x{:x}).\n",
|
||||
pdev.vendor_id(),
|
||||
"Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
|
||||
vendor,
|
||||
pdev.device_id()
|
||||
);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ use kernel::{
|
||||
of, platform,
|
||||
prelude::*,
|
||||
str::CString,
|
||||
types::ARef,
|
||||
sync::aref::ARef,
|
||||
};
|
||||
|
||||
struct SampleDriver {
|
||||
|
||||
Reference in New Issue
Block a user