Hyper-V, Charge CPU et Dérive de l’Horloge Système

This post is also available in english.

En utilisant Hyper-V Server, vous allez peut-être constater que l’horloge dérive beaucoup du temps “réel”, en particulier lorsque les machines virtuelles “invitées” utilisent fortement les CPUs. Comme l’OS de l’hôte est également virtualisé, cela implique que sa charge fait aussi dériver l’horloge système.

Comment atténuer la dérive de l’horloge

  1. Désactiver la synchronisation du temps dans la section “Integration Services”. (Attention, ce paramètre est défini par snapshot)
  2. Importer le fichier de registre suivant :

    Windows Registry Editor Version 5.00

    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\W32Time\Config]
    "MaxAllowedPhaseOffset"=dword:00000001
    "SpecialPollInterval"=dword:00000005
    "SpecialInterval"=dword:00000001


    Note: Si vous utilisez notepad, assurez vous de sauver le fichier en utilisant un encodage Unicode.

  3. Si l’OS “invité” ne fait pas partie d’un domaine, exécutez la ligne suivante pour définir une source de temps :

    w32tm /config /manualpeerlist:"time.windows.com 1.ca.pool.ntp.org 2.ca.pool.ntp.org" /syncfromflags:MANUAL /update

    Note: Les FQDN des hôtes sont séparés par des espaces.
  4. Executer la commande suivante pour forcer une synchronisation du temps :
    w32tm /resync
  5. Vérifier que l’horloge ne dérive plus avec la commande suivante :

    w32tm /monitor /computer:time.windows.com

 

Un peu d’histoire...

Le système sur lequel je travaille en ce moment se sert énormément du temps, et l’utilise pour prendre les instants de différentes étapes du process. Si pour une raison quelconque l’horloge système devient instable, cela implique que les données générées ne sont plus fiables. Cela peut générer de temps à autres des données corrompues, et ce n’est pas bon pour le business...

J’étais en train d’investiguer une séquence d’événements stockés dans une base de données dans un ordre qui ne pouvait pas s’être produit, parce que le code ne peut pas les générer de cette manière.

Après beaucoup de recherche pour trouver des erreurs dans le code, je suis tombé sur quelque chose d’assez étrange dans les logs de mes applications. Considérant que chacune des lignes est générée par la même thread, et que le temps d’une ligne doit être ultérieur à la ligne précédente :

2009-10-13T17:15:26.541T [INFO][][7] ...
2009-10-13T17:15:26.556T [INFO][][7] ...
2009-10-13T17:15:24.203T [INFO][][7] ...
2009-10-13T17:15:24.219T [INFO][][7] ...
2009-10-13T17:15:24.234T [INFO][][7] ...

Comme ces lignes ont été générées par la même thread, cela veut dire que l’heure système a été radicalement changée entre la deuxième et la troisième ligne. Du point de vue le l’application, le temps est revenu en arrière d’environ deux secondes et que durant ces deux secondes la base de donnée contenait des données générées dans le futur. Ce n’est pas très bon...

 

L’investigation

En regardant le code source de Log4net, j’ai pu confirmer que le temps est récupéré en utilisant un appel à System.DateTime.Now, ce qui de fait exclut une erreur de code.

J’ai ensuite regardé l’état du service de temps de windows, et en exécutant la commande suivante :

w32tm /stripchart /computer:time.windows.com

J’ai remarqué que la différence de temps entre mon système et le NTP distant était très grande, quelque chose comme 10 secondes. Mais le plus dérangeant n’est pas la différence de temps, mais l’évolution de cette différence de temps.

Dépendant de la charge CPU de la machine virtuelle, la différence de temps devenait très grande au point d’attendre une seconde de retard en moins d’une minute. La machine “invitée” et l’hôte présentaient ce comportement. Comme Hyper-V Integration Services synchronise par défaut l’horloge de toutes les machines virtuelles avec celle de la machine hôte, cela implique de la charge d’une seule des machine influence l’horloge de toutes les autres. La machine hôte peut elle même influencer la cadence globale de l’horloge, puisqu’elle est elle-même virtualisée.


Essai d’explication du phénomène

En essayant de faire une explication éclairée, la source du temps utilisée par windows est le TSC du processeur (par l’utilisation de l’opcode RDTSC), qui est virtualisé. La préemption du CPU par les autres machines virtuelles semble avoir un impact négatif sur le compteur utilisé comme référence par windows.

Plus le CPU est préempté, plus le compteur dérive.

 

Corriger la dérive

Par défaut, le Service de Temps a un procédé “d’ajustement de phase” qui ralentis ou accélère la cadence l’horloge système pour qu’elle se rapproche de celle d’une source de temps fiable. Le compteur TSC sur le processeur physique est cadencé par le Quartz système (Si cela est toujours le cas). La dérive “normale” de ce genre de composant n’est généralement pas très important, et peut être lié à des facteurs externes tels que la température ambiante. Le service de temps peut gérer sans problème ce genre de dérive lente.

Mais la configuration par défaut semble ne pas être appropriée pour une source de temps qui dérive très rapidement et de manière imprévisible. Il faut donc augmenter la fréquence de l’opération d’ajustement de phase.

Modifier cela est relativement simple, le service de temps doit corriger plus fréquemment la cadence de l’horloge, afin de compenser la charge des machines virtuelles qui ralentissent la cadence de l’horloge.

Les paramètres par défaut dans Hyper-V Server R2 sont ceux d’une machine membre d’un domaine, et qui sont définies ici. La période de récupération du temps depuis une source fiable est beaucoup trop longue, 3600 secondes, considérant la vitesse de dérive de l’horloge.

Quelques paramètres doivent donc être ajustés dans la base de registre pour permettre à l’horloge de rester synchronisée :

  • Assigner la valeur 0x1 à SpecialInterval pour permettre l’utilisation de SpecialPollInterval.
  • Assigner 10 à SpecialPollInterval pour forcer la lecture de la source NTP toutes les 10 secondes.
  • Assigner 1 à MaxAllowedPhaseOffset to 1, pour définir la dérive maximale à une seconde, après quoi l’horloge sera assignée directement si la modification de la cadence de l’horloge ne fonctionne pas.

L’utilisation de ces paramètres ne permet pas d’avoir une horloge parfaitement stable, mais elle va au moins être corrigée rapidement.

Il semblerait qu’un paramètre caché du boot.ini pour Windows 2003, /USEPMTIMER, qui force windows à utiliser le timer ACPI et éviter ce genre de dérive. Je n’ai pas pu confirmer que ce paramètre a quelque sorte d’effet. Je n’ai pas non plus trouvé de méthode pour confirmer quel timer est utilisé par windows.

Publié jeudi 15 octobre 2009 00:13 par jay
Classé sous
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ jeudi 15 octobre 2009 13:52

la source du temps utilisée par windows est le TSC... par RDTSC...

NIET, l'heure est lue par une interruption sur le bios.

RDTSC ne sert qu'à lire des ticks CPU.

brunews

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ jeudi 15 octobre 2009 14:13

@Brunews, ce n'est pas si simple que ça. La preuve en est l'article dans la KB de microsoft. (http://support.microsoft.com/kb/895980) ou windows fait le choix de prendre le TSC ou le PM timer. Le BIOS doit faire office de référence au boot du PC, mais par la suite, ce n'est pas certain.

Je l'interprète comme cela à cause de cette phrase : "When the system first starts, it sets the system time to a value based on the real-time clock of the computer and then regularly updates the time."

http://msdn.microsoft.com/en-us/library/ms724961%28VS.85%29.aspx

jay

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ jeudi 15 octobre 2009 19:03

Ton 1er lien parle de QueryPerformanceCounter, aucun rapport avec l'heure system.

Pour ce qui est de l'heure, elle est toujours lue comme il se doit, c'est à dire en cas d'appel explicite à GetSystemTime (mappage de QuerySystemTime -> QuerySystemInformation).

Je n'ai par contre aucune idée de ce que font vos pseudo langaes.

Voici une démo qui fait un TXT de 100000 lignes de ce type:

1 TAB valeurFileTime

2 TAB valeurFileTime

etc...

Ce fichier s'ouvrira parfaitement dans Excel, on fait un tri sur la colonne du FileTime, on constate que tout est DEJA parfaitement croissant.

#include

<windows.h>

char* __fastcall bnuqwtoa(unsigned __int64 qwnum, char* szdst);

void AffoleCpu()

{

 SYSTEMTIME sttm;

 FILETIME ft;

 HANDLE hfl;

 UINT64 v;

 char buf[48], *c;

 DWORD n, rw;

 hfl = CreateFile("D:\\aa.txt", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);

 if(hfl == INVALID_HANDLE_VALUE) return;

 v = 0;

 n = 100000;

 do {

   GetSystemTime(&amp;sttm);

   SystemTimeToFileTime(&amp;sttm, &amp;ft);

   c = bnuqwtoa(++v, buf);

   *c++ = 9;

   c = bnuqwtoa(*((UINT64*) &amp;ft), c);

   *c = 13; *(c+1) = 10;

   WriteFile(hfl, buf, (DWORD) (c - buf + 2), &amp;rw, 0);

 } while(--n);

 CloseHandle(hfl);

}

#pragma comment(linker, "/entry:myWinMain")

void __fastcall myWinMain()

{

 AffoleCpu();

 ExitProcess(0);

}

brunews

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ jeudi 15 octobre 2009 19:28

Le "pseudo" language, comme tu le dis, fait appel à GetSystemTimeAsFileTime() une fonction interne du CLR de .NET, qui doit lui même appeler GetSystemTime, qui ressemble fortement à la fonction à laquelle tu fais appel.

En effet, le premier lien ne parles que du QueryPerformanceFrequency. Mais ce pourrait-il que Windows utilise ce compteur pour effectuer le calcul de l'heure courante après avoir récupéré l'heure du bios ?

Sinon, exécutes-tu ce code dans une VM Hyper-V ?

En ce qui concerne le résultat, oui, tout va être croissant tant que le Time Service ne va pas réajuster l'horloge. Lorsque tu exécutes ce code, lance en parallèle un monitoring via w32tm /stripchart.

Aussi, est-ce que la progression du temps est relativement linéaire dans tes tests ?

jay

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ jeudi 15 octobre 2009 19:36

Non, jamais utilisé de VM Hyper-V.

Par contre je bosse sur des moteurs de calculs qui tournent sur serveurs dédiés, mes modules mettent les CPU à fond pendant plusieurs minutes et plusieurs fois par jour, malgré cela l'horlore system de ces serveurs reste stable. On le constate sur les logs que j'écris pendant lees calculs, tout comme ici dans la démo.

brunews

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ jeudi 15 octobre 2009 19:49

Dans ce cas, ton code pourrait tout à fait poser problème sur un serveur virtualisé.

Le problème que je décris pourrait se produire sur une machine physique, mais avec une bien moins grande ampleur, quelques millisecondes par jour par exemple, a moins d'avoir un bug du bios qui fait que l'horloge dérive trop par rapport à l'heure réelle.

Sur un serveur physique la charge du CPU n'influe pas sur le TSC, a moins que comme sur les derniers i7 la fréquence du CPU soit augmentée pour overclocker temporairement un core.

jay

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ vendredi 16 octobre 2009 12:03

Il est bien connu qu'il ne faut surtout pas utiliser RDTSC pour construire une horloge (Me suis aussi fait avoir). La fréquence d'un processeur n'est en effet pas garantit : sur de nombreux PCs portables elle varie en permanence et peut se retrouver facilement divisée par 3 ou 4 juste parce qu'il n'y a trop de boulot, afin d'économiser l'énergie.

On peut justement utiliser RDTSC pour constater ces variations.

rt15

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ vendredi 16 octobre 2009 16:00

En effet, les technologies de type SpeedStep (qui sont aussi disponibles sur les version desktop des Core 2, mais souvent désactivées) posent ce genre de problème.

Dans le cas que je décris cependant, cette fonctionnalité du CPU est désactivée, ce n'est donc à priori pas lié...

jay

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ vendredi 16 octobre 2009 21:06

@brunews -&gt; Code de GetSystemTime (Vista, mais équivalent sous XP) :

mov     edi,edi

push    ebp

mov     ebp,esp

sub     esp,18h

mov     eax,dword ptr [SharedUserData+0x18 (7ffe0018)]

mov     ecx,dword ptr [SharedUserData+0x14 (7ffe0014)]

mov     edx,dword ptr [SharedUserData+0x1c (7ffe001c)]

cmp     eax,edx

jne     kernel32!GetSystemTime+0x77 (763f1887)

mov     dword ptr [ebp-4],eax

lea     eax,[ebp-18h]

push    eax

lea     eax,[ebp-8]

push    eax

mov     dword ptr [ebp-8],ecx

call    dword ptr [kernel32+0x10dc (763f10dc)]

Sachant qu'au debug on ne fait pas le jne, et que le call se fait sur RtlTimeToTimeFields qui ne fait que du formatage.

Conclusion, l'heure est simplement lu en mémoire : pas d'interrogation d'un quelconque timer. Cela a très probablement été fait ainsi pour obtenir des performances optimales.

Elle est donc mise à jour régulièrement, très probablement à chaque IRQ 0, avant que le scheduler ne choisisse un nouveau thread. Ces mises à jour se font sans interrogation de l'horloge RTC/CMOS (Celle qui est lu par le BIOS, et dispose d'une batterie).

Ces mises à jour sont par contre ajustées à intervalle régulier et par petites correction en se basant sur l'horloge RTC (On peut remplacer cet ajustement par un ajustement perso via SetSystemTimeAdjustment).

N'empêche, on devrait pouvoir déduire la fréquence des IRQ 0 en surveillant le compteur en mémoire... A tenter !

@jay -&gt; En y repensant, on s'est peut être déjà croisé en fait. DSO, ça te dit quelque chose ?

rt15

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ vendredi 16 octobre 2009 23:51

"la fréquence des IRQ 0 en surveillant le compteur en mémoire... A tenter !"

Mal tenté... Le compteur bouge milles fois par seconde. Doit être en milliseconde le bougre... Bilan même s'il est recalculé plus souvent, sa valeur ne bouge pas.

En tout cas, je sais pas comment la VM simule les IRQ 0, mais je serais pas surpris que ce soit l'origine du problème.

rt15

# re: Hyper-V, Charge CPU et Dérive de l’Horloge Système @ samedi 17 octobre 2009 23:43

Je ne vois rien ici qui pourrait remplir EDX:EAX avec une valeur inférieure à celle obtenue sur l'appel précédent comme il est question dans ce post.

Dommage que le lien "m'envoyer une alerte mail..." ne fonctionne pas sur ce blog.

brunews


Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01