Actualización, 11 de octubre de 2026, 16:30: desde entonces he flasheado la BIOS 1.30. No arregla el desfase. tscsync sigue alineando los núcleos, pero en dos arranques con sincronización el kernel tiró el TSC igualmente, esta vez por su watchdog de clocksource y no por la comprobación de warp: BIOS 1.30: la sincronización aguanta, el TSC se va igual. Con
tsc=nowatchdogañadido, el TSC aguantó, y cinco minutos de medida contra el reloj de tiempo real no encontraron ningún warp ni deriva: tsc=nowatchdog: cinco minutos, ningún warp. Un arranque no es una serie de arranques, así que lo llamo aguantando, no arreglado.
Cada arranque, el mismo aviso
Esto aparecía en el log del kernel de mi ThinkPad E595 en todos y cada uno de los arranques:
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
No se colgó nada. No salió nada en el escritorio. La máquina pasaba en silencio todo su reloj a un chip temporizador más lento desde el momento del arranque, y lo habría seguido haciendo para siempre si yo no hubiera estado leyendo el journal por otra cosa.
El Time Stamp Counter (TSC) es un contador dentro de cada núcleo de la CPU que avanza a un ritmo fijo. Leerlo cuesta una sola instrucción. Linux lo usa como su reloj principal, pero solo si todos los núcleos coinciden en la cuenta1. Si un núcleo va por detrás, el kernel no puede fiarse de él y recurre al High Precision Event Timer (HPET), un dispositivo temporizador aparte. El autor de tscsync midió una lectura del reloj en torno a 1,2 µs con el HPET, frente a decenas de nanosegundos con el TSC, y el kernel y el escritorio leen el reloj decenas de miles de veces por segundo1. Eso es latencia y batería, pagadas en cada arranque, para nada.
¿Y cuánto iba por detrás el núcleo? Más de tres segundos. En un portátil a 2,1 GHz, eso son 6 600 millones de ciclos.
Lo que entrega el firmware
El mensaje nombra la CPU 0 y la CPU 2, pero la culpa no es de la CPU 2. Medido por tscsync desde el gestor de arranque, y por el kernel en cuanto arranca, el cuadro es el mismo cada vez:
| Núcleo | Desfase respecto a la CPU 0 | Qué significa |
|---|---|---|
| CPU 0, el procesador de arranque (BSP) | 0 | la referencia, y el que no encaja |
| CPU 1 a 7, los demás núcleos (AP) | entre +6 200 y +6 800 millones de ciclos | coinciden entre sí con 32 ciclos de margen |
| Distancia a 2 096 MHz | de 2,9 a 3,3 segundos | el procesador de arranque va por detrás, en cada arranque |
Siete núcleos coinciden. Uno llega tres segundos tarde. El firmware levanta el procesador de arranque, hace su trabajo en él y le pasa el control al gestor de arranque sin volver a poner nunca ese contador en línea con los demás.
¿Por qué no puede Linux simplemente arreglarlo? En las CPU Intel hay un registro, IA32_TSC_ADJUST, que deja al kernel corregir el contador de un núcleo, y el código de sincronización del kernel lo usa cuando existe2. Las CPU AMD Zen no lo tienen. Sin él, nada en Linux puede reparar el desfase, y el intento dentro del kernel de escribir los contadores directamente (tsc=directsync) se rechazó upstream por poco fiable3.
Así que el kernel hace lo único honesto que le queda. Mide, encuentra que el tiempo va hacia atrás entre dos núcleos (lo que el kernel llama un warp), imprime el aviso y deja de usar el TSC2.
Cuarenta arranques, cuarenta warps
Un arranque es una anécdota. El journal de esta máquina se remonta al 30 de mayo de 2026, así que se lo pregunté.
| Arranques | Warp, ciclos | Warp a 2 096 MHz | |
|---|---|---|---|
| Del 30 de mayo al 11 de octubre de 2026 | 40 de 40 | de 6 160 a 9 880 millones | de 2,94 a 4,72 s |
| Mediana | 6 570 millones | 3,14 s | |
| Tras un apagado completo de unos 27 s | 1 | 6 600 millones | 3,15 s |
| Con tscsync en modo sync, BIOS 1.28 | 1 | ninguno registrado | TSC conservado |
| BIOS 1.30, tscsync desactivado | 1 | 6 130 millones | 2,92 s |
| BIOS 1.30, tscsync en modo sync | 2 | ninguno registrado | TSC tirado por el watchdog, ver más abajo |
BIOS 1.30, modo sync y tsc=nowatchdog | 1 | ninguno registrado | TSC conservado |
Todos los arranques. Arranque en frío, reinicio, tras un apagado lo bastante largo como para esperar que cualquier resto se hubiera ido. La cantidad varía, lo que encaja con lo que encontró el propio autor de tscsync en un Lenovo Legion: allí, el warp crece casi exactamente con el tiempo que pasa la máquina en el firmware antes de que arranque el gestor de arranque3. Un arranque que se queda más rato en el logo sale más atrasado.
El arreglo tentador es tsc=reliable en la línea de comandos del kernel, que le dice a Linux que deje de comprobar. No lo hagas. Los núcleos están de verdad a tres segundos de distancia, y apagar la comprobación no cambia eso; solo deja que el kernel use un reloj que salta hacia atrás cuando una tarea pasa de un núcleo a otro. El README de tscsync dice lo mismo, y nombra los tirones de audio y vídeo que vienen después1.
¿Puedo preguntar por qué esto sigue saliendo a la venta?
No pregunto quién. Pregunto qué hubo en el proceso de Lenovo que dejó salir una BIOS que deja un núcleo tres segundos por detrás en cada arranque.
La respuesta está en las notas de diseño de tscsync, y no es halagadora. Fisher-Price OS (Windows) escribe él mismo los TSC al arrancar3. Un portátil validado solo con eso pasa la prueba, porque el sistema operativo recoge en silencio lo que deja el firmware. Linux mantiene su comprobación de seguridad y avisa de lo que le han dado, que es exactamente por lo que el fallo aparece ahí y en ningún otro sitio.
Lo incómodo es que, según parece, esto ya se ha arreglado antes. Hay usuarios que cuentan que Lenovo arregló la misma clase de fallo con una actualización de BIOS en el ThinkPad A485, el T14s Gen 1 AMD y el E4954. El E495 es el hermano de 14 pulgadas de esta máquina, y comparten una BIOS: el propio readme de Lenovo para el paquete lista los dos5.
Mi E595 estaba en la 1.28 y daba warp en cada arranque. Lenovo ha publicado desde entonces la 1.29 y la 1.30, y ninguna de las dos listas de cambios dice una palabra del TSC; la 1.30 arregla «system randomly freeze since BIOS V1.27» y algunos problemas de seguridad5. Así que instalé la 1.30 y volví a medir. El primer arranque con ella, con tscsync desactivado, registró un warp de 6 126 446 316 ciclos: 2,92 segundos, CPU#0 -> CPU#2, igual que siempre. La 1.30 no lo arregla. Los dos modelos comparten una BIOS, así que si el informe del E495 fuera correcto, esta máquina también estaría arreglada. El informe no da ninguna versión de BIOS ni enlaza ningún log4.
¿Va a volver Lenovo a un portátil de 2019 por un fallo que el sistema operativo con el que prueban les esconde? No.
tscsync, primero midiendo
tscsync es un pequeño programa UEFI que se ejecuta desde GRUB o systemd-boot justo antes de Linux. Le pide a cada núcleo su TSC a través de los propios servicios multiprocesador del firmware, calcula el desfase de cada núcleo respecto al procesador de arranque, adelanta los contadores rezagados hasta que coinciden, y luego comprueba el resultado con una prueba inspirada en la propia comprobación de warp del kernel1. Nunca escribe en la flash, en la NVRAM ni en los ajustes del firmware; los contadores que cambia se reinician al apagar1.
Eso es mucho que confiarle a un gestor de arranque, así que primero leí el código, en el commit 230eaa9, antes de compilarlo. La instalación la hice después como pide el proyecto:
| Paso | Comando | Qué salió |
|---|---|---|
| Compilar | make | binarios para GRUB y systemd-boot |
| Instalar, solo lectura | sudo scripts/install.sh | modo measure, nada escrito en los contadores |
| Apagar, arrancar, mirar | tscsync-status | CPU 0 a 2 964 ms por detrás de los 7 AP, y los AP coincidiendo con microsegundos de margen |
| Activar | sudo scripts/set-mode.sh sync | surte efecto en el siguiente arranque |
El modo measure es lo que merece la pena copiar en cualquier herramienta de este tipo. Te enseña el fallo en tu propio hardware antes de que se le permita cambiar nada, y un guardián contra cuelgues lo desactiva si un arranque nunca llega a Linux1.
El primer arranque en modo sync, directamente de 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
Y el kernel, un segundo después:
clocksource: Switched to clocksource tsc-early
tsc: Refined TSC clocksource calibration: 2096.066 MHz
clocksource: Switched to clocksource tsc
Ninguna línea de warp. Ningún «unstable». /sys/devices/system/clocksource/clocksource0/current_clocksource dice tsc. 121 ms de arranque para tener el reloj que el hardware debía haber tenido desde el principio. Eso fue con la BIOS 1.28. La 1.30 no ha sido tan limpia.
Los 21 nanosegundos que Linux desautorizó
Hay una línea en ese informe que parece peor de lo que es, y merece la pena explicarla en vez de esconderla. La pasada final de verificación de tscsync marcó un núcleo:
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 ciclos son unos 21 nanosegundos. tscsync apunta a una coincidencia de unos 10 ciclos1, así que según su propio objetivo la CPU 7 falló, y lo dijo. Luego arrancó Linux y no recurrió al HPET en absoluto.
Las dos comprobaciones hacen preguntas distintas:
| Veredicto de desfase de tscsync | Prueba de warp del kernel | |
|---|---|---|
| Pregunta | ¿a qué distancia están los contadores? | ¿va alguna vez el tiempo hacia atrás entre núcleos? |
| Método | estimación a partir de un viaje de ida y vuelta cronometrado | los núcleos leen por turnos y comparan con la última lectura2 |
| Umbral | unos 10 ciclos | ninguna lectura más baja que la anterior |
| CPU 7 en este arranque | 45 ciclos, BAD | ningún warp, TSC conservado |
El warps bsp/ap 0/0 de esa misma línea es tscsync ejecutando la prueba al estilo del kernel. No vio ninguno. El kernel tampoco.
El proyecto ya sabe que su veredicto puede ser más estricto que el del kernel. Un IdeaPad de su lista de hardware tenía cuatro núcleos marcados a entre 28 y 42 ciclos que el kernel aceptó, y desde la 2.1 un viaje de ida y vuelta lento se informa como UNSURE en lugar de BAD4. La regla del README para ese caso es la correcta: decide la propia comprobación del kernel, y si la clocksource es tsc, la ha pasado1. Un viaje de ida y vuelta de 357 ciclos no es lento en esta máquina, así que salió BAD. Una herramienta que enseña sus cálculos y luego deja que el kernel tenga la última palabra está hecha como debe.
BIOS 1.30: la sincronización aguanta, el TSC se va igual
Con la 1.30 volví a poner tscsync en modo sync y reinicié dos veces. Las dos veces hizo su trabajo. Adelantó el procesador de arranque unos 6 170 millones de ciclos, todos los núcleos quedaron a unas pocas decenas de ciclos, y el kernel no registró ningún warp. Las dos veces Linux acabó igualmente en el HPET unos segundos después:
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
Esta es otra comprobación. La prueba de warp se ejecuta a medida que se levanta cada núcleo y pregunta si el tiempo va alguna vez hacia atrás entre núcleos. El watchdog de clocksource sigue funcionando después del arranque. Más o menos cada medio segundo compara cuánto ha avanzado el TSC con cuánto ha avanzado el HPET, y si discrepan en más de un pequeño margen marca el TSC como inestable6. Una vez marcado, se queda así hasta el siguiente arranque.
tsc=nowatchdog se salta solo la segunda, mientras que tsc=reliable se salta las dos y escondería un warp real.| Arranque con la 1.30 | Comprobación de warp | Intervalo HPET | Intervalo TSC | TSC frente a HPET |
|---|---|---|---|---|
| Primer arranque con sync | pasada | 495 426 793 ns | 495 006 210 ns | unas 850 ppm por debajo |
| Segundo arranque con sync | pasada | 495 136 113 ns | 496 026 671 ns | unas 1 800 ppm por encima |
Entonces, ¿va el TSC al ritmo equivocado? No. Con la máquina ya arrancada, cronometré el TSC contra el reloj raw del kernel basado en el HPET durante 30 segundos, en ventanas de 5 segundos. Todas las ventanas dieron 2 096,062 MHz, a unas 4 partes por millón de la propia calibración del kernel. Un fallo de ritmo real no pasa de lento a rápido entre dos arranques para luego desaparecer. Algo perturba una ventana de medio segundo al principio del arranque, y la línea remote CPU 5 read timed out justo antes apunta a un núcleo que deja de responder por un momento. Que el firmware le robe tiempo a las CPU encajaría. No lo he demostrado.
Dos arranques es toda la evidencia que hay. Todavía no puedo decir si es la 1.30, el azar o algo que desencadena la escritura de tscsync. El único arranque con sync en la 1.28 conservó el TSC y los dos en la 1.30 lo perdieron, y eso no basta para culpar a la BIOS.
Para este caso hay una opción del kernel más acotada que tsc=reliable. tsc=nowatchdog apaga el watchdog de clocksource para el TSC y deja en paz la comprobación de warp del arranque7. Si tscsync alguna vez no lograra sincronizar, el kernel seguiría detectando el warp y recurriría al HPET. Esa era la siguiente prueba. Los números, y algunos cambios sugeridos para los propios informes de tscsync, están en el gestor de incidencias del proyecto8.
tsc=nowatchdog: cinco minutos, ningún warp
Añadí tsc=nowatchdog y reinicié. tscsync adelantó el procesador de arranque 6 207 104 421 ciclos, el kernel no registró ni warp ni desviación de frecuencia, y current_clocksource decía tsc. El primer arranque con la 1.30 que lo conservó.
Eso solo demuestra que lo que tiraba el TSC era el watchdog. No demuestra que sea seguro funcionar con el TSC, y con el watchdog apagado ya no hay nada en el kernel vigilándolo. Así que lo vigilé yo.
Mi comprobación de ritmo anterior no servía aquí. Cronometraba el TSC contra CLOCK_MONOTONIC_RAW, lo que estaba bien mientras el kernel funcionaba con el HPET. Con el TSC, ese reloj se construye a partir del TSC, así que la prueba compararía el contador consigo mismo. El HPET tampoco se puede leer desde el espacio de usuario, porque el kernel de Fedora se compila sin CONFIG_HPET_MMAP. El reloj de tiempo real (RTC) sí. Funciona con su propio cristal, y el kernel despierta a un programa en cada tic de su contador de segundos9.
Así que escribí una pequeña comprobación, tsccheck. Se fija a la CPU 0 y ejecuta cuatro pruebas cada 15 segundos:
| Prueba | Cómo | Qué aspecto tiene un fallo |
|---|---|---|
| Ritmo | ciclos del TSC contados entre tics de segundo del RTC | los MHz derivando en un sentido |
| Desfase | 3 000 viajes de ida y vuelta de la CPU 0 a cada núcleo, desfase tomado del más rápido | un desfase que crece |
| Warps | en cada viaje, ¿leyó algún núcleo un valor más bajo que otro ya visto? | cualquier cosa por encima de cero |
| Clocksource | leer current_clocksource | cualquier cosa que no sea tsc |
El viaje de ida y vuelta es el núcleo de todo. La CPU 0 lee su contador y lo pasa, el otro núcleo lee el suyo y lo devuelve, y la CPU 0 vuelve a leer. Si los núcleos coinciden, la lectura del medio tiene que caer entre las otras dos:
El bucle que lo hace:
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));
}
}
El programa entero es C normal para Linux x86-64, sin nada atado a tscsync ni a este portátil. Necesita root para /dev/rtc0, y el tercer argumento es la cifra de tu propio kernel, sacada de journalctl -k -b | grep 'Refined TSC':
gcc -O2 -Wall -pthread -o tsccheck tsccheck.c
sudo ./tsccheck 15 20 2096.073
SHA-256 02eba1966a0097196acd5dfdcad9c4b315cafc1854c60f34c5b56080e6c38408.
Veinte muestras en cinco minutos, nada más arrancar:
| Medida | Resultado |
|---|---|
| Clocksource | tsc en las 20 muestras, ningún mensaje del kernel |
| Warps | 0 en unos 420 000 viajes de ida y vuelta |
| Peor desfase respecto a la CPU 0 | 85 ciclos, unos 41 ns, sin tendencia |
| Ritmo en toda la ejecución | 2 096,027 MHz, entre 2 096,022 y 2 096,031 desde el primer minuto |
| Ritmo por ventana de 15 segundos | de 2 095,943 a 2 096,104 MHz |
Las ventanas parecen peores de lo que son. Un tic del RTC que llega tarde hace una ventana larga y la siguiente corta, así que los errores vienen por parejas que se anulan: 61 partes por millón (ppm) por debajo a los 30 segundos, 15 ppm por encima a los 60. Una deriva no se anula sola. La cifra de toda la ejecución apenas se movió.
La cifra de toda la ejecución se queda 22 ppm por debajo de los 2 096,073 MHz del kernel durante los cinco minutos completos. Esa es la diferencia entre dos cristales, y se mantuvo quieta. chrony, que gobierna el reloj contra servidores de hora de internet, lo tenía entre 16 y 19 ppm por debajo10. Mismo sentido, mismo tamaño.
Los desfases saltan entre los mismos pocos valores, 10, 21 y 33 ciclos a un lado y otro, sin que ningún núcleo se aleje. Eso es ruido de un viaje de ida y vuelta de unos 550 ciclos. La verificación de tscsync seguiría llamando BAD a algunos. El kernel no.
¿Aguantó el TSC? Sí, en todas las medidas que tenía. Cinco minutos tras un único arranque siguen sin ser una serie de arranques. Lo siguiente es la misma comprobación tras mucho tiempo encendida, y otra vez a lo largo de unos cuantos reinicios más.
El arreglo es cosa del firmware
tscsync no debería tener que existir. Su propio README lo dice: los fabricantes de firmware son bienvenidos a usar el enfoque y el código, «which is where this should be fixed»1. Lenovo tiene el código fuente. El autor de tscsync solo tenía lo que su portátil le quisiera contar, y 80 arranques de logs1.
Eso es lo que se queda. El arreglo necesitó a un desconocido de fuera de la empresa para medir, diagnosticar y rodear el problema, mientras la firma que vendió el hardware lo validaba contra el único sistema operativo que esconde el fallo. Una máquina que no puedes reparar porque su firmware es cerrado no es del todo tuya, está prestada con condiciones. Un firmware abierto con actualizaciones firmadas habría permitido arreglar esto una vez, como es debido, a cualquiera a quien le importara lo suficiente como para mirar.
Mientras tanto, buen trabajo del autor de tscsync. Primero el modo measure, un guardián contra cuelgues, los números a la vista y un veredicto honesto incluso cuando no coincide con el kernel. Así es como deberían hacerse las herramientas de bajo nivel.
Todo host KVM con el mismo firmware
En un portátil esto cuesta algo de latencia y algo de batería. En un hipervisor lo paga además cada invitado, y nada de lo que sigue es cosa de Proxmox. Todo vive en el código KVM del kernel Linux, así que es igual en cualquier host basado en KVM, sea lo que sea lo que corra encima: Proxmox VE, oVirt, OpenStack, o QEMU y libvirt a secas11.
Qué hace KVM cuando el host pierde su TSC
KVM solo les da a sus invitados un reloj estable cuando el propio host funciona con el TSC. En el código del kernel, el reloj maestro, una única referencia de tiempo compartida por todas las vCPU de una VM, solo se activa si host_tsc_clocksource es verdadero12. Si el host cae al HPET se desactiva, y cambian varias cosas a la vez:
| Host con TSC | Host con HPET, TSC marcado como inestable | |
|---|---|---|
| Reloj maestro de KVM12 | activado | desactivado |
| Una vCPU se mueve a otro núcleo12 | nada que actualizar | una actualización de reloj forzada en todas las vCPU de esa VM, hasta 10 por segundo |
| El desfase de TSC del invitado12 | se deja como está | se reescribe en cada movimiento, y se pone en modo de recuperación |
PVCLOCK_TSC_STABLE_BIT a los invitados12 | activado | no activado |
clock_gettime() en el invitado13 | leído en espacio de usuario | una llamada al sistema, decidido una vez cuando arranca el invitado |
| Lecturas del reloj del invitado entre vCPU14 | independientes | cada lectura comprobada contra un único valor global compartido |
La última fila es la que escala mal. Sin el bit de estabilidad, el kernel del invitado no puede fiarse de que el reloj de cada vCPU coincida con los demás, así que cada lectura se compara con «last_value, a global accumulator» y se sustituye con un compare-and-exchange atómico si es más reciente14. Todas las vCPU del invitado escriben en ese único valor. Cuantas más vCPU leen el reloj, más hacen cola en él.
Nada de esto aparece como un error. La clocksource del invitado sigue diciendo kvm-clock, y nada en el invitado te va a decir por qué se ha vuelto más lento.
Lo que cuesta, medido
Lo medí en este portátil en vez de dejarlo en teoría. La clocksource del host se puede cambiar en caliente, lo que apaga el reloj maestro de KVM igual que la caída en el arranque. Un invitado de usar y tirar arrancó el propio kernel del host con 4 vCPU y un initramfs diminuto cuyo único programa cronometra clock_gettime(). Dos ejecuciones de cada forma, primero el host, luego el invitado:
clock_gettime(), media de dos ejecuciones de cada forma. El host va de 61 a 73 veces más lento con el HPET; el invitado, unas 7 veces con una vCPU y 8 con las cuatro. Los paneles usan escalas distintas, porque las barras del HPET del host aplastarían las del invitado.El invitado no paga directamente el precio del HPET, porque kvm-clock sigue leyendo el TSC por debajo. Lo que pierde es la lectura en espacio de usuario y la independencia entre vCPU, y solo con eso va siete veces más lento. Con las cuatro vCPU leyendo a la vez el coste sube otra vez, y eso es ese valor global compartido en acción. Un invitado con 32 o 64 vCPU hace más cola en él. Eso no lo he medido.
Pongámosle un número. Un invitado que lee el reloj un millón de veces por segundo en una vCPU gasta en ello el 2,8 % de esa vCPU en un host sano, y el 19 % en este. Las bases de datos, la máquina virtual de Java (JVM) y cualquier otra cosa que ponga marcas de tiempo a su trabajo leen mucho el reloj.
Esta ejecución además se queda corta. Cambiar la clocksource a mano apaga el reloj maestro pero no marca el TSC como inestable, así que las reescrituras de desfase de la tabla de arriba nunca se ejecutaron. La caída en el arranque de este E595 sí lo marca como inestable, y un host en ese estado paga las dos cosas.
clockbench.c, el benchmark, ejecutado en el host o como init de un invitadoCómo comprobar un host, y los límites
Ya expliqué por qué los relojes de los invitados son frágiles de entrada en el artículo sobre ptp_kvm; esto es el mismo problema llegando desde abajo. Comprueba el host, no el invitado:
journalctl -k -b | grep -iE 'TSC warp|TSC unstable'
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
tscsync se ejecuta desde GRUB o systemd-boot1, lo que cubre a la mayoría de los hosts KVM. Proxmox VE usa GRUB, o systemd-boot en una instalación UEFI con ZFS como raíz y Secure Boot desactivado15. Eso sí, sé honesto con los límites antes de acercarlo a un nodo de producción.
| Límite | Qué hacer al respecto |
|---|---|
| Solo AMD Zen e Hygon1 | los hosts Intel con TSC_ADJUST ya los arregla el kernel |
| Todas las máquinas de su lista de hardware son portátiles4 | trata un servidor como no probado e informa de él |
| El modo sync se niega en hardware fuera de su lista salvo que lo aceptes explícitamente1 | lee por qué antes de forzarlo |
| Un primer arranque con sync puede colgarse | primero el modo measure, consola fuera de banda abierta, un nodo cada vez |
| Una actualización de BIOS puede arreglar el fallo | vuelve a ponerlo en measure después y comprueba1 |
| Una sincronización limpia puede perder igualmente el TSC por el watchdog | comprueba current_clocksource tras cada arranque; tsc=nowatchdog si la causa es el watchdog, y luego mídelo tú mismo |
Un fallo de firmware que a un portátil le cuesta algo de batería a un hipervisor le cuesta un trozo de cada invitado que ejecuta, cada segundo que está encendido, y los dueños de esos invitados no tienen forma de verlo. Ese es el argumento para comprobar current_clocksource en cada host que tengas, lo haya fabricado quien lo haya fabricado.
tscsync README — qué hace, el coste de leer el reloj con el HPET, por qué
tsc=reliableno es un arreglo, el modelo de seguridad y la regla deUNSURE. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎Linux kernel — arch/x86/kernel/tsc_sync.c — el manejo de
TSC_ADJUST, la prueba de warp y el mensaje «Measured %Ld cycles TSC warp between CPUs, turning off TSC clock.». ↩︎ ↩︎ ↩︎tscsync — docs/DESIGN.md — AMD Zen no tiene
IA32_TSC_ADJUST,tsc=directsyncrechazado upstream, Windows escribe él mismo los TSC al arrancar, y el warp creciendo con el tiempo en el firmware en el Legion Pro 5. ↩︎ ↩︎ ↩︎tscsync — docs/HARDWARE.md — máquinas probadas, los núcleos del IdeaPad 5 aceptados a entre -28 y -42 ciclos, y los arreglos de BIOS de los que se informa en el A485, el E495 y el T14s Gen 1. ↩︎ ↩︎ ↩︎ ↩︎
Lenovo — BIOS Update Utility readme, ThinkPad E495/E595, version 1.30 — los dos modelos en un mismo paquete, el historial de versiones hasta la 1.30 (14 de mayo de 2024) y su lista de cambios. ↩︎ ↩︎
Linux kernel — kernel/time/clocksource.c — el watchdog de clocksource, su comparación de intervalos y el mensaje «Marking clocksource %s unstable due to frequency skew». ↩︎
Linux kernel — kernel-parameters —
tsc=reliabledesactiva la verificación en tiempo de ejecución y las comprobaciones de estabilidad del arranque;tsc=nowatchdogdesactiva solo el watchdog de clocksource. ↩︎tscsync issue #1 — Lenovo ThinkPad E595 — los informes de la 1.28 y la 1.30, el análisis del watchdog y los cambios sugeridos. ↩︎
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», leído a través de
/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 y las herramientas basadas en libvirt, todas gestionando KVM. ↩︎
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», yPVCLOCK_TSC_STABLE_BITactivado solo con el reloj maestro. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎Linux kernel — arch/x86/kernel/kvmclock.c — el invitado activa el reloj en espacio de usuario
VDSO_CLOCKMODE_PVCLOCKsolo cuandoPVCLOCK_TSC_STABLE_BITestá activado. ↩︎Linux kernel — arch/x86/kernel/pvclock.c — sin
PVCLOCK_TSC_STABLE_BIT, cada lectura se comprueba contra «last_value, a global accumulator» con un compare-and-exchange atómico. ↩︎ ↩︎Proxmox VE — Host Bootloader — GRUB por defecto, systemd-boot para instalaciones UEFI con ZFS como raíz y Secure Boot desactivado. ↩︎