Устранение отставания времени в виртуальных машинах под управлением KVM

На виртуальных машинах с операционной системой NetBSD под управлением системы виртуализации KVM часто можно заметить отставание часов. Причём, как правило, чем сильнее загружена виртуальная машина, тем сильнее отстают часы. Отставание часов на слабо нагруженных виртуальных машинах удаётся устранить с помощью демона синхронизации времени вроде openntpd, но большое отставание оказывается ему не по зубам.

Скорее причина отставания времени не в высокой загрузке виртуальных машин как таковых, а в том, что производительности процессоров компьютера не хватает на удовлетворение потребностей виртуальных машин. Системе виртуализации приходится отнимать процессор у одной виртуальной машины, чтобы дать поработать другой. Когда виртуальная машина не выполняется, она может пропустить сигналы от интервального таймера, в результате чего время внутри виртуальной машины начинает отставать. Из-за того, что нагрузка виртуальных машина меняется непредсказуемым образом, демон синхронизации типа openntpd не может посчитать точную поправку к таймеру - необходимая поправка постоянно меняется.

В состав виртуальной машины входят виртуальные устройства, конфигурацию которых можно посмотреть с помощью команды virsh dumpxml с указанием имени виртуальной машины. Среди этих настроек можно найти настройки часов и таймеров:

# virsh dumpxml buster | egrep 'clock|timer'
  <clock offset='utc'>
    <timer name='rtc' tickpolicy='catchup' track='guest'/>
    <timer name='pit' tickpolicy='delay'/>
    <timer name='hpet' present='no'/>
  </clock>

Я довольно долго искал в интернете решение этой проблемы, перепробовал разные таймеры, варианты их настройки, пробовал выбирать в операционной системе таймеры, используемые в качестве основы для подсчёта реального времени, пробовал использовать для синхронизации времени демон chronyd, но справиться с отставанием времени в виртуальных машинах с NetBSD так и не удавалось. В конце-концов получилось, как в шутке "если ничего не помогает, прочтите, наконец, документацию".

Опция tickpolicy настраивает политику передачи импульсов таймера в гостевую виртуальную машину:

  • delay - доставлять импульсы таймера с обычной скоростью, в том числе с опозданием,
  • catchup - доставлять импульсы таймера с повышенной частотой, чтобы нагнать отставание,
  • merge - объединять импульсы таймера в один импульс,
  • discard - отбрасывать пропущенные импульсы таймера.

Опция track настраивает, что отслеживает таймер. Имеет значение только для таймера со значением rtc в поле name:

  • boot - устаревшее неподдерживаемое значение, которое ранее называлось host,
  • guest - часы реального времени отслеживают время виртуальной машины,
  • wall - часы реального времени всегда отслеживают время в системе виртуализации.

Таким образом, чтобы виртуальная машина не теряла импульсы интервальных таймеров, когда система виртуализации забирает у неё процессор, нужно указать опцию tickpolicy со значением catchup.

В случае часов реального времени стоит указать опцию track со значением wall, чтобы показания часов реального времени в виртуальной машине совпадали с показаниями часов реального времени системы виртуализации.

Для редактирования настроек виртуальной машины можно воспользоваться командой virsh edit с указанием имени виртуальной машины. В соответствии с указанными выше соображениями, я настроил часы и таймеры следующим образом:

  <clock offset='utc'>
    <timer name='rtc' tickpolicy='catchup' track='wall'/>
    <timer name='pit' tickpolicy='catchup'/>
    <timer name='tsc' tickpolicy='catchup' mode='native'/>
    <timer name='hpet' present='no'/>
  </clock>

Таймер tsc остался после предыдущих экспериментов и я не стал его удалять. Этот таймер использует специальный регистр процессора, в котором идёт подсчёт количества циклов центрального процессора. Полагаться на показания этого регистра для расчёта показаний таймера реального времени не стоит, т.к. из-за изменения частоты процессора длительность циклов центрального процессора может меняться.

После перезагрузки виртуальных машин с NetBSD с новыми настройками отставание часов практически исчезло, хотя таймер реального времени недостаточно точен и его показания постоянно плавают, отличаясь от правильных на 1-2 секунды в сторону отставания или опережения.

В качестве дополнительной меры для поддержания точности таймера реального времени внутри виртуальных машин я использую openntpd, хотя, по моим наблюдениям, показания таймера реального времени и так достаточно точны и вряд ли нуждаются в дополнительной коррекции.

Использованные материалы