* [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions
@ 2024-10-22 21:31 Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 01/16] rust: init: introduce `Opaque::try_ffi_init` Danilo Krummrich
` (18 more replies)
0 siblings, 19 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
This patch series implements the necessary Rust abstractions to implement
device drivers in Rust.
This includes some basic generalizations for driver registration, handling of ID
tables, MMIO operations and device resource handling.
Those generalizations are used to implement device driver support for two
busses, the PCI and platfrom bus (with OF IDs) in order to provide some evidence
that the generalizations work as intended.
The patch series also includes two patches adding two driver samples, one PCI
driver and one platform driver.
The PCI bits are motivated by the Nova driver project [1], but are used by at
least one more OOT driver (rnvme [2]).
The platform bits, besides adding some more evidence to the base abstractions,
are required by a few more OOT drivers aiming at going upstream, i.e. rvkms [3],
cpufreq-dt [4], asahi [5] and the i2c work from Fabien [6].
The patches of this series can also be [7], [8] and [9].
Changes in v3:
==============
- add commits for `Opaque::try_ffi_init` and `InPlaceModule`
- rename `DriverOps` to `RegistrationOps`
- rework device ID abstractions to get rid of almost all macro magic (thanks to
Gary Guo for working this out!)
- this is possible due to recently stabilized language features
- add modpost alias generation for device ID tables
- unfortunately, this is the part that still requires some macro magic in the
device ID abstractions
- PCI
- represent the driver private data with the driver specific `Driver` instance
and bind it's lifetime to the time of the driver being bound to a device
- this allows us to handle class / subsystem registrations in a cleaner way
- get rid of `Driver::remove`
- Rust drivers should bind cleanup code to the `Drop` implementation of the
corresponding structure instead and put it into their driver structure for
automatic cleanup
- add a sample PCI driver
- add abstractions for `struct of_device_id`
- add abstractions for the platform bus, including a sample driver
- update the MAINTAINERS file accordingly
- currently this turns out a bit messy, but it should become better once the
build system supports a treewide distribution of the kernel crate
- I didn't add myself as maintainer, but (if requested) I'm willing to do so
and help with maintainance
Changes in v2:
==============
- statically initialize driver structures (Greg)
- move base device ID abstractions to a separate source file (Greg)
- remove `DeviceRemoval` trait in favor of using a `Devres` callback to
unregister drivers
- remove `device::Data`, we don't need this abstraction anymore now that we
`Devres` to revoke resources and registrations
- pass the module name to `Module::init` and `InPlaceModule::init` in a separate
patch
- rework of `Io` including compile time boundary checks (Miguel, Wedson)
- adjust PCI abstractions accordingly and implement a `module_pci_driver!` macro
- rework `pci::Bar` to support a const SIZE
- increase the total amount of Documentation, rephrase some safety comments and
commit messages for less ambiguity
- fix compilation issues with some documentation examples
[1] https://gitlab.freedesktop.org/drm/nova/-/tree/nova-next
[2] https://github.com/metaspace/linux/tree/rnvme
[3] https://lore.kernel.org/all/20240930233257.1189730-1-lyude@redhat.com/
[4] https://git.kernel.org/pub/scm/linux/kernel/git/vireshk/linux.git/log/?h=rust/cpufreq-dt
[5] https://github.com/AsahiLinux/linux
[6] https://github.com/Fabo/linux/tree/fparent/rust-i2c
[7] https://github.com/Rust-for-Linux/linux/tree/staging/rust-device
[8] https://github.com/Rust-for-Linux/linux/tree/staging/rust-pci
[9] https://github.com/Rust-for-Linux/linux/tree/staging/dev
Danilo Krummrich (11):
rust: pass module name to `Module::init`
rust: implement generic driver registration
rust: implement `IdArray`, `IdTable` and `RawDeviceId`
rust: add `io::Io` base type
rust: add devres abstraction
rust: pci: add basic PCI device / driver abstractions
rust: pci: implement I/O mappable `pci::Bar`
samples: rust: add Rust PCI sample driver
rust: of: add `of::DeviceId` abstraction
rust: platform: add basic platform device / driver abstractions
samples: rust: add Rust platform sample driver
Wedson Almeida Filho (5):
rust: init: introduce `Opaque::try_ffi_init`
rust: introduce `InPlaceModule`
rust: add rcu abstraction
rust: add `Revocable` type
rust: add `dev_*` print macros.
MAINTAINERS | 8 +
drivers/block/rnull.rs | 2 +-
rust/bindings/bindings_helper.h | 3 +
rust/helpers/device.c | 10 +
rust/helpers/helpers.c | 5 +
rust/helpers/io.c | 91 ++++++
rust/helpers/pci.c | 18 ++
rust/helpers/platform.c | 13 +
rust/helpers/rcu.c | 13 +
rust/kernel/device.rs | 319 +++++++++++++++++++-
rust/kernel/device_id.rs | 162 ++++++++++
rust/kernel/devres.rs | 180 +++++++++++
rust/kernel/driver.rs | 120 ++++++++
rust/kernel/io.rs | 234 +++++++++++++++
rust/kernel/lib.rs | 46 ++-
rust/kernel/net/phy.rs | 2 +-
rust/kernel/of.rs | 63 ++++
rust/kernel/pci.rs | 429 +++++++++++++++++++++++++++
rust/kernel/platform.rs | 217 ++++++++++++++
rust/kernel/prelude.rs | 2 +
rust/kernel/revocable.rs | 211 +++++++++++++
rust/kernel/sync.rs | 1 +
rust/kernel/sync/rcu.rs | 52 ++++
rust/kernel/types.rs | 20 +-
rust/macros/module.rs | 30 +-
samples/rust/Kconfig | 21 ++
samples/rust/Makefile | 2 +
samples/rust/rust_driver_pci.rs | 109 +++++++
samples/rust/rust_driver_platform.rs | 62 ++++
samples/rust/rust_minimal.rs | 2 +-
samples/rust/rust_print.rs | 2 +-
31 files changed, 2421 insertions(+), 28 deletions(-)
create mode 100644 rust/helpers/device.c
create mode 100644 rust/helpers/io.c
create mode 100644 rust/helpers/pci.c
create mode 100644 rust/helpers/platform.c
create mode 100644 rust/helpers/rcu.c
create mode 100644 rust/kernel/device_id.rs
create mode 100644 rust/kernel/devres.rs
create mode 100644 rust/kernel/driver.rs
create mode 100644 rust/kernel/io.rs
create mode 100644 rust/kernel/of.rs
create mode 100644 rust/kernel/pci.rs
create mode 100644 rust/kernel/platform.rs
create mode 100644 rust/kernel/revocable.rs
create mode 100644 rust/kernel/sync/rcu.rs
create mode 100644 samples/rust/rust_driver_pci.rs
create mode 100644 samples/rust/rust_driver_platform.rs
base-commit: 15541c9263ce34ff95a06bc68f45d9bc5c990bcd
--
2.46.2
^ permalink raw reply [flat|nested] 88+ messages in thread
* [PATCH v3 01/16] rust: init: introduce `Opaque::try_ffi_init`
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-29 12:42 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 02/16] rust: introduce `InPlaceModule` Danilo Krummrich
` (17 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Wedson Almeida Filho, Danilo Krummrich
From: Wedson Almeida Filho <walmeida@microsoft.com>
This is the same as `Opaque::ffi_init`, but returns a `Result`.
This is used by subsequent patches to implement the FFI init of static
driver structures on registration.
Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/types.rs | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index ced143600eb1..236e8de5844b 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -239,14 +239,22 @@ pub const fn uninit() -> Self {
/// uninitialized. Additionally, access to the inner `T` requires `unsafe`, so the caller needs
/// to verify at that point that the inner value is valid.
pub fn ffi_init(init_func: impl FnOnce(*mut T)) -> impl PinInit<Self> {
+ Self::try_ffi_init(move |slot| {
+ init_func(slot);
+ Ok(())
+ })
+ }
+
+ /// Similar to [`Self::ffi_init`], except that the closure can fail.
+ ///
+ /// To avoid leaks on failure, the closure must drop any fields it has initialised before the
+ /// failure.
+ pub fn try_ffi_init<E>(
+ init_func: impl FnOnce(*mut T) -> Result<(), E>,
+ ) -> impl PinInit<Self, E> {
// SAFETY: We contain a `MaybeUninit`, so it is OK for the `init_func` to not fully
// initialize the `T`.
- unsafe {
- init::pin_init_from_closure::<_, ::core::convert::Infallible>(move |slot| {
- init_func(Self::raw_get(slot));
- Ok(())
- })
- }
+ unsafe { init::pin_init_from_closure(|slot| init_func(Self::raw_get(slot))) }
}
/// Returns a raw pointer to the opaque data.
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 02/16] rust: introduce `InPlaceModule`
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 01/16] rust: init: introduce `Opaque::try_ffi_init` Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-29 12:45 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 03/16] rust: pass module name to `Module::init` Danilo Krummrich
` (16 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Wedson Almeida Filho, Danilo Krummrich
From: Wedson Almeida Filho <walmeida@microsoft.com>
This allows modules to be initialised in-place in pinned memory, which
enables the usage of pinned types (e.g., mutexes, spinlocks, driver
registrations, etc.) in modules without any extra allocations.
Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/lib.rs | 23 +++++++++++++++++++++++
rust/macros/module.rs | 28 ++++++++++++----------------
2 files changed, 35 insertions(+), 16 deletions(-)
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b62451f64f6e..4fdb0d91f2ad 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -83,6 +83,29 @@ pub trait Module: Sized + Sync + Send {
fn init(module: &'static ThisModule) -> error::Result<Self>;
}
+/// A module that is pinned and initialised in-place.
+pub trait InPlaceModule: Sync + Send {
+ /// Creates an initialiser for the module.
+ ///
+ /// It is called when the module is loaded.
+ fn init(module: &'static ThisModule) -> impl init::PinInit<Self, error::Error>;
+}
+
+impl<T: Module> InPlaceModule for T {
+ fn init(module: &'static ThisModule) -> impl init::PinInit<Self, error::Error> {
+ let initer = move |slot: *mut Self| {
+ let m = <Self as Module>::init(module)?;
+
+ // SAFETY: `slot` is valid for write per the contract with `pin_init_from_closure`.
+ unsafe { slot.write(m) };
+ Ok(())
+ };
+
+ // SAFETY: On success, `initer` always fully initialises an instance of `Self`.
+ unsafe { init::pin_init_from_closure(initer) }
+ }
+}
+
/// Equivalent to `THIS_MODULE` in the C API.
///
/// C header: [`include/linux/init.h`](srctree/include/linux/init.h)
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index aef3b132f32b..a03266a78cfb 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -232,6 +232,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
mod __module_init {{
mod __module_init {{
use super::super::{type_};
+ use kernel::init::PinInit;
/// The \"Rust loadable module\" mark.
//
@@ -242,7 +243,8 @@ mod __module_init {{
#[used]
static __IS_RUST_MODULE: () = ();
- static mut __MOD: Option<{type_}> = None;
+ static mut __MOD: core::mem::MaybeUninit<{type_}> =
+ core::mem::MaybeUninit::uninit();
// Loadable modules need to export the `{{init,cleanup}}_module` identifiers.
/// # Safety
@@ -331,20 +333,14 @@ mod __module_init {{
///
/// This function must only be called once.
unsafe fn __init() -> core::ffi::c_int {{
- match <{type_} as kernel::Module>::init(&super::super::THIS_MODULE) {{
- Ok(m) => {{
- // SAFETY: No data race, since `__MOD` can only be accessed by this
- // module and there only `__init` and `__exit` access it. These
- // functions are only called once and `__exit` cannot be called
- // before or during `__init`.
- unsafe {{
- __MOD = Some(m);
- }}
- return 0;
- }}
- Err(e) => {{
- return e.to_errno();
- }}
+ let initer =
+ <{type_} as kernel::InPlaceModule>::init(&super::super::THIS_MODULE);
+ // SAFETY: No data race, since `__MOD` can only be accessed by this module
+ // and there only `__init` and `__exit` access it. These functions are only
+ // called once and `__exit` cannot be called before or during `__init`.
+ match unsafe {{ initer.__pinned_init(__MOD.as_mut_ptr()) }} {{
+ Ok(m) => 0,
+ Err(e) => e.to_errno(),
}}
}}
@@ -359,7 +355,7 @@ unsafe fn __exit() {{
// called once and `__init` was already called.
unsafe {{
// Invokes `drop()` on `__MOD`, which should be used for cleanup.
- __MOD = None;
+ __MOD.assume_init_drop();
}}
}}
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 03/16] rust: pass module name to `Module::init`
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 01/16] rust: init: introduce `Opaque::try_ffi_init` Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 02/16] rust: introduce `InPlaceModule` Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-29 12:55 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 04/16] rust: implement generic driver registration Danilo Krummrich
` (15 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
In a subsequent patch we introduce the `Registration` abstraction used
to register driver structures. Some subsystems require the module name on
driver registration (e.g. PCI in __pci_register_driver()), hence pass
the module name to `Module::init`.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/block/rnull.rs | 2 +-
rust/kernel/lib.rs | 14 ++++++++++----
rust/kernel/net/phy.rs | 2 +-
rust/macros/module.rs | 6 ++++--
samples/rust/rust_minimal.rs | 2 +-
samples/rust/rust_print.rs | 2 +-
6 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/drivers/block/rnull.rs b/drivers/block/rnull.rs
index 5de7223beb4d..0e0e9ed7851e 100644
--- a/drivers/block/rnull.rs
+++ b/drivers/block/rnull.rs
@@ -36,7 +36,7 @@ struct NullBlkModule {
}
impl kernel::Module for NullBlkModule {
- fn init(_module: &'static ThisModule) -> Result<Self> {
+ fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust null_blk loaded\n");
let tagset = Arc::pin_init(TagSet::new(1, 256, 1), flags::GFP_KERNEL)?;
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 4fdb0d91f2ad..83f76dc7bad2 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -80,7 +80,7 @@ pub trait Module: Sized + Sync + Send {
/// should do.
///
/// Equivalent to the `module_init` macro in the C API.
- fn init(module: &'static ThisModule) -> error::Result<Self>;
+ fn init(name: &'static str::CStr, module: &'static ThisModule) -> error::Result<Self>;
}
/// A module that is pinned and initialised in-place.
@@ -88,13 +88,19 @@ pub trait InPlaceModule: Sync + Send {
/// Creates an initialiser for the module.
///
/// It is called when the module is loaded.
- fn init(module: &'static ThisModule) -> impl init::PinInit<Self, error::Error>;
+ fn init(
+ name: &'static str::CStr,
+ module: &'static ThisModule,
+ ) -> impl init::PinInit<Self, error::Error>;
}
impl<T: Module> InPlaceModule for T {
- fn init(module: &'static ThisModule) -> impl init::PinInit<Self, error::Error> {
+ fn init(
+ name: &'static str::CStr,
+ module: &'static ThisModule,
+ ) -> impl init::PinInit<Self, error::Error> {
let initer = move |slot: *mut Self| {
- let m = <Self as Module>::init(module)?;
+ let m = <Self as Module>::init(name, module)?;
// SAFETY: `slot` is valid for write per the contract with `pin_init_from_closure`.
unsafe { slot.write(m) };
diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
index 910ce867480a..26631b0e127a 100644
--- a/rust/kernel/net/phy.rs
+++ b/rust/kernel/net/phy.rs
@@ -899,7 +899,7 @@ struct Module {
[$($crate::net::phy::create_phy_driver::<$driver>()),+];
impl $crate::Module for Module {
- fn init(module: &'static ThisModule) -> Result<Self> {
+ fn init(_name: &'static CStr, module: &'static ThisModule) -> Result<Self> {
// SAFETY: The anonymous constant guarantees that nobody else can access
// the `DRIVERS` static. The array is used only in the C side.
let drivers = unsafe { &mut DRIVERS };
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index a03266a78cfb..3292714ff82e 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -333,8 +333,10 @@ mod __module_init {{
///
/// This function must only be called once.
unsafe fn __init() -> core::ffi::c_int {{
- let initer =
- <{type_} as kernel::InPlaceModule>::init(&super::super::THIS_MODULE);
+ let initer = <{type_} as kernel::InPlaceModule>::init(
+ kernel::c_str!(\"{name}\"),
+ &super::super::THIS_MODULE
+ );
// SAFETY: No data race, since `__MOD` can only be accessed by this module
// and there only `__init` and `__exit` access it. These functions are only
// called once and `__exit` cannot be called before or during `__init`.
diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs
index 4aaf117bf8e3..1577dc34e563 100644
--- a/samples/rust/rust_minimal.rs
+++ b/samples/rust/rust_minimal.rs
@@ -17,7 +17,7 @@ struct RustMinimal {
}
impl kernel::Module for RustMinimal {
- fn init(_module: &'static ThisModule) -> Result<Self> {
+ fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust minimal sample (init)\n");
pr_info!("Am I built-in? {}\n", !cfg!(MODULE));
diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs
index ba1606bdbd75..73763ea2dc09 100644
--- a/samples/rust/rust_print.rs
+++ b/samples/rust/rust_print.rs
@@ -41,7 +41,7 @@ fn arc_print() -> Result {
}
impl kernel::Module for RustPrint {
- fn init(_module: &'static ThisModule) -> Result<Self> {
+ fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
pr_info!("Rust printing macros sample (init)\n");
pr_emerg!("Emergency message (level 0) without args\n");
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 04/16] rust: implement generic driver registration
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (2 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 03/16] rust: pass module name to `Module::init` Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 05/16] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
` (14 subsequent siblings)
18 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich, Wedson Almeida Filho
Implement the generic `Registration` type and the `DriverOps` trait.
The `Registration` structure is the common type that represents a driver
registration and is typically bound to the lifetime of a module. However,
it doesn't implement actual calls to the kernel's driver core to register
drivers itself.
Instead the `DriverOps` trait is provided to subsystems, which have to
implement `DriverOps::register` and `DrvierOps::unregister`. Subsystems
have to provide an implementation for both of those methods where the
subsystem specific variants to register / unregister a driver have to
implemented.
For instance, the PCI subsystem would call __pci_register_driver() from
`DriverOps::register` and pci_unregister_driver() from
`DrvierOps::unregister`.
Co-developed-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 1 +
rust/kernel/driver.rs | 120 ++++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
3 files changed, 122 insertions(+)
create mode 100644 rust/kernel/driver.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index b77f4495dcf4..29b9ecb63f81 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6982,6 +6982,7 @@ F: include/linux/kobj*
F: include/linux/property.h
F: lib/kobj*
F: rust/kernel/device.rs
+F: rust/kernel/driver.rs
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
M: Nishanth Menon <nm@ti.com>
diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs
new file mode 100644
index 000000000000..c558f23c33d9
--- /dev/null
+++ b/rust/kernel/driver.rs
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Generic support for drivers of different buses (e.g., PCI, Platform, Amba, etc.).
+//!
+//! Each bus / subsystem is expected to implement [`RegistrationOps`], which allows drivers to
+//! register using the [`Registration`] class.
+
+use crate::error::{Error, Result};
+use crate::{init::PinInit, str::CStr, try_pin_init, types::Opaque, ThisModule};
+use core::pin::Pin;
+use macros::{pin_data, pinned_drop};
+
+/// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform,
+/// Amba, etc.) to privide the corresponding subsystem specific implementation to register /
+/// unregister a driver of the particular type (`RegType`).
+///
+/// For instance, the PCI subsystem would set `RegType` to `bindings::pci_driver` and call
+/// `bindings::__pci_register_driver` from `RegistrationOps::register` and
+/// `bindings::pci_unregister_driver` from `RegistrationOps::unregister`.
+pub trait RegistrationOps {
+ /// The type that holds information about the registration. This is typically a struct defined
+ /// by the C portion of the kernel.
+ type RegType: Default;
+
+ /// Registers a driver.
+ ///
+ /// On success, `reg` must remain pinned and valid until the matching call to
+ /// [`RegistrationOps::unregister`].
+ fn register(
+ reg: &mut Self::RegType,
+ name: &'static CStr,
+ module: &'static ThisModule,
+ ) -> Result;
+
+ /// Unregisters a driver previously registered with [`RegistrationOps::register`].
+ fn unregister(reg: &mut Self::RegType);
+}
+
+/// A [`Registration`] is a generic type that represents the registration of some driver type (e.g.
+/// `bindings::pci_driver`). Therefore a [`Registration`] must be initialized with a type that
+/// implements the [`RegistrationOps`] trait, such that the generic `T::register` and
+/// `T::unregister` calls result in the subsystem specific registration calls.
+///
+///Once the `Registration` structure is dropped, the driver is unregistered.
+#[pin_data(PinnedDrop)]
+pub struct Registration<T: RegistrationOps> {
+ #[pin]
+ reg: Opaque<T::RegType>,
+}
+
+// SAFETY: `Registration` has no fields or methods accessible via `&Registration`, so it is safe to
+// share references to it with multiple threads as nothing can be done.
+unsafe impl<T: RegistrationOps> Sync for Registration<T> {}
+
+// SAFETY: Both registration and unregistration are implemented in C and safe to be performed from
+// any thread, so `Registration` is `Send`.
+unsafe impl<T: RegistrationOps> Send for Registration<T> {}
+
+impl<T: RegistrationOps> Registration<T> {
+ /// Creates a new instance of the registration object.
+ pub fn new(name: &'static CStr, module: &'static ThisModule) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self {
+ reg <- Opaque::try_ffi_init(|ptr: *mut T::RegType| {
+ // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write.
+ unsafe { ptr.write(T::RegType::default()) };
+
+ // SAFETY: `try_ffi_init` guarantees that `ptr` is valid for write, and it has
+ // just been initialised above, so it's also valid for read.
+ let drv = unsafe { &mut *ptr };
+
+ T::register(drv, name, module)
+ }),
+ })
+ }
+}
+
+#[pinned_drop]
+impl<T: RegistrationOps> PinnedDrop for Registration<T> {
+ fn drop(self: Pin<&mut Self>) {
+ // SAFETY: The existence of the `Registration` guarantees that `self.reg.get()` is properly
+ // aligned and points to a valid value.
+ let drv = unsafe { &mut *self.reg.get() };
+
+ T::unregister(drv);
+ }
+}
+
+/// A kernel module that only registers the given driver on init.
+///
+/// This is a helper struct to make it easier to define single-functionality modules, in this case,
+/// modules that offer a single driver.
+#[pin_data]
+pub struct Module<T: RegistrationOps> {
+ #[pin]
+ _driver: Registration<T>,
+}
+
+impl<T: RegistrationOps + Sync + Send> crate::InPlaceModule for Module<T> {
+ fn init(name: &'static CStr, module: &'static ThisModule) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self {
+ _driver <- Registration::<T>::new(name, module),
+ })
+ }
+}
+
+/// Declares a kernel module that exposes a single driver.
+///
+/// It is meant to be used as a helper by other subsystems so they can more easily expose their own
+/// macros.
+#[macro_export]
+macro_rules! module_driver {
+ (<$gen_type:ident>, $driver_ops:ty, { type: $type:ty, $($f:tt)* }) => {
+ type Ops<$gen_type> = $driver_ops;
+ type ModuleType = $crate::driver::Module<Ops<$type>>;
+ $crate::prelude::module! {
+ type: ModuleType,
+ $($f)*
+ }
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 83f76dc7bad2..f70f258fbcdc 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -32,6 +32,7 @@
pub mod block;
mod build_assert;
pub mod device;
+pub mod driver;
pub mod error;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
pub mod firmware;
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 05/16] rust: implement `IdArray`, `IdTable` and `RawDeviceId`
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (3 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 04/16] rust: implement generic driver registration Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-11-25 13:42 ` Miguel Ojeda
2024-10-22 21:31 ` [PATCH v3 06/16] rust: add rcu abstraction Danilo Krummrich
` (13 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich, Wedson Almeida Filho, Fabien Parent
Most subsystems use some kind of ID to match devices and drivers. Hence,
we have to provide Rust drivers an abstraction to register an ID table
for the driver to match.
Generally, those IDs are subsystem specific and hence need to be
implemented by the corresponding subsystem. However, the `IdArray`,
`IdTable` and `RawDeviceId` types provide a generalized implementation
that makes the life of subsystems easier to do so.
Co-developed-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
Co-developed-by: Fabien Parent <fabien.parent@linaro.org>
Signed-off-by: Fabien Parent <fabien.parent@linaro.org>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 1 +
rust/kernel/device_id.rs | 162 +++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 7 ++
3 files changed, 170 insertions(+)
create mode 100644 rust/kernel/device_id.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 29b9ecb63f81..0a8882252257 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6982,6 +6982,7 @@ F: include/linux/kobj*
F: include/linux/property.h
F: lib/kobj*
F: rust/kernel/device.rs
+F: rust/kernel/device_id.rs
F: rust/kernel/driver.rs
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
new file mode 100644
index 000000000000..5b1329fba528
--- /dev/null
+++ b/rust/kernel/device_id.rs
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Generic implementation of device IDs.
+//!
+//! Each bus / subsystem that matches device and driver through a bus / subsystem specific ID is
+//! expected to implement [`RawDeviceId`].
+
+use core::mem::MaybeUninit;
+
+/// Marker trait to indicate a Rust device ID type represents a corresponding C device ID type.
+///
+/// This is meant to be implemented by buses/subsystems so that they can use [`IdTable`] to
+/// guarantee (at compile-time) zero-termination of device id tables provided by drivers.
+///
+/// # Safety
+///
+/// Implementers must ensure that:
+/// - `Self` is layout-compatible with [`RawDeviceId::RawType`]; i.e. it's safe to transmute to
+/// `RawDeviceId`.
+///
+/// This requirement is needed so `IdArray::new` can convert `Self` to `RawType` when building
+/// the ID table.
+///
+/// Ideally, this should be achieved using a const function that does conversion instead of
+/// transmute; however, const trait functions relies on `const_trait_impl` unstable feature,
+/// which is broken/gone in Rust 1.73.
+///
+/// - `DRIVER_DATA_OFFSET` is the offset of context/data field of the device ID (usually named
+/// `driver_data`) of the device ID, the field is suitable sized to write a `usize` value.
+///
+/// Similar to the previous requirement, the data should ideally be added during `Self` to
+/// `RawType` conversion, but there's currently no way to do it when using traits in const.
+pub unsafe trait RawDeviceId {
+ /// The raw type that holds the device id.
+ ///
+ /// Id tables created from [`Self`] are going to hold this type in its zero-terminated array.
+ type RawType: Copy;
+
+ /// The offest to the context/data field.
+ const DRIVER_DATA_OFFSET: usize;
+
+ /// The index stored at `DRIVER_DATA_OFFSET` of the implementor of the [`RawDeviceId`] trait.
+ fn index(&self) -> usize;
+}
+
+/// A zero-terminated device id array.
+#[repr(C)]
+pub struct RawIdArray<T: RawDeviceId, const N: usize> {
+ ids: [T::RawType; N],
+ sentinel: MaybeUninit<T::RawType>,
+}
+
+impl<T: RawDeviceId, const N: usize> RawIdArray<T, N> {
+ #[doc(hidden)]
+ pub const fn size(&self) -> usize {
+ core::mem::size_of::<Self>()
+ }
+}
+
+/// A zero-terminated device id array, followed by context data.
+#[repr(C)]
+pub struct IdArray<T: RawDeviceId, U, const N: usize> {
+ raw_ids: RawIdArray<T, N>,
+ id_infos: [U; N],
+}
+
+impl<T: RawDeviceId, U, const N: usize> IdArray<T, U, N> {
+ /// Creates a new instance of the array.
+ ///
+ /// The contents are derived from the given identifiers and context information.
+ pub const fn new(ids: [(T, U); N]) -> Self {
+ let mut raw_ids = [const { MaybeUninit::<T::RawType>::uninit() }; N];
+ let mut infos = [const { MaybeUninit::uninit() }; N];
+
+ let mut i = 0usize;
+ while i < N {
+ // SAFETY: by the safety requirement of `RawDeviceId`, we're guaranteed that `T` is
+ // layout-wise compatible with `RawType`.
+ raw_ids[i] = unsafe { core::mem::transmute_copy(&ids[i].0) };
+ // SAFETY: by the safety requirement of `RawDeviceId`, this would be effectively
+ // `raw_ids[i].driver_data = i;`.
+ unsafe {
+ raw_ids[i]
+ .as_mut_ptr()
+ .byte_offset(T::DRIVER_DATA_OFFSET as _)
+ .cast::<usize>()
+ .write(i);
+ }
+
+ // SAFETY: this is effectively a move: `infos[i] = ids[i].1`. We make a copy here but
+ // later forget `ids`.
+ infos[i] = MaybeUninit::new(unsafe { core::ptr::read(&ids[i].1) });
+ i += 1;
+ }
+
+ core::mem::forget(ids);
+
+ Self {
+ raw_ids: RawIdArray {
+ // SAFETY: this is effectively `array_assume_init`, which is unstable, so we use
+ // `transmute_copy` instead. We have initialized all elements of `raw_ids` so this
+ // `array_assume_init` is safe.
+ ids: unsafe { core::mem::transmute_copy(&raw_ids) },
+ sentinel: MaybeUninit::zeroed(),
+ },
+ // SAFETY: We have initialized all elements of `infos` so this `array_assume_init` is
+ // safe.
+ id_infos: unsafe { core::mem::transmute_copy(&infos) },
+ }
+ }
+
+ /// Reference to the contained [`RawIdArray`].
+ pub const fn raw_ids(&self) -> &RawIdArray<T, N> {
+ &self.raw_ids
+ }
+}
+
+/// A device id table.
+///
+/// This trait is only implemented by `IdArray`.
+///
+/// The purpose of this trait is to allow `&'static dyn IdArray<T, U>` to be in context when `N` in
+/// `IdArray` doesn't matter.
+pub trait IdTable<T: RawDeviceId, U> {
+ /// Obtain the pointer to the ID table.
+ fn as_ptr(&self) -> *const T::RawType;
+
+ /// Obtain the pointer to the bus specific device ID from an index.
+ fn id(&self, index: usize) -> &T::RawType;
+
+ /// Obtain the pointer to the driver-specific information from an index.
+ fn info(&self, index: usize) -> &U;
+}
+
+impl<T: RawDeviceId, U, const N: usize> IdTable<T, U> for IdArray<T, U, N> {
+ fn as_ptr(&self) -> *const T::RawType {
+ // This cannot be `self.ids.as_ptr()`, as the return pointer must have correct provenance
+ // to access the sentinel.
+ (self as *const Self).cast()
+ }
+
+ fn id(&self, index: usize) -> &T::RawType {
+ &self.raw_ids.ids[index]
+ }
+
+ fn info(&self, index: usize) -> &U {
+ &self.id_infos[index]
+ }
+}
+
+/// Create device table alias for modpost.
+#[macro_export]
+macro_rules! module_device_table {
+ ($table_type: literal, $module_table_name:ident, $table_name:ident) => {
+ #[rustfmt::skip]
+ #[export_name =
+ concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
+ ]
+ static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
+ unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
+ };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index f70f258fbcdc..89f6bd2efcc0 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -14,10 +14,16 @@
#![no_std]
#![feature(arbitrary_self_types)]
#![feature(coerce_unsized)]
+#![feature(const_refs_to_cell)]
#![feature(dispatch_from_dyn)]
#![feature(inline_const)]
#![feature(lint_reasons)]
#![feature(unsize)]
+#![allow(stable_features)]
+// Stable in Rust 1.83
+#![feature(const_mut_refs)]
+#![feature(const_ptr_write)]
+#![feature(const_maybe_uninit_as_mut_ptr)]
// Ensure conditional compilation based on the kernel configuration works;
// otherwise we may silently break things like initcall handling.
@@ -32,6 +38,7 @@
pub mod block;
mod build_assert;
pub mod device;
+pub mod device_id;
pub mod driver;
pub mod error;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 06/16] rust: add rcu abstraction
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (4 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 05/16] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-29 13:59 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 07/16] rust: add `Revocable` type Danilo Krummrich
` (12 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Wedson Almeida Filho, Danilo Krummrich
From: Wedson Almeida Filho <wedsonaf@gmail.com>
Add a simple abstraction to guard critical code sections with an rcu
read lock.
Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/helpers/helpers.c | 1 +
rust/helpers/rcu.c | 13 +++++++++++
rust/kernel/sync.rs | 1 +
rust/kernel/sync/rcu.rs | 52 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 67 insertions(+)
create mode 100644 rust/helpers/rcu.c
create mode 100644 rust/kernel/sync/rcu.rs
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 20a0c69d5cc7..0720debccdd4 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -16,6 +16,7 @@
#include "mutex.c"
#include "page.c"
#include "rbtree.c"
+#include "rcu.c"
#include "refcount.c"
#include "signal.c"
#include "slab.c"
diff --git a/rust/helpers/rcu.c b/rust/helpers/rcu.c
new file mode 100644
index 000000000000..f1cec6583513
--- /dev/null
+++ b/rust/helpers/rcu.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/rcupdate.h>
+
+void rust_helper_rcu_read_lock(void)
+{
+ rcu_read_lock();
+}
+
+void rust_helper_rcu_read_unlock(void)
+{
+ rcu_read_unlock();
+}
diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index 0ab20975a3b5..1806767359fe 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -11,6 +11,7 @@
mod condvar;
pub mod lock;
mod locked_by;
+pub mod rcu;
pub use arc::{Arc, ArcBorrow, UniqueArc};
pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult};
diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
new file mode 100644
index 000000000000..5a35495f69a4
--- /dev/null
+++ b/rust/kernel/sync/rcu.rs
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! RCU support.
+//!
+//! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
+
+use crate::bindings;
+use core::marker::PhantomData;
+
+/// Evidence that the RCU read side lock is held on the current thread/CPU.
+///
+/// The type is explicitly not `Send` because this property is per-thread/CPU.
+///
+/// # Invariants
+///
+/// The RCU read side lock is actually held while instances of this guard exist.
+pub struct Guard {
+ _not_send: PhantomData<*mut ()>,
+}
+
+impl Guard {
+ /// Acquires the RCU read side lock and returns a guard.
+ pub fn new() -> Self {
+ // SAFETY: An FFI call with no additional requirements.
+ unsafe { bindings::rcu_read_lock() };
+ // INVARIANT: The RCU read side lock was just acquired above.
+ Self {
+ _not_send: PhantomData,
+ }
+ }
+
+ /// Explicitly releases the RCU read side lock.
+ pub fn unlock(self) {}
+}
+
+impl Default for Guard {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Drop for Guard {
+ fn drop(&mut self) {
+ // SAFETY: By the type invariants, the rcu read side is locked, so it is ok to unlock it.
+ unsafe { bindings::rcu_read_unlock() };
+ }
+}
+
+/// Acquires the RCU read side lock.
+pub fn read_lock() -> Guard {
+ Guard::new()
+}
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 07/16] rust: add `Revocable` type
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (5 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 06/16] rust: add rcu abstraction Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-29 13:26 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 08/16] rust: add `dev_*` print macros Danilo Krummrich
` (11 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Wedson Almeida Filho, Danilo Krummrich
From: Wedson Almeida Filho <wedsonaf@gmail.com>
Revocable allows access to objects to be safely revoked at run time.
This is useful, for example, for resources allocated during device probe;
when the device is removed, the driver should stop accessing the device
resources even if another state is kept in memory due to existing
references (i.e., device context data is ref-counted and has a non-zero
refcount after removal of the device).
Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/lib.rs | 1 +
rust/kernel/revocable.rs | 211 +++++++++++++++++++++++++++++++++++++++
2 files changed, 212 insertions(+)
create mode 100644 rust/kernel/revocable.rs
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 89f6bd2efcc0..b603b67dcd71 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -54,6 +54,7 @@
pub mod prelude;
pub mod print;
pub mod rbtree;
+pub mod revocable;
pub mod sizes;
mod static_assert;
#[doc(hidden)]
diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs
new file mode 100644
index 000000000000..83455558d795
--- /dev/null
+++ b/rust/kernel/revocable.rs
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Revocable objects.
+//!
+//! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence
+//! of a [`RevocableGuard`] ensures that objects remain valid.
+
+use crate::{
+ bindings,
+ init::{self},
+ prelude::*,
+ sync::rcu,
+};
+use core::{
+ cell::UnsafeCell,
+ marker::PhantomData,
+ mem::MaybeUninit,
+ ops::Deref,
+ ptr::drop_in_place,
+ sync::atomic::{AtomicBool, Ordering},
+};
+
+/// An object that can become inaccessible at runtime.
+///
+/// Once access is revoked and all concurrent users complete (i.e., all existing instances of
+/// [`RevocableGuard`] are dropped), the wrapped object is also dropped.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::revocable::Revocable;
+///
+/// struct Example {
+/// a: u32,
+/// b: u32,
+/// }
+///
+/// fn add_two(v: &Revocable<Example>) -> Option<u32> {
+/// let guard = v.try_access()?;
+/// Some(guard.a + guard.b)
+/// }
+///
+/// let v = KBox::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_KERNEL).unwrap();
+/// assert_eq!(add_two(&v), Some(30));
+/// v.revoke();
+/// assert_eq!(add_two(&v), None);
+/// ```
+///
+/// Sample example as above, but explicitly using the rcu read side lock.
+///
+/// ```
+/// # use kernel::revocable::Revocable;
+/// use kernel::sync::rcu;
+///
+/// struct Example {
+/// a: u32,
+/// b: u32,
+/// }
+///
+/// fn add_two(v: &Revocable<Example>) -> Option<u32> {
+/// let guard = rcu::read_lock();
+/// let e = v.try_access_with_guard(&guard)?;
+/// Some(e.a + e.b)
+/// }
+///
+/// let v = KBox::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_KERNEL).unwrap();
+/// assert_eq!(add_two(&v), Some(30));
+/// v.revoke();
+/// assert_eq!(add_two(&v), None);
+/// ```
+#[pin_data(PinnedDrop)]
+pub struct Revocable<T> {
+ is_available: AtomicBool,
+ #[pin]
+ data: MaybeUninit<UnsafeCell<T>>,
+}
+
+// SAFETY: `Revocable` is `Send` if the wrapped object is also `Send`. This is because while the
+// functionality exposed by `Revocable` can be accessed from any thread/CPU, it is possible that
+// this isn't supported by the wrapped object.
+unsafe impl<T: Send> Send for Revocable<T> {}
+
+// SAFETY: `Revocable` is `Sync` if the wrapped object is both `Send` and `Sync`. We require `Send`
+// from the wrapped object as well because of `Revocable::revoke`, which can trigger the `Drop`
+// implementation of the wrapped object from an arbitrary thread.
+unsafe impl<T: Sync + Send> Sync for Revocable<T> {}
+
+impl<T> Revocable<T> {
+ /// Creates a new revocable instance of the given data.
+ pub fn new(data: impl PinInit<T>) -> impl PinInit<Self> {
+ pin_init!(Self {
+ is_available: AtomicBool::new(true),
+ // SAFETY: The closure only returns `Ok(())` if `slot` is fully initialized; on error
+ // `slot` is not partially initialized and does not need to be dropped.
+ data <- unsafe {
+ init::pin_init_from_closure(move |slot: *mut MaybeUninit<UnsafeCell<T>>| {
+ init::PinInit::<T, core::convert::Infallible>::__pinned_init(data,
+ slot as *mut T)?;
+ Ok::<(), core::convert::Infallible>(())
+ })
+ },
+ })
+ }
+
+ /// Tries to access the \[revocable\] wrapped object.
+ ///
+ /// Returns `None` if the object has been revoked and is therefore no longer accessible.
+ ///
+ /// Returns a guard that gives access to the object otherwise; the object is guaranteed to
+ /// remain accessible while the guard is alive. In such cases, callers are not allowed to sleep
+ /// because another CPU may be waiting to complete the revocation of this object.
+ pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
+ let guard = rcu::read_lock();
+ if self.is_available.load(Ordering::Relaxed) {
+ // SAFETY: Since `self.is_available` is true, data is initialised and has to remain
+ // valid because the RCU read side lock prevents it from being dropped.
+ Some(unsafe { RevocableGuard::new(self.data.assume_init_ref().get(), guard) })
+ } else {
+ None
+ }
+ }
+
+ /// Tries to access the \[revocable\] wrapped object.
+ ///
+ /// Returns `None` if the object has been revoked and is therefore no longer accessible.
+ ///
+ /// Returns a shared reference to the object otherwise; the object is guaranteed to
+ /// remain accessible while the rcu read side guard is alive. In such cases, callers are not
+ /// allowed to sleep because another CPU may be waiting to complete the revocation of this
+ /// object.
+ pub fn try_access_with_guard<'a>(&'a self, _guard: &'a rcu::Guard) -> Option<&'a T> {
+ if self.is_available.load(Ordering::Relaxed) {
+ // SAFETY: Since `self.is_available` is true, data is initialised and has to remain
+ // valid because the RCU read side lock prevents it from being dropped.
+ Some(unsafe { &*self.data.assume_init_ref().get() })
+ } else {
+ None
+ }
+ }
+
+ /// Revokes access to and drops the wrapped object.
+ ///
+ /// Access to the object is revoked immediately to new callers of [`Revocable::try_access`]. If
+ /// there are concurrent users of the object (i.e., ones that called [`Revocable::try_access`]
+ /// beforehand and still haven't dropped the returned guard), this function waits for the
+ /// concurrent access to complete before dropping the wrapped object.
+ pub fn revoke(&self) {
+ if self
+ .is_available
+ .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed)
+ .is_ok()
+ {
+ // SAFETY: Just an FFI call, there are no further requirements.
+ unsafe { bindings::synchronize_rcu() };
+
+ // SAFETY: We know `self.data` is valid because only one CPU can succeed the
+ // `compare_exchange` above that takes `is_available` from `true` to `false`.
+ unsafe { drop_in_place(self.data.assume_init_ref().get()) };
+ }
+ }
+}
+
+#[pinned_drop]
+impl<T> PinnedDrop for Revocable<T> {
+ fn drop(self: Pin<&mut Self>) {
+ // Drop only if the data hasn't been revoked yet (in which case it has already been
+ // dropped).
+ // SAFETY: We are not moving out of `p`, only dropping in place
+ let p = unsafe { self.get_unchecked_mut() };
+ if *p.is_available.get_mut() {
+ // SAFETY: We know `self.data` is valid because no other CPU has changed
+ // `is_available` to `false` yet, and no other CPU can do it anymore because this CPU
+ // holds the only reference (mutable) to `self` now.
+ unsafe { drop_in_place(p.data.assume_init_ref().get()) };
+ }
+ }
+}
+
+/// A guard that allows access to a revocable object and keeps it alive.
+///
+/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context
+/// holding the RCU read-side lock.
+///
+/// # Invariants
+///
+/// The RCU read-side lock is held while the guard is alive.
+pub struct RevocableGuard<'a, T> {
+ data_ref: *const T,
+ _rcu_guard: rcu::Guard,
+ _p: PhantomData<&'a ()>,
+}
+
+impl<T> RevocableGuard<'_, T> {
+ fn new(data_ref: *const T, rcu_guard: rcu::Guard) -> Self {
+ Self {
+ data_ref,
+ _rcu_guard: rcu_guard,
+ _p: PhantomData,
+ }
+ }
+}
+
+impl<T> Deref for RevocableGuard<'_, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: By the type invariants, we hold the rcu read-side lock, so the object is
+ // guaranteed to remain valid.
+ unsafe { &*self.data_ref }
+ }
+}
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 08/16] rust: add `dev_*` print macros.
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (6 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 07/16] rust: add `Revocable` type Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-11-04 0:24 ` Greg KH
2024-10-22 21:31 ` [PATCH v3 09/16] rust: add `io::Io` base type Danilo Krummrich
` (10 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Wedson Almeida Filho, Danilo Krummrich
From: Wedson Almeida Filho <wedsonaf@google.com>
Implement `dev_*` print macros for `device::Device`.
They behave like the macros with the same names in C, i.e., they print
messages to the kernel ring buffer with the given level, prefixing the
messages with corresponding device information.
Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/device.rs | 319 ++++++++++++++++++++++++++++++++++++++++-
rust/kernel/prelude.rs | 2 +
2 files changed, 320 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs
index 851018eef885..0c28b1e6b004 100644
--- a/rust/kernel/device.rs
+++ b/rust/kernel/device.rs
@@ -8,7 +8,10 @@
bindings,
types::{ARef, Opaque},
};
-use core::ptr;
+use core::{fmt, ptr};
+
+#[cfg(CONFIG_PRINTK)]
+use crate::c_str;
/// A reference-counted device.
///
@@ -82,6 +85,110 @@ pub unsafe fn as_ref<'a>(ptr: *mut bindings::device) -> &'a Self {
// SAFETY: Guaranteed by the safety requirements of the function.
unsafe { &*ptr.cast() }
}
+
+ /// Prints an emergency-level message (level 0) prefixed with device information.
+ ///
+ /// More details are available from [`dev_emerg`].
+ ///
+ /// [`dev_emerg`]: crate::dev_emerg
+ pub fn pr_emerg(&self, args: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
+ unsafe { self.printk(bindings::KERN_EMERG, args) };
+ }
+
+ /// Prints an alert-level message (level 1) prefixed with device information.
+ ///
+ /// More details are available from [`dev_alert`].
+ ///
+ /// [`dev_alert`]: crate::dev_alert
+ pub fn pr_alert(&self, args: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
+ unsafe { self.printk(bindings::KERN_ALERT, args) };
+ }
+
+ /// Prints a critical-level message (level 2) prefixed with device information.
+ ///
+ /// More details are available from [`dev_crit`].
+ ///
+ /// [`dev_crit`]: crate::dev_crit
+ pub fn pr_crit(&self, args: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
+ unsafe { self.printk(bindings::KERN_CRIT, args) };
+ }
+
+ /// Prints an error-level message (level 3) prefixed with device information.
+ ///
+ /// More details are available from [`dev_err`].
+ ///
+ /// [`dev_err`]: crate::dev_err
+ pub fn pr_err(&self, args: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
+ unsafe { self.printk(bindings::KERN_ERR, args) };
+ }
+
+ /// Prints a warning-level message (level 4) prefixed with device information.
+ ///
+ /// More details are available from [`dev_warn`].
+ ///
+ /// [`dev_warn`]: crate::dev_warn
+ pub fn pr_warn(&self, args: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
+ unsafe { self.printk(bindings::KERN_WARNING, args) };
+ }
+
+ /// Prints a notice-level message (level 5) prefixed with device information.
+ ///
+ /// More details are available from [`dev_notice`].
+ ///
+ /// [`dev_notice`]: crate::dev_notice
+ pub fn pr_notice(&self, args: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
+ unsafe { self.printk(bindings::KERN_NOTICE, args) };
+ }
+
+ /// Prints an info-level message (level 6) prefixed with device information.
+ ///
+ /// More details are available from [`dev_info`].
+ ///
+ /// [`dev_info`]: crate::dev_info
+ pub fn pr_info(&self, args: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
+ unsafe { self.printk(bindings::KERN_INFO, args) };
+ }
+
+ /// Prints a debug-level message (level 7) prefixed with device information.
+ ///
+ /// More details are available from [`dev_dbg`].
+ ///
+ /// [`dev_dbg`]: crate::dev_dbg
+ pub fn pr_dbg(&self, args: fmt::Arguments<'_>) {
+ if cfg!(debug_assertions) {
+ // SAFETY: `klevel` is null-terminated, uses one of the kernel constants.
+ unsafe { self.printk(bindings::KERN_DEBUG, args) };
+ }
+ }
+
+ /// Prints the provided message to the console.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `klevel` is null-terminated; in particular, one of the
+ /// `KERN_*`constants, for example, `KERN_CRIT`, `KERN_ALERT`, etc.
+ #[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
+ unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated and one of the kernel constants. `self.as_raw`
+ // is valid because `self` is valid. The "%pA" format string expects a pointer to
+ // `fmt::Arguments`, which is what we're passing as the last argument.
+ #[cfg(CONFIG_PRINTK)]
+ unsafe {
+ bindings::_dev_printk(
+ klevel as *const _ as *const core::ffi::c_char,
+ self.as_raw(),
+ c_str!("%pA").as_char_ptr(),
+ &msg as *const _ as *const core::ffi::c_void,
+ )
+ };
+ }
}
// SAFETY: Instances of `Device` are always reference-counted.
@@ -103,3 +210,213 @@ unsafe impl Send for Device {}
// SAFETY: `Device` can be shared among threads because all immutable methods are protected by the
// synchronization in `struct device`.
unsafe impl Sync for Device {}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! dev_printk {
+ ($method:ident, $dev:expr, $($f:tt)*) => {
+ {
+ ($dev).$method(core::format_args!($($f)*));
+ }
+ }
+}
+
+/// Prints an emergency-level message (level 0) prefixed with device information.
+///
+/// This level should be used if the system is unusable.
+///
+/// Equivalent to the kernel's `dev_emerg` macro.
+///
+/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
+/// [`core::fmt`] and `alloc::format!`.
+///
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::device::Device;
+///
+/// fn example(dev: &Device) {
+/// dev_emerg!(dev, "hello {}\n", "there");
+/// }
+/// ```
+#[macro_export]
+macro_rules! dev_emerg {
+ ($($f:tt)*) => { $crate::dev_printk!(pr_emerg, $($f)*); }
+}
+
+/// Prints an alert-level message (level 1) prefixed with device information.
+///
+/// This level should be used if action must be taken immediately.
+///
+/// Equivalent to the kernel's `dev_alert` macro.
+///
+/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
+/// [`core::fmt`] and `alloc::format!`.
+///
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::device::Device;
+///
+/// fn example(dev: &Device) {
+/// dev_alert!(dev, "hello {}\n", "there");
+/// }
+/// ```
+#[macro_export]
+macro_rules! dev_alert {
+ ($($f:tt)*) => { $crate::dev_printk!(pr_alert, $($f)*); }
+}
+
+/// Prints a critical-level message (level 2) prefixed with device information.
+///
+/// This level should be used in critical conditions.
+///
+/// Equivalent to the kernel's `dev_crit` macro.
+///
+/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
+/// [`core::fmt`] and `alloc::format!`.
+///
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::device::Device;
+///
+/// fn example(dev: &Device) {
+/// dev_crit!(dev, "hello {}\n", "there");
+/// }
+/// ```
+#[macro_export]
+macro_rules! dev_crit {
+ ($($f:tt)*) => { $crate::dev_printk!(pr_crit, $($f)*); }
+}
+
+/// Prints an error-level message (level 3) prefixed with device information.
+///
+/// This level should be used in error conditions.
+///
+/// Equivalent to the kernel's `dev_err` macro.
+///
+/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
+/// [`core::fmt`] and `alloc::format!`.
+///
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::device::Device;
+///
+/// fn example(dev: &Device) {
+/// dev_err!(dev, "hello {}\n", "there");
+/// }
+/// ```
+#[macro_export]
+macro_rules! dev_err {
+ ($($f:tt)*) => { $crate::dev_printk!(pr_err, $($f)*); }
+}
+
+/// Prints a warning-level message (level 4) prefixed with device information.
+///
+/// This level should be used in warning conditions.
+///
+/// Equivalent to the kernel's `dev_warn` macro.
+///
+/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
+/// [`core::fmt`] and `alloc::format!`.
+///
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::device::Device;
+///
+/// fn example(dev: &Device) {
+/// dev_warn!(dev, "hello {}\n", "there");
+/// }
+/// ```
+#[macro_export]
+macro_rules! dev_warn {
+ ($($f:tt)*) => { $crate::dev_printk!(pr_warn, $($f)*); }
+}
+
+/// Prints a notice-level message (level 5) prefixed with device information.
+///
+/// This level should be used in normal but significant conditions.
+///
+/// Equivalent to the kernel's `dev_notice` macro.
+///
+/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
+/// [`core::fmt`] and `alloc::format!`.
+///
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::device::Device;
+///
+/// fn example(dev: &Device) {
+/// dev_notice!(dev, "hello {}\n", "there");
+/// }
+/// ```
+#[macro_export]
+macro_rules! dev_notice {
+ ($($f:tt)*) => { $crate::dev_printk!(pr_notice, $($f)*); }
+}
+
+/// Prints an info-level message (level 6) prefixed with device information.
+///
+/// This level should be used for informational messages.
+///
+/// Equivalent to the kernel's `dev_info` macro.
+///
+/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
+/// [`core::fmt`] and `alloc::format!`.
+///
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::device::Device;
+///
+/// fn example(dev: &Device) {
+/// dev_info!(dev, "hello {}\n", "there");
+/// }
+/// ```
+#[macro_export]
+macro_rules! dev_info {
+ ($($f:tt)*) => { $crate::dev_printk!(pr_info, $($f)*); }
+}
+
+/// Prints a debug-level message (level 7) prefixed with device information.
+///
+/// This level should be used for debug messages.
+///
+/// Equivalent to the kernel's `dev_dbg` macro, except that it doesn't support dynamic debug yet.
+///
+/// Mimics the interface of [`std::print!`]. More information about the syntax is available from
+/// [`core::fmt`] and `alloc::format!`.
+///
+/// [`std::print!`]: https://doc.rust-lang.org/std/macro.print.html
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::device::Device;
+///
+/// fn example(dev: &Device) {
+/// dev_dbg!(dev, "hello {}\n", "there");
+/// }
+/// ```
+#[macro_export]
+macro_rules! dev_dbg {
+ ($($f:tt)*) => { $crate::dev_printk!(pr_dbg, $($f)*); }
+}
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index 8bdab9aa0d16..9ab4e0b6cbc9 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -24,6 +24,8 @@
// `super::std_vendor` is hidden, which makes the macro inline for some reason.
#[doc(no_inline)]
pub use super::dbg;
+pub use super::fmt;
+pub use super::{dev_alert, dev_crit, dev_dbg, dev_emerg, dev_err, dev_info, dev_notice, dev_warn};
pub use super::{pr_alert, pr_crit, pr_debug, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};
pub use super::{init, pin_init, try_init, try_pin_init};
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 09/16] rust: add `io::Io` base type
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (7 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 08/16] rust: add `dev_*` print macros Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-28 15:43 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 10/16] rust: add devres abstraction Danilo Krummrich
` (9 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
I/O memory is typically either mapped through direct calls to ioremap()
or subsystem / bus specific ones such as pci_iomap().
Even though subsystem / bus specific functions to map I/O memory are
based on ioremap() / iounmap() it is not desirable to re-implement them
in Rust.
Instead, implement a base type for I/O mapped memory, which generically
provides the corresponding accessors, such as `Io::readb` or
`Io:try_readb`.
`Io` supports an optional const generic, such that a driver can indicate
the minimal expected and required size of the mapping at compile time.
Correspondingly, calls to the 'non-try' accessors, support compile time
checks of the I/O memory offset to read / write, while the 'try'
accessors, provide boundary checks on runtime.
`Io` is meant to be embedded into a structure (e.g. pci::Bar or
io::IoMem) which creates the actual I/O memory mapping and initializes
`Io` accordingly.
To ensure that I/O mapped memory can't out-live the device it may be
bound to, subsystems should embedd the corresponding I/O memory type
(e.g. pci::Bar) into a `Devres` container, such that it gets revoked
once the device is unbound.
Co-developed-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/helpers/helpers.c | 1 +
rust/helpers/io.c | 91 ++++++++++++++++
rust/kernel/io.rs | 234 +++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
4 files changed, 327 insertions(+)
create mode 100644 rust/helpers/io.c
create mode 100644 rust/kernel/io.rs
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 0720debccdd4..e2f6b2197061 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -12,6 +12,7 @@
#include "build_assert.c"
#include "build_bug.c"
#include "err.c"
+#include "io.c"
#include "kunit.c"
#include "mutex.c"
#include "page.c"
diff --git a/rust/helpers/io.c b/rust/helpers/io.c
new file mode 100644
index 000000000000..f9bb1bbf1fd5
--- /dev/null
+++ b/rust/helpers/io.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/io.h>
+
+u8 rust_helper_readb(const volatile void __iomem *addr)
+{
+ return readb(addr);
+}
+
+u16 rust_helper_readw(const volatile void __iomem *addr)
+{
+ return readw(addr);
+}
+
+u32 rust_helper_readl(const volatile void __iomem *addr)
+{
+ return readl(addr);
+}
+
+#ifdef CONFIG_64BIT
+u64 rust_helper_readq(const volatile void __iomem *addr)
+{
+ return readq(addr);
+}
+#endif
+
+void rust_helper_writeb(u8 value, volatile void __iomem *addr)
+{
+ writeb(value, addr);
+}
+
+void rust_helper_writew(u16 value, volatile void __iomem *addr)
+{
+ writew(value, addr);
+}
+
+void rust_helper_writel(u32 value, volatile void __iomem *addr)
+{
+ writel(value, addr);
+}
+
+#ifdef CONFIG_64BIT
+void rust_helper_writeq(u64 value, volatile void __iomem *addr)
+{
+ writeq(value, addr);
+}
+#endif
+
+u8 rust_helper_readb_relaxed(const volatile void __iomem *addr)
+{
+ return readb_relaxed(addr);
+}
+
+u16 rust_helper_readw_relaxed(const volatile void __iomem *addr)
+{
+ return readw_relaxed(addr);
+}
+
+u32 rust_helper_readl_relaxed(const volatile void __iomem *addr)
+{
+ return readl_relaxed(addr);
+}
+
+#ifdef CONFIG_64BIT
+u64 rust_helper_readq_relaxed(const volatile void __iomem *addr)
+{
+ return readq_relaxed(addr);
+}
+#endif
+
+void rust_helper_writeb_relaxed(u8 value, volatile void __iomem *addr)
+{
+ writeb_relaxed(value, addr);
+}
+
+void rust_helper_writew_relaxed(u16 value, volatile void __iomem *addr)
+{
+ writew_relaxed(value, addr);
+}
+
+void rust_helper_writel_relaxed(u32 value, volatile void __iomem *addr)
+{
+ writel_relaxed(value, addr);
+}
+
+#ifdef CONFIG_64BIT
+void rust_helper_writeq_relaxed(u64 value, volatile void __iomem *addr)
+{
+ writeq_relaxed(value, addr);
+}
+#endif
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
new file mode 100644
index 000000000000..750af938f83e
--- /dev/null
+++ b/rust/kernel/io.rs
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Memory-mapped IO.
+//!
+//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
+
+use crate::error::{code::EINVAL, Result};
+use crate::{bindings, build_assert};
+
+/// IO-mapped memory, starting at the base address @addr and spanning @maxlen bytes.
+///
+/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the
+/// mapping, performing an additional region request etc.
+///
+/// # Invariant
+///
+/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size
+/// `maxsize`.
+///
+/// # Examples
+///
+/// ```no_run
+/// # use kernel::{bindings, io::Io};
+/// # use core::ops::Deref;
+///
+/// // See also [`pci::Bar`] for a real example.
+/// struct IoMem<const SIZE: usize>(Io<SIZE>);
+///
+/// impl<const SIZE: usize> IoMem<SIZE> {
+/// /// # Safety
+/// ///
+/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
+/// /// virtual address space.
+/// unsafe fn new(paddr: usize) -> Result<Self>{
+///
+/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
+/// // valid for `ioremap`.
+/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE.try_into().unwrap()) };
+/// if addr.is_null() {
+/// return Err(ENOMEM);
+/// }
+///
+/// // SAFETY: `addr` is guaranteed to be the start of a valid I/O mapped memory region of
+/// // size `SIZE`.
+/// let io = unsafe { Io::new(addr as _, SIZE)? };
+///
+/// Ok(IoMem(io))
+/// }
+/// }
+///
+/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
+/// fn drop(&mut self) {
+/// // SAFETY: Safe as by the invariant of `Io`.
+/// unsafe { bindings::iounmap(self.0.base_addr() as _); };
+/// }
+/// }
+///
+/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
+/// type Target = Io<SIZE>;
+///
+/// fn deref(&self) -> &Self::Target {
+/// &self.0
+/// }
+/// }
+///
+///# fn no_run() -> Result<(), Error> {
+/// // SAFETY: Invalid usage for example purposes.
+/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
+/// iomem.writel(0x42, 0x0);
+/// assert!(iomem.try_writel(0x42, 0x0).is_ok());
+/// assert!(iomem.try_writel(0x42, 0x4).is_err());
+/// # Ok(())
+/// # }
+/// ```
+pub struct Io<const SIZE: usize = 0> {
+ addr: usize,
+ maxsize: usize,
+}
+
+macro_rules! define_read {
+ ($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) => {
+ /// Read IO data from a given offset known at compile time.
+ ///
+ /// Bound checks are performed on compile time, hence if the offset is not known at compile
+ /// time, the build will fail.
+ $(#[$attr])*
+ #[inline]
+ pub fn $name(&self, offset: usize) -> $type_name {
+ let addr = self.io_addr_assert::<$type_name>(offset);
+
+ // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
+ unsafe { bindings::$name(addr as _) }
+ }
+
+ /// Read IO data from a given offset.
+ ///
+ /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
+ /// out of bounds.
+ $(#[$attr])*
+ pub fn $try_name(&self, offset: usize) -> Result<$type_name> {
+ let addr = self.io_addr::<$type_name>(offset)?;
+
+ // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
+ Ok(unsafe { bindings::$name(addr as _) })
+ }
+ };
+}
+
+macro_rules! define_write {
+ ($(#[$attr:meta])* $name:ident, $try_name:ident, $type_name:ty) => {
+ /// Write IO data from a given offset known at compile time.
+ ///
+ /// Bound checks are performed on compile time, hence if the offset is not known at compile
+ /// time, the build will fail.
+ $(#[$attr])*
+ #[inline]
+ pub fn $name(&self, value: $type_name, offset: usize) {
+ let addr = self.io_addr_assert::<$type_name>(offset);
+
+ // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
+ unsafe { bindings::$name(value, addr as _, ) }
+ }
+
+ /// Write IO data from a given offset.
+ ///
+ /// Bound checks are performed on runtime, it fails if the offset (plus the type size) is
+ /// out of bounds.
+ $(#[$attr])*
+ pub fn $try_name(&self, value: $type_name, offset: usize) -> Result {
+ let addr = self.io_addr::<$type_name>(offset)?;
+
+ // SAFETY: By the type invariant `addr` is a valid address for MMIO operations.
+ unsafe { bindings::$name(value, addr as _) }
+ Ok(())
+ }
+ };
+}
+
+impl<const SIZE: usize> Io<SIZE> {
+ ///
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size
+ /// `maxsize`.
+ pub unsafe fn new(addr: usize, maxsize: usize) -> Result<Self> {
+ if maxsize < SIZE {
+ return Err(EINVAL);
+ }
+
+ // INVARIANT: Covered by the safety requirements of this function.
+ Ok(Self { addr, maxsize })
+ }
+
+ /// Returns the base address of this mapping.
+ #[inline]
+ pub fn base_addr(&self) -> usize {
+ self.addr
+ }
+
+ /// Returns the size of this mapping.
+ #[inline]
+ pub fn maxsize(&self) -> usize {
+ self.maxsize
+ }
+
+ #[inline]
+ const fn offset_valid<U>(offset: usize, size: usize) -> bool {
+ let type_size = core::mem::size_of::<U>();
+ if let Some(end) = offset.checked_add(type_size) {
+ end <= size && offset % type_size == 0
+ } else {
+ false
+ }
+ }
+
+ #[inline]
+ fn io_addr<U>(&self, offset: usize) -> Result<usize> {
+ if !Self::offset_valid::<U>(offset, self.maxsize()) {
+ return Err(EINVAL);
+ }
+
+ // Probably no need to check, since the safety requirements of `Self::new` guarantee that
+ // this can't overflow.
+ self.base_addr().checked_add(offset).ok_or(EINVAL)
+ }
+
+ #[inline]
+ fn io_addr_assert<U>(&self, offset: usize) -> usize {
+ build_assert!(Self::offset_valid::<U>(offset, SIZE));
+
+ self.base_addr() + offset
+ }
+
+ define_read!(readb, try_readb, u8);
+ define_read!(readw, try_readw, u16);
+ define_read!(readl, try_readl, u32);
+ define_read!(
+ #[cfg(CONFIG_64BIT)]
+ readq,
+ try_readq,
+ u64
+ );
+
+ define_read!(readb_relaxed, try_readb_relaxed, u8);
+ define_read!(readw_relaxed, try_readw_relaxed, u16);
+ define_read!(readl_relaxed, try_readl_relaxed, u32);
+ define_read!(
+ #[cfg(CONFIG_64BIT)]
+ readq_relaxed,
+ try_readq_relaxed,
+ u64
+ );
+
+ define_write!(writeb, try_writeb, u8);
+ define_write!(writew, try_writew, u16);
+ define_write!(writel, try_writel, u32);
+ define_write!(
+ #[cfg(CONFIG_64BIT)]
+ writeq,
+ try_writeq,
+ u64
+ );
+
+ define_write!(writeb_relaxed, try_writeb_relaxed, u8);
+ define_write!(writew_relaxed, try_writew_relaxed, u16);
+ define_write!(writel_relaxed, try_writel_relaxed, u32);
+ define_write!(
+ #[cfg(CONFIG_64BIT)]
+ writeq_relaxed,
+ try_writeq_relaxed,
+ u64
+ );
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b603b67dcd71..fa3b7514e6ae 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -70,6 +70,7 @@
#[doc(hidden)]
pub use bindings;
+pub mod io;
pub use macros;
pub use uapi;
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 10/16] rust: add devres abstraction
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (8 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 09/16] rust: add `io::Io` base type Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-31 14:03 ` Alice Ryhl
2024-11-27 12:21 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
` (8 subsequent siblings)
18 siblings, 2 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
Add a Rust abstraction for the kernel's devres (device resource
management) implementation.
The Devres type acts as a container to manage the lifetime and
accessibility of device bound resources. Therefore it registers a
devres callback and revokes access to the resource on invocation.
Users of the Devres abstraction can simply free the corresponding
resources in their Drop implementation, which is invoked when either the
Devres instance goes out of scope or the devres callback leads to the
resource being revoked, which implies a call to drop_in_place().
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 1 +
rust/helpers/device.c | 10 +++
rust/helpers/helpers.c | 1 +
rust/kernel/devres.rs | 180 +++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
5 files changed, 193 insertions(+)
create mode 100644 rust/helpers/device.c
create mode 100644 rust/kernel/devres.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 0a8882252257..97914d0752fb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6983,6 +6983,7 @@ F: include/linux/property.h
F: lib/kobj*
F: rust/kernel/device.rs
F: rust/kernel/device_id.rs
+F: rust/kernel/devres.rs
F: rust/kernel/driver.rs
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
diff --git a/rust/helpers/device.c b/rust/helpers/device.c
new file mode 100644
index 000000000000..b2135c6686b0
--- /dev/null
+++ b/rust/helpers/device.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/device.h>
+
+int rust_helper_devm_add_action(struct device *dev,
+ void (*action)(void *),
+ void *data)
+{
+ return devm_add_action(dev, action, data);
+}
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index e2f6b2197061..3acb2b9e52ec 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -11,6 +11,7 @@
#include "bug.c"
#include "build_assert.c"
#include "build_bug.c"
+#include "device.c"
#include "err.c"
#include "io.c"
#include "kunit.c"
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
new file mode 100644
index 000000000000..b23559f55214
--- /dev/null
+++ b/rust/kernel/devres.rs
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Devres abstraction
+//!
+//! [`Devres`] represents an abstraction for the kernel devres (device resource management)
+//! implementation.
+
+use crate::{
+ alloc::Flags,
+ bindings,
+ device::Device,
+ error::{Error, Result},
+ prelude::*,
+ revocable::Revocable,
+ sync::Arc,
+};
+
+use core::ffi::c_void;
+use core::ops::Deref;
+
+#[pin_data]
+struct DevresInner<T> {
+ #[pin]
+ data: Revocable<T>,
+}
+
+/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
+/// manage their lifetime.
+///
+/// [`Device`] bound resources should be freed when either the resource goes out of scope or the
+/// [`Device`] is unbound respectively, depending on what happens first.
+///
+/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the
+/// [`Device`] is unbound, revoking access to the encapsulated resource (see also [`Revocable`]).
+///
+/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource
+/// anymore.
+///
+/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s
+/// [`Drop`] implementation.
+///
+/// # Example
+///
+/// ```no_run
+/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::Io};
+/// # use core::ops::Deref;
+///
+/// // See also [`pci::Bar`] for a real example.
+/// struct IoMem<const SIZE: usize>(Io<SIZE>);
+///
+/// impl<const SIZE: usize> IoMem<SIZE> {
+/// /// # Safety
+/// ///
+/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
+/// /// virtual address space.
+/// unsafe fn new(paddr: usize) -> Result<Self>{
+///
+/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
+/// // valid for `ioremap`.
+/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE.try_into().unwrap()) };
+/// if addr.is_null() {
+/// return Err(ENOMEM);
+/// }
+///
+/// // SAFETY: `addr` is guaranteed to be the start of a valid I/O mapped memory region of
+/// // size `SIZE`.
+/// let io = unsafe { Io::new(addr as _, SIZE)? };
+///
+/// Ok(IoMem(io))
+/// }
+/// }
+///
+/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
+/// fn drop(&mut self) {
+/// // SAFETY: Safe as by the invariant of `Io`.
+/// unsafe { bindings::iounmap(self.0.base_addr() as _); };
+/// }
+/// }
+///
+/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
+/// type Target = Io<SIZE>;
+///
+/// fn deref(&self) -> &Self::Target {
+/// &self.0
+/// }
+/// }
+///
+/// # fn no_run() -> Result<(), Error> {
+/// # // SAFETY: Invalid usage; just for the example to get an `ARef<Device>` instance.
+/// # let dev = unsafe { Device::from_raw(core::ptr::null_mut()) };
+///
+/// // SAFETY: Invalid usage for example purposes.
+/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
+/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?;
+///
+/// let res = devres.try_access().ok_or(ENXIO)?;
+/// res.writel(0x42, 0x0);
+/// # Ok(())
+/// # }
+/// ```
+pub struct Devres<T>(Arc<DevresInner<T>>);
+
+impl<T> DevresInner<T> {
+ fn new(dev: &Device, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
+ let inner = Arc::pin_init(
+ pin_init!( DevresInner {
+ data <- Revocable::new(data),
+ }),
+ flags,
+ )?;
+
+ // Convert `Arc<DevresInner>` into a raw pointer and make devres own this reference until
+ // `Self::devres_callback` is called.
+ let data = inner.clone().into_raw();
+
+ // SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is
+ // detached.
+ let ret = unsafe {
+ bindings::devm_add_action(dev.as_raw(), Some(Self::devres_callback), data as _)
+ };
+
+ if ret != 0 {
+ // SAFETY: We just created another reference to `inner` in order to pass it to
+ // `bindings::devm_add_action`. If `bindings::devm_add_action` fails, we have to drop
+ // this reference accordingly.
+ let _ = unsafe { Arc::from_raw(data) };
+ return Err(Error::from_errno(ret));
+ }
+
+ Ok(inner)
+ }
+
+ #[allow(clippy::missing_safety_doc)]
+ unsafe extern "C" fn devres_callback(ptr: *mut c_void) {
+ let ptr = ptr as *mut DevresInner<T>;
+ // Devres owned this memory; now that we received the callback, drop the `Arc` and hence the
+ // reference.
+ // SAFETY: Safe, since we leaked an `Arc` reference to devm_add_action() in
+ // `DevresInner::new`.
+ let inner = unsafe { Arc::from_raw(ptr) };
+
+ inner.data.revoke();
+ }
+}
+
+impl<T> Devres<T> {
+ /// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the
+ /// returned `Devres` instance' `data` will be revoked once the device is detached.
+ pub fn new(dev: &Device, data: T, flags: Flags) -> Result<Self> {
+ let inner = DevresInner::new(dev, data, flags)?;
+
+ Ok(Devres(inner))
+ }
+
+ /// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data`
+ /// is owned by devres and will be revoked / dropped, once the device is detached.
+ pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result {
+ let _ = DevresInner::new(dev, data, flags)?;
+
+ Ok(())
+ }
+}
+
+impl<T> Deref for Devres<T> {
+ type Target = Revocable<T>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0.data
+ }
+}
+
+impl<T> Drop for Devres<T> {
+ fn drop(&mut self) {
+ // Revoke the data, such that it gets dropped already and the actual resource is freed.
+ // `DevresInner` has to stay alive until the devres callback has been called. This is
+ // necessary since we don't know when `Devres` is dropped and calling
+ // `devm_remove_action()` instead could race with `devres_release_all()`.
+ self.revoke();
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index fa3b7514e6ae..5cb892419453 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -39,6 +39,7 @@
mod build_assert;
pub mod device;
pub mod device_id;
+pub mod devres;
pub mod driver;
pub mod error;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (9 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 10/16] rust: add devres abstraction Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-27 22:42 ` Boqun Feng
2024-10-29 21:16 ` Christian Schrefl
2024-10-22 21:31 ` [PATCH v3 12/16] rust: pci: implement I/O mappable `pci::Bar` Danilo Krummrich
` (7 subsequent siblings)
18 siblings, 2 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI
driver. This includes the following data structures:
The `pci::Driver` trait represents the interface to the driver and
provides `pci::Driver::probe` for the driver to implement.
The `pci::Device` abstraction represents a `struct pci_dev` and provides
abstractions for common functions, such as `pci::Device::set_master`.
In order to provide the PCI specific parts to a generic
`driver::Registration` the `driver::RegistrationOps` trait is implemented
by `pci::Adapter`.
`pci::DeviceId` implements PCI device IDs based on the generic
`device_id::RawDevceId` abstraction.
Co-developed-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 1 +
rust/bindings/bindings_helper.h | 1 +
rust/helpers/helpers.c | 1 +
rust/helpers/pci.c | 18 ++
rust/kernel/lib.rs | 2 +
rust/kernel/pci.rs | 284 ++++++++++++++++++++++++++++++++
6 files changed, 307 insertions(+)
create mode 100644 rust/helpers/pci.c
create mode 100644 rust/kernel/pci.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 97914d0752fb..2d00d3845b4a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17939,6 +17939,7 @@ F: include/asm-generic/pci*
F: include/linux/of_pci.h
F: include/linux/pci*
F: include/uapi/linux/pci*
+F: rust/kernel/pci.rs
PCIE DRIVER FOR AMAZON ANNAPURNA LABS
M: Jonathan Chocron <jonnyc@amazon.com>
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index a80783fcbe04..cd4edd6496ae 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -15,6 +15,7 @@
#include <linux/firmware.h>
#include <linux/jiffies.h>
#include <linux/mdio.h>
+#include <linux/pci.h>
#include <linux/phy.h>
#include <linux/refcount.h>
#include <linux/sched.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 3acb2b9e52ec..8bc6e9735589 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -17,6 +17,7 @@
#include "kunit.c"
#include "mutex.c"
#include "page.c"
+#include "pci.c"
#include "rbtree.c"
#include "rcu.c"
#include "refcount.c"
diff --git a/rust/helpers/pci.c b/rust/helpers/pci.c
new file mode 100644
index 000000000000..7c4f3ba49486
--- /dev/null
+++ b/rust/helpers/pci.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/pci.h>
+
+void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data)
+{
+ pci_set_drvdata(pdev, data);
+}
+
+void *rust_helper_pci_get_drvdata(struct pci_dev *pdev)
+{
+ return pci_get_drvdata(pdev);
+}
+
+u64 rust_helper_pci_resource_len(struct pci_dev *pdev, int bar)
+{
+ return pci_resource_len(pdev, bar);
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 5cb892419453..3ec690eb6d43 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -73,6 +73,8 @@
pub use bindings;
pub mod io;
pub use macros;
+#[cfg(all(CONFIG_PCI, CONFIG_PCI_MSI))]
+pub mod pci;
pub use uapi;
#[doc(hidden)]
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
new file mode 100644
index 000000000000..ccc9a5f322e4
--- /dev/null
+++ b/rust/kernel/pci.rs
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Abstractions for the PCI bus.
+//!
+//! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h)
+
+use crate::{
+ bindings, container_of, device,
+ device_id::RawDeviceId,
+ driver,
+ error::{to_result, Result},
+ str::CStr,
+ types::{ARef, ForeignOwnable},
+ ThisModule,
+};
+use core::ops::Deref;
+use kernel::prelude::*;
+
+/// An adapter for the registration of PCI drivers.
+pub struct Adapter<T: Driver>(T);
+
+impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+ type RegType = bindings::pci_driver;
+
+ fn register(
+ pdrv: &mut Self::RegType,
+ name: &'static CStr,
+ module: &'static ThisModule,
+ ) -> Result {
+ pdrv.name = name.as_char_ptr();
+ pdrv.probe = Some(Self::probe_callback);
+ pdrv.remove = Some(Self::remove_callback);
+ pdrv.id_table = T::ID_TABLE.as_ptr();
+
+ // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
+ to_result(unsafe {
+ bindings::__pci_register_driver(pdrv as _, module.0, name.as_char_ptr())
+ })
+ }
+
+ fn unregister(pdrv: &mut Self::RegType) {
+ // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
+ unsafe { bindings::pci_unregister_driver(pdrv) }
+ }
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+ extern "C" fn probe_callback(
+ pdev: *mut bindings::pci_dev,
+ id: *const bindings::pci_device_id,
+ ) -> core::ffi::c_int {
+ // SAFETY: The PCI bus only ever calls the probe callback with a valid pointer to a
+ // `struct pci_dev`.
+ let dev = unsafe { device::Device::from_raw(&mut (*pdev).dev) };
+ // SAFETY: `dev` is guaranteed to be embedded in a valid `struct pci_dev` by the call
+ // above.
+ let mut pdev = unsafe { Device::from_dev(dev) };
+
+ // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct pci_device_id` and
+ // does not add additional invariants, so it's safe to transmute.
+ let id = unsafe { &*id.cast::<DeviceId>() };
+ let info = T::ID_TABLE.info(id.index());
+
+ match T::probe(&mut pdev, id, info) {
+ Ok(data) => {
+ // Let the `struct pci_dev` own a reference of the driver's private data.
+ // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
+ // `struct pci_dev`.
+ unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
+ }
+ Err(err) => return Error::to_errno(err),
+ }
+
+ 0
+ }
+
+ extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
+ // SAFETY: The PCI bus only ever calls the remove callback with a valid pointer to a
+ // `struct pci_dev`.
+ let ptr = unsafe { bindings::pci_get_drvdata(pdev) };
+
+ // SAFETY: `remove_callback` is only ever called after a successful call to
+ // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
+ // `KBox<T>` pointer created through `KBox::into_foreign`.
+ let _ = unsafe { KBox::<T>::from_foreign(ptr) };
+ }
+}
+
+/// Declares a kernel module that exposes a single PCI driver.
+///
+/// # Example
+///
+///```ignore
+/// kernel::module_pci_driver! {
+/// type: MyDriver,
+/// name: "Module name",
+/// author: "Author name",
+/// description: "Description",
+/// license: "GPL v2",
+/// }
+///```
+#[macro_export]
+macro_rules! module_pci_driver {
+($($f:tt)*) => {
+ $crate::module_driver!(<T>, $crate::pci::Adapter<T>, { $($f)* });
+};
+}
+
+/// Abstraction for bindings::pci_device_id.
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct DeviceId(bindings::pci_device_id);
+
+impl DeviceId {
+ const PCI_ANY_ID: u32 = !0;
+
+ /// PCI_DEVICE macro.
+ pub const fn new(vendor: u32, device: u32) -> Self {
+ Self(bindings::pci_device_id {
+ vendor,
+ device,
+ subvendor: DeviceId::PCI_ANY_ID,
+ subdevice: DeviceId::PCI_ANY_ID,
+ class: 0,
+ class_mask: 0,
+ driver_data: 0,
+ override_only: 0,
+ })
+ }
+
+ /// PCI_DEVICE_CLASS macro.
+ pub const fn with_class(class: u32, class_mask: u32) -> Self {
+ Self(bindings::pci_device_id {
+ vendor: DeviceId::PCI_ANY_ID,
+ device: DeviceId::PCI_ANY_ID,
+ subvendor: DeviceId::PCI_ANY_ID,
+ subdevice: DeviceId::PCI_ANY_ID,
+ class,
+ class_mask,
+ driver_data: 0,
+ override_only: 0,
+ })
+ }
+}
+
+// Allow drivers R/O access to the fields of `pci_device_id`; should we prefer accessor functions
+// to void exposing C structure fields?
+impl Deref for DeviceId {
+ type Target = bindings::pci_device_id;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+// SAFETY:
+// * `DeviceId` is a `#[repr(transparent)` wrapper of `pci_device_id` and does not add
+// additional invariants, so it's safe to transmute to `RawType`.
+// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
+unsafe impl RawDeviceId for DeviceId {
+ type RawType = bindings::pci_device_id;
+
+ const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::pci_device_id, driver_data);
+
+ fn index(&self) -> usize {
+ self.driver_data as _
+ }
+}
+
+/// IdTable type for PCI
+pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
+
+/// Create a PCI `IdTable` with its alias for modpost.
+#[macro_export]
+macro_rules! pci_device_table {
+ ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
+ const $table_name: $crate::device_id::IdArray<
+ $crate::pci::DeviceId,
+ $id_info_type,
+ { $table_data.len() },
+ > = $crate::device_id::IdArray::new($table_data);
+
+ $crate::module_device_table!("pci", $module_table_name, $table_name);
+ };
+}
+
+/// The PCI driver trait.
+///
+/// # Example
+///
+///```
+/// # use kernel::{bindings, pci};
+///
+/// struct MyDriver;
+///
+/// kernel::pci_device_table!(
+/// PCI_TABLE,
+/// MODULE_PCI_TABLE,
+/// <MyDriver as pci::Driver>::IdInfo,
+/// [
+/// (pci::DeviceId::new(bindings::PCI_VENDOR_ID_REDHAT, bindings::PCI_ANY_ID as u32), ())
+/// ]
+/// );
+///
+/// impl pci::Driver for MyDriver {
+/// type IdInfo = ();
+/// const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
+///
+/// fn probe(
+/// _pdev: &mut pci::Device,
+/// _id: &pci::DeviceId,
+/// _id_info: &Self::IdInfo,
+/// ) -> Result<Pin<KBox<Self>>> {
+/// Err(ENODEV)
+/// }
+/// }
+///```
+/// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the
+/// `Adapter` documentation for an example.
+pub trait Driver {
+ /// The type holding information about each device id supported by the driver.
+ ///
+ /// TODO: Use associated_type_defaults once stabilized:
+ ///
+ /// type IdInfo: 'static = ();
+ type IdInfo: 'static;
+
+ /// The table of device ids supported by the driver.
+ const ID_TABLE: IdTable<Self::IdInfo>;
+
+ /// PCI driver probe.
+ ///
+ /// Called when a new platform device is added or discovered.
+ /// Implementers should attempt to initialize the device here.
+ fn probe(dev: &mut Device, id: &DeviceId, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
+}
+
+/// The PCI device representation.
+///
+/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI
+/// device, hence, also increments the base device' reference count.
+#[derive(Clone)]
+pub struct Device(ARef<device::Device>);
+
+impl Device {
+ /// Create a PCI Device instance from an existing `device::Device`.
+ ///
+ /// # Safety
+ ///
+ /// `dev` must be an `ARef<device::Device>` whose underlying `bindings::device` is a member of
+ /// a `bindings::pci_dev`.
+ pub unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
+ Self(dev)
+ }
+
+ fn as_raw(&self) -> *mut bindings::pci_dev {
+ // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
+ // embedded in `struct pci_dev`.
+ unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ }
+ }
+
+ /// Enable memory resources for this device.
+ pub fn enable_device_mem(&self) -> Result {
+ // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
+ let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) };
+ if ret != 0 {
+ Err(Error::from_errno(ret))
+ } else {
+ Ok(())
+ }
+ }
+
+ /// Enable bus-mastering for this device.
+ pub fn set_master(&self) {
+ // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
+ unsafe { bindings::pci_set_master(self.as_raw()) };
+ }
+}
+
+impl AsRef<device::Device> for Device {
+ fn as_ref(&self) -> &device::Device {
+ &self.0
+ }
+}
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 12/16] rust: pci: implement I/O mappable `pci::Bar`
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (10 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 13/16] samples: rust: add Rust PCI sample driver Danilo Krummrich
` (6 subsequent siblings)
18 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
Implement `pci::Bar`, `pci::Device::iomap_region` and
`pci::Device::iomap_region_sized` to allow for I/O mappings of PCI BARs.
To ensure that a `pci::Bar`, and hence the I/O memory mapping, can't
out-live the PCI device, the `pci::Bar` type is always embedded into a
`Devres` container, such that the `pci::Bar` is revoked once the device
is unbound and hence the I/O mapped memory is unmapped.
A `pci::Bar` can be requested with (`pci::Device::iomap_region_sized`) or
without (`pci::Device::iomap_region`) a const generic representing the
minimal requested size of the I/O mapped memory region. In case of the
latter only runtime checked I/O reads / writes are possible.
Co-developed-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Philipp Stanner <pstanner@redhat.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/pci.rs | 145 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 145 insertions(+)
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index ccc9a5f322e4..58f7d9c0045b 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -5,10 +5,13 @@
//! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h)
use crate::{
+ alloc::flags::*,
bindings, container_of, device,
device_id::RawDeviceId,
+ devres::Devres,
driver,
error::{to_result, Result},
+ io::Io,
str::CStr,
types::{ARef, ForeignOwnable},
ThisModule,
@@ -239,9 +242,116 @@ pub trait Driver {
///
/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI
/// device, hence, also increments the base device' reference count.
+///
+/// # Invariants
+///
+/// `Device` hold a valid reference of `ARef<device::Device>` whose underlying `struct device` is a
+/// member of a `struct pci_dev`.
#[derive(Clone)]
pub struct Device(ARef<device::Device>);
+/// A PCI BAR to perform I/O-Operations on.
+///
+/// # Invariants
+///
+/// `Bar` always holds an `Io` inststance that holds a valid pointer to the start of the I/O memory
+/// mapped PCI bar and its size.
+pub struct Bar<const SIZE: usize = 0> {
+ pdev: Device,
+ io: Io<SIZE>,
+ num: i32,
+}
+
+impl<const SIZE: usize> Bar<SIZE> {
+ fn new(pdev: Device, num: u32, name: &CStr) -> Result<Self> {
+ let len = pdev.resource_len(num)?;
+ if len == 0 {
+ return Err(ENOMEM);
+ }
+
+ // Convert to `i32`, since that's what all the C bindings use.
+ let num = i32::try_from(num)?;
+
+ // SAFETY:
+ // `pdev` is valid by the invariants of `Device`.
+ // `num` is checked for validity by a previous call to `Device::resource_len`.
+ // `name` is always valid.
+ let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) };
+ if ret != 0 {
+ return Err(EBUSY);
+ }
+
+ // SAFETY:
+ // `pdev` is valid by the invariants of `Device`.
+ // `num` is checked for validity by a previous call to `Device::resource_len`.
+ // `name` is always valid.
+ let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize;
+ if ioptr == 0 {
+ // SAFETY:
+ // `pdev` valid by the invariants of `Device`.
+ // `num` is checked for validity by a previous call to `Device::resource_len`.
+ unsafe { bindings::pci_release_region(pdev.as_raw(), num) };
+ return Err(ENOMEM);
+ }
+
+ // SAFETY: `ioptr` is guaranteed to be the start of a valid I/O mapped memory region of size
+ // `len`.
+ let io = match unsafe { Io::new(ioptr, len as usize) } {
+ Ok(io) => io,
+ Err(err) => {
+ // SAFETY:
+ // `pdev` is valid by the invariants of `Device`.
+ // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region.
+ // `num` is checked for validity by a previous call to `Device::resource_len`.
+ unsafe { Self::do_release(&pdev, ioptr, num) };
+ return Err(err);
+ }
+ };
+
+ Ok(Bar { pdev, io, num })
+ }
+
+ /// # Safety
+ ///
+ /// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`.
+ unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) {
+ // SAFETY:
+ // `pdev` is valid by the invariants of `Device`.
+ // `ioptr` is valid by the safety requirements.
+ // `num` is valid by the safety requirements.
+ unsafe {
+ bindings::pci_iounmap(pdev.as_raw(), ioptr as _);
+ bindings::pci_release_region(pdev.as_raw(), num);
+ }
+ }
+
+ fn release(&self) {
+ // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`.
+ unsafe { Self::do_release(&self.pdev, self.io.base_addr(), self.num) };
+ }
+}
+
+impl Bar {
+ fn index_is_valid(index: u32) -> bool {
+ // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries.
+ index < bindings::PCI_NUM_RESOURCES
+ }
+}
+
+impl<const SIZE: usize> Drop for Bar<SIZE> {
+ fn drop(&mut self) {
+ self.release();
+ }
+}
+
+impl<const SIZE: usize> Deref for Bar<SIZE> {
+ type Target = Io<SIZE>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.io
+ }
+}
+
impl Device {
/// Create a PCI Device instance from an existing `device::Device`.
///
@@ -275,6 +385,41 @@ pub fn set_master(&self) {
// SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
unsafe { bindings::pci_set_master(self.as_raw()) };
}
+
+ /// Returns the size of the given PCI bar resource.
+ pub fn resource_len(&self, bar: u32) -> Result<bindings::resource_size_t> {
+ if !Bar::index_is_valid(bar) {
+ return Err(EINVAL);
+ }
+
+ // SAFETY:
+ // - `bar` is a valid bar number, as guaranteed by the above call to `Bar::index_is_valid`,
+ // - by its type invariant `self.as_raw` is always a valid pointer to a `struct pci_dev`.
+ Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) })
+ }
+
+ /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks
+ /// can be performed on compile time for offsets (plus the requested type size) < SIZE.
+ pub fn iomap_region_sized<const SIZE: usize>(
+ &self,
+ bar: u32,
+ name: &CStr,
+ ) -> Result<Devres<Bar<SIZE>>> {
+ let bar = Bar::<SIZE>::new(self.clone(), bar, name)?;
+ let devres = Devres::new(self.as_ref(), bar, GFP_KERNEL)?;
+
+ Ok(devres)
+ }
+
+ /// Mapps an entire PCI-BAR after performing a region-request on it.
+ pub fn iomap_region(&self, bar: u32, name: &CStr) -> Result<Devres<Bar>> {
+ self.iomap_region_sized::<0>(bar, name)
+ }
+
+ /// Returns a new `ARef` of the base `device::Device`.
+ pub fn as_dev(&self) -> ARef<device::Device> {
+ self.0.clone()
+ }
}
impl AsRef<device::Device> for Device {
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 13/16] samples: rust: add Rust PCI sample driver
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (11 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 12/16] rust: pci: implement I/O mappable `pci::Bar` Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-23 15:57 ` Rob Herring
2024-10-22 21:31 ` [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
` (5 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
This commit adds a sample Rust PCI driver for QEMU's "pci-testdev"
device. To enable this device QEMU has to be called with
`-device pci-testdev`.
The same driver shows how to use the PCI device / driver abstractions,
as well as how to request and map PCI BARs, including a short sequence of
MMIO operations.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 1 +
samples/rust/Kconfig | 11 ++++
samples/rust/Makefile | 1 +
samples/rust/rust_driver_pci.rs | 109 ++++++++++++++++++++++++++++++++
4 files changed, 122 insertions(+)
create mode 100644 samples/rust/rust_driver_pci.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 2d00d3845b4a..d9c512a3e72b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17940,6 +17940,7 @@ F: include/linux/of_pci.h
F: include/linux/pci*
F: include/uapi/linux/pci*
F: rust/kernel/pci.rs
+F: samples/rust/rust_driver_pci.rs
PCIE DRIVER FOR AMAZON ANNAPURNA LABS
M: Jonathan Chocron <jonnyc@amazon.com>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index b0f74a81c8f9..6d468193cdd8 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -30,6 +30,17 @@ config SAMPLE_RUST_PRINT
If unsure, say N.
+config SAMPLE_RUST_DRIVER_PCI
+ tristate "PCI Driver"
+ depends on PCI
+ help
+ This option builds the Rust PCI driver sample.
+
+ To compile this as a module, choose M here:
+ the module will be called driver_pci.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 03086dabbea4..b66767f4a62a 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -2,5 +2,6 @@
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
new file mode 100644
index 000000000000..d24dc1fde9e8
--- /dev/null
+++ b/samples/rust/rust_driver_pci.rs
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust PCI driver sample (based on QEMU's `pci-testdev`).
+//!
+//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
+
+use kernel::{bindings, c_str, devres::Devres, pci, prelude::*};
+
+struct Regs;
+
+impl Regs {
+ const TEST: usize = 0x0;
+ const OFFSET: usize = 0x4;
+ const DATA: usize = 0x8;
+ const COUNT: usize = 0xC;
+ const END: usize = 0x10;
+}
+
+type Bar0 = pci::Bar<{ Regs::END }>;
+
+#[derive(Debug)]
+struct TestIndex(u8);
+
+impl TestIndex {
+ const NO_EVENTFD: Self = Self(0);
+}
+
+struct SampleDriver {
+ pdev: pci::Device,
+ bar: Devres<Bar0>,
+}
+
+kernel::pci_device_table!(
+ PCI_TABLE,
+ MODULE_PCI_TABLE,
+ <SampleDriver as pci::Driver>::IdInfo,
+ [(
+ pci::DeviceId::new(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
+ TestIndex::NO_EVENTFD
+ )]
+);
+
+impl SampleDriver {
+ fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
+ // Select the test.
+ bar.writeb(index.0, Regs::TEST);
+
+ let offset = u32::from_le(bar.readl(Regs::OFFSET)) as usize;
+ let data = bar.readb(Regs::DATA);
+
+ // Write `data` to `offset` to increase `count` by one.
+ //
+ // Note that we need `try_writeb`, since `offset` can't be checked at compile-time.
+ bar.try_writeb(data, offset)?;
+
+ Ok(u32::from_le(bar.readl(Regs::COUNT)))
+ }
+}
+
+impl pci::Driver for SampleDriver {
+ type IdInfo = TestIndex;
+
+ const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
+
+ fn probe(
+ pdev: &mut pci::Device,
+ _id: &pci::DeviceId,
+ info: &Self::IdInfo,
+ ) -> Result<Pin<KBox<Self>>> {
+ dev_dbg!(pdev.as_ref(), "Probe Rust PCI driver sample.\n");
+
+ pdev.enable_device_mem()?;
+ pdev.set_master();
+
+ let bar = pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci"))?;
+
+ let drvdata = KBox::new(
+ Self {
+ pdev: pdev.clone(),
+ bar,
+ },
+ GFP_KERNEL,
+ )?;
+
+ let bar = drvdata.bar.try_access().ok_or(ENXIO)?;
+
+ dev_info!(
+ pdev.as_ref(),
+ "pci-testdev data-match count: {}\n",
+ Self::testdev(info, &bar)?
+ );
+
+ Ok(drvdata.into())
+ }
+}
+
+impl Drop for SampleDriver {
+ fn drop(&mut self) {
+ dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n");
+ }
+}
+
+kernel::module_pci_driver! {
+ type: SampleDriver,
+ name: "rust_driver_pci",
+ author: "Danilo Krummrich",
+ description: "Rust PCI driver",
+ license: "GPL v2",
+}
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (12 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 13/16] samples: rust: add Rust PCI sample driver Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-22 23:03 ` Rob Herring
` (2 more replies)
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
` (4 subsequent siblings)
18 siblings, 3 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
`of::DeviceId` is an abstraction around `struct of_device_id`.
This is used by subsequent patches, in particular the platform bus
abstractions, to create OF device ID tables.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 1 +
rust/bindings/bindings_helper.h | 1 +
rust/kernel/lib.rs | 1 +
rust/kernel/of.rs | 63 +++++++++++++++++++++++++++++++++
4 files changed, 66 insertions(+)
create mode 100644 rust/kernel/of.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index d9c512a3e72b..87eb9a7869eb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17340,6 +17340,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
F: Documentation/ABI/testing/sysfs-firmware-ofw
F: drivers/of/
F: include/linux/of*.h
+F: rust/kernel/of.rs
F: scripts/dtc/
F: tools/testing/selftests/dt/
K: of_overlay_notifier_
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index cd4edd6496ae..312f03cbdce9 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -15,6 +15,7 @@
#include <linux/firmware.h>
#include <linux/jiffies.h>
#include <linux/mdio.h>
+#include <linux/of_device.h>
#include <linux/pci.h>
#include <linux/phy.h>
#include <linux/refcount.h>
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3ec690eb6d43..5946f59f1688 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -51,6 +51,7 @@
pub mod list;
#[cfg(CONFIG_NET)]
pub mod net;
+pub mod of;
pub mod page;
pub mod prelude;
pub mod print;
diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs
new file mode 100644
index 000000000000..a37629997974
--- /dev/null
+++ b/rust/kernel/of.rs
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Open Firmware abstractions.
+//!
+//! C header: [`include/linux/of_*.h`](srctree/include/linux/of_*.h)
+
+use crate::{bindings, device_id::RawDeviceId, prelude::*};
+
+/// An open firmware device id.
+#[derive(Clone, Copy)]
+pub struct DeviceId(bindings::of_device_id);
+
+// SAFETY:
+// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add
+// additional invariants, so it's safe to transmute to `RawType`.
+// * `DRIVER_DATA_OFFSET` is the offset to the `data` field.
+unsafe impl RawDeviceId for DeviceId {
+ type RawType = bindings::of_device_id;
+
+ const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data);
+
+ fn index(&self) -> usize {
+ self.0.data as _
+ }
+}
+
+impl DeviceId {
+ /// Create a new device id from an OF 'compatible' string.
+ pub const fn new(compatible: &'static CStr) -> Self {
+ let src = compatible.as_bytes_with_nul();
+ // Replace with `bindings::of_device_id::default()` once stabilized for `const`.
+ // SAFETY: FFI type is valid to be zero-initialized.
+ let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() };
+
+ let mut i = 0;
+ while i < src.len() {
+ of.compatible[i] = src[i] as _;
+ i += 1;
+ }
+
+ Self(of)
+ }
+
+ /// The compatible string of the embedded `struct bindings::of_device_id` as `&CStr`.
+ pub fn compatible<'a>(&self) -> &'a CStr {
+ // SAFETY: `self.compatible` is a valid `char` pointer.
+ unsafe { CStr::from_char_ptr(self.0.compatible.as_ptr()) }
+ }
+}
+
+/// Create an OF `IdTable` with an "alias" for modpost.
+#[macro_export]
+macro_rules! of_device_table {
+ ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
+ const $table_name: $crate::device_id::IdArray<
+ $crate::of::DeviceId,
+ $id_info_type,
+ { $table_data.len() },
+ > = $crate::device_id::IdArray::new($table_data);
+
+ $crate::module_device_table!("of", $module_table_name, $table_name);
+ };
+}
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (13 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-22 23:47 ` Rob Herring
` (6 more replies)
2024-10-22 21:31 ` [PATCH v3 16/16] samples: rust: add Rust platform sample driver Danilo Krummrich
` (3 subsequent siblings)
18 siblings, 7 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
Implement the basic platform bus abstractions required to write a basic
platform driver. This includes the following data structures:
The `platform::Driver` trait represents the interface to the driver and
provides `pci::Driver::probe` for the driver to implement.
The `platform::Device` abstraction represents a `struct platform_device`.
In order to provide the platform bus specific parts to a generic
`driver::Registration` the `driver::RegistrationOps` trait is implemented
by `platform::Adapter`.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 1 +
rust/bindings/bindings_helper.h | 1 +
rust/helpers/helpers.c | 1 +
rust/helpers/platform.c | 13 ++
rust/kernel/lib.rs | 1 +
rust/kernel/platform.rs | 217 ++++++++++++++++++++++++++++++++
6 files changed, 234 insertions(+)
create mode 100644 rust/helpers/platform.c
create mode 100644 rust/kernel/platform.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 87eb9a7869eb..173540375863 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6985,6 +6985,7 @@ F: rust/kernel/device.rs
F: rust/kernel/device_id.rs
F: rust/kernel/devres.rs
F: rust/kernel/driver.rs
+F: rust/kernel/platform.rs
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
M: Nishanth Menon <nm@ti.com>
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 312f03cbdce9..217c776615b9 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -18,6 +18,7 @@
#include <linux/of_device.h>
#include <linux/pci.h>
#include <linux/phy.h>
+#include <linux/platform_device.h>
#include <linux/refcount.h>
#include <linux/sched.h>
#include <linux/slab.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 8bc6e9735589..663cdc2a45e0 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -17,6 +17,7 @@
#include "kunit.c"
#include "mutex.c"
#include "page.c"
+#include "platform.c"
#include "pci.c"
#include "rbtree.c"
#include "rcu.c"
diff --git a/rust/helpers/platform.c b/rust/helpers/platform.c
new file mode 100644
index 000000000000..ab9b9f317301
--- /dev/null
+++ b/rust/helpers/platform.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/platform_device.h>
+
+void *rust_helper_platform_get_drvdata(const struct platform_device *pdev)
+{
+ return platform_get_drvdata(pdev);
+}
+
+void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
+{
+ platform_set_drvdata(pdev, data);
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 5946f59f1688..9e8dcd6d7c01 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -53,6 +53,7 @@
pub mod net;
pub mod of;
pub mod page;
+pub mod platform;
pub mod prelude;
pub mod print;
pub mod rbtree;
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
new file mode 100644
index 000000000000..addf5356f44f
--- /dev/null
+++ b/rust/kernel/platform.rs
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Abstractions for the platform bus.
+//!
+//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
+
+use crate::{
+ bindings, container_of, device,
+ device_id::RawDeviceId,
+ driver,
+ error::{to_result, Result},
+ of,
+ prelude::*,
+ str::CStr,
+ types::{ARef, ForeignOwnable},
+ ThisModule,
+};
+
+/// An adapter for the registration of platform drivers.
+pub struct Adapter<T: Driver>(T);
+
+impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+ type RegType = bindings::platform_driver;
+
+ fn register(
+ pdrv: &mut Self::RegType,
+ name: &'static CStr,
+ module: &'static ThisModule,
+ ) -> Result {
+ pdrv.driver.name = name.as_char_ptr();
+ pdrv.probe = Some(Self::probe_callback);
+
+ // Both members of this union are identical in data layout and semantics.
+ pdrv.__bindgen_anon_1.remove = Some(Self::remove_callback);
+ pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();
+
+ // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
+ to_result(unsafe { bindings::__platform_driver_register(pdrv, module.0) })
+ }
+
+ fn unregister(pdrv: &mut Self::RegType) {
+ // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
+ unsafe { bindings::platform_driver_unregister(pdrv) };
+ }
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+ fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
+ let table = T::ID_TABLE;
+ let id = T::of_match_device(pdev)?;
+
+ Some(table.info(id.index()))
+ }
+
+ extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
+ // SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`.
+ let dev = unsafe { device::Device::from_raw(&mut (*pdev).dev) };
+ // SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the
+ // call above.
+ let mut pdev = unsafe { Device::from_dev(dev) };
+
+ let info = Self::id_info(&pdev);
+ match T::probe(&mut pdev, info) {
+ Ok(data) => {
+ // Let the `struct platform_device` own a reference of the driver's private data.
+ // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
+ // `struct platform_device`.
+ unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
+ }
+ Err(err) => return Error::to_errno(err),
+ }
+
+ 0
+ }
+
+ extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
+ // SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
+ let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
+
+ // SAFETY: `remove_callback` is only ever called after a successful call to
+ // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
+ // `KBox<T>` pointer created through `KBox::into_foreign`.
+ let _ = unsafe { KBox::<T>::from_foreign(ptr) };
+ }
+}
+
+/// Declares a kernel module that exposes a single platform driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_platform_driver! {
+/// type: MyDriver,
+/// name: "Module name",
+/// author: "Author name",
+/// description: "Description",
+/// license: "GPL v2",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_platform_driver {
+ ($($f:tt)*) => {
+ $crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
+ };
+}
+
+/// IdTable type for platform drivers.
+pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
+
+/// The platform driver trait.
+///
+/// # Example
+///
+///```
+/// # use kernel::{bindings, c_str, of, platform};
+///
+/// struct MyDriver;
+///
+/// kernel::of_device_table!(
+/// OF_TABLE,
+/// MODULE_OF_TABLE,
+/// <MyDriver as platform::Driver>::IdInfo,
+/// [
+/// (of::DeviceId::new(c_str!("redhat,my-device")), ())
+/// ]
+/// );
+///
+/// impl platform::Driver for MyDriver {
+/// type IdInfo = ();
+/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
+///
+/// fn probe(
+/// _pdev: &mut platform::Device,
+/// _id_info: Option<&Self::IdInfo>,
+/// ) -> Result<Pin<KBox<Self>>> {
+/// Err(ENODEV)
+/// }
+/// }
+///```
+/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
+/// the `Adapter` documentation for an example.
+pub trait Driver {
+ /// The type holding information about each device id supported by the driver.
+ ///
+ /// TODO: Use associated_type_defaults once stabilized:
+ ///
+ /// type IdInfo: 'static = ();
+ type IdInfo: 'static;
+
+ /// The table of device ids supported by the driver.
+ const ID_TABLE: IdTable<Self::IdInfo>;
+
+ /// Platform driver probe.
+ ///
+ /// Called when a new platform device is added or discovered.
+ /// Implementers should attempt to initialize the device here.
+ fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
+
+ /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
+ fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
+ let table = Self::ID_TABLE;
+
+ // SAFETY:
+ // - `table` has static lifetime, hence it's valid for read,
+ // - `dev` is guaranteed to be valid while it's alive, and so is
+ // `pdev.as_dev().as_raw()`.
+ let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_dev().as_raw()) };
+
+ if raw_id.is_null() {
+ None
+ } else {
+ // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and
+ // does not add additional invariants, so it's safe to transmute.
+ Some(unsafe { &*raw_id.cast::<of::DeviceId>() })
+ }
+ }
+}
+
+/// The platform device representation.
+///
+/// A platform device is based on an always reference counted `device:Device` instance. Cloning a
+/// platform device, hence, also increments the base device' reference count.
+///
+/// # Invariants
+///
+/// `Device` holds a valid reference of `ARef<device::Device>` whose underlying `struct device` is a
+/// member of a `struct platform_device`.
+#[derive(Clone)]
+pub struct Device(ARef<device::Device>);
+
+impl Device {
+ /// Convert a raw kernel device into a `Device`
+ ///
+ /// # Safety
+ ///
+ /// `dev` must be an `Aref<device::Device>` whose underlying `bindings::device` is a member of a
+ /// `bindings::platform_device`.
+ unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
+ Self(dev)
+ }
+
+ fn as_dev(&self) -> &device::Device {
+ &self.0
+ }
+
+ fn as_raw(&self) -> *mut bindings::platform_device {
+ // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
+ // embedded in `struct platform_device`.
+ unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut()
+ }
+}
+
+impl AsRef<device::Device> for Device {
+ fn as_ref(&self) -> &device::Device {
+ &self.0
+ }
+}
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* [PATCH v3 16/16] samples: rust: add Rust platform sample driver
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (14 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
@ 2024-10-22 21:31 ` Danilo Krummrich
2024-10-23 0:04 ` Rob Herring
2024-10-23 5:13 ` [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Greg KH
` (2 subsequent siblings)
18 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-22 21:31 UTC (permalink / raw)
To: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
Danilo Krummrich
Add a sample Rust platform driver illustrating the usage of the platform
bus abstractions.
This driver probes through either a match of device / driver name or a
match within the OF ID table.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
MAINTAINERS | 1 +
samples/rust/Kconfig | 10 +++++
samples/rust/Makefile | 1 +
samples/rust/rust_driver_platform.rs | 62 ++++++++++++++++++++++++++++
4 files changed, 74 insertions(+)
create mode 100644 samples/rust/rust_driver_platform.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 173540375863..583b6588fd1e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6986,6 +6986,7 @@ F: rust/kernel/device_id.rs
F: rust/kernel/devres.rs
F: rust/kernel/driver.rs
F: rust/kernel/platform.rs
+F: samples/rust/rust_driver_platform.rs
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
M: Nishanth Menon <nm@ti.com>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 6d468193cdd8..70126b750426 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -41,6 +41,16 @@ config SAMPLE_RUST_DRIVER_PCI
If unsure, say N.
+config SAMPLE_RUST_DRIVER_PLATFORM
+ tristate "Platform Driver"
+ help
+ This option builds the Rust Platform driver sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_driver_platform.
+
+ If unsure, say N.
+
config SAMPLE_RUST_HOSTPROGS
bool "Host programs"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index b66767f4a62a..11fcb312ed36 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -3,5 +3,6 @@
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o
subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
new file mode 100644
index 000000000000..55caaaa4f216
--- /dev/null
+++ b/samples/rust/rust_driver_platform.rs
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust Platform driver sample.
+
+use kernel::{c_str, of, platform, prelude::*};
+
+struct SampleDriver {
+ pdev: platform::Device,
+}
+
+struct Info(u32);
+
+kernel::of_device_table!(
+ OF_TABLE,
+ MODULE_OF_TABLE,
+ <SampleDriver as platform::Driver>::IdInfo,
+ [(
+ of::DeviceId::new(c_str!("redhat,rust-sample-platform-driver")),
+ Info(42)
+ )]
+);
+
+impl platform::Driver for SampleDriver {
+ type IdInfo = Info;
+ const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
+
+ fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
+ dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n");
+
+ match (Self::of_match_device(pdev), info) {
+ (Some(id), Some(info)) => {
+ dev_info!(
+ pdev.as_ref(),
+ "Probed by OF compatible match: '{}' with info: '{}'.\n",
+ id.compatible(),
+ info.0
+ );
+ }
+ _ => {
+ dev_info!(pdev.as_ref(), "Probed by name.\n");
+ }
+ };
+
+ let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?;
+
+ Ok(drvdata.into())
+ }
+}
+
+impl Drop for SampleDriver {
+ fn drop(&mut self) {
+ dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
+ }
+}
+
+kernel::module_platform_driver! {
+ type: SampleDriver,
+ name: "rust_driver_platform",
+ author: "Danilo Krummrich",
+ description: "Rust Platform driver",
+ license: "GPL v2",
+}
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* Re: [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction
2024-10-22 21:31 ` [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
@ 2024-10-22 23:03 ` Rob Herring
2024-10-23 6:33 ` Danilo Krummrich
2024-10-27 4:38 ` Fabien Parent
2024-10-29 13:37 ` Alice Ryhl
2 siblings, 1 reply; 88+ messages in thread
From: Rob Herring @ 2024-10-22 23:03 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:31:51PM +0200, Danilo Krummrich wrote:
> `of::DeviceId` is an abstraction around `struct of_device_id`.
>
> This is used by subsequent patches, in particular the platform bus
> abstractions, to create OF device ID tables.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> rust/bindings/bindings_helper.h | 1 +
> rust/kernel/lib.rs | 1 +
> rust/kernel/of.rs | 63 +++++++++++++++++++++++++++++++++
> 4 files changed, 66 insertions(+)
> create mode 100644 rust/kernel/of.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d9c512a3e72b..87eb9a7869eb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17340,6 +17340,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
> F: Documentation/ABI/testing/sysfs-firmware-ofw
> F: drivers/of/
> F: include/linux/of*.h
> +F: rust/kernel/of.rs
> F: scripts/dtc/
> F: tools/testing/selftests/dt/
> K: of_overlay_notifier_
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index cd4edd6496ae..312f03cbdce9 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -15,6 +15,7 @@
> #include <linux/firmware.h>
> #include <linux/jiffies.h>
> #include <linux/mdio.h>
> +#include <linux/of_device.h>
Technically, you don't need this for *this* patch. You need
mod_devicetable.h for of_device_id. Best to not rely on implicit
includes. I've tried removing it and it still built, so I guess there is
another implicit include somewhere...
> #include <linux/pci.h>
> #include <linux/phy.h>
> #include <linux/refcount.h>
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 3ec690eb6d43..5946f59f1688 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -51,6 +51,7 @@
> pub mod list;
> #[cfg(CONFIG_NET)]
> pub mod net;
> +pub mod of;
> pub mod page;
> pub mod prelude;
> pub mod print;
> diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs
> new file mode 100644
> index 000000000000..a37629997974
> --- /dev/null
> +++ b/rust/kernel/of.rs
> @@ -0,0 +1,63 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Open Firmware abstractions.
s/Open Firmware/Devicetree/
Or keep both that prior versions of this code had. Most of DT/OF today
is not OpenFirmware.
> +//!
> +//! C header: [`include/linux/of_*.h`](srctree/include/linux/of_*.h)
I haven't quite figured out how this gets used. I guess just a link in
documentation? I somewhat doubt this file is going to handle all DT
abstractions. That might become quite long. Most of of_address.h and
of_irq.h I actively don't want to see Rust bindings for because they
are mainly used by higher level interfaces (e.g. platform dev
resources). There's a slew of "don't add new users" APIs which I need to
document. Also, the main header is of.h which wasn't included here.
As of now, only the mod_devicetable.h header is used by this file, so I
think you should just put it until that changes. Maybe there would be
some savings if all of mod_devicetable.h was handled by 1 rust file?
> +
> +use crate::{bindings, device_id::RawDeviceId, prelude::*};
> +
> +/// An open firmware device id.
> +#[derive(Clone, Copy)]
> +pub struct DeviceId(bindings::of_device_id);
> +
> +// SAFETY:
> +// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add
> +// additional invariants, so it's safe to transmute to `RawType`.
> +// * `DRIVER_DATA_OFFSET` is the offset to the `data` field.
> +unsafe impl RawDeviceId for DeviceId {
> + type RawType = bindings::of_device_id;
> +
> + const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data);
> +
> + fn index(&self) -> usize {
> + self.0.data as _
> + }
> +}
> +
> +impl DeviceId {
> + /// Create a new device id from an OF 'compatible' string.
> + pub const fn new(compatible: &'static CStr) -> Self {
> + let src = compatible.as_bytes_with_nul();
> + // Replace with `bindings::of_device_id::default()` once stabilized for `const`.
> + // SAFETY: FFI type is valid to be zero-initialized.
> + let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() };
> +
> + let mut i = 0;
> + while i < src.len() {
> + of.compatible[i] = src[i] as _;
> + i += 1;
> + }
AFAICT, this loop will go away when C char maps to u8. Perhaps a note
to that extent or commented code implementing that.
> +
> + Self(of)
> + }
> +
> + /// The compatible string of the embedded `struct bindings::of_device_id` as `&CStr`.
> + pub fn compatible<'a>(&self) -> &'a CStr {
> + // SAFETY: `self.compatible` is a valid `char` pointer.
> + unsafe { CStr::from_char_ptr(self.0.compatible.as_ptr()) }
> + }
I don't think we need this. The usage model is checking does a node's
compatible string(s) match a compatible in the table. Most of the time
we don't even need that. We just need the match data.
Rob
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
@ 2024-10-22 23:47 ` Rob Herring
2024-10-23 6:44 ` Danilo Krummrich
2024-10-24 9:11 ` Dirk Behme
` (5 subsequent siblings)
6 siblings, 1 reply; 88+ messages in thread
From: Rob Herring @ 2024-10-22 23:47 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:31:52PM +0200, Danilo Krummrich wrote:
> Implement the basic platform bus abstractions required to write a basic
> platform driver. This includes the following data structures:
>
> The `platform::Driver` trait represents the interface to the driver and
> provides `pci::Driver::probe` for the driver to implement.
>
> The `platform::Device` abstraction represents a `struct platform_device`.
>
> In order to provide the platform bus specific parts to a generic
> `driver::Registration` the `driver::RegistrationOps` trait is implemented
> by `platform::Adapter`.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> rust/bindings/bindings_helper.h | 1 +
> rust/helpers/helpers.c | 1 +
> rust/helpers/platform.c | 13 ++
> rust/kernel/lib.rs | 1 +
> rust/kernel/platform.rs | 217 ++++++++++++++++++++++++++++++++
> 6 files changed, 234 insertions(+)
> create mode 100644 rust/helpers/platform.c
> create mode 100644 rust/kernel/platform.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 87eb9a7869eb..173540375863 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6985,6 +6985,7 @@ F: rust/kernel/device.rs
> F: rust/kernel/device_id.rs
> F: rust/kernel/devres.rs
> F: rust/kernel/driver.rs
> +F: rust/kernel/platform.rs
>
> DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
> M: Nishanth Menon <nm@ti.com>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 312f03cbdce9..217c776615b9 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -18,6 +18,7 @@
> #include <linux/of_device.h>
> #include <linux/pci.h>
> #include <linux/phy.h>
> +#include <linux/platform_device.h>
> #include <linux/refcount.h>
> #include <linux/sched.h>
> #include <linux/slab.h>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 8bc6e9735589..663cdc2a45e0 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -17,6 +17,7 @@
> #include "kunit.c"
> #include "mutex.c"
> #include "page.c"
> +#include "platform.c"
> #include "pci.c"
> #include "rbtree.c"
> #include "rcu.c"
> diff --git a/rust/helpers/platform.c b/rust/helpers/platform.c
> new file mode 100644
> index 000000000000..ab9b9f317301
> --- /dev/null
> +++ b/rust/helpers/platform.c
> @@ -0,0 +1,13 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/platform_device.h>
> +
> +void *rust_helper_platform_get_drvdata(const struct platform_device *pdev)
> +{
> + return platform_get_drvdata(pdev);
> +}
> +
> +void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
> +{
> + platform_set_drvdata(pdev, data);
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 5946f59f1688..9e8dcd6d7c01 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -53,6 +53,7 @@
> pub mod net;
> pub mod of;
> pub mod page;
> +pub mod platform;
> pub mod prelude;
> pub mod print;
> pub mod rbtree;
> diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
> new file mode 100644
> index 000000000000..addf5356f44f
> --- /dev/null
> +++ b/rust/kernel/platform.rs
> @@ -0,0 +1,217 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Abstractions for the platform bus.
> +//!
> +//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
> +
> +use crate::{
> + bindings, container_of, device,
> + device_id::RawDeviceId,
> + driver,
> + error::{to_result, Result},
> + of,
> + prelude::*,
> + str::CStr,
> + types::{ARef, ForeignOwnable},
> + ThisModule,
> +};
> +
> +/// An adapter for the registration of platform drivers.
> +pub struct Adapter<T: Driver>(T);
> +
> +impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
> + type RegType = bindings::platform_driver;
> +
> + fn register(
> + pdrv: &mut Self::RegType,
> + name: &'static CStr,
> + module: &'static ThisModule,
> + ) -> Result {
> + pdrv.driver.name = name.as_char_ptr();
> + pdrv.probe = Some(Self::probe_callback);
> +
> + // Both members of this union are identical in data layout and semantics.
> + pdrv.__bindgen_anon_1.remove = Some(Self::remove_callback);
> + pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();
> +
> + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> + to_result(unsafe { bindings::__platform_driver_register(pdrv, module.0) })
> + }
> +
> + fn unregister(pdrv: &mut Self::RegType) {
> + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> + unsafe { bindings::platform_driver_unregister(pdrv) };
> + }
> +}
> +
> +impl<T: Driver + 'static> Adapter<T> {
> + fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> + let table = T::ID_TABLE;
> + let id = T::of_match_device(pdev)?;
> +
> + Some(table.info(id.index()))
> + }
> +
> + extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
> + // SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`.
> + let dev = unsafe { device::Device::from_raw(&mut (*pdev).dev) };
> + // SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the
> + // call above.
> + let mut pdev = unsafe { Device::from_dev(dev) };
> +
> + let info = Self::id_info(&pdev);
> + match T::probe(&mut pdev, info) {
> + Ok(data) => {
> + // Let the `struct platform_device` own a reference of the driver's private data.
> + // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
> + // `struct platform_device`.
> + unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
> + }
> + Err(err) => return Error::to_errno(err),
> + }
> +
> + 0
> + }
> +
> + extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
> + // SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
> + let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
> +
> + // SAFETY: `remove_callback` is only ever called after a successful call to
> + // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
> + // `KBox<T>` pointer created through `KBox::into_foreign`.
> + let _ = unsafe { KBox::<T>::from_foreign(ptr) };
> + }
> +}
> +
> +/// Declares a kernel module that exposes a single platform driver.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// kernel::module_platform_driver! {
> +/// type: MyDriver,
> +/// name: "Module name",
> +/// author: "Author name",
> +/// description: "Description",
> +/// license: "GPL v2",
> +/// }
> +/// ```
> +#[macro_export]
> +macro_rules! module_platform_driver {
> + ($($f:tt)*) => {
> + $crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
> + };
> +}
> +
> +/// IdTable type for platform drivers.
> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> +
> +/// The platform driver trait.
> +///
> +/// # Example
> +///
> +///```
> +/// # use kernel::{bindings, c_str, of, platform};
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::of_device_table!(
> +/// OF_TABLE,
> +/// MODULE_OF_TABLE,
> +/// <MyDriver as platform::Driver>::IdInfo,
> +/// [
> +/// (of::DeviceId::new(c_str!("redhat,my-device")), ())
All compatible strings have to be documented as do vendor prefixes and
I don't think "redhat" is one. An exception is you can use
"test,<whatever>" and not document it.
There's a check for undocumented compatibles. I guess I'll have to add
rust parsing to it...
BTW, how do you compile this code in the kernel?
> +/// ]
> +/// );
> +///
> +/// impl platform::Driver for MyDriver {
> +/// type IdInfo = ();
> +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> +///
> +/// fn probe(
> +/// _pdev: &mut platform::Device,
> +/// _id_info: Option<&Self::IdInfo>,
> +/// ) -> Result<Pin<KBox<Self>>> {
> +/// Err(ENODEV)
> +/// }
> +/// }
> +///```
> +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> +/// the `Adapter` documentation for an example.
> +pub trait Driver {
> + /// The type holding information about each device id supported by the driver.
> + ///
> + /// TODO: Use associated_type_defaults once stabilized:
> + ///
> + /// type IdInfo: 'static = ();
> + type IdInfo: 'static;
> +
> + /// The table of device ids supported by the driver.
> + const ID_TABLE: IdTable<Self::IdInfo>;
> +
> + /// Platform driver probe.
> + ///
> + /// Called when a new platform device is added or discovered.
> + /// Implementers should attempt to initialize the device here.
> + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> +
> + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
Is this visible to drivers? It shouldn't be. I just removed most of the
calls of the C version earlier this year. Drivers should only need the
match data. The preferred driver C API is device_get_match_data(). That
is firmware agnostic and works for DT, ACPI and old platform
driver_data. Obviously, ACPI is not supported here, but it will be soon
enough. We could perhaps get away with not supporting the platform
driver_data because that's generally not used on anything in the last 10
years.
Another potential issue is keeping the match logic for probe and the
match logic for the data in sync. If you implement your own logic here
in rust and probe is using the C version, they might not be the same.
Best case, we have 2 implementations of the same thing.
> + let table = Self::ID_TABLE;
> +
> + // SAFETY:
> + // - `table` has static lifetime, hence it's valid for read,
> + // - `dev` is guaranteed to be valid while it's alive, and so is
> + // `pdev.as_dev().as_raw()`.
> + let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_dev().as_raw()) };
of_match_device depends on CONFIG_OF. There's an empty static inline,
but seems bindgen can't handle those. Prior versions added a helper
function, but that's going to scale terribly. Can we use an annotation
for CONFIG_OF here (assuming we can't get rid of using this directly)?
> +
> + if raw_id.is_null() {
> + None
> + } else {
> + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and
> + // does not add additional invariants, so it's safe to transmute.
> + Some(unsafe { &*raw_id.cast::<of::DeviceId>() })
> + }
> + }
> +}
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 16/16] samples: rust: add Rust platform sample driver
2024-10-22 21:31 ` [PATCH v3 16/16] samples: rust: add Rust platform sample driver Danilo Krummrich
@ 2024-10-23 0:04 ` Rob Herring
2024-10-23 6:59 ` Danilo Krummrich
2024-10-25 10:32 ` Dirk Behme
0 siblings, 2 replies; 88+ messages in thread
From: Rob Herring @ 2024-10-23 0:04 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:31:53PM +0200, Danilo Krummrich wrote:
> Add a sample Rust platform driver illustrating the usage of the platform
> bus abstractions.
>
> This driver probes through either a match of device / driver name or a
> match within the OF ID table.
I know if rust compiles it works, but how does one actually use/test
this? (I know ways, but I might be in the minority. :) )
The DT unittests already define test platform devices. I'd be happy to
add a device node there. Then you don't have to muck with the DT on some
device and it even works on x86 or UML.
And I've started working on DT (fwnode really) property API bindings as
well, and this will be great to test them with.
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> samples/rust/Kconfig | 10 +++++
> samples/rust/Makefile | 1 +
> samples/rust/rust_driver_platform.rs | 62 ++++++++++++++++++++++++++++
> 4 files changed, 74 insertions(+)
> create mode 100644 samples/rust/rust_driver_platform.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 173540375863..583b6588fd1e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6986,6 +6986,7 @@ F: rust/kernel/device_id.rs
> F: rust/kernel/devres.rs
> F: rust/kernel/driver.rs
> F: rust/kernel/platform.rs
> +F: samples/rust/rust_driver_platform.rs
>
> DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
> M: Nishanth Menon <nm@ti.com>
> diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
> index 6d468193cdd8..70126b750426 100644
> --- a/samples/rust/Kconfig
> +++ b/samples/rust/Kconfig
> @@ -41,6 +41,16 @@ config SAMPLE_RUST_DRIVER_PCI
>
> If unsure, say N.
>
> +config SAMPLE_RUST_DRIVER_PLATFORM
> + tristate "Platform Driver"
> + help
> + This option builds the Rust Platform driver sample.
> +
> + To compile this as a module, choose M here:
> + the module will be called rust_driver_platform.
> +
> + If unsure, say N.
> +
> config SAMPLE_RUST_HOSTPROGS
> bool "Host programs"
> help
> diff --git a/samples/rust/Makefile b/samples/rust/Makefile
> index b66767f4a62a..11fcb312ed36 100644
> --- a/samples/rust/Makefile
> +++ b/samples/rust/Makefile
> @@ -3,5 +3,6 @@
> obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
> obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
> obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
> +obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o
>
> subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
> diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
> new file mode 100644
> index 000000000000..55caaaa4f216
> --- /dev/null
> +++ b/samples/rust/rust_driver_platform.rs
> @@ -0,0 +1,62 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Rust Platform driver sample.
> +
> +use kernel::{c_str, of, platform, prelude::*};
> +
> +struct SampleDriver {
> + pdev: platform::Device,
> +}
> +
> +struct Info(u32);
> +
> +kernel::of_device_table!(
> + OF_TABLE,
> + MODULE_OF_TABLE,
> + <SampleDriver as platform::Driver>::IdInfo,
> + [(
> + of::DeviceId::new(c_str!("redhat,rust-sample-platform-driver")),
Perhaps use the same compatible as the commented example. Same comments
on that apply to this.
> + Info(42)
Most of the time this is a pointer to a struct. It would be better to
show how to do that.
> + )]
> +);
> +
> +impl platform::Driver for SampleDriver {
> + type IdInfo = Info;
> + const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
Probably want to name this OF_ID_TABLE for when ACPI_ID_TABLE is added.
> +
> + fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
> + dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n");
> +
> + match (Self::of_match_device(pdev), info) {
That answers my question on being exposed to drivers. This is a big no
for me.
> + (Some(id), Some(info)) => {
> + dev_info!(
> + pdev.as_ref(),
> + "Probed by OF compatible match: '{}' with info: '{}'.\n",
> + id.compatible(),
As I mentioned, "real" drivers don't need the compatible string.
> + info.0
> + );
> + }
> + _ => {
> + dev_info!(pdev.as_ref(), "Probed by name.\n");
> + }
> + };
> +
> + let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?;
> +
> + Ok(drvdata.into())
> + }
> +}
> +
> +impl Drop for SampleDriver {
> + fn drop(&mut self) {
> + dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
> + }
> +}
> +
> +kernel::module_platform_driver! {
> + type: SampleDriver,
> + name: "rust_driver_platform",
> + author: "Danilo Krummrich",
> + description: "Rust Platform driver",
> + license: "GPL v2",
> +}
> --
> 2.46.2
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (15 preceding siblings ...)
2024-10-22 21:31 ` [PATCH v3 16/16] samples: rust: add Rust platform sample driver Danilo Krummrich
@ 2024-10-23 5:13 ` Greg KH
2024-10-23 7:28 ` Danilo Krummrich
2024-10-25 5:15 ` Dirk Behme
2024-11-16 14:32 ` Janne Grunau
18 siblings, 1 reply; 88+ messages in thread
From: Greg KH @ 2024-10-23 5:13 UTC (permalink / raw)
To: Danilo Krummrich
Cc: rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:31:37PM +0200, Danilo Krummrich wrote:
> This patch series implements the necessary Rust abstractions to implement
> device drivers in Rust.
>
> This includes some basic generalizations for driver registration, handling of ID
> tables, MMIO operations and device resource handling.
>
> Those generalizations are used to implement device driver support for two
> busses, the PCI and platfrom bus (with OF IDs) in order to provide some evidence
> that the generalizations work as intended.
>
> The patch series also includes two patches adding two driver samples, one PCI
> driver and one platform driver.
>
> The PCI bits are motivated by the Nova driver project [1], but are used by at
> least one more OOT driver (rnvme [2]).
>
> The platform bits, besides adding some more evidence to the base abstractions,
> are required by a few more OOT drivers aiming at going upstream, i.e. rvkms [3],
> cpufreq-dt [4], asahi [5] and the i2c work from Fabien [6].
>
> The patches of this series can also be [7], [8] and [9].
Nice!
Thanks for redoing this, at first glance it's much better. It will be a
few days before I can dive into this, It's conference season and the
travel is rough, so be patient but I will get to this...
thanks,
greg k-h
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction
2024-10-22 23:03 ` Rob Herring
@ 2024-10-23 6:33 ` Danilo Krummrich
0 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-23 6:33 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Oct 22, 2024 at 06:03:51PM -0500, Rob Herring wrote:
> On Tue, Oct 22, 2024 at 11:31:51PM +0200, Danilo Krummrich wrote:
> > `of::DeviceId` is an abstraction around `struct of_device_id`.
> >
> > This is used by subsequent patches, in particular the platform bus
> > abstractions, to create OF device ID tables.
> >
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > MAINTAINERS | 1 +
> > rust/bindings/bindings_helper.h | 1 +
> > rust/kernel/lib.rs | 1 +
> > rust/kernel/of.rs | 63 +++++++++++++++++++++++++++++++++
> > 4 files changed, 66 insertions(+)
> > create mode 100644 rust/kernel/of.rs
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index d9c512a3e72b..87eb9a7869eb 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -17340,6 +17340,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
> > F: Documentation/ABI/testing/sysfs-firmware-ofw
> > F: drivers/of/
> > F: include/linux/of*.h
> > +F: rust/kernel/of.rs
> > F: scripts/dtc/
> > F: tools/testing/selftests/dt/
> > K: of_overlay_notifier_
> > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> > index cd4edd6496ae..312f03cbdce9 100644
> > --- a/rust/bindings/bindings_helper.h
> > +++ b/rust/bindings/bindings_helper.h
> > @@ -15,6 +15,7 @@
> > #include <linux/firmware.h>
> > #include <linux/jiffies.h>
> > #include <linux/mdio.h>
> > +#include <linux/of_device.h>
>
> Technically, you don't need this for *this* patch. You need
> mod_devicetable.h for of_device_id. Best to not rely on implicit
> includes. I've tried removing it and it still built, so I guess there is
> another implicit include somewhere...
True, however mod_devicetable.h is already needed for a previous patch "rust:
pci: add basic PCI device / driver abstractions" already. So, I'll add it there
and remove the of_device.h include here.
>
> > #include <linux/pci.h>
> > #include <linux/phy.h>
> > #include <linux/refcount.h>
> > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> > index 3ec690eb6d43..5946f59f1688 100644
> > --- a/rust/kernel/lib.rs
> > +++ b/rust/kernel/lib.rs
> > @@ -51,6 +51,7 @@
> > pub mod list;
> > #[cfg(CONFIG_NET)]
> > pub mod net;
> > +pub mod of;
> > pub mod page;
> > pub mod prelude;
> > pub mod print;
> > diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs
> > new file mode 100644
> > index 000000000000..a37629997974
> > --- /dev/null
> > +++ b/rust/kernel/of.rs
> > @@ -0,0 +1,63 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Open Firmware abstractions.
>
> s/Open Firmware/Devicetree/
>
> Or keep both that prior versions of this code had. Most of DT/OF today
> is not OpenFirmware.
>
> > +//!
> > +//! C header: [`include/linux/of_*.h`](srctree/include/linux/of_*.h)
>
> I haven't quite figured out how this gets used. I guess just a link in
> documentation? I somewhat doubt this file is going to handle all DT
> abstractions. That might become quite long. Most of of_address.h and
> of_irq.h I actively don't want to see Rust bindings for because they
> are mainly used by higher level interfaces (e.g. platform dev
> resources). There's a slew of "don't add new users" APIs which I need to
> document. Also, the main header is of.h which wasn't included here.
I think for now it's indeed meaningless and we should just remove it.
If subsequent patches start adding abstractions for things like properties,
device nodes, etc. they can add it back in.
>
> As of now, only the mod_devicetable.h header is used by this file, so I
> think you should just put it until that changes. Maybe there would be
> some savings if all of mod_devicetable.h was handled by 1 rust file?
AFAIK, in C we have all those device ID structs in mod_devicetable.h, such that
in can easily be included in scripts/mod/file2alias.c. But I think implementing
all Rust abstractions for those in a single file would be a bit odd. I'd rather
put them together with the corresponding bus abstractions. OF and ACPI may be a
bit of an exception.
>
> > +
> > +use crate::{bindings, device_id::RawDeviceId, prelude::*};
> > +
> > +/// An open firmware device id.
> > +#[derive(Clone, Copy)]
> > +pub struct DeviceId(bindings::of_device_id);
> > +
> > +// SAFETY:
> > +// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add
> > +// additional invariants, so it's safe to transmute to `RawType`.
> > +// * `DRIVER_DATA_OFFSET` is the offset to the `data` field.
> > +unsafe impl RawDeviceId for DeviceId {
> > + type RawType = bindings::of_device_id;
> > +
> > + const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data);
> > +
> > + fn index(&self) -> usize {
> > + self.0.data as _
> > + }
> > +}
> > +
> > +impl DeviceId {
> > + /// Create a new device id from an OF 'compatible' string.
> > + pub const fn new(compatible: &'static CStr) -> Self {
> > + let src = compatible.as_bytes_with_nul();
> > + // Replace with `bindings::of_device_id::default()` once stabilized for `const`.
> > + // SAFETY: FFI type is valid to be zero-initialized.
> > + let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() };
> > +
> > + let mut i = 0;
> > + while i < src.len() {
> > + of.compatible[i] = src[i] as _;
> > + i += 1;
> > + }
>
> AFAICT, this loop will go away when C char maps to u8. Perhaps a note
> to that extent or commented code implementing that.
That's true, I'll add a note.
>
> > +
> > + Self(of)
> > + }
> > +
> > + /// The compatible string of the embedded `struct bindings::of_device_id` as `&CStr`.
> > + pub fn compatible<'a>(&self) -> &'a CStr {
> > + // SAFETY: `self.compatible` is a valid `char` pointer.
> > + unsafe { CStr::from_char_ptr(self.0.compatible.as_ptr()) }
> > + }
>
> I don't think we need this. The usage model is checking does a node's
> compatible string(s) match a compatible in the table. Most of the time
> we don't even need that. We just need the match data.
Right, I think I just added it for the sample driver, we can get rid of it.
>
> Rob
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 23:47 ` Rob Herring
@ 2024-10-23 6:44 ` Danilo Krummrich
2024-10-23 14:23 ` Rob Herring
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-23 6:44 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Oct 22, 2024 at 06:47:12PM -0500, Rob Herring wrote:
> On Tue, Oct 22, 2024 at 11:31:52PM +0200, Danilo Krummrich wrote:
> > Implement the basic platform bus abstractions required to write a basic
> > platform driver. This includes the following data structures:
> >
> > The `platform::Driver` trait represents the interface to the driver and
> > provides `pci::Driver::probe` for the driver to implement.
> >
> > The `platform::Device` abstraction represents a `struct platform_device`.
> >
> > In order to provide the platform bus specific parts to a generic
> > `driver::Registration` the `driver::RegistrationOps` trait is implemented
> > by `platform::Adapter`.
> >
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > MAINTAINERS | 1 +
> > rust/bindings/bindings_helper.h | 1 +
> > rust/helpers/helpers.c | 1 +
> > rust/helpers/platform.c | 13 ++
> > rust/kernel/lib.rs | 1 +
> > rust/kernel/platform.rs | 217 ++++++++++++++++++++++++++++++++
> > 6 files changed, 234 insertions(+)
> > create mode 100644 rust/helpers/platform.c
> > create mode 100644 rust/kernel/platform.rs
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 87eb9a7869eb..173540375863 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6985,6 +6985,7 @@ F: rust/kernel/device.rs
> > F: rust/kernel/device_id.rs
> > F: rust/kernel/devres.rs
> > F: rust/kernel/driver.rs
> > +F: rust/kernel/platform.rs
> >
> > DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
> > M: Nishanth Menon <nm@ti.com>
> > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> > index 312f03cbdce9..217c776615b9 100644
> > --- a/rust/bindings/bindings_helper.h
> > +++ b/rust/bindings/bindings_helper.h
> > @@ -18,6 +18,7 @@
> > #include <linux/of_device.h>
> > #include <linux/pci.h>
> > #include <linux/phy.h>
> > +#include <linux/platform_device.h>
> > #include <linux/refcount.h>
> > #include <linux/sched.h>
> > #include <linux/slab.h>
> > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > index 8bc6e9735589..663cdc2a45e0 100644
> > --- a/rust/helpers/helpers.c
> > +++ b/rust/helpers/helpers.c
> > @@ -17,6 +17,7 @@
> > #include "kunit.c"
> > #include "mutex.c"
> > #include "page.c"
> > +#include "platform.c"
> > #include "pci.c"
> > #include "rbtree.c"
> > #include "rcu.c"
> > diff --git a/rust/helpers/platform.c b/rust/helpers/platform.c
> > new file mode 100644
> > index 000000000000..ab9b9f317301
> > --- /dev/null
> > +++ b/rust/helpers/platform.c
> > @@ -0,0 +1,13 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +#include <linux/platform_device.h>
> > +
> > +void *rust_helper_platform_get_drvdata(const struct platform_device *pdev)
> > +{
> > + return platform_get_drvdata(pdev);
> > +}
> > +
> > +void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
> > +{
> > + platform_set_drvdata(pdev, data);
> > +}
> > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> > index 5946f59f1688..9e8dcd6d7c01 100644
> > --- a/rust/kernel/lib.rs
> > +++ b/rust/kernel/lib.rs
> > @@ -53,6 +53,7 @@
> > pub mod net;
> > pub mod of;
> > pub mod page;
> > +pub mod platform;
> > pub mod prelude;
> > pub mod print;
> > pub mod rbtree;
> > diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
> > new file mode 100644
> > index 000000000000..addf5356f44f
> > --- /dev/null
> > +++ b/rust/kernel/platform.rs
> > @@ -0,0 +1,217 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Abstractions for the platform bus.
> > +//!
> > +//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
> > +
> > +use crate::{
> > + bindings, container_of, device,
> > + device_id::RawDeviceId,
> > + driver,
> > + error::{to_result, Result},
> > + of,
> > + prelude::*,
> > + str::CStr,
> > + types::{ARef, ForeignOwnable},
> > + ThisModule,
> > +};
> > +
> > +/// An adapter for the registration of platform drivers.
> > +pub struct Adapter<T: Driver>(T);
> > +
> > +impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
> > + type RegType = bindings::platform_driver;
> > +
> > + fn register(
> > + pdrv: &mut Self::RegType,
> > + name: &'static CStr,
> > + module: &'static ThisModule,
> > + ) -> Result {
> > + pdrv.driver.name = name.as_char_ptr();
> > + pdrv.probe = Some(Self::probe_callback);
> > +
> > + // Both members of this union are identical in data layout and semantics.
> > + pdrv.__bindgen_anon_1.remove = Some(Self::remove_callback);
> > + pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();
> > +
> > + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> > + to_result(unsafe { bindings::__platform_driver_register(pdrv, module.0) })
> > + }
> > +
> > + fn unregister(pdrv: &mut Self::RegType) {
> > + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> > + unsafe { bindings::platform_driver_unregister(pdrv) };
> > + }
> > +}
> > +
> > +impl<T: Driver + 'static> Adapter<T> {
> > + fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > + let table = T::ID_TABLE;
> > + let id = T::of_match_device(pdev)?;
> > +
> > + Some(table.info(id.index()))
> > + }
> > +
> > + extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
> > + // SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`.
> > + let dev = unsafe { device::Device::from_raw(&mut (*pdev).dev) };
> > + // SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the
> > + // call above.
> > + let mut pdev = unsafe { Device::from_dev(dev) };
> > +
> > + let info = Self::id_info(&pdev);
> > + match T::probe(&mut pdev, info) {
> > + Ok(data) => {
> > + // Let the `struct platform_device` own a reference of the driver's private data.
> > + // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
> > + // `struct platform_device`.
> > + unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
> > + }
> > + Err(err) => return Error::to_errno(err),
> > + }
> > +
> > + 0
> > + }
> > +
> > + extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
> > + // SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
> > + let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
> > +
> > + // SAFETY: `remove_callback` is only ever called after a successful call to
> > + // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
> > + // `KBox<T>` pointer created through `KBox::into_foreign`.
> > + let _ = unsafe { KBox::<T>::from_foreign(ptr) };
> > + }
> > +}
> > +
> > +/// Declares a kernel module that exposes a single platform driver.
> > +///
> > +/// # Examples
> > +///
> > +/// ```ignore
> > +/// kernel::module_platform_driver! {
> > +/// type: MyDriver,
> > +/// name: "Module name",
> > +/// author: "Author name",
> > +/// description: "Description",
> > +/// license: "GPL v2",
> > +/// }
> > +/// ```
> > +#[macro_export]
> > +macro_rules! module_platform_driver {
> > + ($($f:tt)*) => {
> > + $crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
> > + };
> > +}
> > +
> > +/// IdTable type for platform drivers.
> > +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> > +
> > +/// The platform driver trait.
> > +///
> > +/// # Example
> > +///
> > +///```
> > +/// # use kernel::{bindings, c_str, of, platform};
> > +///
> > +/// struct MyDriver;
> > +///
> > +/// kernel::of_device_table!(
> > +/// OF_TABLE,
> > +/// MODULE_OF_TABLE,
> > +/// <MyDriver as platform::Driver>::IdInfo,
> > +/// [
> > +/// (of::DeviceId::new(c_str!("redhat,my-device")), ())
>
> All compatible strings have to be documented as do vendor prefixes and
> I don't think "redhat" is one. An exception is you can use
> "test,<whatever>" and not document it.
Yeah, I copied that from the sample driver, where it's probably wrong too.
I guess "vendor,device" would be illegal as well?
>
> There's a check for undocumented compatibles. I guess I'll have to add
> rust parsing to it...
>
> BTW, how do you compile this code in the kernel?
You mean this example? It gets compiled as a KUnit doctest, but it obvously
doesn't execute anything, so it's a compile only test.
>
> > +/// ]
> > +/// );
> > +///
> > +/// impl platform::Driver for MyDriver {
> > +/// type IdInfo = ();
> > +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> > +///
> > +/// fn probe(
> > +/// _pdev: &mut platform::Device,
> > +/// _id_info: Option<&Self::IdInfo>,
> > +/// ) -> Result<Pin<KBox<Self>>> {
> > +/// Err(ENODEV)
> > +/// }
> > +/// }
> > +///```
> > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > +/// the `Adapter` documentation for an example.
> > +pub trait Driver {
> > + /// The type holding information about each device id supported by the driver.
> > + ///
> > + /// TODO: Use associated_type_defaults once stabilized:
> > + ///
> > + /// type IdInfo: 'static = ();
> > + type IdInfo: 'static;
> > +
> > + /// The table of device ids supported by the driver.
> > + const ID_TABLE: IdTable<Self::IdInfo>;
> > +
> > + /// Platform driver probe.
> > + ///
> > + /// Called when a new platform device is added or discovered.
> > + /// Implementers should attempt to initialize the device here.
> > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> > +
> > + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> > + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
>
> Is this visible to drivers? It shouldn't be.
Yeah, I think we should just remove it. Looking at struct of_device_id, it
doesn't contain any useful information for a driver. I think when I added this I
was a bit in "autopilot" mode from the PCI stuff, where struct pci_device_id is
useful to drivers.
> I just removed most of the
> calls of the C version earlier this year. Drivers should only need the
> match data. The preferred driver C API is device_get_match_data(). That
> is firmware agnostic and works for DT, ACPI and old platform
> driver_data. Obviously, ACPI is not supported here, but it will be soon
> enough. We could perhaps get away with not supporting the platform
> driver_data because that's generally not used on anything in the last 10
> years.
Otherwise `of_match_device` is only used in `probe_callback` to get the device
info structure, which we indeed should use device_get_match_data() for.
>
> Another potential issue is keeping the match logic for probe and the
> match logic for the data in sync. If you implement your own logic here
> in rust and probe is using the C version, they might not be the same.
> Best case, we have 2 implementations of the same thing.
>
> > + let table = Self::ID_TABLE;
> > +
> > + // SAFETY:
> > + // - `table` has static lifetime, hence it's valid for read,
> > + // - `dev` is guaranteed to be valid while it's alive, and so is
> > + // `pdev.as_dev().as_raw()`.
> > + let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_dev().as_raw()) };
>
> of_match_device depends on CONFIG_OF. There's an empty static inline,
> but seems bindgen can't handle those. Prior versions added a helper
> function, but that's going to scale terribly. Can we use an annotation
> for CONFIG_OF here (assuming we can't get rid of using this directly)?
>
> > +
> > + if raw_id.is_null() {
> > + None
> > + } else {
> > + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and
> > + // does not add additional invariants, so it's safe to transmute.
> > + Some(unsafe { &*raw_id.cast::<of::DeviceId>() })
> > + }
> > + }
> > +}
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 16/16] samples: rust: add Rust platform sample driver
2024-10-23 0:04 ` Rob Herring
@ 2024-10-23 6:59 ` Danilo Krummrich
2024-10-23 15:37 ` Rob Herring
2024-10-25 10:32 ` Dirk Behme
1 sibling, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-23 6:59 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Oct 22, 2024 at 07:04:08PM -0500, Rob Herring wrote:
> On Tue, Oct 22, 2024 at 11:31:53PM +0200, Danilo Krummrich wrote:
> > Add a sample Rust platform driver illustrating the usage of the platform
> > bus abstractions.
> >
> > This driver probes through either a match of device / driver name or a
> > match within the OF ID table.
>
> I know if rust compiles it works, but how does one actually use/test
> this? (I know ways, but I might be in the minority. :) )
For testing a name match I just used platform_device_register_simple() in a
separate module.
Probing through the OF table is indeed a bit more tricky. Since I was too lazy
to pull out a random ARM device of my cupboard I just used QEMU on x86 and did
what drivers/of/unittest.c does. If you're smart you can also just enable those
unit tests and change the compatible string to "unittest". :)
>
> The DT unittests already define test platform devices. I'd be happy to
> add a device node there. Then you don't have to muck with the DT on some
> device and it even works on x86 or UML.
Sounds good, I'll add one in there for this sample driver -- any preferences?
>
> And I've started working on DT (fwnode really) property API bindings as
> well, and this will be great to test them with.
>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > MAINTAINERS | 1 +
> > samples/rust/Kconfig | 10 +++++
> > samples/rust/Makefile | 1 +
> > samples/rust/rust_driver_platform.rs | 62 ++++++++++++++++++++++++++++
> > 4 files changed, 74 insertions(+)
> > create mode 100644 samples/rust/rust_driver_platform.rs
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 173540375863..583b6588fd1e 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6986,6 +6986,7 @@ F: rust/kernel/device_id.rs
> > F: rust/kernel/devres.rs
> > F: rust/kernel/driver.rs
> > F: rust/kernel/platform.rs
> > +F: samples/rust/rust_driver_platform.rs
> >
> > DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
> > M: Nishanth Menon <nm@ti.com>
> > diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
> > index 6d468193cdd8..70126b750426 100644
> > --- a/samples/rust/Kconfig
> > +++ b/samples/rust/Kconfig
> > @@ -41,6 +41,16 @@ config SAMPLE_RUST_DRIVER_PCI
> >
> > If unsure, say N.
> >
> > +config SAMPLE_RUST_DRIVER_PLATFORM
> > + tristate "Platform Driver"
> > + help
> > + This option builds the Rust Platform driver sample.
> > +
> > + To compile this as a module, choose M here:
> > + the module will be called rust_driver_platform.
> > +
> > + If unsure, say N.
> > +
> > config SAMPLE_RUST_HOSTPROGS
> > bool "Host programs"
> > help
> > diff --git a/samples/rust/Makefile b/samples/rust/Makefile
> > index b66767f4a62a..11fcb312ed36 100644
> > --- a/samples/rust/Makefile
> > +++ b/samples/rust/Makefile
> > @@ -3,5 +3,6 @@
> > obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
> > obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
> > obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
> > +obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o
> >
> > subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
> > diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
> > new file mode 100644
> > index 000000000000..55caaaa4f216
> > --- /dev/null
> > +++ b/samples/rust/rust_driver_platform.rs
> > @@ -0,0 +1,62 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Rust Platform driver sample.
> > +
> > +use kernel::{c_str, of, platform, prelude::*};
> > +
> > +struct SampleDriver {
> > + pdev: platform::Device,
> > +}
> > +
> > +struct Info(u32);
> > +
> > +kernel::of_device_table!(
> > + OF_TABLE,
> > + MODULE_OF_TABLE,
> > + <SampleDriver as platform::Driver>::IdInfo,
> > + [(
> > + of::DeviceId::new(c_str!("redhat,rust-sample-platform-driver")),
>
> Perhaps use the same compatible as the commented example. Same comments
> on that apply to this.
>
> > + Info(42)
>
> Most of the time this is a pointer to a struct. It would be better to
> show how to do that.
No, this should never be a raw pointer. There is no reason for a driver to
perfom this kind of unsafe operation to store any ID info data. This ID info
data is moved into the `IdArray` on compile time. And the bus abstraction takes
care of providing a reference to this structure in `Driver::probe`.
So, technically, this example is fine. But if you have ideas for more meaningful
data to store there, I happy to change it.
>
> > + )]
> > +);
> > +
> > +impl platform::Driver for SampleDriver {
> > + type IdInfo = Info;
> > + const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
>
> Probably want to name this OF_ID_TABLE for when ACPI_ID_TABLE is added.
Yes, makes sense.
>
> > +
> > + fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
> > + dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n");
> > +
> > + match (Self::of_match_device(pdev), info) {
>
> That answers my question on being exposed to drivers. This is a big no
> for me.
Agreed, we don't need it. Please also see my previous reply in the platform bus
abstraction.
>
> > + (Some(id), Some(info)) => {
> > + dev_info!(
> > + pdev.as_ref(),
> > + "Probed by OF compatible match: '{}' with info: '{}'.\n",
> > + id.compatible(),
>
> As I mentioned, "real" drivers don't need the compatible string.
Same here.
>
> > + info.0
> > + );
> > + }
> > + _ => {
> > + dev_info!(pdev.as_ref(), "Probed by name.\n");
> > + }
> > + };
> > +
> > + let drvdata = KBox::new(Self { pdev: pdev.clone() }, GFP_KERNEL)?;
> > +
> > + Ok(drvdata.into())
> > + }
> > +}
> > +
> > +impl Drop for SampleDriver {
> > + fn drop(&mut self) {
> > + dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
> > + }
> > +}
> > +
> > +kernel::module_platform_driver! {
> > + type: SampleDriver,
> > + name: "rust_driver_platform",
> > + author: "Danilo Krummrich",
> > + description: "Rust Platform driver",
> > + license: "GPL v2",
> > +}
> > --
> > 2.46.2
> >
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions
2024-10-23 5:13 ` [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Greg KH
@ 2024-10-23 7:28 ` Danilo Krummrich
0 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-23 7:28 UTC (permalink / raw)
To: Greg KH
Cc: rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Wed, Oct 23, 2024 at 07:13:37AM +0200, Greg KH wrote:
> On Tue, Oct 22, 2024 at 11:31:37PM +0200, Danilo Krummrich wrote:
> > This patch series implements the necessary Rust abstractions to implement
> > device drivers in Rust.
> >
> > This includes some basic generalizations for driver registration, handling of ID
> > tables, MMIO operations and device resource handling.
> >
> > Those generalizations are used to implement device driver support for two
> > busses, the PCI and platfrom bus (with OF IDs) in order to provide some evidence
> > that the generalizations work as intended.
> >
> > The patch series also includes two patches adding two driver samples, one PCI
> > driver and one platform driver.
> >
> > The PCI bits are motivated by the Nova driver project [1], but are used by at
> > least one more OOT driver (rnvme [2]).
> >
> > The platform bits, besides adding some more evidence to the base abstractions,
> > are required by a few more OOT drivers aiming at going upstream, i.e. rvkms [3],
> > cpufreq-dt [4], asahi [5] and the i2c work from Fabien [6].
> >
> > The patches of this series can also be [7], [8] and [9].
>
> Nice!
>
> Thanks for redoing this, at first glance it's much better. It will be a
> few days before I can dive into this, It's conference season and the
> travel is rough, so be patient but I will get to this...
No worries, I'll be also a bit less responsive than usual in the next weeks.
>
> thanks,
>
> greg k-h
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-23 6:44 ` Danilo Krummrich
@ 2024-10-23 14:23 ` Rob Herring
2024-10-28 10:15 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Rob Herring @ 2024-10-23 14:23 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Wed, Oct 23, 2024 at 08:44:42AM +0200, Danilo Krummrich wrote:
> On Tue, Oct 22, 2024 at 06:47:12PM -0500, Rob Herring wrote:
> > On Tue, Oct 22, 2024 at 11:31:52PM +0200, Danilo Krummrich wrote:
> > > Implement the basic platform bus abstractions required to write a basic
> > > platform driver. This includes the following data structures:
> > >
> > > The `platform::Driver` trait represents the interface to the driver and
> > > provides `pci::Driver::probe` for the driver to implement.
> > >
> > > The `platform::Device` abstraction represents a `struct platform_device`.
> > >
> > > In order to provide the platform bus specific parts to a generic
> > > `driver::Registration` the `driver::RegistrationOps` trait is implemented
> > > by `platform::Adapter`.
> > >
> > > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > > ---
> > > MAINTAINERS | 1 +
> > > rust/bindings/bindings_helper.h | 1 +
> > > rust/helpers/helpers.c | 1 +
> > > rust/helpers/platform.c | 13 ++
> > > rust/kernel/lib.rs | 1 +
> > > rust/kernel/platform.rs | 217 ++++++++++++++++++++++++++++++++
> > > 6 files changed, 234 insertions(+)
> > > create mode 100644 rust/helpers/platform.c
> > > create mode 100644 rust/kernel/platform.rs
> > >
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 87eb9a7869eb..173540375863 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -6985,6 +6985,7 @@ F: rust/kernel/device.rs
> > > F: rust/kernel/device_id.rs
> > > F: rust/kernel/devres.rs
> > > F: rust/kernel/driver.rs
> > > +F: rust/kernel/platform.rs
> > >
> > > DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
> > > M: Nishanth Menon <nm@ti.com>
> > > diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> > > index 312f03cbdce9..217c776615b9 100644
> > > --- a/rust/bindings/bindings_helper.h
> > > +++ b/rust/bindings/bindings_helper.h
> > > @@ -18,6 +18,7 @@
> > > #include <linux/of_device.h>
> > > #include <linux/pci.h>
> > > #include <linux/phy.h>
> > > +#include <linux/platform_device.h>
> > > #include <linux/refcount.h>
> > > #include <linux/sched.h>
> > > #include <linux/slab.h>
> > > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > > index 8bc6e9735589..663cdc2a45e0 100644
> > > --- a/rust/helpers/helpers.c
> > > +++ b/rust/helpers/helpers.c
> > > @@ -17,6 +17,7 @@
> > > #include "kunit.c"
> > > #include "mutex.c"
> > > #include "page.c"
> > > +#include "platform.c"
> > > #include "pci.c"
> > > #include "rbtree.c"
> > > #include "rcu.c"
> > > diff --git a/rust/helpers/platform.c b/rust/helpers/platform.c
> > > new file mode 100644
> > > index 000000000000..ab9b9f317301
> > > --- /dev/null
> > > +++ b/rust/helpers/platform.c
> > > @@ -0,0 +1,13 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +
> > > +#include <linux/platform_device.h>
> > > +
> > > +void *rust_helper_platform_get_drvdata(const struct platform_device *pdev)
> > > +{
> > > + return platform_get_drvdata(pdev);
> > > +}
> > > +
> > > +void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
> > > +{
> > > + platform_set_drvdata(pdev, data);
> > > +}
> > > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> > > index 5946f59f1688..9e8dcd6d7c01 100644
> > > --- a/rust/kernel/lib.rs
> > > +++ b/rust/kernel/lib.rs
> > > @@ -53,6 +53,7 @@
> > > pub mod net;
> > > pub mod of;
> > > pub mod page;
> > > +pub mod platform;
> > > pub mod prelude;
> > > pub mod print;
> > > pub mod rbtree;
> > > diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
> > > new file mode 100644
> > > index 000000000000..addf5356f44f
> > > --- /dev/null
> > > +++ b/rust/kernel/platform.rs
> > > @@ -0,0 +1,217 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +
> > > +//! Abstractions for the platform bus.
> > > +//!
> > > +//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
> > > +
> > > +use crate::{
> > > + bindings, container_of, device,
> > > + device_id::RawDeviceId,
> > > + driver,
> > > + error::{to_result, Result},
> > > + of,
> > > + prelude::*,
> > > + str::CStr,
> > > + types::{ARef, ForeignOwnable},
> > > + ThisModule,
> > > +};
> > > +
> > > +/// An adapter for the registration of platform drivers.
> > > +pub struct Adapter<T: Driver>(T);
> > > +
> > > +impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
> > > + type RegType = bindings::platform_driver;
> > > +
> > > + fn register(
> > > + pdrv: &mut Self::RegType,
> > > + name: &'static CStr,
> > > + module: &'static ThisModule,
> > > + ) -> Result {
> > > + pdrv.driver.name = name.as_char_ptr();
> > > + pdrv.probe = Some(Self::probe_callback);
> > > +
> > > + // Both members of this union are identical in data layout and semantics.
> > > + pdrv.__bindgen_anon_1.remove = Some(Self::remove_callback);
> > > + pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();
> > > +
> > > + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> > > + to_result(unsafe { bindings::__platform_driver_register(pdrv, module.0) })
> > > + }
> > > +
> > > + fn unregister(pdrv: &mut Self::RegType) {
> > > + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> > > + unsafe { bindings::platform_driver_unregister(pdrv) };
> > > + }
> > > +}
> > > +
> > > +impl<T: Driver + 'static> Adapter<T> {
> > > + fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > > + let table = T::ID_TABLE;
> > > + let id = T::of_match_device(pdev)?;
> > > +
> > > + Some(table.info(id.index()))
> > > + }
> > > +
> > > + extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
> > > + // SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`.
> > > + let dev = unsafe { device::Device::from_raw(&mut (*pdev).dev) };
> > > + // SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the
> > > + // call above.
> > > + let mut pdev = unsafe { Device::from_dev(dev) };
> > > +
> > > + let info = Self::id_info(&pdev);
> > > + match T::probe(&mut pdev, info) {
> > > + Ok(data) => {
> > > + // Let the `struct platform_device` own a reference of the driver's private data.
> > > + // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
> > > + // `struct platform_device`.
> > > + unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
> > > + }
> > > + Err(err) => return Error::to_errno(err),
> > > + }
> > > +
> > > + 0
> > > + }
> > > +
> > > + extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
> > > + // SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
> > > + let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
> > > +
> > > + // SAFETY: `remove_callback` is only ever called after a successful call to
> > > + // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
> > > + // `KBox<T>` pointer created through `KBox::into_foreign`.
> > > + let _ = unsafe { KBox::<T>::from_foreign(ptr) };
> > > + }
> > > +}
> > > +
> > > +/// Declares a kernel module that exposes a single platform driver.
> > > +///
> > > +/// # Examples
> > > +///
> > > +/// ```ignore
> > > +/// kernel::module_platform_driver! {
> > > +/// type: MyDriver,
> > > +/// name: "Module name",
> > > +/// author: "Author name",
> > > +/// description: "Description",
> > > +/// license: "GPL v2",
> > > +/// }
> > > +/// ```
> > > +#[macro_export]
> > > +macro_rules! module_platform_driver {
> > > + ($($f:tt)*) => {
> > > + $crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
> > > + };
> > > +}
> > > +
> > > +/// IdTable type for platform drivers.
> > > +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> > > +
> > > +/// The platform driver trait.
> > > +///
> > > +/// # Example
> > > +///
> > > +///```
> > > +/// # use kernel::{bindings, c_str, of, platform};
> > > +///
> > > +/// struct MyDriver;
> > > +///
> > > +/// kernel::of_device_table!(
> > > +/// OF_TABLE,
> > > +/// MODULE_OF_TABLE,
> > > +/// <MyDriver as platform::Driver>::IdInfo,
> > > +/// [
> > > +/// (of::DeviceId::new(c_str!("redhat,my-device")), ())
> >
> > All compatible strings have to be documented as do vendor prefixes and
> > I don't think "redhat" is one. An exception is you can use
> > "test,<whatever>" and not document it.
>
> Yeah, I copied that from the sample driver, where it's probably wrong too.
>
> I guess "vendor,device" would be illegal as well?
Yes.
> > There's a check for undocumented compatibles. I guess I'll have to add
> > rust parsing to it...
> >
> > BTW, how do you compile this code in the kernel?
>
> You mean this example? It gets compiled as a KUnit doctest, but it obvously
> doesn't execute anything, so it's a compile only test.
Yes. That's a question for my own education.
> >
> > > +/// ]
> > > +/// );
> > > +///
> > > +/// impl platform::Driver for MyDriver {
> > > +/// type IdInfo = ();
> > > +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> > > +///
> > > +/// fn probe(
> > > +/// _pdev: &mut platform::Device,
> > > +/// _id_info: Option<&Self::IdInfo>,
> > > +/// ) -> Result<Pin<KBox<Self>>> {
> > > +/// Err(ENODEV)
> > > +/// }
> > > +/// }
> > > +///```
> > > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > > +/// the `Adapter` documentation for an example.
> > > +pub trait Driver {
> > > + /// The type holding information about each device id supported by the driver.
> > > + ///
> > > + /// TODO: Use associated_type_defaults once stabilized:
> > > + ///
> > > + /// type IdInfo: 'static = ();
> > > + type IdInfo: 'static;
> > > +
> > > + /// The table of device ids supported by the driver.
> > > + const ID_TABLE: IdTable<Self::IdInfo>;
Another thing. I don't think this is quite right. Well, this part is
fine, but assigning the DT table to it is not. The underlying C code has
2 id tables in struct device_driver (DT and ACPI) and then the bus
specific one in the struct ${bus}_driver.
> > > +
> > > + /// Platform driver probe.
> > > + ///
> > > + /// Called when a new platform device is added or discovered.
> > > + /// Implementers should attempt to initialize the device here.
> > > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> > > +
> > > + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> > > + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
> >
> > Is this visible to drivers? It shouldn't be.
>
> Yeah, I think we should just remove it. Looking at struct of_device_id, it
> doesn't contain any useful information for a driver. I think when I added this I
> was a bit in "autopilot" mode from the PCI stuff, where struct pci_device_id is
> useful to drivers.
TBC, you mean other than *data, right? If so, I agree.
The DT type and name fields are pretty much legacy, so I don't think the
rust bindings need to worry about them until someone converts Sparc and
PowerMac drivers to rust (i.e. never).
I would guess the PCI cases might be questionable, too. Like DT, drivers
may be accessing the table fields, but that's not best practice. All the
match fields are stored in pci_dev, so why get them from the match
table?
Rob
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 16/16] samples: rust: add Rust platform sample driver
2024-10-23 6:59 ` Danilo Krummrich
@ 2024-10-23 15:37 ` Rob Herring
2024-10-28 9:32 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Rob Herring @ 2024-10-23 15:37 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Wed, Oct 23, 2024 at 08:59:48AM +0200, Danilo Krummrich wrote:
> On Tue, Oct 22, 2024 at 07:04:08PM -0500, Rob Herring wrote:
> > On Tue, Oct 22, 2024 at 11:31:53PM +0200, Danilo Krummrich wrote:
> > > Add a sample Rust platform driver illustrating the usage of the platform
> > > bus abstractions.
> > >
> > > This driver probes through either a match of device / driver name or a
> > > match within the OF ID table.
> >
> > I know if rust compiles it works, but how does one actually use/test
> > this? (I know ways, but I might be in the minority. :) )
>
> For testing a name match I just used platform_device_register_simple() in a
> separate module.
>
> Probing through the OF table is indeed a bit more tricky. Since I was too lazy
> to pull out a random ARM device of my cupboard I just used QEMU on x86 and did
> what drivers/of/unittest.c does. If you're smart you can also just enable those
> unit tests and change the compatible string to "unittest". :)
>
> >
> > The DT unittests already define test platform devices. I'd be happy to
> > add a device node there. Then you don't have to muck with the DT on some
> > device and it even works on x86 or UML.
>
> Sounds good, I'll add one in there for this sample driver -- any preferences?
I gave this a spin and added the patch below in. Feel free to squash it
into this one.
8<----------------------------------------------------------------
From: "Rob Herring (Arm)" <robh@kernel.org>
Date: Wed, 23 Oct 2024 10:29:47 -0500
Subject: [PATCH] of: unittest: Add a platform device node for rust platform
driver sample
Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
diff --git a/drivers/of/unittest-data/tests-platform.dtsi b/drivers/of/unittest-data/tests-platform.dtsi
index fa39611071b3..575ea260a877 100644
--- a/drivers/of/unittest-data/tests-platform.dtsi
+++ b/drivers/of/unittest-data/tests-platform.dtsi
@@ -33,6 +33,11 @@ dev@100 {
reg = <0x100>;
};
};
+
+ test-device@2 {
+ compatible = "test,rust-device";
+ reg = <0x2>;
+ };
};
};
};
diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
index 55caaaa4f216..5cf4a8f86c13 100644
--- a/samples/rust/rust_driver_platform.rs
+++ b/samples/rust/rust_driver_platform.rs
@@ -15,7 +15,7 @@ struct SampleDriver {
MODULE_OF_TABLE,
<SampleDriver as platform::Driver>::IdInfo,
[(
- of::DeviceId::new(c_str!("redhat,rust-sample-platform-driver")),
+ of::DeviceId::new(c_str!("test,rust-device")),
Info(42)
)]
);
^ permalink raw reply related [flat|nested] 88+ messages in thread
* Re: [PATCH v3 13/16] samples: rust: add Rust PCI sample driver
2024-10-22 21:31 ` [PATCH v3 13/16] samples: rust: add Rust PCI sample driver Danilo Krummrich
@ 2024-10-23 15:57 ` Rob Herring
2024-10-28 13:22 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Rob Herring @ 2024-10-23 15:57 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:31:50PM +0200, Danilo Krummrich wrote:
> This commit adds a sample Rust PCI driver for QEMU's "pci-testdev"
> device. To enable this device QEMU has to be called with
> `-device pci-testdev`.
Note that the DT unittests also use this device. So this means we have 2
drivers that bind to the device. Probably it's okay, but does make
them somewhat mutually-exclusive.
> The same driver shows how to use the PCI device / driver abstractions,
> as well as how to request and map PCI BARs, including a short sequence of
> MMIO operations.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> samples/rust/Kconfig | 11 ++++
> samples/rust/Makefile | 1 +
> samples/rust/rust_driver_pci.rs | 109 ++++++++++++++++++++++++++++++++
> 4 files changed, 122 insertions(+)
> create mode 100644 samples/rust/rust_driver_pci.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2d00d3845b4a..d9c512a3e72b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17940,6 +17940,7 @@ F: include/linux/of_pci.h
> F: include/linux/pci*
> F: include/uapi/linux/pci*
> F: rust/kernel/pci.rs
> +F: samples/rust/rust_driver_pci.rs
>
> PCIE DRIVER FOR AMAZON ANNAPURNA LABS
> M: Jonathan Chocron <jonnyc@amazon.com>
> diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
> index b0f74a81c8f9..6d468193cdd8 100644
> --- a/samples/rust/Kconfig
> +++ b/samples/rust/Kconfig
> @@ -30,6 +30,17 @@ config SAMPLE_RUST_PRINT
>
> If unsure, say N.
>
> +config SAMPLE_RUST_DRIVER_PCI
> + tristate "PCI Driver"
> + depends on PCI
> + help
> + This option builds the Rust PCI driver sample.
> +
> + To compile this as a module, choose M here:
> + the module will be called driver_pci.
> +
> + If unsure, say N.
> +
> config SAMPLE_RUST_HOSTPROGS
> bool "Host programs"
> help
> diff --git a/samples/rust/Makefile b/samples/rust/Makefile
> index 03086dabbea4..b66767f4a62a 100644
> --- a/samples/rust/Makefile
> +++ b/samples/rust/Makefile
> @@ -2,5 +2,6 @@
>
> obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
> obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
> +obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
>
> subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
> diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
> new file mode 100644
> index 000000000000..d24dc1fde9e8
> --- /dev/null
> +++ b/samples/rust/rust_driver_pci.rs
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Rust PCI driver sample (based on QEMU's `pci-testdev`).
> +//!
> +//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
> +
> +use kernel::{bindings, c_str, devres::Devres, pci, prelude::*};
> +
> +struct Regs;
> +
> +impl Regs {
> + const TEST: usize = 0x0;
> + const OFFSET: usize = 0x4;
> + const DATA: usize = 0x8;
> + const COUNT: usize = 0xC;
> + const END: usize = 0x10;
> +}
> +
> +type Bar0 = pci::Bar<{ Regs::END }>;
> +
> +#[derive(Debug)]
> +struct TestIndex(u8);
> +
> +impl TestIndex {
> + const NO_EVENTFD: Self = Self(0);
> +}
> +
> +struct SampleDriver {
> + pdev: pci::Device,
> + bar: Devres<Bar0>,
> +}
> +
> +kernel::pci_device_table!(
> + PCI_TABLE,
> + MODULE_PCI_TABLE,
> + <SampleDriver as pci::Driver>::IdInfo,
> + [(
> + pci::DeviceId::new(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
> + TestIndex::NO_EVENTFD
> + )]
> +);
> +
> +impl SampleDriver {
> + fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
> + // Select the test.
> + bar.writeb(index.0, Regs::TEST);
> +
> + let offset = u32::from_le(bar.readl(Regs::OFFSET)) as usize;
The C version of readl takes care of from_le for you. Why not here?
Also, can't we do better with rust and make this a generic:
let offset = bar.read::<u32>(Regs::OFFSET)) as usize;
> + let data = bar.readb(Regs::DATA);
> +
> + // Write `data` to `offset` to increase `count` by one.
> + //
> + // Note that we need `try_writeb`, since `offset` can't be checked at compile-time.
> + bar.try_writeb(data, offset)?;
> +
> + Ok(u32::from_le(bar.readl(Regs::COUNT)))
> + }
> +}
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
2024-10-22 23:47 ` Rob Herring
@ 2024-10-24 9:11 ` Dirk Behme
2024-10-28 10:19 ` Danilo Krummrich
2024-10-27 4:32 ` Fabien Parent
` (4 subsequent siblings)
6 siblings, 1 reply; 88+ messages in thread
From: Dirk Behme @ 2024-10-24 9:11 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, robh, daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree
Hi Danilo,
On 22.10.2024 23:31, Danilo Krummrich wrote:
> Implement the basic platform bus abstractions required to write a basic
> platform driver. This includes the following data structures:
>
> The `platform::Driver` trait represents the interface to the driver and
> provides `pci::Driver::probe` for the driver to implement.
>
> The `platform::Device` abstraction represents a `struct platform_device`.
>
> In order to provide the platform bus specific parts to a generic
> `driver::Registration` the `driver::RegistrationOps` trait is implemented
> by `platform::Adapter`.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> rust/bindings/bindings_helper.h | 1 +
> rust/helpers/helpers.c | 1 +
> rust/helpers/platform.c | 13 ++
> rust/kernel/lib.rs | 1 +
> rust/kernel/platform.rs | 217 ++++++++++++++++++++++++++++++++
> 6 files changed, 234 insertions(+)
> create mode 100644 rust/helpers/platform.c
> create mode 100644 rust/kernel/platform.rs
...
> diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
> new file mode 100644
> index 000000000000..addf5356f44f
> --- /dev/null
> +++ b/rust/kernel/platform.rs
....
> +/// Declares a kernel module that exposes a single platform driver.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// kernel::module_platform_driver! {
> +/// type: MyDriver,
> +/// name: "Module name",
> +/// author: "Author name",
> +/// description: "Description",
> +/// license: "GPL v2",
> +/// }
> +/// ```
> +#[macro_export]
> +macro_rules! module_platform_driver {
> + ($($f:tt)*) => {
> + $crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
> + };
> +}
> +
> +/// IdTable type for platform drivers.
> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> +
> +/// The platform driver trait.
> +///
> +/// # Example
> +///
> +///```
> +/// # use kernel::{bindings, c_str, of, platform};
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::of_device_table!(
> +/// OF_TABLE,
> +/// MODULE_OF_TABLE,
It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
used here. Shouldn't they be somehow driver specific, e.g.
OF_TABLE_MYDRIVER and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the
other examples/samples in this patch series. Found that while using the
*same* somewhere else ;)
Best regards
Dirk
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (16 preceding siblings ...)
2024-10-23 5:13 ` [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Greg KH
@ 2024-10-25 5:15 ` Dirk Behme
2024-11-16 14:32 ` Janne Grunau
18 siblings, 0 replies; 88+ messages in thread
From: Dirk Behme @ 2024-10-25 5:15 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, robh, daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree
Hi Danilo,
On 22.10.2024 23:31, Danilo Krummrich wrote:
> This patch series implements the necessary Rust abstractions to implement
> device drivers in Rust.
>
> This includes some basic generalizations for driver registration, handling of ID
> tables, MMIO operations and device resource handling.
>
> Those generalizations are used to implement device driver support for two
> busses, the PCI and platfrom bus (with OF IDs) in order to provide some evidence
> that the generalizations work as intended.
>
> The patch series also includes two patches adding two driver samples, one PCI
> driver and one platform driver.
>
> The PCI bits are motivated by the Nova driver project [1], but are used by at
> least one more OOT driver (rnvme [2]).
>
> The platform bits, besides adding some more evidence to the base abstractions,
> are required by a few more OOT drivers aiming at going upstream, i.e. rvkms [3],
> cpufreq-dt [4], asahi [5] and the i2c work from Fabien [6].
>
> The patches of this series can also be [7], [8] and [9].
Just some minor typos:
-----------------------------------------------------
0004_rust_implement_generic_driver_registration.patch
-----------------------------------------------------
WARNING: 'privide' may be misspelled - perhaps 'provide'?
#63: FILE: rust/kernel/driver.rs:14:
+/// Amba, etc.) to privide the corresponding subsystem specific
implementation to register /
^^^^^^^
---------------------------------------------------------
0005_rust_implement_idarray_idtable_and_rawdeviceid.patch
---------------------------------------------------------
WARNING: 'offest' may be misspelled - perhaps 'offset'?
#84: FILE: rust/kernel/device_id.rs:39:
+ /// The offest to the context/data field.
^^^^^^
-----------------------------------
0009_rust_add_io_io_base_type.patch
-----------------------------------
WARNING: 'embedd' may be misspelled - perhaps 'embed'?
#27:
bound to, subsystems should embedd the corresponding I/O memory type
^^^^^^
Best regards
Dirk
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 16/16] samples: rust: add Rust platform sample driver
2024-10-23 0:04 ` Rob Herring
2024-10-23 6:59 ` Danilo Krummrich
@ 2024-10-25 10:32 ` Dirk Behme
2024-10-25 16:08 ` Rob Herring
1 sibling, 1 reply; 88+ messages in thread
From: Dirk Behme @ 2024-10-25 10:32 UTC (permalink / raw)
To: Rob Herring, Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On 23.10.2024 02:04, Rob Herring wrote:
> On Tue, Oct 22, 2024 at 11:31:53PM +0200, Danilo Krummrich wrote:
>> Add a sample Rust platform driver illustrating the usage of the platform
>> bus abstractions.
>>
>> This driver probes through either a match of device / driver name or a
>> match within the OF ID table.
>
> I know if rust compiles it works, but how does one actually use/test
> this? (I know ways, but I might be in the minority. :) )
>
> The DT unittests already define test platform devices. I'd be happy to
> add a device node there. Then you don't have to muck with the DT on some
> device and it even works on x86 or UML.
Assuming being on x86, having CONFIG_OF and CONFIG_OF_UNITTEST enabled,
seeing the ### dt-test ### running nicely at kernel startup and seeing
the compiled in test device tree under /proc/device-tree:
Would using a compatible from the test device tree (e.g. "test-device")
in the Rust Platform driver sample [1] let the probe() of that driver
sample run?
Or is this a wrong/not sufficient understanding?
I tried that, without success ;)
Best regards
Dirk
--- a/samples/rust/rust_driver_platform.rs
+++ b/samples/rust/rust_driver_platform.rs
@@ -15,7 +15,7 @@ struct SampleDriver {
SAMPLE_MODULE_OF_TABLE,
<SampleDriver as platform::Driver>::IdInfo,
[(
- of::DeviceId::new(c_str!("redhat,rust-sample-platform-driver")),
+ of::DeviceId::new(c_str!("test-device")),
Info(42)
)]
);
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 16/16] samples: rust: add Rust platform sample driver
2024-10-25 10:32 ` Dirk Behme
@ 2024-10-25 16:08 ` Rob Herring
0 siblings, 0 replies; 88+ messages in thread
From: Rob Herring @ 2024-10-25 16:08 UTC (permalink / raw)
To: Dirk Behme
Cc: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Fri, Oct 25, 2024 at 5:33 AM Dirk Behme <dirk.behme@de.bosch.com> wrote:
>
> On 23.10.2024 02:04, Rob Herring wrote:
> > On Tue, Oct 22, 2024 at 11:31:53PM +0200, Danilo Krummrich wrote:
> >> Add a sample Rust platform driver illustrating the usage of the platform
> >> bus abstractions.
> >>
> >> This driver probes through either a match of device / driver name or a
> >> match within the OF ID table.
> >
> > I know if rust compiles it works, but how does one actually use/test
> > this? (I know ways, but I might be in the minority. :) )
> >
> > The DT unittests already define test platform devices. I'd be happy to
> > add a device node there. Then you don't have to muck with the DT on some
> > device and it even works on x86 or UML.
>
> Assuming being on x86, having CONFIG_OF and CONFIG_OF_UNITTEST enabled,
> seeing the ### dt-test ### running nicely at kernel startup and seeing
> the compiled in test device tree under /proc/device-tree:
>
> Would using a compatible from the test device tree (e.g. "test-device")
> in the Rust Platform driver sample [1] let the probe() of that driver
> sample run?
No, because that binds with the platform driver within the unittest.
Maybe it would work if you manually unbind the unittest driver and
bind the rust sample.
> Or is this a wrong/not sufficient understanding?
>
> I tried that, without success ;)
Did you try the patch I sent in this thread? That works and only
depends on kconfig options to work.
Rob
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
2024-10-22 23:47 ` Rob Herring
2024-10-24 9:11 ` Dirk Behme
@ 2024-10-27 4:32 ` Fabien Parent
2024-10-28 13:44 ` Dirk Behme
` (3 subsequent siblings)
6 siblings, 0 replies; 88+ messages in thread
From: Fabien Parent @ 2024-10-27 4:32 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, robh, daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree
Hi Danilo,
On Tue Oct 22, 2024 at 2:31 PM PDT, Danilo Krummrich wrote:
> +/// An adapter for the registration of platform drivers.
> +pub struct Adapter<T: Driver>(T);
...
> +/// The platform driver trait.
> +///
> +/// # Example
> +///
> +///```
> +/// # use kernel::{bindings, c_str, of, platform};
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::of_device_table!(
> +/// OF_TABLE,
> +/// MODULE_OF_TABLE,
> +/// <MyDriver as platform::Driver>::IdInfo,
> +/// [
> +/// (of::DeviceId::new(c_str!("redhat,my-device")), ())
> +/// ]
> +/// );
> +///
> +/// impl platform::Driver for MyDriver {
> +/// type IdInfo = ();
> +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> +///
> +/// fn probe(
> +/// _pdev: &mut platform::Device,
> +/// _id_info: Option<&Self::IdInfo>,
> +/// ) -> Result<Pin<KBox<Self>>> {
> +/// Err(ENODEV)
> +/// }
> +/// }
> +///```
...
> +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> +/// the `Adapter` documentation for an example.
The `Adapter` doesn't have any examples, but there is one right
above this paragraph. Anyway I think the example is in the right place and
should not be moved over there.
I think this paragraph should be moved above the example and the last
sentence should be deleted.
Fabien
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction
2024-10-22 21:31 ` [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
2024-10-22 23:03 ` Rob Herring
@ 2024-10-27 4:38 ` Fabien Parent
2024-10-29 13:37 ` Alice Ryhl
2 siblings, 0 replies; 88+ messages in thread
From: Fabien Parent @ 2024-10-27 4:38 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, robh, daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree
Hi Danilo,
On Tue Oct 22, 2024 at 2:31 PM PDT, Danilo Krummrich wrote:
> +/// An open firmware device id.
> +#[derive(Clone, Copy)]
> +pub struct DeviceId(bindings::of_device_id);
...
> +// SAFETY:
> +// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add
DeviceId is missing the `#[repr(transparent)]`.
BR,
Fabien
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
@ 2024-10-27 22:42 ` Boqun Feng
2024-10-28 10:21 ` Danilo Krummrich
2024-11-26 14:06 ` Danilo Krummrich
2024-10-29 21:16 ` Christian Schrefl
1 sibling, 2 replies; 88+ messages in thread
From: Boqun Feng @ 2024-10-27 22:42 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, gary, bjorn3_gh,
benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
Hi Danilo,
On Tue, Oct 22, 2024 at 11:31:48PM +0200, Danilo Krummrich wrote:
[...]
> +/// The PCI device representation.
> +///
> +/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI
> +/// device, hence, also increments the base device' reference count.
> +#[derive(Clone)]
> +pub struct Device(ARef<device::Device>);
> +
Similar to https://lore.kernel.org/rust-for-linux/ZgG7TlybSa00cuoy@boqun-archlinux/
Could you also avoid wrapping a point to a PCI device? Instead, wrap the
object type:
#[repr(transparent)]
pub struct Device(Opaque<bindings::pci_dev>);
impl AlwaysRefCounted for Device {
<put_device() and get_device() on ->dev>
}
Regards,
Boqun
> +impl Device {
> + /// Create a PCI Device instance from an existing `device::Device`.
> + ///
> + /// # Safety
> + ///
> + /// `dev` must be an `ARef<device::Device>` whose underlying `bindings::device` is a member of
> + /// a `bindings::pci_dev`.
> + pub unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
> + Self(dev)
> + }
> +
> + fn as_raw(&self) -> *mut bindings::pci_dev {
> + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
> + // embedded in `struct pci_dev`.
> + unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ }
> + }
> +
> + /// Enable memory resources for this device.
> + pub fn enable_device_mem(&self) -> Result {
> + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
> + let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) };
> + if ret != 0 {
> + Err(Error::from_errno(ret))
> + } else {
> + Ok(())
> + }
> + }
> +
> + /// Enable bus-mastering for this device.
> + pub fn set_master(&self) {
> + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
> + unsafe { bindings::pci_set_master(self.as_raw()) };
> + }
> +}
> +
> +impl AsRef<device::Device> for Device {
> + fn as_ref(&self) -> &device::Device {
> + &self.0
> + }
> +}
> --
> 2.46.2
>
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 16/16] samples: rust: add Rust platform sample driver
2024-10-23 15:37 ` Rob Herring
@ 2024-10-28 9:32 ` Danilo Krummrich
0 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-28 9:32 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Wed, Oct 23, 2024 at 10:37:03AM -0500, Rob Herring wrote:
> On Wed, Oct 23, 2024 at 08:59:48AM +0200, Danilo Krummrich wrote:
> > On Tue, Oct 22, 2024 at 07:04:08PM -0500, Rob Herring wrote:
> > > On Tue, Oct 22, 2024 at 11:31:53PM +0200, Danilo Krummrich wrote:
> > > > Add a sample Rust platform driver illustrating the usage of the platform
> > > > bus abstractions.
> > > >
> > > > This driver probes through either a match of device / driver name or a
> > > > match within the OF ID table.
> > >
> > > I know if rust compiles it works, but how does one actually use/test
> > > this? (I know ways, but I might be in the minority. :) )
> >
> > For testing a name match I just used platform_device_register_simple() in a
> > separate module.
> >
> > Probing through the OF table is indeed a bit more tricky. Since I was too lazy
> > to pull out a random ARM device of my cupboard I just used QEMU on x86 and did
> > what drivers/of/unittest.c does. If you're smart you can also just enable those
> > unit tests and change the compatible string to "unittest". :)
> >
> > >
> > > The DT unittests already define test platform devices. I'd be happy to
> > > add a device node there. Then you don't have to muck with the DT on some
> > > device and it even works on x86 or UML.
> >
> > Sounds good, I'll add one in there for this sample driver -- any preferences?
>
> I gave this a spin and added the patch below in. Feel free to squash it
> into this one.
Looks good -- it only works if the sample driver is built-in though, but I think
that should be fine.
>
> 8<----------------------------------------------------------------
> From: "Rob Herring (Arm)" <robh@kernel.org>
> Date: Wed, 23 Oct 2024 10:29:47 -0500
> Subject: [PATCH] of: unittest: Add a platform device node for rust platform
> driver sample
>
> Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
>
> diff --git a/drivers/of/unittest-data/tests-platform.dtsi b/drivers/of/unittest-data/tests-platform.dtsi
> index fa39611071b3..575ea260a877 100644
> --- a/drivers/of/unittest-data/tests-platform.dtsi
> +++ b/drivers/of/unittest-data/tests-platform.dtsi
> @@ -33,6 +33,11 @@ dev@100 {
> reg = <0x100>;
> };
> };
> +
> + test-device@2 {
> + compatible = "test,rust-device";
> + reg = <0x2>;
> + };
> };
> };
> };
> diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
> index 55caaaa4f216..5cf4a8f86c13 100644
> --- a/samples/rust/rust_driver_platform.rs
> +++ b/samples/rust/rust_driver_platform.rs
> @@ -15,7 +15,7 @@ struct SampleDriver {
> MODULE_OF_TABLE,
> <SampleDriver as platform::Driver>::IdInfo,
> [(
> - of::DeviceId::new(c_str!("redhat,rust-sample-platform-driver")),
> + of::DeviceId::new(c_str!("test,rust-device")),
> Info(42)
> )]
> );
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-23 14:23 ` Rob Herring
@ 2024-10-28 10:15 ` Danilo Krummrich
2024-10-30 12:23 ` Rob Herring
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-28 10:15 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Wed, Oct 23, 2024 at 09:23:55AM -0500, Rob Herring wrote:
> On Wed, Oct 23, 2024 at 08:44:42AM +0200, Danilo Krummrich wrote:
> > On Tue, Oct 22, 2024 at 06:47:12PM -0500, Rob Herring wrote:
> > > On Tue, Oct 22, 2024 at 11:31:52PM +0200, Danilo Krummrich wrote:
> > > > +/// ]
> > > > +/// );
> > > > +///
> > > > +/// impl platform::Driver for MyDriver {
> > > > +/// type IdInfo = ();
> > > > +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> > > > +///
> > > > +/// fn probe(
> > > > +/// _pdev: &mut platform::Device,
> > > > +/// _id_info: Option<&Self::IdInfo>,
> > > > +/// ) -> Result<Pin<KBox<Self>>> {
> > > > +/// Err(ENODEV)
> > > > +/// }
> > > > +/// }
> > > > +///```
> > > > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > > > +/// the `Adapter` documentation for an example.
> > > > +pub trait Driver {
> > > > + /// The type holding information about each device id supported by the driver.
> > > > + ///
> > > > + /// TODO: Use associated_type_defaults once stabilized:
> > > > + ///
> > > > + /// type IdInfo: 'static = ();
> > > > + type IdInfo: 'static;
> > > > +
> > > > + /// The table of device ids supported by the driver.
> > > > + const ID_TABLE: IdTable<Self::IdInfo>;
>
> Another thing. I don't think this is quite right. Well, this part is
> fine, but assigning the DT table to it is not. The underlying C code has
> 2 id tables in struct device_driver (DT and ACPI) and then the bus
> specific one in the struct ${bus}_driver.
The assignment of this table in `Adapter::register` looks like this:
`pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();`
What do you think is wrong with this assignment?
>
> > > > +
> > > > + /// Platform driver probe.
> > > > + ///
> > > > + /// Called when a new platform device is added or discovered.
> > > > + /// Implementers should attempt to initialize the device here.
> > > > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> > > > +
> > > > + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> > > > + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
> > >
> > > Is this visible to drivers? It shouldn't be.
> >
> > Yeah, I think we should just remove it. Looking at struct of_device_id, it
> > doesn't contain any useful information for a driver. I think when I added this I
> > was a bit in "autopilot" mode from the PCI stuff, where struct pci_device_id is
> > useful to drivers.
>
> TBC, you mean other than *data, right? If so, I agree.
Yes.
>
> The DT type and name fields are pretty much legacy, so I don't think the
> rust bindings need to worry about them until someone converts Sparc and
> PowerMac drivers to rust (i.e. never).
>
> I would guess the PCI cases might be questionable, too. Like DT, drivers
> may be accessing the table fields, but that's not best practice. All the
> match fields are stored in pci_dev, so why get them from the match
> table?
Fair question, I'd like to forward it to Greg. IIRC, he explicitly requested to
make the corresponding struct pci_device_id available in probe() at Kangrejos.
>
> Rob
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-24 9:11 ` Dirk Behme
@ 2024-10-28 10:19 ` Danilo Krummrich
2024-10-29 7:20 ` Dirk Behme
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-28 10:19 UTC (permalink / raw)
To: Dirk Behme
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Thu, Oct 24, 2024 at 11:11:50AM +0200, Dirk Behme wrote:
> > +/// IdTable type for platform drivers.
> > +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> > +
> > +/// The platform driver trait.
> > +///
> > +/// # Example
> > +///
> > +///```
> > +/// # use kernel::{bindings, c_str, of, platform};
> > +///
> > +/// struct MyDriver;
> > +///
> > +/// kernel::of_device_table!(
> > +/// OF_TABLE,
> > +/// MODULE_OF_TABLE,
>
> It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
> used here. Shouldn't they be somehow driver specific, e.g. OF_TABLE_MYDRIVER
> and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the other
> examples/samples in this patch series. Found that while using the *same*
> somewhere else ;)
I think the names by themselves are fine. They're local to the module. However,
we stringify `OF_TABLE` in `module_device_table` to build the export name, i.e.
"__mod_of__OF_TABLE_device_table". Hence the potential duplicate symbols.
I think we somehow need to build the module name into the symbol name as well.
>
> Best regards
>
> Dirk
>
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions
2024-10-27 22:42 ` Boqun Feng
@ 2024-10-28 10:21 ` Danilo Krummrich
2024-11-26 14:06 ` Danilo Krummrich
1 sibling, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-28 10:21 UTC (permalink / raw)
To: Boqun Feng
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, gary, bjorn3_gh,
benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Sun, Oct 27, 2024 at 03:42:41PM -0700, Boqun Feng wrote:
> Hi Danilo,
>
> On Tue, Oct 22, 2024 at 11:31:48PM +0200, Danilo Krummrich wrote:
> [...]
> > +/// The PCI device representation.
> > +///
> > +/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI
> > +/// device, hence, also increments the base device' reference count.
> > +#[derive(Clone)]
> > +pub struct Device(ARef<device::Device>);
> > +
>
> Similar to https://lore.kernel.org/rust-for-linux/ZgG7TlybSa00cuoy@boqun-archlinux/
>
> Could you also avoid wrapping a point to a PCI device? Instead, wrap the
> object type:
>
> #[repr(transparent)]
> pub struct Device(Opaque<bindings::pci_dev>);
>
> impl AlwaysRefCounted for Device {
> <put_device() and get_device() on ->dev>
> }
Yeah, the idea was to avoid the duplicate implementation of `AlwaysRefCounted`,
but I agree it's a bit messy. Will change it.
>
> Regards,
> Boqun
>
> > +impl Device {
> > + /// Create a PCI Device instance from an existing `device::Device`.
> > + ///
> > + /// # Safety
> > + ///
> > + /// `dev` must be an `ARef<device::Device>` whose underlying `bindings::device` is a member of
> > + /// a `bindings::pci_dev`.
> > + pub unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
> > + Self(dev)
> > + }
> > +
> > + fn as_raw(&self) -> *mut bindings::pci_dev {
> > + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
> > + // embedded in `struct pci_dev`.
> > + unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ }
> > + }
> > +
> > + /// Enable memory resources for this device.
> > + pub fn enable_device_mem(&self) -> Result {
> > + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
> > + let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) };
> > + if ret != 0 {
> > + Err(Error::from_errno(ret))
> > + } else {
> > + Ok(())
> > + }
> > + }
> > +
> > + /// Enable bus-mastering for this device.
> > + pub fn set_master(&self) {
> > + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
> > + unsafe { bindings::pci_set_master(self.as_raw()) };
> > + }
> > +}
> > +
> > +impl AsRef<device::Device> for Device {
> > + fn as_ref(&self) -> &device::Device {
> > + &self.0
> > + }
> > +}
> > --
> > 2.46.2
> >
> >
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 13/16] samples: rust: add Rust PCI sample driver
2024-10-23 15:57 ` Rob Herring
@ 2024-10-28 13:22 ` Danilo Krummrich
0 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-28 13:22 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Wed, Oct 23, 2024 at 10:57:37AM -0500, Rob Herring wrote:
> On Tue, Oct 22, 2024 at 11:31:50PM +0200, Danilo Krummrich wrote:
> > This commit adds a sample Rust PCI driver for QEMU's "pci-testdev"
> > device. To enable this device QEMU has to be called with
> > `-device pci-testdev`.
>
> Note that the DT unittests also use this device. So this means we have 2
> drivers that bind to the device. Probably it's okay, but does make
> them somewhat mutually-exclusive.
>
> > The same driver shows how to use the PCI device / driver abstractions,
> > as well as how to request and map PCI BARs, including a short sequence of
> > MMIO operations.
> >
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> > ---
> > MAINTAINERS | 1 +
> > samples/rust/Kconfig | 11 ++++
> > samples/rust/Makefile | 1 +
> > samples/rust/rust_driver_pci.rs | 109 ++++++++++++++++++++++++++++++++
> > 4 files changed, 122 insertions(+)
> > create mode 100644 samples/rust/rust_driver_pci.rs
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 2d00d3845b4a..d9c512a3e72b 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -17940,6 +17940,7 @@ F: include/linux/of_pci.h
> > F: include/linux/pci*
> > F: include/uapi/linux/pci*
> > F: rust/kernel/pci.rs
> > +F: samples/rust/rust_driver_pci.rs
> >
> > PCIE DRIVER FOR AMAZON ANNAPURNA LABS
> > M: Jonathan Chocron <jonnyc@amazon.com>
> > diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
> > index b0f74a81c8f9..6d468193cdd8 100644
> > --- a/samples/rust/Kconfig
> > +++ b/samples/rust/Kconfig
> > @@ -30,6 +30,17 @@ config SAMPLE_RUST_PRINT
> >
> > If unsure, say N.
> >
> > +config SAMPLE_RUST_DRIVER_PCI
> > + tristate "PCI Driver"
> > + depends on PCI
> > + help
> > + This option builds the Rust PCI driver sample.
> > +
> > + To compile this as a module, choose M here:
> > + the module will be called driver_pci.
> > +
> > + If unsure, say N.
> > +
> > config SAMPLE_RUST_HOSTPROGS
> > bool "Host programs"
> > help
> > diff --git a/samples/rust/Makefile b/samples/rust/Makefile
> > index 03086dabbea4..b66767f4a62a 100644
> > --- a/samples/rust/Makefile
> > +++ b/samples/rust/Makefile
> > @@ -2,5 +2,6 @@
> >
> > obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
> > obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o
> > +obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o
> >
> > subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs
> > diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
> > new file mode 100644
> > index 000000000000..d24dc1fde9e8
> > --- /dev/null
> > +++ b/samples/rust/rust_driver_pci.rs
> > @@ -0,0 +1,109 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Rust PCI driver sample (based on QEMU's `pci-testdev`).
> > +//!
> > +//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
> > +
> > +use kernel::{bindings, c_str, devres::Devres, pci, prelude::*};
> > +
> > +struct Regs;
> > +
> > +impl Regs {
> > + const TEST: usize = 0x0;
> > + const OFFSET: usize = 0x4;
> > + const DATA: usize = 0x8;
> > + const COUNT: usize = 0xC;
> > + const END: usize = 0x10;
> > +}
> > +
> > +type Bar0 = pci::Bar<{ Regs::END }>;
> > +
> > +#[derive(Debug)]
> > +struct TestIndex(u8);
> > +
> > +impl TestIndex {
> > + const NO_EVENTFD: Self = Self(0);
> > +}
> > +
> > +struct SampleDriver {
> > + pdev: pci::Device,
> > + bar: Devres<Bar0>,
> > +}
> > +
> > +kernel::pci_device_table!(
> > + PCI_TABLE,
> > + MODULE_PCI_TABLE,
> > + <SampleDriver as pci::Driver>::IdInfo,
> > + [(
> > + pci::DeviceId::new(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
> > + TestIndex::NO_EVENTFD
> > + )]
> > +);
> > +
> > +impl SampleDriver {
> > + fn testdev(index: &TestIndex, bar: &Bar0) -> Result<u32> {
> > + // Select the test.
> > + bar.writeb(index.0, Regs::TEST);
> > +
> > + let offset = u32::from_le(bar.readl(Regs::OFFSET)) as usize;
>
> The C version of readl takes care of from_le for you. Why not here?
It's just an abstraction around the C readl(), so it does -- good catch.
>
> Also, can't we do better with rust and make this a generic:
>
> let offset = bar.read::<u32>(Regs::OFFSET)) as usize;
I think we probably could, but we'd still need to handle the special cases for 1
to 8 bytes type size (always using memcopy_{to,from}io() would lead to
unnecessary overhead). Hence, there's probably not much benefit in that.
Also, what would be the logic for a generic `{read, write}::<T>` in terms of
memory barriers? I think memcopy_{to,from}io() is always "relaxed", isn't it?
I think it's probably best to keep the two separate, the b,w,l,q variants and
a generic one that maps to memcopy_{to,from}io().
>
>
> > + let data = bar.readb(Regs::DATA);
> > +
> > + // Write `data` to `offset` to increase `count` by one.
> > + //
> > + // Note that we need `try_writeb`, since `offset` can't be checked at compile-time.
> > + bar.try_writeb(data, offset)?;
> > +
> > + Ok(u32::from_le(bar.readl(Regs::COUNT)))
> > + }
> > +}
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
` (2 preceding siblings ...)
2024-10-27 4:32 ` Fabien Parent
@ 2024-10-28 13:44 ` Dirk Behme
2024-10-29 13:16 ` Alice Ryhl
` (2 subsequent siblings)
6 siblings, 0 replies; 88+ messages in thread
From: Dirk Behme @ 2024-10-28 13:44 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, robh, daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree
On 22.10.2024 23:31, Danilo Krummrich wrote:
> Implement the basic platform bus abstractions required to write a basic
> platform driver. This includes the following data structures:
>
> The `platform::Driver` trait represents the interface to the driver and
> provides `pci::Driver::probe` for the driver to implement.
>
> The `platform::Device` abstraction represents a `struct platform_device`.
>
> In order to provide the platform bus specific parts to a generic
> `driver::Registration` the `driver::RegistrationOps` trait is implemented
> by `platform::Adapter`.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
...
> diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
> new file mode 100644
> index 000000000000..addf5356f44f
> --- /dev/null
> +++ b/rust/kernel/platform.rs
...
> +/// IdTable type for platform drivers.
> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> +
> +/// The platform driver trait.
> +///
> +/// # Example
> +///
> +///```
> +/// # use kernel::{bindings, c_str, of, platform};
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::of_device_table!(
> +/// OF_TABLE,
> +/// MODULE_OF_TABLE,
> +/// <MyDriver as platform::Driver>::IdInfo,
> +/// [
> +/// (of::DeviceId::new(c_str!("redhat,my-device")), ())
> +/// ]
> +/// );
> +///
> +/// impl platform::Driver for MyDriver {
> +/// type IdInfo = ();
> +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> +///
> +/// fn probe(
> +/// _pdev: &mut platform::Device,
> +/// _id_info: Option<&Self::IdInfo>,
> +/// ) -> Result<Pin<KBox<Self>>> {
> +/// Err(ENODEV)
> +/// }
> +/// }
> +///```
Just in case it helps, having CONFIG_OF_UNITTEST with Rob's device tree
add ons enabled adding something like [1] makes this example not compile
only, but being executed as well:
...
rust_example_platform_driver testcase-data:platform-tests:test-device@2:
Rust example platform driver probe() called.
...
# rust_doctest_kernel_platform_rs_0.location: rust/kernel/platform.rs:114
ok 63 rust_doctest_kernel_platform_rs_0
...
Best regards
Dirk
[1]
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index addf5356f44f..a926233a789f 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -112,7 +112,8 @@ macro_rules! module_platform_driver {
/// # Example
///
///```
-/// # use kernel::{bindings, c_str, of, platform};
+/// # mod module_example_platform_driver {
+/// # use kernel::{bindings, c_str, of, platform, prelude::*};
///
/// struct MyDriver;
///
@@ -121,7 +122,7 @@ macro_rules! module_platform_driver {
/// MODULE_OF_TABLE,
/// <MyDriver as platform::Driver>::IdInfo,
/// [
-/// (of::DeviceId::new(c_str!("redhat,my-device")), ())
+/// (of::DeviceId::new(c_str!("test,rust-device")), ())
/// ]
/// );
///
@@ -130,12 +131,22 @@ macro_rules! module_platform_driver {
/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
///
/// fn probe(
-/// _pdev: &mut platform::Device,
+/// pdev: &mut platform::Device,
/// _id_info: Option<&Self::IdInfo>,
/// ) -> Result<Pin<KBox<Self>>> {
+/// dev_info!(pdev.as_ref(), "Rust example platform driver
probe() called.\n");
/// Err(ENODEV)
/// }
/// }
+///
+/// kernel::module_platform_driver! {
+/// type: MyDriver,
+/// name: "rust_example_platform_driver",
+/// author: "Danilo Krummrich",
+/// description: "Rust example platform driver",
+/// license: "GPL v2",
+/// }
+/// # }
///```
/// Drivers must implement this trait in order to get a platform
driver registered. Please refer to
/// the `Adapter` documentation for an example.
^ permalink raw reply related [flat|nested] 88+ messages in thread
* Re: [PATCH v3 09/16] rust: add `io::Io` base type
2024-10-22 21:31 ` [PATCH v3 09/16] rust: add `io::Io` base type Danilo Krummrich
@ 2024-10-28 15:43 ` Alice Ryhl
2024-10-29 9:20 ` Danilo Krummrich
2024-11-06 23:31 ` Daniel Almeida
0 siblings, 2 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-10-28 15:43 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> I/O memory is typically either mapped through direct calls to ioremap()
> or subsystem / bus specific ones such as pci_iomap().
>
> Even though subsystem / bus specific functions to map I/O memory are
> based on ioremap() / iounmap() it is not desirable to re-implement them
> in Rust.
>
> Instead, implement a base type for I/O mapped memory, which generically
> provides the corresponding accessors, such as `Io::readb` or
> `Io:try_readb`.
>
> `Io` supports an optional const generic, such that a driver can indicate
> the minimal expected and required size of the mapping at compile time.
> Correspondingly, calls to the 'non-try' accessors, support compile time
> checks of the I/O memory offset to read / write, while the 'try'
> accessors, provide boundary checks on runtime.
And using zero works because the user then statically knows that zero
bytes are available ... ?
> `Io` is meant to be embedded into a structure (e.g. pci::Bar or
> io::IoMem) which creates the actual I/O memory mapping and initializes
> `Io` accordingly.
>
> To ensure that I/O mapped memory can't out-live the device it may be
> bound to, subsystems should embedd the corresponding I/O memory type
> (e.g. pci::Bar) into a `Devres` container, such that it gets revoked
> once the device is unbound.
I wonder if `Io` should be a reference type instead. That is:
struct Io<'a, const SIZE: usize> {
addr: usize,
maxsize: usize,
_lifetime: PhantomData<&'a ()>,
}
and then the constructor requires "addr must be valid I/O mapped
memory for maxsize bytes for the duration of 'a". And instead of
embedding it in another struct, the other struct creates an `Io` on
each access?
> Co-developed-by: Philipp Stanner <pstanner@redhat.com>
> Signed-off-by: Philipp Stanner <pstanner@redhat.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
[...]
> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> new file mode 100644
> index 000000000000..750af938f83e
> --- /dev/null
> +++ b/rust/kernel/io.rs
> @@ -0,0 +1,234 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Memory-mapped IO.
> +//!
> +//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
> +
> +use crate::error::{code::EINVAL, Result};
> +use crate::{bindings, build_assert};
> +
> +/// IO-mapped memory, starting at the base address @addr and spanning @maxlen bytes.
> +///
> +/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the
> +/// mapping, performing an additional region request etc.
> +///
> +/// # Invariant
> +///
> +/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size
> +/// `maxsize`.
Do you not also need an invariant that `SIZE <= maxsize`?
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-28 10:19 ` Danilo Krummrich
@ 2024-10-29 7:20 ` Dirk Behme
2024-10-29 8:50 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Dirk Behme @ 2024-10-29 7:20 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On 28.10.2024 11:19, Danilo Krummrich wrote:
> On Thu, Oct 24, 2024 at 11:11:50AM +0200, Dirk Behme wrote:
>>> +/// IdTable type for platform drivers.
>>> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
>>> +
>>> +/// The platform driver trait.
>>> +///
>>> +/// # Example
>>> +///
>>> +///```
>>> +/// # use kernel::{bindings, c_str, of, platform};
>>> +///
>>> +/// struct MyDriver;
>>> +///
>>> +/// kernel::of_device_table!(
>>> +/// OF_TABLE,
>>> +/// MODULE_OF_TABLE,
>>
>> It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
>> used here. Shouldn't they be somehow driver specific, e.g. OF_TABLE_MYDRIVER
>> and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the other
>> examples/samples in this patch series. Found that while using the *same*
>> somewhere else ;)
>
> I think the names by themselves are fine. They're local to the module. However,
> we stringify `OF_TABLE` in `module_device_table` to build the export name, i.e.
> "__mod_of__OF_TABLE_device_table". Hence the potential duplicate symbols.
>
> I think we somehow need to build the module name into the symbol name as well.
Something like this?
Subject: [PATCH] rust: device: Add the module name to the symbol name
Make the symbol name unique by adding the module name to avoid
duplicate symbol errors like
ld.lld: error: duplicate symbol: __mod_of__OF_TABLE_device_table
>>> defined at doctests_kernel_generated.ff18649a828ae8c4-cgu.0
>>>
rust/doctests_kernel_generated.o:(__mod_of__OF_TABLE_device_table) in
archive vmlinux.a
>>> defined at rust_driver_platform.2308c4225c4e08b3-cgu.0
>>> samples/rust/rust_driver_platform.o:(.rodata+0x5A8) in
archive vmlinux.a
make[2]: *** [scripts/Makefile.vmlinux_o:65: vmlinux.o] Error 1
make[1]: *** [Makefile:1154: vmlinux_o] Error 2
__mod_of__OF_TABLE_device_table is too generic. Add the module name.
Proposed-by: Danilo Krummrich <dakr@kernel.org>
Link: https://lore.kernel.org/rust-for-linux/Zx9lFG1XKnC_WaG0@pollux/
Signed-off-by: Dirk Behme <dirk.behme@de.bosch.com>
---
rust/kernel/device_id.rs | 4 ++--
rust/kernel/of.rs | 4 ++--
rust/kernel/pci.rs | 5 +++--
rust/kernel/platform.rs | 1 +
samples/rust/rust_driver_pci.rs | 1 +
samples/rust/rust_driver_platform.rs | 1 +
6 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
index 5b1329fba528..231f34362da9 100644
--- a/rust/kernel/device_id.rs
+++ b/rust/kernel/device_id.rs
@@ -151,10 +151,10 @@ fn info(&self, index: usize) -> &U {
/// Create device table alias for modpost.
#[macro_export]
macro_rules! module_device_table {
- ($table_type: literal, $module_table_name:ident, $table_name:ident)
=> {
+ ($table_type: literal, $device_name: literal,
$module_table_name:ident, $table_name:ident) => {
#[rustfmt::skip]
#[export_name =
- concat!("__mod_", $table_type, "__",
stringify!($table_name), "_device_table")
+ concat!("__mod_", $table_type, "__",
stringify!($table_name), "_", $device_name, "_device_table")
]
static $module_table_name: [core::mem::MaybeUninit<u8>;
$table_name.raw_ids().size()] =
unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs
index a37629997974..77679c30638c 100644
--- a/rust/kernel/of.rs
+++ b/rust/kernel/of.rs
@@ -51,13 +51,13 @@ pub fn compatible<'a>(&self) -> &'a CStr {
/// Create an OF `IdTable` with an "alias" for modpost.
#[macro_export]
macro_rules! of_device_table {
- ($table_name:ident, $module_table_name:ident, $id_info_type: ty,
$table_data: expr) => {
+ ($device_name: literal, $table_name:ident,
$module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
const $table_name: $crate::device_id::IdArray<
$crate::of::DeviceId,
$id_info_type,
{ $table_data.len() },
> = $crate::device_id::IdArray::new($table_data);
- $crate::module_device_table!("of", $module_table_name,
$table_name);
+ $crate::module_device_table!("of", $device_name,
$module_table_name, $table_name);
};
}
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index 58f7d9c0045b..806d192b9600 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -176,14 +176,14 @@ fn index(&self) -> usize {
/// Create a PCI `IdTable` with its alias for modpost.
#[macro_export]
macro_rules! pci_device_table {
- ($table_name:ident, $module_table_name:ident, $id_info_type: ty,
$table_data: expr) => {
+ ($device_name: literal, $table_name:ident,
$module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
const $table_name: $crate::device_id::IdArray<
$crate::pci::DeviceId,
$id_info_type,
{ $table_data.len() },
> = $crate::device_id::IdArray::new($table_data);
- $crate::module_device_table!("pci", $module_table_name,
$table_name);
+ $crate::module_device_table!("pci", $device_name,
$module_table_name, $table_name);
};
}
@@ -197,6 +197,7 @@ macro_rules! pci_device_table {
/// struct MyDriver;
///
/// kernel::pci_device_table!(
+/// "MyDriver",
/// PCI_TABLE,
/// MODULE_PCI_TABLE,
/// <MyDriver as pci::Driver>::IdInfo,
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index a926233a789f..fcdd3c5da0e5 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -118,6 +118,7 @@ macro_rules! module_platform_driver {
/// struct MyDriver;
///
/// kernel::of_device_table!(
+/// "MyDriver",
/// OF_TABLE,
/// MODULE_OF_TABLE,
/// <MyDriver as platform::Driver>::IdInfo,
diff --git a/samples/rust/rust_driver_pci.rs
b/samples/rust/rust_driver_pci.rs
index d24dc1fde9e8..6ee570b59233 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -31,6 +31,7 @@ struct SampleDriver {
}
kernel::pci_device_table!(
+ "SampleDriver",
PCI_TABLE,
MODULE_PCI_TABLE,
<SampleDriver as pci::Driver>::IdInfo,
diff --git a/samples/rust/rust_driver_platform.rs
b/samples/rust/rust_driver_platform.rs
index fd7a5ad669fe..9dfbe3b9932b 100644
--- a/samples/rust/rust_driver_platform.rs
+++ b/samples/rust/rust_driver_platform.rs
@@ -11,6 +11,7 @@ struct SampleDriver {
struct Info(u32);
kernel::of_device_table!(
+ "SampleDriver",
OF_TABLE,
MODULE_OF_TABLE,
<SampleDriver as platform::Driver>::IdInfo,
--
2.46.2
^ permalink raw reply related [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-29 7:20 ` Dirk Behme
@ 2024-10-29 8:50 ` Danilo Krummrich
2024-10-29 9:19 ` Dirk Behme
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-29 8:50 UTC (permalink / raw)
To: Dirk Behme
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 29, 2024 at 08:20:55AM +0100, Dirk Behme wrote:
> On 28.10.2024 11:19, Danilo Krummrich wrote:
> > On Thu, Oct 24, 2024 at 11:11:50AM +0200, Dirk Behme wrote:
> > > > +/// IdTable type for platform drivers.
> > > > +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> > > > +
> > > > +/// The platform driver trait.
> > > > +///
> > > > +/// # Example
> > > > +///
> > > > +///```
> > > > +/// # use kernel::{bindings, c_str, of, platform};
> > > > +///
> > > > +/// struct MyDriver;
> > > > +///
> > > > +/// kernel::of_device_table!(
> > > > +/// OF_TABLE,
> > > > +/// MODULE_OF_TABLE,
> > >
> > > It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
> > > used here. Shouldn't they be somehow driver specific, e.g. OF_TABLE_MYDRIVER
> > > and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the other
> > > examples/samples in this patch series. Found that while using the *same*
> > > somewhere else ;)
> >
> > I think the names by themselves are fine. They're local to the module. However,
> > we stringify `OF_TABLE` in `module_device_table` to build the export name, i.e.
> > "__mod_of__OF_TABLE_device_table". Hence the potential duplicate symbols.
> >
> > I think we somehow need to build the module name into the symbol name as well.
>
> Something like this?
No, I think we should just encode the Rust module name / path, which should make
this a unique symbol name.
diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
index 5b1329fba528..63e81ec2d6fd 100644
--- a/rust/kernel/device_id.rs
+++ b/rust/kernel/device_id.rs
@@ -154,7 +154,7 @@ macro_rules! module_device_table {
($table_type: literal, $module_table_name:ident, $table_name:ident) => {
#[rustfmt::skip]
#[export_name =
- concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
+ concat!("__mod_", $table_type, "__", module_path!(), "_", stringify!($table_name), "_device_table")
]
static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
For the doctests for instance this
"__mod_of__OF_TABLE_device_table"
becomes
"__mod_of__doctests_kernel_generated_OF_TABLE_device_table".
>
>
> Subject: [PATCH] rust: device: Add the module name to the symbol name
>
> Make the symbol name unique by adding the module name to avoid
> duplicate symbol errors like
>
> ld.lld: error: duplicate symbol: __mod_of__OF_TABLE_device_table
> >>> defined at doctests_kernel_generated.ff18649a828ae8c4-cgu.0
> >>> rust/doctests_kernel_generated.o:(__mod_of__OF_TABLE_device_table) in
> archive vmlinux.a
> >>> defined at rust_driver_platform.2308c4225c4e08b3-cgu.0
> >>> samples/rust/rust_driver_platform.o:(.rodata+0x5A8) in
> archive vmlinux.a
> make[2]: *** [scripts/Makefile.vmlinux_o:65: vmlinux.o] Error 1
> make[1]: *** [Makefile:1154: vmlinux_o] Error 2
>
> __mod_of__OF_TABLE_device_table is too generic. Add the module name.
>
> Proposed-by: Danilo Krummrich <dakr@kernel.org>
> Link: https://lore.kernel.org/rust-for-linux/Zx9lFG1XKnC_WaG0@pollux/
> Signed-off-by: Dirk Behme <dirk.behme@de.bosch.com>
> ---
> rust/kernel/device_id.rs | 4 ++--
> rust/kernel/of.rs | 4 ++--
> rust/kernel/pci.rs | 5 +++--
> rust/kernel/platform.rs | 1 +
> samples/rust/rust_driver_pci.rs | 1 +
> samples/rust/rust_driver_platform.rs | 1 +
> 6 files changed, 10 insertions(+), 6 deletions(-)
>
> diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
> index 5b1329fba528..231f34362da9 100644
> --- a/rust/kernel/device_id.rs
> +++ b/rust/kernel/device_id.rs
> @@ -151,10 +151,10 @@ fn info(&self, index: usize) -> &U {
> /// Create device table alias for modpost.
> #[macro_export]
> macro_rules! module_device_table {
> - ($table_type: literal, $module_table_name:ident, $table_name:ident) =>
> {
> + ($table_type: literal, $device_name: literal, $module_table_name:ident,
> $table_name:ident) => {
> #[rustfmt::skip]
> #[export_name =
> - concat!("__mod_", $table_type, "__", stringify!($table_name),
> "_device_table")
> + concat!("__mod_", $table_type, "__", stringify!($table_name),
> "_", $device_name, "_device_table")
> ]
> static $module_table_name: [core::mem::MaybeUninit<u8>;
> $table_name.raw_ids().size()] =
> unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
> diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs
> index a37629997974..77679c30638c 100644
> --- a/rust/kernel/of.rs
> +++ b/rust/kernel/of.rs
> @@ -51,13 +51,13 @@ pub fn compatible<'a>(&self) -> &'a CStr {
> /// Create an OF `IdTable` with an "alias" for modpost.
> #[macro_export]
> macro_rules! of_device_table {
> - ($table_name:ident, $module_table_name:ident, $id_info_type: ty,
> $table_data: expr) => {
> + ($device_name: literal, $table_name:ident, $module_table_name:ident,
> $id_info_type: ty, $table_data: expr) => {
> const $table_name: $crate::device_id::IdArray<
> $crate::of::DeviceId,
> $id_info_type,
> { $table_data.len() },
> > = $crate::device_id::IdArray::new($table_data);
>
> - $crate::module_device_table!("of", $module_table_name,
> $table_name);
> + $crate::module_device_table!("of", $device_name,
> $module_table_name, $table_name);
> };
> }
> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
> index 58f7d9c0045b..806d192b9600 100644
> --- a/rust/kernel/pci.rs
> +++ b/rust/kernel/pci.rs
> @@ -176,14 +176,14 @@ fn index(&self) -> usize {
> /// Create a PCI `IdTable` with its alias for modpost.
> #[macro_export]
> macro_rules! pci_device_table {
> - ($table_name:ident, $module_table_name:ident, $id_info_type: ty,
> $table_data: expr) => {
> + ($device_name: literal, $table_name:ident, $module_table_name:ident,
> $id_info_type: ty, $table_data: expr) => {
> const $table_name: $crate::device_id::IdArray<
> $crate::pci::DeviceId,
> $id_info_type,
> { $table_data.len() },
> > = $crate::device_id::IdArray::new($table_data);
>
> - $crate::module_device_table!("pci", $module_table_name,
> $table_name);
> + $crate::module_device_table!("pci", $device_name,
> $module_table_name, $table_name);
> };
> }
>
> @@ -197,6 +197,7 @@ macro_rules! pci_device_table {
> /// struct MyDriver;
> ///
> /// kernel::pci_device_table!(
> +/// "MyDriver",
> /// PCI_TABLE,
> /// MODULE_PCI_TABLE,
> /// <MyDriver as pci::Driver>::IdInfo,
> diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
> index a926233a789f..fcdd3c5da0e5 100644
> --- a/rust/kernel/platform.rs
> +++ b/rust/kernel/platform.rs
> @@ -118,6 +118,7 @@ macro_rules! module_platform_driver {
> /// struct MyDriver;
> ///
> /// kernel::of_device_table!(
> +/// "MyDriver",
> /// OF_TABLE,
> /// MODULE_OF_TABLE,
> /// <MyDriver as platform::Driver>::IdInfo,
> diff --git a/samples/rust/rust_driver_pci.rs
> b/samples/rust/rust_driver_pci.rs
> index d24dc1fde9e8..6ee570b59233 100644
> --- a/samples/rust/rust_driver_pci.rs
> +++ b/samples/rust/rust_driver_pci.rs
> @@ -31,6 +31,7 @@ struct SampleDriver {
> }
>
> kernel::pci_device_table!(
> + "SampleDriver",
> PCI_TABLE,
> MODULE_PCI_TABLE,
> <SampleDriver as pci::Driver>::IdInfo,
> diff --git a/samples/rust/rust_driver_platform.rs
> b/samples/rust/rust_driver_platform.rs
> index fd7a5ad669fe..9dfbe3b9932b 100644
> --- a/samples/rust/rust_driver_platform.rs
> +++ b/samples/rust/rust_driver_platform.rs
> @@ -11,6 +11,7 @@ struct SampleDriver {
> struct Info(u32);
>
> kernel::of_device_table!(
> + "SampleDriver",
> OF_TABLE,
> MODULE_OF_TABLE,
> <SampleDriver as platform::Driver>::IdInfo,
> --
> 2.46.2
>
^ permalink raw reply related [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-29 8:50 ` Danilo Krummrich
@ 2024-10-29 9:19 ` Dirk Behme
2024-10-29 9:50 ` Danilo Krummrich
2024-10-30 13:18 ` Rob Herring
0 siblings, 2 replies; 88+ messages in thread
From: Dirk Behme @ 2024-10-29 9:19 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On 29.10.2024 09:50, Danilo Krummrich wrote:
> On Tue, Oct 29, 2024 at 08:20:55AM +0100, Dirk Behme wrote:
>> On 28.10.2024 11:19, Danilo Krummrich wrote:
>>> On Thu, Oct 24, 2024 at 11:11:50AM +0200, Dirk Behme wrote:
>>>>> +/// IdTable type for platform drivers.
>>>>> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
>>>>> +
>>>>> +/// The platform driver trait.
>>>>> +///
>>>>> +/// # Example
>>>>> +///
>>>>> +///```
>>>>> +/// # use kernel::{bindings, c_str, of, platform};
>>>>> +///
>>>>> +/// struct MyDriver;
>>>>> +///
>>>>> +/// kernel::of_device_table!(
>>>>> +/// OF_TABLE,
>>>>> +/// MODULE_OF_TABLE,
>>>>
>>>> It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
>>>> used here. Shouldn't they be somehow driver specific, e.g. OF_TABLE_MYDRIVER
>>>> and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the other
>>>> examples/samples in this patch series. Found that while using the *same*
>>>> somewhere else ;)
>>>
>>> I think the names by themselves are fine. They're local to the module. However,
>>> we stringify `OF_TABLE` in `module_device_table` to build the export name, i.e.
>>> "__mod_of__OF_TABLE_device_table". Hence the potential duplicate symbols.
>>>
>>> I think we somehow need to build the module name into the symbol name as well.
>>
>> Something like this?
>
> No, I think we should just encode the Rust module name / path, which should make
> this a unique symbol name.
>
> diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
> index 5b1329fba528..63e81ec2d6fd 100644
> --- a/rust/kernel/device_id.rs
> +++ b/rust/kernel/device_id.rs
> @@ -154,7 +154,7 @@ macro_rules! module_device_table {
> ($table_type: literal, $module_table_name:ident, $table_name:ident) => {
> #[rustfmt::skip]
> #[export_name =
> - concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
> + concat!("__mod_", $table_type, "__", module_path!(), "_", stringify!($table_name), "_device_table")
> ]
> static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
> unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
>
> For the doctests for instance this
>
> "__mod_of__OF_TABLE_device_table"
>
> becomes
>
> "__mod_of__doctests_kernel_generated_OF_TABLE_device_table".
What implies *one* OF/PCI_TABLE per path (file)?
For example adding a second FooDriver example to platform.rs won't be
possible?
+/// struct FooDriver;
+///
+/// kernel::of_device_table!(
+/// OF_TABLE,
+/// MODULE_OF_TABLE,
+/// <FooDriver as platform::Driver>::IdInfo,
+/// [
+/// (of::DeviceId::new(c_str!("test,rust-device2")), ())
+/// ]
+/// );
Best regards
Dirk
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 09/16] rust: add `io::Io` base type
2024-10-28 15:43 ` Alice Ryhl
@ 2024-10-29 9:20 ` Danilo Krummrich
2024-10-29 10:18 ` Alice Ryhl
2024-11-06 23:31 ` Daniel Almeida
1 sibling, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-29 9:20 UTC (permalink / raw)
To: Alice Ryhl
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Mon, Oct 28, 2024 at 04:43:02PM +0100, Alice Ryhl wrote:
> On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > I/O memory is typically either mapped through direct calls to ioremap()
> > or subsystem / bus specific ones such as pci_iomap().
> >
> > Even though subsystem / bus specific functions to map I/O memory are
> > based on ioremap() / iounmap() it is not desirable to re-implement them
> > in Rust.
> >
> > Instead, implement a base type for I/O mapped memory, which generically
> > provides the corresponding accessors, such as `Io::readb` or
> > `Io:try_readb`.
> >
> > `Io` supports an optional const generic, such that a driver can indicate
> > the minimal expected and required size of the mapping at compile time.
> > Correspondingly, calls to the 'non-try' accessors, support compile time
> > checks of the I/O memory offset to read / write, while the 'try'
> > accessors, provide boundary checks on runtime.
>
> And using zero works because the user then statically knows that zero
> bytes are available ... ?
Zero would mean that the (minimum) resource size is unknown at compile time.
Correspondingly, any call to `read` and `write` would not compile, since the
compile time check requires that `offset + type_size <= SIZE`.
(Hope this answers the questions, I'm not sure I got it correctly.)
>
> > `Io` is meant to be embedded into a structure (e.g. pci::Bar or
> > io::IoMem) which creates the actual I/O memory mapping and initializes
> > `Io` accordingly.
> >
> > To ensure that I/O mapped memory can't out-live the device it may be
> > bound to, subsystems should embedd the corresponding I/O memory type
> > (e.g. pci::Bar) into a `Devres` container, such that it gets revoked
> > once the device is unbound.
>
> I wonder if `Io` should be a reference type instead. That is:
>
> struct Io<'a, const SIZE: usize> {
> addr: usize,
> maxsize: usize,
> _lifetime: PhantomData<&'a ()>,
> }
>
> and then the constructor requires "addr must be valid I/O mapped
> memory for maxsize bytes for the duration of 'a". And instead of
> embedding it in another struct, the other struct creates an `Io` on
> each access?
So, we'd create the `Io` instance in `deref` of the parent structure, right?
What would be the advantage?
>
> > Co-developed-by: Philipp Stanner <pstanner@redhat.com>
> > Signed-off-by: Philipp Stanner <pstanner@redhat.com>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>
> [...]
>
> > diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> > new file mode 100644
> > index 000000000000..750af938f83e
> > --- /dev/null
> > +++ b/rust/kernel/io.rs
> > @@ -0,0 +1,234 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Memory-mapped IO.
> > +//!
> > +//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
> > +
> > +use crate::error::{code::EINVAL, Result};
> > +use crate::{bindings, build_assert};
> > +
> > +/// IO-mapped memory, starting at the base address @addr and spanning @maxlen bytes.
> > +///
> > +/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the
> > +/// mapping, performing an additional region request etc.
> > +///
> > +/// # Invariant
> > +///
> > +/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size
> > +/// `maxsize`.
>
> Do you not also need an invariant that `SIZE <= maxsize`?
I guess so, yes. It's enforced by `Io::new`, which fails if `SIZE > maxsize`.
>
> Alice
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-29 9:19 ` Dirk Behme
@ 2024-10-29 9:50 ` Danilo Krummrich
2024-10-29 9:55 ` Danilo Krummrich
2024-10-30 13:18 ` Rob Herring
1 sibling, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-29 9:50 UTC (permalink / raw)
To: Dirk Behme
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 29, 2024 at 10:19:08AM +0100, Dirk Behme wrote:
> On 29.10.2024 09:50, Danilo Krummrich wrote:
> > On Tue, Oct 29, 2024 at 08:20:55AM +0100, Dirk Behme wrote:
> > > On 28.10.2024 11:19, Danilo Krummrich wrote:
> > > > On Thu, Oct 24, 2024 at 11:11:50AM +0200, Dirk Behme wrote:
> > > > > > +/// IdTable type for platform drivers.
> > > > > > +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> > > > > > +
> > > > > > +/// The platform driver trait.
> > > > > > +///
> > > > > > +/// # Example
> > > > > > +///
> > > > > > +///```
> > > > > > +/// # use kernel::{bindings, c_str, of, platform};
> > > > > > +///
> > > > > > +/// struct MyDriver;
> > > > > > +///
> > > > > > +/// kernel::of_device_table!(
> > > > > > +/// OF_TABLE,
> > > > > > +/// MODULE_OF_TABLE,
> > > > >
> > > > > It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
> > > > > used here. Shouldn't they be somehow driver specific, e.g. OF_TABLE_MYDRIVER
> > > > > and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the other
> > > > > examples/samples in this patch series. Found that while using the *same*
> > > > > somewhere else ;)
> > > >
> > > > I think the names by themselves are fine. They're local to the module. However,
> > > > we stringify `OF_TABLE` in `module_device_table` to build the export name, i.e.
> > > > "__mod_of__OF_TABLE_device_table". Hence the potential duplicate symbols.
> > > >
> > > > I think we somehow need to build the module name into the symbol name as well.
> > >
> > > Something like this?
> >
> > No, I think we should just encode the Rust module name / path, which should make
> > this a unique symbol name.
> >
> > diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
> > index 5b1329fba528..63e81ec2d6fd 100644
> > --- a/rust/kernel/device_id.rs
> > +++ b/rust/kernel/device_id.rs
> > @@ -154,7 +154,7 @@ macro_rules! module_device_table {
> > ($table_type: literal, $module_table_name:ident, $table_name:ident) => {
> > #[rustfmt::skip]
> > #[export_name =
> > - concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
> > + concat!("__mod_", $table_type, "__", module_path!(), "_", stringify!($table_name), "_device_table")
> > ]
> > static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
> > unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
> >
> > For the doctests for instance this
> >
> > "__mod_of__OF_TABLE_device_table"
> >
> > becomes
> >
> > "__mod_of__doctests_kernel_generated_OF_TABLE_device_table".
>
>
> What implies *one* OF/PCI_TABLE per path (file)?
No, you can still have as many as you want for the same file, you just have to
give them different identifier names -- you can't have two statics with the same
name in one file anyways.
Well, I guess you somehow can (just like the doctests do), but it does make
sense to declare drivers in such a way.
I think as long as we take care that separate Rust modules can't interfere with
each other it's good enough.
>
> For example adding a second FooDriver example to platform.rs won't be
> possible?
Not unless you change the identifier name unfortunately. But that might be
fixable by putting doctests in separate `mod $(DOCTEST) {}` blocks.
>
> +/// struct FooDriver;
> +///
> +/// kernel::of_device_table!(
> +/// OF_TABLE,
> +/// MODULE_OF_TABLE,
> +/// <FooDriver as platform::Driver>::IdInfo,
> +/// [
> +/// (of::DeviceId::new(c_str!("test,rust-device2")), ())
> +/// ]
> +/// );
>
> Best regards
>
> Dirk
>
>
>
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-29 9:50 ` Danilo Krummrich
@ 2024-10-29 9:55 ` Danilo Krummrich
2024-10-29 10:08 ` Dirk Behme
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-29 9:55 UTC (permalink / raw)
To: Dirk Behme
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 29, 2024 at 10:50:11AM +0100, Danilo Krummrich wrote:
> On Tue, Oct 29, 2024 at 10:19:08AM +0100, Dirk Behme wrote:
> > On 29.10.2024 09:50, Danilo Krummrich wrote:
> > > On Tue, Oct 29, 2024 at 08:20:55AM +0100, Dirk Behme wrote:
> > > > On 28.10.2024 11:19, Danilo Krummrich wrote:
> > > > > On Thu, Oct 24, 2024 at 11:11:50AM +0200, Dirk Behme wrote:
> > > > > > > +/// IdTable type for platform drivers.
> > > > > > > +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> > > > > > > +
> > > > > > > +/// The platform driver trait.
> > > > > > > +///
> > > > > > > +/// # Example
> > > > > > > +///
> > > > > > > +///```
> > > > > > > +/// # use kernel::{bindings, c_str, of, platform};
> > > > > > > +///
> > > > > > > +/// struct MyDriver;
> > > > > > > +///
> > > > > > > +/// kernel::of_device_table!(
> > > > > > > +/// OF_TABLE,
> > > > > > > +/// MODULE_OF_TABLE,
> > > > > >
> > > > > > It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
> > > > > > used here. Shouldn't they be somehow driver specific, e.g. OF_TABLE_MYDRIVER
> > > > > > and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the other
> > > > > > examples/samples in this patch series. Found that while using the *same*
> > > > > > somewhere else ;)
> > > > >
> > > > > I think the names by themselves are fine. They're local to the module. However,
> > > > > we stringify `OF_TABLE` in `module_device_table` to build the export name, i.e.
> > > > > "__mod_of__OF_TABLE_device_table". Hence the potential duplicate symbols.
> > > > >
> > > > > I think we somehow need to build the module name into the symbol name as well.
> > > >
> > > > Something like this?
> > >
> > > No, I think we should just encode the Rust module name / path, which should make
> > > this a unique symbol name.
> > >
> > > diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
> > > index 5b1329fba528..63e81ec2d6fd 100644
> > > --- a/rust/kernel/device_id.rs
> > > +++ b/rust/kernel/device_id.rs
> > > @@ -154,7 +154,7 @@ macro_rules! module_device_table {
> > > ($table_type: literal, $module_table_name:ident, $table_name:ident) => {
> > > #[rustfmt::skip]
> > > #[export_name =
> > > - concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
> > > + concat!("__mod_", $table_type, "__", module_path!(), "_", stringify!($table_name), "_device_table")
> > > ]
> > > static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
> > > unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
> > >
> > > For the doctests for instance this
> > >
> > > "__mod_of__OF_TABLE_device_table"
> > >
> > > becomes
> > >
> > > "__mod_of__doctests_kernel_generated_OF_TABLE_device_table".
> >
> >
> > What implies *one* OF/PCI_TABLE per path (file)?
>
> No, you can still have as many as you want for the same file, you just have to
> give them different identifier names -- you can't have two statics with the same
> name in one file anyways.
>
> Well, I guess you somehow can (just like the doctests do), but it does make
> sense to declare drivers in such a way.
>
> I think as long as we take care that separate Rust modules can't interfere with
> each other it's good enough.
>
> >
> > For example adding a second FooDriver example to platform.rs won't be
> > possible?
>
> Not unless you change the identifier name unfortunately. But that might be
> fixable by putting doctests in separate `mod $(DOCTEST) {}` blocks.
Another option would be to not only encode the module path, but also the line
number:
diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
index 5b1329fba528..7956edbbad52 100644
--- a/rust/kernel/device_id.rs
+++ b/rust/kernel/device_id.rs
@@ -154,7 +154,7 @@ macro_rules! module_device_table {
($table_type: literal, $module_table_name:ident, $table_name:ident) => {
#[rustfmt::skip]
#[export_name =
- concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
+ concat!("__mod_", $table_type, "__", module_path!(), "_", line!(), "_", stringify!($table_name), "_device_table")
]
static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
This way you'll get
"__mod_of__doctests_kernel_generated_3875_OF_TABLE_device_table"
"__mod_of__doctests_kernel_generated_3946_OF_TABLE_device_table"
if you put identical doctests.
>
> >
> > +/// struct FooDriver;
> > +///
> > +/// kernel::of_device_table!(
> > +/// OF_TABLE,
> > +/// MODULE_OF_TABLE,
> > +/// <FooDriver as platform::Driver>::IdInfo,
> > +/// [
> > +/// (of::DeviceId::new(c_str!("test,rust-device2")), ())
> > +/// ]
> > +/// );
> >
> > Best regards
> >
> > Dirk
> >
> >
> >
> >
^ permalink raw reply related [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-29 9:55 ` Danilo Krummrich
@ 2024-10-29 10:08 ` Dirk Behme
0 siblings, 0 replies; 88+ messages in thread
From: Dirk Behme @ 2024-10-29 10:08 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On 29.10.2024 10:55, Danilo Krummrich wrote:
> On Tue, Oct 29, 2024 at 10:50:11AM +0100, Danilo Krummrich wrote:
>> On Tue, Oct 29, 2024 at 10:19:08AM +0100, Dirk Behme wrote:
>>> On 29.10.2024 09:50, Danilo Krummrich wrote:
>>>> On Tue, Oct 29, 2024 at 08:20:55AM +0100, Dirk Behme wrote:
>>>>> On 28.10.2024 11:19, Danilo Krummrich wrote:
>>>>>> On Thu, Oct 24, 2024 at 11:11:50AM +0200, Dirk Behme wrote:
>>>>>>>> +/// IdTable type for platform drivers.
>>>>>>>> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
>>>>>>>> +
>>>>>>>> +/// The platform driver trait.
>>>>>>>> +///
>>>>>>>> +/// # Example
>>>>>>>> +///
>>>>>>>> +///```
>>>>>>>> +/// # use kernel::{bindings, c_str, of, platform};
>>>>>>>> +///
>>>>>>>> +/// struct MyDriver;
>>>>>>>> +///
>>>>>>>> +/// kernel::of_device_table!(
>>>>>>>> +/// OF_TABLE,
>>>>>>>> +/// MODULE_OF_TABLE,
>>>>>>>
>>>>>>> It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
>>>>>>> used here. Shouldn't they be somehow driver specific, e.g. OF_TABLE_MYDRIVER
>>>>>>> and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the other
>>>>>>> examples/samples in this patch series. Found that while using the *same*
>>>>>>> somewhere else ;)
>>>>>>
>>>>>> I think the names by themselves are fine. They're local to the module. However,
>>>>>> we stringify `OF_TABLE` in `module_device_table` to build the export name, i.e.
>>>>>> "__mod_of__OF_TABLE_device_table". Hence the potential duplicate symbols.
>>>>>>
>>>>>> I think we somehow need to build the module name into the symbol name as well.
>>>>>
>>>>> Something like this?
>>>>
>>>> No, I think we should just encode the Rust module name / path, which should make
>>>> this a unique symbol name.
>>>>
>>>> diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
>>>> index 5b1329fba528..63e81ec2d6fd 100644
>>>> --- a/rust/kernel/device_id.rs
>>>> +++ b/rust/kernel/device_id.rs
>>>> @@ -154,7 +154,7 @@ macro_rules! module_device_table {
>>>> ($table_type: literal, $module_table_name:ident, $table_name:ident) => {
>>>> #[rustfmt::skip]
>>>> #[export_name =
>>>> - concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
>>>> + concat!("__mod_", $table_type, "__", module_path!(), "_", stringify!($table_name), "_device_table")
>>>> ]
>>>> static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
>>>> unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
>>>>
>>>> For the doctests for instance this
>>>>
>>>> "__mod_of__OF_TABLE_device_table"
>>>>
>>>> becomes
>>>>
>>>> "__mod_of__doctests_kernel_generated_OF_TABLE_device_table".
>>>
>>>
>>> What implies *one* OF/PCI_TABLE per path (file)?
>>
>> No, you can still have as many as you want for the same file, you just have to
>> give them different identifier names -- you can't have two statics with the same
>> name in one file anyways.
>>
>> Well, I guess you somehow can (just like the doctests do), but it does make
>> sense to declare drivers in such a way.
>>
>> I think as long as we take care that separate Rust modules can't interfere with
>> each other it's good enough.
>>
>>>
>>> For example adding a second FooDriver example to platform.rs won't be
>>> possible?
>>
>> Not unless you change the identifier name unfortunately. But that might be
>> fixable by putting doctests in separate `mod $(DOCTEST) {}` blocks.
>
> Another option would be to not only encode the module path, but also the line
> number:
>
> diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
> index 5b1329fba528..7956edbbad52 100644
> --- a/rust/kernel/device_id.rs
> +++ b/rust/kernel/device_id.rs
> @@ -154,7 +154,7 @@ macro_rules! module_device_table {
> ($table_type: literal, $module_table_name:ident, $table_name:ident) => {
> #[rustfmt::skip]
> #[export_name =
> - concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
> + concat!("__mod_", $table_type, "__", module_path!(), "_", line!(), "_", stringify!($table_name), "_device_table")
> ]
> static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
> unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
>
> This way you'll get
>
> "__mod_of__doctests_kernel_generated_3875_OF_TABLE_device_table"
> "__mod_of__doctests_kernel_generated_3946_OF_TABLE_device_table"
Yes, if we want to avoid adding a unique name that makes sense and is a
good "automatic" option :)
Thanks
Dirk
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 09/16] rust: add `io::Io` base type
2024-10-29 9:20 ` Danilo Krummrich
@ 2024-10-29 10:18 ` Alice Ryhl
2024-11-06 23:44 ` Daniel Almeida
0 siblings, 1 reply; 88+ messages in thread
From: Alice Ryhl @ 2024-10-29 10:18 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 29, 2024 at 10:21 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Mon, Oct 28, 2024 at 04:43:02PM +0100, Alice Ryhl wrote:
> > On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > >
> > > I/O memory is typically either mapped through direct calls to ioremap()
> > > or subsystem / bus specific ones such as pci_iomap().
> > >
> > > Even though subsystem / bus specific functions to map I/O memory are
> > > based on ioremap() / iounmap() it is not desirable to re-implement them
> > > in Rust.
> > >
> > > Instead, implement a base type for I/O mapped memory, which generically
> > > provides the corresponding accessors, such as `Io::readb` or
> > > `Io:try_readb`.
> > >
> > > `Io` supports an optional const generic, such that a driver can indicate
> > > the minimal expected and required size of the mapping at compile time.
> > > Correspondingly, calls to the 'non-try' accessors, support compile time
> > > checks of the I/O memory offset to read / write, while the 'try'
> > > accessors, provide boundary checks on runtime.
> >
> > And using zero works because the user then statically knows that zero
> > bytes are available ... ?
>
> Zero would mean that the (minimum) resource size is unknown at compile time.
> Correspondingly, any call to `read` and `write` would not compile, since the
> compile time check requires that `offset + type_size <= SIZE`.
>
> (Hope this answers the questions, I'm not sure I got it correctly.)
Yeah, thanks! I got it now.
> > > `Io` is meant to be embedded into a structure (e.g. pci::Bar or
> > > io::IoMem) which creates the actual I/O memory mapping and initializes
> > > `Io` accordingly.
> > >
> > > To ensure that I/O mapped memory can't out-live the device it may be
> > > bound to, subsystems should embedd the corresponding I/O memory type
> > > (e.g. pci::Bar) into a `Devres` container, such that it gets revoked
> > > once the device is unbound.
> >
> > I wonder if `Io` should be a reference type instead. That is:
> >
> > struct Io<'a, const SIZE: usize> {
> > addr: usize,
> > maxsize: usize,
> > _lifetime: PhantomData<&'a ()>,
> > }
> >
> > and then the constructor requires "addr must be valid I/O mapped
> > memory for maxsize bytes for the duration of 'a". And instead of
> > embedding it in another struct, the other struct creates an `Io` on
> > each access?
>
> So, we'd create the `Io` instance in `deref` of the parent structure, right?
> What would be the advantage?
What you're doing now is a bit awkward to use. You have to make sure
that it never escapes the struct it's created for, so e.g. you can't
give out a mutable reference as the user could otherwise `mem::swap`
it with another Io. Similarly, the Io can never be in a public field.
Your safety comment on Io::new really needs to say something like
"while this struct exists, the `addr` must be a valid I/O region",
since I assume such regions are not valid forever? Similarly if we
look at [1], the I/O region actually gets unmapped *before* the Io is
destroyed since IoMem::drop runs before the fields of IoMem are
destroyed, so you really need something like "until the last use of
this Io" and not "until this Io is destroyed" in the safety comment.
If you compare similar cases in Rust, then they also do what I
suggested. For example, Vec<T> holds a raw pointer, and it uses unsafe
to assert that it's valid on each use of the raw pointer - it does not
create e.g. an `&'static mut [T]` to hold in a field of the Vec<T>.
Having an IoRaw<S> and an Io<'a, S> corresponds to what Vec<T> does
with IoRaw being like NonNull<T> and Io<'a, S> being like &'a T.
[1]: https://lore.kernel.org/all/20241024-topic-panthor-rs-platform_io_support-v1-1-3d1addd96e30@collabora.com/
> > > diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
> > > new file mode 100644
> > > index 000000000000..750af938f83e
> > > --- /dev/null
> > > +++ b/rust/kernel/io.rs
> > > @@ -0,0 +1,234 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +
> > > +//! Memory-mapped IO.
> > > +//!
> > > +//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
> > > +
> > > +use crate::error::{code::EINVAL, Result};
> > > +use crate::{bindings, build_assert};
> > > +
> > > +/// IO-mapped memory, starting at the base address @addr and spanning @maxlen bytes.
> > > +///
> > > +/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the
> > > +/// mapping, performing an additional region request etc.
> > > +///
> > > +/// # Invariant
> > > +///
> > > +/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size
> > > +/// `maxsize`.
> >
> > Do you not also need an invariant that `SIZE <= maxsize`?
>
> I guess so, yes. It's enforced by `Io::new`, which fails if `SIZE > maxsize`.
Sure. It's still an invariant.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 01/16] rust: init: introduce `Opaque::try_ffi_init`
2024-10-22 21:31 ` [PATCH v3 01/16] rust: init: introduce `Opaque::try_ffi_init` Danilo Krummrich
@ 2024-10-29 12:42 ` Alice Ryhl
0 siblings, 0 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-10-29 12:42 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On Tue, Oct 22, 2024 at 11:32 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> From: Wedson Almeida Filho <walmeida@microsoft.com>
>
> This is the same as `Opaque::ffi_init`, but returns a `Result`.
>
> This is used by subsequent patches to implement the FFI init of static
> driver structures on registration.
>
> Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Just as an FYI, try_ffi_init is landing through char-misc-next for 6.13.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 02/16] rust: introduce `InPlaceModule`
2024-10-22 21:31 ` [PATCH v3 02/16] rust: introduce `InPlaceModule` Danilo Krummrich
@ 2024-10-29 12:45 ` Alice Ryhl
2024-11-04 0:15 ` Greg KH
0 siblings, 1 reply; 88+ messages in thread
From: Alice Ryhl @ 2024-10-29 12:45 UTC (permalink / raw)
To: Danilo Krummrich, ojeda
Cc: gregkh, rafael, bhelgaas, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On Tue, Oct 22, 2024 at 11:32 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> From: Wedson Almeida Filho <walmeida@microsoft.com>
>
> This allows modules to be initialised in-place in pinned memory, which
> enables the usage of pinned types (e.g., mutexes, spinlocks, driver
> registrations, etc.) in modules without any extra allocations.
>
> Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Miguel: I think this patch would be good to land in 6.13. I'd like to
submit a miscdevice sample once 6.14 comes around, and having this
already in will make the miscdevice sample nicer.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 03/16] rust: pass module name to `Module::init`
2024-10-22 21:31 ` [PATCH v3 03/16] rust: pass module name to `Module::init` Danilo Krummrich
@ 2024-10-29 12:55 ` Alice Ryhl
0 siblings, 0 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-10-29 12:55 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:32 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> In a subsequent patch we introduce the `Registration` abstraction used
> to register driver structures. Some subsystems require the module name on
> driver registration (e.g. PCI in __pci_register_driver()), hence pass
> the module name to `Module::init`.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
The C-side just uses KBUILD_MODNAME for this. Could we do something similar?
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
` (3 preceding siblings ...)
2024-10-28 13:44 ` Dirk Behme
@ 2024-10-29 13:16 ` Alice Ryhl
2024-10-30 15:50 ` Alice Ryhl
2024-12-04 19:25 ` Daniel Almeida
6 siblings, 0 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-10-29 13:16 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
> + let table = Self::ID_TABLE;
> +
> + // SAFETY:
> + // - `table` has static lifetime, hence it's valid for read,
> + // - `dev` is guaranteed to be valid while it's alive, and so is
> + // `pdev.as_dev().as_raw()`.
> + let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_dev().as_raw()) };
> +
> + if raw_id.is_null() {
> + None
> + } else {
> + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and
> + // does not add additional invariants, so it's safe to transmute.
> + Some(unsafe { &*raw_id.cast::<of::DeviceId>() })
> + }
> + }
Sorry if this has already been mentioned, but this method should
probably be marked #[cfg(CONFIG_OF)] so that you can use these
abstractions even if OF is disabled.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 07/16] rust: add `Revocable` type
2024-10-22 21:31 ` [PATCH v3 07/16] rust: add `Revocable` type Danilo Krummrich
@ 2024-10-29 13:26 ` Alice Ryhl
2024-12-03 9:21 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Alice Ryhl @ 2024-10-29 13:26 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> From: Wedson Almeida Filho <wedsonaf@gmail.com>
>
> Revocable allows access to objects to be safely revoked at run time.
>
> This is useful, for example, for resources allocated during device probe;
> when the device is removed, the driver should stop accessing the device
> resources even if another state is kept in memory due to existing
> references (i.e., device context data is ref-counted and has a non-zero
> refcount after removal of the device).
>
> Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/lib.rs | 1 +
> rust/kernel/revocable.rs | 211 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 212 insertions(+)
> create mode 100644 rust/kernel/revocable.rs
>
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 89f6bd2efcc0..b603b67dcd71 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -54,6 +54,7 @@
> pub mod prelude;
> pub mod print;
> pub mod rbtree;
> +pub mod revocable;
> pub mod sizes;
> mod static_assert;
> #[doc(hidden)]
> diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs
> new file mode 100644
> index 000000000000..83455558d795
> --- /dev/null
> +++ b/rust/kernel/revocable.rs
> @@ -0,0 +1,211 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Revocable objects.
> +//!
> +//! The [`Revocable`] type wraps other types and allows access to them to be revoked. The existence
> +//! of a [`RevocableGuard`] ensures that objects remain valid.
> +
> +use crate::{
> + bindings,
> + init::{self},
> + prelude::*,
> + sync::rcu,
> +};
> +use core::{
> + cell::UnsafeCell,
> + marker::PhantomData,
> + mem::MaybeUninit,
> + ops::Deref,
> + ptr::drop_in_place,
> + sync::atomic::{AtomicBool, Ordering},
> +};
> +
> +/// An object that can become inaccessible at runtime.
> +///
> +/// Once access is revoked and all concurrent users complete (i.e., all existing instances of
> +/// [`RevocableGuard`] are dropped), the wrapped object is also dropped.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// # use kernel::revocable::Revocable;
> +///
> +/// struct Example {
> +/// a: u32,
> +/// b: u32,
> +/// }
> +///
> +/// fn add_two(v: &Revocable<Example>) -> Option<u32> {
> +/// let guard = v.try_access()?;
> +/// Some(guard.a + guard.b)
> +/// }
> +///
> +/// let v = KBox::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_KERNEL).unwrap();
> +/// assert_eq!(add_two(&v), Some(30));
> +/// v.revoke();
> +/// assert_eq!(add_two(&v), None);
> +/// ```
> +///
> +/// Sample example as above, but explicitly using the rcu read side lock.
> +///
> +/// ```
> +/// # use kernel::revocable::Revocable;
> +/// use kernel::sync::rcu;
> +///
> +/// struct Example {
> +/// a: u32,
> +/// b: u32,
> +/// }
> +///
> +/// fn add_two(v: &Revocable<Example>) -> Option<u32> {
> +/// let guard = rcu::read_lock();
> +/// let e = v.try_access_with_guard(&guard)?;
> +/// Some(e.a + e.b)
> +/// }
> +///
> +/// let v = KBox::pin_init(Revocable::new(Example { a: 10, b: 20 }), GFP_KERNEL).unwrap();
> +/// assert_eq!(add_two(&v), Some(30));
> +/// v.revoke();
> +/// assert_eq!(add_two(&v), None);
> +/// ```
> +#[pin_data(PinnedDrop)]
> +pub struct Revocable<T> {
> + is_available: AtomicBool,
> + #[pin]
> + data: MaybeUninit<UnsafeCell<T>>,
> +}
> +
> +// SAFETY: `Revocable` is `Send` if the wrapped object is also `Send`. This is because while the
> +// functionality exposed by `Revocable` can be accessed from any thread/CPU, it is possible that
> +// this isn't supported by the wrapped object.
> +unsafe impl<T: Send> Send for Revocable<T> {}
> +
> +// SAFETY: `Revocable` is `Sync` if the wrapped object is both `Send` and `Sync`. We require `Send`
> +// from the wrapped object as well because of `Revocable::revoke`, which can trigger the `Drop`
> +// implementation of the wrapped object from an arbitrary thread.
> +unsafe impl<T: Sync + Send> Sync for Revocable<T> {}
> +
> +impl<T> Revocable<T> {
> + /// Creates a new revocable instance of the given data.
> + pub fn new(data: impl PinInit<T>) -> impl PinInit<Self> {
> + pin_init!(Self {
> + is_available: AtomicBool::new(true),
> + // SAFETY: The closure only returns `Ok(())` if `slot` is fully initialized; on error
> + // `slot` is not partially initialized and does not need to be dropped.
> + data <- unsafe {
> + init::pin_init_from_closure(move |slot: *mut MaybeUninit<UnsafeCell<T>>| {
> + init::PinInit::<T, core::convert::Infallible>::__pinned_init(data,
> + slot as *mut T)?;
> + Ok::<(), core::convert::Infallible>(())
> + })
If you change `data` to be `Opaque`, then this can just be
data <- Opaque::ffi_init(data)
(or maybe you need try_ffi_init)
> +
> + /// Tries to access the \[revocable\] wrapped object.
> + ///
> + /// Returns `None` if the object has been revoked and is therefore no longer accessible.
> + ///
> + /// Returns a guard that gives access to the object otherwise; the object is guaranteed to
> + /// remain accessible while the guard is alive. In such cases, callers are not allowed to sleep
> + /// because another CPU may be waiting to complete the revocation of this object.
> + pub fn try_access(&self) -> Option<RevocableGuard<'_, T>> {
> + let guard = rcu::read_lock();
> + if self.is_available.load(Ordering::Relaxed) {
> + // SAFETY: Since `self.is_available` is true, data is initialised and has to remain
> + // valid because the RCU read side lock prevents it from being dropped.
> + Some(unsafe { RevocableGuard::new(self.data.assume_init_ref().get(), guard) })
> + } else {
> + None
> + }
> + }
> +
> + /// Tries to access the \[revocable\] wrapped object.
These backslashes seem wrong.
> + /// Returns `None` if the object has been revoked and is therefore no longer accessible.
> + ///
> + /// Returns a shared reference to the object otherwise; the object is guaranteed to
> + /// remain accessible while the rcu read side guard is alive. In such cases, callers are not
> + /// allowed to sleep because another CPU may be waiting to complete the revocation of this
> + /// object.
> + pub fn try_access_with_guard<'a>(&'a self, _guard: &'a rcu::Guard) -> Option<&'a T> {
> + if self.is_available.load(Ordering::Relaxed) {
> + // SAFETY: Since `self.is_available` is true, data is initialised and has to remain
> + // valid because the RCU read side lock prevents it from being dropped.
> + Some(unsafe { &*self.data.assume_init_ref().get() })
> + } else {
> + None
> + }
> + }
> +
> + /// Revokes access to and drops the wrapped object.
> + ///
> + /// Access to the object is revoked immediately to new callers of [`Revocable::try_access`]. If
> + /// there are concurrent users of the object (i.e., ones that called [`Revocable::try_access`]
> + /// beforehand and still haven't dropped the returned guard), this function waits for the
> + /// concurrent access to complete before dropping the wrapped object.
> + pub fn revoke(&self) {
> + if self
> + .is_available
> + .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed)
> + .is_ok()
> + {
> + // SAFETY: Just an FFI call, there are no further requirements.
> + unsafe { bindings::synchronize_rcu() };
> +
> + // SAFETY: We know `self.data` is valid because only one CPU can succeed the
> + // `compare_exchange` above that takes `is_available` from `true` to `false`.
> + unsafe { drop_in_place(self.data.assume_init_ref().get()) };
> + }
> + }
> +}
> +
> +#[pinned_drop]
> +impl<T> PinnedDrop for Revocable<T> {
> + fn drop(self: Pin<&mut Self>) {
> + // Drop only if the data hasn't been revoked yet (in which case it has already been
> + // dropped).
> + // SAFETY: We are not moving out of `p`, only dropping in place
> + let p = unsafe { self.get_unchecked_mut() };
> + if *p.is_available.get_mut() {
> + // SAFETY: We know `self.data` is valid because no other CPU has changed
> + // `is_available` to `false` yet, and no other CPU can do it anymore because this CPU
> + // holds the only reference (mutable) to `self` now.
> + unsafe { drop_in_place(p.data.assume_init_ref().get()) };
> + }
> + }
> +}
> +
> +/// A guard that allows access to a revocable object and keeps it alive.
> +///
> +/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context
> +/// holding the RCU read-side lock.
> +///
> +/// # Invariants
> +///
> +/// The RCU read-side lock is held while the guard is alive.
> +pub struct RevocableGuard<'a, T> {
> + data_ref: *const T,
> + _rcu_guard: rcu::Guard,
> + _p: PhantomData<&'a ()>,
> +}
Is this needed? Can't all users just use `try_access_with_guard`?
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction
2024-10-22 21:31 ` [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
2024-10-22 23:03 ` Rob Herring
2024-10-27 4:38 ` Fabien Parent
@ 2024-10-29 13:37 ` Alice Ryhl
2 siblings, 0 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-10-29 13:37 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> `of::DeviceId` is an abstraction around `struct of_device_id`.
>
> This is used by subsequent patches, in particular the platform bus
> abstractions, to create OF device ID tables.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> rust/bindings/bindings_helper.h | 1 +
> rust/kernel/lib.rs | 1 +
> rust/kernel/of.rs | 63 +++++++++++++++++++++++++++++++++
> 4 files changed, 66 insertions(+)
> create mode 100644 rust/kernel/of.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d9c512a3e72b..87eb9a7869eb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17340,6 +17340,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git
> F: Documentation/ABI/testing/sysfs-firmware-ofw
> F: drivers/of/
> F: include/linux/of*.h
> +F: rust/kernel/of.rs
> F: scripts/dtc/
> F: tools/testing/selftests/dt/
> K: of_overlay_notifier_
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index cd4edd6496ae..312f03cbdce9 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -15,6 +15,7 @@
> #include <linux/firmware.h>
> #include <linux/jiffies.h>
> #include <linux/mdio.h>
> +#include <linux/of_device.h>
> #include <linux/pci.h>
> #include <linux/phy.h>
> #include <linux/refcount.h>
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 3ec690eb6d43..5946f59f1688 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -51,6 +51,7 @@
> pub mod list;
> #[cfg(CONFIG_NET)]
> pub mod net;
> +pub mod of;
> pub mod page;
> pub mod prelude;
> pub mod print;
> diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs
> new file mode 100644
> index 000000000000..a37629997974
> --- /dev/null
> +++ b/rust/kernel/of.rs
> @@ -0,0 +1,63 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Open Firmware abstractions.
> +//!
> +//! C header: [`include/linux/of_*.h`](srctree/include/linux/of_*.h)
> +
> +use crate::{bindings, device_id::RawDeviceId, prelude::*};
> +
> +/// An open firmware device id.
> +#[derive(Clone, Copy)]
> +pub struct DeviceId(bindings::of_device_id);
> +
> +// SAFETY:
> +// * `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and does not add
> +// additional invariants, so it's safe to transmute to `RawType`.
Your #[repr(transparent)] marker is missing.
> +// * `DRIVER_DATA_OFFSET` is the offset to the `data` field.
> +unsafe impl RawDeviceId for DeviceId {
> + type RawType = bindings::of_device_id;
> +
> + const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::of_device_id, data);
> +
> + fn index(&self) -> usize {
> + self.0.data as _
> + }
> +}
> +
> +impl DeviceId {
> + /// Create a new device id from an OF 'compatible' string.
> + pub const fn new(compatible: &'static CStr) -> Self {
Since you make a copy of `compatible`, you don't need 'static.
> + let src = compatible.as_bytes_with_nul();
> + // Replace with `bindings::of_device_id::default()` once stabilized for `const`.
> + // SAFETY: FFI type is valid to be zero-initialized.
> + let mut of: bindings::of_device_id = unsafe { core::mem::zeroed() };
> +
> + let mut i = 0;
> + while i < src.len() {
> + of.compatible[i] = src[i] as _;
> + i += 1;
> + }
> +
> + Self(of)
> + }
> +
> + /// The compatible string of the embedded `struct bindings::of_device_id` as `&CStr`.
> + pub fn compatible<'a>(&self) -> &'a CStr {
This should probably be:
pub fn compatible(&self) -> &CStr {
Right now, the returned CStr is not tied to self at all, even though
it points inside `self`.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 06/16] rust: add rcu abstraction
2024-10-22 21:31 ` [PATCH v3 06/16] rust: add rcu abstraction Danilo Krummrich
@ 2024-10-29 13:59 ` Alice Ryhl
0 siblings, 0 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-10-29 13:59 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> From: Wedson Almeida Filho <wedsonaf@gmail.com>
>
> Add a simple abstraction to guard critical code sections with an rcu
> read lock.
>
> Signed-off-by: Wedson Almeida Filho <wedsonaf@gmail.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> rust/helpers/helpers.c | 1 +
> rust/helpers/rcu.c | 13 +++++++++++
> rust/kernel/sync.rs | 1 +
> rust/kernel/sync/rcu.rs | 52 +++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 67 insertions(+)
> create mode 100644 rust/helpers/rcu.c
> create mode 100644 rust/kernel/sync/rcu.rs
>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 20a0c69d5cc7..0720debccdd4 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -16,6 +16,7 @@
> #include "mutex.c"
> #include "page.c"
> #include "rbtree.c"
> +#include "rcu.c"
> #include "refcount.c"
> #include "signal.c"
> #include "slab.c"
> diff --git a/rust/helpers/rcu.c b/rust/helpers/rcu.c
> new file mode 100644
> index 000000000000..f1cec6583513
> --- /dev/null
> +++ b/rust/helpers/rcu.c
> @@ -0,0 +1,13 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/rcupdate.h>
> +
> +void rust_helper_rcu_read_lock(void)
> +{
> + rcu_read_lock();
> +}
> +
> +void rust_helper_rcu_read_unlock(void)
> +{
> + rcu_read_unlock();
> +}
> diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
> index 0ab20975a3b5..1806767359fe 100644
> --- a/rust/kernel/sync.rs
> +++ b/rust/kernel/sync.rs
> @@ -11,6 +11,7 @@
> mod condvar;
> pub mod lock;
> mod locked_by;
> +pub mod rcu;
>
> pub use arc::{Arc, ArcBorrow, UniqueArc};
> pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult};
> diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs
> new file mode 100644
> index 000000000000..5a35495f69a4
> --- /dev/null
> +++ b/rust/kernel/sync/rcu.rs
> @@ -0,0 +1,52 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! RCU support.
> +//!
> +//! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
> +
> +use crate::bindings;
> +use core::marker::PhantomData;
> +
> +/// Evidence that the RCU read side lock is held on the current thread/CPU.
> +///
> +/// The type is explicitly not `Send` because this property is per-thread/CPU.
> +///
> +/// # Invariants
> +///
> +/// The RCU read side lock is actually held while instances of this guard exist.
> +pub struct Guard {
> + _not_send: PhantomData<*mut ()>,
Once 6.13 is released, you'll want to use NotThreadSafe here instead
of PhantomData. It's landing upstream through vfs.rust.file.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
2024-10-27 22:42 ` Boqun Feng
@ 2024-10-29 21:16 ` Christian Schrefl
1 sibling, 0 replies; 88+ messages in thread
From: Christian Schrefl @ 2024-10-29 21:16 UTC (permalink / raw)
To: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, robh, daniel.almeida, saravanak
Cc: rust-for-linux, linux-kernel, linux-pci, devicetree
Hi,
On 22.10.24 11:31 PM, Danilo Krummrich wrote:
> Implement the basic PCI abstractions required to write a basic PCI
> driver. This includes the following data structures:
>
> The `pci::Driver` trait represents the interface to the driver and
> provides `pci::Driver::probe` for the driver to implement.
>
> The `pci::Device` abstraction represents a `struct pci_dev` and provides
> abstractions for common functions, such as `pci::Device::set_master`.
>
> In order to provide the PCI specific parts to a generic
> `driver::Registration` the `driver::RegistrationOps` trait is implemented
> by `pci::Adapter`.
>
> `pci::DeviceId` implements PCI device IDs based on the generic
> `device_id::RawDevceId` abstraction.
>
> Co-developed-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> rust/bindings/bindings_helper.h | 1 +
> rust/helpers/helpers.c | 1 +
> rust/helpers/pci.c | 18 ++
> rust/kernel/lib.rs | 2 +
> rust/kernel/pci.rs | 284 ++++++++++++++++++++++++++++++++
> 6 files changed, 307 insertions(+)
> create mode 100644 rust/helpers/pci.c
> create mode 100644 rust/kernel/pci.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 97914d0752fb..2d00d3845b4a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17939,6 +17939,7 @@ F: include/asm-generic/pci*
> F: include/linux/of_pci.h
> F: include/linux/pci*
> F: include/uapi/linux/pci*
> +F: rust/kernel/pci.rs
>
> PCIE DRIVER FOR AMAZON ANNAPURNA LABS
> M: Jonathan Chocron <jonnyc@amazon.com>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index a80783fcbe04..cd4edd6496ae 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -15,6 +15,7 @@
> #include <linux/firmware.h>
> #include <linux/jiffies.h>
> #include <linux/mdio.h>
> +#include <linux/pci.h>
> #include <linux/phy.h>
> #include <linux/refcount.h>
> #include <linux/sched.h>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 3acb2b9e52ec..8bc6e9735589 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -17,6 +17,7 @@
> #include "kunit.c"
> #include "mutex.c"
> #include "page.c"
> +#include "pci.c"
> #include "rbtree.c"
> #include "rcu.c"
> #include "refcount.c"
> diff --git a/rust/helpers/pci.c b/rust/helpers/pci.c
> new file mode 100644
> index 000000000000..7c4f3ba49486
> --- /dev/null
> +++ b/rust/helpers/pci.c
> @@ -0,0 +1,18 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/pci.h>
> +
> +void rust_helper_pci_set_drvdata(struct pci_dev *pdev, void *data)
> +{
> + pci_set_drvdata(pdev, data);
> +}
> +
> +void *rust_helper_pci_get_drvdata(struct pci_dev *pdev)
> +{
> + return pci_get_drvdata(pdev);
> +}
> +
> +u64 rust_helper_pci_resource_len(struct pci_dev *pdev, int bar)
> +{
> + return pci_resource_len(pdev, bar);
> +}
This should return resource_size_t to build on 32 bit architectures.
Error when building with my 32 bit arm patch:
error[E0308]: mismatched types
--> rust/kernel/pci.rs:398:21
|
398 | Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?) })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found `u64`
|
help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit
|
398 | Ok(unsafe { bindings::pci_resource_len(self.as_raw(), bar.try_into()?).try_into().unwrap() })
| ++++++++++++++++++++
error: aborting due to 1 previous error
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 5cb892419453..3ec690eb6d43 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -73,6 +73,8 @@
> pub use bindings;
> pub mod io;
> pub use macros;
> +#[cfg(all(CONFIG_PCI, CONFIG_PCI_MSI))]
> +pub mod pci;
> pub use uapi;
>
> #[doc(hidden)]
> diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
> new file mode 100644
> index 000000000000..ccc9a5f322e4
> --- /dev/null
> +++ b/rust/kernel/pci.rs
> @@ -0,0 +1,284 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Abstractions for the PCI bus.
> +//!
> +//! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h)
> +
> +use crate::{
> + bindings, container_of, device,
> + device_id::RawDeviceId,
> + driver,
> + error::{to_result, Result},
> + str::CStr,
> + types::{ARef, ForeignOwnable},
> + ThisModule,
> +};
> +use core::ops::Deref;
> +use kernel::prelude::*;
> +
> +/// An adapter for the registration of PCI drivers.
> +pub struct Adapter<T: Driver>(T);
> +
> +impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
> + type RegType = bindings::pci_driver;
> +
> + fn register(
> + pdrv: &mut Self::RegType,
> + name: &'static CStr,
> + module: &'static ThisModule,
> + ) -> Result {
> + pdrv.name = name.as_char_ptr();
> + pdrv.probe = Some(Self::probe_callback);
> + pdrv.remove = Some(Self::remove_callback);
> + pdrv.id_table = T::ID_TABLE.as_ptr();
> +
> + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> + to_result(unsafe {
> + bindings::__pci_register_driver(pdrv as _, module.0, name.as_char_ptr())
> + })
> + }
> +
> + fn unregister(pdrv: &mut Self::RegType) {
> + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> + unsafe { bindings::pci_unregister_driver(pdrv) }
> + }
> +}
> +
> +impl<T: Driver + 'static> Adapter<T> {
> + extern "C" fn probe_callback(
> + pdev: *mut bindings::pci_dev,
> + id: *const bindings::pci_device_id,
> + ) -> core::ffi::c_int {
> + // SAFETY: The PCI bus only ever calls the probe callback with a valid pointer to a
> + // `struct pci_dev`.
> + let dev = unsafe { device::Device::from_raw(&mut (*pdev).dev) };
> + // SAFETY: `dev` is guaranteed to be embedded in a valid `struct pci_dev` by the call
> + // above.
> + let mut pdev = unsafe { Device::from_dev(dev) };
> +
> + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct pci_device_id` and
> + // does not add additional invariants, so it's safe to transmute.
> + let id = unsafe { &*id.cast::<DeviceId>() };
> + let info = T::ID_TABLE.info(id.index());
> +
> + match T::probe(&mut pdev, id, info) {
> + Ok(data) => {
> + // Let the `struct pci_dev` own a reference of the driver's private data.
> + // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
> + // `struct pci_dev`.
> + unsafe { bindings::pci_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
> + }
> + Err(err) => return Error::to_errno(err),
> + }
> +
> + 0
> + }
> +
> + extern "C" fn remove_callback(pdev: *mut bindings::pci_dev) {
> + // SAFETY: The PCI bus only ever calls the remove callback with a valid pointer to a
> + // `struct pci_dev`.
> + let ptr = unsafe { bindings::pci_get_drvdata(pdev) };
> +
> + // SAFETY: `remove_callback` is only ever called after a successful call to
> + // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
> + // `KBox<T>` pointer created through `KBox::into_foreign`.
> + let _ = unsafe { KBox::<T>::from_foreign(ptr) };
> + }
> +}
> +
> +/// Declares a kernel module that exposes a single PCI driver.
> +///
> +/// # Example
> +///
> +///```ignore
> +/// kernel::module_pci_driver! {
> +/// type: MyDriver,
> +/// name: "Module name",
> +/// author: "Author name",
> +/// description: "Description",
> +/// license: "GPL v2",
> +/// }
> +///```
> +#[macro_export]
> +macro_rules! module_pci_driver {
> +($($f:tt)*) => {
> + $crate::module_driver!(<T>, $crate::pci::Adapter<T>, { $($f)* });
> +};
> +}
> +
> +/// Abstraction for bindings::pci_device_id.
> +#[repr(transparent)]
> +#[derive(Clone, Copy)]
> +pub struct DeviceId(bindings::pci_device_id);
> +
> +impl DeviceId {
> + const PCI_ANY_ID: u32 = !0;
> +
> + /// PCI_DEVICE macro.
> + pub const fn new(vendor: u32, device: u32) -> Self {
> + Self(bindings::pci_device_id {
> + vendor,
> + device,
> + subvendor: DeviceId::PCI_ANY_ID,
> + subdevice: DeviceId::PCI_ANY_ID,
> + class: 0,
> + class_mask: 0,
> + driver_data: 0,
> + override_only: 0,
> + })
> + }
> +
> + /// PCI_DEVICE_CLASS macro.
> + pub const fn with_class(class: u32, class_mask: u32) -> Self {
> + Self(bindings::pci_device_id {
> + vendor: DeviceId::PCI_ANY_ID,
> + device: DeviceId::PCI_ANY_ID,
> + subvendor: DeviceId::PCI_ANY_ID,
> + subdevice: DeviceId::PCI_ANY_ID,
> + class,
> + class_mask,
> + driver_data: 0,
> + override_only: 0,
> + })
> + }
> +}
> +
> +// Allow drivers R/O access to the fields of `pci_device_id`; should we prefer accessor functions
> +// to void exposing C structure fields?
> +impl Deref for DeviceId {
> + type Target = bindings::pci_device_id;
> +
> + fn deref(&self) -> &Self::Target {
> + &self.0
> + }
> +}
> +
> +// SAFETY:
> +// * `DeviceId` is a `#[repr(transparent)` wrapper of `pci_device_id` and does not add
> +// additional invariants, so it's safe to transmute to `RawType`.
> +// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
> +unsafe impl RawDeviceId for DeviceId {
> + type RawType = bindings::pci_device_id;
> +
> + const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::pci_device_id, driver_data);
> +
> + fn index(&self) -> usize {
> + self.driver_data as _
> + }
> +}
> +
> +/// IdTable type for PCI
> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
> +
> +/// Create a PCI `IdTable` with its alias for modpost.
> +#[macro_export]
> +macro_rules! pci_device_table {
> + ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
> + const $table_name: $crate::device_id::IdArray<
> + $crate::pci::DeviceId,
> + $id_info_type,
> + { $table_data.len() },
> + > = $crate::device_id::IdArray::new($table_data);
> +
> + $crate::module_device_table!("pci", $module_table_name, $table_name);
> + };
> +}
> +
> +/// The PCI driver trait.
> +///
> +/// # Example
> +///
> +///```
> +/// # use kernel::{bindings, pci};
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::pci_device_table!(
> +/// PCI_TABLE,
> +/// MODULE_PCI_TABLE,
> +/// <MyDriver as pci::Driver>::IdInfo,
> +/// [
> +/// (pci::DeviceId::new(bindings::PCI_VENDOR_ID_REDHAT, bindings::PCI_ANY_ID as u32), ())
> +/// ]
> +/// );
> +///
> +/// impl pci::Driver for MyDriver {
> +/// type IdInfo = ();
> +/// const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
> +///
> +/// fn probe(
> +/// _pdev: &mut pci::Device,
> +/// _id: &pci::DeviceId,
> +/// _id_info: &Self::IdInfo,
> +/// ) -> Result<Pin<KBox<Self>>> {
> +/// Err(ENODEV)
> +/// }
> +/// }
> +///```
> +/// Drivers must implement this trait in order to get a PCI driver registered. Please refer to the
> +/// `Adapter` documentation for an example.
> +pub trait Driver {
> + /// The type holding information about each device id supported by the driver.
> + ///
> + /// TODO: Use associated_type_defaults once stabilized:
> + ///
> + /// type IdInfo: 'static = ();
> + type IdInfo: 'static;
> +
> + /// The table of device ids supported by the driver.
> + const ID_TABLE: IdTable<Self::IdInfo>;
> +
> + /// PCI driver probe.
> + ///
> + /// Called when a new platform device is added or discovered.
> + /// Implementers should attempt to initialize the device here.
> + fn probe(dev: &mut Device, id: &DeviceId, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>;
> +}
> +
> +/// The PCI device representation.
> +///
> +/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI
> +/// device, hence, also increments the base device' reference count.
> +#[derive(Clone)]
> +pub struct Device(ARef<device::Device>);
> +
> +impl Device {
> + /// Create a PCI Device instance from an existing `device::Device`.
> + ///
> + /// # Safety
> + ///
> + /// `dev` must be an `ARef<device::Device>` whose underlying `bindings::device` is a member of
> + /// a `bindings::pci_dev`.
> + pub unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
> + Self(dev)
> + }
> +
> + fn as_raw(&self) -> *mut bindings::pci_dev {
> + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
> + // embedded in `struct pci_dev`.
> + unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ }
> + }
> +
> + /// Enable memory resources for this device.
> + pub fn enable_device_mem(&self) -> Result {
> + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
> + let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) };
> + if ret != 0 {
> + Err(Error::from_errno(ret))
> + } else {
> + Ok(())
> + }
> + }
> +
> + /// Enable bus-mastering for this device.
> + pub fn set_master(&self) {
> + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
> + unsafe { bindings::pci_set_master(self.as_raw()) };
> + }
> +}
> +
> +impl AsRef<device::Device> for Device {
> + fn as_ref(&self) -> &device::Device {
> + &self.0
> + }
> +}
Cheers
Christian
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-28 10:15 ` Danilo Krummrich
@ 2024-10-30 12:23 ` Rob Herring
2024-11-26 12:39 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Rob Herring @ 2024-10-30 12:23 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Mon, Oct 28, 2024 at 5:15 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Oct 23, 2024 at 09:23:55AM -0500, Rob Herring wrote:
> > On Wed, Oct 23, 2024 at 08:44:42AM +0200, Danilo Krummrich wrote:
> > > On Tue, Oct 22, 2024 at 06:47:12PM -0500, Rob Herring wrote:
> > > > On Tue, Oct 22, 2024 at 11:31:52PM +0200, Danilo Krummrich wrote:
> > > > > +/// ]
> > > > > +/// );
> > > > > +///
> > > > > +/// impl platform::Driver for MyDriver {
> > > > > +/// type IdInfo = ();
> > > > > +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> > > > > +///
> > > > > +/// fn probe(
> > > > > +/// _pdev: &mut platform::Device,
> > > > > +/// _id_info: Option<&Self::IdInfo>,
> > > > > +/// ) -> Result<Pin<KBox<Self>>> {
> > > > > +/// Err(ENODEV)
> > > > > +/// }
> > > > > +/// }
> > > > > +///```
> > > > > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > > > > +/// the `Adapter` documentation for an example.
> > > > > +pub trait Driver {
> > > > > + /// The type holding information about each device id supported by the driver.
> > > > > + ///
> > > > > + /// TODO: Use associated_type_defaults once stabilized:
> > > > > + ///
> > > > > + /// type IdInfo: 'static = ();
> > > > > + type IdInfo: 'static;
> > > > > +
> > > > > + /// The table of device ids supported by the driver.
> > > > > + const ID_TABLE: IdTable<Self::IdInfo>;
> >
> > Another thing. I don't think this is quite right. Well, this part is
> > fine, but assigning the DT table to it is not. The underlying C code has
> > 2 id tables in struct device_driver (DT and ACPI) and then the bus
> > specific one in the struct ${bus}_driver.
>
> The assignment of this table in `Adapter::register` looks like this:
>
> `pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();`
>
> What do you think is wrong with this assignment?
Every bus implementation will need the DT and ACPI tables, so they
should not be declared and assigned in platform driver code, but in
the generic device/driver abstractions just like the underlying C
code. The one here should be for platform_device_id. You could put all
3 tables here, but that's going to be a lot of duplication I think.
> >
> > > > > +
> > > > > + /// Platform driver probe.
> > > > > + ///
> > > > > + /// Called when a new platform device is added or discovered.
> > > > > + /// Implementers should attempt to initialize the device here.
> > > > > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> > > > > +
> > > > > + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> > > > > + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
> > > >
> > > > Is this visible to drivers? It shouldn't be.
> > >
> > > Yeah, I think we should just remove it. Looking at struct of_device_id, it
> > > doesn't contain any useful information for a driver. I think when I added this I
> > > was a bit in "autopilot" mode from the PCI stuff, where struct pci_device_id is
> > > useful to drivers.
> >
> > TBC, you mean other than *data, right? If so, I agree.
>
> Yes.
>
> >
> > The DT type and name fields are pretty much legacy, so I don't think the
> > rust bindings need to worry about them until someone converts Sparc and
> > PowerMac drivers to rust (i.e. never).
> >
> > I would guess the PCI cases might be questionable, too. Like DT, drivers
> > may be accessing the table fields, but that's not best practice. All the
> > match fields are stored in pci_dev, so why get them from the match
> > table?
>
> Fair question, I'd like to forward it to Greg. IIRC, he explicitly requested to
> make the corresponding struct pci_device_id available in probe() at Kangrejos.
Which table gets passed in though? Is the IdInfo parameter generic and
can be platform_device_id, of_device_id or acpi_device_id? Not sure if
that's possible in rust or not.
PCI is the exception, not the rule here, in that it only matches with
pci_device_id. At least I think that is the case currently, but it is
entirely possible we may want to do ACPI/DT matching like every other
bus. There are cases where PCI devices are described in DT.
Rob
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-29 9:19 ` Dirk Behme
2024-10-29 9:50 ` Danilo Krummrich
@ 2024-10-30 13:18 ` Rob Herring
1 sibling, 0 replies; 88+ messages in thread
From: Rob Herring @ 2024-10-30 13:18 UTC (permalink / raw)
To: Dirk Behme
Cc: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 29, 2024 at 4:19 AM Dirk Behme <dirk.behme@de.bosch.com> wrote:
>
> On 29.10.2024 09:50, Danilo Krummrich wrote:
> > On Tue, Oct 29, 2024 at 08:20:55AM +0100, Dirk Behme wrote:
> >> On 28.10.2024 11:19, Danilo Krummrich wrote:
> >>> On Thu, Oct 24, 2024 at 11:11:50AM +0200, Dirk Behme wrote:
> >>>>> +/// IdTable type for platform drivers.
> >>>>> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> >>>>> +
> >>>>> +/// The platform driver trait.
> >>>>> +///
> >>>>> +/// # Example
> >>>>> +///
> >>>>> +///```
> >>>>> +/// # use kernel::{bindings, c_str, of, platform};
> >>>>> +///
> >>>>> +/// struct MyDriver;
> >>>>> +///
> >>>>> +/// kernel::of_device_table!(
> >>>>> +/// OF_TABLE,
> >>>>> +/// MODULE_OF_TABLE,
> >>>>
> >>>> It looks to me that OF_TABLE and MODULE_OF_TABLE are quite generic names
> >>>> used here. Shouldn't they be somehow driver specific, e.g. OF_TABLE_MYDRIVER
> >>>> and MODULE_OF_TABLE_MYDRIVER or whatever? Same for the other
> >>>> examples/samples in this patch series. Found that while using the *same*
> >>>> somewhere else ;)
> >>>
> >>> I think the names by themselves are fine. They're local to the module. However,
> >>> we stringify `OF_TABLE` in `module_device_table` to build the export name, i.e.
> >>> "__mod_of__OF_TABLE_device_table". Hence the potential duplicate symbols.
> >>>
> >>> I think we somehow need to build the module name into the symbol name as well.
> >>
> >> Something like this?
> >
> > No, I think we should just encode the Rust module name / path, which should make
> > this a unique symbol name.
> >
> > diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
> > index 5b1329fba528..63e81ec2d6fd 100644
> > --- a/rust/kernel/device_id.rs
> > +++ b/rust/kernel/device_id.rs
> > @@ -154,7 +154,7 @@ macro_rules! module_device_table {
> > ($table_type: literal, $module_table_name:ident, $table_name:ident) => {
> > #[rustfmt::skip]
> > #[export_name =
> > - concat!("__mod_", $table_type, "__", stringify!($table_name), "_device_table")
> > + concat!("__mod_", $table_type, "__", module_path!(), "_", stringify!($table_name), "_device_table")
> > ]
> > static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
> > unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
> >
> > For the doctests for instance this
> >
> > "__mod_of__OF_TABLE_device_table"
> >
> > becomes
> >
> > "__mod_of__doctests_kernel_generated_OF_TABLE_device_table".
>
>
> What implies *one* OF/PCI_TABLE per path (file)?
It's generally one per module, but it's one per type because it is one
type per driver. So platform (and most other) drivers can have $bus,
DT, and ACPI tables.
While you could have 1 module with N drivers, I don't think I've ever
seen that case and certainly not something we'd encourage. Perhaps it
is just not possible to disallow in C, but we can in rust? That may be
a benefit, not a limitation.
Rob
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
` (4 preceding siblings ...)
2024-10-29 13:16 ` Alice Ryhl
@ 2024-10-30 15:50 ` Alice Ryhl
2024-10-30 18:07 ` Danilo Krummrich
2024-12-04 19:25 ` Daniel Almeida
6 siblings, 1 reply; 88+ messages in thread
From: Alice Ryhl @ 2024-10-30 15:50 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> +/// the `Adapter` documentation for an example.
> +pub trait Driver {
> + /// The type holding information about each device id supported by the driver.
> + ///
> + /// TODO: Use associated_type_defaults once stabilized:
> + ///
> + /// type IdInfo: 'static = ();
> + type IdInfo: 'static;
> +
> + /// The table of device ids supported by the driver.
> + const ID_TABLE: IdTable<Self::IdInfo>;
> +
> + /// Platform driver probe.
> + ///
> + /// Called when a new platform device is added or discovered.
> + /// Implementers should attempt to initialize the device here.
> + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
This forces the user to put their driver data in a KBox, but they
might want to use an Arc instead. You don't actually *need* a KBox -
any ForeignOwnable seems to fit your purposes.
Please see my miscdevice and shrinker patchsets for examples of how
you can extend this to allow any ForeignOwnable.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-30 15:50 ` Alice Ryhl
@ 2024-10-30 18:07 ` Danilo Krummrich
2024-10-31 8:23 ` Alice Ryhl
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-10-30 18:07 UTC (permalink / raw)
To: Alice Ryhl
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Wed, Oct 30, 2024 at 04:50:43PM +0100, Alice Ryhl wrote:
> On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > +/// the `Adapter` documentation for an example.
> > +pub trait Driver {
> > + /// The type holding information about each device id supported by the driver.
> > + ///
> > + /// TODO: Use associated_type_defaults once stabilized:
> > + ///
> > + /// type IdInfo: 'static = ();
> > + type IdInfo: 'static;
> > +
> > + /// The table of device ids supported by the driver.
> > + const ID_TABLE: IdTable<Self::IdInfo>;
> > +
> > + /// Platform driver probe.
> > + ///
> > + /// Called when a new platform device is added or discovered.
> > + /// Implementers should attempt to initialize the device here.
> > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
>
> This forces the user to put their driver data in a KBox, but they
> might want to use an Arc instead. You don't actually *need* a KBox -
> any ForeignOwnable seems to fit your purposes.
This is intentional, I do need a `KBox` here.
The reason is that I want to enforce that the returned `Pin<KBox<Self>>` has
exactly the lifetime of the binding of the device and driver, i.e. from probe()
until remove(). This is the lifetime the structure should actually represent.
This way we can attach things like `Registration` objects to this structure, or
anything else that should only exist from probe() until remove().
If a driver needs some private driver data that needs to be reference counted,
it is usually attached to the class representation of the driver.
For instance, in Nova the reference counted stuff is attached to the DRM device
and then I just have the DRM device (which itself is reference counted) embedded
in the `Driver` structure.
In any case, drivers can always embed a separate `Arc` in their `Driver`
structure if they really have a need for that.
- Danilo
>
> Please see my miscdevice and shrinker patchsets for examples of how
> you can extend this to allow any ForeignOwnable.
>
> Alice
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-30 18:07 ` Danilo Krummrich
@ 2024-10-31 8:23 ` Alice Ryhl
2024-11-26 14:13 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Alice Ryhl @ 2024-10-31 8:23 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Wed, Oct 30, 2024 at 7:07 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Oct 30, 2024 at 04:50:43PM +0100, Alice Ryhl wrote:
> > On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > > +/// the `Adapter` documentation for an example.
> > > +pub trait Driver {
> > > + /// The type holding information about each device id supported by the driver.
> > > + ///
> > > + /// TODO: Use associated_type_defaults once stabilized:
> > > + ///
> > > + /// type IdInfo: 'static = ();
> > > + type IdInfo: 'static;
> > > +
> > > + /// The table of device ids supported by the driver.
> > > + const ID_TABLE: IdTable<Self::IdInfo>;
> > > +
> > > + /// Platform driver probe.
> > > + ///
> > > + /// Called when a new platform device is added or discovered.
> > > + /// Implementers should attempt to initialize the device here.
> > > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> >
> > This forces the user to put their driver data in a KBox, but they
> > might want to use an Arc instead. You don't actually *need* a KBox -
> > any ForeignOwnable seems to fit your purposes.
>
> This is intentional, I do need a `KBox` here.
>
> The reason is that I want to enforce that the returned `Pin<KBox<Self>>` has
> exactly the lifetime of the binding of the device and driver, i.e. from probe()
> until remove(). This is the lifetime the structure should actually represent.
>
> This way we can attach things like `Registration` objects to this structure, or
> anything else that should only exist from probe() until remove().
>
> If a driver needs some private driver data that needs to be reference counted,
> it is usually attached to the class representation of the driver.
>
> For instance, in Nova the reference counted stuff is attached to the DRM device
> and then I just have the DRM device (which itself is reference counted) embedded
> in the `Driver` structure.
>
> In any case, drivers can always embed a separate `Arc` in their `Driver`
> structure if they really have a need for that.
Is this needed for soundness of those registrations?
Also, I've often seen drivers use devm_kzalloc or similar to allocate
exactly this object. KBox doesn't allow for that.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 10/16] rust: add devres abstraction
2024-10-22 21:31 ` [PATCH v3 10/16] rust: add devres abstraction Danilo Krummrich
@ 2024-10-31 14:03 ` Alice Ryhl
2024-11-27 12:21 ` Alice Ryhl
1 sibling, 0 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-10-31 14:03 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> Add a Rust abstraction for the kernel's devres (device resource
> management) implementation.
>
> The Devres type acts as a container to manage the lifetime and
> accessibility of device bound resources. Therefore it registers a
> devres callback and revokes access to the resource on invocation.
>
> Users of the Devres abstraction can simply free the corresponding
> resources in their Drop implementation, which is invoked when either the
> Devres instance goes out of scope or the devres callback leads to the
> resource being revoked, which implies a call to drop_in_place().
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
How is this intended to be used? Every single field I add of this type
is going to result in an additional synchronize_rcu() call in my
destructor. Those calls are pretty expensive.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 02/16] rust: introduce `InPlaceModule`
2024-10-29 12:45 ` Alice Ryhl
@ 2024-11-04 0:15 ` Greg KH
2024-11-04 17:36 ` Miguel Ojeda
0 siblings, 1 reply; 88+ messages in thread
From: Greg KH @ 2024-11-04 0:15 UTC (permalink / raw)
To: Alice Ryhl
Cc: Danilo Krummrich, ojeda, rafael, bhelgaas, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
airlied, fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On Tue, Oct 29, 2024 at 01:45:36PM +0100, Alice Ryhl wrote:
> On Tue, Oct 22, 2024 at 11:32 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > From: Wedson Almeida Filho <walmeida@microsoft.com>
> >
> > This allows modules to be initialised in-place in pinned memory, which
> > enables the usage of pinned types (e.g., mutexes, spinlocks, driver
> > registrations, etc.) in modules without any extra allocations.
> >
> > Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
>
> Miguel: I think this patch would be good to land in 6.13. I'd like to
> submit a miscdevice sample once 6.14 comes around, and having this
> already in will make the miscdevice sample nicer.
If no one objects, I'll just add this to the char/misc tree that already
has the other misc device rust code in it.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 08/16] rust: add `dev_*` print macros.
2024-10-22 21:31 ` [PATCH v3 08/16] rust: add `dev_*` print macros Danilo Krummrich
@ 2024-11-04 0:24 ` Greg KH
0 siblings, 0 replies; 88+ messages in thread
From: Greg KH @ 2024-11-04 0:24 UTC (permalink / raw)
To: Danilo Krummrich
Cc: rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary, bjorn3_gh,
benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On Tue, Oct 22, 2024 at 11:31:45PM +0200, Danilo Krummrich wrote:
> From: Wedson Almeida Filho <wedsonaf@google.com>
>
> Implement `dev_*` print macros for `device::Device`.
>
> They behave like the macros with the same names in C, i.e., they print
> messages to the kernel ring buffer with the given level, prefixing the
> messages with corresponding device information.
>
> Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/kernel/device.rs | 319 ++++++++++++++++++++++++++++++++++++++++-
> rust/kernel/prelude.rs | 2 +
> 2 files changed, 320 insertions(+), 1 deletion(-)
This is good to grab now, I've added it to my char-misc tree, thanks!
greg k-h
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 02/16] rust: introduce `InPlaceModule`
2024-11-04 0:15 ` Greg KH
@ 2024-11-04 17:36 ` Miguel Ojeda
0 siblings, 0 replies; 88+ messages in thread
From: Miguel Ojeda @ 2024-11-04 17:36 UTC (permalink / raw)
To: Greg KH
Cc: Alice Ryhl, Danilo Krummrich, ojeda, rafael, bhelgaas,
alex.gaynor, boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross,
a.hindborg, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, robh, daniel.almeida, saravanak, rust-for-linux,
linux-kernel, linux-pci, devicetree, Wedson Almeida Filho
On Mon, Nov 4, 2024 at 6:48 AM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> If no one objects, I'll just add this to the char/misc tree that already
> has the other misc device rust code in it.
Sounds good, thanks! I gave it a quick spin (on top of `rust-next`)
just in case it triggered lints etc.:
Acked-by: Miguel Ojeda <ojeda@kernel.org>
Cheers,
Miguel
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 09/16] rust: add `io::Io` base type
2024-10-28 15:43 ` Alice Ryhl
2024-10-29 9:20 ` Danilo Krummrich
@ 2024-11-06 23:31 ` Daniel Almeida
1 sibling, 0 replies; 88+ messages in thread
From: Daniel Almeida @ 2024-11-06 23:31 UTC (permalink / raw)
To: Alice Ryhl
Cc: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
airlied, fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
Hi,
> On 28 Oct 2024, at 12:43, Alice Ryhl <aliceryhl@google.com> wrote:
>
> On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> I/O memory is typically either mapped through direct calls to ioremap()
>> or subsystem / bus specific ones such as pci_iomap().
>>
>> Even though subsystem / bus specific functions to map I/O memory are
>> based on ioremap() / iounmap() it is not desirable to re-implement them
>> in Rust.
>>
>> Instead, implement a base type for I/O mapped memory, which generically
>> provides the corresponding accessors, such as `Io::readb` or
>> `Io:try_readb`.
>>
>> `Io` supports an optional const generic, such that a driver can indicate
>> the minimal expected and required size of the mapping at compile time.
>> Correspondingly, calls to the 'non-try' accessors, support compile time
>> checks of the I/O memory offset to read / write, while the 'try'
>> accessors, provide boundary checks on runtime.
>
> And using zero works because the user then statically knows that zero
> bytes are available ... ?
>
>> `Io` is meant to be embedded into a structure (e.g. pci::Bar or
>> io::IoMem) which creates the actual I/O memory mapping and initializes
>> `Io` accordingly.
>>
>> To ensure that I/O mapped memory can't out-live the device it may be
>> bound to, subsystems should embedd the corresponding I/O memory type
>> (e.g. pci::Bar) into a `Devres` container, such that it gets revoked
>> once the device is unbound.
>
> I wonder if `Io` should be a reference type instead. That is:
>
> struct Io<'a, const SIZE: usize> {
> addr: usize,
> maxsize: usize,
> _lifetime: PhantomData<&'a ()>,
> }
>
> and then the constructor requires "addr must be valid I/O mapped
> memory for maxsize bytes for the duration of 'a". And instead of
> embedding it in another struct, the other struct creates an `Io` on
> each access?
Please let’s not add a lifetime here. Drivers will usually have this mapped
at probe time and it will usually remain mapped until remove(), so I think this
works fine the way it is.
>
>> Co-developed-by: Philipp Stanner <pstanner@redhat.com>
>> Signed-off-by: Philipp Stanner <pstanner@redhat.com>
>> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>
> [...]
>
>> diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
>> new file mode 100644
>> index 000000000000..750af938f83e
>> --- /dev/null
>> +++ b/rust/kernel/io.rs
>> @@ -0,0 +1,234 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +//! Memory-mapped IO.
>> +//!
>> +//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
>> +
>> +use crate::error::{code::EINVAL, Result};
>> +use crate::{bindings, build_assert};
>> +
>> +/// IO-mapped memory, starting at the base address @addr and spanning @maxlen bytes.
>> +///
>> +/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the
>> +/// mapping, performing an additional region request etc.
>> +///
>> +/// # Invariant
>> +///
>> +/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size
>> +/// `maxsize`.
>
> Do you not also need an invariant that `SIZE <= maxsize`?
>
> Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 09/16] rust: add `io::Io` base type
2024-10-29 10:18 ` Alice Ryhl
@ 2024-11-06 23:44 ` Daniel Almeida
0 siblings, 0 replies; 88+ messages in thread
From: Daniel Almeida @ 2024-11-06 23:44 UTC (permalink / raw)
To: Alice Ryhl
Cc: Danilo Krummrich, gregkh, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
airlied, fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
Sorry, I didn’t see this:
> On 29 Oct 2024, at 07:18, Alice Ryhl <aliceryhl@google.com> wrote:
>
> What you're doing now is a bit awkward to use. You have to make sure
> that it never escapes the struct it's created for, so e.g. you can't
> give out a mutable reference as the user could otherwise `mem::swap`
> it with another Io. Similarly, the Io can never be in a public field.
> Your safety comment on Io::new really needs to say something like
> "while this struct exists, the `addr` must be a valid I/O region",
> since I assume such regions are not valid forever? Similarly if we
Io is meant to be a private member within a wrapper type that actually
acquires the underlying I/O region, like `pci::Bar` or `Platform::IoMem`.
Doesn’t that fix the above?
> look at [1], the I/O region actually gets unmapped *before* the Io is
> destroyed since IoMem::drop runs before the fields of IoMem are
> destroyed, so you really need something like "until the last use of
> this Io" and not "until this Io is destroyed" in the safety comment.
>
> If you compare similar cases in Rust, then they also do what I
> suggested. For example, Vec<T> holds a raw pointer, and it uses unsafe
> to assert that it's valid on each use of the raw pointer - it does not
> create e.g. an `&'static mut [T]` to hold in a field of the Vec<T>.
> Having an IoRaw<S> and an Io<'a, S> corresponds to what Vec<T> does
> with IoRaw being like NonNull<T> and Io<'a, S> being like &'a T.
>
> [1]: https://lore.kernel.org/all/20241024-topic-panthor-rs-platform_io_support-v1-1-3d1addd96e30@collabora.com/
What I was trying to say in my last message is that the wrapper type, i.e.:
IoMem and so on, should not have a lifetime parameter, but IIUC this is not
what you’re suggesting here, right?
— Daniel
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
` (17 preceding siblings ...)
2024-10-25 5:15 ` Dirk Behme
@ 2024-11-16 14:32 ` Janne Grunau
2024-11-16 14:50 ` Greg KH
18 siblings, 1 reply; 88+ messages in thread
From: Janne Grunau @ 2024-11-16 14:32 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, asahi
On Tue, Oct 22, 2024 at 11:31:37PM +0200, Danilo Krummrich wrote:
> This patch series implements the necessary Rust abstractions to implement
> device drivers in Rust.
>
> This includes some basic generalizations for driver registration, handling of ID
> tables, MMIO operations and device resource handling.
>
> Those generalizations are used to implement device driver support for two
> busses, the PCI and platfrom bus (with OF IDs) in order to provide some evidence
> that the generalizations work as intended.
>
> The patch series also includes two patches adding two driver samples, one PCI
> driver and one platform driver.
>
> The PCI bits are motivated by the Nova driver project [1], but are used by at
> least one more OOT driver (rnvme [2]).
>
> The platform bits, besides adding some more evidence to the base abstractions,
> are required by a few more OOT drivers aiming at going upstream, i.e. rvkms [3],
> cpufreq-dt [4], asahi [5] and the i2c work from Fabien [6].
A rebase of the asahi driver onto this series still probes the platform
device and the driver works as expected.
Feel free to add
Tested-by: Janne Grunau <j@jannau>
We plan to import this series for the Asahi Linux downstream kernel
starting with v6.12 and replace the old rust-for-linux Device/Driver
abstractions with this.
Janne
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions
2024-11-16 14:32 ` Janne Grunau
@ 2024-11-16 14:50 ` Greg KH
0 siblings, 0 replies; 88+ messages in thread
From: Greg KH @ 2024-11-16 14:50 UTC (permalink / raw)
To: Janne Grunau
Cc: Danilo Krummrich, rafael, bhelgaas, ojeda, alex.gaynor,
boqun.feng, gary, bjorn3_gh, benno.lossin, tmgross, a.hindborg,
aliceryhl, airlied, fujita.tomonori, lina, pstanner, ajanulgu,
lyude, robh, daniel.almeida, saravanak, rust-for-linux,
linux-kernel, linux-pci, devicetree, asahi
On Sat, Nov 16, 2024 at 03:32:40PM +0100, Janne Grunau wrote:
> On Tue, Oct 22, 2024 at 11:31:37PM +0200, Danilo Krummrich wrote:
> > This patch series implements the necessary Rust abstractions to implement
> > device drivers in Rust.
> >
> > This includes some basic generalizations for driver registration, handling of ID
> > tables, MMIO operations and device resource handling.
> >
> > Those generalizations are used to implement device driver support for two
> > busses, the PCI and platfrom bus (with OF IDs) in order to provide some evidence
> > that the generalizations work as intended.
> >
> > The patch series also includes two patches adding two driver samples, one PCI
> > driver and one platform driver.
> >
> > The PCI bits are motivated by the Nova driver project [1], but are used by at
> > least one more OOT driver (rnvme [2]).
> >
> > The platform bits, besides adding some more evidence to the base abstractions,
> > are required by a few more OOT drivers aiming at going upstream, i.e. rvkms [3],
> > cpufreq-dt [4], asahi [5] and the i2c work from Fabien [6].
>
> A rebase of the asahi driver onto this series still probes the platform
> device and the driver works as expected.
>
> Feel free to add
> Tested-by: Janne Grunau <j@jannau>
>
> We plan to import this series for the Asahi Linux downstream kernel
> starting with v6.12 and replace the old rust-for-linux Device/Driver
> abstractions with this.
Great! I'll wait for the next respin of this as it seems there's been a
lot of review already, and I've taken some of the patches already, so
odds are after 6.13-rc1 is out the series can get a lot smaller.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 05/16] rust: implement `IdArray`, `IdTable` and `RawDeviceId`
2024-10-22 21:31 ` [PATCH v3 05/16] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
@ 2024-11-25 13:42 ` Miguel Ojeda
0 siblings, 0 replies; 88+ messages in thread
From: Miguel Ojeda @ 2024-11-25 13:42 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho, Fabien Parent
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> +#![allow(stable_features)]
This should be possible to remove (starting with v6.11 we do this in
the command line).
> +// Stable in Rust 1.83
> +#![feature(const_mut_refs)]
> +#![feature(const_ptr_write)]
> +#![feature(const_maybe_uninit_as_mut_ptr)]
`const_refs_to_cell` is also stable in 1.83, so you could move it also here.
Having said that, to be consistent, I would just put them above sorted
with the rest -- the compiler can tell us and we track this elsewhere
(just added the last two here to our issue #2 list). Either way, it is
not a big deal, this list will be going away soon and we can
celebrate! :)
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-30 12:23 ` Rob Herring
@ 2024-11-26 12:39 ` Danilo Krummrich
2024-11-26 14:44 ` Rob Herring
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-11-26 12:39 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Wed, Oct 30, 2024 at 07:23:47AM -0500, Rob Herring wrote:
> On Mon, Oct 28, 2024 at 5:15 AM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Wed, Oct 23, 2024 at 09:23:55AM -0500, Rob Herring wrote:
> > > On Wed, Oct 23, 2024 at 08:44:42AM +0200, Danilo Krummrich wrote:
> > > > On Tue, Oct 22, 2024 at 06:47:12PM -0500, Rob Herring wrote:
> > > > > On Tue, Oct 22, 2024 at 11:31:52PM +0200, Danilo Krummrich wrote:
> > > > > > +/// ]
> > > > > > +/// );
> > > > > > +///
> > > > > > +/// impl platform::Driver for MyDriver {
> > > > > > +/// type IdInfo = ();
> > > > > > +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> > > > > > +///
> > > > > > +/// fn probe(
> > > > > > +/// _pdev: &mut platform::Device,
> > > > > > +/// _id_info: Option<&Self::IdInfo>,
> > > > > > +/// ) -> Result<Pin<KBox<Self>>> {
> > > > > > +/// Err(ENODEV)
> > > > > > +/// }
> > > > > > +/// }
> > > > > > +///```
> > > > > > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > > > > > +/// the `Adapter` documentation for an example.
> > > > > > +pub trait Driver {
> > > > > > + /// The type holding information about each device id supported by the driver.
> > > > > > + ///
> > > > > > + /// TODO: Use associated_type_defaults once stabilized:
> > > > > > + ///
> > > > > > + /// type IdInfo: 'static = ();
> > > > > > + type IdInfo: 'static;
> > > > > > +
> > > > > > + /// The table of device ids supported by the driver.
> > > > > > + const ID_TABLE: IdTable<Self::IdInfo>;
> > >
> > > Another thing. I don't think this is quite right. Well, this part is
> > > fine, but assigning the DT table to it is not. The underlying C code has
> > > 2 id tables in struct device_driver (DT and ACPI) and then the bus
> > > specific one in the struct ${bus}_driver.
> >
> > The assignment of this table in `Adapter::register` looks like this:
> >
> > `pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();`
> >
> > What do you think is wrong with this assignment?
>
> Every bus implementation will need the DT and ACPI tables, so they
> should not be declared and assigned in platform driver code, but in
> the generic device/driver abstractions just like the underlying C
> code. The one here should be for platform_device_id. You could put all
> 3 tables here, but that's going to be a lot of duplication I think.
That's indeed true. But I'm not sure that at this point we need a generalized
`Driver` abstraction just for assigning the DT and ACPI tables.
Maybe it's better to do this in a subsequent series?
>
> > >
> > > > > > +
> > > > > > + /// Platform driver probe.
> > > > > > + ///
> > > > > > + /// Called when a new platform device is added or discovered.
> > > > > > + /// Implementers should attempt to initialize the device here.
> > > > > > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> > > > > > +
> > > > > > + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> > > > > > + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
> > > > >
> > > > > Is this visible to drivers? It shouldn't be.
> > > >
> > > > Yeah, I think we should just remove it. Looking at struct of_device_id, it
> > > > doesn't contain any useful information for a driver. I think when I added this I
> > > > was a bit in "autopilot" mode from the PCI stuff, where struct pci_device_id is
> > > > useful to drivers.
> > >
> > > TBC, you mean other than *data, right? If so, I agree.
> >
> > Yes.
> >
> > >
> > > The DT type and name fields are pretty much legacy, so I don't think the
> > > rust bindings need to worry about them until someone converts Sparc and
> > > PowerMac drivers to rust (i.e. never).
> > >
> > > I would guess the PCI cases might be questionable, too. Like DT, drivers
> > > may be accessing the table fields, but that's not best practice. All the
> > > match fields are stored in pci_dev, so why get them from the match
> > > table?
> >
> > Fair question, I'd like to forward it to Greg. IIRC, he explicitly requested to
> > make the corresponding struct pci_device_id available in probe() at Kangrejos.
>
> Which table gets passed in though? Is the IdInfo parameter generic and
> can be platform_device_id, of_device_id or acpi_device_id? Not sure if
> that's possible in rust or not.
Not sure I can follow you here.
The `IdInfo` parameter is of a type given by the driver for driver specific data
for a certain ID table entry.
It's analogue to resolving `pci_device_id::driver_data` in C.
>
> PCI is the exception, not the rule here, in that it only matches with
> pci_device_id. At least I think that is the case currently, but it is
> entirely possible we may want to do ACPI/DT matching like every other
> bus. There are cases where PCI devices are described in DT.
>
> Rob
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions
2024-10-27 22:42 ` Boqun Feng
2024-10-28 10:21 ` Danilo Krummrich
@ 2024-11-26 14:06 ` Danilo Krummrich
1 sibling, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-11-26 14:06 UTC (permalink / raw)
To: Boqun Feng
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, gary, bjorn3_gh,
benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Sun, Oct 27, 2024 at 03:42:41PM -0700, Boqun Feng wrote:
> Hi Danilo,
>
> On Tue, Oct 22, 2024 at 11:31:48PM +0200, Danilo Krummrich wrote:
> [...]
> > +/// The PCI device representation.
> > +///
> > +/// A PCI device is based on an always reference counted `device:Device` instance. Cloning a PCI
> > +/// device, hence, also increments the base device' reference count.
> > +#[derive(Clone)]
> > +pub struct Device(ARef<device::Device>);
> > +
>
> Similar to https://lore.kernel.org/rust-for-linux/ZgG7TlybSa00cuoy@boqun-archlinux/
>
> Could you also avoid wrapping a point to a PCI device? Instead, wrap the
> object type:
It's not wrapping a pointer, but an `ARef<device::Device>`.
>
> #[repr(transparent)]
> pub struct Device(Opaque<bindings::pci_dev>);
>
> impl AlwaysRefCounted for Device {
> <put_device() and get_device() on ->dev>
> }
This implementation is currently implicit, since `pci::Device` just wraps an
`ARef<device::Device>` (like any other bus specific device structure does), and
hence increments and decrements the reference count of the underlying `struct
device` automatically.
However, what I dislike about it is that with that, `pci::Device` behaves like
an `ARef<T>`, but isn't wrapped by `ARef` itself.
Just doing what you proposed is probably cleaner is this aspect, but generates a
bit of duplicated code in reference counting the underlying `struct device`.
- Danilo
>
> Regards,
> Boqun
>
> > +impl Device {
> > + /// Create a PCI Device instance from an existing `device::Device`.
> > + ///
> > + /// # Safety
> > + ///
> > + /// `dev` must be an `ARef<device::Device>` whose underlying `bindings::device` is a member of
> > + /// a `bindings::pci_dev`.
> > + pub unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
> > + Self(dev)
> > + }
> > +
> > + fn as_raw(&self) -> *mut bindings::pci_dev {
> > + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
> > + // embedded in `struct pci_dev`.
> > + unsafe { container_of!(self.0.as_raw(), bindings::pci_dev, dev) as _ }
> > + }
> > +
> > + /// Enable memory resources for this device.
> > + pub fn enable_device_mem(&self) -> Result {
> > + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
> > + let ret = unsafe { bindings::pci_enable_device_mem(self.as_raw()) };
> > + if ret != 0 {
> > + Err(Error::from_errno(ret))
> > + } else {
> > + Ok(())
> > + }
> > + }
> > +
> > + /// Enable bus-mastering for this device.
> > + pub fn set_master(&self) {
> > + // SAFETY: `self.as_raw` is guaranteed to be a pointer to a valid `struct pci_dev`.
> > + unsafe { bindings::pci_set_master(self.as_raw()) };
> > + }
> > +}
> > +
> > +impl AsRef<device::Device> for Device {
> > + fn as_ref(&self) -> &device::Device {
> > + &self.0
> > + }
> > +}
> > --
> > 2.46.2
> >
> >
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-31 8:23 ` Alice Ryhl
@ 2024-11-26 14:13 ` Danilo Krummrich
0 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-11-26 14:13 UTC (permalink / raw)
To: Alice Ryhl
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Thu, Oct 31, 2024 at 09:23:58AM +0100, Alice Ryhl wrote:
> On Wed, Oct 30, 2024 at 7:07 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Wed, Oct 30, 2024 at 04:50:43PM +0100, Alice Ryhl wrote:
> > > On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > > > +/// the `Adapter` documentation for an example.
> > > > +pub trait Driver {
> > > > + /// The type holding information about each device id supported by the driver.
> > > > + ///
> > > > + /// TODO: Use associated_type_defaults once stabilized:
> > > > + ///
> > > > + /// type IdInfo: 'static = ();
> > > > + type IdInfo: 'static;
> > > > +
> > > > + /// The table of device ids supported by the driver.
> > > > + const ID_TABLE: IdTable<Self::IdInfo>;
> > > > +
> > > > + /// Platform driver probe.
> > > > + ///
> > > > + /// Called when a new platform device is added or discovered.
> > > > + /// Implementers should attempt to initialize the device here.
> > > > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> > >
> > > This forces the user to put their driver data in a KBox, but they
> > > might want to use an Arc instead. You don't actually *need* a KBox -
> > > any ForeignOwnable seems to fit your purposes.
> >
> > This is intentional, I do need a `KBox` here.
> >
> > The reason is that I want to enforce that the returned `Pin<KBox<Self>>` has
> > exactly the lifetime of the binding of the device and driver, i.e. from probe()
> > until remove(). This is the lifetime the structure should actually represent.
> >
> > This way we can attach things like `Registration` objects to this structure, or
> > anything else that should only exist from probe() until remove().
> >
> > If a driver needs some private driver data that needs to be reference counted,
> > it is usually attached to the class representation of the driver.
> >
> > For instance, in Nova the reference counted stuff is attached to the DRM device
> > and then I just have the DRM device (which itself is reference counted) embedded
> > in the `Driver` structure.
> >
> > In any case, drivers can always embed a separate `Arc` in their `Driver`
> > structure if they really have a need for that.
>
> Is this needed for soundness of those registrations?
Yes, we must ensure that the class is unregistered before the driver is removed.
However, I just noticed that by letting class abstractions return the
`Registration` without being wrapped as `Devres<Registration>` the driver can
theoretically still stuff them in whatever other structure and keep the
registration alive.
So, maybe there is no way around `Devres` even for class registrations.
>
> Also, I've often seen drivers use devm_kzalloc or similar to allocate
> exactly this object. KBox doesn't allow for that.
>
>
> Alice
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-11-26 12:39 ` Danilo Krummrich
@ 2024-11-26 14:44 ` Rob Herring
2024-11-26 15:17 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Rob Herring @ 2024-11-26 14:44 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Nov 26, 2024 at 6:39 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Oct 30, 2024 at 07:23:47AM -0500, Rob Herring wrote:
> > On Mon, Oct 28, 2024 at 5:15 AM Danilo Krummrich <dakr@kernel.org> wrote:
> > >
> > > On Wed, Oct 23, 2024 at 09:23:55AM -0500, Rob Herring wrote:
> > > > On Wed, Oct 23, 2024 at 08:44:42AM +0200, Danilo Krummrich wrote:
> > > > > On Tue, Oct 22, 2024 at 06:47:12PM -0500, Rob Herring wrote:
> > > > > > On Tue, Oct 22, 2024 at 11:31:52PM +0200, Danilo Krummrich wrote:
> > > > > > > +/// ]
> > > > > > > +/// );
> > > > > > > +///
> > > > > > > +/// impl platform::Driver for MyDriver {
> > > > > > > +/// type IdInfo = ();
> > > > > > > +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> > > > > > > +///
> > > > > > > +/// fn probe(
> > > > > > > +/// _pdev: &mut platform::Device,
> > > > > > > +/// _id_info: Option<&Self::IdInfo>,
> > > > > > > +/// ) -> Result<Pin<KBox<Self>>> {
> > > > > > > +/// Err(ENODEV)
> > > > > > > +/// }
> > > > > > > +/// }
> > > > > > > +///```
> > > > > > > +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> > > > > > > +/// the `Adapter` documentation for an example.
> > > > > > > +pub trait Driver {
> > > > > > > + /// The type holding information about each device id supported by the driver.
> > > > > > > + ///
> > > > > > > + /// TODO: Use associated_type_defaults once stabilized:
> > > > > > > + ///
> > > > > > > + /// type IdInfo: 'static = ();
> > > > > > > + type IdInfo: 'static;
> > > > > > > +
> > > > > > > + /// The table of device ids supported by the driver.
> > > > > > > + const ID_TABLE: IdTable<Self::IdInfo>;
> > > >
> > > > Another thing. I don't think this is quite right. Well, this part is
> > > > fine, but assigning the DT table to it is not. The underlying C code has
> > > > 2 id tables in struct device_driver (DT and ACPI) and then the bus
> > > > specific one in the struct ${bus}_driver.
> > >
> > > The assignment of this table in `Adapter::register` looks like this:
> > >
> > > `pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();`
> > >
> > > What do you think is wrong with this assignment?
> >
> > Every bus implementation will need the DT and ACPI tables, so they
> > should not be declared and assigned in platform driver code, but in
> > the generic device/driver abstractions just like the underlying C
> > code. The one here should be for platform_device_id. You could put all
> > 3 tables here, but that's going to be a lot of duplication I think.
>
> That's indeed true. But I'm not sure that at this point we need a generalized
> `Driver` abstraction just for assigning the DT and ACPI tables.
Why not? Practically *every* non-discoverable bus type needs that.
That's essentially everything except USB and PCI.
> Maybe it's better to do this in a subsequent series?
Sure, but only because there will be a limited number of users to fix.
It looks to me like things are designed for exactly 1 IdInfo
type/table per driver and that's not a correct assumption.
> > > > > > > +
> > > > > > > + /// Platform driver probe.
> > > > > > > + ///
> > > > > > > + /// Called when a new platform device is added or discovered.
> > > > > > > + /// Implementers should attempt to initialize the device here.
> > > > > > > + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> > > > > > > +
> > > > > > > + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> > > > > > > + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
> > > > > >
> > > > > > Is this visible to drivers? It shouldn't be.
> > > > >
> > > > > Yeah, I think we should just remove it. Looking at struct of_device_id, it
> > > > > doesn't contain any useful information for a driver. I think when I added this I
> > > > > was a bit in "autopilot" mode from the PCI stuff, where struct pci_device_id is
> > > > > useful to drivers.
> > > >
> > > > TBC, you mean other than *data, right? If so, I agree.
> > >
> > > Yes.
> > >
> > > >
> > > > The DT type and name fields are pretty much legacy, so I don't think the
> > > > rust bindings need to worry about them until someone converts Sparc and
> > > > PowerMac drivers to rust (i.e. never).
> > > >
> > > > I would guess the PCI cases might be questionable, too. Like DT, drivers
> > > > may be accessing the table fields, but that's not best practice. All the
> > > > match fields are stored in pci_dev, so why get them from the match
> > > > table?
> > >
> > > Fair question, I'd like to forward it to Greg. IIRC, he explicitly requested to
> > > make the corresponding struct pci_device_id available in probe() at Kangrejos.
Making it available is not necessarily the same thing as passing it in
via probe. I agree it may need to be available in probe(), but that
can be an explicit call to get it.
> > Which table gets passed in though? Is the IdInfo parameter generic and
> > can be platform_device_id, of_device_id or acpi_device_id? Not sure if
> > that's possible in rust or not.
>
> Not sure I can follow you here.
>
> The `IdInfo` parameter is of a type given by the driver for driver specific data
> for a certain ID table entry.
>
> It's analogue to resolving `pci_device_id::driver_data` in C.
As I said below, the PCI case is simpler than for platform devices.
Platform devices have 3 possible match tables. The *_device_id type we
end up with is determined at runtime (because matching is done at
runtime), so IdInfo could be any of those 3 types. Is the exact type
opaque to probe() and will that magically work in rust? Or do we need
to pass in the 'driver_data' ptr (or reference) itself? The matched
driver data is generally all the driver needs or cares about. We can
probably assume that it is the same type no matter which match table
is used whether it is platform_device_id::driver_data,
of_device_id::data, or acpi_device_id::driver_data. Nothing in the C
API guarantees that, but that's just best practice. Best practice in C
looks like this:
my_probe()
{
struct my_driver_data *data = device_get_match_data();
...
}
device_get_match_data() is just a wrapper to handle the 3 possible match tables.
The decision for rust is whether we pass in "data" to probe or have an
explicit call. There is a need to get to the *_device_id entry, but
that's the exception. I would go as far as saying we may never need
that in rust drivers.
Rob
> > PCI is the exception, not the rule here, in that it only matches with
> > pci_device_id. At least I think that is the case currently, but it is
> > entirely possible we may want to do ACPI/DT matching like every other
> > bus. There are cases where PCI devices are described in DT.
> >
> > Rob
> >
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-11-26 14:44 ` Rob Herring
@ 2024-11-26 15:17 ` Danilo Krummrich
2024-11-26 19:15 ` Rob Herring
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-11-26 15:17 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Nov 26, 2024 at 08:44:19AM -0600, Rob Herring wrote:
> > > > > The DT type and name fields are pretty much legacy, so I don't think the
> > > > > rust bindings need to worry about them until someone converts Sparc and
> > > > > PowerMac drivers to rust (i.e. never).
> > > > >
> > > > > I would guess the PCI cases might be questionable, too. Like DT, drivers
> > > > > may be accessing the table fields, but that's not best practice. All the
> > > > > match fields are stored in pci_dev, so why get them from the match
> > > > > table?
> > > >
> > > > Fair question, I'd like to forward it to Greg. IIRC, he explicitly requested to
> > > > make the corresponding struct pci_device_id available in probe() at Kangrejos.
>
> Making it available is not necessarily the same thing as passing it in
> via probe.
IIRC, that was exactly the request.
> I agree it may need to be available in probe(), but that
> can be an explicit call to get it.
Sure, I did exactly that for the platform abstraction, because there we may
probe through different ID tables.
A `struct pci_driver`'s probe function has the following signature [1] though:
`int (*probe)(struct pci_dev *dev, const struct pci_device_id *id)`
[1] https://elixir.bootlin.com/linux/v6.12/source/include/linux/pci.h#L950
>
> > > Which table gets passed in though? Is the IdInfo parameter generic and
> > > can be platform_device_id, of_device_id or acpi_device_id? Not sure if
> > > that's possible in rust or not.
> >
> > Not sure I can follow you here.
> >
> > The `IdInfo` parameter is of a type given by the driver for driver specific data
> > for a certain ID table entry.
> >
> > It's analogue to resolving `pci_device_id::driver_data` in C.
>
> As I said below, the PCI case is simpler than for platform devices.
> Platform devices have 3 possible match tables. The *_device_id type we
> end up with is determined at runtime (because matching is done at
> runtime), so IdInfo could be any of those 3 types.
`IdInfo` is *not* any of the three *_device_id types. It's the type of the
drivers private data associated with an entry of any of the three ID tables.
It is true that a driver, which registers multiple out of those three tables is
currently forced to have the same private data type for all of them.
I don't think this is a concern, is it? If so, it's easily resolvable by just
adding two more associated types, e.g. `PlatformIdInfo`, `DtIdInfo` and
`AcpiIdInfo`.
In this case we would indeed need accessor functions like `dt_match_data`,
`platform_match_data`, `acpi_match_data`, since we don't know the type at
compile time anymore.
I don't think that's necessary though.
> Is the exact type
> opaque to probe() and will that magically work in rust? Or do we need
> to pass in the 'driver_data' ptr (or reference) itself? The matched
> driver data is generally all the driver needs or cares about. We can
> probably assume that it is the same type no matter which match table
> is used whether it is platform_device_id::driver_data,
> of_device_id::data, or acpi_device_id::driver_data. Nothing in the C
> API guarantees that, but that's just best practice. Best practice in C
> looks like this:
>
> my_probe()
> {
> struct my_driver_data *data = device_get_match_data();
> ...
> }
>
> device_get_match_data() is just a wrapper to handle the 3 possible match tables.
>
> The decision for rust is whether we pass in "data" to probe or have an
> explicit call. There is a need to get to the *_device_id entry, but
> that's the exception. I would go as far as saying we may never need
> that in rust drivers.
>
> Rob
>
> > > PCI is the exception, not the rule here, in that it only matches with
> > > pci_device_id. At least I think that is the case currently, but it is
> > > entirely possible we may want to do ACPI/DT matching like every other
> > > bus. There are cases where PCI devices are described in DT.
> > >
> > > Rob
> > >
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-11-26 15:17 ` Danilo Krummrich
@ 2024-11-26 19:15 ` Rob Herring
2024-11-26 20:01 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Rob Herring @ 2024-11-26 19:15 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Nov 26, 2024 at 9:17 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Tue, Nov 26, 2024 at 08:44:19AM -0600, Rob Herring wrote:
> > > > > > The DT type and name fields are pretty much legacy, so I don't think the
> > > > > > rust bindings need to worry about them until someone converts Sparc and
> > > > > > PowerMac drivers to rust (i.e. never).
> > > > > >
> > > > > > I would guess the PCI cases might be questionable, too. Like DT, drivers
> > > > > > may be accessing the table fields, but that's not best practice. All the
> > > > > > match fields are stored in pci_dev, so why get them from the match
> > > > > > table?
> > > > >
> > > > > Fair question, I'd like to forward it to Greg. IIRC, he explicitly requested to
> > > > > make the corresponding struct pci_device_id available in probe() at Kangrejos.
> >
> > Making it available is not necessarily the same thing as passing it in
> > via probe.
>
> IIRC, that was exactly the request.
>
> > I agree it may need to be available in probe(), but that
> > can be an explicit call to get it.
>
> Sure, I did exactly that for the platform abstraction, because there we may
> probe through different ID tables.
TBC, I think of_match_device() (both calling the C API and the method)
should not be part of this series. I think we agreed on that already.
Only if there is a need at some point later should we add it.
> A `struct pci_driver`'s probe function has the following signature [1] though:
>
> `int (*probe)(struct pci_dev *dev, const struct pci_device_id *id)`
>
> [1] https://elixir.bootlin.com/linux/v6.12/source/include/linux/pci.h#L950
We have a mixture of probe with and without the _device_id parameter.
I'd question if we really want to keep that for PCI when we have a
chance to align things with Rust. We can't really with C as it would
be too many drivers to change. Passing the _device_id only works if
firmware matching is never used which can change over time. But if
aligning things is not something we want to do, then I'll shut up.
> > > > Which table gets passed in though? Is the IdInfo parameter generic and
> > > > can be platform_device_id, of_device_id or acpi_device_id? Not sure if
> > > > that's possible in rust or not.
> > >
> > > Not sure I can follow you here.
> > >
> > > The `IdInfo` parameter is of a type given by the driver for driver specific data
> > > for a certain ID table entry.
> > >
> > > It's analogue to resolving `pci_device_id::driver_data` in C.
> >
> > As I said below, the PCI case is simpler than for platform devices.
> > Platform devices have 3 possible match tables. The *_device_id type we
> > end up with is determined at runtime (because matching is done at
> > runtime), so IdInfo could be any of those 3 types.
>
> `IdInfo` is *not* any of the three *_device_id types. It's the type of the
> drivers private data associated with an entry of any of the three ID tables.
Ah yes, indeed. So no issue with the probe method.
> It is true that a driver, which registers multiple out of those three tables is
> currently forced to have the same private data type for all of them.
I think that's a feature actually as it enforces best practices.
> I don't think this is a concern, is it? If so, it's easily resolvable by just
> adding two more associated types, e.g. `PlatformIdInfo`, `DtIdInfo` and
> `AcpiIdInfo`.
>
> In this case we would indeed need accessor functions like `dt_match_data`,
> `platform_match_data`, `acpi_match_data`, since we don't know the type at
> compile time anymore.
Do we need to split those out in rust or can we just call
device_get_match_data()?
>
> I don't think that's necessary though.
Even if you don't support all 3 tables now, at a minimum I think you
need to rename things to be clear what table type is supported and
allow for adding the other types. For example, T::ID_TABLE needs to be
renamed to be clear it's the of_device_id table.
Rob
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-11-26 19:15 ` Rob Herring
@ 2024-11-26 20:01 ` Danilo Krummrich
0 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-11-26 20:01 UTC (permalink / raw)
To: Rob Herring
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, daniel.almeida,
saravanak, rust-for-linux, linux-kernel, linux-pci, devicetree
On Tue, Nov 26, 2024 at 01:15:59PM -0600, Rob Herring wrote:
> On Tue, Nov 26, 2024 at 9:17 AM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Tue, Nov 26, 2024 at 08:44:19AM -0600, Rob Herring wrote:
> > > > > > > The DT type and name fields are pretty much legacy, so I don't think the
> > > > > > > rust bindings need to worry about them until someone converts Sparc and
> > > > > > > PowerMac drivers to rust (i.e. never).
> > > > > > >
> > > > > > > I would guess the PCI cases might be questionable, too. Like DT, drivers
> > > > > > > may be accessing the table fields, but that's not best practice. All the
> > > > > > > match fields are stored in pci_dev, so why get them from the match
> > > > > > > table?
> > > > > >
> > > > > > Fair question, I'd like to forward it to Greg. IIRC, he explicitly requested to
> > > > > > make the corresponding struct pci_device_id available in probe() at Kangrejos.
> > >
> > > Making it available is not necessarily the same thing as passing it in
> > > via probe.
> >
> > IIRC, that was exactly the request.
> >
> > > I agree it may need to be available in probe(), but that
> > > can be an explicit call to get it.
> >
> > Sure, I did exactly that for the platform abstraction, because there we may
> > probe through different ID tables.
>
> TBC, I think of_match_device() (both calling the C API and the method)
> should not be part of this series. I think we agreed on that already.
> Only if there is a need at some point later should we add it.
That matches my understanding.
>
> > A `struct pci_driver`'s probe function has the following signature [1] though:
> >
> > `int (*probe)(struct pci_dev *dev, const struct pci_device_id *id)`
> >
> > [1] https://elixir.bootlin.com/linux/v6.12/source/include/linux/pci.h#L950
>
> We have a mixture of probe with and without the _device_id parameter.
> I'd question if we really want to keep that for PCI when we have a
> chance to align things with Rust. We can't really with C as it would
> be too many drivers to change. Passing the _device_id only works if
> firmware matching is never used which can change over time. But if
> aligning things is not something we want to do, then I'll shut up.
I don't disagree. Again, this one is on Greg to comment on. Personally, I'm
fine with both.
>
> > > > > Which table gets passed in though? Is the IdInfo parameter generic and
> > > > > can be platform_device_id, of_device_id or acpi_device_id? Not sure if
> > > > > that's possible in rust or not.
> > > >
> > > > Not sure I can follow you here.
> > > >
> > > > The `IdInfo` parameter is of a type given by the driver for driver specific data
> > > > for a certain ID table entry.
> > > >
> > > > It's analogue to resolving `pci_device_id::driver_data` in C.
> > >
> > > As I said below, the PCI case is simpler than for platform devices.
> > > Platform devices have 3 possible match tables. The *_device_id type we
> > > end up with is determined at runtime (because matching is done at
> > > runtime), so IdInfo could be any of those 3 types.
> >
> > `IdInfo` is *not* any of the three *_device_id types. It's the type of the
> > drivers private data associated with an entry of any of the three ID tables.
>
> Ah yes, indeed. So no issue with the probe method.
>
> > It is true that a driver, which registers multiple out of those three tables is
> > currently forced to have the same private data type for all of them.
>
> I think that's a feature actually as it enforces best practices.
Agreed.
>
> > I don't think this is a concern, is it? If so, it's easily resolvable by just
> > adding two more associated types, e.g. `PlatformIdInfo`, `DtIdInfo` and
> > `AcpiIdInfo`.
> >
> > In this case we would indeed need accessor functions like `dt_match_data`,
> > `platform_match_data`, `acpi_match_data`, since we don't know the type at
> > compile time anymore.
>
> Do we need to split those out in rust or can we just call
> device_get_match_data()?
We'd need to split them, because they potentially would return different types.
(Again, I don't think that's necessary though.)
For C it's just a void pointer, so we don't bother there, but in Rust we'd want
it type safe.
>
> >
> > I don't think that's necessary though.
>
> Even if you don't support all 3 tables now, at a minimum I think you
> need to rename things to be clear what table type is supported and
> allow for adding the other types. For example, T::ID_TABLE needs to be
> renamed to be clear it's the of_device_id table.
Sure, I already got this on my list of changes for the next version.
>
> Rob
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 10/16] rust: add devres abstraction
2024-10-22 21:31 ` [PATCH v3 10/16] rust: add devres abstraction Danilo Krummrich
2024-10-31 14:03 ` Alice Ryhl
@ 2024-11-27 12:21 ` Alice Ryhl
2024-11-27 13:19 ` Danilo Krummrich
1 sibling, 1 reply; 88+ messages in thread
From: Alice Ryhl @ 2024-11-27 12:21 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> Add a Rust abstraction for the kernel's devres (device resource
> management) implementation.
>
> The Devres type acts as a container to manage the lifetime and
> accessibility of device bound resources. Therefore it registers a
> devres callback and revokes access to the resource on invocation.
>
> Users of the Devres abstraction can simply free the corresponding
> resources in their Drop implementation, which is invoked when either the
> Devres instance goes out of scope or the devres callback leads to the
> resource being revoked, which implies a call to drop_in_place().
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> rust/helpers/device.c | 10 +++
> rust/helpers/helpers.c | 1 +
> rust/kernel/devres.rs | 180 +++++++++++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 1 +
> 5 files changed, 193 insertions(+)
> create mode 100644 rust/helpers/device.c
> create mode 100644 rust/kernel/devres.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0a8882252257..97914d0752fb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6983,6 +6983,7 @@ F: include/linux/property.h
> F: lib/kobj*
> F: rust/kernel/device.rs
> F: rust/kernel/device_id.rs
> +F: rust/kernel/devres.rs
> F: rust/kernel/driver.rs
>
> DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
> diff --git a/rust/helpers/device.c b/rust/helpers/device.c
> new file mode 100644
> index 000000000000..b2135c6686b0
> --- /dev/null
> +++ b/rust/helpers/device.c
> @@ -0,0 +1,10 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/device.h>
> +
> +int rust_helper_devm_add_action(struct device *dev,
> + void (*action)(void *),
> + void *data)
> +{
> + return devm_add_action(dev, action, data);
> +}
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index e2f6b2197061..3acb2b9e52ec 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -11,6 +11,7 @@
> #include "bug.c"
> #include "build_assert.c"
> #include "build_bug.c"
> +#include "device.c"
> #include "err.c"
> #include "io.c"
> #include "kunit.c"
> diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
> new file mode 100644
> index 000000000000..b23559f55214
> --- /dev/null
> +++ b/rust/kernel/devres.rs
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Devres abstraction
> +//!
> +//! [`Devres`] represents an abstraction for the kernel devres (device resource management)
> +//! implementation.
> +
> +use crate::{
> + alloc::Flags,
> + bindings,
> + device::Device,
> + error::{Error, Result},
> + prelude::*,
> + revocable::Revocable,
> + sync::Arc,
> +};
> +
> +use core::ffi::c_void;
> +use core::ops::Deref;
> +
> +#[pin_data]
> +struct DevresInner<T> {
> + #[pin]
> + data: Revocable<T>,
> +}
> +
> +/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
> +/// manage their lifetime.
> +///
> +/// [`Device`] bound resources should be freed when either the resource goes out of scope or the
> +/// [`Device`] is unbound respectively, depending on what happens first.
> +///
> +/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the
> +/// [`Device`] is unbound, revoking access to the encapsulated resource (see also [`Revocable`]).
> +///
> +/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource
> +/// anymore.
> +///
> +/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s
> +/// [`Drop`] implementation.
> +///
> +/// # Example
> +///
> +/// ```no_run
> +/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::Io};
> +/// # use core::ops::Deref;
> +///
> +/// // See also [`pci::Bar`] for a real example.
> +/// struct IoMem<const SIZE: usize>(Io<SIZE>);
> +///
> +/// impl<const SIZE: usize> IoMem<SIZE> {
> +/// /// # Safety
> +/// ///
> +/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
> +/// /// virtual address space.
> +/// unsafe fn new(paddr: usize) -> Result<Self>{
> +///
> +/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
> +/// // valid for `ioremap`.
> +/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE.try_into().unwrap()) };
> +/// if addr.is_null() {
> +/// return Err(ENOMEM);
> +/// }
> +///
> +/// // SAFETY: `addr` is guaranteed to be the start of a valid I/O mapped memory region of
> +/// // size `SIZE`.
> +/// let io = unsafe { Io::new(addr as _, SIZE)? };
> +///
> +/// Ok(IoMem(io))
> +/// }
> +/// }
> +///
> +/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
> +/// fn drop(&mut self) {
> +/// // SAFETY: Safe as by the invariant of `Io`.
> +/// unsafe { bindings::iounmap(self.0.base_addr() as _); };
> +/// }
> +/// }
> +///
> +/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
> +/// type Target = Io<SIZE>;
> +///
> +/// fn deref(&self) -> &Self::Target {
> +/// &self.0
> +/// }
> +/// }
> +///
> +/// # fn no_run() -> Result<(), Error> {
> +/// # // SAFETY: Invalid usage; just for the example to get an `ARef<Device>` instance.
> +/// # let dev = unsafe { Device::from_raw(core::ptr::null_mut()) };
> +///
> +/// // SAFETY: Invalid usage for example purposes.
> +/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
> +/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?;
> +///
> +/// let res = devres.try_access().ok_or(ENXIO)?;
> +/// res.writel(0x42, 0x0);
> +/// # Ok(())
> +/// # }
> +/// ```
> +pub struct Devres<T>(Arc<DevresInner<T>>);
> +
> +impl<T> DevresInner<T> {
> + fn new(dev: &Device, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
> + let inner = Arc::pin_init(
> + pin_init!( DevresInner {
> + data <- Revocable::new(data),
> + }),
> + flags,
> + )?;
> +
> + // Convert `Arc<DevresInner>` into a raw pointer and make devres own this reference until
> + // `Self::devres_callback` is called.
> + let data = inner.clone().into_raw();
> +
> + // SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is
> + // detached.
> + let ret = unsafe {
> + bindings::devm_add_action(dev.as_raw(), Some(Self::devres_callback), data as _)
> + };
> +
> + if ret != 0 {
> + // SAFETY: We just created another reference to `inner` in order to pass it to
> + // `bindings::devm_add_action`. If `bindings::devm_add_action` fails, we have to drop
> + // this reference accordingly.
> + let _ = unsafe { Arc::from_raw(data) };
> + return Err(Error::from_errno(ret));
> + }
> +
> + Ok(inner)
> + }
> +
> + #[allow(clippy::missing_safety_doc)]
> + unsafe extern "C" fn devres_callback(ptr: *mut c_void) {
> + let ptr = ptr as *mut DevresInner<T>;
> + // Devres owned this memory; now that we received the callback, drop the `Arc` and hence the
> + // reference.
> + // SAFETY: Safe, since we leaked an `Arc` reference to devm_add_action() in
> + // `DevresInner::new`.
> + let inner = unsafe { Arc::from_raw(ptr) };
> +
> + inner.data.revoke();
> + }
> +}
> +
> +impl<T> Devres<T> {
> + /// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the
> + /// returned `Devres` instance' `data` will be revoked once the device is detached.
> + pub fn new(dev: &Device, data: T, flags: Flags) -> Result<Self> {
> + let inner = DevresInner::new(dev, data, flags)?;
> +
> + Ok(Devres(inner))
> + }
> +
> + /// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data`
> + /// is owned by devres and will be revoked / dropped, once the device is detached.
> + pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result {
> + let _ = DevresInner::new(dev, data, flags)?;
> +
> + Ok(())
> + }
> +}
> +
> +impl<T> Deref for Devres<T> {
> + type Target = Revocable<T>;
> +
> + fn deref(&self) -> &Self::Target {
> + &self.0.data
> + }
> +}
> +
> +impl<T> Drop for Devres<T> {
> + fn drop(&mut self) {
> + // Revoke the data, such that it gets dropped already and the actual resource is freed.
> + // `DevresInner` has to stay alive until the devres callback has been called. This is
> + // necessary since we don't know when `Devres` is dropped and calling
> + // `devm_remove_action()` instead could race with `devres_release_all()`.
> + self.revoke();
When the destructor runs, it's guaranteed that nobody is accessing the
inner resource since the only way to do that is through the Devres
handle, but its destructor is running. Therefore, you can skip the
synchronize_rcu() call in this case.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 10/16] rust: add devres abstraction
2024-11-27 12:21 ` Alice Ryhl
@ 2024-11-27 13:19 ` Danilo Krummrich
2024-11-27 13:20 ` Alice Ryhl
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-11-27 13:19 UTC (permalink / raw)
To: Alice Ryhl
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On 11/27/24 1:21 PM, Alice Ryhl wrote:
> On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> Add a Rust abstraction for the kernel's devres (device resource
>> management) implementation.
>>
>> The Devres type acts as a container to manage the lifetime and
>> accessibility of device bound resources. Therefore it registers a
>> devres callback and revokes access to the resource on invocation.
>>
>> Users of the Devres abstraction can simply free the corresponding
>> resources in their Drop implementation, which is invoked when either the
>> Devres instance goes out of scope or the devres callback leads to the
>> resource being revoked, which implies a call to drop_in_place().
>>
>> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>> ---
>> MAINTAINERS | 1 +
>> rust/helpers/device.c | 10 +++
>> rust/helpers/helpers.c | 1 +
>> rust/kernel/devres.rs | 180 +++++++++++++++++++++++++++++++++++++++++
>> rust/kernel/lib.rs | 1 +
>> 5 files changed, 193 insertions(+)
>> create mode 100644 rust/helpers/device.c
>> create mode 100644 rust/kernel/devres.rs
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 0a8882252257..97914d0752fb 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6983,6 +6983,7 @@ F: include/linux/property.h
>> F: lib/kobj*
>> F: rust/kernel/device.rs
>> F: rust/kernel/device_id.rs
>> +F: rust/kernel/devres.rs
>> F: rust/kernel/driver.rs
>>
>> DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
>> diff --git a/rust/helpers/device.c b/rust/helpers/device.c
>> new file mode 100644
>> index 000000000000..b2135c6686b0
>> --- /dev/null
>> +++ b/rust/helpers/device.c
>> @@ -0,0 +1,10 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +#include <linux/device.h>
>> +
>> +int rust_helper_devm_add_action(struct device *dev,
>> + void (*action)(void *),
>> + void *data)
>> +{
>> + return devm_add_action(dev, action, data);
>> +}
>> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
>> index e2f6b2197061..3acb2b9e52ec 100644
>> --- a/rust/helpers/helpers.c
>> +++ b/rust/helpers/helpers.c
>> @@ -11,6 +11,7 @@
>> #include "bug.c"
>> #include "build_assert.c"
>> #include "build_bug.c"
>> +#include "device.c"
>> #include "err.c"
>> #include "io.c"
>> #include "kunit.c"
>> diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
>> new file mode 100644
>> index 000000000000..b23559f55214
>> --- /dev/null
>> +++ b/rust/kernel/devres.rs
>> @@ -0,0 +1,180 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +//! Devres abstraction
>> +//!
>> +//! [`Devres`] represents an abstraction for the kernel devres (device resource management)
>> +//! implementation.
>> +
>> +use crate::{
>> + alloc::Flags,
>> + bindings,
>> + device::Device,
>> + error::{Error, Result},
>> + prelude::*,
>> + revocable::Revocable,
>> + sync::Arc,
>> +};
>> +
>> +use core::ffi::c_void;
>> +use core::ops::Deref;
>> +
>> +#[pin_data]
>> +struct DevresInner<T> {
>> + #[pin]
>> + data: Revocable<T>,
>> +}
>> +
>> +/// This abstraction is meant to be used by subsystems to containerize [`Device`] bound resources to
>> +/// manage their lifetime.
>> +///
>> +/// [`Device`] bound resources should be freed when either the resource goes out of scope or the
>> +/// [`Device`] is unbound respectively, depending on what happens first.
>> +///
>> +/// To achieve that [`Devres`] registers a devres callback on creation, which is called once the
>> +/// [`Device`] is unbound, revoking access to the encapsulated resource (see also [`Revocable`]).
>> +///
>> +/// After the [`Devres`] has been unbound it is not possible to access the encapsulated resource
>> +/// anymore.
>> +///
>> +/// [`Devres`] users should make sure to simply free the corresponding backing resource in `T`'s
>> +/// [`Drop`] implementation.
>> +///
>> +/// # Example
>> +///
>> +/// ```no_run
>> +/// # use kernel::{bindings, c_str, device::Device, devres::Devres, io::Io};
>> +/// # use core::ops::Deref;
>> +///
>> +/// // See also [`pci::Bar`] for a real example.
>> +/// struct IoMem<const SIZE: usize>(Io<SIZE>);
>> +///
>> +/// impl<const SIZE: usize> IoMem<SIZE> {
>> +/// /// # Safety
>> +/// ///
>> +/// /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
>> +/// /// virtual address space.
>> +/// unsafe fn new(paddr: usize) -> Result<Self>{
>> +///
>> +/// // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
>> +/// // valid for `ioremap`.
>> +/// let addr = unsafe { bindings::ioremap(paddr as _, SIZE.try_into().unwrap()) };
>> +/// if addr.is_null() {
>> +/// return Err(ENOMEM);
>> +/// }
>> +///
>> +/// // SAFETY: `addr` is guaranteed to be the start of a valid I/O mapped memory region of
>> +/// // size `SIZE`.
>> +/// let io = unsafe { Io::new(addr as _, SIZE)? };
>> +///
>> +/// Ok(IoMem(io))
>> +/// }
>> +/// }
>> +///
>> +/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
>> +/// fn drop(&mut self) {
>> +/// // SAFETY: Safe as by the invariant of `Io`.
>> +/// unsafe { bindings::iounmap(self.0.base_addr() as _); };
>> +/// }
>> +/// }
>> +///
>> +/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
>> +/// type Target = Io<SIZE>;
>> +///
>> +/// fn deref(&self) -> &Self::Target {
>> +/// &self.0
>> +/// }
>> +/// }
>> +///
>> +/// # fn no_run() -> Result<(), Error> {
>> +/// # // SAFETY: Invalid usage; just for the example to get an `ARef<Device>` instance.
>> +/// # let dev = unsafe { Device::from_raw(core::ptr::null_mut()) };
>> +///
>> +/// // SAFETY: Invalid usage for example purposes.
>> +/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
>> +/// let devres = Devres::new(&dev, iomem, GFP_KERNEL)?;
>> +///
>> +/// let res = devres.try_access().ok_or(ENXIO)?;
>> +/// res.writel(0x42, 0x0);
>> +/// # Ok(())
>> +/// # }
>> +/// ```
>> +pub struct Devres<T>(Arc<DevresInner<T>>);
>> +
>> +impl<T> DevresInner<T> {
>> + fn new(dev: &Device, data: T, flags: Flags) -> Result<Arc<DevresInner<T>>> {
>> + let inner = Arc::pin_init(
>> + pin_init!( DevresInner {
>> + data <- Revocable::new(data),
>> + }),
>> + flags,
>> + )?;
>> +
>> + // Convert `Arc<DevresInner>` into a raw pointer and make devres own this reference until
>> + // `Self::devres_callback` is called.
>> + let data = inner.clone().into_raw();
>> +
>> + // SAFETY: `devm_add_action` guarantees to call `Self::devres_callback` once `dev` is
>> + // detached.
>> + let ret = unsafe {
>> + bindings::devm_add_action(dev.as_raw(), Some(Self::devres_callback), data as _)
>> + };
>> +
>> + if ret != 0 {
>> + // SAFETY: We just created another reference to `inner` in order to pass it to
>> + // `bindings::devm_add_action`. If `bindings::devm_add_action` fails, we have to drop
>> + // this reference accordingly.
>> + let _ = unsafe { Arc::from_raw(data) };
>> + return Err(Error::from_errno(ret));
>> + }
>> +
>> + Ok(inner)
>> + }
>> +
>> + #[allow(clippy::missing_safety_doc)]
>> + unsafe extern "C" fn devres_callback(ptr: *mut c_void) {
>> + let ptr = ptr as *mut DevresInner<T>;
>> + // Devres owned this memory; now that we received the callback, drop the `Arc` and hence the
>> + // reference.
>> + // SAFETY: Safe, since we leaked an `Arc` reference to devm_add_action() in
>> + // `DevresInner::new`.
>> + let inner = unsafe { Arc::from_raw(ptr) };
>> +
>> + inner.data.revoke();
>> + }
>> +}
>> +
>> +impl<T> Devres<T> {
>> + /// Creates a new [`Devres`] instance of the given `data`. The `data` encapsulated within the
>> + /// returned `Devres` instance' `data` will be revoked once the device is detached.
>> + pub fn new(dev: &Device, data: T, flags: Flags) -> Result<Self> {
>> + let inner = DevresInner::new(dev, data, flags)?;
>> +
>> + Ok(Devres(inner))
>> + }
>> +
>> + /// Same as [`Devres::new`], but does not return a `Devres` instance. Instead the given `data`
>> + /// is owned by devres and will be revoked / dropped, once the device is detached.
>> + pub fn new_foreign_owned(dev: &Device, data: T, flags: Flags) -> Result {
>> + let _ = DevresInner::new(dev, data, flags)?;
>> +
>> + Ok(())
>> + }
>> +}
>> +
>> +impl<T> Deref for Devres<T> {
>> + type Target = Revocable<T>;
>> +
>> + fn deref(&self) -> &Self::Target {
>> + &self.0.data
>> + }
>> +}
>> +
>> +impl<T> Drop for Devres<T> {
>> + fn drop(&mut self) {
>> + // Revoke the data, such that it gets dropped already and the actual resource is freed.
>> + // `DevresInner` has to stay alive until the devres callback has been called. This is
>> + // necessary since we don't know when `Devres` is dropped and calling
>> + // `devm_remove_action()` instead could race with `devres_release_all()`.
>> + self.revoke();
>
> When the destructor runs, it's guaranteed that nobody is accessing the
> inner resource since the only way to do that is through the Devres
> handle, but its destructor is running. Therefore, you can skip the
> synchronize_rcu() call in this case.
Yeah, I think this optimization should be possible.
We'd require `Revocable` to have a `revoke_nosync` method for that I guess...
>
> Alice
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 10/16] rust: add devres abstraction
2024-11-27 13:19 ` Danilo Krummrich
@ 2024-11-27 13:20 ` Alice Ryhl
0 siblings, 0 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-11-27 13:20 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree
On Wed, Nov 27, 2024 at 2:19 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On 11/27/24 1:21 PM, Alice Ryhl wrote:
> > On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >> +impl<T> Drop for Devres<T> {
> >> + fn drop(&mut self) {
> >> + // Revoke the data, such that it gets dropped already and the actual resource is freed.
> >> + // `DevresInner` has to stay alive until the devres callback has been called. This is
> >> + // necessary since we don't know when `Devres` is dropped and calling
> >> + // `devm_remove_action()` instead could race with `devres_release_all()`.
> >> + self.revoke();
> >
> > When the destructor runs, it's guaranteed that nobody is accessing the
> > inner resource since the only way to do that is through the Devres
> > handle, but its destructor is running. Therefore, you can skip the
> > synchronize_rcu() call in this case.
>
> Yeah, I think this optimization should be possible.
>
> We'd require `Revocable` to have a `revoke_nosync` method for that I guess...
Agreed, you could have an unsafe method for revoking where you assert
that nobody else is accessing the value.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 07/16] rust: add `Revocable` type
2024-10-29 13:26 ` Alice Ryhl
@ 2024-12-03 9:21 ` Danilo Krummrich
2024-12-03 9:24 ` Alice Ryhl
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-12-03 9:21 UTC (permalink / raw)
To: Alice Ryhl
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On 10/29/24 2:26 PM, Alice Ryhl wrote:
> On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>> +/// A guard that allows access to a revocable object and keeps it alive.
>> +///
>> +/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context
>> +/// holding the RCU read-side lock.
>> +///
>> +/// # Invariants
>> +///
>> +/// The RCU read-side lock is held while the guard is alive.
>> +pub struct RevocableGuard<'a, T> {
>> + data_ref: *const T,
>> + _rcu_guard: rcu::Guard,
>> + _p: PhantomData<&'a ()>,
>> +}
>
> Is this needed? Can't all users just use `try_access_with_guard`?
Without this guard, how to we access `T` with just the `rcu::Guard`?
>
> Alice
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 07/16] rust: add `Revocable` type
2024-12-03 9:21 ` Danilo Krummrich
@ 2024-12-03 9:24 ` Alice Ryhl
2024-12-03 9:35 ` Danilo Krummrich
0 siblings, 1 reply; 88+ messages in thread
From: Alice Ryhl @ 2024-12-03 9:24 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On Tue, Dec 3, 2024 at 10:21 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On 10/29/24 2:26 PM, Alice Ryhl wrote:
> > On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >> +/// A guard that allows access to a revocable object and keeps it alive.
> >> +///
> >> +/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context
> >> +/// holding the RCU read-side lock.
> >> +///
> >> +/// # Invariants
> >> +///
> >> +/// The RCU read-side lock is held while the guard is alive.
> >> +pub struct RevocableGuard<'a, T> {
> >> + data_ref: *const T,
> >> + _rcu_guard: rcu::Guard,
> >> + _p: PhantomData<&'a ()>,
> >> +}
> >
> > Is this needed? Can't all users just use `try_access_with_guard`?
>
> Without this guard, how to we access `T` with just the `rcu::Guard`?
I don't think `try_access_with_guard` provides any access that you
can't get by doing `try_access_with_guard`.
That said, I guess this guard functions as a convenience accessors, so
I don't mind it.
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 07/16] rust: add `Revocable` type
2024-12-03 9:24 ` Alice Ryhl
@ 2024-12-03 9:35 ` Danilo Krummrich
2024-12-03 10:58 ` Alice Ryhl
0 siblings, 1 reply; 88+ messages in thread
From: Danilo Krummrich @ 2024-12-03 9:35 UTC (permalink / raw)
To: Alice Ryhl
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On 12/3/24 10:24 AM, Alice Ryhl wrote:
> On Tue, Dec 3, 2024 at 10:21 AM Danilo Krummrich <dakr@kernel.org> wrote:
>>
>> On 10/29/24 2:26 PM, Alice Ryhl wrote:
>>> On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
>>>> +/// A guard that allows access to a revocable object and keeps it alive.
>>>> +///
>>>> +/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context
>>>> +/// holding the RCU read-side lock.
>>>> +///
>>>> +/// # Invariants
>>>> +///
>>>> +/// The RCU read-side lock is held while the guard is alive.
>>>> +pub struct RevocableGuard<'a, T> {
>>>> + data_ref: *const T,
>>>> + _rcu_guard: rcu::Guard,
>>>> + _p: PhantomData<&'a ()>,
>>>> +}
>>>
>>> Is this needed? Can't all users just use `try_access_with_guard`?
>>
>> Without this guard, how to we access `T` with just the `rcu::Guard`?
>
> I don't think `try_access_with_guard` provides any access that you
> can't get by doing `try_access_with_guard`.
>
> That said, I guess this guard functions as a convenience accessors, so
> I don't mind it.
What I mean is, how does the following work without `RevocableGuard`?
```
struct Foo;
impl Foo {
pub fn bar() { ... }
}
let data: Revocable<Foo> = ...;
let guard = data.try_access()?;
guard.bar();
```
>
> Alice
>
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 07/16] rust: add `Revocable` type
2024-12-03 9:35 ` Danilo Krummrich
@ 2024-12-03 10:58 ` Alice Ryhl
0 siblings, 0 replies; 88+ messages in thread
From: Alice Ryhl @ 2024-12-03 10:58 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
daniel.almeida, saravanak, rust-for-linux, linux-kernel,
linux-pci, devicetree, Wedson Almeida Filho
On Tue, Dec 3, 2024 at 10:36 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On 12/3/24 10:24 AM, Alice Ryhl wrote:
> > On Tue, Dec 3, 2024 at 10:21 AM Danilo Krummrich <dakr@kernel.org> wrote:
> >>
> >> On 10/29/24 2:26 PM, Alice Ryhl wrote:
> >>> On Tue, Oct 22, 2024 at 11:33 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >>>> +/// A guard that allows access to a revocable object and keeps it alive.
> >>>> +///
> >>>> +/// CPUs may not sleep while holding on to [`RevocableGuard`] because it's in atomic context
> >>>> +/// holding the RCU read-side lock.
> >>>> +///
> >>>> +/// # Invariants
> >>>> +///
> >>>> +/// The RCU read-side lock is held while the guard is alive.
> >>>> +pub struct RevocableGuard<'a, T> {
> >>>> + data_ref: *const T,
> >>>> + _rcu_guard: rcu::Guard,
> >>>> + _p: PhantomData<&'a ()>,
> >>>> +}
> >>>
> >>> Is this needed? Can't all users just use `try_access_with_guard`?
> >>
> >> Without this guard, how to we access `T` with just the `rcu::Guard`?
> >
> > I don't think `try_access_with_guard` provides any access that you
> > can't get by doing `try_access_with_guard`.
> >
> > That said, I guess this guard functions as a convenience accessors, so
> > I don't mind it.
>
> What I mean is, how does the following work without `RevocableGuard`?
>
> ```
> struct Foo;
>
> impl Foo {
> pub fn bar() { ... }
> }
>
> let data: Revocable<Foo> = ...;
> let guard = data.try_access()?;
>
> guard.bar();
> ```
Is there a reason you can do this?
let guard = rcu::Guard::new();
let value = data.try_access_with_guard(&guard);
value.bar();
Alice
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
` (5 preceding siblings ...)
2024-10-30 15:50 ` Alice Ryhl
@ 2024-12-04 19:25 ` Daniel Almeida
2024-12-04 19:30 ` Danilo Krummrich
6 siblings, 1 reply; 88+ messages in thread
From: Daniel Almeida @ 2024-12-04 19:25 UTC (permalink / raw)
To: Danilo Krummrich
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh, saravanak,
rust-for-linux, linux-kernel, linux-pci, devicetree
Hi Danilo,
> On 22 Oct 2024, at 18:31, Danilo Krummrich <dakr@kernel.org> wrote:
>
> Implement the basic platform bus abstractions required to write a basic
> platform driver. This includes the following data structures:
>
> The `platform::Driver` trait represents the interface to the driver and
> provides `pci::Driver::probe` for the driver to implement.
>
> The `platform::Device` abstraction represents a `struct platform_device`.
>
> In order to provide the platform bus specific parts to a generic
> `driver::Registration` the `driver::RegistrationOps` trait is implemented
> by `platform::Adapter`.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> MAINTAINERS | 1 +
> rust/bindings/bindings_helper.h | 1 +
> rust/helpers/helpers.c | 1 +
> rust/helpers/platform.c | 13 ++
> rust/kernel/lib.rs | 1 +
> rust/kernel/platform.rs | 217 ++++++++++++++++++++++++++++++++
> 6 files changed, 234 insertions(+)
> create mode 100644 rust/helpers/platform.c
> create mode 100644 rust/kernel/platform.rs
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 87eb9a7869eb..173540375863 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6985,6 +6985,7 @@ F: rust/kernel/device.rs
> F: rust/kernel/device_id.rs
> F: rust/kernel/devres.rs
> F: rust/kernel/driver.rs
> +F: rust/kernel/platform.rs
>
> DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
> M: Nishanth Menon <nm@ti.com>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 312f03cbdce9..217c776615b9 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -18,6 +18,7 @@
> #include <linux/of_device.h>
> #include <linux/pci.h>
> #include <linux/phy.h>
> +#include <linux/platform_device.h>
> #include <linux/refcount.h>
> #include <linux/sched.h>
> #include <linux/slab.h>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 8bc6e9735589..663cdc2a45e0 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -17,6 +17,7 @@
> #include "kunit.c"
> #include "mutex.c"
> #include "page.c"
> +#include "platform.c"
> #include "pci.c"
> #include "rbtree.c"
> #include "rcu.c"
> diff --git a/rust/helpers/platform.c b/rust/helpers/platform.c
> new file mode 100644
> index 000000000000..ab9b9f317301
> --- /dev/null
> +++ b/rust/helpers/platform.c
> @@ -0,0 +1,13 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/platform_device.h>
> +
> +void *rust_helper_platform_get_drvdata(const struct platform_device *pdev)
> +{
> + return platform_get_drvdata(pdev);
> +}
> +
> +void rust_helper_platform_set_drvdata(struct platform_device *pdev, void *data)
> +{
> + platform_set_drvdata(pdev, data);
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 5946f59f1688..9e8dcd6d7c01 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -53,6 +53,7 @@
> pub mod net;
> pub mod of;
> pub mod page;
> +pub mod platform;
> pub mod prelude;
> pub mod print;
> pub mod rbtree;
> diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
> new file mode 100644
> index 000000000000..addf5356f44f
> --- /dev/null
> +++ b/rust/kernel/platform.rs
> @@ -0,0 +1,217 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Abstractions for the platform bus.
> +//!
> +//! C header: [`include/linux/platform_device.h`](srctree/include/linux/platform_device.h)
> +
> +use crate::{
> + bindings, container_of, device,
> + device_id::RawDeviceId,
> + driver,
> + error::{to_result, Result},
> + of,
> + prelude::*,
> + str::CStr,
> + types::{ARef, ForeignOwnable},
> + ThisModule,
> +};
> +
> +/// An adapter for the registration of platform drivers.
> +pub struct Adapter<T: Driver>(T);
> +
> +impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
> + type RegType = bindings::platform_driver;
> +
> + fn register(
> + pdrv: &mut Self::RegType,
> + name: &'static CStr,
> + module: &'static ThisModule,
> + ) -> Result {
> + pdrv.driver.name = name.as_char_ptr();
> + pdrv.probe = Some(Self::probe_callback);
> +
> + // Both members of this union are identical in data layout and semantics.
> + pdrv.__bindgen_anon_1.remove = Some(Self::remove_callback);
> + pdrv.driver.of_match_table = T::ID_TABLE.as_ptr();
> +
> + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> + to_result(unsafe { bindings::__platform_driver_register(pdrv, module.0) })
> + }
> +
> + fn unregister(pdrv: &mut Self::RegType) {
> + // SAFETY: `pdrv` is guaranteed to be a valid `RegType`.
> + unsafe { bindings::platform_driver_unregister(pdrv) };
> + }
> +}
> +
> +impl<T: Driver + 'static> Adapter<T> {
> + fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> + let table = T::ID_TABLE;
> + let id = T::of_match_device(pdev)?;
> +
> + Some(table.info(id.index()))
> + }
> +
> + extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> core::ffi::c_int {
> + // SAFETY: The platform bus only ever calls the probe callback with a valid `pdev`.
> + let dev = unsafe { device::Device::from_raw(&mut (*pdev).dev) };
> + // SAFETY: `dev` is guaranteed to be embedded in a valid `struct platform_device` by the
> + // call above.
> + let mut pdev = unsafe { Device::from_dev(dev) };
> +
> + let info = Self::id_info(&pdev);
> + match T::probe(&mut pdev, info) {
> + Ok(data) => {
> + // Let the `struct platform_device` own a reference of the driver's private data.
> + // SAFETY: By the type invariant `pdev.as_raw` returns a valid pointer to a
> + // `struct platform_device`.
> + unsafe { bindings::platform_set_drvdata(pdev.as_raw(), data.into_foreign() as _) };
> + }
> + Err(err) => return Error::to_errno(err),
> + }
> +
> + 0
> + }
> +
> + extern "C" fn remove_callback(pdev: *mut bindings::platform_device) {
> + // SAFETY: `pdev` is a valid pointer to a `struct platform_device`.
> + let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
> +
> + // SAFETY: `remove_callback` is only ever called after a successful call to
> + // `probe_callback`, hence it's guaranteed that `ptr` points to a valid and initialized
> + // `KBox<T>` pointer created through `KBox::into_foreign`.
> + let _ = unsafe { KBox::<T>::from_foreign(ptr) };
> + }
> +}
> +
> +/// Declares a kernel module that exposes a single platform driver.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// kernel::module_platform_driver! {
> +/// type: MyDriver,
> +/// name: "Module name",
> +/// author: "Author name",
> +/// description: "Description",
> +/// license: "GPL v2",
> +/// }
> +/// ```
> +#[macro_export]
> +macro_rules! module_platform_driver {
> + ($($f:tt)*) => {
> + $crate::module_driver!(<T>, $crate::platform::Adapter<T>, { $($f)* });
> + };
> +}
> +
> +/// IdTable type for platform drivers.
> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<of::DeviceId, T>;
> +
> +/// The platform driver trait.
> +///
> +/// # Example
> +///
> +///```
> +/// # use kernel::{bindings, c_str, of, platform};
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::of_device_table!(
> +/// OF_TABLE,
> +/// MODULE_OF_TABLE,
> +/// <MyDriver as platform::Driver>::IdInfo,
> +/// [
> +/// (of::DeviceId::new(c_str!("redhat,my-device")), ())
> +/// ]
> +/// );
> +///
> +/// impl platform::Driver for MyDriver {
> +/// type IdInfo = ();
> +/// const ID_TABLE: platform::IdTable<Self::IdInfo> = &OF_TABLE;
> +///
> +/// fn probe(
> +/// _pdev: &mut platform::Device,
> +/// _id_info: Option<&Self::IdInfo>,
> +/// ) -> Result<Pin<KBox<Self>>> {
> +/// Err(ENODEV)
> +/// }
> +/// }
> +///```
> +/// Drivers must implement this trait in order to get a platform driver registered. Please refer to
> +/// the `Adapter` documentation for an example.
> +pub trait Driver {
> + /// The type holding information about each device id supported by the driver.
> + ///
> + /// TODO: Use associated_type_defaults once stabilized:
> + ///
> + /// type IdInfo: 'static = ();
> + type IdInfo: 'static;
> +
> + /// The table of device ids supported by the driver.
> + const ID_TABLE: IdTable<Self::IdInfo>;
> +
> + /// Platform driver probe.
> + ///
> + /// Called when a new platform device is added or discovered.
> + /// Implementers should attempt to initialize the device here.
> + fn probe(dev: &mut Device, id_info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>>;
> +
> + /// Find the [`of::DeviceId`] within [`Driver::ID_TABLE`] matching the given [`Device`], if any.
> + fn of_match_device(pdev: &Device) -> Option<&of::DeviceId> {
> + let table = Self::ID_TABLE;
> +
> + // SAFETY:
> + // - `table` has static lifetime, hence it's valid for read,
> + // - `dev` is guaranteed to be valid while it's alive, and so is
> + // `pdev.as_dev().as_raw()`.
> + let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_dev().as_raw()) };
> +
> + if raw_id.is_null() {
> + None
> + } else {
> + // SAFETY: `DeviceId` is a `#[repr(transparent)` wrapper of `struct of_device_id` and
> + // does not add additional invariants, so it's safe to transmute.
> + Some(unsafe { &*raw_id.cast::<of::DeviceId>() })
> + }
> + }
> +}
> +
> +/// The platform device representation.
> +///
> +/// A platform device is based on an always reference counted `device:Device` instance. Cloning a
> +/// platform device, hence, also increments the base device' reference count.
> +///
> +/// # Invariants
> +///
> +/// `Device` holds a valid reference of `ARef<device::Device>` whose underlying `struct device` is a
> +/// member of a `struct platform_device`.
> +#[derive(Clone)]
> +pub struct Device(ARef<device::Device>);
> +
> +impl Device {
> + /// Convert a raw kernel device into a `Device`
> + ///
> + /// # Safety
> + ///
> + /// `dev` must be an `Aref<device::Device>` whose underlying `bindings::device` is a member of a
> + /// `bindings::platform_device`.
> + unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
> + Self(dev)
> + }
> +
> + fn as_dev(&self) -> &device::Device {
This has to be pub, since a platform::Device is at least as useful as a device::Device.
IOW: if an API takes &device::Device, there is no reason why someone with a &platform::Device
shouldn’t be able to call it.
In particular, having this as private makes it impossible for a platform driver to use Abdiel’s DMA allocator at [0].
> + &self.0
> + }
> +
> + fn as_raw(&self) -> *mut bindings::platform_device {
> + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
> + // embedded in `struct platform_device`.
> + unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut()
> + }
> +}
> +
> +impl AsRef<device::Device> for Device {
> + fn as_ref(&self) -> &device::Device {
> + &self.0
> + }
> +}
> --
> 2.46.2
>
— Daniel
[0]: https://lkml.org/lkml/2024/12/3/1281
^ permalink raw reply [flat|nested] 88+ messages in thread
* Re: [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions
2024-12-04 19:25 ` Daniel Almeida
@ 2024-12-04 19:30 ` Danilo Krummrich
0 siblings, 0 replies; 88+ messages in thread
From: Danilo Krummrich @ 2024-12-04 19:30 UTC (permalink / raw)
To: Daniel Almeida
Cc: gregkh, rafael, bhelgaas, ojeda, alex.gaynor, boqun.feng, gary,
bjorn3_gh, benno.lossin, tmgross, a.hindborg, aliceryhl, airlied,
fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh, saravanak,
rust-for-linux, linux-kernel, linux-pci, devicetree
On Wed, Dec 04, 2024 at 04:25:32PM -0300, Daniel Almeida wrote:
> Hi Danilo,
>
> > On 22 Oct 2024, at 18:31, Danilo Krummrich <dakr@kernel.org> wrote:
> > +
> > +/// The platform device representation.
> > +///
> > +/// A platform device is based on an always reference counted `device:Device` instance. Cloning a
> > +/// platform device, hence, also increments the base device' reference count.
> > +///
> > +/// # Invariants
> > +///
> > +/// `Device` holds a valid reference of `ARef<device::Device>` whose underlying `struct device` is a
> > +/// member of a `struct platform_device`.
> > +#[derive(Clone)]
> > +pub struct Device(ARef<device::Device>);
> > +
> > +impl Device {
> > + /// Convert a raw kernel device into a `Device`
> > + ///
> > + /// # Safety
> > + ///
> > + /// `dev` must be an `Aref<device::Device>` whose underlying `bindings::device` is a member of a
> > + /// `bindings::platform_device`.
> > + unsafe fn from_dev(dev: ARef<device::Device>) -> Self {
> > + Self(dev)
> > + }
> > +
> > + fn as_dev(&self) -> &device::Device {
>
> This has to be pub, since a platform::Device is at least as useful as a device::Device.
>
> IOW: if an API takes &device::Device, there is no reason why someone with a &platform::Device
> shouldn’t be able to call it.
>
> In particular, having this as private makes it impossible for a platform driver to use Abdiel’s DMA allocator at [0].
No worries, I already made it public in my branch [1], I'll send out a v4 soon.
[1] https://github.com/Rust-for-Linux/linux/blob/staging/dev/rust/kernel/platform.rs#L213
- Danilo
>
> > + &self.0
> > + }
> > +
> > + fn as_raw(&self) -> *mut bindings::platform_device {
> > + // SAFETY: By the type invariant `self.0.as_raw` is a pointer to the `struct device`
> > + // embedded in `struct platform_device`.
> > + unsafe { container_of!(self.0.as_raw(), bindings::platform_device, dev) }.cast_mut()
> > + }
> > +}
> > +
> > +impl AsRef<device::Device> for Device {
> > + fn as_ref(&self) -> &device::Device {
> > + &self.0
> > + }
> > +}
> > --
> > 2.46.2
> >
>
> — Daniel
>
> [0]: https://lkml.org/lkml/2024/12/3/1281
>
^ permalink raw reply [flat|nested] 88+ messages in thread
end of thread, other threads:[~2024-12-04 19:30 UTC | newest]
Thread overview: 88+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-22 21:31 [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 01/16] rust: init: introduce `Opaque::try_ffi_init` Danilo Krummrich
2024-10-29 12:42 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 02/16] rust: introduce `InPlaceModule` Danilo Krummrich
2024-10-29 12:45 ` Alice Ryhl
2024-11-04 0:15 ` Greg KH
2024-11-04 17:36 ` Miguel Ojeda
2024-10-22 21:31 ` [PATCH v3 03/16] rust: pass module name to `Module::init` Danilo Krummrich
2024-10-29 12:55 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 04/16] rust: implement generic driver registration Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 05/16] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
2024-11-25 13:42 ` Miguel Ojeda
2024-10-22 21:31 ` [PATCH v3 06/16] rust: add rcu abstraction Danilo Krummrich
2024-10-29 13:59 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 07/16] rust: add `Revocable` type Danilo Krummrich
2024-10-29 13:26 ` Alice Ryhl
2024-12-03 9:21 ` Danilo Krummrich
2024-12-03 9:24 ` Alice Ryhl
2024-12-03 9:35 ` Danilo Krummrich
2024-12-03 10:58 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 08/16] rust: add `dev_*` print macros Danilo Krummrich
2024-11-04 0:24 ` Greg KH
2024-10-22 21:31 ` [PATCH v3 09/16] rust: add `io::Io` base type Danilo Krummrich
2024-10-28 15:43 ` Alice Ryhl
2024-10-29 9:20 ` Danilo Krummrich
2024-10-29 10:18 ` Alice Ryhl
2024-11-06 23:44 ` Daniel Almeida
2024-11-06 23:31 ` Daniel Almeida
2024-10-22 21:31 ` [PATCH v3 10/16] rust: add devres abstraction Danilo Krummrich
2024-10-31 14:03 ` Alice Ryhl
2024-11-27 12:21 ` Alice Ryhl
2024-11-27 13:19 ` Danilo Krummrich
2024-11-27 13:20 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 11/16] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
2024-10-27 22:42 ` Boqun Feng
2024-10-28 10:21 ` Danilo Krummrich
2024-11-26 14:06 ` Danilo Krummrich
2024-10-29 21:16 ` Christian Schrefl
2024-10-22 21:31 ` [PATCH v3 12/16] rust: pci: implement I/O mappable `pci::Bar` Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 13/16] samples: rust: add Rust PCI sample driver Danilo Krummrich
2024-10-23 15:57 ` Rob Herring
2024-10-28 13:22 ` Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 14/16] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
2024-10-22 23:03 ` Rob Herring
2024-10-23 6:33 ` Danilo Krummrich
2024-10-27 4:38 ` Fabien Parent
2024-10-29 13:37 ` Alice Ryhl
2024-10-22 21:31 ` [PATCH v3 15/16] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
2024-10-22 23:47 ` Rob Herring
2024-10-23 6:44 ` Danilo Krummrich
2024-10-23 14:23 ` Rob Herring
2024-10-28 10:15 ` Danilo Krummrich
2024-10-30 12:23 ` Rob Herring
2024-11-26 12:39 ` Danilo Krummrich
2024-11-26 14:44 ` Rob Herring
2024-11-26 15:17 ` Danilo Krummrich
2024-11-26 19:15 ` Rob Herring
2024-11-26 20:01 ` Danilo Krummrich
2024-10-24 9:11 ` Dirk Behme
2024-10-28 10:19 ` Danilo Krummrich
2024-10-29 7:20 ` Dirk Behme
2024-10-29 8:50 ` Danilo Krummrich
2024-10-29 9:19 ` Dirk Behme
2024-10-29 9:50 ` Danilo Krummrich
2024-10-29 9:55 ` Danilo Krummrich
2024-10-29 10:08 ` Dirk Behme
2024-10-30 13:18 ` Rob Herring
2024-10-27 4:32 ` Fabien Parent
2024-10-28 13:44 ` Dirk Behme
2024-10-29 13:16 ` Alice Ryhl
2024-10-30 15:50 ` Alice Ryhl
2024-10-30 18:07 ` Danilo Krummrich
2024-10-31 8:23 ` Alice Ryhl
2024-11-26 14:13 ` Danilo Krummrich
2024-12-04 19:25 ` Daniel Almeida
2024-12-04 19:30 ` Danilo Krummrich
2024-10-22 21:31 ` [PATCH v3 16/16] samples: rust: add Rust platform sample driver Danilo Krummrich
2024-10-23 0:04 ` Rob Herring
2024-10-23 6:59 ` Danilo Krummrich
2024-10-23 15:37 ` Rob Herring
2024-10-28 9:32 ` Danilo Krummrich
2024-10-25 10:32 ` Dirk Behme
2024-10-25 16:08 ` Rob Herring
2024-10-23 5:13 ` [PATCH v3 00/16] Device / Driver PCI / Platform Rust abstractions Greg KH
2024-10-23 7:28 ` Danilo Krummrich
2024-10-25 5:15 ` Dirk Behme
2024-11-16 14:32 ` Janne Grunau
2024-11-16 14:50 ` Greg KH
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).