KVM: PPC: Book3s: PR: Add SPAPR H_BULK_REMOVE support
authorMatt Evans <matt@ozlabs.org>
Mon, 30 Jan 2012 20:25:31 +0000 (20:25 +0000)
committerAvi Kivity <avi@redhat.com>
Sun, 8 Apr 2012 09:55:31 +0000 (12:55 +0300)
SPAPR support includes various in-kernel hypercalls, improving performance
by cutting out the exit to userspace.  H_BULK_REMOVE is implemented in this
patch.

Signed-off-by: Matt Evans <matt@ozlabs.org>
Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Avi Kivity <avi@redhat.com>
arch/powerpc/kvm/book3s_pr_papr.c

index b958932..6d1bfe2 100644 (file)
@@ -98,6 +98,83 @@ static int kvmppc_h_pr_remove(struct kvm_vcpu *vcpu)
        return EMULATE_DONE;
 }
 
+/* Request defs for kvmppc_h_pr_bulk_remove() */
+#define H_BULK_REMOVE_TYPE             0xc000000000000000ULL
+#define   H_BULK_REMOVE_REQUEST        0x4000000000000000ULL
+#define   H_BULK_REMOVE_RESPONSE       0x8000000000000000ULL
+#define   H_BULK_REMOVE_END            0xc000000000000000ULL
+#define H_BULK_REMOVE_CODE             0x3000000000000000ULL
+#define   H_BULK_REMOVE_SUCCESS        0x0000000000000000ULL
+#define   H_BULK_REMOVE_NOT_FOUND      0x1000000000000000ULL
+#define   H_BULK_REMOVE_PARM           0x2000000000000000ULL
+#define   H_BULK_REMOVE_HW             0x3000000000000000ULL
+#define H_BULK_REMOVE_RC               0x0c00000000000000ULL
+#define H_BULK_REMOVE_FLAGS            0x0300000000000000ULL
+#define   H_BULK_REMOVE_ABSOLUTE       0x0000000000000000ULL
+#define   H_BULK_REMOVE_ANDCOND        0x0100000000000000ULL
+#define   H_BULK_REMOVE_AVPN           0x0200000000000000ULL
+#define H_BULK_REMOVE_PTEX             0x00ffffffffffffffULL
+#define H_BULK_REMOVE_MAX_BATCH        4
+
+static int kvmppc_h_pr_bulk_remove(struct kvm_vcpu *vcpu)
+{
+       int i;
+       int paramnr = 4;
+       int ret = H_SUCCESS;
+
+       for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
+               unsigned long tsh = kvmppc_get_gpr(vcpu, paramnr+(2*i));
+               unsigned long tsl = kvmppc_get_gpr(vcpu, paramnr+(2*i)+1);
+               unsigned long pteg, rb, flags;
+               unsigned long pte[2];
+               unsigned long v = 0;
+
+               if ((tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) {
+                       break; /* Exit success */
+               } else if ((tsh & H_BULK_REMOVE_TYPE) !=
+                          H_BULK_REMOVE_REQUEST) {
+                       ret = H_PARAMETER;
+                       break; /* Exit fail */
+               }
+
+               tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS;
+               tsh |= H_BULK_REMOVE_RESPONSE;
+
+               if ((tsh & H_BULK_REMOVE_ANDCOND) &&
+                   (tsh & H_BULK_REMOVE_AVPN)) {
+                       tsh |= H_BULK_REMOVE_PARM;
+                       kvmppc_set_gpr(vcpu, paramnr+(2*i), tsh);
+                       ret = H_PARAMETER;
+                       break; /* Exit fail */
+               }
+
+               pteg = get_pteg_addr(vcpu, tsh & H_BULK_REMOVE_PTEX);
+               copy_from_user(pte, (void __user *)pteg, sizeof(pte));
+
+               /* tsl = AVPN */
+               flags = (tsh & H_BULK_REMOVE_FLAGS) >> 26;
+
+               if ((pte[0] & HPTE_V_VALID) == 0 ||
+                   ((flags & H_AVPN) && (pte[0] & ~0x7fUL) != tsl) ||
+                   ((flags & H_ANDCOND) && (pte[0] & tsl) != 0)) {
+                       tsh |= H_BULK_REMOVE_NOT_FOUND;
+               } else {
+                       /* Splat the pteg in (userland) hpt */
+                       copy_to_user((void __user *)pteg, &v, sizeof(v));
+
+                       rb = compute_tlbie_rb(pte[0], pte[1],
+                                             tsh & H_BULK_REMOVE_PTEX);
+                       vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false);
+                       tsh |= H_BULK_REMOVE_SUCCESS;
+                       tsh |= (pte[1] & (HPTE_R_C | HPTE_R_R)) << 43;
+               }
+               kvmppc_set_gpr(vcpu, paramnr+(2*i), tsh);
+       }
+       kvmppc_set_gpr(vcpu, 3, ret);
+
+       return EMULATE_DONE;
+}
+
 static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu)
 {
        unsigned long flags = kvmppc_get_gpr(vcpu, 4);
@@ -144,10 +221,7 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd)
        case H_PROTECT:
                return kvmppc_h_pr_protect(vcpu);
        case H_BULK_REMOVE:
-               /* We just flush all PTEs, so user space can
-                  handle the HPT modifications */
-               kvmppc_mmu_pte_flush(vcpu, 0, 0);
-               break;
+               return kvmppc_h_pr_bulk_remove(vcpu);
        case H_CEDE:
                kvm_vcpu_block(vcpu);
                vcpu->stat.halt_wakeup++;