Update, 11 oktober 2026, 16:30: ik heb inmiddels BIOS 1.30 geflasht. Die herstelt de afwijking niet. tscsync zet de kernen nog steeds gelijk, maar op twee sync-boots liet de kernel de TSC toch vallen, via zijn clocksource-watchdog in plaats van de warpcontrole: BIOS 1.30: de synchronisatie houdt, de TSC gaat toch. Met
tsc=nowatchdogerbij hield de TSC stand, en vijf minuten meten tegen de real-time clock vond geen warps en geen wegdrijven: tsc=nowatchdog: vijf minuten, geen warps. Eén boot is geen reeks boots, dus ik noem het houdt stand, niet opgelost.
Elke boot dezelfde waarschuwing
Dit stond bij elke boot in het kernellog van mijn ThinkPad E595:
TSC synchronization [CPU#0 -> CPU#2]:
Measured 6601281288 cycles TSC warp between CPUs, turning off TSC clock.
tsc: Marking TSC unstable due to check_tsc_sync_source failed
clocksource: Switched to clocksource hpet
Niets crashte. Er verscheen niets op het bureaublad. De machine draaide vanaf het moment van opstarten stilletjes haar hele klok op een tragere timerchip, en dat was eeuwig zo doorgegaan als ik het journal niet voor iets anders had zitten lezen.
De Time Stamp Counter (TSC) is een teller in elke CPU-kern die op een vast tempo tikt. Hem lezen is één instructie. Linux gebruikt hem als hoofdklok, maar alleen als elke kern het over de stand eens is1. Loopt één kern achter, dan kan de kernel hem niet vertrouwen en valt hij terug op de High Precision Event Timer (HPET), een apart timerapparaat. De auteur van tscsync mat een kloklezing op HPET van ongeveer 1,2 µs, tegen tientallen nanoseconden op de TSC, en de kernel en het bureaublad lezen de klok tienduizenden keren per seconde1. Dat is latentie en batterij, bij elke boot betaald, voor niks.
Hoe ver liep de kern dan achter? Ruim drie seconden. Op een laptop van 2,1 GHz is dat 6,6 miljard cycli.
Wat de firmware overdraagt
De melding noemt CPU 0 en CPU 2, maar de fout zit niet in CPU 2. Gemeten door tscsync vanuit de bootloader, en door de kernel zodra die start, is het beeld elke keer hetzelfde:
| Kern | Afwijking ten opzichte van CPU 0 | Wat het betekent |
|---|---|---|
| CPU 0, de opstartprocessor (BSP) | 0 | de referentie, en de vreemde eend |
| CPU’s 1 tot en met 7, de andere kernen (AP’s) | ongeveer +6,2 tot +6,8 miljard cycli | onderling eens tot op 32 cycli |
| Gat bij 2.096 MHz | 2,9 tot 3,3 seconden | de opstartprocessor loopt achter, bij elke boot |
Zeven kernen zijn het eens. Eén is drie seconden te laat. De firmware brengt de opstartprocessor op gang, doet haar werk erop en geeft het stokje door aan de bootloader zonder die ene teller ooit weer gelijk te zetten met de rest.
Waarom kan Linux het niet gewoon herstellen? Op Intel-CPU’s is er een register, IA32_TSC_ADJUST, waarmee de kernel de teller van een kern kan bijstellen, en de synchronisatiecode van de kernel gebruikt het als het er is2. AMD Zen-CPU’s hebben het niet. Zonder dat register kan niets in Linux de afwijking herstellen, en de poging in de kernel om de tellers direct te schrijven (tsc=directsync) is upstream als onbetrouwbaar afgewezen3.
Dus doet de kernel het enige eerlijke dat overblijft. Hij meet, ziet de tijd tussen twee kernen achteruitgaan, drukt de waarschuwing af en stopt met de TSC2.
Veertig boots, veertig warps
Eén boot is een anekdote. Het journal op deze machine gaat terug tot 30 mei 2026, dus ik heb het gevraagd.
| Boots | Warp, cycli | Warp bij 2.096 MHz | |
|---|---|---|---|
| 30 mei tot 11 oktober 2026 | 40 van 40 | 6,16 tot 9,88 miljard | 2,94 tot 4,72 s |
| Mediaan | 6,57 miljard | 3,14 s | |
| Na volledig uitschakelen van ongeveer 27 s | 1 | 6,60 miljard | 3,15 s |
| Met tscsync in sync-modus, BIOS 1.28 | 1 | niets gelogd | TSC behouden |
| BIOS 1.30, tscsync uit | 1 | 6,13 miljard | 2,92 s |
| BIOS 1.30, tscsync in sync-modus | 2 | niets gelogd | TSC laten vallen door de watchdog, zie verderop |
BIOS 1.30, sync-modus en tsc=nowatchdog | 1 | niets gelogd | TSC behouden |
Elke boot. Koude start, herstart, na een uitschakeling die lang genoeg was dat je zou verwachten dat alles wat bleef hangen weg is. De hoeveelheid schommelt, en dat past bij wat de auteur van tscsync zelf vond op een Lenovo Legion: daar schaalt de warp vrijwel perfect met hoe lang de machine in de firmware doorbrengt voordat de bootloader start3. Een boot die langer op het logo blijft hangen, loopt daarna verder achter.
De verleidelijke oplossing is tsc=reliable op de kernelopdrachtregel, wat Linux zegt te stoppen met controleren. Doe het niet. De kernen liggen echt drie seconden uit elkaar, en de controle uitzetten verandert daar niets aan; het laat de kernel alleen een klok gebruiken die achteruit springt als een taak naar een andere kern verhuist. De README van tscsync zegt hetzelfde, en noemt het haperende geluid en beeld dat volgt1.
Mag ik vragen waarom dit nog steeds de deur uitgaat?
Ik vraag niet wie. Ik vraag wat er in het proces van Lenovo een BIOS de deur uit liet gaan die bij elke boot een kern drie seconden achter laat lopen.
Het antwoord staat in de ontwerpnotities van tscsync, en het is niet vleiend. Fisher-Price OS (Windows) schrijft de TSC’s bij het opstarten zelf3. Een laptop die alleen daarop is gevalideerd, slaagt, omdat het besturingssysteem stilletjes achter de firmware opruimt. Linux laat zijn veiligheidscontrole aan en meldt wat het kreeg, en precies daarom duikt de fout daar op en nergens anders.
Het pijnlijke is dat dit naar verluidt al eens eerder is opgelost. Gebruikers melden dat Lenovo dezelfde soort fout met een BIOS-update heeft opgelost op de ThinkPad A485, de T14s Gen 1 AMD en de E4954. De E495 is het 14-inch zusje van deze machine, en ze delen één BIOS: de eigen readme van Lenovo bij het pakket noemt ze allebei5.
Mijn E595 zat op 1.28 en warpte bij elke boot. Lenovo heeft sindsdien 1.29 en 1.30 uitgebracht, en geen van beide wijzigingslijsten zegt een woord over de TSC; 1.30 verhelpt “system randomly freeze since BIOS V1.27” en een paar beveiligingsproblemen5. Dus heb ik 1.30 erop gezet en opnieuw gemeten. De eerste boot erop, met tscsync uitgeschakeld, logde een warp van 6.126.446.316 cycli: 2,92 seconden, CPU#0 -> CPU#2, zoals altijd. 1.30 lost het niet op. De twee modellen delen één BIOS, dus als de melding over de E495 klopte, was deze machine ook hersteld. De melding noemt geen BIOS-versie en linkt geen logs4.
Gaat Lenovo terug naar een laptop uit 2019 voor een fout die het besturingssysteem waarop ze testen voor ze verbergt? Nee.
tscsync, eerst gemeten
tscsync is een klein UEFI-programma dat vanuit GRUB of systemd-boot draait, vlak voor Linux. Het vraagt elke kern om zijn TSC via de eigen multiprocessordiensten van de firmware, rekent per kern de afwijking ten opzichte van de opstartprocessor uit, zet de achterlopende tellers vooruit tot ze het eens zijn, en controleert het resultaat met een test naar het voorbeeld van de eigen warpcontrole van de kernel1. Het schrijft nooit naar flash, NVRAM of firmware-instellingen; de tellers die het verandert, worden bij uitschakelen teruggezet1.
Dat is veel om aan een bootloader over te laten, dus ik heb de code eerst gelezen, op commit 230eaa9, voordat ik hem bouwde. De installatie ging daarna zoals het project vraagt:
| Stap | Opdracht | Wat er terugkwam |
|---|---|---|
| Bouwen | make | binaries voor GRUB en systemd-boot |
| Installeren, alleen lezen | sudo scripts/install.sh | meetmodus, niets naar de tellers geschreven |
| Uitschakelen, opstarten, kijken | tscsync-status | CPU 0 2.964 ms achter alle 7 AP’s, de AP’s onderling eens tot op microseconden |
| Aanzetten | sudo scripts/set-mode.sh sync | werkt vanaf de volgende boot |
De meetmodus is het deel dat het waard is om in elk soortgelijk gereedschap over te nemen. Hij laat je de fout op je eigen hardware zien voordat hij iets mag veranderen, en een hangbeveiliging zet hem uit als een boot Linux nooit bereikt1.
De eerste boot in sync-modus, rechtstreeks uit tscsync-status:
survey (read-only):
cpu 1 apic 1: offset 6815086011 rtt 126 BAD
...
cpu 7 apic 7: offset 6815086010 rtt 378 BAD
0 ok, 7 not ok
moving BSP forward by ~6815086010 cycles to match cpu 7:
bsp vs cpu 7 apic 0: before 6815086001 after 3 rtt 357 iter 3 warps bsp/ap 0/0 OK
AP pass (corrects any AP still off):
7 ok, 0 not ok
total 121 ms
En de kernel, een seconde later:
clocksource: Switched to clocksource tsc-early
tsc: Refined TSC clocksource calibration: 2096.066 MHz
clocksource: Switched to clocksource tsc
Geen warpregel. Geen “unstable”. /sys/devices/system/clocksource/clocksource0/current_clocksource geeft tsc. 121 ms boottijd voor de klok die de hardware altijd al had moeten hebben. Dat was op BIOS 1.28. 1.30 is niet zo netjes geweest.
De 21 nanoseconden die Linux terzijde schoof
Er staat één regel in dat rapport die er erger uitziet dan hij is, en die is het waard om uit te leggen in plaats van te verstoppen. De laatste verificatieronde van tscsync markeerde één kern:
cpu 7 apic 7: offset 45 rtt 357 warps bsp/ap 0/0 BAD
6 ok, 1 not ok
RESULT: some APs out of sync (Linux will fall back to HPET)
45 cycli is ongeveer 21 nanoseconden. tscsync mikt op overeenstemming binnen ruwweg 10 cycli1, dus volgens zijn eigen doel miste CPU 7, en dat zei het ook. Daarna startte Linux en viel het helemaal niet terug op HPET.
De twee controles stellen verschillende vragen:
| Oordeel van tscsync over de afwijking | Warptest van de kernel | |
|---|---|---|
| Vraag | hoe ver liggen de tellers uit elkaar? | gaat de tijd tussen kernen ooit achteruit? |
| Methode | schatting uit een getimed heen-en-terug | kernen lezen om beurten, vergelijken met de vorige lezing2 |
| Norm | ongeveer 10 cycli | geen lezing lager dan de vorige |
| CPU 7 bij deze boot | 45 cycli, BAD | geen warp, TSC behouden |
De warps bsp/ap 0/0 op diezelfde regel is tscsync dat de test in kernelstijl draait. Het zag er geen. De kernel ook niet.
Het project weet al dat zijn oordeel strenger kan zijn dan dat van de kernel. Een IdeaPad in de hardwarelijst had vier kernen gemarkeerd op 28 tot 42 cycli die de kernel accepteerde, en sinds 2.1 wordt een traag heen-en-terug als UNSURE gemeld in plaats van BAD4. De regel van de README voor dat geval is de juiste: de eigen controle van de kernel beslist, en als de clocksource tsc is, is hij geslaagd1. Een heen-en-terug van 357 cycli is op deze machine niet traag, dus kwam het er als BAD uit. Gereedschap dat zijn werk laat zien en de kernel dan het laatste woord geeft, is de goede volgorde.
BIOS 1.30: de synchronisatie houdt, de TSC gaat toch
Op 1.30 heb ik tscsync terug in sync-modus gezet en twee keer herstart. Beide keren deed het zijn werk. Het zette de opstartprocessor ongeveer 6,17 miljard cycli vooruit, elke kern kwam binnen een paar dozijn cycli uit, en de kernel logde geen warp. Beide keren belandde Linux een paar seconden later toch op HPET:
tsc: Detected 2095.890 MHz processor
tsc: Refined TSC clocksource calibration: 2096.053 MHz
clocksource: Watchdog remote CPU 5 read timed out
clocksource: Marking clocksource tsc unstable due to frequency skew
clocksource: Watchdog hpet interval: 495136113ns
clocksource: Clocksource tsc interval: 496026671ns
tsc: Marking TSC unstable due to clocksource watchdog
Dit is een andere controle. De warptest draait terwijl elke kern opkomt en vraagt of de tijd tussen kernen ooit achteruitgaat. De clocksource-watchdog blijft na het opstarten draaien. Ongeveer elke halve seconde vergelijkt hij hoe ver de TSC is opgeschoven met hoe ver HPET is opgeschoven, en als ze meer dan een kleine marge verschillen, markeert hij de TSC als instabiel6. Eenmaal gemarkeerd blijft dat zo tot de volgende boot.
tsc=nowatchdog slaat alleen de tweede over, terwijl tsc=reliable ze allebei overslaat en een echte warp zou verbergen.| Boot op 1.30 | Warpcontrole | HPET-interval | TSC-interval | TSC tegen HPET |
|---|---|---|---|---|
| Eerste sync-boot | geslaagd | 495.426.793 ns | 495.006.210 ns | ongeveer 850 ppm traag |
| Tweede sync-boot | geslaagd | 495.136.113 ns | 496.026.671 ns | ongeveer 1.800 ppm snel |
Loopt de TSC dan op het verkeerde tempo? Nee. Met de machine eenmaal op heb ik de TSC 30 seconden lang in vensters van 5 seconden getimed tegen de op HPET gebaseerde ruwe klok van de kernel. Elk venster kwam uit op 2.096,062 MHz, ongeveer 4 miljoenste van de eigen kalibratie van de kernel. Een echte tempofout zwaait niet tussen twee boots van traag naar snel om daarna te verdwijnen. Iets verstoort vroeg in de boot één venster van een halve seconde, en de regel remote CPU 5 read timed out vlak ervoor wijst op een kern die even niet antwoordt. Firmware die tijd van de CPU’s steelt, zou passen. Bewezen heb ik het niet.
Twee boots is al het bewijs dat er is. Ik kan nog niet zeggen of het 1.30 is, toeval, of iets wat de schrijfactie van tscsync in gang zet. De ene sync-boot op 1.28 hield de TSC en de twee op 1.30 verloren hem, en dat is niet genoeg om de BIOS de schuld te geven.
Voor dit geval is er een smallere kerneloptie dan tsc=reliable. tsc=nowatchdog zet de clocksource-watchdog voor de TSC uit en laat de warpcontrole bij het opstarten met rust7. Als tscsync ooit niet zou synchroniseren, zou de kernel de warp nog steeds vangen en terugvallen op HPET. Dat was de volgende test. De cijfers, en een paar voorgestelde wijzigingen aan de eigen rapportage van tscsync, staan in de issuetracker van het project8.
tsc=nowatchdog: vijf minuten, geen warps
Ik voegde tsc=nowatchdog toe en herstartte. tscsync zette de opstartprocessor 6.207.104.421 cycli vooruit, de kernel logde geen warp en geen skew, en current_clocksource gaf tsc. De eerste boot op 1.30 die hem hield.
Dat bewijst alleen dat de watchdog de TSC liet vallen. Het bewijst niet dat de TSC veilig is om op te draaien, en met de watchdog uit kijkt niets in de kernel er nog naar. Dus heb ik zelf gekeken.
Mijn eerdere tempocontrole had hier geen zin. Die timede de TSC tegen CLOCK_MONOTONIC_RAW, en dat was prima zolang de kernel op HPET draaide. Op de TSC is die klok uit de TSC opgebouwd, dus de test zou de teller met zichzelf vergelijken. HPET is vanuit gebruikersruimte ook niet te lezen, omdat de kernel van Fedora zonder CONFIG_HPET_MMAP is gebouwd. De real-time clock (RTC) wel. Die loopt op een eigen kristal, en de kernel wekt een programma bij elke tik van zijn secondenteller9.
Dus schreef ik een kleine controle, tsccheck. Hij pint zichzelf vast op CPU 0 en draait elke 15 seconden vier tests:
| Controle | Hoe | Hoe een fout eruitziet |
|---|---|---|
| Tempo | TSC-cycli geteld tussen secondentikken van de RTC | de MHz die één kant op drijven |
| Afwijking | 3.000 keer heen-en-terug van CPU 0 naar elke kern, afwijking uit de snelste | een afwijking die groeit |
| Warps | bij elk heen-en-terug: las een kern lager dan een waarde die al gezien was | alles boven nul |
| Clocksource | current_clocksource lezen | alles behalve tsc |
Het heen-en-terug is de kern ervan. CPU 0 leest zijn teller en geeft hem door, de andere kern leest de zijne en geeft die terug, en CPU 0 leest opnieuw. Als de kernen het eens zijn, moet de middelste lezing tussen de andere twee vallen:
De lus die het doet:
for (int i = 0; i < ITERS; i++) {
uint64_t t0 = rdtsc_o(); /* CPU 0 */
atomic_store(&pp.t0, t0);
atomic_store(&pp.seq, 2 * i + 1); /* the other core reads t1 now */
while (atomic_load(&pp.seq) != 2 * i + 2)
;
uint64_t t2 = rdtsc_o(); /* CPU 0 again */
uint64_t t1 = atomic_load(&pp.t1);
if (t1 < t0 || t2 < t1)
r.warps++; /* time went backwards */
if (i > 100 && t2 - t0 < r.rtt) {
r.rtt = t2 - t0;
r.offset = (int64_t)(t1 - (t0 + (t2 - t0) / 2));
}
}
Het hele programma is gewone C voor x86-64 Linux, met niets dat aan tscsync of aan deze laptop vastzit. Het heeft root nodig voor /dev/rtc0, en het derde argument is het getal van je eigen kernel uit journalctl -k -b | grep 'Refined TSC':
gcc -O2 -Wall -pthread -o tsccheck tsccheck.c
sudo ./tsccheck 15 20 2096.073
SHA-256 02eba1966a0097196acd5dfdcad9c4b315cafc1854c60f34c5b56080e6c38408.
Twintig metingen over vijf minuten, direct na het opstarten:
| Meting | Resultaat |
|---|---|
| Clocksource | tsc in alle 20 metingen, geen kernelmeldingen |
| Warps | 0 in ongeveer 420.000 keer heen-en-terug |
| Grootste afwijking van CPU 0 | 85 cycli, ongeveer 41 ns, geen trend |
| Tempo over de hele run | 2.096,027 MHz, vanaf de eerste minuut binnen 2.096,022 tot 2.096,031 |
| Tempo per venster van 15 seconden | 2.095,943 tot 2.096,104 MHz |
De vensters zien er erger uit dan ze zijn. Een late RTC-tik maakt één venster lang en het volgende kort, dus de fouten komen in paren die elkaar opheffen: 61 miljoenste (ppm) traag na 30 seconden, 15 ppm snel na 60. Wegdrijven heft zichzelf niet op. Het getal over de hele run bewoog nauwelijks.
Het getal over de hele run zit de volle vijf minuten 22 ppm onder de 2.096,073 MHz van de kernel. Dat is het verschil tussen twee kristallen, en het stond stil. chrony, dat de klok bijstuurt tegen tijdservers op internet, had hem 16 tot 19 ppm traag10. Dezelfde richting, dezelfde grootte.
De afwijkingen springen tussen dezelfde paar waarden heen en weer, 10, 21 en 33 cycli aan weerszijden, zonder dat een kern wegloopt. Dat is ruis van een heen-en-terug van ongeveer 550 cycli. De verificatie van tscsync zou er sommige nog steeds BAD noemen. De kernel niet.
Hield de TSC stand? Ja, op elke maat die ik had. Vijf minuten na één boot is nog steeds geen reeks boots. Hierna komt dezelfde controle na een lange uptime, en nog eens over een paar herstarts.
De oplossing hoort in de firmware
tscsync zou niet moeten hoeven bestaan. De eigen README zegt het: firmwareleveranciers mogen de aanpak en de code hebben, “which is where this should be fixed”1. Lenovo heeft de broncode. De auteur van tscsync had alleen wat de eigen laptop prijsgaf, en 80 boots aan logs1.
Dat is wat blijft hangen. Er was een vreemde van buiten het bedrijf nodig om de fout te meten, te diagnosticeren en te omzeilen, terwijl de firma die de hardware verkocht hem valideerde tegen het ene besturingssysteem dat de fout verbergt. Een machine die je niet kunt repareren omdat de firmware gesloten is, is niet echt van jou; je hebt hem in bruikleen, onder voorwaarden. Open firmware met ondertekende updates had dit één keer, goed, laten oplossen door iedereen die genoeg gaf om te kijken.
Intussen: goed werk van de auteur van tscsync. Eerst de meetmodus, een hangbeveiliging, de cijfers openbaar en een eerlijk oordeel, ook als het met de kernel van mening verschilt. Zo hoort gereedschap op laag niveau gebouwd te worden.
Elke KVM-host met dezelfde firmware
Op een laptop kost dit wat latentie en wat batterij. Op een hypervisor betaalt elke gast mee, en niets van wat volgt komt door Proxmox. Het zit allemaal in de KVM-code van de Linux-kernel, dus het is hetzelfde op elke host die op KVM is gebouwd, wat er ook bovenop zit: Proxmox VE, oVirt, OpenStack, of kale QEMU en libvirt11.
Wat KVM doet als de host zijn TSC verliest
KVM geeft zijn gasten alleen een stabiele klok als de host zelf op de TSC draait. In de kernelbroncode wordt de master clock, één gedeelde tijdreferentie voor elke vCPU in een VM, alleen aangezet als host_tsc_clocksource waar is12. Val terug op HPET en hij gaat uit, en er verandert meteen van alles:
| Host op TSC | Host op HPET, TSC als instabiel gemarkeerd | |
|---|---|---|
| KVM-master clock12 | aan | uit |
| Een vCPU verhuist naar een andere kern12 | niets bij te werken | een klokupdate afgedwongen op elke vCPU in die VM, tot 10 per seconde |
| De TSC-offset van de gast12 | met rust gelaten | bij elke verhuizing herschreven, en in inhaalmodus gezet |
PVCLOCK_TSC_STABLE_BIT naar gasten12 | gezet | niet gezet |
clock_gettime() in de gast13 | gelezen in gebruikersruimte | een systeemaanroep, één keer besloten bij het opstarten van de gast |
| Kloklezingen in de gast over vCPU’s heen14 | onafhankelijk | elke lezing gecontroleerd tegen één gedeelde globale waarde |
De laatste rij is degene die slecht schaalt. Zonder de stable bit kan de gastkernel er niet op vertrouwen dat de klok van elke vCPU met de andere overeenkomt, dus elke lezing wordt vergeleken met “last_value, a global accumulator” en met een atomische compare-and-exchange ingeruild als hij nieuwer is14. Elke vCPU in de gast schrijft naar die ene waarde. Hoe meer vCPU’s de klok lezen, hoe meer ze erop in de rij staan.
Niets ervan verschijnt als fout. De clocksource van de gast geeft nog steeds kvm-clock, en niets in de gast vertelt je waarom hij trager werd.
Wat het kost, gemeten
Ik heb het op deze laptop gemeten in plaats van het als theorie te laten. De clocksource van de host kan tijdens het draaien worden omgezet, en dat zet de master clock van KVM uit, net als de terugval bij het opstarten. Een wegwerpgast startte de eigen kernel van de host met 4 vCPU’s en een piepkleine initramfs waarvan het enige programma clock_gettime() timet. Twee runs per kant, eerst de host, dan de gast:
clock_gettime(), gemiddelde van twee runs per kant. De host wordt op HPET 61 tot 73 keer trager; de gast ongeveer 7 keer met één vCPU en 8 keer met alle vier. De panelen hebben verschillende schalen, omdat de HPET-balken van de host die van de gast plat zouden drukken.De gast betaalt de prijs van HPET niet direct, want kvm-clock leest daaronder nog steeds de TSC. Wat hij verliest, is de lezing in gebruikersruimte en de onafhankelijkheid tussen vCPU’s, en dat alleen al is zeven keer trager. Met alle vier de vCPU’s tegelijk aan het lezen gaan de kosten weer omhoog, en dat is die gedeelde globale waarde aan het werk. Een gast met 32 of 64 vCPU’s staat er harder voor in de rij. Dat heb ik niet gemeten.
Zet er een getal op. Een gast die op één vCPU een miljoen keer per seconde de klok leest, besteedt daar op een gezonde host 2,8% van die vCPU aan, en op deze 19%. Databases, de Java Virtual Machine (JVM) en alles wat zijn werk van een tijdstempel voorziet, lezen de klok veel.
Deze run onderschat het ook nog. De clocksource met de hand omzetten zet de master clock uit, maar markeert de TSC niet als instabiel, dus het herschrijven van de offset uit de tabel hierboven draaide nooit. De terugval bij het opstarten op deze E595 markeert hem wel als instabiel, en een host in die toestand betaalt allebei.
clockbench.c, de benchmark, te draaien op de host of als init van een gastEen host controleren, en de grenzen
Waarom gastklokken überhaupt kwetsbaar zijn, heb ik behandeld in het ptp_kvm-artikel; dit is hetzelfde probleem dat van onderaf binnenkomt. Controleer de host, niet de gast:
journalctl -k -b | grep -iE 'TSC warp|TSC unstable'
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tscsync draait vanuit GRUB of systemd-boot1, en dat dekt de meeste KVM-hosts. Proxmox VE gebruikt GRUB, of systemd-boot bij een UEFI-installatie met ZFS als root en Secure Boot uit15. Wees wel eerlijk over de grenzen voordat het in de buurt van een productienode komt.
| Grens | Wat je eraan doet |
|---|---|
| Alleen AMD Zen en Hygon1 | Intel-hosts met TSC_ADJUST worden al door de kernel hersteld |
| Elke machine in de hardwarelijst is een laptop4 | behandel een server als ongetest en meld hem |
| Sync-modus weigert hardware buiten de lijst, tenzij je ervoor kiest1 | lees waarom voordat je het overschrijft |
| Een eerste sync-boot kan blijven hangen | eerst meetmodus, out-of-band-console open, één node tegelijk |
| Een BIOS-update kan de fout verhelpen | zet het daarna terug op meten en controleer1 |
| Een schone synchronisatie kan de TSC nog steeds aan de watchdog verliezen | controleer current_clocksource na elke boot; tsc=nowatchdog als de watchdog de oorzaak is, en meet het dan zelf |
Een firmwarefout die een laptop wat batterij kost, kost een hypervisor een stukje van elke gast die hij draait, elke seconde dat hij aanstaat, en de eigenaars van de gasten kunnen het op geen enkele manier zien. Daarom controleer je current_clocksource op elke host die je draait, wie hem ook gemaakt heeft.
tscsync README — wat het doet, de kosten van een kloklezing op HPET, waarom
tsc=reliablegeen oplossing is, het veiligheidsmodel en deUNSURE-regel. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎Linux kernel — arch/x86/kernel/tsc_sync.c — de afhandeling van
TSC_ADJUST, de warptest en de melding “Measured %Ld cycles TSC warp between CPUs, turning off TSC clock.” ↩︎ ↩︎ ↩︎tscsync — docs/DESIGN.md — AMD Zen heeft geen
IA32_TSC_ADJUST,tsc=directsyncupstream afgewezen, Windows schrijft de TSC’s bij het opstarten zelf, en de warp die schaalt met de tijd in de firmware op de Legion Pro 5. ↩︎ ↩︎ ↩︎tscsync — docs/HARDWARE.md — geteste machines, de kernen van de IdeaPad 5 die op -28 tot -42 cycli werden geaccepteerd, en de gemelde BIOS-oplossingen op de A485, E495 en T14s Gen 1. ↩︎ ↩︎ ↩︎ ↩︎
Lenovo — BIOS Update Utility readme, ThinkPad E495/E595, version 1.30 — beide modellen in één pakket, de versiegeschiedenis tot 1.30 (14 mei 2024) en de wijzigingslijst ervan. ↩︎ ↩︎
Linux kernel — kernel/time/clocksource.c — de clocksource-watchdog, zijn vergelijking van intervallen en de melding “Marking clocksource %s unstable due to frequency skew”. ↩︎
Linux kernel — kernel-parameters —
tsc=reliableschakelt de verificatie tijdens het draaien en de stabiliteitscontroles bij het opstarten uit;tsc=nowatchdogschakelt alleen de clocksource-watchdog uit. ↩︎tscsync issue #1 — Lenovo ThinkPad E595 — de meldingen voor 1.28 en 1.30, de analyse van de watchdog en de voorgestelde wijzigingen. ↩︎
Linux kernel — Real Time Clock (RTC) Drivers for Linux — “The clock can also be set to generate an interrupt upon every clock update, thus generating a 1Hz signal”, gelezen via
/dev/rtc. ↩︎chrony — chronyc manual —
tracking: “The ‘frequency’ is the rate by which the system’s clock would be wrong if chronyd was not correcting it.” ↩︎KVM — Management Tools — Proxmox VE, oVirt, OpenStack en de op libvirt gebaseerde tools, die allemaal KVM beheren. ↩︎
Linux kernel — arch/x86/kvm/x86.c —
pvclock_update_vm_gtod_copy(): “If the host uses TSC clock, then passthrough TSC as stable to the guest”, enPVCLOCK_TSC_STABLE_BITalleen gezet met de master clock. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎Linux kernel — arch/x86/kernel/kvmclock.c — de gast zet de klok in gebruikersruimte
VDSO_CLOCKMODE_PVCLOCKalleen aan alsPVCLOCK_TSC_STABLE_BITgezet is. ↩︎Linux kernel — arch/x86/kernel/pvclock.c — zonder
PVCLOCK_TSC_STABLE_BITwordt elke lezing gecontroleerd tegen “last_value, a global accumulator” met een atomische compare-and-exchange. ↩︎ ↩︎Proxmox VE — Host Bootloader — standaard GRUB, systemd-boot voor UEFI-installaties met ZFS als root en Secure Boot uit. ↩︎