|
@@ -37,56 +37,121 @@
|
|
|
/* 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
|