//! A microsecond clock for the network stack. //! //! The PIT ticks only ~18.2 times a second (55 ms), far too coarse to time //! a ping (QEMU answers in well under a millisecond) or to run TCP's //! retransmit timers. Every x86-64 CPU has a *time stamp counter* (TSC): a //! 64-bit counter that goes up at a fixed rate, read with `rdtsc`. We don't //! know that rate, so we measure it once against the PIT (apic.rs does this //! while it calibrates the APIC timer, over 2 PIT ticks) and from then on //! `now_us` is just `rdtsc / rate`. //! //! Before calibration (or if it never happens, e.g. in some tests) we fall //! back to counting PIT ticks. use core::sync::atomic::{AtomicU64, Ordering}; /// One PIT tick in microseconds (1 / 18.2065 Hz). pub const PIT_TICK_US: u64 = 54_925; static TSC_PER_MS: AtomicU64 = AtomicU64::new(0); static TSC_START: AtomicU64 = AtomicU64::new(0); fn rdtsc() -> u64 { unsafe { core::arch::x86_64::_rdtsc() } } /// Called with a TSC reading at a PIT tick edge (`start`) and the number of /// ticks it took to reach `end`. pub fn calibrate(start: u64, end: u64, pit_ticks: u64) { let per_ms = (end - start) * 1000 / (pit_ticks * PIT_TICK_US); // Keep the clock monotonic: the TSC clock starts at the PIT uptime. let uptime_us = crate::interrupts::ticks() * PIT_TICK_US; TSC_START.store(end.saturating_sub(uptime_us / 1000 * per_ms), Ordering::Relaxed); TSC_PER_MS.store(per_ms.max(1), Ordering::Relaxed); } /// Microseconds since boot. pub fn now_us() -> u64 { let per_ms = TSC_PER_MS.load(Ordering::Relaxed); if per_ms == 0 { return crate::interrupts::ticks() * PIT_TICK_US; } (rdtsc() - TSC_START.load(Ordering::Relaxed)) * 1000 / per_ms } /// The PIT tick at which `us` microseconds from now will have passed /// (rounded up, at least the next tick). For sleeping/timeouts. pub fn deadline_after_us(us: u64) -> u64 { crate::interrupts::ticks() + us.div_ceil(PIT_TICK_US).max(1) }