Commit b9cebc5d authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Some clean up of the time code.

From: george anzinger <george@mvista.com>

This patch does the following:

Pushs down the change from timeval to timespec in the settime routines.

Fixes two places where time was set without updating the monotonic clock
offset.  (Changes sys_stime() to call do_settimeofday() and changes
clock_warp to do the update directly.) These were bugs!

Changes the uptime code to use the posix_clock_monotonic notion of uptime
instead of the jiffies.  This time will track NTP changes and so should be
better than your standard wristwatch (if your using ntp).

Changes posix_clock_monotonic to start at 0 on boot (was set to start at
initial jiffies).

Fixes a bug (never experienced) in timer_create() in posix-timers.c where
we "could" have released timer_id 0 if "id resources" were low.

Adds a test in do_settimeofday() to error out (EINVAL) attempts to use
unnormalized times.  This is passed back up to both settimeofday and
posix_setclock().

Warning: Requires changes in .../arch/???/kernel/time.c to change
do_settimeofday() to return an error if time is not normalized and to use a
timespec instead of timeval for its input.
parent 90ea34be
...@@ -114,8 +114,11 @@ void do_gettimeofday(struct timeval *tv) ...@@ -114,8 +114,11 @@ void do_gettimeofday(struct timeval *tv)
tv->tv_usec = usec; tv->tv_usec = usec;
} }
void do_settimeofday(struct timeval *tv) int do_settimeofday(struct timespec *tv)
{ {
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
write_seqlock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* /*
* This is revolting. We need to set "xtime" correctly. However, the * This is revolting. We need to set "xtime" correctly. However, the
...@@ -123,17 +126,16 @@ void do_settimeofday(struct timeval *tv) ...@@ -123,17 +126,16 @@ void do_settimeofday(struct timeval *tv)
* wall time. Discover what correction gettimeofday() would have * wall time. Discover what correction gettimeofday() would have
* made, and then undo it! * made, and then undo it!
*/ */
tv->tv_usec -= timer->get_offset(); tv->tv_nsec -= timer->get_offset() * NSEC_PER_USEC;
tv->tv_usec -= (jiffies - wall_jiffies) * (USEC_PER_SEC / HZ); tv->tv_nsec -= (jiffies - wall_jiffies) * TICK_NSEC(TICK_USEC);
while (tv->tv_usec < 0) { while (tv->tv_nsec < 0) {
tv->tv_usec += USEC_PER_SEC; tv->tv_nsec += NSEC_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_sec += xtime.tv_sec - tv->tv_sec;
wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_usec; wall_to_monotonic.tv_nsec += xtime.tv_nsec - tv->tv_nsec;
if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) { if (wall_to_monotonic.tv_nsec > NSEC_PER_SEC) {
wall_to_monotonic.tv_nsec -= NSEC_PER_SEC; wall_to_monotonic.tv_nsec -= NSEC_PER_SEC;
...@@ -145,13 +147,14 @@ void do_settimeofday(struct timeval *tv) ...@@ -145,13 +147,14 @@ void do_settimeofday(struct timeval *tv)
} }
xtime.tv_sec = tv->tv_sec; xtime.tv_sec = tv->tv_sec;
xtime.tv_nsec = tv->tv_usec; xtime.tv_nsec = tv->tv_nsec;
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;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_sequnlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
clock_was_set(); clock_was_set();
return 0;
} }
static int set_rtc_mmss(unsigned long nowtime) static int set_rtc_mmss(unsigned long nowtime)
...@@ -303,9 +306,9 @@ void __init time_init(void) ...@@ -303,9 +306,9 @@ void __init time_init(void)
{ {
xtime.tv_sec = get_cmos_time(); xtime.tv_sec = get_cmos_time();
wall_to_monotonic.tv_sec = -xtime.tv_sec + INITIAL_JIFFIES / HZ; wall_to_monotonic.tv_sec = -xtime.tv_sec;
xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
wall_to_monotonic.tv_nsec = 0; wall_to_monotonic.tv_nsec = -xtime.tv_nsec;
timer = select_timer(); timer = select_timer();
......
...@@ -137,36 +137,19 @@ static struct vmalloc_info get_vmalloc_info(void) ...@@ -137,36 +137,19 @@ static struct vmalloc_info get_vmalloc_info(void)
static int uptime_read_proc(char *page, char **start, off_t off, static int uptime_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data) int count, int *eof, void *data)
{ {
u64 uptime; struct timespec uptime;
unsigned long uptime_remainder; struct timespec idle;
int len; int len;
u64 idle_jiffies = init_task.utime + init_task.stime;
uptime = get_jiffies_64() - INITIAL_JIFFIES; do_posix_clock_monotonic_gettime(&uptime);
uptime_remainder = (unsigned long) do_div(uptime, HZ); jiffies_to_timespec(idle_jiffies, &idle);
#if HZ!=100
{
u64 idle = init_task.utime + init_task.stime;
unsigned long idle_remainder;
idle_remainder = (unsigned long) do_div(idle, HZ);
len = sprintf(page,"%lu.%02lu %lu.%02lu\n", len = sprintf(page,"%lu.%02lu %lu.%02lu\n",
(unsigned long) uptime, (unsigned long) uptime.tv_sec,
(uptime_remainder * 100) / HZ, (uptime.tv_nsec / (NSEC_PER_SEC / 100)),
(unsigned long) idle, (unsigned long) idle.tv_sec,
(idle_remainder * 100) / HZ); (idle.tv_nsec / (NSEC_PER_SEC / 100)));
}
#else
{
unsigned long idle = init_task.utime + init_task.stime;
len = sprintf(page,"%lu.%02lu %lu.%02lu\n",
(unsigned long) uptime,
uptime_remainder,
idle / HZ,
idle % HZ);
}
#endif
return proc_calc_metrics(page, start, off, count, eof, len); return proc_calc_metrics(page, start, off, count, eof, len);
} }
......
...@@ -200,9 +200,10 @@ struct timespec current_kernel_time(void); ...@@ -200,9 +200,10 @@ struct timespec current_kernel_time(void);
#ifdef __KERNEL__ #ifdef __KERNEL__
extern void do_gettimeofday(struct timeval *tv); extern void do_gettimeofday(struct timeval *tv);
extern void do_settimeofday(struct timeval *tv); extern int do_settimeofday(struct timespec *tv);
extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); extern int do_sys_settimeofday(struct timespec *tv, struct timezone *tz);
extern void clock_was_set(void); // call when ever the clock is set extern void clock_was_set(void); // call when ever the clock is set
extern int do_posix_clock_monotonic_gettime(struct timespec *tp);
extern long do_nanosleep(struct timespec *t); extern long do_nanosleep(struct timespec *t);
extern long do_utimes(char __user * filename, struct timeval * times); extern long do_utimes(char __user * filename, struct timeval * times);
#endif #endif
......
...@@ -409,7 +409,7 @@ sys_timer_create(clockid_t which_clock, ...@@ -409,7 +409,7 @@ sys_timer_create(clockid_t which_clock,
do { do {
if (unlikely(!idr_pre_get(&posix_timers_id))) { if (unlikely(!idr_pre_get(&posix_timers_id))) {
error = -EAGAIN; error = -EAGAIN;
new_timer_id = (timer_t)-1; new_timer->it_id = (timer_t)-1;
goto out; goto out;
} }
spin_lock_irq(&idr_lock); spin_lock_irq(&idr_lock);
...@@ -1026,8 +1026,7 @@ sys_clock_settime(clockid_t which_clock, const struct timespec __user *tp) ...@@ -1026,8 +1026,7 @@ sys_clock_settime(clockid_t which_clock, const struct timespec __user *tp)
if (posix_clocks[which_clock].clock_set) if (posix_clocks[which_clock].clock_set)
return posix_clocks[which_clock].clock_set(&new_tp); return posix_clocks[which_clock].clock_set(&new_tp);
new_tp.tv_nsec /= NSEC_PER_USEC; return do_sys_settimeofday(&new_tp, NULL);
return do_sys_settimeofday((struct timeval *) &new_tp, NULL);
} }
asmlinkage long asmlinkage long
......
...@@ -68,22 +68,15 @@ asmlinkage long sys_time(int * tloc) ...@@ -68,22 +68,15 @@ asmlinkage long sys_time(int * tloc)
asmlinkage long sys_stime(int * tptr) asmlinkage long sys_stime(int * tptr)
{ {
int value; struct timespec tv;
if (!capable(CAP_SYS_TIME)) if (!capable(CAP_SYS_TIME))
return -EPERM; return -EPERM;
if (get_user(value, tptr)) if (get_user(tv.tv_sec, tptr))
return -EFAULT; return -EFAULT;
write_seqlock_irq(&xtime_lock);
time_interpolator_reset(); tv.tv_nsec = 0;
xtime.tv_sec = value; do_settimeofday(&tv);
xtime.tv_nsec = 0;
time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
write_sequnlock_irq(&xtime_lock);
return 0; return 0;
} }
...@@ -123,9 +116,11 @@ asmlinkage long sys_gettimeofday(struct timeval __user *tv, struct timezone __us ...@@ -123,9 +116,11 @@ asmlinkage long sys_gettimeofday(struct timeval __user *tv, struct timezone __us
inline static void warp_clock(void) inline static void warp_clock(void)
{ {
write_seqlock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
wall_to_monotonic.tv_sec -= sys_tz.tz_minuteswest * 60;
xtime.tv_sec += sys_tz.tz_minuteswest * 60; xtime.tv_sec += sys_tz.tz_minuteswest * 60;
time_interpolator_update(sys_tz.tz_minuteswest * 60 * NSEC_PER_SEC); time_interpolator_update(sys_tz.tz_minuteswest * 60 * NSEC_PER_SEC);
write_sequnlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
clock_was_set();
} }
/* /*
...@@ -139,7 +134,7 @@ inline static void warp_clock(void) ...@@ -139,7 +134,7 @@ inline static void warp_clock(void)
* various programs will get confused when the clock gets warped. * various programs will get confused when the clock gets warped.
*/ */
int do_sys_settimeofday(struct timeval *tv, struct timezone *tz) int do_sys_settimeofday(struct timespec *tv, struct timezone *tz)
{ {
static int firsttime = 1; static int firsttime = 1;
...@@ -160,14 +155,14 @@ int do_sys_settimeofday(struct timeval *tv, struct timezone *tz) ...@@ -160,14 +155,14 @@ int do_sys_settimeofday(struct timeval *tv, struct timezone *tz)
/* SMP safe, again the code in arch/foo/time.c should /* SMP safe, again the code in arch/foo/time.c should
* globally block out interrupts when it runs. * globally block out interrupts when it runs.
*/ */
do_settimeofday(tv); return do_settimeofday(tv);
} }
return 0; return 0;
} }
asmlinkage long sys_settimeofday(struct timeval __user *tv, struct timezone __user *tz) asmlinkage long sys_settimeofday(struct timeval __user *tv, struct timezone __user *tz)
{ {
struct timeval new_tv; struct timespec new_tv;
struct timezone new_tz; struct timezone new_tz;
if (tv) { if (tv) {
...@@ -177,6 +172,7 @@ asmlinkage long sys_settimeofday(struct timeval __user *tv, struct timezone __us ...@@ -177,6 +172,7 @@ asmlinkage long sys_settimeofday(struct timeval __user *tv, struct timezone __us
if (tz) { if (tz) {
if (copy_from_user(&new_tz, tz, sizeof(*tz))) if (copy_from_user(&new_tz, tz, sizeof(*tz)))
return -EFAULT; return -EFAULT;
new_tv.tv_nsec *= NSEC_PER_USEC;
} }
return do_sys_settimeofday(tv ? &new_tv : NULL, tz ? &new_tz : NULL); return do_sys_settimeofday(tv ? &new_tv : NULL, tz ? &new_tz : NULL);
......
...@@ -1109,7 +1109,6 @@ asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp) ...@@ -1109,7 +1109,6 @@ asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp)
asmlinkage long sys_sysinfo(struct sysinfo __user *info) asmlinkage long sys_sysinfo(struct sysinfo __user *info)
{ {
struct sysinfo val; struct sysinfo val;
u64 uptime;
unsigned long mem_total, sav_total; unsigned long mem_total, sav_total;
unsigned int mem_unit, bitcount; unsigned int mem_unit, bitcount;
unsigned long seq; unsigned long seq;
...@@ -1117,11 +1116,25 @@ asmlinkage long sys_sysinfo(struct sysinfo __user *info) ...@@ -1117,11 +1116,25 @@ asmlinkage long sys_sysinfo(struct sysinfo __user *info)
memset((char *)&val, 0, sizeof(struct sysinfo)); memset((char *)&val, 0, sizeof(struct sysinfo));
do { do {
struct timespec tp;
seq = read_seqbegin(&xtime_lock); seq = read_seqbegin(&xtime_lock);
uptime = jiffies_64 - INITIAL_JIFFIES; /*
do_div(uptime, HZ); * This is annoying. The below is the same thing
val.uptime = (unsigned long) uptime; * posix_get_clock_monotonic() does, but it wants to
* take the lock which we want to cover the loads stuff
* too.
*/
do_gettimeofday((struct timeval *)&tp);
tp.tv_nsec *= NSEC_PER_USEC;
tp.tv_sec += wall_to_monotonic.tv_sec;
tp.tv_nsec += wall_to_monotonic.tv_nsec;
if (tp.tv_nsec - NSEC_PER_SEC >= 0) {
tp.tv_nsec = tp.tv_nsec - NSEC_PER_SEC;
tp.tv_sec++;
}
val.uptime = tp.tv_sec + (tp.tv_nsec ? 1 : 0);
val.loads[0] = avenrun[0] << (SI_LOAD_SHIFT - FSHIFT); val.loads[0] = avenrun[0] << (SI_LOAD_SHIFT - FSHIFT);
val.loads[1] = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT); val.loads[1] = avenrun[1] << (SI_LOAD_SHIFT - FSHIFT);
......
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