Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da9d1d7f authored by Paul Mackerras's avatar Paul Mackerras Committed by Avi Kivity
Browse files

KVM: PPC: Allow use of small pages to back Book3S HV guests



This relaxes the requirement that the guest memory be provided as
16MB huge pages, allowing it to be provided as normal memory, i.e.
in pages of PAGE_SIZE bytes (4k or 64k).  To allow this, we index
the kvm->arch.slot_phys[] arrays with a small page index, even if
huge pages are being used, and use the low-order 5 bits of each
entry to store the order of the enclosing page with respect to
normal pages, i.e. log_2(enclosing_page_size / PAGE_SIZE).

Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent c77162de
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -113,4 +113,14 @@ static inline unsigned long hpte_page_size(unsigned long h, unsigned long l)
	return 0;				/* error */
	return 0;				/* error */
}
}


static inline bool slot_is_aligned(struct kvm_memory_slot *memslot,
				   unsigned long pagesize)
{
	unsigned long mask = (pagesize >> PAGE_SHIFT) - 1;

	if (pagesize <= PAGE_SIZE)
		return 1;
	return !(memslot->base_gfn & mask) && !(memslot->npages & mask);
}

#endif /* __ASM_KVM_BOOK3S_64_H__ */
#endif /* __ASM_KVM_BOOK3S_64_H__ */
+1 −2
Original line number Original line Diff line number Diff line
@@ -177,14 +177,13 @@ struct revmap_entry {
};
};


/* Low-order bits in kvm->arch.slot_phys[][] */
/* Low-order bits in kvm->arch.slot_phys[][] */
#define KVMPPC_PAGE_ORDER_MASK	0x1f
#define KVMPPC_GOT_PAGE		0x80
#define KVMPPC_GOT_PAGE		0x80


struct kvm_arch {
struct kvm_arch {
#ifdef CONFIG_KVM_BOOK3S_64_HV
#ifdef CONFIG_KVM_BOOK3S_64_HV
	unsigned long hpt_virt;
	unsigned long hpt_virt;
	struct revmap_entry *revmap;
	struct revmap_entry *revmap;
	unsigned long ram_psize;
	unsigned long ram_porder;
	unsigned int lpid;
	unsigned int lpid;
	unsigned int host_lpid;
	unsigned int host_lpid;
	unsigned long host_lpcr;
	unsigned long host_lpcr;
+1 −1
Original line number Original line Diff line number Diff line
@@ -122,7 +122,7 @@ extern void kvmppc_free_hpt(struct kvm *kvm);
extern long kvmppc_prepare_vrma(struct kvm *kvm,
extern long kvmppc_prepare_vrma(struct kvm *kvm,
				struct kvm_userspace_memory_region *mem);
				struct kvm_userspace_memory_region *mem);
extern void kvmppc_map_vrma(struct kvm_vcpu *vcpu,
extern void kvmppc_map_vrma(struct kvm_vcpu *vcpu,
			    struct kvm_memory_slot *memslot);
			struct kvm_memory_slot *memslot, unsigned long porder);
extern int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu);
extern int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu);
extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
extern long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
				struct kvm_create_spapr_tce *args);
				struct kvm_create_spapr_tce *args);
+1 −0
Original line number Original line Diff line number Diff line
@@ -237,6 +237,7 @@
#define   LPCR_ISL	(1ul << (63-2))
#define   LPCR_ISL	(1ul << (63-2))
#define   LPCR_VC_SH	(63-2)
#define   LPCR_VC_SH	(63-2)
#define   LPCR_DPFD_SH	(63-11)
#define   LPCR_DPFD_SH	(63-11)
#define   LPCR_VRMASD	(0x1ful << (63-16))
#define   LPCR_VRMA_L	(1ul << (63-12))
#define   LPCR_VRMA_L	(1ul << (63-12))
#define   LPCR_VRMA_LP0	(1ul << (63-15))
#define   LPCR_VRMA_LP0	(1ul << (63-15))
#define   LPCR_VRMA_LP1	(1ul << (63-16))
#define   LPCR_VRMA_LP1	(1ul << (63-16))
+83 −39
Original line number Original line Diff line number Diff line
@@ -34,8 +34,6 @@
#include <asm/ppc-opcode.h>
#include <asm/ppc-opcode.h>
#include <asm/cputable.h>
#include <asm/cputable.h>


/* Pages in the VRMA are 16MB pages */
#define VRMA_PAGE_ORDER	24
#define VRMA_VSID	0x1ffffffUL	/* 1TB VSID reserved for VRMA */
#define VRMA_VSID	0x1ffffffUL	/* 1TB VSID reserved for VRMA */


/* POWER7 has 10-bit LPIDs, PPC970 has 6-bit LPIDs */
/* POWER7 has 10-bit LPIDs, PPC970 has 6-bit LPIDs */
@@ -95,17 +93,31 @@ void kvmppc_free_hpt(struct kvm *kvm)
	free_pages(kvm->arch.hpt_virt, HPT_ORDER - PAGE_SHIFT);
	free_pages(kvm->arch.hpt_virt, HPT_ORDER - PAGE_SHIFT);
}
}


void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot)
/* Bits in first HPTE dword for pagesize 4k, 64k or 16M */
static inline unsigned long hpte0_pgsize_encoding(unsigned long pgsize)
{
	return (pgsize > 0x1000) ? HPTE_V_LARGE : 0;
}

/* Bits in second HPTE dword for pagesize 4k, 64k or 16M */
static inline unsigned long hpte1_pgsize_encoding(unsigned long pgsize)
{
	return (pgsize == 0x10000) ? 0x1000 : 0;
}

void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
		     unsigned long porder)
{
{
	struct kvm *kvm = vcpu->kvm;
	unsigned long i;
	unsigned long i;
	unsigned long npages;
	unsigned long npages;
	unsigned long hp_v, hp_r;
	unsigned long hp_v, hp_r;
	unsigned long addr, hash;
	unsigned long addr, hash;
	unsigned long porder = kvm->arch.ram_porder;
	unsigned long psize;
	unsigned long hp0, hp1;
	long ret;
	long ret;


	npages = kvm->arch.slot_npages[memslot->id];
	psize = 1ul << porder;
	npages = memslot->npages >> (porder - PAGE_SHIFT);


	/* VRMA can't be > 1TB */
	/* VRMA can't be > 1TB */
	if (npages > 1ul << (40 - porder))
	if (npages > 1ul << (40 - porder))
@@ -114,6 +126,11 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot)
	if (npages > HPT_NPTEG)
	if (npages > HPT_NPTEG)
		npages = HPT_NPTEG;
		npages = HPT_NPTEG;


	hp0 = HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) |
		HPTE_V_BOLTED | hpte0_pgsize_encoding(psize);
	hp1 = hpte1_pgsize_encoding(psize) |
		HPTE_R_R | HPTE_R_C | HPTE_R_M | PP_RWXX;

	for (i = 0; i < npages; ++i) {
	for (i = 0; i < npages; ++i) {
		addr = i << porder;
		addr = i << porder;
		/* can't use hpt_hash since va > 64 bits */
		/* can't use hpt_hash since va > 64 bits */
@@ -125,10 +142,8 @@ void kvmppc_map_vrma(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot)
		 * is available and use it.
		 * is available and use it.
		 */
		 */
		hash = (hash << 3) + 7;
		hash = (hash << 3) + 7;
		hp_v = HPTE_V_1TB_SEG | (VRMA_VSID << (40 - 16)) |
		hp_v = hp0 | ((addr >> 16) & ~0x7fUL);
			(i << (VRMA_PAGE_ORDER - 16)) | HPTE_V_BOLTED |
		hp_r = hp1 | addr;
			HPTE_V_LARGE | HPTE_V_VALID;
		hp_r = addr | HPTE_R_R | HPTE_R_C | HPTE_R_M | PP_RWXX;
		ret = kvmppc_virtmode_h_enter(vcpu, H_EXACT, hash, hp_v, hp_r);
		ret = kvmppc_virtmode_h_enter(vcpu, H_EXACT, hash, hp_v, hp_r);
		if (ret != H_SUCCESS) {
		if (ret != H_SUCCESS) {
			pr_err("KVM: map_vrma at %lx failed, ret=%ld\n",
			pr_err("KVM: map_vrma at %lx failed, ret=%ld\n",
@@ -176,22 +191,25 @@ static void kvmppc_mmu_book3s_64_hv_reset_msr(struct kvm_vcpu *vcpu)
 * one already in the kvm->arch.slot_phys[][] arrays.
 * one already in the kvm->arch.slot_phys[][] arrays.
 */
 */
static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
				  struct kvm_memory_slot *memslot)
				  struct kvm_memory_slot *memslot,
				  unsigned long psize)
{
{
	unsigned long start;
	unsigned long start;
	long np;
	long np, err;
	struct page *page, *pages[1];
	struct page *page, *hpage, *pages[1];
	unsigned long s, pgsize;
	unsigned long *physp;
	unsigned long *physp;
	unsigned long pfn, i;
	unsigned int got, pgorder;
	unsigned long pfn, i, npages;


	physp = kvm->arch.slot_phys[memslot->id];
	physp = kvm->arch.slot_phys[memslot->id];
	if (!physp)
	if (!physp)
		return -EINVAL;
		return -EINVAL;
	i = (gfn - memslot->base_gfn) >> (kvm->arch.ram_porder - PAGE_SHIFT);
	if (physp[gfn - memslot->base_gfn])
	if (physp[i])
		return 0;
		return 0;


	page = NULL;
	page = NULL;
	pgsize = psize;
	start = gfn_to_hva_memslot(memslot, gfn);
	start = gfn_to_hva_memslot(memslot, gfn);


	/* Instantiate and get the page we want access to */
	/* Instantiate and get the page we want access to */
@@ -199,25 +217,46 @@ static long kvmppc_get_guest_page(struct kvm *kvm, unsigned long gfn,
	if (np != 1)
	if (np != 1)
		return -EINVAL;
		return -EINVAL;
	page = pages[0];
	page = pages[0];

	got = KVMPPC_GOT_PAGE;
	/* Check it's a 16MB page */

	if (!PageHead(page) ||
	/* See if this is a large page */
	    compound_order(page) != (kvm->arch.ram_porder - PAGE_SHIFT)) {
	s = PAGE_SIZE;
		pr_err("page at %lx isn't 16MB (o=%d)\n",
	if (PageHuge(page)) {
		       start, compound_order(page));
		hpage = compound_head(page);
		put_page(page);
		s <<= compound_order(hpage);
		return -EINVAL;
		/* Get the whole large page if slot alignment is ok */
		if (s > psize && slot_is_aligned(memslot, s) &&
		    !(memslot->userspace_addr & (s - 1))) {
			start &= ~(s - 1);
			pgsize = s;
			page = hpage;
		}
	}
	}
	err = -EINVAL;
	if (s < psize)
		goto out;
	pfn = page_to_pfn(page);
	pfn = page_to_pfn(page);


	npages = pgsize >> PAGE_SHIFT;
	pgorder = __ilog2(npages);
	physp += (gfn - memslot->base_gfn) & ~(npages - 1);
	spin_lock(&kvm->arch.slot_phys_lock);
	spin_lock(&kvm->arch.slot_phys_lock);
	if (!physp[i])
	for (i = 0; i < npages; ++i) {
		physp[i] = (pfn << PAGE_SHIFT) | KVMPPC_GOT_PAGE;
		if (!physp[i]) {
	else
			physp[i] = ((pfn + i) << PAGE_SHIFT) + got + pgorder;
		put_page(page);
			got = 0;
		}
	}
	spin_unlock(&kvm->arch.slot_phys_lock);
	spin_unlock(&kvm->arch.slot_phys_lock);
	err = 0;


	return 0;
 out:
	if (got) {
		if (PageHuge(page))
			page = compound_head(page);
		put_page(page);
	}
	return err;
}
}


/*
/*
@@ -242,7 +281,9 @@ long kvmppc_virtmode_h_enter(struct kvm_vcpu *vcpu, unsigned long flags,
	memslot = gfn_to_memslot(kvm, gfn);
	memslot = gfn_to_memslot(kvm, gfn);
	if (!memslot || (memslot->flags & KVM_MEMSLOT_INVALID))
	if (!memslot || (memslot->flags & KVM_MEMSLOT_INVALID))
		return H_PARAMETER;
		return H_PARAMETER;
	if (kvmppc_get_guest_page(kvm, gfn, memslot) < 0)
	if (!slot_is_aligned(memslot, psize))
		return H_PARAMETER;
	if (kvmppc_get_guest_page(kvm, gfn, memslot, psize) < 0)
		return H_PARAMETER;
		return H_PARAMETER;


	preempt_disable();
	preempt_disable();
@@ -269,8 +310,8 @@ void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa,
	struct kvm_memory_slot *memslot;
	struct kvm_memory_slot *memslot;
	unsigned long gfn = gpa >> PAGE_SHIFT;
	unsigned long gfn = gpa >> PAGE_SHIFT;
	struct page *page;
	struct page *page;
	unsigned long offset;
	unsigned long psize, offset;
	unsigned long pfn, pa;
	unsigned long pa;
	unsigned long *physp;
	unsigned long *physp;


	memslot = gfn_to_memslot(kvm, gfn);
	memslot = gfn_to_memslot(kvm, gfn);
@@ -279,20 +320,23 @@ void *kvmppc_pin_guest_page(struct kvm *kvm, unsigned long gpa,
	physp = kvm->arch.slot_phys[memslot->id];
	physp = kvm->arch.slot_phys[memslot->id];
	if (!physp)
	if (!physp)
		return NULL;
		return NULL;
	physp += (gfn - memslot->base_gfn) >>
	physp += gfn - memslot->base_gfn;
		(kvm->arch.ram_porder - PAGE_SHIFT);
	pa = *physp;
	pa = *physp;
	if (!pa) {
	if (!pa) {
		if (kvmppc_get_guest_page(kvm, gfn, memslot) < 0)
		if (kvmppc_get_guest_page(kvm, gfn, memslot, PAGE_SIZE) < 0)
			return NULL;
			return NULL;
		pa = *physp;
		pa = *physp;
	}
	}
	pfn = pa >> PAGE_SHIFT;
	page = pfn_to_page(pa >> PAGE_SHIFT);
	page = pfn_to_page(pfn);
	psize = PAGE_SIZE;
	if (PageHuge(page)) {
		page = compound_head(page);
		psize <<= compound_order(page);
	}
	get_page(page);
	get_page(page);
	offset = gpa & (kvm->arch.ram_psize - 1);
	offset = gpa & (psize - 1);
	if (nb_ret)
	if (nb_ret)
		*nb_ret = kvm->arch.ram_psize - offset;
		*nb_ret = psize - offset;
	return page_address(page) + offset;
	return page_address(page) + offset;
}
}


Loading