wireshift-uring 0.1.1

Native Linux io_uring backend for wireshift
Documentation
//! Kernel capability probing for the native `io_uring` backend.

use std::os::fd::AsRawFd;
use std::sync::OnceLock;
use io_uring::IoUring;

/// Supported `io_uring` opcodes on the current kernel.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[allow(clippy::struct_excessive_bools)]
pub struct UringCapabilities {
    /// Native `IORING_OP_READ`.
    pub read: bool,
    /// Native `IORING_OP_WRITE`.
    pub write: bool,
    /// Native `IORING_OP_FSYNC`.
    pub fsync: bool,
    /// Native `IORING_OP_POLL_ADD`.
    pub poll_add: bool,
    /// Native `IORING_OP_ACCEPT`.
    pub accept: bool,
    /// Native `IORING_OP_OPENAT`.
    pub openat: bool,
    /// Native `IORING_OP_CLOSE`.
    pub close: bool,
    /// Native `IORING_OP_PROVIDE_BUFFERS`.
    pub provide_buffers: bool,
    /// Native `IORING_OP_SEND_ZC`.
    pub send_zc: bool,
}

static CAPABILITIES: OnceLock<UringCapabilities> = OnceLock::new();

const PROBE_OPS: usize = 64;
const IO_URING_OP_SUPPORTED: u16 = 1;
const IORING_REGISTER_PROBE: libc::c_uint = 8;
const IORING_OP_POLL_ADD: u32 = 6;
const IORING_OP_ACCEPT: u32 = 13;
const IORING_OP_OPENAT: u32 = 18;
const IORING_OP_CLOSE: u32 = 19;
const IORING_OP_READ: u32 = 22;
const IORING_OP_WRITE: u32 = 23;
const IORING_OP_PROVIDE_BUFFERS: u32 = 31;
const IORING_OP_SEND_ZC: u32 = 47;
const IORING_OP_FSYNC: u32 = 3;

/// Probes kernel `io_uring` support and returns the opcodes relevant to
/// `wireshift`.
#[must_use]
pub fn probe_capabilities() -> UringCapabilities {
    *CAPABILITIES.get_or_init(|| {
        let Ok(ring) = IoUring::new(8) else {
            return UringCapabilities::default();
        };

        let mut bytes = vec![0u8; 1024];

        let rc = unsafe {
            libc::syscall(
                libc::SYS_io_uring_register,
                ring.as_raw_fd(),
                IORING_REGISTER_PROBE,
                bytes.as_mut_ptr() as *mut libc::c_void,
                64 as libc::c_uint,
            )
        };
        if rc < 0 {
            return UringCapabilities::default();
        }

        let last = usize::from(bytes[0]);
        let ops_len = usize::from(bytes[1]).min(PROBE_OPS);
        let supported = |opcode: u32| -> bool {
            let opcode = opcode as usize;
            if opcode >= ops_len || opcode > last {
                return false;
            }
            let offset = 8 + opcode * 8 + 2;
            let flags = u16::from_ne_bytes([bytes[offset], bytes[offset + 1]]);
            (flags & IO_URING_OP_SUPPORTED) != 0
        };

        UringCapabilities {
            read: supported(IORING_OP_READ),
            write: supported(IORING_OP_WRITE),
            fsync: supported(IORING_OP_FSYNC),
            poll_add: supported(IORING_OP_POLL_ADD),
            accept: supported(IORING_OP_ACCEPT),
            openat: supported(IORING_OP_OPENAT),
            close: supported(IORING_OP_CLOSE),
            provide_buffers: supported(IORING_OP_PROVIDE_BUFFERS),
            send_zc: supported(IORING_OP_SEND_ZC),
        }
    })
}