diff -urN 2.2.14/arch/alpha/kernel/time.c nanosleep-3/arch/alpha/kernel/time.c --- 2.2.14/arch/alpha/kernel/time.c Sun Jan 2 18:26:32 2000 +++ nanosleep-3/arch/alpha/kernel/time.c Thu Feb 24 00:18:42 2000 @@ -326,6 +326,7 @@ irq_handler = timer_interrupt; if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL)) panic("Could not allocate timer IRQ!"); + do_get_fast_time = do_gettimeofday; } /* @@ -376,13 +377,11 @@ #endif usec += delta_usec; - if (usec >= 1000000) { - sec += 1; - usec -= 1000000; - } tv->tv_sec = sec; tv->tv_usec = usec; + + timeval_normalize(tv); } void diff -urN 2.2.14/arch/i386/kernel/time.c nanosleep-3/arch/i386/kernel/time.c --- 2.2.14/arch/i386/kernel/time.c Mon Jan 17 16:44:33 2000 +++ nanosleep-3/arch/i386/kernel/time.c Thu Feb 24 00:19:02 2000 @@ -252,13 +252,10 @@ usec += xtime.tv_usec; read_unlock_irqrestore(&xtime_lock, flags); - while (usec >= 1000000) { - usec -= 1000000; - sec++; - } - tv->tv_sec = sec; tv->tv_usec = usec; + + timeval_normalize(tv); } void do_settimeofday(struct timeval *tv) diff -urN 2.2.14/arch/ppc/kernel/time.c nanosleep-3/arch/ppc/kernel/time.c --- 2.2.14/arch/ppc/kernel/time.c Mon Jan 17 16:44:35 2000 +++ nanosleep-3/arch/ppc/kernel/time.c Thu Feb 24 00:19:14 2000 @@ -153,10 +153,7 @@ #ifndef __SMP__ tv->tv_usec += (decrementer_count - get_dec()) * count_period_num / count_period_den; - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; - } + timeval_normalize(tv); #endif restore_flags(flags); } diff -urN 2.2.14/include/linux/time.h nanosleep-3/include/linux/time.h --- 2.2.14/include/linux/time.h Tue Feb 1 18:24:19 2000 +++ nanosleep-3/include/linux/time.h Thu Feb 24 00:19:25 2000 @@ -46,10 +46,67 @@ value->tv_sec = jiffies / HZ; } +static __inline__ int +timespec_before(struct timespec a, struct timespec b) +{ + if (a.tv_sec == b.tv_sec) + return a.tv_nsec < b.tv_nsec; + return a.tv_sec < b.tv_sec; +} + +/* computes `a - b' and write the result in `result', assumes `a >= b' */ +static inline void +timespec_less(struct timespec a, struct timespec b, struct timespec * result) +{ + if (a.tv_nsec < b.tv_nsec) + { + a.tv_sec--; + a.tv_nsec += 1000000000; + } + + result->tv_sec = a.tv_sec - b.tv_sec; + result->tv_nsec = a.tv_nsec - b.tv_nsec; +} + struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ }; + +/* FIXME: should be inline but gcc is buggy and breaks */ +static void +timeval_normalize(struct timeval * tv) +{ + time_t __sec; + + __sec = tv->tv_usec / 1000000; + if (__sec) + { + tv->tv_usec %= 1000000; + tv->tv_sec += __sec; + } +} + +/* computes `a - b' and write the result in `result', assumes `a >= b' */ +static inline void +timeval_less(struct timeval a, struct timeval b, struct timeval * result) +{ + if (a.tv_usec < b.tv_usec) + { + a.tv_sec--; + a.tv_usec += 1000000; + } + + result->tv_sec = a.tv_sec - b.tv_sec; + result->tv_usec = a.tv_usec - b.tv_usec; +} + +static __inline__ void +timeval_to_timespec(struct timeval tv, struct timespec * ts) +{ + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = (long) tv.tv_usec * 1000; +} struct timezone { int tz_minuteswest; /* minutes west of Greenwich */ diff -urN 2.2.14/kernel/sched.c nanosleep-3/kernel/sched.c --- 2.2.14/kernel/sched.c Wed Jan 5 14:16:56 2000 +++ nanosleep-3/kernel/sched.c Thu Feb 24 00:16:38 2000 @@ -1904,6 +1904,7 @@ { struct timespec t; unsigned long expire; + struct timeval before, after; if(copy_from_user(&t, rqtp, sizeof(struct timespec))) return -EFAULT; @@ -1928,11 +1929,20 @@ expire = timespec_to_jiffies(&t) + (t.tv_sec || t.tv_nsec); current->state = TASK_INTERRUPTIBLE; + get_fast_time(&before); expire = schedule_timeout(expire); + get_fast_time(&after); if (expire) { if (rmtp) { - jiffies_to_timespec(expire, &t); + struct timespec elapsed; + + timeval_less(after, before, &after); + timeval_to_timespec(after, &elapsed); + if (timespec_before(elapsed, t)) + timespec_less(t, elapsed, &t); + else + t.tv_nsec = t.tv_sec = 0; if (copy_to_user(rmtp, &t, sizeof(struct timespec))) return -EFAULT; }