wireshift-core 0.1.1

Core typed operations, buffers, and backend traits for wireshift
Documentation
//! Ring configuration types.

use std::time::Duration;

use crate::error::{Error, Result};

/// Controls backend selection.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub enum BackendPreference {
    /// Prefer `io_uring`, falling back automatically if unavailable.
    #[default]
    Auto,
    /// Require Linux `io_uring`.
    IoUring,
    /// Force the fallback backend.
    Fallback,
}

/// Configuration for pre-registered kernel buffer pools.
///
/// When enabled, the ring allocates `count` buffers of `size` bytes and
/// registers them with the kernel via `IORING_REGISTER_BUFFERS`. Subsequent
/// read and write operations using these buffers bypass per-I/O page pinning,
/// yielding 2-3× throughput on high-IOPS workloads.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegisteredBufferConfig {
    /// Number of buffers to pre-allocate and register.
    pub count: u32,
    /// Size of each buffer in bytes.
    pub size: u32,
}

/// Configuration for a [`crate::Ring`].
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RingConfig {
    /// Maximum in-flight operations.
    pub queue_depth: u32,
    /// Worker threads for the fallback backend.
    pub fallback_threads: usize,
    /// Completion batch size for drain strategies.
    pub completion_batch: usize,
    /// Backend selection policy.
    pub backend: BackendPreference,
    /// Optional default timeout for blocking waits.
    pub default_timeout: Option<Duration>,
    /// Enable `IORING_SETUP_SQPOLL`  -  the kernel polls the submission queue
    /// from a dedicated kernel thread, eliminating the submit syscall entirely.
    ///
    /// The value is the idle timeout in milliseconds: the kernel thread spins
    /// for this long after the last submission before sleeping. A typical value
    /// is `2000` (2 seconds).
    ///
    /// Requires Linux 5.11+ (unprivileged SQPOLL was added in 5.19).
    pub sq_poll_idle_ms: Option<u32>,
    /// Pre-register a pool of kernel-pinned buffers for zero-copy I/O.
    ///
    /// When set, the native Linux `io_uring` backend uses this count and size
    /// with [`IORING_REGISTER_BUFFERS`](https://man7.org/linux/man-pages/man2/io_uring_register.2.html).
    /// When unset, that backend still registers a default pool (32 buffers of
    /// 4096 bytes each) so fixed-buffer machinery is always available unless
    /// registration fails during setup.
    pub registered_buffers: Option<RegisteredBufferConfig>,
}

impl Default for RingConfig {
    fn default() -> Self {
        Self {
            queue_depth: 256,
            fallback_threads: 4,
            completion_batch: 64,
            backend: BackendPreference::Auto,
            default_timeout: Some(Duration::from_secs(30)),
            sq_poll_idle_ms: None,
            registered_buffers: None,
        }
    }
}

impl RingConfig {
    /// Returns a copy with the provided queue depth.
    #[must_use]
    pub fn with_queue_depth(mut self, queue_depth: u32) -> Self {
        self.queue_depth = queue_depth;
        self
    }

    /// Returns a copy with the provided backend policy.
    #[must_use]
    pub fn with_backend(mut self, backend: BackendPreference) -> Self {
        self.backend = backend;
        self
    }

    /// Returns a copy with the provided fallback thread count.
    #[must_use]
    pub fn with_fallback_threads(mut self, fallback_threads: usize) -> Self {
        self.fallback_threads = fallback_threads;
        self
    }

    /// Returns a copy with the provided completion batch size.
    #[must_use]
    pub fn with_completion_batch(mut self, completion_batch: usize) -> Self {
        self.completion_batch = completion_batch;
        self
    }

    /// Returns a copy with the provided default timeout.
    #[must_use]
    pub fn with_default_timeout(mut self, default_timeout: Option<Duration>) -> Self {
        self.default_timeout = default_timeout;
        self
    }

    /// Enables `IORING_SETUP_SQPOLL` with the given idle timeout in milliseconds.
    ///
    /// When active, the kernel polls the submission queue from a dedicated
    /// thread, eliminating the `io_uring_enter` syscall for submissions.
    /// The kernel thread sleeps after `idle_ms` milliseconds of inactivity.
    #[must_use]
    pub fn with_sq_poll(mut self, idle_ms: u32) -> Self {
        self.sq_poll_idle_ms = Some(idle_ms);
        self
    }

    /// Enables pre-registered kernel-pinned buffers.
    ///
    /// The ring allocates `count` buffers of `size` bytes and registers them
    /// with the kernel at creation time. I/O operations using these buffers
    /// bypass per-operation page pinning overhead.
    #[must_use]
    pub fn with_registered_buffers(mut self, count: u32, size: u32) -> Self {
        self.registered_buffers = Some(RegisteredBufferConfig { count, size });
        self
    }

    /// Validates the configuration.
    pub fn validate(&self) -> Result<()> {
        if self.queue_depth == 0 {
            return Err(Error::invalid_config(
                "queue_depth must be greater than zero",
            ));
        }
        if self.queue_depth > 4096 {
            return Err(Error::invalid_config(
                "queue_depth above 4096 is rejected to bound kernel and fallback memory use",
            ));
        }
        if self.fallback_threads == 0 {
            return Err(Error::invalid_config(
                "fallback_threads must be greater than zero",
            ));
        }
        if self.completion_batch == 0 {
            return Err(Error::invalid_config(
                "completion_batch must be greater than zero",
            ));
        }
        if let Some(ref rb) = self.registered_buffers {
            if rb.count == 0 {
                return Err(Error::invalid_config(
                    "registered buffer count must be greater than zero",
                ));
            }
            if rb.size == 0 {
                return Err(Error::invalid_config(
                    "registered buffer size must be greater than zero",
                ));
            }
        }
        Ok(())
    }
}