From mboxrd@z Thu Jan 1 00:00:00 1970 From: "Jan Beulich" Subject: [PATCH] linux/x86: fix for special behavior of first sys_settimeofday(NULL, &tz) invocation Date: Fri, 18 Jun 2010 11:00:18 +0100 Message-ID: <4C1B5FD20200007800007120@vpn.id2.novell.com> Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: quoted-printable Return-path: Content-Disposition: inline List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: xen-devel@lists.xensource.com List-Id: xen-devel@lists.xenproject.org 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 --- 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; =20 -static struct timeval monotonic_tv; -static spinlock_t monotonic_lock =3D 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); =20 +static struct { + spinlock_t lock; + struct timeval tv; + u32 version; +} monotonic =3D { .lock =3D 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; =20 cpu =3D get_cpu(); shadow =3D &per_cpu(shadow_time, cpu); @@ -420,6 +423,8 @@ void do_gettimeofday(struct timeval *tv) __normalize_time(&sec, &nsec); usec +=3D (long)nsec / NSEC_PER_USEC; =20 + monotonic_version =3D 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++; } =20 - spin_lock_irqsave(&monotonic_lock, flags); - if ((sec > monotonic_tv.tv_sec) || - ((sec =3D=3D monotonic_tv.tv_sec) && (usec > monotonic_tv.tv_us= ec))) - { - monotonic_tv.tv_sec =3D sec; - monotonic_tv.tv_usec =3D usec; - } else { - sec =3D monotonic_tv.tv_sec; - usec =3D monotonic_tv.tv_usec; + spin_lock_irqsave(&monotonic.lock, flags); + if (unlikely(sec < monotonic.tv.tv_sec) || + (sec =3D=3D monotonic.tv.tv_sec && usec <=3D monotonic.tv.tv_us= ec)) { + sec =3D monotonic.tv.tv_sec; + usec =3D monotonic.tv.tv_usec; + } else if (likely(monotonic_version =3D=3D monotonic.version)) { + monotonic.tv.tv_sec =3D sec; + monotonic.tv.tv_usec =3D usec; } - spin_unlock_irqrestore(&monotonic_lock, flags); + spin_unlock_irqrestore(&monotonic.lock, flags); =20 tv->tv_sec =3D sec; tv->tv_usec =3D usec; @@ -459,6 +463,16 @@ void do_gettimeofday(struct timeval *tv) =20 EXPORT_SYMBOL(do_gettimeofday); =20 +/* Reset monotonic gettimeofday() timeval. */ +static inline void monotonic_reset(void) +{ + spin_lock(&monotonic.lock); + monotonic.tv.tv_sec =3D 0; + monotonic.tv.tv_usec =3D 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; =20 + if (unlikely(!tv)) { + monotonic_reset(); + return 0; + } + if ((unsigned long)tv->tv_nsec >=3D NSEC_PER_SEC) return -EINVAL; =20 @@ -506,11 +525,7 @@ int do_settimeofday(struct timespec *tv) } ntp_clear(); =20 - /* Reset monotonic gettimeofday() timeval. */ - spin_lock(&monotonic_lock); - monotonic_tv.tv_sec =3D 0; - monotonic_tv.tv_usec =3D 0; - spin_unlock(&monotonic_lock); + monotonic_reset(); =20 write_sequnlock_irq(&xtime_lock); =20 --- sle10sp3-2010-06-17.orig/kernel/time.c 2010-06-18 08:19:43.0000000= 00 +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 -=3D sys_tz.tz_minuteswest * 60; xtime.tv_sec +=3D 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(); }