devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions
@ 2024-12-05 14:14 Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 01/13] rust: pass module name to `Module::init` Danilo Krummrich
                   ` (12 more replies)
  0 siblings, 13 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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 platform 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 v4:
==============
- Revocable
  - convert `Revocable::data` to `Opaque`
  - introduce `revoke_nosync`, which does not wait for an RCU grace period
  - fix minor typo

- Device ID
  - fix ID table export name to always be unique
  - remove `#![allow(stable_features)]`
  - move `#![allow(const_refs_to_cell)]` to the "stable in 1.83" list

- I/O
  - split `Io<SIZE>` in `IoRaw<SIZE>` and `Io<SIZE>`
  - add rust helper for ioremap() and iounmap() (needed by doctests)

- Devres
  - optimze `drop` by using `revoke_nosync` (we're guaranteed there are no more
    concurrent users, hence no need to wait for a full grace period)

- OF
  - rename "Open Firmware" -> "Device Tree / Open Firmware" (Rob)
  - remove unused C header reference
  - add TODO comment to loop in `DeviceId::new`
  - remove `DeviceId::compatible`; was only used in sample code
  - add missing `#[repr(transparent)]`
  - use #[cfg(CONFIG_OF)] for the relevant functions

- PCI
  - let Rust helper for pci_resource_len() return resource_size_t

- PCI Sample
  - remove unnecessary `from_le`

- Platform
  - fix example compatible string
  - add example for `platform::Driver`
  - rename `ID_TABLE` to `OF_ID_TABLE`
  - don't allow public use of `of_match_table`; convert to `of_id_info` to
    retrieve the `of::DeviceId` to gather the corresponding `IdInfo`
  - remove wrong example reference in the documentation of `platform::Driver`
  - remove `as_dev`, as we already implement `AsRef` for `platform::Device`

- Platform Sample
  - fix compatible string; add corresponding entry to drivers/of/unittest-data/tests-platform.dtsi
  - remove usage of `of_match_table`

- MISC
  - fix a few spelling mistakes
  - rebase onto rust-next (v6.13-rc1)

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 maintenance

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, IoRaw}` base types
  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 (2):
  rust: add rcu abstraction
  rust: add `Revocable` type

 MAINTAINERS                                  |   8 +
 drivers/block/rnull.rs                       |   2 +-
 drivers/of/unittest-data/tests-platform.dtsi |   5 +
 rust/bindings/bindings_helper.h              |   3 +
 rust/helpers/device.c                        |  10 +
 rust/helpers/helpers.c                       |   5 +
 rust/helpers/io.c                            | 101 +++++
 rust/helpers/pci.c                           |  18 +
 rust/helpers/platform.c                      |  13 +
 rust/helpers/rcu.c                           |  13 +
 rust/kernel/device_id.rs                     | 166 +++++++
 rust/kernel/devres.rs                        | 179 ++++++++
 rust/kernel/driver.rs                        | 120 ++++++
 rust/kernel/io.rs                            | 260 +++++++++++
 rust/kernel/lib.rs                           |  28 +-
 rust/kernel/net/phy.rs                       |   2 +-
 rust/kernel/of.rs                            |  57 +++
 rust/kernel/pci.rs                           | 429 +++++++++++++++++++
 rust/kernel/platform.rs                      | 222 ++++++++++
 rust/kernel/revocable.rs                     | 235 ++++++++++
 rust/kernel/sync.rs                          |   1 +
 rust/kernel/sync/lock/global.rs              |   6 +-
 rust/kernel/sync/rcu.rs                      |  47 ++
 rust/macros/lib.rs                           |   6 +-
 rust/macros/module.rs                        |   7 +-
 samples/rust/Kconfig                         |  21 +
 samples/rust/Makefile                        |   2 +
 samples/rust/rust_driver_pci.rs              | 109 +++++
 samples/rust/rust_driver_platform.rs         |  49 +++
 samples/rust/rust_minimal.rs                 |   2 +-
 samples/rust/rust_print_main.rs              |   2 +-
 31 files changed, 2114 insertions(+), 14 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: 40384c840ea1944d7c5a392e8975ed088ecf0b37
-- 
2.47.0


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v4 01/13] rust: pass module name to `Module::init`
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 02/13] rust: implement generic driver registration Danilo Krummrich
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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/kernel/sync/lock/global.rs |  6 ++++--
 rust/macros/lib.rs              |  6 ++++--
 rust/macros/module.rs           |  7 +++++--
 samples/rust/rust_minimal.rs    |  2 +-
 samples/rust/rust_print_main.rs |  2 +-
 8 files changed, 27 insertions(+), 14 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 e1065a7551a3..686db6aa3323 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -90,7 +90,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.
@@ -98,13 +98,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 b89c681d97c0..0f41df2e6b96 100644
--- a/rust/kernel/net/phy.rs
+++ b/rust/kernel/net/phy.rs
@@ -903,7 +903,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/kernel/sync/lock/global.rs b/rust/kernel/sync/lock/global.rs
index 480ee724e3cc..feff4638e20b 100644
--- a/rust/kernel/sync/lock/global.rs
+++ b/rust/kernel/sync/lock/global.rs
@@ -187,6 +187,7 @@ pub fn get_mut(&mut self) -> &mut T {
 /// ```
 /// # mod ex {
 /// # use kernel::prelude::*;
+/// # use kernel::str;
 /// kernel::sync::global_lock! {
 ///     // SAFETY: Initialized in module initializer before first use.
 ///     unsafe(uninit) static MY_COUNTER: Mutex<u32> = 0;
@@ -199,7 +200,7 @@ pub fn get_mut(&mut self) -> &mut T {
 /// }
 ///
 /// impl kernel::Module for MyModule {
-///     fn init(_module: &'static ThisModule) -> Result<Self> {
+///     fn init(_name: &'static str::CStr, _module: &'static ThisModule) -> Result<Self> {
 ///         // SAFETY: Called exactly once.
 ///         unsafe { MY_COUNTER.init() };
 ///
@@ -216,6 +217,7 @@ pub fn get_mut(&mut self) -> &mut T {
 /// # mod ex {
 /// # use kernel::prelude::*;
 /// use kernel::sync::{GlobalGuard, GlobalLockedBy};
+/// # use kernel::str;
 ///
 /// kernel::sync::global_lock! {
 ///     // SAFETY: Initialized in module initializer before first use.
@@ -239,7 +241,7 @@ pub fn get_mut(&mut self) -> &mut T {
 /// }
 ///
 /// impl kernel::Module for MyModule {
-///     fn init(_module: &'static ThisModule) -> Result<Self> {
+///     fn init(_name: &'static str::CStr, _module: &'static ThisModule) -> Result<Self> {
 ///         // SAFETY: Called exactly once.
 ///         unsafe { MY_MUTEX.init() };
 ///
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 4ab94e44adfe..d669f9cd1726 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -32,6 +32,7 @@
 ///
 /// ```
 /// use kernel::prelude::*;
+/// use kernel::str;
 ///
 /// module!{
 ///     type: MyModule,
@@ -45,7 +46,7 @@
 /// struct MyModule(i32);
 ///
 /// impl kernel::Module for MyModule {
-///     fn init(_module: &'static ThisModule) -> Result<Self> {
+///     fn init(_name: &'static str::CStr, _module: &'static ThisModule) -> Result<Self> {
 ///         let foo: i32 = 42;
 ///         pr_info!("I contain:  {}\n", foo);
 ///         Ok(Self(foo))
@@ -65,6 +66,7 @@
 ///
 /// ```
 /// use kernel::prelude::*;
+/// use kernel::str;
 ///
 /// module!{
 ///     type: MyDeviceDriverModule,
@@ -78,7 +80,7 @@
 /// struct MyDeviceDriverModule;
 ///
 /// impl kernel::Module for MyDeviceDriverModule {
-///     fn init(_module: &'static ThisModule) -> Result<Self> {
+///     fn init(_name: &'static str::CStr, _module: &'static ThisModule) -> Result<Self> {
 ///         Ok(Self)
 ///     }
 /// }
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 2587f41b0d39..1f14ef55341a 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -333,8 +333,11 @@ mod __module_init {{
                     ///
                     /// This function must only be called once.
                     unsafe fn __init() -> kernel::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_main.rs b/samples/rust/rust_print_main.rs
index aed90a6feecf..0853d767439b 100644
--- a/samples/rust/rust_print_main.rs
+++ b/samples/rust/rust_print_main.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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 02/13] rust: implement generic driver registration
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 01/13] rust: pass module name to `Module::init` Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-06 13:57   ` Alice Ryhl
  2024-12-05 14:14 ` [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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 1e930c7a58b1..085b20dc5c0b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7031,6 +7031,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..3ec0ba0556a1
--- /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 provide 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 686db6aa3323..0a719396256f 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -35,6 +35,7 @@
 mod build_assert;
 pub mod cred;
 pub mod device;
+pub mod driver;
 pub mod error;
 #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
 pub mod firmware;
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId`
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 01/13] rust: pass module name to `Module::init` Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 02/13] rust: implement generic driver registration Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-07  1:14   ` Fabien Parent
  2024-12-05 14:14 ` [PATCH v4 04/13] rust: add rcu abstraction Danilo Krummrich
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  Cc: rust-for-linux, linux-kernel, linux-pci, devicetree,
	Danilo Krummrich, Wedson Almeida Filho

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 | 166 +++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs       |   6 ++
 3 files changed, 173 insertions(+)
 create mode 100644 rust/kernel/device_id.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 085b20dc5c0b..6f952d5508e9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7031,6 +7031,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..8ba227dc3d3a
--- /dev/null
+++ b/rust/kernel/device_id.rs
@@ -0,0 +1,166 @@
+// 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 offset 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,
+                    "__", 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()) };
+    };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 0a719396256f..b5da7c520eb8 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -18,6 +18,11 @@
 #![feature(inline_const)]
 #![feature(lint_reasons)]
 #![feature(unsize)]
+// Stable in Rust 1.83
+#![feature(const_maybe_uninit_as_mut_ptr)]
+#![feature(const_mut_refs)]
+#![feature(const_ptr_write)]
+#![feature(const_refs_to_cell)]
 
 // Ensure conditional compilation based on the kernel configuration works;
 // otherwise we may silently break things like initcall handling.
@@ -35,6 +40,7 @@
 mod build_assert;
 pub mod cred;
 pub mod device;
+pub mod device_id;
 pub mod driver;
 pub mod error;
 #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 04/13] rust: add rcu abstraction
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (2 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 05/13] rust: add `Revocable` type Danilo Krummrich
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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>
Co-developed-by: Danilo Krummrich <dakr@kernel.org>
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 | 47 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 62 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 dcf827a61b52..060750af6524 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -20,6 +20,7 @@
 #include "page.c"
 #include "pid_namespace.c"
 #include "rbtree.c"
+#include "rcu.c"
 #include "refcount.c"
 #include "security.c"
 #include "signal.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 1eab7ebf25fd..0654008198b2 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -12,6 +12,7 @@
 pub mod lock;
 mod locked_by;
 pub mod poll;
+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..3beef70d5f34
--- /dev/null
+++ b/rust/kernel/sync/rcu.rs
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! RCU support.
+//!
+//! C header: [`include/linux/rcupdate.h`](srctree/include/linux/rcupdate.h)
+
+use crate::{bindings, types::NotThreadSafe};
+
+/// 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(NotThreadSafe);
+
+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(NotThreadSafe)
+    }
+
+    /// 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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 05/13] rust: add `Revocable` type
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (3 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 04/13] rust: add rcu abstraction Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-06 15:11   ` Alice Ryhl
  2024-12-05 14:14 ` [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types Danilo Krummrich
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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>
Co-developed-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/lib.rs       |   1 +
 rust/kernel/revocable.rs | 235 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 236 insertions(+)
 create mode 100644 rust/kernel/revocable.rs

diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b5da7c520eb8..200c5f99a805 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -60,6 +60,7 @@
 pub mod prelude;
 pub mod print;
 pub mod rbtree;
+pub mod revocable;
 pub mod security;
 pub mod seq_file;
 pub mod sizes;
diff --git a/rust/kernel/revocable.rs b/rust/kernel/revocable.rs
new file mode 100644
index 000000000000..fb675e58625a
--- /dev/null
+++ b/rust/kernel/revocable.rs
@@ -0,0 +1,235 @@
+// 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,
+    types::Opaque,
+};
+use core::{
+    marker::PhantomData,
+    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: Opaque<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 `ptr` is fully initialized; on error
+            // `ptr` is not partially initialized and does not need to be dropped.
+            data <- unsafe {
+                Opaque::try_ffi_init(|ptr: *mut T| {
+                    init::PinInit::<T, core::convert::Infallible>::__pinned_init(data, ptr)
+                })
+            },
+        })
+    }
+
+    /// 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) {
+            // 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(RevocableGuard::new(self.data.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.get() })
+        } else {
+            None
+        }
+    }
+
+    /// # Safety
+    ///
+    /// Callers must ensure that there are no more concurrent users of the revocable object.
+    unsafe fn revoke_internal(&self, sync: bool) {
+        if self
+            .is_available
+            .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed)
+            .is_ok()
+        {
+            if sync {
+                // 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.get()) };
+        }
+    }
+
+    /// Revokes access to and drops the wrapped object.
+    ///
+    /// Access to the object is revoked immediately to new callers of [`Revocable::try_access`],
+    /// expecting that there are no concurrent users of the object.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that there are no more concurrent users of the revocable object.
+    pub unsafe fn revoke_nosync(&self) {
+        // SAFETY: By the safety requirement of this function, the caller ensures that nobody is
+        // accessing the data anymore and hence we don't have to wait for the grace period to
+        // finish.
+        unsafe { self.revoke_internal(false) }
+    }
+
+    /// 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) {
+        // SAFETY: By passing `true` we ask `revoke_internal` to wait for the grace period to
+        // finish.
+        unsafe { self.revoke_internal(true) }
+    }
+}
+
+#[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.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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (4 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 05/13] rust: add `Revocable` type Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-06 14:13   ` Alice Ryhl
  2024-12-11 14:52   ` Daniel Almeida
  2024-12-05 14:14 ` [PATCH v4 07/13] rust: add devres abstraction Danilo Krummrich
                   ` (6 subsequent siblings)
  12 siblings, 2 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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.

`IoRaw` 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
`IoRaw` accordingly.

To ensure that I/O mapped memory can't out-live the device it may be
bound to, subsystems must embed the corresponding I/O memory type (e.g.
pci::Bar) into a `Devres` container, such that it gets revoked once the
device is unbound.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/helpers/helpers.c |   1 +
 rust/helpers/io.c      | 101 ++++++++++++++++
 rust/kernel/io.rs      | 260 +++++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs     |   1 +
 4 files changed, 363 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 060750af6524..63f9b1da179f 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -14,6 +14,7 @@
 #include "cred.c"
 #include "err.c"
 #include "fs.c"
+#include "io.c"
 #include "jump_label.c"
 #include "kunit.c"
 #include "mutex.c"
diff --git a/rust/helpers/io.c b/rust/helpers/io.c
new file mode 100644
index 000000000000..1dde6374c0e2
--- /dev/null
+++ b/rust/helpers/io.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/io.h>
+
+void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size)
+{
+	return ioremap(offset, size);
+}
+
+void rust_helper_iounmap(volatile void __iomem *addr)
+{
+	return iounmap(addr);
+}
+
+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..36aa2ec1bcc8
--- /dev/null
+++ b/rust/kernel/io.rs
@@ -0,0 +1,260 @@
+// 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};
+
+/// Raw representation of an MMIO region.
+///
+/// By itself, the existence of an instance of this structure does not provide any guarantees that
+/// the represented MMIO region does exist or is properly mapped.
+///
+/// Instead, the bus specific MMIO implementation must convert this raw representation into an `Io`
+/// instance providing the actual memory accessors. Only by the conversion into an `Io` structure
+/// any guarantees are given.
+pub struct IoRaw<const SIZE: usize = 0> {
+    addr: usize,
+    maxsize: usize,
+}
+
+impl<const SIZE: usize> IoRaw<SIZE> {
+    /// Returns a new `IoRaw` instance on success, an error otherwise.
+    pub fn new(addr: usize, maxsize: usize) -> Result<Self> {
+        if maxsize < SIZE {
+            return Err(EINVAL);
+        }
+
+        Ok(Self { addr, maxsize })
+    }
+
+    /// Returns the base address of the MMIO region.
+    #[inline]
+    pub fn addr(&self) -> usize {
+        self.addr
+    }
+
+    /// Returns the maximum size of the MMIO region.
+    #[inline]
+    pub fn maxsize(&self) -> usize {
+        self.maxsize
+    }
+}
+
+/// 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, IoRaw}};
+/// # use core::ops::Deref;
+///
+/// // See also [`pci::Bar`] for a real example.
+/// struct IoMem<const SIZE: usize>(IoRaw<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);
+///         }
+///
+///         Ok(IoMem(IoRaw::new(addr as _, SIZE)?))
+///     }
+/// }
+///
+/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
+///     fn drop(&mut self) {
+///         // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
+///         unsafe { bindings::iounmap(self.0.addr() as _); };
+///     }
+/// }
+///
+/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
+///    type Target = Io<SIZE>;
+///
+///    fn deref(&self) -> &Self::Target {
+///         // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
+///         unsafe { Io::from_raw(&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(())
+/// # }
+/// ```
+#[repr(transparent)]
+pub struct Io<const SIZE: usize = 0>(IoRaw<SIZE>);
+
+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> {
+    /// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size
+    /// `maxsize`.
+    pub unsafe fn from_raw<'a>(raw: &IoRaw<SIZE>) -> &'a Self {
+        // SAFETY: `Io` is a transparent wrapper around `IoRaw`.
+        unsafe { &*core::ptr::from_ref(raw).cast() }
+    }
+
+    /// Returns the base address of this mapping.
+    #[inline]
+    pub fn addr(&self) -> usize {
+        self.0.addr()
+    }
+
+    /// Returns the maximum size of this mapping.
+    #[inline]
+    pub fn maxsize(&self) -> usize {
+        self.0.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.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.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 200c5f99a805..d1f30b1a05fb 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -79,6 +79,7 @@
 
 #[doc(hidden)]
 pub use bindings;
+pub mod io;
 pub use macros;
 pub use uapi;
 
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 07/13] rust: add devres abstraction
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (5 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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  | 179 +++++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs     |   1 +
 5 files changed, 192 insertions(+)
 create mode 100644 rust/helpers/device.c
 create mode 100644 rust/kernel/devres.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 6f952d5508e9..a3535297ee6f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7032,6 +7032,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 63f9b1da179f..a3b52aa021de 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -12,6 +12,7 @@
 #include "build_assert.c"
 #include "build_bug.c"
 #include "cred.c"
+#include "device.c"
 #include "err.c"
 #include "fs.c"
 #include "io.c"
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
new file mode 100644
index 000000000000..57ddb49fde1d
--- /dev/null
+++ b/rust/kernel/devres.rs
@@ -0,0 +1,179 @@
+// 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, IoRaw}};
+/// # use core::ops::Deref;
+///
+/// // See also [`pci::Bar`] for a real example.
+/// struct IoMem<const SIZE: usize>(IoRaw<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);
+///         }
+///
+///         Ok(IoMem(IoRaw::new(addr as _, SIZE)?))
+///     }
+/// }
+///
+/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
+///     fn drop(&mut self) {
+///         // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
+///         unsafe { bindings::iounmap(self.0.addr() as _); };
+///     }
+/// }
+///
+/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
+///    type Target = Io<SIZE>;
+///
+///    fn deref(&self) -> &Self::Target {
+///         // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
+///         unsafe { Io::from_raw(&self.0) }
+///    }
+/// }
+/// # fn no_run() -> Result<(), Error> {
+/// # // SAFETY: Invalid usage; just for the example to get an `ARef<Device>` instance.
+/// # let dev = unsafe { Device::get_device(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()`.
+        //
+        // SAFETY: When `drop` runs, it's guaranteed that nobody is accessing the revocable data
+        // anymore, hence it is safe not to wait for the grace period to finish.
+        unsafe { self.revoke_nosync() };
+    }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index d1f30b1a05fb..4b94e6072c63 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -41,6 +41,7 @@
 pub mod cred;
 pub mod device;
 pub mod device_id;
+pub mod devres;
 pub mod driver;
 pub mod error;
 #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
-- 
2.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (6 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 07/13] rust: add devres abstraction Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-06 14:01   ` Alice Ryhl
  2024-12-06 15:25   ` Alice Ryhl
  2024-12-05 14:14 ` [PATCH v4 09/13] rust: pci: implement I/O mappable `pci::Bar` Danilo Krummrich
                   ` (4 subsequent siblings)
  12 siblings, 2 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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 a3535297ee6f..041b0c1b476f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18099,6 +18099,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 BANDWIDTH CONTROLLER
 M:	Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 5c4dfe22f41a..6d7a68e2ecb7 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -20,6 +20,7 @@
 #include <linux/jump_label.h>
 #include <linux/mdio.h>
 #include <linux/miscdevice.h>
+#include <linux/pci.h>
 #include <linux/phy.h>
 #include <linux/pid_namespace.h>
 #include <linux/poll.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index a3b52aa021de..3fda33cd42d4 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -20,6 +20,7 @@
 #include "kunit.c"
 #include "mutex.c"
 #include "page.c"
+#include "pci.c"
 #include "pid_namespace.c"
 #include "rbtree.c"
 #include "rcu.c"
diff --git a/rust/helpers/pci.c b/rust/helpers/pci.c
new file mode 100644
index 000000000000..8ba22f911459
--- /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);
+}
+
+resource_size_t 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 4b94e6072c63..07770add5ee2 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -82,6 +82,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..748912c1c994
--- /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::get_device(&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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 09/13] rust: pci: implement I/O mappable `pci::Bar`
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (7 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-06 10:44   ` Philipp Stanner
  2024-12-05 14:14 ` [PATCH v4 10/13] samples: rust: add Rust PCI sample driver Danilo Krummrich
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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 748912c1c994..0a32a5935c9c 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -5,10 +5,14 @@
 //! 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,
+    io::IoRaw,
     str::CStr,
     types::{ARef, ForeignOwnable},
     ThisModule,
@@ -239,9 +243,115 @@ 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 `IoRaw` 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: IoRaw<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);
+        }
+
+        let io = match IoRaw::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.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 {
+        // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped.
+        unsafe { Io::from_raw(&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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 10/13] samples: rust: add Rust PCI sample driver
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (8 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 09/13] rust: pci: implement I/O mappable `pci::Bar` Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 11/13] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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 041b0c1b476f..f97052d5f454 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18100,6 +18100,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 BANDWIDTH CONTROLLER
 M:	Ilpo Järvinen <ilpo.jarvinen@linux.intel.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 c1a5c1655395..2f5b6bdb2fa5 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -3,6 +3,7 @@ ccflags-y += -I$(src)				# needed for trace events
 
 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
 
 rust_print-y := rust_print_main.o rust_print_events.o
 
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
new file mode 100644
index 000000000000..76d742189c00
--- /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(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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 11/13] rust: of: add `of::DeviceId` abstraction
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (9 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 10/13] samples: rust: add Rust PCI sample driver Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-09 21:22   ` Rob Herring
  2024-12-05 14:14 ` [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
  2024-12-05 14:14 ` [PATCH v4 13/13] samples: rust: add Rust platform sample driver Danilo Krummrich
  12 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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/kernel/lib.rs |  1 +
 rust/kernel/of.rs  | 57 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+)
 create mode 100644 rust/kernel/of.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index f97052d5f454..7d6bb4b15d2c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17499,6 +17499,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/kernel/lib.rs b/rust/kernel/lib.rs
index 07770add5ee2..7a0e4c82ad0c 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -56,6 +56,7 @@
 pub mod miscdevice;
 #[cfg(CONFIG_NET)]
 pub mod net;
+pub mod of;
 pub mod page;
 pub mod pid_namespace;
 pub mod prelude;
diff --git a/rust/kernel/of.rs b/rust/kernel/of.rs
new file mode 100644
index 000000000000..2b424f3bfa93
--- /dev/null
+++ b/rust/kernel/of.rs
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Device Tree / Open Firmware abstractions.
+
+use crate::{bindings, device_id::RawDeviceId, prelude::*};
+
+/// An open firmware device id.
+#[repr(transparent)]
+#[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() };
+
+        // TODO: Use `clone_from_slice` once the corresponding types do match.
+        let mut i = 0;
+        while i < src.len() {
+            of.compatible[i] = src[i] as _;
+            i += 1;
+        }
+
+        Self(of)
+    }
+}
+
+/// 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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (10 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 11/13] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-09 22:37   ` Rob Herring
  2024-12-05 14:14 ` [PATCH v4 13/13] samples: rust: add Rust platform sample driver Danilo Krummrich
  12 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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 |   2 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/platform.c         |  13 ++
 rust/kernel/lib.rs              |   1 +
 rust/kernel/platform.rs         | 222 ++++++++++++++++++++++++++++++++
 6 files changed, 240 insertions(+)
 create mode 100644 rust/helpers/platform.c
 create mode 100644 rust/kernel/platform.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 7d6bb4b15d2c..365fc48b7041 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7034,6 +7034,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 6d7a68e2ecb7..e9fdceb568b8 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -20,9 +20,11 @@
 #include <linux/jump_label.h>
 #include <linux/mdio.h>
 #include <linux/miscdevice.h>
+#include <linux/of_device.h>
 #include <linux/pci.h>
 #include <linux/phy.h>
 #include <linux/pid_namespace.h>
+#include <linux/platform_device.h>
 #include <linux/poll.h>
 #include <linux/refcount.h>
 #include <linux/sched.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 3fda33cd42d4..0640b7e115be 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -20,6 +20,7 @@
 #include "kunit.c"
 #include "mutex.c"
 #include "page.c"
+#include "platform.c"
 #include "pci.c"
 #include "pid_namespace.c"
 #include "rbtree.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 7a0e4c82ad0c..cc8f48aa162b 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -59,6 +59,7 @@
 pub mod of;
 pub mod page;
 pub mod pid_namespace;
+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..868cfddb75a2
--- /dev/null
+++ b/rust/kernel/platform.rs
@@ -0,0 +1,222 @@
+// 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, 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::OF_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> {
+    #[cfg(CONFIG_OF)]
+    fn of_id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
+        let table = T::OF_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_ref().as_raw()`.
+        let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_ref().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.
+            let id = unsafe { &*raw_id.cast::<of::DeviceId>() };
+
+            Some(table.info(<of::DeviceId as crate::device_id::RawDeviceId>::index(id)))
+        }
+    }
+
+    #[cfg(not(CONFIG_OF))]
+    fn of_id_info(_pdev: &Device) -> Option<&'static T::IdInfo> {
+        None
+    }
+
+    // Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular
+    // table, it means we don't have a match in there. If we don't match any of the ID tables, it
+    // means we were matched by name.
+    fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
+        let id = Self::of_id_info(pdev);
+        if id.is_some() {
+            return id;
+        }
+
+        None
+    }
+
+    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::get_device(&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.
+///
+/// Drivers must implement this trait in order to get a platform driver registered.
+///
+/// # 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!("test,device")), ())
+///     ]
+/// );
+///
+/// impl platform::Driver for MyDriver {
+///     type IdInfo = ();
+///     const OF_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)
+///     }
+/// }
+///```
+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 OF device ids supported by the driver.
+    const OF_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>>>;
+}
+
+/// 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_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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4 13/13] samples: rust: add Rust platform sample driver
  2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
                   ` (11 preceding siblings ...)
  2024-12-05 14:14 ` [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
@ 2024-12-05 14:14 ` Danilo Krummrich
  2024-12-05 17:09   ` Dirk Behme
  2024-12-10 22:59   ` Rob Herring (Arm)
  12 siblings, 2 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-05 14:14 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  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 +
 drivers/of/unittest-data/tests-platform.dtsi |  5 ++
 samples/rust/Kconfig                         | 10 ++++
 samples/rust/Makefile                        |  1 +
 samples/rust/rust_driver_platform.rs         | 49 ++++++++++++++++++++
 5 files changed, 66 insertions(+)
 create mode 100644 samples/rust/rust_driver_platform.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 365fc48b7041..ae576c842c51 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7035,6 +7035,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/drivers/of/unittest-data/tests-platform.dtsi b/drivers/of/unittest-data/tests-platform.dtsi
index fa39611071b3..2caaf1c10ee6 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/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 2f5b6bdb2fa5..761d13fff018 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -4,6 +4,7 @@ ccflags-y += -I$(src)				# needed for trace events
 obj-$(CONFIG_SAMPLE_RUST_MINIMAL)		+= rust_minimal.o
 obj-$(CONFIG_SAMPLE_RUST_PRINT)			+= rust_print.o
 obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI)		+= rust_driver_pci.o
+obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM)	+= rust_driver_platform.o
 
 rust_print-y := rust_print_main.o rust_print_events.o
 
diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
new file mode 100644
index 000000000000..2f0dbbe69e10
--- /dev/null
+++ b/samples/rust/rust_driver_platform.rs
@@ -0,0 +1,49 @@
+// 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!("test,rust-device")), Info(42))]
+);
+
+impl platform::Driver for SampleDriver {
+    type IdInfo = Info;
+    const OF_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");
+
+        if let Some(info) = info {
+            dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0);
+        }
+
+        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.47.0


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
  2024-12-05 14:14 ` [PATCH v4 13/13] samples: rust: add Rust platform sample driver Danilo Krummrich
@ 2024-12-05 17:09   ` Dirk Behme
  2024-12-05 18:03     ` Rob Herring
  2024-12-06  8:33     ` Danilo Krummrich
  2024-12-10 22:59   ` Rob Herring (Arm)
  1 sibling, 2 replies; 45+ messages in thread
From: Dirk Behme @ 2024-12-05 17:09 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, dirk.behme, j,
	fabien.parent, chrisi.schrefl
  Cc: rust-for-linux, linux-kernel, linux-pci, devicetree

Hi Danilo,

On 05.12.24 15:14, 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.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Not a review comment, but a question/proposal:

What do you think to convert the platform sample into an example/test?
And drop it in samples/rust then? Like [1] below?

We would have (a) a complete example in the documentation and (b) some
(KUnit) test coverage and (c) have one patch less in the series and
(d) one file less to maintain long term.

I think to remember that it was mentioned somewhere that a
documentation example / KUnit test is preferred over samples/rust (?).

Just an idea :)

Best regards

Dirk

[1]

diff --git a/MAINTAINERS b/MAINTAINERS
index ae576c842c51..365fc48b7041 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7035,7 +7035,6 @@ 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/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 868cfddb75a2..77aeb6fc2120 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -142,30 +142,55 @@ macro_rules! module_platform_driver {
 /// # Example
 ///
 ///```
-/// # use kernel::{bindings, c_str, of, platform};
+/// # mod mydriver {
+/// #
+/// # // Get this into the scope of the module to make the
assert_eq!() buildable
+/// # static __DOCTEST_ANCHOR: i32 = core::line!() as i32 - 4;
+/// #
+/// # use kernel::{c_str, of, platform, prelude::*};
+/// #
+/// struct MyDriver {
+///     pdev: platform::Device,
+/// }
 ///
-/// struct MyDriver;
+/// struct Info(u32);
 ///
 /// kernel::of_device_table!(
 ///     OF_TABLE,
 ///     MODULE_OF_TABLE,
 ///     <MyDriver as platform::Driver>::IdInfo,
-///     [
-///         (of::DeviceId::new(c_str!("test,device")), ())
-///     ]
+///     [(of::DeviceId::new(c_str!("test,rust-device")), Info(42))]
 /// );
 ///
 /// impl platform::Driver for MyDriver {
-///     type IdInfo = ();
+///     type IdInfo = Info;
 ///     const OF_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)
+///     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");
+///
+///         assert_eq!(info.unwrap().0, 42);
+///
+///         let drvdata = KBox::new(Self { pdev: pdev.clone() },
GFP_KERNEL)?;
+///
+///         Ok(drvdata.into())
+///     }
+/// }
+///
+/// impl Drop for MyDriver {
+///     fn drop(&mut self) {
+///         dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver
sample.\n");
 ///     }
 /// }
+///
+/// kernel::module_platform_driver! {
+///     type: MyDriver,
+///     name: "rust_driver_platform",
+///     author: "Danilo Krummrich",
+///     description: "Rust Platform driver",
+///     license: "GPL v2",
+/// }
+/// # }
 ///```
 pub trait Driver {
     /// The type holding information about each device id supported
by the driver.
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 70126b750426..6d468193cdd8 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -41,16 +41,6 @@ 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 761d13fff018..2f5b6bdb2fa5 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -4,7 +4,6 @@ ccflags-y += -I$(src)				# needed for trace events
 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

 rust_print-y := rust_print_main.o rust_print_events.o

diff --git a/samples/rust/rust_driver_platform.rs
b/samples/rust/rust_driver_platform.rs
deleted file mode 100644
index 2f0dbbe69e10..000000000000
--- a/samples/rust/rust_driver_platform.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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!("test,rust-device")), Info(42))]
-);
-
-impl platform::Driver for SampleDriver {
-    type IdInfo = Info;
-    const OF_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");
-
-        if let Some(info) = info {
-            dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n",
info.0);
-        }
-
-        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",
-}


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
  2024-12-05 17:09   ` Dirk Behme
@ 2024-12-05 18:03     ` Rob Herring
  2024-12-06  6:39       ` Dirk Behme
  2024-12-06  8:33     ` Danilo Krummrich
  1 sibling, 1 reply; 45+ messages in thread
From: Rob Herring @ 2024-12-05 18:03 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Thu, Dec 5, 2024 at 11:09 AM Dirk Behme <dirk.behme@gmail.com> wrote:
>
> Hi Danilo,
>
> On 05.12.24 15:14, 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.
> >
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>
> Not a review comment, but a question/proposal:
>
> What do you think to convert the platform sample into an example/test?
> And drop it in samples/rust then? Like [1] below?
>
> We would have (a) a complete example in the documentation and (b) some
> (KUnit) test coverage and (c) have one patch less in the series and
> (d) one file less to maintain long term.

I think that's going to become unwieldy when/if we add properties,
iomem, and every other thing a driver can call in probe.

OTOH, the need for the sample will quickly diminish once there are
real drivers using this stuff.

> I think to remember that it was mentioned somewhere that a
> documentation example / KUnit test is preferred over samples/rust (?).

Really? I've only figured out how you build and run the samples. I
started looking into how to do the documentation kunit stuff and still
haven't figured it out. I'm sure it is "easy", but it's not as easy as
the samples and yet another thing to go learn with the rust stuff. For
example, just ensuring it builds is more than just "compile the
kernel". We already have so many steps for submitting things upstream
and rust is just adding more on top.

Rob

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
  2024-12-05 18:03     ` Rob Herring
@ 2024-12-06  6:39       ` Dirk Behme
  0 siblings, 0 replies; 45+ messages in thread
From: Dirk Behme @ 2024-12-06  6:39 UTC (permalink / raw)
  To: Rob Herring
  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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

Hi Rob,

On 05.12.24 19:03, Rob Herring wrote:
> On Thu, Dec 5, 2024 at 11:09 AM Dirk Behme <dirk.behme@gmail.com> wrote:
>>
>> Hi Danilo,
>>
>> On 05.12.24 15:14, 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.
>>>
>>> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>>
>> Not a review comment, but a question/proposal:
>>
>> What do you think to convert the platform sample into an example/test?
>> And drop it in samples/rust then? Like [1] below?
>>
>> We would have (a) a complete example in the documentation and (b) some
>> (KUnit) test coverage and (c) have one patch less in the series and
>> (d) one file less to maintain long term.
> 
> I think that's going to become unwieldy when/if we add properties,
> iomem, and every other thing a driver can call in probe.


Yes, I agree. In your property RFC you added some (nice!) property
access examples to the sample we are talking about here. That would
become difficult with the documentation/Kunit example proposed here.
So I think there are pros & cons for both options ;)


> OTOH, the need for the sample will quickly diminish once there are
> real drivers using this stuff.
> 
>> I think to remember that it was mentioned somewhere that a
>> documentation example / KUnit test is preferred over samples/rust (?).
> 
> Really? I've only figured out how you build and run the samples. I
> started looking into how to do the documentation kunit stuff and still
> haven't figured it out. 

If you like try CONFIG_KUNIT=y and CONFIG_RUST_KERNEL_DOCTESTS=y.

I guess it will run automatically at boot, then. For me that was the
easiest way. But yes, everything else will need some additional steps.

Dirk

> I'm sure it is "easy", but it's not as easy as
> the samples and yet another thing to go learn with the rust stuff. For
> example, just ensuring it builds is more than just "compile the
> kernel". We already have so many steps for submitting things upstream
> and rust is just adding more on top.



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
  2024-12-05 17:09   ` Dirk Behme
  2024-12-05 18:03     ` Rob Herring
@ 2024-12-06  8:33     ` Danilo Krummrich
  2024-12-06  9:29       ` Dirk Behme
  1 sibling, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-06  8:33 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Thu, Dec 05, 2024 at 06:09:10PM +0100, Dirk Behme wrote:
> Hi Danilo,
> 
> On 05.12.24 15:14, 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.
> > 
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> 
> Not a review comment, but a question/proposal:
> 
> What do you think to convert the platform sample into an example/test?
> And drop it in samples/rust then? Like [1] below?

Generally, I think doctests are indeed preferrable. In this particular case
though, I think it's better to have a sample module, since this way it can serve
as go-to example of how to write a platform driver in Rust.

Especially for (kernel) folks who do not have a Rust (for Linux) background it's
way more accessible.

> 
> We would have (a) a complete example in the documentation and (b) some
> (KUnit) test coverage and (c) have one patch less in the series and
> (d) one file less to maintain long term.
> 
> I think to remember that it was mentioned somewhere that a
> documentation example / KUnit test is preferred over samples/rust (?).
> 
> Just an idea :)
> 
> Best regards
> 
> Dirk
> 
> [1]
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ae576c842c51..365fc48b7041 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7035,7 +7035,6 @@ 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/rust/kernel/platform.rs b/rust/kernel/platform.rs
> index 868cfddb75a2..77aeb6fc2120 100644
> --- a/rust/kernel/platform.rs
> +++ b/rust/kernel/platform.rs
> @@ -142,30 +142,55 @@ macro_rules! module_platform_driver {
>  /// # Example
>  ///
>  ///```
> -/// # use kernel::{bindings, c_str, of, platform};
> +/// # mod mydriver {
> +/// #
> +/// # // Get this into the scope of the module to make the
> assert_eq!() buildable
> +/// # static __DOCTEST_ANCHOR: i32 = core::line!() as i32 - 4;
> +/// #
> +/// # use kernel::{c_str, of, platform, prelude::*};
> +/// #
> +/// struct MyDriver {
> +///     pdev: platform::Device,
> +/// }
>  ///
> -/// struct MyDriver;
> +/// struct Info(u32);
>  ///
>  /// kernel::of_device_table!(
>  ///     OF_TABLE,
>  ///     MODULE_OF_TABLE,
>  ///     <MyDriver as platform::Driver>::IdInfo,
> -///     [
> -///         (of::DeviceId::new(c_str!("test,device")), ())
> -///     ]
> +///     [(of::DeviceId::new(c_str!("test,rust-device")), Info(42))]
>  /// );
>  ///
>  /// impl platform::Driver for MyDriver {
> -///     type IdInfo = ();
> +///     type IdInfo = Info;
>  ///     const OF_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)
> +///     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");
> +///
> +///         assert_eq!(info.unwrap().0, 42);
> +///
> +///         let drvdata = KBox::new(Self { pdev: pdev.clone() },
> GFP_KERNEL)?;
> +///
> +///         Ok(drvdata.into())
> +///     }
> +/// }
> +///
> +/// impl Drop for MyDriver {
> +///     fn drop(&mut self) {
> +///         dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver
> sample.\n");
>  ///     }
>  /// }
> +///
> +/// kernel::module_platform_driver! {
> +///     type: MyDriver,
> +///     name: "rust_driver_platform",
> +///     author: "Danilo Krummrich",
> +///     description: "Rust Platform driver",
> +///     license: "GPL v2",
> +/// }
> +/// # }
>  ///```
>  pub trait Driver {
>      /// The type holding information about each device id supported
> by the driver.
> diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
> index 70126b750426..6d468193cdd8 100644
> --- a/samples/rust/Kconfig
> +++ b/samples/rust/Kconfig
> @@ -41,16 +41,6 @@ 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 761d13fff018..2f5b6bdb2fa5 100644
> --- a/samples/rust/Makefile
> +++ b/samples/rust/Makefile
> @@ -4,7 +4,6 @@ ccflags-y += -I$(src)				# needed for trace events
>  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
> 
>  rust_print-y := rust_print_main.o rust_print_events.o
> 
> diff --git a/samples/rust/rust_driver_platform.rs
> b/samples/rust/rust_driver_platform.rs
> deleted file mode 100644
> index 2f0dbbe69e10..000000000000
> --- a/samples/rust/rust_driver_platform.rs
> +++ /dev/null
> @@ -1,49 +0,0 @@
> -// 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!("test,rust-device")), Info(42))]
> -);
> -
> -impl platform::Driver for SampleDriver {
> -    type IdInfo = Info;
> -    const OF_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");
> -
> -        if let Some(info) = info {
> -            dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n",
> info.0);
> -        }
> -
> -        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",
> -}
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
  2024-12-06  8:33     ` Danilo Krummrich
@ 2024-12-06  9:29       ` Dirk Behme
  0 siblings, 0 replies; 45+ messages in thread
From: Dirk Behme @ 2024-12-06  9:29 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On 06.12.24 09:33, Danilo Krummrich wrote:
> On Thu, Dec 05, 2024 at 06:09:10PM +0100, Dirk Behme wrote:
>> Hi Danilo,
>>
>> On 05.12.24 15:14, 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.
>>>
>>> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
>>
>> Not a review comment, but a question/proposal:
>>
>> What do you think to convert the platform sample into an example/test?
>> And drop it in samples/rust then? Like [1] below?
> 
> Generally, I think doctests are indeed preferrable. In this particular case
> though, I think it's better to have a sample module, since this way it can serve
> as go-to example of how to write a platform driver in Rust.
> 
> Especially for (kernel) folks who do not have a Rust (for Linux) background it's
> way more accessible.


Yes, ack. Rob said the same :)

Thanks

Dirk


>> We would have (a) a complete example in the documentation and (b) some
>> (KUnit) test coverage and (c) have one patch less in the series and
>> (d) one file less to maintain long term.
>>
>> I think to remember that it was mentioned somewhere that a
>> documentation example / KUnit test is preferred over samples/rust (?).
>>
>> Just an idea :)
>>
>> Best regards
>>
>> Dirk
>>
>> [1]
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index ae576c842c51..365fc48b7041 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7035,7 +7035,6 @@ 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/rust/kernel/platform.rs b/rust/kernel/platform.rs
>> index 868cfddb75a2..77aeb6fc2120 100644
>> --- a/rust/kernel/platform.rs
>> +++ b/rust/kernel/platform.rs
>> @@ -142,30 +142,55 @@ macro_rules! module_platform_driver {
>>  /// # Example
>>  ///
>>  ///```
>> -/// # use kernel::{bindings, c_str, of, platform};
>> +/// # mod mydriver {
>> +/// #
>> +/// # // Get this into the scope of the module to make the
>> assert_eq!() buildable
>> +/// # static __DOCTEST_ANCHOR: i32 = core::line!() as i32 - 4;
>> +/// #
>> +/// # use kernel::{c_str, of, platform, prelude::*};
>> +/// #
>> +/// struct MyDriver {
>> +///     pdev: platform::Device,
>> +/// }
>>  ///
>> -/// struct MyDriver;
>> +/// struct Info(u32);
>>  ///
>>  /// kernel::of_device_table!(
>>  ///     OF_TABLE,
>>  ///     MODULE_OF_TABLE,
>>  ///     <MyDriver as platform::Driver>::IdInfo,
>> -///     [
>> -///         (of::DeviceId::new(c_str!("test,device")), ())
>> -///     ]
>> +///     [(of::DeviceId::new(c_str!("test,rust-device")), Info(42))]
>>  /// );
>>  ///
>>  /// impl platform::Driver for MyDriver {
>> -///     type IdInfo = ();
>> +///     type IdInfo = Info;
>>  ///     const OF_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)
>> +///     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");
>> +///
>> +///         assert_eq!(info.unwrap().0, 42);
>> +///
>> +///         let drvdata = KBox::new(Self { pdev: pdev.clone() },
>> GFP_KERNEL)?;
>> +///
>> +///         Ok(drvdata.into())
>> +///     }
>> +/// }
>> +///
>> +/// impl Drop for MyDriver {
>> +///     fn drop(&mut self) {
>> +///         dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver
>> sample.\n");
>>  ///     }
>>  /// }
>> +///
>> +/// kernel::module_platform_driver! {
>> +///     type: MyDriver,
>> +///     name: "rust_driver_platform",
>> +///     author: "Danilo Krummrich",
>> +///     description: "Rust Platform driver",
>> +///     license: "GPL v2",
>> +/// }
>> +/// # }
>>  ///```
>>  pub trait Driver {
>>      /// The type holding information about each device id supported
>> by the driver.
>> diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
>> index 70126b750426..6d468193cdd8 100644
>> --- a/samples/rust/Kconfig
>> +++ b/samples/rust/Kconfig
>> @@ -41,16 +41,6 @@ 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 761d13fff018..2f5b6bdb2fa5 100644
>> --- a/samples/rust/Makefile
>> +++ b/samples/rust/Makefile
>> @@ -4,7 +4,6 @@ ccflags-y += -I$(src)				# needed for trace events
>>  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
>>
>>  rust_print-y := rust_print_main.o rust_print_events.o
>>
>> diff --git a/samples/rust/rust_driver_platform.rs
>> b/samples/rust/rust_driver_platform.rs
>> deleted file mode 100644
>> index 2f0dbbe69e10..000000000000
>> --- a/samples/rust/rust_driver_platform.rs
>> +++ /dev/null
>> @@ -1,49 +0,0 @@
>> -// 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!("test,rust-device")), Info(42))]
>> -);
>> -
>> -impl platform::Driver for SampleDriver {
>> -    type IdInfo = Info;
>> -    const OF_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");
>> -
>> -        if let Some(info) = info {
>> -            dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n",
>> info.0);
>> -        }
>> -
>> -        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",
>> -}
>>


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 09/13] rust: pci: implement I/O mappable `pci::Bar`
  2024-12-05 14:14 ` [PATCH v4 09/13] rust: pci: implement I/O mappable `pci::Bar` Danilo Krummrich
@ 2024-12-06 10:44   ` Philipp Stanner
  0 siblings, 0 replies; 45+ messages in thread
From: Philipp Stanner @ 2024-12-06 10: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, ajanulgu, lyude, robh,
	daniel.almeida, saravanak, dirk.behme, j, fabien.parent,
	chrisi.schrefl
  Cc: rust-for-linux, linux-kernel, linux-pci, devicetree

btw the PCI subsystem consistently always writes the acronym all-
uppercase (PCI) in commit messages and comments.

If you go for a v5, might make sense to adjust that.

Same applies for patch 8.


P.


On Thu, 2024-12-05 at 15:14 +0100, Danilo Krummrich wrote:
> 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 748912c1c994..0a32a5935c9c 100644
> --- a/rust/kernel/pci.rs
> +++ b/rust/kernel/pci.rs
> @@ -5,10 +5,14 @@
>  //! 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,
> +    io::IoRaw,
>      str::CStr,
>      types::{ARef, ForeignOwnable},
>      ThisModule,
> @@ -239,9 +243,115 @@ 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 `IoRaw` 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: IoRaw<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);
> +        }
> +
> +        let io = match IoRaw::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.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 {
> +        // SAFETY: By the type invariant of `Self`, the MMIO range
> in `self.io` is properly mapped.
> +        unsafe { Io::from_raw(&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 {


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 02/13] rust: implement generic driver registration
  2024-12-05 14:14 ` [PATCH v4 02/13] rust: implement generic driver registration Danilo Krummrich
@ 2024-12-06 13:57   ` Alice Ryhl
  2024-12-06 18:13     ` Danilo Krummrich
  0 siblings, 1 reply; 45+ messages in thread
From: Alice Ryhl @ 2024-12-06 13:57 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree, Wedson Almeida Filho

On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> 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

typo

> 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>

[...]

> +/// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform,
> +/// Amba, etc.) to provide 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;

This Default implementation doesn't seem useful. You initialize it and
then `register` calls a C function to initialize it. Having `register`
return an `impl PinInit` seems like it would work better here.

> +    /// Registers a driver.
> +    ///
> +    /// On success, `reg` must remain pinned and valid until the matching call to
> +    /// [`RegistrationOps::unregister`].
> +    fn register(
> +        reg: &mut Self::RegType,

If the intent is that RegType is going to be the raw bindings:: type,
then this isn't going to work because you're creating &mut references
to the raw type without a Opaque wrapper in between.

> +        name: &'static CStr,
> +        module: &'static ThisModule,
> +    ) -> Result;
> +
> +    /// Unregisters a driver previously registered with [`RegistrationOps::register`].
> +    fn unregister(reg: &mut Self::RegType);

I believe this handles pinning incorrectly. You can't hand out &mut
references to pinned values.

Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-05 14:14 ` [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
@ 2024-12-06 14:01   ` Alice Ryhl
  2024-12-09 10:44     ` Danilo Krummrich
  2024-12-06 15:25   ` Alice Ryhl
  1 sibling, 1 reply; 45+ messages in thread
From: Alice Ryhl @ 2024-12-06 14:01 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>

> +/// 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>);

It seems more natural for this to be a wrapper around
`Opaque<bindings::pci_dev>`. Then you can have both &Device and
ARef<Device> depending on whether you want to hold a refcount or not.

Alice

> +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.47.0
>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types
  2024-12-05 14:14 ` [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types Danilo Krummrich
@ 2024-12-06 14:13   ` Alice Ryhl
  2024-12-11 14:52   ` Daniel Almeida
  1 sibling, 0 replies; 45+ messages in thread
From: Alice Ryhl @ 2024-12-06 14:13 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Thu, Dec 5, 2024 at 3:16 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.
>
> `IoRaw` 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
> `IoRaw` accordingly.
>
> To ensure that I/O mapped memory can't out-live the device it may be
> bound to, subsystems must embed the corresponding I/O memory type (e.g.
> pci::Bar) into a `Devres` container, such that it gets revoked once the
> device is unbound.
>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

One nit below. With that addressed:

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

> +impl<const SIZE: usize> Io<SIZE> {
> +    /// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size
> +    /// `maxsize`.
> +    pub unsafe fn from_raw<'a>(raw: &IoRaw<SIZE>) -> &'a Self {

I would use this signature:

pub unsafe fn from_raw(raw: &IoRaw<SIZE>) -> &Self;

Otherwise, you're saying that the returned reference is allowed to
outlive the IoRaw instance, which wouldn't be okay.

Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 05/13] rust: add `Revocable` type
  2024-12-05 14:14 ` [PATCH v4 05/13] rust: add `Revocable` type Danilo Krummrich
@ 2024-12-06 15:11   ` Alice Ryhl
  2024-12-09 10:40     ` Danilo Krummrich
  0 siblings, 1 reply; 45+ messages in thread
From: Alice Ryhl @ 2024-12-06 15:11 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree, Wedson Almeida Filho

On Thu, Dec 5, 2024 at 3:16 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>
> Co-developed-by: Danilo Krummrich <dakr@kernel.org>
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>

Overall looks reasonable, but some comments below.

> +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 `ptr` is fully initialized; on error
> +            // `ptr` is not partially initialized and does not need to be dropped.
> +            data <- unsafe {
> +                Opaque::try_ffi_init(|ptr: *mut T| {
> +                    init::PinInit::<T, core::convert::Infallible>::__pinned_init(data, ptr)
> +                })

This is pretty awkward ... could we have an Opaque::pin_init that
takes an `impl PinInit instead of using fii_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) {
> +            // 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(RevocableGuard::new(self.data.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.get() })
> +        } else {
> +            None
> +        }
> +    }
> +
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that there are no more concurrent users of the revocable object.
> +    unsafe fn revoke_internal(&self, sync: bool) {

This boolean could be a const generic to enforce that it must be a
compile-time value.

Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-05 14:14 ` [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
  2024-12-06 14:01   ` Alice Ryhl
@ 2024-12-06 15:25   ` Alice Ryhl
  1 sibling, 0 replies; 45+ messages in thread
From: Alice Ryhl @ 2024-12-06 15:25 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> +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::get_device(&mut (*pdev).dev) };

It shouldn't be necessary to increment the refcount here.

Also, the mutable reference is illegal because it references a C type
without a Opaque wrapper. Please use the addr_of_mut! macro instead of
a mutable reference.

Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 02/13] rust: implement generic driver registration
  2024-12-06 13:57   ` Alice Ryhl
@ 2024-12-06 18:13     ` Danilo Krummrich
  0 siblings, 0 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-06 18: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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree, Wedson Almeida Filho

On Fri, Dec 06, 2024 at 02:57:19PM +0100, Alice Ryhl wrote:
> On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > 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
> 
> typo
> 
> > 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>
> 
> [...]
> 
> > +/// The [`RegistrationOps`] trait serves as generic interface for subsystems (e.g., PCI, Platform,
> > +/// Amba, etc.) to provide 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;
> 
> This Default implementation doesn't seem useful. You initialize it and

I think it is -- `RegType` is always the raw bindings:: type and in
`Registration::new` in `Opaque::try_ffi_init` we call
`ptr.write(T::RegType::default())` for - since `RegType` is a raw bindings::
type - zero initialization.

> then `register` calls a C function to initialize it. Having `register`
> return an `impl PinInit` seems like it would work better here.

This would work as well, but it would effectively move the common code from
`Registration::new` to the bus specific type.

I think it's quite nice that the bus specific code does not need to care about
messing with `try_pin_init`, `Opaque::try_ffi_init`, zero initialization, etc.,
but just needs to assign the relevant fields and call register.

> 
> > +    /// Registers a driver.
> > +    ///
> > +    /// On success, `reg` must remain pinned and valid until the matching call to
> > +    /// [`RegistrationOps::unregister`].
> > +    fn register(
> > +        reg: &mut Self::RegType,
> 
> If the intent is that RegType is going to be the raw bindings:: type,
> then this isn't going to work because you're creating &mut references
> to the raw type without a Opaque wrapper in between.

True, that seems unsound. Since this is called from when the corresponding
`Opaque` wrapper is created, I think we need to fall back to a raw pointer then
and make `register` and `unregister` unsafe.

I don't think that too big of a deal though, since those two should never be
called from anywhere else than `Registration:new` or `Registration::drop`.

> 
> > +        name: &'static CStr,
> > +        module: &'static ThisModule,
> > +    ) -> Result;
> > +
> > +    /// Unregisters a driver previously registered with [`RegistrationOps::register`].
> > +    fn unregister(reg: &mut Self::RegType);
> 
> I believe this handles pinning incorrectly. You can't hand out &mut
> references to pinned values.

Same as above.

> 
> Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId`
  2024-12-05 14:14 ` [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
@ 2024-12-07  1:14   ` Fabien Parent
  2024-12-09 10:45     ` Danilo Krummrich
  0 siblings, 1 reply; 45+ messages in thread
From: Fabien Parent @ 2024-12-07  1:14 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, dirk.behme, j, chrisi.schrefl,
	rust-for-linux, linux-kernel, linux-pci, devicetree,
	Wedson Almeida Filho

Hi Danilo,

> +/// 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,
> +                    "__", module_path!(),
> +                    "_", line!(),
> +                    "_", stringify!($table_name),
> +                    "_device_table")

This doesn't work on top of v6.13-rc1. The alias symbol name has been
renamed by commit 054a9cd395a7 (modpost: rename alias
symbol for MODULE_DEVICE_TABLE())

I applied the following change to make it work again:
-            concat!("__mod_", $table_type,
+            concat!("__mod_device_table__", $table_type,
                     "__", module_path!(),
                     "_", line!(),
-                    "_", stringify!($table_name),
-                    "_device_table")
+                    "_", stringify!($table_name))


> +        ]
> +        static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
> +            unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
> +    };
> +}

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 05/13] rust: add `Revocable` type
  2024-12-06 15:11   ` Alice Ryhl
@ 2024-12-09 10:40     ` Danilo Krummrich
  0 siblings, 0 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-09 10:40 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree, Wedson Almeida Filho

On Fri, Dec 06, 2024 at 04:11:39PM +0100, Alice Ryhl wrote:
> On Thu, Dec 5, 2024 at 3:16 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>
> > Co-developed-by: Danilo Krummrich <dakr@kernel.org>
> > Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> 
> Overall looks reasonable, but some comments below.
> 
> > +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 `ptr` is fully initialized; on error
> > +            // `ptr` is not partially initialized and does not need to be dropped.
> > +            data <- unsafe {
> > +                Opaque::try_ffi_init(|ptr: *mut T| {
> > +                    init::PinInit::<T, core::convert::Infallible>::__pinned_init(data, ptr)
> > +                })
> 
> This is pretty awkward ... could we have an Opaque::pin_init that
> takes an `impl PinInit instead of using fii_init?

Using ffi_init was your suggestion. :) But I agree, having Opaque::pin_init
would be more convenient. I can add a patch for that.

> 
> > +            },
> > +        })
> > +    }
> > +
> > +    /// 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) {
> > +            // 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(RevocableGuard::new(self.data.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.get() })
> > +        } else {
> > +            None
> > +        }
> > +    }
> > +
> > +    /// # Safety
> > +    ///
> > +    /// Callers must ensure that there are no more concurrent users of the revocable object.
> > +    unsafe fn revoke_internal(&self, sync: bool) {
> 
> This boolean could be a const generic to enforce that it must be a
> compile-time value.

Agreed.

> 
> Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-06 14:01   ` Alice Ryhl
@ 2024-12-09 10:44     ` Danilo Krummrich
  2024-12-10 10:55       ` Alice Ryhl
  0 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-09 10:44 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Fri, Dec 06, 2024 at 03:01:18PM +0100, Alice Ryhl wrote:
> On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> 
> > +/// 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>);
> 
> It seems more natural for this to be a wrapper around
> `Opaque<bindings::pci_dev>`. Then you can have both &Device and
> ARef<Device> depending on whether you want to hold a refcount or not.

Yeah, but then every bus device has to re-implement the refcount dance we
already have in `device::Device` for the underlying base `struct device`.

I forgot to mention this in my previous reply to Boqun, but we even documented
it this way in `device::Device` [1].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/device.rs#n28

> 
> Alice
> 
> > +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.47.0
> >

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId`
  2024-12-07  1:14   ` Fabien Parent
@ 2024-12-09 10:45     ` Danilo Krummrich
  0 siblings, 0 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-09 10:45 UTC (permalink / raw)
  To: Fabien Parent
  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, dirk.behme, j, chrisi.schrefl,
	rust-for-linux, linux-kernel, linux-pci, devicetree,
	Wedson Almeida Filho

On Fri, Dec 06, 2024 at 05:14:00PM -0800, Fabien Parent wrote:
> Hi Danilo,
> 
> > +/// 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,
> > +                    "__", module_path!(),
> > +                    "_", line!(),
> > +                    "_", stringify!($table_name),
> > +                    "_device_table")
> 
> This doesn't work on top of v6.13-rc1. The alias symbol name has been
> renamed by commit 054a9cd395a7 (modpost: rename alias
> symbol for MODULE_DEVICE_TABLE())
> 
> I applied the following change to make it work again:
> -            concat!("__mod_", $table_type,
> +            concat!("__mod_device_table__", $table_type,
>                      "__", module_path!(),
>                      "_", line!(),
> -                    "_", stringify!($table_name),
> -                    "_device_table")
> +                    "_", stringify!($table_name))

Good catch, thanks!

> 
> 
> > +        ]
> > +        static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] =
> > +            unsafe { core::mem::transmute_copy($table_name.raw_ids()) };
> > +    };
> > +}

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 11/13] rust: of: add `of::DeviceId` abstraction
  2024-12-05 14:14 ` [PATCH v4 11/13] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
@ 2024-12-09 21:22   ` Rob Herring
  0 siblings, 0 replies; 45+ messages in thread
From: Rob Herring @ 2024-12-09 21:22 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, dirk.behme, j, fabien.parent, chrisi.schrefl,
	rust-for-linux, linux-kernel, linux-pci, devicetree

On Thu, Dec 05, 2024 at 03:14:42PM +0100, 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/kernel/lib.rs |  1 +
>  rust/kernel/of.rs  | 57 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 59 insertions(+)
>  create mode 100644 rust/kernel/of.rs

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
  2024-12-05 14:14 ` [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
@ 2024-12-09 22:37   ` Rob Herring
  2024-12-09 23:13     ` Danilo Krummrich
  2024-12-10  7:46     ` Greg KH
  0 siblings, 2 replies; 45+ messages in thread
From: Rob Herring @ 2024-12-09 22: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, dirk.behme, j, fabien.parent, chrisi.schrefl,
	rust-for-linux, linux-kernel, linux-pci, devicetree

On Thu, Dec 05, 2024 at 03:14:43PM +0100, 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 |   2 +
>  rust/helpers/helpers.c          |   1 +
>  rust/helpers/platform.c         |  13 ++
>  rust/kernel/lib.rs              |   1 +
>  rust/kernel/platform.rs         | 222 ++++++++++++++++++++++++++++++++
>  6 files changed, 240 insertions(+)
>  create mode 100644 rust/helpers/platform.c
>  create mode 100644 rust/kernel/platform.rs
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7d6bb4b15d2c..365fc48b7041 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7034,6 +7034,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 6d7a68e2ecb7..e9fdceb568b8 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -20,9 +20,11 @@
>  #include <linux/jump_label.h>
>  #include <linux/mdio.h>
>  #include <linux/miscdevice.h>
> +#include <linux/of_device.h>
>  #include <linux/pci.h>
>  #include <linux/phy.h>
>  #include <linux/pid_namespace.h>
> +#include <linux/platform_device.h>
>  #include <linux/poll.h>
>  #include <linux/refcount.h>
>  #include <linux/sched.h>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 3fda33cd42d4..0640b7e115be 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -20,6 +20,7 @@
>  #include "kunit.c"
>  #include "mutex.c"
>  #include "page.c"
> +#include "platform.c"
>  #include "pci.c"
>  #include "pid_namespace.c"
>  #include "rbtree.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 7a0e4c82ad0c..cc8f48aa162b 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -59,6 +59,7 @@
>  pub mod of;
>  pub mod page;
>  pub mod pid_namespace;
> +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..868cfddb75a2
> --- /dev/null
> +++ b/rust/kernel/platform.rs
> @@ -0,0 +1,222 @@
> +// 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, 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::OF_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> {
> +    #[cfg(CONFIG_OF)]
> +    fn of_id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> +        let table = T::OF_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_ref().as_raw()`.
> +        let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_ref().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.
> +            let id = unsafe { &*raw_id.cast::<of::DeviceId>() };
> +
> +            Some(table.info(<of::DeviceId as crate::device_id::RawDeviceId>::index(id)))
> +        }
> +    }
> +
> +    #[cfg(not(CONFIG_OF))]
> +    fn of_id_info(_pdev: &Device) -> Option<&'static T::IdInfo> {
> +        None
> +    }
> +
> +    // Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular
> +    // table, it means we don't have a match in there. If we don't match any of the ID tables, it
> +    // means we were matched by name.
> +    fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> +        let id = Self::of_id_info(pdev);
> +        if id.is_some() {
> +            return id;
> +        }
> +
> +        None
> +    }

These methods are going to have to be duplicated by every bus type which 
can do DT matching (and later ACPI). Can't this be moved to be part of 
the common Driver trait.


I'll say it again for Greg to comment (doubtful he will look at v3 
again). Really, I think we should also align the probe method interface 
across bus types. That means getting rid of the 'id' in the PCI probe 
(or add it for everyone). Most drivers never need it. The typical case 
is needing nothing or the matched data. In a quick scan[1], there's 
only a handful of cases. So I think probe should match the common 
scenario and make retrieving the id match explicit if needed.

Rob

[1] git grep -W 'const struct pci_device_id \*' drivers/ | grep -P 'id->(?!driver_data)'

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
  2024-12-09 22:37   ` Rob Herring
@ 2024-12-09 23:13     ` Danilo Krummrich
  2024-12-10  7:46     ` Greg KH
  1 sibling, 0 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-09 23:13 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, dirk.behme, j, fabien.parent, chrisi.schrefl,
	rust-for-linux, linux-kernel, linux-pci, devicetree

On Mon, Dec 09, 2024 at 04:37:06PM -0600, Rob Herring wrote:
> On Thu, Dec 05, 2024 at 03:14:43PM +0100, 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 |   2 +
> >  rust/helpers/helpers.c          |   1 +
> >  rust/helpers/platform.c         |  13 ++
> >  rust/kernel/lib.rs              |   1 +
> >  rust/kernel/platform.rs         | 222 ++++++++++++++++++++++++++++++++
> >  6 files changed, 240 insertions(+)
> >  create mode 100644 rust/helpers/platform.c
> >  create mode 100644 rust/kernel/platform.rs
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 7d6bb4b15d2c..365fc48b7041 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -7034,6 +7034,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 6d7a68e2ecb7..e9fdceb568b8 100644
> > --- a/rust/bindings/bindings_helper.h
> > +++ b/rust/bindings/bindings_helper.h
> > @@ -20,9 +20,11 @@
> >  #include <linux/jump_label.h>
> >  #include <linux/mdio.h>
> >  #include <linux/miscdevice.h>
> > +#include <linux/of_device.h>
> >  #include <linux/pci.h>
> >  #include <linux/phy.h>
> >  #include <linux/pid_namespace.h>
> > +#include <linux/platform_device.h>
> >  #include <linux/poll.h>
> >  #include <linux/refcount.h>
> >  #include <linux/sched.h>
> > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > index 3fda33cd42d4..0640b7e115be 100644
> > --- a/rust/helpers/helpers.c
> > +++ b/rust/helpers/helpers.c
> > @@ -20,6 +20,7 @@
> >  #include "kunit.c"
> >  #include "mutex.c"
> >  #include "page.c"
> > +#include "platform.c"
> >  #include "pci.c"
> >  #include "pid_namespace.c"
> >  #include "rbtree.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 7a0e4c82ad0c..cc8f48aa162b 100644
> > --- a/rust/kernel/lib.rs
> > +++ b/rust/kernel/lib.rs
> > @@ -59,6 +59,7 @@
> >  pub mod of;
> >  pub mod page;
> >  pub mod pid_namespace;
> > +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..868cfddb75a2
> > --- /dev/null
> > +++ b/rust/kernel/platform.rs
> > @@ -0,0 +1,222 @@
> > +// 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, 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::OF_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> {
> > +    #[cfg(CONFIG_OF)]
> > +    fn of_id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > +        let table = T::OF_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_ref().as_raw()`.
> > +        let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_ref().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.
> > +            let id = unsafe { &*raw_id.cast::<of::DeviceId>() };
> > +
> > +            Some(table.info(<of::DeviceId as crate::device_id::RawDeviceId>::index(id)))
> > +        }
> > +    }
> > +
> > +    #[cfg(not(CONFIG_OF))]
> > +    fn of_id_info(_pdev: &Device) -> Option<&'static T::IdInfo> {
> > +        None
> > +    }
> > +
> > +    // Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular
> > +    // table, it means we don't have a match in there. If we don't match any of the ID tables, it
> > +    // means we were matched by name.
> > +    fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > +        let id = Self::of_id_info(pdev);
> > +        if id.is_some() {
> > +            return id;
> > +        }
> > +
> > +        None
> > +    }
> 
> These methods are going to have to be duplicated by every bus type which 
> can do DT matching (and later ACPI). Can't this be moved to be part of 
> the common Driver trait.

As mentioned in v3, I agree, but I'd prefer to do this in a follow up series.

> 
> 
> I'll say it again for Greg to comment (doubtful he will look at v3 
> again). Really, I think we should also align the probe method interface 
> across bus types. That means getting rid of the 'id' in the PCI probe 
> (or add it for everyone). Most drivers never need it. The typical case 
> is needing nothing or the matched data. In a quick scan[1], there's 
> only a handful of cases. So I think probe should match the common 
> scenario and make retrieving the id match explicit if needed.

I agree, but I will keep it until Greg commented on it, since I explicitly
promised to add it on request.

> 
> Rob
> 
> [1] git grep -W 'const struct pci_device_id \*' drivers/ | grep -P 'id->(?!driver_data)'

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
  2024-12-09 22:37   ` Rob Herring
  2024-12-09 23:13     ` Danilo Krummrich
@ 2024-12-10  7:46     ` Greg KH
  2024-12-10  9:34       ` Danilo Krummrich
  1 sibling, 1 reply; 45+ messages in thread
From: Greg KH @ 2024-12-10  7:46 UTC (permalink / raw)
  To: Rob Herring
  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, daniel.almeida, saravanak, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Mon, Dec 09, 2024 at 04:37:06PM -0600, Rob Herring wrote:
> On Thu, Dec 05, 2024 at 03:14:43PM +0100, 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 |   2 +
> >  rust/helpers/helpers.c          |   1 +
> >  rust/helpers/platform.c         |  13 ++
> >  rust/kernel/lib.rs              |   1 +
> >  rust/kernel/platform.rs         | 222 ++++++++++++++++++++++++++++++++
> >  6 files changed, 240 insertions(+)
> >  create mode 100644 rust/helpers/platform.c
> >  create mode 100644 rust/kernel/platform.rs
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 7d6bb4b15d2c..365fc48b7041 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -7034,6 +7034,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 6d7a68e2ecb7..e9fdceb568b8 100644
> > --- a/rust/bindings/bindings_helper.h
> > +++ b/rust/bindings/bindings_helper.h
> > @@ -20,9 +20,11 @@
> >  #include <linux/jump_label.h>
> >  #include <linux/mdio.h>
> >  #include <linux/miscdevice.h>
> > +#include <linux/of_device.h>
> >  #include <linux/pci.h>
> >  #include <linux/phy.h>
> >  #include <linux/pid_namespace.h>
> > +#include <linux/platform_device.h>
> >  #include <linux/poll.h>
> >  #include <linux/refcount.h>
> >  #include <linux/sched.h>
> > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > index 3fda33cd42d4..0640b7e115be 100644
> > --- a/rust/helpers/helpers.c
> > +++ b/rust/helpers/helpers.c
> > @@ -20,6 +20,7 @@
> >  #include "kunit.c"
> >  #include "mutex.c"
> >  #include "page.c"
> > +#include "platform.c"
> >  #include "pci.c"
> >  #include "pid_namespace.c"
> >  #include "rbtree.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 7a0e4c82ad0c..cc8f48aa162b 100644
> > --- a/rust/kernel/lib.rs
> > +++ b/rust/kernel/lib.rs
> > @@ -59,6 +59,7 @@
> >  pub mod of;
> >  pub mod page;
> >  pub mod pid_namespace;
> > +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..868cfddb75a2
> > --- /dev/null
> > +++ b/rust/kernel/platform.rs
> > @@ -0,0 +1,222 @@
> > +// 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, 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::OF_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> {
> > +    #[cfg(CONFIG_OF)]
> > +    fn of_id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > +        let table = T::OF_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_ref().as_raw()`.
> > +        let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_ref().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.
> > +            let id = unsafe { &*raw_id.cast::<of::DeviceId>() };
> > +
> > +            Some(table.info(<of::DeviceId as crate::device_id::RawDeviceId>::index(id)))
> > +        }
> > +    }
> > +
> > +    #[cfg(not(CONFIG_OF))]
> > +    fn of_id_info(_pdev: &Device) -> Option<&'static T::IdInfo> {
> > +        None
> > +    }
> > +
> > +    // Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular
> > +    // table, it means we don't have a match in there. If we don't match any of the ID tables, it
> > +    // means we were matched by name.
> > +    fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > +        let id = Self::of_id_info(pdev);
> > +        if id.is_some() {
> > +            return id;
> > +        }
> > +
> > +        None
> > +    }
> 
> These methods are going to have to be duplicated by every bus type which 
> can do DT matching (and later ACPI). Can't this be moved to be part of 
> the common Driver trait.
> 
> 
> I'll say it again for Greg to comment (doubtful he will look at v3 
> again). Really, I think we should also align the probe method interface 
> across bus types. That means getting rid of the 'id' in the PCI probe 
> (or add it for everyone). Most drivers never need it. The typical case 
> is needing nothing or the matched data. In a quick scan[1], there's 
> only a handful of cases. So I think probe should match the common 
> scenario and make retrieving the id match explicit if needed.

If there's a way for the probe callback to get access to the id that
caused it to match, then it's fine with me to remove it.  But is that
possible?  Many many drivers encode "stuff" in the id that the driver
needs to know (quirks, functions, etc.)

If that's not possible, then it needs to stay.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
  2024-12-10  7:46     ` Greg KH
@ 2024-12-10  9:34       ` Danilo Krummrich
  2024-12-10  9:40         ` Greg KH
  0 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-10  9:34 UTC (permalink / raw)
  To: Greg KH
  Cc: Rob Herring, 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Tue, Dec 10, 2024 at 08:46:29AM +0100, Greg KH wrote:
> On Mon, Dec 09, 2024 at 04:37:06PM -0600, Rob Herring wrote:
> > On Thu, Dec 05, 2024 at 03:14:43PM +0100, 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 |   2 +
> > >  rust/helpers/helpers.c          |   1 +
> > >  rust/helpers/platform.c         |  13 ++
> > >  rust/kernel/lib.rs              |   1 +
> > >  rust/kernel/platform.rs         | 222 ++++++++++++++++++++++++++++++++
> > >  6 files changed, 240 insertions(+)
> > >  create mode 100644 rust/helpers/platform.c
> > >  create mode 100644 rust/kernel/platform.rs
> > > 
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 7d6bb4b15d2c..365fc48b7041 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -7034,6 +7034,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 6d7a68e2ecb7..e9fdceb568b8 100644
> > > --- a/rust/bindings/bindings_helper.h
> > > +++ b/rust/bindings/bindings_helper.h
> > > @@ -20,9 +20,11 @@
> > >  #include <linux/jump_label.h>
> > >  #include <linux/mdio.h>
> > >  #include <linux/miscdevice.h>
> > > +#include <linux/of_device.h>
> > >  #include <linux/pci.h>
> > >  #include <linux/phy.h>
> > >  #include <linux/pid_namespace.h>
> > > +#include <linux/platform_device.h>
> > >  #include <linux/poll.h>
> > >  #include <linux/refcount.h>
> > >  #include <linux/sched.h>
> > > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > > index 3fda33cd42d4..0640b7e115be 100644
> > > --- a/rust/helpers/helpers.c
> > > +++ b/rust/helpers/helpers.c
> > > @@ -20,6 +20,7 @@
> > >  #include "kunit.c"
> > >  #include "mutex.c"
> > >  #include "page.c"
> > > +#include "platform.c"
> > >  #include "pci.c"
> > >  #include "pid_namespace.c"
> > >  #include "rbtree.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 7a0e4c82ad0c..cc8f48aa162b 100644
> > > --- a/rust/kernel/lib.rs
> > > +++ b/rust/kernel/lib.rs
> > > @@ -59,6 +59,7 @@
> > >  pub mod of;
> > >  pub mod page;
> > >  pub mod pid_namespace;
> > > +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..868cfddb75a2
> > > --- /dev/null
> > > +++ b/rust/kernel/platform.rs
> > > @@ -0,0 +1,222 @@
> > > +// 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, 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::OF_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> {
> > > +    #[cfg(CONFIG_OF)]
> > > +    fn of_id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > > +        let table = T::OF_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_ref().as_raw()`.
> > > +        let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_ref().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.
> > > +            let id = unsafe { &*raw_id.cast::<of::DeviceId>() };
> > > +
> > > +            Some(table.info(<of::DeviceId as crate::device_id::RawDeviceId>::index(id)))
> > > +        }
> > > +    }
> > > +
> > > +    #[cfg(not(CONFIG_OF))]
> > > +    fn of_id_info(_pdev: &Device) -> Option<&'static T::IdInfo> {
> > > +        None
> > > +    }
> > > +
> > > +    // Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular
> > > +    // table, it means we don't have a match in there. If we don't match any of the ID tables, it
> > > +    // means we were matched by name.
> > > +    fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > > +        let id = Self::of_id_info(pdev);
> > > +        if id.is_some() {
> > > +            return id;
> > > +        }
> > > +
> > > +        None
> > > +    }
> > 
> > These methods are going to have to be duplicated by every bus type which 
> > can do DT matching (and later ACPI). Can't this be moved to be part of 
> > the common Driver trait.
> > 
> > 
> > I'll say it again for Greg to comment (doubtful he will look at v3 
> > again). Really, I think we should also align the probe method interface 
> > across bus types. That means getting rid of the 'id' in the PCI probe 
> > (or add it for everyone). Most drivers never need it. The typical case 
> > is needing nothing or the matched data. In a quick scan[1], there's 
> > only a handful of cases. So I think probe should match the common 
> > scenario and make retrieving the id match explicit if needed.
> 
> If there's a way for the probe callback to get access to the id that
> caused it to match, then it's fine with me to remove it.  But is that
> possible?  Many many drivers encode "stuff" in the id that the driver
> needs to know (quirks, functions, etc.)

The signature would be

`fn probe(dev: &mut Device, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>`

where `id_info` is the driver private data.

The other fields of struct pci_device_id should also be available from struct
pci_dev`, i.e. `dev`; just need to add the corresponding accessors for it.

- Danilo

> 
> If that's not possible, then it needs to stay.
> 
> thanks,
> 
> greg k-h

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
  2024-12-10  9:34       ` Danilo Krummrich
@ 2024-12-10  9:40         ` Greg KH
  0 siblings, 0 replies; 45+ messages in thread
From: Greg KH @ 2024-12-10  9:40 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Rob Herring, 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Tue, Dec 10, 2024 at 10:34:07AM +0100, Danilo Krummrich wrote:
> On Tue, Dec 10, 2024 at 08:46:29AM +0100, Greg KH wrote:
> > On Mon, Dec 09, 2024 at 04:37:06PM -0600, Rob Herring wrote:
> > > On Thu, Dec 05, 2024 at 03:14:43PM +0100, 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 |   2 +
> > > >  rust/helpers/helpers.c          |   1 +
> > > >  rust/helpers/platform.c         |  13 ++
> > > >  rust/kernel/lib.rs              |   1 +
> > > >  rust/kernel/platform.rs         | 222 ++++++++++++++++++++++++++++++++
> > > >  6 files changed, 240 insertions(+)
> > > >  create mode 100644 rust/helpers/platform.c
> > > >  create mode 100644 rust/kernel/platform.rs
> > > > 
> > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > index 7d6bb4b15d2c..365fc48b7041 100644
> > > > --- a/MAINTAINERS
> > > > +++ b/MAINTAINERS
> > > > @@ -7034,6 +7034,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 6d7a68e2ecb7..e9fdceb568b8 100644
> > > > --- a/rust/bindings/bindings_helper.h
> > > > +++ b/rust/bindings/bindings_helper.h
> > > > @@ -20,9 +20,11 @@
> > > >  #include <linux/jump_label.h>
> > > >  #include <linux/mdio.h>
> > > >  #include <linux/miscdevice.h>
> > > > +#include <linux/of_device.h>
> > > >  #include <linux/pci.h>
> > > >  #include <linux/phy.h>
> > > >  #include <linux/pid_namespace.h>
> > > > +#include <linux/platform_device.h>
> > > >  #include <linux/poll.h>
> > > >  #include <linux/refcount.h>
> > > >  #include <linux/sched.h>
> > > > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > > > index 3fda33cd42d4..0640b7e115be 100644
> > > > --- a/rust/helpers/helpers.c
> > > > +++ b/rust/helpers/helpers.c
> > > > @@ -20,6 +20,7 @@
> > > >  #include "kunit.c"
> > > >  #include "mutex.c"
> > > >  #include "page.c"
> > > > +#include "platform.c"
> > > >  #include "pci.c"
> > > >  #include "pid_namespace.c"
> > > >  #include "rbtree.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 7a0e4c82ad0c..cc8f48aa162b 100644
> > > > --- a/rust/kernel/lib.rs
> > > > +++ b/rust/kernel/lib.rs
> > > > @@ -59,6 +59,7 @@
> > > >  pub mod of;
> > > >  pub mod page;
> > > >  pub mod pid_namespace;
> > > > +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..868cfddb75a2
> > > > --- /dev/null
> > > > +++ b/rust/kernel/platform.rs
> > > > @@ -0,0 +1,222 @@
> > > > +// 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, 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::OF_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> {
> > > > +    #[cfg(CONFIG_OF)]
> > > > +    fn of_id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > > > +        let table = T::OF_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_ref().as_raw()`.
> > > > +        let raw_id = unsafe { bindings::of_match_device(table.as_ptr(), pdev.as_ref().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.
> > > > +            let id = unsafe { &*raw_id.cast::<of::DeviceId>() };
> > > > +
> > > > +            Some(table.info(<of::DeviceId as crate::device_id::RawDeviceId>::index(id)))
> > > > +        }
> > > > +    }
> > > > +
> > > > +    #[cfg(not(CONFIG_OF))]
> > > > +    fn of_id_info(_pdev: &Device) -> Option<&'static T::IdInfo> {
> > > > +        None
> > > > +    }
> > > > +
> > > > +    // Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular
> > > > +    // table, it means we don't have a match in there. If we don't match any of the ID tables, it
> > > > +    // means we were matched by name.
> > > > +    fn id_info(pdev: &Device) -> Option<&'static T::IdInfo> {
> > > > +        let id = Self::of_id_info(pdev);
> > > > +        if id.is_some() {
> > > > +            return id;
> > > > +        }
> > > > +
> > > > +        None
> > > > +    }
> > > 
> > > These methods are going to have to be duplicated by every bus type which 
> > > can do DT matching (and later ACPI). Can't this be moved to be part of 
> > > the common Driver trait.
> > > 
> > > 
> > > I'll say it again for Greg to comment (doubtful he will look at v3 
> > > again). Really, I think we should also align the probe method interface 
> > > across bus types. That means getting rid of the 'id' in the PCI probe 
> > > (or add it for everyone). Most drivers never need it. The typical case 
> > > is needing nothing or the matched data. In a quick scan[1], there's 
> > > only a handful of cases. So I think probe should match the common 
> > > scenario and make retrieving the id match explicit if needed.
> > 
> > If there's a way for the probe callback to get access to the id that
> > caused it to match, then it's fine with me to remove it.  But is that
> > possible?  Many many drivers encode "stuff" in the id that the driver
> > needs to know (quirks, functions, etc.)
> 
> The signature would be
> 
> `fn probe(dev: &mut Device, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>`
> 
> where `id_info` is the driver private data.
> 
> The other fields of struct pci_device_id should also be available from struct
> pci_dev`, i.e. `dev`; just need to add the corresponding accessors for it.

Ok, then yes, that's fine, let's make this generic and only pass in the
private data, I think that's what Rob is asking for, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-09 10:44     ` Danilo Krummrich
@ 2024-12-10 10:55       ` Alice Ryhl
  2024-12-10 22:38         ` Danilo Krummrich
  0 siblings, 1 reply; 45+ messages in thread
From: Alice Ryhl @ 2024-12-10 10: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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Mon, Dec 9, 2024 at 11:44 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Fri, Dec 06, 2024 at 03:01:18PM +0100, Alice Ryhl wrote:
> > On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> >
> > > +/// 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>);
> >
> > It seems more natural for this to be a wrapper around
> > `Opaque<bindings::pci_dev>`. Then you can have both &Device and
> > ARef<Device> depending on whether you want to hold a refcount or not.
>
> Yeah, but then every bus device has to re-implement the refcount dance we
> already have in `device::Device` for the underlying base `struct device`.
>
> I forgot to mention this in my previous reply to Boqun, but we even documented
> it this way in `device::Device` [1].
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/device.rs#n28

We could perhaps write a derive macro for AlwaysRefCounted that
delegates to the inner type? That way, we can have the best of both
worlds.

Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-10 10:55       ` Alice Ryhl
@ 2024-12-10 22:38         ` Danilo Krummrich
  2024-12-11 13:06           ` Alice Ryhl
  0 siblings, 1 reply; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-10 22:38 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Tue, Dec 10, 2024 at 11:55:33AM +0100, Alice Ryhl wrote:
> On Mon, Dec 9, 2024 at 11:44 AM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Fri, Dec 06, 2024 at 03:01:18PM +0100, Alice Ryhl wrote:
> > > On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> > >
> > > > +/// 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>);
> > >
> > > It seems more natural for this to be a wrapper around
> > > `Opaque<bindings::pci_dev>`. Then you can have both &Device and
> > > ARef<Device> depending on whether you want to hold a refcount or not.
> >
> > Yeah, but then every bus device has to re-implement the refcount dance we
> > already have in `device::Device` for the underlying base `struct device`.
> >
> > I forgot to mention this in my previous reply to Boqun, but we even documented
> > it this way in `device::Device` [1].
> >
> > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/device.rs#n28
> 
> We could perhaps write a derive macro for AlwaysRefCounted that
> delegates to the inner type? That way, we can have the best of both
> worlds.

Sounds interesting, how exactly would this work?

(I'll already send out a v5, but let's keep discussing this.)

> 
> Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
  2024-12-05 14:14 ` [PATCH v4 13/13] samples: rust: add Rust platform sample driver Danilo Krummrich
  2024-12-05 17:09   ` Dirk Behme
@ 2024-12-10 22:59   ` Rob Herring (Arm)
  1 sibling, 0 replies; 45+ messages in thread
From: Rob Herring (Arm) @ 2024-12-10 22:59 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: bhelgaas, dirk.behme, aliceryhl, fabien.parent, fujita.tomonori,
	lyude, alex.gaynor, devicetree, j, ajanulgu, gary, daniel.almeida,
	chrisi.schrefl, a.hindborg, ojeda, boqun.feng, linux-pci, lina,
	bjorn3_gh, tmgross, airlied, saravanak, benno.lossin, rafael,
	gregkh, linux-kernel, rust-for-linux, pstanner


On Thu, 05 Dec 2024 15:14:44 +0100, 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.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  MAINTAINERS                                  |  1 +
>  drivers/of/unittest-data/tests-platform.dtsi |  5 ++
>  samples/rust/Kconfig                         | 10 ++++
>  samples/rust/Makefile                        |  1 +
>  samples/rust/rust_driver_platform.rs         | 49 ++++++++++++++++++++
>  5 files changed, 66 insertions(+)
>  create mode 100644 samples/rust/rust_driver_platform.rs
> 

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-10 22:38         ` Danilo Krummrich
@ 2024-12-11 13:06           ` Alice Ryhl
  2024-12-11 14:32             ` Danilo Krummrich
  0 siblings, 1 reply; 45+ messages in thread
From: Alice Ryhl @ 2024-12-11 13:06 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Tue, Dec 10, 2024 at 11:38 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Tue, Dec 10, 2024 at 11:55:33AM +0100, Alice Ryhl wrote:
> > On Mon, Dec 9, 2024 at 11:44 AM Danilo Krummrich <dakr@kernel.org> wrote:
> > >
> > > On Fri, Dec 06, 2024 at 03:01:18PM +0100, Alice Ryhl wrote:
> > > > On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> > > >
> > > > > +/// 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>);
> > > >
> > > > It seems more natural for this to be a wrapper around
> > > > `Opaque<bindings::pci_dev>`. Then you can have both &Device and
> > > > ARef<Device> depending on whether you want to hold a refcount or not.
> > >
> > > Yeah, but then every bus device has to re-implement the refcount dance we
> > > already have in `device::Device` for the underlying base `struct device`.
> > >
> > > I forgot to mention this in my previous reply to Boqun, but we even documented
> > > it this way in `device::Device` [1].
> > >
> > > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/device.rs#n28
> >
> > We could perhaps write a derive macro for AlwaysRefCounted that
> > delegates to the inner type? That way, we can have the best of both
> > worlds.
>
> Sounds interesting, how exactly would this work?
>
> (I'll already send out a v5, but let's keep discussing this.)

Well, the derive macro could assume that the refcount is manipulated
in the same way as the inner type does it. I admit that the idea is
not fully formed, but if we can avoid wrapping ARef, that would be
ideal. It sounds like the only reason you don't do that is that it's
more unsafe, which the macro could reduce.


Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-11 13:06           ` Alice Ryhl
@ 2024-12-11 14:32             ` Danilo Krummrich
  2024-12-11 14:41               ` Greg KH
  2024-12-11 14:44               ` Alice Ryhl
  0 siblings, 2 replies; 45+ messages in thread
From: Danilo Krummrich @ 2024-12-11 14:32 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Wed, Dec 11, 2024 at 02:06:50PM +0100, Alice Ryhl wrote:
> On Tue, Dec 10, 2024 at 11:38 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Tue, Dec 10, 2024 at 11:55:33AM +0100, Alice Ryhl wrote:
> > > On Mon, Dec 9, 2024 at 11:44 AM Danilo Krummrich <dakr@kernel.org> wrote:
> > > >
> > > > On Fri, Dec 06, 2024 at 03:01:18PM +0100, Alice Ryhl wrote:
> > > > > On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> > > > >
> > > > > > +/// 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>);
> > > > >
> > > > > It seems more natural for this to be a wrapper around
> > > > > `Opaque<bindings::pci_dev>`. Then you can have both &Device and
> > > > > ARef<Device> depending on whether you want to hold a refcount or not.
> > > >
> > > > Yeah, but then every bus device has to re-implement the refcount dance we
> > > > already have in `device::Device` for the underlying base `struct device`.
> > > >
> > > > I forgot to mention this in my previous reply to Boqun, but we even documented
> > > > it this way in `device::Device` [1].
> > > >
> > > > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/device.rs#n28
> > >
> > > We could perhaps write a derive macro for AlwaysRefCounted that
> > > delegates to the inner type? That way, we can have the best of both
> > > worlds.
> >
> > Sounds interesting, how exactly would this work?
> >
> > (I'll already send out a v5, but let's keep discussing this.)
> 
> Well, the derive macro could assume that the refcount is manipulated
> in the same way as the inner type does it. I admit that the idea is
> not fully formed, but if we can avoid wrapping ARef, that would be
> ideal.

If we can get this to work, I agree it's a good solution.

What do you think about making this a follow up of this series?

> It sounds like the only reason you don't do that is that it's
> more unsafe, which the macro could reduce.

Exactly, yes.

> 
> 
> Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-11 14:32             ` Danilo Krummrich
@ 2024-12-11 14:41               ` Greg KH
  2024-12-11 14:42                 ` Greg KH
  2024-12-11 14:44               ` Alice Ryhl
  1 sibling, 1 reply; 45+ messages in thread
From: Greg KH @ 2024-12-11 14:41 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Alice Ryhl, 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Wed, Dec 11, 2024 at 03:32:21PM +0100, Danilo Krummrich wrote:
> On Wed, Dec 11, 2024 at 02:06:50PM +0100, Alice Ryhl wrote:
> > On Tue, Dec 10, 2024 at 11:38 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > >
> > > On Tue, Dec 10, 2024 at 11:55:33AM +0100, Alice Ryhl wrote:
> > > > On Mon, Dec 9, 2024 at 11:44 AM Danilo Krummrich <dakr@kernel.org> wrote:
> > > > >
> > > > > On Fri, Dec 06, 2024 at 03:01:18PM +0100, Alice Ryhl wrote:
> > > > > > On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> > > > > >
> > > > > > > +/// 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>);
> > > > > >
> > > > > > It seems more natural for this to be a wrapper around
> > > > > > `Opaque<bindings::pci_dev>`. Then you can have both &Device and
> > > > > > ARef<Device> depending on whether you want to hold a refcount or not.
> > > > >
> > > > > Yeah, but then every bus device has to re-implement the refcount dance we
> > > > > already have in `device::Device` for the underlying base `struct device`.
> > > > >
> > > > > I forgot to mention this in my previous reply to Boqun, but we even documented
> > > > > it this way in `device::Device` [1].
> > > > >
> > > > > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/device.rs#n28
> > > >
> > > > We could perhaps write a derive macro for AlwaysRefCounted that
> > > > delegates to the inner type? That way, we can have the best of both
> > > > worlds.
> > >
> > > Sounds interesting, how exactly would this work?
> > >
> > > (I'll already send out a v5, but let's keep discussing this.)
> > 
> > Well, the derive macro could assume that the refcount is manipulated
> > in the same way as the inner type does it. I admit that the idea is
> > not fully formed, but if we can avoid wrapping ARef, that would be
> > ideal.
> 
> If we can get this to work, I agree it's a good solution.
> 
> What do you think about making this a follow up of this series?

That's fine, if you remove the patch that adds the module name to the
function as ideally that's what we are trying to remove here :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-11 14:41               ` Greg KH
@ 2024-12-11 14:42                 ` Greg KH
  0 siblings, 0 replies; 45+ messages in thread
From: Greg KH @ 2024-12-11 14:42 UTC (permalink / raw)
  To: Danilo Krummrich
  Cc: Alice Ryhl, 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, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Wed, Dec 11, 2024 at 03:41:35PM +0100, Greg KH wrote:
> On Wed, Dec 11, 2024 at 03:32:21PM +0100, Danilo Krummrich wrote:
> > On Wed, Dec 11, 2024 at 02:06:50PM +0100, Alice Ryhl wrote:
> > > On Tue, Dec 10, 2024 at 11:38 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > > >
> > > > On Tue, Dec 10, 2024 at 11:55:33AM +0100, Alice Ryhl wrote:
> > > > > On Mon, Dec 9, 2024 at 11:44 AM Danilo Krummrich <dakr@kernel.org> wrote:
> > > > > >
> > > > > > On Fri, Dec 06, 2024 at 03:01:18PM +0100, Alice Ryhl wrote:
> > > > > > > On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> > > > > > >
> > > > > > > > +/// 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>);
> > > > > > >
> > > > > > > It seems more natural for this to be a wrapper around
> > > > > > > `Opaque<bindings::pci_dev>`. Then you can have both &Device and
> > > > > > > ARef<Device> depending on whether you want to hold a refcount or not.
> > > > > >
> > > > > > Yeah, but then every bus device has to re-implement the refcount dance we
> > > > > > already have in `device::Device` for the underlying base `struct device`.
> > > > > >
> > > > > > I forgot to mention this in my previous reply to Boqun, but we even documented
> > > > > > it this way in `device::Device` [1].
> > > > > >
> > > > > > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/device.rs#n28
> > > > >
> > > > > We could perhaps write a derive macro for AlwaysRefCounted that
> > > > > delegates to the inner type? That way, we can have the best of both
> > > > > worlds.
> > > >
> > > > Sounds interesting, how exactly would this work?
> > > >
> > > > (I'll already send out a v5, but let's keep discussing this.)
> > > 
> > > Well, the derive macro could assume that the refcount is manipulated
> > > in the same way as the inner type does it. I admit that the idea is
> > > not fully formed, but if we can avoid wrapping ARef, that would be
> > > ideal.
> > 
> > If we can get this to work, I agree it's a good solution.
> > 
> > What do you think about making this a follow up of this series?
> 
> That's fine, if you remove the patch that adds the module name to the
> function as ideally that's what we are trying to remove here :)

Argh, wrong thread, nevermind me, I'm fine with this...

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
  2024-12-11 14:32             ` Danilo Krummrich
  2024-12-11 14:41               ` Greg KH
@ 2024-12-11 14:44               ` Alice Ryhl
  1 sibling, 0 replies; 45+ messages in thread
From: Alice Ryhl @ 2024-12-11 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, airlied,
	fujita.tomonori, lina, pstanner, ajanulgu, lyude, robh,
	daniel.almeida, saravanak, dirk.behme, j, fabien.parent,
	chrisi.schrefl, rust-for-linux, linux-kernel, linux-pci,
	devicetree

On Wed, Dec 11, 2024 at 3:32 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Wed, Dec 11, 2024 at 02:06:50PM +0100, Alice Ryhl wrote:
> > On Tue, Dec 10, 2024 at 11:38 PM Danilo Krummrich <dakr@kernel.org> wrote:
> > >
> > > On Tue, Dec 10, 2024 at 11:55:33AM +0100, Alice Ryhl wrote:
> > > > On Mon, Dec 9, 2024 at 11:44 AM Danilo Krummrich <dakr@kernel.org> wrote:
> > > > >
> > > > > On Fri, Dec 06, 2024 at 03:01:18PM +0100, Alice Ryhl wrote:
> > > > > > On Thu, Dec 5, 2024 at 3:16 PM Danilo Krummrich <dakr@kernel.org> 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>
> > > > > >
> > > > > > > +/// 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>);
> > > > > >
> > > > > > It seems more natural for this to be a wrapper around
> > > > > > `Opaque<bindings::pci_dev>`. Then you can have both &Device and
> > > > > > ARef<Device> depending on whether you want to hold a refcount or not.
> > > > >
> > > > > Yeah, but then every bus device has to re-implement the refcount dance we
> > > > > already have in `device::Device` for the underlying base `struct device`.
> > > > >
> > > > > I forgot to mention this in my previous reply to Boqun, but we even documented
> > > > > it this way in `device::Device` [1].
> > > > >
> > > > > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/rust/kernel/device.rs#n28
> > > >
> > > > We could perhaps write a derive macro for AlwaysRefCounted that
> > > > delegates to the inner type? That way, we can have the best of both
> > > > worlds.
> > >
> > > Sounds interesting, how exactly would this work?
> > >
> > > (I'll already send out a v5, but let's keep discussing this.)
> >
> > Well, the derive macro could assume that the refcount is manipulated
> > in the same way as the inner type does it. I admit that the idea is
> > not fully formed, but if we can avoid wrapping ARef, that would be
> > ideal.
>
> If we can get this to work, I agree it's a good solution.
>
> What do you think about making this a follow up of this series?

I'm fine with it being a follow-up.

Alice

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types
  2024-12-05 14:14 ` [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types Danilo Krummrich
  2024-12-06 14:13   ` Alice Ryhl
@ 2024-12-11 14:52   ` Daniel Almeida
  1 sibling, 0 replies; 45+ messages in thread
From: Daniel Almeida @ 2024-12-11 14:52 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,
	dirk.behme, j, fabien.parent, chrisi.schrefl, rust-for-linux,
	linux-kernel, linux-pci, devicetree

Hi Danilo,

> On 5 Dec 2024, at 11:14, 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.
> 
> `IoRaw` 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

Maybe the in-flight platform::IoMem would be the other example? I see no `io::IoMem` in
your patch other than the one in the doctest.

> `IoRaw` accordingly.
> 
> To ensure that I/O mapped memory can't out-live the device it may be
> bound to, subsystems must embed the corresponding I/O memory type (e.g.
> pci::Bar) into a `Devres` container, such that it gets revoked once the
> device is unbound.
> 
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
> rust/helpers/helpers.c |   1 +
> rust/helpers/io.c      | 101 ++++++++++++++++
> rust/kernel/io.rs      | 260 +++++++++++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs     |   1 +
> 4 files changed, 363 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 060750af6524..63f9b1da179f 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -14,6 +14,7 @@
> #include "cred.c"
> #include "err.c"
> #include "fs.c"
> +#include "io.c"
> #include "jump_label.c"
> #include "kunit.c"
> #include "mutex.c"
> diff --git a/rust/helpers/io.c b/rust/helpers/io.c
> new file mode 100644
> index 000000000000..1dde6374c0e2
> --- /dev/null
> +++ b/rust/helpers/io.c
> @@ -0,0 +1,101 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/io.h>
> +
> +void __iomem *rust_helper_ioremap(phys_addr_t offset, size_t size)
> +{
> + return ioremap(offset, size);
> +}
> +
> +void rust_helper_iounmap(volatile void __iomem *addr)
> +{
> + return iounmap(addr);
> +}
> +
> +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..36aa2ec1bcc8
> --- /dev/null
> +++ b/rust/kernel/io.rs
> @@ -0,0 +1,260 @@
> +// 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};
> +
> +/// Raw representation of an MMIO region.
> +///
> +/// By itself, the existence of an instance of this structure does not provide any guarantees that
> +/// the represented MMIO region does exist or is properly mapped.
> +///
> +/// Instead, the bus specific MMIO implementation must convert this raw representation into an `Io`
> +/// instance providing the actual memory accessors. Only by the conversion into an `Io` structure
> +/// any guarantees are given.
> +pub struct IoRaw<const SIZE: usize = 0> {
> +    addr: usize,
> +    maxsize: usize,
> +}
> +
> +impl<const SIZE: usize> IoRaw<SIZE> {
> +    /// Returns a new `IoRaw` instance on success, an error otherwise.
> +    pub fn new(addr: usize, maxsize: usize) -> Result<Self> {
> +        if maxsize < SIZE {
> +            return Err(EINVAL);
> +        }
> +
> +        Ok(Self { addr, maxsize })
> +    }
> +
> +    /// Returns the base address of the MMIO region.
> +    #[inline]
> +    pub fn addr(&self) -> usize {
> +        self.addr
> +    }
> +
> +    /// Returns the maximum size of the MMIO region.
> +    #[inline]
> +    pub fn maxsize(&self) -> usize {
> +        self.maxsize
> +    }
> +}
> +
> +/// 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, IoRaw}};
> +/// # use core::ops::Deref;
> +///
> +/// // See also [`pci::Bar`] for a real example.
> +/// struct IoMem<const SIZE: usize>(IoRaw<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);
> +///         }
> +///
> +///         Ok(IoMem(IoRaw::new(addr as _, SIZE)?))
> +///     }
> +/// }
> +///
> +/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
> +///     fn drop(&mut self) {
> +///         // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
> +///         unsafe { bindings::iounmap(self.0.addr() as _); };
> +///     }
> +/// }
> +///
> +/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
> +///    type Target = Io<SIZE>;
> +///
> +///    fn deref(&self) -> &Self::Target {
> +///         // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
> +///         unsafe { Io::from_raw(&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(())
> +/// # }
> +/// ```
> +#[repr(transparent)]
> +pub struct Io<const SIZE: usize = 0>(IoRaw<SIZE>);
> +
> +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> {
> +    /// Converts an `IoRaw` into an `Io` instance, providing the accessors to the MMIO mapping.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size
> +    /// `maxsize`.
> +    pub unsafe fn from_raw<'a>(raw: &IoRaw<SIZE>) -> &'a Self {
> +        // SAFETY: `Io` is a transparent wrapper around `IoRaw`.
> +        unsafe { &*core::ptr::from_ref(raw).cast() }
> +    }
> +
> +    /// Returns the base address of this mapping.
> +    #[inline]
> +    pub fn addr(&self) -> usize {
> +        self.0.addr()
> +    }
> +
> +    /// Returns the maximum size of this mapping.
> +    #[inline]
> +    pub fn maxsize(&self) -> usize {
> +        self.0.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.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.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 200c5f99a805..d1f30b1a05fb 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -79,6 +79,7 @@
> 
> #[doc(hidden)]
> pub use bindings;
> +pub mod io;
> pub use macros;
> pub use uapi;
> 
> -- 
> 2.47.0
> 

I rebased platform::IoMem on top of this, and it still works fine.

As I said, I am testing it by modifying the compatible in `rust_driver_platform`, and
I’ve been able to successfully map and read from a memory-mapped device register.

It’s a read-only register containing some information about the device, and the value
read matches what we get in the C driver.

Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Daniel Almeida  <daniel.almeida@collabora.com>


^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2024-12-11 14:54 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-05 14:14 [PATCH v4 00/13] Device / Driver PCI / Platform Rust abstractions Danilo Krummrich
2024-12-05 14:14 ` [PATCH v4 01/13] rust: pass module name to `Module::init` Danilo Krummrich
2024-12-05 14:14 ` [PATCH v4 02/13] rust: implement generic driver registration Danilo Krummrich
2024-12-06 13:57   ` Alice Ryhl
2024-12-06 18:13     ` Danilo Krummrich
2024-12-05 14:14 ` [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId` Danilo Krummrich
2024-12-07  1:14   ` Fabien Parent
2024-12-09 10:45     ` Danilo Krummrich
2024-12-05 14:14 ` [PATCH v4 04/13] rust: add rcu abstraction Danilo Krummrich
2024-12-05 14:14 ` [PATCH v4 05/13] rust: add `Revocable` type Danilo Krummrich
2024-12-06 15:11   ` Alice Ryhl
2024-12-09 10:40     ` Danilo Krummrich
2024-12-05 14:14 ` [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types Danilo Krummrich
2024-12-06 14:13   ` Alice Ryhl
2024-12-11 14:52   ` Daniel Almeida
2024-12-05 14:14 ` [PATCH v4 07/13] rust: add devres abstraction Danilo Krummrich
2024-12-05 14:14 ` [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions Danilo Krummrich
2024-12-06 14:01   ` Alice Ryhl
2024-12-09 10:44     ` Danilo Krummrich
2024-12-10 10:55       ` Alice Ryhl
2024-12-10 22:38         ` Danilo Krummrich
2024-12-11 13:06           ` Alice Ryhl
2024-12-11 14:32             ` Danilo Krummrich
2024-12-11 14:41               ` Greg KH
2024-12-11 14:42                 ` Greg KH
2024-12-11 14:44               ` Alice Ryhl
2024-12-06 15:25   ` Alice Ryhl
2024-12-05 14:14 ` [PATCH v4 09/13] rust: pci: implement I/O mappable `pci::Bar` Danilo Krummrich
2024-12-06 10:44   ` Philipp Stanner
2024-12-05 14:14 ` [PATCH v4 10/13] samples: rust: add Rust PCI sample driver Danilo Krummrich
2024-12-05 14:14 ` [PATCH v4 11/13] rust: of: add `of::DeviceId` abstraction Danilo Krummrich
2024-12-09 21:22   ` Rob Herring
2024-12-05 14:14 ` [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions Danilo Krummrich
2024-12-09 22:37   ` Rob Herring
2024-12-09 23:13     ` Danilo Krummrich
2024-12-10  7:46     ` Greg KH
2024-12-10  9:34       ` Danilo Krummrich
2024-12-10  9:40         ` Greg KH
2024-12-05 14:14 ` [PATCH v4 13/13] samples: rust: add Rust platform sample driver Danilo Krummrich
2024-12-05 17:09   ` Dirk Behme
2024-12-05 18:03     ` Rob Herring
2024-12-06  6:39       ` Dirk Behme
2024-12-06  8:33     ` Danilo Krummrich
2024-12-06  9:29       ` Dirk Behme
2024-12-10 22:59   ` Rob Herring (Arm)

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).