use std::fmt;
#[derive(Debug)]
pub enum LinuxError {
Xsk(XskError),
Umem(UmemError),
Ring(RingError),
Descriptor(DescriptorError),
Syscall {
syscall: &'static str,
errno: i32,
},
InsufficientResources(String),
Unsupported(String),
}
impl fmt::Display for LinuxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LinuxError::Xsk(e) => write!(f, "AF_XDP error: {}", e),
LinuxError::Umem(e) => write!(f, "UMEM error: {}", e),
LinuxError::Ring(e) => write!(f, "Ring error: {}", e),
LinuxError::Descriptor(e) => write!(f, "Descriptor error: {}", e),
LinuxError::Syscall { syscall, errno } => {
write!(f, "Syscall '{}' failed with errno {}", syscall, errno)
}
LinuxError::InsufficientResources(msg) => {
write!(f, "Insufficient resources: {}", msg)
}
LinuxError::Unsupported(msg) => write!(f, "Unsupported: {}", msg),
}
}
}
impl std::error::Error for LinuxError {}
impl From<XskError> for LinuxError {
fn from(e: XskError) -> Self {
LinuxError::Xsk(e)
}
}
impl From<UmemError> for LinuxError {
fn from(e: UmemError) -> Self {
LinuxError::Umem(e)
}
}
impl From<RingError> for LinuxError {
fn from(e: RingError) -> Self {
LinuxError::Ring(e)
}
}
impl From<DescriptorError> for LinuxError {
fn from(e: DescriptorError) -> Self {
LinuxError::Descriptor(e)
}
}
#[derive(Debug)]
pub enum XskError {
SocketCreate(String),
SocketOption(String),
BindFailed(String),
QueueNotFound(u32),
AlreadyBound,
NotBound,
CloseFailed(String),
NotifyFailed(String),
InvalidDescriptor(u64),
InvalidState(String),
}
impl fmt::Display for XskError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
XskError::SocketCreate(msg) => write!(f, "Socket creation failed: {}", msg),
XskError::SocketOption(msg) => write!(f, "Socket option failed: {}", msg),
XskError::BindFailed(msg) => write!(f, "Bind failed: {}", msg),
XskError::QueueNotFound(q) => write!(f, "Queue not found: {}", q),
XskError::AlreadyBound => write!(f, "XSK already bound"),
XskError::NotBound => write!(f, "XSK not bound"),
XskError::CloseFailed(msg) => write!(f, "Close failed: {}", msg),
XskError::NotifyFailed(msg) => write!(f, "XSK wakeup notify failed: {}", msg),
XskError::InvalidDescriptor(d) => write!(f, "Invalid descriptor: {}", d),
XskError::InvalidState(msg) => write!(f, "Invalid XSK state: {}", msg),
}
}
}
impl std::error::Error for XskError {}
#[derive(Debug)]
pub enum UmemError {
MmapFailed(String),
LockFailed(String),
NotAligned {
actual: usize,
expected: usize,
},
InsufficientSize {
actual: usize,
required: usize,
},
HugePageNotAvailable,
AlreadyCreated,
NotCreated,
MunmapFailed(String),
}
impl fmt::Display for UmemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UmemError::MmapFailed(msg) => write!(f, "mmap failed: {}", msg),
UmemError::LockFailed(msg) => write!(f, "mlock failed: {}", msg),
UmemError::NotAligned { actual, expected } => {
write!(f, "Memory not aligned: actual {}, expected {}", actual, expected)
}
UmemError::InsufficientSize { actual, required } => {
write!(
f,
"Insufficient memory: actual {} bytes, required {} bytes",
actual, required
)
}
UmemError::HugePageNotAvailable => write!(f, "HugePage not available"),
UmemError::AlreadyCreated => write!(f, "UMEM already created"),
UmemError::NotCreated => write!(f, "UMEM not created"),
UmemError::MunmapFailed(msg) => write!(f, "munmap failed: {}", msg),
}
}
}
impl std::error::Error for UmemError {}
#[derive(Debug)]
pub enum RingError {
RingFull,
RingEmpty,
InvalidDescriptor(u64),
IndexOutOfBounds {
index: u32,
capacity: u32,
},
ProducerConsumerConflict,
BatchSizeExceeded {
requested: u32,
maximum: u32,
},
InvalidOffsets(String),
}
impl fmt::Display for RingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RingError::RingFull => write!(f, "Ring is full"),
RingError::RingEmpty => write!(f, "Ring is empty"),
RingError::InvalidDescriptor(d) => write!(f, "Invalid descriptor: {}", d),
RingError::IndexOutOfBounds { index, capacity } => {
write!(
f,
"Ring index {} out of bounds (capacity {})",
index, capacity
)
}
RingError::ProducerConsumerConflict => {
write!(f, "Producer-consumer conflict")
}
RingError::BatchSizeExceeded { requested, maximum } => {
write!(
f,
"Batch size {} exceeded maximum {}",
requested, maximum
)
}
RingError::InvalidOffsets(msg) => write!(f, "Invalid ring offsets: {}", msg),
}
}
}
impl std::error::Error for RingError {}
#[derive(Debug)]
pub enum DescriptorError {
ZeroDescriptor,
OutOfRange {
descriptor: u64,
max_valid: u64,
},
AlreadyFreed(u64),
AlreadyInUse(u64),
AlreadyAllocated(u64),
InvalidCapacity(u64),
OwnershipMismatch {
expected: u32,
actual: u32,
},
GenerationMismatch {
expected: u64,
actual: u64,
},
InvalidFrameShift(u32),
TransactionFailed(String),
}
impl fmt::Display for DescriptorError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DescriptorError::ZeroDescriptor => write!(f, "Zero descriptor (invalid)"),
DescriptorError::OutOfRange {
descriptor,
max_valid,
} => write!(
f,
"Descriptor {} out of range (max valid: {})",
descriptor, max_valid
),
DescriptorError::AlreadyFreed(d) => write!(f, "Descriptor {} already freed", d),
DescriptorError::AlreadyInUse(d) => write!(f, "Descriptor {} already in use", d),
DescriptorError::AlreadyAllocated(frame) => {
write!(f, "Frame {} already allocated", frame)
}
DescriptorError::InvalidCapacity(c) => write!(
f,
"Descriptor capacity {c} exceeds 20-bit frame index domain (max 2^20 frames)"
),
DescriptorError::OwnershipMismatch { expected, actual } => {
write!(
f,
"Ownership mismatch: expected {}, actual {}",
expected, actual
)
}
DescriptorError::InvalidFrameShift(shift) => write!(
f,
"Invalid frame_shift {shift}: must be in 11..=15 (frame size 2048..=32768 bytes)"
),
DescriptorError::GenerationMismatch { expected, actual } => {
write!(
f,
"Generation mismatch: expected {}, actual {}",
expected, actual
)
}
DescriptorError::TransactionFailed(msg) => {
write!(f, "Transaction failed: {}", msg)
}
}
}
}
impl std::error::Error for DescriptorError {}
pub type Result<T> = std::result::Result<T, LinuxError>;
pub use zenith_foundation::error::ErrorSeverity;
impl LinuxError {
pub fn severity(&self) -> ErrorSeverity {
match self {
LinuxError::Syscall { .. } => ErrorSeverity::Error,
LinuxError::InsufficientResources(_) => ErrorSeverity::Critical,
LinuxError::Unsupported(_) => ErrorSeverity::Critical,
LinuxError::Xsk(e) => e.severity(),
LinuxError::Umem(e) => e.severity(),
LinuxError::Ring(e) => e.severity(),
LinuxError::Descriptor(e) => e.severity(),
}
}
pub fn is_syscall_error(&self) -> bool {
matches!(self, LinuxError::Syscall { .. })
}
pub fn is_config_error(&self) -> bool {
matches!(
self,
LinuxError::Xsk(XskError::SocketOption(_))
| LinuxError::Umem(UmemError::NotAligned { .. })
| LinuxError::Umem(UmemError::InsufficientSize { .. })
| LinuxError::Ring(RingError::BatchSizeExceeded { .. })
| LinuxError::Descriptor(DescriptorError::OutOfRange { .. })
)
}
pub fn is_resource_error(&self) -> bool {
matches!(
self,
LinuxError::InsufficientResources(_)
| LinuxError::Umem(UmemError::MmapFailed(_))
| LinuxError::Umem(UmemError::LockFailed(_))
| LinuxError::Ring(RingError::RingFull)
| LinuxError::Ring(RingError::RingEmpty)
)
}
}
impl XskError {
pub fn severity(&self) -> ErrorSeverity {
match self {
XskError::SocketCreate(_) => ErrorSeverity::Critical,
XskError::SocketOption(_) => ErrorSeverity::Error,
XskError::BindFailed(_) => ErrorSeverity::Error,
XskError::QueueNotFound(_) => ErrorSeverity::Warning,
XskError::AlreadyBound => ErrorSeverity::Warning,
XskError::NotBound => ErrorSeverity::Warning,
XskError::CloseFailed(_) => ErrorSeverity::Warning,
XskError::NotifyFailed(_) => ErrorSeverity::Error,
XskError::InvalidDescriptor(_) => ErrorSeverity::Error,
XskError::InvalidState(_) => ErrorSeverity::Warning,
}
}
}
impl UmemError {
pub fn severity(&self) -> ErrorSeverity {
match self {
UmemError::MmapFailed(_) => ErrorSeverity::Critical,
UmemError::LockFailed(_) => ErrorSeverity::Error,
UmemError::NotAligned { .. } => ErrorSeverity::Error,
UmemError::InsufficientSize { .. } => ErrorSeverity::Error,
UmemError::HugePageNotAvailable => ErrorSeverity::Warning,
UmemError::AlreadyCreated => ErrorSeverity::Warning,
UmemError::NotCreated => ErrorSeverity::Warning,
UmemError::MunmapFailed(_) => ErrorSeverity::Warning,
}
}
}
impl RingError {
pub fn severity(&self) -> ErrorSeverity {
match self {
RingError::RingFull => ErrorSeverity::Warning,
RingError::RingEmpty => ErrorSeverity::Info,
RingError::InvalidDescriptor(_) => ErrorSeverity::Error,
RingError::IndexOutOfBounds { .. } => ErrorSeverity::Error,
RingError::ProducerConsumerConflict => ErrorSeverity::Critical,
RingError::BatchSizeExceeded { .. } => ErrorSeverity::Warning,
RingError::InvalidOffsets(_) => ErrorSeverity::Error,
}
}
}
impl DescriptorError {
pub fn severity(&self) -> ErrorSeverity {
match self {
DescriptorError::ZeroDescriptor => ErrorSeverity::Error,
DescriptorError::OutOfRange { .. } => ErrorSeverity::Error,
DescriptorError::AlreadyFreed(_) => ErrorSeverity::Warning,
DescriptorError::AlreadyInUse(_) => ErrorSeverity::Warning,
DescriptorError::AlreadyAllocated(_) => ErrorSeverity::Warning,
DescriptorError::InvalidCapacity(_) => ErrorSeverity::Error,
DescriptorError::OwnershipMismatch { .. } => ErrorSeverity::Error,
DescriptorError::InvalidFrameShift(_) => ErrorSeverity::Error,
DescriptorError::GenerationMismatch { .. } => ErrorSeverity::Warning,
DescriptorError::TransactionFailed(_) => ErrorSeverity::Error,
}
}
}
impl From<LinuxError> for std::io::Error {
fn from(err: LinuxError) -> Self {
match err {
LinuxError::Syscall { errno, .. } => std::io::Error::from_raw_os_error(errno),
LinuxError::Umem(UmemError::MmapFailed(msg)) => {
std::io::Error::new(std::io::ErrorKind::OutOfMemory, msg)
}
LinuxError::Umem(UmemError::LockFailed(msg)) => {
std::io::Error::new(std::io::ErrorKind::PermissionDenied, msg)
}
LinuxError::InsufficientResources(msg) => {
std::io::Error::new(std::io::ErrorKind::OutOfMemory, msg)
}
other => std::io::Error::other(other.to_string()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_linux_error_xsk_display() {
let err = LinuxError::Xsk(XskError::SocketCreate("test error".to_string()));
let display = format!("{}", err);
assert!(display.contains("AF_XDP error"));
assert!(display.contains("Socket creation failed"));
assert!(display.contains("test error"));
}
#[test]
fn test_linux_error_umem_display() {
let err = LinuxError::Umem(UmemError::MmapFailed("mmap test".to_string()));
let display = format!("{}", err);
assert!(display.contains("UMEM error"));
assert!(display.contains("mmap failed"));
assert!(display.contains("mmap test"));
}
#[test]
fn test_linux_error_ring_display() {
let err = LinuxError::Ring(RingError::RingFull);
let display = format!("{}", err);
assert!(display.contains("Ring error"));
assert!(display.contains("Ring is full"));
}
#[test]
fn test_linux_error_descriptor_display() {
let err = LinuxError::Descriptor(DescriptorError::ZeroDescriptor);
let display = format!("{}", err);
assert!(display.contains("Descriptor error"));
assert!(display.contains("Zero descriptor"));
}
#[test]
fn test_linux_error_syscall_display() {
let err = LinuxError::Syscall {
syscall: "socket",
errno: 13,
};
let display = format!("{}", err);
assert!(display.contains("Syscall 'socket' failed"));
assert!(display.contains("errno 13"));
}
#[test]
fn test_linux_error_insufficient_resources_display() {
let err = LinuxError::InsufficientResources("out of memory".to_string());
let display = format!("{}", err);
assert!(display.contains("Insufficient resources"));
assert!(display.contains("out of memory"));
}
#[test]
fn test_linux_error_unsupported_display() {
let err = LinuxError::Unsupported("feature not available".to_string());
let display = format!("{}", err);
assert!(display.contains("Unsupported"));
assert!(display.contains("feature not available"));
}
#[test]
fn test_xsk_error_variants_display() {
let cases = vec![
(
XskError::SocketCreate("a".to_string()),
"Socket creation failed",
),
(
XskError::SocketOption("b".to_string()),
"Socket option failed",
),
(XskError::BindFailed("c".to_string()), "Bind failed"),
(XskError::QueueNotFound(5), "Queue not found: 5"),
(XskError::AlreadyBound, "XSK already bound"),
(XskError::NotBound, "XSK not bound"),
(XskError::CloseFailed("d".to_string()), "Close failed"),
(
XskError::NotifyFailed("e".to_string()),
"XSK wakeup notify failed",
),
(XskError::InvalidDescriptor(42), "Invalid descriptor: 42"),
];
for (err, expected) in cases {
let display = format!("{}", err);
assert!(display.contains(expected), "Expected '{}' in '{}'", expected, display);
}
}
#[test]
fn test_umem_error_variants_display() {
let cases = vec![
(UmemError::MmapFailed("a".to_string()), "mmap failed"),
(UmemError::LockFailed("b".to_string()), "mlock failed"),
(
UmemError::NotAligned {
actual: 100,
expected: 4096,
},
"Memory not aligned",
),
(
UmemError::InsufficientSize {
actual: 100,
required: 200,
},
"Insufficient memory",
),
(UmemError::HugePageNotAvailable, "HugePage not available"),
(UmemError::AlreadyCreated, "UMEM already created"),
(UmemError::NotCreated, "UMEM not created"),
(UmemError::MunmapFailed("c".to_string()), "munmap failed"),
];
for (err, expected) in cases {
let display = format!("{}", err);
assert!(display.contains(expected), "Expected '{}' in '{}'", expected, display);
}
}
#[test]
fn test_ring_error_variants_display() {
let cases = vec![
(RingError::RingFull, "Ring is full"),
(RingError::RingEmpty, "Ring is empty"),
(RingError::InvalidDescriptor(123), "Invalid descriptor: 123"),
(
RingError::IndexOutOfBounds {
index: 10,
capacity: 5,
},
"Ring index 10 out of bounds",
),
(RingError::ProducerConsumerConflict, "Producer-consumer conflict"),
(
RingError::BatchSizeExceeded {
requested: 100,
maximum: 50,
},
"Batch size 100 exceeded maximum 50",
),
];
for (err, expected) in cases {
let display = format!("{}", err);
assert!(display.contains(expected), "Expected '{}' in '{}'", expected, display);
}
}
#[test]
fn test_descriptor_error_variants_display() {
let cases = vec![
(DescriptorError::ZeroDescriptor, "Zero descriptor"),
(
DescriptorError::OutOfRange {
descriptor: 100,
max_valid: 50,
},
"Descriptor 100 out of range",
),
(DescriptorError::AlreadyFreed(42), "Descriptor 42 already freed"),
(DescriptorError::AlreadyInUse(99), "Descriptor 99 already in use"),
(
DescriptorError::AlreadyAllocated(7),
"Frame 7 already allocated",
),
(
DescriptorError::InvalidCapacity(1 << 21),
"exceeds 20-bit frame index domain",
),
(
DescriptorError::OwnershipMismatch {
expected: 1,
actual: 2,
},
"Ownership mismatch",
),
(
DescriptorError::GenerationMismatch {
expected: 3,
actual: 5,
},
"Generation mismatch",
),
(
DescriptorError::TransactionFailed("tx fail".to_string()),
"Transaction failed",
),
];
for (err, expected) in cases {
let display = format!("{}", err);
assert!(display.contains(expected), "Expected '{}' in '{}'", expected, display);
}
}
#[test]
fn test_error_from_conversions() {
let xsk_err = XskError::AlreadyBound;
let linux_err: LinuxError = xsk_err.into();
assert!(matches!(linux_err, LinuxError::Xsk(XskError::AlreadyBound)));
let umem_err = UmemError::NotCreated;
let linux_err: LinuxError = umem_err.into();
assert!(matches!(linux_err, LinuxError::Umem(UmemError::NotCreated)));
let ring_err = RingError::RingFull;
let linux_err: LinuxError = ring_err.into();
assert!(matches!(linux_err, LinuxError::Ring(RingError::RingFull)));
let desc_err = DescriptorError::ZeroDescriptor;
let linux_err: LinuxError = desc_err.into();
assert!(matches!(
linux_err,
LinuxError::Descriptor(DescriptorError::ZeroDescriptor)
));
}
#[test]
fn test_error_severity() {
assert_eq!(
LinuxError::Syscall {
syscall: "test",
errno: 1
}
.severity(),
ErrorSeverity::Error
);
assert_eq!(
LinuxError::InsufficientResources("x".to_string()).severity(),
ErrorSeverity::Critical
);
assert_eq!(
LinuxError::Unsupported("x".to_string()).severity(),
ErrorSeverity::Critical
);
assert_eq!(
LinuxError::Xsk(XskError::SocketCreate("x".to_string())).severity(),
ErrorSeverity::Critical
);
assert_eq!(
LinuxError::Xsk(XskError::QueueNotFound(0)).severity(),
ErrorSeverity::Warning
);
assert_eq!(
LinuxError::Umem(UmemError::MmapFailed("x".to_string())).severity(),
ErrorSeverity::Critical
);
assert_eq!(
LinuxError::Umem(UmemError::HugePageNotAvailable).severity(),
ErrorSeverity::Warning
);
assert_eq!(
LinuxError::Ring(RingError::RingEmpty).severity(),
ErrorSeverity::Info
);
assert_eq!(
LinuxError::Ring(RingError::ProducerConsumerConflict).severity(),
ErrorSeverity::Critical
);
assert_eq!(
LinuxError::Descriptor(DescriptorError::ZeroDescriptor).severity(),
ErrorSeverity::Error
);
assert_eq!(
LinuxError::Descriptor(DescriptorError::AlreadyFreed(0)).severity(),
ErrorSeverity::Warning
);
}
#[test]
fn test_error_classification() {
let syscall_err = LinuxError::Syscall {
syscall: "socket",
errno: 1,
};
assert!(syscall_err.is_syscall_error());
assert!(!syscall_err.is_config_error());
assert!(!syscall_err.is_resource_error());
let config_err = LinuxError::Umem(UmemError::NotAligned {
actual: 100,
expected: 4096,
});
assert!(!config_err.is_syscall_error());
assert!(config_err.is_config_error());
assert!(!config_err.is_resource_error());
let resource_err = LinuxError::InsufficientResources("oom".to_string());
assert!(!resource_err.is_syscall_error());
assert!(!resource_err.is_config_error());
assert!(resource_err.is_resource_error());
let ring_full = LinuxError::Ring(RingError::RingFull);
assert!(ring_full.is_resource_error());
}
#[test]
fn test_linux_error_to_io_error() {
let syscall_err = LinuxError::Syscall {
syscall: "test",
errno: 12, };
let io_err: std::io::Error = syscall_err.into();
assert_eq!(io_err.raw_os_error(), Some(12));
let mmap_err = LinuxError::Umem(UmemError::MmapFailed("failed".to_string()));
let io_err: std::io::Error = mmap_err.into();
assert_eq!(io_err.kind(), std::io::ErrorKind::OutOfMemory);
let lock_err = LinuxError::Umem(UmemError::LockFailed("denied".to_string()));
let io_err: std::io::Error = lock_err.into();
assert_eq!(io_err.kind(), std::io::ErrorKind::PermissionDenied);
let oom_err = LinuxError::InsufficientResources("oom".to_string());
let io_err: std::io::Error = oom_err.into();
assert_eq!(io_err.kind(), std::io::ErrorKind::OutOfMemory);
let other_err = LinuxError::Xsk(XskError::AlreadyBound);
let io_err: std::io::Error = other_err.into();
assert_eq!(io_err.kind(), std::io::ErrorKind::Other);
}
#[test]
fn test_error_trait_implementation() {
let err: Box<dyn std::error::Error> =
Box::new(LinuxError::Syscall {
syscall: "test",
errno: 1,
});
assert!(err.source().is_none());
let err: Box<dyn std::error::Error> = Box::new(XskError::AlreadyBound);
assert!(err.source().is_none());
let err: Box<dyn std::error::Error> = Box::new(UmemError::NotCreated);
assert!(err.source().is_none());
let err: Box<dyn std::error::Error> = Box::new(RingError::RingEmpty);
assert!(err.source().is_none());
let err: Box<dyn std::error::Error> = Box::new(DescriptorError::ZeroDescriptor);
assert!(err.source().is_none());
}
#[test]
fn test_result_type_alias() {
let ok: Result<i32> = Ok(42);
assert!(ok.is_ok());
let err: Result<i32> = Err(LinuxError::Unsupported("test".to_string()));
assert!(err.is_err());
}
#[test]
fn test_error_debug_format() {
let err = LinuxError::Syscall {
syscall: "mmap",
errno: 12,
};
let debug = format!("{:?}", err);
assert!(debug.contains("Syscall"));
assert!(debug.contains("mmap"));
assert!(debug.contains("12"));
}
}