Tag: KERNEL-2-2-18-PRE11-PATCH-6 Patch: nanosleep-4 From: Andrea Arcangeli Provide nanosleep usec resolution so that a signal flood doesn't hang glibc folks that correctly trust the rem field to resume the nanosleep after a syscall interruption. (without the patch nanosleep resolution is instead 10msec on IA32 and around 1msec on alpha) Index: linux/arch/alpha/kernel/time.c diff -u linux/arch/alpha/kernel/time.c:1.2 linux/arch/alpha/kernel/time.c:1.2.6.1 --- linux/arch/alpha/kernel/time.c:1.2 Wed Sep 13 08:32:22 2000 +++ linux/arch/alpha/kernel/time.c Wed Sep 27 23:55:48 2000 @@ -339,8 +339,22 @@ 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; } +static inline void +timeval_normalize(struct timeval * tv) +{ + time_t __sec; + + __sec = tv->tv_usec / 1000000; + if (__sec) + { + tv->tv_usec %= 1000000; + tv->tv_sec += __sec; + } +} + /* * Use the cycle counter to estimate an displacement from the last time * tick. Unfortunately the Alpha designers made only the low 32-bits of @@ -389,13 +403,11 @@ #endif usec += delta_usec; - if (usec >= 1000000) { - sec += 1; - usec -= 1000000; - } tv->tv_sec = sec; tv->tv_usec = usec; + + timeval_normalize(tv); } void Index: linux/arch/i386/kernel/time.c diff -u linux/arch/i386/kernel/time.c:1.2 linux/arch/i386/kernel/time.c:1.2.6.1 --- linux/arch/i386/kernel/time.c:1.2 Wed Sep 13 08:32:22 2000 +++ linux/arch/i386/kernel/time.c Wed Sep 27 23:55:48 2000 @@ -239,6 +239,20 @@ #endif +/* 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; + } +} + /* * This version of gettimeofday has microsecond resolution * and better than microsecond precision on fast x86 machines with TSC. @@ -259,13 +273,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) Index: linux/arch/ppc/kernel/time.c diff -u linux/arch/ppc/kernel/time.c:1.2 linux/arch/ppc/kernel/time.c:1.2.14.1 --- linux/arch/ppc/kernel/time.c:1.2 Thu Jul 6 22:41:19 2000 +++ linux/arch/ppc/kernel/time.c Wed Sep 27 23:55:48 2000 @@ -147,6 +147,19 @@ hardirq_exit(cpu); } +static inline void +timeval_normalize(struct timeval * tv) +{ + time_t __sec; + + __sec = tv->tv_usec / 1000000; + if (__sec) + { + tv->tv_usec %= 1000000; + tv->tv_sec += __sec; + } +} + /* * This version of gettimeofday has microsecond resolution. */ @@ -161,10 +174,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); } Index: linux/include/linux/time.h diff -u linux/include/linux/time.h:1.1 linux/include/linux/time.h:1.1.14.1 --- linux/include/linux/time.h:1.1 Thu Jul 6 22:04:59 2000 +++ linux/include/linux/time.h Wed Sep 27 23:55:48 2000 @@ -46,10 +46,53 @@ 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 */ }; + +/* 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 */ Index: linux/kernel/sched.c diff -u linux/kernel/sched.c:1.2.10.2 linux/kernel/sched.c:1.2.10.3 --- linux/kernel/sched.c:1.2.10.2 Wed Sep 27 23:55:16 2000 +++ linux/kernel/sched.c Wed Sep 27 23:55:48 2000 @@ -1946,6 +1946,7 @@ { struct timespec t; unsigned long expire; + struct timeval before, after; if(copy_from_user(&t, rqtp, sizeof(struct timespec))) return -EFAULT; @@ -1970,11 +1971,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; }