All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] Rust abstractions for network device drivers
@ 2023-07-10  7:36 FUJITA Tomonori
  2023-07-10  7:36 ` [PATCH v2 1/5] rust: core " FUJITA Tomonori
                   ` (5 more replies)
  0 siblings, 6 replies; 24+ messages in thread
From: FUJITA Tomonori @ 2023-07-10  7:36 UTC (permalink / raw)
  To: rust-for-linux, netdev
  Cc: kuba, andrew, aliceryhl, miguel.ojeda.sandonis, benno.lossin

This patchset adds minimum Rust abstractions for network device
drivers and an example of a Rust network device driver, a simpler
version of drivers/net/dummy.c.

The major change is a way to drop an skb (1/5 patch); a driver needs
to explicitly call a function to drop a skb. The code to let a skb
go out of scope can't be compiled.

I dropped get_stats64 support patch that the current sample driver
doesn't use. Instead I added a patch to update the NETWORKING DRIVERS
entry in MAINTAINERS.

Changes since v1 [1]:
- a driver must explicitly call a function to drop a skb.
- simplify the code (thanks to Benno Lossin).
- update MAINTAINERS file.

[1] https://lwn.net/ml/netdev/20230613045326.3938283-1-fujita.tomonori@gmail.com/

FUJITA Tomonori (5):
  rust: core abstractions for network device drivers
  rust: add support for ethernet operations
  rust: add methods for configure net_device
  samples: rust: add dummy network driver
  MAINTAINERS: add Rust network abstractions files to the NETWORKING
    DRIVERS entry

 MAINTAINERS                     |   2 +
 rust/bindings/bindings_helper.h |   3 +
 rust/helpers.c                  |  23 ++
 rust/kernel/lib.rs              |   3 +
 rust/kernel/net.rs              |   5 +
 rust/kernel/net/dev.rs          | 598 ++++++++++++++++++++++++++++++++
 samples/rust/Kconfig            |  13 +
 samples/rust/Makefile           |   1 +
 samples/rust/rust_net_dummy.rs  |  75 ++++
 scripts/Makefile.build          |   2 +-
 10 files changed, 724 insertions(+), 1 deletion(-)
 create mode 100644 rust/kernel/net.rs
 create mode 100644 rust/kernel/net/dev.rs
 create mode 100644 samples/rust/rust_net_dummy.rs


base-commit: d2e3115d717197cb2bc020dd1f06b06538474ac3
-- 
2.34.1


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

* [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-07-10  7:36 [PATCH v2 0/5] Rust abstractions for network device drivers FUJITA Tomonori
@ 2023-07-10  7:36 ` FUJITA Tomonori
  2023-07-14 18:59   ` Benno Lossin
  2023-07-10  7:37 ` [PATCH v2 2/5] rust: add support for ethernet operations FUJITA Tomonori
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 24+ messages in thread
From: FUJITA Tomonori @ 2023-07-10  7:36 UTC (permalink / raw)
  To: rust-for-linux, netdev
  Cc: kuba, andrew, aliceryhl, miguel.ojeda.sandonis, benno.lossin

This patch adds very basic abstractions to implement network device
drivers, corresponds to the kernel's net_device and net_device_ops
structs with support for register_netdev/unregister_netdev functions.

allows the const_maybe_uninit_zeroed feature for
core::mem::MaybeUinit::<T>::zeroed() in const function.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 rust/bindings/bindings_helper.h |   2 +
 rust/helpers.c                  |  16 ++
 rust/kernel/lib.rs              |   3 +
 rust/kernel/net.rs              |   5 +
 rust/kernel/net/dev.rs          | 330 ++++++++++++++++++++++++++++++++
 5 files changed, 356 insertions(+)
 create mode 100644 rust/kernel/net.rs
 create mode 100644 rust/kernel/net/dev.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 3e601ce2548d..468bf606f174 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -7,6 +7,8 @@
  */
 
 #include <linux/errname.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
 #include <linux/slab.h>
 #include <linux/refcount.h>
 #include <linux/wait.h>
diff --git a/rust/helpers.c b/rust/helpers.c
index bb594da56137..70d50767ff4e 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -24,10 +24,26 @@
 #include <linux/errname.h>
 #include <linux/refcount.h>
 #include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <linux/sched/signal.h>
 #include <linux/wait.h>
 
+#ifdef CONFIG_NET
+void *rust_helper_netdev_priv(const struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);
+
+void rust_helper_skb_tx_timestamp(struct sk_buff *skb)
+{
+	skb_tx_timestamp(skb);
+}
+EXPORT_SYMBOL_GPL(rust_helper_skb_tx_timestamp);
+#endif
+
 __noreturn void rust_helper_BUG(void)
 {
 	BUG();
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 85b261209977..fc7d048d359d 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -13,6 +13,7 @@
 
 #![no_std]
 #![feature(allocator_api)]
+#![feature(const_maybe_uninit_zeroed)]
 #![feature(coerce_unsized)]
 #![feature(dispatch_from_dyn)]
 #![feature(new_uninit)]
@@ -34,6 +35,8 @@
 pub mod error;
 pub mod init;
 pub mod ioctl;
+#[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..28fe8f398463
--- /dev/null
+++ b/rust/kernel/net.rs
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Networking core.
+
+pub mod dev;
diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs
new file mode 100644
index 000000000000..fe20616668a9
--- /dev/null
+++ b/rust/kernel/net/dev.rs
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Network device.
+//!
+//! C headers: [`include/linux/etherdevice.h`](../../../../include/linux/etherdevice.h),
+//! [`include/linux/ethtool.h`](../../../../include/linux/ethtool.h),
+//! [`include/linux/netdevice.h`](../../../../include/linux/netdevice.h),
+//! [`include/linux/skbuff.h`](../../../../include/linux/skbuff.h),
+//! [`include/uapi/linux/if_link.h`](../../../../include/uapi/linux/if_link.h).
+
+use crate::{bindings, build_error, error::*, prelude::vtable, types::ForeignOwnable};
+use {core::ffi::c_void, core::marker::PhantomData};
+
+/// Corresponds to the kernel's `struct net_device`.
+///
+/// # Invariants
+///
+/// The `ptr` points to the contiguous memory for `struct net_device` and a pointer,
+/// which stores an address returned by `ForeignOwnable::into_foreign()`.
+pub struct Device<D: ForeignOwnable + Send + Sync> {
+    ptr: *mut bindings::net_device,
+    _p: PhantomData<D>,
+}
+
+impl<D: ForeignOwnable + Send + Sync> Device<D> {
+    /// Creates a new [`Device`] instance.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `ptr` must point to the contiguous memory
+    /// for `struct net_device` and a pointer, which stores an address returned
+    /// by `ForeignOwnable::into_foreign()`.
+    unsafe fn from_ptr(ptr: *mut bindings::net_device) -> Self {
+        // INVARIANT: The safety requirements ensure the invariant.
+        Self {
+            ptr,
+            _p: PhantomData,
+        }
+    }
+
+    /// Gets the private data of a device driver.
+    pub fn drv_priv_data(&self) -> D::Borrowed<'_> {
+        // SAFETY: The type invariants guarantee that self.ptr is valid and
+        // bindings::netdev_priv(self.ptr) returns a pointer that stores an address
+        // returned by `ForeignOwnable::into_foreign()`.
+        unsafe {
+            D::borrow(core::ptr::read(
+                bindings::netdev_priv(self.ptr) as *const *const c_void
+            ))
+        }
+    }
+}
+
+// SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used
+// from any thread. `struct net_device` stores a pointer to an object, which is `Sync`
+// so it's safe to sharing its pointer.
+unsafe impl<D: ForeignOwnable + Send + Sync> Send for Device<D> {}
+// SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used
+// from any thread. `struct net_device` stores a pointer to an object, which is `Sync`,
+// can be used from any thread too.
+unsafe impl<D: ForeignOwnable + Send + Sync> Sync for Device<D> {}
+
+/// Registration structure for a network device driver.
+///
+/// This allocates and owns a `struct net_device` object.
+/// Once the `net_device` object is registered via `register_netdev` function,
+/// the kernel calls various functions such as `struct net_device_ops` operations with
+/// the `net_device` object.
+///
+/// A driver must implement `struct net_device_ops` so the trait for it is tied.
+/// Other operations like `struct ethtool_ops` are optional.
+pub struct Registration<T: DeviceOperations> {
+    dev: Device<T>,
+    is_registered: bool,
+    _p: PhantomData<T>,
+}
+
+impl<T: DeviceOperations> Drop for Registration<T> {
+    fn drop(&mut self) {
+        // SAFETY: The type invariants of `Device` guarantee that `self.dev.ptr` is valid and
+        // bindings::netdev_priv(self.ptr) returns a pointer that stores an address
+        // returned by `ForeignOwnable::into_foreign()`.
+        unsafe {
+            let _ = T::from_foreign(core::ptr::read(
+                bindings::netdev_priv(self.dev.ptr) as *const *const c_void
+            ));
+        }
+        // SAFETY: The type invariants of `Device` guarantee that `self.dev.ptr` is valid.
+        unsafe {
+            if self.is_registered {
+                bindings::unregister_netdev(self.dev.ptr);
+            }
+            bindings::free_netdev(self.dev.ptr);
+        }
+    }
+}
+
+impl<T: DeviceOperations> Registration<T> {
+    /// Creates a new [`Registration`] instance for ethernet device.
+    ///
+    /// A device driver can pass private data.
+    pub fn try_new_ether(tx_queue_size: u32, rx_queue_size: u32, data: T) -> Result<Self> {
+        // SAFETY: Just an FFI call with no additional safety requirements.
+        let ptr = unsafe {
+            bindings::alloc_etherdev_mqs(
+                core::mem::size_of::<*const c_void>() as i32,
+                tx_queue_size,
+                rx_queue_size,
+            )
+        };
+        if ptr.is_null() {
+            return Err(code::ENOMEM);
+        }
+
+        // SAFETY: It's safe to write an address returned pointer
+        // from `netdev_priv()` because `alloc_etherdev_mqs()` allocates
+        // contiguous memory for `struct net_device` and a pointer.
+        unsafe {
+            let priv_ptr = bindings::netdev_priv(ptr) as *mut *const c_void;
+            core::ptr::write(priv_ptr, data.into_foreign());
+        }
+
+        // SAFETY: `ptr` points to contiguous memory for `struct net_device` and a pointer,
+        // which stores an address returned by `ForeignOwnable::into_foreign()`.
+        let dev = unsafe { Device::from_ptr(ptr) };
+        Ok(Registration {
+            dev,
+            is_registered: false,
+            _p: PhantomData,
+        })
+    }
+
+    /// Returns a network device.
+    ///
+    /// A device driver normally configures the device before registration.
+    pub fn dev_get(&mut self) -> &mut Device<T> {
+        &mut self.dev
+    }
+
+    /// Registers a network device.
+    pub fn register(&mut self) -> Result {
+        if self.is_registered {
+            return Err(code::EINVAL);
+        }
+        // SAFETY: The type invariants guarantee that `self.dev.ptr` is valid.
+        let ret = unsafe {
+            (*self.dev.ptr).netdev_ops = &Self::DEVICE_OPS;
+            bindings::register_netdev(self.dev.ptr)
+        };
+        if ret != 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            self.is_registered = true;
+            Ok(())
+        }
+    }
+
+    const DEVICE_OPS: bindings::net_device_ops = bindings::net_device_ops {
+        ndo_init: if <T>::HAS_INIT {
+            Some(Self::init_callback)
+        } else {
+            None
+        },
+        ndo_uninit: if <T>::HAS_UNINIT {
+            Some(Self::uninit_callback)
+        } else {
+            None
+        },
+        ndo_open: if <T>::HAS_OPEN {
+            Some(Self::open_callback)
+        } else {
+            None
+        },
+        ndo_stop: if <T>::HAS_STOP {
+            Some(Self::stop_callback)
+        } else {
+            None
+        },
+        ndo_start_xmit: if <T>::HAS_START_XMIT {
+            Some(Self::start_xmit_callback)
+        } else {
+            None
+        },
+        // SAFETY: The rest is zeroed out to initialize `struct net_device_ops`,
+        // set `Option<&F>` to be `None`.
+        ..unsafe { core::mem::MaybeUninit::<bindings::net_device_ops>::zeroed().assume_init() }
+    };
+
+    unsafe extern "C" fn init_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+            let dev = unsafe { Device::from_ptr(netdev) };
+            T::init(dev)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn uninit_callback(netdev: *mut bindings::net_device) {
+        // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+        let dev = unsafe { Device::from_ptr(netdev) };
+        T::uninit(dev);
+    }
+
+    unsafe extern "C" fn open_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+            let dev = unsafe { Device::from_ptr(netdev) };
+            T::open(dev)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn stop_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+            let dev = unsafe { Device::from_ptr(netdev) };
+            T::stop(dev)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn start_xmit_callback(
+        skb: *mut bindings::sk_buff,
+        netdev: *mut bindings::net_device,
+    ) -> bindings::netdev_tx_t {
+        // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+        let dev = unsafe { Device::from_ptr(netdev) };
+        // SAFETY: The C API guarantees that `skb` is valid until a driver releases the skb.
+        let skb = unsafe { SkBuff::from_ptr(skb) };
+        T::start_xmit(dev, skb) as bindings::netdev_tx_t
+    }
+}
+
+// SAFETY: `Registration` exposes only `Device` object which can be used from any thread.
+unsafe impl<T: DeviceOperations> Send for Registration<T> {}
+// SAFETY: `Registration` exposes only `Device` object which can be used from any thread.
+unsafe impl<T: DeviceOperations> Sync for Registration<T> {}
+
+/// Corresponds to the kernel's `enum netdev_tx`.
+#[repr(i32)]
+pub enum TxCode {
+    /// Driver took care of packet.
+    Ok = bindings::netdev_tx_NETDEV_TX_OK,
+    /// Driver tx path was busy.
+    Busy = bindings::netdev_tx_NETDEV_TX_BUSY,
+}
+
+/// Corresponds to the kernel's `struct net_device_ops`.
+///
+/// A device driver must implement this. Only very basic operations are supported for now.
+#[vtable]
+pub trait DeviceOperations: ForeignOwnable + Send + Sync {
+    /// Corresponds to `ndo_init` in `struct net_device_ops`.
+    fn init(_dev: Device<Self>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_uninit` in `struct net_device_ops`.
+    fn uninit(_dev: Device<Self>) {}
+
+    /// Corresponds to `ndo_open` in `struct net_device_ops`.
+    fn open(_dev: Device<Self>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_stop` in `struct net_device_ops`.
+    fn stop(_dev: Device<Self>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_start_xmit` in `struct net_device_ops`.
+    fn start_xmit(_dev: Device<Self>, _skb: SkBuff) -> TxCode {
+        TxCode::Busy
+    }
+}
+
+/// Corresponds to the kernel's `struct sk_buff`.
+///
+/// A driver manages `struct sk_buff` in two ways. In both ways, the ownership is transferred
+/// between C and Rust. The allocation and release are done asymmetrically.
+///
+/// On the tx side (`ndo_start_xmit` operation in `struct net_device_ops`), the kernel allocates
+/// a `sk_buff' object and passes it to the driver. The driver is responsible for the release
+/// after transmission.
+/// On the rx side, the driver allocates a `sk_buff` object then passes it to the kernel
+/// after receiving data.
+///
+/// A driver must explicitly call a function to drop a `sk_buff` object.
+/// The code to let a `SkBuff` object go out of scope can't be compiled.
+///
+/// # Invariants
+///
+/// The pointer is valid.
+pub struct SkBuff(*mut bindings::sk_buff);
+
+impl SkBuff {
+    /// Creates a new [`SkBuff`] instance.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `ptr` must be valid.
+    unsafe fn from_ptr(ptr: *mut bindings::sk_buff) -> Self {
+        // INVARIANT: The safety requirements ensure the invariant.
+        Self(ptr)
+    }
+
+    /// Provides a time stamp.
+    pub fn tx_timestamp(&mut self) {
+        // SAFETY: The type invariants guarantee that `self.0` is valid.
+        unsafe {
+            bindings::skb_tx_timestamp(self.0);
+        }
+    }
+
+    /// Consumes a [`sk_buff`] object.
+    pub fn consume(self) {
+        // SAFETY: The type invariants guarantee that `self.0` is valid.
+        unsafe {
+            bindings::kfree_skb_reason(self.0, bindings::skb_drop_reason_SKB_CONSUMED);
+        }
+        core::mem::forget(self);
+    }
+}
+
+impl Drop for SkBuff {
+    #[inline(always)]
+    fn drop(&mut self) {
+        build_error!("skb must be released explicitly");
+    }
+}
-- 
2.34.1


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

* [PATCH v2 2/5] rust: add support for ethernet operations
  2023-07-10  7:36 [PATCH v2 0/5] Rust abstractions for network device drivers FUJITA Tomonori
  2023-07-10  7:36 ` [PATCH v2 1/5] rust: core " FUJITA Tomonori
@ 2023-07-10  7:37 ` FUJITA Tomonori
  2023-07-14 19:00   ` Benno Lossin
  2023-07-10  7:37 ` [PATCH v2 3/5] rust: add methods for configure net_device FUJITA Tomonori
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 24+ messages in thread
From: FUJITA Tomonori @ 2023-07-10  7:37 UTC (permalink / raw)
  To: rust-for-linux, netdev
  Cc: kuba, andrew, aliceryhl, miguel.ojeda.sandonis, benno.lossin

This improves abstractions for network device drivers to implement
struct ethtool_ops, the majority of ethernet device drivers need to
do.

struct ethtool_ops also needs to access to device private data like
struct net_device_ops.

Currently, only get_ts_info operation is supported. The following
patch adds the Rust version of the dummy network driver, which uses
the operation.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 rust/bindings/bindings_helper.h |  1 +
 rust/kernel/net/dev.rs          | 87 ++++++++++++++++++++++++++++++++-
 2 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 468bf606f174..6446ff764980 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -8,6 +8,7 @@
 
 #include <linux/errname.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
 #include <linux/netdevice.h>
 #include <linux/slab.h>
 #include <linux/refcount.h>
diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs
index fe20616668a9..ff00616e4fef 100644
--- a/rust/kernel/net/dev.rs
+++ b/rust/kernel/net/dev.rs
@@ -142,7 +142,7 @@ pub fn register(&mut self) -> Result {
         if self.is_registered {
             return Err(code::EINVAL);
         }
-        // SAFETY: The type invariants guarantee that `self.dev.ptr` is valid.
+        // SAFETY: The type invariants of `Device` guarantee that `self.dev.ptr` is valid.
         let ret = unsafe {
             (*self.dev.ptr).netdev_ops = &Self::DEVICE_OPS;
             bindings::register_netdev(self.dev.ptr)
@@ -155,6 +155,18 @@ pub fn register(&mut self) -> Result {
         }
     }
 
+    /// Sets `ethtool_ops` of `net_device`.
+    pub fn set_ether_operations<E: EtherOperations>(&mut self) -> Result {
+        if self.is_registered {
+            return Err(code::EINVAL);
+        }
+        // SAFETY: The type invariants of `Device` guarantee that `self.dev.ptr` is valid.
+        unsafe {
+            (*(self.dev.ptr)).ethtool_ops = &EtherOperationsAdapter::<E>::ETHER_OPS;
+        }
+        Ok(())
+    }
+
     const DEVICE_OPS: bindings::net_device_ops = bindings::net_device_ops {
         ndo_init: if <T>::HAS_INIT {
             Some(Self::init_callback)
@@ -328,3 +340,76 @@ fn drop(&mut self) {
         build_error!("skb must be released explicitly");
     }
 }
+
+/// Builds the kernel's `struct ethtool_ops`.
+struct EtherOperationsAdapter<E: EtherOperations> {
+    _p: PhantomData<E>,
+}
+
+impl<E: EtherOperations> EtherOperationsAdapter<E> {
+    unsafe extern "C" fn get_ts_info_callback(
+        netdev: *mut bindings::net_device,
+        info: *mut bindings::ethtool_ts_info,
+    ) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+            let dev = unsafe { Device::from_ptr(netdev) };
+            // SAFETY: The C API guarantees that `info` is valid while this function is running.
+            let info = unsafe { EthtoolTsInfo::from_ptr(info) };
+            E::get_ts_info(dev, info)?;
+            Ok(0)
+        })
+    }
+
+    const ETHER_OPS: bindings::ethtool_ops = bindings::ethtool_ops {
+        get_ts_info: if <E>::HAS_GET_TS_INFO {
+            Some(Self::get_ts_info_callback)
+        } else {
+            None
+        },
+        // SAFETY: The rest is zeroed out to initialize `struct ethtool_ops`,
+        // set `Option<&F>` to be `None`.
+        ..unsafe { core::mem::MaybeUninit::<bindings::ethtool_ops>::zeroed().assume_init() }
+    };
+}
+
+/// Corresponds to the kernel's `struct ethtool_ops`.
+#[vtable]
+pub trait EtherOperations: ForeignOwnable + Send + Sync {
+    /// Corresponds to `get_ts_info` in `struct ethtool_ops`.
+    fn get_ts_info(_dev: Device<Self>, _info: EthtoolTsInfo) -> Result {
+        Err(Error::from_errno(bindings::EOPNOTSUPP as i32))
+    }
+}
+
+/// Corresponds to the kernel's `struct ethtool_ts_info`.
+///
+/// # Invariants
+///
+/// The pointer is valid.
+pub struct EthtoolTsInfo(*mut bindings::ethtool_ts_info);
+
+impl EthtoolTsInfo {
+    /// Creates a new `EthtoolTsInfo' instance.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `ptr` must be valid.
+    unsafe fn from_ptr(ptr: *mut bindings::ethtool_ts_info) -> Self {
+        // INVARIANT: The safety requirements ensure the invariant.
+        Self(ptr)
+    }
+
+    /// Sets up the info for software timestamping.
+    pub fn ethtool_op_get_ts_info<D: ForeignOwnable + Send + Sync>(
+        dev: &Device<D>,
+        info: &mut EthtoolTsInfo,
+    ) -> Result {
+        // SAFETY: The type invariants of `Device` guarantee that `dev.ptr` are valid.
+        // The type invariants guarantee that `info.0` are valid.
+        unsafe {
+            bindings::ethtool_op_get_ts_info(dev.ptr, info.0);
+        }
+        Ok(())
+    }
+}
-- 
2.34.1


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

* [PATCH v2 3/5] rust: add methods for configure net_device
  2023-07-10  7:36 [PATCH v2 0/5] Rust abstractions for network device drivers FUJITA Tomonori
  2023-07-10  7:36 ` [PATCH v2 1/5] rust: core " FUJITA Tomonori
  2023-07-10  7:37 ` [PATCH v2 2/5] rust: add support for ethernet operations FUJITA Tomonori
@ 2023-07-10  7:37 ` FUJITA Tomonori
  2023-07-14 19:01   ` Benno Lossin
  2023-07-10  7:37 ` [PATCH v2 4/5] samples: rust: add dummy network driver FUJITA Tomonori
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 24+ messages in thread
From: FUJITA Tomonori @ 2023-07-10  7:37 UTC (permalink / raw)
  To: rust-for-linux, netdev
  Cc: kuba, andrew, aliceryhl, miguel.ojeda.sandonis, benno.lossin

adds methods to net::Device for the basic configurations of
net_device.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 rust/helpers.c         |   7 ++
 rust/kernel/net/dev.rs | 185 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 191 insertions(+), 1 deletion(-)

diff --git a/rust/helpers.c b/rust/helpers.c
index 70d50767ff4e..6c51deb18dc1 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -22,6 +22,7 @@
 #include <linux/build_bug.h>
 #include <linux/err.h>
 #include <linux/errname.h>
+#include <linux/etherdevice.h>
 #include <linux/refcount.h>
 #include <linux/mutex.h>
 #include <linux/netdevice.h>
@@ -31,6 +32,12 @@
 #include <linux/wait.h>
 
 #ifdef CONFIG_NET
+void rust_helper_eth_hw_addr_random(struct net_device *dev)
+{
+	eth_hw_addr_random(dev);
+}
+EXPORT_SYMBOL_GPL(rust_helper_eth_hw_addr_random);
+
 void *rust_helper_netdev_priv(const struct net_device *dev)
 {
 	return netdev_priv(dev);
diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs
index ff00616e4fef..e4d8d8260c10 100644
--- a/rust/kernel/net/dev.rs
+++ b/rust/kernel/net/dev.rs
@@ -8,9 +8,116 @@
 //! [`include/linux/skbuff.h`](../../../../include/linux/skbuff.h),
 //! [`include/uapi/linux/if_link.h`](../../../../include/uapi/linux/if_link.h).
 
-use crate::{bindings, build_error, error::*, prelude::vtable, types::ForeignOwnable};
+use crate::{bindings, build_error, error::*, prelude::vtable, str::CStr, types::ForeignOwnable};
 use {core::ffi::c_void, core::marker::PhantomData};
 
+/// Flags associated with a [`Device`].
+pub mod flags {
+    /// Interface is up.
+    pub const IFF_UP: u32 = bindings::net_device_flags_IFF_UP;
+    /// Broadcast address valid.
+    pub const IFF_BROADCAST: u32 = bindings::net_device_flags_IFF_BROADCAST;
+    /// Device on debugging.
+    pub const IFF_DEBUG: u32 = bindings::net_device_flags_IFF_DEBUG;
+    /// Loopback device.
+    pub const IFF_LOOPBACK: u32 = bindings::net_device_flags_IFF_LOOPBACK;
+    /// Has p-p link.
+    pub const IFF_POINTOPOINT: u32 = bindings::net_device_flags_IFF_POINTOPOINT;
+    /// Avoids use of trailers.
+    pub const IFF_NOTRAILERS: u32 = bindings::net_device_flags_IFF_NOTRAILERS;
+    /// Interface RFC2863 OPER_UP.
+    pub const IFF_RUNNING: u32 = bindings::net_device_flags_IFF_RUNNING;
+    /// No ARP protocol.
+    pub const IFF_NOARP: u32 = bindings::net_device_flags_IFF_NOARP;
+    /// Receives all packets.
+    pub const IFF_PROMISC: u32 = bindings::net_device_flags_IFF_PROMISC;
+    /// Receive all multicast packets.
+    pub const IFF_ALLMULTI: u32 = bindings::net_device_flags_IFF_ALLMULTI;
+    /// Master of a load balancer.
+    pub const IFF_MASTER: u32 = bindings::net_device_flags_IFF_MASTER;
+    /// Slave of a load balancer.
+    pub const IFF_SLAVE: u32 = bindings::net_device_flags_IFF_SLAVE;
+    /// Supports multicast.
+    pub const IFF_MULTICAST: u32 = bindings::net_device_flags_IFF_MULTICAST;
+    /// Capable of setting media type.
+    pub const IFF_PORTSEL: u32 = bindings::net_device_flags_IFF_PORTSEL;
+    /// Auto media select active.
+    pub const IFF_AUTOMEDIA: u32 = bindings::net_device_flags_IFF_AUTOMEDIA;
+    /// Dialup device with changing addresses.
+    pub const IFF_DYNAMIC: u32 = bindings::net_device_flags_IFF_DYNAMIC;
+}
+
+/// Private flags associated with a [`Device`].
+pub mod priv_flags {
+    /// 802.1Q VLAN device.
+    pub const IFF_802_1Q_VLAN: u64 = bindings::netdev_priv_flags_IFF_802_1Q_VLAN;
+    /// Ethernet bridging device.
+    pub const IFF_EBRIDGE: u64 = bindings::netdev_priv_flags_IFF_EBRIDGE;
+    /// Bonding master or slave device.
+    pub const IFF_BONDING: u64 = bindings::netdev_priv_flags_IFF_BONDING;
+    /// ISATAP interface (RFC4214).
+    pub const IFF_ISATAP: u64 = bindings::netdev_priv_flags_IFF_ISATAP;
+    /// WAN HDLC device.
+    pub const IFF_WAN_HDLC: u64 = bindings::netdev_priv_flags_IFF_WAN_HDLC;
+    /// dev_hard_start_xmit() is allowed to release skb->dst.
+    pub const IFF_XMIT_DST_RELEASE: u64 = bindings::netdev_priv_flags_IFF_XMIT_DST_RELEASE;
+    /// Disallows bridging this ether device.
+    pub const IFF_DONT_BRIDGE: u64 = bindings::netdev_priv_flags_IFF_DONT_BRIDGE;
+    /// Disables netpoll at run-time.
+    pub const IFF_DISABLE_NETPOLL: u64 = bindings::netdev_priv_flags_IFF_DISABLE_NETPOLL;
+    /// Device used as macvlan port.
+    pub const IFF_MACVLAN_PORT: u64 = bindings::netdev_priv_flags_IFF_MACVLAN_PORT;
+    /// Device used as bridge port.
+    pub const IFF_BRIDGE_PORT: u64 = bindings::netdev_priv_flags_IFF_BRIDGE_PORT;
+    /// Device used as Open vSwitch datapath port.
+    pub const IFF_OVS_DATAPATH: u64 = bindings::netdev_priv_flags_IFF_OVS_DATAPATH;
+    /// The interface supports sharing skbs on transmit.
+    pub const IFF_TX_SKB_SHARING: u64 = bindings::netdev_priv_flags_IFF_TX_SKB_SHARING;
+    /// Supports unicast filtering.
+    pub const IFF_UNICAST_FLT: u64 = bindings::netdev_priv_flags_IFF_UNICAST_FLT;
+    /// Device used as team port.
+    pub const IFF_TEAM_PORT: u64 = bindings::netdev_priv_flags_IFF_TEAM_PORT;
+    /// Device supports sending custom FCS.
+    pub const IFF_SUPP_NOFCS: u64 = bindings::netdev_priv_flags_IFF_SUPP_NOFCS;
+    /// Device supports hardware address change when it's running.
+    pub const IFF_LIVE_ADDR_CHANGE: u64 = bindings::netdev_priv_flags_IFF_LIVE_ADDR_CHANGE;
+    /// Macvlan device.
+    pub const IFF_MACVLAN: u64 = bindings::netdev_priv_flags_IFF_MACVLAN;
+    /// IFF_XMIT_DST_RELEASE not taking into account underlying stacked devices.
+    pub const IFF_XMIT_DST_RELEASE_PERM: u64 =
+        bindings::netdev_priv_flags_IFF_XMIT_DST_RELEASE_PERM;
+    /// L3 master device.
+    pub const IFF_L3MDEV_MASTER: u64 = bindings::netdev_priv_flags_IFF_L3MDEV_MASTER;
+    /// Device can run without qdisc attached.
+    pub const IFF_NO_QUEUE: u64 = bindings::netdev_priv_flags_IFF_NO_QUEUE;
+    /// Device is a Open vSwitch master.
+    pub const IFF_OPENVSWITCH: u64 = bindings::netdev_priv_flags_IFF_OPENVSWITCH;
+    /// Device is enslaved to an L3 master.
+    pub const IFF_L3MDEV_SLAVE: u64 = bindings::netdev_priv_flags_IFF_L3MDEV_SLAVE;
+    /// Team device.
+    pub const IFF_TEAM: u64 = bindings::netdev_priv_flags_IFF_TEAM;
+    /// Device has had Rx Flow indirection table configured.
+    pub const IFF_RXFH_CONFIGURED: u64 = bindings::netdev_priv_flags_IFF_RXFH_CONFIGURED;
+    /// The headroom value is controlled by an external entity.
+    pub const IFF_PHONY_HEADROOM: u64 = bindings::netdev_priv_flags_IFF_PHONY_HEADROOM;
+    /// MACsec device.
+    pub const IFF_MACSEC: u64 = bindings::netdev_priv_flags_IFF_MACSEC;
+    /// Device doesn't support the rx_handler hook.
+    pub const IFF_NO_RX_HANDLER: u64 = bindings::netdev_priv_flags_IFF_NO_RX_HANDLER;
+    /// Failover master device.
+    pub const IFF_FAILOVER: u64 = bindings::netdev_priv_flags_IFF_FAILOVER;
+    /// Lower device of a failover master device.
+    pub const IFF_FAILOVER_SLAVE: u64 = bindings::netdev_priv_flags_IFF_FAILOVER_SLAVE;
+    /// Only invokes the rx handler of L3 master device.
+    pub const IFF_L3MDEV_RX_HANDLER: u64 = bindings::netdev_priv_flags_IFF_L3MDEV_RX_HANDLER;
+    /// Prevents ipv6 addrconf.
+    pub const IFF_NO_ADDRCONF: u64 = bindings::netdev_priv_flags_IFF_NO_ADDRCONF;
+    /// Capable of xmitting frames with skb_headlen(skb) == 0.
+    pub const IFF_TX_SKB_NO_LINEAR: u64 = bindings::netdev_priv_flags_IFF_TX_SKB_NO_LINEAR;
+    /// Supports setting carrier via IFLA_PROTO_DOWN.
+    pub const IFF_CHANGE_PROTO_DOWN: u64 = bindings::netdev_priv_flags_IFF_CHANGE_PROTO_DOWN;
+}
+
 /// Corresponds to the kernel's `struct net_device`.
 ///
 /// # Invariants
@@ -49,6 +156,82 @@ pub fn drv_priv_data(&self) -> D::Borrowed<'_> {
             ))
         }
     }
+
+    /// Sets the name of a device.
+    pub fn set_name(&mut self, name: &CStr) -> Result {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe {
+            if name.len() > (*self.ptr).name.len() {
+                return Err(code::EINVAL);
+            }
+            (*self.ptr)
+                .name
+                .as_mut_ptr()
+                .copy_from_nonoverlapping(name.as_char_ptr(), name.len());
+        }
+        Ok(())
+    }
+
+    /// Sets carrier.
+    pub fn netif_carrier_on(&mut self) {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe { bindings::netif_carrier_on(self.ptr) }
+    }
+
+    /// Clears carrier.
+    pub fn netif_carrier_off(&mut self) {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe { bindings::netif_carrier_off(self.ptr) }
+    }
+
+    /// Sets the max mtu of the device.
+    pub fn set_max_mtu(&mut self, max_mtu: u32) {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe {
+            (*self.ptr).max_mtu = max_mtu;
+        }
+    }
+
+    /// Sets the minimum mtu of the device.
+    pub fn set_min_mtu(&mut self, min_mtu: u32) {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe {
+            (*self.ptr).min_mtu = min_mtu;
+        }
+    }
+
+    /// Returns the flags of the device.
+    pub fn get_flags(&self) -> u32 {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe { (*self.ptr).flags }
+    }
+
+    /// Sets the flags of the device.
+    pub fn set_flags(&mut self, flags: u32) {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe {
+            (*self.ptr).flags = flags;
+        }
+    }
+
+    /// Returns the priv_flags of the device.
+    pub fn get_priv_flags(&self) -> u64 {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe { (*self.ptr).priv_flags }
+    }
+
+    /// Sets the priv_flags of the device.
+    pub fn set_priv_flags(&mut self, flags: u64) {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe { (*self.ptr).priv_flags = flags }
+    }
+
+    /// Generate a random Ethernet address (MAC) to be used by a net device
+    /// and set addr_assign_type.
+    pub fn set_random_eth_hw_addr(&mut self) {
+        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
+        unsafe { bindings::eth_hw_addr_random(self.ptr) }
+    }
 }
 
 // SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used
-- 
2.34.1


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

* [PATCH v2 4/5] samples: rust: add dummy network driver
  2023-07-10  7:36 [PATCH v2 0/5] Rust abstractions for network device drivers FUJITA Tomonori
                   ` (2 preceding siblings ...)
  2023-07-10  7:37 ` [PATCH v2 3/5] rust: add methods for configure net_device FUJITA Tomonori
@ 2023-07-10  7:37 ` FUJITA Tomonori
  2023-07-10  7:37 ` [PATCH v2 5/5] MAINTAINERS: add Rust network abstractions files to the NETWORKING DRIVERS entry FUJITA Tomonori
  2023-07-10 18:29 ` [PATCH v2 0/5] Rust abstractions for network device drivers Jakub Kicinski
  5 siblings, 0 replies; 24+ messages in thread
From: FUJITA Tomonori @ 2023-07-10  7:37 UTC (permalink / raw)
  To: rust-for-linux, netdev
  Cc: kuba, andrew, aliceryhl, miguel.ojeda.sandonis, benno.lossin

This is a simpler version of drivers/net/dummy.c.

This demonstrates the usage of abstractions for network device drivers.

Allows allocator_api feature for Box::try_new();

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 samples/rust/Kconfig           | 13 ++++++
 samples/rust/Makefile          |  1 +
 samples/rust/rust_net_dummy.rs | 75 ++++++++++++++++++++++++++++++++++
 scripts/Makefile.build         |  2 +-
 4 files changed, 90 insertions(+), 1 deletion(-)
 create mode 100644 samples/rust/rust_net_dummy.rs

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index b0f74a81c8f9..02bda7bdf722 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -30,6 +30,19 @@ config SAMPLE_RUST_PRINT
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_NET_DUMMY
+	tristate "Dummy network driver"
+	depends on NET
+	help
+	  This is the simpler version of drivers/net/dummy.c.
+	  No intention to replace it. This provides educational information
+	  for Rust abstractions for network device drivers.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_net_dummy.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_HOSTPROGS
 	bool "Host programs"
 	help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 03086dabbea4..440dee2971ba 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -2,5 +2,6 @@
 
 obj-$(CONFIG_SAMPLE_RUST_MINIMAL)		+= rust_minimal.o
 obj-$(CONFIG_SAMPLE_RUST_PRINT)			+= rust_print.o
+obj-$(CONFIG_SAMPLE_RUST_NET_DUMMY)		+= rust_net_dummy.o
 
 subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS)		+= hostprogs
diff --git a/samples/rust/rust_net_dummy.rs b/samples/rust/rust_net_dummy.rs
new file mode 100644
index 000000000000..78f8f3321fd2
--- /dev/null
+++ b/samples/rust/rust_net_dummy.rs
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+//! Rust dummy netdev.
+
+//use kernel::types::ForeignOwnable;
+use kernel::{
+    c_str,
+    net::dev::{
+        flags, priv_flags, Device, DeviceOperations, EtherOperations, EthtoolTsInfo, Registration,
+        SkBuff, TxCode,
+    },
+    prelude::*,
+};
+
+module! {
+    type: DummyNetdev,
+    name: "rust_net_dummy",
+    author: "Rust for Linux Contributors",
+    description: "Rust dummy netdev",
+    license: "GPL v2",
+}
+
+#[vtable]
+impl DeviceOperations for Box<DriverData> {
+    fn init(dev: Device<Box<DriverData>>) -> Result {
+        // how to access to the driver private data.
+        let _ = dev.drv_priv_data();
+        Ok(())
+    }
+
+    fn start_xmit(_dev: Device<Box<DriverData>>, mut skb: SkBuff) -> TxCode {
+        skb.tx_timestamp();
+        skb.consume();
+        TxCode::Ok
+    }
+}
+
+/// For device driver specific information.
+struct DriverData {}
+
+struct DummyNetdev {
+    _r: Registration<Box<DriverData>>,
+}
+
+#[vtable]
+impl EtherOperations for Box<DriverData> {
+    fn get_ts_info(dev: Device<Box<DriverData>>, mut info: EthtoolTsInfo) -> Result {
+        EthtoolTsInfo::ethtool_op_get_ts_info(&dev, &mut info)
+    }
+}
+
+impl kernel::Module for DummyNetdev {
+    fn init(_module: &'static ThisModule) -> Result<Self> {
+        let data = Box::try_new(DriverData {})?;
+        let mut r = Registration::<Box<DriverData>>::try_new_ether(1, 1, data)?;
+        r.set_ether_operations::<Box<DriverData>>()?;
+
+        let netdev = r.dev_get();
+        netdev.set_name(c_str!("dummy%d"))?;
+
+        netdev.set_flags(netdev.get_flags() | flags::IFF_NOARP & !flags::IFF_MULTICAST);
+        netdev.set_priv_flags(
+            netdev.get_priv_flags() | priv_flags::IFF_LIVE_ADDR_CHANGE | priv_flags::IFF_NO_QUEUE,
+        );
+        netdev.set_random_eth_hw_addr();
+        netdev.set_min_mtu(0);
+        netdev.set_max_mtu(0);
+
+        r.register()?;
+
+        // TODO: Replaces pr_info with the wrapper of netdev_info().
+        pr_info!("Hello Rust dummy netdev!");
+        Ok(DummyNetdev { _r: r })
+    }
+}
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 78175231c969..1404967e908e 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -277,7 +277,7 @@ $(obj)/%.lst: $(src)/%.c FORCE
 # Compile Rust sources (.rs)
 # ---------------------------------------------------------------------------
 
-rust_allowed_features := new_uninit
+rust_allowed_features := allocator_api,new_uninit
 
 rust_common_cmd = \
 	RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \
-- 
2.34.1


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

* [PATCH v2 5/5] MAINTAINERS: add Rust network abstractions files to the NETWORKING DRIVERS entry
  2023-07-10  7:36 [PATCH v2 0/5] Rust abstractions for network device drivers FUJITA Tomonori
                   ` (3 preceding siblings ...)
  2023-07-10  7:37 ` [PATCH v2 4/5] samples: rust: add dummy network driver FUJITA Tomonori
@ 2023-07-10  7:37 ` FUJITA Tomonori
  2023-07-10 18:29 ` [PATCH v2 0/5] Rust abstractions for network device drivers Jakub Kicinski
  5 siblings, 0 replies; 24+ messages in thread
From: FUJITA Tomonori @ 2023-07-10  7:37 UTC (permalink / raw)
  To: rust-for-linux, netdev
  Cc: kuba, andrew, aliceryhl, miguel.ojeda.sandonis, benno.lossin

The files are placed at rust/kernel/ directory for now but the files
are likely to be moved to net/ directory if things go well.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 MAINTAINERS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 250518fc70ff..66b8e43b05a2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14581,6 +14581,8 @@ F:	include/linux/inetdevice.h
 F:	include/linux/netdevice.h
 F:	include/uapi/linux/if_*
 F:	include/uapi/linux/netdevice.h
+F:	rust/kernel/net.rs
+F:	rust/kernel/net/
 
 NETWORKING DRIVERS (WIRELESS)
 M:	Kalle Valo <kvalo@kernel.org>
-- 
2.34.1


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

* Re: [PATCH v2 0/5] Rust abstractions for network device drivers
  2023-07-10  7:36 [PATCH v2 0/5] Rust abstractions for network device drivers FUJITA Tomonori
                   ` (4 preceding siblings ...)
  2023-07-10  7:37 ` [PATCH v2 5/5] MAINTAINERS: add Rust network abstractions files to the NETWORKING DRIVERS entry FUJITA Tomonori
@ 2023-07-10 18:29 ` Jakub Kicinski
  2023-07-10 19:53   ` Greg KH
  2023-07-11 10:16   ` FUJITA Tomonori
  5 siblings, 2 replies; 24+ messages in thread
From: Jakub Kicinski @ 2023-07-10 18:29 UTC (permalink / raw)
  To: FUJITA Tomonori
  Cc: rust-for-linux, netdev, andrew, aliceryhl, miguel.ojeda.sandonis,
	benno.lossin

On Mon, 10 Jul 2023 16:36:58 +0900 FUJITA Tomonori wrote:
> This patchset adds minimum Rust abstractions for network device
> drivers and an example of a Rust network device driver, a simpler
> version of drivers/net/dummy.c.
> 
> The major change is a way to drop an skb (1/5 patch); a driver needs
> to explicitly call a function to drop a skb. The code to let a skb
> go out of scope can't be compiled.
> 
> I dropped get_stats64 support patch that the current sample driver
> doesn't use. Instead I added a patch to update the NETWORKING DRIVERS
> entry in MAINTAINERS.

I'd like to double down on my suggestion to try to implement a real
PHY driver. Most of the bindings in patch 3 will never be used by
drivers. (Re)implementing a real driver will guide you towards useful
stuff and real problems.

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

* Re: [PATCH v2 0/5] Rust abstractions for network device drivers
  2023-07-10 18:29 ` [PATCH v2 0/5] Rust abstractions for network device drivers Jakub Kicinski
@ 2023-07-10 19:53   ` Greg KH
  2023-07-11 10:16   ` FUJITA Tomonori
  1 sibling, 0 replies; 24+ messages in thread
From: Greg KH @ 2023-07-10 19:53 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: FUJITA Tomonori, rust-for-linux, netdev, andrew, aliceryhl,
	miguel.ojeda.sandonis, benno.lossin

On Mon, Jul 10, 2023 at 11:29:52AM -0700, Jakub Kicinski wrote:
> On Mon, 10 Jul 2023 16:36:58 +0900 FUJITA Tomonori wrote:
> > This patchset adds minimum Rust abstractions for network device
> > drivers and an example of a Rust network device driver, a simpler
> > version of drivers/net/dummy.c.
> > 
> > The major change is a way to drop an skb (1/5 patch); a driver needs
> > to explicitly call a function to drop a skb. The code to let a skb
> > go out of scope can't be compiled.
> > 
> > I dropped get_stats64 support patch that the current sample driver
> > doesn't use. Instead I added a patch to update the NETWORKING DRIVERS
> > entry in MAINTAINERS.
> 
> I'd like to double down on my suggestion to try to implement a real
> PHY driver. Most of the bindings in patch 3 will never be used by
> drivers. (Re)implementing a real driver will guide you towards useful
> stuff and real problems.

And I'd recommend that we not take any more bindings without real users,
as there seems to be just a collection of these and it's hard to
actually review them to see how they are used...

thanks,

greg k-h

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

* Re: [PATCH v2 0/5] Rust abstractions for network device drivers
  2023-07-10 18:29 ` [PATCH v2 0/5] Rust abstractions for network device drivers Jakub Kicinski
  2023-07-10 19:53   ` Greg KH
@ 2023-07-11 10:16   ` FUJITA Tomonori
  2023-07-11 13:17     ` Andrew Lunn
  1 sibling, 1 reply; 24+ messages in thread
From: FUJITA Tomonori @ 2023-07-11 10:16 UTC (permalink / raw)
  To: kuba
  Cc: fujita.tomonori, rust-for-linux, netdev, andrew, aliceryhl,
	miguel.ojeda.sandonis, benno.lossin

Hi,

On Mon, 10 Jul 2023 11:29:52 -0700
Jakub Kicinski <kuba@kernel.org> wrote:

> On Mon, 10 Jul 2023 16:36:58 +0900 FUJITA Tomonori wrote:
>> This patchset adds minimum Rust abstractions for network device
>> drivers and an example of a Rust network device driver, a simpler
>> version of drivers/net/dummy.c.
>> 
>> The major change is a way to drop an skb (1/5 patch); a driver needs
>> to explicitly call a function to drop a skb. The code to let a skb
>> go out of scope can't be compiled.
>> 
>> I dropped get_stats64 support patch that the current sample driver
>> doesn't use. Instead I added a patch to update the NETWORKING DRIVERS
>> entry in MAINTAINERS.
> 
> I'd like to double down on my suggestion to try to implement a real
> PHY driver. Most of the bindings in patch 3 will never be used by
> drivers. (Re)implementing a real driver will guide you towards useful
> stuff and real problems.

You meant reimplementing one of drivers in drivers/net/phy in Rust
(with Rust abstractions for PHY drivers)?

Even the approach, we would get hit the same problem? Replacing an
existing working driver in C doesn't make sense much thus the
abstractions cannot be merged until someone want to implement a driver
in Rust for new PHY hardware.

Or you think that PHY drivers (and probably the abstractions) are
relatively simple so merging the abstractions for them is acceptable
without a real driver (we could put a real drivers under
samples/rust/)?

thanks,


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

* Re: [PATCH v2 0/5] Rust abstractions for network device drivers
  2023-07-11 10:16   ` FUJITA Tomonori
@ 2023-07-11 13:17     ` Andrew Lunn
  2023-07-12 11:45       ` FUJITA Tomonori
  0 siblings, 1 reply; 24+ messages in thread
From: Andrew Lunn @ 2023-07-11 13:17 UTC (permalink / raw)
  To: FUJITA Tomonori
  Cc: kuba, rust-for-linux, netdev, aliceryhl, miguel.ojeda.sandonis,
	benno.lossin

> Or you think that PHY drivers (and probably the abstractions) are
> relatively simple so merging the abstractions for them is acceptable
> without a real driver (we could put a real drivers under
> samples/rust/)?

PHY drivers are much simpler than Ethernet drivers. But more
importantly, the API to the rest of network stack is much much
smaller. So a binding for a sample driver is going to cover a large
part of that API, unlike your sample Ethernet driver binding which
covers a tiny part of the API. The PHY binding is then actually
useful, unlike the binding for Ethernet.

As for reimplementing an existing driver, vs a new driver for hardware
which is currently unsupported, that would depend on you. You could
reach out to some vendors and see if they have devices which are
missing mainline drivers. See if they will donate an RDK and the
datasheet in return for a free driver written in Rust. Whole new
drivers do come along reasonably frequently, so there is probably
scope for a new driver. Automotive is a big source of new code and
devices at the moment.

	Andrew

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

* Re: [PATCH v2 0/5] Rust abstractions for network device drivers
  2023-07-11 13:17     ` Andrew Lunn
@ 2023-07-12 11:45       ` FUJITA Tomonori
  0 siblings, 0 replies; 24+ messages in thread
From: FUJITA Tomonori @ 2023-07-12 11:45 UTC (permalink / raw)
  To: andrew
  Cc: fujita.tomonori, kuba, rust-for-linux, netdev, aliceryhl,
	miguel.ojeda.sandonis, benno.lossin

Hi,

On Tue, 11 Jul 2023 15:17:33 +0200
Andrew Lunn <andrew@lunn.ch> wrote:

>> Or you think that PHY drivers (and probably the abstractions) are
>> relatively simple so merging the abstractions for them is acceptable
>> without a real driver (we could put a real drivers under
>> samples/rust/)?
> 
> PHY drivers are much simpler than Ethernet drivers. But more
> importantly, the API to the rest of network stack is much much
> smaller. So a binding for a sample driver is going to cover a large
> part of that API, unlike your sample Ethernet driver binding which
> covers a tiny part of the API. The PHY binding is then actually
> useful, unlike the binding for Ethernet.

Point taken, I work on PHY drivers first.

> As for reimplementing an existing driver, vs a new driver for hardware
> which is currently unsupported, that would depend on you. You could
> reach out to some vendors and see if they have devices which are
> missing mainline drivers. See if they will donate an RDK and the
> datasheet in return for a free driver written in Rust. Whole new
> drivers do come along reasonably frequently, so there is probably
> scope for a new driver. Automotive is a big source of new code and
> devices at the moment.

Understood. Let me reseach the existing drivers to think about that.

Thanks,

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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-07-10  7:36 ` [PATCH v2 1/5] rust: core " FUJITA Tomonori
@ 2023-07-14 18:59   ` Benno Lossin
  0 siblings, 0 replies; 24+ messages in thread
From: Benno Lossin @ 2023-07-14 18:59 UTC (permalink / raw)
  To: FUJITA Tomonori
  Cc: rust-for-linux, netdev, kuba, andrew, aliceryhl, miguel.ojeda.sandonis

> This patch adds very basic abstractions to implement network device
> drivers, corresponds to the kernel's net_device and net_device_ops
> structs with support for register_netdev/unregister_netdev functions.
> 
> allows the const_maybe_uninit_zeroed feature for
> core::mem::MaybeUinit::<T>::zeroed() in const function.
> 
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> ---
>  rust/bindings/bindings_helper.h |   2 +
>  rust/helpers.c                  |  16 ++
>  rust/kernel/lib.rs              |   3 +
>  rust/kernel/net.rs              |   5 +
>  rust/kernel/net/dev.rs          | 330 ++++++++++++++++++++++++++++++++
>  5 files changed, 356 insertions(+)
>  create mode 100644 rust/kernel/net.rs
>  create mode 100644 rust/kernel/net/dev.rs
> 
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 3e601ce2548d..468bf606f174 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -7,6 +7,8 @@
>   */
> 
>  #include <linux/errname.h>
> +#include <linux/etherdevice.h>
> +#include <linux/netdevice.h>
>  #include <linux/slab.h>
>  #include <linux/refcount.h>
>  #include <linux/wait.h>
> diff --git a/rust/helpers.c b/rust/helpers.c
> index bb594da56137..70d50767ff4e 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -24,10 +24,26 @@
>  #include <linux/errname.h>
>  #include <linux/refcount.h>
>  #include <linux/mutex.h>
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
>  #include <linux/spinlock.h>
>  #include <linux/sched/signal.h>
>  #include <linux/wait.h>
> 
> +#ifdef CONFIG_NET
> +void *rust_helper_netdev_priv(const struct net_device *dev)
> +{
> +	return netdev_priv(dev);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);
> +
> +void rust_helper_skb_tx_timestamp(struct sk_buff *skb)
> +{
> +	skb_tx_timestamp(skb);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_skb_tx_timestamp);
> +#endif
> +
>  __noreturn void rust_helper_BUG(void)
>  {
>  	BUG();
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 85b261209977..fc7d048d359d 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -13,6 +13,7 @@
> 
>  #![no_std]
>  #![feature(allocator_api)]
> +#![feature(const_maybe_uninit_zeroed)]
>  #![feature(coerce_unsized)]
>  #![feature(dispatch_from_dyn)]
>  #![feature(new_uninit)]
> @@ -34,6 +35,8 @@
>  pub mod error;
>  pub mod init;
>  pub mod ioctl;
> +#[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..28fe8f398463
> --- /dev/null
> +++ b/rust/kernel/net.rs
> @@ -0,0 +1,5 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Networking core.
> +
> +pub mod dev;
> diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs
> new file mode 100644
> index 000000000000..fe20616668a9
> --- /dev/null
> +++ b/rust/kernel/net/dev.rs
> @@ -0,0 +1,330 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Network device.
> +//!
> +//! C headers: [`include/linux/etherdevice.h`](../../../../include/linux/etherdevice.h),
> +//! [`include/linux/ethtool.h`](../../../../include/linux/ethtool.h),
> +//! [`include/linux/netdevice.h`](../../../../include/linux/netdevice.h),
> +//! [`include/linux/skbuff.h`](../../../../include/linux/skbuff.h),
> +//! [`include/uapi/linux/if_link.h`](../../../../include/uapi/linux/if_link.h).
> +
> +use crate::{bindings, build_error, error::*, prelude::vtable, types::ForeignOwnable};
> +use {core::ffi::c_void, core::marker::PhantomData};
> +
> +/// Corresponds to the kernel's `struct net_device`.
> +///
> +/// # Invariants
> +///
> +/// The `ptr` points to the contiguous memory for `struct net_device` and a pointer,
> +/// which stores an address returned by `ForeignOwnable::into_foreign()`.
> +pub struct Device<D: ForeignOwnable + Send + Sync> {
> +    ptr: *mut bindings::net_device,
> +    _p: PhantomData<D>,
> +}
> +
> +impl<D: ForeignOwnable + Send + Sync> Device<D> {
> +    /// Creates a new [`Device`] instance.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that `ptr` must point to the contiguous memory
> +    /// for `struct net_device` and a pointer, which stores an address returned
> +    /// by `ForeignOwnable::into_foreign()`.
> +    unsafe fn from_ptr(ptr: *mut bindings::net_device) -> Self {
> +        // INVARIANT: The safety requirements ensure the invariant.
> +        Self {
> +            ptr,
> +            _p: PhantomData,
> +        }
> +    }
> +
> +    /// Gets the private data of a device driver.
> +    pub fn drv_priv_data(&self) -> D::Borrowed<'_> {
> +        // SAFETY: The type invariants guarantee that self.ptr is valid and
> +        // bindings::netdev_priv(self.ptr) returns a pointer that stores an address
> +        // returned by `ForeignOwnable::into_foreign()`.
> +        unsafe {
> +            D::borrow(core::ptr::read(
> +                bindings::netdev_priv(self.ptr) as *const *const c_void
> +            ))
> +        }
> +    }
> +}
> +
> +// SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used
> +// from any thread. `struct net_device` stores a pointer to an object, which is `Sync`
> +// so it's safe to sharing its pointer.
> +unsafe impl<D: ForeignOwnable + Send + Sync> Send for Device<D> {}
> +// SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used
> +// from any thread. `struct net_device` stores a pointer to an object, which is `Sync`,
> +// can be used from any thread too.
> +unsafe impl<D: ForeignOwnable + Send + Sync> Sync for Device<D> {}
> +
> +/// Registration structure for a network device driver.
> +///
> +/// This allocates and owns a `struct net_device` object.
> +/// Once the `net_device` object is registered via `register_netdev` function,
> +/// the kernel calls various functions such as `struct net_device_ops` operations with
> +/// the `net_device` object.
> +///
> +/// A driver must implement `struct net_device_ops` so the trait for it is tied.
> +/// Other operations like `struct ethtool_ops` are optional.
> +pub struct Registration<T: DeviceOperations> {
> +    dev: Device<T>,
> +    is_registered: bool,
> +    _p: PhantomData<T>,
> +}
> +
> +impl<T: DeviceOperations> Drop for Registration<T> {
> +    fn drop(&mut self) {
> +        // SAFETY: The type invariants of `Device` guarantee that `self.dev.ptr` is valid and
> +        // bindings::netdev_priv(self.ptr) returns a pointer that stores an address
> +        // returned by `ForeignOwnable::into_foreign()`.
> +        unsafe {
> +            let _ = T::from_foreign(core::ptr::read(
> +                bindings::netdev_priv(self.dev.ptr) as *const *const c_void
> +            ));
> +        }
> +        // SAFETY: The type invariants of `Device` guarantee that `self.dev.ptr` is valid.
> +        unsafe {
> +            if self.is_registered {
> +                bindings::unregister_netdev(self.dev.ptr);
> +            }
> +            bindings::free_netdev(self.dev.ptr);
> +        }
> +    }
> +}
> +
> +impl<T: DeviceOperations> Registration<T> {
> +    /// Creates a new [`Registration`] instance for ethernet device.
> +    ///
> +    /// A device driver can pass private data.
> +    pub fn try_new_ether(tx_queue_size: u32, rx_queue_size: u32, data: T) -> Result<Self> {
> +        // SAFETY: Just an FFI call with no additional safety requirements.
> +        let ptr = unsafe {
> +            bindings::alloc_etherdev_mqs(
> +                core::mem::size_of::<*const c_void>() as i32,
> +                tx_queue_size,
> +                rx_queue_size,
> +            )
> +        };
> +        if ptr.is_null() {
> +            return Err(code::ENOMEM);
> +        }
> +
> +        // SAFETY: It's safe to write an address returned pointer
> +        // from `netdev_priv()` because `alloc_etherdev_mqs()` allocates
> +        // contiguous memory for `struct net_device` and a pointer.
> +        unsafe {
> +            let priv_ptr = bindings::netdev_priv(ptr) as *mut *const c_void;
> +            core::ptr::write(priv_ptr, data.into_foreign());
> +        }
> +
> +        // SAFETY: `ptr` points to contiguous memory for `struct net_device` and a pointer,
> +        // which stores an address returned by `ForeignOwnable::into_foreign()`.
> +        let dev = unsafe { Device::from_ptr(ptr) };
> +        Ok(Registration {
> +            dev,
> +            is_registered: false,
> +            _p: PhantomData,
> +        })
> +    }
> +
> +    /// Returns a network device.
> +    ///
> +    /// A device driver normally configures the device before registration.
> +    pub fn dev_get(&mut self) -> &mut Device<T> {
> +        &mut self.dev
> +    }

I think you could instead implement `AsMut`.

> +
> +    /// Registers a network device.
> +    pub fn register(&mut self) -> Result {
> +        if self.is_registered {
> +            return Err(code::EINVAL);
> +        }
> +        // SAFETY: The type invariants guarantee that `self.dev.ptr` is valid.
> +        let ret = unsafe {
> +            (*self.dev.ptr).netdev_ops = &Self::DEVICE_OPS;
> +            bindings::register_netdev(self.dev.ptr)
> +        };
> +        if ret != 0 {
> +            Err(Error::from_errno(ret))
> +        } else {
> +            self.is_registered = true;
> +            Ok(())
> +        }
> +    }
> +
> +    const DEVICE_OPS: bindings::net_device_ops = bindings::net_device_ops {
> +        ndo_init: if <T>::HAS_INIT {
> +            Some(Self::init_callback)
> +        } else {
> +            None
> +        },
> +        ndo_uninit: if <T>::HAS_UNINIT {
> +            Some(Self::uninit_callback)
> +        } else {
> +            None
> +        },
> +        ndo_open: if <T>::HAS_OPEN {
> +            Some(Self::open_callback)
> +        } else {
> +            None
> +        },
> +        ndo_stop: if <T>::HAS_STOP {
> +            Some(Self::stop_callback)
> +        } else {
> +            None
> +        },
> +        ndo_start_xmit: if <T>::HAS_START_XMIT {
> +            Some(Self::start_xmit_callback)
> +        } else {
> +            None
> +        },
> +        // SAFETY: The rest is zeroed out to initialize `struct net_device_ops`,
> +        // set `Option<&F>` to be `None`.
> +        ..unsafe { core::mem::MaybeUninit::<bindings::net_device_ops>::zeroed().assume_init() }
> +    };
> +
> +    unsafe extern "C" fn init_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
> +        from_result(|| {
> +            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
> +            let dev = unsafe { Device::from_ptr(netdev) };
> +            T::init(dev)?;
> +            Ok(0)
> +        })
> +    }
> +
> +    unsafe extern "C" fn uninit_callback(netdev: *mut bindings::net_device) {
> +        // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
> +        let dev = unsafe { Device::from_ptr(netdev) };
> +        T::uninit(dev);
> +    }
> +
> +    unsafe extern "C" fn open_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
> +        from_result(|| {
> +            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
> +            let dev = unsafe { Device::from_ptr(netdev) };
> +            T::open(dev)?;
> +            Ok(0)
> +        })
> +    }
> +
> +    unsafe extern "C" fn stop_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
> +        from_result(|| {
> +            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
> +            let dev = unsafe { Device::from_ptr(netdev) };
> +            T::stop(dev)?;
> +            Ok(0)
> +        })
> +    }
> +
> +    unsafe extern "C" fn start_xmit_callback(
> +        skb: *mut bindings::sk_buff,
> +        netdev: *mut bindings::net_device,
> +    ) -> bindings::netdev_tx_t {
> +        // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
> +        let dev = unsafe { Device::from_ptr(netdev) };
> +        // SAFETY: The C API guarantees that `skb` is valid until a driver releases the skb.
> +        let skb = unsafe { SkBuff::from_ptr(skb) };
> +        T::start_xmit(dev, skb) as bindings::netdev_tx_t
> +    }
> +}
> +
> +// SAFETY: `Registration` exposes only `Device` object which can be used from any thread.
> +unsafe impl<T: DeviceOperations> Send for Registration<T> {}
> +// SAFETY: `Registration` exposes only `Device` object which can be used from any thread.
> +unsafe impl<T: DeviceOperations> Sync for Registration<T> {}
> +
> +/// Corresponds to the kernel's `enum netdev_tx`.
> +#[repr(i32)]
> +pub enum TxCode {
> +    /// Driver took care of packet.
> +    Ok = bindings::netdev_tx_NETDEV_TX_OK,
> +    /// Driver tx path was busy.
> +    Busy = bindings::netdev_tx_NETDEV_TX_BUSY,
> +}

Is it really necessary that this has the same representation as the
C constants? Would a function that converts be sufficient? I think we
should let the compiler decide the layout when we have no concrete
requirements.

> +
> +/// Corresponds to the kernel's `struct net_device_ops`.
> +///
> +/// A device driver must implement this. Only very basic operations are supported for now.
> +#[vtable]
> +pub trait DeviceOperations: ForeignOwnable + Send + Sync {
> +    /// Corresponds to `ndo_init` in `struct net_device_ops`.
> +    fn init(_dev: Device<Self>) -> Result {
> +        Ok(())
> +    }
> +
> +    /// Corresponds to `ndo_uninit` in `struct net_device_ops`.
> +    fn uninit(_dev: Device<Self>) {}
> +
> +    /// Corresponds to `ndo_open` in `struct net_device_ops`.
> +    fn open(_dev: Device<Self>) -> Result {
> +        Ok(())
> +    }
> +
> +    /// Corresponds to `ndo_stop` in `struct net_device_ops`.
> +    fn stop(_dev: Device<Self>) -> Result {
> +        Ok(())
> +    }
> +
> +    /// Corresponds to `ndo_start_xmit` in `struct net_device_ops`.
> +    fn start_xmit(_dev: Device<Self>, _skb: SkBuff) -> TxCode {
> +        TxCode::Busy
> +    }
> +}
> +
> +/// Corresponds to the kernel's `struct sk_buff`.
> +///
> +/// A driver manages `struct sk_buff` in two ways. In both ways, the ownership is transferred
> +/// between C and Rust. The allocation and release are done asymmetrically.
> +///
> +/// On the tx side (`ndo_start_xmit` operation in `struct net_device_ops`), the kernel allocates
> +/// a `sk_buff' object and passes it to the driver. The driver is responsible for the release
> +/// after transmission.
> +/// On the rx side, the driver allocates a `sk_buff` object then passes it to the kernel
> +/// after receiving data.
> +///
> +/// A driver must explicitly call a function to drop a `sk_buff` object.
> +/// The code to let a `SkBuff` object go out of scope can't be compiled.
> +///
> +/// # Invariants
> +///
> +/// The pointer is valid.
> +pub struct SkBuff(*mut bindings::sk_buff);
> +
> +impl SkBuff {
> +    /// Creates a new [`SkBuff`] instance.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that `ptr` must be valid.
> +    unsafe fn from_ptr(ptr: *mut bindings::sk_buff) -> Self {
> +        // INVARIANT: The safety requirements ensure the invariant.
> +        Self(ptr)
> +    }
> +
> +    /// Provides a time stamp.
> +    pub fn tx_timestamp(&mut self) {
> +        // SAFETY: The type invariants guarantee that `self.0` is valid.
> +        unsafe {
> +            bindings::skb_tx_timestamp(self.0);
> +        }
> +    }
> +
> +    /// Consumes a [`sk_buff`] object.
> +    pub fn consume(self) {
> +        // SAFETY: The type invariants guarantee that `self.0` is valid.
> +        unsafe {
> +            bindings::kfree_skb_reason(self.0, bindings::skb_drop_reason_SKB_CONSUMED);
> +        }
> +        core::mem::forget(self);

I read the prior discussion and I just wanted to make one thing sure:
dropping the `sk_buff` is not required for the safety of a program (of course
it still is a leak and that is not good), so it does not mean UB can occur
at some later point.

If leaking a `sk_buff` is fine, then I have no complaints, but we need to
keep this in mind when reviewing code that uses `sk_buff`, since there
using `forget` or other ways of leaking objects should not happen.

--
Cheers,
Benno

> +    }
> +}
> +
> +impl Drop for SkBuff {
> +    #[inline(always)]
> +    fn drop(&mut self) {
> +        build_error!("skb must be released explicitly");
> +    }
> +}
> --
> 2.34.1
> 


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

* Re: [PATCH v2 2/5] rust: add support for ethernet operations
  2023-07-10  7:37 ` [PATCH v2 2/5] rust: add support for ethernet operations FUJITA Tomonori
@ 2023-07-14 19:00   ` Benno Lossin
  0 siblings, 0 replies; 24+ messages in thread
From: Benno Lossin @ 2023-07-14 19:00 UTC (permalink / raw)
  To: FUJITA Tomonori
  Cc: rust-for-linux, netdev, kuba, andrew, aliceryhl, miguel.ojeda.sandonis

> This improves abstractions for network device drivers to implement
> struct ethtool_ops, the majority of ethernet device drivers need to
> do.
> 
> struct ethtool_ops also needs to access to device private data like
> struct net_device_ops.
> 
> Currently, only get_ts_info operation is supported. The following
> patch adds the Rust version of the dummy network driver, which uses
> the operation.
> 
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> ---
>  rust/bindings/bindings_helper.h |  1 +
>  rust/kernel/net/dev.rs          | 87 ++++++++++++++++++++++++++++++++-
>  2 files changed, 87 insertions(+), 1 deletion(-)
> 
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 468bf606f174..6446ff764980 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -8,6 +8,7 @@
> 
>  #include <linux/errname.h>
>  #include <linux/etherdevice.h>
> +#include <linux/ethtool.h>
>  #include <linux/netdevice.h>
>  #include <linux/slab.h>
>  #include <linux/refcount.h>
> diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs
> index fe20616668a9..ff00616e4fef 100644
> --- a/rust/kernel/net/dev.rs
> +++ b/rust/kernel/net/dev.rs
> @@ -142,7 +142,7 @@ pub fn register(&mut self) -> Result {
>          if self.is_registered {
>              return Err(code::EINVAL);
>          }
> -        // SAFETY: The type invariants guarantee that `self.dev.ptr` is valid.
> +        // SAFETY: The type invariants of `Device` guarantee that `self.dev.ptr` is valid.

Shouldn't this be squashed into patch 1?

>          let ret = unsafe {
>              (*self.dev.ptr).netdev_ops = &Self::DEVICE_OPS;
>              bindings::register_netdev(self.dev.ptr)
> @@ -155,6 +155,18 @@ pub fn register(&mut self) -> Result {
>          }
>      }
> 
> +    /// Sets `ethtool_ops` of `net_device`.
> +    pub fn set_ether_operations<E: EtherOperations>(&mut self) -> Result {
> +        if self.is_registered {
> +            return Err(code::EINVAL);
> +        }
> +        // SAFETY: The type invariants of `Device` guarantee that `self.dev.ptr` is valid.
> +        unsafe {
> +            (*(self.dev.ptr)).ethtool_ops = &EtherOperationsAdapter::<E>::ETHER_OPS;
> +        }
> +        Ok(())
> +    }
> +

I think it would make sense to also add

```
pub fn setup_ether_operations(&mut self) -> Result
where
    T: EtherOperations
{
    self.set_ether_operations::<T>();
}
```

Since normally you would implement `EtherOperations` for the same type
that also implement `DeviceOperations`, right?

>      const DEVICE_OPS: bindings::net_device_ops = bindings::net_device_ops {
>          ndo_init: if <T>::HAS_INIT {
>              Some(Self::init_callback)
> @@ -328,3 +340,76 @@ fn drop(&mut self) {
>          build_error!("skb must be released explicitly");
>      }
>  }
> +
> +/// Builds the kernel's `struct ethtool_ops`.
> +struct EtherOperationsAdapter<E: EtherOperations> {
> +    _p: PhantomData<E>,
> +}
> +
> +impl<E: EtherOperations> EtherOperationsAdapter<E> {
> +    unsafe extern "C" fn get_ts_info_callback(
> +        netdev: *mut bindings::net_device,
> +        info: *mut bindings::ethtool_ts_info,
> +    ) -> core::ffi::c_int {
> +        from_result(|| {
> +            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
> +            let dev = unsafe { Device::from_ptr(netdev) };
> +            // SAFETY: The C API guarantees that `info` is valid while this function is running.
> +            let info = unsafe { EthtoolTsInfo::from_ptr(info) };
> +            E::get_ts_info(dev, info)?;
> +            Ok(0)
> +        })
> +    }
> +
> +    const ETHER_OPS: bindings::ethtool_ops = bindings::ethtool_ops {
> +        get_ts_info: if <E>::HAS_GET_TS_INFO {
> +            Some(Self::get_ts_info_callback)
> +        } else {
> +            None
> +        },
> +        // SAFETY: The rest is zeroed out to initialize `struct ethtool_ops`,
> +        // set `Option<&F>` to be `None`.
> +        ..unsafe { core::mem::MaybeUninit::<bindings::ethtool_ops>::zeroed().assume_init() }
> +    };
> +}
> +
> +/// Corresponds to the kernel's `struct ethtool_ops`.
> +#[vtable]
> +pub trait EtherOperations: ForeignOwnable + Send + Sync {
> +    /// Corresponds to `get_ts_info` in `struct ethtool_ops`.
> +    fn get_ts_info(_dev: Device<Self>, _info: EthtoolTsInfo) -> Result {
> +        Err(Error::from_errno(bindings::EOPNOTSUPP as i32))

Note that you have to use `-(EOPNOTSUPP as i32)`. Maybe just add this
in `error.rs` via the `declare_err` macro.

--
Cheers,
Benno

> +    }
> +}
> +
> +/// Corresponds to the kernel's `struct ethtool_ts_info`.
> +///
> +/// # Invariants
> +///
> +/// The pointer is valid.
> +pub struct EthtoolTsInfo(*mut bindings::ethtool_ts_info);
> +
> +impl EthtoolTsInfo {
> +    /// Creates a new `EthtoolTsInfo' instance.
> +    ///
> +    /// # Safety
> +    ///
> +    /// Callers must ensure that `ptr` must be valid.
> +    unsafe fn from_ptr(ptr: *mut bindings::ethtool_ts_info) -> Self {
> +        // INVARIANT: The safety requirements ensure the invariant.
> +        Self(ptr)
> +    }
> +
> +    /// Sets up the info for software timestamping.
> +    pub fn ethtool_op_get_ts_info<D: ForeignOwnable + Send + Sync>(
> +        dev: &Device<D>,
> +        info: &mut EthtoolTsInfo,
> +    ) -> Result {
> +        // SAFETY: The type invariants of `Device` guarantee that `dev.ptr` are valid.
> +        // The type invariants guarantee that `info.0` are valid.
> +        unsafe {
> +            bindings::ethtool_op_get_ts_info(dev.ptr, info.0);
> +        }
> +        Ok(())
> +    }
> +}
> --
> 2.34.1
> 


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

* Re: [PATCH v2 3/5] rust: add methods for configure net_device
  2023-07-10  7:37 ` [PATCH v2 3/5] rust: add methods for configure net_device FUJITA Tomonori
@ 2023-07-14 19:01   ` Benno Lossin
  0 siblings, 0 replies; 24+ messages in thread
From: Benno Lossin @ 2023-07-14 19:01 UTC (permalink / raw)
  To: FUJITA Tomonori
  Cc: rust-for-linux, netdev, kuba, andrew, aliceryhl, miguel.ojeda.sandonis

> adds methods to net::Device for the basic configurations of
> net_device.
> 
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> ---
>  rust/helpers.c         |   7 ++
>  rust/kernel/net/dev.rs | 185 ++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 191 insertions(+), 1 deletion(-)
> 
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 70d50767ff4e..6c51deb18dc1 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -22,6 +22,7 @@
>  #include <linux/build_bug.h>
>  #include <linux/err.h>
>  #include <linux/errname.h>
> +#include <linux/etherdevice.h>
>  #include <linux/refcount.h>
>  #include <linux/mutex.h>
>  #include <linux/netdevice.h>
> @@ -31,6 +32,12 @@
>  #include <linux/wait.h>
> 
>  #ifdef CONFIG_NET
> +void rust_helper_eth_hw_addr_random(struct net_device *dev)
> +{
> +	eth_hw_addr_random(dev);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_eth_hw_addr_random);
> +
>  void *rust_helper_netdev_priv(const struct net_device *dev)
>  {
>  	return netdev_priv(dev);
> diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs
> index ff00616e4fef..e4d8d8260c10 100644
> --- a/rust/kernel/net/dev.rs
> +++ b/rust/kernel/net/dev.rs
> @@ -8,9 +8,116 @@
>  //! [`include/linux/skbuff.h`](../../../../include/linux/skbuff.h),
>  //! [`include/uapi/linux/if_link.h`](../../../../include/uapi/linux/if_link.h).
> 
> -use crate::{bindings, build_error, error::*, prelude::vtable, types::ForeignOwnable};
> +use crate::{bindings, build_error, error::*, prelude::vtable, str::CStr, types::ForeignOwnable};
>  use {core::ffi::c_void, core::marker::PhantomData};
> 
> +/// Flags associated with a [`Device`].
> +pub mod flags {
> +    /// Interface is up.
> +    pub const IFF_UP: u32 = bindings::net_device_flags_IFF_UP;
> +    /// Broadcast address valid.
> +    pub const IFF_BROADCAST: u32 = bindings::net_device_flags_IFF_BROADCAST;
> +    /// Device on debugging.
> +    pub const IFF_DEBUG: u32 = bindings::net_device_flags_IFF_DEBUG;
> +    /// Loopback device.
> +    pub const IFF_LOOPBACK: u32 = bindings::net_device_flags_IFF_LOOPBACK;
> +    /// Has p-p link.
> +    pub const IFF_POINTOPOINT: u32 = bindings::net_device_flags_IFF_POINTOPOINT;
> +    /// Avoids use of trailers.
> +    pub const IFF_NOTRAILERS: u32 = bindings::net_device_flags_IFF_NOTRAILERS;
> +    /// Interface RFC2863 OPER_UP.
> +    pub const IFF_RUNNING: u32 = bindings::net_device_flags_IFF_RUNNING;
> +    /// No ARP protocol.
> +    pub const IFF_NOARP: u32 = bindings::net_device_flags_IFF_NOARP;
> +    /// Receives all packets.
> +    pub const IFF_PROMISC: u32 = bindings::net_device_flags_IFF_PROMISC;
> +    /// Receive all multicast packets.
> +    pub const IFF_ALLMULTI: u32 = bindings::net_device_flags_IFF_ALLMULTI;
> +    /// Master of a load balancer.
> +    pub const IFF_MASTER: u32 = bindings::net_device_flags_IFF_MASTER;
> +    /// Slave of a load balancer.
> +    pub const IFF_SLAVE: u32 = bindings::net_device_flags_IFF_SLAVE;
> +    /// Supports multicast.
> +    pub const IFF_MULTICAST: u32 = bindings::net_device_flags_IFF_MULTICAST;
> +    /// Capable of setting media type.
> +    pub const IFF_PORTSEL: u32 = bindings::net_device_flags_IFF_PORTSEL;
> +    /// Auto media select active.
> +    pub const IFF_AUTOMEDIA: u32 = bindings::net_device_flags_IFF_AUTOMEDIA;
> +    /// Dialup device with changing addresses.
> +    pub const IFF_DYNAMIC: u32 = bindings::net_device_flags_IFF_DYNAMIC;
> +}
> +
> +/// Private flags associated with a [`Device`].
> +pub mod priv_flags {
> +    /// 802.1Q VLAN device.
> +    pub const IFF_802_1Q_VLAN: u64 = bindings::netdev_priv_flags_IFF_802_1Q_VLAN;
> +    /// Ethernet bridging device.
> +    pub const IFF_EBRIDGE: u64 = bindings::netdev_priv_flags_IFF_EBRIDGE;
> +    /// Bonding master or slave device.
> +    pub const IFF_BONDING: u64 = bindings::netdev_priv_flags_IFF_BONDING;
> +    /// ISATAP interface (RFC4214).
> +    pub const IFF_ISATAP: u64 = bindings::netdev_priv_flags_IFF_ISATAP;
> +    /// WAN HDLC device.
> +    pub const IFF_WAN_HDLC: u64 = bindings::netdev_priv_flags_IFF_WAN_HDLC;
> +    /// dev_hard_start_xmit() is allowed to release skb->dst.
> +    pub const IFF_XMIT_DST_RELEASE: u64 = bindings::netdev_priv_flags_IFF_XMIT_DST_RELEASE;
> +    /// Disallows bridging this ether device.
> +    pub const IFF_DONT_BRIDGE: u64 = bindings::netdev_priv_flags_IFF_DONT_BRIDGE;
> +    /// Disables netpoll at run-time.
> +    pub const IFF_DISABLE_NETPOLL: u64 = bindings::netdev_priv_flags_IFF_DISABLE_NETPOLL;
> +    /// Device used as macvlan port.
> +    pub const IFF_MACVLAN_PORT: u64 = bindings::netdev_priv_flags_IFF_MACVLAN_PORT;
> +    /// Device used as bridge port.
> +    pub const IFF_BRIDGE_PORT: u64 = bindings::netdev_priv_flags_IFF_BRIDGE_PORT;
> +    /// Device used as Open vSwitch datapath port.
> +    pub const IFF_OVS_DATAPATH: u64 = bindings::netdev_priv_flags_IFF_OVS_DATAPATH;
> +    /// The interface supports sharing skbs on transmit.
> +    pub const IFF_TX_SKB_SHARING: u64 = bindings::netdev_priv_flags_IFF_TX_SKB_SHARING;
> +    /// Supports unicast filtering.
> +    pub const IFF_UNICAST_FLT: u64 = bindings::netdev_priv_flags_IFF_UNICAST_FLT;
> +    /// Device used as team port.
> +    pub const IFF_TEAM_PORT: u64 = bindings::netdev_priv_flags_IFF_TEAM_PORT;
> +    /// Device supports sending custom FCS.
> +    pub const IFF_SUPP_NOFCS: u64 = bindings::netdev_priv_flags_IFF_SUPP_NOFCS;
> +    /// Device supports hardware address change when it's running.
> +    pub const IFF_LIVE_ADDR_CHANGE: u64 = bindings::netdev_priv_flags_IFF_LIVE_ADDR_CHANGE;
> +    /// Macvlan device.
> +    pub const IFF_MACVLAN: u64 = bindings::netdev_priv_flags_IFF_MACVLAN;
> +    /// IFF_XMIT_DST_RELEASE not taking into account underlying stacked devices.
> +    pub const IFF_XMIT_DST_RELEASE_PERM: u64 =
> +        bindings::netdev_priv_flags_IFF_XMIT_DST_RELEASE_PERM;
> +    /// L3 master device.
> +    pub const IFF_L3MDEV_MASTER: u64 = bindings::netdev_priv_flags_IFF_L3MDEV_MASTER;
> +    /// Device can run without qdisc attached.
> +    pub const IFF_NO_QUEUE: u64 = bindings::netdev_priv_flags_IFF_NO_QUEUE;
> +    /// Device is a Open vSwitch master.
> +    pub const IFF_OPENVSWITCH: u64 = bindings::netdev_priv_flags_IFF_OPENVSWITCH;
> +    /// Device is enslaved to an L3 master.
> +    pub const IFF_L3MDEV_SLAVE: u64 = bindings::netdev_priv_flags_IFF_L3MDEV_SLAVE;
> +    /// Team device.
> +    pub const IFF_TEAM: u64 = bindings::netdev_priv_flags_IFF_TEAM;
> +    /// Device has had Rx Flow indirection table configured.
> +    pub const IFF_RXFH_CONFIGURED: u64 = bindings::netdev_priv_flags_IFF_RXFH_CONFIGURED;
> +    /// The headroom value is controlled by an external entity.
> +    pub const IFF_PHONY_HEADROOM: u64 = bindings::netdev_priv_flags_IFF_PHONY_HEADROOM;
> +    /// MACsec device.
> +    pub const IFF_MACSEC: u64 = bindings::netdev_priv_flags_IFF_MACSEC;
> +    /// Device doesn't support the rx_handler hook.
> +    pub const IFF_NO_RX_HANDLER: u64 = bindings::netdev_priv_flags_IFF_NO_RX_HANDLER;
> +    /// Failover master device.
> +    pub const IFF_FAILOVER: u64 = bindings::netdev_priv_flags_IFF_FAILOVER;
> +    /// Lower device of a failover master device.
> +    pub const IFF_FAILOVER_SLAVE: u64 = bindings::netdev_priv_flags_IFF_FAILOVER_SLAVE;
> +    /// Only invokes the rx handler of L3 master device.
> +    pub const IFF_L3MDEV_RX_HANDLER: u64 = bindings::netdev_priv_flags_IFF_L3MDEV_RX_HANDLER;
> +    /// Prevents ipv6 addrconf.
> +    pub const IFF_NO_ADDRCONF: u64 = bindings::netdev_priv_flags_IFF_NO_ADDRCONF;
> +    /// Capable of xmitting frames with skb_headlen(skb) == 0.
> +    pub const IFF_TX_SKB_NO_LINEAR: u64 = bindings::netdev_priv_flags_IFF_TX_SKB_NO_LINEAR;
> +    /// Supports setting carrier via IFLA_PROTO_DOWN.
> +    pub const IFF_CHANGE_PROTO_DOWN: u64 = bindings::netdev_priv_flags_IFF_CHANGE_PROTO_DOWN;
> +}
> +
>  /// Corresponds to the kernel's `struct net_device`.
>  ///
>  /// # Invariants
> @@ -49,6 +156,82 @@ pub fn drv_priv_data(&self) -> D::Borrowed<'_> {
>              ))
>          }
>      }
> +
> +    /// Sets the name of a device.
> +    pub fn set_name(&mut self, name: &CStr) -> Result {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe {
> +            if name.len() > (*self.ptr).name.len() {
> +                return Err(code::EINVAL);
> +            }
> +            (*self.ptr)
> +                .name
> +                .as_mut_ptr()
> +                .copy_from_nonoverlapping(name.as_char_ptr(), name.len());
> +        }

Just to make sure, the `name` field in `net_device` does not need to
be nul-terminated, right?

--
Cheers,
Benno

> +        Ok(())
> +    }
> +
> +    /// Sets carrier.
> +    pub fn netif_carrier_on(&mut self) {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe { bindings::netif_carrier_on(self.ptr) }
> +    }
> +
> +    /// Clears carrier.
> +    pub fn netif_carrier_off(&mut self) {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe { bindings::netif_carrier_off(self.ptr) }
> +    }
> +
> +    /// Sets the max mtu of the device.
> +    pub fn set_max_mtu(&mut self, max_mtu: u32) {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe {
> +            (*self.ptr).max_mtu = max_mtu;
> +        }
> +    }
> +
> +    /// Sets the minimum mtu of the device.
> +    pub fn set_min_mtu(&mut self, min_mtu: u32) {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe {
> +            (*self.ptr).min_mtu = min_mtu;
> +        }
> +    }
> +
> +    /// Returns the flags of the device.
> +    pub fn get_flags(&self) -> u32 {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe { (*self.ptr).flags }
> +    }
> +
> +    /// Sets the flags of the device.
> +    pub fn set_flags(&mut self, flags: u32) {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe {
> +            (*self.ptr).flags = flags;
> +        }
> +    }
> +
> +    /// Returns the priv_flags of the device.
> +    pub fn get_priv_flags(&self) -> u64 {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe { (*self.ptr).priv_flags }
> +    }
> +
> +    /// Sets the priv_flags of the device.
> +    pub fn set_priv_flags(&mut self, flags: u64) {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe { (*self.ptr).priv_flags = flags }
> +    }
> +
> +    /// Generate a random Ethernet address (MAC) to be used by a net device
> +    /// and set addr_assign_type.
> +    pub fn set_random_eth_hw_addr(&mut self) {
> +        // SAFETY: The type invariants guarantee that `self.ptr` is valid.
> +        unsafe { bindings::eth_hw_addr_random(self.ptr) }
> +    }
>  }
> 
>  // SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used
> --
> 2.34.1
> 


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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-12  5:04     ` FUJITA Tomonori
@ 2023-06-12 13:26       ` Miguel Ojeda
  0 siblings, 0 replies; 24+ messages in thread
From: Miguel Ojeda @ 2023-06-12 13:26 UTC (permalink / raw)
  To: FUJITA Tomonori; +Cc: rust-for-linux, aliceryhl, andrew, fujita.tomonori

On Mon, Jun 12, 2023 at 7:04 AM FUJITA Tomonori <tomo@exabit.dev> wrote:
>
> Sorry, obviously, I didn't understand comments about safety. Aded `#
> Invariants` section to both structures.
>
> I've updated the comments and put the fixed version at the end. Can
> you check again? If it looks fine, I'll update the comments in the
> rest of patches.

Looks better now (without having checked if they are correct w.r.t.
what the C side guarantees) -- thanks! A couple comments below.

> +    /// Gets a pointer to network device private data.
> +    ///
> +    /// A driver in C uses contiguous memory `struct net_device` and its private data.
> +    /// A driver in Rust contiguous memory `struct net_device` and a pointer to its private data.
> +    /// So a driver in Rust pays extra cost to access to its private data.
> +    /// A device driver passes an properly initialized private data and the kernel saves
> +    /// its pointer.
> +    fn priv_data_ptr(&self) -> *const c_void {
> +        // SAFETY: The type invariants guarantee that `self.0` is valid.
> +        unsafe { core::ptr::read(bindings::netdev_priv(self.0) as *const *const c_void) }
> +    }

Part of the docs of this function should probably be moved to the
`SAFETY` comment, since it is needed to justify the `ptr::read` (and
it is an implementation detail): `self.0` is valid here, but we need
to justify why reading from the returned pointer from `netdev_priv` is
OK, and for that one needs to know the layout explained in the docs
above.

Also, in general, please always aim to minimize what `unsafe` blocks
cover. For instance, here there should be two blocks: one for the FFI
call, and the other for the pointer read. Splitting such blocks makes
it very clear that there are two different proofs needed here.

> +        // SAFETY: `ptr` is valid and non-null since `alloc_etherdev_mqs()`
> +        // returned a valid pointer which was null-checked.
> +        let dev = unsafe {
> +            let priv_ptr = bindings::netdev_priv(ptr) as *mut *const c_void;
> +            core::ptr::write(priv_ptr, data.into_foreign());
> +            Device::from_ptr(ptr)
> +        };

Similarly here, i.e. the `SAFETY` comment does not mention why is
`priv_ptr` valid for the `ptr::write`.

Cheers,
Miguel

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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-12  6:47     ` FUJITA Tomonori
@ 2023-06-12 12:46       ` Andrew Lunn
  0 siblings, 0 replies; 24+ messages in thread
From: Andrew Lunn @ 2023-06-12 12:46 UTC (permalink / raw)
  To: FUJITA Tomonori; +Cc: rust-for-linux, aliceryhl, fujita.tomonori

> Once someone takes up the issue, I guess that we could add new
> functions for other allocators.

Then it would be good to name this function to make it clear it
allocates and ethernet netdev, so leaving the namespace clean for
other allocators.

      Andrew

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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-10 14:11   ` Andrew Lunn
  2023-06-11  8:03     ` Alice Ryhl
@ 2023-06-12  6:47     ` FUJITA Tomonori
  2023-06-12 12:46       ` Andrew Lunn
  1 sibling, 1 reply; 24+ messages in thread
From: FUJITA Tomonori @ 2023-06-12  6:47 UTC (permalink / raw)
  To: andrew; +Cc: tomo, rust-for-linux, aliceryhl, fujita.tomonori

Hi,

On Sat, 10 Jun 2023 16:11:41 +0200
Andrew Lunn <andrew@lunn.ch> wrote:

>> +impl<D: DriverData, T: DeviceOperations<D>> Registration<T, D> {
>> +    /// Creates new instance of registration.
>> +    pub fn try_new(tx_queue_size: u32, rx_queue_size: u32, data: D::Data) -> Result<Self> {
>> +        // SAFETY: FFI call.
>> +        let ptr = from_err_ptr(unsafe {
>> +            bindings::alloc_etherdev_mqs(
>> +                core::mem::size_of::<*const c_void>() as i32,
>> +                tx_queue_size,
>> +                rx_queue_size,
>> +            )
> 
> In the future, there might be some naming issues there. Not every
> network device in Linux is an Ethernet network device. Some network
> drivers will call alloc_netdev, or some other specialised
> allocator. 

Yeah, I thought about it. Actually dummy.c uses alloc_netdev (then
calls ether_setup so does the same things that alloc_etherdev_mqs).

Wrapping alloc_etherdev_mqs is simpler because of a function pointer
argument in alloc_netdev so I used alloc_etherdev_mqs.

Once someone takes up the issue, I guess that we could add new
functions for other allocators.

thanks,



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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-10 19:49   ` Miguel Ojeda
@ 2023-06-12  5:04     ` FUJITA Tomonori
  2023-06-12 13:26       ` Miguel Ojeda
  0 siblings, 1 reply; 24+ messages in thread
From: FUJITA Tomonori @ 2023-06-12  5:04 UTC (permalink / raw)
  To: miguel.ojeda.sandonis
  Cc: tomo, rust-for-linux, aliceryhl, andrew, fujita.tomonori

Hi,
Thanks a lot for reviewing!

On Sat, 10 Jun 2023 21:49:50 +0200
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:

> On Sat, Jun 10, 2023 at 9:35 AM FUJITA Tomonori <tomo@exabit.dev> wrote:
>>
>> +/// Corresponds to the kernel's `struct net_device`.
>> +///
>> +/// # Safety
>> +///
>> +/// The kernel uses a `net_device` object with various functions like `struct net_device_ops`.
>> +/// This object is passed to Rust callbacks while these functions are running.
>> +/// The C API guarantees that `net_device` isn't released while this function is running.
>> +pub struct Device(*mut bindings::net_device);
> 
> This is not a function :) So "while this function is running" does not
> make too much sense here.
> 
> Also, this is a `struct`, so we do not use `# Safety` sections.
> Instead, you may want to give this struct a  `# Invariants` section,
> and explain what is guaranteed by this type.
> 
> Similarly for `SkBuff` below.

Sorry, obviously, I didn't understand comments about safety. Aded `#
Invariants` section to both structures.

I've updated the comments and put the fixed version at the end. Can
you check again? If it looks fine, I'll update the comments in the
rest of patches.


>> +    fn priv_data_ptr(netdev: *mut bindings::net_device) -> *const c_void {
>> +        // SAFETY: The safety requirement ensures that the pointer is valid.
>> +        unsafe { core::ptr::read(bindings::netdev_priv(&mut *netdev) as *const *const c_void) }
>> +    }
> 
> Like in the other patch, there is no safety requirement in this
> function, so you can't use that to justify it.
> 
> Even if this is a private function, it would be best if this is
> `unsafe`, then you can require callers to pass you a valid pointer --
> you already have a `SAFETY` comment in the callers anyway, and those
> are the ones that can actually claim that the pointer is valid.

Fixed.

>> +// SAFETY: `Device` exposes the state, `D::Data` object across threads
>> +// but that type is required to be Send and Sync.
>> +unsafe impl Send for Device {}
>> +unsafe impl Sync for Device {}
> 
> Please provide a `SAFETY` comment for each. See e.g. the ones we have
> for `ARef`.
> 
> In addition, what is `D::Data` here? I guess you are referring to
> `DriverData::Data` somehow, but it is unclear what is `D` here.
> 
> Also, please use Markdown in comments, not just documentation, to be
> consistent, e.g.
> 
>     ... to be `Send` and `Sync`.

Fixed both.

>> +/// # Safety
>> +///
>> +/// The pointer to the `net_device` object is guaranteed to be valid until
>> +/// the registration object is dropped.
>> +pub struct Registration<T: DeviceOperations<D>, D: DriverData> {
>> +    dev: Device,
>> +    is_registered: bool,
>> +    _p: PhantomData<(D, T)>,
>> +}
> 
> Ditto about this being a `struct` and `# Safety`.
>
> Also, if it is valid until dropped, then it is "always" valid (as far
> as someone having such an object is concerned), so there is no need to
> say so.

Fixed.


>> +    fn drop(&mut self) {
>> +        // SAFETY: `dev` was allocated during initialization and guaranteed to be valid.
> 
> Do you mean `dev.0`?
> 
>> +        // SAFETY: the kernel allocated the space for a pointer.
> 
> Please capitalize to be consistent.

Fixed both.

>> +        unsafe {
>> +            let priv_ptr = bindings::netdev_priv(ptr) as *mut *const c_void;
>> +            core::ptr::write(priv_ptr, data.into_foreign());
>> +        }
> 
>> +        ..unsafe { core::mem::MaybeUninit::<bindings::net_device_ops>::zeroed().assume_init() }
> 
> A comment on this would be nice. Also, missing `SAFETY` comment, even
> if it is a `const`.

Added.

Here's a fixed version.


rust: core abstractions for network device drivers

This patch adds very basic abstractions to implement network device
drivers, corresponds to the kernel's net_device and net_device_ops
structs with support for register_netdev/unregister_netdev functions.

allows the const_maybe_uninit_zeroed feature for
core::mem::MaybeUinit::<T>::zeroed() in const function.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 rust/bindings/bindings_helper.h |   2 +
 rust/helpers.c                  |  16 ++
 rust/kernel/lib.rs              |   3 +
 rust/kernel/net.rs              |   5 +
 rust/kernel/net/dev.rs          | 340 ++++++++++++++++++++++++++++++++
 5 files changed, 366 insertions(+)
 create mode 100644 rust/kernel/net.rs
 create mode 100644 rust/kernel/net/dev.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 50e7a76d5455..e3ebaaf9ab4f 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -6,6 +6,8 @@
  * Sorted alphabetically.
  */
 
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
 #include <linux/slab.h>
 #include <linux/refcount.h>
 #include <linux/wait.h>
diff --git a/rust/helpers.c b/rust/helpers.c
index 81e80261d597..a91d2a99035b 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -23,10 +23,26 @@
 #include <linux/err.h>
 #include <linux/refcount.h>
 #include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <linux/sched/signal.h>
 #include <linux/wait.h>
 
+#ifdef CONFIG_NET
+void *rust_helper_netdev_priv(const struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);
+
+void rust_helper_skb_tx_timestamp(struct sk_buff *skb)
+{
+	skb_tx_timestamp(skb);
+}
+EXPORT_SYMBOL_GPL(rust_helper_skb_tx_timestamp);
+#endif
+
 __noreturn void rust_helper_BUG(void)
 {
 	BUG();
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 85b261209977..fc7d048d359d 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -13,6 +13,7 @@
 
 #![no_std]
 #![feature(allocator_api)]
+#![feature(const_maybe_uninit_zeroed)]
 #![feature(coerce_unsized)]
 #![feature(dispatch_from_dyn)]
 #![feature(new_uninit)]
@@ -34,6 +35,8 @@
 pub mod error;
 pub mod init;
 pub mod ioctl;
+#[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..28fe8f398463
--- /dev/null
+++ b/rust/kernel/net.rs
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Networking core.
+
+pub mod dev;
diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs
new file mode 100644
index 000000000000..86f525b02479
--- /dev/null
+++ b/rust/kernel/net/dev.rs
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Network device.
+//!
+//! C headers: [`include/linux/etherdevice.h`](../../../../include/linux/etherdevice.h),
+//! [`include/linux/ethtool.h`](../../../../include/linux/ethtool.h),
+//! [`include/linux/netdevice.h`](../../../../include/linux/netdevice.h),
+//! [`include/linux/skbuff.h`](../../../../include/linux/skbuff.h),
+//! [`include/uapi/linux/if_link.h`](../../../../include/uapi/linux/if_link.h).
+
+use crate::{bindings, error::*, prelude::vtable, types::ForeignOwnable};
+use {core::ffi::c_void, core::marker::PhantomData};
+
+/// Corresponds to the kernel's `struct net_device`.
+///
+/// # Invariants
+///
+/// The pointer is valid.
+pub struct Device(*mut bindings::net_device);
+
+impl Device {
+    /// Creates a new [`Device`] instance.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `ptr` must be valid.
+    unsafe fn from_ptr(ptr: *mut bindings::net_device) -> Self {
+        // INVARIANT: The safety requirements ensure the invariant.
+        Self(ptr)
+    }
+
+    /// Gets a pointer to network device private data.
+    ///
+    /// A driver in C uses contiguous memory `struct net_device` and its private data.
+    /// A driver in Rust contiguous memory `struct net_device` and a pointer to its private data.
+    /// So a driver in Rust pays extra cost to access to its private data.
+    /// A device driver passes an properly initialized private data and the kernel saves
+    /// its pointer.
+    fn priv_data_ptr(&self) -> *const c_void {
+        // SAFETY: The type invariants guarantee that `self.0` is valid.
+        unsafe { core::ptr::read(bindings::netdev_priv(self.0) as *const *const c_void) }
+    }
+}
+
+// SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used
+// from any thread. `struct net_device` stores a pointer to `DriverData::Data`, which is `Sync`
+// so it's safe to sharing its pointer.
+unsafe impl Send for Device {}
+// SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used
+// from any thread. `struct net_device` stores a pointer to `DriverData::Data`, which is `Sync`,
+// can be used from any thread too.
+unsafe impl Sync for Device {}
+
+/// Trait for device driver specific information.
+///
+/// This data structure is passed to a driver with the operations for `struct net_device`
+/// like `struct net_device_ops`, `struct ethtool_ops`, `struct rtnl_link_ops`, etc.
+pub trait DriverData {
+    /// The object are stored in C object, `struct net_device`.
+    type Data: ForeignOwnable + Send + Sync;
+}
+
+/// Registration structure for a network device driver.
+///
+/// This allocates and owns a `struct net_device` object.
+/// Once the `net_device` object is registered via `register_netdev` function,
+/// the kernel calls various functions such as `struct net_device_ops` operations with
+/// the `net_device` object.
+///
+/// A driver must implement `struct net_device_ops` so the trait for it is tied.
+/// Other operations like `struct ethtool_ops` are optional.
+pub struct Registration<T: DeviceOperations<D>, D: DriverData> {
+    dev: Device,
+    is_registered: bool,
+    _p: PhantomData<(D, T)>,
+}
+
+impl<D: DriverData, T: DeviceOperations<D>> Drop for Registration<T, D> {
+    fn drop(&mut self) {
+        // SAFETY: The type invariants guarantee that `self.dev.0` is valid.
+        unsafe {
+            let _ = D::Data::from_foreign(self.dev.priv_data_ptr());
+            if self.is_registered {
+                bindings::unregister_netdev(self.dev.0);
+            }
+            bindings::free_netdev(self.dev.0);
+        }
+    }
+}
+
+impl<D: DriverData, T: DeviceOperations<D>> Registration<T, D> {
+    /// Creates a new [`Registration`] instance.
+    pub fn try_new(tx_queue_size: u32, rx_queue_size: u32, data: D::Data) -> Result<Self> {
+        // SAFETY: FFI call.
+        let ptr = from_err_ptr(unsafe {
+            bindings::alloc_etherdev_mqs(
+                core::mem::size_of::<*const c_void>() as i32,
+                tx_queue_size,
+                rx_queue_size,
+            )
+        })?;
+
+        // SAFETY: `ptr` is valid and non-null since `alloc_etherdev_mqs()`
+        // returned a valid pointer which was null-checked.
+        let dev = unsafe {
+            let priv_ptr = bindings::netdev_priv(ptr) as *mut *const c_void;
+            core::ptr::write(priv_ptr, data.into_foreign());
+            Device::from_ptr(ptr)
+        };
+        Ok(Registration {
+            dev,
+            is_registered: false,
+            _p: PhantomData,
+        })
+    }
+
+    /// Returns a network device.
+    ///
+    /// A device driver normally configures the device before registration.
+    pub fn dev_get(&mut self) -> &mut Device {
+        &mut self.dev
+    }
+
+    /// Registers a network device.
+    pub fn register(&mut self) -> Result {
+        if self.is_registered {
+            return Err(code::EINVAL);
+        }
+        // SAFETY: The type invariants guarantee that `self.dev.0` is valid.
+        let ret = unsafe {
+            (*self.dev.0).netdev_ops = Self::build_device_ops();
+            bindings::register_netdev(self.dev.0)
+        };
+        if ret != 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            self.is_registered = true;
+            Ok(())
+        }
+    }
+
+    const DEVICE_OPS: bindings::net_device_ops = bindings::net_device_ops {
+        ndo_init: if <T>::HAS_INIT {
+            Some(Self::init_callback)
+        } else {
+            None
+        },
+        ndo_uninit: if <T>::HAS_UNINIT {
+            Some(Self::uninit_callback)
+        } else {
+            None
+        },
+        ndo_open: if <T>::HAS_OPEN {
+            Some(Self::open_callback)
+        } else {
+            None
+        },
+        ndo_stop: if <T>::HAS_STOP {
+            Some(Self::stop_callback)
+        } else {
+            None
+        },
+        ndo_start_xmit: if <T>::HAS_START_XMIT {
+            Some(Self::start_xmit_callback)
+        } else {
+            None
+        },
+        // SAFETY: The rest is zeroed out to initialize `struct net_device_ops`,
+        // set `Option<&T>` to be `None`.
+        ..unsafe { core::mem::MaybeUninit::<bindings::net_device_ops>::zeroed().assume_init() }
+    };
+
+    const fn build_device_ops() -> &'static bindings::net_device_ops {
+        &Self::DEVICE_OPS
+    }
+
+    unsafe extern "C" fn init_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+            let mut dev = unsafe { Device::from_ptr(netdev) };
+            // SAFETY: The returned pointer was initialized by `D::Data::into_foreign` when
+            // `Registration` object was created.
+            // `D::Data::from_foreign` is only called by the object was released.
+            // so we know `data` is valid while this function is running.
+            let data = unsafe { D::Data::borrow(dev.priv_data_ptr()) };
+            T::init(&mut dev, data)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn uninit_callback(netdev: *mut bindings::net_device) {
+        // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+        let mut dev = unsafe { Device::from_ptr(netdev) };
+        // SAFETY: The returned pointer was initialized by `D::Data::into_foreign` when
+        // `Registration` object was created.
+        // `D::Data::from_foreign` is only called by the object was released.
+        // so we know `data` is valid while this function is running.
+        let data = unsafe { D::Data::borrow(dev.priv_data_ptr()) };
+        T::uninit(&mut dev, data);
+    }
+
+    unsafe extern "C" fn open_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+            let mut dev = unsafe { Device::from_ptr(netdev) };
+            // SAFETY: The returned pointer was initialized by `D::Data::into_foreign` when
+            // `Registration` object was created.
+            // `D::Data::from_foreign` is only called by the object was released.
+            // so we know `data` is valid while this function is running.
+            let data = unsafe { D::Data::borrow(dev.priv_data_ptr()) };
+            T::open(&mut dev, data)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn stop_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+            let mut dev = unsafe { Device::from_ptr(netdev) };
+            // SAFETY: The returned pointer was initialized by `D::Data::into_foreign` when
+            // `Registration` object was created.
+            // `D::Data::from_foreign` is only called by the object was released.
+            // so we know `data` is valid while this function is running.
+            let data = unsafe { D::Data::borrow(dev.priv_data_ptr()) };
+            T::stop(&mut dev, data)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn start_xmit_callback(
+        skb: *mut bindings::sk_buff,
+        netdev: *mut bindings::net_device,
+    ) -> bindings::netdev_tx_t {
+        // SAFETY: The C API guarantees that `netdev` is valid while this function is running.
+        let mut dev = unsafe { Device::from_ptr(netdev) };
+        // SAFETY: The returned pointer was initialized by `D::Data::into_foreign` when
+        // `Registration` object was created.
+        // `D::Data::from_foreign` is only called by the object was released.
+        // so we know `data` is valid while this function is running.
+        let data = unsafe { D::Data::borrow(dev.priv_data_ptr()) };
+        // SAFETY: The C API guarantees that `skb` is valid while this function is running.
+        let skb = unsafe { SkBuff::from_ptr(skb) };
+        T::start_xmit(&mut dev, data, skb) as bindings::netdev_tx_t
+    }
+}
+
+// SAFETY: `Registration` exposes its state, `Device` object across threads
+// but that type is `Sync`.
+unsafe impl<D: DriverData, T: DeviceOperations<D>> Send for Registration<T, D> {}
+unsafe impl<D: DriverData, T: DeviceOperations<D>> Sync for Registration<T, D> {}
+
+/// Corresponds to the kernel's `enum netdev_tx`.
+#[repr(i32)]
+pub enum TxCode {
+    /// Driver took care of packet.
+    Ok = bindings::netdev_tx_NETDEV_TX_OK,
+    /// Driver tx path was busy.
+    Busy = bindings::netdev_tx_NETDEV_TX_BUSY,
+}
+
+/// Corresponds to the kernel's `struct net_device_ops`.
+///
+/// A device driver must implement this. Only very basic operations are supported for now.
+#[vtable]
+pub trait DeviceOperations<D: DriverData> {
+    /// Corresponds to `ndo_init` in `struct net_device_ops`.
+    fn init(_dev: &mut Device, _data: <D::Data as ForeignOwnable>::Borrowed<'_>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_uninit` in `struct net_device_ops`.
+    fn uninit(_dev: &mut Device, _data: <D::Data as ForeignOwnable>::Borrowed<'_>) {}
+
+    /// Corresponds to `ndo_open` in `struct net_device_ops`.
+    fn open(_dev: &mut Device, _data: <D::Data as ForeignOwnable>::Borrowed<'_>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_stop` in `struct net_device_ops`.
+    fn stop(_dev: &mut Device, _data: <D::Data as ForeignOwnable>::Borrowed<'_>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_start_xmit` in `struct net_device_ops`.
+    fn start_xmit(
+        _dev: &mut Device,
+        _data: <D::Data as ForeignOwnable>::Borrowed<'_>,
+        _skb: SkBuff,
+    ) -> TxCode {
+        TxCode::Busy
+    }
+}
+
+/// Corresponds to the kernel's `struct sk_buff`.
+///
+/// A driver manages `struct sk_buff` in two ways. In both ways, the ownership is transferred
+/// between C and Rust. The allocation and release are done asymmetrically.
+///
+/// On the tx side (`ndo_start_xmit` operation in `struct net_device_ops`), the kernel allocates
+/// a `sk_buff' object and passes it to the driver. The driver is responsible for the release
+/// after transmission.
+/// On the rx side, the driver allocates a `sk_buff` object then passes it to the kernel
+/// after receiving data.
+///
+/// # Invariants
+///
+/// The pointer is valid.
+pub struct SkBuff(*mut bindings::sk_buff);
+
+impl SkBuff {
+    /// Creates a new [`SkBuff`] instance.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `ptr` must be valid.
+    unsafe fn from_ptr(ptr: *mut bindings::sk_buff) -> Self {
+        // INVARIANT: The safety requirements ensure the invariant.
+        Self(ptr)
+    }
+
+    /// Provides a time stamp.
+    pub fn tx_timestamp(&mut self) {
+        // SAFETY: The type invariants guarantee that `self.0` is valid.
+        unsafe {
+            bindings::skb_tx_timestamp(self.0);
+        }
+    }
+}
+
+impl Drop for SkBuff {
+    fn drop(&mut self) {
+        // SAFETY: The type invariants guarantee that `self.0` is valid.
+        unsafe {
+            bindings::kfree_skb_reason(
+                self.0,
+                bindings::skb_drop_reason_SKB_DROP_REASON_NOT_SPECIFIED,
+            )
+        }
+    }
+}
-- 
2.34.1


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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-11 15:30       ` Andrew Lunn
@ 2023-06-11 17:48         ` Miguel Ojeda
  0 siblings, 0 replies; 24+ messages in thread
From: Miguel Ojeda @ 2023-06-11 17:48 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Alice Ryhl, FUJITA Tomonori, rust-for-linux, aliceryhl,
	FUJITA Tomonori, Andreas Hindborg

On Sun, Jun 11, 2023 at 6:01 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> Ah, thanks for the explanation.
>
> This is going to be a problem for networking. The hot path has a lot
> of inline functions, because a function call is expensive. So there
> are going to be a lot of little wrappers like this. I don't want to
> encourage early optimisation without proper profiling, but at some
> point you might want to replace these wrappers with Rust, using
> whatever its equivalent of inline is.

Yeah, other use cases will also need that solved, e.g. Andreas for his
NVMe work.

We discussed reimplementing performance-critical bits in Rust as you
suggest, as well as cross-language LTO. We also talked about possible
alternative approaches like "manual local LTO" for the helpers only
via feeding their LLVM IR to `rustc`, which may recover most of the
performance without having to go for full LTO and its associated
kernel link times.

Cheers,
Miguel

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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-11  8:03     ` Alice Ryhl
@ 2023-06-11 15:30       ` Andrew Lunn
  2023-06-11 17:48         ` Miguel Ojeda
  0 siblings, 1 reply; 24+ messages in thread
From: Andrew Lunn @ 2023-06-11 15:30 UTC (permalink / raw)
  To: Alice Ryhl; +Cc: FUJITA Tomonori, rust-for-linux, aliceryhl, FUJITA Tomonori

On Sun, Jun 11, 2023 at 10:03:39AM +0200, Alice Ryhl wrote:
> On 6/10/23 16:11, Andrew Lunn wrote:
> > > diff --git a/rust/helpers.c b/rust/helpers.c
> > > index 81e80261d597..a91d2a99035b 100644
> > > --- a/rust/helpers.c
> > > +++ b/rust/helpers.c
> > > @@ -23,10 +23,26 @@
> > >   #include <linux/err.h>
> > >   #include <linux/refcount.h>
> > >   #include <linux/mutex.h>
> > > +#include <linux/netdevice.h>
> > > +#include <linux/skbuff.h>
> > >   #include <linux/spinlock.h>
> > >   #include <linux/sched/signal.h>
> > >   #include <linux/wait.h>
> > > +#ifdef CONFIG_NET
> > > +void *rust_helper_netdev_priv(const struct net_device *dev)
> > > +{
> > > +	return netdev_priv(dev);
> > > +}
> > > +EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);
> > > +
> > > +void rust_helper_skb_tx_timestamp(struct sk_buff *skb)
> > > +{
> > > +	skb_tx_timestamp(skb);
> > > +}
> > > +EXPORT_SYMBOL_GPL(rust_helper_skb_tx_timestamp);
> > 
> > This question is probably due to me not knowing about rust. Why have
> > these helpers? They don't appear to don't do anything.
> 
> It's because we can't call inline C functions directly, so we need a
> non-inline version of it. (Similarly for #defines.)

Ah, thanks for the explanation.

This is going to be a problem for networking. The hot path has a lot
of inline functions, because a function call is expensive. So there
are going to be a lot of little wrappers like this. I don't want to
encourage early optimisation without proper profiling, but at some
point you might want to replace these wrappers with Rust, using
whatever its equivalent of inline is.

	 Andrew

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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-10 14:11   ` Andrew Lunn
@ 2023-06-11  8:03     ` Alice Ryhl
  2023-06-11 15:30       ` Andrew Lunn
  2023-06-12  6:47     ` FUJITA Tomonori
  1 sibling, 1 reply; 24+ messages in thread
From: Alice Ryhl @ 2023-06-11  8:03 UTC (permalink / raw)
  To: Andrew Lunn, FUJITA Tomonori; +Cc: rust-for-linux, aliceryhl, FUJITA Tomonori

On 6/10/23 16:11, Andrew Lunn wrote:
>> diff --git a/rust/helpers.c b/rust/helpers.c
>> index 81e80261d597..a91d2a99035b 100644
>> --- a/rust/helpers.c
>> +++ b/rust/helpers.c
>> @@ -23,10 +23,26 @@
>>   #include <linux/err.h>
>>   #include <linux/refcount.h>
>>   #include <linux/mutex.h>
>> +#include <linux/netdevice.h>
>> +#include <linux/skbuff.h>
>>   #include <linux/spinlock.h>
>>   #include <linux/sched/signal.h>
>>   #include <linux/wait.h>
>>   
>> +#ifdef CONFIG_NET
>> +void *rust_helper_netdev_priv(const struct net_device *dev)
>> +{
>> +	return netdev_priv(dev);
>> +}
>> +EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);
>> +
>> +void rust_helper_skb_tx_timestamp(struct sk_buff *skb)
>> +{
>> +	skb_tx_timestamp(skb);
>> +}
>> +EXPORT_SYMBOL_GPL(rust_helper_skb_tx_timestamp);
> 
> This question is probably due to me not knowing about rust. Why have
> these helpers? They don't appear to don't do anything.

It's because we can't call inline C functions directly, so we need a 
non-inline version of it. (Similarly for #defines.)

Alice

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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-10  7:20 ` [PATCH v2 1/5] rust: core " FUJITA Tomonori
  2023-06-10 14:11   ` Andrew Lunn
@ 2023-06-10 19:49   ` Miguel Ojeda
  2023-06-12  5:04     ` FUJITA Tomonori
  1 sibling, 1 reply; 24+ messages in thread
From: Miguel Ojeda @ 2023-06-10 19:49 UTC (permalink / raw)
  To: FUJITA Tomonori; +Cc: rust-for-linux, aliceryhl, andrew, FUJITA Tomonori

On Sat, Jun 10, 2023 at 9:35 AM FUJITA Tomonori <tomo@exabit.dev> wrote:
>
> +/// Corresponds to the kernel's `struct net_device`.
> +///
> +/// # Safety
> +///
> +/// The kernel uses a `net_device` object with various functions like `struct net_device_ops`.
> +/// This object is passed to Rust callbacks while these functions are running.
> +/// The C API guarantees that `net_device` isn't released while this function is running.
> +pub struct Device(*mut bindings::net_device);

This is not a function :) So "while this function is running" does not
make too much sense here.

Also, this is a `struct`, so we do not use `# Safety` sections.
Instead, you may want to give this struct a  `# Invariants` section,
and explain what is guaranteed by this type.

Similarly for `SkBuff` below.

> +    fn priv_data_ptr(netdev: *mut bindings::net_device) -> *const c_void {
> +        // SAFETY: The safety requirement ensures that the pointer is valid.
> +        unsafe { core::ptr::read(bindings::netdev_priv(&mut *netdev) as *const *const c_void) }
> +    }

Like in the other patch, there is no safety requirement in this
function, so you can't use that to justify it.

Even if this is a private function, it would be best if this is
`unsafe`, then you can require callers to pass you a valid pointer --
you already have a `SAFETY` comment in the callers anyway, and those
are the ones that can actually claim that the pointer is valid.

> +// SAFETY: `Device` exposes the state, `D::Data` object across threads
> +// but that type is required to be Send and Sync.
> +unsafe impl Send for Device {}
> +unsafe impl Sync for Device {}

Please provide a `SAFETY` comment for each. See e.g. the ones we have
for `ARef`.

In addition, what is `D::Data` here? I guess you are referring to
`DriverData::Data` somehow, but it is unclear what is `D` here.

Also, please use Markdown in comments, not just documentation, to be
consistent, e.g.

    ... to be `Send` and `Sync`.

> +/// # Safety
> +///
> +/// The pointer to the `net_device` object is guaranteed to be valid until
> +/// the registration object is dropped.
> +pub struct Registration<T: DeviceOperations<D>, D: DriverData> {
> +    dev: Device,
> +    is_registered: bool,
> +    _p: PhantomData<(D, T)>,
> +}

Ditto about this being a `struct` and `# Safety`.

Also, if it is valid until dropped, then it is "always" valid (as far
as someone having such an object is concerned), so there is no need to
say so.

> +    fn drop(&mut self) {
> +        // SAFETY: `dev` was allocated during initialization and guaranteed to be valid.

Do you mean `dev.0`?

> +        // SAFETY: the kernel allocated the space for a pointer.

Please capitalize to be consistent.

> +        unsafe {
> +            let priv_ptr = bindings::netdev_priv(ptr) as *mut *const c_void;
> +            core::ptr::write(priv_ptr, data.into_foreign());
> +        }

> +        ..unsafe { core::mem::MaybeUninit::<bindings::net_device_ops>::zeroed().assume_init() }

A comment on this would be nice. Also, missing `SAFETY` comment, even
if it is a `const`.

Thanks!

Cheers,
Miguel

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

* Re: [PATCH v2 1/5] rust: core abstractions for network device drivers
  2023-06-10  7:20 ` [PATCH v2 1/5] rust: core " FUJITA Tomonori
@ 2023-06-10 14:11   ` Andrew Lunn
  2023-06-11  8:03     ` Alice Ryhl
  2023-06-12  6:47     ` FUJITA Tomonori
  2023-06-10 19:49   ` Miguel Ojeda
  1 sibling, 2 replies; 24+ messages in thread
From: Andrew Lunn @ 2023-06-10 14:11 UTC (permalink / raw)
  To: FUJITA Tomonori; +Cc: rust-for-linux, aliceryhl, FUJITA Tomonori

> diff --git a/rust/helpers.c b/rust/helpers.c
> index 81e80261d597..a91d2a99035b 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -23,10 +23,26 @@
>  #include <linux/err.h>
>  #include <linux/refcount.h>
>  #include <linux/mutex.h>
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
>  #include <linux/spinlock.h>
>  #include <linux/sched/signal.h>
>  #include <linux/wait.h>
>  
> +#ifdef CONFIG_NET
> +void *rust_helper_netdev_priv(const struct net_device *dev)
> +{
> +	return netdev_priv(dev);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);
> +
> +void rust_helper_skb_tx_timestamp(struct sk_buff *skb)
> +{
> +	skb_tx_timestamp(skb);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_skb_tx_timestamp);

This question is probably due to me not knowing about rust. Why have
these helpers? They don't appear to don't do anything.

> +impl<D: DriverData, T: DeviceOperations<D>> Registration<T, D> {
> +    /// Creates new instance of registration.
> +    pub fn try_new(tx_queue_size: u32, rx_queue_size: u32, data: D::Data) -> Result<Self> {
> +        // SAFETY: FFI call.
> +        let ptr = from_err_ptr(unsafe {
> +            bindings::alloc_etherdev_mqs(
> +                core::mem::size_of::<*const c_void>() as i32,
> +                tx_queue_size,
> +                rx_queue_size,
> +            )

In the future, there might be some naming issues there. Not every
network device in Linux is an Ethernet network device. Some network
drivers will call alloc_netdev, or some other specialised
allocator. 

	   Andrew

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

* [PATCH v2 1/5] rust: core abstractions for network device drivers
       [not found] <20230610071848.3722492-1-tomo@exabit.dev>
@ 2023-06-10  7:20 ` FUJITA Tomonori
  2023-06-10 14:11   ` Andrew Lunn
  2023-06-10 19:49   ` Miguel Ojeda
  0 siblings, 2 replies; 24+ messages in thread
From: FUJITA Tomonori @ 2023-06-10  7:20 UTC (permalink / raw)
  To: rust-for-linux; +Cc: aliceryhl, andrew, FUJITA Tomonori

From: FUJITA Tomonori <fujita.tomonori@gmail.com>

This patch adds very basic abstractions to implement network device
drivers, corresponds to the kernel's net_device and net_device_ops
structs with support for register_netdev/unregister_netdev functions.

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 rust/bindings/bindings_helper.h |   2 +
 rust/helpers.c                  |  16 ++
 rust/kernel/lib.rs              |   3 +
 rust/kernel/net.rs              |   5 +
 rust/kernel/net/dev.rs          | 292 ++++++++++++++++++++++++++++++++
 5 files changed, 318 insertions(+)
 create mode 100644 rust/kernel/net.rs
 create mode 100644 rust/kernel/net/dev.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 50e7a76d5455..e3ebaaf9ab4f 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -6,6 +6,8 @@
  * Sorted alphabetically.
  */
 
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
 #include <linux/slab.h>
 #include <linux/refcount.h>
 #include <linux/wait.h>
diff --git a/rust/helpers.c b/rust/helpers.c
index 81e80261d597..a91d2a99035b 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -23,10 +23,26 @@
 #include <linux/err.h>
 #include <linux/refcount.h>
 #include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <linux/sched/signal.h>
 #include <linux/wait.h>
 
+#ifdef CONFIG_NET
+void *rust_helper_netdev_priv(const struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);
+
+void rust_helper_skb_tx_timestamp(struct sk_buff *skb)
+{
+	skb_tx_timestamp(skb);
+}
+EXPORT_SYMBOL_GPL(rust_helper_skb_tx_timestamp);
+#endif
+
 __noreturn void rust_helper_BUG(void)
 {
 	BUG();
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 85b261209977..fc7d048d359d 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -13,6 +13,7 @@
 
 #![no_std]
 #![feature(allocator_api)]
+#![feature(const_maybe_uninit_zeroed)]
 #![feature(coerce_unsized)]
 #![feature(dispatch_from_dyn)]
 #![feature(new_uninit)]
@@ -34,6 +35,8 @@
 pub mod error;
 pub mod init;
 pub mod ioctl;
+#[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..28fe8f398463
--- /dev/null
+++ b/rust/kernel/net.rs
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Networking core.
+
+pub mod dev;
diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs
new file mode 100644
index 000000000000..407d7d5bff2c
--- /dev/null
+++ b/rust/kernel/net/dev.rs
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Network device.
+//!
+//! C headers: [`include/linux/etherdevice.h`](../../../../include/linux/etherdevice.h),
+//! [`include/linux/ethtool.h`](../../../../include/linux/ethtool.h),
+//! [`include/linux/netdevice.h`](../../../../include/linux/netdevice.h),
+//! [`include/linux/skbuff.h`](../../../../include/linux/skbuff.h),
+//! [`include/uapi/linux/if_link.h`](../../../../include/uapi/linux/if_link.h).
+
+use crate::{bindings, error::*, prelude::vtable, types::ForeignOwnable};
+use {core::ffi::c_void, core::marker::PhantomData};
+
+/// Corresponds to the kernel's `struct net_device`.
+///
+/// # Safety
+///
+/// The kernel uses a `net_device` object with various functions like `struct net_device_ops`.
+/// This object is passed to Rust callbacks while these functions are running.
+/// The C API guarantees that `net_device` isn't released while this function is running.
+pub struct Device(*mut bindings::net_device);
+
+impl Device {
+    /// Gets a pointer to network device private data.
+    ///
+    /// A driver in C uses contiguous memory `struct net_device` and its private data.
+    /// A driver in Rust contiguous memory `struct net_device` and a pointer to its private data.
+    /// So a driver in Rust pays extra cost to access to its private data.
+    /// A device driver passes an properly initialized private data and the kernel saves its pointer.
+    fn priv_data_ptr(netdev: *mut bindings::net_device) -> *const c_void {
+        // SAFETY: The safety requirement ensures that the pointer is valid.
+        unsafe { core::ptr::read(bindings::netdev_priv(&mut *netdev) as *const *const c_void) }
+    }
+}
+
+// SAFETY: `Device` exposes the state, `D::Data` object across threads
+// but that type is required to be Send and Sync.
+unsafe impl Send for Device {}
+unsafe impl Sync for Device {}
+
+/// Trait for device driver specific information.
+///
+/// This data structure is passed to a driver with the operations for `struct net_device`
+/// like `struct net_device_ops`, `struct ethtool_ops`, `struct rtnl_link_ops`, etc.
+pub trait DriverData {
+    /// The object are stored in C object, `struct net_device`.
+    type Data: ForeignOwnable + Send + Sync;
+}
+
+/// Registration structure for a network device driver.
+///
+/// This allocates and owns a `struct net_device` object.
+/// Once the `net_device` object is registered via `register_netdev` function,
+/// the kernel calls various functions such as `struct net_device_ops` operations with
+/// the `net_device` object.
+///
+/// A driver must implement `struct net_device_ops` so the trait for it is tied.
+/// Other operations like `struct ethtool_ops` are optional.
+///
+/// # Safety
+///
+/// The pointer to the `net_device` object is guaranteed to be valid until
+/// the registration object is dropped.
+pub struct Registration<T: DeviceOperations<D>, D: DriverData> {
+    dev: Device,
+    is_registered: bool,
+    _p: PhantomData<(D, T)>,
+}
+
+impl<D: DriverData, T: DeviceOperations<D>> Drop for Registration<T, D> {
+    fn drop(&mut self) {
+        // SAFETY: `dev` was allocated during initialization and guaranteed to be valid.
+        unsafe {
+            if self.is_registered {
+                bindings::unregister_netdev(self.dev.0);
+            }
+            let _ = D::Data::from_foreign(Device::priv_data_ptr(self.dev.0));
+            bindings::free_netdev(self.dev.0);
+        }
+    }
+}
+
+impl<D: DriverData, T: DeviceOperations<D>> Registration<T, D> {
+    /// Creates new instance of registration.
+    pub fn try_new(tx_queue_size: u32, rx_queue_size: u32, data: D::Data) -> Result<Self> {
+        // SAFETY: FFI call.
+        let ptr = from_err_ptr(unsafe {
+            bindings::alloc_etherdev_mqs(
+                core::mem::size_of::<*const c_void>() as i32,
+                tx_queue_size,
+                rx_queue_size,
+            )
+        })?;
+
+        // SAFETY: the kernel allocated the space for a pointer.
+        unsafe {
+            let priv_ptr = bindings::netdev_priv(ptr) as *mut *const c_void;
+            core::ptr::write(priv_ptr, data.into_foreign());
+        }
+        Ok(Registration {
+            dev: Device(ptr),
+            is_registered: false,
+            _p: PhantomData,
+        })
+    }
+
+    /// Returns a network device.
+    ///
+    /// A device driver normally configures the device before registration.
+    pub fn dev_get(&mut self) -> &mut Device {
+        &mut self.dev
+    }
+
+    /// Registers a network device.
+    pub fn register(&mut self) -> Result {
+        if self.is_registered {
+            return Err(code::EINVAL);
+        }
+        // SAFETY: `dev` was allocated during initialization and is guaranteed to be valid.
+        let ret = unsafe {
+            (*self.dev.0).netdev_ops = Self::build_device_ops();
+            bindings::register_netdev(self.dev.0)
+        };
+        if ret != 0 {
+            Err(Error::from_errno(ret))
+        } else {
+            self.is_registered = true;
+            Ok(())
+        }
+    }
+
+    const DEVICE_OPS: bindings::net_device_ops = bindings::net_device_ops {
+        ndo_init: if <T>::HAS_INIT {
+            Some(Self::init_callback)
+        } else {
+            None
+        },
+        ndo_uninit: if <T>::HAS_UNINIT {
+            Some(Self::uninit_callback)
+        } else {
+            None
+        },
+        ndo_open: if <T>::HAS_OPEN {
+            Some(Self::open_callback)
+        } else {
+            None
+        },
+        ndo_stop: if <T>::HAS_STOP {
+            Some(Self::stop_callback)
+        } else {
+            None
+        },
+        ndo_start_xmit: if <T>::HAS_START_XMIT {
+            Some(Self::start_xmit_callback)
+        } else {
+            None
+        },
+        ..unsafe { core::mem::MaybeUninit::<bindings::net_device_ops>::zeroed().assume_init() }
+    };
+
+    const fn build_device_ops() -> &'static bindings::net_device_ops {
+        &Self::DEVICE_OPS
+    }
+
+    unsafe extern "C" fn init_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `net_device` isn't released while this function is running.
+            let data = unsafe { D::Data::borrow(Device::priv_data_ptr(netdev)) };
+            let mut dev = Device(netdev);
+            T::init(&mut dev, data)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn uninit_callback(netdev: *mut bindings::net_device) {
+        // SAFETY: The C API guarantees that `net_device` isn't released while this function is running.
+        let data = unsafe { D::Data::borrow(Device::priv_data_ptr(netdev)) };
+        let mut dev = Device(netdev);
+        T::uninit(&mut dev, data);
+    }
+
+    unsafe extern "C" fn open_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `net_device` isn't released while this function is running.
+            let data = unsafe { D::Data::borrow(Device::priv_data_ptr(netdev)) };
+            let mut dev = Device(netdev);
+            T::open(&mut dev, data)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn stop_callback(netdev: *mut bindings::net_device) -> core::ffi::c_int {
+        from_result(|| {
+            // SAFETY: The C API guarantees that `net_device` isn't released while this function is running.
+            let data = unsafe { D::Data::borrow(Device::priv_data_ptr(netdev)) };
+            let mut dev = Device(netdev);
+            T::stop(&mut dev, data)?;
+            Ok(0)
+        })
+    }
+
+    unsafe extern "C" fn start_xmit_callback(
+        skb: *mut bindings::sk_buff,
+        netdev: *mut bindings::net_device,
+    ) -> bindings::netdev_tx_t {
+        // SAFETY: The C API guarantees that `net_device` isn't released while this function is running.
+        let mut dev = Device(netdev);
+        let data = unsafe { D::Data::borrow(Device::priv_data_ptr(netdev)) };
+        // SAFETY: The C API guarantees that `sk_buff` isn't released while this function is running.
+        let skb = SkBuff(skb);
+        T::start_xmit(&mut dev, data, skb) as bindings::netdev_tx_t
+    }
+}
+
+// SAFETY: `Registration` exposes its state, `D::Data` object across threads
+// but that type is required to be Send and Sync.
+unsafe impl<D: DriverData, T: DeviceOperations<D>> Send for Registration<T, D> {}
+unsafe impl<D: DriverData, T: DeviceOperations<D>> Sync for Registration<T, D> {}
+
+/// Corresponds to the kernel's `enum netdev_tx`.
+#[repr(i32)]
+pub enum TxCode {
+    /// Driver took care of packet.
+    Ok = bindings::netdev_tx_NETDEV_TX_OK,
+    /// Driver tx path was busy.
+    Busy = bindings::netdev_tx_NETDEV_TX_BUSY,
+}
+
+/// Corresponds to the kernel's `struct net_device_ops`.
+///
+/// A device driver must implement this. Only very basic operations are supported for now.
+#[vtable]
+pub trait DeviceOperations<D: DriverData> {
+    /// Corresponds to `ndo_init` in `struct net_device_ops`.
+    fn init(_dev: &mut Device, _data: <D::Data as ForeignOwnable>::Borrowed<'_>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_uninit` in `struct net_device_ops`.
+    fn uninit(_dev: &mut Device, _data: <D::Data as ForeignOwnable>::Borrowed<'_>) {}
+
+    /// Corresponds to `ndo_open` in `struct net_device_ops`.
+    fn open(_dev: &mut Device, _data: <D::Data as ForeignOwnable>::Borrowed<'_>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_stop` in `struct net_device_ops`.
+    fn stop(_dev: &mut Device, _data: <D::Data as ForeignOwnable>::Borrowed<'_>) -> Result {
+        Ok(())
+    }
+
+    /// Corresponds to `ndo_start_xmit` in `struct net_device_ops`.
+    fn start_xmit(
+        _dev: &mut Device,
+        _data: <D::Data as ForeignOwnable>::Borrowed<'_>,
+        _skb: SkBuff,
+    ) -> TxCode {
+        TxCode::Busy
+    }
+}
+
+/// Corresponds to the kernel's `struct sk_buff`.
+///
+/// A driver manages `struct sk_buff` in two ways. In both ways, the ownership is transferred
+/// between C and Rust. The allocation and release are done asymmetrically.
+///
+/// On the tx side (`ndo_start_xmit` operation in `struct net_device_ops`), the kernel allocates
+/// a `sk_buff' object and passes it to the driver. The driver is responsible for the release after transmission.
+/// On the rx side, the driver allocates a `sk_buff` object then passes it to the kernel after receiving data.
+pub struct SkBuff(*mut bindings::sk_buff);
+
+impl SkBuff {
+    /// Provides a time stamp.
+    pub fn tx_timestamp(&mut self) {
+        // SAFETY: The safety requirement ensures that the pointer is valid.
+        unsafe {
+            bindings::skb_tx_timestamp(self.0);
+        }
+    }
+}
+
+impl Drop for SkBuff {
+    fn drop(&mut self) {
+        // SAFETY: The safety requirement ensures that the pointer is valid.
+        unsafe {
+            bindings::kfree_skb_reason(
+                self.0,
+                bindings::skb_drop_reason_SKB_DROP_REASON_NOT_SPECIFIED,
+            )
+        }
+    }
+}
-- 
2.34.1


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

end of thread, other threads:[~2023-07-14 19:01 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-10  7:36 [PATCH v2 0/5] Rust abstractions for network device drivers FUJITA Tomonori
2023-07-10  7:36 ` [PATCH v2 1/5] rust: core " FUJITA Tomonori
2023-07-14 18:59   ` Benno Lossin
2023-07-10  7:37 ` [PATCH v2 2/5] rust: add support for ethernet operations FUJITA Tomonori
2023-07-14 19:00   ` Benno Lossin
2023-07-10  7:37 ` [PATCH v2 3/5] rust: add methods for configure net_device FUJITA Tomonori
2023-07-14 19:01   ` Benno Lossin
2023-07-10  7:37 ` [PATCH v2 4/5] samples: rust: add dummy network driver FUJITA Tomonori
2023-07-10  7:37 ` [PATCH v2 5/5] MAINTAINERS: add Rust network abstractions files to the NETWORKING DRIVERS entry FUJITA Tomonori
2023-07-10 18:29 ` [PATCH v2 0/5] Rust abstractions for network device drivers Jakub Kicinski
2023-07-10 19:53   ` Greg KH
2023-07-11 10:16   ` FUJITA Tomonori
2023-07-11 13:17     ` Andrew Lunn
2023-07-12 11:45       ` FUJITA Tomonori
     [not found] <20230610071848.3722492-1-tomo@exabit.dev>
2023-06-10  7:20 ` [PATCH v2 1/5] rust: core " FUJITA Tomonori
2023-06-10 14:11   ` Andrew Lunn
2023-06-11  8:03     ` Alice Ryhl
2023-06-11 15:30       ` Andrew Lunn
2023-06-11 17:48         ` Miguel Ojeda
2023-06-12  6:47     ` FUJITA Tomonori
2023-06-12 12:46       ` Andrew Lunn
2023-06-10 19:49   ` Miguel Ojeda
2023-06-12  5:04     ` FUJITA Tomonori
2023-06-12 13:26       ` Miguel Ojeda

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.