Commit 0ebcfd99 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix jiffies_to_time[spec | val] and converse to use

From: george anzinger <george@mvista.com>

In the current system (2.5.67) time_spec to jiffies, time_val to
jiffies and the converse (jiffies to time_val and jiffies to
time_spec) all use 1/HZ as the measure of a jiffie.  Because of the
inability of the PIT to actually generate an accurate 1/HZ interrupt,
the wall clock is updated with a more accurate value (999848
nanoseconds per jiffie for HZ = 1000).  This causes a 1/HZ
interpretation of jiffies based timing to run faster than the wall
clock, thus causing sleeps and timers to expire short of the requested
time.  Try, for example:

time sleep 60

This patch changes the conversion routines to use the same value as
the wall clock update code to do the conversions.

The actual math is almost all done at compile time.  The run time
conversions require little if any more execution time.

This patch must be applied after the patch I posted earlier today
which fixed the CLOCK_MONOTONIC resolution issue.
parent 2f98681f
......@@ -14,4 +14,22 @@
__mod; \
})
/*
* (long)X = ((long long)divs) / (long)div
* (long)rem = ((long long)divs) % (long)div
*
* Warning, this will do an exception if X overflows.
*/
#define div_long_long_rem(a,b,c) div_ll_X_l_rem(a,b,c)
extern inline long
div_ll_X_l_rem(long long divs, long div, long *rem)
{
long dum2;
__asm__("divl %2":"=a"(dum2), "=d"(*rem)
: "rm"(div), "A"(divs));
return dum2;
}
#endif
......@@ -26,6 +26,16 @@ struct timezone {
#include <linux/spinlock.h>
#include <linux/seqlock.h>
#include <linux/timex.h>
#include <asm/div64.h>
#ifndef div_long_long_rem
#define div_long_long_rem(dividend,divisor,remainder) ({ \
u64 result = dividend; \
*remainder = do_div(result,divisor); \
result; })
#endif
/*
* Have the 32 bit jiffies value wrap 5 minutes after boot
......@@ -59,25 +69,52 @@ struct timezone {
#ifndef NSEC_PER_USEC
#define NSEC_PER_USEC (1000L)
#endif
/*
* We want to do realistic conversions of time so we need to use the same
* values the update wall clock code uses as the jiffie size. This value
* is: TICK_NSEC(TICK_USEC) (both of which are defined in timex.h). This
* is a constant and is in nanoseconds. We will used scaled math and
* with a scales defined here as SEC_JIFFIE_SC, USEC_JIFFIE_SC and
* NSEC_JIFFIE_SC. Note that these defines contain nothing but
* constants and so are computed at compile time. SHIFT_HZ (computed in
* timex.h) adjusts the scaling for different HZ values.
*/
#define SEC_JIFFIE_SC (30 - SHIFT_HZ)
#define NSEC_JIFFIE_SC (SEC_JIFFIE_SC + 30)
#define USEC_JIFFIE_SC (SEC_JIFFIE_SC + 20)
#define SEC_CONVERSION ((unsigned long)(((u64)NSEC_PER_SEC << SEC_JIFFIE_SC) /\
(u64)TICK_NSEC(TICK_USEC)))
#define NSEC_CONVERSION ((unsigned long)(((u64)1 << NSEC_JIFFIE_SC) / \
(u64)TICK_NSEC(TICK_USEC)))
#define USEC_CONVERSION \
((unsigned long)(((u64)NSEC_PER_USEC << USEC_JIFFIE_SC)/ \
(u64)TICK_NSEC(TICK_USEC)))
#define MAX_SEC_IN_JIFFIES \
(u32)((u64)((u64)MAX_JIFFY_OFFSET * TICK_NSEC(TICK_USEC)) / NSEC_PER_SEC)
static __inline__ unsigned long
timespec_to_jiffies(struct timespec *value)
{
unsigned long sec = value->tv_sec;
long nsec = value->tv_nsec;
long nsec = value->tv_nsec + TICK_NSEC(TICK_USEC) - 1;
if (sec >= (MAX_JIFFY_OFFSET / HZ))
if (sec >= MAX_SEC_IN_JIFFIES)
return MAX_JIFFY_OFFSET;
nsec += 1000000000L / HZ - 1;
nsec /= 1000000000L / HZ;
return HZ * sec + nsec;
return (((u64)sec * SEC_CONVERSION) +
(((u64)nsec * NSEC_CONVERSION) >>
(NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
}
static __inline__ void
jiffies_to_timespec(unsigned long jiffies, struct timespec *value)
{
value->tv_nsec = (jiffies % HZ) * (1000000000L / HZ);
value->tv_sec = jiffies / HZ;
/*
* Convert jiffies to nanoseconds and seperate with
* one divide.
*/
u64 nsec = (u64)jiffies * TICK_NSEC(TICK_USEC);
value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_nsec);
}
/* Same for "timeval" */
......@@ -85,20 +122,25 @@ static __inline__ unsigned long
timeval_to_jiffies(struct timeval *value)
{
unsigned long sec = value->tv_sec;
long usec = value->tv_usec;
long usec = value->tv_usec + USEC_PER_SEC / HZ - 1;
if (sec >= (MAX_JIFFY_OFFSET / HZ))
if (sec >= MAX_SEC_IN_JIFFIES)
return MAX_JIFFY_OFFSET;
usec += 1000000L / HZ - 1;
usec /= 1000000L / HZ;
return HZ * sec + usec;
return (((u64)sec * SEC_CONVERSION) +
(((u64)usec * USEC_CONVERSION) >>
(USEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
}
static __inline__ void
jiffies_to_timeval(unsigned long jiffies, struct timeval *value)
{
value->tv_usec = (jiffies % HZ) * (1000000L / HZ);
value->tv_sec = jiffies / HZ;
/*
* Convert jiffies to nanoseconds and seperate with
* one divide.
*/
u64 nsec = (u64)jiffies * TICK_NSEC(TICK_USEC);
value->tv_sec = div_long_long_rem(nsec, NSEC_PER_SEC, &value->tv_usec);
value->tv_usec /= NSEC_PER_USEC;
}
static __inline__ int timespec_equal(struct timespec *a, struct timespec *b)
......
......@@ -51,7 +51,6 @@
#ifndef _LINUX_TIMEX_H
#define _LINUX_TIMEX_H
#include <linux/time.h>
#include <asm/param.h>
/*
......@@ -177,6 +176,7 @@
/* a value TUSEC for TICK_USEC (can be set bij adjtimex) */
#define TICK_NSEC(TUSEC) (SH_DIV (TUSEC * USER_HZ * 1000, ACTHZ, 8))
#include <linux/time.h>
/*
* syscall interface - used (mainly by NTP daemon)
* to discipline kernel clock oscillator
......
......@@ -33,7 +33,12 @@
result; })
#endif
#define CLOCK_REALTIME_RES TICK_NSEC(TICK_USEC) // In nano seconds.
static inline u64 mpy_l_X_l_ll(unsigned long mpy1,unsigned long mpy2)
{
return (u64)mpy1 * mpy2;
}
/*
* Management arrays for POSIX timers. Timers are kept in slab memory
* Timer ids are allocated by an external routine that keeps track of the
......@@ -175,8 +180,8 @@ static inline void unlock_timer(struct k_itimer *timr, unsigned long flags);
*/
static __init int init_posix_timers(void)
{
struct k_clock clock_realtime = {.res = NSEC_PER_SEC / HZ };
struct k_clock clock_monotonic = {.res = NSEC_PER_SEC / HZ,
struct k_clock clock_realtime = {.res = CLOCK_REALTIME_RES };
struct k_clock clock_monotonic = {.res = CLOCK_REALTIME_RES,
.clock_get = do_posix_clock_monotonic_gettime,
.clock_set = do_posix_clock_monotonic_settime
};
......@@ -204,24 +209,14 @@ static void tstojiffie(struct timespec *tp, int res, u64 *jiff)
}
/*
* A note on jiffy overflow: It is possible for the system to
* have been up long enough for the jiffies quanity to overflow.
* In order for correct timer evaluations we require that the
* specified time be somewhere between now and now + (max
* unsigned int/2). Times beyond this will be truncated back to
* this value. This is done in the absolute adjustment code,
* below. Here it is enough to just discard the high order
* bits.
*/
*jiff = (s64)sec * HZ;
/*
* Do the res thing. (Don't forget the add in the declaration of nsec)
*/
nsec -= nsec % res;
/*
* Split to jiffie and sub jiffie
*/
*jiff += nsec / (NSEC_PER_SEC / HZ);
* The scaling constants are defined in <linux/time.h>
* The difference between there and here is that we do the
* res rounding and compute a 64-bit result (well so does that
* but it then throws away the high bits).
*/
*jiff = (mpy_l_X_l_ll(sec, SEC_CONVERSION) +
(mpy_l_X_l_ll(nsec, NSEC_CONVERSION) >>
(NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
}
static void schedule_next_timer(struct k_itimer *timr)
......@@ -1232,7 +1227,6 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave)
finish_wait(&nanosleep_abs_wqueue, &abs_wqueue);
if (left > (s64)0) {
unsigned long rmd;
/*
* Always restart abs calls from scratch to pick up any
......@@ -1241,9 +1235,10 @@ do_clock_nanosleep(clockid_t which_clock, int flags, struct timespec *tsave)
if (abs)
return -ERESTARTNOHAND;
tsave->tv_sec = div_long_long_rem(left, HZ, &rmd);
tsave->tv_nsec = rmd * (NSEC_PER_SEC / HZ);
left *= TICK_NSEC(TICK_USEC);
tsave->tv_sec = div_long_long_rem(left,
NSEC_PER_SEC,
&tsave->tv_nsec);
restart_block->fn = clock_nanosleep_restart;
restart_block->arg0 = which_clock;
restart_block->arg1 = (unsigned long)tsave;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment