From: FUJITA Tomonori <fujita.tomonori@gmail.com>
To: benno.lossin@proton.me
Cc: fujita.tomonori@gmail.com, rust-for-linux@vger.kernel.org,
andrew@lunn.ch, tmgross@umich.edu,
miguel.ojeda.sandonis@gmail.com
Subject: Re: [RFC PATCH v2 1/3] rust: core abstractions for network PHY drivers
Date: Mon, 25 Sep 2023 19:24:33 +0900 (JST) [thread overview]
Message-ID: <20230925.192433.108741056500744806.fujita.tomonori@gmail.com> (raw)
In-Reply-To: <70dbdfd2-9e8d-6985-ae42-0d4f1a7dcef9@proton.me>
On Sun, 24 Sep 2023 13:19:21 +0000
Benno Lossin <benno.lossin@proton.me> wrote:
>> +/// Wraps the kernel's `struct phy_device`.
>> +#[repr(transparent)]
>> +pub struct Device(Opaque<bindings::phy_device>);
>
> This struct should have a type invariant saying that "`self.0` is
> always in a valid state.".
Added.
>> +impl Device {
>> + /// Creates a new [`Device`] instance from a raw pointer.
>> + ///
>> + /// # Safety
>> + ///
>> + /// For the duration of the lifetime 'a, the pointer must be valid for writing and nobody else
>> + /// may read or write to the `phy_device` object.
>> + pub unsafe fn from_raw<'a>(ptr: *mut bindings::phy_device) -> &'a mut Self {
>> + unsafe { &mut *(ptr as *mut Self) }
>> + }
>> +
>> + /// Gets the id of the PHY.
>> + pub fn id(&mut self) -> u32 {
>> + let phydev = Opaque::get(&self.0);
>> + // SAFETY: This object is initialized by the `from_raw` constructor, so it's valid.
>
> These kinds of safety comments should be replaced by "`phydev` is
> pointing to a valid object by the type invariant of `Self`."
> Also for the other safety comments below.
Fixed all these comments.
>> + /// Reads a given C22 PHY register.
>> + pub fn read(&mut self, regnum: u16) -> Result<u16> {
>
> I have no knowledge of the driver and the device, but is
> it really fine to supply any 16 bit integer value here?
No, I assume that the C side returns an error in such case.
>> + let phydev = Opaque::get(&self.0);
>> + // SAFETY: This object is initialized by the `from_raw` constructor,
>> + // so an FFI call with a valid pointer.
>> + let ret = unsafe {
>> + bindings::mdiobus_read((*phydev).mdio.bus, (*phydev).mdio.addr, regnum as u32)
>> + };
>> + if ret < 0 {
>> + Err(Error::from_errno(ret))
>> + } else {
>> + Ok(ret as u16)
>> + }
>
> Not sure if this is nicer, but you could use
> `to_result(ret).map(|()| ret as u16)` instead.
I'm not sure either. I have no preference though.
>> +
>> + /// Writes a given C22 PHY register.
>> + pub fn write(&mut self, regnum: u16, val: u16) -> Result {
>> + let phydev = Opaque::get(&self.0);
>> + // SAFETY: This object is initialized by the `from_raw` constructor,
>> + // so an FFI call with a valid pointer.
>> + let ret = unsafe {
>> + bindings::mdiobus_write((*phydev).mdio.bus, (*phydev).mdio.addr, regnum as u32, val)
>> + };
>> + if ret < 0 {
>> + Err(Error::from_errno(ret))
>> + } else {
>> + Ok(())
>> + }
>
> Use `error::to_result` instead.
Fixed.
>> + }
>> +
>> + /// Reads a paged register.
>> + pub fn read_paged(&mut self, page: u16, regnum: u16) -> Result<u16> {
>> + let phydev = Opaque::get(&self.0);
>> + // SAFETY: This object is initialized by the `from_raw` constructor,
>> + // so an FFI call with a valid pointer.
>> + let ret = unsafe { bindings::phy_read_paged(phydev, page as i32, regnum as u32) };
>> + if ret < 0 {
>> + return 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 = Opaque::get(&self.0);
>> + // SAFETY: This object is initialized by the `from_raw` constructor,
>> + // 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 = Opaque::get(&self.0);
>> + // SAFETY: This object is initialized by the `from_raw` constructor,
>> + // so an FFI call with a valid pointer.
>> + let ret = unsafe { bindings::genphy_soft_reset(phydev) };
>> + if ret < 0 {
>> + Err(Error::from_errno(ret))
>> + } else {
>> + Ok(())
>> + }
>
> Use `error::to_result` instead.
Fixed.
>> + }
>> +
>> + /// Initializes the PHY.
>> + pub fn init_hw(&mut self) -> Result {
>> + let phydev = Opaque::get(&self.0);
>> + // SAFETY: This object is initialized by the `from_raw` constructor,
>> + // so an FFI call with a valid pointer.
>> + let ret = unsafe { bindings::phy_init_hw(phydev) };
>> + if ret != 0 {
>> + return Err(Error::from_errno(ret));
>> + } else {
>> + Ok(())
>> + }
>
> Use `error::to_result` instead. And below...
Fixed all.
>> +/// An adapter for the registration of a PHY driver.
>> +pub struct Adapter<T: Driver> {
>> + _p: PhantomData<T>,
>> +}
>
> I think this type can be private.
>
>> +
>> +impl<T: Driver> Adapter<T> {
>> + /// Creates a new `Adapter` instance.
>> + pub const fn new() -> Adapter<T> {
>> + Self { _p: PhantomData }
>> + }
>> +
>> + unsafe extern "C" fn soft_reset_callback(
>> + phydev: *mut bindings::phy_device,
>> + ) -> core::ffi::c_int {
>> + from_result(|| {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::soft_reset(dev)?;
>> + Ok(0)
>> + })
>> + }
>> +
>> + unsafe extern "C" fn get_features_callback(
>> + phydev: *mut bindings::phy_device,
>> + ) -> core::ffi::c_int {
>> + from_result(|| {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::get_features(dev)?;
>> + Ok(0)
>> + })
>> + }
>> +
>> + unsafe extern "C" fn suspend_callback(phydev: *mut bindings::phy_device) -> core::ffi::c_int {
>> + from_result(|| {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::suspend(dev)?;
>> + Ok(0)
>> + })
>> + }
>> +
>> + unsafe extern "C" fn resume_callback(phydev: *mut bindings::phy_device) -> core::ffi::c_int {
>> + from_result(|| {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::resume(dev)?;
>> + Ok(0)
>> + })
>> + }
>> +
>> + unsafe extern "C" fn config_aneg_callback(
>> + phydev: *mut bindings::phy_device,
>> + ) -> core::ffi::c_int {
>> + from_result(|| {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::config_aneg(dev)?;
>> + Ok(0)
>> + })
>> + }
>> +
>> + unsafe extern "C" fn read_status_callback(
>> + phydev: *mut bindings::phy_device,
>> + ) -> core::ffi::c_int {
>> + from_result(|| {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::read_status(dev)?;
>> + Ok(0)
>> + })
>> + }
>> +
>> + unsafe extern "C" fn match_phy_device_callback(
>> + phydev: *mut bindings::phy_device,
>> + ) -> core::ffi::c_int {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::match_phy_device(dev) as i32
>> + }
>> +
>> + unsafe extern "C" fn read_mmd_callback(
>> + phydev: *mut bindings::phy_device,
>> + devnum: i32,
>> + regnum: u16,
>> + ) -> i32 {
>> + from_result(|| {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + let ret = T::read_mmd(dev, devnum as u8, regnum)?;
>> + Ok(ret.into())
>> + })
>> + }
>> +
>> + unsafe extern "C" fn write_mmd_callback(
>> + phydev: *mut bindings::phy_device,
>> + devnum: i32,
>> + regnum: u16,
>> + val: u16,
>> + ) -> i32 {
>> + from_result(|| {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::write_mmd(dev, devnum as u8, regnum, val)?;
>> + Ok(0)
>> + })
>> + }
>> +
>> + unsafe extern "C" fn link_change_notify_callback(phydev: *mut bindings::phy_device) {
>> + // SAFETY: The C API guarantees that `phydev` is valid while this function is running.
>> + let dev = unsafe { Device::from_raw(phydev) };
>> + T::link_change_notify(dev);
>> + }
>> +
>> + /// Creates the kernel's `phy_driver` instance.
>> + ///
>> + /// This is used by [`module_phy_driver`] macro to create a static array of phy_driver`.
>> + pub const fn create_phy_driver() -> Opaque<bindings::phy_driver> {
>
> In order to make `Adapter` a private type, you have
> to make this function standalone (and generic).
> Or create a standalone function that just calls this.
done, nice.
>> +/// Declares a kernel module for PHYs drivers.
>> +///
>> +/// This creates a static array of `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.
>> +///
>> +/// # Examples
>> +///
>> +/// ```ignore
>> +///
>> +/// use kernel::net::phy::{self, DeviceId, Driver};
>> +/// use kernel::prelude::*;
>> +///
>> +/// 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>()
>> +/// ],
>> +/// type: RustAsixPhy,
>> +/// name: "rust_asix_phy",
>> +/// author: "Rust for Linux Contributors",
>> +/// description: "Rust Asix PHYs driver",
>> +/// license: "GPL",
>> +/// }
>> +/// ```
>> +#[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 $name:ident, [$($dev:expr),+]) => {
>> + ::kernel::macros::paste! {
>> + #[no_mangle]
>> + static [<__mod_mdio__ $name _device_table>]: [kernel::bindings::mdio_device_id; $crate::module_phy_driver!(@count_devices $($dev),+) + 1] =
>> + [ $(kernel::bindings::mdio_device_id{phy_id: $dev.id, phy_id_mask: $dev.mask_as_int()}),+, kernel::bindings::mdio_device_id {phy_id: 0, phy_id_mask: 0}];
>> + }
>> + };
>> +
>> + (drivers: [$($driver:ident),+], device_table: [$($dev:expr),+], type: $modname:ident, $($f:tt)*) => {
>> + struct Module<$modname> {
>
> Why do you use `$modname` as a generic parameter? Can't
> you just use `$modname` as the name of this struct?
I followed module_driver! macro and similr macros like
module_platform_driver! in rust branch; a device driver programmer can
define a struct for Module.
>> + _reg: kernel::net::phy::Registration,
>> + _p: core::marker::PhantomData<$modname>,
>> + }
>> +
>> + type ModuleType = Module<$modname>;
>> +
>> + $crate::prelude::module! {
>> + type: ModuleType,
>> + $($f)*
>> + }
>> +
>> + static mut DRIVERS: [kernel::types::Opaque<kernel::bindings::phy_driver>; $crate::module_phy_driver!(@count_devices $($driver),+)] = [
>> + $(kernel::net::phy::Adapter::<$driver>::create_phy_driver()),+,
>> + ];
>
> Not sure if a static is the best way to do this. You
> might also be able to just store this array inside
> of the `Module<$modname>` struct.
With that approach, we can't make the array static, right? I can't
figure out a way to handle lifetime of Registration and
Module<$modname>; Registration needs a pointer to the array for
its drop().
>> + impl kernel::Module for Module<$modname> {
>> + fn init(module: &'static ThisModule) -> Result<Self> {
>> + let mut reg: kernel::net::phy::Registration = kernel::net::phy::Registration::new(module);
>> + // SAFETY: static `DRIVERS` array is used only in the C side.
>
> This safety comment should address why you are allowed
> to access this mutable static here.
The Rust sides just passes static `DRIVERS` array to the C side, which
is used only in the C side with appropriate synchronization mechanism.
?
next prev parent reply other threads:[~2023-09-25 10:24 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-24 6:48 [RFC PATCH v2 0/3] Rust abstractions for network PHY drivers FUJITA Tomonori
2023-09-24 6:49 ` [RFC PATCH v2 1/3] rust: core " FUJITA Tomonori
2023-09-24 12:56 ` Wedson Almeida Filho
2023-09-24 13:39 ` Benno Lossin
2023-09-24 13:51 ` Wedson Almeida Filho
2023-09-25 11:30 ` FUJITA Tomonori
2023-09-25 13:14 ` Andrew Lunn
2023-09-24 15:42 ` Andrew Lunn
2023-09-25 1:13 ` FUJITA Tomonori
2023-09-25 13:25 ` Andrew Lunn
2023-09-25 6:47 ` Alice Ryhl
2023-09-26 1:19 ` FUJITA Tomonori
2023-09-26 2:50 ` Andrew Lunn
2023-09-24 13:19 ` Benno Lossin
2023-09-25 10:24 ` FUJITA Tomonori [this message]
2023-09-25 15:41 ` Miguel Ojeda
2023-09-26 13:46 ` FUJITA Tomonori
2023-09-27 10:49 ` Miguel Ojeda
2023-09-27 11:19 ` FUJITA Tomonori
2023-10-09 12:28 ` Miguel Ojeda
2023-09-24 17:00 ` Andrew Lunn
2023-09-24 18:03 ` Miguel Ojeda
2023-09-25 13:28 ` Andrew Lunn
2023-09-25 13:43 ` Andrew Lunn
2023-09-25 15:42 ` Miguel Ojeda
2023-09-25 16:53 ` Andrew Lunn
2023-09-25 17:26 ` Miguel Ojeda
2023-09-25 18:32 ` Andrew Lunn
2023-09-25 19:15 ` Miguel Ojeda
2023-09-26 6:05 ` Trevor Gross
2023-09-26 12:11 ` Andrew Lunn
2023-09-27 3:26 ` FUJITA Tomonori
2023-09-26 6:54 ` Trevor Gross
2023-09-27 3:39 ` FUJITA Tomonori
2023-09-27 12:21 ` Andrew Lunn
2023-09-24 6:49 ` [RFC PATCH v2 2/3] MAINTAINERS: add Rust PHY abstractions file to the ETHERNET PHY LIBRARY FUJITA Tomonori
2023-09-24 6:49 ` [RFC PATCH v2 3/3] net: phy: add Rust Asix PHY driver FUJITA Tomonori
2023-09-24 8:05 ` Miguel Ojeda
2023-09-24 9:38 ` FUJITA Tomonori
2023-09-24 10:10 ` Miguel Ojeda
2023-09-24 11:00 ` FUJITA Tomonori
2023-09-24 13:33 ` Benno Lossin
2023-09-25 2:31 ` FUJITA Tomonori
2023-09-26 6:20 ` Trevor Gross
2023-09-26 7:07 ` FUJITA Tomonori
[not found] ` <CALNs47uYnQC+AXbJuk8d5506D25SDhZ-ZKuhimFkZnYOhhdfCg@mail.gmail.com>
2023-09-26 12:36 ` Andrew Lunn
2023-09-27 1:18 ` FUJITA Tomonori
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230925.192433.108741056500744806.fujita.tomonori@gmail.com \
--to=fujita.tomonori@gmail.com \
--cc=andrew@lunn.ch \
--cc=benno.lossin@proton.me \
--cc=miguel.ojeda.sandonis@gmail.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).