Oct 182012
 

Pour ceux qui font de la virtualisation avec Xen, il existe un bug assez pénible qui fait « shifter » le timestamp d’environ 3000000000 de nanoscondes (soit environ 50minutes) de temps en temps.
Ca semble dépendent d’un hardware (uniquement sur des machines ayant plusieurs CPU physiques avec des TSC indépendants avec des VM utilisant plus de VCPU qu’il n’y a de coeurs en tout et sous forte charge pour que le temps « stolen » accumulé de chaque VM soit non-négligeable. Je ne constate ce défaut qu’environ 1 fois tous les 15 jours. J’imagine que la chose est due à un débordement quelque part dans la façon dont l’hyperviseur gère la clocksource mais je n’ai pas trouvé de solution sur le net et je n’ai pas les compétences requises pour isoler et résoudre ce problème moi-même.

Voici donc un petit bout de code source pour faire un petit démon qui évite tout changement brutal du timestamps. Ca n’est pas une vraie solution car lorsque le problème se produit, l’heure système est effectivement fausse pendant une ou deux milisecondes. Mais vu la rareté des « shifts » et la brièveté du défaut, dans les faits cette erreur est très généralement sans aucune conséquences.

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

const int delay_us = 1000;
const double threshold = 1.0;
const bool debug = true;
FILE* debugstream = stderr;

void fprint_timestamp(FILE* stream, struct timeval * tv)
{   fprintf(stream, "%010d.%06d", (signed int)(tv->tv_sec) , (signed int)(tv->tv_usec) );
}

double tv2dbl(struct timeval * tv)
{   double sec=0;
    sec=tv->tv_usec;
    sec/=1000000;
    sec+=tv->tv_sec;
    return sec;
}

double tvDiff(struct timeval * a , struct timeval * b )
{   return tv2dbl(a) - tv2dbl(b);
}

void debug_shift(FILE* stream, struct timeval * curtime, struct timeval *oldtime )
{   fprintf(stream, "Le timestamp a augmenté de %lf ", tvDiff(curtime, oldtime) );
    fprintf(stream, "secondes depuis la derniere verification.\n" );
}

void settime(struct timeval * t)
{   if ( settimeofday(t, NULL) == -1 )
    {   fprintf(stderr, "Erreur : Impossible de régler l'heure. (êtes vous root?)\n");
        exit(1);
    }
}

void timehasshifted(struct timeval * curtime, struct timeval *oldtime )
{    
    if ( tvDiff(curtime, oldtime) <= threshold && tvDiff(curtime, oldtime) >= -threshold )
    {   if (debug) debug_shift(debugstream, curtime, oldtime);
        return;
    }
    debug_shift(stdout, curtime, oldtime);
    fprintf(stdout, "Remise du timestamp ");
    fprint_timestamp(stdout, curtime);
    fprintf(stdout, " à l'ancienne valeur de ");
    fprint_timestamp(stdout, oldtime);
    fprintf(stdout, "\n");
    settime(oldtime);
    *curtime=*oldtime;
    fprintf(stdout, "Timestamp remis à ");
    fprint_timestamp(stdout, oldtime);
    fprintf(stdout, "\n");
}

int main(void)
{
    struct timeval oldtime;
    struct timeval curtime;
    gettimeofday(&oldtime, NULL);
    while (true)
    {   
        usleep(delay_us);
        gettimeofday(&curtime, NULL);
        timehasshifted(&curtime, &oldtime);
        oldtime = curtime;
    }
}

Sorry, the comment form is closed at this time.