patch-2.3.99-pre6 linux/arch/arm/kernel/time.c

Next file: linux/arch/arm/lib/csumpartialcopy.S
Previous file: linux/arch/arm/kernel/setup.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre5/linux/arch/arm/kernel/time.c linux/arch/arm/kernel/time.c
@@ -30,6 +30,7 @@
 
 extern int setup_arm_irq(int, struct irqaction *);
 extern void setup_timer(void);
+extern rwlock_t xtime_lock;
 extern volatile unsigned long lost_ticks;
 
 /* change this if you have some constant time drift */
@@ -59,7 +60,8 @@
 }
 
 /*
- * hook for getting the time offset
+ * hook for getting the time offset.  Note that it is
+ * always called with interrupts disabled.
  */
 unsigned long (*gettimeoffset)(void) = dummy_gettimeoffset;
 
@@ -175,29 +177,33 @@
 void do_gettimeofday(struct timeval *tv)
 {
 	unsigned long flags;
+	unsigned long usec, sec;
 
-	save_flags_cli (flags);
-	*tv = xtime;
-	tv->tv_usec += gettimeoffset();
-
-	/*
-	 * xtime is atomically updated in timer_bh. lost_ticks is
-	 * nonzero if the timer bottom half hasnt executed yet.
-	 */
-	if (lost_ticks)
-		tv->tv_usec += USECS_PER_JIFFY;
+	read_lock_irqsave(&xtime_lock, flags);
+	usec = gettimeoffset();
+	{
+		unsigned long lost = lost_ticks;
 
-	restore_flags(flags);
+		if (lost)
+			usec += lost * USECS_PER_JIFFY;
+	}
+	sec = xtime.tv_sec;
+	usec += xtime.tv_usec;
+	read_unlock_irqrestore(&xtime_lock, flags);
 
-	if (tv->tv_usec >= 1000000) {
-		tv->tv_usec -= 1000000;
-		tv->tv_sec++;
+	/* usec may have gone up a lot: be safe */
+	while (usec >= 1000000) {
+		usec -= 1000000;
+		sec++;
 	}
+
+	tv->tv_sec = sec;
+	tv->tv_usec = usec;
 }
 
 void do_settimeofday(struct timeval *tv)
 {
-	cli ();
+	write_lock_irq(&xtime_lock);
 	/* 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.
@@ -205,8 +211,9 @@
 	 * would have done, and then undo it!
 	 */
 	tv->tv_usec -= gettimeoffset();
+	tv->tv_usec -= lost_ticks * USECS_PER_JIFFY;
 
-	if (tv->tv_usec < 0) {
+	while (tv->tv_usec < 0) {
 		tv->tv_usec += 1000000;
 		tv->tv_sec--;
 	}
@@ -216,7 +223,7 @@
 	time_status |= STA_UNSYNC;
 	time_maxerror = NTP_PHASE_LIMIT;
 	time_esterror = NTP_PHASE_LIMIT;
-	sti();
+	write_unlock_irq(&xtime_lock);
 }
 
 static struct irqaction timer_irq = {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)