From cd9b15bf4d6b6607fd1c4d127540c272d2ab110e Mon Sep 17 00:00:00 2001 From: Li Hongyu Date: Tue, 13 Sep 2022 08:22:45 +0000 Subject: [PATCH 1/3] rust: net: add some ops in the net_device Signed-off-by: Li Hongyu --- rust/bindings/bindings_helper.h | 6 + rust/helpers.c | 6 + rust/kernel/net.rs | 1220 ++++++++++++++++++++++++++++++- 3 files changed, 1230 insertions(+), 2 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 284793085d5530..ad97c0eae8195d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +38,10 @@ #include #include #include +#include +#include +#include +#include #include /* `bindgen` gets confused at certain things. */ diff --git a/rust/helpers.c b/rust/helpers.c index bf790f46c76317..7973b473ff1dfb 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -655,6 +655,12 @@ int rust_helper_fs_parse(struct fs_context *fc, } EXPORT_SYMBOL_GPL(rust_helper_fs_parse); +void *rust_helper_netdev_priv(const struct net_device *dev) +{ + return netdev_priv(dev); +} +EXPORT_SYMBOL_GPL(rust_helper_netdev_priv); + /* * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type * as the Rust `usize` type, so we can use it in contexts where Rust diff --git a/rust/kernel/net.rs b/rust/kernel/net.rs index 0115f3a35cd0fc..e4e22be6a5b46f 100644 --- a/rust/kernel/net.rs +++ b/rust/kernel/net.rs @@ -5,9 +5,17 @@ //! C headers: [`include/net/net_namespace.h`](../../../../include/linux/net/net_namespace.h), //! [`include/linux/netdevice.h`](../../../../include/linux/netdevice.h), //! [`include/linux/skbuff.h`](../../../../include/linux/skbuff.h). +//! [`include/linux/ethtool.h`](../../../../include/linux/ethtool.h). -use crate::{bindings, str::CStr, to_result, ARef, AlwaysRefCounted, Error, Result}; -use core::{cell::UnsafeCell, ptr::NonNull}; +use crate::{ + bindings, + error::{from_kernel_err_ptr, from_kernel_result}, + prelude::*, + str::CStr, + to_result, ARef, AlwaysRefCounted, Error, PointerWrapper, Result, +}; +use alloc::slice; +use core::{self, cell::UnsafeCell, ptr::NonNull}; #[cfg(CONFIG_NETFILTER)] pub mod filter; @@ -16,6 +24,17 @@ pub mod filter; #[repr(transparent)] pub struct Device(UnsafeCell); +impl Device { + pub fn alloc_etherdev_mqs(sizeof: i32, count: u32) -> Result> { + // SAFETY: FFI call. + let res = + from_kernel_err_ptr(unsafe { bindings::alloc_etherdev_mqs(sizeof, count, count) })?; + // SAFETY: Since the `net_device` creation succeeded, the `res` must be valid. + let net: ARef<_> = unsafe { &*(res as *const Device) }.into(); + Ok(net) + } +} + // SAFETY: Instances of `Device` are created on the C side. They are always refcounted. unsafe impl AlwaysRefCounted for Device { fn inc_ref(&self) { @@ -204,6 +223,25 @@ impl SocketAddrV4 { __pad: [0; 8], }) } + + /// Creates a new IPv4 socket address from C's `sockaddr_in` or `sockaddr` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` points to C's `sockaddr_in` or `sockaddr`. + pub const unsafe fn from_ptr(ptr: *mut core::ffi::c_void) -> Self { + // Safety: The safety requirements guarantee the validity of the cast. + let ptr_n = ptr as *mut bindings::sockaddr_in; + // SAFETY: The above cast guarantees the validity of the dereference. + unsafe { + Self(bindings::sockaddr_in { + sin_family: (*ptr_n).sin_family, + sin_port: (*ptr_n).sin_port, + sin_addr: (*ptr_n).sin_addr, + __pad: (*ptr_n).__pad, + }) + } + } } /// An IPv6 socket address. @@ -390,3 +428,1181 @@ impl Drop for TcpStream { unsafe { bindings::sock_release(self.sock) }; } } + +/// A structure for `NAPI` scheduling with weighting. +pub struct NapiStruct { + napi: bindings::napi_struct, +} + +impl NapiStruct { + /// New an uninitialized `NapiStruct`. + /// + /// # Safety + /// + /// Callers must call [`NapiStruct::init`] before using the `NapiStruct` item. + unsafe fn new() -> Self { + Self { + napi: bindings::napi_struct::default(), + } + } + + // Init the `NapiStruct` item. + fn init( + &mut self, + dev: &mut Device, + poll: Option< + unsafe extern "C" fn( + arg1: *mut bindings::napi_struct, + arg2: core::ffi::c_int, + ) -> core::ffi::c_int, + >, + weight: i32, + ) -> Result { + // SAFETY: The existence of the shared references mean `dev.0` and `self.0` are valid. + unsafe { + bindings::netif_napi_add_weight( + dev.0.get_mut() as _, + &mut self.napi as *mut bindings::napi_struct, + poll, + weight, + ); + } + Ok(0) + } +} + +/// The main device statistics structure. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct RtnlLinkStats64 { + ptr: *mut bindings::rtnl_link_stats64, +} + +impl RtnlLinkStats64 { + /// Constructs a new `struct rtnl_link_stats64` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::rtnl_link_stats64) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } +} + +/// RX/TX ring parameters. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct EthtoolRingparam { + ptr: *mut bindings::ethtool_ringparam, +} + +impl EthtoolRingparam { + /// Constructs a new `struct ethtool_ringparam` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::ethtool_ringparam) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } + + /// Sets the rx_max_pending associated with the ethtool ring param. + pub fn set_rx_max_pending(&mut self, rx_max_pending: u32) { + // SAFETY: `self.ptr` is valid by the type invariants. + unsafe { (*self.ptr).rx_max_pending = rx_max_pending as _ }; + } + + /// Sets the tx_max_pending associated with the ethtool ring param. + pub fn set_tx_max_pending(&mut self, tx_max_pending: u32) { + // SAFETY: `self.ptr` is valid by the type invariants. + unsafe { (*self.ptr).tx_max_pending = tx_max_pending as _ }; + } + + /// Sets the rx_pending associated with the ethtool ring param. + pub fn set_rx_pending(&mut self, rx_pending: u32) { + // SAFETY: `self.ptr` is valid by the type invariants. + unsafe { (*self.ptr).rx_pending = rx_pending as _ }; + } + + /// Sets the tx_pending associated with the ethtool ring param. + pub fn set_tx_pending(&mut self, tx_pending: u32) { + // SAFETY: `self.ptr` is valid by the type invariants. + unsafe { (*self.ptr).tx_pending = tx_pending as _ }; + } +} + +/// General driver and device information. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct EthtoolDrvinfo { + ptr: *mut bindings::ethtool_drvinfo, +} + +impl EthtoolDrvinfo { + /// Constructs a new `struct irq_domain` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::ethtool_drvinfo) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } +} + +/// Device-specific statistics. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct EthtoolStats { + ptr: *mut bindings::ethtool_stats, +} + +impl EthtoolStats { + /// Constructs a new `struct ethtool_stats` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::ethtool_stats) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } +} + +/// Configuring number of network channel. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct EthtoolChannels { + ptr: *mut bindings::ethtool_channels, +} + +impl EthtoolChannels { + /// Constructs a new `struct ethtool_channels` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::ethtool_channels) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } +} + +/// Link control and status. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct EthtoolLinkKsettings { + ptr: *const bindings::ethtool_link_ksettings, +} + +impl EthtoolLinkKsettings { + /// Constructs a new `struct ethtool_link_ksettings` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *const bindings::ethtool_link_ksettings) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } +} + +/// Command to get or set RX flow classification rules. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct EthtoolRxnfc { + ptr: *mut bindings::ethtool_rxnfc, +} + +impl EthtoolRxnfc { + /// Constructs a new `struct ethtool_rxnfc` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::ethtool_rxnfc) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } +} + +/// Coalescing parameters for IRQs and stats updates. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct EthtoolCoalesce { + ptr: *mut bindings::ethtool_coalesce, +} + +impl EthtoolCoalesce { + /// Constructs a new `struct ethtool_coalesce` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::ethtool_coalesce) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } +} + +/// Holds a device's timestamping and PHC association. +/// +/// # Invariants +/// +/// `ptr` is always non-null and valid. +pub struct EthtoolTsInfo { + ptr: *mut bindings::ethtool_ts_info, +} + +impl EthtoolTsInfo { + /// Constructs a new `struct ethtool_ts_info` wrapper. + /// + /// # Safety + /// + /// The pointer `ptr` must be non-null and valid for the lifetime of the returned object. + pub(crate) unsafe fn from_ptr(ptr: *mut bindings::ethtool_ts_info) -> Self { + // INVARIANT: The safety requirements ensure the invariant. + Self { ptr } + } +} + +#[vtable] +pub trait EthtoolOps { + /// The pointer type that will be used to hold user-defined data type. + type DataEthtoolOps: PointerWrapper + Send + Sync = (); + + fn get_drvinfo( + data: ::Borrowed<'_>, + info: EthtoolDrvinfo, + ); + + fn get_link(data: ::Borrowed<'_>) -> u32; + + fn get_ringparam( + data: ::Borrowed<'_>, + ring: EthtoolRingparam, + ); + + fn get_strings( + data: ::Borrowed<'_>, + stringset: u32, + ptr: *mut u8, + ) -> Result; + + fn get_sset_count( + data: ::Borrowed<'_>, + sset: i32, + ) -> Result; + + fn get_ethtool_stats( + data: ::Borrowed<'_>, + stats: EthtoolStats, + data: *mut u64, + ); + + fn set_channels( + data: ::Borrowed<'_>, + ethtool_channels: EthtoolChannels, + ) -> Result; + + fn get_channels( + data: ::Borrowed<'_>, + ethtool_channels: EthtoolChannels, + ); + + fn get_ts_info( + data: ::Borrowed<'_>, + info: EthtoolTsInfo, + ) -> Result; + + fn get_link_ksettings( + data: ::Borrowed<'_>, + ethtool_link_ksettings: EthtoolLinkKsettings, + ) -> Result; + + fn set_link_ksettings( + data: ::Borrowed<'_>, + ethtool_link_ksettings: EthtoolLinkKsettings, + ) -> Result; + + fn set_coalesce( + data: ::Borrowed<'_>, + ethtool_coalesce: EthtoolCoalesce, + ) -> Result; + + fn get_coalesce( + data: ::Borrowed<'_>, + ethtool_coalesce: EthtoolCoalesce, + ) -> Result; + + fn get_rxfh_key_size(data: ::Borrowed<'_>) -> u32; + + fn get_rxfh_indir_size(data: ::Borrowed<'_>) -> u32; + + fn get_rxfh( + data: ::Borrowed<'_>, + indir: *mut u32, + key: *mut u8, + hfunc: *mut u8, + ) -> Result; + + fn set_rxfh( + data: ::Borrowed<'_>, + indir: *const u32, + key: *const u8, + hfunc: u8, + ) -> Result; + + fn get_rxnfc( + data: ::Borrowed<'_>, + info: EthtoolRxnfc, + ) -> Result; + + fn set_rxnfc( + data: ::Borrowed<'_>, + info: EthtoolRxnfc, + ) -> Result; +} + +#[vtable] +pub trait NetdevOps { + /// The pointer type that will be used to hold user-defined data type. + type DataNetdevOps: PointerWrapper + Send + Sync = (); + + fn ndo_open(data: ::Borrowed<'_>) -> Result; + + fn ndo_stop(data: ::Borrowed<'_>) -> Result; + + fn ndo_start_xmit( + skb: &SkBuff, + data: ::Borrowed<'_>, + ) -> bindings::netdev_tx_t; + + fn ndo_validate_addr( + data: ::Borrowed<'_>, + ) -> Result; + + fn ndo_set_mac_address( + data: ::Borrowed<'_>, + addr: SocketAddr, + ) -> Result; + + fn ndo_set_rx_mode(data: ::Borrowed<'_>); + + fn ndo_get_stats64( + data: ::Borrowed<'_>, + storage: RtnlLinkStats64, + ); + + fn ndo_vlan_rx_add_vid( + data: ::Borrowed<'_>, + proto: u16, + vid: u16, + ) -> Result; + + fn ndo_vlan_rx_kill_vid( + data: ::Borrowed<'_>, + proto: u16, + vid: u16, + ) -> Result; + + fn ndo_bpf( + data: ::Borrowed<'_>, + buffer: &mut [u8], + ) -> Result; + + fn ndo_xdp_xmit( + data: ::Borrowed<'_>, + buffer: &mut [u8], + ) -> Result; + + fn ndo_features_check( + data: ::Borrowed<'_>, + skb: &SkBuff, + info: bindings::netdev_features_t, + ) -> bindings::netdev_features_t; + + fn ndo_get_phys_port_name( + data: ::Borrowed<'_>, + buffer: &mut [u8], + ) -> Result; + + fn ndo_set_features( + data: ::Borrowed<'_>, + features: bindings::netdev_features_t, + ) -> Result; + + fn ndo_tx_timeout( + data: ::Borrowed<'_>, + txqueue: core::ffi::c_uint, + ); +} + +struct NetDeviceTables(T); +impl NetDeviceTables { + const ETHTOOL_OPS: bindings::ethtool_ops = bindings::ethtool_ops { + _bitfield_1: bindings::__BindgenBitfieldUnit::new([1; 1]), + supported_ring_params: 0, + supported_coalesce_params: 0, + get_drvinfo: if ::HAS_GET_DRVINFO { + Some(Self::get_drvinfo_callback) + } else { + None + }, + get_link: if ::HAS_GET_LINK { + Some(Self::get_link_callback) + } else { + None + }, + get_ringparam: if ::HAS_GET_RINGPARAM { + Some(Self::get_ringparam_callback) + } else { + None + }, + get_strings: if ::HAS_GET_STRINGS { + Some(Self::get_strings_callback) + } else { + None + }, + get_sset_count: if ::HAS_GET_SSET_COUNT { + Some(Self::get_sset_count_callback) + } else { + None + }, + get_ethtool_stats: if ::HAS_GET_ETHTOOL_STATS { + Some(Self::get_ethtool_stats_callback) + } else { + None + }, + set_channels: if ::HAS_SET_CHANNELS { + Some(Self::set_channels_callback) + } else { + None + }, + get_channels: if ::HAS_GET_CHANNELS { + Some(Self::get_channels_callback) + } else { + None + }, + get_ts_info: if ::HAS_GET_TS_INFO { + Some(Self::get_ts_info_callback) + } else { + None + }, + get_link_ksettings: if ::HAS_GET_LINK_KSETTINGS { + Some(Self::get_link_ksettings_callback) + } else { + None + }, + set_link_ksettings: if ::HAS_SET_LINK_KSETTINGS { + Some(Self::set_link_ksettings_callback) + } else { + None + }, + set_coalesce: if ::HAS_SET_COALESCE { + Some(Self::set_coalesce_callback) + } else { + None + }, + get_coalesce: if ::HAS_GET_COALESCE { + Some(Self::get_coalesce_callback) + } else { + None + }, + get_rxfh_key_size: if ::HAS_GET_RXFH_KEY_SIZE { + Some(Self::get_rxfh_key_size_callback) + } else { + None + }, + get_rxfh_indir_size: if ::HAS_GET_RXFH_INDIR_SIZE { + Some(Self::get_rxfh_indir_size_callback) + } else { + None + }, + get_rxfh: if ::HAS_GET_RXFH { + Some(Self::get_rxfh_callback) + } else { + None + }, + set_rxfh: if ::HAS_SET_RXFH { + Some(Self::set_rxfh_callback) + } else { + None + }, + get_rxnfc: if ::HAS_GET_RXNFC { + Some(Self::get_rxnfc_callback) + } else { + None + }, + set_rxnfc: if ::HAS_SET_RXNFC { + Some(Self::set_rxnfc_callback) + } else { + None + }, + get_regs_len: None, + get_regs: None, + get_wol: None, + set_wol: None, + get_msglevel: None, + set_msglevel: None, + nway_reset: None, + get_link_ext_state: None, + get_eeprom_len: None, + get_eeprom: None, + set_eeprom: None, + set_ringparam: None, + get_pause_stats: None, + get_pauseparam: None, + set_pauseparam: None, + self_test: None, + set_phys_id: None, + begin: None, + complete: None, + get_priv_flags: None, + set_priv_flags: None, + flash_device: None, + reset: None, + get_rxfh_context: None, + set_rxfh_context: None, + get_dump_flag: None, + get_dump_data: None, + set_dump: None, + get_module_info: None, + get_module_eeprom: None, + get_eee: None, + set_eee: None, + get_tunable: None, + set_tunable: None, + get_per_queue_coalesce: None, + set_per_queue_coalesce: None, + get_fec_stats: None, + get_fecparam: None, + set_fecparam: None, + get_ethtool_phy_stats: None, + get_phy_tunable: None, + set_phy_tunable: None, + get_module_eeprom_by_page: None, + get_eth_phy_stats: None, + get_eth_mac_stats: None, + get_eth_ctrl_stats: None, + get_rmon_stats: None, + get_module_power_mode: None, + set_module_power_mode: None, + }; + + unsafe extern "C" fn get_drvinfo_callback( + dev: *mut bindings::net_device, + info: *mut bindings::ethtool_drvinfo, + ) { + // SAFETY: `dev` is valid as it was passed in by the C portion. + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let driver_info = unsafe { EthtoolDrvinfo::from_ptr(info) }; + T::get_drvinfo(data, driver_info); + } + + unsafe extern "C" fn get_link_callback(dev: *mut bindings::net_device) -> bindings::u32_ { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::get_link(data); + res + } + + unsafe extern "C" fn get_ringparam_callback( + dev: *mut bindings::net_device, + ring: *mut bindings::ethtool_ringparam, + kernel_ring: *mut bindings::kernel_ethtool_ringparam, + extack: *mut bindings::netlink_ext_ack, + ) { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ring` remains valid for the duration of this + // function call. + let ring = unsafe { EthtoolRingparam::from_ptr(ring) }; + T::get_ringparam(data, ring); + } + + unsafe extern "C" fn get_strings_callback( + dev: *mut bindings::net_device, + stringset: u32, + ptr: *mut u8, + ) { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + T::get_strings(data, stringset, ptr); + } + + unsafe extern "C" fn get_sset_count_callback( + dev: *mut bindings::net_device, + sset: i32, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::get_sset_count(data, sset)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn get_ethtool_stats_callback( + dev: *mut bindings::net_device, + stats: *mut bindings::ethtool_stats, + ptr: *mut u64, + ) { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let ethtool_stats = unsafe { EthtoolStats::from_ptr(stats) }; + T::get_ethtool_stats(data, ethtool_stats, ptr); + } + + unsafe extern "C" fn set_channels_callback( + dev: *mut bindings::net_device, + ptr: *mut bindings::ethtool_channels, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let ethtool_channels = unsafe{EthtoolChannels::from_ptr(ptr)}; + let res = T::set_channels(data, ethtool_channels)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn get_channels_callback( + dev: *mut bindings::net_device, + ptr: *mut bindings::ethtool_channels, + ) { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let ethtool_channels = unsafe { EthtoolChannels::from_ptr(ptr) }; + T::get_channels(data, ethtool_channels); + } + + unsafe extern "C" fn get_ts_info_callback( + dev: *mut bindings::net_device, + ptr: *mut bindings::ethtool_ts_info, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let info = unsafe{EthtoolTsInfo::from_ptr(ptr)}; + let res = T::get_ts_info(data, info)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn get_link_ksettings_callback( + dev: *mut bindings::net_device, + ptr: *mut bindings::ethtool_link_ksettings, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let cmd = unsafe{EthtoolLinkKsettings::from_ptr(ptr)}; + let res = T::get_link_ksettings(data, cmd)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn set_link_ksettings_callback( + dev: *mut bindings::net_device, + ptr: *const bindings::ethtool_link_ksettings, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let cmd = unsafe{EthtoolLinkKsettings::from_ptr(ptr)}; + let res = T::set_link_ksettings(data, cmd)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn set_coalesce_callback( + dev: *mut bindings::net_device, + ptr: *mut bindings::ethtool_coalesce, + kernel_coal: *mut bindings::kernel_ethtool_coalesce, + extack: *mut bindings::netlink_ext_ack, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let ethtool_coalesce = unsafe{EthtoolCoalesce::from_ptr(ptr)}; + let res = T::set_coalesce(data, ethtool_coalesce)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn get_coalesce_callback( + dev: *mut bindings::net_device, + ptr: *mut bindings::ethtool_coalesce, + kernel_coal: *mut bindings::kernel_ethtool_coalesce, + extack: *mut bindings::netlink_ext_ack, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let ethtool_coalesce = unsafe{EthtoolCoalesce::from_ptr(ptr)}; + let res = T::get_coalesce(data, ethtool_coalesce)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn get_rxfh_key_size_callback( + dev: *mut bindings::net_device, + ) -> bindings::u32_ { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::get_rxfh_key_size(data); + res + } + + unsafe extern "C" fn get_rxfh_indir_size_callback( + dev: *mut bindings::net_device, + ) -> bindings::u32_ { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::get_rxfh_indir_size(data); + res + } + + unsafe extern "C" fn get_rxfh_callback( + dev: *mut bindings::net_device, + indir: *mut bindings::u32_, + key: *mut bindings::u8_, + hfunc: *mut bindings::u8_, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::get_rxfh(data, indir, key, hfunc)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn set_rxfh_callback( + dev: *mut bindings::net_device, + indir: *const bindings::u32_, + key: *const bindings::u8_, + hfunc: bindings::u8_, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::set_rxfh(data, indir, key, hfunc)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn get_rxnfc_callback( + dev: *mut bindings::net_device, + ptr: *mut bindings::ethtool_rxnfc, + rule_locs: *mut bindings::u32_, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let info = unsafe{EthtoolRxnfc::from_ptr(ptr)}; + let res = T::get_rxnfc(data, info)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn set_rxnfc_callback( + dev: *mut bindings::net_device, + ptr: *mut bindings::ethtool_rxnfc, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::ethtool_ops_build`, we know that `data` must + // be valid. + let data = unsafe { + T::DataEthtoolOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `ptr` remains valid for the duration of this + // function call. + let info = unsafe{EthtoolRxnfc::from_ptr(ptr)}; + let res = T::set_rxnfc(data, info)?; + Ok(res.try_into().unwrap()) + } + } + + /// Builds an instance of `struct ethtool_ops`. + /// + /// # Safety + /// + /// The caller must ensure that `netdev_priv` will result in a value + /// that must be valid. + pub(crate) const unsafe fn ethtool_ops_build() -> &'static bindings::ethtool_ops { + &Self::ETHTOOL_OPS + } + + const NET_DEVICE_OPS: bindings::net_device_ops = bindings::net_device_ops { + ndo_open: if ::HAS_NDO_OPEN { + Some(Self::ndo_open_callbacks) + } else { + None + }, + ndo_stop: if ::HAS_NDO_STOP { + Some(Self::ndo_stop_callbacks) + } else { + None + }, + ndo_start_xmit: if ::HAS_NDO_START_XMIT { + Some(Self::ndo_start_xmit_callbacks) + } else { + None + }, + ndo_validate_addr: if ::HAS_NDO_VALIDATE_ADDR { + Some(Self::ndo_validate_addr_callbacks) + } else { + None + }, + ndo_set_mac_address: if ::HAS_NDO_SET_MAC_ADDRESS { + Some(Self::ndo_set_mac_address_callbacks) + } else { + None + }, + ndo_set_rx_mode: if ::HAS_NDO_SET_RX_MODE { + Some(Self::ndo_set_rx_mode_callbacks) + } else { + None + }, + ndo_get_stats64: if ::HAS_NDO_GET_STATS64 { + Some(Self::ndo_get_stats64_callbacks) + } else { + None + }, + ndo_vlan_rx_add_vid: if ::HAS_NDO_VLAN_RX_ADD_VID { + Some(Self::ndo_vlan_rx_add_vid_callbacks) + } else { + None + }, + ndo_vlan_rx_kill_vid: if ::HAS_NDO_VLAN_RX_KILL_VID { + Some(Self::ndo_vlan_rx_kill_vid_callbacks) + } else { + None + }, + // todo + ndo_bpf: None, + // todo + ndo_xdp_xmit: None, + ndo_features_check: if ::HAS_NDO_FEATURES_CHECK { + Some(Self::ndo_features_check_callbacks) + } else { + None + }, + ndo_get_phys_port_name: if ::HAS_NDO_GET_PHYS_PORT_NAME { + Some(Self::ndo_get_phys_port_name_callbacks) + } else { + None + }, + ndo_set_features: if ::HAS_NDO_SET_FEATURES { + Some(Self::ndo_set_features_callbacks) + } else { + None + }, + ndo_tx_timeout: if ::HAS_NDO_TX_TIMEOUT { + Some(Self::ndo_tx_timeout_callbacks) + } else { + None + }, + ndo_init: None, + ndo_uninit: None, + ndo_select_queue: None, + ndo_change_rx_flags: None, + ndo_do_ioctl: None, + ndo_eth_ioctl: None, + ndo_siocbond: None, + ndo_siocwandev: None, + ndo_siocdevprivate: None, + ndo_set_config: None, + ndo_change_mtu: None, + ndo_neigh_setup: None, + ndo_has_offload_stats: None, + ndo_get_offload_stats: None, + ndo_get_stats: None, + ndo_set_vf_mac: None, + ndo_set_vf_vlan: None, + ndo_set_vf_rate: None, + ndo_set_vf_spoofchk: None, + ndo_set_vf_trust: None, + ndo_get_vf_config: None, + ndo_set_vf_link_state: None, + ndo_get_vf_stats: None, + ndo_set_vf_port: None, + ndo_get_vf_port: None, + ndo_get_vf_guid: None, + ndo_set_vf_guid: None, + ndo_set_vf_rss_query_en: None, + ndo_setup_tc: None, + ndo_rx_flow_steer: None, + ndo_add_slave: None, + ndo_del_slave: None, + ndo_get_xmit_slave: None, + ndo_sk_get_lower_dev: None, + ndo_fix_features: None, + ndo_neigh_construct: None, + ndo_neigh_destroy: None, + ndo_fdb_add: None, + ndo_fdb_del: None, + ndo_fdb_del_bulk: None, + ndo_fdb_dump: None, + ndo_fdb_get: None, + ndo_bridge_setlink: None, + ndo_bridge_getlink: None, + ndo_bridge_dellink: None, + ndo_change_carrier: None, + ndo_get_phys_port_id: None, + ndo_get_port_parent_id: None, + ndo_dfwd_add_station: None, + ndo_dfwd_del_station: None, + ndo_set_tx_maxrate: None, + ndo_get_iflink: None, + ndo_fill_metadata_dst: None, + ndo_set_rx_headroom: None, + ndo_xdp_get_xmit_slave: None, + ndo_xsk_wakeup: None, + ndo_get_devlink_port: None, + ndo_tunnel_ctl: None, + ndo_get_peer_dev: None, + ndo_fill_forward_path: None, + ndo_get_tstamp: None, + }; + + unsafe extern "C" fn ndo_open_callbacks(dev: *mut bindings::net_device) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::ndo_open(data)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn ndo_stop_callbacks(dev: *mut bindings::net_device) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::ndo_stop(data)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn ndo_start_xmit_callbacks( + skb: *mut bindings::sk_buff, + dev: *mut bindings::net_device, + ) -> bindings::netdev_tx_t { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `skb` remains valid for the duration of this + // function call. + let skb = unsafe { SkBuff::from_ptr(skb) }; + let res = T::ndo_start_xmit(skb, data); + res + } + + unsafe extern "C" fn ndo_validate_addr_callbacks( + dev: *mut bindings::net_device, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::ndo_validate_addr(data)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn ndo_set_mac_address_callbacks( + dev: *mut bindings::net_device, + addr: *mut core::ffi::c_void, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `addr` remains valid for the duration of this + // function call. + let addr = unsafe{SocketAddr::V4(SocketAddrV4::from_ptr(addr))}; + let res = T::ndo_set_mac_address(data, addr)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn ndo_set_rx_mode_callbacks(dev: *mut bindings::net_device) { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + T::ndo_set_rx_mode(data); + } + + unsafe extern "C" fn ndo_get_stats64_callbacks( + dev: *mut bindings::net_device, + storage: *mut bindings::rtnl_link_stats64, + ) { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `storage` remains valid for the duration of this + // function call. + let stat = unsafe { RtnlLinkStats64::from_ptr(storage) }; + T::ndo_get_stats64(data, stat); + } + + unsafe extern "C" fn ndo_vlan_rx_add_vid_callbacks( + dev: *mut bindings::net_device, + proto: bindings::__be16, + vid: bindings::u16_, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::ndo_vlan_rx_add_vid(data, proto, vid)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn ndo_vlan_rx_kill_vid_callbacks( + dev: *mut bindings::net_device, + proto: bindings::__be16, + vid: bindings::u16_, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::ndo_vlan_rx_kill_vid(data, proto, vid)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn ndo_features_check_callbacks( + skb_ptr: *mut bindings::sk_buff, + dev: *mut bindings::net_device, + features: bindings::netdev_features_t, + ) -> bindings::netdev_features_t { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + // SAFETY: The C contract guarantees that `skb_ptr` remains valid for the duration of this + // function call. + let skb = unsafe { SkBuff::from_ptr(skb_ptr) }; + let res = T::ndo_features_check(data, skb, features); + res + } + + unsafe extern "C" fn ndo_get_phys_port_name_callbacks( + dev: *mut bindings::net_device, + name: *mut core::ffi::c_char, + len: usize, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + let buffer = unsafe { slice::from_raw_parts_mut(name as *mut u8, len) }; + let res = T::ndo_get_phys_port_name(data, buffer)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn ndo_set_features_callbacks( + dev: *mut bindings::net_device, + features: bindings::netdev_features_t, + ) -> core::ffi::c_int { + from_kernel_result! { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + let res = T::ndo_set_features(data, features)?; + Ok(res.try_into().unwrap()) + } + } + + unsafe extern "C" fn ndo_tx_timeout_callbacks( + dev: *mut bindings::net_device, + txqueue: core::ffi::c_uint, + ) { + // By the safety requirements of `NetDeviceTables::net_device_ops_build`, we know that `data` must + // be valid. + let data = unsafe { T::DataNetdevOps::borrow(bindings::netdev_priv(dev)) }; + T::ndo_tx_timeout(data, txqueue); + } + + /// Builds an instance of `struct net_device_ops`. + /// + /// # Safety + /// + /// The caller must ensure that `netdev_priv` will result in a value + /// that must be valid. + pub(crate) const unsafe fn net_device_ops_build() -> &'static bindings::net_device_ops { + &Self::NET_DEVICE_OPS + } +} From 9458339b374e12257212823c58cf819690f9926a Mon Sep 17 00:00:00 2001 From: Li Hongyu Date: Wed, 24 Aug 2022 01:25:06 +0900 Subject: [PATCH 2/3] rust: page: wrap the struct page_frag in the rust Many network drivers use struct page_frag to represent an area of memory in a page. This commits wrap it in the rust. Signed-off-by: Li Hongyu --- rust/helpers.c | 12 +++++++++++ rust/kernel/pages.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/rust/helpers.c b/rust/helpers.c index 7973b473ff1dfb..ec09d0085401d0 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -661,6 +661,18 @@ void *rust_helper_netdev_priv(const struct net_device *dev) } EXPORT_SYMBOL_GPL(rust_helper_netdev_priv); +void rust_helper_get_page(struct page *page) +{ + get_page(page); +} +EXPORT_SYMBOL_GPL(rust_helper_get_page); + +void rust_helper_put_page(struct page *page) +{ + put_page(page); +} +EXPORT_SYMBOL_GPL(rust_helper_put_page); + /* * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type * as the Rust `usize` type, so we can use it in contexts where Rust diff --git a/rust/kernel/pages.rs b/rust/kernel/pages.rs index f2bb26810cd74a..7ab8d9fcf665f1 100644 --- a/rust/kernel/pages.rs +++ b/rust/kernel/pages.rs @@ -142,3 +142,50 @@ impl Drop for PageMapping<'_> { unsafe { bindings::kunmap(self.page) }; } } +/// An area of memory in a page. +/// +/// `PageFrag` contains a `page_frag` which has a pointer to the page of order `ORDER`, a offset of +/// memory area in the page address and the size of the memory area. +/// +/// # Invariants +/// +/// The `page_frag::page` mut be initialized before using and points a valid address. +pub struct PageFrag { + pub(crate) page_frag: bindings::page_frag, +} + +impl PageFrag { + /// New an uninitialized `PageFrag`. + /// + /// # Safety + /// + /// Callers must call [`PageFrag::skb_page_frag_refill`] before using the `PageFrag` item. + pub unsafe fn new() -> Result { + let ret = bindings::page_frag::default(); + Ok(Self { page_frag: ret }) + } + + /// Refill the `PageFrag` or let `PageFrag.` point to a page having sufficient room. + pub fn skb_page_frag_refill(&mut self, len: u32, gfp: bindings::gfp_t) -> Result { + // SAFETY: We check the result in the following statement. + let ret = unsafe { + bindings::skb_page_frag_refill( + len, + &mut self.page_frag as *mut bindings::page_frag, + gfp, + ) + }; + if ret { + return Err(ENOMEM); + } + + Ok(0) + } +} + +impl Drop for PageFrag { + fn drop(&mut self) { + // SAFETY: By the type invariants, the `page` pointer inside `PageFrag` is valid. + unsafe { bindings::put_page(self.page_frag.page) }; + } +} From 7b6d8d67c3bfd3bd6e1ecaf12217e487032dd0b6 Mon Sep 17 00:00:00 2001 From: Li Hongyu Date: Tue, 13 Sep 2022 08:45:53 +0000 Subject: [PATCH 3/3] rust: scatterlist: add the support of scatterlist Signed-off-by: Li Hongyu --- rust/kernel/lib.rs | 1 + rust/kernel/scatterlist.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 rust/kernel/scatterlist.rs diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index b55fe00761c2bb..ebb0617e996e2d 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -83,6 +83,7 @@ mod build_assert; pub mod prelude; pub mod print; pub mod random; +pub mod scatterlist; mod static_assert; #[doc(hidden)] pub mod std_vendor; diff --git a/rust/kernel/scatterlist.rs b/rust/kernel/scatterlist.rs new file mode 100644 index 00000000000000..775205a712b1a2 --- /dev/null +++ b/rust/kernel/scatterlist.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! scatterlist +//! +//! C header: [`include/scatterlist.h`](../../../../../include/scatterlist.h) + +#![allow(dead_code)] + +use crate::bindings; + +/// A `Scatterlist` represents an area of physical memory. +/// +/// `Scatterlist` is always shown as an array. Each `Scatterlist` represents a continuous memory area in a page. +/// The `Scatterlist` array can represent continuous physical memory area. +#[repr(transparent)] +#[derive(Copy, Clone)] +pub struct Scatterlist { + pub(crate) scatterlist: bindings::scatterlist, +} + +impl Scatterlist { + /// New an array of `Scatterlist` and initialize the array. + pub fn new_and_init_sg_table() -> [Self; L] { + let mut sgs = [Self { + scatterlist: bindings::scatterlist::default(), + }; L]; + // SAFETY: The address of `sgs` is valid and the `L` matches the length of `sgs`. + unsafe { + bindings::sg_init_table( + &mut sgs as *mut [Self; L] as *mut bindings::scatterlist, + L.try_into().unwrap(), + ); + } + sgs + } +}