]> xenbits.xensource.com Git - people/royger/linux-2.6.18-xen.git/commitdiff
xen/x86: fix for special behavior of first sys_settimeofday(NULL, &tz) invocation
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 18 Jun 2010 13:11:57 +0000 (14:11 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 18 Jun 2010 13:11:57 +0000 (14:11 +0100)
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>
arch/i386/kernel/time-xen.c
kernel/time.c

index 90ac8ce4a8cc39db531bb7c5e9ccc7956303bc09..60292c4ecde6e6b90587b4f1220313b9acd14fb1 100644 (file)
@@ -114,9 +114,6 @@ static DEFINE_PER_CPU(struct shadow_time_info, 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);
@@ -377,6 +374,12 @@ void rtc_cmos_write(unsigned char val, unsigned char addr)
 }
 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.
@@ -389,7 +392,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);
@@ -413,6 +416,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,
@@ -434,17 +439,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;
@@ -452,6 +456,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;
@@ -460,6 +474,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;
 
@@ -499,11 +518,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);
 
index 5bd489747643c9e9dced9316cd372bba8f81a9e1..14f464ad30b21531a4d1d05c7bc4f86290e26979 100644 (file)
@@ -135,6 +135,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();
 }