Eu tenho uma máquina rodando Debian 8 (jessie) rebaixada para o kernel 3.2-rt. Isso está executando um aplicativo que é definido para a prioridade em tempo real (SCHED_RR), acorda a cada 1ms para fazer algum processamento, e a cada 100ms envia uma mensagem serial para outro PC. Isso funciona perfeitamente. (A máquina realmente não executa nada além dos serviços padrão; em particular, não é uma máquina desktop.)
Ao tentar atualizar isto para o Debian 9 (stretch) com o kernel 4.9-rt eu encontrei problemas consideráveis; Um dos maiores problemas é a estabilidade do relógio. Embora inicialmente pareça rodar ok, pouco tempo depois da inicialização eu recebo isso:
clocksource: timekeeping watchdog on CPU0: Marking clocksource 'tsc' as unstable because the skew is too large:
clocksource: 'acpi_pm' wd_now: 74c6dd wd_last: 8dd916 mask: ffffff
clocksource: 'tsc' cs_now: 119ac91c7b1 cs_last: 1158a25441d mask: ffffffffffffffff
clocksource: Switched to clocksource acpi_pm
Na primeira vez que isso ocorreu, a mensagem acima relatou a mudança para "hpet"; depois que essa opção ocorreu, todo o tempo na máquina parecia estar instável - o código usa clock_gettime(CLOCK_MONOTONIC, ...)
para sincronismo e isso estava reportando que agora ele estava acordando a cada 4ms em vez de 1ms, a mensagem serial estava sendo transmitida a cada 200ms em vez de cada 100ms, mas os timestamps dentro da mensagem serial indicavam que estava enviando a cada 66ms. (É possível que alguns deles estivessem contando ticks de ativação em vez de ms reais.)
Eu tentei desativar o HPET no BIOS, o que levou ao erro mostrado acima, e novamente ele roda ok até inicializar o ponto acima, enquanto parece funcionar, algumas vezes alguns temporizadores relatam durações negativas.
A outra coisa principal errada é que o código de processamento que roda a cada 1ms leva cerca de 200us para ser executado em Jessie, mas 900us para ser executado no Stretch, sem nenhuma razão aparente. Eu tentei executar perf record
sobre ele e ele relata que a maior parte do tempo (40% das amostras) está sendo gasto em sys_clock_gettime
.
Este é o mesmo hardware em ambos os casos: uma placa-mãe Supermicro X11SSQ com um CPU i7-7700K na arquitetura AMD64. A aceleração da CPU está desativada no BIOS e reporta constant_tsc
e nonstop_tsc
.
Possivelmente de interesse é que o aplicativo estava fazendo um loop de espera ocupada ( if clock_gettime() < wakeup then sched_yield()
, no pseudocódigo simplificado) para agendar o código de processamento.
Como experiência, eu mudei para dormir ( clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &wakeup)
) e pelo menos até agora o erro da fonte clocks não voltou a ocorrer e ele permaneceu usando o clock tsc, que parece funcionar corretamente (diferente dos outros relógios ).
No entanto, o jitter de despertar é agora 10x pior: 700-1200us (SD 36us) para o sono vs. 890-1120us (SD 2us) para espera ocupada.
Como experimento adicional, tentei alterá-lo para SCHED_DEADLINE (com um único sched_yield()
), mas isso tem o mesmo comportamento de clock_nanosleep.
Alguma idéia de por que esse comportamento mudou entre Jessie e Stretch (ou entre 3.2 e 4.9, mais provavelmente)? Existe uma maneira melhor de realizar uma espera de pouca instabilidade sem aparentemente quebrar o relógio do sistema?