* [PATCH net-next v5 0/5] Rust abstractions for network PHY drivers
@ 2023-10-17 11:30 FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 1/5] rust: core " FUJITA Tomonori
` (4 more replies)
0 siblings, 5 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-17 11:30 UTC (permalink / raw)
To: netdev
Cc: rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg
This patchset adds Rust abstractions for phylib. It doesn't fully
cover the C APIs yet but I think that it's already useful. I implement
two PHY drivers (Asix AX88772A PHYs and Realtek Generic FE-GE). Seems
they work well with real hardware.
The first patch introduces Rust bindings for phylib.
The second patch add a macro to declare a kernel module for PHYs
drivers.
The third patch add Miguel's work to make sure that the C's enum is
sync with Rust sides. If not, compiling fails. Note that this is a
temporary solution. It will be replaced with bindgen when it supports
generating the enum conversion code.
The fourth add the Rust ETHERNET PHY LIBRARY entry to MAINTAINERS
file; adds the binding file and me as a maintainer (as Andrew Lunn
suggested) with Trevor Gross as a reviewer.
The last patch introduces the Rust version of Asix PHY drivers,
drivers/net/phy/ax88796b.c. The features are equivalent to the C
version. You can choose C (by default) or Rust version on kernel
configuration.
v5:
- drops the rustified-enum option, writes match by hand; no *risk* of UB
- adds Miguel's patch for enum checking
- moves CONFIG_RUST_PHYLIB_ABSTRACTIONS to drivers/net/phy/Kconfig
- adds a new entry for this abstractions in MAINTAINERS
- changes some of Device's methods to take &mut self
- comment improvment
v4: https://lore.kernel.org/netdev/20231012125349.2702474-1-fujita.tomonori@gmail.com/
- split the core patch
- making Device::from_raw() private
- comment improvement with code update
- commit message improvement
- avoiding using bindings::phy_driver in public functions
- using an anonymous constant in module_phy_driver macro
v3: https://lore.kernel.org/netdev/20231011.231607.1747074555988728415.fujita.tomonori@gmail.com/T/
- changes the base tree to net-next from rust-next
- makes this feature optional; only enabled with CONFIG_RUST_PHYLIB_BINDINGS=y
- cosmetic code and comment improvement
- adds copyright
v2: https://lore.kernel.org/netdev/20231006094911.3305152-2-fujita.tomonori@gmail.com/T/
- build failure fix
- function renaming
v1: https://lore.kernel.org/netdev/20231002085302.2274260-3-fujita.tomonori@gmail.com/T/
FUJITA Tomonori (4):
rust: core abstractions for network PHY drivers
rust: net::phy add module_phy_driver macro
MAINTAINERS: add Rust PHY abstractions for ETHERNET PHY LIBRARY
net: phy: add Rust Asix PHY driver
Miguel Ojeda (1):
WIP rust: add second `bindgen` pass for enum exhaustiveness checking
MAINTAINERS | 16 +
drivers/net/phy/Kconfig | 16 +
drivers/net/phy/Makefile | 6 +-
drivers/net/phy/ax88796b_rust.rs | 129 +++++
rust/.gitignore | 1 +
rust/Makefile | 14 +
rust/bindings/bindings_enum_check.rs | 36 ++
rust/bindings/bindings_helper.h | 3 +
rust/kernel/lib.rs | 3 +
rust/kernel/net.rs | 6 +
rust/kernel/net/phy.rs | 835 +++++++++++++++++++++++++++
rust/uapi/uapi_helper.h | 2 +
12 files changed, 1066 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/phy/ax88796b_rust.rs
create mode 100644 rust/bindings/bindings_enum_check.rs
create mode 100644 rust/kernel/net.rs
create mode 100644 rust/kernel/net/phy.rs
base-commit: a3c2dd96487f1dd734c9443a3472c8dafa689813
--
2.34.1
^ permalink raw reply [flat|nested] 65+ messages in thread
* [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-17 11:30 [PATCH net-next v5 0/5] Rust abstractions for network PHY drivers FUJITA Tomonori
@ 2023-10-17 11:30 ` FUJITA Tomonori
2023-10-18 15:07 ` Benno Lossin
` (2 more replies)
2023-10-17 11:30 ` [PATCH net-next v5 2/5] rust: net::phy add module_phy_driver macro FUJITA Tomonori
` (3 subsequent siblings)
4 siblings, 3 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-17 11:30 UTC (permalink / raw)
To: netdev
Cc: rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg
This patch adds abstractions to implement network PHY drivers; the
driver registration and bindings for some of callback functions in
struct phy_driver and many genphy_ functions.
This feature is enabled with CONFIG_RUST_PHYLIB_ABSTRACTIONS=y.
This patch enables unstable const_maybe_uninit_zeroed feature for
kernel crate to enable unsafe code to handle a constant value with
uninitialized data. With the feature, the abstractions can initialize
a phy_driver structure with zero easily; instead of initializing all
the members by hand. It's supposed to be stable in the not so distant
future.
Link: https://github.com/rust-lang/rust/pull/116218
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
drivers/net/phy/Kconfig | 8 +
rust/bindings/bindings_helper.h | 3 +
rust/kernel/lib.rs | 3 +
rust/kernel/net.rs | 6 +
rust/kernel/net/phy.rs | 701 ++++++++++++++++++++++++++++++++
5 files changed, 721 insertions(+)
create mode 100644 rust/kernel/net.rs
create mode 100644 rust/kernel/net/phy.rs
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 421d2b62918f..0faebdb184ca 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -60,6 +60,14 @@ config FIXED_PHY
Currently tested with mpc866ads and mpc8349e-mitx.
+config RUST_PHYLIB_ABSTRACTIONS
+ bool "PHYLIB abstractions support"
+ depends on RUST
+ depends on PHYLIB=y
+ help
+ Adds support needed for PHY drivers written in Rust. It provides
+ a wrapper around the C phylib core.
+
config SFP
tristate "SFP cage support"
depends on I2C && PHYLINK
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index c91a3c24f607..ec4ee09a34ad 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -8,6 +8,9 @@
#include <kunit/test.h>
#include <linux/errname.h>
+#include <linux/ethtool.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
#include <linux/slab.h>
#include <linux/refcount.h>
#include <linux/wait.h>
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index e8811700239a..0588422e273c 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -14,6 +14,7 @@
#![no_std]
#![feature(allocator_api)]
#![feature(coerce_unsized)]
+#![feature(const_maybe_uninit_zeroed)]
#![feature(dispatch_from_dyn)]
#![feature(new_uninit)]
#![feature(receiver_trait)]
@@ -36,6 +37,8 @@
pub mod ioctl;
#[cfg(CONFIG_KUNIT)]
pub mod kunit;
+#[cfg(CONFIG_NET)]
+pub mod net;
pub mod prelude;
pub mod print;
mod static_assert;
diff --git a/rust/kernel/net.rs b/rust/kernel/net.rs
new file mode 100644
index 000000000000..fe415cb369d3
--- /dev/null
+++ b/rust/kernel/net.rs
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Networking.
+
+#[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)]
+pub mod phy;
diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
new file mode 100644
index 000000000000..7d4927ece32f
--- /dev/null
+++ b/rust/kernel/net/phy.rs
@@ -0,0 +1,701 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2023 FUJITA Tomonori <fujita.tomonori@gmail.com>
+
+//! Network PHY device.
+//!
+//! C headers: [`include/linux/phy.h`](../../../../include/linux/phy.h).
+//!
+//! The C side (PHYLIB) guarantees that there is only one thread accessing to one `phy_device` instance
+//! during a callback in `phy_driver`. The callback safely accesses to the instance exclusively.
+//! Except for `resume()` and `suspend()`, PHYLIB holds `phy_device`'s lock during a callback.
+//! PHYLIB doesn't hold `phy_device`'s lock in both `resume()` and `suspend()`. Instead, PHYLIB
+//! updates `phy_device`'s state with `phy_device`'s lock hold, to guarantee that resume() accesses
+//! to the instance exclusively. Note that `resume()` and `suspend()` also are called where only
+//! one thread can access to the instance.
+//!
+//! This abstractions creates [`Device`] instance only during a callback so it's guaranteed that
+//! only one reference exists. All its methods can accesses to the instance exclusively.
+//!
+//! All the PHYLIB helper functions for `phy_device` modify some members in `phy_device`. Except for
+//! getter functions, [`Device`] methods take `&mut self`. This also applied to `read()`, which reads
+//! a hardware register and updates the stats.
+
+use crate::{bindings, error::*, prelude::vtable, str::CStr, types::Opaque};
+use core::marker::PhantomData;
+
+/// Corresponds to the kernel's `enum phy_state`.
+#[derive(PartialEq)]
+pub enum DeviceState {
+ /// PHY device and driver are not ready for anything.
+ Down,
+ /// PHY is ready to send and receive packets.
+ Ready,
+ /// PHY is up, but no polling or interrupts are done.
+ Halted,
+ /// PHY is up, but is in an error state.
+ Error,
+ /// PHY and attached device are ready to do work.
+ Up,
+ /// PHY is currently running.
+ Running,
+ /// PHY is up, but not currently plugged in.
+ NoLink,
+ /// PHY is performing a cable test.
+ CableTest,
+}
+
+/// Represents duplex mode.
+pub enum DuplexMode {
+ /// PHY is in full-duplex mode.
+ Full,
+ /// PHY is in half-duplex mode.
+ Half,
+ /// PHY is in unknown duplex mode.
+ Unknown,
+}
+
+/// An instance of a PHY device.
+///
+/// Wraps the kernel's `struct phy_device`.
+///
+/// # Invariants
+///
+/// `self.0` is always in a valid state.
+#[repr(transparent)]
+pub struct Device(Opaque<bindings::phy_device>);
+
+impl Device {
+ /// Creates a new [`Device`] instance from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// This function must only be called from the callbacks in `phy_driver`. PHYLIB guarantees
+ /// the exclusive access for the duration of the lifetime `'a`.
+ unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
+ // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::phy_device`.
+ let ptr = ptr.cast::<Self>();
+ // SAFETY: by the function requirements the pointer is valid and we have unique access for
+ // the duration of `'a`.
+ unsafe { &mut *ptr }
+ }
+
+ /// Gets the id of the PHY.
+ pub fn phy_id(&self) -> u32 {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ unsafe { (*phydev).phy_id }
+ }
+
+ /// Gets the state of the PHY.
+ pub fn state(&self) -> DeviceState {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ let state = unsafe { (*phydev).state };
+ // TODO: this conversion code will be replaced with automatically generated code by bindgen
+ // when it becomes possible.
+ // better to call WARN_ONCE() when the state is out-of-range (needs to add WARN_ONCE support).
+ match state {
+ bindings::phy_state_PHY_DOWN => DeviceState::Down,
+ bindings::phy_state_PHY_READY => DeviceState::Ready,
+ bindings::phy_state_PHY_HALTED => DeviceState::Halted,
+ bindings::phy_state_PHY_ERROR => DeviceState::Error,
+ bindings::phy_state_PHY_UP => DeviceState::Up,
+ bindings::phy_state_PHY_RUNNING => DeviceState::Running,
+ bindings::phy_state_PHY_NOLINK => DeviceState::NoLink,
+ bindings::phy_state_PHY_CABLETEST => DeviceState::CableTest,
+ _ => DeviceState::Error,
+ }
+ }
+
+ /// Returns true if the link is up.
+ pub fn get_link(&self) -> bool {
+ const LINK_IS_UP: u32 = 1;
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ let phydev = unsafe { *self.0.get() };
+ phydev.link() == LINK_IS_UP
+ }
+
+ /// Returns true if auto-negotiation is enabled.
+ pub fn is_autoneg_enabled(&self) -> bool {
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ let phydev = unsafe { *self.0.get() };
+ phydev.autoneg() == bindings::AUTONEG_ENABLE
+ }
+
+ /// Returns true if auto-negotiation is completed.
+ pub fn is_autoneg_completed(&self) -> bool {
+ const AUTONEG_COMPLETED: u32 = 1;
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ let phydev = unsafe { *self.0.get() };
+ phydev.autoneg_complete() == AUTONEG_COMPLETED
+ }
+
+ /// Sets the speed of the PHY.
+ pub fn set_speed(&mut self, speed: u32) {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ unsafe { (*phydev).speed = speed as i32 };
+ }
+
+ /// Sets duplex mode.
+ pub fn set_duplex(&mut self, mode: DuplexMode) {
+ let phydev = self.0.get();
+ let v = match mode {
+ DuplexMode::Full => bindings::DUPLEX_FULL as i32,
+ DuplexMode::Half => bindings::DUPLEX_HALF as i32,
+ DuplexMode::Unknown => bindings::DUPLEX_UNKNOWN as i32,
+ };
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ unsafe { (*phydev).duplex = v };
+ }
+
+ /// Reads a given C22 PHY register.
+ pub fn read(&mut self, regnum: u16) -> Result<u16> {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ let ret = unsafe {
+ bindings::mdiobus_read((*phydev).mdio.bus, (*phydev).mdio.addr, regnum.into())
+ };
+ if ret < 0 {
+ Err(Error::from_errno(ret))
+ } else {
+ Ok(ret as u16)
+ }
+ }
+
+ /// Writes a given C22 PHY register.
+ pub fn write(&mut self, regnum: u16, val: u16) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ to_result(unsafe {
+ bindings::mdiobus_write((*phydev).mdio.bus, (*phydev).mdio.addr, regnum.into(), val)
+ })
+ }
+
+ /// Reads a paged register.
+ pub fn read_paged(&mut self, page: u16, regnum: u16) -> Result<u16> {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ let ret = unsafe { bindings::phy_read_paged(phydev, page.into(), regnum.into()) };
+ if ret < 0 {
+ Err(Error::from_errno(ret))
+ } else {
+ Ok(ret as u16)
+ }
+ }
+
+ /// Resolves the advertisements into PHY settings.
+ pub fn resolve_aneg_linkmode(&mut self) {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ unsafe { bindings::phy_resolve_aneg_linkmode(phydev) };
+ }
+
+ /// Executes software reset the PHY via `BMCR_RESET` bit.
+ pub fn genphy_soft_reset(&mut self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ to_result(unsafe { bindings::genphy_soft_reset(phydev) })
+ }
+
+ /// Initializes the PHY.
+ pub fn init_hw(&mut self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // so an FFI call with a valid pointer.
+ to_result(unsafe { bindings::phy_init_hw(phydev) })
+ }
+
+ /// Starts auto-negotiation.
+ pub fn start_aneg(&mut self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ to_result(unsafe { bindings::phy_start_aneg(phydev) })
+ }
+
+ /// Resumes the PHY via `BMCR_PDOWN` bit.
+ pub fn genphy_resume(&mut self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ to_result(unsafe { bindings::genphy_resume(phydev) })
+ }
+
+ /// Suspends the PHY via `BMCR_PDOWN` bit.
+ pub fn genphy_suspend(&mut self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ to_result(unsafe { bindings::genphy_suspend(phydev) })
+ }
+
+ /// Checks the link status and updates current link state.
+ pub fn genphy_read_status(&mut self) -> Result<u16> {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ let ret = unsafe { bindings::genphy_read_status(phydev) };
+ if ret < 0 {
+ Err(Error::from_errno(ret))
+ } else {
+ Ok(ret as u16)
+ }
+ }
+
+ /// Updates the link status.
+ pub fn genphy_update_link(&mut self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ to_result(unsafe { bindings::genphy_update_link(phydev) })
+ }
+
+ /// Reads link partner ability.
+ pub fn genphy_read_lpa(&mut self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ to_result(unsafe { bindings::genphy_read_lpa(phydev) })
+ }
+
+ /// Reads PHY abilities.
+ pub fn genphy_read_abilities(&mut self) -> Result {
+ let phydev = self.0.get();
+ // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
+ // So an FFI call with a valid pointer.
+ to_result(unsafe { bindings::genphy_read_abilities(phydev) })
+ }
+}
+
+/// Defines certain other features this PHY supports (like interrupts).
+///
+/// These flag values are used in [`Driver::FLAGS`].
+pub mod flags {
+ /// PHY is internal.
+ pub const IS_INTERNAL: u32 = bindings::PHY_IS_INTERNAL;
+ /// PHY needs to be reset after the refclk is enabled.
+ pub const RST_AFTER_CLK_EN: u32 = bindings::PHY_RST_AFTER_CLK_EN;
+ /// Polling is used to detect PHY status changes.
+ pub const POLL_CABLE_TEST: u32 = bindings::PHY_POLL_CABLE_TEST;
+ /// Don't suspend.
+ pub const ALWAYS_CALL_SUSPEND: u32 = bindings::PHY_ALWAYS_CALL_SUSPEND;
+}
+
+/// An adapter for the registration of a PHY driver.
+struct Adapter<T: Driver> {
+ _p: PhantomData<T>,
+}
+
+impl<T: Driver> Adapter<T> {
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn soft_reset_callback(
+ phydev: *mut bindings::phy_device,
+ ) -> core::ffi::c_int {
+ from_result(|| {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::soft_reset(dev)?;
+ Ok(0)
+ })
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn get_features_callback(
+ phydev: *mut bindings::phy_device,
+ ) -> core::ffi::c_int {
+ from_result(|| {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::get_features(dev)?;
+ Ok(0)
+ })
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn suspend_callback(phydev: *mut bindings::phy_device) -> core::ffi::c_int {
+ from_result(|| {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::suspend(dev)?;
+ Ok(0)
+ })
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn resume_callback(phydev: *mut bindings::phy_device) -> core::ffi::c_int {
+ from_result(|| {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::resume(dev)?;
+ Ok(0)
+ })
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn config_aneg_callback(
+ phydev: *mut bindings::phy_device,
+ ) -> core::ffi::c_int {
+ from_result(|| {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::config_aneg(dev)?;
+ Ok(0)
+ })
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn read_status_callback(
+ phydev: *mut bindings::phy_device,
+ ) -> core::ffi::c_int {
+ from_result(|| {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::read_status(dev)?;
+ Ok(0)
+ })
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn match_phy_device_callback(
+ phydev: *mut bindings::phy_device,
+ ) -> core::ffi::c_int {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::match_phy_device(dev) as i32
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn read_mmd_callback(
+ phydev: *mut bindings::phy_device,
+ devnum: i32,
+ regnum: u16,
+ ) -> i32 {
+ from_result(|| {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ // CAST: the C side verifies devnum < 32.
+ let ret = T::read_mmd(dev, devnum as u8, regnum)?;
+ Ok(ret.into())
+ })
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn write_mmd_callback(
+ phydev: *mut bindings::phy_device,
+ devnum: i32,
+ regnum: u16,
+ val: u16,
+ ) -> i32 {
+ from_result(|| {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::write_mmd(dev, devnum as u8, regnum, val)?;
+ Ok(0)
+ })
+ }
+
+ /// # Safety
+ ///
+ /// `phydev` must be passed by the corresponding callback in `phy_driver`.
+ unsafe extern "C" fn link_change_notify_callback(phydev: *mut bindings::phy_device) {
+ // SAFETY: Preconditions ensure `phydev` is valid.
+ let dev = unsafe { Device::from_raw(phydev) };
+ T::link_change_notify(dev);
+ }
+}
+
+/// An instance of a PHY driver.
+///
+/// Wraps the kernel's `struct phy_driver`.
+///
+/// # Invariants
+///
+/// `self.0` is always in a valid state.
+#[repr(transparent)]
+pub struct DriverType(Opaque<bindings::phy_driver>);
+
+/// Creates the kernel's `phy_driver` instance.
+///
+/// This is used by [`module_phy_driver`] macro to create a static array of `phy_driver`.
+///
+/// [`module_phy_driver`]: crate::module_phy_driver
+pub const fn create_phy_driver<T: Driver>() -> DriverType {
+ // All the fields of `struct phy_driver` are initialized properly.
+ // It ensures the invariants.
+ DriverType(Opaque::new(bindings::phy_driver {
+ name: T::NAME.as_char_ptr().cast_mut(),
+ flags: T::FLAGS,
+ phy_id: T::PHY_DEVICE_ID.id,
+ phy_id_mask: T::PHY_DEVICE_ID.mask_as_int(),
+ soft_reset: if T::HAS_SOFT_RESET {
+ Some(Adapter::<T>::soft_reset_callback)
+ } else {
+ None
+ },
+ get_features: if T::HAS_GET_FEATURES {
+ Some(Adapter::<T>::get_features_callback)
+ } else {
+ None
+ },
+ match_phy_device: if T::HAS_MATCH_PHY_DEVICE {
+ Some(Adapter::<T>::match_phy_device_callback)
+ } else {
+ None
+ },
+ suspend: if T::HAS_SUSPEND {
+ Some(Adapter::<T>::suspend_callback)
+ } else {
+ None
+ },
+ resume: if T::HAS_RESUME {
+ Some(Adapter::<T>::resume_callback)
+ } else {
+ None
+ },
+ config_aneg: if T::HAS_CONFIG_ANEG {
+ Some(Adapter::<T>::config_aneg_callback)
+ } else {
+ None
+ },
+ read_status: if T::HAS_READ_STATUS {
+ Some(Adapter::<T>::read_status_callback)
+ } else {
+ None
+ },
+ read_mmd: if T::HAS_READ_MMD {
+ Some(Adapter::<T>::read_mmd_callback)
+ } else {
+ None
+ },
+ write_mmd: if T::HAS_WRITE_MMD {
+ Some(Adapter::<T>::write_mmd_callback)
+ } else {
+ None
+ },
+ link_change_notify: if T::HAS_LINK_CHANGE_NOTIFY {
+ Some(Adapter::<T>::link_change_notify_callback)
+ } else {
+ None
+ },
+ // SAFETY: The rest is zeroed out to initialize `struct phy_driver`,
+ // sets `Option<&F>` to be `None`.
+ ..unsafe { core::mem::MaybeUninit::<bindings::phy_driver>::zeroed().assume_init() }
+ }))
+}
+
+/// Corresponds to functions in `struct phy_driver`.
+///
+/// This is used to register a PHY driver.
+#[vtable]
+pub trait Driver {
+ /// Defines certain other features this PHY supports.
+ /// It is a combination of the flags in the [`flags`] module.
+ const FLAGS: u32 = 0;
+
+ /// The friendly name of this PHY type.
+ const NAME: &'static CStr;
+
+ /// This driver only works for PHYs with IDs which match this field.
+ /// The default id and mask are zero.
+ const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_custom_mask(0, 0);
+
+ /// Issues a PHY software reset.
+ fn soft_reset(_dev: &mut Device) -> Result {
+ Err(code::ENOTSUPP)
+ }
+
+ /// Probes the hardware to determine what abilities it has.
+ fn get_features(_dev: &mut Device) -> Result {
+ Err(code::ENOTSUPP)
+ }
+
+ /// Returns true if this is a suitable driver for the given phydev.
+ /// If not implemented, matching is based on [`Driver::PHY_DEVICE_ID`].
+ fn match_phy_device(_dev: &Device) -> bool {
+ false
+ }
+
+ /// Configures the advertisement and resets auto-negotiation
+ /// if auto-negotiation is enabled.
+ fn config_aneg(_dev: &mut Device) -> Result {
+ Err(code::ENOTSUPP)
+ }
+
+ /// Determines the negotiated speed and duplex.
+ fn read_status(_dev: &mut Device) -> Result<u16> {
+ Err(code::ENOTSUPP)
+ }
+
+ /// Suspends the hardware, saving state if needed.
+ fn suspend(_dev: &mut Device) -> Result {
+ Err(code::ENOTSUPP)
+ }
+
+ /// Resumes the hardware, restoring state if needed.
+ fn resume(_dev: &mut Device) -> Result {
+ Err(code::ENOTSUPP)
+ }
+
+ /// Overrides the default MMD read function for reading a MMD register.
+ fn read_mmd(_dev: &mut Device, _devnum: u8, _regnum: u16) -> Result<u16> {
+ Err(code::ENOTSUPP)
+ }
+
+ /// Overrides the default MMD write function for writing a MMD register.
+ fn write_mmd(_dev: &mut Device, _devnum: u8, _regnum: u16, _val: u16) -> Result {
+ Err(code::ENOTSUPP)
+ }
+
+ /// Callback for notification of link change.
+ fn link_change_notify(_dev: &mut Device) {}
+}
+
+/// Registration structure for a PHY driver.
+///
+/// # Invariants
+///
+/// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
+pub struct Registration {
+ drivers: &'static [DriverType],
+}
+
+impl Registration {
+ /// Registers a PHY driver.
+ pub fn register(
+ module: &'static crate::ThisModule,
+ drivers: &'static [DriverType],
+ ) -> Result<Self> {
+ if drivers.is_empty() {
+ return Err(code::EINVAL);
+ }
+ // SAFETY: The type invariants of [`DriverType`] ensure that all elements of the `drivers` slice
+ // are initialized properly. So an FFI call with a valid pointer.
+ to_result(unsafe {
+ bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
+ })?;
+ // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
+ Ok(Registration { drivers })
+ }
+}
+
+impl Drop for Registration {
+ fn drop(&mut self) {
+ // SAFETY: The type invariants guarantee that `self.drivers` is valid.
+ // So an FFI call with a valid pointer.
+ unsafe {
+ bindings::phy_drivers_unregister(self.drivers[0].0.get(), self.drivers.len() as i32)
+ };
+ }
+}
+
+// SAFETY: `Registration` does not expose any of its state across threads.
+unsafe impl Send for Registration {}
+
+// SAFETY: `Registration` does not expose any of its state across threads.
+unsafe impl Sync for Registration {}
+
+/// Represents the kernel's `struct mdio_device_id`.
+pub struct DeviceId {
+ id: u32,
+ mask: DeviceMask,
+}
+
+impl DeviceId {
+ /// Creates a new instance with the exact match mask.
+ pub const fn new_with_exact_mask(id: u32) -> Self {
+ DeviceId {
+ id,
+ mask: DeviceMask::Exact,
+ }
+ }
+
+ /// Creates a new instance with the model match mask.
+ pub const fn new_with_model_mask(id: u32) -> Self {
+ DeviceId {
+ id,
+ mask: DeviceMask::Model,
+ }
+ }
+
+ /// Creates a new instance with the vendor match mask.
+ pub const fn new_with_vendor_mask(id: u32) -> Self {
+ DeviceId {
+ id,
+ mask: DeviceMask::Vendor,
+ }
+ }
+
+ /// Creates a new instance with a custom match mask.
+ pub const fn new_with_custom_mask(id: u32, mask: u32) -> Self {
+ DeviceId {
+ id,
+ mask: DeviceMask::Custom(mask),
+ }
+ }
+
+ /// Creates a new instance from [`Driver`].
+ pub const fn new_with_driver<T: Driver>() -> Self {
+ T::PHY_DEVICE_ID
+ }
+
+ /// Get a `mask` as u32.
+ pub const fn mask_as_int(&self) -> u32 {
+ self.mask.as_int()
+ }
+
+ // macro use only
+ #[doc(hidden)]
+ pub const fn mdio_device_id(&self) -> bindings::mdio_device_id {
+ bindings::mdio_device_id {
+ phy_id: self.id,
+ phy_id_mask: self.mask.as_int(),
+ }
+ }
+}
+
+enum DeviceMask {
+ Exact,
+ Model,
+ Vendor,
+ Custom(u32),
+}
+
+impl DeviceMask {
+ const MASK_EXACT: u32 = !0;
+ const MASK_MODEL: u32 = !0 << 4;
+ const MASK_VENDOR: u32 = !0 << 10;
+
+ const fn as_int(&self) -> u32 {
+ match self {
+ DeviceMask::Exact => Self::MASK_EXACT,
+ DeviceMask::Model => Self::MASK_MODEL,
+ DeviceMask::Vendor => Self::MASK_VENDOR,
+ DeviceMask::Custom(mask) => *mask,
+ }
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 65+ messages in thread
* [PATCH net-next v5 2/5] rust: net::phy add module_phy_driver macro
2023-10-17 11:30 [PATCH net-next v5 0/5] Rust abstractions for network PHY drivers FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 1/5] rust: core " FUJITA Tomonori
@ 2023-10-17 11:30 ` FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking FUJITA Tomonori
` (2 subsequent siblings)
4 siblings, 0 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-17 11:30 UTC (permalink / raw)
To: netdev
Cc: rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg
This macro creates a static array of kernel's `struct phy_driver` and
registers it. This also corresponds to the kernel's
`MODULE_DEVICE_TABLE` macro, which embeds the information for module
loading into the module binary file.
A PHY driver should use this macro.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
rust/kernel/net/phy.rs | 134 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 134 insertions(+)
diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
index 7d4927ece32f..5b74a7450b36 100644
--- a/rust/kernel/net/phy.rs
+++ b/rust/kernel/net/phy.rs
@@ -699,3 +699,137 @@ const fn as_int(&self) -> u32 {
}
}
}
+
+/// Declares a kernel module for PHYs drivers.
+///
+/// This creates a static array of kernel's `struct phy_driver` and registers it.
+/// This also corresponds to the kernel's `MODULE_DEVICE_TABLE` macro, which embeds the information
+/// for module loading into the module binary file. Every driver needs an entry in `device_table`.
+///
+/// # Examples
+///
+/// ```ignore
+/// use kernel::c_str;
+/// use kernel::net::phy::{self, DeviceId};
+/// use kernel::prelude::*;
+///
+/// kernel::module_phy_driver! {
+/// drivers: [PhyAX88772A],
+/// device_table: [
+/// DeviceId::new_with_driver::<PhyAX88772A>(),
+/// ],
+/// name: "rust_asix_phy",
+/// author: "Rust for Linux Contributors",
+/// description: "Rust Asix PHYs driver",
+/// license: "GPL",
+/// }
+///
+/// struct PhyAX88772A;
+///
+/// impl phy::Driver for PhyAX88772A {
+/// const NAME: &'static CStr = c_str!("Asix Electronics AX88772A");
+/// const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x003b1861);
+/// }
+/// ```
+///
+/// This expands to the following code:
+///
+/// ```ignore
+/// use kernel::c_str;
+/// use kernel::net::phy::{self, DeviceId};
+/// use kernel::prelude::*;
+///
+/// struct Module {
+/// _reg: ::kernel::net::phy::Registration,
+/// }
+///
+/// module! {
+/// type: Module,
+/// name: "rust_asix_phy",
+/// author: "Rust for Linux Contributors",
+/// description: "Rust Asix PHYs driver",
+/// license: "GPL",
+/// }
+///
+/// const _: () = {
+/// static mut DRIVERS: [::kernel::net::phy::DriverType; 1] = [
+/// ::kernel::net::phy::create_phy_driver::<PhyAX88772A>::(),
+/// ];
+///
+/// impl ::kernel::Module for Module {
+/// fn init(module: &'static ThisModule) -> Result<Self> {
+/// let mut reg = unsafe { ::kernel::net::phy::Registration::register(module, &DRIVERS) }?;
+/// Ok(Module {
+/// _reg: reg,
+/// })
+/// }
+/// }
+/// }
+///
+/// static __mod_mdio__phydev_device_table: [::kernel::bindings::mdio_device_id; 2] = [
+/// ::kernel::bindings::mdio_device_id {
+/// phy_id: 0x003b1861,
+/// phy_id_mask: 0xffffffff,
+/// },
+/// ::kernel::bindings::mdio_device_id {
+/// phy_id: 0,
+/// phy_id_mask: 0,
+/// }
+/// ];
+/// ```
+#[macro_export]
+macro_rules! module_phy_driver {
+ (@replace_expr $_t:tt $sub:expr) => {$sub};
+
+ (@count_devices $($x:expr),*) => {
+ 0usize $(+ $crate::module_phy_driver!(@replace_expr $x 1usize))*
+ };
+
+ (@device_table [$($dev:expr),+]) => {
+ #[no_mangle]
+ static __mod_mdio__phydev_device_table: [
+ ::kernel::bindings::mdio_device_id;
+ $crate::module_phy_driver!(@count_devices $($dev),+) + 1
+ ] = [
+ $($dev.mdio_device_id()),+,
+ ::kernel::bindings::mdio_device_id {
+ phy_id: 0,
+ phy_id_mask: 0
+ }
+ ];
+ };
+
+ (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
+ struct Module {
+ _reg: ::kernel::net::phy::Registration,
+ }
+
+ $crate::prelude::module! {
+ type: Module,
+ $($f)*
+ }
+
+ const _: () = {
+ static mut DRIVERS: [
+ ::kernel::net::phy::DriverType;
+ $crate::module_phy_driver!(@count_devices $($driver),+)
+ ] = [
+ $(::kernel::net::phy::create_phy_driver::<$driver>()),+
+ ];
+
+ impl ::kernel::Module for Module {
+ fn init(module: &'static ThisModule) -> Result<Self> {
+ // SAFETY: The anonymous constant guarantees that nobody else can access the `DRIVERS` static.
+ // The array is used only in the C side.
+ let mut reg = unsafe { ::kernel::net::phy::Registration::register(module, &DRIVERS) }?;
+
+ Ok(Module {
+ _reg: reg,
+ })
+ }
+ }
+ };
+
+ $crate::module_phy_driver!(@device_table [$($dev),+]);
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 65+ messages in thread
* [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-17 11:30 [PATCH net-next v5 0/5] Rust abstractions for network PHY drivers FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 1/5] rust: core " FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 2/5] rust: net::phy add module_phy_driver macro FUJITA Tomonori
@ 2023-10-17 11:30 ` FUJITA Tomonori
2023-10-20 11:37 ` Andreas Hindborg (Samsung)
2023-10-17 11:30 ` [PATCH net-next v5 4/5] MAINTAINERS: add Rust PHY abstractions for ETHERNET PHY LIBRARY FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 5/5] net: phy: add Rust Asix PHY driver FUJITA Tomonori
4 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-17 11:30 UTC (permalink / raw)
To: netdev
Cc: rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg, Miguel Ojeda
From: Miguel Ojeda <ojeda@kernel.org>
error[E0005]: refutable pattern in function argument
--> rust/bindings/bindings_enum_check.rs:29:6
|
29 | (phy_state::PHY_DOWN
| ______^
30 | | | phy_state::PHY_READY
31 | | | phy_state::PHY_HALTED
32 | | | phy_state::PHY_ERROR
... |
35 | | | phy_state::PHY_NOLINK
36 | | | phy_state::PHY_CABLETEST): phy_state,
| |______________________________^ pattern `phy_state::PHY_NEW` not covered
|
note: `phy_state` defined here
--> rust/bindings/bindings_generated_enum_check.rs:60739:10
|
60739 | pub enum phy_state {
| ^^^^^^^^^
...
60745 | PHY_NEW = 5,
| ------- not covered
= note: the matched value is of type `phy_state`
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
rust/.gitignore | 1 +
rust/Makefile | 14 +++++++++++
rust/bindings/bindings_enum_check.rs | 36 ++++++++++++++++++++++++++++
3 files changed, 51 insertions(+)
create mode 100644 rust/bindings/bindings_enum_check.rs
diff --git a/rust/.gitignore b/rust/.gitignore
index d3829ffab80b..1a76ad0d6603 100644
--- a/rust/.gitignore
+++ b/rust/.gitignore
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
bindings_generated.rs
+bindings_generated_enum_check.rs
bindings_helpers_generated.rs
doctests_kernel_generated.rs
doctests_kernel_generated_kunit.c
diff --git a/rust/Makefile b/rust/Makefile
index 87958e864be0..4a1c7a48dfad 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -15,6 +15,7 @@ always-$(CONFIG_RUST) += libmacros.so
no-clean-files += libmacros.so
always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs
+always-$(CONFIG_RUST) += bindings/bindings_generated_enum_check.rs
obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o
always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \
exports_kernel_generated.h
@@ -341,6 +342,19 @@ $(obj)/bindings/bindings_generated.rs: $(src)/bindings/bindings_helper.h \
$(src)/bindgen_parameters FORCE
$(call if_changed_dep,bindgen)
+$(obj)/bindings/bindings_generated_enum_check.rs: private bindgen_target_flags = \
+ $(shell grep -v '^#\|^$$' $(srctree)/$(src)/bindgen_parameters) \
+ --default-enum-style rust
+$(obj)/bindings/bindings_generated_enum_check.rs: private bindgen_target_extra = ; \
+ OBJTREE=$(abspath $(objtree)) $(RUSTC_OR_CLIPPY) $(rust_flags) $(rustc_target_flags) \
+ --crate-type rlib -L$(objtree)/$(obj) \
+ --emit=dep-info=$(obj)/bindings/.bindings_enum_check.rs.d \
+ --emit=metadata=$(obj)/bindings/libbindings_enum_check.rmeta \
+ --crate-name enum_check $(obj)/bindings/bindings_enum_check.rs
+$(obj)/bindings/bindings_generated_enum_check.rs: $(src)/bindings/bindings_helper.h \
+ $(src)/bindings/bindings_enum_check.rs $(src)/bindgen_parameters FORCE
+ $(call if_changed_dep,bindgen)
+
$(obj)/uapi/uapi_generated.rs: private bindgen_target_flags = \
$(shell grep -v '^#\|^$$' $(srctree)/$(src)/bindgen_parameters)
$(obj)/uapi/uapi_generated.rs: $(src)/uapi/uapi_helper.h \
diff --git a/rust/bindings/bindings_enum_check.rs b/rust/bindings/bindings_enum_check.rs
new file mode 100644
index 000000000000..eef7e9ca3c54
--- /dev/null
+++ b/rust/bindings/bindings_enum_check.rs
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Bindings' enum exhaustiveness check.
+//!
+//! Eventually, this should be replaced by a safe version of `--rustified-enum`, see
+//! https://github.com/rust-lang/rust-bindgen/issues/2646.
+
+#![no_std]
+#![allow(
+ clippy::all,
+ dead_code,
+ missing_docs,
+ non_camel_case_types,
+ non_upper_case_globals,
+ non_snake_case,
+ improper_ctypes,
+ unreachable_pub,
+ unsafe_op_in_unsafe_fn
+)]
+
+include!(concat!(
+ env!("OBJTREE"),
+ "/rust/bindings/bindings_generated_enum_check.rs"
+));
+
+fn check_phy_state(
+ (phy_state::PHY_DOWN
+ | phy_state::PHY_READY
+ | phy_state::PHY_HALTED
+ | phy_state::PHY_ERROR
+ | phy_state::PHY_UP
+ | phy_state::PHY_RUNNING
+ | phy_state::PHY_NOLINK
+ | phy_state::PHY_CABLETEST): phy_state,
+) {
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 65+ messages in thread
* [PATCH net-next v5 4/5] MAINTAINERS: add Rust PHY abstractions for ETHERNET PHY LIBRARY
2023-10-17 11:30 [PATCH net-next v5 0/5] Rust abstractions for network PHY drivers FUJITA Tomonori
` (2 preceding siblings ...)
2023-10-17 11:30 ` [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking FUJITA Tomonori
@ 2023-10-17 11:30 ` FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 5/5] net: phy: add Rust Asix PHY driver FUJITA Tomonori
4 siblings, 0 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-17 11:30 UTC (permalink / raw)
To: netdev
Cc: rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg
Adds me as a maintainer and Trevor as a reviewer.
The files are placed at rust/kernel/ directory for now but the files
are likely to be moved to net/ directory once a new Rust build system
is implemented.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index ed33b9df8b3d..e85651c35cb9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7818,6 +7818,14 @@ F: include/uapi/linux/mdio.h
F: include/uapi/linux/mii.h
F: net/core/of_net.c
+ETHERNET PHY LIBRARY [RUST]
+M: FUJITA Tomonori <fujita.tomonori@gmail.com>
+R: Trevor Gross <tmgross@umich.edu>
+L: netdev@vger.kernel.org
+L: rust-for-linux@vger.kernel.org
+S: Maintained
+F: rust/kernel/net/phy.rs
+
EXEC & BINFMT API
R: Eric Biederman <ebiederm@xmission.com>
R: Kees Cook <keescook@chromium.org>
--
2.34.1
^ permalink raw reply related [flat|nested] 65+ messages in thread
* [PATCH net-next v5 5/5] net: phy: add Rust Asix PHY driver
2023-10-17 11:30 [PATCH net-next v5 0/5] Rust abstractions for network PHY drivers FUJITA Tomonori
` (3 preceding siblings ...)
2023-10-17 11:30 ` [PATCH net-next v5 4/5] MAINTAINERS: add Rust PHY abstractions for ETHERNET PHY LIBRARY FUJITA Tomonori
@ 2023-10-17 11:30 ` FUJITA Tomonori
4 siblings, 0 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-17 11:30 UTC (permalink / raw)
To: netdev
Cc: rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg
This is the Rust implementation of drivers/net/phy/ax88796b.c. The
features are equivalent. You can choose C or Rust versionon kernel
configuration.
Reviewed-by: Trevor Gross <tmgross@umich.edu>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
MAINTAINERS | 8 ++
drivers/net/phy/Kconfig | 8 ++
drivers/net/phy/Makefile | 6 +-
drivers/net/phy/ax88796b_rust.rs | 129 +++++++++++++++++++++++++++++++
rust/uapi/uapi_helper.h | 2 +
5 files changed, 152 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/phy/ax88796b_rust.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index e85651c35cb9..fad9563d2c80 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3058,6 +3058,14 @@ S: Maintained
F: Documentation/devicetree/bindings/net/asix,ax88796c.yaml
F: drivers/net/ethernet/asix/ax88796c_*
+ASIX PHY DRIVER [RUST]
+M: FUJITA Tomonori <fujita.tomonori@gmail.com>
+R: Trevor Gross <tmgross@umich.edu>
+L: netdev@vger.kernel.org
+L: rust-for-linux@vger.kernel.org
+S: Maintained
+F: drivers/net/phy/ax88796b_rust.rs
+
ASPEED CRYPTO DRIVER
M: Neal Liu <neal_liu@aspeedtech.com>
L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 0faebdb184ca..11b18370a05b 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -115,6 +115,14 @@ config AX88796B_PHY
Currently supports the Asix Electronics PHY found in the X-Surf 100
AX88796B package.
+config AX88796B_RUST_PHY
+ bool "Rust reference driver for Asix PHYs"
+ depends on RUST_PHYLIB_ABSTRACTIONS && AX88796B_PHY
+ help
+ Uses the Rust reference driver for Asix PHYs (ax88796b_rust.ko).
+ The features are equivalent. It supports the Asix Electronics PHY
+ found in the X-Surf 100 AX88796B package.
+
config BROADCOM_PHY
tristate "Broadcom 54XX PHYs"
select BCM_NET_PHYLIB
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index c945ed9bd14b..58d7dfb095ab 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -41,7 +41,11 @@ aquantia-objs += aquantia_hwmon.o
endif
obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
obj-$(CONFIG_AT803X_PHY) += at803x.o
-obj-$(CONFIG_AX88796B_PHY) += ax88796b.o
+ifdef CONFIG_AX88796B_RUST_PHY
+ obj-$(CONFIG_AX88796B_PHY) += ax88796b_rust.o
+else
+ obj-$(CONFIG_AX88796B_PHY) += ax88796b.o
+endif
obj-$(CONFIG_BCM54140_PHY) += bcm54140.o
obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o
obj-$(CONFIG_BCM7XXX_PHY) += bcm7xxx.o
diff --git a/drivers/net/phy/ax88796b_rust.rs b/drivers/net/phy/ax88796b_rust.rs
new file mode 100644
index 000000000000..017f817f6f8d
--- /dev/null
+++ b/drivers/net/phy/ax88796b_rust.rs
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2023 FUJITA Tomonori <fujita.tomonori@gmail.com>
+
+//! Rust Asix PHYs driver
+//!
+//! C version of this driver: [`drivers/net/phy/ax88796b.c`](./ax88796b.c)
+use kernel::c_str;
+use kernel::net::phy::{self, DeviceId, Driver};
+use kernel::prelude::*;
+use kernel::uapi;
+
+kernel::module_phy_driver! {
+ drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B],
+ device_table: [
+ DeviceId::new_with_driver::<PhyAX88772A>(),
+ DeviceId::new_with_driver::<PhyAX88772C>(),
+ DeviceId::new_with_driver::<PhyAX88796B>()
+ ],
+ name: "rust_asix_phy",
+ author: "FUJITA Tomonori <fujita.tomonori@gmail.com>",
+ description: "Rust Asix PHYs driver",
+ license: "GPL",
+}
+
+// Performs a software PHY reset using the standard
+// BMCR_RESET bit and poll for the reset bit to be cleared.
+// Toggle BMCR_RESET bit off to accommodate broken AX8796B PHY implementation
+// such as used on the Individual Computers' X-Surf 100 Zorro card.
+fn asix_soft_reset(dev: &mut phy::Device) -> Result {
+ dev.write(uapi::MII_BMCR as u16, 0)?;
+ dev.genphy_soft_reset()
+}
+
+struct PhyAX88772A;
+
+#[vtable]
+impl phy::Driver for PhyAX88772A {
+ const FLAGS: u32 = phy::flags::IS_INTERNAL;
+ const NAME: &'static CStr = c_str!("Asix Electronics AX88772A");
+ const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x003b1861);
+
+ // AX88772A is not working properly with some old switches (NETGEAR EN 108TP):
+ // after autoneg is done and the link status is reported as active, the MII_LPA
+ // register is 0. This issue is not reproducible on AX88772C.
+ fn read_status(dev: &mut phy::Device) -> Result<u16> {
+ dev.genphy_update_link()?;
+ if !dev.get_link() {
+ return Ok(0);
+ }
+ // If MII_LPA is 0, phy_resolve_aneg_linkmode() will fail to resolve
+ // linkmode so use MII_BMCR as default values.
+ let ret = dev.read(uapi::MII_BMCR as u16)?;
+
+ if ret as u32 & uapi::BMCR_SPEED100 != 0 {
+ dev.set_speed(uapi::SPEED_100);
+ } else {
+ dev.set_speed(uapi::SPEED_10);
+ }
+
+ let duplex = if ret as u32 & uapi::BMCR_FULLDPLX != 0 {
+ phy::DuplexMode::Full
+ } else {
+ phy::DuplexMode::Half
+ };
+ dev.set_duplex(duplex);
+
+ dev.genphy_read_lpa()?;
+
+ if dev.is_autoneg_enabled() && dev.is_autoneg_completed() {
+ dev.resolve_aneg_linkmode();
+ }
+
+ Ok(0)
+ }
+
+ fn suspend(dev: &mut phy::Device) -> Result {
+ dev.genphy_suspend()
+ }
+
+ fn resume(dev: &mut phy::Device) -> Result {
+ dev.genphy_resume()
+ }
+
+ fn soft_reset(dev: &mut phy::Device) -> Result {
+ asix_soft_reset(dev)
+ }
+
+ fn link_change_notify(dev: &mut phy::Device) {
+ // Reset PHY, otherwise MII_LPA will provide outdated information.
+ // This issue is reproducible only with some link partner PHYs.
+ if dev.state() == phy::DeviceState::NoLink {
+ let _ = dev.init_hw();
+ let _ = dev.start_aneg();
+ }
+ }
+}
+
+struct PhyAX88772C;
+
+#[vtable]
+impl Driver for PhyAX88772C {
+ const FLAGS: u32 = phy::flags::IS_INTERNAL;
+ const NAME: &'static CStr = c_str!("Asix Electronics AX88772C");
+ const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_exact_mask(0x003b1881);
+
+ fn suspend(dev: &mut phy::Device) -> Result {
+ dev.genphy_suspend()
+ }
+
+ fn resume(dev: &mut phy::Device) -> Result {
+ dev.genphy_resume()
+ }
+
+ fn soft_reset(dev: &mut phy::Device) -> Result {
+ asix_soft_reset(dev)
+ }
+}
+
+struct PhyAX88796B;
+
+#[vtable]
+impl Driver for PhyAX88796B {
+ const NAME: &'static CStr = c_str!("Asix Electronics AX88796B");
+ const PHY_DEVICE_ID: phy::DeviceId = phy::DeviceId::new_with_model_mask(0x003b1841);
+
+ fn soft_reset(dev: &mut phy::Device) -> Result {
+ asix_soft_reset(dev)
+ }
+}
diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h
index 301f5207f023..08f5e9334c9e 100644
--- a/rust/uapi/uapi_helper.h
+++ b/rust/uapi/uapi_helper.h
@@ -7,3 +7,5 @@
*/
#include <uapi/asm-generic/ioctl.h>
+#include <uapi/linux/mii.h>
+#include <uapi/linux/ethtool.h>
--
2.34.1
^ permalink raw reply related [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-17 11:30 ` [PATCH net-next v5 1/5] rust: core " FUJITA Tomonori
@ 2023-10-18 15:07 ` Benno Lossin
2023-10-19 0:24 ` FUJITA Tomonori
2023-10-20 18:42 ` Andrew Lunn
2023-10-18 20:27 ` Andrew Lunn
2023-10-20 17:26 ` Andreas Hindborg (Samsung)
2 siblings, 2 replies; 65+ messages in thread
From: Benno Lossin @ 2023-10-18 15:07 UTC (permalink / raw)
To: FUJITA Tomonori, netdev
Cc: rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 17.10.23 13:30, FUJITA Tomonori wrote:
> This patch adds abstractions to implement network PHY drivers; the
> driver registration and bindings for some of callback functions in
> struct phy_driver and many genphy_ functions.
>
> This feature is enabled with CONFIG_RUST_PHYLIB_ABSTRACTIONS=y.
>
> This patch enables unstable const_maybe_uninit_zeroed feature for
> kernel crate to enable unsafe code to handle a constant value with
> uninitialized data. With the feature, the abstractions can initialize
> a phy_driver structure with zero easily; instead of initializing all
> the members by hand. It's supposed to be stable in the not so distant
> future.
>
> Link: https://github.com/rust-lang/rust/pull/116218
>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> ---
> drivers/net/phy/Kconfig | 8 +
> rust/bindings/bindings_helper.h | 3 +
> rust/kernel/lib.rs | 3 +
> rust/kernel/net.rs | 6 +
> rust/kernel/net/phy.rs | 701 ++++++++++++++++++++++++++++++++
> 5 files changed, 721 insertions(+)
> create mode 100644 rust/kernel/net.rs
> create mode 100644 rust/kernel/net/phy.rs
>
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index 421d2b62918f..0faebdb184ca 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -60,6 +60,14 @@ config FIXED_PHY
>
> Currently tested with mpc866ads and mpc8349e-mitx.
>
> +config RUST_PHYLIB_ABSTRACTIONS
> + bool "PHYLIB abstractions support"
> + depends on RUST
> + depends on PHYLIB=y
> + help
> + Adds support needed for PHY drivers written in Rust. It provides
> + a wrapper around the C phylib core.
> +
> config SFP
> tristate "SFP cage support"
> depends on I2C && PHYLINK
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index c91a3c24f607..ec4ee09a34ad 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -8,6 +8,9 @@
>
> #include <kunit/test.h>
> #include <linux/errname.h>
> +#include <linux/ethtool.h>
> +#include <linux/mdio.h>
> +#include <linux/phy.h>
> #include <linux/slab.h>
> #include <linux/refcount.h>
> #include <linux/wait.h>
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index e8811700239a..0588422e273c 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -14,6 +14,7 @@
> #![no_std]
> #![feature(allocator_api)]
> #![feature(coerce_unsized)]
> +#![feature(const_maybe_uninit_zeroed)]
> #![feature(dispatch_from_dyn)]
> #![feature(new_uninit)]
> #![feature(receiver_trait)]
> @@ -36,6 +37,8 @@
> pub mod ioctl;
> #[cfg(CONFIG_KUNIT)]
> pub mod kunit;
> +#[cfg(CONFIG_NET)]
> +pub mod net;
> pub mod prelude;
> pub mod print;
> mod static_assert;
> diff --git a/rust/kernel/net.rs b/rust/kernel/net.rs
> new file mode 100644
> index 000000000000..fe415cb369d3
> --- /dev/null
> +++ b/rust/kernel/net.rs
> @@ -0,0 +1,6 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Networking.
> +
> +#[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)]
> +pub mod phy;
> diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
> new file mode 100644
> index 000000000000..7d4927ece32f
> --- /dev/null
> +++ b/rust/kernel/net/phy.rs
> @@ -0,0 +1,701 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +// Copyright (C) 2023 FUJITA Tomonori <fujita.tomonori@gmail.com>
> +
> +//! Network PHY device.
> +//!
> +//! C headers: [`include/linux/phy.h`](../../../../include/linux/phy.h).
> +//!
Add a new section "# Abstraction Overview" or similar.
> +//! The C side (PHYLIB) guarantees that there is only one thread accessing to one `phy_device` instance
> +//! during a callback in `phy_driver`. The callback safely accesses to the instance exclusively.
> +//! Except for `resume()` and `suspend()`, PHYLIB holds `phy_device`'s lock during a callback.
I would start with "During the calls to most functions in [`Driver`],
the C side (`PHYLIB`) holds a lock that is unique for every instance of
[`Device`]". Then you can note the exception for `resume` and `suspend`
(and also link them e.g. [`Driver::resume`]).
> +//! PHYLIB doesn't hold `phy_device`'s lock in both `resume()` and `suspend()`. Instead, PHYLIB
> +//! updates `phy_device`'s state with `phy_device`'s lock hold, to guarantee that resume() accesses
> +//! to the instance exclusively. Note that `resume()` and `suspend()` also are called where only
> +//! one thread can access to the instance.
I would just write "`PHYLIB` uses a different serialization technique for
[`Driver::resume`] and [`Driver::suspend`]: <use the above explanation>".
> +//! This abstractions creates [`Device`] instance only during a callback so it's guaranteed that
> +//! only one reference exists. All its methods can accesses to the instance exclusively.
Not really sure if this is needed, what are you trying to explain here?
> +//! All the PHYLIB helper functions for `phy_device` modify some members in `phy_device`. Except for
> +//! getter functions, [`Device`] methods take `&mut self`. This also applied to `read()`, which reads
> +//! a hardware register and updates the stats.
I would use [`Device`] instead of `phy_device`, since the Rust reader
might not be aware what wraps `phy_device`.
> +use crate::{bindings, error::*, prelude::vtable, str::CStr, types::Opaque};
> +use core::marker::PhantomData;
> +
> +/// Corresponds to the kernel's `enum phy_state`.
> +#[derive(PartialEq)]
> +pub enum DeviceState {
> + /// PHY device and driver are not ready for anything.
> + Down,
> + /// PHY is ready to send and receive packets.
> + Ready,
> + /// PHY is up, but no polling or interrupts are done.
> + Halted,
> + /// PHY is up, but is in an error state.
> + Error,
> + /// PHY and attached device are ready to do work.
> + Up,
> + /// PHY is currently running.
> + Running,
> + /// PHY is up, but not currently plugged in.
> + NoLink,
> + /// PHY is performing a cable test.
> + CableTest,
> +}
> +
> +/// Represents duplex mode.
> +pub enum DuplexMode {
> + /// PHY is in full-duplex mode.
> + Full,
> + /// PHY is in half-duplex mode.
> + Half,
> + /// PHY is in unknown duplex mode.
> + Unknown,
> +}
> +
> +/// An instance of a PHY device.
> +///
> +/// Wraps the kernel's `struct phy_device`.
> +///
> +/// # Invariants
> +///
> +/// `self.0` is always in a valid state.
> +#[repr(transparent)]
> +pub struct Device(Opaque<bindings::phy_device>);
> +
> +impl Device {
> + /// Creates a new [`Device`] instance from a raw pointer.
> + ///
> + /// # Safety
> + ///
> + /// This function must only be called from the callbacks in `phy_driver`. PHYLIB guarantees
> + /// the exclusive access for the duration of the lifetime `'a`.
I would not put the second sentence in the `# Safety` section. Just move it
above. The reason behind this is the following: the second sentence is not
a precondition needed to call the function.
> + unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
> + // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::phy_device`.
> + let ptr = ptr.cast::<Self>();
> + // SAFETY: by the function requirements the pointer is valid and we have unique access for
> + // the duration of `'a`.
> + unsafe { &mut *ptr }
> + }
> +
> + /// Gets the id of the PHY.
> + pub fn phy_id(&self) -> u32 {
> + let phydev = self.0.get();
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + unsafe { (*phydev).phy_id }
> + }
> +
> + /// Gets the state of the PHY.
> + pub fn state(&self) -> DeviceState {
> + let phydev = self.0.get();
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + let state = unsafe { (*phydev).state };
> + // TODO: this conversion code will be replaced with automatically generated code by bindgen
> + // when it becomes possible.
> + // better to call WARN_ONCE() when the state is out-of-range (needs to add WARN_ONCE support).
> + match state {
> + bindings::phy_state_PHY_DOWN => DeviceState::Down,
> + bindings::phy_state_PHY_READY => DeviceState::Ready,
> + bindings::phy_state_PHY_HALTED => DeviceState::Halted,
> + bindings::phy_state_PHY_ERROR => DeviceState::Error,
> + bindings::phy_state_PHY_UP => DeviceState::Up,
> + bindings::phy_state_PHY_RUNNING => DeviceState::Running,
> + bindings::phy_state_PHY_NOLINK => DeviceState::NoLink,
> + bindings::phy_state_PHY_CABLETEST => DeviceState::CableTest,
> + _ => DeviceState::Error,
> + }
> + }
> +
> + /// Returns true if the link is up.
> + pub fn get_link(&self) -> bool {
I still think this name should be changed. My response at [1] has not yet
been replied to. This has already been discussed before:
- https://lore.kernel.org/rust-for-linux/2023100237-satirical-prance-bd57@gregkh/
- https://lore.kernel.org/rust-for-linux/20231004.084644.50784533959398755.fujita.tomonori@gmail.com/
- https://lore.kernel.org/rust-for-linux/CALNs47syMxiZBUwKLk3vKxzmCbX0FS5A37FjwUzZO9Fn-iPaoA@mail.gmail.com/
And I want to suggest to change it to `is_link_up`.
Reasons why I do not like the name:
- `get_`/`put_` are used for ref counting on the C side, I would like to
avoid confusion.
- `get` in Rust is often used to fetch a value from e.g. a datastructure
such as a hashmap, so I expect the call to do some computation.
- getters in Rust usually are not prefixed with `get_`, but rather are
just the name of the field.
- in this case I like the name `is_link_up` much better, since code becomes
a lot more readable with that.
- I do not want this pattern as an example for other drivers.
[1]: https://lore.kernel.org/rust-for-linux/f5878806-5ba2-d932-858d-dda3f55ceb67@proton.me/
> + const LINK_IS_UP: u32 = 1;
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + let phydev = unsafe { *self.0.get() };
> + phydev.link() == LINK_IS_UP
> + }
[...]
> +/// An instance of a PHY driver.
> +///
> +/// Wraps the kernel's `struct phy_driver`.
> +///
> +/// # Invariants
> +///
> +/// `self.0` is always in a valid state.
> +#[repr(transparent)]
> +pub struct DriverType(Opaque<bindings::phy_driver>);
I think `DriverVTable` is a better name.
> +/// Creates the kernel's `phy_driver` instance.
This function returns a `DriverType`, not a `phy_driver`.
> +///
> +/// This is used by [`module_phy_driver`] macro to create a static array of `phy_driver`.
> +///
> +/// [`module_phy_driver`]: crate::module_phy_driver
> +pub const fn create_phy_driver<T: Driver>() -> DriverType {
> + // All the fields of `struct phy_driver` are initialized properly.
> + // It ensures the invariants.
Use `// INVARIANT: `.
> + DriverType(Opaque::new(bindings::phy_driver {
[...]
> +
> +/// Registration structure for a PHY driver.
> +///
> +/// # Invariants
> +///
> +/// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
> +pub struct Registration {
> + drivers: &'static [DriverType],
> +}
You did not reply to my suggestion [2] to remove this type,
what do you think?
[2]: https://lore.kernel.org/rust-for-linux/85d5c498-efbc-4c1a-8d12-f1eca63c45cf@proton.me/
--
Cheers,
Benno
> +
> +impl Registration {
> + /// Registers a PHY driver.
> + pub fn register(
> + module: &'static crate::ThisModule,
> + drivers: &'static [DriverType],
> + ) -> Result<Self> {
> + if drivers.is_empty() {
> + return Err(code::EINVAL);
> + }
> + // SAFETY: The type invariants of [`DriverType`] ensure that all elements of the `drivers` slice
> + // are initialized properly. So an FFI call with a valid pointer.
> + to_result(unsafe {
> + bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
> + })?;
> + // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
> + Ok(Registration { drivers })
> + }
> +}
[...]
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-17 11:30 ` [PATCH net-next v5 1/5] rust: core " FUJITA Tomonori
2023-10-18 15:07 ` Benno Lossin
@ 2023-10-18 20:27 ` Andrew Lunn
2023-10-19 0:41 ` FUJITA Tomonori
2023-10-20 17:26 ` Andreas Hindborg (Samsung)
2 siblings, 1 reply; 65+ messages in thread
From: Andrew Lunn @ 2023-10-18 20:27 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg
> + /// Reads a given C22 PHY register.
> + pub fn read(&mut self, regnum: u16) -> Result<u16> {
> + let phydev = self.0.get();
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + // So an FFI call with a valid pointer.
> + let ret = unsafe {
> + bindings::mdiobus_read((*phydev).mdio.bus, (*phydev).mdio.addr, regnum.into())
If i've understood the discussion about &mut, it is not needed here,
and for write. Performing a read/write does not change anything in
phydev. There was mention of statistics, but they are in the mii_bus
structure, which is pointed to by this structure, but is not part of
this structure.
> + };
> + if ret < 0 {
> + Err(Error::from_errno(ret))
> + } else {
> + Ok(ret as u16)
> + }
> + }
> +
> + /// Writes a given C22 PHY register.
> + pub fn write(&mut self, regnum: u16, val: u16) -> Result {
> + let phydev = self.0.get();
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + // So an FFI call with a valid pointer.
> + to_result(unsafe {
> + bindings::mdiobus_write((*phydev).mdio.bus, (*phydev).mdio.addr, regnum.into(), val)
> + })
> + }
> +
> + /// Reads a paged register.
> + pub fn read_paged(&mut self, page: u16, regnum: u16) -> Result<u16> {
From my reading of the code, read_paged also does not modify phydev.
Andrew
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-18 15:07 ` Benno Lossin
@ 2023-10-19 0:24 ` FUJITA Tomonori
2023-10-19 13:45 ` Benno Lossin
2023-10-20 18:42 ` Andrew Lunn
1 sibling, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-19 0:24 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Wed, 18 Oct 2023 15:07:52 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
>> diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
>> new file mode 100644
>> index 000000000000..7d4927ece32f
>> --- /dev/null
>> +++ b/rust/kernel/net/phy.rs
>> @@ -0,0 +1,701 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +// Copyright (C) 2023 FUJITA Tomonori <fujita.tomonori@gmail.com>
>> +
>> +//! Network PHY device.
>> +//!
>> +//! C headers: [`include/linux/phy.h`](../../../../include/linux/phy.h).
>> +//!
>
> Add a new section "# Abstraction Overview" or similar.
With the rest of comments on this secsion addressed, how about the following?
//! Network PHY device.
//!
//! C headers: [`include/linux/phy.h`](../../../../include/linux/phy.h).
//!
//! # Abstraction Overview
//!
//! "During the calls to most functions in [`Driver`], the C side (`PHYLIB`) holds a lock that is unique
//! for every instance of [`Device`]". `PHYLIB` uses a different serialization technique for
//! [`Driver::resume`] and [`Driver::suspend`]: `PHYLIB` updates `phy_device`'s state with the lock hold,
//! to guarantee that [`Driver::resume`] accesses to the instance exclusively. [`Driver::resume`] and
//! [`Driver::suspend`] also are called where only one thread can access to the instance.
//!
//! All the PHYLIB helper functions for [`Device`] modify some members in [`Device`]. Except for
//! getter functions, [`Device`] methods take `&mut self`. This also applied to `[Device::read]`,
//! which reads a hardware register and updates the stats.
>> +//! The C side (PHYLIB) guarantees that there is only one thread accessing to one `phy_device` instance
>> +//! during a callback in `phy_driver`. The callback safely accesses to the instance exclusively.
>> +//! Except for `resume()` and `suspend()`, PHYLIB holds `phy_device`'s lock during a callback.
>
> I would start with "During the calls to most functions in [`Driver`],
> the C side (`PHYLIB`) holds a lock that is unique for every instance of
> [`Device`]". Then you can note the exception for `resume` and `suspend`
> (and also link them e.g. [`Driver::resume`]).
>
>> +//! PHYLIB doesn't hold `phy_device`'s lock in both `resume()` and `suspend()`. Instead, PHYLIB
>> +//! updates `phy_device`'s state with `phy_device`'s lock hold, to guarantee that resume() accesses
>> +//! to the instance exclusively. Note that `resume()` and `suspend()` also are called where only
>> +//! one thread can access to the instance.
>
> I would just write "`PHYLIB` uses a different serialization technique for
> [`Driver::resume`] and [`Driver::suspend`]: <use the above explanation>".
>
>> +//! This abstractions creates [`Device`] instance only during a callback so it's guaranteed that
>> +//! only one reference exists. All its methods can accesses to the instance exclusively.
>
> Not really sure if this is needed, what are you trying to explain here?
I tried to add an explanation that Device::from_raw() return &mut. But
I guess that the description of Device is enough.
>> +//! All the PHYLIB helper functions for `phy_device` modify some members in `phy_device`. Except for
>> +//! getter functions, [`Device`] methods take `&mut self`. This also applied to `read()`, which reads
>> +//! a hardware register and updates the stats.
>
> I would use [`Device`] instead of `phy_device`, since the Rust reader
> might not be aware what wraps `phy_device`.
>
>> +use crate::{bindings, error::*, prelude::vtable, str::CStr, types::Opaque};
>> +use core::marker::PhantomData;
(snip)
>> +/// An instance of a PHY device.
>> +///
>> +/// Wraps the kernel's `struct phy_device`.
>> +///
>> +/// # Invariants
>> +///
>> +/// `self.0` is always in a valid state.
>> +#[repr(transparent)]
>> +pub struct Device(Opaque<bindings::phy_device>);
>> +
>> +impl Device {
>> + /// Creates a new [`Device`] instance from a raw pointer.
>> + ///
>> + /// # Safety
>> + ///
>> + /// This function must only be called from the callbacks in `phy_driver`. PHYLIB guarantees
>> + /// the exclusive access for the duration of the lifetime `'a`.
>
> I would not put the second sentence in the `# Safety` section. Just move it
> above. The reason behind this is the following: the second sentence is not
> a precondition needed to call the function.
Where is the `above`? You meant the following?
impl Device {
/// Creates a new [`Device`] instance from a raw pointer.
///
/// `PHYLIB` guarantees the exclusive access for the duration of the lifetime `'a`.
///
/// # Safety
///
/// This function must only be called from the callbacks in `phy_driver`.
unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
>> + unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
>> + // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::phy_device`.
>> + let ptr = ptr.cast::<Self>();
>> + // SAFETY: by the function requirements the pointer is valid and we have unique access for
>> + // the duration of `'a`.
>> + unsafe { &mut *ptr }
>> + }
>> +
>> + /// Gets the id of the PHY.
>> + pub fn phy_id(&self) -> u32 {
>> + let phydev = self.0.get();
>> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>> + unsafe { (*phydev).phy_id }
>> + }
>> +
>> + /// Gets the state of the PHY.
>> + pub fn state(&self) -> DeviceState {
>> + let phydev = self.0.get();
>> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>> + let state = unsafe { (*phydev).state };
>> + // TODO: this conversion code will be replaced with automatically generated code by bindgen
>> + // when it becomes possible.
>> + // better to call WARN_ONCE() when the state is out-of-range (needs to add WARN_ONCE support).
>> + match state {
>> + bindings::phy_state_PHY_DOWN => DeviceState::Down,
>> + bindings::phy_state_PHY_READY => DeviceState::Ready,
>> + bindings::phy_state_PHY_HALTED => DeviceState::Halted,
>> + bindings::phy_state_PHY_ERROR => DeviceState::Error,
>> + bindings::phy_state_PHY_UP => DeviceState::Up,
>> + bindings::phy_state_PHY_RUNNING => DeviceState::Running,
>> + bindings::phy_state_PHY_NOLINK => DeviceState::NoLink,
>> + bindings::phy_state_PHY_CABLETEST => DeviceState::CableTest,
>> + _ => DeviceState::Error,
>> + }
>> + }
>> +
>> + /// Returns true if the link is up.
>> + pub fn get_link(&self) -> bool {
>
> I still think this name should be changed. My response at [1] has not yet
> been replied to. This has already been discussed before:
> - https://lore.kernel.org/rust-for-linux/2023100237-satirical-prance-bd57@gregkh/
> - https://lore.kernel.org/rust-for-linux/20231004.084644.50784533959398755.fujita.tomonori@gmail.com/
> - https://lore.kernel.org/rust-for-linux/CALNs47syMxiZBUwKLk3vKxzmCbX0FS5A37FjwUzZO9Fn-iPaoA@mail.gmail.com/
>
> And I want to suggest to change it to `is_link_up`.
>
> Reasons why I do not like the name:
> - `get_`/`put_` are used for ref counting on the C side, I would like to
> avoid confusion.
> - `get` in Rust is often used to fetch a value from e.g. a datastructure
> such as a hashmap, so I expect the call to do some computation.
> - getters in Rust usually are not prefixed with `get_`, but rather are
> just the name of the field.
> - in this case I like the name `is_link_up` much better, since code becomes
> a lot more readable with that.
> - I do not want this pattern as an example for other drivers.
>
> [1]: https://lore.kernel.org/rust-for-linux/f5878806-5ba2-d932-858d-dda3f55ceb67@proton.me/
IIRC, Andrew suggested the current name. If the change is fine by him,
I'll rename.
>> +/// An instance of a PHY driver.
>> +///
>> +/// Wraps the kernel's `struct phy_driver`.
>> +///
>> +/// # Invariants
>> +///
>> +/// `self.0` is always in a valid state.
>> +#[repr(transparent)]
>> +pub struct DriverType(Opaque<bindings::phy_driver>);
>
> I think `DriverVTable` is a better name.
Renamed.
>> +/// Creates the kernel's `phy_driver` instance.
>
> This function returns a `DriverType`, not a `phy_driver`.
How about?
/// Creates the [`DriverVTable`] instance from [`Driver`] trait.
>> +///
>> +/// This is used by [`module_phy_driver`] macro to create a static array of `phy_driver`.
>> +///
>> +/// [`module_phy_driver`]: crate::module_phy_driver
>> +pub const fn create_phy_driver<T: Driver>() -> DriverType {
>> + // All the fields of `struct phy_driver` are initialized properly.
>> + // It ensures the invariants.
>
> Use `// INVARIANT: `.
Oops,
// INVARIANT: All the fields of `struct phy_driver` are initialized properly.
DriverVTable(Opaque::new(bindings::phy_driver {
name: T::NAME.as_char_ptr().cast_mut(),
>> +/// Registration structure for a PHY driver.
>> +///
>> +/// # Invariants
>> +///
>> +/// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>> +pub struct Registration {
>> + drivers: &'static [DriverType],
>> +}
>
> You did not reply to my suggestion [2] to remove this type,
> what do you think?
>
> [2]: https://lore.kernel.org/rust-for-linux/85d5c498-efbc-4c1a-8d12-f1eca63c45cf@proton.me/
I tried before but I'm not sure it simplifies the implementation.
Firstly, instead of Reservation, we need a public function like
pub fn phy_drivers_register(module: &'static crate::ThisModule, drivers: &[DriverVTable]) -> Result {
to_result(unsafe {
bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
})
}
This is because module.0 is private.
Also if we keep DriverVtable.0 private, we need another public function.
pub unsafe fn phy_drivers_unregister(drivers: &'static [DriverVTable])
{
unsafe {
bindings::phy_drivers_unregister(drivers[0].0.get(), drivers.len() as i32)
};
}
DriverVTable isn't guaranteed to be registered to the kernel so needs
to be unsafe, I guesss.
Also Module trait support exit()?
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-18 20:27 ` Andrew Lunn
@ 2023-10-19 0:41 ` FUJITA Tomonori
2023-10-19 13:57 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-19 0:41 UTC (permalink / raw)
To: andrew
Cc: fujita.tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, benno.lossin, greg
On Wed, 18 Oct 2023 22:27:55 +0200
Andrew Lunn <andrew@lunn.ch> wrote:
>> + /// Reads a given C22 PHY register.
>> + pub fn read(&mut self, regnum: u16) -> Result<u16> {
>> + let phydev = self.0.get();
>> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>> + // So an FFI call with a valid pointer.
>> + let ret = unsafe {
>> + bindings::mdiobus_read((*phydev).mdio.bus, (*phydev).mdio.addr, regnum.into())
>
> If i've understood the discussion about &mut, it is not needed here,
> and for write. Performing a read/write does not change anything in
> phydev. There was mention of statistics, but they are in the mii_bus
> structure, which is pointed to by this structure, but is not part of
> this structure.
If I understand correctly, he said that either (&self or &mut self) is
fine for read().
https://lore.kernel.org/netdev/3469de1c-0e6f-4fe5-9d93-2542f87ffd0d@proton.me/
Since `&mut self` is unique, only one thread per instance of `Self`
can call that function. So use this when the C side would use a lock.
(or requires that only one thread calls that code)
Since multiple `&self` references are allowed to coexist, you should
use this for functions which perform their own serialization/do not
require serialization.
I applied the first case here.
>> + };
>> + if ret < 0 {
>> + Err(Error::from_errno(ret))
>> + } else {
>> + Ok(ret as u16)
>> + }
>> + }
>> +
>> + /// Writes a given C22 PHY register.
>> + pub fn write(&mut self, regnum: u16, val: u16) -> Result {
>> + let phydev = self.0.get();
>> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>> + // So an FFI call with a valid pointer.
>> + to_result(unsafe {
>> + bindings::mdiobus_write((*phydev).mdio.bus, (*phydev).mdio.addr, regnum.into(), val)
>> + })
>> + }
>> +
>> + /// Reads a paged register.
>> + pub fn read_paged(&mut self, page: u16, regnum: u16) -> Result<u16> {
>
> From my reading of the code, read_paged also does not modify phydev.
__phy_read is called so I use &mut self like read().
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 0:24 ` FUJITA Tomonori
@ 2023-10-19 13:45 ` Benno Lossin
2023-10-19 14:42 ` FUJITA Tomonori
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-19 13:45 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 19.10.23 02:24, FUJITA Tomonori wrote:
> On Wed, 18 Oct 2023 15:07:52 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>>> diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
>>> new file mode 100644
>>> index 000000000000..7d4927ece32f
>>> --- /dev/null
>>> +++ b/rust/kernel/net/phy.rs
>>> @@ -0,0 +1,701 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +
>>> +// Copyright (C) 2023 FUJITA Tomonori <fujita.tomonori@gmail.com>
>>> +
>>> +//! Network PHY device.
>>> +//!
>>> +//! C headers: [`include/linux/phy.h`](../../../../include/linux/phy.h).
>>> +//!
>>
>> Add a new section "# Abstraction Overview" or similar.
>
> With the rest of comments on this secsion addressed, how about the following?
>
> //! Network PHY device.
> //!
> //! C headers: [`include/linux/phy.h`](../../../../include/linux/phy.h).
> //!
> //! # Abstraction Overview
> //!
> //! "During the calls to most functions in [`Driver`], the C side (`PHYLIB`) holds a lock that is unique
Please remove the quotes ", they were intended to separate my comments
from my suggestion.
> //! for every instance of [`Device`]". `PHYLIB` uses a different serialization technique for
> //! [`Driver::resume`] and [`Driver::suspend`]: `PHYLIB` updates `phy_device`'s state with the lock hold,
hold -> held
> //! to guarantee that [`Driver::resume`] accesses to the instance exclusively. [`Driver::resume`] and
to guarantee -> thus guaranteeing
accesses to the instance exclusively. -> has exclusive access to the instance.
> //! [`Driver::suspend`] also are called where only one thread can access to the instance.
> //!
> //! All the PHYLIB helper functions for [`Device`] modify some members in [`Device`]. Except for
PHYLIB -> `PHYLIB`
> //! getter functions, [`Device`] methods take `&mut self`. This also applied to `[Device::read]`,
`[Device::read]` -> [`Device::read`]
> //! which reads a hardware register and updates the stats.
Otherwise this looks good.
[...]
>>> +impl Device {
>>> + /// Creates a new [`Device`] instance from a raw pointer.
>>> + ///
>>> + /// # Safety
>>> + ///
>>> + /// This function must only be called from the callbacks in `phy_driver`. PHYLIB guarantees
>>> + /// the exclusive access for the duration of the lifetime `'a`.
>>
>> I would not put the second sentence in the `# Safety` section. Just move it
>> above. The reason behind this is the following: the second sentence is not
>> a precondition needed to call the function.
>
> Where is the `above`? You meant the following?
>
> impl Device {
> /// Creates a new [`Device`] instance from a raw pointer.
> ///
> /// `PHYLIB` guarantees the exclusive access for the duration of the lifetime `'a`.
> ///
> /// # Safety
> ///
> /// This function must only be called from the callbacks in `phy_driver`.
> unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
Yes this is what I had in mind. Although now that I see it in code,
I am not so sure that this comment is needed. If you feel the same
way, just remove it.
That being said, I am not too happy with the safety requirement of this
function. It does not really match with the safety comment in the function
body. Since I have not yet finished my safety standardization, I think we
can defer that problem until it is finished. Unless some other reviewer
wants to change this, you can keep it as is.
>>> + unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
>>> + // CAST: `Self` is a `repr(transparent)` wrapper around `bindings::phy_device`.
>>> + let ptr = ptr.cast::<Self>();
>>> + // SAFETY: by the function requirements the pointer is valid and we have unique access for
>>> + // the duration of `'a`.
>>> + unsafe { &mut *ptr }
>>> + }
>>> +
>>> + /// Gets the id of the PHY.
>>> + pub fn phy_id(&self) -> u32 {
>>> + let phydev = self.0.get();
>>> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>>> + unsafe { (*phydev).phy_id }
>>> + }
>>> +
>>> + /// Gets the state of the PHY.
>>> + pub fn state(&self) -> DeviceState {
>>> + let phydev = self.0.get();
>>> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>>> + let state = unsafe { (*phydev).state };
>>> + // TODO: this conversion code will be replaced with automatically generated code by bindgen
>>> + // when it becomes possible.
>>> + // better to call WARN_ONCE() when the state is out-of-range (needs to add WARN_ONCE support).
>>> + match state {
>>> + bindings::phy_state_PHY_DOWN => DeviceState::Down,
>>> + bindings::phy_state_PHY_READY => DeviceState::Ready,
>>> + bindings::phy_state_PHY_HALTED => DeviceState::Halted,
>>> + bindings::phy_state_PHY_ERROR => DeviceState::Error,
>>> + bindings::phy_state_PHY_UP => DeviceState::Up,
>>> + bindings::phy_state_PHY_RUNNING => DeviceState::Running,
>>> + bindings::phy_state_PHY_NOLINK => DeviceState::NoLink,
>>> + bindings::phy_state_PHY_CABLETEST => DeviceState::CableTest,
>>> + _ => DeviceState::Error,
>>> + }
>>> + }
>>> +
>>> + /// Returns true if the link is up.
>>> + pub fn get_link(&self) -> bool {
>>
>> I still think this name should be changed. My response at [1] has not yet
>> been replied to. This has already been discussed before:
>> - https://lore.kernel.org/rust-for-linux/2023100237-satirical-prance-bd57@gregkh/
>> - https://lore.kernel.org/rust-for-linux/20231004.084644.50784533959398755.fujita.tomonori@gmail.com/
>> - https://lore.kernel.org/rust-for-linux/CALNs47syMxiZBUwKLk3vKxzmCbX0FS5A37FjwUzZO9Fn-iPaoA@mail.gmail.com/
>>
>> And I want to suggest to change it to `is_link_up`.
>>
>> Reasons why I do not like the name:
>> - `get_`/`put_` are used for ref counting on the C side, I would like to
>> avoid confusion.
>> - `get` in Rust is often used to fetch a value from e.g. a datastructure
>> such as a hashmap, so I expect the call to do some computation.
>> - getters in Rust usually are not prefixed with `get_`, but rather are
>> just the name of the field.
>> - in this case I like the name `is_link_up` much better, since code becomes
>> a lot more readable with that.
>> - I do not want this pattern as an example for other drivers.
>>
>> [1]: https://lore.kernel.org/rust-for-linux/f5878806-5ba2-d932-858d-dda3f55ceb67@proton.me/
>
> IIRC, Andrew suggested the current name. If the change is fine by him,
> I'll rename.
>
>
>>> +/// An instance of a PHY driver.
>>> +///
>>> +/// Wraps the kernel's `struct phy_driver`.
>>> +///
>>> +/// # Invariants
>>> +///
>>> +/// `self.0` is always in a valid state.
>>> +#[repr(transparent)]
>>> +pub struct DriverType(Opaque<bindings::phy_driver>);
>>
>> I think `DriverVTable` is a better name.
>
> Renamed.
>
>>> +/// Creates the kernel's `phy_driver` instance.
>>
>> This function returns a `DriverType`, not a `phy_driver`.
>
> How about?
>
> /// Creates the [`DriverVTable`] instance from [`Driver`] trait.
Sounds good, but to this sounds a bit more natural:
/// Creates a [`DriverVTable`] instance from a [`Driver`].
>>> +///
>>> +/// This is used by [`module_phy_driver`] macro to create a static array of `phy_driver`.
>>> +///
>>> +/// [`module_phy_driver`]: crate::module_phy_driver
>>> +pub const fn create_phy_driver<T: Driver>() -> DriverType {
>>> + // All the fields of `struct phy_driver` are initialized properly.
>>> + // It ensures the invariants.
>>
>> Use `// INVARIANT: `.
>
> Oops,
>
> // INVARIANT: All the fields of `struct phy_driver` are initialized properly.
> DriverVTable(Opaque::new(bindings::phy_driver {
> name: T::NAME.as_char_ptr().cast_mut(),
Seems good.
>
>
>>> +/// Registration structure for a PHY driver.
>>> +///
>>> +/// # Invariants
>>> +///
>>> +/// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>> +pub struct Registration {
>>> + drivers: &'static [DriverType],
>>> +}
>>
>> You did not reply to my suggestion [2] to remove this type,
>> what do you think?
>>
>> [2]: https://lore.kernel.org/rust-for-linux/85d5c498-efbc-4c1a-8d12-f1eca63c45cf@proton.me/
>
> I tried before but I'm not sure it simplifies the implementation.
>
> Firstly, instead of Reservation, we need a public function like
>
> pub fn phy_drivers_register(module: &'static crate::ThisModule, drivers: &[DriverVTable]) -> Result {
> to_result(unsafe {
> bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
> })
> }
>
> This is because module.0 is private.
Why can't this be part of the macro?
> Also if we keep DriverVtable.0 private, we need another public function.
>
> pub unsafe fn phy_drivers_unregister(drivers: &'static [DriverVTable])
> {
> unsafe {
> bindings::phy_drivers_unregister(drivers[0].0.get(), drivers.len() as i32)
> };
> }
>
> DriverVTable isn't guaranteed to be registered to the kernel so needs
> to be unsafe, I guesss.
In one of the options I suggest to make that an invariant of `DriverVTable`.
>
> Also Module trait support exit()?
Yes, just implement `Drop` and do the cleanup there.
In the two options that I suggested there is a trade off. I do not know
which option is better, I hoped that you or Andrew would know more:
Option 1:
* advantages:
- manual creation of a phy driver module becomes possible.
- less complex `module_phy_driver` macro.
- no static variable needed.
* disadvantages:
- calls `phy_drivers_register` for every driver on module
initialization.
- calls `phy_drivers_unregister` for every driver on module
exit.
Option 2:
* advantages:
- less complex `module_phy_driver` macro.
- no static variable needed.
- only a single call to
`phy_drivers_register`/`phy_drivers_unregister`.
* disadvantages:
- no safe manual creation of phy drivers possible, the only safe
way is to use the `module_phy_driver` macro.
I suppose that it would be ok to call the register function multiple
times, since it only is on module startup/shutdown and it is not
performance critical.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 0:41 ` FUJITA Tomonori
@ 2023-10-19 13:57 ` Benno Lossin
2023-10-20 19:50 ` Andrew Lunn
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-19 13:57 UTC (permalink / raw)
To: FUJITA Tomonori, andrew
Cc: netdev, rust-for-linux, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 19.10.23 02:41, FUJITA Tomonori wrote:
> On Wed, 18 Oct 2023 22:27:55 +0200
> Andrew Lunn <andrew@lunn.ch> wrote:
>
>>> + /// Reads a given C22 PHY register.
>>> + pub fn read(&mut self, regnum: u16) -> Result<u16> {
>>> + let phydev = self.0.get();
>>> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>>> + // So an FFI call with a valid pointer.
>>> + let ret = unsafe {
>>> + bindings::mdiobus_read((*phydev).mdio.bus, (*phydev).mdio.addr, regnum.into())
>>
>> If i've understood the discussion about &mut, it is not needed here,
>> and for write. Performing a read/write does not change anything in
>> phydev. There was mention of statistics, but they are in the mii_bus
>> structure, which is pointed to by this structure, but is not part of
>> this structure.
>
> If I understand correctly, he said that either (&self or &mut self) is
> fine for read().
>
> https://lore.kernel.org/netdev/3469de1c-0e6f-4fe5-9d93-2542f87ffd0d@proton.me/
>
> Since `&mut self` is unique, only one thread per instance of `Self`
> can call that function. So use this when the C side would use a lock.
> (or requires that only one thread calls that code)
>
> Since multiple `&self` references are allowed to coexist, you should
> use this for functions which perform their own serialization/do not
> require serialization.
>
>
> I applied the first case here.
I will try to explain things a bit more.
So this case is a bit difficult to figure out, because what is
going on is not really a pattern that is used in Rust.
We already have exclusive access to the `phy_device`, so in Rust
you would not need to lock anything to also have exclusive access to the
embedded `mii_bus`. In this sense, mutable references (`&mut T`) are
infectious.
Since C always locks the `mdio_lock` when we call the read & write
functions, we however could also just use a shared reference (`&T`)
for the function receiver, since the C side guarantees serialization.
Another reason for choosing `&mut self` here is the following: it is
easier to later change to `&self` compared to going with `&self` now
and changing to `&mut self` later. This is because if you have a `&mut T`
you can also call all of its `&T` functions, but not the other way around.
`&mut self` is as a receiver also more conservative, since it is more
strict as to where it can be called. So let's just go with that.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 13:45 ` Benno Lossin
@ 2023-10-19 14:42 ` FUJITA Tomonori
2023-10-19 15:20 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-19 14:42 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Thu, 19 Oct 2023 13:45:27 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> On 19.10.23 02:24, FUJITA Tomonori wrote:
>> On Wed, 18 Oct 2023 15:07:52 +0000
>> Benno Lossin <benno.lossin@proton.me> wrote:
>>
>>>> diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs
>>>> new file mode 100644
>>>> index 000000000000..7d4927ece32f
>>>> --- /dev/null
>>>> +++ b/rust/kernel/net/phy.rs
>>>> @@ -0,0 +1,701 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +
>>>> +// Copyright (C) 2023 FUJITA Tomonori <fujita.tomonori@gmail.com>
>>>> +
>>>> +//! Network PHY device.
>>>> +//!
>>>> +//! C headers: [`include/linux/phy.h`](../../../../include/linux/phy.h).
>>>> +//!
>>>
>>> Add a new section "# Abstraction Overview" or similar.
>>
>> With the rest of comments on this secsion addressed, how about the following?
>>
>> //! Network PHY device.
>> //!
>> //! C headers: [`include/linux/phy.h`](../../../../include/linux/phy.h).
>> //!
>> //! # Abstraction Overview
>> //!
>> //! "During the calls to most functions in [`Driver`], the C side (`PHYLIB`) holds a lock that is unique
>
> Please remove the quotes ", they were intended to separate my comments
> from my suggestion.
>
>> //! for every instance of [`Device`]". `PHYLIB` uses a different serialization technique for
>> //! [`Driver::resume`] and [`Driver::suspend`]: `PHYLIB` updates `phy_device`'s state with the lock hold,
>
> hold -> held
>
>> //! to guarantee that [`Driver::resume`] accesses to the instance exclusively. [`Driver::resume`] and
>
> to guarantee -> thus guaranteeing
> accesses to the instance exclusively. -> has exclusive access to the instance.
>
>> //! [`Driver::suspend`] also are called where only one thread can access to the instance.
>> //!
>> //! All the PHYLIB helper functions for [`Device`] modify some members in [`Device`]. Except for
>
> PHYLIB -> `PHYLIB`
>
>> //! getter functions, [`Device`] methods take `&mut self`. This also applied to `[Device::read]`,
>
> `[Device::read]` -> [`Device::read`]
>
>> //! which reads a hardware register and updates the stats.
>
> Otherwise this looks good.
Thanks, I fixed the comment accordingly.
> [...]
>
>>>> +impl Device {
>>>> + /// Creates a new [`Device`] instance from a raw pointer.
>>>> + ///
>>>> + /// # Safety
>>>> + ///
>>>> + /// This function must only be called from the callbacks in `phy_driver`. PHYLIB guarantees
>>>> + /// the exclusive access for the duration of the lifetime `'a`.
>>>
>>> I would not put the second sentence in the `# Safety` section. Just move it
>>> above. The reason behind this is the following: the second sentence is not
>>> a precondition needed to call the function.
>>
>> Where is the `above`? You meant the following?
>>
>> impl Device {
>> /// Creates a new [`Device`] instance from a raw pointer.
>> ///
>> /// `PHYLIB` guarantees the exclusive access for the duration of the lifetime `'a`.
>> ///
>> /// # Safety
>> ///
>> /// This function must only be called from the callbacks in `phy_driver`.
>> unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
>
> Yes this is what I had in mind. Although now that I see it in code,
> I am not so sure that this comment is needed. If you feel the same
> way, just remove it.
Then let me drop it.
> That being said, I am not too happy with the safety requirement of this
> function. It does not really match with the safety comment in the function
> body. Since I have not yet finished my safety standardization, I think we
> can defer that problem until it is finished. Unless some other reviewer
> wants to change this, you can keep it as is.
Understood.
>> /// Creates the [`DriverVTable`] instance from [`Driver`] trait.
>
> Sounds good, but to this sounds a bit more natural:
>
> /// Creates a [`DriverVTable`] instance from a [`Driver`].
Oops, fixed.
>>>> +/// Registration structure for a PHY driver.
>>>> +///
>>>> +/// # Invariants
>>>> +///
>>>> +/// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>>> +pub struct Registration {
>>>> + drivers: &'static [DriverType],
>>>> +}
>>>
>>> You did not reply to my suggestion [2] to remove this type,
>>> what do you think?
>>>
>>> [2]: https://lore.kernel.org/rust-for-linux/85d5c498-efbc-4c1a-8d12-f1eca63c45cf@proton.me/
>>
>> I tried before but I'm not sure it simplifies the implementation.
>>
>> Firstly, instead of Reservation, we need a public function like
>>
>> pub fn phy_drivers_register(module: &'static crate::ThisModule, drivers: &[DriverVTable]) -> Result {
>> to_result(unsafe {
>> bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
>> })
>> }
>>
>> This is because module.0 is private.
>
> Why can't this be part of the macro?
I'm not sure I correctly understand what you suggest so you meant the following?
(drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
struct Module {
_drv: [
::kernel::net::phy::DriverVTable;
$crate::module_phy_driver!(@count_devices $($driver),+)
],
}
unsafe impl Sync for Module {}
$crate::prelude::module! {
type: Module,
$($f)*
}
impl ::kernel::Module for Module {
fn init(module: &'static ThisModule) -> Result<Self> {
let drv = [
$(::kernel::net::phy::create_phy_driver::<$driver>()),+
];
::kernel::error::to_result(unsafe {
::kernel::bindings::phy_drivers_register(drv[0].0.get(), drv.len().try_into()?, module.0)
})?;
Ok(Module {
_drv: drv,
})
}
}
Then we got the following error:
error[E0616]: field `0` of struct `DriverVTable` is private
--> drivers/net/phy/ax88796b_rust.rs:12:1
|
12 | / kernel::module_phy_driver! {
13 | | drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B],
14 | | device_table: [
15 | | DeviceId::new_with_driver::<PhyAX88772A>(),
... |
22 | | license: "GPL",
23 | | }
| |_^ private field
|
= note: this error originates in the macro
`kernel::module_phy_driver` (in Nightly builds, run with
-Z macro-backtrace for more info)
error[E0616]: field `0` of struct `kernel::ThisModule` is private
--> drivers/net/phy/ax88796b_rust.rs:12:1
|
12 | / kernel::module_phy_driver! {
13 | | drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B],
14 | | device_table: [
15 | | DeviceId::new_with_driver::<PhyAX88772A>(),
... |
22 | | license: "GPL",
23 | | }
| |_^ private field
>> Also if we keep DriverVtable.0 private, we need another public function.
>>
>> pub unsafe fn phy_drivers_unregister(drivers: &'static [DriverVTable])
>> {
>> unsafe {
>> bindings::phy_drivers_unregister(drivers[0].0.get(), drivers.len() as i32)
>> };
>> }
>>
>> DriverVTable isn't guaranteed to be registered to the kernel so needs
>> to be unsafe, I guesss.
>
> In one of the options I suggest to make that an invariant of `DriverVTable`.
>
>>
>> Also Module trait support exit()?
>
> Yes, just implement `Drop` and do the cleanup there.
>
> In the two options that I suggested there is a trade off. I do not know
> which option is better, I hoped that you or Andrew would know more:
> Option 1:
> * advantages:
> - manual creation of a phy driver module becomes possible.
> - less complex `module_phy_driver` macro.
> - no static variable needed.
> * disadvantages:
> - calls `phy_drivers_register` for every driver on module
> initialization.
> - calls `phy_drivers_unregister` for every driver on module
> exit.
>
> Option 2:
> * advantages:
> - less complex `module_phy_driver` macro.
> - no static variable needed.
> - only a single call to
> `phy_drivers_register`/`phy_drivers_unregister`.
> * disadvantages:
> - no safe manual creation of phy drivers possible, the only safe
> way is to use the `module_phy_driver` macro.
>
> I suppose that it would be ok to call the register function multiple
> times, since it only is on module startup/shutdown and it is not
> performance critical.
I think that we can use the current implantation using Reservation
struct until someone requests manual creation. I doubt that we will
need to support such.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 14:42 ` FUJITA Tomonori
@ 2023-10-19 15:20 ` Benno Lossin
2023-10-19 15:32 ` FUJITA Tomonori
2023-10-20 0:34 ` FUJITA Tomonori
0 siblings, 2 replies; 65+ messages in thread
From: Benno Lossin @ 2023-10-19 15:20 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 19.10.23 16:42, FUJITA Tomonori wrote:
>>>>> +/// Registration structure for a PHY driver.
>>>>> +///
>>>>> +/// # Invariants
>>>>> +///
>>>>> +/// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>>>> +pub struct Registration {
>>>>> + drivers: &'static [DriverType],
>>>>> +}
>>>>
>>>> You did not reply to my suggestion [2] to remove this type,
>>>> what do you think?
>>>>
>>>> [2]: https://lore.kernel.org/rust-for-linux/85d5c498-efbc-4c1a-8d12-f1eca63c45cf@proton.me/
>>>
>>> I tried before but I'm not sure it simplifies the implementation.
>>>
>>> Firstly, instead of Reservation, we need a public function like
>>>
>>> pub fn phy_drivers_register(module: &'static crate::ThisModule, drivers: &[DriverVTable]) -> Result {
>>> to_result(unsafe {
>>> bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
>>> })
>>> }
>>>
>>> This is because module.0 is private.
>>
>> Why can't this be part of the macro?
>
> I'm not sure I correctly understand what you suggest so you meant the following?
>
> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
> struct Module {
> _drv: [
> ::kernel::net::phy::DriverVTable;
> $crate::module_phy_driver!(@count_devices $($driver),+)
> ],
> }
> unsafe impl Sync for Module {}
>
> $crate::prelude::module! {
> type: Module,
> $($f)*
> }
>
> impl ::kernel::Module for Module {
> fn init(module: &'static ThisModule) -> Result<Self> {
> let drv = [
> $(::kernel::net::phy::create_phy_driver::<$driver>()),+
> ];
> ::kernel::error::to_result(unsafe {
> ::kernel::bindings::phy_drivers_register(drv[0].0.get(), drv.len().try_into()?, module.0)
You can just do this (I omitted the `::kernel::` prefix for
readability, if you add this in the macro, please include it):
// CAST: `DriverVTable` is `repr(transparent)` and wrapping `bindings::phy_driver`.
let ptr = drv.as_mut_ptr().cast::<bindings::phy_driver>();
let len = drv.len().try_into()?;
// SAFETY: ...
to_result(unsafe { bindings::phy_drivers_register(ptr, len, module.0) })?;
> })?;
>
> Ok(Module {
> _drv: drv,
> })
> }
> }
>
> Then we got the following error:
>
> error[E0616]: field `0` of struct `DriverVTable` is private
> --> drivers/net/phy/ax88796b_rust.rs:12:1
> |
> 12 | / kernel::module_phy_driver! {
> 13 | | drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B],
> 14 | | device_table: [
> 15 | | DeviceId::new_with_driver::<PhyAX88772A>(),
> ... |
> 22 | | license: "GPL",
> 23 | | }
> | |_^ private field
> |
> = note: this error originates in the macro
> `kernel::module_phy_driver` (in Nightly builds, run with
> -Z macro-backtrace for more info)
>
> error[E0616]: field `0` of struct `kernel::ThisModule` is private
> --> drivers/net/phy/ax88796b_rust.rs:12:1
> |
> 12 | / kernel::module_phy_driver! {
> 13 | | drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B],
> 14 | | device_table: [
> 15 | | DeviceId::new_with_driver::<PhyAX88772A>(),
> ... |
> 22 | | license: "GPL",
> 23 | | }
> | |_^ private field
>
>
>>> Also if we keep DriverVtable.0 private, we need another public function.
>>>
>>> pub unsafe fn phy_drivers_unregister(drivers: &'static [DriverVTable])
>>> {
>>> unsafe {
>>> bindings::phy_drivers_unregister(drivers[0].0.get(), drivers.len() as i32)
>>> };
>>> }
>>>
>>> DriverVTable isn't guaranteed to be registered to the kernel so needs
>>> to be unsafe, I guesss.
>>
>> In one of the options I suggest to make that an invariant of `DriverVTable`.
>>
>>>
>>> Also Module trait support exit()?
>>
>> Yes, just implement `Drop` and do the cleanup there.
>>
>> In the two options that I suggested there is a trade off. I do not know
>> which option is better, I hoped that you or Andrew would know more:
>> Option 1:
>> * advantages:
>> - manual creation of a phy driver module becomes possible.
>> - less complex `module_phy_driver` macro.
>> - no static variable needed.
>> * disadvantages:
>> - calls `phy_drivers_register` for every driver on module
>> initialization.
>> - calls `phy_drivers_unregister` for every driver on module
>> exit.
>>
>> Option 2:
>> * advantages:
>> - less complex `module_phy_driver` macro.
>> - no static variable needed.
>> - only a single call to
>> `phy_drivers_register`/`phy_drivers_unregister`.
>> * disadvantages:
>> - no safe manual creation of phy drivers possible, the only safe
>> way is to use the `module_phy_driver` macro.
>>
>> I suppose that it would be ok to call the register function multiple
>> times, since it only is on module startup/shutdown and it is not
>> performance critical.
>
> I think that we can use the current implantation using Reservation
> struct until someone requests manual creation. I doubt that we will
> need to support such.
I would like to remove the mutable static variable and simplify
the macro.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 15:20 ` Benno Lossin
@ 2023-10-19 15:32 ` FUJITA Tomonori
2023-10-19 16:37 ` Benno Lossin
2023-10-20 0:34 ` FUJITA Tomonori
1 sibling, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-19 15:32 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Thu, 19 Oct 2023 15:20:51 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> On 19.10.23 16:42, FUJITA Tomonori wrote:
>>>>>> +/// Registration structure for a PHY driver.
>>>>>> +///
>>>>>> +/// # Invariants
>>>>>> +///
>>>>>> +/// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>>>>> +pub struct Registration {
>>>>>> + drivers: &'static [DriverType],
>>>>>> +}
>>>>>
>>>>> You did not reply to my suggestion [2] to remove this type,
>>>>> what do you think?
>>>>>
>>>>> [2]: https://lore.kernel.org/rust-for-linux/85d5c498-efbc-4c1a-8d12-f1eca63c45cf@proton.me/
>>>>
>>>> I tried before but I'm not sure it simplifies the implementation.
>>>>
>>>> Firstly, instead of Reservation, we need a public function like
>>>>
>>>> pub fn phy_drivers_register(module: &'static crate::ThisModule, drivers: &[DriverVTable]) -> Result {
>>>> to_result(unsafe {
>>>> bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
>>>> })
>>>> }
>>>>
>>>> This is because module.0 is private.
>>>
>>> Why can't this be part of the macro?
>>
>> I'm not sure I correctly understand what you suggest so you meant the following?
>>
>> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
>> struct Module {
>> _drv: [
>> ::kernel::net::phy::DriverVTable;
>> $crate::module_phy_driver!(@count_devices $($driver),+)
>> ],
>> }
>> unsafe impl Sync for Module {}
>>
>> $crate::prelude::module! {
>> type: Module,
>> $($f)*
>> }
>>
>> impl ::kernel::Module for Module {
>> fn init(module: &'static ThisModule) -> Result<Self> {
>> let drv = [
>> $(::kernel::net::phy::create_phy_driver::<$driver>()),+
>> ];
>> ::kernel::error::to_result(unsafe {
>> ::kernel::bindings::phy_drivers_register(drv[0].0.get(), drv.len().try_into()?, module.0)
>
> You can just do this (I omitted the `::kernel::` prefix for
> readability, if you add this in the macro, please include it):
>
> // CAST: `DriverVTable` is `repr(transparent)` and wrapping `bindings::phy_driver`.
> let ptr = drv.as_mut_ptr().cast::<bindings::phy_driver>();
> let len = drv.len().try_into()?;
> // SAFETY: ...
> to_result(unsafe { bindings::phy_drivers_register(ptr, len, module.0) })?;
>
>> })?;
The above solves DriverVTable.0 but still the macro can't access to
kernel::ThisModule.0. I got the following error:
error[E0616]: field `0` of struct `kernel::ThisModule` is private
--> drivers/net/phy/ax88796b_rust.rs:12:1
|
12 | / kernel::module_phy_driver! {
13 | | drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B],
14 | | device_table: [
15 | | DeviceId::new_with_driver::<PhyAX88772A>(),
... |
22 | | license: "GPL",
23 | | }
| |_^ private field
|
= note: this error originates in the macro
`kernel::module_phy_driver` (in Nightly builds, run with
-Z macro-backtrace for more info)
>> Ok(Module {
>> _drv: drv,
>> })
>> }
>> }
>>
>> Then we got the following error:
>>
>> error[E0616]: field `0` of struct `DriverVTable` is private
>> --> drivers/net/phy/ax88796b_rust.rs:12:1
>> |
>> 12 | / kernel::module_phy_driver! {
>> 13 | | drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B],
>> 14 | | device_table: [
>> 15 | | DeviceId::new_with_driver::<PhyAX88772A>(),
>> ... |
>> 22 | | license: "GPL",
>> 23 | | }
>> | |_^ private field
>> |
>> = note: this error originates in the macro
>> `kernel::module_phy_driver` (in Nightly builds, run with
>> -Z macro-backtrace for more info)
>>
>> error[E0616]: field `0` of struct `kernel::ThisModule` is private
>> --> drivers/net/phy/ax88796b_rust.rs:12:1
>> |
>> 12 | / kernel::module_phy_driver! {
>> 13 | | drivers: [PhyAX88772A, PhyAX88772C, PhyAX88796B],
>> 14 | | device_table: [
>> 15 | | DeviceId::new_with_driver::<PhyAX88772A>(),
>> ... |
>> 22 | | license: "GPL",
>> 23 | | }
>> | |_^ private field
>>
>>
>>>> Also if we keep DriverVtable.0 private, we need another public function.
>>>>
>>>> pub unsafe fn phy_drivers_unregister(drivers: &'static [DriverVTable])
>>>> {
>>>> unsafe {
>>>> bindings::phy_drivers_unregister(drivers[0].0.get(), drivers.len() as i32)
>>>> };
>>>> }
>>>>
>>>> DriverVTable isn't guaranteed to be registered to the kernel so needs
>>>> to be unsafe, I guesss.
>>>
>>> In one of the options I suggest to make that an invariant of `DriverVTable`.
>>>
>>>>
>>>> Also Module trait support exit()?
>>>
>>> Yes, just implement `Drop` and do the cleanup there.
>>>
>>> In the two options that I suggested there is a trade off. I do not know
>>> which option is better, I hoped that you or Andrew would know more:
>>> Option 1:
>>> * advantages:
>>> - manual creation of a phy driver module becomes possible.
>>> - less complex `module_phy_driver` macro.
>>> - no static variable needed.
>>> * disadvantages:
>>> - calls `phy_drivers_register` for every driver on module
>>> initialization.
>>> - calls `phy_drivers_unregister` for every driver on module
>>> exit.
>>>
>>> Option 2:
>>> * advantages:
>>> - less complex `module_phy_driver` macro.
>>> - no static variable needed.
>>> - only a single call to
>>> `phy_drivers_register`/`phy_drivers_unregister`.
>>> * disadvantages:
>>> - no safe manual creation of phy drivers possible, the only safe
>>> way is to use the `module_phy_driver` macro.
>>>
>>> I suppose that it would be ok to call the register function multiple
>>> times, since it only is on module startup/shutdown and it is not
>>> performance critical.
>>
>> I think that we can use the current implantation using Reservation
>> struct until someone requests manual creation. I doubt that we will
>> need to support such.
>
> I would like to remove the mutable static variable and simplify
> the macro.
It's worse than having public unsafe function (phy_drivers_unregister)?
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 15:32 ` FUJITA Tomonori
@ 2023-10-19 16:37 ` Benno Lossin
2023-10-19 21:51 ` FUJITA Tomonori
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-19 16:37 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 19.10.23 17:32, FUJITA Tomonori wrote:
>> You can just do this (I omitted the `::kernel::` prefix for
>> readability, if you add this in the macro, please include it):
>>
>> // CAST: `DriverVTable` is `repr(transparent)` and wrapping `bindings::phy_driver`.
>> let ptr = drv.as_mut_ptr().cast::<bindings::phy_driver>();
>> let len = drv.len().try_into()?;
>> // SAFETY: ...
>> to_result(unsafe { bindings::phy_drivers_register(ptr, len, module.0) })?;
>>
>>> })?;
>
> The above solves DriverVTable.0 but still the macro can't access to
> kernel::ThisModule.0. I got the following error:
I think we could just provide an `as_ptr` getter function
for `ThisModule`. But need to check with the others.
[...]
>>>> I suppose that it would be ok to call the register function multiple
>>>> times, since it only is on module startup/shutdown and it is not
>>>> performance critical.
>>>
>>> I think that we can use the current implantation using Reservation
>>> struct until someone requests manual creation. I doubt that we will
>>> need to support such.
>>
>> I would like to remove the mutable static variable and simplify
>> the macro.
>
> It's worse than having public unsafe function (phy_drivers_unregister)?
Why would that function have to be public?
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 16:37 ` Benno Lossin
@ 2023-10-19 21:51 ` FUJITA Tomonori
2023-10-21 7:21 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-19 21:51 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Thu, 19 Oct 2023 16:37:46 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> On 19.10.23 17:32, FUJITA Tomonori wrote:
>>> You can just do this (I omitted the `::kernel::` prefix for
>>> readability, if you add this in the macro, please include it):
>>>
>>> // CAST: `DriverVTable` is `repr(transparent)` and wrapping `bindings::phy_driver`.
>>> let ptr = drv.as_mut_ptr().cast::<bindings::phy_driver>();
>>> let len = drv.len().try_into()?;
>>> // SAFETY: ...
>>> to_result(unsafe { bindings::phy_drivers_register(ptr, len, module.0) })?;
>>>
>>>> })?;
>>
>> The above solves DriverVTable.0 but still the macro can't access to
>> kernel::ThisModule.0. I got the following error:
>
> I think we could just provide an `as_ptr` getter function
> for `ThisModule`. But need to check with the others.
>
ThisModule.0 is *mut bindings::module. Drivers should not use
bindings?
>>>>> I suppose that it would be ok to call the register function multiple
>>>>> times, since it only is on module startup/shutdown and it is not
>>>>> performance critical.
>>>>
>>>> I think that we can use the current implantation using Reservation
>>>> struct until someone requests manual creation. I doubt that we will
>>>> need to support such.
>>>
>>> I would like to remove the mutable static variable and simplify
>>> the macro.
>>
>> It's worse than having public unsafe function (phy_drivers_unregister)?
>
> Why would that function have to be public?
If we don't make ThisModule.0 public, phy_drivers_unregister has to be
public.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 15:20 ` Benno Lossin
2023-10-19 15:32 ` FUJITA Tomonori
@ 2023-10-20 0:34 ` FUJITA Tomonori
2023-10-20 12:54 ` FUJITA Tomonori
1 sibling, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-20 0:34 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Thu, 19 Oct 2023 15:20:51 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> I would like to remove the mutable static variable and simplify
> the macro.
How about adding DriverVTable array to Registration?
/// Registration structure for a PHY driver.
///
/// # Invariants
///
/// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
pub struct Registration<const N: usize> {
drivers: [DriverVTable; N],
}
impl<const N: usize> Registration<{ N }> {
/// Registers a PHY driver.
pub fn register(
module: &'static crate::ThisModule,
drivers: [DriverVTable; N],
) -> Result<Self> {
let mut reg = Registration { drivers };
let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
// SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
// are initialized properly. So an FFI call with a valid pointer.
to_result(unsafe {
bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
})?;
// INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
Ok(reg)
}
}
Then the macro becomes simpler. No need to add any public
functions. Also I think that this approach supports the manual
registration.
(drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
struct Module {
_reg: ::kernel::net::phy::Registration<N>,
}
$crate::prelude::module! {
type: Module,
$($f)*
}
impl ::kernel::Module for Module {
fn init(module: &'static ThisModule) -> Result<Self> {
let drivers = [$(::kernel::net::phy::create_phy_driver::<$driver>()),+];
let reg = ::kernel::net::phy::Registration::register(module, drivers)?;
Ok(Module { _reg: reg })
}
}
$crate::module_phy_driver!(@device_table [$($dev),+]);
}
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-17 11:30 ` [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking FUJITA Tomonori
@ 2023-10-20 11:37 ` Andreas Hindborg (Samsung)
2023-10-20 12:34 ` Miguel Ojeda
2023-10-21 3:51 ` FUJITA Tomonori
0 siblings, 2 replies; 65+ messages in thread
From: Andreas Hindborg (Samsung) @ 2023-10-20 11:37 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg, Miguel Ojeda
FUJITA Tomonori <fujita.tomonori@gmail.com> writes:
> From: Miguel Ojeda <ojeda@kernel.org>
>
> error[E0005]: refutable pattern in function argument
> --> rust/bindings/bindings_enum_check.rs:29:6
> |
> 29 | (phy_state::PHY_DOWN
> | ______^
> 30 | | | phy_state::PHY_READY
> 31 | | | phy_state::PHY_HALTED
> 32 | | | phy_state::PHY_ERROR
> ... |
> 35 | | | phy_state::PHY_NOLINK
> 36 | | | phy_state::PHY_CABLETEST): phy_state,
> | |______________________________^ pattern `phy_state::PHY_NEW` not covered
> |
> note: `phy_state` defined here
> --> rust/bindings/bindings_generated_enum_check.rs:60739:10
> |
> 60739 | pub enum phy_state {
> | ^^^^^^^^^
> ...
> 60745 | PHY_NEW = 5,
> | ------- not covered
> = note: the matched value is of type `phy_state`
>
> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This patch does not build for me. Do I need to do something to make it
work?
BR Andreas
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-20 11:37 ` Andreas Hindborg (Samsung)
@ 2023-10-20 12:34 ` Miguel Ojeda
2023-10-20 12:35 ` Miguel Ojeda
2023-10-23 8:57 ` Andreas Hindborg (Samsung)
2023-10-21 3:51 ` FUJITA Tomonori
1 sibling, 2 replies; 65+ messages in thread
From: Miguel Ojeda @ 2023-10-20 12:34 UTC (permalink / raw)
To: Andreas Hindborg (Samsung)
Cc: FUJITA Tomonori, netdev, rust-for-linux, andrew, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg, Miguel Ojeda
On Fri, Oct 20, 2023 at 1:42 PM Andreas Hindborg (Samsung)
<nmi@metaspace.dk> wrote:
>
> This patch does not build for me. Do I need to do something to make it
> work?
Please change:
- --crate-name enum_check $(obj)/bindings/bindings_enum_check.rs
+ --crate-name enum_check
$(srctree)/$(src)/bindings/bindings_enum_check.rs
Cheers,
Miguel
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-20 12:34 ` Miguel Ojeda
@ 2023-10-20 12:35 ` Miguel Ojeda
2023-10-23 8:57 ` Andreas Hindborg (Samsung)
1 sibling, 0 replies; 65+ messages in thread
From: Miguel Ojeda @ 2023-10-20 12:35 UTC (permalink / raw)
To: Andreas Hindborg (Samsung)
Cc: FUJITA Tomonori, netdev, rust-for-linux, andrew, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg, Miguel Ojeda
On Fri, Oct 20, 2023 at 2:34 PM Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
>
> Please change:
>
> - --crate-name enum_check $(obj)/bindings/bindings_enum_check.rs
> + --crate-name enum_check
> $(srctree)/$(src)/bindings/bindings_enum_check.rs
...without the email wrapping.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 0:34 ` FUJITA Tomonori
@ 2023-10-20 12:54 ` FUJITA Tomonori
2023-10-21 7:25 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-20 12:54 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Fri, 20 Oct 2023 09:34:46 +0900 (JST)
FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
> On Thu, 19 Oct 2023 15:20:51 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>> I would like to remove the mutable static variable and simplify
>> the macro.
>
> How about adding DriverVTable array to Registration?
>
> /// Registration structure for a PHY driver.
> ///
> /// # Invariants
> ///
> /// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
> pub struct Registration<const N: usize> {
> drivers: [DriverVTable; N],
> }
>
> impl<const N: usize> Registration<{ N }> {
> /// Registers a PHY driver.
> pub fn register(
> module: &'static crate::ThisModule,
> drivers: [DriverVTable; N],
> ) -> Result<Self> {
> let mut reg = Registration { drivers };
> let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
> // are initialized properly. So an FFI call with a valid pointer.
> to_result(unsafe {
> bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
> })?;
> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
> Ok(reg)
> }
> }
Scratch this.
This doesn't work. Also simply putting slice of DriverVTable into
Module strcut doesn't work.
We cannot move a slice of DriverVTable. Except for static allocation,
is there a simple way?
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-17 11:30 ` [PATCH net-next v5 1/5] rust: core " FUJITA Tomonori
2023-10-18 15:07 ` Benno Lossin
2023-10-18 20:27 ` Andrew Lunn
@ 2023-10-20 17:26 ` Andreas Hindborg (Samsung)
2023-10-20 17:56 ` Benno Lossin
` (2 more replies)
2 siblings, 3 replies; 65+ messages in thread
From: Andreas Hindborg (Samsung) @ 2023-10-20 17:26 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg
Hi,
FUJITA Tomonori <fujita.tomonori@gmail.com> writes:
<cut>
> +
> + /// Returns true if the link is up.
> + pub fn get_link(&self) -> bool {
> + const LINK_IS_UP: u32 = 1;
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + let phydev = unsafe { *self.0.get() };
> + phydev.link() == LINK_IS_UP
> + }
I would prefer `is_link_up` or `link_is_up`.
> +
> + /// Returns true if auto-negotiation is enabled.
> + pub fn is_autoneg_enabled(&self) -> bool {
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + let phydev = unsafe { *self.0.get() };
> + phydev.autoneg() == bindings::AUTONEG_ENABLE
> + }
> +
> + /// Returns true if auto-negotiation is completed.
> + pub fn is_autoneg_completed(&self) -> bool {
> + const AUTONEG_COMPLETED: u32 = 1;
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + let phydev = unsafe { *self.0.get() };
> + phydev.autoneg_complete() == AUTONEG_COMPLETED
> + }
> +
> + /// Sets the speed of the PHY.
> + pub fn set_speed(&mut self, speed: u32) {
> + let phydev = self.0.get();
> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> + unsafe { (*phydev).speed = speed as i32 };
> + }
If this function is called with `u32::MAX` `(*phydev).speed` will become -1. Is that OK?
<cut>
> +
> +/// An instance of a PHY driver.
> +///
> +/// Wraps the kernel's `struct phy_driver`.
> +///
> +/// # Invariants
> +///
> +/// `self.0` is always in a valid state.
> +#[repr(transparent)]
> +pub struct DriverType(Opaque<bindings::phy_driver>);
I don't like the name `DriverType`. How about `DriverDesciptor` or
something like that?
<cut>
> +
> +/// Corresponds to functions in `struct phy_driver`.
> +///
> +/// This is used to register a PHY driver.
> +#[vtable]
> +pub trait Driver {
> + /// Defines certain other features this PHY supports.
> + /// It is a combination of the flags in the [`flags`] module.
> + const FLAGS: u32 = 0;
> +
> + /// The friendly name of this PHY type.
> + const NAME: &'static CStr;
> +
> + /// This driver only works for PHYs with IDs which match this field.
> + /// The default id and mask are zero.
> + const PHY_DEVICE_ID: DeviceId = DeviceId::new_with_custom_mask(0, 0);
> +
> + /// Issues a PHY software reset.
> + fn soft_reset(_dev: &mut Device) -> Result {
> + Err(code::ENOTSUPP)
> + }
> +
> + /// Probes the hardware to determine what abilities it has.
> + fn get_features(_dev: &mut Device) -> Result {
> + Err(code::ENOTSUPP)
> + }
> +
> + /// Returns true if this is a suitable driver for the given phydev.
> + /// If not implemented, matching is based on [`Driver::PHY_DEVICE_ID`].
> + fn match_phy_device(_dev: &Device) -> bool {
> + false
> + }
> +
> + /// Configures the advertisement and resets auto-negotiation
> + /// if auto-negotiation is enabled.
> + fn config_aneg(_dev: &mut Device) -> Result {
> + Err(code::ENOTSUPP)
> + }
> +
> + /// Determines the negotiated speed and duplex.
> + fn read_status(_dev: &mut Device) -> Result<u16> {
> + Err(code::ENOTSUPP)
> + }
> +
> + /// Suspends the hardware, saving state if needed.
> + fn suspend(_dev: &mut Device) -> Result {
> + Err(code::ENOTSUPP)
> + }
> +
> + /// Resumes the hardware, restoring state if needed.
> + fn resume(_dev: &mut Device) -> Result {
> + Err(code::ENOTSUPP)
> + }
> +
> + /// Overrides the default MMD read function for reading a MMD register.
> + fn read_mmd(_dev: &mut Device, _devnum: u8, _regnum: u16) -> Result<u16> {
> + Err(code::ENOTSUPP)
> + }
> +
> + /// Overrides the default MMD write function for writing a MMD register.
> + fn write_mmd(_dev: &mut Device, _devnum: u8, _regnum: u16, _val: u16) -> Result {
> + Err(code::ENOTSUPP)
> + }
> +
> + /// Callback for notification of link change.
> + fn link_change_notify(_dev: &mut Device) {}
It is probably an error if these functions are called, and so BUG() would be
appropriate? See the discussion in [1].
[1] https://lore.kernel.org/rust-for-linux/20231019171540.259173-1-benno.lossin@proton.me/
<cut>
> +
> + // macro use only
> + #[doc(hidden)]
> + pub const fn mdio_device_id(&self) -> bindings::mdio_device_id {
> + bindings::mdio_device_id {
> + phy_id: self.id,
> + phy_id_mask: self.mask.as_int(),
> + }
> + }
Would it make sense to move this function to the macro patch?
Best regards,
Andreas
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 17:26 ` Andreas Hindborg (Samsung)
@ 2023-10-20 17:56 ` Benno Lossin
2023-10-20 19:59 ` Andrew Lunn
2023-10-21 4:01 ` FUJITA Tomonori
2 siblings, 0 replies; 65+ messages in thread
From: Benno Lossin @ 2023-10-20 17:56 UTC (permalink / raw)
To: Andreas Hindborg (Samsung), FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 20.10.23 19:26, Andreas Hindborg (Samsung) wrote:
>> +
>> + /// Overrides the default MMD write function for writing a MMD register.
>> + fn write_mmd(_dev: &mut Device, _devnum: u8, _regnum: u16, _val: u16) -> Result {
>> + Err(code::ENOTSUPP)
>> + }
>> +
>> + /// Callback for notification of link change.
>> + fn link_change_notify(_dev: &mut Device) {}
>
> It is probably an error if these functions are called, and so BUG() would be
> appropriate? See the discussion in [1].
Please do not use `BUG()` I wanted to wait for my patch [1] to be merged
before suggesting this, since then Tomo can then just use the constant
that I introduced.
> [1] https://lore.kernel.org/rust-for-linux/20231019171540.259173-1-benno.lossin@proton.me/
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-18 15:07 ` Benno Lossin
2023-10-19 0:24 ` FUJITA Tomonori
@ 2023-10-20 18:42 ` Andrew Lunn
2023-10-21 4:44 ` FUJITA Tomonori
` (2 more replies)
1 sibling, 3 replies; 65+ messages in thread
From: Andrew Lunn @ 2023-10-20 18:42 UTC (permalink / raw)
To: Benno Lossin
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, greg
> > +//! All the PHYLIB helper functions for `phy_device` modify some members in `phy_device`. Except for
> > +//! getter functions, [`Device`] methods take `&mut self`. This also applied to `read()`, which reads
> > +//! a hardware register and updates the stats.
>
> I would use [`Device`] instead of `phy_device`, since the Rust reader
> might not be aware what wraps `phy_device`.
We don't want to hide phy_device too much, since at the moment, the
abstraction is very minimal. Anybody writing a driver is going to need
a good understanding of the C code in order to find the helpers they
need, and then add them to the abstraction. So i would say we need to
explain the relationship between the C structure and the Rust
structure, to aid developers.
> > + /// Returns true if the link is up.
> > + pub fn get_link(&self) -> bool {
>
> I still think this name should be changed. My response at [1] has not yet
> been replied to. This has already been discussed before:
> - https://lore.kernel.org/rust-for-linux/2023100237-satirical-prance-bd57@gregkh/
> - https://lore.kernel.org/rust-for-linux/20231004.084644.50784533959398755.fujita.tomonori@gmail.com/
> - https://lore.kernel.org/rust-for-linux/CALNs47syMxiZBUwKLk3vKxzmCbX0FS5A37FjwUzZO9Fn-iPaoA@mail.gmail.com/
>
> And I want to suggest to change it to `is_link_up`.
>
> Reasons why I do not like the name:
> - `get_`/`put_` are used for ref counting on the C side, I would like to
> avoid confusion.
> - `get` in Rust is often used to fetch a value from e.g. a datastructure
> such as a hashmap, so I expect the call to do some computation.
> - getters in Rust usually are not prefixed with `get_`, but rather are
> just the name of the field.
> - in this case I like the name `is_link_up` much better, since code becomes
> a lot more readable with that.
> - I do not want this pattern as an example for other drivers.
>
> [1]: https://lore.kernel.org/rust-for-linux/f5878806-5ba2-d932-858d-dda3f55ceb67@proton.me/
>
> > + const LINK_IS_UP: u32 = 1;
> > + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> > + let phydev = unsafe { *self.0.get() };
> > + phydev.link() == LINK_IS_UP
> > + }
During the reviews we have had a lot of misunderstanding what this
actually does, given its name. Some thought it poked around in
registers to get the current state of the link. Some thought it
triggered the PHY to establish a link. When in fact its just a dumb
getter. And we have a few other dumb getters and setters.
So i would prefer something which indicates its a dumb getter. If the
norm of Rust is just the field name, lets just use the field name. But
we should do that for all the getters and setters. Is there a naming
convention for things which take real actions?
And maybe we need to add a comment: Get the current link state, as
stored in the [`Device`]. Set the duplex value in [`Device`], etc.
Andrew
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 13:57 ` Benno Lossin
@ 2023-10-20 19:50 ` Andrew Lunn
2023-10-21 8:01 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: Andrew Lunn @ 2023-10-20 19:50 UTC (permalink / raw)
To: Benno Lossin
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, greg
> I will try to explain things a bit more.
>
> So this case is a bit difficult to figure out, because what is
> going on is not really a pattern that is used in Rust.
It is however a reasonably common pattern in the kernel. It stems from
driver writers often don't understand locking. So the core does the
locking, leaving the driver writers to just handle the problems of the
hardware.
Rust maybe makes locking more of a visible issue, but if driver
writers don't understand locking, the language itself does not make
much difference.
> We already have exclusive access to the `phy_device`, so in Rust
> you would not need to lock anything to also have exclusive access to the
> embedded `mii_bus`.
I would actually say its not the PHY drivers problem at all. The
mii_bus is a property of the MDIO layers, and it is the MDIO layers
problem to impose whatever locking it needs for its properties.
Also, mii_bus is not embedded. Its a pointer to an mii_bus. The phy
lock protects the pointer. But its the MDIO layer which needs to
protect what the pointer points to.
Andrew
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 17:26 ` Andreas Hindborg (Samsung)
2023-10-20 17:56 ` Benno Lossin
@ 2023-10-20 19:59 ` Andrew Lunn
2023-10-20 20:30 ` Andreas Hindborg (Samsung)
2023-10-21 4:01 ` FUJITA Tomonori
2 siblings, 1 reply; 65+ messages in thread
From: Andrew Lunn @ 2023-10-20 19:59 UTC (permalink / raw)
To: Andreas Hindborg (Samsung)
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, benno.lossin, greg
On Fri, Oct 20, 2023 at 07:26:50PM +0200, Andreas Hindborg (Samsung) wrote:
>
> Hi,
>
> FUJITA Tomonori <fujita.tomonori@gmail.com> writes:
>
> <cut>
>
> > +
> > + /// Returns true if the link is up.
> > + pub fn get_link(&self) -> bool {
> > + const LINK_IS_UP: u32 = 1;
> > + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
> > + let phydev = unsafe { *self.0.get() };
> > + phydev.link() == LINK_IS_UP
> > + }
>
> I would prefer `is_link_up` or `link_is_up`.
Hi Andreas
Have you read the comment on the previous versions of this patch. It
seems like everybody wants a different name for this, and we are going
round and round and round. Please could you spend the time to read all
the previous comments and then see if you still want this name, and
explain why. Otherwise i doubt we are going to reach consensus.
> If this function is called with `u32::MAX` `(*phydev).speed` will become -1. Is that OK?
Have you ever seen a Copper Ethernet interface which can do u32:MAX
Mbps? Do you think such a thing will ever be possible?
> > + /// Callback for notification of link change.
> > + fn link_change_notify(_dev: &mut Device) {}
>
> It is probably an error if these functions are called, and so BUG() would be
> appropriate? See the discussion in [1].
Do you really want to kill the machine dead, no syncing of the disk,
loose everything not yet written to the disk, maybe corrupt the disk
etc?
Andrew
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 19:59 ` Andrew Lunn
@ 2023-10-20 20:30 ` Andreas Hindborg (Samsung)
2023-10-21 3:49 ` FUJITA Tomonori
0 siblings, 1 reply; 65+ messages in thread
From: Andreas Hindborg (Samsung) @ 2023-10-20 20:30 UTC (permalink / raw)
To: Andrew Lunn
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, benno.lossin, greg
Andrew Lunn <andrew@lunn.ch> writes:
> On Fri, Oct 20, 2023 at 07:26:50PM +0200, Andreas Hindborg (Samsung) wrote:
>>
>> Hi,
>>
>> FUJITA Tomonori <fujita.tomonori@gmail.com> writes:
>>
>> <cut>
>>
>> > +
>> > + /// Returns true if the link is up.
>> > + pub fn get_link(&self) -> bool {
>> > + const LINK_IS_UP: u32 = 1;
>> > + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>> > + let phydev = unsafe { *self.0.get() };
>> > + phydev.link() == LINK_IS_UP
>> > + }
>>
>> I would prefer `is_link_up` or `link_is_up`.
>
> Hi Andreas
>
> Have you read the comment on the previous versions of this patch. It
> seems like everybody wants a different name for this, and we are going
> round and round and round. Please could you spend the time to read all
> the previous comments and then see if you still want this name, and
> explain why. Otherwise i doubt we are going to reach consensus.
Thanks, I'll read through it.
>
>> If this function is called with `u32::MAX` `(*phydev).speed` will become -1. Is that OK?
>
> Have you ever seen a Copper Ethernet interface which can do u32:MAX
> Mbps? Do you think such a thing will ever be possible?
Probably not. Maybe a dummy device for testing? I don't know if it would
be OK with a negative value, hence the question. If things break with a
negative value, it makes sense to force the value into the valid range.
Make it impossible to break it, instead of relying on nobody ever doing
things you did not expect.
>
>> > + /// Callback for notification of link change.
>> > + fn link_change_notify(_dev: &mut Device) {}
>>
>> It is probably an error if these functions are called, and so BUG() would be
>> appropriate? See the discussion in [1].
>
> Do you really want to kill the machine dead, no syncing of the disk,
> loose everything not yet written to the disk, maybe corrupt the disk
> etc?
A WARN then? Benno suggests a compile time error, that is probably a
better approach. The code should not be called, so I would really want
to know if that was ever the case.
BR Andreas
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 20:30 ` Andreas Hindborg (Samsung)
@ 2023-10-21 3:49 ` FUJITA Tomonori
0 siblings, 0 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 3:49 UTC (permalink / raw)
To: nmi
Cc: andrew, fujita.tomonori, netdev, rust-for-linux,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf,
benno.lossin, greg
On Fri, 20 Oct 2023 22:30:49 +0200
"Andreas Hindborg (Samsung)" <nmi@metaspace.dk> wrote:
>>> If this function is called with `u32::MAX` `(*phydev).speed` will become -1. Is that OK?
>>
>> Have you ever seen a Copper Ethernet interface which can do u32:MAX
>> Mbps? Do you think such a thing will ever be possible?
>
> Probably not. Maybe a dummy device for testing? I don't know if it would
> be OK with a negative value, hence the question. If things break with a
> negative value, it makes sense to force the value into the valid range.
> Make it impossible to break it, instead of relying on nobody ever doing
> things you did not expect.
You can find discussion on this in the previous comments too.
>>> > + /// Callback for notification of link change.
>>> > + fn link_change_notify(_dev: &mut Device) {}
>>>
>>> It is probably an error if these functions are called, and so BUG() would be
>>> appropriate? See the discussion in [1].
>>
>> Do you really want to kill the machine dead, no syncing of the disk,
>> loose everything not yet written to the disk, maybe corrupt the disk
>> etc?
>
> A WARN then? Benno suggests a compile time error, that is probably a
> better approach. The code should not be called, so I would really want
> to know if that was ever the case.
These functions are never called so I don't think that WARN or
whatever doesn't matter. The api of vtable macro is misleading so I'm
not sure it's worth trying something with the api. I would prefer to
leave this alone until Benno's work.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-20 11:37 ` Andreas Hindborg (Samsung)
2023-10-20 12:34 ` Miguel Ojeda
@ 2023-10-21 3:51 ` FUJITA Tomonori
2023-10-21 12:05 ` Miguel Ojeda
1 sibling, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 3:51 UTC (permalink / raw)
To: nmi
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf,
benno.lossin, greg, ojeda
On Fri, 20 Oct 2023 13:37:14 +0200
"Andreas Hindborg (Samsung)" <nmi@metaspace.dk> wrote:
>
> FUJITA Tomonori <fujita.tomonori@gmail.com> writes:
>
>> From: Miguel Ojeda <ojeda@kernel.org>
>>
>> error[E0005]: refutable pattern in function argument
>> --> rust/bindings/bindings_enum_check.rs:29:6
>> |
>> 29 | (phy_state::PHY_DOWN
>> | ______^
>> 30 | | | phy_state::PHY_READY
>> 31 | | | phy_state::PHY_HALTED
>> 32 | | | phy_state::PHY_ERROR
>> ... |
>> 35 | | | phy_state::PHY_NOLINK
>> 36 | | | phy_state::PHY_CABLETEST): phy_state,
>> | |______________________________^ pattern `phy_state::PHY_NEW` not covered
>> |
>> note: `phy_state` defined here
>> --> rust/bindings/bindings_generated_enum_check.rs:60739:10
>> |
>> 60739 | pub enum phy_state {
>> | ^^^^^^^^^
>> ...
>> 60745 | PHY_NEW = 5,
>> | ------- not covered
>> = note: the matched value is of type `phy_state`
>>
>> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
>
> This patch does not build for me. Do I need to do something to make it
> work?
Hmm, this works for me.
Note that it depends on the first patch (the changes to binings_helper.h).
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 17:26 ` Andreas Hindborg (Samsung)
2023-10-20 17:56 ` Benno Lossin
2023-10-20 19:59 ` Andrew Lunn
@ 2023-10-21 4:01 ` FUJITA Tomonori
2 siblings, 0 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 4:01 UTC (permalink / raw)
To: nmi
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf,
benno.lossin, greg
On Fri, 20 Oct 2023 19:26:50 +0200
"Andreas Hindborg (Samsung)" <nmi@metaspace.dk> wrote:
>> +/// An instance of a PHY driver.
>> +///
>> +/// Wraps the kernel's `struct phy_driver`.
>> +///
>> +/// # Invariants
>> +///
>> +/// `self.0` is always in a valid state.
>> +#[repr(transparent)]
>> +pub struct DriverType(Opaque<bindings::phy_driver>);
>
> I don't like the name `DriverType`. How about `DriverDesciptor` or
> something like that?
Benno suggested DriverVTable. I plan to use that name in the next
version.
>> +
>> + // macro use only
>> + #[doc(hidden)]
>> + pub const fn mdio_device_id(&self) -> bindings::mdio_device_id {
>> + bindings::mdio_device_id {
>> + phy_id: self.id,
>> + phy_id_mask: self.mask.as_int(),
>> + }
>> + }
>
> Would it make sense to move this function to the macro patch?
IMHO, either is fine.
You could say that the function is used only in the macro but also you
could say that this is the method of DeviceId.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 18:42 ` Andrew Lunn
@ 2023-10-21 4:44 ` FUJITA Tomonori
2023-10-21 7:36 ` Benno Lossin
2023-10-21 12:47 ` Miguel Ojeda
2 siblings, 0 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 4:44 UTC (permalink / raw)
To: andrew
Cc: benno.lossin, fujita.tomonori, netdev, rust-for-linux,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Fri, 20 Oct 2023 20:42:10 +0200
Andrew Lunn <andrew@lunn.ch> wrote:
>> > +//! All the PHYLIB helper functions for `phy_device` modify some members in `phy_device`. Except for
>> > +//! getter functions, [`Device`] methods take `&mut self`. This also applied to `read()`, which reads
>> > +//! a hardware register and updates the stats.
>>
>> I would use [`Device`] instead of `phy_device`, since the Rust reader
>> might not be aware what wraps `phy_device`.
>
> We don't want to hide phy_device too much, since at the moment, the
> abstraction is very minimal. Anybody writing a driver is going to need
> a good understanding of the C code in order to find the helpers they
> need, and then add them to the abstraction. So i would say we need to
> explain the relationship between the C structure and the Rust
> structure, to aid developers.
I'm sure that Rust readers would notice Device wraps `phy_device`
because the comment on Device clearly says so.
/// An instance of a PHY device.
///
/// Wraps the kernel's `struct phy_device`.
///
/// # Invariants
///
/// `self.0` is always in a valid state.
#[repr(transparent)]
pub struct Device(Opaque<bindings::phy_device>);
I think that the relationships between the C and Rust structures are
documented in phy.rs.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-19 21:51 ` FUJITA Tomonori
@ 2023-10-21 7:21 ` Benno Lossin
0 siblings, 0 replies; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 7:21 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 19.10.23 23:51, FUJITA Tomonori wrote:
> On Thu, 19 Oct 2023 16:37:46 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>> On 19.10.23 17:32, FUJITA Tomonori wrote:
>>>> You can just do this (I omitted the `::kernel::` prefix for
>>>> readability, if you add this in the macro, please include it):
>>>>
>>>> // CAST: `DriverVTable` is `repr(transparent)` and wrapping `bindings::phy_driver`.
>>>> let ptr = drv.as_mut_ptr().cast::<bindings::phy_driver>();
>>>> let len = drv.len().try_into()?;
>>>> // SAFETY: ...
>>>> to_result(unsafe { bindings::phy_drivers_register(ptr, len, module.0) })?;
>>>>
>>>>> })?;
>>>
>>> The above solves DriverVTable.0 but still the macro can't access to
>>> kernel::ThisModule.0. I got the following error:
>>
>> I think we could just provide an `as_ptr` getter function
>> for `ThisModule`. But need to check with the others.
>>
>
> ThisModule.0 is *mut bindings::module. Drivers should not use
> bindings?
This is a special case, since it `module` is used on a lot of functions
(and it does not make sense to provide abstractions for those on
`ThisModule`). Additionally, `ThisModule` already has a public `from_raw`
function that takes a `*mut bindings::module`.
If you add a `as_ptr` function, please create a separate patch for it.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 12:54 ` FUJITA Tomonori
@ 2023-10-21 7:25 ` Benno Lossin
2023-10-21 7:30 ` FUJITA Tomonori
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 7:25 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 20.10.23 14:54, FUJITA Tomonori wrote:
> On Fri, 20 Oct 2023 09:34:46 +0900 (JST)
> FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
>
>> On Thu, 19 Oct 2023 15:20:51 +0000
>> Benno Lossin <benno.lossin@proton.me> wrote:
>>
>>> I would like to remove the mutable static variable and simplify
>>> the macro.
>>
>> How about adding DriverVTable array to Registration?
>>
>> /// Registration structure for a PHY driver.
>> ///
>> /// # Invariants
>> ///
>> /// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>> pub struct Registration<const N: usize> {
>> drivers: [DriverVTable; N],
>> }
>>
>> impl<const N: usize> Registration<{ N }> {
>> /// Registers a PHY driver.
>> pub fn register(
>> module: &'static crate::ThisModule,
>> drivers: [DriverVTable; N],
>> ) -> Result<Self> {
>> let mut reg = Registration { drivers };
>> let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
>> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
>> // are initialized properly. So an FFI call with a valid pointer.
>> to_result(unsafe {
>> bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
>> })?;
>> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
>> Ok(reg)
>> }
>> }
>
> Scratch this.
>
> This doesn't work. Also simply putting slice of DriverVTable into
> Module strcut doesn't work.
Why does it not work? I tried it and it compiled fine for me.
> We cannot move a slice of DriverVTable. Except for static allocation,
> is there a simple way?
I do not know what you are referring to, you can certainly move an array
of `DriverVTable`s.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 7:25 ` Benno Lossin
@ 2023-10-21 7:30 ` FUJITA Tomonori
2023-10-21 8:37 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 7:30 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Sat, 21 Oct 2023 07:25:17 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> On 20.10.23 14:54, FUJITA Tomonori wrote:
>> On Fri, 20 Oct 2023 09:34:46 +0900 (JST)
>> FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
>>
>>> On Thu, 19 Oct 2023 15:20:51 +0000
>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>
>>>> I would like to remove the mutable static variable and simplify
>>>> the macro.
>>>
>>> How about adding DriverVTable array to Registration?
>>>
>>> /// Registration structure for a PHY driver.
>>> ///
>>> /// # Invariants
>>> ///
>>> /// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>> pub struct Registration<const N: usize> {
>>> drivers: [DriverVTable; N],
>>> }
>>>
>>> impl<const N: usize> Registration<{ N }> {
>>> /// Registers a PHY driver.
>>> pub fn register(
>>> module: &'static crate::ThisModule,
>>> drivers: [DriverVTable; N],
>>> ) -> Result<Self> {
>>> let mut reg = Registration { drivers };
>>> let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
>>> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
>>> // are initialized properly. So an FFI call with a valid pointer.
>>> to_result(unsafe {
>>> bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
>>> })?;
>>> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
>>> Ok(reg)
>>> }
>>> }
>>
>> Scratch this.
>>
>> This doesn't work. Also simply putting slice of DriverVTable into
>> Module strcut doesn't work.
>
> Why does it not work? I tried it and it compiled fine for me.
You can compile but the kernel crashes. The addresses of the callback
functions are invalid.
>> We cannot move a slice of DriverVTable. Except for static allocation,
>> is there a simple way?
>
> I do not know what you are referring to, you can certainly move an array
> of `DriverVTable`s.
>
> --
> Cheers,
> Benno
>
>
>
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 18:42 ` Andrew Lunn
2023-10-21 4:44 ` FUJITA Tomonori
@ 2023-10-21 7:36 ` Benno Lossin
2023-10-21 12:47 ` Miguel Ojeda
2 siblings, 0 replies; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 7:36 UTC (permalink / raw)
To: Andrew Lunn
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, greg
On 20.10.23 20:42, Andrew Lunn wrote:
>>> +//! All the PHYLIB helper functions for `phy_device` modify some members in `phy_device`. Except for
>>> +//! getter functions, [`Device`] methods take `&mut self`. This also applied to `read()`, which reads
>>> +//! a hardware register and updates the stats.
>>
>> I would use [`Device`] instead of `phy_device`, since the Rust reader
>> might not be aware what wraps `phy_device`.
>
> We don't want to hide phy_device too much, since at the moment, the
> abstraction is very minimal. Anybody writing a driver is going to need
> a good understanding of the C code in order to find the helpers they
> need, and then add them to the abstraction. So i would say we need to
> explain the relationship between the C structure and the Rust
> structure, to aid developers.
There is a comment on `Device` that explains that it wraps `phy_device`.
Since [`Device`] is a link, readers who do not know what it means can
immediately click the link and find out. This is not possible with
`phy_device`, since you have to search the web for it, so I would
prefer to use the link.
>>> + /// Returns true if the link is up.
>>> + pub fn get_link(&self) -> bool {
>>
>> I still think this name should be changed. My response at [1] has not yet
>> been replied to. This has already been discussed before:
>> - https://lore.kernel.org/rust-for-linux/2023100237-satirical-prance-bd57@gregkh/
>> - https://lore.kernel.org/rust-for-linux/20231004.084644.50784533959398755.fujita.tomonori@gmail.com/
>> - https://lore.kernel.org/rust-for-linux/CALNs47syMxiZBUwKLk3vKxzmCbX0FS5A37FjwUzZO9Fn-iPaoA@mail.gmail.com/
>>
>> And I want to suggest to change it to `is_link_up`.
>>
>> Reasons why I do not like the name:
>> - `get_`/`put_` are used for ref counting on the C side, I would like to
>> avoid confusion.
>> - `get` in Rust is often used to fetch a value from e.g. a datastructure
>> such as a hashmap, so I expect the call to do some computation.
>> - getters in Rust usually are not prefixed with `get_`, but rather are
>> just the name of the field.
>> - in this case I like the name `is_link_up` much better, since code becomes
>> a lot more readable with that.
>> - I do not want this pattern as an example for other drivers.
>>
>> [1]: https://lore.kernel.org/rust-for-linux/f5878806-5ba2-d932-858d-dda3f55ceb67@proton.me/
>>
>>> + const LINK_IS_UP: u32 = 1;
>>> + // SAFETY: `phydev` is pointing to a valid object by the type invariant of `Self`.
>>> + let phydev = unsafe { *self.0.get() };
>>> + phydev.link() == LINK_IS_UP
>>> + }
>
> During the reviews we have had a lot of misunderstanding what this
> actually does, given its name. Some thought it poked around in
> registers to get the current state of the link. Some thought it
> triggered the PHY to establish a link. When in fact its just a dumb
> getter. And we have a few other dumb getters and setters.
IMO `is_link_up` would indicate that it is a dumb getter.
> So i would prefer something which indicates its a dumb getter. If the
> norm of Rust is just the field name, lets just use the field name. But
> we should do that for all the getters and setters. Is there a naming
> convention for things which take real actions?
For bool getters it would be the norm to use `is_` as the prefix, see
[1]. In this case I would say it is also natural to not use `is_link`,
but rather `is_link_up`, since the former sounds weird.
[1]: https://doc.rust-lang.org/std/?search=is_
> And maybe we need to add a comment: Get the current link state, as
> stored in the [`Device`]. Set the duplex value in [`Device`], etc.
Sure, we can document dumb getters explicitly, I would prefer to do:
Getter for the current link state. Setter for the duplex value.
I don't think that we need to link to `Device`, since the functions
are defined on that type.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 19:50 ` Andrew Lunn
@ 2023-10-21 8:01 ` Benno Lossin
2023-10-21 15:35 ` Andrew Lunn
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 8:01 UTC (permalink / raw)
To: Andrew Lunn
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, greg
On 20.10.23 21:50, Andrew Lunn wrote:
>> I will try to explain things a bit more.
>>
>> So this case is a bit difficult to figure out, because what is
>> going on is not really a pattern that is used in Rust.
>
> It is however a reasonably common pattern in the kernel. It stems from
> driver writers often don't understand locking. So the core does the
> locking, leaving the driver writers to just handle the problems of the
> hardware.
I have seen this pattern the first time here, I am sure more experienced
members such as Miguel and Wedson have seen it before.
I am not saying that it is incompatible with Rust, just that it
wouldn't be done like this if it were purely Rust.
> Rust maybe makes locking more of a visible issue, but if driver
> writers don't understand locking, the language itself does not make
> much difference.
I think Rust will make a big difference:
- you cannot access data protected by a lock without locking the
lock beforehand.
- you cannot forget to unlock a lock.
>> We already have exclusive access to the `phy_device`, so in Rust
>> you would not need to lock anything to also have exclusive access to the
>> embedded `mii_bus`.
>
> I would actually say its not the PHY drivers problem at all. The
> mii_bus is a property of the MDIO layers, and it is the MDIO layers
> problem to impose whatever locking it needs for its properties.
Since the MDIO layer would provide its own serialization, in Rust
we would not protect the `mdio_device` with a lock. In this case
it could just be a coincidence that both locks are locked, since
IIUC `phy_device` is locked whenever callbacks are called.
> Also, mii_bus is not embedded. Its a pointer to an mii_bus. The phy
> lock protects the pointer. But its the MDIO layer which needs to
> protect what the pointer points to.
Oh I overlooked the `*`. Then it depends what type of pointer that is,
is the `mii_bus` unique or is it shared? If it is shared, then Rust
would also need another lock there.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 7:30 ` FUJITA Tomonori
@ 2023-10-21 8:37 ` Benno Lossin
2023-10-21 10:27 ` FUJITA Tomonori
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 8:37 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 21.10.23 09:30, FUJITA Tomonori wrote:
> On Sat, 21 Oct 2023 07:25:17 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>> On 20.10.23 14:54, FUJITA Tomonori wrote:
>>> On Fri, 20 Oct 2023 09:34:46 +0900 (JST)
>>> FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
>>>
>>>> On Thu, 19 Oct 2023 15:20:51 +0000
>>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>
>>>>> I would like to remove the mutable static variable and simplify
>>>>> the macro.
>>>>
>>>> How about adding DriverVTable array to Registration?
>>>>
>>>> /// Registration structure for a PHY driver.
>>>> ///
>>>> /// # Invariants
>>>> ///
>>>> /// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>>> pub struct Registration<const N: usize> {
>>>> drivers: [DriverVTable; N],
>>>> }
>>>>
>>>> impl<const N: usize> Registration<{ N }> {
>>>> /// Registers a PHY driver.
>>>> pub fn register(
>>>> module: &'static crate::ThisModule,
>>>> drivers: [DriverVTable; N],
>>>> ) -> Result<Self> {
>>>> let mut reg = Registration { drivers };
>>>> let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
>>>> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
>>>> // are initialized properly. So an FFI call with a valid pointer.
>>>> to_result(unsafe {
>>>> bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
>>>> })?;
>>>> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
>>>> Ok(reg)
>>>> }
>>>> }
>>>
>>> Scratch this.
>>>
>>> This doesn't work. Also simply putting slice of DriverVTable into
>>> Module strcut doesn't work.
>>
>> Why does it not work? I tried it and it compiled fine for me.
>
> You can compile but the kernel crashes. The addresses of the callback
> functions are invalid.
Can you please share your setup and the error? For me it booted fine.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 8:37 ` Benno Lossin
@ 2023-10-21 10:27 ` FUJITA Tomonori
2023-10-21 11:21 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 10:27 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Sat, 21 Oct 2023 08:37:08 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> On 21.10.23 09:30, FUJITA Tomonori wrote:
>> On Sat, 21 Oct 2023 07:25:17 +0000
>> Benno Lossin <benno.lossin@proton.me> wrote:
>>
>>> On 20.10.23 14:54, FUJITA Tomonori wrote:
>>>> On Fri, 20 Oct 2023 09:34:46 +0900 (JST)
>>>> FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
>>>>
>>>>> On Thu, 19 Oct 2023 15:20:51 +0000
>>>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>>
>>>>>> I would like to remove the mutable static variable and simplify
>>>>>> the macro.
>>>>>
>>>>> How about adding DriverVTable array to Registration?
>>>>>
>>>>> /// Registration structure for a PHY driver.
>>>>> ///
>>>>> /// # Invariants
>>>>> ///
>>>>> /// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>>>> pub struct Registration<const N: usize> {
>>>>> drivers: [DriverVTable; N],
>>>>> }
>>>>>
>>>>> impl<const N: usize> Registration<{ N }> {
>>>>> /// Registers a PHY driver.
>>>>> pub fn register(
>>>>> module: &'static crate::ThisModule,
>>>>> drivers: [DriverVTable; N],
>>>>> ) -> Result<Self> {
>>>>> let mut reg = Registration { drivers };
>>>>> let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
>>>>> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
>>>>> // are initialized properly. So an FFI call with a valid pointer.
>>>>> to_result(unsafe {
>>>>> bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
>>>>> })?;
>>>>> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
>>>>> Ok(reg)
>>>>> }
>>>>> }
>>>>
>>>> Scratch this.
>>>>
>>>> This doesn't work. Also simply putting slice of DriverVTable into
>>>> Module strcut doesn't work.
>>>
>>> Why does it not work? I tried it and it compiled fine for me.
>>
>> You can compile but the kernel crashes. The addresses of the callback
>> functions are invalid.
>
> Can you please share your setup and the error? For me it booted
>fine.
You use ASIX PHY hardware?
I use the following macro:
(drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
struct Module {
_drivers: [::kernel::net::phy::DriverVTable; N],
}
$crate::prelude::module! {
type: Module,
$($f)*
}
unsafe impl Sync for Module {}
impl ::kernel::Module for Module {
fn init(module: &'static ThisModule) -> Result<Self> {
let mut m = Module {
_drivers:[$(::kernel::net::phy::create_phy_driver::<$driver>()),+],
};
let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
::kernel::error::to_result(unsafe {
kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
})?;
Ok(m)
}
}
[ 176.809218] asix 1-7:1.0 (unnamed net_device) (uninitialized): PHY [usb-001:003:10] driver [Asix Electronics AX88772A] (irq=POLL)
[ 176.812371] Asix Electronics AX88772A usb-001:003:10: attached PHY driver (mii_bus:phy_addr=usb-001:003:10, irq=POLL)
[ 176.812840] asix 1-7:1.0 eth0: register 'asix' at usb-0000:00:14.0-7, ASIX AX88772 USB 2.0 Ethernet, 08:6d:41:e4:30:66
[ 176.812927] usbcore: registered new interface driver asix
[ 176.816371] asix 1-7:1.0 enx086d41e43066: renamed from eth0
[ 176.872711] asix 1-7:1.0 enx086d41e43066: configuring for phy/internal link mode
[ 179.002965] asix 1-7:1.0 enx086d41e43066: Link is Up - 100Mbps/Full - flow control off
[ 319.672300] loop0: detected capacity change from 0 to 8
[ 367.936371] asix 1-7:1.0 enx086d41e43066: Link is Down
[ 370.459947] asix 1-7:1.0 enx086d41e43066: configuring for phy/internal link mode
[ 372.599320] asix 1-7:1.0 enx086d41e43066: Link is Up - 100Mbps/Full - flow control off
[ 615.277509] BUG: unable to handle page fault for address: ffffc90000752e98
[ 615.277598] #PF: supervisor read access in kernel mode
[ 615.277653] #PF: error_code(0x0000) - not-present page
[ 615.277706] PGD 100000067 P4D 100000067 PUD 1001f0067 PMD 102dad067 PTE 0
[ 615.277761] Oops: 0000 [#1] PREEMPT SMP
[ 615.277793] CPU: 14 PID: 147 Comm: kworker/14:1 Tainted: G OE 6.6.0-rc4+ #2
[ 615.277877] Hardware name: HP HP Slim Desktop S01-pF3xxx/8B3C, BIOS F.05 02/08/2023
[ 615.277929] Workqueue: events_power_efficient phy_state_machine
[ 615.277978] RIP: 0010:phy_check_link_status+0x28/0xd0
[ 615.278018] Code: 1f 00 0f 1f 44 00 00 55 48 89 e5 41 56 53 f6 87 dd 03 00 00 01 0f 85 ac 00 00 00 49 89 fe 48 8b 87 40 03 00 00 48 85 c0 74 13 <48> 8b 80 10 01 00 00 4c 89 f7 48 85 c0 74 0e ff d0 eb 0f bb fb ff
[ 615.278136] RSP: 0018:ffffc90000823de8 EFLAGS: 00010286
[ 615.278174] RAX: ffffc90000752d88 RBX: ffff8881023524e0 RCX: ffff888102e39980
[ 615.278223] RDX: ffff88846fbb18e8 RSI: 0000000000000000 RDI: ffff888102352000
[ 615.278269] RBP: ffffc90000823df8 R08: 8080808080808080 R09: fefefefefefefeff
[ 615.278316] R10: 0000000000000007 R11: 6666655f7265776f R12: ffff88846fbb18c0
[ 615.278364] R13: ffff888102b75000 R14: ffff888102352000 R15: ffff888102352000
[ 615.278412] FS: 0000000000000000(0000) GS:ffff88846fb80000(0000) knlGS:0000000000000000
[ 615.278491] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 615.278532] CR2: ffffc90000752e98 CR3: 0000000005433000 CR4: 0000000000750ee0
[ 615.278579] PKRU: 55555554
[ 615.278609] Call Trace:
[ 615.278629] <TASK>
[ 615.278649] ? __die_body+0x6b/0xb0
[ 615.278686] ? __die+0x86/0x90
[ 615.278725] ? page_fault_oops+0x369/0x3e0
[ 615.278771] ? usb_control_msg+0xfc/0x140
[ 615.278809] ? kfree+0x82/0x180
[ 615.278838] ? usb_control_msg+0xfc/0x140
[ 615.278871] ? kernelmode_fixup_or_oops+0xd5/0x100
[ 615.278923] ? __bad_area_nosemaphore+0x69/0x290
[ 615.278972] ? bad_area_nosemaphore+0x16/0x20
[ 615.279004] ? do_kern_addr_fault+0x7c/0x90
[ 615.279036] ? exc_page_fault+0xbc/0x220
[ 615.279081] ? asm_exc_page_fault+0x27/0x30
[ 615.279120] ? phy_check_link_status+0x28/0xd0
[ 615.279167] ? mutex_lock+0x14/0x70
[ 615.279198] phy_state_machine+0xb1/0x2c0
[ 615.279231] process_one_work+0x16f/0x3f0
[ 615.279263] ? wq_worker_running+0x11/0x90
[ 615.279310] worker_thread+0x360/0x4c0
[ 615.279351] ? __kthread_parkme+0x4c/0xb0
[ 615.279384] kthread+0xf6/0x120
[ 615.279412] ? pr_cont_work_flush+0xf0/0xf0
[ 615.279442] ? kthread_blkcg+0x30/0x30
[ 615.279485] ret_from_fork+0x35/0x40
[ 615.279528] ? kthread_blkcg+0x30/0x30
[ 615.279570] ret_from_fork_asm+0x11/0x20
[ 615.279619] </TASK>
[ 615.279644] Modules linked in: asix(E) rust_ax88796b(OE) intel_rapl_msr(E) intel_rapl_common(E) intel_uncore_frequency(E) intel_uncore_frequency_common(E) rtw88_8821ce(E) rtw88_8821c(E) rtw88_pci(E) rtw88_core(E) x86_pkg_temp_thermal(E) intel_powerclamp(E) mac80211(E) coretemp(E) rapl(E) libarc4(E) nls_iso8859_1(E) mei_me(E) intel_cstate(E) input_leds(E) apple_mfi_fastcharge(E) wmi_bmof(E) ee1004(E) cfg80211(E) mei(E) acpi_pad(E) acpi_tad(E) sch_fq_codel(E) msr(E) ramoops(E) reed_solomon(E) pstore_blk(E) pstore_zone(E) efi_pstore(E) ip_tables(E) x_tables(E) hid_generic(E) usbhid(E) hid(E) usbnet(E) phylink(E) mii(E) crct10dif_pclmul(E) crc32_pclmul(E) ghash_clmulni_intel(E) sha512_ssse3(E) r8169(E) aesni_intel(E) crypto_simd(E) cryptd(E) i2c_i801(E) i2c_smbus(E) realtek(E) xhci_pci(E) xhci_pci_renesas(E) video(E) wmi(E) [last unloaded: ax88796b(E)]
[ 615.280107] CR2: ffffc90000752e98
[ 615.280133] ---[ end trace 0000000000000000 ]---
[ 615.365054] RIP: 0010:phy_check_link_status+0x28/0xd0
[ 615.365065] Code: 1f 00 0f 1f 44 00 00 55 48 89 e5 41 56 53 f6 87 dd 03 00 00 01 0f 85 ac 00 00 00 49 89 fe 48 8b 87 40 03 00 00 48 85 c0 74 13 <48> 8b 80 10 01 00 00 4c 89 f7 48 85 c0 74 0e ff d0 eb 0f bb fb ff
[ 615.365104] RSP: 0018:ffffc90000823de8 EFLAGS: 00010286
[ 615.365116] RAX: ffffc90000752d88 RBX: ffff8881023524e0 RCX: ffff888102e39980
[ 615.365130] RDX: ffff88846fbb18e8 RSI: 0000000000000000 RDI: ffff888102352000
[ 615.365144] RBP: ffffc90000823df8 R08: 8080808080808080 R09: fefefefefefefeff
[ 615.365157] R10: 0000000000000007 R11: 6666655f7265776f R12: ffff88846fbb18c0
[ 615.365171] R13: ffff888102b75000 R14: ffff888102352000 R15: ffff888102352000
[ 615.365185] FS: 0000000000000000(0000) GS:ffff88846fb80000(0000) knlGS:0000000000000000
[ 615.365210] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 615.365222] CR2: ffffc90000752e98 CR3: 0000000111635000 CR4: 0000000000750ee0
[ 615.365237] PKRU: 55555554
[ 615.365247] note: kworker/14:1[147] exited with irqs disabled
[ 619.668322] loop0: detected capacity change from 0 to 8
[ 919.664303] loop0: detected capacity change from 0 to 8
[ 1219.660223] loop0: detected capacity change from 0 to 8
[ 1519.656041] loop0: detected capacity change from 0 to 8
[ 1819.651769] loop0: detected capacity change from 0 to 8
[ 2119.647826] loop0: detected capacity change from 0 to 8
[ 2419.643470] loop0: detected capacity change from 0 to 8
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 10:27 ` FUJITA Tomonori
@ 2023-10-21 11:21 ` Benno Lossin
2023-10-21 11:36 ` FUJITA Tomonori
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 11:21 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 21.10.23 12:27, FUJITA Tomonori wrote:
> On Sat, 21 Oct 2023 08:37:08 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>> On 21.10.23 09:30, FUJITA Tomonori wrote:
>>> On Sat, 21 Oct 2023 07:25:17 +0000
>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>
>>>> On 20.10.23 14:54, FUJITA Tomonori wrote:
>>>>> On Fri, 20 Oct 2023 09:34:46 +0900 (JST)
>>>>> FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
>>>>>
>>>>>> On Thu, 19 Oct 2023 15:20:51 +0000
>>>>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>>>
>>>>>>> I would like to remove the mutable static variable and simplify
>>>>>>> the macro.
>>>>>>
>>>>>> How about adding DriverVTable array to Registration?
>>>>>>
>>>>>> /// Registration structure for a PHY driver.
>>>>>> ///
>>>>>> /// # Invariants
>>>>>> ///
>>>>>> /// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>>>>> pub struct Registration<const N: usize> {
>>>>>> drivers: [DriverVTable; N],
>>>>>> }
>>>>>>
>>>>>> impl<const N: usize> Registration<{ N }> {
>>>>>> /// Registers a PHY driver.
>>>>>> pub fn register(
>>>>>> module: &'static crate::ThisModule,
>>>>>> drivers: [DriverVTable; N],
>>>>>> ) -> Result<Self> {
>>>>>> let mut reg = Registration { drivers };
>>>>>> let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
>>>>>> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
>>>>>> // are initialized properly. So an FFI call with a valid pointer.
>>>>>> to_result(unsafe {
>>>>>> bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
>>>>>> })?;
>>>>>> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
>>>>>> Ok(reg)
>>>>>> }
>>>>>> }
>>>>>
>>>>> Scratch this.
>>>>>
>>>>> This doesn't work. Also simply putting slice of DriverVTable into
>>>>> Module strcut doesn't work.
>>>>
>>>> Why does it not work? I tried it and it compiled fine for me.
>>>
>>> You can compile but the kernel crashes. The addresses of the callback
>>> functions are invalid.
>>
>> Can you please share your setup and the error? For me it booted
>> fine.
>
> You use ASIX PHY hardware?
It seems I have configured something wrong. Can you share your testing
setup? Do you use a virtual PHY device in qemu, or do you boot it from
real hardware with a real ASIX PHY device?
> I use the following macro:
>
> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
> const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
> struct Module {
> _drivers: [::kernel::net::phy::DriverVTable; N],
> }
>
> $crate::prelude::module! {
> type: Module,
> $($f)*
> }
>
> unsafe impl Sync for Module {}
>
> impl ::kernel::Module for Module {
> fn init(module: &'static ThisModule) -> Result<Self> {
> let mut m = Module {
> _drivers:[$(::kernel::net::phy::create_phy_driver::<$driver>()),+],
> };
> let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
> ::kernel::error::to_result(unsafe {
> kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
> })?;
> Ok(m)
> }
> }
>
>
> [ 176.809218] asix 1-7:1.0 (unnamed net_device) (uninitialized): PHY [usb-001:003:10] driver [Asix Electronics AX88772A] (irq=POLL)
> [ 176.812371] Asix Electronics AX88772A usb-001:003:10: attached PHY driver (mii_bus:phy_addr=usb-001:003:10, irq=POLL)
> [ 176.812840] asix 1-7:1.0 eth0: register 'asix' at usb-0000:00:14.0-7, ASIX AX88772 USB 2.0 Ethernet, 08:6d:41:e4:30:66
> [ 176.812927] usbcore: registered new interface driver asix
> [ 176.816371] asix 1-7:1.0 enx086d41e43066: renamed from eth0
> [ 176.872711] asix 1-7:1.0 enx086d41e43066: configuring for phy/internal link mode
> [ 179.002965] asix 1-7:1.0 enx086d41e43066: Link is Up - 100Mbps/Full - flow control off
> [ 319.672300] loop0: detected capacity change from 0 to 8
> [ 367.936371] asix 1-7:1.0 enx086d41e43066: Link is Down
> [ 370.459947] asix 1-7:1.0 enx086d41e43066: configuring for phy/internal link mode
> [ 372.599320] asix 1-7:1.0 enx086d41e43066: Link is Up - 100Mbps/Full - flow control off
> [ 615.277509] BUG: unable to handle page fault for address: ffffc90000752e98
> [ 615.277598] #PF: supervisor read access in kernel mode
> [ 615.277653] #PF: error_code(0x0000) - not-present page
> [ 615.277706] PGD 100000067 P4D 100000067 PUD 1001f0067 PMD 102dad067 PTE 0
> [ 615.277761] Oops: 0000 [#1] PREEMPT SMP
> [ 615.277793] CPU: 14 PID: 147 Comm: kworker/14:1 Tainted: G OE 6.6.0-rc4+ #2
> [ 615.277877] Hardware name: HP HP Slim Desktop S01-pF3xxx/8B3C, BIOS F.05 02/08/2023
> [ 615.277929] Workqueue: events_power_efficient phy_state_machine
> [ 615.277978] RIP: 0010:phy_check_link_status+0x28/0xd0
> [ 615.278018] Code: 1f 00 0f 1f 44 00 00 55 48 89 e5 41 56 53 f6 87 dd 03 00 00 01 0f 85 ac 00 00 00 49 89 fe 48 8b 87 40 03 00 00 48 85 c0 74 13 <48> 8b 80 10 01 00 00 4c 89 f7 48 85 c0 74 0e ff d0 eb 0f bb fb ff
> [ 615.278136] RSP: 0018:ffffc90000823de8 EFLAGS: 00010286
> [ 615.278174] RAX: ffffc90000752d88 RBX: ffff8881023524e0 RCX: ffff888102e39980
> [ 615.278223] RDX: ffff88846fbb18e8 RSI: 0000000000000000 RDI: ffff888102352000
> [ 615.278269] RBP: ffffc90000823df8 R08: 8080808080808080 R09: fefefefefefefeff
> [ 615.278316] R10: 0000000000000007 R11: 6666655f7265776f R12: ffff88846fbb18c0
> [ 615.278364] R13: ffff888102b75000 R14: ffff888102352000 R15: ffff888102352000
> [ 615.278412] FS: 0000000000000000(0000) GS:ffff88846fb80000(0000) knlGS:0000000000000000
> [ 615.278491] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 615.278532] CR2: ffffc90000752e98 CR3: 0000000005433000 CR4: 0000000000750ee0
> [ 615.278579] PKRU: 55555554
> [ 615.278609] Call Trace:
> [ 615.278629] <TASK>
> [ 615.278649] ? __die_body+0x6b/0xb0
> [ 615.278686] ? __die+0x86/0x90
> [ 615.278725] ? page_fault_oops+0x369/0x3e0
> [ 615.278771] ? usb_control_msg+0xfc/0x140
> [ 615.278809] ? kfree+0x82/0x180
> [ 615.278838] ? usb_control_msg+0xfc/0x140
> [ 615.278871] ? kernelmode_fixup_or_oops+0xd5/0x100
> [ 615.278923] ? __bad_area_nosemaphore+0x69/0x290
> [ 615.278972] ? bad_area_nosemaphore+0x16/0x20
> [ 615.279004] ? do_kern_addr_fault+0x7c/0x90
> [ 615.279036] ? exc_page_fault+0xbc/0x220
> [ 615.279081] ? asm_exc_page_fault+0x27/0x30
> [ 615.279120] ? phy_check_link_status+0x28/0xd0
> [ 615.279167] ? mutex_lock+0x14/0x70
> [ 615.279198] phy_state_machine+0xb1/0x2c0
> [ 615.279231] process_one_work+0x16f/0x3f0
> [ 615.279263] ? wq_worker_running+0x11/0x90
> [ 615.279310] worker_thread+0x360/0x4c0
> [ 615.279351] ? __kthread_parkme+0x4c/0xb0
> [ 615.279384] kthread+0xf6/0x120
> [ 615.279412] ? pr_cont_work_flush+0xf0/0xf0
> [ 615.279442] ? kthread_blkcg+0x30/0x30
> [ 615.279485] ret_from_fork+0x35/0x40
> [ 615.279528] ? kthread_blkcg+0x30/0x30
> [ 615.279570] ret_from_fork_asm+0x11/0x20
> [ 615.279619] </TASK>
> [ 615.279644] Modules linked in: asix(E) rust_ax88796b(OE) intel_rapl_msr(E) intel_rapl_common(E) intel_uncore_frequency(E) intel_uncore_frequency_common(E) rtw88_8821ce(E) rtw88_8821c(E) rtw88_pci(E) rtw88_core(E) x86_pkg_temp_thermal(E) intel_powerclamp(E) mac80211(E) coretemp(E) rapl(E) libarc4(E) nls_iso8859_1(E) mei_me(E) intel_cstate(E) input_leds(E) apple_mfi_fastcharge(E) wmi_bmof(E) ee1004(E) cfg80211(E) mei(E) acpi_pad(E) acpi_tad(E) sch_fq_codel(E) msr(E) ramoops(E) reed_solomon(E) pstore_blk(E) pstore_zone(E) efi_pstore(E) ip_tables(E) x_tables(E) hid_generic(E) usbhid(E) hid(E) usbnet(E) phylink(E) mii(E) crct10dif_pclmul(E) crc32_pclmul(E) ghash_clmulni_intel(E) sha512_ssse3(E) r8169(E) aesni_intel(E) crypto_simd(E) cryptd(E) i2c_i801(E) i2c_smbus(E) realtek(E) xhci_pci(E) xhci_pci_renesas(E) video(E) wmi(E) [last unloaded: ax88796b(E)]
> [ 615.280107] CR2: ffffc90000752e98
> [ 615.280133] ---[ end trace 0000000000000000 ]---
> [ 615.365054] RIP: 0010:phy_check_link_status+0x28/0xd0
> [ 615.365065] Code: 1f 00 0f 1f 44 00 00 55 48 89 e5 41 56 53 f6 87 dd 03 00 00 01 0f 85 ac 00 00 00 49 89 fe 48 8b 87 40 03 00 00 48 85 c0 74 13 <48> 8b 80 10 01 00 00 4c 89 f7 48 85 c0 74 0e ff d0 eb 0f bb fb ff
> [ 615.365104] RSP: 0018:ffffc90000823de8 EFLAGS: 00010286
> [ 615.365116] RAX: ffffc90000752d88 RBX: ffff8881023524e0 RCX: ffff888102e39980
> [ 615.365130] RDX: ffff88846fbb18e8 RSI: 0000000000000000 RDI: ffff888102352000
> [ 615.365144] RBP: ffffc90000823df8 R08: 8080808080808080 R09: fefefefefefefeff
> [ 615.365157] R10: 0000000000000007 R11: 6666655f7265776f R12: ffff88846fbb18c0
> [ 615.365171] R13: ffff888102b75000 R14: ffff888102352000 R15: ffff888102352000
> [ 615.365185] FS: 0000000000000000(0000) GS:ffff88846fb80000(0000) knlGS:0000000000000000
> [ 615.365210] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 615.365222] CR2: ffffc90000752e98 CR3: 0000000111635000 CR4: 0000000000750ee0
> [ 615.365237] PKRU: 55555554
> [ 615.365247] note: kworker/14:1[147] exited with irqs disabled
> [ 619.668322] loop0: detected capacity change from 0 to 8
> [ 919.664303] loop0: detected capacity change from 0 to 8
> [ 1219.660223] loop0: detected capacity change from 0 to 8
> [ 1519.656041] loop0: detected capacity change from 0 to 8
> [ 1819.651769] loop0: detected capacity change from 0 to 8
> [ 2119.647826] loop0: detected capacity change from 0 to 8
> [ 2419.643470] loop0: detected capacity change from 0 to 8
I think this is very weird, do you have any idea why this
could happen?
If you don't mind, could you try if the following changes
anything?
(drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
struct Module {
_drivers: [::kernel::net::phy::DriverVTable; N],
}
$crate::prelude::module! {
type: Module,
$($f)*
}
unsafe impl Sync for Module {}
impl ::kernel::Module for Module {
fn init(module: &'static ThisModule) -> Result<Self> {
const DRIVERS: [::kernel::net::phy::DriverVTable; N] = [$(::kernel::net::phy::create_phy_driver::<$driver>()),+];
let mut m = Module {
_drivers: unsafe { core::ptr::read(&DRIVERS) },
};
let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
::kernel::error::to_result(unsafe {
kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
})?;
Ok(m)
}
}
and also the variation where you replace `const DRIVERS` with
`static DRIVERS`.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 11:21 ` Benno Lossin
@ 2023-10-21 11:36 ` FUJITA Tomonori
2023-10-21 12:13 ` Benno Lossin
2023-10-21 15:41 ` Andrew Lunn
0 siblings, 2 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 11:36 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Sat, 21 Oct 2023 11:21:12 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> On 21.10.23 12:27, FUJITA Tomonori wrote:
>> On Sat, 21 Oct 2023 08:37:08 +0000
>> Benno Lossin <benno.lossin@proton.me> wrote:
>>
>>> On 21.10.23 09:30, FUJITA Tomonori wrote:
>>>> On Sat, 21 Oct 2023 07:25:17 +0000
>>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>
>>>>> On 20.10.23 14:54, FUJITA Tomonori wrote:
>>>>>> On Fri, 20 Oct 2023 09:34:46 +0900 (JST)
>>>>>> FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
>>>>>>
>>>>>>> On Thu, 19 Oct 2023 15:20:51 +0000
>>>>>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>>>>
>>>>>>>> I would like to remove the mutable static variable and simplify
>>>>>>>> the macro.
>>>>>>>
>>>>>>> How about adding DriverVTable array to Registration?
>>>>>>>
>>>>>>> /// Registration structure for a PHY driver.
>>>>>>> ///
>>>>>>> /// # Invariants
>>>>>>> ///
>>>>>>> /// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>>>>>> pub struct Registration<const N: usize> {
>>>>>>> drivers: [DriverVTable; N],
>>>>>>> }
>>>>>>>
>>>>>>> impl<const N: usize> Registration<{ N }> {
>>>>>>> /// Registers a PHY driver.
>>>>>>> pub fn register(
>>>>>>> module: &'static crate::ThisModule,
>>>>>>> drivers: [DriverVTable; N],
>>>>>>> ) -> Result<Self> {
>>>>>>> let mut reg = Registration { drivers };
>>>>>>> let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
>>>>>>> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
>>>>>>> // are initialized properly. So an FFI call with a valid pointer.
>>>>>>> to_result(unsafe {
>>>>>>> bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
>>>>>>> })?;
>>>>>>> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
>>>>>>> Ok(reg)
>>>>>>> }
>>>>>>> }
>>>>>>
>>>>>> Scratch this.
>>>>>>
>>>>>> This doesn't work. Also simply putting slice of DriverVTable into
>>>>>> Module strcut doesn't work.
>>>>>
>>>>> Why does it not work? I tried it and it compiled fine for me.
>>>>
>>>> You can compile but the kernel crashes. The addresses of the callback
>>>> functions are invalid.
>>>
>>> Can you please share your setup and the error? For me it booted
>>> fine.
>>
>> You use ASIX PHY hardware?
>
> It seems I have configured something wrong. Can you share your testing
> setup? Do you use a virtual PHY device in qemu, or do you boot it from
> real hardware with a real ASIX PHY device?
real hardware with real ASIX PHY device.
Qemu supports a virtual PHY device?
>> I use the following macro:
>>
>> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
>> const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
>> struct Module {
>> _drivers: [::kernel::net::phy::DriverVTable; N],
>> }
>>
>> $crate::prelude::module! {
>> type: Module,
>> $($f)*
>> }
>>
>> unsafe impl Sync for Module {}
>>
>> impl ::kernel::Module for Module {
>> fn init(module: &'static ThisModule) -> Result<Self> {
>> let mut m = Module {
>> _drivers:[$(::kernel::net::phy::create_phy_driver::<$driver>()),+],
>> };
>> let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
>> ::kernel::error::to_result(unsafe {
>> kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
>> })?;
>> Ok(m)
>> }
>> }
>>
(snip)
>> [ 615.365054] RIP: 0010:phy_check_link_status+0x28/0xd0
>> [ 615.365065] Code: 1f 00 0f 1f 44 00 00 55 48 89 e5 41 56 53 f6 87 dd 03 00 00 01 0f 85 ac 00 00 00 49 89 fe 48 8b 87 40 03 00 00 48 85 c0 74 13 <48> 8b 80 10 01 00 00 4c 89 f7 48 85 c0 74 0e ff d0 eb 0f bb fb ff
>> [ 615.365104] RSP: 0018:ffffc90000823de8 EFLAGS: 00010286
>> [ 615.365116] RAX: ffffc90000752d88 RBX: ffff8881023524e0 RCX: ffff888102e39980
>> [ 615.365130] RDX: ffff88846fbb18e8 RSI: 0000000000000000 RDI: ffff888102352000
>> [ 615.365144] RBP: ffffc90000823df8 R08: 8080808080808080 R09: fefefefefefefeff
>> [ 615.365157] R10: 0000000000000007 R11: 6666655f7265776f R12: ffff88846fbb18c0
>> [ 615.365171] R13: ffff888102b75000 R14: ffff888102352000 R15: ffff888102352000
>> [ 615.365185] FS: 0000000000000000(0000) GS:ffff88846fb80000(0000) knlGS:0000000000000000
>> [ 615.365210] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>> [ 615.365222] CR2: ffffc90000752e98 CR3: 0000000111635000 CR4: 0000000000750ee0
>> [ 615.365237] PKRU: 55555554
>> [ 615.365247] note: kworker/14:1[147] exited with irqs disabled
>
> I think this is very weird, do you have any idea why this
> could happen?
DriverVtable is created on kernel stack, I guess.
> If you don't mind, could you try if the following changes
> anything?
I don't think it works. If you use const for DriverTable, DriverTable
is placed on read-only pages. The C side modifies DriverVTable array
so it does't work.
> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
> const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
> struct Module {
> _drivers: [::kernel::net::phy::DriverVTable; N],
> }
>
> $crate::prelude::module! {
> type: Module,
> $($f)*
> }
>
> unsafe impl Sync for Module {}
>
> impl ::kernel::Module for Module {
> fn init(module: &'static ThisModule) -> Result<Self> {
> const DRIVERS: [::kernel::net::phy::DriverVTable; N] = [$(::kernel::net::phy::create_phy_driver::<$driver>()),+];
> let mut m = Module {
> _drivers: unsafe { core::ptr::read(&DRIVERS) },
> };
> let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
> ::kernel::error::to_result(unsafe {
> kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
> })?;
> Ok(m)
> }
> }
>
> and also the variation where you replace `const DRIVERS` with
> `static DRIVERS`.
Probably works. But looks like similar with the current code? This is
simpler?
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-21 3:51 ` FUJITA Tomonori
@ 2023-10-21 12:05 ` Miguel Ojeda
2023-10-22 6:30 ` FUJITA Tomonori
2023-10-23 8:58 ` Andreas Hindborg (Samsung)
0 siblings, 2 replies; 65+ messages in thread
From: Miguel Ojeda @ 2023-10-21 12:05 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: nmi, netdev, rust-for-linux, andrew, tmgross, boqun.feng,
wedsonaf, benno.lossin, greg, ojeda
On Sat, Oct 21, 2023 at 5:51 AM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> Hmm, this works for me.
Andreas was probably using `O=`, but you were not.
At least that is what I guessed yesterday and what the suggestion I gave fixes.
This is also why sending unfinished work by someone else is not the
best idea. I would also have marked that patch as RFC and put it at
the end perhaps, to make it clearer.
By the way, the patch is missing your SoB. I would recommend using
`--signoff` in Git and b4's `am`, `cherry-pick` etc.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 11:36 ` FUJITA Tomonori
@ 2023-10-21 12:13 ` Benno Lossin
2023-10-21 12:38 ` FUJITA Tomonori
2023-10-21 15:41 ` Andrew Lunn
1 sibling, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 12:13 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 21.10.23 13:36, FUJITA Tomonori wrote:
> On Sat, 21 Oct 2023 11:21:12 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>> On 21.10.23 12:27, FUJITA Tomonori wrote:
>>> On Sat, 21 Oct 2023 08:37:08 +0000
>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>
>>>> On 21.10.23 09:30, FUJITA Tomonori wrote:
>>>>> On Sat, 21 Oct 2023 07:25:17 +0000
>>>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>>
>>>>>> On 20.10.23 14:54, FUJITA Tomonori wrote:
>>>>>>> On Fri, 20 Oct 2023 09:34:46 +0900 (JST)
>>>>>>> FUJITA Tomonori <fujita.tomonori@gmail.com> wrote:
>>>>>>>
>>>>>>>> On Thu, 19 Oct 2023 15:20:51 +0000
>>>>>>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>>>>>
>>>>>>>>> I would like to remove the mutable static variable and simplify
>>>>>>>>> the macro.
>>>>>>>>
>>>>>>>> How about adding DriverVTable array to Registration?
>>>>>>>>
>>>>>>>> /// Registration structure for a PHY driver.
>>>>>>>> ///
>>>>>>>> /// # Invariants
>>>>>>>> ///
>>>>>>>> /// The `drivers` slice are currently registered to the kernel via `phy_drivers_register`.
>>>>>>>> pub struct Registration<const N: usize> {
>>>>>>>> drivers: [DriverVTable; N],
>>>>>>>> }
>>>>>>>>
>>>>>>>> impl<const N: usize> Registration<{ N }> {
>>>>>>>> /// Registers a PHY driver.
>>>>>>>> pub fn register(
>>>>>>>> module: &'static crate::ThisModule,
>>>>>>>> drivers: [DriverVTable; N],
>>>>>>>> ) -> Result<Self> {
>>>>>>>> let mut reg = Registration { drivers };
>>>>>>>> let ptr = reg.drivers.as_mut_ptr().cast::<bindings::phy_driver>();
>>>>>>>> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
>>>>>>>> // are initialized properly. So an FFI call with a valid pointer.
>>>>>>>> to_result(unsafe {
>>>>>>>> bindings::phy_drivers_register(ptr, reg.drivers.len().try_into()?, module.0)
>>>>>>>> })?;
>>>>>>>> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
>>>>>>>> Ok(reg)
>>>>>>>> }
>>>>>>>> }
>>>>>>>
>>>>>>> Scratch this.
>>>>>>>
>>>>>>> This doesn't work. Also simply putting slice of DriverVTable into
>>>>>>> Module strcut doesn't work.
>>>>>>
>>>>>> Why does it not work? I tried it and it compiled fine for me.
>>>>>
>>>>> You can compile but the kernel crashes. The addresses of the callback
>>>>> functions are invalid.
>>>>
>>>> Can you please share your setup and the error? For me it booted
>>>> fine.
>>>
>>> You use ASIX PHY hardware?
>>
>> It seems I have configured something wrong. Can you share your testing
>> setup? Do you use a virtual PHY device in qemu, or do you boot it from
>> real hardware with a real ASIX PHY device?
>
> real hardware with real ASIX PHY device.
I see.
> Qemu supports a virtual PHY device?
I have no idea.
[...]
>> I think this is very weird, do you have any idea why this
>> could happen?
>
> DriverVtable is created on kernel stack, I guess.
But how does that invalidate the function pointers?
>> If you don't mind, could you try if the following changes
>> anything?
>
> I don't think it works. If you use const for DriverTable, DriverTable
> is placed on read-only pages. The C side modifies DriverVTable array
> so it does't work.
Did you try it? Note that I copy the `DriverVTable` into the Module
struct, so it will not be placed on a read-only page.
>> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
>> const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
>> struct Module {
>> _drivers: [::kernel::net::phy::DriverVTable; N],
>> }
>>
>> $crate::prelude::module! {
>> type: Module,
>> $($f)*
>> }
>>
>> unsafe impl Sync for Module {}
>>
>> impl ::kernel::Module for Module {
>> fn init(module: &'static ThisModule) -> Result<Self> {
>> const DRIVERS: [::kernel::net::phy::DriverVTable; N] = [$(::kernel::net::phy::create_phy_driver::<$driver>()),+];
>> let mut m = Module {
>> _drivers: unsafe { core::ptr::read(&DRIVERS) },
>> };
>> let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
>> ::kernel::error::to_result(unsafe {
>> kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
>> })?;
>> Ok(m)
>> }
>> }
>>
>> and also the variation where you replace `const DRIVERS` with
>> `static DRIVERS`.
>
> Probably works. But looks like similar with the current code? This is
> simpler?
Just curious if it has to do with using `static` vs `const`.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 12:13 ` Benno Lossin
@ 2023-10-21 12:38 ` FUJITA Tomonori
2023-10-21 12:50 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 12:38 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Sat, 21 Oct 2023 12:13:32 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
>>>>> Can you please share your setup and the error? For me it booted
>>>>> fine.
>>>>
>>>> You use ASIX PHY hardware?
>>>
>>> It seems I have configured something wrong. Can you share your testing
>>> setup? Do you use a virtual PHY device in qemu, or do you boot it from
>>> real hardware with a real ASIX PHY device?
>>
>> real hardware with real ASIX PHY device.
>
> I see.
>
>> Qemu supports a virtual PHY device?
>
> I have no idea.
When I had a look at Qemu several months ago, it didn't support such.
> [...]
>
>>> I think this is very weird, do you have any idea why this
>>> could happen?
>>
>> DriverVtable is created on kernel stack, I guess.
>
> But how does that invalidate the function pointers?
Not only funciton pointers. You can't store something on stack for
later use.
>>> If you don't mind, could you try if the following changes
>>> anything?
>>
>> I don't think it works. If you use const for DriverTable, DriverTable
>> is placed on read-only pages. The C side modifies DriverVTable array
>> so it does't work.
>
> Did you try it? Note that I copy the `DriverVTable` into the Module
> struct, so it will not be placed on a read-only page.
Ah, I misunderstood code. It doesn't work. DriverVTable on stack.
>>> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
>>> const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
>>> struct Module {
>>> _drivers: [::kernel::net::phy::DriverVTable; N],
>>> }
>>>
>>> $crate::prelude::module! {
>>> type: Module,
>>> $($f)*
>>> }
>>>
>>> unsafe impl Sync for Module {}
>>>
>>> impl ::kernel::Module for Module {
>>> fn init(module: &'static ThisModule) -> Result<Self> {
>>> const DRIVERS: [::kernel::net::phy::DriverVTable; N] = [$(::kernel::net::phy::create_phy_driver::<$driver>()),+];
>>> let mut m = Module {
>>> _drivers: unsafe { core::ptr::read(&DRIVERS) },
>>> };
>>> let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
>>> ::kernel::error::to_result(unsafe {
>>> kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
>>> })?;
>>> Ok(m)
>>> }
>>> }
>>>
>>> and also the variation where you replace `const DRIVERS` with
>>> `static DRIVERS`.
>>
>> Probably works. But looks like similar with the current code? This is
>> simpler?
>
> Just curious if it has to do with using `static` vs `const`.
static doesn't work too due to the same reason.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-20 18:42 ` Andrew Lunn
2023-10-21 4:44 ` FUJITA Tomonori
2023-10-21 7:36 ` Benno Lossin
@ 2023-10-21 12:47 ` Miguel Ojeda
2023-10-22 9:47 ` FUJITA Tomonori
2 siblings, 1 reply; 65+ messages in thread
From: Miguel Ojeda @ 2023-10-21 12:47 UTC (permalink / raw)
To: Andrew Lunn
Cc: Benno Lossin, FUJITA Tomonori, netdev, rust-for-linux, tmgross,
boqun.feng, wedsonaf, greg
On Fri, Oct 20, 2023 at 8:42 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> We don't want to hide phy_device too much, since at the moment, the
> abstraction is very minimal. Anybody writing a driver is going to need
> a good understanding of the C code in order to find the helpers they
> need, and then add them to the abstraction. So i would say we need to
> explain the relationship between the C structure and the Rust
> structure, to aid developers.
I don't see how exposing `phy_device` in the documentation (note: not
the implementation) helps with that. If someone has to add things to
the abstraction, then at that point they need to be reading the
implementation of the abstraction, and thus they should read the
comments.
That is why the helpers should in general not be mentioned in the
documentation, i.e. a Rust API user should not care / need to know
about them.
If we mix things up in the docs, then it actually becomes harder later
on to properly split it; and in the Rust side we want to maintain this
"API documentation" vs. "implementation comments" split. Thus it is
important to do it right in the first examples we will have in-tree.
And if an API is not abstracted yet, it should not be documented. APIs
and their docs should be added together, in the same patch, wherever
possible. Of course, implementation comments are different, and
possibly a designer of an abstraction may establish some rules or
guidelines for future APIs added -- that is fine, but if the user does
not need to know, it should not be in the docs, even if it is added
early.
Regarding this, part of the `phy` module documentation (i.e. the three
paragraphs) in this patch currently sounds more like an implementation
comment to me. It should probably be rewritten/split properly in docs
vs. comments.
> During the reviews we have had a lot of misunderstanding what this
> actually does, given its name. Some thought it poked around in
> registers to get the current state of the link. Some thought it
> triggered the PHY to establish a link. When in fact its just a dumb
> getter. And we have a few other dumb getters and setters.
>
> So i would prefer something which indicates its a dumb getter. If the
Agreed.
> norm of Rust is just the field name, lets just use the field name. But
> we should do that for all the getters and setters. Is there a naming
> convention for things which take real actions?
For the getters, there is
https://rust-lang.github.io/api-guidelines/naming.html#getter-names-follow-rust-convention-c-getter.
For "actual actions" that are non-trivial, starting with a prefix that
is not `set_*` would probably be ideal, i.e. things like read, load,
push, init, register, attach, resolve, link, lock, add, create,
find...
Cheers,
Miguel
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 12:38 ` FUJITA Tomonori
@ 2023-10-21 12:50 ` Benno Lossin
2023-10-21 13:00 ` FUJITA Tomonori
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 12:50 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 21.10.23 14:38, FUJITA Tomonori wrote:
> On Sat, 21 Oct 2023 12:13:32 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>>>>>> Can you please share your setup and the error? For me it booted
>>>>>> fine.
>>>>>
>>>>> You use ASIX PHY hardware?
>>>>
>>>> It seems I have configured something wrong. Can you share your testing
>>>> setup? Do you use a virtual PHY device in qemu, or do you boot it from
>>>> real hardware with a real ASIX PHY device?
>>>
>>> real hardware with real ASIX PHY device.
>>
>> I see.
>>
>>> Qemu supports a virtual PHY device?
>>
>> I have no idea.
>
> When I had a look at Qemu several months ago, it didn't support such.
>
>> [...]
>>
>>>> I think this is very weird, do you have any idea why this
>>>> could happen?
>>>
>>> DriverVtable is created on kernel stack, I guess.
>>
>> But how does that invalidate the function pointers?
>
> Not only funciton pointers. You can't store something on stack for
> later use.
It is not stored on the stack, it is only created on the stack and
moved to a global static later on. The `module!` macro creates a
`static mut __MOD: Option<Module>` where the module data is stored in.
It seems that constructing the driver table not at that location
is somehow interfering with something?
Wedson has a patch [1] to create in-place initialized modules, but
it probably is not completely finished, as he has not yet begun to
post it to the list. But I am sure that it is mature enough for
you to test this hypothesis.
[1]: https://github.com/wedsonaf/linux/commit/484ec70025ff9887d9ca228ec631264039cee355
--
Cheers,
Benno
>>>> If you don't mind, could you try if the following changes
>>>> anything?
>>>
>>> I don't think it works. If you use const for DriverTable, DriverTable
>>> is placed on read-only pages. The C side modifies DriverVTable array
>>> so it does't work.
>>
>> Did you try it? Note that I copy the `DriverVTable` into the Module
>> struct, so it will not be placed on a read-only page.
>
> Ah, I misunderstood code. It doesn't work. DriverVTable on stack.
>
>
>>>> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
>>>> const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
>>>> struct Module {
>>>> _drivers: [::kernel::net::phy::DriverVTable; N],
>>>> }
>>>>
>>>> $crate::prelude::module! {
>>>> type: Module,
>>>> $($f)*
>>>> }
>>>>
>>>> unsafe impl Sync for Module {}
>>>>
>>>> impl ::kernel::Module for Module {
>>>> fn init(module: &'static ThisModule) -> Result<Self> {
>>>> const DRIVERS: [::kernel::net::phy::DriverVTable; N] = [$(::kernel::net::phy::create_phy_driver::<$driver>()),+];
>>>> let mut m = Module {
>>>> _drivers: unsafe { core::ptr::read(&DRIVERS) },
>>>> };
>>>> let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
>>>> ::kernel::error::to_result(unsafe {
>>>> kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
>>>> })?;
>>>> Ok(m)
>>>> }
>>>> }
>>>>
>>>> and also the variation where you replace `const DRIVERS` with
>>>> `static DRIVERS`.
>>>
>>> Probably works. But looks like similar with the current code? This is
>>> simpler?
>>
>> Just curious if it has to do with using `static` vs `const`.
>
> static doesn't work too due to the same reason.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 12:50 ` Benno Lossin
@ 2023-10-21 13:00 ` FUJITA Tomonori
2023-10-21 13:05 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 13:00 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Sat, 21 Oct 2023 12:50:10 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> On 21.10.23 14:38, FUJITA Tomonori wrote:
>> On Sat, 21 Oct 2023 12:13:32 +0000
>> Benno Lossin <benno.lossin@proton.me> wrote:
>>
>>>>>>> Can you please share your setup and the error? For me it booted
>>>>>>> fine.
>>>>>>
>>>>>> You use ASIX PHY hardware?
>>>>>
>>>>> It seems I have configured something wrong. Can you share your testing
>>>>> setup? Do you use a virtual PHY device in qemu, or do you boot it from
>>>>> real hardware with a real ASIX PHY device?
>>>>
>>>> real hardware with real ASIX PHY device.
>>>
>>> I see.
>>>
>>>> Qemu supports a virtual PHY device?
>>>
>>> I have no idea.
>>
>> When I had a look at Qemu several months ago, it didn't support such.
>>
>>> [...]
>>>
>>>>> I think this is very weird, do you have any idea why this
>>>>> could happen?
>>>>
>>>> DriverVtable is created on kernel stack, I guess.
>>>
>>> But how does that invalidate the function pointers?
>>
>> Not only funciton pointers. You can't store something on stack for
>> later use.
>
> It is not stored on the stack, it is only created on the stack and
> moved to a global static later on. The `module!` macro creates a
> `static mut __MOD: Option<Module>` where the module data is stored in.
I know. The problem is that we call phy_drivers_register() with
DriverVTable on stack. Then it was moved.
> It seems that constructing the driver table not at that location
> is somehow interfering with something?
>
> Wedson has a patch [1] to create in-place initialized modules, but
> it probably is not completely finished, as he has not yet begun to
> post it to the list. But I am sure that it is mature enough for
> you to test this hypothesis.
>
> [1]: https://github.com/wedsonaf/linux/commit/484ec70025ff9887d9ca228ec631264039cee355
>
> --
> Cheers,
> Benno
>
>>>>> If you don't mind, could you try if the following changes
>>>>> anything?
>>>>
>>>> I don't think it works. If you use const for DriverTable, DriverTable
>>>> is placed on read-only pages. The C side modifies DriverVTable array
>>>> so it does't work.
>>>
>>> Did you try it? Note that I copy the `DriverVTable` into the Module
>>> struct, so it will not be placed on a read-only page.
>>
>> Ah, I misunderstood code. It doesn't work. DriverVTable on stack.
>>
>>
>>>>> (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], $($f:tt)*) => {
>>>>> const N: usize = $crate::module_phy_driver!(@count_devices $($driver),+);
>>>>> struct Module {
>>>>> _drivers: [::kernel::net::phy::DriverVTable; N],
>>>>> }
>>>>>
>>>>> $crate::prelude::module! {
>>>>> type: Module,
>>>>> $($f)*
>>>>> }
>>>>>
>>>>> unsafe impl Sync for Module {}
>>>>>
>>>>> impl ::kernel::Module for Module {
>>>>> fn init(module: &'static ThisModule) -> Result<Self> {
>>>>> const DRIVERS: [::kernel::net::phy::DriverVTable; N] = [$(::kernel::net::phy::create_phy_driver::<$driver>()),+];
>>>>> let mut m = Module {
>>>>> _drivers: unsafe { core::ptr::read(&DRIVERS) },
>>>>> };
>>>>> let ptr = m._drivers.as_mut_ptr().cast::<::kernel::bindings::phy_driver>();
>>>>> ::kernel::error::to_result(unsafe {
>>>>> kernel::bindings::phy_drivers_register(ptr, m._drivers.len().try_into()?, module.as_ptr())
>>>>> })?;
>>>>> Ok(m)
>>>>> }
>>>>> }
>>>>>
>>>>> and also the variation where you replace `const DRIVERS` with
>>>>> `static DRIVERS`.
>>>>
>>>> Probably works. But looks like similar with the current code? This is
>>>> simpler?
>>>
>>> Just curious if it has to do with using `static` vs `const`.
>>
>> static doesn't work too due to the same reason.
>
>
>
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 13:00 ` FUJITA Tomonori
@ 2023-10-21 13:05 ` Benno Lossin
2023-10-21 13:31 ` FUJITA Tomonori
2023-10-21 15:57 ` Andrew Lunn
0 siblings, 2 replies; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 13:05 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 21.10.23 15:00, FUJITA Tomonori wrote:
> On Sat, 21 Oct 2023 12:50:10 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>>> I think this is very weird, do you have any idea why this
>>>>>> could happen?
>>>>>
>>>>> DriverVtable is created on kernel stack, I guess.
>>>>
>>>> But how does that invalidate the function pointers?
>>>
>>> Not only funciton pointers. You can't store something on stack for
>>> later use.
>>
>> It is not stored on the stack, it is only created on the stack and
>> moved to a global static later on. The `module!` macro creates a
>> `static mut __MOD: Option<Module>` where the module data is stored in.
>
> I know. The problem is that we call phy_drivers_register() with
> DriverVTable on stack. Then it was moved.
I see, what exactly is the problem with that? In other words:
why does PHYLIB need `phy_driver` to stay at the same address?
This is an important requirement in Rust. Rust can ensure that
types are not moved by means of pinning them. In this case, Wedson's
patch below should fix the issue completely.
But we should also fix this in the abstractions, the `DriverVTable`
type should only be constructible in a pinned state. For this purpose
we have the `pin-init` API [2].
Are there any other things in PHY that must not change address?
[2]: https://rust-for-linux.github.io/docs/v6.6-rc2/kernel/init/index.html
--
Cheers,
Benno
>> It seems that constructing the driver table not at that location
>> is somehow interfering with something?
>>
>> Wedson has a patch [1] to create in-place initialized modules, but
>> it probably is not completely finished, as he has not yet begun to
>> post it to the list. But I am sure that it is mature enough for
>> you to test this hypothesis.
>>
>> [1]: https://github.com/wedsonaf/linux/commit/484ec70025ff9887d9ca228ec631264039cee35
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 13:05 ` Benno Lossin
@ 2023-10-21 13:31 ` FUJITA Tomonori
2023-10-21 13:35 ` Benno Lossin
2023-10-21 15:57 ` Andrew Lunn
1 sibling, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 13:31 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Sat, 21 Oct 2023 13:05:59 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
> On 21.10.23 15:00, FUJITA Tomonori wrote:
>> On Sat, 21 Oct 2023 12:50:10 +0000
>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>>>> I think this is very weird, do you have any idea why this
>>>>>>> could happen?
>>>>>>
>>>>>> DriverVtable is created on kernel stack, I guess.
>>>>>
>>>>> But how does that invalidate the function pointers?
>>>>
>>>> Not only funciton pointers. You can't store something on stack for
>>>> later use.
>>>
>>> It is not stored on the stack, it is only created on the stack and
>>> moved to a global static later on. The `module!` macro creates a
>>> `static mut __MOD: Option<Module>` where the module data is stored in.
>>
>> I know. The problem is that we call phy_drivers_register() with
>> DriverVTable on stack. Then it was moved.
>
> I see, what exactly is the problem with that? In other words:
> why does PHYLIB need `phy_driver` to stay at the same address?
phy_driver_register stores addresses that you passed.
> This is an important requirement in Rust. Rust can ensure that
> types are not moved by means of pinning them. In this case, Wedson's
> patch below should fix the issue completely.
>
> But we should also fix this in the abstractions, the `DriverVTable`
> type should only be constructible in a pinned state. For this purpose
> we have the `pin-init` API [2].
You can create DriverVTable freely. The restriction is what
phy_driver_register takes. Currently, it needs &'static DriverVTable
array so it works.
The C side uses static allocation too. If someone asks for, we could
loosen the restriction with a complicated implentation. But I doubt
that someone would ask for such.
> Are there any other things in PHY that must not change address?
I don't think so.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 13:31 ` FUJITA Tomonori
@ 2023-10-21 13:35 ` Benno Lossin
2023-10-21 21:45 ` FUJITA Tomonori
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 13:35 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 21.10.23 15:31, FUJITA Tomonori wrote:
> On Sat, 21 Oct 2023 13:05:59 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>> On 21.10.23 15:00, FUJITA Tomonori wrote:
>>> On Sat, 21 Oct 2023 12:50:10 +0000
>>> Benno Lossin <benno.lossin@proton.me> wrote:
>>>>>>>> I think this is very weird, do you have any idea why this
>>>>>>>> could happen?
>>>>>>>
>>>>>>> DriverVtable is created on kernel stack, I guess.
>>>>>>
>>>>>> But how does that invalidate the function pointers?
>>>>>
>>>>> Not only funciton pointers. You can't store something on stack for
>>>>> later use.
>>>>
>>>> It is not stored on the stack, it is only created on the stack and
>>>> moved to a global static later on. The `module!` macro creates a
>>>> `static mut __MOD: Option<Module>` where the module data is stored in.
>>>
>>> I know. The problem is that we call phy_drivers_register() with
>>> DriverVTable on stack. Then it was moved.
>>
>> I see, what exactly is the problem with that? In other words:
>> why does PHYLIB need `phy_driver` to stay at the same address?
>
> phy_driver_register stores addresses that you passed.
But the function pointers don't change?
>> This is an important requirement in Rust. Rust can ensure that
>> types are not moved by means of pinning them. In this case, Wedson's
>> patch below should fix the issue completely.
>>
>> But we should also fix this in the abstractions, the `DriverVTable`
>> type should only be constructible in a pinned state. For this purpose
>> we have the `pin-init` API [2].
>
> You can create DriverVTable freely. The restriction is what
> phy_driver_register takes.
Sure you can also keep it this way, I just thought only allowing
pinned creation makes things simpler.
> Currently, it needs &'static DriverVTable
> array so it works.
That is actually also incorrect. As the C side is going to modify
the `DriverVTable`, you should actually use `&'static mut DriverVTable`.
But since it is not allowed to be moved you have to use
`Pin<&'static mut DriverVTable>`.
> The C side uses static allocation too. If someone asks for, we could
> loosen the restriction with a complicated implentation. But I doubt
> that someone would ask for such.
With Wedson's patch you also would be using the static allocation
from `module!`. What my problem is, is that you are using a `static mut`
which is `unsafe` and you do not actually have to use it (with
Wedson's patch of course).
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 8:01 ` Benno Lossin
@ 2023-10-21 15:35 ` Andrew Lunn
0 siblings, 0 replies; 65+ messages in thread
From: Andrew Lunn @ 2023-10-21 15:35 UTC (permalink / raw)
To: Benno Lossin
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, greg
> I think Rust will make a big difference:
> - you cannot access data protected by a lock without locking the
> lock beforehand.
> - you cannot forget to unlock a lock.
It is going to be interesting to look at this in 5 to 10 years time.
By then we hopefully have Rust drivers in subsystems which do the
locking in the core and those which leave it to the drivers. We can
then see if Rust written drivers which have to handle locking do
better than C drivers, or is it still better to do it all in the core.
> >> We already have exclusive access to the `phy_device`, so in Rust
> >> you would not need to lock anything to also have exclusive access to the
> >> embedded `mii_bus`.
> >
> > I would actually say its not the PHY drivers problem at all. The
> > mii_bus is a property of the MDIO layers, and it is the MDIO layers
> > problem to impose whatever locking it needs for its properties.
>
> Since the MDIO layer would provide its own serialization, in Rust
> we would not protect the `mdio_device` with a lock. In this case
> it could just be a coincidence that both locks are locked, since
> IIUC `phy_device` is locked whenever callbacks are called.
>
> > Also, mii_bus is not embedded. Its a pointer to an mii_bus. The phy
> > lock protects the pointer. But its the MDIO layer which needs to
> > protect what the pointer points to.
>
> Oh I overlooked the `*`. Then it depends what type of pointer that is,
> is the `mii_bus` unique or is it shared? If it is shared, then Rust
> would also need another lock there.
There can be up to 32 PHY drivers using one mii_bus, but in practice
you never get this density. Because there can be multiple PHYs this is
why the mii_bus has a lock, to serialise accesses from those PHYs to
the bus.
And MDIO is to some extend a generic bus. Not everything on an MII bus
is a PHY. Some Ethernet switches are MDIO devices, and they often take
up multiple addresses on the bus. But the locking is all the same.
PHYLIB core holds a reference to the MII bus, so the bus is not going
to go away before the PHY goes away. This is all standard Linux
bus/clients locking. It gets a bit messy with hot-plug, devices like
USB devices. The physical hardware can disappear at any time, but the
software representation stays around until it gets cleaned up in a
controlled manor. So a read/write on a bus can fail because its
physically gone, but you don't have to worry about the mii_bus
structure disappearing.
Andrew
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 11:36 ` FUJITA Tomonori
2023-10-21 12:13 ` Benno Lossin
@ 2023-10-21 15:41 ` Andrew Lunn
1 sibling, 0 replies; 65+ messages in thread
From: Andrew Lunn @ 2023-10-21 15:41 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: benno.lossin, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, greg
> Qemu supports a virtual PHY device?
Not really. I wish it did. Some MAC emulators have very minimal PHY
driver emulation, but they don't make use of PHYLIB.
Having a real emulated PHY would be nice, it would make testing things
like Energy Efficient Ethernet much simpler.
Andrew
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 13:05 ` Benno Lossin
2023-10-21 13:31 ` FUJITA Tomonori
@ 2023-10-21 15:57 ` Andrew Lunn
2023-10-21 16:31 ` Benno Lossin
1 sibling, 1 reply; 65+ messages in thread
From: Andrew Lunn @ 2023-10-21 15:57 UTC (permalink / raw)
To: Benno Lossin
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, greg
> I see, what exactly is the problem with that? In other words:
> why does PHYLIB need `phy_driver` to stay at the same address?
Again, pretty standard kernel behaviour. The core keeps a linked list
of drivers which have been registered with it. So when the driver
loads, it calls phy_driver_register() and the core adds the passed
structure to a linked list of drivers. Sometime later, the bus is
enumerated and devices found. The core will read a couple of registers
which contain the manufactures ID, model and revision. The linked list
of drivers is walked and a match is performed on the IDs. When a match
is found, phydev->drv is set to the driver structure. Calls into the
driver are then performed through this pointer.
A typically C driver has statically initialised driver structures
which are placed in the data section, or better still the rodata
section. They are not going anywhere until the driver is unloaded. So
there is no problem keeping them on a linked list. Dynamically
creating them is unusual. They are just structures of pointers to
functions, everything is known at link time.
Andrew
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 15:57 ` Andrew Lunn
@ 2023-10-21 16:31 ` Benno Lossin
0 siblings, 0 replies; 65+ messages in thread
From: Benno Lossin @ 2023-10-21 16:31 UTC (permalink / raw)
To: Andrew Lunn
Cc: FUJITA Tomonori, netdev, rust-for-linux, miguel.ojeda.sandonis,
tmgross, boqun.feng, wedsonaf, greg
On 21.10.23 17:57, Andrew Lunn wrote:
>> I see, what exactly is the problem with that? In other words:
>> why does PHYLIB need `phy_driver` to stay at the same address?
>
> Again, pretty standard kernel behaviour. The core keeps a linked list
> of drivers which have been registered with it. So when the driver
> loads, it calls phy_driver_register() and the core adds the passed
> structure to a linked list of drivers. Sometime later, the bus is
> enumerated and devices found. The core will read a couple of registers
> which contain the manufactures ID, model and revision. The linked list
> of drivers is walked and a match is performed on the IDs. When a match
> is found, phydev->drv is set to the driver structure. Calls into the
> driver are then performed through this pointer.
We have several examples of abstractions over things that embed linked
lists upstream already (e.g. `mutex`) and have developed a special API
that handles them very well. This API ensures that the values cannot be
moved (and if one tries to move it, the compiler errors). In this case
I was not aware of the requirement -- and it was also not noted in any
SAFETY comment (e.g. on `phy_drivers_register`).
> A typically C driver has statically initialised driver structures
> which are placed in the data section, or better still the rodata
> section. They are not going anywhere until the driver is unloaded. So
> there is no problem keeping them on a linked list. Dynamically
> creating them is unusual. They are just structures of pointers to
> functions, everything is known at link time.
In the ideal case I would just like to store them inside of the
`Module` struct (which is placed in the data section). However,
that requires Wedson's patch I linked in this thread.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 13:35 ` Benno Lossin
@ 2023-10-21 21:45 ` FUJITA Tomonori
2023-10-23 6:35 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-21 21:45 UTC (permalink / raw)
To: benno.lossin
Cc: fujita.tomonori, netdev, rust-for-linux, andrew,
miguel.ojeda.sandonis, tmgross, boqun.feng, wedsonaf, greg
On Sat, 21 Oct 2023 13:35:57 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
>> Currently, it needs &'static DriverVTable
>> array so it works.
>
> That is actually also incorrect. As the C side is going to modify
> the `DriverVTable`, you should actually use `&'static mut DriverVTable`.
> But since it is not allowed to be moved you have to use
> `Pin<&'static mut DriverVTable>`.
I updated Registration::register(). Needs to add comments on requirement?
impl Registration {
/// Registers a PHY driver.
pub fn register(
module: &'static crate::ThisModule,
drivers: Pin<&'static mut [DriverVTable]>,
) -> Result<Self> {
// SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
// are initialized properly. So an FFI call with a valid pointer.
to_result(unsafe {
bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
})?;
// INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
Ok(Registration { drivers })
}
}
>> The C side uses static allocation too. If someone asks for, we could
>> loosen the restriction with a complicated implentation. But I doubt
>> that someone would ask for such.
>
> With Wedson's patch you also would be using the static allocation
> from `module!`. What my problem is, is that you are using a `static mut`
> which is `unsafe` and you do not actually have to use it (with
> Wedson's patch of course).
Like your vtable patch, I improve the code when something useful is
available.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-21 12:05 ` Miguel Ojeda
@ 2023-10-22 6:30 ` FUJITA Tomonori
2023-10-23 8:58 ` Andreas Hindborg (Samsung)
1 sibling, 0 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-22 6:30 UTC (permalink / raw)
To: miguel.ojeda.sandonis
Cc: fujita.tomonori, nmi, netdev, rust-for-linux, andrew, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg, ojeda
On Sat, 21 Oct 2023 14:05:53 +0200
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:
> On Sat, Oct 21, 2023 at 5:51 AM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
>>
>> Hmm, this works for me.
>
> Andreas was probably using `O=`, but you were not.
>
> At least that is what I guessed yesterday and what the suggestion I gave fixes.
I see, thanks.
> This is also why sending unfinished work by someone else is not the
> best idea. I would also have marked that patch as RFC and put it at
> the end perhaps, to make it clearer.
>
> By the way, the patch is missing your SoB. I would recommend using
> `--signoff` in Git and b4's `am`, `cherry-pick` etc.
Incorporated the Makefile fix and added my Signed-off-by. You would
like to keep WIP in the subject?
=
From ff5b567a8b4e4a48f8a0c7f1ac71f8ef2c2ed226 Mon Sep 17 00:00:00 2001
From: Miguel Ojeda <ojeda@kernel.org>
Date: Fri, 13 Oct 2023 12:37:58 +0200
Subject: [PATCH] WIP rust: add second `bindgen` pass for enum exhaustiveness
checking
error[E0005]: refutable pattern in function argument
--> rust/bindings/bindings_enum_check.rs:29:6
|
29 | (phy_state::PHY_DOWN
| ______^
30 | | | phy_state::PHY_READY
31 | | | phy_state::PHY_HALTED
32 | | | phy_state::PHY_ERROR
... |
35 | | | phy_state::PHY_NOLINK
36 | | | phy_state::PHY_CABLETEST): phy_state,
| |______________________________^ pattern `phy_state::PHY_NEW` not covered
|
note: `phy_state` defined here
--> rust/bindings/bindings_generated_enum_check.rs:60739:10
|
60739 | pub enum phy_state {
| ^^^^^^^^^
...
60745 | PHY_NEW = 5,
| ------- not covered
= note: the matched value is of type `phy_state`
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
rust/.gitignore | 1 +
rust/Makefile | 14 +++++++++++
rust/bindings/bindings_enum_check.rs | 36 ++++++++++++++++++++++++++++
3 files changed, 51 insertions(+)
create mode 100644 rust/bindings/bindings_enum_check.rs
diff --git a/rust/.gitignore b/rust/.gitignore
index d3829ffab80b..1a76ad0d6603 100644
--- a/rust/.gitignore
+++ b/rust/.gitignore
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
bindings_generated.rs
+bindings_generated_enum_check.rs
bindings_helpers_generated.rs
doctests_kernel_generated.rs
doctests_kernel_generated_kunit.c
diff --git a/rust/Makefile b/rust/Makefile
index a27f35f924ec..b37842120b74 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -15,6 +15,7 @@ always-$(CONFIG_RUST) += libmacros.so
no-clean-files += libmacros.so
always-$(CONFIG_RUST) += bindings/bindings_generated.rs bindings/bindings_helpers_generated.rs
+always-$(CONFIG_RUST) += bindings/bindings_generated_enum_check.rs
obj-$(CONFIG_RUST) += alloc.o bindings.o kernel.o
always-$(CONFIG_RUST) += exports_alloc_generated.h exports_bindings_generated.h \
exports_kernel_generated.h
@@ -341,6 +342,19 @@ $(obj)/bindings/bindings_generated.rs: $(src)/bindings/bindings_helper.h \
$(src)/bindgen_parameters FORCE
$(call if_changed_dep,bindgen)
+$(obj)/bindings/bindings_generated_enum_check.rs: private bindgen_target_flags = \
+ $(shell grep -v '^#\|^$$' $(srctree)/$(src)/bindgen_parameters) \
+ --default-enum-style rust
+$(obj)/bindings/bindings_generated_enum_check.rs: private bindgen_target_extra = ; \
+ OBJTREE=$(abspath $(objtree)) $(RUSTC_OR_CLIPPY) $(rust_flags) $(rustc_target_flags) \
+ --crate-type rlib -L$(objtree)/$(obj) \
+ --emit=dep-info=$(obj)/bindings/.bindings_enum_check.rs.d \
+ --emit=metadata=$(obj)/bindings/libbindings_enum_check.rmeta \
+ --crate-name enum_check $(srctree)/$(src)/bindings/bindings_enum_check.rs
+$(obj)/bindings/bindings_generated_enum_check.rs: $(src)/bindings/bindings_helper.h \
+ $(src)/bindings/bindings_enum_check.rs $(src)/bindgen_parameters FORCE
+ $(call if_changed_dep,bindgen)
+
$(obj)/uapi/uapi_generated.rs: private bindgen_target_flags = \
$(shell grep -Ev '^#|^$$' $(srctree)/$(src)/bindgen_parameters)
$(obj)/uapi/uapi_generated.rs: $(src)/uapi/uapi_helper.h \
diff --git a/rust/bindings/bindings_enum_check.rs b/rust/bindings/bindings_enum_check.rs
new file mode 100644
index 000000000000..eef7e9ca3c54
--- /dev/null
+++ b/rust/bindings/bindings_enum_check.rs
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Bindings' enum exhaustiveness check.
+//!
+//! Eventually, this should be replaced by a safe version of `--rustified-enum`, see
+//! https://github.com/rust-lang/rust-bindgen/issues/2646.
+
+#![no_std]
+#![allow(
+ clippy::all,
+ dead_code,
+ missing_docs,
+ non_camel_case_types,
+ non_upper_case_globals,
+ non_snake_case,
+ improper_ctypes,
+ unreachable_pub,
+ unsafe_op_in_unsafe_fn
+)]
+
+include!(concat!(
+ env!("OBJTREE"),
+ "/rust/bindings/bindings_generated_enum_check.rs"
+));
+
+fn check_phy_state(
+ (phy_state::PHY_DOWN
+ | phy_state::PHY_READY
+ | phy_state::PHY_HALTED
+ | phy_state::PHY_ERROR
+ | phy_state::PHY_UP
+ | phy_state::PHY_RUNNING
+ | phy_state::PHY_NOLINK
+ | phy_state::PHY_CABLETEST): phy_state,
+) {
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 12:47 ` Miguel Ojeda
@ 2023-10-22 9:47 ` FUJITA Tomonori
2023-10-22 11:37 ` Miguel Ojeda
0 siblings, 1 reply; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-22 9:47 UTC (permalink / raw)
To: miguel.ojeda.sandonis
Cc: andrew, benno.lossin, fujita.tomonori, netdev, rust-for-linux,
tmgross, boqun.feng, wedsonaf, greg
On Sat, 21 Oct 2023 14:47:04 +0200
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:
> On Fri, Oct 20, 2023 at 8:42 PM Andrew Lunn <andrew@lunn.ch> wrote:
>>
>> We don't want to hide phy_device too much, since at the moment, the
>> abstraction is very minimal. Anybody writing a driver is going to need
>> a good understanding of the C code in order to find the helpers they
>> need, and then add them to the abstraction. So i would say we need to
>> explain the relationship between the C structure and the Rust
>> structure, to aid developers.
>
> I don't see how exposing `phy_device` in the documentation (note: not
> the implementation) helps with that. If someone has to add things to
> the abstraction, then at that point they need to be reading the
> implementation of the abstraction, and thus they should read the
> comments.
>
> That is why the helpers should in general not be mentioned in the
> documentation, i.e. a Rust API user should not care / need to know
> about them.
>
> If we mix things up in the docs, then it actually becomes harder later
> on to properly split it; and in the Rust side we want to maintain this
> "API documentation" vs. "implementation comments" split. Thus it is
> important to do it right in the first examples we will have in-tree.
>
> And if an API is not abstracted yet, it should not be documented. APIs
> and their docs should be added together, in the same patch, wherever
> possible. Of course, implementation comments are different, and
> possibly a designer of an abstraction may establish some rules or
> guidelines for future APIs added -- that is fine, but if the user does
> not need to know, it should not be in the docs, even if it is added
> early.
>
> Regarding this, part of the `phy` module documentation (i.e. the three
> paragraphs) in this patch currently sounds more like an implementation
> comment to me. It should probably be rewritten/split properly in docs
> vs. comments.
Agreed that the first three paragraphs at the top of the file are
implementation comments. Are there any other comments in the file,
which look implementation comments to you? To me, the rest look the
docs for Rust API users.
I'm not sure that a comment on the relationship between C and Rust
structures like "Wraps the kernel's `struct phy_driver`" is API docs
but the in-tree files like mutex.rs have the similar so I assume it's
fine.
Where the implementation comments are supposed to be placed?
Documentation/networking?
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-22 9:47 ` FUJITA Tomonori
@ 2023-10-22 11:37 ` Miguel Ojeda
2023-10-22 15:34 ` Andrew Lunn
0 siblings, 1 reply; 65+ messages in thread
From: Miguel Ojeda @ 2023-10-22 11:37 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: andrew, benno.lossin, netdev, rust-for-linux, tmgross, boqun.feng,
wedsonaf, greg
On Sun, Oct 22, 2023 at 11:47 AM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> Agreed that the first three paragraphs at the top of the file are
> implementation comments. Are there any other comments in the file,
> which look implementation comments to you? To me, the rest look the
> docs for Rust API users.
I think some should be improved with that in mind, yeah. For instance,
this one seems good to me:
/// An instance of a PHY driver.
But this one is not:
/// Creates the kernel's `phy_driver` instance.
It is especially bad because the first line of the docs is the "short
description" used for lists by `rustdoc`.
For similar reasons, this one is bad (and in this case it is the only line!):
/// Corresponds to the kernel's `enum phy_state`.
That line could be part of the documentation if you think it is
helpful for a reader as a practical note explaining what it is
supposed to map in the C side. But it should really not be the very
first line / short description.
Instead, the documentation should answer the question "What is this?".
And the answer should be something like "The state of the PHY ......"
as the short description. Then ideally a longer explanation of why it
is needed, how it is intended to be used, what this maps to in the C
side (if relevant), anything else that the user may need to know about
it, particular subtleties if any, examples if relevant, etc.
> I'm not sure that a comment on the relationship between C and Rust
> structures like "Wraps the kernel's `struct phy_driver`" is API docs
> but the in-tree files like mutex.rs have the similar so I assume it's
> fine.
Yes, documenting that something wraps/relies on/maps a particular C
functionality is something we do for clarity and practicality (we also
link the related C headers). This is, I assume, the kind of clarity
Andrew was asking for, i.e. to be practical and let the user know what
they are dealing with on the C side, especially early on.
But if some detail is not needed to use the API, then we should avoid
writing it in the documentation. And if it is needed, but it can be
written in a way that does not depend/reference the C side, then it
should be.
For instance, as you can see from the `mutex.rs` you mention, the
short description does not mention the C side -- it does so
afterwards, and then it goes onto explaining why it is needed, how it
is used (with examples), and so on. The fact that it exposes the C
`struct mutex` is there, because it adds value ("oh, ok, so this is
what I would use if I wanted the C mutex"), but that bit (and the
rest) is not really about explaining how `Mutex` is implemented:
/// A mutual exclusion primitive.
///
/// Exposes the kernel's [`struct mutex`]. When multiple threads
attempt to lock the same mutex,
/// only one at a time is allowed to progress, the others will
block (sleep) until the mutex is
/// unlocked, at which point another thread will be allowed to
wake up and make progress.
///
/// Since it may block, [`Mutex`] needs to be used with care in
atomic contexts.
///
/// Instances of [`Mutex`] need a lock class and to be pinned. The
recommended way to create such
/// instances is with the [`pin_init`](crate::pin_init) and
[`new_mutex`] macros.
///
/// # Examples
///
/// The following example shows how to declare, allocate and
initialise a struct (`Example`) that
/// contains an inner struct (`Inner`) that is protected by a mutex.
...
/// The following example shows how to use interior mutability to
modify the contents of a struct
/// protected by a mutex despite only having a shared reference:
...
`Mutex`'s docs are, in fact, a good a good example of how to write docs!
> Where the implementation comments are supposed to be placed?
> Documentation/networking?
No, they would be normal `//` comments and they should be as close to
the relevant code as possible -- please see
https://docs.kernel.org/rust/coding-guidelines.html#comments. They can
still be read in the generated docs via the "source" buttons, so they
will be there for those reading the actual implementation in the
browser.
Instead, `Documentation/` is detached from the actual code. For Rust
code, we hope to use only those for out-of-line information that is
not related to any particular API.
For instance, the "coding guidelines" document I just linked. Or
end-user / distributor documentation. Or, as another example, Wedson
at some point considered adding some high-level design documents, and
if those would not fit any particular API or if they are not intended
for users of the API, they could perhaps go into `Doc/`. Or perhaps
Boqun's idea of having a reviewers guide, etc.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-22 11:37 ` Miguel Ojeda
@ 2023-10-22 15:34 ` Andrew Lunn
2023-10-24 1:37 ` FUJITA Tomonori
2023-10-24 8:48 ` Miguel Ojeda
0 siblings, 2 replies; 65+ messages in thread
From: Andrew Lunn @ 2023-10-22 15:34 UTC (permalink / raw)
To: Miguel Ojeda
Cc: FUJITA Tomonori, benno.lossin, netdev, rust-for-linux, tmgross,
boqun.feng, wedsonaf, greg
On Sun, Oct 22, 2023 at 01:37:33PM +0200, Miguel Ojeda wrote:
> On Sun, Oct 22, 2023 at 11:47 AM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
> >
> > Agreed that the first three paragraphs at the top of the file are
> > implementation comments. Are there any other comments in the file,
> > which look implementation comments to you? To me, the rest look the
> > docs for Rust API users.
>
> I think some should be improved with that in mind, yeah. For instance,
> this one seems good to me:
>
> /// An instance of a PHY driver.
>
> But this one is not:
>
> /// Creates the kernel's `phy_driver` instance.
>
> It is especially bad because the first line of the docs is the "short
> description" used for lists by `rustdoc`.
>
> For similar reasons, this one is bad (and in this case it is the only line!):
>
> /// Corresponds to the kernel's `enum phy_state`.
>
> That line could be part of the documentation if you think it is
> helpful for a reader as a practical note explaining what it is
> supposed to map in the C side. But it should really not be the very
> first line / short description.
>
> Instead, the documentation should answer the question "What is this?".
> And the answer should be something like "The state of the PHY ......"
Its the state of the state machine, not the state of the PHY. It is
already documented in kernel doc, so we don't really want to duplicate
it. So maybe just cross reference to the kdoc:
https://docs.kernel.org/networking/kapi.html#c.phy_state
> Yes, documenting that something wraps/relies on/maps a particular C
> functionality is something we do for clarity and practicality (we also
> link the related C headers). This is, I assume, the kind of clarity
> Andrew was asking for, i.e. to be practical and let the user know what
> they are dealing with on the C side, especially early on.
I don't think 'early on' is relevant. In the kernel, you pretty much
always need the bigger picture, how a pieces of the puzzle fits in
with what is above it and what is below it. Sometimes you need to
extend what is above and below. Or a reviewer will tell you to move
code into the core, so others can share it, etc.
Andrew
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-21 21:45 ` FUJITA Tomonori
@ 2023-10-23 6:35 ` Benno Lossin
2023-10-23 6:37 ` Benno Lossin
0 siblings, 1 reply; 65+ messages in thread
From: Benno Lossin @ 2023-10-23 6:35 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 21.10.23 23:45, FUJITA Tomonori wrote:
> On Sat, 21 Oct 2023 13:35:57 +0000
> Benno Lossin <benno.lossin@proton.me> wrote:
>
>>> Currently, it needs &'static DriverVTable
>>> array so it works.
>>
>> That is actually also incorrect. As the C side is going to modify
>> the `DriverVTable`, you should actually use `&'static mut DriverVTable`.
>> But since it is not allowed to be moved you have to use
>> `Pin<&'static mut DriverVTable>`.
>
> I updated Registration::register(). Needs to add comments on requirement?
>
> impl Registration {
> /// Registers a PHY driver.
> pub fn register(
> module: &'static crate::ThisModule,
> drivers: Pin<&'static mut [DriverVTable]>,
> ) -> Result<Self> {
> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
> // are initialized properly. So an FFI call with a valid pointer.
This SAFETY comment needs to mention that `drivers[0].0.get()` are
pinned and will not change address.
> to_result(unsafe {
> bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
> })?;
> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
> Ok(Registration { drivers })
> }
> }
Otherwise this looks good.
>
>
>>> The C side uses static allocation too. If someone asks for, we could
>>> loosen the restriction with a complicated implentation. But I doubt
>>> that someone would ask for such.
>>
>> With Wedson's patch you also would be using the static allocation
>> from `module!`. What my problem is, is that you are using a `static mut`
>> which is `unsafe` and you do not actually have to use it (with
>> Wedson's patch of course).
>
> Like your vtable patch, I improve the code when something useful is
> available.
Sure. If you have the time though, it would be helpful to know
if the patch actually fixes the issue. I am pretty sure it will,
but you never know unless you try.
--
Cheers,
Benno
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-23 6:35 ` Benno Lossin
@ 2023-10-23 6:37 ` Benno Lossin
0 siblings, 0 replies; 65+ messages in thread
From: Benno Lossin @ 2023-10-23 6:37 UTC (permalink / raw)
To: FUJITA Tomonori
Cc: netdev, rust-for-linux, andrew, miguel.ojeda.sandonis, tmgross,
boqun.feng, wedsonaf, greg
On 23.10.23 08:35, Benno Lossin wrote:
> On 21.10.23 23:45, FUJITA Tomonori wrote:
>> On Sat, 21 Oct 2023 13:35:57 +0000
>> Benno Lossin <benno.lossin@proton.me> wrote:
>>
>>>> Currently, it needs &'static DriverVTable
>>>> array so it works.
>>>
>>> That is actually also incorrect. As the C side is going to modify
>>> the `DriverVTable`, you should actually use `&'static mut DriverVTable`.
>>> But since it is not allowed to be moved you have to use
>>> `Pin<&'static mut DriverVTable>`.
>>
>> I updated Registration::register(). Needs to add comments on requirement?
>>
>> impl Registration {
>> /// Registers a PHY driver.
>> pub fn register(
>> module: &'static crate::ThisModule,
>> drivers: Pin<&'static mut [DriverVTable]>,
>> ) -> Result<Self> {
>> // SAFETY: The type invariants of [`DriverVTable`] ensure that all elements of the `drivers` slice
>> // are initialized properly. So an FFI call with a valid pointer.
>
> This SAFETY comment needs to mention that `drivers[0].0.get()` are
Sorry, I meant `drivers` instead of `drivers[0].0.get()`
--
Cheers,
Benno
> pinned and will not change address.
>
>> to_result(unsafe {
>> bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0)
>> })?;
>> // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`.
>> Ok(Registration { drivers })
>> }
>> }
>
> Otherwise this looks good.
>
>>
>>
>>>> The C side uses static allocation too. If someone asks for, we could
>>>> loosen the restriction with a complicated implentation. But I doubt
>>>> that someone would ask for such.
>>>
>>> With Wedson's patch you also would be using the static allocation
>>> from `module!`. What my problem is, is that you are using a `static mut`
>>> which is `unsafe` and you do not actually have to use it (with
>>> Wedson's patch of course).
>>
>> Like your vtable patch, I improve the code when something useful is
>> available.
>
> Sure. If you have the time though, it would be helpful to know
> if the patch actually fixes the issue. I am pretty sure it will,
> but you never know unless you try.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-20 12:34 ` Miguel Ojeda
2023-10-20 12:35 ` Miguel Ojeda
@ 2023-10-23 8:57 ` Andreas Hindborg (Samsung)
1 sibling, 0 replies; 65+ messages in thread
From: Andreas Hindborg (Samsung) @ 2023-10-23 8:57 UTC (permalink / raw)
To: Miguel Ojeda
Cc: FUJITA Tomonori, netdev, rust-for-linux, andrew, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg, Miguel Ojeda
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> writes:
> On Fri, Oct 20, 2023 at 1:42 PM Andreas Hindborg (Samsung)
> <nmi@metaspace.dk> wrote:
>>
>> This patch does not build for me. Do I need to do something to make it
>> work?
>
> Please change:
>
> - --crate-name enum_check $(obj)/bindings/bindings_enum_check.rs
> + --crate-name enum_check
> $(srctree)/$(src)/bindings/bindings_enum_check.rs
This works, thanks 👍
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking
2023-10-21 12:05 ` Miguel Ojeda
2023-10-22 6:30 ` FUJITA Tomonori
@ 2023-10-23 8:58 ` Andreas Hindborg (Samsung)
1 sibling, 0 replies; 65+ messages in thread
From: Andreas Hindborg (Samsung) @ 2023-10-23 8:58 UTC (permalink / raw)
To: Miguel Ojeda
Cc: FUJITA Tomonori, netdev, rust-for-linux, andrew, tmgross,
boqun.feng, wedsonaf, benno.lossin, greg, ojeda
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> writes:
> On Sat, Oct 21, 2023 at 5:51 AM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
>>
>> Hmm, this works for me.
>
> Andreas was probably using `O=`, but you were not.
>
> At least that is what I guessed yesterday and what the suggestion I gave fixes.
Yes, out of tree build 👍
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-22 15:34 ` Andrew Lunn
@ 2023-10-24 1:37 ` FUJITA Tomonori
2023-10-24 8:48 ` Miguel Ojeda
1 sibling, 0 replies; 65+ messages in thread
From: FUJITA Tomonori @ 2023-10-24 1:37 UTC (permalink / raw)
To: andrew
Cc: miguel.ojeda.sandonis, fujita.tomonori, benno.lossin, netdev,
rust-for-linux, tmgross, boqun.feng, wedsonaf, greg
On Sun, 22 Oct 2023 17:34:04 +0200
Andrew Lunn <andrew@lunn.ch> wrote:
> On Sun, Oct 22, 2023 at 01:37:33PM +0200, Miguel Ojeda wrote:
>> On Sun, Oct 22, 2023 at 11:47 AM FUJITA Tomonori
>> <fujita.tomonori@gmail.com> wrote:
>> >
>> > Agreed that the first three paragraphs at the top of the file are
>> > implementation comments. Are there any other comments in the file,
>> > which look implementation comments to you? To me, the rest look the
>> > docs for Rust API users.
>>
>> I think some should be improved with that in mind, yeah. For instance,
>> this one seems good to me:
>>
>> /// An instance of a PHY driver.
>>
>> But this one is not:
>>
>> /// Creates the kernel's `phy_driver` instance.
>>
>> It is especially bad because the first line of the docs is the "short
>> description" used for lists by `rustdoc`.
>>
>> For similar reasons, this one is bad (and in this case it is the only line!):
>>
>> /// Corresponds to the kernel's `enum phy_state`.
>>
>> That line could be part of the documentation if you think it is
>> helpful for a reader as a practical note explaining what it is
>> supposed to map in the C side. But it should really not be the very
>> first line / short description.
>>
>> Instead, the documentation should answer the question "What is this?".
>> And the answer should be something like "The state of the PHY ......"
>
> Its the state of the state machine, not the state of the PHY. It is
> already documented in kernel doc, so we don't really want to duplicate
> it. So maybe just cross reference to the kdoc:
>
> https://docs.kernel.org/networking/kapi.html#c.phy_state
I added links to the kdoc like:
/// Corresponds to the kernel's [`enum phy_state`](https://docs.kernel.org/networking/kapi.html#c.phy_state).
But the first line needs to a short description so I copy the C
description:
/// PHY state machine states.
I revised all the comments.
^ permalink raw reply [flat|nested] 65+ messages in thread
* Re: [PATCH net-next v5 1/5] rust: core abstractions for network PHY drivers
2023-10-22 15:34 ` Andrew Lunn
2023-10-24 1:37 ` FUJITA Tomonori
@ 2023-10-24 8:48 ` Miguel Ojeda
1 sibling, 0 replies; 65+ messages in thread
From: Miguel Ojeda @ 2023-10-24 8:48 UTC (permalink / raw)
To: Andrew Lunn
Cc: FUJITA Tomonori, benno.lossin, netdev, rust-for-linux, tmgross,
boqun.feng, wedsonaf, greg
On Sun, Oct 22, 2023 at 5:34 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> On Sun, Oct 22, 2023 at 01:37:33PM +0200, Miguel Ojeda wrote:
> >
> > Instead, the documentation should answer the question "What is this?".
> > And the answer should be something like "The state of the PHY ......"
>
> Its the state of the state machine, not the state of the PHY. It is
I didn't say it was the state of the PHY -- please note the dots above.
> already documented in kernel doc, so we don't really want to duplicate
> it. So maybe just cross reference to the kdoc:
>
> https://docs.kernel.org/networking/kapi.html#c.phy_state
Yes, that can be worth it for simple 1:1 cases like the `enum`
(assuming they are properly documented in the C side), but we still
want a suitable short description (e.g. "PHY state machine states"),
like Tomonori did in the version he just sent (v6).
I wondered about the docs of each variant, too, but those should be OK
too, because `rustdoc` does not create an individual page for them, so
one can always see the link to the C docs at the top from the `enum`
description.
> > Yes, documenting that something wraps/relies on/maps a particular C
> > functionality is something we do for clarity and practicality (we also
> > link the related C headers). This is, I assume, the kind of clarity
> > Andrew was asking for, i.e. to be practical and let the user know what
> > they are dealing with on the C side, especially early on.
>
> I don't think 'early on' is relevant. In the kernel, you pretty much
> always need the bigger picture, how a pieces of the puzzle fits in
> with what is above it and what is below it. Sometimes you need to
> extend what is above and below. Or a reviewer will tell you to move
> code into the core, so others can share it, etc.
"Early on" in my reply is referring to what you said earlier, i.e.
that initially abstractions are minimal.
In any case, yes, using complex systems typically requires knowing a
bit of what is going on in different parts, but that does not mean we
cannot make self-contained documentation as much as reasonably
possible. We want that using a particular Rust abstraction does not
require reading its source code or the code in the C side.
In the example that you mention, if the reviewer wants to share code,
then it should be extracted into a new Rust abstraction (and possibly
the C core depending on what it is, of course) and using it from the
driver, but also documenting the Rust abstraction.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 65+ messages in thread
end of thread, other threads:[~2023-10-24 8:48 UTC | newest]
Thread overview: 65+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-10-17 11:30 [PATCH net-next v5 0/5] Rust abstractions for network PHY drivers FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 1/5] rust: core " FUJITA Tomonori
2023-10-18 15:07 ` Benno Lossin
2023-10-19 0:24 ` FUJITA Tomonori
2023-10-19 13:45 ` Benno Lossin
2023-10-19 14:42 ` FUJITA Tomonori
2023-10-19 15:20 ` Benno Lossin
2023-10-19 15:32 ` FUJITA Tomonori
2023-10-19 16:37 ` Benno Lossin
2023-10-19 21:51 ` FUJITA Tomonori
2023-10-21 7:21 ` Benno Lossin
2023-10-20 0:34 ` FUJITA Tomonori
2023-10-20 12:54 ` FUJITA Tomonori
2023-10-21 7:25 ` Benno Lossin
2023-10-21 7:30 ` FUJITA Tomonori
2023-10-21 8:37 ` Benno Lossin
2023-10-21 10:27 ` FUJITA Tomonori
2023-10-21 11:21 ` Benno Lossin
2023-10-21 11:36 ` FUJITA Tomonori
2023-10-21 12:13 ` Benno Lossin
2023-10-21 12:38 ` FUJITA Tomonori
2023-10-21 12:50 ` Benno Lossin
2023-10-21 13:00 ` FUJITA Tomonori
2023-10-21 13:05 ` Benno Lossin
2023-10-21 13:31 ` FUJITA Tomonori
2023-10-21 13:35 ` Benno Lossin
2023-10-21 21:45 ` FUJITA Tomonori
2023-10-23 6:35 ` Benno Lossin
2023-10-23 6:37 ` Benno Lossin
2023-10-21 15:57 ` Andrew Lunn
2023-10-21 16:31 ` Benno Lossin
2023-10-21 15:41 ` Andrew Lunn
2023-10-20 18:42 ` Andrew Lunn
2023-10-21 4:44 ` FUJITA Tomonori
2023-10-21 7:36 ` Benno Lossin
2023-10-21 12:47 ` Miguel Ojeda
2023-10-22 9:47 ` FUJITA Tomonori
2023-10-22 11:37 ` Miguel Ojeda
2023-10-22 15:34 ` Andrew Lunn
2023-10-24 1:37 ` FUJITA Tomonori
2023-10-24 8:48 ` Miguel Ojeda
2023-10-18 20:27 ` Andrew Lunn
2023-10-19 0:41 ` FUJITA Tomonori
2023-10-19 13:57 ` Benno Lossin
2023-10-20 19:50 ` Andrew Lunn
2023-10-21 8:01 ` Benno Lossin
2023-10-21 15:35 ` Andrew Lunn
2023-10-20 17:26 ` Andreas Hindborg (Samsung)
2023-10-20 17:56 ` Benno Lossin
2023-10-20 19:59 ` Andrew Lunn
2023-10-20 20:30 ` Andreas Hindborg (Samsung)
2023-10-21 3:49 ` FUJITA Tomonori
2023-10-21 4:01 ` FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 2/5] rust: net::phy add module_phy_driver macro FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 3/5] WIP rust: add second `bindgen` pass for enum exhaustiveness checking FUJITA Tomonori
2023-10-20 11:37 ` Andreas Hindborg (Samsung)
2023-10-20 12:34 ` Miguel Ojeda
2023-10-20 12:35 ` Miguel Ojeda
2023-10-23 8:57 ` Andreas Hindborg (Samsung)
2023-10-21 3:51 ` FUJITA Tomonori
2023-10-21 12:05 ` Miguel Ojeda
2023-10-22 6:30 ` FUJITA Tomonori
2023-10-23 8:58 ` Andreas Hindborg (Samsung)
2023-10-17 11:30 ` [PATCH net-next v5 4/5] MAINTAINERS: add Rust PHY abstractions for ETHERNET PHY LIBRARY FUJITA Tomonori
2023-10-17 11:30 ` [PATCH net-next v5 5/5] net: phy: add Rust Asix PHY driver FUJITA Tomonori
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).