xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] linux/x86: fix for special behavior of first sys_settimeofday(NULL, &tz) invocation
@ 2010-06-18 10:00 Jan Beulich
  0 siblings, 0 replies; only message in thread
From: Jan Beulich @ 2010-06-18 10:00 UTC (permalink / raw)
  To: xen-devel

The data Xen's time implementation maintains to make do_gettimeofday()
return values monotonic needs to be reset not only during normal
do_gettimeofday() invocations, but also when the clock gets warped
due to the hardware (CMOS) clock running on local (rather than UTC)
time.

Additionally there was a time window in do_gettimeofday() (between
the end of the xtime read loop and the acquiring of the monotonicity
data lock) where, if on another processor do_settimeofday() would
execute to completion, the zeroes written by the latter could get
overwritten by the former with values obtained before the time was
updated. This now gets prevented by maintaining a version for the
monotonicity data.

Signed-off-by: Jan Beulich <jbeulich@novell.com>

--- sle10sp3-2010-06-17.orig/arch/i386/kernel/time-xen.c	2009-07-27 10:22:10.000000000 +0200
+++ sle10sp3-2010-06-17/arch/i386/kernel/time-xen.c	2010-06-18 08:21:37.000000000 +0200
@@ -119,9 +119,6 @@ static DEFINE_PER_CPU(struct shadow_time
 static struct timespec shadow_tv;
 static u32 shadow_tv_version;
 
-static struct timeval monotonic_tv;
-static spinlock_t monotonic_lock = SPIN_LOCK_UNLOCKED;
-
 /* Keep track of last time we did processing/updating of jiffies and xtime. */
 static u64 processed_system_time;   /* System time (ns) at last processing. */
 static DEFINE_PER_CPU(u64, processed_system_time);
@@ -384,6 +381,12 @@ void rtc_cmos_write(unsigned char val, u
 }
 EXPORT_SYMBOL(rtc_cmos_write);
 
+static struct {
+	spinlock_t lock;
+	struct timeval tv;
+	u32 version;
+} monotonic = { .lock = SPIN_LOCK_UNLOCKED };
+
 /*
  * This version of gettimeofday has microsecond resolution
  * and better than microsecond precision on fast x86 machines with TSC.
@@ -396,7 +399,7 @@ void do_gettimeofday(struct timeval *tv)
 	s64 nsec;
 	unsigned int cpu;
 	struct shadow_time_info *shadow;
-	u32 local_time_version;
+	u32 local_time_version, monotonic_version;
 
 	cpu = get_cpu();
 	shadow = &per_cpu(shadow_time, cpu);
@@ -420,6 +423,8 @@ void do_gettimeofday(struct timeval *tv)
 		__normalize_time(&sec, &nsec);
 		usec += (long)nsec / NSEC_PER_USEC;
 
+		monotonic_version = monotonic.version;
+
 		if (unlikely(!time_values_up_to_date(cpu))) {
 			/*
 			 * We may have blocked for a long time,
@@ -441,17 +446,16 @@ void do_gettimeofday(struct timeval *tv)
 		sec++;
 	}
 
-	spin_lock_irqsave(&monotonic_lock, flags);
-	if ((sec > monotonic_tv.tv_sec) ||
-	    ((sec == monotonic_tv.tv_sec) && (usec > monotonic_tv.tv_usec)))
-	{
-		monotonic_tv.tv_sec = sec;
-		monotonic_tv.tv_usec = usec;
-	} else {
-		sec = monotonic_tv.tv_sec;
-		usec = monotonic_tv.tv_usec;
+	spin_lock_irqsave(&monotonic.lock, flags);
+	if (unlikely(sec < monotonic.tv.tv_sec) ||
+	    (sec == monotonic.tv.tv_sec && usec <= monotonic.tv.tv_usec)) {
+		sec = monotonic.tv.tv_sec;
+		usec = monotonic.tv.tv_usec;
+	} else if (likely(monotonic_version == monotonic.version)) {
+		monotonic.tv.tv_sec = sec;
+		monotonic.tv.tv_usec = usec;
 	}
-	spin_unlock_irqrestore(&monotonic_lock, flags);
+	spin_unlock_irqrestore(&monotonic.lock, flags);
 
 	tv->tv_sec = sec;
 	tv->tv_usec = usec;
@@ -459,6 +463,16 @@ void do_gettimeofday(struct timeval *tv)
 
 EXPORT_SYMBOL(do_gettimeofday);
 
+/* Reset monotonic gettimeofday() timeval. */
+static inline void monotonic_reset(void)
+{
+	spin_lock(&monotonic.lock);
+	monotonic.tv.tv_sec = 0;
+	monotonic.tv.tv_usec = 0;
+	++monotonic.version;
+	spin_unlock(&monotonic.lock);
+}
+
 int do_settimeofday(struct timespec *tv)
 {
 	time_t sec;
@@ -467,6 +481,11 @@ int do_settimeofday(struct timespec *tv)
 	struct shadow_time_info *shadow;
 	struct xen_platform_op op;
 
+	if (unlikely(!tv)) {
+		monotonic_reset();
+		return 0;
+	}
+
 	if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
 		return -EINVAL;
 
@@ -506,11 +525,7 @@ int do_settimeofday(struct timespec *tv)
 	}
 	ntp_clear();
 
-	/* Reset monotonic gettimeofday() timeval. */
-	spin_lock(&monotonic_lock);
-	monotonic_tv.tv_sec = 0;
-	monotonic_tv.tv_usec = 0;
-	spin_unlock(&monotonic_lock);
+	monotonic_reset();
 
 	write_sequnlock_irq(&xtime_lock);
 
--- sle10sp3-2010-06-17.orig/kernel/time.c	2010-06-18 08:19:43.000000000 +0200
+++ sle10sp3-2010-06-17/kernel/time.c	2010-06-17 17:46:07.000000000 +0200
@@ -136,6 +136,9 @@ static inline void warp_clock(void)
 	wall_to_monotonic.tv_sec -= sys_tz.tz_minuteswest * 60;
 	xtime.tv_sec += sys_tz.tz_minuteswest * 60;
 	time_interpolator_reset();
+#if defined(CONFIG_XEN) && defined(CONFIG_X86)
+	do_settimeofday(NULL);
+#endif
 	write_sequnlock_irq(&xtime_lock);
 	clock_was_set();
 }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2010-06-18 10:00 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-06-18 10:00 [PATCH] linux/x86: fix for special behavior of first sys_settimeofday(NULL, &tz) invocation Jan Beulich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).