pvclock: introduce seqcount-like API
authorPaolo Bonzini <pbonzini@redhat.com>
Thu, 9 Jun 2016 11:06:08 +0000 (13:06 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 4 Aug 2016 11:52:21 +0000 (13:52 +0200)
The version field in struct pvclock_vcpu_time_info basically implements
a seqcount.  Wrap it with the usual read_begin and read_retry functions,
and use these APIs instead of peppering the code with smp_rmb()s.
While at it, change it to the more pedantically correct virt_rmb().

With this change, __pvclock_read_cycles can be simplified noticeably.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/entry/vdso/vclock_gettime.c
arch/x86/include/asm/pvclock.h
arch/x86/kernel/pvclock.c

index 2f02d23..db1e3b4 100644 (file)
@@ -123,9 +123,7 @@ static notrace cycle_t vread_pvclock(int *mode)
         */
 
        do {
-               version = pvti->version;
-
-               smp_rmb();
+               version = pvclock_read_begin(pvti);
 
                if (unlikely(!(pvti->flags & PVCLOCK_TSC_STABLE_BIT))) {
                        *mode = VCLOCK_NONE;
@@ -137,10 +135,7 @@ static notrace cycle_t vread_pvclock(int *mode)
                pvti_tsc_shift = pvti->tsc_shift;
                pvti_system_time = pvti->system_time;
                pvti_tsc = pvti->tsc_timestamp;
-
-               /* Make sure that the version double-check is last. */
-               smp_rmb();
-       } while (unlikely((version & 1) || version != pvti->version));
+       } while (pvclock_read_retry(pvti, version));
 
        delta = tsc - pvti_tsc;
        ret = pvti_system_time +
index 7c1c895..d019f0c 100644 (file)
@@ -25,6 +25,24 @@ void pvclock_resume(void);
 
 void pvclock_touch_watchdogs(void);
 
+static __always_inline
+unsigned pvclock_read_begin(const struct pvclock_vcpu_time_info *src)
+{
+       unsigned version = src->version & ~1;
+       /* Make sure that the version is read before the data. */
+       virt_rmb();
+       return version;
+}
+
+static __always_inline
+bool pvclock_read_retry(const struct pvclock_vcpu_time_info *src,
+                       unsigned version)
+{
+       /* Make sure that the version is re-read after the data. */
+       virt_rmb();
+       return unlikely(version != src->version);
+}
+
 /*
  * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
  * yielding a 64-bit result.
@@ -69,23 +87,12 @@ static inline u64 pvclock_scale_delta(u64 delta, u32 mul_frac, int shift)
 }
 
 static __always_inline
-unsigned __pvclock_read_cycles(const struct pvclock_vcpu_time_info *src,
-                              cycle_t *cycles, u8 *flags)
+cycle_t __pvclock_read_cycles(const struct pvclock_vcpu_time_info *src)
 {
-       unsigned version;
-       cycle_t offset;
-       u64 delta;
-
-       version = src->version;
-       /* Make the latest version visible */
-       smp_rmb();
-
-       delta = rdtsc_ordered() - src->tsc_timestamp;
-       offset = pvclock_scale_delta(delta, src->tsc_to_system_mul,
-                                  src->tsc_shift);
-       *cycles = src->system_time + offset;
-       *flags = src->flags;
-       return version;
+       u64 delta = rdtsc_ordered() - src->tsc_timestamp;
+       cycle_t offset = pvclock_scale_delta(delta, src->tsc_to_system_mul,
+                                            src->tsc_shift);
+       return src->system_time + offset;
 }
 
 struct pvclock_vsyscall_time_info {
index 06c58ce..3599404 100644 (file)
@@ -64,14 +64,9 @@ u8 pvclock_read_flags(struct pvclock_vcpu_time_info *src)
        u8 flags;
 
        do {
-               version = src->version;
-               /* Make the latest version visible */
-               smp_rmb();
-
+               version = pvclock_read_begin(src);
                flags = src->flags;
-               /* Make sure that the version double-check is last. */
-               smp_rmb();
-       } while ((src->version & 1) || version != src->version);
+       } while (pvclock_read_retry(src, version));
 
        return flags & valid_flags;
 }
@@ -84,10 +79,10 @@ cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
        u8 flags;
 
        do {
-               version = __pvclock_read_cycles(src, &ret, &flags);
-               /* Make sure that the version double-check is last. */
-               smp_rmb();
-       } while ((src->version & 1) || version != src->version);
+               version = pvclock_read_begin(src);
+               ret = __pvclock_read_cycles(src);
+               flags = src->flags;
+       } while (pvclock_read_retry(src, version));
 
        if (unlikely((flags & PVCLOCK_GUEST_STOPPED) != 0)) {
                src->flags &= ~PVCLOCK_GUEST_STOPPED;