Page MenuHomeFreeBSD

vmm: compute the ACPI PM timer value without a lossy division
Needs ReviewPublic

Authored by wanpengqian_gmail.com on Wed, Oct 7, 5:53 AM.

Details

Reviewers
None
Group Reviewers
bhyve
Summary

The ACPI Power Management Timer is "a 24-bit or 32-bit fixed rate free running
count-up timer that runs off a 3.579545 MHz clock" (ACPI specification, section
4.8.3.3; the rate is PMTMR_FREQ). vpmtmr_handler() returned the uptime divided
by that period held as an sbintime_t, and the period, 2^32 / 3579545 = 1199.864
sbintime units, is truncated to 1199 when stored, so the timer runs
1199.864 / 1199 - 1 = 720 ppm fast. A guest that keeps time by the PM timer
gains about a minute a day.

Compute the count as seconds times the frequency, which is exact: split the
32.32 fixed-point delta into its whole and fractional seconds and multiply
each by PMTMR_FREQ.

Signed-off-by: Wanpeng Qian <wanpengqian@gmail.com>
Sponsored by: keelos.dev

Test Plan

The error is deterministic and follows from the spec. The ACPI specification
(section 4.8.3.3, Power Management Timer) defines the timer as running off a
3.579545 MHz clock; FreeBSD's PMTMR_FREQ is 3579545. The old code stored the
period as bttosbt(FREQ2BT(PMTMR_FREQ)) = floor(2^32 / 3579545) = 1199 sbintime
units, dropping the true 1199.864, and divided the delta by it.
1199.864 / 1199 = 1.000720, i.e. 720 ppm fast. Multiplying by PMTMR_FREQ
instead is exact.

Effect on a guest, observed on real hardware (a bhyve host whose TSC is 3.600
GHz): Linux calibrates its TSC against the PM timer at boot. Before the fix it
reported "Refined TSC clocksource calibration: 3597.410 MHz" on every boot
(3600 / 1.000720 = 3597.41), and its clocksource watchdog then saw the TSC
about 346 us off per 0.5 s against the HPET, near the 500 us it tolerates; with
a little jitter the guest marked the TSC unstable and fell back to the HPET,
after which every clock read is a VM exit. After the fix the guest calibrates
to 3600.000 MHz and keeps the TSC as its clocksource.

A FreeBSD guest's ACPI-fast timecounter shows the same 720 ppm before and none
after. Only the returned count changes.

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 77793
Build 74676: arc lint + arc unit