diff -urN ref/arch/alpha/kernel/time.c nanosleep/arch/alpha/kernel/time.c --- ref/arch/alpha/kernel/time.c Thu Feb 17 01:13:28 2000 +++ nanosleep/arch/alpha/kernel/time.c Thu Feb 17 01:15:37 2000 @@ -341,6 +341,8 @@ /* setup timer */ setup_irq(TIMER_IRQ, &timer_irqaction); + + do_get_fast_time = do_gettimeofday; } /* @@ -391,10 +393,8 @@ #endif usec += delta_usec; - if (usec >= 1000000) { - sec += 1; - usec -= 1000000; - } + + normalize_timeval(&sec, &usec); tv->tv_sec = sec; tv->tv_usec = usec; diff -urN ref/arch/i386/kernel/time.c nanosleep/arch/i386/kernel/time.c --- ref/arch/i386/kernel/time.c Thu Feb 17 01:13:28 2000 +++ nanosleep/arch/i386/kernel/time.c Thu Feb 17 01:15:37 2000 @@ -253,10 +253,7 @@ usec += xtime.tv_usec; read_unlock_irqrestore(&xtime_lock, flags); - while (usec >= 1000000) { - usec -= 1000000; - sec++; - } + normalize_timeval(&sec, &usec); tv->tv_sec = sec; tv->tv_usec = usec; diff -urN ref/arch/ppc/kernel/time.c nanosleep/arch/ppc/kernel/time.c --- ref/arch/ppc/kernel/time.c Thu Feb 17 01:13:28 2000 +++ nanosleep/arch/ppc/kernel/time.c Thu Feb 17 01:15:37 2000 @@ -158,10 +158,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++; - } + normalize_timeval(&tv->tv_sec, &tv->tv_usec); #endif restore_flags(flags); } diff -urN ref/include/linux/time.h nanosleep/include/linux/time.h --- ref/include/linux/time.h Thu Feb 17 01:13:28 2000 +++ nanosleep/include/linux/time.h Thu Feb 17 01:15:37 2000 @@ -46,10 +46,66 @@ 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 */ }; + +static inline void +normalize_timeval(time_t * sec, suseconds_t * usec) +{ + time_t __sec; + + __sec = *usec / 1000000; + if (__sec) + { + *usec %= 1000000; + *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 ref/kernel/timer.c nanosleep/kernel/timer.c --- ref/kernel/timer.c Thu Feb 17 01:13:28 2000 +++ nanosleep/kernel/timer.c Thu Feb 17 01:15:37 2000 @@ -807,6 +807,7 @@ { struct timespec t; unsigned long expire; + struct timeval before, after; if(copy_from_user(&t, rqtp, sizeof(struct timespec))) return -EFAULT; @@ -831,11 +832,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; }