Commit bb59cfa4 authored by Stephen Hemminger's avatar Stephen Hemminger Committed by Linus Torvalds

[PATCH] seqlock for xtime

Add "seqlock" infrastructure for doing low-overhead optimistic reader
locks (writer increments a sequence number, reader verifies that no
writers came in during the critical region, and lots of careful memory
barriers to take care of business).

Make xtime/get_jiffies_64() use this new locking.
parent 62672619
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <asm/hwrpb.h> #include <asm/hwrpb.h>
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include "proto.h" #include "proto.h"
...@@ -51,7 +52,6 @@ ...@@ -51,7 +52,6 @@
u64 jiffies_64; u64 jiffies_64;
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies; /* kernel/timer.c */ extern unsigned long wall_jiffies; /* kernel/timer.c */
static int set_rtc_mmss(unsigned long); static int set_rtc_mmss(unsigned long);
...@@ -106,7 +106,7 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) ...@@ -106,7 +106,7 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs)
alpha_do_profile(regs->pc); alpha_do_profile(regs->pc);
#endif #endif
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
/* /*
* Calculate how many ticks have passed since the last update, * Calculate how many ticks have passed since the last update,
...@@ -138,7 +138,7 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs) ...@@ -138,7 +138,7 @@ void timer_interrupt(int irq, void *dev, struct pt_regs * regs)
state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0); state.last_rtc_update = xtime.tv_sec - (tmp ? 600 : 0);
} }
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
void void
...@@ -395,18 +395,20 @@ time_init(void) ...@@ -395,18 +395,20 @@ time_init(void)
void void
do_gettimeofday(struct timeval *tv) do_gettimeofday(struct timeval *tv)
{ {
unsigned long sec, usec, lost, flags; unsigned long flags;
unsigned long sec, usec, lost, seq;
unsigned long delta_cycles, delta_usec, partial_tick; unsigned long delta_cycles, delta_usec, partial_tick;
read_lock_irqsave(&xtime_lock, flags); do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
delta_cycles = rpcc() - state.last_time; delta_cycles = rpcc() - state.last_time;
sec = xtime.tv_sec; sec = xtime.tv_sec;
usec = (xtime.tv_nsec / 1000); usec = (xtime.tv_nsec / 1000);
partial_tick = state.partial_tick; partial_tick = state.partial_tick;
lost = jiffies - wall_jiffies; lost = jiffies - wall_jiffies;
read_unlock_irqrestore(&xtime_lock, flags); } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* Until and unless we figure out how to get cpu cycle counters /* Until and unless we figure out how to get cpu cycle counters
...@@ -448,7 +450,7 @@ do_settimeofday(struct timeval *tv) ...@@ -448,7 +450,7 @@ do_settimeofday(struct timeval *tv)
unsigned long delta_usec; unsigned long delta_usec;
long sec, usec; long sec, usec;
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* The offset that is added into time in do_gettimeofday above /* The offset that is added into time in do_gettimeofday above
must be subtracted out here to keep a coherent view of the must be subtracted out here to keep a coherent view of the
...@@ -479,7 +481,7 @@ do_settimeofday(struct timeval *tv) ...@@ -479,7 +481,7 @@ do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
u64 jiffies_64; u64 jiffies_64;
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
/* this needs a better home */ /* this needs a better home */
...@@ -148,18 +147,20 @@ static void do_leds(void) ...@@ -148,18 +147,20 @@ static void do_leds(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long usec, sec, lost; unsigned long usec, sec, lost;
read_lock_irqsave(&xtime_lock, flags); do {
usec = gettimeoffset(); seq = read_seqbegin_irqsave(&xtime_lock, flags);
usec = gettimeoffset();
lost = jiffies - wall_jiffies; lost = jiffies - wall_jiffies;
if (lost) if (lost)
usec += lost * USECS_PER_JIFFY; usec += lost * USECS_PER_JIFFY;
sec = xtime.tv_sec; sec = xtime.tv_sec;
usec += xtime.tv_nsec / 1000; usec += xtime.tv_nsec / 1000;
read_unlock_irqrestore(&xtime_lock, flags); } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
/* usec may have gone up a lot: be safe */ /* usec may have gone up a lot: be safe */
while (usec >= 1000000) { while (usec >= 1000000) {
...@@ -173,7 +174,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -173,7 +174,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_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
* value in this location is the value at the most recent update of * value in this location is the value at the most recent update of
...@@ -194,7 +195,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -194,7 +195,7 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
static struct irqaction timer_irq = { static struct irqaction timer_irq = {
......
...@@ -215,6 +215,7 @@ ...@@ -215,6 +215,7 @@
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/apm_bios.h> #include <linux/apm_bios.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/time.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -227,7 +228,6 @@ ...@@ -227,7 +228,6 @@
#include <linux/sysrq.h> #include <linux/sysrq.h>
extern rwlock_t xtime_lock;
extern spinlock_t i8253_lock; extern spinlock_t i8253_lock;
extern unsigned long get_cmos_time(void); extern unsigned long get_cmos_time(void);
extern void machine_real_restart(unsigned char *, int); extern void machine_real_restart(unsigned char *, int);
...@@ -1264,7 +1264,7 @@ static int suspend(int vetoable) ...@@ -1264,7 +1264,7 @@ static int suspend(int vetoable)
printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n"); printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n");
} }
/* serialize with the timer interrupt */ /* serialize with the timer interrupt */
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* protect against access to timer chip registers */ /* protect against access to timer chip registers */
spin_lock(&i8253_lock); spin_lock(&i8253_lock);
...@@ -1276,7 +1276,7 @@ static int suspend(int vetoable) ...@@ -1276,7 +1276,7 @@ static int suspend(int vetoable)
ignore_normal_resume = 1; ignore_normal_resume = 1;
spin_unlock(&i8253_lock); spin_unlock(&i8253_lock);
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
if (err == APM_NO_ERROR) if (err == APM_NO_ERROR)
err = APM_SUCCESS; err = APM_SUCCESS;
...@@ -1301,10 +1301,10 @@ static void standby(void) ...@@ -1301,10 +1301,10 @@ static void standby(void)
int err; int err;
/* serialize with the timer interrupt */ /* serialize with the timer interrupt */
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* If needed, notify drivers here */ /* If needed, notify drivers here */
get_time_diff(); get_time_diff();
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
err = set_system_power_state(APM_STATE_STANDBY); err = set_system_power_state(APM_STATE_STANDBY);
if ((err != APM_SUCCESS) && (err != APM_NO_ERROR)) if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
...@@ -1393,9 +1393,9 @@ static void check_events(void) ...@@ -1393,9 +1393,9 @@ static void check_events(void)
ignore_bounce = 1; ignore_bounce = 1;
if ((event != APM_NORMAL_RESUME) if ((event != APM_NORMAL_RESUME)
|| (ignore_normal_resume == 0)) { || (ignore_normal_resume == 0)) {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
set_time(); set_time();
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
pm_send_all(PM_RESUME, (void *)0); pm_send_all(PM_RESUME, (void *)0);
queue_event(event, NULL); queue_event(event, NULL);
} }
...@@ -1410,9 +1410,9 @@ static void check_events(void) ...@@ -1410,9 +1410,9 @@ static void check_events(void)
break; break;
case APM_UPDATE_TIME: case APM_UPDATE_TIME:
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
set_time(); set_time();
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
break; break;
case APM_CRITICAL_SUSPEND: case APM_CRITICAL_SUSPEND:
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/time.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/processor.h> #include <asm/processor.h>
......
...@@ -70,7 +70,6 @@ u64 jiffies_64; ...@@ -70,7 +70,6 @@ u64 jiffies_64;
unsigned long cpu_khz; /* Detected as we calibrate the TSC */ unsigned long cpu_khz; /* Detected as we calibrate the TSC */
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
...@@ -87,19 +86,21 @@ struct timer_opts* timer = &timer_none; ...@@ -87,19 +86,21 @@ struct timer_opts* timer = &timer_none;
*/ */
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long seq;
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
usec = timer->get_offset(); seq = read_seqbegin(&xtime_lock);
{
unsigned long lost = jiffies - wall_jiffies; usec = timer->get_offset();
if (lost) {
usec += lost * (1000000 / HZ); unsigned long lost = jiffies - wall_jiffies;
} if (lost)
sec = xtime.tv_sec; usec += lost * (1000000 / HZ);
usec += (xtime.tv_nsec / 1000); }
read_unlock_irqrestore(&xtime_lock, flags); sec = xtime.tv_sec;
usec += (xtime.tv_nsec / 1000);
} while (read_seqretry(&xtime_lock, seq));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -112,7 +113,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -112,7 +113,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_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
* value in this location is the value at the most recent update of * value in this location is the value at the most recent update of
...@@ -133,7 +134,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -133,7 +134,7 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
/* /*
...@@ -314,14 +315,14 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -314,14 +315,14 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* the irq version of write_lock because as just said we have irq * the irq version of write_lock because as just said we have irq
* locally disabled. -arca * locally disabled. -arca
*/ */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
detect_lost_tick(); detect_lost_tick();
timer->mark_offset(); timer->mark_offset();
do_timer_interrupt(irq, NULL, regs); do_timer_interrupt(irq, NULL, regs);
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
......
...@@ -76,7 +76,7 @@ static void delay_pit(unsigned long loops) ...@@ -76,7 +76,7 @@ static void delay_pit(unsigned long loops)
static unsigned long get_offset_pit(void) static unsigned long get_offset_pit(void)
{ {
int count; int count;
unsigned long flags;
static int count_p = LATCH; /* for the first call after boot */ static int count_p = LATCH; /* for the first call after boot */
static unsigned long jiffies_p = 0; static unsigned long jiffies_p = 0;
...@@ -85,8 +85,7 @@ static unsigned long get_offset_pit(void) ...@@ -85,8 +85,7 @@ static unsigned long get_offset_pit(void)
*/ */
unsigned long jiffies_t; unsigned long jiffies_t;
/* gets recalled with irq locally disabled */ spin_lock_irqsave(&i8253_lock, flags);
spin_lock(&i8253_lock);
/* timer count may underflow right here */ /* timer count may underflow right here */
outb_p(0x00, 0x43); /* latch the count ASAP */ outb_p(0x00, 0x43); /* latch the count ASAP */
...@@ -108,7 +107,7 @@ static unsigned long get_offset_pit(void) ...@@ -108,7 +107,7 @@ static unsigned long get_offset_pit(void)
count = LATCH - 1; count = LATCH - 1;
} }
spin_unlock(&i8253_lock); spin_unlock_irqrestore(&i8253_lock, flags);
/* /*
* avoiding timer inconsistencies (they are rare, but they happen)... * avoiding timer inconsistencies (they are rare, but they happen)...
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <asm/sal.h> #include <asm/sal.h>
#include <asm/system.h> #include <asm/system.h>
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
extern unsigned long last_time_offset; extern unsigned long last_time_offset;
...@@ -89,7 +88,7 @@ gettimeoffset (void) ...@@ -89,7 +88,7 @@ gettimeoffset (void)
void void
do_settimeofday (struct timeval *tv) do_settimeofday (struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
{ {
/* /*
* This is revolting. We need to set "xtime" correctly. However, the value * This is revolting. We need to set "xtime" correctly. However, the value
...@@ -112,21 +111,21 @@ do_settimeofday (struct timeval *tv) ...@@ -112,21 +111,21 @@ do_settimeofday (struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
} }
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
void void
do_gettimeofday (struct timeval *tv) do_gettimeofday (struct timeval *tv)
{ {
unsigned long flags, usec, sec, old; unsigned long seq, usec, sec, old;
read_lock_irqsave(&xtime_lock, flags); do {
{ seq = read_seqbegin(&xtime_lock);
usec = gettimeoffset(); usec = gettimeoffset();
/* /*
* Ensure time never goes backwards, even when ITC on different CPUs are * Ensure time never goes backwards, even when ITC on
* not perfectly synchronized. * different CPUs are not perfectly synchronized.
*/ */
do { do {
old = last_time_offset; old = last_time_offset;
...@@ -138,8 +137,8 @@ do_gettimeofday (struct timeval *tv) ...@@ -138,8 +137,8 @@ do_gettimeofday (struct timeval *tv)
sec = xtime.tv_sec; sec = xtime.tv_sec;
usec += xtime.tv_nsec / 1000; usec += xtime.tv_nsec / 1000;
} } while (read_seqend(&xtime_lock, seq));
read_unlock_irqrestore(&xtime_lock, flags);
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -182,10 +181,10 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -182,10 +181,10 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* another CPU. We need to avoid to SMP race by acquiring the * another CPU. We need to avoid to SMP race by acquiring the
* xtime_lock. * xtime_lock.
*/ */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
do_timer(regs); do_timer(regs);
local_cpu_data->itm_next = new_itm; local_cpu_data->itm_next = new_itm;
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} else } else
local_cpu_data->itm_next = new_itm; local_cpu_data->itm_next = new_itm;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/io.h> #include <asm/io.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/profile.h> #include <linux/profile.h>
...@@ -129,25 +130,27 @@ void time_init(void) ...@@ -129,25 +130,27 @@ void time_init(void)
mach_sched_init(timer_interrupt); mach_sched_init(timer_interrupt);
} }
extern rwlock_t xtime_lock;
/* /*
* This version of gettimeofday has near microsecond resolution. * This version of gettimeofday has near microsecond resolution.
*/ */
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
extern unsigned long wall_jiffies;
unsigned long flags; unsigned long flags;
extern unsigned long wall_jiffies;
unsigned long seq;
unsigned long usec, sec, lost; unsigned long usec, sec, lost;
read_lock_irqsave(&xtime_lock, flags); do {
usec = mach_gettimeoffset(); seq = read_seqbegin_irqsave(&xtime_lock, flags);
lost = jiffies - wall_jiffies;
if (lost) usec = mach_gettimeoffset();
usec += lost * (1000000/HZ); lost = jiffies - wall_jiffies;
sec = xtime.tv_sec; if (lost)
usec += xtime.tv_nsec/1000; usec += lost * (1000000/HZ);
read_unlock_irqrestore(&xtime_lock, flags); sec = xtime.tv_sec;
usec += xtime.tv_nsec/1000;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -162,7 +165,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -162,7 +165,7 @@ void do_settimeofday(struct timeval *tv)
{ {
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* This is revolting. We need to set the xtime.tv_nsec /* This is revolting. We need to set the xtime.tv_nsec
* correctly. However, the value in this location is * correctly. However, the value in this location is
* is value at the last tick. * is value at the last tick.
...@@ -183,5 +186,5 @@ void do_settimeofday(struct timeval *tv) ...@@ -183,5 +186,5 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/profile.h> #include <linux/profile.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <asm/machdep.h> #include <asm/machdep.h>
...@@ -126,21 +127,23 @@ void time_init(void) ...@@ -126,21 +127,23 @@ void time_init(void)
mach_sched_init(timer_interrupt); mach_sched_init(timer_interrupt);
} }
extern rwlock_t xtime_lock;
/* /*
* This version of gettimeofday has near microsecond resolution. * This version of gettimeofday has near microsecond resolution.
*/ */
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
usec = mach_gettimeoffset ? mach_gettimeoffset() : 0; seq = read_seqbegin_irqsave(&xtime_lock, flags);
sec = xtime.tv_sec;
usec += (xtime.tv_nsec / 1000); usec = mach_gettimeoffset ? mach_gettimeoffset() : 0;
read_unlock_irqrestore(&xtime_lock, flags); sec = xtime.tv_sec;
usec += (xtime.tv_nsec / 1000);
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -153,7 +156,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -153,7 +156,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec /* This is revolting. We need to set the xtime.tv_usec
* correctly. However, the value in this location is * correctly. However, the value in this location is
* is value at the last tick. * is value at the last tick.
...@@ -174,5 +177,5 @@ void do_settimeofday(struct timeval *tv) ...@@ -174,5 +177,5 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/time.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -44,7 +45,6 @@ unsigned long uart_baud_base; ...@@ -44,7 +45,6 @@ unsigned long uart_baud_base;
static unsigned long r4k_offset; /* Amount to increment compare reg each time */ static unsigned long r4k_offset; /* Amount to increment compare reg each time */
static unsigned long r4k_cur; /* What counter should be at next timer irq */ static unsigned long r4k_cur; /* What counter should be at next timer irq */
extern rwlock_t xtime_lock;
#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
...@@ -150,10 +150,10 @@ void __init time_init(void) ...@@ -150,10 +150,10 @@ void __init time_init(void)
set_cp0_status(ALLINTS); set_cp0_status(ALLINTS);
/* Read time from the RTC chipset. */ /* Read time from the RTC chipset. */
write_lock_irqsave (&xtime_lock, flags); write_seqlock_irqsave (&xtime_lock, flags);
xtime.tv_sec = get_mips_time(); xtime.tv_sec = get_mips_time();
xtime.tv_usec = 0; xtime.tv_usec = 0;
write_unlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
} }
/* This is for machines which generate the exact clock. */ /* This is for machines which generate the exact clock. */
...@@ -229,20 +229,24 @@ static unsigned long do_fast_gettimeoffset(void) ...@@ -229,20 +229,24 @@ static unsigned long do_fast_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned int flags; unsigned long flags;
unsigned long seq;
read_lock_irqsave (&xtime_lock, flags); do {
*tv = xtime; seq = read_seqbegin_irqsave(&xtime_lock, flags);
tv->tv_usec += do_fast_gettimeoffset();
/* *tv = xtime;
* xtime is atomically updated in timer_bh. jiffies - wall_jiffies tv->tv_usec += do_fast_gettimeoffset();
* is nonzero if the timer bottom half hasnt executed yet.
*/ /*
if (jiffies - wall_jiffies) * xtime is atomically updated in timer_bh. jiffies - wall_jiffies
tv->tv_usec += USECS_PER_JIFFY; * is nonzero if the timer bottom half hasnt executed yet.
*/
if (jiffies - wall_jiffies)
tv->tv_usec += USECS_PER_JIFFY;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
read_unlock_irqrestore (&xtime_lock, flags);
if (tv->tv_usec >= 1000000) { if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000; tv->tv_usec -= 1000000;
...@@ -252,7 +256,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -252,7 +256,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq (&xtime_lock); write_seqlock_irq (&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec correctly. /* This is revolting. We need to set the xtime.tv_usec correctly.
* However, the value in this location is is value at the last tick. * However, the value in this location is is value at the last tick.
...@@ -272,7 +276,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -272,7 +276,7 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq (&xtime_lock); write_sequnlock_irq (&xtime_lock);
} }
/* /*
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/param.h> #include <linux/param.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/time.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -23,8 +24,6 @@ ...@@ -23,8 +24,6 @@
#include <asm/baget/baget.h> #include <asm/baget/baget.h>
extern rwlock_t xtime_lock;
/* /*
* To have precision clock, we need to fix available clock frequency * To have precision clock, we need to fix available clock frequency
*/ */
...@@ -79,20 +78,21 @@ void __init time_init(void) ...@@ -79,20 +78,21 @@ void __init time_init(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long seq;
read_lock_irqsave (&xtime_lock, flags); do {
*tv = xtime; seq = read_seqbegin(&xtime_lock);
read_unlock_irqrestore (&xtime_lock, flags); *tv = xtime;
} while (read_seqretry(&xtime_lock, seq));
} }
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq (&xtime_lock); write_seqlock_irq (&xtime_lock);
xtime = *tv; xtime = *tv;
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_unlock_irq (&xtime_lock); write_sequnlock_irq (&xtime_lock);
} }
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/param.h> #include <linux/param.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/time.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/bcd.h> #include <linux/bcd.h>
...@@ -35,7 +36,6 @@ ...@@ -35,7 +36,6 @@
extern void (*board_time_init)(struct irqaction *irq); extern void (*board_time_init)(struct irqaction *irq);
extern volatile unsigned long wall_jiffies; extern volatile unsigned long wall_jiffies;
extern rwlock_t xtime_lock;
/* /*
* Change this if you have some constant time drift * Change this if you have some constant time drift
...@@ -211,19 +211,22 @@ static unsigned long (*do_gettimeoffset) (void) = do_slow_gettimeoffset; ...@@ -211,19 +211,22 @@ static unsigned long (*do_gettimeoffset) (void) = do_slow_gettimeoffset;
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
read_lock_irqsave(&xtime_lock, flags); do {
*tv = xtime; seq = read_seqbegin_irqsave(&xtime_lock, flags);
tv->tv_usec += do_gettimeoffset(); *tv = xtime;
tv->tv_usec += do_gettimeoffset();
/* /*
* xtime is atomically updated in timer_bh. jiffies - wall_jiffies * xtime is atomically updated in timer_bh. jiffies - wall_jiffies
* is nonzero if the timer bottom half hasnt executed yet. * is nonzero if the timer bottom half hasnt executed yet.
*/ */
if (jiffies - wall_jiffies) if (jiffies - wall_jiffies)
tv->tv_usec += USECS_PER_JIFFY; tv->tv_usec += USECS_PER_JIFFY;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
read_unlock_irqrestore(&xtime_lock, flags);
if (tv->tv_usec >= 1000000) { if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000; tv->tv_usec -= 1000000;
...@@ -233,7 +236,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -233,7 +236,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec /* This is revolting. We need to set the xtime.tv_usec
* correctly. However, the value in this location is * correctly. However, the value in this location is
...@@ -254,7 +257,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -254,7 +257,7 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
/* /*
...@@ -330,6 +333,7 @@ static inline void ...@@ -330,6 +333,7 @@ static inline void
timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{ {
volatile unsigned char dummy; volatile unsigned char dummy;
unsigned long seq;
dummy = CMOS_READ(RTC_REG_C); /* ACK RTC Interrupt */ dummy = CMOS_READ(RTC_REG_C); /* ACK RTC Interrupt */
...@@ -357,23 +361,27 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -357,23 +361,27 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
* called as close as possible to 500 ms before the new second starts. * called as close as possible to 500 ms before the new second starts.
*/ */
read_lock(&xtime_lock); do {
if ((time_status & STA_UNSYNC) == 0 seq = read_seqbegin(&xtime_lock);
&& xtime.tv_sec > last_rtc_update + 660
&& xtime.tv_usec >= 500000 - tick / 2 if ((time_status & STA_UNSYNC) == 0
&& xtime.tv_usec <= 500000 + tick / 2) { && xtime.tv_sec > last_rtc_update + 660
if (set_rtc_mmss(xtime.tv_sec) == 0) && xtime.tv_usec >= 500000 - tick / 2
last_rtc_update = xtime.tv_sec; && xtime.tv_usec <= 500000 + tick / 2) {
else if (set_rtc_mmss(xtime.tv_sec) == 0)
/* do it again in 60 s */ last_rtc_update = xtime.tv_sec;
last_rtc_update = xtime.tv_sec - 600; else
} /* do it again in 60 s */
last_rtc_update = xtime.tv_sec - 600;
}
} while (read_seqretry(&xtime_lock, seq));
/* As we return to user mode fire off the other CPU schedulers.. this is /* As we return to user mode fire off the other CPU schedulers.. this is
basically because we don't yet share IRQ's around. This message is basically because we don't yet share IRQ's around. This message is
rigged to be safe on the 386 - basically it's a hack, so don't look rigged to be safe on the 386 - basically it's a hack, so don't look
closely for now.. */ closely for now.. */
/*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */ /*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */
read_unlock(&xtime_lock);
} }
static void r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) static void r4k_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
...@@ -470,10 +478,10 @@ void __init time_init(void) ...@@ -470,10 +478,10 @@ void __init time_init(void)
real_year = CMOS_READ(RTC_DEC_YEAR); real_year = CMOS_READ(RTC_DEC_YEAR);
year += real_year - 72 + 2000; year += real_year - 72 + 2000;
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
xtime.tv_sec = mktime(year, mon, day, hour, min, sec); xtime.tv_sec = mktime(year, mon, day, hour, min, sec);
xtime.tv_usec = 0; xtime.tv_usec = 0;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
if (mips_cpu.options & MIPS_CPU_COUNTER) { if (mips_cpu.options & MIPS_CPU_COUNTER) {
write_32bit_cp0_register(CP0_COUNT, 0); write_32bit_cp0_register(CP0_COUNT, 0);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/time.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/mipsregs.h> #include <asm/mipsregs.h>
...@@ -38,7 +39,6 @@ ...@@ -38,7 +39,6 @@
extern void enable_cpu_timer(void); extern void enable_cpu_timer(void);
extern volatile unsigned long wall_jiffies; extern volatile unsigned long wall_jiffies;
extern rwlock_t xtime_lock;
unsigned long missed_heart_beats = 0; unsigned long missed_heart_beats = 0;
static long last_rtc_update = 0; static long last_rtc_update = 0;
...@@ -119,6 +119,8 @@ static int set_rtc_mmss(unsigned long nowtime) ...@@ -119,6 +119,8 @@ static int set_rtc_mmss(unsigned long nowtime)
*/ */
void mips_timer_interrupt(struct pt_regs *regs) void mips_timer_interrupt(struct pt_regs *regs)
{ {
unsigned long seq;
if (r4k_offset == 0) if (r4k_offset == 0)
goto null; goto null;
...@@ -133,18 +135,22 @@ void mips_timer_interrupt(struct pt_regs *regs) ...@@ -133,18 +135,22 @@ void mips_timer_interrupt(struct pt_regs *regs)
* within 500ms before the * next second starts, * within 500ms before the * next second starts,
* thus the following code. * thus the following code.
*/ */
read_lock(&xtime_lock); do {
if ((time_status & STA_UNSYNC) == 0 seq = read_seqbegin(&xtime_lock);
&& xtime.tv_sec > last_rtc_update + 660
&& xtime.tv_usec >= 500000 - (tick >> 1)
&& xtime.tv_usec <= 500000 + (tick >> 1)) if ((time_status & STA_UNSYNC) == 0
if (set_rtc_mmss(xtime.tv_sec) == 0) && xtime.tv_sec > last_rtc_update + 660
last_rtc_update = xtime.tv_sec; && xtime.tv_usec >= 500000 - (tick >> 1)
else { && xtime.tv_usec <= 500000 + (tick >> 1))
/* do it again in 60 s */ if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec - 600; last_rtc_update = xtime.tv_sec;
} else {
read_unlock(&xtime_lock); /* do it again in 60 s */
last_rtc_update = xtime.tv_sec - 600;
}
} while (read_seqretry(&xtime_lock, seq));
r4k_cur += r4k_offset; r4k_cur += r4k_offset;
ack_r4ktimer(r4k_cur); ack_r4ktimer(r4k_cur);
...@@ -247,10 +253,10 @@ void __init time_init(void) ...@@ -247,10 +253,10 @@ void __init time_init(void)
enable_cpu_timer(); enable_cpu_timer();
/* Read time from the RTC chipset. */ /* Read time from the RTC chipset. */
write_lock_irqsave (&xtime_lock, flags); write_seqlock_irqsave (&xtime_lock, flags);
xtime.tv_sec = get_mips_time(); xtime.tv_sec = get_mips_time();
xtime.tv_usec = 0; xtime.tv_usec = 0;
write_unlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
} }
/* This is for machines which generate the exact clock. */ /* This is for machines which generate the exact clock. */
...@@ -332,20 +338,24 @@ static unsigned long do_fast_gettimeoffset(void) ...@@ -332,20 +338,24 @@ static unsigned long do_fast_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned int flags; unsigned long flags;
unsigned int seq;
do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
read_lock_irqsave (&xtime_lock, flags); *tv = xtime;
*tv = xtime; tv->tv_usec += do_fast_gettimeoffset();
tv->tv_usec += do_fast_gettimeoffset();
/* /*
* xtime is atomically updated in timer_bh. jiffies - wall_jiffies * xtime is atomically updated in timer_bh.
* is nonzero if the timer bottom half hasnt executed yet. * jiffies - wall_jiffies
*/ * is nonzero if the timer bottom half hasnt executed yet.
if (jiffies - wall_jiffies) */
tv->tv_usec += USECS_PER_JIFFY; if (jiffies - wall_jiffies)
tv->tv_usec += USECS_PER_JIFFY;
read_unlock_irqrestore (&xtime_lock, flags); } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
if (tv->tv_usec >= 1000000) { if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000; tv->tv_usec -= 1000000;
...@@ -355,7 +365,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -355,7 +365,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq (&xtime_lock); write_seqlock_irq (&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec correctly. /* This is revolting. We need to set the xtime.tv_usec correctly.
* However, the value in this location is is value at the last tick. * However, the value in this location is is value at the last tick.
...@@ -375,5 +385,5 @@ void do_settimeofday(struct timeval *tv) ...@@ -375,5 +385,5 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq (&xtime_lock); write_sequnlock_irq (&xtime_lock);
} }
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/times.h> #include <linux/times.h>
#include <linux/elf.h> #include <linux/elf.h>
...@@ -615,19 +616,17 @@ asmlinkage int irix_getgid(struct pt_regs *regs) ...@@ -615,19 +616,17 @@ asmlinkage int irix_getgid(struct pt_regs *regs)
return current->gid; return current->gid;
} }
extern rwlock_t xtime_lock;
asmlinkage int irix_stime(int value) asmlinkage int irix_stime(int value)
{ {
if (!capable(CAP_SYS_TIME)) if (!capable(CAP_SYS_TIME))
return -EPERM; return -EPERM;
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
xtime.tv_sec = value; xtime.tv_sec = value;
xtime.tv_usec = 0; xtime.tv_usec = 0;
time_maxerror = MAXPHASE; time_maxerror = MAXPHASE;
time_esterror = MAXPHASE; time_esterror = MAXPHASE;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
return 0; return 0;
} }
......
...@@ -37,7 +37,6 @@ u64 jiffies_64; ...@@ -37,7 +37,6 @@ u64 jiffies_64;
/* /*
* forward reference * forward reference
*/ */
extern rwlock_t xtime_lock;
extern volatile unsigned long wall_jiffies; extern volatile unsigned long wall_jiffies;
/* /*
...@@ -63,19 +62,23 @@ int (*rtc_set_time)(unsigned long) = null_rtc_set_time; ...@@ -63,19 +62,23 @@ int (*rtc_set_time)(unsigned long) = null_rtc_set_time;
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
read_lock_irqsave (&xtime_lock, flags); do {
*tv = xtime; seq = read_seqbegin_irqsave(&xtime_lock, flags);
tv->tv_usec += do_gettimeoffset();
/* *tv = xtime;
* xtime is atomically updated in timer_bh. jiffies - wall_jiffies tv->tv_usec += do_gettimeoffset();
* is nonzero if the timer bottom half hasnt executed yet.
*/ /*
if (jiffies - wall_jiffies) * xtime is atomically updated in timer_bh.
tv->tv_usec += USECS_PER_JIFFY; * jiffies - wall_jiffies
* is nonzero if the timer bottom half hasnt executed yet.
*/
if (jiffies - wall_jiffies)
tv->tv_usec += USECS_PER_JIFFY;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
read_unlock_irqrestore (&xtime_lock, flags);
if (tv->tv_usec >= 1000000) { if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000; tv->tv_usec -= 1000000;
...@@ -85,7 +88,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -85,7 +88,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq (&xtime_lock); write_seqlock_irq (&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec /* This is revolting. We need to set the xtime.tv_usec
* correctly. However, the value in this location is * correctly. However, the value in this location is
...@@ -105,7 +108,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -105,7 +108,7 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq (&xtime_lock); write_sequnlock_irq (&xtime_lock);
} }
...@@ -291,6 +294,8 @@ unsigned long calibrate_div64_gettimeoffset(void) ...@@ -291,6 +294,8 @@ unsigned long calibrate_div64_gettimeoffset(void)
*/ */
void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{ {
unsigned long seq;
if (mips_cpu.options & MIPS_CPU_COUNTER) { if (mips_cpu.options & MIPS_CPU_COUNTER) {
unsigned int count; unsigned int count;
...@@ -340,19 +345,21 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -340,19 +345,21 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* CMOS clock accordingly every ~11 minutes. rtc_set_time() has to be * CMOS clock accordingly every ~11 minutes. rtc_set_time() has to be
* called as close as possible to 500 ms before the new second starts. * called as close as possible to 500 ms before the new second starts.
*/ */
read_lock (&xtime_lock); do {
if ((time_status & STA_UNSYNC) == 0 && seq = read_seqbegin(&xtime_lock);
xtime.tv_sec > last_rtc_update + 660 &&
xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 && if ((time_status & STA_UNSYNC) == 0 &&
xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) { xtime.tv_sec > last_rtc_update + 660 &&
if (rtc_set_time(xtime.tv_sec) == 0) { xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 &&
last_rtc_update = xtime.tv_sec; xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) {
} else { if (rtc_set_time(xtime.tv_sec) == 0) {
last_rtc_update = xtime.tv_sec - 600; last_rtc_update = xtime.tv_sec;
/* do it again in 60 s */ } else {
last_rtc_update = xtime.tv_sec - 600;
/* do it again in 60 s */
}
} }
} } while (read_seqretry(&xtime_lock, seq));
read_unlock (&xtime_lock);
/* /*
* If jiffies has overflowed in this timer_interrupt we must * If jiffies has overflowed in this timer_interrupt we must
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <asm/div64.h> #include <asm/div64.h>
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <asm/mips-boards/generic.h> #include <asm/mips-boards/generic.h>
...@@ -45,7 +46,6 @@ unsigned long missed_heart_beats = 0; ...@@ -45,7 +46,6 @@ unsigned long missed_heart_beats = 0;
static unsigned long r4k_offset; /* Amount to increment compare reg each time */ static unsigned long r4k_offset; /* Amount to increment compare reg each time */
static unsigned long r4k_cur; /* What counter should be at next timer irq */ static unsigned long r4k_cur; /* What counter should be at next timer irq */
extern rwlock_t xtime_lock;
#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
...@@ -133,7 +133,9 @@ static int set_rtc_mmss(unsigned long nowtime) ...@@ -133,7 +133,9 @@ static int set_rtc_mmss(unsigned long nowtime)
*/ */
void mips_timer_interrupt(struct pt_regs *regs) void mips_timer_interrupt(struct pt_regs *regs)
{ {
unsigned long flags;
int irq = 7; int irq = 7;
unsigned long seq;
if (r4k_offset == 0) if (r4k_offset == 0)
goto null; goto null;
...@@ -149,18 +151,21 @@ void mips_timer_interrupt(struct pt_regs *regs) ...@@ -149,18 +151,21 @@ void mips_timer_interrupt(struct pt_regs *regs)
* within 500ms before the * next second starts, * within 500ms before the * next second starts,
* thus the following code. * thus the following code.
*/ */
read_lock(&xtime_lock);
if ((time_status & STA_UNSYNC) == 0
&& xtime.tv_sec > last_rtc_update + 660
&& xtime.tv_usec >= 500000 - (tick >> 1)
&& xtime.tv_usec <= 500000 + (tick >> 1))
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
/* do it again in 60 s */
last_rtc_update = xtime.tv_sec - 600;
read_unlock(&xtime_lock);
do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
if ((time_status & STA_UNSYNC) == 0
&& xtime.tv_sec > last_rtc_update + 660
&& xtime.tv_usec >= 500000 - (tick >> 1)
&& xtime.tv_usec <= 500000 + (tick >> 1))
if (set_rtc_mmss(xtime.tv_sec) == 0)
last_rtc_update = xtime.tv_sec;
else
/* do it again in 60 s */
last_rtc_update = xtime.tv_sec - 600;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
if ((timer_tick_count++ % HZ) == 0) { if ((timer_tick_count++ % HZ) == 0) {
mips_display_message(&display_string[display_count++]); mips_display_message(&display_string[display_count++]);
if (display_count == MAX_DISPLAY_COUNT) if (display_count == MAX_DISPLAY_COUNT)
...@@ -267,10 +272,10 @@ void __init time_init(void) ...@@ -267,10 +272,10 @@ void __init time_init(void)
change_cp0_status(ST0_IM, ALLINTS); change_cp0_status(ST0_IM, ALLINTS);
/* Read time from the RTC chipset. */ /* Read time from the RTC chipset. */
write_lock_irqsave (&xtime_lock, flags); write_seqlock_irqsave (&xtime_lock, flags);
xtime.tv_sec = get_mips_time(); xtime.tv_sec = get_mips_time();
xtime.tv_usec = 0; xtime.tv_usec = 0;
write_unlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
} }
/* This is for machines which generate the exact clock. */ /* This is for machines which generate the exact clock. */
...@@ -363,20 +368,24 @@ static unsigned long do_fast_gettimeoffset(void) ...@@ -363,20 +368,24 @@ static unsigned long do_fast_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned int flags; unsigned long flags;
unsigned long seq;
read_lock_irqsave (&xtime_lock, flags); do {
*tv = xtime; seq = read_seqbegin_irqsave(&xtime_lock, flags);
tv->tv_usec += do_fast_gettimeoffset();
/* *tv = xtime;
* xtime is atomically updated in timer_bh. jiffies - wall_jiffies tv->tv_usec += do_fast_gettimeoffset();
* is nonzero if the timer bottom half hasnt executed yet.
*/ /*
if (jiffies - wall_jiffies) * xtime is atomically updated in timer_bh.
tv->tv_usec += USECS_PER_JIFFY; * jiffies - wall_jiffies
* is nonzero if the timer bottom half hasnt executed yet.
*/
if (jiffies - wall_jiffies)
tv->tv_usec += USECS_PER_JIFFY;
read_unlock_irqrestore (&xtime_lock, flags); } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
if (tv->tv_usec >= 1000000) { if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000; tv->tv_usec -= 1000000;
...@@ -386,7 +395,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -386,7 +395,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq (&xtime_lock); write_seqlock_irq (&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec correctly. /* This is revolting. We need to set the xtime.tv_usec correctly.
* However, the value in this location is is value at the last tick. * However, the value in this location is is value at the last tick.
...@@ -406,5 +415,5 @@ void do_settimeofday(struct timeval *tv) ...@@ -406,5 +415,5 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq (&xtime_lock); write_sequnlock_irq (&xtime_lock);
} }
...@@ -19,12 +19,12 @@ ...@@ -19,12 +19,12 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <asm/tx3912.h> #include <asm/tx3912.h>
extern volatile unsigned long wall_jiffies; extern volatile unsigned long wall_jiffies;
extern rwlock_t xtime_lock;
static struct timeval xbase; static struct timeval xbase;
...@@ -62,29 +62,31 @@ void inline readRTC(unsigned long *high, unsigned long *low) ...@@ -62,29 +62,31 @@ void inline readRTC(unsigned long *high, unsigned long *low)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long high, low; unsigned long high, low;
read_lock_irqsave(&xtime_lock, flags); do {
// 40 bit RTC, driven by 32khz source: seq = read_seqbegin_irqsave(&xtime_lock, flags);
// +-----------+-----------------------------------------+
// | HHHH.HHHH | LLLL.LLLL.LLLL.LLLL.LMMM.MMMM.MMMM.MMMM |
// +-----------+-----------------------------------------+
readRTC(&high,&low);
tv->tv_sec = (high << 17) | (low >> 15);
tv->tv_usec = (low % 32768) * 1953 / 64;
tv->tv_sec += xbase.tv_sec;
tv->tv_usec += xbase.tv_usec;
tv->tv_usec += do_gettimeoffset(); // 40 bit RTC, driven by 32khz source:
// +-----------+-----------------------------------------+
// | HHHH.HHHH | LLLL.LLLL.LLLL.LLLL.LMMM.MMMM.MMMM.MMMM |
// +-----------+-----------------------------------------+
readRTC(&high,&low);
tv->tv_sec = (high << 17) | (low >> 15);
tv->tv_usec = (low % 32768) * 1953 / 64;
tv->tv_sec += xbase.tv_sec;
tv->tv_usec += xbase.tv_usec;
/* tv->tv_usec += do_gettimeoffset();
* xtime is atomically updated in timer_bh. lost_ticks is
* nonzero if the timer bottom half hasnt executed yet.
*/
if (jiffies - wall_jiffies)
tv->tv_usec += USECS_PER_JIFFY;
read_unlock_irqrestore(&xtime_lock, flags); /*
* xtime is atomically updated in timer_bh. lost_ticks is
* nonzero if the timer bottom half hasnt executed yet.
*/
if (jiffies - wall_jiffies)
tv->tv_usec += USECS_PER_JIFFY;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
if (tv->tv_usec >= 1000000) { if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000; tv->tv_usec -= 1000000;
...@@ -94,7 +96,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -94,7 +96,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec /* This is revolting. We need to set the xtime.tv_usec
* correctly. However, the value in this location is * correctly. However, the value in this location is
* is value at the last tick. * is value at the last tick.
...@@ -118,7 +120,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -118,7 +120,7 @@ void do_settimeofday(struct timeval *tv)
time_state = TIME_BAD; time_state = TIME_BAD;
time_maxerror = MAXPHASE; time_maxerror = MAXPHASE;
time_esterror = MAXPHASE; time_esterror = MAXPHASE;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
static int set_rtc_mmss(unsigned long nowtime) static int set_rtc_mmss(unsigned long nowtime)
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/time.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/mipsregs.h> #include <asm/mipsregs.h>
...@@ -44,7 +45,6 @@ unsigned long missed_heart_beats = 0; ...@@ -44,7 +45,6 @@ unsigned long missed_heart_beats = 0;
static unsigned long r4k_offset; /* Amount to increment compare reg each time */ static unsigned long r4k_offset; /* Amount to increment compare reg each time */
static unsigned long r4k_cur; /* What counter should be at next timer irq */ static unsigned long r4k_cur; /* What counter should be at next timer irq */
extern rwlock_t xtime_lock;
#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
...@@ -132,6 +132,8 @@ static int set_rtc_mmss(unsigned long nowtime) ...@@ -132,6 +132,8 @@ static int set_rtc_mmss(unsigned long nowtime)
*/ */
void mips_timer_interrupt(struct pt_regs *regs) void mips_timer_interrupt(struct pt_regs *regs)
{ {
unsigned long flags;
unsigned long seq;
int irq = 7; int irq = 7;
if (r4k_offset == 0) if (r4k_offset == 0)
...@@ -148,17 +150,20 @@ void mips_timer_interrupt(struct pt_regs *regs) ...@@ -148,17 +150,20 @@ void mips_timer_interrupt(struct pt_regs *regs)
* within 500ms before the * next second starts, * within 500ms before the * next second starts,
* thus the following code. * thus the following code.
*/ */
read_lock(&xtime_lock); do {
if ((time_status & STA_UNSYNC) == 0 seq = read_seqbegin_irqsave(&xtime_lock, flags);
&& xtime.tv_sec > last_rtc_update + 660
&& xtime.tv_usec >= 500000 - (tick >> 1) if ((time_status & STA_UNSYNC) == 0
&& xtime.tv_usec <= 500000 + (tick >> 1)) && xtime.tv_sec > last_rtc_update + 660
if (set_rtc_mmss(xtime.tv_sec) == 0) && xtime.tv_usec >= 500000 - (tick >> 1)
last_rtc_update = xtime.tv_sec; && xtime.tv_usec <= 500000 + (tick >> 1))
else if (set_rtc_mmss(xtime.tv_sec) == 0)
/* do it again in 60 s */ last_rtc_update = xtime.tv_sec;
last_rtc_update = xtime.tv_sec - 600; else
read_unlock(&xtime_lock); /* do it again in 60 s */
last_rtc_update = xtime.tv_sec - 600;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
if ((timer_tick_count++ % HZ) == 0) { if ((timer_tick_count++ % HZ) == 0) {
mips_display_message(&display_string[display_count++]); mips_display_message(&display_string[display_count++]);
...@@ -266,10 +271,10 @@ void __init time_init(void) ...@@ -266,10 +271,10 @@ void __init time_init(void)
set_cp0_status(ST0_IM, ALLINTS); set_cp0_status(ST0_IM, ALLINTS);
/* Read time from the RTC chipset. */ /* Read time from the RTC chipset. */
write_lock_irqsave (&xtime_lock, flags); write_seqlock_irqsave (&xtime_lock, flags);
xtime.tv_sec = get_mips_time(); xtime.tv_sec = get_mips_time();
xtime.tv_usec = 0; xtime.tv_usec = 0;
write_unlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
} }
/* This is for machines which generate the exact clock. */ /* This is for machines which generate the exact clock. */
...@@ -352,20 +357,25 @@ static unsigned long do_fast_gettimeoffset(void) ...@@ -352,20 +357,25 @@ static unsigned long do_fast_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned int flags; unsigned long flags;
unsigned long seq;
read_lock_irqsave (&xtime_lock, flags); do {
*tv = xtime; seq = read_seqbegin_irqsave(&xtime_lock, flags);
tv->tv_usec += do_fast_gettimeoffset();
/* *tv = xtime;
* xtime is atomically updated in timer_bh. jiffies - wall_jiffies tv->tv_usec += do_fast_gettimeoffset();
* is nonzero if the timer bottom half hasnt executed yet.
*/ /*
if (jiffies - wall_jiffies) * xtime is atomically updated in timer_bh.
tv->tv_usec += USECS_PER_JIFFY; * jiffies - wall_jiffies
* is nonzero if the timer bottom half hasnt executed yet.
*/
if (jiffies - wall_jiffies)
tv->tv_usec += USECS_PER_JIFFY;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
read_unlock_irqrestore (&xtime_lock, flags);
if (tv->tv_usec >= 1000000) { if (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000; tv->tv_usec -= 1000000;
...@@ -375,7 +385,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -375,7 +385,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq (&xtime_lock); write_seqlock_irq (&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec correctly. /* This is revolting. We need to set the xtime.tv_usec correctly.
* However, the value in this location is is value at the last tick. * However, the value in this location is is value at the last tick.
...@@ -395,5 +405,5 @@ void do_settimeofday(struct timeval *tv) ...@@ -395,5 +405,5 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq (&xtime_lock); write_sequnlock_irq (&xtime_lock);
} }
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/param.h> #include <linux/param.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/time.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
...@@ -32,8 +33,6 @@ ...@@ -32,8 +33,6 @@
static unsigned long r4k_offset; /* Amount to increment compare reg each time */ static unsigned long r4k_offset; /* Amount to increment compare reg each time */
static unsigned long r4k_cur; /* What counter should be at next timer irq */ static unsigned long r4k_cur; /* What counter should be at next timer irq */
extern rwlock_t xtime_lock;
static inline void ack_r4ktimer(unsigned long newval) static inline void ack_r4ktimer(unsigned long newval)
{ {
write_32bit_cp0_register(CP0_COMPARE, newval); write_32bit_cp0_register(CP0_COMPARE, newval);
...@@ -86,7 +85,7 @@ void indy_timer_interrupt(struct pt_regs *regs) ...@@ -86,7 +85,7 @@ void indy_timer_interrupt(struct pt_regs *regs)
unsigned long count; unsigned long count;
int irq = 7; int irq = 7;
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
/* Ack timer and compute new compare. */ /* Ack timer and compute new compare. */
count = read_32bit_cp0_register(CP0_COUNT); count = read_32bit_cp0_register(CP0_COUNT);
/* This has races. */ /* This has races. */
...@@ -116,7 +115,7 @@ void indy_timer_interrupt(struct pt_regs *regs) ...@@ -116,7 +115,7 @@ void indy_timer_interrupt(struct pt_regs *regs)
/* do it again in 60s */ /* do it again in 60s */
last_rtc_update = xtime.tv_sec - 600; last_rtc_update = xtime.tv_sec - 600;
} }
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
static unsigned long dosample(volatile unsigned char *tcwp, static unsigned long dosample(volatile unsigned char *tcwp,
...@@ -224,10 +223,10 @@ void __init indy_timer_init(void) ...@@ -224,10 +223,10 @@ void __init indy_timer_init(void)
set_cp0_status(ST0_IM, ALLINTS); set_cp0_status(ST0_IM, ALLINTS);
sti(); sti();
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
xtime.tv_sec = get_indy_time(); /* Read time from RTC. */ xtime.tv_sec = get_indy_time(); /* Read time from RTC. */
xtime.tv_usec = 0; xtime.tv_usec = 0;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
void indy_8254timer_irq(void) void indy_8254timer_irq(void)
...@@ -243,20 +242,21 @@ void indy_8254timer_irq(void) ...@@ -243,20 +242,21 @@ void indy_8254timer_irq(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long seq;
read_lock_irqsave(&xtime_lock, flags); do {
*tv = xtime; seq = read_seqbegin(&xtime_lock);
read_unlock_irqrestore(&xtime_lock, flags); *tv = xtime;
} while (read_seqretry(&xtime_lock, seq));
} }
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
xtime = *tv; xtime = *tv;
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel_stat.h> #include <linux/kernel_stat.h>
#include <linux/param.h> #include <linux/param.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/bcd.h> #include <linux/bcd.h>
...@@ -40,7 +41,6 @@ ...@@ -40,7 +41,6 @@
static unsigned long ct_cur[NR_CPUS]; /* What counter should be at next timer irq */ static unsigned long ct_cur[NR_CPUS]; /* What counter should be at next timer irq */
static long last_rtc_update; /* Last time the rtc clock got updated */ static long last_rtc_update; /* Last time the rtc clock got updated */
extern rwlock_t xtime_lock;
extern volatile unsigned long wall_jiffies; extern volatile unsigned long wall_jiffies;
...@@ -94,7 +94,7 @@ void rt_timer_interrupt(struct pt_regs *regs) ...@@ -94,7 +94,7 @@ void rt_timer_interrupt(struct pt_regs *regs)
int cpuA = ((cputoslice(cpu)) == 0); int cpuA = ((cputoslice(cpu)) == 0);
int irq = 7; /* XXX Assign number */ int irq = 7; /* XXX Assign number */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
again: again:
LOCAL_HUB_S(cpuA ? PI_RT_PEND_A : PI_RT_PEND_B, 0); /* Ack */ LOCAL_HUB_S(cpuA ? PI_RT_PEND_A : PI_RT_PEND_B, 0); /* Ack */
...@@ -145,7 +145,7 @@ void rt_timer_interrupt(struct pt_regs *regs) ...@@ -145,7 +145,7 @@ void rt_timer_interrupt(struct pt_regs *regs)
} }
} }
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
if (softirq_pending(cpu)) if (softirq_pending(cpu))
do_softirq(); do_softirq();
...@@ -162,17 +162,20 @@ void do_gettimeofday(struct timeval *tv) ...@@ -162,17 +162,20 @@ void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long usec, sec; unsigned long usec, sec;
unsigned long seq;
read_lock_irqsave(&xtime_lock, flags); do {
usec = do_gettimeoffset(); seq = read_seqbegin_irqsave(&xtime_lock, flags);
{
unsigned long lost = jiffies - wall_jiffies; usec = do_gettimeoffset();
if (lost) {
usec += lost * (1000000 / HZ); unsigned long lost = jiffies - wall_jiffies;
} if (lost)
sec = xtime.tv_sec; usec += lost * (1000000 / HZ);
usec += xtime.tv_usec; }
read_unlock_irqrestore(&xtime_lock, flags); sec = xtime.tv_sec;
usec += xtime.tv_usec;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -185,7 +188,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -185,7 +188,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
tv->tv_usec -= do_gettimeoffset(); tv->tv_usec -= do_gettimeoffset();
tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ); tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ);
...@@ -199,7 +202,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -199,7 +202,7 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
/* Includes for ioc3_init(). */ /* Includes for ioc3_init(). */
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/resource.h> #include <linux/resource.h>
#include <linux/times.h> #include <linux/times.h>
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
...@@ -2361,21 +2362,23 @@ asmlinkage int sys32_sysinfo(struct sysinfo32 *info) ...@@ -2361,21 +2362,23 @@ asmlinkage int sys32_sysinfo(struct sysinfo32 *info)
{ {
struct sysinfo val; struct sysinfo val;
int err; int err;
extern rwlock_t xtime_lock; unsigned long seq;
/* We don't need a memset here because we copy the /* We don't need a memset here because we copy the
* struct to userspace once element at a time. * struct to userspace once element at a time.
*/ */
read_lock_irq(&xtime_lock); do {
val.uptime = jiffies / HZ; seq = read_seqbegin(&xtime_lock);
val.uptime = jiffies / HZ;
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);
val.loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT); val.loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT);
val.procs = nr_threads;
} while (read_seqretry(&xtime_lock, seq));
val.procs = nr_threads;
read_unlock_irq(&xtime_lock);
si_meminfo(&val); si_meminfo(&val);
si_swapinfo(&val); si_swapinfo(&val);
......
...@@ -36,7 +36,6 @@ u64 jiffies_64; ...@@ -36,7 +36,6 @@ u64 jiffies_64;
/* xtime and wall_jiffies keep wall-clock time */ /* xtime and wall_jiffies keep wall-clock time */
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
extern rwlock_t xtime_lock;
static long clocktick; /* timer cycles per tick */ static long clocktick; /* timer cycles per tick */
static long halftick; static long halftick;
...@@ -115,9 +114,9 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -115,9 +114,9 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
smp_do_timer(regs); smp_do_timer(regs);
#endif #endif
if (cpu == 0) { if (cpu == 0) {
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
do_timer(regs); do_timer(regs);
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
} }
...@@ -172,16 +171,14 @@ gettimeoffset (void) ...@@ -172,16 +171,14 @@ gettimeoffset (void)
void void
do_gettimeofday (struct timeval *tv) do_gettimeofday (struct timeval *tv)
{ {
unsigned long flags, usec, sec; unsigned long flags, seq, usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
{ seq = read_seqbegin_irqsave(&xtime_lock, flags);
usec = gettimeoffset(); usec = gettimeoffset();
sec = xtime.tv_sec; sec = xtime.tv_sec;
usec += (xtime.tv_nsec / 1000); usec += (xtime.tv_nsec / 1000);
} } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
read_unlock_irqrestore(&xtime_lock, flags);
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -195,7 +192,7 @@ do_gettimeofday (struct timeval *tv) ...@@ -195,7 +192,7 @@ do_gettimeofday (struct timeval *tv)
void void
do_settimeofday (struct timeval *tv) do_settimeofday (struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
{ {
/* /*
* This is revolting. We need to set "xtime" * This is revolting. We need to set "xtime"
...@@ -219,7 +216,7 @@ do_settimeofday (struct timeval *tv) ...@@ -219,7 +216,7 @@ do_settimeofday (struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
} }
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
...@@ -241,10 +238,10 @@ void __init time_init(void) ...@@ -241,10 +238,10 @@ void __init time_init(void)
mtctl(next_tick, 16); mtctl(next_tick, 16);
if(pdc_tod_read(&tod_data) == 0) { if(pdc_tod_read(&tod_data) == 0) {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
xtime.tv_sec = tod_data.tod_sec; xtime.tv_sec = tod_data.tod_sec;
xtime.tv_nsec = tod_data.tod_usec * 1000; xtime.tv_nsec = tod_data.tod_usec * 1000;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} else { } else {
printk(KERN_ERR "Error reading tod clock\n"); printk(KERN_ERR "Error reading tod clock\n");
xtime.tv_sec = 0; xtime.tv_sec = 0;
......
...@@ -76,7 +76,6 @@ extern struct timezone sys_tz; ...@@ -76,7 +76,6 @@ extern struct timezone sys_tz;
/* keep track of when we need to update the rtc */ /* keep track of when we need to update the rtc */
time_t last_rtc_update; time_t last_rtc_update;
extern rwlock_t xtime_lock;
/* The decrementer counts down by 128 every 128ns on a 601. */ /* The decrementer counts down by 128 every 128ns on a 601. */
#define DECREMENTER_COUNT_601 (1000000000 / HZ) #define DECREMENTER_COUNT_601 (1000000000 / HZ)
...@@ -161,7 +160,7 @@ void timer_interrupt(struct pt_regs * regs) ...@@ -161,7 +160,7 @@ void timer_interrupt(struct pt_regs * regs)
continue; continue;
/* We are in an interrupt, no need to save/restore flags */ /* We are in an interrupt, no need to save/restore flags */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
tb_last_stamp = jiffy_stamp; tb_last_stamp = jiffy_stamp;
do_timer(regs); do_timer(regs);
...@@ -191,7 +190,7 @@ void timer_interrupt(struct pt_regs * regs) ...@@ -191,7 +190,7 @@ void timer_interrupt(struct pt_regs * regs)
/* Try again one minute later */ /* Try again one minute later */
last_rtc_update += 60; last_rtc_update += 60;
} }
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
if ( !disarm_decr[smp_processor_id()] ) if ( !disarm_decr[smp_processor_id()] )
set_dec(next_dec); set_dec(next_dec);
...@@ -213,21 +212,23 @@ void timer_interrupt(struct pt_regs * regs) ...@@ -213,21 +212,23 @@ void timer_interrupt(struct pt_regs * regs)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned delta, lost_ticks, usec, sec; unsigned delta, lost_ticks, usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
sec = xtime.tv_sec; seq = read_seqbegin_irqsave(&xtime_lock, flags);
usec = (xtime.tv_nsec / 1000); sec = xtime.tv_sec;
delta = tb_ticks_since(tb_last_stamp); usec = (xtime.tv_nsec / 1000);
delta = tb_ticks_since(tb_last_stamp);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
/* As long as timebases are not in sync, gettimeofday can only /* As long as timebases are not in sync, gettimeofday can only
* have jiffy resolution on SMP. * have jiffy resolution on SMP.
*/ */
if (!smp_tb_synchronized) if (!smp_tb_synchronized)
delta = 0; delta = 0;
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
lost_ticks = jiffies - wall_jiffies; lost_ticks = jiffies - wall_jiffies;
read_unlock_irqrestore(&xtime_lock, flags); } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
usec += mulhwu(tb_to_us, tb_ticks_per_jiffy * lost_ticks + delta); usec += mulhwu(tb_to_us, tb_ticks_per_jiffy * lost_ticks + delta);
while (usec >= 1000000) { while (usec >= 1000000) {
...@@ -243,7 +244,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -243,7 +244,7 @@ void do_settimeofday(struct timeval *tv)
unsigned long flags; unsigned long flags;
int tb_delta, new_usec, new_sec; int tb_delta, new_usec, new_sec;
write_lock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
/* Updating the RTC is not the job of this code. If the time is /* Updating the RTC is not the job of this code. If the time is
* stepped under NTP, the RTC will be update after STA_UNSYNC * stepped under NTP, the RTC will be update after STA_UNSYNC
* is cleared. Tool like clock/hwclock either copy the RTC * is cleared. Tool like clock/hwclock either copy the RTC
...@@ -283,7 +284,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -283,7 +284,7 @@ void do_settimeofday(struct timeval *tv)
time_state = TIME_ERROR; /* p. 24, (a) */ time_state = TIME_ERROR; /* p. 24, (a) */
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
} }
/* This function is only called on the boot processor */ /* This function is only called on the boot processor */
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/time.h>
#include <linux/adb.h> #include <linux/adb.h>
#include <linux/cuda.h> #include <linux/cuda.h>
#include <linux/pmu.h> #include <linux/pmu.h>
...@@ -29,8 +30,6 @@ ...@@ -29,8 +30,6 @@
#include <asm/time.h> #include <asm/time.h>
#include <asm/nvram.h> #include <asm/nvram.h>
extern rwlock_t xtime_lock;
/* Apparently the RTC stores seconds since 1 Jan 1904 */ /* Apparently the RTC stores seconds since 1 Jan 1904 */
#define RTC_OFFSET 2082844800 #define RTC_OFFSET 2082844800
...@@ -215,19 +214,21 @@ time_sleep_notify(struct pmu_sleep_notifier *self, int when) ...@@ -215,19 +214,21 @@ time_sleep_notify(struct pmu_sleep_notifier *self, int when)
{ {
static unsigned long time_diff; static unsigned long time_diff;
unsigned long flags; unsigned long flags;
unsigned long seq;
switch (when) { switch (when) {
case PBOOK_SLEEP_NOW: case PBOOK_SLEEP_NOW:
read_lock_irqsave(&xtime_lock, flags); do {
time_diff = xtime.tv_sec - pmac_get_rtc_time(); seq = read_seqbegin_irqsave(&xtime_lock, flags);
read_unlock_irqrestore(&xtime_lock, flags); time_diff = xtime.tv_sec - pmac_get_rtc_time();
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
break; break;
case PBOOK_WAKE: case PBOOK_WAKE:
write_lock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
xtime.tv_sec = pmac_get_rtc_time() + time_diff; xtime.tv_sec = pmac_get_rtc_time() + time_diff;
xtime.tv_nsec = 0; xtime.tv_nsec = 0;
last_rtc_update = xtime.tv_sec; last_rtc_update = xtime.tv_sec;
write_unlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
break; break;
} }
return PBOOK_SLEEP_OK; return PBOOK_SLEEP_OK;
......
...@@ -69,7 +69,6 @@ u64 jiffies_64; ...@@ -69,7 +69,6 @@ u64 jiffies_64;
/* keep track of when we need to update the rtc */ /* keep track of when we need to update the rtc */
time_t last_rtc_update; time_t last_rtc_update;
extern rwlock_t xtime_lock;
extern int piranha_simulator; extern int piranha_simulator;
#ifdef CONFIG_PPC_ISERIES #ifdef CONFIG_PPC_ISERIES
unsigned long iSeries_recal_titan = 0; unsigned long iSeries_recal_titan = 0;
...@@ -284,12 +283,12 @@ int timer_interrupt(struct pt_regs * regs) ...@@ -284,12 +283,12 @@ int timer_interrupt(struct pt_regs * regs)
smp_local_timer_interrupt(regs); smp_local_timer_interrupt(regs);
#endif #endif
if (cpu == boot_cpuid) { if (cpu == boot_cpuid) {
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
tb_last_stamp = lpaca->next_jiffy_update_tb; tb_last_stamp = lpaca->next_jiffy_update_tb;
do_timer(regs); do_timer(regs);
timer_sync_xtime( cur_tb ); timer_sync_xtime( cur_tb );
timer_check_rtc(); timer_check_rtc();
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
if ( adjusting_time && (time_adjust == 0) ) if ( adjusting_time && (time_adjust == 0) )
ppc_adjtimex(); ppc_adjtimex();
} }
...@@ -348,7 +347,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -348,7 +347,7 @@ void do_settimeofday(struct timeval *tv)
long int tb_delta, new_usec, new_sec; long int tb_delta, new_usec, new_sec;
unsigned long new_xsec; unsigned long new_xsec;
write_lock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
/* Updating the RTC is not the job of this code. If the time is /* Updating the RTC is not the job of this code. If the time is
* stepped under NTP, the RTC will be update after STA_UNSYNC * stepped under NTP, the RTC will be update after STA_UNSYNC
* is cleared. Tool like clock/hwclock either copy the RTC * is cleared. Tool like clock/hwclock either copy the RTC
...@@ -399,7 +398,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -399,7 +398,7 @@ void do_settimeofday(struct timeval *tv)
do_gtod.tb_orig_stamp = tb_last_stamp; do_gtod.tb_orig_stamp = tb_last_stamp;
} }
write_unlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
} }
/* /*
...@@ -465,7 +464,7 @@ void __init time_init(void) ...@@ -465,7 +464,7 @@ void __init time_init(void)
#endif #endif
ppc_md.get_boot_time(&tm); ppc_md.get_boot_time(&tm);
write_lock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
xtime.tv_sec = mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, xtime.tv_sec = mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec); tm.tm_hour, tm.tm_min, tm.tm_sec);
tb_last_stamp = get_tb(); tb_last_stamp = get_tb();
...@@ -484,7 +483,7 @@ void __init time_init(void) ...@@ -484,7 +483,7 @@ void __init time_init(void)
xtime.tv_nsec = 0; xtime.tv_nsec = 0;
last_rtc_update = xtime.tv_sec; last_rtc_update = xtime.tv_sec;
write_unlock_irqrestore(&xtime_lock, flags); write_sequnlock_irqrestore(&xtime_lock, flags);
/* Not exact, but the timer interrupt takes care of this */ /* Not exact, but the timer interrupt takes care of this */
set_dec(tb_ticks_per_jiffy); set_dec(tb_ticks_per_jiffy);
...@@ -587,7 +586,7 @@ void ppc_adjtimex(void) ...@@ -587,7 +586,7 @@ void ppc_adjtimex(void)
new_tb_to_xs = divres.result_low; new_tb_to_xs = divres.result_low;
new_xsec = mulhdu( tb_ticks, new_tb_to_xs ); new_xsec = mulhdu( tb_ticks, new_tb_to_xs );
write_lock_irqsave( &xtime_lock, flags ); write_seqlock_irqsave( &xtime_lock, flags );
old_xsec = mulhdu( tb_ticks, do_gtod.varp->tb_to_xs ); old_xsec = mulhdu( tb_ticks, do_gtod.varp->tb_to_xs );
new_stamp_xsec = do_gtod.varp->stamp_xsec + old_xsec - new_xsec; new_stamp_xsec = do_gtod.varp->stamp_xsec + old_xsec - new_xsec;
...@@ -609,7 +608,7 @@ void ppc_adjtimex(void) ...@@ -609,7 +608,7 @@ void ppc_adjtimex(void)
do_gtod.varp = temp_varp; do_gtod.varp = temp_varp;
do_gtod.var_idx = temp_idx; do_gtod.var_idx = temp_idx;
write_unlock_irqrestore( &xtime_lock, flags ); write_sequnlock_irqrestore( &xtime_lock, flags );
} }
......
...@@ -52,7 +52,6 @@ static ext_int_info_t ext_int_info_timer; ...@@ -52,7 +52,6 @@ static ext_int_info_t ext_int_info_timer;
static uint64_t xtime_cc; static uint64_t xtime_cc;
static uint64_t init_timer_cc; static uint64_t init_timer_cc;
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
void tod_to_timeval(__u64 todval, struct timespec *xtime) void tod_to_timeval(__u64 todval, struct timespec *xtime)
...@@ -83,12 +82,15 @@ static inline unsigned long do_gettimeoffset(void) ...@@ -83,12 +82,15 @@ static inline unsigned long do_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
sec = xtime.tv_sec; seq = read_seqbegin_irqsave(&xtime_lock, flags);
usec = xtime.tv_nsec / 1000 + do_gettimeoffset();
read_unlock_irqrestore(&xtime_lock, flags); sec = xtime.tv_sec;
usec = xtime.tv_nsec / 1000 + do_gettimeoffset();
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -102,7 +104,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -102,7 +104,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* This is revolting. We need to set the xtime.tv_nsec /* This is revolting. We need to set the xtime.tv_nsec
* correctly. However, the value in this location is * correctly. However, the value in this location is
* is value at the last tick. * is value at the last tick.
...@@ -122,7 +124,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -122,7 +124,7 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
static inline __u32 div64_32(__u64 dividend, __u32 divisor) static inline __u32 div64_32(__u64 dividend, __u32 divisor)
...@@ -166,7 +168,7 @@ static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code) ...@@ -166,7 +168,7 @@ static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
* Do not rely on the boot cpu to do the calls to do_timer. * Do not rely on the boot cpu to do the calls to do_timer.
* Spread it over all cpus instead. * Spread it over all cpus instead.
*/ */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
if (S390_lowcore.jiffy_timer > xtime_cc) { if (S390_lowcore.jiffy_timer > xtime_cc) {
__u32 xticks; __u32 xticks;
...@@ -181,7 +183,7 @@ static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code) ...@@ -181,7 +183,7 @@ static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
while (xticks--) while (xticks--)
do_timer(regs); do_timer(regs);
} }
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
while (ticks--) while (ticks--)
update_process_times(user_mode(regs)); update_process_times(user_mode(regs));
#else #else
......
...@@ -51,7 +51,6 @@ static ext_int_info_t ext_int_info_timer; ...@@ -51,7 +51,6 @@ static ext_int_info_t ext_int_info_timer;
static uint64_t xtime_cc; static uint64_t xtime_cc;
static uint64_t init_timer_cc; static uint64_t init_timer_cc;
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
void tod_to_timeval(__u64 todval, struct timespec *xtime) void tod_to_timeval(__u64 todval, struct timespec *xtime)
...@@ -78,12 +77,14 @@ static inline unsigned long do_gettimeoffset(void) ...@@ -78,12 +77,14 @@ static inline unsigned long do_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
sec = xtime.tv_sec; seq = read_seqbegin_irqsave(&xtime_lock, flags);
usec = xtime.tv_nsec + do_gettimeoffset(); sec = xtime.tv_sec;
read_unlock_irqrestore(&xtime_lock, flags); usec = xtime.tv_nsec + do_gettimeoffset();
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -97,7 +98,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -97,7 +98,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
/* This is revolting. We need to set the xtime.tv_usec /* This is revolting. We need to set the xtime.tv_usec
* correctly. However, the value in this location is * correctly. However, the value in this location is
* is value at the last tick. * is value at the last tick.
...@@ -117,7 +118,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -117,7 +118,7 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
/* /*
...@@ -152,7 +153,7 @@ static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code) ...@@ -152,7 +153,7 @@ static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
* Do not rely on the boot cpu to do the calls to do_timer. * Do not rely on the boot cpu to do the calls to do_timer.
* Spread it over all cpus instead. * Spread it over all cpus instead.
*/ */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
if (S390_lowcore.jiffy_timer > xtime_cc) { if (S390_lowcore.jiffy_timer > xtime_cc) {
__u32 xticks; __u32 xticks;
...@@ -167,7 +168,7 @@ static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code) ...@@ -167,7 +168,7 @@ static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
while (xticks--) while (xticks--)
do_timer(regs); do_timer(regs);
} }
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
while (ticks--) while (ticks--)
update_process_times(user_mode(regs)); update_process_times(user_mode(regs));
#else #else
......
...@@ -72,7 +72,6 @@ ...@@ -72,7 +72,6 @@
u64 jiffies_64; u64 jiffies_64;
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
#define TICK_SIZE tick #define TICK_SIZE tick
...@@ -128,18 +127,20 @@ static unsigned long do_gettimeoffset(void) ...@@ -128,18 +127,20 @@ static unsigned long do_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
usec = do_gettimeoffset(); seq = read_seqbegin_irqsave(&xtime_lock, flags);
{ usec = do_gettimeoffset();
unsigned long lost = jiffies - wall_jiffies; {
if (lost) unsigned long lost = jiffies - wall_jiffies;
usec += lost * (1000000 / HZ); if (lost)
} usec += lost * (1000000 / HZ);
sec = xtime.tv_sec; }
usec += xtime.tv_usec; sec = xtime.tv_sec;
read_unlock_irqrestore(&xtime_lock, flags); usec += xtime.tv_usec;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -152,7 +153,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -152,7 +153,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_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
* value in this location is the value at the most recent update of * value in this location is the value at the most recent update of
...@@ -172,7 +173,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -172,7 +173,7 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
/* last time the RTC clock got updated */ /* last time the RTC clock got updated */
...@@ -231,9 +232,9 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -231,9 +232,9 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* the irq version of write_lock because as just said we have irq * the irq version of write_lock because as just said we have irq
* locally disabled. -arca * locally disabled. -arca
*/ */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
do_timer_interrupt(irq, NULL, regs); do_timer_interrupt(irq, NULL, regs);
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
static unsigned int __init get_timer_frequency(void) static unsigned int __init get_timer_frequency(void)
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -34,8 +35,6 @@ ...@@ -34,8 +35,6 @@
#include <asm/timer.h> #include <asm/timer.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
extern rwlock_t xtime_lock;
#ifndef CONFIG_PCI #ifndef CONFIG_PCI
asmlinkage int sys_pciconfig_read(unsigned long bus, asmlinkage int sys_pciconfig_read(unsigned long bus,
...@@ -739,10 +738,10 @@ static void pcic_clear_clock_irq(void) ...@@ -739,10 +738,10 @@ static void pcic_clear_clock_irq(void)
static void pcic_timer_handler (int irq, void *h, struct pt_regs *regs) static void pcic_timer_handler (int irq, void *h, struct pt_regs *regs)
{ {
write_lock(&xtime_lock); /* Dummy, to show that we remember */ write_seqlock(&xtime_lock); /* Dummy, to show that we remember */
pcic_clear_clock_irq(); pcic_clear_clock_irq();
do_timer(regs); do_timer(regs);
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
#define USECS_PER_JIFFY 10000 /* We have 100HZ "standard" timer for sparc */ #define USECS_PER_JIFFY 10000 /* We have 100HZ "standard" timer for sparc */
...@@ -795,18 +794,20 @@ extern unsigned long wall_jiffies; ...@@ -795,18 +794,20 @@ extern unsigned long wall_jiffies;
static void pci_do_gettimeofday(struct timeval *tv) static void pci_do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
usec = do_gettimeoffset(); seq = read_seqbegin_irqsave(&xtime_lock, flags);
{ usec = do_gettimeoffset();
unsigned long lost = jiffies - wall_jiffies; {
if (lost) unsigned long lost = jiffies - wall_jiffies;
usec += lost * (1000000 / HZ); if (lost)
} usec += lost * (1000000 / HZ);
sec = xtime.tv_sec; }
usec += (xtime.tv_nsec / 1000); sec = xtime.tv_sec;
read_unlock_irqrestore(&xtime_lock, flags); usec += (xtime.tv_nsec / 1000);
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/pci.h> #include <linux/pci.h>
...@@ -42,8 +43,6 @@ ...@@ -42,8 +43,6 @@
#include <asm/page.h> #include <asm/page.h>
#include <asm/pcic.h> #include <asm/pcic.h>
extern rwlock_t xtime_lock;
extern unsigned long wall_jiffies; extern unsigned long wall_jiffies;
u64 jiffies_64; u64 jiffies_64;
...@@ -131,7 +130,7 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -131,7 +130,7 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
#endif #endif
/* Protect counter clear so that do_gettimeoffset works */ /* Protect counter clear so that do_gettimeoffset works */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
#ifdef CONFIG_SUN4 #ifdef CONFIG_SUN4
if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) || if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) ||
(idprom->id_machtype == (SM_SUN4 | SM_4_110))) { (idprom->id_machtype == (SM_SUN4 | SM_4_110))) {
...@@ -155,7 +154,7 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -155,7 +154,7 @@ void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
else else
last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
} }
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ /* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */
...@@ -470,18 +469,20 @@ extern __inline__ unsigned long do_gettimeoffset(void) ...@@ -470,18 +469,20 @@ extern __inline__ unsigned long do_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
usec = do_gettimeoffset(); seq = read_seqbegin_irqsave(&xtime_lock, flags);
{ usec = do_gettimeoffset();
unsigned long lost = jiffies - wall_jiffies; {
if (lost) unsigned long lost = jiffies - wall_jiffies;
usec += lost * (1000000 / HZ); if (lost)
} usec += lost * (1000000 / HZ);
sec = xtime.tv_sec; }
usec += (xtime.tv_nsec / 1000); sec = xtime.tv_sec;
read_unlock_irqrestore(&xtime_lock, flags); usec += (xtime.tv_nsec / 1000);
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -494,9 +495,9 @@ void do_gettimeofday(struct timeval *tv) ...@@ -494,9 +495,9 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
bus_do_settimeofday(tv); bus_do_settimeofday(tv);
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
static void sbus_do_settimeofday(struct timeval *tv) static void sbus_do_settimeofday(struct timeval *tv)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/ioport.h> #include <linux/ioport.h>
...@@ -37,8 +38,6 @@ ...@@ -37,8 +38,6 @@
#include <asm/isa.h> #include <asm/isa.h>
#include <asm/starfire.h> #include <asm/starfire.h>
extern rwlock_t xtime_lock;
spinlock_t mostek_lock = SPIN_LOCK_UNLOCKED; spinlock_t mostek_lock = SPIN_LOCK_UNLOCKED;
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
unsigned long mstk48t02_regs = 0UL; unsigned long mstk48t02_regs = 0UL;
...@@ -134,7 +133,7 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -134,7 +133,7 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{ {
unsigned long ticks, pstate; unsigned long ticks, pstate;
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
do { do {
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
...@@ -196,13 +195,13 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) ...@@ -196,13 +195,13 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs)
timer_check_rtc(); timer_check_rtc();
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
void timer_tick_interrupt(struct pt_regs *regs) void timer_tick_interrupt(struct pt_regs *regs)
{ {
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
do_timer(regs); do_timer(regs);
...@@ -225,7 +224,7 @@ void timer_tick_interrupt(struct pt_regs *regs) ...@@ -225,7 +224,7 @@ void timer_tick_interrupt(struct pt_regs *regs)
timer_check_rtc(); timer_check_rtc();
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
#endif #endif
...@@ -665,7 +664,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -665,7 +664,7 @@ void do_settimeofday(struct timeval *tv)
if (this_is_starfire) if (this_is_starfire)
return; return;
write_lock_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
* value in this location is the value at the most recent update of * value in this location is the value at the most recent update of
...@@ -686,7 +685,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -686,7 +685,7 @@ void do_settimeofday(struct timeval *tv)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
/* Ok, my cute asm atomicity trick doesn't work anymore. /* Ok, my cute asm atomicity trick doesn't work anymore.
...@@ -696,18 +695,20 @@ void do_settimeofday(struct timeval *tv) ...@@ -696,18 +695,20 @@ void do_settimeofday(struct timeval *tv)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags; unsigned long flags;
unsigned long seq;
unsigned long usec, sec; unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags); do {
usec = do_gettimeoffset(); seq = read_seqbegin_irqsave(&xtime_lock, flags);
{ usec = do_gettimeoffset();
unsigned long lost = jiffies - wall_jiffies; {
if (lost) unsigned long lost = jiffies - wall_jiffies;
usec += lost * (1000000 / HZ); if (lost)
} usec += lost * (1000000 / HZ);
sec = xtime.tv_sec; }
usec += (xtime.tv_nsec / 1000); sec = xtime.tv_sec;
read_unlock_irqrestore(&xtime_lock, flags); usec += (xtime.tv_nsec / 1000);
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "linux/unistd.h" #include "linux/unistd.h"
#include "linux/stddef.h" #include "linux/stddef.h"
#include "linux/spinlock.h" #include "linux/spinlock.h"
#include "linux/time.h"
#include "linux/sched.h" #include "linux/sched.h"
#include "linux/interrupt.h" #include "linux/interrupt.h"
#include "linux/init.h" #include "linux/init.h"
...@@ -21,8 +22,6 @@ ...@@ -21,8 +22,6 @@
u64 jiffies_64; u64 jiffies_64;
extern rwlock_t xtime_lock;
int hz(void) int hz(void)
{ {
return(HZ); return(HZ);
...@@ -57,9 +56,9 @@ void boot_timer_handler(int sig) ...@@ -57,9 +56,9 @@ void boot_timer_handler(int sig)
void um_timer(int irq, void *dev, struct pt_regs *regs) void um_timer(int irq, void *dev, struct pt_regs *regs)
{ {
do_timer(regs); do_timer(regs);
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
timer(); timer();
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
long um_time(int * tloc) long um_time(int * tloc)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/profile.h> #include <linux/profile.h>
...@@ -107,8 +108,6 @@ static void timer_interrupt (int irq, void *dummy, struct pt_regs *regs) ...@@ -107,8 +108,6 @@ static void timer_interrupt (int irq, void *dummy, struct pt_regs *regs)
#endif /* 0 */ #endif /* 0 */
} }
extern rwlock_t xtime_lock;
/* /*
* This version of gettimeofday has near microsecond resolution. * This version of gettimeofday has near microsecond resolution.
*/ */
...@@ -120,21 +119,24 @@ void do_gettimeofday (struct timeval *tv) ...@@ -120,21 +119,24 @@ void do_gettimeofday (struct timeval *tv)
#endif #endif
unsigned long flags; unsigned long flags;
unsigned long usec, sec; unsigned long usec, sec;
unsigned long seq;
do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
read_lock_irqsave (&xtime_lock, flags);
#if 0 #if 0
usec = mach_gettimeoffset ? mach_gettimeoffset () : 0; usec = mach_gettimeoffset ? mach_gettimeoffset () : 0;
#else #else
usec = 0; usec = 0;
#endif #endif
#if 0 /* DAVIDM later if possible */ #if 0 /* DAVIDM later if possible */
lost = lost_ticks; lost = lost_ticks;
if (lost) if (lost)
usec += lost * (1000000/HZ); usec += lost * (1000000/HZ);
#endif #endif
sec = xtime.tv_sec; sec = xtime.tv_sec;
usec += xtime.tv_nsec / 1000; usec += xtime.tv_nsec / 1000;
read_unlock_irqrestore (&xtime_lock, flags); } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (usec >= 1000000) { while (usec >= 1000000) {
usec -= 1000000; usec -= 1000000;
...@@ -147,7 +149,7 @@ void do_gettimeofday (struct timeval *tv) ...@@ -147,7 +149,7 @@ void do_gettimeofday (struct timeval *tv)
void do_settimeofday (struct timeval *tv) void do_settimeofday (struct timeval *tv)
{ {
write_lock_irq (&xtime_lock); write_seqlock_irq (&xtime_lock);
/* This is revolting. We need to set the xtime.tv_nsec /* This is revolting. We need to set the xtime.tv_nsec
* correctly. However, the value in this location is * correctly. However, the value in this location is
...@@ -172,7 +174,7 @@ void do_settimeofday (struct timeval *tv) ...@@ -172,7 +174,7 @@ void do_settimeofday (struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq (&xtime_lock); write_sequnlock_irq (&xtime_lock);
} }
static int timer_dev_id; static int timer_dev_id;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/mc146818rtc.h> #include <linux/mc146818rtc.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/time.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -27,7 +28,6 @@ ...@@ -27,7 +28,6 @@
u64 jiffies_64; u64 jiffies_64;
extern rwlock_t xtime_lock;
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
unsigned int cpu_khz; /* TSC clocks / usec, not used here */ unsigned int cpu_khz; /* TSC clocks / usec, not used here */
...@@ -70,21 +70,22 @@ inline unsigned int do_gettimeoffset(void) ...@@ -70,21 +70,22 @@ inline unsigned int do_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv) void do_gettimeofday(struct timeval *tv)
{ {
unsigned long flags, t; unsigned long flags, t, seq;
unsigned int sec, usec; unsigned int sec, usec;
read_lock_irqsave(&xtime_lock, flags); spin_lock_irqsave(&time_offset_lock, flags);
spin_lock(&time_offset_lock); do {
seq = read_seqbegin(&xtime_lock);
sec = xtime.tv_sec; sec = xtime.tv_sec;
usec = xtime.tv_nsec / 1000; usec = xtime.tv_nsec / 1000;
t = (jiffies - wall_jiffies) * (1000000L / HZ) + do_gettimeoffset(); t = (jiffies - wall_jiffies) * (1000000L / HZ) + do_gettimeoffset();
if (t > timeoffset) timeoffset = t; if (t > timeoffset) timeoffset = t;
usec += timeoffset; usec += timeoffset;
spin_unlock(&time_offset_lock); } while (read_seqretry(&xtime_lock, seq));
read_unlock_irqrestore(&xtime_lock, flags); spin_unlock_irqrestore(&time_offset_lock, flags);
tv->tv_sec = sec + usec / 1000000; tv->tv_sec = sec + usec / 1000000;
tv->tv_usec = usec % 1000000; tv->tv_usec = usec % 1000000;
...@@ -98,7 +99,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -98,7 +99,7 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv) void do_settimeofday(struct timeval *tv)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
vxtime_lock(); vxtime_lock();
tv->tv_usec -= do_gettimeoffset() + tv->tv_usec -= do_gettimeoffset() +
...@@ -118,7 +119,7 @@ void do_settimeofday(struct timeval *tv) ...@@ -118,7 +119,7 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT; time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
/* /*
...@@ -201,7 +202,7 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -201,7 +202,7 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
* variables, because both do_timer() and us change them -arca+vojtech * variables, because both do_timer() and us change them -arca+vojtech
*/ */
write_lock(&xtime_lock); write_seqlock(&xtime_lock);
vxtime_lock(); vxtime_lock();
{ {
...@@ -251,7 +252,7 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -251,7 +252,7 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
} }
vxtime_unlock(); vxtime_unlock();
write_unlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
unsigned long get_cmos_time(void) unsigned long get_cmos_time(void)
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/seqlock.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/param.h> /* for HZ */ #include <asm/param.h> /* for HZ */
...@@ -17,13 +18,15 @@ extern unsigned long volatile jiffies; ...@@ -17,13 +18,15 @@ extern unsigned long volatile jiffies;
static inline u64 get_jiffies_64(void) static inline u64 get_jiffies_64(void)
{ {
#if BITS_PER_LONG < 64 #if BITS_PER_LONG < 64
extern rwlock_t xtime_lock; extern seqlock_t xtime_lock;
unsigned long flags; unsigned long seq;
u64 tmp; u64 tmp;
read_lock_irqsave(&xtime_lock, flags); do {
tmp = jiffies_64; seq = read_seqbegin(&xtime_lock);
read_unlock_irqrestore(&xtime_lock, flags); tmp = jiffies_64;
} while (read_seqretry(&xtime_lock, seq));
return tmp; return tmp;
#else #else
return (u64)jiffies; return (u64)jiffies;
......
#ifndef __LINUX_SEQLOCK_H
#define __LINUX_SEQLOCK_H
/*
* Reader/writer consistent mechanism without starving writers. This type of
* lock for data where the reader wants a consitent set of information
* and is willing to retry if the information changes. Readers never
* block but they may have to retry if a writer is in
* progress. Writers do not wait for readers.
*
* This is not as cache friendly as brlock. Also, this will not work
* for data that contains pointers, because any writer could
* invalidate a pointer that a reader was following.
*
* Expected reader usage:
* do {
* seq = read_seqbegin(&foo);
* ...
* } while (read_seqretry(&foo, seq));
*
*
* On non-SMP the spin locks disappear but the writer still needs
* to increment the sequence variables because an interrupt routine could
* change the state of the data.
*
* Based on x86_64 vsyscall gettimeofday
* by Keith Owens and Andrea Arcangeli
*/
#include <linux/config.h>
#include <linux/spinlock.h>
#include <linux/preempt.h>
typedef struct {
unsigned sequence;
spinlock_t lock;
} seqlock_t;
/*
* These macros triggered gcc-3.x compile-time problems. We think these are
* OK now. Be cautious.
*/
#define SEQLOCK_UNLOCKED { 0, SPIN_LOCK_UNLOCKED }
#define seqlock_init(x) do { *(x) = (seqlock_t) SEQLOCK_UNLOCKED; } while (0)
/* Lock out other writers and update the count.
* Acts like a normal spin_lock/unlock.
* Don't need preempt_disable() because that is in the spin_lock already.
*/
static inline void write_seqlock(seqlock_t *sl)
{
spin_lock(&sl->lock);
++sl->sequence;
smp_wmb();
}
static inline void write_sequnlock(seqlock_t *sl)
{
smp_wmb();
sl->sequence++;
spin_unlock(&sl->lock);
}
static inline int write_tryseqlock(seqlock_t *sl)
{
int ret = spin_trylock(&sl->lock);
if (ret) {
++sl->sequence;
smp_wmb();
}
return ret;
}
/* Start of read calculation -- fetch last complete writer token */
static inline unsigned read_seqbegin(const seqlock_t *sl)
{
unsigned ret = sl->sequence;
smp_rmb();
return ret;
}
/* Test if reader processed invalid data.
* If initial values is odd,
* then writer had already started when section was entered
* If sequence value changed
* then writer changed data while in section
*
* Using xor saves one conditional branch.
*/
static inline int read_seqretry(const seqlock_t *sl, unsigned iv)
{
smp_rmb();
return (iv & 1) | (sl->sequence ^ iv);
}
/*
* Possible sw/hw IRQ protected versions of the interfaces.
*/
#define write_seqlock_irqsave(lock, flags) \
do { local_irq_save(flags); write_seqlock(lock); } while (0)
#define write_seqlock_irq(lock) \
do { local_irq_disable(); write_seqlock(lock); } while (0)
#define write_seqlock_bh(lock) \
do { local_bh_disable(); write_seqlock(lock); } while (0)
#define write_sequnlock_irqrestore(lock, flags) \
do { write_sequnlock(lock); local_irq_restore(flags); } while(0)
#define write_sequnlock_irq(lock) \
do { write_sequnlock(lock); local_irq_enable(); } while(0)
#define write_sequnlock_bh(lock) \
do { write_sequnlock(lock); local_bh_enable(); } while(0)
#define read_seqbegin_irqsave(lock, flags) \
({ local_irq_save(flags); read_seqbegin(lock); })
#define read_seqretry_irqrestore(lock, iv, flags) \
({int ret = read_seqretry(&(lock)->seq, iv); \
local_irq_restore(flags); \
ret; \
})
#endif /* __LINUX_SEQLOCK_H */
...@@ -25,6 +25,7 @@ struct timezone { ...@@ -25,6 +25,7 @@ struct timezone {
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/seqlock.h>
/* /*
* Change timeval to jiffies, trying to avoid the * Change timeval to jiffies, trying to avoid the
...@@ -120,7 +121,7 @@ mktime (unsigned int year, unsigned int mon, ...@@ -120,7 +121,7 @@ mktime (unsigned int year, unsigned int mon,
} }
extern struct timespec xtime; extern struct timespec xtime;
extern rwlock_t xtime_lock; extern seqlock_t xtime_lock;
static inline unsigned long get_seconds(void) static inline unsigned long get_seconds(void)
{ {
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#include <linux/dnotify.h> #include <linux/dnotify.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/time.h>
#include <asm/checksum.h> #include <asm/checksum.h>
#if defined(CONFIG_PROC_FS) #if defined(CONFIG_PROC_FS)
...@@ -485,6 +486,7 @@ EXPORT_SYMBOL(kernel_flag); ...@@ -485,6 +486,7 @@ EXPORT_SYMBOL(kernel_flag);
EXPORT_SYMBOL(jiffies); EXPORT_SYMBOL(jiffies);
EXPORT_SYMBOL(jiffies_64); EXPORT_SYMBOL(jiffies_64);
EXPORT_SYMBOL(xtime); EXPORT_SYMBOL(xtime);
EXPORT_SYMBOL(xtime_lock);
EXPORT_SYMBOL(do_gettimeofday); EXPORT_SYMBOL(do_gettimeofday);
EXPORT_SYMBOL(do_settimeofday); EXPORT_SYMBOL(do_settimeofday);
#ifdef CONFIG_DEBUG_SPINLOCK_SLEEP #ifdef CONFIG_DEBUG_SPINLOCK_SLEEP
......
...@@ -36,9 +36,6 @@ ...@@ -36,9 +36,6 @@
*/ */
struct timezone sys_tz; struct timezone sys_tz;
/* The xtime_lock is not only serializing the xtime read/writes but it's also
serializing all accesses to the global NTP variables now. */
extern rwlock_t xtime_lock;
extern unsigned long last_time_offset; extern unsigned long last_time_offset;
#if !defined(__alpha__) && !defined(__ia64__) #if !defined(__alpha__) && !defined(__ia64__)
...@@ -80,7 +77,7 @@ asmlinkage long sys_stime(int * tptr) ...@@ -80,7 +77,7 @@ asmlinkage long sys_stime(int * tptr)
return -EPERM; return -EPERM;
if (get_user(value, tptr)) if (get_user(value, tptr))
return -EFAULT; return -EFAULT;
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
xtime.tv_sec = value; xtime.tv_sec = value;
xtime.tv_nsec = 0; xtime.tv_nsec = 0;
last_time_offset = 0; last_time_offset = 0;
...@@ -88,7 +85,7 @@ asmlinkage long sys_stime(int * tptr) ...@@ -88,7 +85,7 @@ asmlinkage long sys_stime(int * tptr)
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_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
return 0; return 0;
} }
...@@ -96,13 +93,13 @@ asmlinkage long sys_stime(int * tptr) ...@@ -96,13 +93,13 @@ asmlinkage long sys_stime(int * tptr)
asmlinkage long sys_gettimeofday(struct timeval *tv, struct timezone *tz) asmlinkage long sys_gettimeofday(struct timeval *tv, struct timezone *tz)
{ {
if (tv) { if (likely(tv != NULL)) {
struct timeval ktv; struct timeval ktv;
do_gettimeofday(&ktv); do_gettimeofday(&ktv);
if (copy_to_user(tv, &ktv, sizeof(ktv))) if (copy_to_user(tv, &ktv, sizeof(ktv)))
return -EFAULT; return -EFAULT;
} }
if (tz) { if (unlikely(tz != NULL)) {
if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) if (copy_to_user(tz, &sys_tz, sizeof(sys_tz)))
return -EFAULT; return -EFAULT;
} }
...@@ -127,10 +124,10 @@ asmlinkage long sys_gettimeofday(struct timeval *tv, struct timezone *tz) ...@@ -127,10 +124,10 @@ asmlinkage long sys_gettimeofday(struct timeval *tv, struct timezone *tz)
*/ */
inline static void warp_clock(void) inline static void warp_clock(void)
{ {
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
xtime.tv_sec += sys_tz.tz_minuteswest * 60; xtime.tv_sec += sys_tz.tz_minuteswest * 60;
last_time_offset = 0; last_time_offset = 0;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
} }
/* /*
...@@ -235,7 +232,7 @@ int do_adjtimex(struct timex *txc) ...@@ -235,7 +232,7 @@ int do_adjtimex(struct timex *txc)
txc->tick > 1100000/USER_HZ) txc->tick > 1100000/USER_HZ)
return -EINVAL; return -EINVAL;
write_lock_irq(&xtime_lock); write_seqlock_irq(&xtime_lock);
result = time_state; /* mostly `TIME_OK' */ result = time_state; /* mostly `TIME_OK' */
/* Save for later - semantics of adjtime is to return old value */ /* Save for later - semantics of adjtime is to return old value */
...@@ -386,7 +383,7 @@ leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0 ...@@ -386,7 +383,7 @@ leave: if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0
txc->errcnt = pps_errcnt; txc->errcnt = pps_errcnt;
txc->stbcnt = pps_stbcnt; txc->stbcnt = pps_stbcnt;
last_time_offset = 0; last_time_offset = 0;
write_unlock_irq(&xtime_lock); write_sequnlock_irq(&xtime_lock);
do_gettimeofday(&txc->time); do_gettimeofday(&txc->time);
return(result); return(result);
} }
...@@ -409,9 +406,13 @@ asmlinkage long sys_adjtimex(struct timex *txc_p) ...@@ -409,9 +406,13 @@ asmlinkage long sys_adjtimex(struct timex *txc_p)
struct timespec current_kernel_time(void) struct timespec current_kernel_time(void)
{ {
struct timespec now; struct timespec now;
unsigned long flags; unsigned long seq;
read_lock_irqsave(&xtime_lock,flags);
now = xtime; do {
read_unlock_irqrestore(&xtime_lock,flags); seq = read_seqbegin(&xtime_lock);
now = xtime;
} while (read_seqretry(&xtime_lock, seq));
return now; return now;
} }
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <linux/time.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -760,7 +761,7 @@ unsigned long wall_jiffies; ...@@ -760,7 +761,7 @@ unsigned long wall_jiffies;
* This read-write spinlock protects us from races in SMP while * This read-write spinlock protects us from races in SMP while
* playing with xtime and avenrun. * playing with xtime and avenrun.
*/ */
rwlock_t xtime_lock __cacheline_aligned_in_smp = RW_LOCK_UNLOCKED; seqlock_t xtime_lock __cacheline_aligned_in_smp = SEQLOCK_UNLOCKED;
unsigned long last_time_offset; unsigned long last_time_offset;
/* /*
...@@ -801,7 +802,7 @@ static inline void update_times(void) ...@@ -801,7 +802,7 @@ static inline void update_times(void)
/* /*
* The 64-bit jiffies value is not atomic - you MUST NOT read it * The 64-bit jiffies value is not atomic - you MUST NOT read it
* without holding read_lock_irq(&xtime_lock). * without sampling the sequence number in xtime_lock.
* jiffies is defined in the linker script... * jiffies is defined in the linker script...
*/ */
...@@ -1090,20 +1091,23 @@ asmlinkage long sys_sysinfo(struct sysinfo *info) ...@@ -1090,20 +1091,23 @@ asmlinkage long sys_sysinfo(struct sysinfo *info)
u64 uptime; 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;
memset((char *)&val, 0, sizeof(struct sysinfo)); memset((char *)&val, 0, sizeof(struct sysinfo));
read_lock_irq(&xtime_lock); do {
uptime = jiffies_64; seq = read_seqbegin(&xtime_lock);
do_div(uptime, HZ);
val.uptime = (unsigned long) uptime; uptime = jiffies_64;
do_div(uptime, HZ);
val.uptime = (unsigned long) uptime;
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);
val.loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT); val.loads[2] = avenrun[2] << (SI_LOAD_SHIFT - FSHIFT);
val.procs = nr_threads; val.procs = nr_threads;
read_unlock_irq(&xtime_lock); } while (read_seqretry(&xtime_lock, seq));
si_meminfo(&val); si_meminfo(&val);
si_swapinfo(&val); si_swapinfo(&val);
...@@ -1148,7 +1152,7 @@ asmlinkage long sys_sysinfo(struct sysinfo *info) ...@@ -1148,7 +1152,7 @@ asmlinkage long sys_sysinfo(struct sysinfo *info)
val.totalhigh <<= bitcount; val.totalhigh <<= bitcount;
val.freehigh <<= bitcount; val.freehigh <<= bitcount;
out: out:
if (copy_to_user(info, &val, sizeof(struct sysinfo))) if (copy_to_user(info, &val, sizeof(struct sysinfo)))
return -EFAULT; return -EFAULT;
......
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