RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / src / time.rs
1977 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//! 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)
}