mirror of
https://github.com/RhapsodiOS/RhapsodiOS.git
synced 2026-11-03 22:01:40 -07:00
[GH-ISSUE #64] pexpert: i386 local APIC timer calibration can be off by a large factor (one 10 ms sample), skewing every guest time interval #57
Labels
No labels
bug
documentation
enhancement
enhancement
good first issue
help wanted
help wanted
pull-request
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
github-starred/RhapsodiOS#57
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @raynorpat on GitHub (Oct 19, 2026).
Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/64
Symptom
On i386, when pexpert switches the system clock to the local APIC timer, the calibration sometimes comes out wrong by a large factor. Every guest-time interval is then wrong by that factor: the clock,
sleep, timeouts, and the stress suite's watchdog.Seen under QEMU TCG on a loaded host. Two guests booted at the same time from the same image and kernels:
pexpert: local APIC timer is the clock, 104817 counts per tick (160 MHz bus / 16)... 1247564 counts per tick (1984 MHz bus / 16)621777/624164counts per tick (~992 MHz bus / 16)On guest A the stress suite's 600 s watchdog fired after about 175 real seconds. That turned a normal
mmap truncrun into aFAIL reason=timeout secs=600, which first looked like a kernel hang.Cause (as far as known)
src/drivers-i386/bus/drvPExpert/i386/clock.c:lapic_ticks_in()(:28-49) measures one 10 ms window (CALIBRATE_US).IOGetTimestamp()and scales the result to the window.lapic_clock_enable()(:51-) takes that single sample asper_10msand multiplies it by 100.There is no repeat, no sanity check, and no agreement test between samples. If the vCPU is descheduled, or the reference timestamp misbehaves inside that one 10 ms window, the error carries straight into
count_per_tick. The errors seen are close to whole multiples (about 6x and 0.5x of the true rate), which suggests a missed or doubled wrap of the 8254-based reference rather than random jitter.Expected
Calibration should be robust:
Optionally, cross-check the result against the RTC over a longer interval.
Reproduce
Boot several
-snapshotQEMU guests ofvm/work/rhap-i386-bootstrapped-v2.imgat once on a busy host (TCG) and read thepexpert: local APIC timer is the clock, N counts per tickline on COM2. Correct is about 624000 at 992 MHz / 16. Comparedatein the guest against the host over a minute.Found by the stress suite (the soak
mmap trunc"hang", investigated invm/work/soak-trunc/). Related: #46 (early timers).@raynorpat commented on GitHub (Oct 20, 2026):
Fixed by
dd7f4ebcb(pexpert: calibrate the local APIC timer against 8254 counter 2, median of five bracketed samples), merged into ffs-perf ina104feaaa.Mechanism. The old
lapic_ticks_intimed one 10 ms window withIOGetTimestamp, which is IRQ 0's tick count plus counter 0's fraction (kernel-7/machdep/i386/machine_clock.c~450-490).Fix.
tests/lapic_calibration_test.pynow simulates stalls; it passes, and 4 checks fail when the guards are removed.Concurrent boots, 6 guests at a time on a loaded host, stock and fixed side by side:
Guest
dateagainst the host over 60 s: within ±1 s on all 6 fixed guests.misc timerandvm clockpass.Left open: the kernel's own timestamp (the 8254 clock path) still loses ticks when the CPU is held off. That is outside pexpert and only matters when the LAPIC clock isn't in use.