use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
pub const NET_SENDQ_BACKOFF_HIGH_PCT: u8 = 60; pub const NET_SENDQ_BACKOFF_SLEEP_MS: u64 = 2; pub const NET_SENDQ_PROBE_STRIDE: u64 = 32; pub const NET_TCP_DRAIN_POLL_MS: u64 = 10;
pub const NET_BACKOFF_SMALL_BYPASS_BYTES: usize = 1024;
pub const NET_BACKOFF_SMALL_PROBE_MS: u64 = 1;
pub const NET_BACKOFF_LARGE_PROBE_MS: u64 = 1;
pub const NET_SMALL_STRIDE_BASE_DIV: usize = 16;
pub const NET_SMALL_STRIDE_MIN_BYTES: usize = 64 * 1024;
pub const NET_SMALL_STRIDE_MAX_BYTES: usize = 256 * 1024;
pub const NET_LARGE_STRIDE_MIN_BYTES: usize = 16 * 1024;
#[inline]
pub fn small_probe_stride(sndbuf_cap: usize) -> usize {
let mut base = sndbuf_cap / NET_SMALL_STRIDE_BASE_DIV;
if base > NET_SMALL_STRIDE_MAX_BYTES {
base = NET_SMALL_STRIDE_MAX_BYTES;
}
NET_SMALL_STRIDE_MIN_BYTES.max(base)
}
#[inline]
pub fn large_probe_stride(sndbuf_cap: usize, avg: usize) -> usize {
let cap_stride = sndbuf_cap / NET_SMALL_STRIDE_BASE_DIV;
let avg_stride = avg.saturating_mul(NET_SMALL_STRIDE_BASE_DIV);
cap_stride.max(avg_stride).max(NET_LARGE_STRIDE_MIN_BYTES)
}
#[inline]
pub fn is_probe_tick_due(sent_cnt: u64) -> bool {
if NET_SENDQ_PROBE_STRIDE.is_power_of_two() {
let mask = NET_SENDQ_PROBE_STRIDE - 1;
(sent_cnt & mask) == 0
} else if NET_SENDQ_PROBE_STRIDE > 0 {
sent_cnt.is_multiple_of(NET_SENDQ_PROBE_STRIDE)
} else {
false
}
}
pub const NET_EMERG_PCT_400B: u8 = 30;
pub const NET_EMERG_PCT_1K: u8 = 50;
pub const NET_EMERG_PCT_2K: u8 = 60;
pub const NET_EMERG_PCT_DEFAULT: u8 = 80;
pub const NET_EMERGENCY_SLEEP_MS: u64 = 2;
#[inline]
pub fn emergency_pct_for(avg_len: usize) -> u8 {
if avg_len <= 400 {
NET_EMERG_PCT_400B
} else if avg_len <= 1024 {
NET_EMERG_PCT_1K
} else if avg_len <= 2048 {
NET_EMERG_PCT_2K
} else {
NET_EMERG_PCT_DEFAULT
}
}
#[derive(Clone, Copy, Debug)]
pub struct AdaptiveCfg {
pub(crate) target_pct: u8,
pub(crate) hysteresis_pct: u8,
pub(crate) min_ms: u64,
pub(crate) max_ms: u64,
pub(crate) step_ms: u64,
pub(crate) cur_ms: u64,
}
impl Default for AdaptiveCfg {
fn default() -> Self {
Self {
target_pct: 30,
hysteresis_pct: 5,
min_ms: 0,
max_ms: 8,
step_ms: 1,
cur_ms: 0,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct BackpressureCfg {
pub(crate) high_water_pct: u8,
pub(crate) sleep_ms: u64,
pub(crate) adaptive: Option<AdaptiveCfg>,
}
impl Default for BackpressureCfg {
fn default() -> Self {
Self {
high_water_pct: NET_SENDQ_BACKOFF_HIGH_PCT,
sleep_ms: NET_SENDQ_BACKOFF_SLEEP_MS,
adaptive: None,
}
}
}
impl BackpressureCfg {
pub fn adaptive_default() -> Self {
Self {
high_water_pct: NET_SENDQ_BACKOFF_HIGH_PCT,
sleep_ms: 0,
adaptive: Some(AdaptiveCfg::default()),
}
}
pub(crate) fn auto_sleep_ms(&mut self, pct: u8) -> u64 {
if let Some(mut ad) = self.adaptive {
let hi = ad.target_pct.saturating_add(ad.hysteresis_pct);
let lo = ad.target_pct.saturating_sub(ad.hysteresis_pct);
if pct > hi {
ad.cur_ms = (ad.cur_ms + ad.step_ms).min(ad.max_ms);
} else if pct < lo {
ad.cur_ms = ad.cur_ms.saturating_sub(ad.step_ms);
if ad.cur_ms < ad.min_ms {
ad.cur_ms = ad.min_ms;
}
}
self.sleep_ms = ad.cur_ms;
self.adaptive = Some(ad);
ad.cur_ms
} else if pct >= self.high_water_pct && self.sleep_ms > 0 {
self.sleep_ms
} else {
0
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum BackoffMode {
Auto,
ForceOn,
ForceOff,
}
#[derive(Clone, Copy, Debug)]
pub struct NetSendPolicy {
pub rate_limit_rps: usize,
pub backoff_mode: BackoffMode,
pub adaptive: bool,
}
impl Default for NetSendPolicy {
fn default() -> Self {
Self {
rate_limit_rps: 0,
backoff_mode: BackoffMode::Auto,
adaptive: true,
}
}
}
static NET_BACKOFF_ADAPTIVE_ENABLE: AtomicBool = AtomicBool::new(true);
pub fn set_net_backoff_adaptive(v: bool) {
NET_BACKOFF_ADAPTIVE_ENABLE.store(v, Ordering::Relaxed);
}
pub fn net_backoff_adaptive() -> bool {
NET_BACKOFF_ADAPTIVE_ENABLE.load(Ordering::Relaxed)
}
thread_local! {
static TCP_BUILD_RATE_LIMIT_HINT: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
}
pub fn set_tcp_build_rate_limit_hint(rps: Option<usize>) {
TCP_BUILD_RATE_LIMIT_HINT.with(|c| c.set(rps));
}
pub fn get_tcp_build_rate_limit_hint() -> Option<usize> {
TCP_BUILD_RATE_LIMIT_HINT.with(|c| c.get())
}
static GLOBAL_RATE_LIMIT_RPS: AtomicUsize = AtomicUsize::new(0);
pub fn set_global_rate_limit_rps(v: usize) {
GLOBAL_RATE_LIMIT_RPS.store(v, Ordering::Relaxed);
}
pub fn get_global_rate_limit_rps() -> usize {
GLOBAL_RATE_LIMIT_RPS.load(Ordering::Relaxed)
}