hardware 0.0.9

A no_std bare-metal hardware abstraction layer — all port I/O, memory and swap allocations are guarded at runtime. Do not consider this dependency stable before x.1.x
Documentation
use core::sync::atomic::{AtomicU64, Ordering};

static SURGE_MEM_WINDOW_NS: AtomicU64 = AtomicU64::new(0);
static SURGE_MEM_BUDGET: AtomicU64 = AtomicU64::new(0);
static SURGE_MEM_COUNTER: AtomicU64 = AtomicU64::new(0);
static SURGE_MEM_WINDOW_START: AtomicU64 = AtomicU64::new(0);
static SURGE_SWAP_WINDOW_NS: AtomicU64 = AtomicU64::new(0);
static SURGE_SWAP_BUDGET: AtomicU64 = AtomicU64::new(0);
static SURGE_SWAP_COUNTER: AtomicU64 = AtomicU64::new(0);
static SURGE_SWAP_WINDOW_START: AtomicU64 = AtomicU64::new(0);
static SURGE_DMA_WINDOW_NS: AtomicU64 = AtomicU64::new(0);
static SURGE_DMA_BUDGET: AtomicU64 = AtomicU64::new(0);
static SURGE_DMA_COUNTER: AtomicU64 = AtomicU64::new(0);
static SURGE_DMA_WINDOW_START: AtomicU64 = AtomicU64::new(0);
static SURGE_IRQ_WINDOW_NS: AtomicU64 = AtomicU64::new(0);
static SURGE_IRQ_BUDGET: AtomicU64 = AtomicU64::new(0);
static SURGE_IRQ_COUNTER: AtomicU64 = AtomicU64::new(0);
static SURGE_IRQ_WINDOW_START: AtomicU64 = AtomicU64::new(0);
static SURGE_CPU_WINDOW_NS: AtomicU64 = AtomicU64::new(0);
static SURGE_CPU_BUDGET: AtomicU64 = AtomicU64::new(0);
static SURGE_CPU_COUNTER: AtomicU64 = AtomicU64::new(0);
static SURGE_CPU_WINDOW_START: AtomicU64 = AtomicU64::new(0);
pub fn set_memory_surge(window_ns: u64, budget: u64) {
    SURGE_MEM_WINDOW_NS.store(window_ns, Ordering::Release);
    SURGE_MEM_BUDGET.store(budget, Ordering::Release);
    SURGE_MEM_COUNTER.store(0, Ordering::Release);
    SURGE_MEM_WINDOW_START.store(0, Ordering::Release);
}

pub fn set_swap_surge(window_ns: u64, budget: u64) {
    SURGE_SWAP_WINDOW_NS.store(window_ns, Ordering::Release);
    SURGE_SWAP_BUDGET.store(budget, Ordering::Release);
    SURGE_SWAP_COUNTER.store(0, Ordering::Release);
    SURGE_SWAP_WINDOW_START.store(0, Ordering::Release);
}

pub fn set_dma_surge(window_ns: u64, budget: u64) {
    SURGE_DMA_WINDOW_NS.store(window_ns, Ordering::Release);
    SURGE_DMA_BUDGET.store(budget, Ordering::Release);
    SURGE_DMA_COUNTER.store(0, Ordering::Release);
    SURGE_DMA_WINDOW_START.store(0, Ordering::Release);
}

pub fn set_irq_surge(window_ns: u64, budget: u64) {
    SURGE_IRQ_WINDOW_NS.store(window_ns, Ordering::Release);
    SURGE_IRQ_BUDGET.store(budget, Ordering::Release);
    SURGE_IRQ_COUNTER.store(0, Ordering::Release);
    SURGE_IRQ_WINDOW_START.store(0, Ordering::Release);
}

pub fn set_cpu_surge(window_ns: u64, budget: u64) {
    SURGE_CPU_WINDOW_NS.store(window_ns, Ordering::Release);
    SURGE_CPU_BUDGET.store(budget, Ordering::Release);
    SURGE_CPU_COUNTER.store(0, Ordering::Release);
    SURGE_CPU_WINDOW_START.store(0, Ordering::Release);
}

fn check_surge(
    window_ns: &AtomicU64,
    budget: &AtomicU64,
    counter: &AtomicU64,
    window_start: &AtomicU64,
    amount: u64,
) -> bool {
    let win = window_ns.load(Ordering::Acquire);
    if win == 0 {
        return true;
    }
    let cap = budget.load(Ordering::Acquire);
    if cap == 0 {
        return true;
    }

    let now = crate::sys::monotonic_ns();
    let start = window_start.load(Ordering::Acquire);

    if now.wrapping_sub(start) >= win
        && window_start
            .compare_exchange(start, now, Ordering::AcqRel, Ordering::Relaxed)
            .is_ok()
    {
        counter.store(0, Ordering::Release);
    }

    loop {
        let current = counter.load(Ordering::Acquire);
        let next = current.saturating_add(amount);
        if next > cap {
            return false;
        }
        if counter
            .compare_exchange_weak(current, next, Ordering::AcqRel, Ordering::Relaxed)
            .is_ok()
        {
            return true;
        }
    }
}

pub fn gate_memory_surge(requested: u64) -> bool {
    check_surge(
        &SURGE_MEM_WINDOW_NS,
        &SURGE_MEM_BUDGET,
        &SURGE_MEM_COUNTER,
        &SURGE_MEM_WINDOW_START,
        requested,
    )
}

pub fn gate_swap_surge(requested: u64) -> bool {
    check_surge(
        &SURGE_SWAP_WINDOW_NS,
        &SURGE_SWAP_BUDGET,
        &SURGE_SWAP_COUNTER,
        &SURGE_SWAP_WINDOW_START,
        requested,
    )
}

pub fn gate_dma_surge(requested: u64) -> bool {
    check_surge(
        &SURGE_DMA_WINDOW_NS,
        &SURGE_DMA_BUDGET,
        &SURGE_DMA_COUNTER,
        &SURGE_DMA_WINDOW_START,
        requested,
    )
}

pub fn gate_irq_surge(requested: u64) -> bool {
    check_surge(
        &SURGE_IRQ_WINDOW_NS,
        &SURGE_IRQ_BUDGET,
        &SURGE_IRQ_COUNTER,
        &SURGE_IRQ_WINDOW_START,
        requested,
    )
}

pub fn gate_cpu_surge(requested: u64) -> bool {
    check_surge(
        &SURGE_CPU_WINDOW_NS,
        &SURGE_CPU_BUDGET,
        &SURGE_CPU_COUNTER,
        &SURGE_CPU_WINDOW_START,
        requested,
    )
}

#[derive(Clone, Copy)]
pub struct SurgeSnapshot {
    pub mem_window_ns: u64,
    pub mem_budget: u64,
    pub mem_counter: u64,
    pub swap_window_ns: u64,
    pub swap_budget: u64,
    pub swap_counter: u64,
    pub dma_window_ns: u64,
    pub dma_budget: u64,
    pub dma_counter: u64,
    pub irq_window_ns: u64,
    pub irq_budget: u64,
    pub irq_counter: u64,
    pub cpu_window_ns: u64,
    pub cpu_budget: u64,
    pub cpu_counter: u64,
}

pub fn surge_snapshot() -> SurgeSnapshot {
    SurgeSnapshot {
        mem_window_ns: SURGE_MEM_WINDOW_NS.load(Ordering::Acquire),
        mem_budget: SURGE_MEM_BUDGET.load(Ordering::Acquire),
        mem_counter: SURGE_MEM_COUNTER.load(Ordering::Acquire),
        swap_window_ns: SURGE_SWAP_WINDOW_NS.load(Ordering::Acquire),
        swap_budget: SURGE_SWAP_BUDGET.load(Ordering::Acquire),
        swap_counter: SURGE_SWAP_COUNTER.load(Ordering::Acquire),
        dma_window_ns: SURGE_DMA_WINDOW_NS.load(Ordering::Acquire),
        dma_budget: SURGE_DMA_BUDGET.load(Ordering::Acquire),
        dma_counter: SURGE_DMA_COUNTER.load(Ordering::Acquire),
        irq_window_ns: SURGE_IRQ_WINDOW_NS.load(Ordering::Acquire),
        irq_budget: SURGE_IRQ_BUDGET.load(Ordering::Acquire),
        irq_counter: SURGE_IRQ_COUNTER.load(Ordering::Acquire),
        cpu_window_ns: SURGE_CPU_WINDOW_NS.load(Ordering::Acquire),
        cpu_budget: SURGE_CPU_BUDGET.load(Ordering::Acquire),
        cpu_counter: SURGE_CPU_COUNTER.load(Ordering::Acquire),
    }
}