diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -556,60 +556,39 @@ } /* - * Compute number of ticks in the specified amount of time. + * Compute number of ticks representing the specified amount of time. + * If the specified time is negative, a value of 1 is returned. This + * function returns a value from 1 up to and including INT_MAX. */ int tvtohz(struct timeval *tv) { - unsigned long ticks; - long sec, usec; + int64_t retval; + + /* range check tv_sec so the multiplication below doesn't overflow */ + if (__predict_false(tv->tv_sec > INT_MAX)) + return (INT_MAX); + else if (__predict_false(tv->tv_sec < 0)) + return (1); /* - * If the number of usecs in the whole seconds part of the time - * difference fits in a long, then the total number of usecs will - * fit in an unsigned long. Compute the total and convert it to - * ticks, rounding up and adding 1 to allow for the current tick - * to expire. Rounding also depends on unsigned long arithmetic - * to avoid overflow. - * - * Otherwise, if the number of ticks in the whole seconds part of - * the time difference fits in a long, then convert the parts to - * ticks separately and add, using similar rounding methods and - * overflow avoidance. This method would work in the previous - * case but it is slightly slower and assumes that hz is integral. - * - * Otherwise, round the time difference down to the maximum - * representable value. - * - * If ints have 32 bits, then the maximum value for any timeout in - * 10ms ticks is 248 days. + * Convert to ticks. The cast to 32-bit tells the compiler on + * 32-bit platforms it can use a regular 32-bit multiplication + * with 64-bit result as an optimization, instead of a full + * 64-bit multiplication. */ - sec = tv->tv_sec; - usec = tv->tv_usec; - if (usec < 0) { - sec--; - usec += 1000000; - } - if (sec < 0) { -#ifdef DIAGNOSTIC - if (usec > 0) { - sec++; - usec -= 1000000; - } - printf("tvotohz: negative time difference %ld sec %ld usec\n", - sec, usec); -#endif - ticks = 1; - } else if (sec <= LONG_MAX / 1000000) - ticks = howmany(sec * 1000000 + (unsigned long)usec, tick) + 1; - else if (sec <= LONG_MAX / hz) - ticks = sec * hz - + howmany((unsigned long)usec, tick) + 1; - else - ticks = LONG_MAX; - if (ticks > INT_MAX) - ticks = INT_MAX; - return ((int)ticks); + retval = + (int64_t)(int32_t)tv->tv_sec * hz + + (int64_t)(int32_t)tv->tv_usec * hz / 1000000; + + /* range check */ + if (__predict_false(retval > INT_MAX - 1)) + return (INT_MAX); + else if (__predict_false(retval < 0)) + return (1); + + /* add one additional tick */ + return ((int)retval + 1); } /*