#[derive(Clone, Copy, Debug)]
pub struct Config {
pub ring_entries: u32,
pub cq_entries: u32,
pub(super) fixed_file_slots: u32,
pub(super) accept_slots: u32,
pub(super) provided_buf_entries: u16,
pub(super) provided_buf_len: usize,
pub(super) defer_taskrun: bool,
}
impl Config {
const IORING_MAX_ENTRIES: u32 = 32768;
fn sized_sq(max_conn: u32, outbound_reserve: u32) -> u32 {
max_conn
.saturating_add(outbound_reserve)
.next_power_of_two()
.clamp(64, Self::IORING_MAX_ENTRIES)
}
}
impl crate::backend::DriverConfig for Config {
fn for_profile<P: crate::backend::profile::Profile>() -> Self {
Self {
ring_entries: P::RING_ENTRIES,
cq_entries: P::CQ_ENTRIES,
fixed_file_slots: P::FIXED_FILE_SLOTS,
accept_slots: P::FIXED_FILE_SLOTS.saturating_sub(P::OUTBOUND_RESERVE),
provided_buf_entries: P::PROVIDED_BUF_ENTRIES,
provided_buf_len: P::PROVIDED_BUF_LEN,
defer_taskrun: P::DEFER_TASKRUN,
}
}
fn for_tcp_profile<P: crate::backend::profile::Profile>(max_conn: usize) -> Self {
let max_conn = max_conn as u32;
let accept_slots = max_conn.min(P::FIXED_FILE_SLOTS.saturating_sub(P::OUTBOUND_RESERVE));
Self {
ring_entries: Self::sized_sq(accept_slots, P::OUTBOUND_RESERVE).min(P::RING_ENTRIES),
cq_entries: P::CQ_ENTRIES,
fixed_file_slots: accept_slots.saturating_add(P::OUTBOUND_RESERVE),
accept_slots,
provided_buf_entries: crate::backend::provided_entries(
accept_slots,
P::PROVIDED_BUF_LEN,
Self::IORING_MAX_ENTRIES,
),
provided_buf_len: P::PROVIDED_BUF_LEN,
defer_taskrun: P::DEFER_TASKRUN,
}
}
fn for_quic_udp(provided_buf_entries: u32, provided_buf_len: u32) -> Self {
Self {
ring_entries: 256,
cq_entries: 1024,
fixed_file_slots: 16,
accept_slots: 0,
provided_buf_entries: provided_buf_entries.min(u16::MAX as u32) as u16,
provided_buf_len: provided_buf_len as usize,
defer_taskrun: false,
}
}
fn with_provided(mut self, len: usize, entries: u16) -> Self {
crate::backend::apply_provided(
len,
entries,
&mut self.provided_buf_len,
&mut self.provided_buf_entries,
);
self
}
}
#[cfg(test)]
mod tests {
use super::Config;
use crate::backend::DriverConfig;
use crate::backend::profile::{Production, Profile, Throughput};
const MIB: usize = 1024 * 1024;
fn pool_bytes(c: &Config) -> usize {
c.provided_buf_entries as usize * c.provided_buf_len
}
#[test]
fn production_pool_is_hwm_sized() {
let prod = Config::for_tcp_profile::<Production>(1024);
assert_eq!(prod.provided_buf_len, 8192);
assert_eq!(prod.provided_buf_entries, 1024);
assert_eq!(pool_bytes(&prod), 8 * MIB);
}
#[test]
fn production_pool_flat_across_conns() {
let want = 1024u16;
for conns in [512usize, 1024, 4096, 8192, 16384] {
let c = Config::for_tcp_profile::<Production>(conns);
assert_eq!(
c.provided_buf_entries, want,
"pool must be flat in conn count (conns={conns})"
);
}
}
#[test]
fn production_ring_is_capped() {
assert_eq!(Production::RING_ENTRIES, 8192);
let c = Config::for_tcp_profile::<Production>(16384);
assert!(
c.ring_entries <= Production::RING_ENTRIES,
"ring {} must not exceed RING_ENTRIES {}",
c.ring_entries,
Production::RING_ENTRIES
);
}
#[test]
fn production_fixed_file_tracks_requested_conns() {
let c = Config::for_tcp_profile::<Production>(16384);
assert_eq!(c.fixed_file_slots, 16384 + Production::OUTBOUND_RESERVE);
assert_eq!(c.accept_slots, 16384);
assert!(c.fixed_file_slots < Production::FIXED_FILE_SLOTS);
let small = Config::for_tcp_profile::<Production>(1024);
assert_eq!(small.fixed_file_slots, 1024 + Production::OUTBOUND_RESERVE);
let big = Config::for_tcp_profile::<Production>(100_000);
assert_eq!(Production::FIXED_FILE_SLOTS, 65535);
assert_eq!(big.fixed_file_slots, 65535);
assert_eq!(
big.accept_slots,
65535 - Production::OUTBOUND_RESERVE,
"large deployments are not capped below the ceiling"
);
}
#[test]
fn throughput_keeps_large_buffer() {
let tp = Config::for_tcp_profile::<Throughput>(1024);
assert_eq!(tp.provided_buf_len, 64 * 1024);
assert_eq!(tp.provided_buf_entries, 1024);
}
#[test]
fn with_provided_override() {
let base = Config::for_tcp_profile::<Production>(1024);
let tuned = base.with_provided(8192, 2048);
assert_eq!(tuned.provided_buf_len, 8192);
assert_eq!(tuned.provided_buf_entries, 2048);
assert_eq!(pool_bytes(&tuned), 16 * MIB);
let len_only = base.with_provided(4096, 0);
assert_eq!(len_only.provided_buf_len, 4096);
assert_eq!(len_only.provided_buf_entries, base.provided_buf_entries);
let entries_only = base.with_provided(0, 512);
assert_eq!(entries_only.provided_buf_len, base.provided_buf_len);
assert_eq!(entries_only.provided_buf_entries, 512);
}
}