aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorGlauber Costa <glommer@redhat.com>2010-05-11 12:17:40 -0400
committerAvi Kivity <avi@redhat.com>2010-05-19 04:41:00 -0400
commit489fb490dbf8dab0249ad82b56688ae3842a79e8 (patch)
treea111ced108dd12ebc107ed5ffd2c65dc6288f549 /arch
parent424c32f1aa3112632a657d45698c8e7666668f78 (diff)
x86, paravirt: Add a global synchronization point for pvclock
In recent stress tests, it was found that pvclock-based systems could seriously warp in smp systems. Using ingo's time-warp-test.c, I could trigger a scenario as bad as 1.5mi warps a minute in some systems. (to be fair, it wasn't that bad in most of them). Investigating further, I found out that such warps were caused by the very offset-based calculation pvclock is based on. This happens even on some machines that report constant_tsc in its tsc flags, specially on multi-socket ones. Two reads of the same kernel timestamp at approx the same time, will likely have tsc timestamped in different occasions too. This means the delta we calculate is unpredictable at best, and can probably be smaller in a cpu that is legitimately reading clock in a forward ocasion. Some adjustments on the host could make this window less likely to happen, but still, it pretty much poses as an intrinsic problem of the mechanism. A while ago, I though about using a shared variable anyway, to hold clock last state, but gave up due to the high contention locking was likely to introduce, possibly rendering the thing useless on big machines. I argue, however, that locking is not necessary. We do a read-and-return sequence in pvclock, and between read and return, the global value can have changed. However, it can only have changed by means of an addition of a positive value. So if we detected that our clock timestamp is less than the current global, we know that we need to return a higher one, even though it is not exactly the one we compared to. OTOH, if we detect we're greater than the current time source, we atomically replace the value with our new readings. This do causes contention on big boxes (but big here means *BIG*), but it seems like a good trade off, since it provide us with a time source guaranteed to be stable wrt time warps. After this patch is applied, I don't see a single warp in time during 5 days of execution, in any of the machines I saw them before. Signed-off-by: Glauber Costa <glommer@redhat.com> Acked-by: Zachary Amsden <zamsden@redhat.com> CC: Jeremy Fitzhardinge <jeremy@goop.org> CC: Avi Kivity <avi@redhat.com> CC: Marcelo Tosatti <mtosatti@redhat.com> CC: Zachary Amsden <zamsden@redhat.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/pvclock.c24
1 files changed, 24 insertions, 0 deletions
diff --git a/arch/x86/kernel/pvclock.c b/arch/x86/kernel/pvclock.c
index f7fdd56bc0ab..f5bc40e1697e 100644
--- a/arch/x86/kernel/pvclock.c
+++ b/arch/x86/kernel/pvclock.c
@@ -118,11 +118,14 @@ unsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src)
118 return pv_tsc_khz; 118 return pv_tsc_khz;
119} 119}
120 120
121static atomic64_t last_value = ATOMIC64_INIT(0);
122
121cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src) 123cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
122{ 124{
123 struct pvclock_shadow_time shadow; 125 struct pvclock_shadow_time shadow;
124 unsigned version; 126 unsigned version;
125 cycle_t ret, offset; 127 cycle_t ret, offset;
128 u64 last;
126 129
127 do { 130 do {
128 version = pvclock_get_time_values(&shadow, src); 131 version = pvclock_get_time_values(&shadow, src);
@@ -132,6 +135,27 @@ cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
132 barrier(); 135 barrier();
133 } while (version != src->version); 136 } while (version != src->version);
134 137
138 /*
139 * Assumption here is that last_value, a global accumulator, always goes
140 * forward. If we are less than that, we should not be much smaller.
141 * We assume there is an error marging we're inside, and then the correction
142 * does not sacrifice accuracy.
143 *
144 * For reads: global may have changed between test and return,
145 * but this means someone else updated poked the clock at a later time.
146 * We just need to make sure we are not seeing a backwards event.
147 *
148 * For updates: last_value = ret is not enough, since two vcpus could be
149 * updating at the same time, and one of them could be slightly behind,
150 * making the assumption that last_value always go forward fail to hold.
151 */
152 last = atomic64_read(&last_value);
153 do {
154 if (ret < last)
155 return last;
156 last = atomic64_cmpxchg(&last_value, last, ret);
157 } while (unlikely(last != ret));
158
135 return ret; 159 return ret;
136} 160}
137 161