Re: [PATCH] [RFC] Potential fix for leapsecond caused futex relatedload spikes

From: John Stultz
Date: Sun Jul 01 2012 - 05:45:12 EST


On 07/01/2012 02:36 AM, John Stultz wrote:
As widely reported on the internet today, some Linux systems after
the leapsecond was inserted are experiencing futex related load
spikes (usually connected to MySQL, Firefox, Thunderbird, Java, etc).

An apparent for this issue workaround is running:
$ date -s "`date`"

Credit: http://www.sheeri.com/content/mysql-and-leap-second-high-cpu-and-fix

I believe this issue is due to the leapsecond being added without
calling clock_was_set() to notify the hrtimer subsystem of the
change. (Although I've not yet chased all the way down to the
hrtimer code to validate exactly what's going on there).

The workaround functions as it forces a clock_was_set()
call from settimeofday().

This fix adds some extra logic to track when a leapsecond
is added from update_wall_time() and calls clock_was_set()
once the timekeeper.lock is released.

I've been able to reproduce the load spike using Thunderbird
when triggering a leap second and with this patch the issue
did not crop up.

Also, attached is the test case I've been using to trigger leapseconds, in case anyone else is interested in trying to either test this fix or help reproduce the reported hard hangs.

To build:
gcc leaptest.c -o leaptest -lrt

thanks
-john



/* Leap second test
* by: john stultz (johnstul@xxxxxxxxxx)
* (C) Copyright IBM 2012
* Licensed under the GPL
*/


#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <sys/timex.h>


#define CALLS_PER_LOOP 64
#define NSEC_PER_SEC 1000000000ULL

/* returns 1 if a <= b, 0 otherwise */
static inline int in_order(struct timespec a, struct timespec b)
{
if(a.tv_sec < b.tv_sec)
return 1;
if(a.tv_sec > b.tv_sec)
return 0;
if(a.tv_nsec > b.tv_nsec)
return 0;
return 1;
}


int main(void)
{
struct timeval tv;
struct timex tx;
struct timespec list[CALLS_PER_LOOP];
int i, inconsistent;
int clock_type = CLOCK_REALTIME;
long now, then;

/* Get the current time */
gettimeofday(&tv, NULL);

/* Calculate the next leap second */
tv.tv_sec += 86400 - tv.tv_sec % 86400;

/* Set the time to be 10 seconds from that time */
tv.tv_sec -= 10;
settimeofday(&tv, NULL);

/* Set the leap second insert flag */
tx.modes = ADJ_STATUS;
tx.status = STA_INS;
adjtimex(&tx);

clock_gettime(clock_type, &list[0]);
now = then = list[0].tv_sec;
while(now - then < 30){
inconsistent = 0;

/* Fill list */
for(i=0; i < CALLS_PER_LOOP; i++)
clock_gettime(clock_type, &list[i]);

/* Check for inconsistencies */
for(i=0; i < CALLS_PER_LOOP-1; i++)
if(!in_order(list[i],list[i+1]))
inconsistent = i;

/* display inconsistency */
if(inconsistent){
unsigned long long delta;
for(i=0; i < CALLS_PER_LOOP; i++){
if(i == inconsistent)
printf("--------------------\n");
printf("%lu:%lu\n",list[i].tv_sec,
list[i].tv_nsec);
if(i == inconsistent + 1 )
printf("--------------------\n");
}
delta = list[inconsistent].tv_sec*NSEC_PER_SEC;
delta += list[inconsistent].tv_nsec;
delta -= list[inconsistent+1].tv_sec*NSEC_PER_SEC;
delta -= list[inconsistent+1].tv_nsec;
printf("Delta: %llu ns\n", delta);
fflush(0);
break;
}
now = list[0].tv_sec;
}

/* clear TIME_WAIT */
tx.modes = ADJ_STATUS;
tx.status = 0;
adjtimex(&tx);

return 0;
}