Subject: race condition in i8254_microtime() from i386/i386/microtime.S
To: None <port-i386@netbsd.org>
From: Erik E. Fair <fair@netbsd.org>
List: port-i386
Date: 06/30/2005 00:56:04
I've been observing a race condition in an i386 box I have here that has a
broken TSC (and therefore must not use cc_microtime()), and shows these
forward/backward jumps of one tick (10,000 us, 10ms) probably due to a race
condition that I don't understand in i8254_microtime().

I'd like those of you who also have systems that are old enough not to have
TSC (see the CPU features list in the CPU probe at boot time), or who have
compiled your kernel with NO_TSC_TIME option to run this program and let me
know if it prints anything.

It will beat up your computer during the period in which it runs; it's
continuously calling gettimeofday(2), which calls microtime(), which is (in
the i386) a macroized function pointer to either of cc_microtime() or
i8254_microtime(). The point is to look for backward time jumps, and print
out a range of samples around the event.

Run for a couple of hours while your system is doing stuff. I was able to
consistently tickle the race by running /usr/games/rain on a vga/wscons window,
or cat /usr/share/dict/words to a vga/wscons.

	Erik <fair@clock.org>

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
#include <assert.h>

#define	SAMPLES	8	/* a power of 2 */

struct timeval tv[SAMPLES];

#define	INDX(x)		((x) & (SAMPLES - 1))

#define	BINDX1(x)	INDX(((x) - ((SAMPLES/2) - 1)))
#define	BINDX2(x)	INDX(((x) - (SAMPLES/2)))

#define	B1SEC(x)	(tv[BINDX1(x)].tv_sec)
#define	B2SEC(x)	(tv[BINDX2(x)].tv_sec)

#define	B1uSEC(x)	(tv[BINDX1(x)].tv_usec)
#define	B2uSEC(x)	(tv[BINDX2(x)].tv_usec)
	
int main(void) {
	unsigned int iterations = 0;
	unsigned int j;

	for(;;) {
		gettimeofday(&tv[INDX(iterations)], NULL);

		if (iterations <= (SAMPLES/2)) {
			iterations++;
			continue;
		}

		if (B1SEC(iterations) > B2SEC(iterations)) {
			iterations++;
			continue;
		}

		if (B1SEC(iterations) == B2SEC(iterations) &&
			B1uSEC(iterations) >= B2uSEC(iterations))
		{
			iterations++;
			continue;
		}

		/* N samples, N minus one reports */
		for(j = (iterations - (SAMPLES - 2)); j <= iterations; j++) {
			printf("%d iterations, %ld sec diff, %ld usec diff\n", j,
				(tv[INDX(j)].tv_sec - tv[INDX(j - 1)].tv_sec), 
				(tv[INDX(j)].tv_usec - tv[INDX(j - 1)].tv_usec));
		}
		puts("");	/* cheap \n */

		iterations++;
	}
}