lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20231019.092436.1433321157817125498.fujita.tomonori@gmail.com>
Date: Thu, 19 Oct 2023 09:24:36 +0900 (JST)
From: FUJITA Tomonori <fujita.tomonori@...il.com>
To: benno.lossin@...ton.me
Cc: fujita.tomonori@...il.com, netdev@...r.kernel.org,
 rust-for-linux@...r.kernel.org, andrew@...n.ch,
 miguel.ojeda.sandonis@...il.com, tmgross@...ch.edu, boqun.feng@...il.com,
 wedsonaf@...il.com, greg@...ah.com
Subject: Re: [PATCH net-next v5 1/5] rust: core abstractions for network
 PHY drivers

On Wed, 18 Oct 2023 15:07:52 +0000
Benno Lossin <benno.lossin@...ton.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@...il.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()?

Powered by blists - more mailing lists