Device / Driver PCI / Platform Rust abstractions
Subsystems (13)
Danilo Krummrich
maintainerv4 cover Device / Driver PCI / Platform Rust abstractions
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/[email protected]/
[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
Signature · 1 line
2.47.0
Danilo Krummrich
maintainerPATCH v4 1/13 rust: pass module name to `Module::init`
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.
--- 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(-)
drivers/block/rnull.rs +1 -1
@@ -36,7 +36,7 @@ struct NullBlkModule { }impl kernel::Module for NullBlkModule { -fn init (_module: &'staticThisModule ) ->Result <Self > { +fn init (_name: &'staticCStr , _module: &'staticThisModule ) ->Result <Self > {pr_info! ("Rust null_blk loaded\n" );let tagset =Arc ::pin_init (TagSet ::new (1 ,256 ,1 ), flags::GFP_KERNEL )?;
rust/kernel/lib.rs +10 -4
@@ -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: &'staticThisModule ) -> error::Result <Self >; +fn init (name: &'static str::CStr , module: &'staticThisModule ) -> 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: &'staticThisModule ) ->impl init::PinInit <Self , error::Error >; +fn init ( + name: &'static str::CStr , + module: &'staticThisModule , + ) ->impl init::PinInit <Self , error::Error >; }impl <T :Module >InPlaceModule for T { -fn init (module: &'staticThisModule ) ->impl init::PinInit <Self , error::Error > { +fn init ( + name: &'static str::CStr , + module: &'staticThisModule , + ) ->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) };
rust/kernel/net/phy.rs +1 -1
@@ -903,7 +903,7 @@ struct Module { [$($crate::net::phy::create_phy_driver::<$driver>()),+];impl $crate::Module for Module { -fn init (module: &'staticThisModule ) ->Result <Self > { +fn init (_name: &'staticCStr , module: &'staticThisModule ) ->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 };
rust/kernel/sync/lock/global.rs +4 -2
@@ -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() }; ///
rust/macros/lib.rs +4 -2
@@ -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) /// } /// }
rust/macros/module.rs +5 -2
@@ -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`.
samples/rust/rust_minimal.rs +1 -1
@@ -17,7 +17,7 @@ struct RustMinimal { }impl kernel::Module for RustMinimal { -fn init (_module: &'staticThisModule ) ->Result <Self > { +fn init (_name: &'staticCStr , _module: &'staticThisModule ) ->Result <Self > {pr_info! ("Rust minimal sample (init)\n" );pr_info! ("Am I built-in? {}\n" , !cfg! (MODULE ));
samples/rust/rust_print_main.rs +1 -1
@@ -41,7 +41,7 @@ fn arc_print() -> Result { }impl kernel::Module for RustPrint { -fn init (_module: &'staticThisModule ) ->Result <Self > { +fn init (_name: &'staticCStr , _module: &'staticThisModule ) ->Result <Self > {pr_info! ("Rust printing macros sample (init)\n" );pr_emerg! ("Emergency message (level 0) without args\n" );
Signature · 1 line
2.47.0
Danilo Krummrich
maintainerPATCH v4 2/13 rust: implement generic driver registration
In reply to Danilo KrummrichImplement 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.
--- MAINTAINERS | 1 + rust/kernel/driver.rs | 120 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 122 insertions(+) create mode 100644 rust/kernel/driver.rs
MAINTAINERS +1 -0
@@ -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 <[email protected]>
rust/kernel/driver.rs new file +120 -0
120 lines not on this page · raw message
rust/kernel/lib.rs +1 -0
@@ -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;
Signature · 1 line
2.47.0
Alice Ryhl
maintainerRe: [PATCH v4 02/13] rust: implement generic driver registration
Quoted 10 lines · Danilo Krummrich
Implement the generic
Registrationtype and theDriverOpstrait.The
Registrationstructure 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
DriverOpstrait is provided to subsystems, which have to implementDriverOps::registerandDrvierOps::unregister. Subsystems
typo
Quoted 11 lines
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()fromDriverOps::registerandpci_unregister_driver()fromDrvierOps::unregister.Co-developed-by Wedson Almeida Filho <[email protected]>Signed-off-by Wedson Almeida Filho <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
[...]
rust/kernel/driver.rs line 23
Quoted 11 lines
rust/kernel/driver.rs new file +11
-0+/// 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.
rust/kernel/driver.rs line 30
Quoted 6 lines
rust/kernel/driver.rs new file +6
-0+/// 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.
rust/kernel/driver.rs line 36
Quoted 6 lines
rust/kernel/driver.rs new file +6
-0+ 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
Danilo Krummrich
maintainerRe: [PATCH v4 02/13] rust: implement generic driver registration
Quoted 28 lines · Alice Ryhl
Quoted 10 lines · Danilo Krummrich
Implement the generic
Registrationtype and theDriverOpstrait.The
Registrationstructure 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
DriverOpstrait is provided to subsystems, which have to implementDriverOps::registerandDrvierOps::unregister. Subsystemstypo
Quoted 11 lines
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()fromDriverOps::registerandpci_unregister_driver()fromDrvierOps::unregister.Co-developed-by Wedson Almeida Filho <[email protected]>Signed-off-by Wedson Almeida Filho <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>[...]
Quoted 13 lines
rust/kernel/driver.rs line 23
Quoted 11 lines
rust/kernel/driver.rs new file +11
-0+/// 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.
Quoted 2 lines
then
registercalls a C function to initialize it. Havingregisterreturn animpl PinInitseems 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.
Quoted 11 lines
rust/kernel/driver.rs line 30
Quoted 7 lines
rust/kernel/driver.rs new file +6
-0+/// 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.
Quoted 10 lines
rust/kernel/driver.rs line 36
Quoted 7 lines
rust/kernel/driver.rs new file +6
-0+ 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.
Quoted 2 lines
Alice
Danilo Krummrich
maintainerPATCH v4 3/13 rust: implement `IdArray`, `IdTable` and `RawDeviceId`
In reply to Danilo KrummrichMost 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.
--- 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
MAINTAINERS +1 -0
@@ -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)
rust/kernel/device_id.rs new file +166 -0
166 lines not on this page · raw message
rust/kernel/lib.rs +6 -0
@@ -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 )]
Signature · 1 line
2.47.0
Re: [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId`
Hi Danilo,
rust/kernel/device_id.rs line 161
Quoted 11 lines
rust/kernel/device_id.rs new file +11
-0+/// 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))Quoted 5 lines
rust/kernel/device_id.rs new file +5
-0+ ] + static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] = +unsafe { core::mem::transmute_copy ($table_name.raw_ids ()) }; + }; +}
Danilo Krummrich
maintainerRe: [PATCH v4 03/13] rust: implement `IdArray`, `IdTable` and `RawDeviceId`
Quoted 19 lines · Fabien Parent
Hi Danilo,
rust/kernel/device_id.rs line 161
Quoted 11 lines
rust/kernel/device_id.rs new file +11
-0+/// 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 forMODULE_DEVICE_TABLE())I applied the following change to make it work again:
Review
Quoted 7 lines
(quoted diff) +2
-3- concat!("__mod_", $table_type, + concat!("__mod_device_table__", $table_type, "__", module_path!(), "_", line!(), - "_", stringify!($table_name), - "_device_table") + "_", stringify!($table_name))
Good catch, thanks!
Quoted 7 lines
Quoted 7 lines
rust/kernel/device_id.rs new file +5
-0+ ] + static $module_table_name: [core::mem::MaybeUninit<u8>; $table_name.raw_ids().size()] = +unsafe { core::mem::transmute_copy ($table_name.raw_ids ()) }; + }; +}
Danilo Krummrich
maintainerPATCH v4 4/13 rust: add rcu abstraction
In reply to Danilo KrummrichFrom: Wedson Almeida Filho <[email protected]>
Add a simple abstraction to guard critical code sections with an rcu read lock.
--- 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
rust/helpers/helpers.c +1 -0
@@ -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"
rust/helpers/rcu.c new file +13 -0
@@ -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 (); +}
rust/kernel/sync.rs +1 -0
@@ -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 };
rust/kernel/sync/rcu.rs new file +47 -0
47 lines not on this page · raw message
Signature · 1 line
2.47.0
Danilo Krummrich
maintainerPATCH v4 5/13 rust: add `Revocable` type
In reply to Danilo KrummrichFrom: Wedson Almeida Filho <[email protected]>
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).
--- rust/kernel/lib.rs | 1 + rust/kernel/revocable.rs | 235 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+) create mode 100644 rust/kernel/revocable.rs
rust/kernel/lib.rs +1 -0
@@ -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;
rust/kernel/revocable.rs new file +235 -0
235 lines not on this page · raw message
Signature · 1 line
2.47.0
Alice Ryhl
maintainerRe: [PATCH v4 05/13] rust: add `Revocable` type
Quoted 11 lines · Danilo Krummrich
From: Wedson Almeida Filho <[email protected]>
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).
Quoted 3 lines
Signed-off-by Wedson Almeida Filho <[email protected]>Co-developed-by Danilo Krummrich <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
Overall looks reasonable, but some comments below.
rust/kernel/revocable.rs line 97
Quoted 11 lines
rust/kernel/revocable.rs new file +11
-0+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?
Quoted 34 lines
rust/kernel/revocable.rs new file +34
-0+ }, + }) + } + +/// 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 <&'aT > { +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.
rust/kernel/revocable.rs line 141
Quoted 10 lines
rust/kernel/revocable.rs new file +10
-0+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
Danilo Krummrich
maintainerRe: [PATCH v4 05/13] rust: add `Revocable` type
Quoted 30 lines · Alice Ryhl
Quoted 11 lines · Danilo Krummrich
From: Wedson Almeida Filho <[email protected]>
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).
Quoted 3 lines
Signed-off-by Wedson Almeida Filho <[email protected]>Co-developed-by Danilo Krummrich <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Overall looks reasonable, but some comments below.
rust/kernel/revocable.rs line 97
Quoted 11 lines
rust/kernel/revocable.rs new file +11
-0+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) + })
Quoted 2 lines
This is pretty awkward ... could we have an
Opaque::pin_initthat takes an `impl PinInit instead of usingfii_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.
Quoted 40 lines
Quoted 40 lines
rust/kernel/revocable.rs new file +39
-0+ }, + }) + } + +/// 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 <&'aT > { +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 + } + }
Quoted 8 lines
rust/kernel/revocable.rs line 141
Quoted 5 lines
rust/kernel/revocable.rs new file +5
-0+ +/// # 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.
Quoted 2 lines
Alice
Danilo Krummrich
maintainerPATCH v4 6/13 rust: add `io::{Io, IoRaw}` base types
In reply to Danilo KrummrichI/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.
--- 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
rust/helpers/helpers.c +1 -0
@@ -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"
rust/helpers/io.c new file +101 -0
101 lines not on this page · raw message
rust/kernel/io.rs new file +260 -0
260 lines not on this page · raw message
rust/kernel/lib.rs +1 -0
@@ -79,6 +79,7 @@#[doc(hidden)] pub use bindings; +pub mod io;pub use macros;pub use uapi;
Signature · 1 line
2.47.0
Re: [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types
Quoted 22 lines · Danilo Krummrich
I/O memory is typically either mapped through direct calls to
ioremap()or subsystem / bus specific ones such aspci_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::readborIo:try_readb.
Iosupports 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.
IoRawis meant to be embedded into a structure (e.g.pci::Barorio::IoMem) which creates the actual I/O memory mapping and initializesIoRawaccordingly.
Quoted 6 lines
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 aDevrescontainer, such that it gets revoked once the device is unbound.Signed-off-by Danilo Krummrich <[email protected]>
One nit below. With that addressed:
rust/kernel/io.rs line 176
Quoted 8 lines
rust/kernel/io.rs new file +8
-0+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 >) -> &'aSelf {
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
Re: [PATCH v4 06/13] rust: add `io::{Io, IoRaw}` base types
In reply to Danilo KrummrichHi Danilo,
Quoted 13 lines · Danilo Krummrich
I/O memory is typically either mapped through direct calls to
ioremap()or subsystem / bus specific ones such aspci_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::readborIo:try_readb.
Quoted 8 lines
Iosupports 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.
IoRawis meant to be embedded into a structure (e.g.pci::Barorio::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.
Quoted 417 lines
IoRawaccordingly.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 aDevrescontainer, such that it gets revoked once the device is unbound.Signed-off-by Danilo Krummrich <[email protected]>---
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 100644rust/kernel/io.rsrust/helpers/helpers.c +1
-0@@ -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" rust/helpers/io.c new file +101
-0@@ -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 rust/kernel/io.rs new file +260
-0@@ -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(addras _ ) } + } + +/// 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(addras _ ) }) + } + }; +} + +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, addras _ , ) } + } + +/// 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, addras _ ) } +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 >) -> &'aSelf { +// 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 + ); +}rust/kernel/lib.rs +1
-0@@ -79,6 +79,7 @@#[doc(hidden)] pub use bindings; +pub mod io;pub use macros;pub use uapi;Signature · 1 line
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.
Danilo Krummrich
maintainerPATCH v4 7/13 rust: add devres abstraction
In reply to Danilo KrummrichAdd 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().
--- 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
MAINTAINERS +1 -0
@@ -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)
rust/helpers/device.c new file +10 -0
@@ -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); +}
rust/helpers/helpers.c +1 -0
@@ -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"
rust/kernel/devres.rs new file +179 -0
179 lines not on this page · raw message
rust/kernel/lib.rs +1 -0
@@ -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 )]
Signature · 1 line
2.47.0
Danilo Krummrich
maintainerPATCH v4 8/13 rust: pci: add basic PCI device / driver abstractions
In reply to Danilo KrummrichImplement 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.
--- 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
MAINTAINERS +1 -0
@@ -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 <[email protected]>
rust/bindings/bindings_helper.h +1 -0
@@ -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>
rust/helpers/helpers.c +1 -0
@@ -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"
rust/helpers/pci.c new file +18 -0
@@ -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); +}
rust/kernel/lib.rs +2 -0
@@ -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)]
rust/kernel/pci.rs new file +284 -0
284 lines not on this page · raw message
Signature · 1 line
2.47.0
Alice Ryhl
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
Quoted 21 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
rust/kernel/pci.rs line 243
Quoted 6 lines
rust/kernel/pci.rs new file +6
-0+/// 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
Quoted 43 lines
rust/kernel/pci.rs new file +40
-1+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
Danilo Krummrich
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
Quoted 22 lines · Alice Ryhl
Quoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
Quoted 10 lines
rust/kernel/pci.rs line 243
Quoted 6 lines
rust/kernel/pci.rs new file +6
-0+/// 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
Quoted 46 lines
Alice
Quoted 43 lines
rust/kernel/pci.rs new file +40
-1+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
Alice Ryhl
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
Quoted 35 lines · Danilo Krummrich
Quoted 22 lines · Alice Ryhl
Quoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Quoted 10 lines
rust/kernel/pci.rs line 243
Quoted 6 lines
rust/kernel/pci.rs new file +6
-0+/// 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.
Quoted 7 lines
Yeah, but then every bus device has to re-implement the refcount dance we already have in
device::Devicefor the underlying basestruct 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
Danilo Krummrich
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
Quoted 39 lines · Alice Ryhl
Quoted 38 lines · Danilo Krummrich
Quoted 22 lines · Alice Ryhl
Quoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Quoted 10 lines
rust/kernel/pci.rs line 243
Quoted 6 lines
rust/kernel/pci.rs new file +6
-0+/// 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::Devicefor the underlying basestruct device.
Quoted 8 lines
Quoted 4 lines
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.)
Quoted 2 lines
Alice
Alice Ryhl
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
Quoted 46 lines · Danilo Krummrich
Quoted 44 lines · Alice Ryhl
Quoted 42 lines · Danilo Krummrich
Quoted 22 lines · Alice Ryhl
Quoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Quoted 10 lines
rust/kernel/pci.rs line 243
Quoted 6 lines
rust/kernel/pci.rs new file +6
-0+/// 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::Devicefor the underlying basestruct 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
Quoted 7 lines
Quoted 3 lines
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
Danilo Krummrich
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
Quoted 51 lines · Alice Ryhl
Quoted 50 lines · Danilo Krummrich
Quoted 47 lines · Alice Ryhl
Quoted 35 lines · Danilo Krummrich
Quoted 22 lines · Alice Ryhl
Quoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Quoted 10 lines
rust/kernel/pci.rs line 243
Quoted 6 lines
rust/kernel/pci.rs new file +6
-0+/// 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.Quoted 7 lines
Yeah, but then every bus device has to re-implement the refcount dance we already have in
device::Devicefor the underlying basestruct 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.
Quoted 8 lines
Quoted 3 lines
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?
Quoted 2 lines
It sounds like the only reason you don't do that is that it's more unsafe, which the macro could reduce.
Exactly, yes.
Quoted 3 lines
Alice
Greg KH
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
Quoted 56 lines · Danilo Krummrich
Quoted 55 lines · Alice Ryhl
Quoted 53 lines · Danilo Krummrich
Quoted 39 lines · Alice Ryhl
Quoted 38 lines · Danilo Krummrich
Quoted 22 lines · Alice Ryhl
Quoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Quoted 10 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+/// 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::Devicefor the underlying basestruct device.Quoted 8 lines
Quoted 4 lines
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.)
Quoted 8 lines
Quoted 4 lines
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
Greg KH
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
Quoted 62 lines · Greg KH
Quoted 61 lines · Danilo Krummrich
Quoted 59 lines · Alice Ryhl
Quoted 46 lines · Danilo Krummrich
Quoted 44 lines · Alice Ryhl
Quoted 42 lines · Danilo Krummrich
Quoted 22 lines · Alice Ryhl
Quoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Quoted 10 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+/// 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::Devicefor the underlying basestruct 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
Quoted 7 lines
Quoted 3 lines
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.
Quoted 6 lines
Quoted 3 lines
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...
Alice Ryhl
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
In reply to Danilo KrummrichQuoted 57 lines · Danilo Krummrich
Quoted 55 lines · Alice Ryhl
Quoted 53 lines · Danilo Krummrich
Quoted 39 lines · Alice Ryhl
Quoted 38 lines · Danilo Krummrich
Quoted 22 lines · Alice Ryhl
Quoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>Quoted 10 lines
Review
Quoted 6 lines
(quoted diff) +6
-0+/// 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::Devicefor the underlying basestruct device.Quoted 8 lines
Quoted 4 lines
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.)
Quoted 8 lines
Quoted 4 lines
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
Alice Ryhl
maintainerRe: [PATCH v4 08/13] rust: pci: add basic PCI device / driver abstractions
In reply to Danilo KrummrichQuoted 20 lines · Danilo Krummrich
Implement the basic PCI abstractions required to write a basic PCI driver. This includes the following data structures:
The
pci::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
pci::Deviceabstraction represents astruct pci_devand provides abstractions for common functions, such aspci::Device::set_master.In order to provide the PCI specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented bypci::Adapter.
pci::DeviceIdimplements PCI device IDs based on the genericdevice_id::RawDevceIdabstraction.Co-developed-by FUJITA Tomonori <[email protected]>Signed-off-by FUJITA Tomonori <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>
rust/kernel/pci.rs line 54
Quoted 8 lines
rust/kernel/pci.rs new file +8
-0+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
Danilo Krummrich
maintainerPATCH v4 9/13 rust: pci: implement I/O mappable `pci::Bar`
In reply to Danilo KrummrichImplement 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.
--- rust/kernel/pci.rs | 145 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+)
rust/kernel/pci.rs +145 -0
@@ -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, lenas 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 (), ioptras _ ); + 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 {
Signature · 1 line
2.47.0
Re: [PATCH v4 09/13] rust: pci: implement I/O mappable `pci::Bar`
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.
Quoted 232 lines · Danilo Krummrich
Implement
pci::Bar,pci::Device::iomap_regionandpci::Device::iomap_region_sizedto 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, thepci::Bartype is always embedded into aDevrescontainer, such that thepci::Baris revoked once the device
is unbound and hence the I/O mapped memory is unmapped.A
pci::Barcan 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 <[email protected]>Signed-off-by Philipp Stanner <[email protected]>Signed-off-by Danilo Krummrich <[email protected]>---
rust/kernel/pci.rs | 145
+++++++++++++++++++++++++++++++++++++++++++++1 file changed, 145 insertions(+)rust/kernel/pci.rs +0
-0@@ -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,rust/kernel/pci.rs +0
-0@@ -239,9 +243,115 @@ pub trait Driver { /// /// A PCI device is based on an always reference counted
device:Deviceinstance. Cloning a PCI
/// device, hence, also increments the base device' reference count.rust/kernel/pci.rs +4
-0+/// +/// # Invariants +/// +/// `Device` hold a valid reference of `ARef<device::Device>` whose underlying
struct deviceis a
+/// member of astruct pci_dev.
#[derive(Clone)]
pub struct Device(ARef<device::Device>);rust/kernel/pci.rs +5
-0+/// 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
rust/kernel/pci.rs +15
-0+/// 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 bindingsuse.
+ let num =i32::try_from(num)?;
+
+ // SAFETY:
+ //pdevis valid by the invariants ofDevice. + //numis checked for validity by a previous call toDevice::resource_len.
+ //nameis always valid.
+ let ret = unsafe {bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr())};
+ if ret != 0 {
+ return Err(EBUSY);
+ }
+
+ // SAFETY:
+ //pdevis valid by the invariants ofDevice. + //numis checked for validity by a previous call toDevice::resource_len.
+ //nameis always valid.
+ let ioptr: usize = unsafe {bindings::pci_iomap(pdev.as_raw(), num, 0)} as usize;
+ if ioptr == 0 {
+ // SAFETY:
+ //pdevvalid by the invariants ofDevice. + //numis checked for validity by a previous call toDevice::resource_len.
+ unsafe {bindings::pci_release_region(pdev.as_raw(), num)};
+ return Err(ENOMEM);
+ }
+
+ let io = matchIoRaw::new(ioptr, len as usize){ + Ok(io) => io,
+ Err(err) => {
+ // SAFETY:
+ //pdevis valid by the invariants ofDevice. + //ioptris guaranteed to be the start of a valid I/O mapped memory region.
+ //numis checked for validity by a previous call toDevice::resource_len.
+ unsafe {Self::do_release(&pdev, ioptr, num)}; + return Err(err);
+ }
+ };rust/kernel/pci.rs +7
-0+ + Ok(Bar { pdev, io, num }) + } + + /// # Safety + /// + /// `ioptr` must be a valid pointer to the memory mapped PCI barnumber
num.
+ unsafe fndo_release(pdev: &Device, ioptr: usize, num: i32){ + // SAFETY:
+ //pdevis valid by the invariants ofDevice. + //ioptris valid by the safety requirements. + //numis valid by the safety requirements. + unsafe {
+bindings::pci_iounmap(pdev.as_raw(), ioptr as _); +bindings::pci_release_region(pdev.as_raw(), num); + }
+ }
+
+ fnrelease(&self){
+ // SAFETY: The safety requirements are guaranteed by the type invariant ofself.pdev.
+ unsafe {Self::do_release(&self.pdev, self.io.addr(), self.num)};
+ }rust/kernel/pci.rs +5
-0+} + +impl Bar { + fn index_is_valid(index: u32) -> bool { + // A `struct pci_dev` owns an array of resources with atmost
PCI_NUM_RESOURCESentries.
+ index <bindings::PCI_NUM_RESOURCES
+ }rust/kernel/pci.rs +13
-0+} + +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 rangein
self.iois properly mapped.
+ unsafe {Io::from_raw(&self.io)}
+ }rust/kernel/pci.rs +2
-0+} +impl Device {
/// Create a PCI Device instance from an existingdevice::Device.
///rust/kernel/pci.rs +0
-0@@ -275,6 +385,41 @@ pub fn set_master(&self) { // SAFETY: `self.as_raw` is guaranteed to be a pointer to avalid
struct pci_dev.
unsafe { bindings::pci_set_master(self.as_raw()) }; }
+
+ /// Returns the size of the given PCI bar resource.
+ pub fnresource_len(&self, bar: u32)-> Result<bindings::resource_size_t> {
+ if !Bar::index_is_valid(bar) {
+ return Err(EINVAL);
+ }
+
+ // SAFETY:
+ // -baris a valid bar number, as guaranteed by the above call toBar::index_is_valid,
+ // - by its type invariantself.as_rawis always a valid pointer to astruct 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)?;rust/kernel/pci.rs +5
-0+ + Ok(devres) + } + + /// Mapps an entire PCI-BAR after performing a region-request onit.
+ pub fniomap_region(&self, bar: u32, name: &CStr)-> Result<Devres<Bar>> {
+ self.iomap_region_sized::<0>(bar, name)
+ }
+
+ /// Returns a newARefof the basedevice::Device. + pub fnas_dev(&self)-> ARef<device::Device> { + self.0.clone()
+ }
}
impl AsRef<device::Device> for Device {
Danilo Krummrich
maintainerPATCH v4 10/13 samples: rust: add Rust PCI sample driver
In reply to Danilo KrummrichThis 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.
--- 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
MAINTAINERS +1 -0
@@ -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 <[email protected]>
samples/rust/Kconfig +11 -0
@@ -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
samples/rust/Makefile +1 -0
@@ -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
samples/rust/rust_driver_pci.rs new file +109 -0
109 lines not on this page · raw message
Signature · 1 line
2.47.0
Danilo Krummrich
maintainerPATCH v4 11/13 rust: of: add `of::DeviceId` abstraction
In reply to Danilo Krummrichof::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.
--- MAINTAINERS | 1 + rust/kernel/lib.rs | 1 + rust/kernel/of.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 rust/kernel/of.rs
MAINTAINERS +1 -0
@@ -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_
rust/kernel/lib.rs +1 -0
@@ -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;
rust/kernel/of.rs new file +57 -0
57 lines not on this page · raw message
Signature · 1 line
2.47.0
Re: [PATCH v4 11/13] rust: of: add `of::DeviceId` abstraction
Quoted 5 lines · Danilo Krummrich
of::DeviceIdis an abstraction aroundstruct of_device_id.This is used by subsequent patches, in particular the platform bus abstractions, to create OF device ID tables.
Quoted 7 lines
Signed-off-by Danilo Krummrich <[email protected]>--- MAINTAINERS | 1 + rust/kernel/lib.rs | 1 + rust/kernel/of.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 rust/kernel/of.rs
Danilo Krummrich
maintainerPATCH v4 12/13 rust: platform: add basic platform device / driver abstractions
In reply to Danilo KrummrichImplement 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.
--- 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
MAINTAINERS +1 -0
@@ -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 <[email protected]>
rust/bindings/bindings_helper.h +2 -0
@@ -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>
rust/helpers/helpers.c +1 -0
@@ -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"
rust/helpers/platform.c new file +13 -0
@@ -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); +}
rust/kernel/lib.rs +1 -0
@@ -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;
rust/kernel/platform.rs new file +222 -0
222 lines not on this page · raw message
Signature · 1 line
2.47.0
Rob Herring
maintainerRe: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
Quoted 172 lines · Danilo Krummrich
Implement the basic platform bus abstractions required to write a basic platform driver. This includes the following data structures:
The
platform::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
platform::Deviceabstraction represents astruct platform_device.In order to provide the platform bus specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented byplatform::Adapter.Signed-off-by Danilo Krummrich <[email protected]>--- 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.rsMAINTAINERS +1
-0@@ -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 <[email protected]>rust/bindings/bindings_helper.h +2
-0@@ -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> rust/helpers/helpers.c +1
-0@@ -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" rust/helpers/platform.c new file +13
-0@@ -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); +}rust/kernel/lib.rs +1
-0@@ -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;rust/kernel/platform.rs new file +71
-0@@ -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: &'staticCStr , + module: &'staticThisModule , + ) ->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 <&'staticT ::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 <&'staticT ::IdInfo > { +None + } + +// Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular
rust/kernel/platform.rs line 81
Quoted 10 lines
rust/kernel/platform.rs new file +10
-0+// 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 <&'staticT ::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)'
Danilo Krummrich
maintainerRe: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
Quoted 179 lines · Rob Herring
Quoted 178 lines · Danilo Krummrich
Implement the basic platform bus abstractions required to write a basic platform driver. This includes the following data structures:
The
platform::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
platform::Deviceabstraction represents astruct platform_device.In order to provide the platform bus specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented byplatform::Adapter.Signed-off-by Danilo Krummrich <[email protected]>--- 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.rsMAINTAINERS +1
-0@@ -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 <[email protected]>rust/bindings/bindings_helper.h +2
-0@@ -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> rust/helpers/helpers.c +1
-0@@ -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" rust/helpers/platform.c new file +13
-0@@ -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); +}rust/kernel/lib.rs +1
-0@@ -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;rust/kernel/platform.rs new file +77
-0@@ -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: &'staticCStr , + module: &'staticThisModule , + ) ->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 <&'staticT ::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 <&'staticT ::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 <&'staticT ::IdInfo > { +let id =Self ::of_id_info (pdev); +if id.is_some () { +return id;
Quoted 8 lines
rust/kernel/platform.rs line 63
Quoted 4 lines
rust/kernel/platform.rs new file +4
-0+ } + +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.
Quoted 9 lines
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.
Quoted 4 lines
Rob
[1] git grep -W 'const struct
pci_device_id\*'drivers/| grep -P 'id->(?!driver_data)'
Greg KH
maintainerRe: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
In reply to Rob HerringQuoted 189 lines · Rob Herring
Quoted 172 lines · Danilo Krummrich
Implement the basic platform bus abstractions required to write a basic platform driver. This includes the following data structures:
The
platform::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
platform::Deviceabstraction represents astruct platform_device.In order to provide the platform bus specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented byplatform::Adapter.Signed-off-by Danilo Krummrich <[email protected]>--- 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.rsMAINTAINERS +1
-0@@ -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 <[email protected]>rust/bindings/bindings_helper.h +2
-0@@ -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> rust/helpers/helpers.c +1
-0@@ -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" rust/helpers/platform.c new file +13
-0@@ -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); +}rust/kernel/lib.rs +1
-0@@ -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;rust/kernel/platform.rs new file +71
-0@@ -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: &'staticCStr , + module: &'staticThisModule , + ) ->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 <&'staticT ::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 <&'staticT ::IdInfo > { +None + } + +// Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular rust/kernel/platform.rs line 81
Quoted 10 lines
rust/kernel/platform.rs new file +10
-0+// 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 <&'staticT ::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.
Quoted 7 lines
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
Danilo Krummrich
maintainerRe: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
Quoted 198 lines · Greg KH
Quoted 189 lines · Rob Herring
Quoted 172 lines · Danilo Krummrich
Implement the basic platform bus abstractions required to write a basic platform driver. This includes the following data structures:
The
platform::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
platform::Deviceabstraction represents astruct platform_device.In order to provide the platform bus specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented byplatform::Adapter.Signed-off-by Danilo Krummrich <[email protected]>--- 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.rsMAINTAINERS +1
-0@@ -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 <[email protected]>rust/bindings/bindings_helper.h +2
-0@@ -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> rust/helpers/helpers.c +1
-0@@ -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" rust/helpers/platform.c new file +13
-0@@ -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); +}rust/kernel/lib.rs +1
-0@@ -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;rust/kernel/platform.rs new file +71
-0@@ -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: &'staticCStr , + module: &'staticThisModule , + ) ->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 <&'staticT ::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 <&'staticT ::IdInfo > { +None + } + +// Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular rust/kernel/platform.rs line 81
Quoted 10 lines
rust/kernel/platform.rs new file +10
-0+// 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 <&'staticT ::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.
Quoted 7 lines
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.
Quoted 4 lines
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
Quoted 6 lines
If that's not possible, then it needs to stay.
thanks,
greg k-h
Greg KH
maintainerRe: [PATCH v4 12/13] rust: platform: add basic platform device / driver abstractions
Quoted 204 lines · Danilo Krummrich
Quoted 198 lines · Greg KH
Quoted 189 lines · Rob Herring
Quoted 172 lines · Danilo Krummrich
Implement the basic platform bus abstractions required to write a basic platform driver. This includes the following data structures:
The
platform::Drivertrait represents the interface to the driver and providespci::Driver::probefor the driver to implement.The
platform::Deviceabstraction represents astruct platform_device.In order to provide the platform bus specific parts to a generic
driver::Registrationthedriver::RegistrationOpstrait is implemented byplatform::Adapter.Signed-off-by Danilo Krummrich <[email protected]>--- 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.rsMAINTAINERS +1
-0@@ -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 <[email protected]>rust/bindings/bindings_helper.h +2
-0@@ -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> rust/helpers/helpers.c +1
-0@@ -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" rust/helpers/platform.c new file +13
-0@@ -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); +}rust/kernel/lib.rs +1
-0@@ -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;rust/kernel/platform.rs new file +71
-0@@ -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: &'staticCStr , + module: &'staticThisModule , + ) ->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 <&'staticT ::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 <&'staticT ::IdInfo > { +None + } + +// Try to retrieve an `IdInfo` from any of the ID tables; if we can't find one for a particular rust/kernel/platform.rs line 81
Quoted 10 lines
rust/kernel/platform.rs new file +10
-0+// 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 <&'staticT ::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.
Quoted 7 lines
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.
Quoted 4 lines
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.)
Quoted 8 lines
The signature would be
fn probe(dev: &mut Device, id_info: &Self::IdInfo) -> Result<Pin<KBox<Self>>>where
id_infois the driver private data.The other fields of struct
pci_device_idshould also be available from structpci_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
Danilo Krummrich
maintainerPATCH v4 13/13 samples: rust: add Rust platform sample driver
In reply to Danilo KrummrichAdd 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.
--- 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
MAINTAINERS +1 -0
@@ -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 <[email protected]>
drivers/of/unittest-data/tests-platform.dtsi +5 -0
@@ -33,6 +33,11 @@ dev@100 { reg = <0x100>; }; }; + + test-device@2 { + compatible = "test,rust-device"; + reg = <0x2>; + }; }; }; };
samples/rust/Kconfig +10 -0
@@ -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
samples/rust/Makefile +1 -0
@@ -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
samples/rust/rust_driver_platform.rs new file +49 -0
49 lines not on this page · raw message
Signature · 1 line
2.47.0
Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
Hi Danilo,
Quoted 7 lines · 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 <[email protected]>
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]
MAINTAINERS +0 -1
@@ -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 <[email protected]>
rust/kernel/platform.rs +3 -1
@@ -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 supportedby the driver.
samples/rust/Kconfig +0 -10
@@ -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
samples/rust/Makefile +0 -1
@@ -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
(quoted diff) +0 -0
b/samples/rust/rust_driver_platform.rs
deleted file mode 100644
index 2f0dbbe69e10..000000000000
samples/rust/rust_driver_platform.rs deleted +0 -24
@@ -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",
-}
Rob Herring
maintainerRe: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
Quoted 12 lines · Dirk Behme
Hi Danilo,
Quoted 7 lines · 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 <[email protected]>
Quoted 8 lines
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/rustthen? 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.
Quoted 2 lines
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
Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
Hi Rob,
Quoted 18 lines · Rob Herring
Quoted 17 lines · Dirk Behme
Hi Danilo,
Quoted 7 lines · 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 <[email protected]>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/rustthen? Like [1] below?
Quoted 6 lines
Quoted 3 lines
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 ;)
Quoted 9 lines
OTOH, the need for the sample will quickly diminish once there are real drivers using this stuff.
Quoted 2 lines
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
Quoted 5 lines
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.
Danilo Krummrich
maintainerRe: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
In reply to Dirk BehmeQuoted 9 lines · Dirk Behme
Hi Danilo,
Quoted 6 lines · 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.
Quoted 6 lines
Quoted 1 line
Signed-off-by Danilo Krummrich <[email protected]>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/rustthen? 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.
Quoted 199 lines
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]
MAINTAINERS +0
-1@@ -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 <[email protected]>rust/kernel/platform.rs +3
-1@@ -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
rust/kernel/platform.rs +11
-10+/// # 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");rust/kernel/platform.rs +4
-0+/// +/// assert_eq!(info.unwrap().0, 42); +/// +/// let drvdata = KBox::new(Self { pdev: pdev.clone() },
GFP_KERNEL)?;rust/kernel/platform.rs +8
-0+/// +/// Ok(drvdata.into()) +/// } +/// } +/// +/// impl Drop for MyDriver { +/// fn drop(&mut self) { +/// dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
/// }
/// }rust/kernel/platform.rs +9
-0+/// +/// 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.
samples/rust/Kconfig +0
-10@@ -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" helpsamples/rust/Makefile +0
-1@@ -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.osamples/rust/rust_driver_platform.rs deleted +0
-24@@ -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>>> {
samples/rust/rust_driver_platform.rs deleted +0
-4-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);
samples/rust/rust_driver_platform.rs deleted +0
-3- } - - let drvdata = KBox::new(Self { pdev: pdev.clone() },
GFP_KERNEL)?;samples/rust/rust_driver_platform.rs deleted +0
-8- -Ok (drvdata.into ()) - } -} - -impl Drop for SampleDriver { -fn drop (&mut self) { - dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driversample.\n");
samples/rust/rust_driver_platform.rs deleted +0
-10- } -} - -kernel::module_platform_driver! { -type :SampleDriver , - name:"rust_driver_platform" , - author:"Danilo Krummrich" , - description:"Rust Platform driver" , - license:"GPL v2" , -}
Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
Quoted 17 lines · Danilo Krummrich
Quoted 9 lines · Dirk Behme
Hi Danilo,
Quoted 6 lines · 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.
Quoted 6 lines
Quoted 1 line
Signed-off-by Danilo Krummrich <[email protected]>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/rustthen? Like [1] below?
Quoted 6 lines
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
Quoted 198 lines
Quoted 198 lines
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]
MAINTAINERS +0
-1@@ -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 <[email protected]>rust/kernel/platform.rs +3
-1@@ -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
rust/kernel/platform.rs +11
-10+/// # 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");rust/kernel/platform.rs +4
-0+/// +/// assert_eq!(info.unwrap().0, 42); +/// +/// let drvdata = KBox::new(Self { pdev: pdev.clone() },
GFP_KERNEL)?;rust/kernel/platform.rs +8
-0+/// +/// Ok(drvdata.into()) +/// } +/// } +/// +/// impl Drop for MyDriver { +/// fn drop(&mut self) { +/// dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
/// }
/// }rust/kernel/platform.rs +9
-0+/// +/// 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.
samples/rust/Kconfig +0
-10@@ -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" helpsamples/rust/Makefile +0
-1@@ -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.osamples/rust/rust_driver_platform.rs deleted +0
-24@@ -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>>> {
samples/rust/rust_driver_platform.rs deleted +0
-4-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);
samples/rust/rust_driver_platform.rs deleted +0
-3- } - - let drvdata = KBox::new(Self { pdev: pdev.clone() },
GFP_KERNEL)?;samples/rust/rust_driver_platform.rs deleted +0
-8- -Ok (drvdata.into ()) - } -} - -impl Drop for SampleDriver { -fn drop (&mut self) { - dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driversample.\n");
samples/rust/rust_driver_platform.rs deleted +0
-10- } -} - -kernel::module_platform_driver! { -type :SampleDriver , - name:"rust_driver_platform" , - author:"Danilo Krummrich" , - description:"Rust Platform driver" , - license:"GPL v2" , -}
Re: [PATCH v4 13/13] samples: rust: add Rust platform sample driver
In reply to Danilo KrummrichQuoted 7 lines · 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 <[email protected]>
Quoted 9 lines
--- 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