Commit 2f98681f authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Fix POSIX timers to give CLOCK_MONOTONIC full

The POSIX CLOCK_MONOTONIC currently has only 1/HZ resolution.  Further, it is
tied to jiffies (i.e.  is a restatment of jiffies) rather than "xtime" or the
gettimeofday() clock.

This patch changes CLOCK_MONOTONIC to be a restatment of gettimeofday() plus
an offset to remove any clock setting activity from CLOCK_MONOTONIC.  An
offset is kept that represents the difference between CLOCK_MONOTONIC and
gettimeofday().  This offset is updated when ever the gettimeofday() clock is
set to back the clock setting change out of CLOCK_MONOTONIC (which by the
standard, can not be set).

With this change CLOCK_REALTIME (a direct restatement of gettimeofday()),
CLOCK_MONOTONIC and gettimeofday() will all tick at the same time and with
the same rate.  And all will be affected by NTP adjustments (save those which
actually set the time).
parent 0e3efbd1
...@@ -124,15 +124,28 @@ void do_settimeofday(struct timeval *tv) ...@@ -124,15 +124,28 @@ void do_settimeofday(struct timeval *tv)
* made, and then undo it! * made, and then undo it!
*/ */
tv->tv_usec -= timer->get_offset(); tv->tv_usec -= timer->get_offset();
tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ); tv->tv_usec -= (jiffies - wall_jiffies) * (USEC_PER_SEC / HZ);
while (tv->tv_usec < 0) { while (tv->tv_usec < 0) {
tv->tv_usec += 1000000; tv->tv_usec += USEC_PER_SEC;
tv->tv_sec--; tv->tv_sec--;
} }
tv->tv_usec *= NSEC_PER_USEC;
wall_to_monotonic.tv_sec += xtime.tv_sec - tv->tv_sec;
wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_usec;
if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) {
wall_to_monotonic.tv_nsec -= NSEC_PER_SEC;
wall_to_monotonic.tv_sec++;
}
if (wall_to_monotonic.tv_nsec < 0) {
wall_to_monotonic.tv_nsec += NSEC_PER_SEC;
wall_to_monotonic.tv_sec--;
}
xtime.tv_sec = tv->tv_sec; xtime.tv_sec = tv->tv_sec;
xtime.tv_nsec = (tv->tv_usec * 1000); xtime.tv_nsec = tv->tv_usec;
time_adjust = 0; /* stop active adjtime() */ time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC; time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
...@@ -322,7 +335,9 @@ void __init time_init(void) ...@@ -322,7 +335,9 @@ void __init time_init(void)
{ {
xtime.tv_sec = get_cmos_time(); xtime.tv_sec = get_cmos_time();
xtime.tv_nsec = 0; wall_to_monotonic.tv_sec = -xtime.tv_sec + INITIAL_JIFFIES / HZ;
xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
wall_to_monotonic.tv_nsec = 0;
timer = select_timer(); timer = select_timer();
......
...@@ -140,6 +140,7 @@ mktime (unsigned int year, unsigned int mon, ...@@ -140,6 +140,7 @@ mktime (unsigned int year, unsigned int mon,
} }
extern struct timespec xtime; extern struct timespec xtime;
extern struct timespec wall_to_monotonic;
extern seqlock_t xtime_lock; extern seqlock_t xtime_lock;
static inline unsigned long get_seconds(void) static inline unsigned long get_seconds(void)
...@@ -200,6 +201,9 @@ struct itimerval { ...@@ -200,6 +201,9 @@ struct itimerval {
#define CLOCK_MONOTONIC_HR 5 #define CLOCK_MONOTONIC_HR 5
#define MAX_CLOCKS 6 #define MAX_CLOCKS 6
#define CLOCKS_MASK (CLOCK_REALTIME | CLOCK_MONOTONIC | \
CLOCK_REALTIME_HR | CLOCK_MONOTONIC_HR)
#define CLOCKS_MONO (CLOCK_MONOTONIC & CLOCK_MONOTONIC_HR)
/* /*
* The various flags for setting POSIX.1b interval timers. * The various flags for setting POSIX.1b interval timers.
......
This diff is collapsed.
...@@ -441,8 +441,16 @@ static inline void __run_timers(tvec_base_t *base) ...@@ -441,8 +441,16 @@ static inline void __run_timers(tvec_base_t *base)
unsigned long tick_usec = TICK_USEC; /* ACTHZ period (usec) */ unsigned long tick_usec = TICK_USEC; /* ACTHZ period (usec) */
unsigned long tick_nsec = TICK_NSEC(TICK_USEC); /* USER_HZ period (nsec) */ unsigned long tick_nsec = TICK_NSEC(TICK_USEC); /* USER_HZ period (nsec) */
/* The current time */ /*
* The current time
* wall_to_monotonic is what we need to add to xtime (or xtime corrected
* for sub jiffie times) to get to monotonic time. Monotonic is pegged at zero
* at zero at system boot time, so wall_to_monotonic will be negative,
* however, we will ALWAYS keep the tv_nsec part positive so we can use
* the usual normalization.
*/
struct timespec xtime __attribute__ ((aligned (16))); struct timespec xtime __attribute__ ((aligned (16)));
struct timespec wall_to_monotonic __attribute__ ((aligned (16)));
/* Don't completely fail for HZ > 500. */ /* Don't completely fail for HZ > 500. */
int tickadj = 500/HZ ? : 1; /* microsecs */ int tickadj = 500/HZ ? : 1; /* microsecs */
...@@ -508,6 +516,7 @@ static void second_overflow(void) ...@@ -508,6 +516,7 @@ static void second_overflow(void)
case TIME_INS: case TIME_INS:
if (xtime.tv_sec % 86400 == 0) { if (xtime.tv_sec % 86400 == 0) {
xtime.tv_sec--; xtime.tv_sec--;
wall_to_monotonic.tv_sec++;
time_state = TIME_OOP; time_state = TIME_OOP;
clock_was_set(); clock_was_set();
printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n"); printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n");
...@@ -517,6 +526,7 @@ static void second_overflow(void) ...@@ -517,6 +526,7 @@ static void second_overflow(void)
case TIME_DEL: case TIME_DEL:
if ((xtime.tv_sec + 1) % 86400 == 0) { if ((xtime.tv_sec + 1) % 86400 == 0) {
xtime.tv_sec++; xtime.tv_sec++;
wall_to_monotonic.tv_sec--;
time_state = TIME_WAIT; time_state = TIME_WAIT;
clock_was_set(); clock_was_set();
printk(KERN_NOTICE "Clock: deleting leap second 23:59:59 UTC\n"); printk(KERN_NOTICE "Clock: deleting leap second 23:59:59 UTC\n");
......
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