KVM: PPC: Book3S HV: Make the guest hash table size configurable
[deliverable/linux.git] / arch / powerpc / kvm / book3s_64_mmu_hv.c
index 80a57751758444a427797ecaff2a6f2e07a1ba03..d03eb6f7b0584e368bdd96e0f1969af58e885371 100644 (file)
 /* POWER7 has 10-bit LPIDs, PPC970 has 6-bit LPIDs */
 #define MAX_LPID_970   63
 
-long kvmppc_alloc_hpt(struct kvm *kvm)
+/* Power architecture requires HPT is at least 256kB */
+#define PPC_MIN_HPT_ORDER      18
+
+long kvmppc_alloc_hpt(struct kvm *kvm, u32 *htab_orderp)
 {
        unsigned long hpt;
-       long lpid;
        struct revmap_entry *rev;
        struct kvmppc_linear_info *li;
+       long order = kvm_hpt_order;
 
-       /* Allocate guest's hashed page table */
-       li = kvm_alloc_hpt();
-       if (li) {
-               /* using preallocated memory */
-               hpt = (ulong)li->base_virt;
-               kvm->arch.hpt_li = li;
-       } else {
-               /* using dynamic memory */
+       if (htab_orderp) {
+               order = *htab_orderp;
+               if (order < PPC_MIN_HPT_ORDER)
+                       order = PPC_MIN_HPT_ORDER;
+       }
+
+       /*
+        * If the user wants a different size from default,
+        * try first to allocate it from the kernel page allocator.
+        */
+       hpt = 0;
+       if (order != kvm_hpt_order) {
                hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT|
-                                      __GFP_NOWARN, HPT_ORDER - PAGE_SHIFT);
+                                      __GFP_NOWARN, order - PAGE_SHIFT);
+               if (!hpt)
+                       --order;
        }
 
+       /* Next try to allocate from the preallocated pool */
        if (!hpt) {
-               pr_err("kvm_alloc_hpt: Couldn't alloc HPT\n");
-               return -ENOMEM;
+               li = kvm_alloc_hpt();
+               if (li) {
+                       hpt = (ulong)li->base_virt;
+                       kvm->arch.hpt_li = li;
+                       order = kvm_hpt_order;
+               }
        }
+
+       /* Lastly try successively smaller sizes from the page allocator */
+       while (!hpt && order > PPC_MIN_HPT_ORDER) {
+               hpt = __get_free_pages(GFP_KERNEL|__GFP_ZERO|__GFP_REPEAT|
+                                      __GFP_NOWARN, order - PAGE_SHIFT);
+               if (!hpt)
+                       --order;
+       }
+
+       if (!hpt)
+               return -ENOMEM;
+
        kvm->arch.hpt_virt = hpt;
+       kvm->arch.hpt_order = order;
+       /* HPTEs are 2**4 bytes long */
+       kvm->arch.hpt_npte = 1ul << (order - 4);
+       /* 128 (2**7) bytes in each HPTEG */
+       kvm->arch.hpt_mask = (1ul << (order - 7)) - 1;
 
        /* Allocate reverse map array */
-       rev = vmalloc(sizeof(struct revmap_entry) * HPT_NPTE);
+       rev = vmalloc(sizeof(struct revmap_entry) * kvm->arch.hpt_npte);
        if (!rev) {
                pr_err("kvmppc_alloc_hpt: Couldn't alloc reverse map array\n");
                goto out_freehpt;
        }
        kvm->arch.revmap = rev;
+       kvm->arch.sdr1 = __pa(hpt) | (order - 18);
 
-       lpid = kvmppc_alloc_lpid();
-       if (lpid < 0)
-               goto out_freeboth;
+       pr_info("KVM guest htab at %lx (order %ld), LPID %x\n",
+               hpt, order, kvm->arch.lpid);
 
-       kvm->arch.sdr1 = __pa(hpt) | (HPT_ORDER - 18);
-       kvm->arch.lpid = lpid;
-
-       pr_info("KVM guest htab at %lx, LPID %lx\n", hpt, lpid);
+       if (htab_orderp)
+               *htab_orderp = order;
        return 0;
 
- out_freeboth:
-       vfree(rev);
  out_freehpt:
-       free_pages(hpt, HPT_ORDER - PAGE_SHIFT);
+       if (kvm->arch.hpt_li)
+               kvm_release_hpt(kvm->arch.hpt_li);
+       else
+               free_pages(hpt, order - PAGE_SHIFT);
        return -ENOMEM;
 }
 
+long kvmppc_alloc_reset_hpt(struct kvm *kvm, u32 *htab_orderp)
+{
+       long err = -EBUSY;
+       long order;
+
+       mutex_lock(&kvm->lock);
+       if (kvm->arch.rma_setup_done) {
+               kvm->arch.rma_setup_done = 0;
+               /* order rma_setup_done vs. vcpus_running */
+               smp_mb();
+               if (atomic_read(&kvm->arch.vcpus_running)) {
+                       kvm->arch.rma_setup_done = 1;
+                       goto out;
+               }
+       }
+       if (kvm->arch.hpt_virt) {
+               order = kvm->arch.hpt_order;
+               /* Set the entire HPT to 0, i.e. invalid HPTEs */
+               memset((void *)kvm->arch.hpt_virt, 0, 1ul << order);
+               /*
+                * Set the whole last_vcpu array to an invalid vcpu number.
+                * This ensures that each vcpu will flush its TLB on next entry.
+                */
+               memset(kvm->arch.last_vcpu, 0xff, sizeof(kvm->arch.last_vcpu));
+               *htab_orderp = order;
+               err = 0;
+       } else {
+               err = kvmppc_alloc_hpt(kvm, htab_orderp);
+               order = *htab_orderp;
+       }
+ out:
+       mutex_unlock(&kvm->lock);
+       return err;
+}
+
 void kvmppc_free_hpt(struct kvm *kvm)
 {
        kvmppc_free_lpid(kvm->arch.lpid);
@@ -94,7 +159,8 @@ void kvmppc_free_hpt(struct kvm *kvm)
        if (kvm->arch.hpt_li)
                kvm_release_hpt(kvm->arch.hpt_li);
        else
-               free_pages(kvm->arch.hpt_virt, HPT_ORDER - PAGE_SHIFT);
+               free_pages(kvm->arch.hpt_virt,
+                          kvm->arch.hpt_order - PAGE_SHIFT);
 }
 
 /* Bits in first HPTE dword for pagesize 4k, 64k or 16M */
@@ -119,6 +185,7 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
        unsigned long psize;
        unsigned long hp0, hp1;
        long ret;
+       struct kvm *kvm = vcpu->kvm;
 
        psize = 1ul << porder;
        npages = memslot->npages >> (porder - PAGE_SHIFT);
@@ -127,8 +194,8 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
        if (npages > 1ul << (40 - porder))
                npages = 1ul << (40 - porder);
        /* Can't use more than 1 HPTE per HPTEG */
-       if (npages > HPT_NPTEG)
-               npages = HPT_NPTEG;
+       if (npages > kvm->arch.hpt_mask + 1)
+               npages = kvm->arch.hpt_mask + 1;
 
        hp0 = HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) |
                HPTE_V_BOLTED | hpte0_pgsize_encoding(psize);
@@ -138,7 +205,7 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
        for (i = 0; i < npages; ++i) {
                addr = i << porder;
                /* can't use hpt_hash since va > 64 bits */
-               hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & HPT_HASH_MASK;
+               hash = (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & kvm->arch.hpt_mask;
                /*
                 * We assume that the hash table is empty and no
                 * vcpus are using it at this stage.  Since we create
This page took 0.027703 seconds and 5 git commands to generate.