wireshift-uring 0.1.1

Native Linux io_uring backend for wireshift
Documentation
//! Unsafe kernel-facing helpers for the native `io_uring` backend.

use io_uring::{squeue, IoUring};
use wireshift_core::{Error, Result};

/// Pushes a prepared SQE into the ring submission queue.
pub(crate) fn push_entry(ring: &mut IoUring, entry: &squeue::Entry) -> Result<()> {
    let mut submission = ring.submission();
    // SAFETY: the entry points only at memory owned by the descriptor stored in
    // the ring backend's in-flight map. That descriptor remains alive until the
    // corresponding completion is observed and routed.
    unsafe {
        submission.push(entry).map_err(|_| {
            Error::submission(
                "io_uring submission queue is full",
                "drain completions or increase queue_depth in RingConfig",
            )
        })?;
    }
    submission.sync();
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use io_uring::{opcode, types};

    #[test]
    fn test_push_entry_queue_full() {
        // Test that push_entry properly errors when the queue is full
        let mut ring = IoUring::new(2).unwrap();
        let mut buf = [0u8; 1];
        let entry = opcode::Read::new(types::Fd(0), buf.as_mut_ptr(), 1).build();

        // Push 2 entries, filling the queue
        push_entry(&mut ring, &entry).unwrap();
        push_entry(&mut ring, &entry).unwrap();

        // The 3rd entry should fail
        let result = push_entry(&mut ring, &entry);
        assert!(result.is_err());
        assert_eq!(result.unwrap_err().to_string(), "io_uring submission queue is full. Fix: drain completions or increase queue_depth in RingConfig");
    }

    #[test]
    fn test_push_entry_invariant_drop() {
        // This test documents the safety invariant. If the invariant were broken
        // (i.e. if the buffer is dropped before the completion), memory corruption would occur.
        // We verify that executing a valid push retains data until completed.
        let mut ring = IoUring::new(2).unwrap();

        // Provide the valid memory outliving the entry.
        let entry = opcode::Nop::new().build().user_data(42);
        push_entry(&mut ring, &entry).unwrap();

        ring.submit_and_wait(1).unwrap();
        let cqes: Vec<_> = ring.completion().collect();
        assert_eq!(cqes.len(), 1);
        assert_eq!(cqes[0].user_data(), 42);
    }
}