aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorChristian Borntraeger <borntraeger@de.ibm.com>2014-11-04 02:31:16 -0500
committerChristian Borntraeger <borntraeger@de.ibm.com>2014-11-07 05:10:26 -0500
commit1365039d0cb32c0cf96eb9f750f4277c9a90f87d (patch)
treeca992446f706e9025cb540797f8e2aeb40fa69d2 /arch/s390
parent0df1f2487d2f0d04703f142813d53615d62a1da4 (diff)
KVM: s390: Fix ipte locking
ipte_unlock_siif uses cmpxchg to replace the in-memory data of the ipte lock together with ACCESS_ONCE for the intial read. union ipte_control { unsigned long val; struct { unsigned long k : 1; unsigned long kh : 31; unsigned long kg : 32; }; }; [...] static void ipte_unlock_siif(struct kvm_vcpu *vcpu) { union ipte_control old, new, *ic; ic = &vcpu->kvm->arch.sca->ipte_control; do { new = old = ACCESS_ONCE(*ic); new.kh--; if (!new.kh) new.k = 0; } while (cmpxchg(&ic->val, old.val, new.val) != old.val); if (!new.kh) wake_up(&vcpu->kvm->arch.ipte_wq); } The new value, is loaded twice from memory with gcc 4.7.2 of fedora 18, despite the ACCESS_ONCE: ---> l %r4,0(%r3) <--- load first 32 bit of lock (k and kh) in r4 alfi %r4,2147483647 <--- add -1 to r4 llgtr %r4,%r4 <--- zero out the sign bit of r4 lg %r1,0(%r3) <--- load all 64 bit of lock into new lgr %r2,%r1 <--- load the same into old risbg %r1,%r4,1,31,32 <--- shift and insert r4 into the bits 1-31 of new llihf %r4,2147483647 ngrk %r4,%r1,%r4 jne aa0 <ipte_unlock+0xf8> nihh %r1,32767 lgr %r4,%r2 csg %r4,%r1,0(%r3) cgr %r2,%r4 jne a70 <ipte_unlock+0xc8> If the memory value changes between the first load (l) and the second load (lg) we are broken. If that happens VCPU threads will hang (unkillable) in handle_ipte_interlock. Andreas Krebbel analyzed this and tracked it down to a compiler bug in that version: "while it is not that obvious the C99 standard basically forbids duplicating the memory access also in that case. For an argumentation of a similiar case please see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=22278#c43 For the implementation-defined cases regarding volatile there are some GCC-specific clarifications which can be found here: https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html#Volatiles I've tracked down the problem with a reduced testcase. The problem was that during a tree level optimization (SRA - scalar replacement of aggregates) the volatile marker is lost. And an RTL level optimizer (CSE - common subexpression elimination) then propagated the memory read into its second use introducing another access to the memory location. So indeed Christian's suspicion that the union access has something to do with it is correct (since it triggered the SRA optimization). This issue has been reported and fixed in the GCC 4.8 development cycle: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145" This patch replaces the ACCESS_ONCE scheme with a barrier() based scheme that should work for all supported compilers. Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com> Cc: stable@vger.kernel.org # v3.16+
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/kvm/gaccess.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c
index 0f961a1c64b3..6dc0ad9c7050 100644
--- a/arch/s390/kvm/gaccess.c
+++ b/arch/s390/kvm/gaccess.c
@@ -229,10 +229,12 @@ static void ipte_lock_simple(struct kvm_vcpu *vcpu)
229 goto out; 229 goto out;
230 ic = &vcpu->kvm->arch.sca->ipte_control; 230 ic = &vcpu->kvm->arch.sca->ipte_control;
231 do { 231 do {
232 old = ACCESS_ONCE(*ic); 232 old = *ic;
233 barrier();
233 while (old.k) { 234 while (old.k) {
234 cond_resched(); 235 cond_resched();
235 old = ACCESS_ONCE(*ic); 236 old = *ic;
237 barrier();
236 } 238 }
237 new = old; 239 new = old;
238 new.k = 1; 240 new.k = 1;
@@ -251,7 +253,9 @@ static void ipte_unlock_simple(struct kvm_vcpu *vcpu)
251 goto out; 253 goto out;
252 ic = &vcpu->kvm->arch.sca->ipte_control; 254 ic = &vcpu->kvm->arch.sca->ipte_control;
253 do { 255 do {
254 new = old = ACCESS_ONCE(*ic); 256 old = *ic;
257 barrier();
258 new = old;
255 new.k = 0; 259 new.k = 0;
256 } while (cmpxchg(&ic->val, old.val, new.val) != old.val); 260 } while (cmpxchg(&ic->val, old.val, new.val) != old.val);
257 wake_up(&vcpu->kvm->arch.ipte_wq); 261 wake_up(&vcpu->kvm->arch.ipte_wq);
@@ -265,10 +269,12 @@ static void ipte_lock_siif(struct kvm_vcpu *vcpu)
265 269
266 ic = &vcpu->kvm->arch.sca->ipte_control; 270 ic = &vcpu->kvm->arch.sca->ipte_control;
267 do { 271 do {
268 old = ACCESS_ONCE(*ic); 272 old = *ic;
273 barrier();
269 while (old.kg) { 274 while (old.kg) {
270 cond_resched(); 275 cond_resched();
271 old = ACCESS_ONCE(*ic); 276 old = *ic;
277 barrier();
272 } 278 }
273 new = old; 279 new = old;
274 new.k = 1; 280 new.k = 1;
@@ -282,7 +288,9 @@ static void ipte_unlock_siif(struct kvm_vcpu *vcpu)
282 288
283 ic = &vcpu->kvm->arch.sca->ipte_control; 289 ic = &vcpu->kvm->arch.sca->ipte_control;
284 do { 290 do {
285 new = old = ACCESS_ONCE(*ic); 291 old = *ic;
292 barrier();
293 new = old;
286 new.kh--; 294 new.kh--;
287 if (!new.kh) 295 if (!new.kh)
288 new.k = 0; 296 new.k = 0;