diff --git a/.cargo/config.toml b/.cargo/config.toml index e44b40a0..95460b58 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -9,3 +9,9 @@ target = "x86_64-unknown-linux-gnu" [alias] just = ["just", "cargo"] sterile = ["just", "sterile", "cargo"] + +[target.x86_64-unknown-linux-gnu] +runner = "sudo -E" + +[target.x86_64-unknown-linux-musl] +runner = "sudo -E" diff --git a/Cargo.lock b/Cargo.lock index 9cf99b35..c146555a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -373,13 +373,14 @@ dependencies = [ name = "dpdk" version = "0.1.0" dependencies = [ + "bitflags", "dpdk-sys", "dpdk-sysroot-helper", "errno", "etherparse", "net", "serde", - "thiserror-no-std", + "thiserror", "tracing", ] @@ -412,7 +413,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" name = "errno" version = "0.1.0" dependencies = [ - "thiserror-no-std", + "thiserror", ] [[package]] @@ -581,7 +582,7 @@ dependencies = [ "etherparse", "rand", "serde", - "thiserror-no-std", + "thiserror", "tracing", ] @@ -884,7 +885,7 @@ dependencies = [ "dpdk-sys", "dpdk-sysroot-helper", "rand", - "thiserror-no-std", + "thiserror", "tracing", "tracing-subscriber", ] @@ -971,23 +972,23 @@ dependencies = [ ] [[package]] -name = "thiserror-impl-no-std" +name = "thiserror" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" +checksum = "037e29b009aa709f293b974da5cd33b15783c049e07f8435778ce8c4871525d8" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "thiserror-impl", ] [[package]] -name = "thiserror-no-std" +name = "thiserror-impl" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" +checksum = "ea4778c7e8ff768bdb32a58a2349903859fe719a320300d7d4ce8636f19a1e69" dependencies = [ - "thiserror-impl-no-std", + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 85172e2d..e72f15d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,9 @@ rand = { version = "0.8.5", default-features = false, features = [] } rstest = { version = "0.23.0", default-features = false, features = [] } serde = { version = "1.0.213", default-features = false, features = ["derive", "alloc", "rc"] } syscalls = { version = "0.6.18" } -thiserror = { version = "2.0.0", package = "thiserror-no-std" } # replace with regular thiserror when https://github.com/dtolnay/thiserror/pull/304 lands +thiserror = { version = "2.0.2", default-features = false } tracing = { version = "0.1.40", default-features = false, features = ["attributes"] } tracing-subscriber = { version = "0.3.18" } tracing-test = { version = "0.2.5" } +bitflags = { version = "2.6.0", default-features = false } diff --git a/dpdk-sys/build.rs b/dpdk-sys/build.rs index 23267f4b..d3ff1b73 100644 --- a/dpdk-sys/build.rs +++ b/dpdk-sys/build.rs @@ -46,7 +46,7 @@ fn bind(path: &Path, sysroot: &str) { // (not the items themselves, just the documentation associated with them) // I suspect this is a bug in bindgen, but I'm not sure. // I don't have any reason to think we need any of these functions and I'd - // rather have the doc comments on for the rest of the project + // rather have the doc comments on for the rest of the project, so turn these off. .blocklist_type("rte_bus_cmp_t") .blocklist_type("rte_class_cmp_t") .blocklist_function("rte_bus_find") @@ -57,6 +57,7 @@ fn bind(path: &Path, sysroot: &str) { .blocklist_function("rte_pci_addr_cmp") .blocklist_function("rte_pci_addr_parse") // rustc doesn't like repr(packed) types which contain other repr(packed) types + // I am very confident that we don't need these structs anyway. .opaque_type("rte_arp_hdr") .opaque_type("rte_arp_ipv4") .opaque_type("rte_gtp_psc_generic_hdr") @@ -103,6 +104,7 @@ fn main() { "mlx5", "nl-route-3", "nl-3", + "numa", ]; diff --git a/dpdk/Cargo.toml b/dpdk/Cargo.toml index 06c2d424..aedee8b3 100644 --- a/dpdk/Cargo.toml +++ b/dpdk/Cargo.toml @@ -32,6 +32,7 @@ etherparse = { workspace = true, default-features = false, features = [] } serde = { workspace = true, optional = true } thiserror = { workspace = true } tracing = { workspace = true, features = ["attributes"] } +bitflags = { workspace = true } [build-dependencies] dpdk-sysroot-helper = { path = "../dpdk-sysroot-helper" } diff --git a/dpdk/src/dev.rs b/dpdk/src/dev.rs index 269af95e..90158bf3 100644 --- a/dpdk/src/dev.rs +++ b/dpdk/src/dev.rs @@ -59,7 +59,6 @@ impl DevIndex { self.0 } - #[tracing::instrument(level = "trace", ret)] /// Get information about an ethernet device. /// /// # Arguments @@ -74,33 +73,34 @@ impl DevIndex { /// # Safety /// /// This function should never panic assuming DPDK is correctly implemented. + #[tracing::instrument(level = "trace", ret)] pub fn info(&self) -> Result { let mut dev_info = rte_eth_dev_info::default(); let ret = unsafe { rte_eth_dev_info_get(self.0, &mut dev_info) }; if ret != 0 { - match ret { + return match ret { errno::NEG_ENOTSUP => { error!( "Device information not supported for port {index}", index = self.0 ); - return Err(DevInfoError::NotSupported); + Err(DevInfoError::NotSupported) } errno::NEG_ENODEV => { error!( "Device information not available for port {index}", index = self.0 ); - return Err(DevInfoError::NotAvailable); + Err(DevInfoError::NotAvailable) } errno::NEG_EINVAL => { error!( "Invalid argument when getting device info for port {index}", index = self.0 ); - return Err(DevInfoError::InvalidArgument); + Err(DevInfoError::InvalidArgument) } val => { let _unknown = match StandardErrno::parse_i32(val) { @@ -120,9 +120,9 @@ impl DevIndex { index = self.0, val = val ); - return Err(DevInfoError::Unknown(errno::Errno(val))); + Err(DevInfoError::Unknown(Errno(val))) } - } + }; // error!( // "Failed to get device info for port {index}: {err}", // index = self.0 @@ -150,7 +150,8 @@ impl DevIndex { /// (statically ensured). /// * This function may panic if DPDK returns an unexpected (undocumented) error code after /// failing to determine the socket id. - pub fn socket_id(&self) -> Result { + #[tracing::instrument(level = "trace", ret)] + pub fn socket_id(&self) -> Result { let socket_id = unsafe { rte_eth_dev_socket_id(self.as_u16()) }; if socket_id == -1 { match unsafe { wrte_errno() } { @@ -160,7 +161,7 @@ impl DevIndex { } errno::EINVAL => { // We are asking DPDK for the socket id of a port that doesn't exist. - return Err(errno::ErrorCode::parse_i32(errno::EINVAL)); + return Err(ErrorCode::parse_i32(errno::EINVAL)); } errno => { // Getting here means we have an unknown error. @@ -313,13 +314,13 @@ impl From for RxOffload { } } -#[non_exhaustive] -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] /// Verbose configuration for transmit offloads. /// /// This struct is mostly for coherent reporting on network cards. /// /// TODO: fill in remaining offload types from `rte_ethdev.h` +#[non_exhaustive] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TxOffloadConfig { /// GENEVE tunnel segmentation offload. pub geneve_tnl_tso: bool, @@ -382,6 +383,36 @@ impl Display for TxOffloadConfig { } } +bitflags! { + #[repr(transparent)] + #[derive(Debug, PartialEq, Eq)] + pub struct TxOffloads: u64 { + const VLAN_INSERT = wrte_eth_tx_offload::TX_OFFLOAD_VLAN_INSERT; + const IPV4_CKSUM = wrte_eth_tx_offload::TX_OFFLOAD_IPV4_CKSUM; + const UDP_CKSUM = wrte_eth_tx_offload::TX_OFFLOAD_UDP_CKSUM; + const TCP_CKSUM = wrte_eth_tx_offload::TX_OFFLOAD_TCP_CKSUM; + const SCTP_CKSUM = wrte_eth_tx_offload::TX_OFFLOAD_SCTP_CKSUM; + const TCP_TSO = wrte_eth_tx_offload::TX_OFFLOAD_TCP_TSO; + const UDP_TSO = wrte_eth_tx_offload::TX_OFFLOAD_UDP_TSO; + const OUTER_IPV4_CKSUM = wrte_eth_tx_offload::TX_OFFLOAD_OUTER_IPV4_CKSUM; + const QINQ_INSERT = wrte_eth_tx_offload::TX_OFFLOAD_QINQ_INSERT; + const VXLAN_TNL_TSO = wrte_eth_tx_offload::TX_OFFLOAD_VXLAN_TNL_TSO; + const GRE_TNL_TSO = wrte_eth_tx_offload::TX_OFFLOAD_GRE_TNL_TSO; + const IPIP_TNL_TSO = wrte_eth_tx_offload::TX_OFFLOAD_IPIP_TNL_TSO; + const GENEVE_TNL_TSO = wrte_eth_tx_offload::TX_OFFLOAD_GENEVE_TNL_TSO; + const MACSEC_INSERT = wrte_eth_tx_offload::TX_OFFLOAD_MACSEC_INSERT; + const MT_LOCKFREE = wrte_eth_tx_offload::TX_OFFLOAD_MT_LOCKFREE; + const MULTI_SEGS = wrte_eth_tx_offload::TX_OFFLOAD_MULTI_SEGS; + const MBUF_FAST_FREE = wrte_eth_tx_offload::TX_OFFLOAD_MBUF_FAST_FREE; + const SECURITY = wrte_eth_tx_offload::TX_OFFLOAD_SECURITY; + const UDP_TNL_TSO = wrte_eth_tx_offload::TX_OFFLOAD_UDP_TNL_TSO; + const IP_TNL_TSO = wrte_eth_tx_offload::TX_OFFLOAD_IP_TNL_TSO; + const OUTER_UDP_CKSUM = wrte_eth_tx_offload::TX_OFFLOAD_OUTER_UDP_CKSUM; + const SEND_ON_TIMESTAMP = wrte_eth_tx_offload::TX_OFFLOAD_SEND_ON_TIMESTAMP; + const _ = !0; + } +} + impl From for TxOffload { fn from(value: TxOffloadConfig) -> Self { use wrte_eth_tx_offload::*; @@ -428,6 +459,13 @@ impl From for TxOffload { } else { 0 } + | if value.qinq_insert { TX_OFFLOAD_QINQ_INSERT } else { 0 } + | if value.sctp_cksum { TX_OFFLOAD_SCTP_CKSUM } else { 0 } + | if value.tcp_cksum { TX_OFFLOAD_TCP_CKSUM } else { 0 } + | if value.tcp_tso { TX_OFFLOAD_TCP_TSO } else { 0 } + | if value.udp_cksum { TX_OFFLOAD_UDP_CKSUM } else { 0 } + | if value.udp_tso { TX_OFFLOAD_UDP_TSO } else { 0 } + | if value.vlan_insert { TX_OFFLOAD_VLAN_INSERT } else { 0 } | if value.udp_tso { TX_OFFLOAD_UDP_TSO } else { 0 } | if value.vlan_insert { TX_OFFLOAD_VLAN_INSERT @@ -479,8 +517,7 @@ impl TxOffload { /// MACsec insertion. pub const MACSEC_INSERT: TxOffload = TxOffload(wrte_eth_tx_offload::TX_OFFLOAD_MACSEC_INSERT); /// Outer IPv4 checksum calculation. - pub const OUTER_IPV4_CKSUM: TxOffload = - TxOffload(wrte_eth_tx_offload::TX_OFFLOAD_OUTER_IPV4_CKSUM); + pub const OUTER_IPV4_CKSUM: TxOffload = TxOffload(wrte_eth_tx_offload::TX_OFFLOAD_OUTER_IPV4_CKSUM); /// QinQ (double VLAN) insertion. pub const QINQ_INSERT: TxOffload = TxOffload(wrte_eth_tx_offload::TX_OFFLOAD_QINQ_INSERT); /// SCTP checksum calculation. @@ -562,15 +599,28 @@ impl BitXorAssign for TxOffload { } } -#[derive(Debug, PartialEq)] /// Information about a DPDK ethernet device. /// /// This struct is a wrapper around the `rte_eth_dev_info` struct from DPDK. +#[derive(Debug, PartialEq)] pub struct DevInfo { pub(crate) index: DevIndex, pub(crate) inner: rte_eth_dev_info, } +bitflags! { + #[repr(transparent)] + #[derive(Debug, PartialEq, Eq)] + pub struct DevCapabilities: u64 { + const RUNTIME_RX_QUEUE_SETUP = RTE_ETH_DEV_CAPA_RUNTIME_RX_QUEUE_SETUP as u64; + const RUNTIME_TX_QUEUE_SETUP = RTE_ETH_DEV_CAPA_RUNTIME_TX_QUEUE_SETUP as u64; + const RXQ_SHARE = RTE_ETH_DEV_CAPA_RXQ_SHARE as u64; + const FLOW_RULE_KEEP = RTE_ETH_DEV_CAPA_FLOW_RULE_KEEP as u64; + const FLOW_SHARED_OBJECT_KEEP = RTE_ETH_DEV_CAPA_FLOW_SHARED_OBJECT_KEEP as u64; + const _ = !0; + } +} + #[repr(transparent)] #[derive(Debug)] struct DevIterator { @@ -693,30 +743,38 @@ impl DevInfo { self.inner.if_index } - #[allow(clippy::expect_used)] - #[tracing::instrument(level = "debug")] /// Get the driver name of the device. /// /// # Panics /// /// This function will panic if the driver name is not valid utf-8. + #[allow(clippy::expect_used)] + #[tracing::instrument(level = "debug")] pub fn driver_name(&self) -> &str { unsafe { CStr::from_ptr(self.inner.driver_name) } .to_str() .expect("driver name is not valid utf-8") } - #[tracing::instrument(level = "trace")] /// Get the maximum set of available tx offloads supported by the device. + #[tracing::instrument(level = "trace")] pub fn tx_offload_caps(&self) -> TxOffload { self.inner.tx_offload_capa.into() } - #[tracing::instrument(level = "trace")] /// Get the maximum set of available rx offloads supported by the device. + #[tracing::instrument(level = "trace")] pub fn rx_offload_caps(&self) -> RxOffload { self.inner.rx_offload_capa.into() } + + /// Get the capabilities of the device. + /// + /// See [`DevCapabilities`] + #[tracing::instrument(level = "trace")] + pub fn capabilities(&self) -> DevCapabilities { + DevCapabilities::from_bits_retain(self.inner.dev_capa) + } } #[derive(Debug)] @@ -728,7 +786,7 @@ pub struct Dev { pub config: DevConfig, pub(crate) rx_queues: Vec, pub(crate) tx_queues: Vec, - pub(crate) hairpin_queues: Vec, + pub(crate) hairpin_queues: Vec, } impl Dev { @@ -854,7 +912,7 @@ impl Drop for Dev { port = self.info.index() ); match self.stop() { - Ok(()) => { + Ok(_) => { info!("Device {port} stopped", port = self.info.index()); } Err(err) => { @@ -873,5 +931,5 @@ pub enum SocketIdLookupError { #[error("Invalid port ID")] DevDoesNotExist(DevIndex), #[error("Unknown error code set")] - UnknownErrno(errno::ErrorCode), + UnknownErrno(ErrorCode), } diff --git a/dpdk/src/mem.rs b/dpdk/src/mem.rs index c40fbf20..9a40cdab 100644 --- a/dpdk/src/mem.rs +++ b/dpdk/src/mem.rs @@ -385,7 +385,7 @@ impl Mbuf { pub(crate) fn new_from_raw(raw: *mut rte_mbuf) -> Option { let raw = match NonNull::new(raw) { None => { - warn!("Attempted to create Mbuf from null pointer"); + error!("Attempted to create Mbuf from null pointer"); return None; } Some(raw) => raw, diff --git a/dpdk/src/queue/rx.rs b/dpdk/src/queue/rx.rs index b5cffcff..eda96eaa 100644 --- a/dpdk/src/queue/rx.rs +++ b/dpdk/src/queue/rx.rs @@ -8,7 +8,8 @@ use crate::mem::Mbuf; use crate::socket::SocketId; use crate::{dev, mem, socket}; use dpdk_sys::*; -use tracing::{trace, warn}; +use errno::ErrorCode; +use tracing::{info, trace, warn}; #[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -60,15 +61,15 @@ pub struct RxQueueConfig { #[derive(Debug)] pub enum ConfigFailure { /// The device has been removed. - DeviceRemoved(errno::Errno), + DeviceRemoved(ErrorCode), /// Invalid arguments were passed to the receive queue configuration. - InvalidArgument(errno::Errno), + InvalidArgument(ErrorCode), /// Memory allocation failed. - NoMemory(errno::Errno), + NoMemory(ErrorCode), /// An unexpected (i.e. undocumented) error occurred. - Unexpected(errno::Errno), + Unexpected(ErrorCode), /// The socket preference setting did not resolve a known socket. - InvalidSocket(errno::Errno), + InvalidSocket(ErrorCode), } /// DPDK rx queue @@ -87,9 +88,13 @@ impl RxQueue { /// /// This design ensures that the hairpin queue is correctly tracked in the list of queues /// associated with the device. + #[tracing::instrument(level = "info")] pub(crate) fn configure(dev: &dev::Dev, config: RxQueueConfig) -> Result { - let socket_id = SocketId::try_from(config.socket_preference) - .map_err(|_| ConfigFailure::InvalidSocket(errno::Errno(errno::NEG_EINVAL)))?; + use ConfigFailure::*; + let socket_id = + SocketId::try_from(config.socket_preference).map_err(InvalidSocket)?; + // dev.info.index + // info!("Configuring RX queue on socket {socket_id} for device {dev_info}", d); let rx_conf = rte_eth_rxconf { offloads: dev.info.inner.rx_queue_offload_capa, @@ -111,14 +116,15 @@ impl RxQueue { dev: dev.info.index(), config, }), - errno::NEG_ENODEV => Err(ConfigFailure::DeviceRemoved(errno::Errno(ret))), - errno::NEG_EINVAL => Err(ConfigFailure::InvalidArgument(errno::Errno(ret))), - errno::NEG_ENOMEM => Err(ConfigFailure::NoMemory(errno::Errno(ret))), - val => Err(ConfigFailure::Unexpected(errno::Errno(val))), + errno::NEG_ENODEV => Err(DeviceRemoved(ErrorCode::parse_i32(ret))), + errno::NEG_EINVAL => Err(InvalidArgument(ErrorCode::parse_i32(ret))), + errno::NEG_ENOMEM => Err(NoMemory(ErrorCode::parse_i32(ret))), + _ => Err(Unexpected(ErrorCode::parse_i32(ret))), } } /// Start the receive queue. + #[tracing::instrument(level = "info")] pub(crate) fn start(self) -> Result { let ret = unsafe { rte_eth_dev_rx_queue_start(self.dev.as_u16(), self.config.queue_index.as_u16()) @@ -135,6 +141,7 @@ impl RxQueue { } /// Start the receive queue. + #[tracing::instrument(level = "info")] pub(crate) fn stop(self) -> Result { let ret = unsafe { rte_eth_dev_rx_queue_stop(self.dev.as_u16(), self.config.queue_index.as_u16()) @@ -175,57 +182,45 @@ impl RxQueue { } } -/// TODO +/// Types of errors associated with starting RX queues #[derive(thiserror::Error, Debug)] pub enum RxQueueStartError { - /// TODO - #[error("Invalid port ID")] + #[error("The port ID associated with this RX queue is invalid")] InvalidPortId, - /// TODO - #[error("Queue ID out of range")] + #[error("The specified queue id is outside the range of known RX queues")] QueueIdOutOfRange, - /// TODO - #[error("Device removed")] + #[error("The network device associated with this RX queue has been removed")] DeviceRemoved, - /// TODO - #[error("Invalid argument")] + #[error("Invalid arguments supplied to RX queue configuration")] InvalidArgument, - /// TODO - #[error("Operation not supported")] + #[error("Operation is not supported (check device state and rx queue config)")] NotSupported, - /// TODO - #[error("Unknown error")] + #[error("Unknown error encountered when starting RX queue")] Unexpected(errno::Errno), } -/// TODO +/// Types of errors associated with stopping RX queues #[derive(thiserror::Error, Debug)] pub enum RxQueueStopError { - /// TODO #[error("Invalid port ID")] InvalidPortId, - /// TODO #[error("Queue ID out of range")] QueueIdOutOfRange, - /// TODO #[error("Device removed")] DeviceRemoved, - /// TODO #[error("Invalid argument")] InvalidArgument, - /// TODO #[error("Operation not supported")] NotSupported, - /// TODO #[error("Unexpected error")] Unexpected(errno::Errno), } -/// TODO +/// States of RX queues #[derive(Debug)] pub enum RxQueueState { - /// TODO + /// Stopped RX queue Stopped, - /// TODO + /// Started RX queue Started, } diff --git a/dpdk/src/queue/tx.rs b/dpdk/src/queue/tx.rs index bf4fbe25..479dfdb3 100644 --- a/dpdk/src/queue/tx.rs +++ b/dpdk/src/queue/tx.rs @@ -3,6 +3,7 @@ //! Transmit queue configuration and management. +use tracing::info; use crate::dev::DevIndex; use crate::{dev, socket}; use dpdk_sys::*; @@ -89,6 +90,7 @@ impl TxQueue { /// /// This design ensures that the hairpin queue is correctly tracked in the list of queues /// associated with the device. + #[tracing::instrument(level = "info")] pub(crate) fn configure(dev: &dev::Dev, config: TxQueueConfig) -> Result { let socket_id = socket::SocketId::try_from(config.socket_preference) .map_err(|err| ConfigFailure::InvalidSocket(ConfigError { code: -1, err }))?; @@ -125,84 +127,74 @@ impl TxQueue { /// Start the transmit queue. pub(crate) fn start(self) -> Result { + use TxQueueStartError::*; let ret = unsafe { rte_eth_dev_tx_queue_start(self.dev.as_u16(), self.config.queue_index.as_u16()) }; match ret { errno::SUCCESS => Ok(self), - errno::NEG_ENODEV => Err(TxQueueStartError::DeviceRemoved), - errno::NEG_EINVAL => Err(TxQueueStartError::InvalidArgument), - errno::NEG_EIO => Err(TxQueueStartError::DeviceRemoved), - errno::NEG_ENOTSUP => Err(TxQueueStartError::NotSupported), - val => Err(TxQueueStartError::Unexpected(errno::Errno(val))), + errno::NEG_ENODEV => Err(DeviceRemoved), + errno::NEG_EINVAL => Err(InvalidArgument), + errno::NEG_EIO => Err(DeviceRemoved), + errno::NEG_ENOTSUP => Err(NotSupported), + val => Err(Unexpected(errno::Errno(val))), } } /// Stop the transmit queue. pub(crate) fn stop(self) -> Result { + use TxQueueStopError::*; let ret = unsafe { rte_eth_dev_tx_queue_stop(self.dev.as_u16(), self.config.queue_index.as_u16()) }; match ret { errno::SUCCESS => Ok(self), - errno::NEG_ENODEV => Err(TxQueueStopError::DeviceRemoved), - errno::NEG_EINVAL => Err(TxQueueStopError::InvalidArgument), - errno::NEG_EIO => Err(TxQueueStopError::DeviceRemoved), - errno::NEG_ENOTSUP => Err(TxQueueStopError::NotSupported), - val => Err(TxQueueStopError::Unexpected(errno::Errno(val))), + errno::NEG_ENODEV => Err(DeviceRemoved), + errno::NEG_EINVAL => Err(InvalidArgument), + errno::NEG_EIO => Err(DeviceRemoved), + errno::NEG_ENOTSUP => Err(NotSupported), + val => Err(Unexpected(errno::Errno(val))), } } } -/// TODO +/// A DPDK transmit queue #[derive(Debug)] pub struct TxQueue { pub(crate) config: TxQueueConfig, pub(crate) dev: DevIndex, } -/// TODO +/// Types of errors associated with starting TX queues #[derive(thiserror::Error, Debug)] pub enum TxQueueStartError { - /// TODO - #[error("Invalid port ID")] + #[error("The port ID associated with this TX queue is invalid")] InvalidPortId, - /// TODO - #[error("Queue ID out of range")] + #[error("The specified queue id is outside the range of known TX queues")] QueueIdOutOfRange, - /// TODO - #[error("Device removed")] + #[error("The network device associated with this TX queue has been removed")] DeviceRemoved, - /// TODO - #[error("Invalid argument")] + #[error("Invalid arguments supplied to TX queue configuration")] InvalidArgument, - /// TODO - #[error("Operation not supported")] + #[error("Operation is not supported (check device state and rx queue config)")] NotSupported, - /// TODO - #[error("Unknown error")] + #[error("Unknown error encountered when starting TX queue")] Unexpected(errno::Errno), } -/// TODO +/// Types of errors associated with stopping TX queues #[derive(thiserror::Error, Debug)] -#[repr(i32)] pub enum TxQueueStopError { - /// TODO #[error("Invalid port ID")] - InvalidPortId = errno::NEG_ENODEV, - /// TODO + InvalidPortId, #[error("Device removed")] - DeviceRemoved = errno::NEG_EIO, - /// TODO + DeviceRemoved, #[error("Invalid argument")] - InvalidArgument = errno::NEG_EINVAL, - /// TODO + InvalidArgument, #[error("Operation not supported")] - NotSupported = errno::NEG_ENOTSUP, - /// TODO - #[error("Unknown error")] + NotSupported, + #[error("Unexpected error")] Unexpected(errno::Errno), } diff --git a/dpdk/src/socket.rs b/dpdk/src/socket.rs index c2558540..7ddcad14 100644 --- a/dpdk/src/socket.rs +++ b/dpdk/src/socket.rs @@ -9,11 +9,13 @@ //! we're sticking with that. //! //! [NUMA]: https://en.wikipedia.org/wiki/Non-uniform_memory_access + use crate::dev::DevIndex; #[allow(unused_imports)] /// imported for rustdoc use crate::eal::Eal; use core::ffi::c_uint; +use core::fmt::{Display, Formatter}; use core::marker::PhantomData; use dpdk_sys::rte_socket_id; use errno::{ErrorCode, StandardErrno}; @@ -164,6 +166,12 @@ impl Iterator for SocketIdIterator { /// [NUMA]: https://en.wikipedia.org/wiki/Non-uniform_memory_access pub struct SocketId(pub(crate) c_uint); +impl Display for SocketId { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", self.0) + } +} + impl SocketId { /// A special [`SocketId`] that represents any socket. pub const ANY: SocketId = SocketId(c_uint::MAX /* -1 in c_int */); @@ -179,7 +187,7 @@ impl SocketId { /// # Note /// /// Ideally, this method should be accessed via the [`Manager::id_for_index`] object as that - /// makes lifetime issues simpler. + /// simplifies lifetime issues. pub(crate) fn current() -> SocketId { SocketId(unsafe { rte_socket_id() }) } diff --git a/errno/src/lib.rs b/errno/src/lib.rs index 67295c80..d792e1ab 100644 --- a/errno/src/lib.rs +++ b/errno/src/lib.rs @@ -1094,12 +1094,12 @@ impl TryFrom for NegStandardErrno { /// Standard errno values negated #[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)] +#[must_use] #[error(transparent)] -pub struct NegStandardErrno(StandardErrno); +pub struct NegStandardErrno(pub StandardErrno); impl StandardErrno { /// Negate the errno value. - #[must_use] pub const fn neg(self) -> NegStandardErrno { NegStandardErrno(self) } @@ -1193,379 +1193,3 @@ impl ErrorCode { Self::parse_i32(val.into()) } } - -/// Standard errno values -#[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)] -#[repr(i32)] -pub enum StandardErrnoAgain { - /// No error. Successful operation. - #[error("No error. Successful operation.")] - Success(u8) = SUCCESS, - /// Operation not permitted - #[error("Operation not permitted")] - PermissionDenied = EPERM, - /// No such file or directory - #[error("No such file or directory")] - NoSuchFileOrDirectory = ENOENT, - /// No such process - #[error("No such process")] - NoSuchProcess = ESRCH, - /// Interrupted system call - #[error("Interrupted system call")] - Interrupted = EINTR, - /// I/O error - #[error("I/O error")] - Io = EIO, - /// No such device or address - #[error("No such device or address")] - NoSuchDeviceOrAddress = ENXIO, - /// Argument list too long - #[error("Argument list too long")] - TooBig = E2BIG, - /// Exec format error - #[error("Exec format error")] - ExecFormat = ENOEXEC, - /// Bad file number - #[error("Bad file number")] - BadFileNumber = EBADF, - /// No child processes - #[error("No child processes")] - NoChildProcesses = ECHILD, - /// Try again - #[error("Try again")] - TryAgain = EAGAIN, - /// No memory available - #[error("No memory available")] - NoMemory = ENOMEM, - /// Access denied - #[error("Access denied")] - AccessDenied = EACCES, - /// Bad address - #[error("Bad address")] - BadAddress = EFAULT, - /// Block device required - #[error("Block device required")] - BlockDeviceRequired = ENOTBLK, - /// Device or resource busy - #[error("Device or resource busy")] - Busy = EBUSY, - /// File exists - #[error("File exists")] - FileExists = EEXIST, - /// Cross-device link - #[error("Cross-device link")] - CrossDeviceLink = EXDEV, - /// No such device - #[error("No such device")] - NoSuchDevice = ENODEV, - /// Not a directory - #[error("Not a directory")] - NotADirectory = ENOTDIR, - /// Is a directory - #[error("Is a directory")] - IsADirectory = EISDIR, - /// Invalid argument - #[error("Invalid argument")] - InvalidArgument = EINVAL, - /// File table overflow - #[error("File table overflow")] - FileTableOverflow = ENFILE, - /// Too many open files - #[error("Too many open files")] - TooManyOpenFiles = EMFILE, - /// Not a tty - #[error("Not a tty")] - NotATty = ENOTTY, - /// Text file busy - #[error("Text file busy")] - TextFileBusy = ETXTBSY, - /// File too large - #[error("File too large")] - FileTooLarge = EFBIG, - /// No space left on a device - #[error("No space left on device")] - NoSpaceLeftOnDevice = ENOSPC, - /// Illegal seek - #[error("Illegal seek")] - IllegalSeek = ESPIPE, - /// Read-only file system - #[error("Read-only file system")] - ReadOnlyFileSystem = EROFS, - /// Too many links - #[error("Too many links")] - TooManyLinks = EMLINK, - /// Broken pipe - #[error("Broken pipe")] - BrokenPipe = EPIPE, - /// Numerical argument out of domain - #[error("Numerical argument out of domain")] - NumberOutOfDomain = EDOM, - /// Result too large - #[error("Result too large")] - ResultTooLarge = ERANGE, - /// No message of desired type - #[error("No message of desired type")] - NoMessage = ENOMSG, - /// Identifier removed - #[error("Identifier removed")] - IdentifierRemoved = EIDRM, - /// Channel number out of range - #[error("Channel number out of range")] - ChannelNumberOutOfRange = ECHRNG, - /// Level 2 not synchronized - #[error("Level 2 not synchronized")] - Level2NotSynchronized = EL2NSYNC, - /// Level 3 halted - #[error("Level 3 halted")] - Level3Halted = EL3HLT, - /// Level 3 reset - #[error("Level 3 reset")] - Level3Reset = EL3RST, - /// Link number out of range - #[error("Link number out of range")] - LinkNumberOutOfRange = ELNRNG, - /// Protocol driver not attached - #[error("Protocol driver not attached")] - ProtocolDriverNotAttached = EUNATCH, - /// No CSI structure available - #[error("No CSI structure available")] - NoCsiStructureAvailable = ENOCSI, - /// Level 2 halted - #[error("Level 2 halted")] - Level2Halted = EL2HLT, - /// Deadlock condition - #[error("Deadlock condition")] - Deadlock = EDEADLK, - /// No record locks available - #[error("No record locks available")] - NoRecordLocksAvailable = ENOLCK, - /// Invalid exchange - #[error("Invalid exchange")] - InvalidExchange = EBADE, - /// Invalid request descriptor - #[error("Invalid request descriptor")] - InvalidRequestDescriptor = EBADR, - /// Exchange full - #[error("Exchange full")] - ExchangeFull = EXFULL, - /// No anode - #[error("No anode")] - NoAnode = ENOANO, - /// Invalid request code - #[error("Invalid request code")] - InvalidRequestCode = EBADRQC, - /// Invalid slot - #[error("Invalid slot")] - InvalidSlot = EBADSLT, - /// File locking deadlock error - #[error("File locking deadlock error")] - FileLockingDeadlock = EDEADLOCK, - /// Bad font file format - #[error("Bad font file format")] - BadFontFileFormat = EBFONT, - /// Device not a stream - #[error("Device not a stream")] - DeviceNotAStream = ENOSTR, - /// No data available - #[error("No data available")] - NoDataAvailable = ENODATA, - /// Timer expired - #[error("Timer expired")] - TimerExpired = ETIME, - /// Out of streams resources - #[error("Out of streams resources")] - OutOfStreamsResources = ENOSR, - /// Machine is not on the network - #[error("Machine is not on the network")] - MachineNotOnTheNetwork = ENONET, - /// Package not installed - #[error("Package not installed")] - PackageNotInstalled = ENOPKG, - /// The object is remote - #[error("The object is remote")] - ObjectIsRemote = EREMOTE, - /// The link has been severed - #[error("The link has been severed")] - LinkSevered = ENOLINK, - /// Advertise error - #[error("Advertise error")] - AdvertiseError = EADV, - /// Srmount error - #[error("Srmount error")] - SrmountError = ESRMNT, - /// Communication error on send - #[error("Communication error on send")] - CommunicationErrorOnSend = ECOMM, - /// Protocol error - #[error("Protocol error")] - ProtocolError = EPROTO, - /// Multihop attempted - #[error("Multihop attempted")] - MultihopAttempted = EMULTIHOP, - /// Inode is remote (not really error) - #[error("Inode is remote (not really error)")] - InodeIsRemote = ELBIN, - /// Cross-mount point (not really error) - #[error("Cross mount point (not really error)")] - CrossMountPoint = EDOTDOT, - /// Trying to read an unreadable message - #[error("Trying to read unreadable message")] - TryingToReadUnreadableMessage = EBADMSG, - /// Inappropriate file type or format - #[error("Inappropriate file type or format")] - InappropriateFileTypeOrFormat = EFTYPE, - /// Given log name not unique - #[error("Given log name not unique")] - GivenLogNameNotUnique = ENOTUNIQ, - /// f.d. invalid for this operation - #[error("f.d. invalid for this operation")] - FdInvalidForThisOperation = EBADFD, - /// Remote address changed - #[error("Remote address changed")] - RemoteAddressChanged = EREMCHG, - /// Can't access a necessary shared library - #[error("Can't access a needed shared library")] - CantAccessNeededSharedLibrary = ELIBACC, - /// Accessing a corrupted shared library - #[error("Accessing a corrupted shared library")] - AccessingCorruptedSharedLibrary = ELIBBAD, - /// .lib section in a.out corrupted - #[error(".lib section in a.out corrupted")] - LibSectionInAOutCorrupted = ELIBSCN, - /// Attempting to link in too many libs - #[error("Attempting to link in too many libs")] - AttemptingToLinkInTooManyLibs = ELIBMAX, - /// Attempting to exec a shared library - #[error("Attempting to exec a shared library")] - AttemptingToExecASharedLibrary = ELIBEXEC, - /// Function is not implemented - #[error("Function not implemented")] - FunctionNotImplemented = ENOSYS, - /// No more files - #[error("No more files")] - NoMoreFiles = ENMFILE, - /// Directory is not empty - #[error("Directory not empty")] - DirectoryNotEmpty = ENOTEMPTY, - /// File or path name too long - #[error("File or path name too long")] - FileOrPathNameTooLong = ENAMETOOLONG, - /// Too many symbolic links - #[error("Too many symbolic links")] - TooManySymbolicLinks = ELOOP, - /// Operation not supported on transport endpoint - #[error("Operation not supported on transport endpoint")] - OperationNotSupportedOnTransportEndpoint = EOPNOTSUPP, - /// Protocol family is not supported - #[error("Protocol family not supported")] - ProtocolFamilyNotSupported = EPFNOSUPPORT, - /// Connection reset by peer - #[error("Connection reset by peer")] - ConnectionResetByPeer = ECONNRESET, - /// No buffer space available - #[error("No buffer space available")] - NoBufferSpaceAvailable = ENOBUFS, - /// Address family not supported by protocol family - #[error("Address family not supported by protocol family")] - AddressFamilyNotSupportedByProtocolFamily = EAFNOSUPPORT, - /// Protocol wrong type for socket - #[error("Protocol wrong type for socket")] - ProtocolWrongTypeForSocket = EPROTOTYPE, - /// Socket operation on non-socket - #[error("Socket operation on non-socket")] - SocketOperationOnNonSocket = ENOTSOCK, - /// Protocol not available - #[error("Protocol not available")] - ProtocolNotAvailable = ENOPROTOOPT, - /// Can't send after socket shutdown - #[error("Can't send after socket shutdown")] - CantSendAfterSocketShutdown = ESHUTDOWN, - /// Connection refused - #[error("Connection refused")] - ConnectionRefused = ECONNREFUSED, - /// Address already in use - #[error("Address already in use")] - AddressAlreadyInUse = EADDRINUSE, - /// Connection aborted - #[error("Connection aborted")] - ConnectionAborted = ECONNABORTED, - /// Network is unreachable - #[error("Network is unreachable")] - NetworkIsUnreachable = ENETUNREACH, - /// Network interface is not configured - #[error("Network interface is not configured")] - NetworkInterfaceNotConfigured = ENETDOWN, - /// Connection timed out - #[error("Connection timed out")] - ConnectionTimedOut = ETIMEDOUT, - /// Host is down - #[error("Host is down")] - HostIsDown = EHOSTDOWN, - /// Host is unreachable - #[error("Host is unreachable")] - HostIsUnreachable = EHOSTUNREACH, - /// Connection already in progress - #[error("Connection already in progress")] - ConnectionAlreadyInProgress = EINPROGRESS, - /// Socket already connected - #[error("Socket already connected")] - SocketAlreadyConnected = EALREADY, - /// Destination address required - #[error("Destination address required")] - DestinationAddressRequired = EDESTADDRREQ, - /// Message too long - #[error("Message too long")] - MessageTooLong = EMSGSIZE, - /// Unknown protocol - #[error("Unknown protocol")] - UnknownProtocol = EPROTONOSUPPORT, - /// Socket type is not supported - #[error("Socket type not supported")] - SocketTypeNotSupported = ESOCKTNOSUPPORT, - /// Address not available - #[error("Address not available")] - AddressNotAvailable = EADDRNOTAVAIL, - /// Network dropped connection on reset - #[error("Network dropped connection on reset")] - NetworkDroppedConnectionOnReset = ENETRESET, - /// Socket is already connected - #[error("Socket is already connected")] - SocketIsAlreadyConnected = EISCONN, - /// Socket is not connected - #[error("Socket is not connected")] - SocketIsNotConnected = ENOTCONN, - /// Too many references: cannot splice - #[error("Too many references: cannot splice")] - TooManyReferences = ETOOMANYREFS, - /// The per-user limit on the new process would be exceeded by an attempted fork. - #[error("The per-user limit on new process would be exceeded by an attempted fork.")] - ProcessLimitExceeded = EPROCLIM, - /// The file quota system is confused because there are too many users. - #[error("The file quota system is confused because there are too many users.")] - TooManyUsers = EUSERS, - /// The user's disk quota was exceeded. - #[error("The user's disk quota was exceeded.")] - DiskQuotaExceeded = EDQUOT, - /// Stale NFS file handle - #[error("Stale NFS file handle")] - StaleNfsFileHandle = ESTALE, - /// Not supported - #[error("Not supported")] - NotSupported = ENOTSUP, - /// No medium (in tape drive) - #[error("No medium (in tape drive)")] - NoMedium = ENOMEDIUM, - /// No such host or network path - #[error("No such host or network path")] - NoShare = ENOSHARE, - /// Filename exists with different case - #[error("Filename exists with different case")] - CaseClash = ECASECLASH, - /// While decoding a multibyte character the function came along an invalid or an incomplete - /// sequence of bytes or the given wide character is invalid. - #[error("While decoding a multibyte character the function came along an invalid or an incomplete sequence of bytes or the given wide character is invalid.")] - IllegalSequence = EILSEQ, - /// Value too large for defined data type - #[error("Value too large for defined data type")] - Overflow = EOVERFLOW, -} diff --git a/net/src/lib.rs b/net/src/lib.rs index 42b3e8d9..33656f93 100644 --- a/net/src/lib.rs +++ b/net/src/lib.rs @@ -13,21 +13,23 @@ extern crate alloc; pub mod vlan; pub mod vxlan; +#[allow(clippy::unwrap_used)] #[cfg(test)] mod tests { use super::*; use alloc::vec::Vec; + use etherparse::{PacketBuilder, PacketHeaders}; pub fn gen_random_udp_packet() -> Vec { - use etherparse::PacketBuilder; let src_mac: [u8; 6] = rand::random(); let dst_mac: [u8; 6] = rand::random(); let src_ip: [u8; 4] = rand::random(); let dst_ip: [u8; 4] = rand::random(); let src_port: u16 = rand::random(); let dst_port: u16 = rand::random(); + let ttl = rand::random::(); let builder = PacketBuilder::ethernet2(src_mac, dst_mac) - .ipv4(src_ip, dst_ip, rand::random()) + .ipv4(src_ip, dst_ip, ttl) .udp(src_port, dst_port); let payload_length = (rand::random::() % 1200) as usize; let mut payload = Vec::with_capacity(payload_length + 50); @@ -50,7 +52,6 @@ mod tests { #[test] fn parse_udp_packet_bit_by_bit() { let packet = gen_random_udp_packet(); - let cursor = 0; - let _eth = etherparse::Ethernet2Header::from_slice(&packet[cursor..]).unwrap(); + etherparse::Ethernet2Header::from_slice(packet.as_slice()).unwrap(); } } diff --git a/scratch/src/main.rs b/scratch/src/main.rs index 56101352..ae8a6ac8 100644 --- a/scratch/src/main.rs +++ b/scratch/src/main.rs @@ -606,7 +606,7 @@ fn main() { fn eal_main() { tracing_subscriber::fmt() - .with_max_level(tracing::Level::WARN) + .with_max_level(tracing::Level::INFO) .with_target(false) .with_thread_ids(true) .with_line_number(true) @@ -621,7 +621,7 @@ fn eal_main() { "--huge-dir", "/mnt/huge/1G", "--allow", - "0000:85:00.0,dv_flow_en=1", + "0000:01:00.0,dv_flow_en=1", // "--trace=.*", // "--iova-mode=va", // "-l", @@ -669,6 +669,7 @@ fn eal_main() { "Device rx offload capabilities: {rx_offload:?}", rx_offload = dev.rx_offload_caps() ); + info!("Device capas: {capas:?}", capas = dev.capabilities()); let config = dev::DevConfig { num_rx_queues: 5,