aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2011-12-14 21:01:10 -0500
committerAvi Kivity <avi@redhat.com>2012-03-05 07:52:39 -0500
commita92bce95f0f967dfa6205527d7143d276b0be6a7 (patch)
treefe0fb65201a532f360f6100959b6442f6829c94d
parentb5434032fcfd7490c6453feb397fb781762b6f09 (diff)
KVM: PPC: Book3S HV: Keep HPTE locked when invalidating
This reworks the implementations of the H_REMOVE and H_BULK_REMOVE hcalls to make sure that we keep the HPTE locked and in the reverse- mapping chain until we have finished invalidating it. Previously we would remove it from the chain and unlock it before invalidating it, leaving a tiny window when the guest could access the page even though we believe we have removed it from the guest (e.g., kvm_unmap_hva() has been called for the page and has found no HPTEs in the chain). In addition, we'll need this for future patches where we will need to read the R and C bits in the HPTE after invalidating it. Doing this required restructuring kvmppc_h_bulk_remove() substantially. Since we want to batch up the tlbies, we now need to keep several HPTEs locked simultaneously. In order to avoid possible deadlocks, we don't spin on the HPTE bitlock for any except the first HPTE in a batch. If we can't acquire the HPTE bitlock for the second or subsequent HPTE, we terminate the batch at that point, do the tlbies that we have accumulated so far, unlock those HPTEs, and then start a new batch to do the remaining invalidations. Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r--arch/powerpc/kvm/book3s_hv_rm_mmu.c212
1 files changed, 125 insertions, 87 deletions
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index d3e36fc77e2..ba4a1376b33 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -140,6 +140,12 @@ static pte_t lookup_linux_pte(struct kvm_vcpu *vcpu, unsigned long hva,
140 return kvmppc_read_update_linux_pte(ptep, writing); 140 return kvmppc_read_update_linux_pte(ptep, writing);
141} 141}
142 142
143static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v)
144{
145 asm volatile(PPC_RELEASE_BARRIER "" : : : "memory");
146 hpte[0] = hpte_v;
147}
148
143long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags, 149long kvmppc_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
144 long pte_index, unsigned long pteh, unsigned long ptel) 150 long pte_index, unsigned long pteh, unsigned long ptel)
145{ 151{
@@ -356,6 +362,7 @@ long kvmppc_h_remove(struct kvm_vcpu *vcpu, unsigned long flags,
356 struct kvm *kvm = vcpu->kvm; 362 struct kvm *kvm = vcpu->kvm;
357 unsigned long *hpte; 363 unsigned long *hpte;
358 unsigned long v, r, rb; 364 unsigned long v, r, rb;
365 struct revmap_entry *rev;
359 366
360 if (pte_index >= HPT_NPTE) 367 if (pte_index >= HPT_NPTE)
361 return H_PARAMETER; 368 return H_PARAMETER;
@@ -368,30 +375,32 @@ long kvmppc_h_remove(struct kvm_vcpu *vcpu, unsigned long flags,
368 hpte[0] &= ~HPTE_V_HVLOCK; 375 hpte[0] &= ~HPTE_V_HVLOCK;
369 return H_NOT_FOUND; 376 return H_NOT_FOUND;
370 } 377 }
371 if (atomic_read(&kvm->online_vcpus) == 1) 378
372 flags |= H_LOCAL; 379 rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
373 vcpu->arch.gpr[4] = v = hpte[0] & ~HPTE_V_HVLOCK; 380 v = hpte[0] & ~HPTE_V_HVLOCK;
374 vcpu->arch.gpr[5] = r = hpte[1]; 381 if (v & HPTE_V_VALID) {
375 rb = compute_tlbie_rb(v, r, pte_index); 382 hpte[0] &= ~HPTE_V_VALID;
376 if (v & HPTE_V_VALID) 383 rb = compute_tlbie_rb(v, hpte[1], pte_index);
384 if (!(flags & H_LOCAL) && atomic_read(&kvm->online_vcpus) > 1) {
385 while (!try_lock_tlbie(&kvm->arch.tlbie_lock))
386 cpu_relax();
387 asm volatile("ptesync" : : : "memory");
388 asm volatile(PPC_TLBIE(%1,%0)"; eieio; tlbsync"
389 : : "r" (rb), "r" (kvm->arch.lpid));
390 asm volatile("ptesync" : : : "memory");
391 kvm->arch.tlbie_lock = 0;
392 } else {
393 asm volatile("ptesync" : : : "memory");
394 asm volatile("tlbiel %0" : : "r" (rb));
395 asm volatile("ptesync" : : : "memory");
396 }
377 remove_revmap_chain(kvm, pte_index, v); 397 remove_revmap_chain(kvm, pte_index, v);
378 smp_wmb();
379 hpte[0] = 0;
380 if (!(v & HPTE_V_VALID))
381 return H_SUCCESS;
382 if (!(flags & H_LOCAL)) {
383 while (!try_lock_tlbie(&kvm->arch.tlbie_lock))
384 cpu_relax();
385 asm volatile("ptesync" : : : "memory");
386 asm volatile(PPC_TLBIE(%1,%0)"; eieio; tlbsync"
387 : : "r" (rb), "r" (kvm->arch.lpid));
388 asm volatile("ptesync" : : : "memory");
389 kvm->arch.tlbie_lock = 0;
390 } else {
391 asm volatile("ptesync" : : : "memory");
392 asm volatile("tlbiel %0" : : "r" (rb));
393 asm volatile("ptesync" : : : "memory");
394 } 398 }
399 r = rev->guest_rpte;
400 unlock_hpte(hpte, 0);
401
402 vcpu->arch.gpr[4] = v;
403 vcpu->arch.gpr[5] = r;
395 return H_SUCCESS; 404 return H_SUCCESS;
396} 405}
397 406
@@ -399,82 +408,113 @@ long kvmppc_h_bulk_remove(struct kvm_vcpu *vcpu)
399{ 408{
400 struct kvm *kvm = vcpu->kvm; 409 struct kvm *kvm = vcpu->kvm;
401 unsigned long *args = &vcpu->arch.gpr[4]; 410 unsigned long *args = &vcpu->arch.gpr[4];
402 unsigned long *hp, tlbrb[4]; 411 unsigned long *hp, *hptes[4], tlbrb[4];
403 long int i, found; 412 long int i, j, k, n, found, indexes[4];
404 long int n_inval = 0; 413 unsigned long flags, req, pte_index, rcbits;
405 unsigned long flags, req, pte_index;
406 long int local = 0; 414 long int local = 0;
407 long int ret = H_SUCCESS; 415 long int ret = H_SUCCESS;
416 struct revmap_entry *rev, *revs[4];
408 417
409 if (atomic_read(&kvm->online_vcpus) == 1) 418 if (atomic_read(&kvm->online_vcpus) == 1)
410 local = 1; 419 local = 1;
411 for (i = 0; i < 4; ++i) { 420 for (i = 0; i < 4 && ret == H_SUCCESS; ) {
412 pte_index = args[i * 2]; 421 n = 0;
413 flags = pte_index >> 56; 422 for (; i < 4; ++i) {
414 pte_index &= ((1ul << 56) - 1); 423 j = i * 2;
415 req = flags >> 6; 424 pte_index = args[j];
416 flags &= 3; 425 flags = pte_index >> 56;
417 if (req == 3) 426 pte_index &= ((1ul << 56) - 1);
418 break; 427 req = flags >> 6;
419 if (req != 1 || flags == 3 || 428 flags &= 3;
420 pte_index >= HPT_NPTE) { 429 if (req == 3) { /* no more requests */
421 /* parameter error */ 430 i = 4;
422 args[i * 2] = ((0xa0 | flags) << 56) + pte_index;
423 ret = H_PARAMETER;
424 break;
425 }
426 hp = (unsigned long *)(kvm->arch.hpt_virt + (pte_index << 4));
427 while (!try_lock_hpte(hp, HPTE_V_HVLOCK))
428 cpu_relax();
429 found = 0;
430 if (hp[0] & (HPTE_V_ABSENT | HPTE_V_VALID)) {
431 switch (flags & 3) {
432 case 0: /* absolute */
433 found = 1;
434 break; 431 break;
435 case 1: /* andcond */ 432 }
436 if (!(hp[0] & args[i * 2 + 1])) 433 if (req != 1 || flags == 3 || pte_index >= HPT_NPTE) {
437 found = 1; 434 /* parameter error */
435 args[j] = ((0xa0 | flags) << 56) + pte_index;
436 ret = H_PARAMETER;
438 break; 437 break;
439 case 2: /* AVPN */ 438 }
440 if ((hp[0] & ~0x7fUL) == args[i * 2 + 1]) 439 hp = (unsigned long *)
440 (kvm->arch.hpt_virt + (pte_index << 4));
441 /* to avoid deadlock, don't spin except for first */
442 if (!try_lock_hpte(hp, HPTE_V_HVLOCK)) {
443 if (n)
444 break;
445 while (!try_lock_hpte(hp, HPTE_V_HVLOCK))
446 cpu_relax();
447 }
448 found = 0;
449 if (hp[0] & (HPTE_V_ABSENT | HPTE_V_VALID)) {
450 switch (flags & 3) {
451 case 0: /* absolute */
441 found = 1; 452 found = 1;
442 break; 453 break;
454 case 1: /* andcond */
455 if (!(hp[0] & args[j + 1]))
456 found = 1;
457 break;
458 case 2: /* AVPN */
459 if ((hp[0] & ~0x7fUL) == args[j + 1])
460 found = 1;
461 break;
462 }
463 }
464 if (!found) {
465 hp[0] &= ~HPTE_V_HVLOCK;
466 args[j] = ((0x90 | flags) << 56) + pte_index;
467 continue;
443 } 468 }
469
470 args[j] = ((0x80 | flags) << 56) + pte_index;
471 rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
472 /* insert R and C bits from guest PTE */
473 rcbits = rev->guest_rpte & (HPTE_R_R|HPTE_R_C);
474 args[j] |= rcbits << (56 - 5);
475
476 if (!(hp[0] & HPTE_V_VALID))
477 continue;
478
479 hp[0] &= ~HPTE_V_VALID; /* leave it locked */
480 tlbrb[n] = compute_tlbie_rb(hp[0], hp[1], pte_index);
481 indexes[n] = j;
482 hptes[n] = hp;
483 revs[n] = rev;
484 ++n;
444 } 485 }
445 if (!found) { 486
446 hp[0] &= ~HPTE_V_HVLOCK; 487 if (!n)
447 args[i * 2] = ((0x90 | flags) << 56) + pte_index; 488 break;
448 continue; 489
490 /* Now that we've collected a batch, do the tlbies */
491 if (!local) {
492 while(!try_lock_tlbie(&kvm->arch.tlbie_lock))
493 cpu_relax();
494 asm volatile("ptesync" : : : "memory");
495 for (k = 0; k < n; ++k)
496 asm volatile(PPC_TLBIE(%1,%0) : :
497 "r" (tlbrb[k]),
498 "r" (kvm->arch.lpid));
499 asm volatile("eieio; tlbsync; ptesync" : : : "memory");
500 kvm->arch.tlbie_lock = 0;
501 } else {
502 asm volatile("ptesync" : : : "memory");
503 for (k = 0; k < n; ++k)
504 asm volatile("tlbiel %0" : : "r" (tlbrb[k]));
505 asm volatile("ptesync" : : : "memory");
449 } 506 }
450 /* insert R and C bits from PTE */ 507
451 flags |= (hp[1] >> 5) & 0x0c; 508 for (k = 0; k < n; ++k) {
452 args[i * 2] = ((0x80 | flags) << 56) + pte_index; 509 j = indexes[k];
453 if (hp[0] & HPTE_V_VALID) { 510 pte_index = args[j] & ((1ul << 56) - 1);
454 tlbrb[n_inval++] = compute_tlbie_rb(hp[0], hp[1], pte_index); 511 hp = hptes[k];
512 rev = revs[k];
455 remove_revmap_chain(kvm, pte_index, hp[0]); 513 remove_revmap_chain(kvm, pte_index, hp[0]);
514 unlock_hpte(hp, 0);
456 } 515 }
457 smp_wmb();
458 hp[0] = 0;
459 }
460 if (n_inval == 0)
461 return ret;
462
463 if (!local) {
464 while(!try_lock_tlbie(&kvm->arch.tlbie_lock))
465 cpu_relax();
466 asm volatile("ptesync" : : : "memory");
467 for (i = 0; i < n_inval; ++i)
468 asm volatile(PPC_TLBIE(%1,%0)
469 : : "r" (tlbrb[i]), "r" (kvm->arch.lpid));
470 asm volatile("eieio; tlbsync; ptesync" : : : "memory");
471 kvm->arch.tlbie_lock = 0;
472 } else {
473 asm volatile("ptesync" : : : "memory");
474 for (i = 0; i < n_inval; ++i)
475 asm volatile("tlbiel %0" : : "r" (tlbrb[i]));
476 asm volatile("ptesync" : : : "memory");
477 } 516 }
517
478 return ret; 518 return ret;
479} 519}
480 520
@@ -720,9 +760,7 @@ long kvmppc_hpte_hv_fault(struct kvm_vcpu *vcpu, unsigned long addr,
720 rev = real_vmalloc_addr(&kvm->arch.revmap[index]); 760 rev = real_vmalloc_addr(&kvm->arch.revmap[index]);
721 gr = rev->guest_rpte; 761 gr = rev->guest_rpte;
722 762
723 /* Unlock the HPTE */ 763 unlock_hpte(hpte, v);
724 asm volatile("lwsync" : : : "memory");
725 hpte[0] = v;
726 764
727 /* For not found, if the HPTE is valid by now, retry the instruction */ 765 /* For not found, if the HPTE is valid by now, retry the instruction */
728 if ((status & DSISR_NOHPTE) && (v & HPTE_V_VALID)) 766 if ((status & DSISR_NOHPTE) && (v & HPTE_V_VALID))