Thread (19 messages) 19 messages, 3 authors, 2011-12-19

Re: [PATCH v3 04/14] KVM: PPC: Keep page physical addresses in per-slot arrays

flat view

From: Alexander Graf <hidden>
Date: 2011-12-19 15:10:17
Also in: kvm

On 12.12.2011, at 23:28, Paul Mackerras wrote:
This allocates an array for each memory slot that is added to store
the physical addresses of the pages in the slot.  This array is
vmalloc'd and accessed in kvmppc_h_enter using real_vmalloc_addr().
This allows us to remove the ram_pginfo field from the kvm_arch
struct, and removes the 64GB guest RAM limit that we had.
=20
We use the low-order bits of the array entries to store a flag
indicating that we have done get_page on the corresponding page,
and therefore need to call put_page when we are finished with the
page.  Currently this is set for all pages except those in our
special RMO regions.
=20
Signed-off-by: Paul Mackerras <redacted>
---
arch/powerpc/include/asm/kvm_host.h |    9 ++-
arch/powerpc/kvm/book3s_64_mmu_hv.c |   18 +++---
arch/powerpc/kvm/book3s_hv.c        |  114 =
+++++++++++++++++------------------
quoted hunk ↗ jump to hunk
arch/powerpc/kvm/book3s_hv_rm_mmu.c |   41 +++++++++++-
4 files changed, 107 insertions(+), 75 deletions(-)
=20
diff --git a/arch/powerpc/include/asm/kvm_host.h =
b/arch/powerpc/include/asm/kvm_host.h
quoted hunk ↗ jump to hunk
index 629df2e..7a17ab5 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -38,6 +38,7 @@
#define KVM_MEMORY_SLOTS 32
/* memory slots that does not exposed to userspace */
#define KVM_PRIVATE_MEM_SLOTS 4
+#define KVM_MEM_SLOTS_NUM (KVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS)
=20
#ifdef CONFIG_KVM_MMIO
#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
@@ -175,25 +176,27 @@ struct revmap_entry {
	unsigned long guest_rpte;
};
=20
+/* Low-order bits in kvm->arch.slot_phys[][] */
+#define KVMPPC_GOT_PAGE		0x80
+
struct kvm_arch {
#ifdef CONFIG_KVM_BOOK3S_64_HV
	unsigned long hpt_virt;
	struct revmap_entry *revmap;
-	unsigned long ram_npages;
	unsigned long ram_psize;
	unsigned long ram_porder;
-	struct kvmppc_pginfo *ram_pginfo;
	unsigned int lpid;
	unsigned int host_lpid;
	unsigned long host_lpcr;
	unsigned long sdr1;
	unsigned long host_sdr1;
	int tlbie_lock;
-	int n_rma_pages;
	unsigned long lpcr;
	unsigned long rmor;
	struct kvmppc_rma_info *rma;
	struct list_head spapr_tce_tables;
+	unsigned long *slot_phys[KVM_MEM_SLOTS_NUM];
+	int slot_npages[KVM_MEM_SLOTS_NUM];
	unsigned short last_vcpu[NR_CPUS];
	struct kvmppc_vcore *vcores[KVM_MAX_VCORES];
#endif /* CONFIG_KVM_BOOK3S_64_HV */
diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c =
b/arch/powerpc/kvm/book3s_64_mmu_hv.c
quoted hunk ↗ jump to hunk
index 80ece8d..e4c6069 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_hv.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c
@@ -98,16 +98,16 @@ void kvmppc_free_hpt(struct kvm *kvm)
void kvmppc_map_vrma(struct kvm *kvm, struct =
kvm_userspace_memory_region *mem)
quoted hunk ↗ jump to hunk
{
	unsigned long i;
-	unsigned long npages =3D kvm->arch.ram_npages;
-	unsigned long pfn;
+	unsigned long npages;
+	unsigned long pa;
	unsigned long *hpte;
	unsigned long hash;
	unsigned long porder =3D kvm->arch.ram_porder;
	struct revmap_entry *rev;
-	struct kvmppc_pginfo *pginfo =3D kvm->arch.ram_pginfo;
+	unsigned long *physp;
=20
-	if (!pginfo)
-		return;
+	physp =3D kvm->arch.slot_phys[mem->slot];
+	npages =3D kvm->arch.slot_npages[mem->slot];
=20
	/* VRMA can't be > 1TB */
	if (npages > 1ul << (40 - porder))
@@ -117,9 +117,10 @@ void kvmppc_map_vrma(struct kvm *kvm, struct =
kvm_userspace_memory_region *mem)
		npages =3D HPT_NPTEG;
=20
	for (i =3D 0; i < npages; ++i) {
-		pfn =3D pginfo[i].pfn;
-		if (!pfn)
+		pa =3D physp[i];
+		if (!pa)
			break;
+		pa &=3D PAGE_MASK;
		/* can't use hpt_hash since va > 64 bits */
		hash =3D (i ^ (VRMA_VSID ^ (VRMA_VSID << 25))) & =
HPT_HASH_MASK;
quoted hunk ↗ jump to hunk
		/*
@@ -131,8 +132,7 @@ void kvmppc_map_vrma(struct kvm *kvm, struct =
kvm_userspace_memory_region *mem)
		hash =3D (hash << 3) + 7;
		hpte =3D (unsigned long *) (kvm->arch.hpt_virt + (hash =
<< 4));
		/* HPTE low word - RPN, protection, etc. */
-		hpte[1] =3D (pfn << PAGE_SHIFT) | HPTE_R_R | HPTE_R_C |
-			HPTE_R_M | PP_RWXX;
+		hpte[1] =3D pa | HPTE_R_R | HPTE_R_C | HPTE_R_M | =
PP_RWXX;
quoted hunk ↗ jump to hunk
		smp_wmb();
		hpte[0] =3D HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) |
			(i << (VRMA_PAGE_ORDER - 16)) | HPTE_V_BOLTED |
diff --git a/arch/powerpc/kvm/book3s_hv.c =
b/arch/powerpc/kvm/book3s_hv.c
quoted hunk ↗ jump to hunk
index da7db14..86d3e4b 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -50,14 +50,6 @@
#include <linux/vmalloc.h>
#include <linux/highmem.h>
=20
-/*
- * For now, limit memory to 64GB and require it to be large pages.
- * This value is chosen because it makes the ram_pginfo array be
- * 64kB in size, which is about as large as we want to be trying
- * to allocate with kmalloc.
- */
-#define MAX_MEM_ORDER		36
-
#define LARGE_PAGE_ORDER	24	/* 16MB pages */
=20
/* #define EXIT_DEBUG */
@@ -147,10 +139,12 @@ static unsigned long do_h_register_vpa(struct =
kvm_vcpu *vcpu,
				       unsigned long vcpuid, unsigned =
long vpa)
quoted hunk ↗ jump to hunk
{
	struct kvm *kvm =3D vcpu->kvm;
-	unsigned long pg_index, ra, len;
+	unsigned long gfn, pg_index, ra, len;
	unsigned long pg_offset;
	void *va;
	struct kvm_vcpu *tvcpu;
+	struct kvm_memory_slot *memslot;
+	unsigned long *physp;
=20
	tvcpu =3D kvmppc_find_vcpu(kvm, vcpuid);
	if (!tvcpu)
@@ -164,14 +158,20 @@ static unsigned long do_h_register_vpa(struct =
kvm_vcpu *vcpu,
quoted hunk ↗ jump to hunk
		if (vpa & 0x7f)
			return H_PARAMETER;
		/* registering new area; convert logical addr to real */
-		pg_index =3D vpa >> kvm->arch.ram_porder;
-		pg_offset =3D vpa & (kvm->arch.ram_psize - 1);
-		if (pg_index >=3D kvm->arch.ram_npages)
+		gfn =3D vpa >> PAGE_SHIFT;
+		memslot =3D gfn_to_memslot(kvm, gfn);
+		if (!memslot || !(memslot->flags & KVM_MEMSLOT_INVALID))
+			return H_PARAMETER;
+		physp =3D kvm->arch.slot_phys[memslot->id];
+		if (!physp)
			return H_PARAMETER;
-		if (kvm->arch.ram_pginfo[pg_index].pfn =3D=3D 0)
+		pg_index =3D (gfn - memslot->base_gfn) >>
+			(kvm->arch.ram_porder - PAGE_SHIFT);
+		pg_offset =3D vpa & (kvm->arch.ram_psize - 1);
+		ra =3D physp[pg_index];
+		if (!ra)
			return H_PARAMETER;
-		ra =3D kvm->arch.ram_pginfo[pg_index].pfn << PAGE_SHIFT;
-		ra |=3D pg_offset;
+		ra =3D (ra & PAGE_MASK) | pg_offset;
		va =3D __va(ra);
		if (flags <=3D 1)
			len =3D *(unsigned short *)(va + 4);
@@ -1108,12 +1108,11 @@ int kvmppc_core_prepare_memory_region(struct =
kvm *kvm,
quoted hunk ↗ jump to hunk
				struct kvm_userspace_memory_region *mem)
{
	unsigned long psize, porder;
-	unsigned long i, npages, totalpages;
-	unsigned long pg_ix;
-	struct kvmppc_pginfo *pginfo;
+	unsigned long i, npages;
	unsigned long hva;
	struct kvmppc_rma_info *ri =3D NULL;
	struct page *page;
+	unsigned long *phys;
=20
	/* For now, only allow 16MB pages */
	porder =3D LARGE_PAGE_ORDER;
@@ -1125,20 +1124,21 @@ int kvmppc_core_prepare_memory_region(struct =
kvm *kvm,
		return -EINVAL;
	}
=20
+	/* Allocate a slot_phys array */
	npages =3D mem->memory_size >> porder;
-	totalpages =3D (mem->guest_phys_addr + mem->memory_size) >> =
porder;
quoted hunk ↗ jump to hunk
-
-	/* More memory than we have space to track? */
-	if (totalpages > (1ul << (MAX_MEM_ORDER - LARGE_PAGE_ORDER)))
-		return -EINVAL;
+	phys =3D kvm->arch.slot_phys[mem->slot];
+	if (!phys) {
+		phys =3D vzalloc(npages * sizeof(unsigned long));
+		if (!phys)
+			return -ENOMEM;
+		kvm->arch.slot_phys[mem->slot] =3D phys;
+		kvm->arch.slot_npages[mem->slot] =3D npages;
+	}
=20
	/* Do we already have an RMA registered? */
	if (mem->guest_phys_addr =3D=3D 0 && kvm->arch.rma)
		return -EINVAL;
=20
-	if (totalpages > kvm->arch.ram_npages)
-		kvm->arch.ram_npages =3D totalpages;
-
	/* Is this one of our preallocated RMAs? */
	if (mem->guest_phys_addr =3D=3D 0) {
		struct vm_area_struct *vma;
@@ -1171,7 +1171,6 @@ int kvmppc_core_prepare_memory_region(struct kvm =
*kvm,
quoted hunk ↗ jump to hunk
		}
		atomic_inc(&ri->use_count);
		kvm->arch.rma =3D ri;
-		kvm->arch.n_rma_pages =3D rma_size >> porder;
=20
		/* Update LPCR and RMOR */
		lpcr =3D kvm->arch.lpcr;
@@ -1195,12 +1194,9 @@ int kvmppc_core_prepare_memory_region(struct =
kvm *kvm,
			ri->base_pfn << PAGE_SHIFT, rma_size, lpcr);
	}
=20
-	pg_ix =3D mem->guest_phys_addr >> porder;
-	pginfo =3D kvm->arch.ram_pginfo + pg_ix;
-	for (i =3D 0; i < npages; ++i, ++pg_ix) {
-		if (ri && pg_ix < kvm->arch.n_rma_pages) {
-			pginfo[i].pfn =3D ri->base_pfn +
-				(pg_ix << (porder - PAGE_SHIFT));
+	for (i =3D 0; i < npages; ++i) {
+		if (ri && i < ri->npages) {
+			phys[i] =3D (ri->base_pfn << PAGE_SHIFT) + (i << =
porder);
quoted hunk ↗ jump to hunk
			continue;
		}
		hva =3D mem->userspace_addr + (i << porder);
@@ -1216,7 +1212,7 @@ int kvmppc_core_prepare_memory_region(struct kvm =
*kvm,
			       hva, compound_order(page));
			goto err;
		}
-		pginfo[i].pfn =3D page_to_pfn(page);
+		phys[i] =3D (page_to_pfn(page) << PAGE_SHIFT) | =
KVMPPC_GOT_PAGE;
quoted hunk ↗ jump to hunk
	}
=20
	return 0;
@@ -1225,6 +1221,28 @@ int kvmppc_core_prepare_memory_region(struct =
kvm *kvm,
quoted hunk ↗ jump to hunk
	return -EINVAL;
}
=20
+static void unpin_slot(struct kvm *kvm, int slot_id)
+{
+	unsigned long *physp;
+	unsigned long j, npages, pfn;
+	struct page *page;
+
+	physp =3D kvm->arch.slot_phys[slot_id];
+	npages =3D kvm->arch.slot_npages[slot_id];
+	if (physp) {
+		for (j =3D 0; j < npages; j++) {
+			if (!(physp[j] & KVMPPC_GOT_PAGE))
+				continue;
+			pfn =3D physp[j] >> PAGE_SHIFT;
+			page =3D pfn_to_page(pfn);
+			SetPageDirty(page);
+			put_page(page);
+		}
+		vfree(physp);
+		kvm->arch.slot_phys[slot_id] =3D NULL;
+	}
+}
+
void kvmppc_core_commit_memory_region(struct kvm *kvm,
				struct kvm_userspace_memory_region *mem)
{
@@ -1236,8 +1254,6 @@ void kvmppc_core_commit_memory_region(struct kvm =
*kvm,
int kvmppc_core_init_vm(struct kvm *kvm)
{
	long r;
-	unsigned long npages =3D 1ul << (MAX_MEM_ORDER - =
LARGE_PAGE_ORDER);
quoted hunk ↗ jump to hunk
-	long err =3D -ENOMEM;
	unsigned long lpcr;
=20
	/* Allocate hashed page table */
@@ -1247,19 +1263,9 @@ int kvmppc_core_init_vm(struct kvm *kvm)
=20
	INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables);
=20
-	kvm->arch.ram_pginfo =3D kzalloc(npages * sizeof(struct =
kvmppc_pginfo),
-				       GFP_KERNEL);
-	if (!kvm->arch.ram_pginfo) {
-		pr_err("kvmppc_core_init_vm: couldn't alloc %lu =
bytes\n",
quoted hunk ↗ jump to hunk
-		       npages * sizeof(struct kvmppc_pginfo));
-		goto out_free;
-	}
-
-	kvm->arch.ram_npages =3D 0;
	kvm->arch.ram_psize =3D 1ul << LARGE_PAGE_ORDER;
	kvm->arch.ram_porder =3D LARGE_PAGE_ORDER;
	kvm->arch.rma =3D NULL;
-	kvm->arch.n_rma_pages =3D 0;
=20
	kvm->arch.host_sdr1 =3D mfspr(SPRN_SDR1);
=20
@@ -1282,25 +1288,15 @@ int kvmppc_core_init_vm(struct kvm *kvm)
	kvm->arch.lpcr =3D lpcr;
=20
	return 0;
-
- out_free:
-	kvmppc_free_hpt(kvm);
-	return err;
}
=20
void kvmppc_core_destroy_vm(struct kvm *kvm)
{
-	struct kvmppc_pginfo *pginfo;
	unsigned long i;
=20
-	if (kvm->arch.ram_pginfo) {
-		pginfo =3D kvm->arch.ram_pginfo;
-		kvm->arch.ram_pginfo =3D NULL;
-		for (i =3D kvm->arch.n_rma_pages; i < =
kvm->arch.ram_npages; ++i)
quoted hunk ↗ jump to hunk
-			if (pginfo[i].pfn)
-				put_page(pfn_to_page(pginfo[i].pfn));
-		kfree(pginfo);
-	}
+	for (i =3D 0; i < KVM_MEM_SLOTS_NUM; i++)
+		unpin_slot(kvm, i);
+
	if (kvm->arch.rma) {
		kvm_release_rma(kvm->arch.rma);
		kvm->arch.rma =3D NULL;
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c =
b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
quoted hunk ↗ jump to hunk
index 6148493..84dae82 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -20,6 +20,25 @@
#include <asm/synch.h>
#include <asm/ppc-opcode.h>
=20
+/*
+ * Since this file is built in even if KVM is a module, we need
+ * a local copy of this function for the case where kvm_main.c is
+ * modular.
+ */
+static struct kvm_memory_slot *builtin_gfn_to_memslot(struct kvm =
*kvm,
+						gfn_t gfn)
+{
Shouldn't this rather be in a header file then? I'd rather not have this =
code duplicated. Please follow up with a patch to merge this copy and =
the real one into something in a header file.


Alex
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help