From patchwork Tue Jun 13 04:53:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: FUJITA Tomonori X-Patchwork-Id: 13277944 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9F8CA210D for ; Tue, 13 Jun 2023 04:53:37 +0000 (UTC) Received: from mail-pl1-x62c.google.com (mail-pl1-x62c.google.com [IPv6:2607:f8b0:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F2813195; Mon, 12 Jun 2023 21:53:32 -0700 (PDT) Received: by mail-pl1-x62c.google.com with SMTP id d9443c01a7336-1b3a3e34f4cso3517545ad.0; Mon, 12 Jun 2023 21:53:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1686632012; x=1689224012; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=zyUj1AWdXsheW8reIvyb+3Dn5KTj/5V3D/xuksehCWw=; b=jX6qxTyWuNgndnObouyeNGDCWQaCjd3bQrcVvQ3tAjRozvTUaI1NeMXwPylcoEXRoy IVrmJN//ts2fMUGYTiupk8vhBN8YXH1075w7sTymwGSFK+2iYJ3X3k+CouqZm8NQ324F NZK5mjVWT7yGAWTw79uTF63P+1jX+0EA8gcB2yf0SWMwVoMDkB+hywywLNDYqSU21DdB TFLk1npI/ASopin68/DUGvCyUS5cnbRkJwNZghfXTZ4Bx+/zWJNBYrOYrcmoX20M4FDF qe3o28BtpcP+E35SYSnCpIQn2GKQ2c9OvBgSUIbS0AT2PP6La0UP2zo5qZgjaiYt9Jjl bmyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686632012; x=1689224012; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=zyUj1AWdXsheW8reIvyb+3Dn5KTj/5V3D/xuksehCWw=; b=NPzAQIeVo0WNmNcjU91qTqJ7+SbVdh1+XNW585yLpUJxLipWOxwadeSbRPPTzsMuKJ 5NgzIOLelmAS0y+NTlnDMrdLDPOAiWEUmrT9ocDHMqOMiyX+FaiNo6Cgy/rVGjao7uq6 7q1iPBz40IXRPcwwFCqEswwxGytYDaq3iRZKuzZbt184yGAt+sjgFzp5brpjnxqshrh6 7Kde41v96sQxY+XWIWE6Nkf44CqzqH83wR41kmgtSUJ7sb/LAEJ0BmthwDOQQAQR7Mr3 wqn7P2wi2wVlmtWjjTdN5FCJlQ7xCxcKUdMcfS43BnW2EzxoR3Nt50/ZUfRS2ubvXJWJ QQeQ== X-Gm-Message-State: AC+VfDyDfEK1aoJxmxwQsbNJXV8Ta4u1ppzFOHCneu4e/laeM5U/Dyzn 584KRJobiPTdCHKYPb7A7QfXdy0rrKJ1S/mW X-Google-Smtp-Source: ACHHUZ4Brw/YnIsK7lHVPY4nXM/n7mQFsgtoJzewzO989X+La5Krohtu+ymDc6N/G/Yj074Xt9Vr9A== X-Received: by 2002:a17:902:e748:b0:1a9:6467:aa8d with SMTP id p8-20020a170902e74800b001a96467aa8dmr12844223plf.1.1686632011828; Mon, 12 Jun 2023 21:53:31 -0700 (PDT) Received: from ip-172-30-47-114.us-west-2.compute.internal (ec2-54-68-170-188.us-west-2.compute.amazonaws.com. [54.68.170.188]) by smtp.gmail.com with ESMTPSA id c5-20020a170902c1c500b001b027221393sm9095249plc.43.2023.06.12.21.53.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jun 2023 21:53:31 -0700 (PDT) From: FUJITA Tomonori To: netdev@vger.kernel.org Cc: rust-for-linux@vger.kernel.org, aliceryhl@google.com, andrew@lunn.ch, miguel.ojeda.sandonis@gmail.com Subject: [PATCH 1/5] rust: core abstractions for network device drivers Date: Tue, 13 Jun 2023 13:53:22 +0900 Message-Id: <20230613045326.3938283-2-fujita.tomonori@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230613045326.3938283-1-fujita.tomonori@gmail.com> References: <20230613045326.3938283-1-fujita.tomonori@gmail.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net 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::::zeroed() in const function. Signed-off-by: FUJITA Tomonori --- 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 | 344 ++++++++++++++++++++++++++++++++ 5 files changed, 370 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 +#include +#include #include #include #include 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 #include #include +#include +#include #include #include #include +#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..d072c81f99ce --- /dev/null +++ b/rust/kernel/net/dev.rs @@ -0,0 +1,344 @@ +// 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. + fn priv_data_ptr(&self) -> *const c_void { + // SAFETY: The type invariants guarantee that `self.0` is valid. + // During the initialization of `Registration` instance, the kernel allocates + // contiguous memory for `struct net_device` and a pointer to its private data. + // So it's safe to read an address from the returned address from `netdev_priv()`. + 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, D: DriverData> { + dev: Device, + is_registered: bool, + _p: PhantomData<(D, T)>, +} + +impl> Drop for Registration { + 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> Registration { + /// 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: D::Data) -> Result { + // 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 { Device::from_ptr(ptr) }; + // SAFETY: It's safe to write an address to the 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()); + } + 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 ::HAS_INIT { + Some(Self::init_callback) + } else { + None + }, + ndo_uninit: if ::HAS_UNINIT { + Some(Self::uninit_callback) + } else { + None + }, + ndo_open: if ::HAS_OPEN { + Some(Self::open_callback) + } else { + None + }, + ndo_stop: if ::HAS_STOP { + Some(Self::stop_callback) + } else { + None + }, + ndo_start_xmit: if ::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::::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 only `Device` object which can be used from +// any thread. +unsafe impl> Send for Registration {} +// SAFETY: `Registration` exposes only `Device` object which can be used from +// any thread. +unsafe impl> Sync for Registration {} + +/// 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 { + /// Corresponds to `ndo_init` in `struct net_device_ops`. + fn init(_dev: &mut Device, _data: ::Borrowed<'_>) -> Result { + Ok(()) + } + + /// Corresponds to `ndo_uninit` in `struct net_device_ops`. + fn uninit(_dev: &mut Device, _data: ::Borrowed<'_>) {} + + /// Corresponds to `ndo_open` in `struct net_device_ops`. + fn open(_dev: &mut Device, _data: ::Borrowed<'_>) -> Result { + Ok(()) + } + + /// Corresponds to `ndo_stop` in `struct net_device_ops`. + fn stop(_dev: &mut Device, _data: ::Borrowed<'_>) -> Result { + Ok(()) + } + + /// Corresponds to `ndo_start_xmit` in `struct net_device_ops`. + fn start_xmit( + _dev: &mut Device, + _data: ::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, + ) + } + } +} From patchwork Tue Jun 13 04:53:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: FUJITA Tomonori X-Patchwork-Id: 13277946 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B67EF2584 for ; Tue, 13 Jun 2023 04:53:41 +0000 (UTC) Received: from mail-pl1-x630.google.com (mail-pl1-x630.google.com [IPv6:2607:f8b0:4864:20::630]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 745211BC; Mon, 12 Jun 2023 21:53:33 -0700 (PDT) Received: by mail-pl1-x630.google.com with SMTP id d9443c01a7336-1b3df438cf1so1760735ad.1; Mon, 12 Jun 2023 21:53:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1686632012; x=1689224012; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=8WLrWgTq3lGZWH7CAbwlMcBDXHc2js7ZTMM+S02Ruag=; b=m3N/cOnIaiwJ7cbH0olTlh0AF9FTdYhTV7bHJ7fH/JLKfcY7J0ZioI95mnImUSiIz/ 9BB6qmlLckpYYiTs3/6uJghouLnJAq8ssaE+TuBUNhA4Gf03SPKSFPKd8CkG+0Unczus Ny0ttRzdmu7mCngyuEvTKHZiSI37y8LM68MxI48VtPrTYBTDQ5PI5XJ0WWwdii6XNbam qizPV7+YrGU07MBLoeMqzk4GcfdNPO3/l+HAUDF7Ue1rA9dptPEw7e+1gMpYmG9GJDNb NVOfw/J3N7WOgucz1iXiWKrB+wKtTnlZKu6tGW/y8Cl1IhHQnMiq671234udZw7J2tMt Vj5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686632012; x=1689224012; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8WLrWgTq3lGZWH7CAbwlMcBDXHc2js7ZTMM+S02Ruag=; b=MaNkcuT4Bxn4B/5feCQWM4qoBoS0Nk4tYuiKu1vCr0S7vo4DGaURT6VUYd/68umJle zV9aFlMluWRBnch8DgajBMiKfVbwKn9TJ0gPLGoWDXh+defauE0Jrb4gr0OQot+JuXns 9bXld7qcboYavhtJlqXpEiVUuf7fKavsoddaJ7uvWeI8hpUWzCNdAdYtqi1fAi69OnmX bzKMKGiDUSeefifNnqNEX8AVdSPHpSTgPUlf735rS96R3KwkRPIj/4q1LbWCx89AXXHJ Csy6Sm3TbIfrY8He+mEHLrGm/fgg4zonpJOLf44dOVdVp8k+UCgoKmallqxCqYwC5h/q zTSg== X-Gm-Message-State: AC+VfDzXAplD6DTVe/b8drooM1Cy4oc8+LuEotw6YHXt0Og35ZdzGvpz t5QGY3oHXSCZMg3UVubdXAnC/lcS8UVFSPQ6 X-Google-Smtp-Source: ACHHUZ64rKFdC/O48vXNxgWgRahU9tU3oUYK32DI4dByxrLFNr0Bs71bIHEs8XwPLGUBHUtr6g3JxQ== X-Received: by 2002:a17:902:da8b:b0:1b0:3d54:358f with SMTP id j11-20020a170902da8b00b001b03d54358fmr12528132plx.0.1686632012572; Mon, 12 Jun 2023 21:53:32 -0700 (PDT) Received: from ip-172-30-47-114.us-west-2.compute.internal (ec2-54-68-170-188.us-west-2.compute.amazonaws.com. [54.68.170.188]) by smtp.gmail.com with ESMTPSA id c5-20020a170902c1c500b001b027221393sm9095249plc.43.2023.06.12.21.53.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jun 2023 21:53:32 -0700 (PDT) From: FUJITA Tomonori To: netdev@vger.kernel.org Cc: rust-for-linux@vger.kernel.org, aliceryhl@google.com, andrew@lunn.ch, miguel.ojeda.sandonis@gmail.com Subject: [PATCH 2/5] rust: add support for ethernet operations Date: Tue, 13 Jun 2023 13:53:23 +0900 Message-Id: <20230613045326.3938283-3-fujita.tomonori@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230613045326.3938283-1-fujita.tomonori@gmail.com> References: <20230613045326.3938283-1-fujita.tomonori@gmail.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net 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_devicve_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 --- rust/bindings/bindings_helper.h | 1 + rust/kernel/net/dev.rs | 108 ++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) 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 #include +#include #include #include #include diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs index d072c81f99ce..d6012b2eea33 100644 --- a/rust/kernel/net/dev.rs +++ b/rust/kernel/net/dev.rs @@ -141,6 +141,18 @@ pub fn register(&mut self) -> Result { } } + /// Sets `ethtool_ops` of `net_device`. + pub fn set_ether_operations(&mut self) -> Result + where + U: EtherOperations, + { + if self.is_registered { + return Err(code::EINVAL); + } + EtherOperationsAdapter::::new().set_ether_ops(&mut self.dev); + Ok(()) + } + const DEVICE_OPS: bindings::net_device_ops = bindings::net_device_ops { ndo_init: if ::HAS_INIT { Some(Self::init_callback) @@ -342,3 +354,99 @@ fn drop(&mut self) { } } } + +/// Builds the kernel's `struct ethtool_ops`. +struct EtherOperationsAdapter { + _p: PhantomData<(D, T)>, +} + +impl EtherOperationsAdapter +where + D: DriverData, + T: EtherOperations, +{ + /// Creates a new instance. + fn new() -> Self { + EtherOperationsAdapter { _p: PhantomData } + } + + 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 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 `info` is valid while this function is running. + let mut info = unsafe { EthtoolTsInfo::from_ptr(info) }; + T::get_ts_info(&mut dev, data, &mut info)?; + Ok(0) + }) + } + + const ETHER_OPS: bindings::ethtool_ops = bindings::ethtool_ops { + get_ts_info: if ::HAS_GET_TS_INFO { + Some(Self::get_ts_info_callback) + } else { + None + }, + ..unsafe { core::mem::MaybeUninit::::zeroed().assume_init() } + }; + + const fn build_ether_ops() -> &'static bindings::ethtool_ops { + &Self::ETHER_OPS + } + + fn set_ether_ops(&self, dev: &mut Device) { + // SAFETY: The type invariants guarantee that `dev.0` is valid. + unsafe { + (*dev.0).ethtool_ops = Self::build_ether_ops(); + } + } +} + +/// Corresponds to the kernel's `struct ethtool_ops`. +#[vtable] +pub trait EtherOperations { + /// Corresponds to `get_ts_info` in `struct ethtool_ops`. + fn get_ts_info( + _dev: &mut Device, + _data: ::Borrowed<'_>, + _info: &mut 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(dev: &mut Device, info: &mut EthtoolTsInfo) -> Result { + // SAFETY: The type invariants guarantee that `dev.0` and `info.0` are valid. + unsafe { + bindings::ethtool_op_get_ts_info(dev.0, info.0); + } + Ok(()) +} From patchwork Tue Jun 13 04:53:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: FUJITA Tomonori X-Patchwork-Id: 13277948 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F17272598 for ; Tue, 13 Jun 2023 04:53:41 +0000 (UTC) Received: from mail-pf1-x42d.google.com (mail-pf1-x42d.google.com [IPv6:2607:f8b0:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6DEB710D7; Mon, 12 Jun 2023 21:53:34 -0700 (PDT) Received: by mail-pf1-x42d.google.com with SMTP id d2e1a72fcca58-6584553892cso1211700b3a.0; Mon, 12 Jun 2023 21:53:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1686632013; x=1689224013; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=10EEaoF+OSn0OiVxHg3Z7Dn0kpK8O8AdByu3lBZtS3o=; b=ntBQbooEhd9TlMNmSPCfYSz5hhDeiKng1YR7x310F9RvNNYP+NfIuaIi+sSU5u/MEu a3FMDrRvTUUC3si3QTlu1VJlsN7hrpZfXary+2p8ANg8PE7xGXiVtklaPZNHHwacV/t2 +ZjEsOmcL6fElDaltv1p8Vrmb0dVhWyu2oLFplo8Obx6fUjIv4WngiZmCSqlwgGwhejK MTtjiC7Gee4w15dTazT2UBzoRwOVWjo4Gl2tAuAS0LqZOZHqPz8yI7/CmgLFCddwxtGZ Z6Jmoa4qeOHiVjXrmBixo8Ac+ESYPfBtZWfpVFsgo1CSMzzSLy3BQko6Fy4/KKuAYm1V hUdw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686632013; x=1689224013; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=10EEaoF+OSn0OiVxHg3Z7Dn0kpK8O8AdByu3lBZtS3o=; b=itm3oNFUKHsaaLQc6cu7bf/e7i5elqL3Ex90vLvqRslu8lRHvKSSgM1cRcauQjRdXt 888kKZ8KPP6SjIEnnAuMYyOmVhDpDG/szFDftPId79HWiW5TDatR4pWJLTeojfG4Mntx B6YCnD/plW+eIIF3MnfGfa7iEphes+1yucraFzURBGA/crv2E9xeW1mwfs69anbW8Gr7 siKwLqOm4JaeoKyZ3aLyP+blvHgLHLh8ODM+/b6jJh6dGuntj10UhcBAM3n4ceR75XfK KvKYORkRnHgwb92tpGrRtWs80aq0J13b6haUb5ZNysdRL7w+wobajEvU8HuSlzllnIgM Gt4w== X-Gm-Message-State: AC+VfDz6W/5rH6nM4t0s/asokdaqzVroN0Q8uns+syzL2xFEtSiNSJqQ EDiYAIknJNZzs9AOCMuVl7bkv0yKMMowkxaN X-Google-Smtp-Source: ACHHUZ7i06fQildyFTnV6Lw6q6EoVIn4gsHpWBa4aGcvRc1jFj/ooTczaxiXFunidI5O17h6bKb3bw== X-Received: by 2002:a17:902:e80e:b0:1b3:ec39:f42c with SMTP id u14-20020a170902e80e00b001b3ec39f42cmr924182plg.5.1686632013542; Mon, 12 Jun 2023 21:53:33 -0700 (PDT) Received: from ip-172-30-47-114.us-west-2.compute.internal (ec2-54-68-170-188.us-west-2.compute.amazonaws.com. [54.68.170.188]) by smtp.gmail.com with ESMTPSA id c5-20020a170902c1c500b001b027221393sm9095249plc.43.2023.06.12.21.53.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jun 2023 21:53:32 -0700 (PDT) From: FUJITA Tomonori To: netdev@vger.kernel.org Cc: rust-for-linux@vger.kernel.org, aliceryhl@google.com, andrew@lunn.ch, miguel.ojeda.sandonis@gmail.com Subject: [PATCH 3/5] rust: add support for get_stats64 in struct net_device_ops Date: Tue, 13 Jun 2023 13:53:24 +0900 Message-Id: <20230613045326.3938283-4-fujita.tomonori@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230613045326.3938283-1-fujita.tomonori@gmail.com> References: <20230613045326.3938283-1-fujita.tomonori@gmail.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net get_stats64() is used to return the stats for user-space like the number of packets, which network device drivers are supposed to support. Signed-off-by: FUJITA Tomonori --- rust/kernel/net/dev.rs | 64 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/rust/kernel/net/dev.rs b/rust/kernel/net/dev.rs index d6012b2eea33..452944cf9fb8 100644 --- a/rust/kernel/net/dev.rs +++ b/rust/kernel/net/dev.rs @@ -8,7 +8,12 @@ //! [`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 crate::{ + bindings, + error::*, + prelude::vtable, + types::{ForeignOwnable, Opaque}, +}; use {core::ffi::c_void, core::marker::PhantomData}; /// Corresponds to the kernel's `struct net_device`. @@ -179,6 +184,11 @@ pub fn set_ether_operations(&mut self) -> Result } else { None }, + ndo_get_stats64: if ::HAS_GET_STATS64 { + Some(Self::get_stats64_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::::zeroed().assume_init() } @@ -256,6 +266,22 @@ const fn build_device_ops() -> &'static bindings::net_device_ops { let skb = unsafe { SkBuff::from_ptr(skb) }; T::start_xmit(&mut dev, data, skb) as bindings::netdev_tx_t } + + unsafe extern "C" fn get_stats64_callback( + netdev: *mut bindings::net_device, + stats: *mut bindings::rtnl_link_stats64, + ) { + // 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: for writing and nobody else will read or write to it. + let stats = unsafe { RtnlLinkStats64::from_ptr(stats) }; + T::get_stats64(&mut dev, data, stats); + } } // SAFETY: `Registration` exposes only `Device` object which can be used from @@ -305,6 +331,14 @@ fn start_xmit( ) -> TxCode { TxCode::Busy } + + /// Corresponds to `ndo_get_stats64` in `struct net_device_ops`. + fn get_stats64( + _dev: &mut Device, + _data: ::Borrowed<'_>, + _stats: &mut RtnlLinkStats64, + ) { + } } /// Corresponds to the kernel's `struct sk_buff`. @@ -355,6 +389,34 @@ fn drop(&mut self) { } } +/// Corresponds to the kernel's `struct rtnl_link_stats64`. +#[repr(transparent)] +pub struct RtnlLinkStats64(Opaque); + +impl RtnlLinkStats64 { + /// Creates a new [`RtnlLinkStats64`] instance. + /// + /// # Safety + /// + /// For the duration of the lifetime 'a, the pointer must be valid for writing and nobody else + /// may read or write to the `rtnl_link_stats64` object. + unsafe fn from_ptr<'a>(ptr: *mut bindings::rtnl_link_stats64) -> &'a mut Self { + unsafe { &mut *(ptr as *mut Self) } + } + + /// Updates TX stats. + pub fn set_tx_stats(&mut self, packets: u64, bytes: u64, errors: u64, dropped: u64) { + // SAFETY: We have exclusive access to the `rtnl_link_stats64`, so writing to it is okay. + unsafe { + let inner = Opaque::get(&self.0); + (*inner).tx_packets = packets; + (*inner).tx_bytes = bytes; + (*inner).tx_errors = errors; + (*inner).tx_dropped = dropped; + } + } +} + /// Builds the kernel's `struct ethtool_ops`. struct EtherOperationsAdapter { _p: PhantomData<(D, T)>, From patchwork Tue Jun 13 04:53:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: FUJITA Tomonori X-Patchwork-Id: 13277947 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EE94D258D for ; Tue, 13 Jun 2023 04:53:41 +0000 (UTC) Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8211C10DE; Mon, 12 Jun 2023 21:53:35 -0700 (PDT) Received: by mail-pl1-x62b.google.com with SMTP id d9443c01a7336-1b3ecb17721so385155ad.0; Mon, 12 Jun 2023 21:53:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1686632015; x=1689224015; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=fkVW9LtF2PjCtcJMURPcG//ByYcBQf1FiDlTUjHWK6c=; b=FBPRzDvS9tl999BNyD/swxNYJuI6DOYCBf0XCA8OoUzjMrPBMnAcjmX2O27ds/h4Vk pDQ4FQS9IPFotX9WGw2CmvmDbfZ0P18Xmla79VKmC5MsVDsHbxiKBCacC7q9dMXZXYci YvTksjAjdSBj8Iqkgc8MNHnU4y35mmqMMENeRgHJpbm4pWjxHrhc9fEXhQGefHA5mUnG IxWgUrRqTbAmoEXQZCreZFv+Dcq18qB3LvFMOjbv6dOtISozxZuR1dgOhwcvBOqyHvMg xA6dgDvY9DWpaqXt0Zn0TNzm9auFfQwiP9pstWu8uz9fFUoSEy2om6SmuV48EzxYT6YD JRig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686632015; x=1689224015; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=fkVW9LtF2PjCtcJMURPcG//ByYcBQf1FiDlTUjHWK6c=; b=eQeAgVkhtJebxJTV0D4FhDoBjy7jlTWCC3KrxMyATE2l0ROu37aMFhXiFzBegahgEx P6A07TnK+uNoUBz18PqMx3G0BIV4/weKd6eZEN9pYvZqkWxEIUTQU2JTlyhhwluMi5dH gp8KWpADUCGMiPX73l81kYkM9x1t2M3KyLDAeIg4b3CXqCDTljP13X2QPFFwRdKmbgUm cdpZqvNmxVTVFyd5yPTNkuYOReDGBYvLdk4jK5zbQztQFPimEKJpTkCLkEm7qjFSP2Jx /uIxk5b/wK/6QZvF4MwMpx6bJ1oY7SZ1ABh+abDK/O7V0QGqLhco6i3n1TpJHt2Z+7MR MpUw== X-Gm-Message-State: AC+VfDz4rdZ3i99kujxeg3tA/UzKapzm3jmYniVrmVl2Gvwkzw3PGGly uYGDidbeaCca2W7KWAf32ZVsvSBAdXd53ST5 X-Google-Smtp-Source: ACHHUZ5LH/IBNCKWc6KVd+wF7iSB8lPGochbodtfmgaGK9VQ92FsrM0IR8mdgO8PH8tXRVzXsSM4wg== X-Received: by 2002:a17:902:da8b:b0:1b0:3d54:358f with SMTP id j11-20020a170902da8b00b001b03d54358fmr12528187plx.0.1686632014514; Mon, 12 Jun 2023 21:53:34 -0700 (PDT) Received: from ip-172-30-47-114.us-west-2.compute.internal (ec2-54-68-170-188.us-west-2.compute.amazonaws.com. [54.68.170.188]) by smtp.gmail.com with ESMTPSA id c5-20020a170902c1c500b001b027221393sm9095249plc.43.2023.06.12.21.53.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jun 2023 21:53:33 -0700 (PDT) From: FUJITA Tomonori To: netdev@vger.kernel.org Cc: rust-for-linux@vger.kernel.org, aliceryhl@google.com, andrew@lunn.ch, miguel.ojeda.sandonis@gmail.com Subject: [PATCH 4/5] rust: add methods for configure net_device Date: Tue, 13 Jun 2023 13:53:25 +0900 Message-Id: <20230613045326.3938283-5-fujita.tomonori@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230613045326.3938283-1-fujita.tomonori@gmail.com> References: <20230613045326.3938283-1-fujita.tomonori@gmail.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net adds methods to net::Device for the basic configurations of net_device. Signed-off-by: FUJITA Tomonori --- rust/helpers.c | 7 ++ rust/kernel/net/dev.rs | 183 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) 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 #include #include +#include #include #include #include @@ -31,6 +32,12 @@ #include #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 452944cf9fb8..4767f331973e 100644 --- a/rust/kernel/net/dev.rs +++ b/rust/kernel/net/dev.rs @@ -12,10 +12,118 @@ bindings, error::*, prelude::vtable, + str::CStr, types::{ForeignOwnable, Opaque}, }; 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 @@ -42,6 +150,81 @@ fn priv_data_ptr(&self) -> *const c_void { // So it's safe to read an address from the returned address from `netdev_priv()`. unsafe { core::ptr::read(bindings::netdev_priv(self.0) as *const *const c_void) } } + + /// Sets the name of a device. + pub fn set_name(&mut self, name: &CStr) -> Result { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { + if name.len() > (*self.0).name.len() { + return Err(code::EINVAL); + } + for i in 0..name.len() { + (*self.0).name[i] = name[i] as i8; + } + } + Ok(()) + } + + /// Sets carrier. + pub fn netif_carrier_on(&mut self) { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { bindings::netif_carrier_on(self.0) } + } + + /// Clears carrier. + pub fn netif_carrier_off(&mut self) { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { bindings::netif_carrier_off(self.0) } + } + + /// Sets the max mtu of the device. + pub fn set_max_mtu(&mut self, max_mtu: u32) { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { + (*self.0).max_mtu = max_mtu; + } + } + + /// Sets the minimum mtu of the device. + pub fn set_min_mtu(&mut self, min_mtu: u32) { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { + (*self.0).min_mtu = min_mtu; + } + } + + /// Returns the flags of the device. + pub fn get_flags(&self) -> u32 { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { (*self.0).flags } + } + + /// Sets the flags of the device. + pub fn set_flags(&mut self, flags: u32) { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { + (*self.0).flags = flags; + } + } + + /// Returns the priv_flags of the device. + pub fn get_priv_flags(&self) -> u64 { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { (*self.0).priv_flags } + } + + /// Sets the priv_flags of the device. + pub fn set_priv_flags(&mut self, flags: u64) { + // SAFETY: The safety requirement ensures that the pointer is valid. + unsafe { (*self.0).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 safety requirement ensures that the pointer is valid. + unsafe { bindings::eth_hw_addr_random(self.0) } + } } // SAFETY: `Device` is just a wrapper for the kernel`s `struct net_device`, which can be used From patchwork Tue Jun 13 04:53:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: FUJITA Tomonori X-Patchwork-Id: 13277945 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4B7A323C4 for ; Tue, 13 Jun 2023 04:53:38 +0000 (UTC) Received: from mail-pl1-x62c.google.com (mail-pl1-x62c.google.com [IPv6:2607:f8b0:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 97A1210DF; Mon, 12 Jun 2023 21:53:36 -0700 (PDT) Received: by mail-pl1-x62c.google.com with SMTP id d9443c01a7336-1b3ecb17721so385215ad.0; Mon, 12 Jun 2023 21:53:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1686632016; x=1689224016; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=7D7KD+4/hvSzq7CFXPABTFsWyQuluvzVlksOwlgt0j4=; b=lBt7XkrWoWv4WtbnZEMyO+/Ha6V5eGYfdnP3Kzg2oJj0twC7C6ccryxk9+LyDemmPj JOS3LdIiVUSdcl9LKXAuCvb79EjI/NbjqSpo6YoFZgCwBWxl6u1qT7Y/sT1SS+a7dO62 qQGkjvP+rtq3AMxWSEcfjbK5sRX0S5dgI9h8T9rQiOqQKUErPP52p4/ODLxz9p6wVUiy 9BOt2qp/F3F7ho3ecmxxZJbp965EtG0HIvR/zQ9s26bg+UCM45EMrDNJ5KBg33ObDA+3 mzpi4hwf/DNXWQlFWpwcE96dd2Y7LtYyFbILBul+sXmGNnXT9ixxFk/EmW2XS8Uq01Qk Zufg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686632016; x=1689224016; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=7D7KD+4/hvSzq7CFXPABTFsWyQuluvzVlksOwlgt0j4=; b=CqjZi3UcjNpDXEgH7pNdcN+w1Btn7fvtJJug3opMGaMxHz7f8GTFBU7WbjHng1GY6S aekJqCnFxn4k7MtvUCN3aQblmYnTvCJ8kEc5tlvCze3eyTfb2LeiGSlHYFZ6BG+9sW3s 5sr5L3pbYDs18MFYwVqp7lbszsAxKsevFKsaJoq01SbvOAWH/kW6N7X3oj+d2QhbG4s0 +5kSjYTE+P2Mq2HEFPNEEbh9/Z35fo9oyhJtPJG3Gcy6gv1NhMj5oIKIo4nvWICcmUa2 ynKwW1PQa6rdtoPHHubJhCIjiZxnu1Bz+WuXizwbqPi85gG33YUJdg+EV8hPMcxcm5g2 rN1g== X-Gm-Message-State: AC+VfDz8c3J+OXUtIZVl5rS70NL52iziQjB3o4Z7tEpP2DR7lxLOpSIy h4MjWl0Yqhbjhbo1iJLVbXcyTt37NDO+bMZA X-Google-Smtp-Source: ACHHUZ44OEkRfiOJ41O+zs4PNyrhQqZG9RkIftnA1tvpJkvTqJBrVSk69FN0hR8Bu+rTPCzNesUPbg== X-Received: by 2002:a17:902:e844:b0:1b3:ebda:654e with SMTP id t4-20020a170902e84400b001b3ebda654emr1013355plg.5.1686632015580; Mon, 12 Jun 2023 21:53:35 -0700 (PDT) Received: from ip-172-30-47-114.us-west-2.compute.internal (ec2-54-68-170-188.us-west-2.compute.amazonaws.com. [54.68.170.188]) by smtp.gmail.com with ESMTPSA id c5-20020a170902c1c500b001b027221393sm9095249plc.43.2023.06.12.21.53.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jun 2023 21:53:34 -0700 (PDT) From: FUJITA Tomonori To: netdev@vger.kernel.org Cc: rust-for-linux@vger.kernel.org, aliceryhl@google.com, andrew@lunn.ch, miguel.ojeda.sandonis@gmail.com Subject: [PATCH 5/5] samples: rust: add dummy network driver Date: Tue, 13 Jun 2023 13:53:26 +0900 Message-Id: <20230613045326.3938283-6-fujita.tomonori@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230613045326.3938283-1-fujita.tomonori@gmail.com> References: <20230613045326.3938283-1-fujita.tomonori@gmail.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net 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 --- samples/rust/Kconfig | 12 +++++ samples/rust/Makefile | 1 + samples/rust/rust_net_dummy.rs | 81 ++++++++++++++++++++++++++++++++++ scripts/Makefile.build | 2 +- 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 samples/rust/rust_net_dummy.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index b0f74a81c8f9..8b52ba620ae3 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -30,6 +30,18 @@ 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 drivers. + + To compile this as a module, choose M here: + the module will be called rust_minimal. + + 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..6c49a7ba7ba2 --- /dev/null +++ b/samples/rust/rust_net_dummy.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +// +//! Rust dummy netdev. + +use kernel::{ + c_str, + net::dev::{ + ethtool_op_get_ts_info, flags, priv_flags, Device, DeviceOperations, DriverData, + EtherOperations, EthtoolTsInfo, Registration, RtnlLinkStats64, SkBuff, TxCode, + }, + prelude::*, +}; + +module! { + type: DummyNetdev, + name: "rust_net_dummy", + author: "Rust for Linux Contributors", + description: "Rust dummy netdev", + license: "GPL v2", +} + +struct DevOps {} + +#[vtable] +impl>> DeviceOperations for DevOps { + fn init(_dev: &mut Device, _data: &Stats) -> Result { + Ok(()) + } + + fn start_xmit(_dev: &mut Device, _data: &Stats, mut skb: SkBuff) -> TxCode { + skb.tx_timestamp(); + TxCode::Ok + } + + fn get_stats64(_dev: &mut Device, _data: &Stats, _stats: &mut RtnlLinkStats64) {} +} + +/// For device driver specific information. +struct Stats {} + +impl DriverData for Stats { + type Data = Box; +} + +struct DummyNetdev { + _r: Registration, +} + +struct EtherOps {} + +#[vtable] +impl>> EtherOperations for EtherOps { + fn get_ts_info(dev: &mut Device, _data: &Stats, info: &mut EthtoolTsInfo) -> Result { + ethtool_op_get_ts_info(dev, info) + } +} + +impl kernel::Module for DummyNetdev { + fn init(_module: &'static ThisModule) -> Result { + let data = Box::try_new(Stats {})?; + let mut r = Registration::::try_new_ether(1, 1, data)?; + r.set_ether_operations::()?; + + 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) \