KVM: PPC: e500: Support large page mappings of PFNMAP vmas.
This allows large pages to be used on guest mappings backed by things like /dev/mem, resulting in a significant speedup when guest memory is mapped this way (it's useful for directly-assigned MMIO, too). This is not a substitute for hugetlbfs integration, but is useful for configurations where devices are directly assigned on chips without an IOMMU -- in these cases, we need guest physical and true physical to match, and be contiguous, so static reservation and mapping via /dev/mem is the most straightforward way to set things up. Signed-off-by: Scott Wood <scottwood@freescale.com> Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
parent
59c1f4e35c
commit
9973d54eea
|
@ -270,28 +270,113 @@ static inline void kvmppc_e500_deliver_tlb_miss(struct kvm_vcpu *vcpu,
|
||||||
static inline void kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,
|
static inline void kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,
|
||||||
u64 gvaddr, gfn_t gfn, struct tlbe *gtlbe, int tlbsel, int esel)
|
u64 gvaddr, gfn_t gfn, struct tlbe *gtlbe, int tlbsel, int esel)
|
||||||
{
|
{
|
||||||
|
struct kvm_memory_slot *slot;
|
||||||
struct tlbe *stlbe;
|
struct tlbe *stlbe;
|
||||||
unsigned long pfn;
|
unsigned long pfn, hva;
|
||||||
|
int pfnmap = 0;
|
||||||
|
int tsize = BOOK3E_PAGESZ_4K;
|
||||||
|
|
||||||
stlbe = &vcpu_e500->shadow_tlb[tlbsel][esel];
|
stlbe = &vcpu_e500->shadow_tlb[tlbsel][esel];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Translate guest physical to true physical, acquiring
|
* Translate guest physical to true physical, acquiring
|
||||||
* a page reference if it is normal, non-reserved memory.
|
* a page reference if it is normal, non-reserved memory.
|
||||||
|
*
|
||||||
|
* gfn_to_memslot() must succeed because otherwise we wouldn't
|
||||||
|
* have gotten this far. Eventually we should just pass the slot
|
||||||
|
* pointer through from the first lookup.
|
||||||
*/
|
*/
|
||||||
pfn = gfn_to_pfn(vcpu_e500->vcpu.kvm, gfn);
|
slot = gfn_to_memslot(vcpu_e500->vcpu.kvm, gfn);
|
||||||
if (is_error_pfn(pfn)) {
|
hva = gfn_to_hva_memslot(slot, gfn);
|
||||||
printk(KERN_ERR "Couldn't get real page for gfn %lx!\n",
|
|
||||||
(long)gfn);
|
if (tlbsel == 1) {
|
||||||
kvm_release_pfn_clean(pfn);
|
struct vm_area_struct *vma;
|
||||||
return;
|
down_read(¤t->mm->mmap_sem);
|
||||||
|
|
||||||
|
vma = find_vma(current->mm, hva);
|
||||||
|
if (vma && hva >= vma->vm_start &&
|
||||||
|
(vma->vm_flags & VM_PFNMAP)) {
|
||||||
|
/*
|
||||||
|
* This VMA is a physically contiguous region (e.g.
|
||||||
|
* /dev/mem) that bypasses normal Linux page
|
||||||
|
* management. Find the overlap between the
|
||||||
|
* vma and the memslot.
|
||||||
|
*/
|
||||||
|
|
||||||
|
unsigned long start, end;
|
||||||
|
unsigned long slot_start, slot_end;
|
||||||
|
|
||||||
|
pfnmap = 1;
|
||||||
|
|
||||||
|
start = vma->vm_pgoff;
|
||||||
|
end = start +
|
||||||
|
((vma->vm_end - vma->vm_start) >> PAGE_SHIFT);
|
||||||
|
|
||||||
|
pfn = start + ((hva - vma->vm_start) >> PAGE_SHIFT);
|
||||||
|
|
||||||
|
slot_start = pfn - (gfn - slot->base_gfn);
|
||||||
|
slot_end = slot_start + slot->npages;
|
||||||
|
|
||||||
|
if (start < slot_start)
|
||||||
|
start = slot_start;
|
||||||
|
if (end > slot_end)
|
||||||
|
end = slot_end;
|
||||||
|
|
||||||
|
tsize = (gtlbe->mas1 & MAS1_TSIZE_MASK) >>
|
||||||
|
MAS1_TSIZE_SHIFT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* e500 doesn't implement the lowest tsize bit,
|
||||||
|
* or 1K pages.
|
||||||
|
*/
|
||||||
|
tsize = max(BOOK3E_PAGESZ_4K, tsize & ~1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now find the largest tsize (up to what the guest
|
||||||
|
* requested) that will cover gfn, stay within the
|
||||||
|
* range, and for which gfn and pfn are mutually
|
||||||
|
* aligned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (; tsize > BOOK3E_PAGESZ_4K; tsize -= 2) {
|
||||||
|
unsigned long gfn_start, gfn_end, tsize_pages;
|
||||||
|
tsize_pages = 1 << (tsize - 2);
|
||||||
|
|
||||||
|
gfn_start = gfn & ~(tsize_pages - 1);
|
||||||
|
gfn_end = gfn_start + tsize_pages;
|
||||||
|
|
||||||
|
if (gfn_start + pfn - gfn < start)
|
||||||
|
continue;
|
||||||
|
if (gfn_end + pfn - gfn > end)
|
||||||
|
continue;
|
||||||
|
if ((gfn & (tsize_pages - 1)) !=
|
||||||
|
(pfn & (tsize_pages - 1)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gvaddr &= ~((tsize_pages << PAGE_SHIFT) - 1);
|
||||||
|
pfn &= ~(tsize_pages - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
up_read(¤t->mm->mmap_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(!pfnmap)) {
|
||||||
|
pfn = gfn_to_pfn_memslot(vcpu_e500->vcpu.kvm, slot, gfn);
|
||||||
|
if (is_error_pfn(pfn)) {
|
||||||
|
printk(KERN_ERR "Couldn't get real page for gfn %lx!\n",
|
||||||
|
(long)gfn);
|
||||||
|
kvm_release_pfn_clean(pfn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drop reference to old page. */
|
/* Drop reference to old page. */
|
||||||
kvmppc_e500_shadow_release(vcpu_e500, tlbsel, esel);
|
kvmppc_e500_shadow_release(vcpu_e500, tlbsel, esel);
|
||||||
|
|
||||||
/* Force TS=1 IPROT=0 TSIZE=4KB for all guest mappings. */
|
/* Force TS=1 IPROT=0 for all guest mappings. */
|
||||||
stlbe->mas1 = MAS1_TSIZE(BOOK3E_PAGESZ_4K)
|
stlbe->mas1 = MAS1_TSIZE(tsize)
|
||||||
| MAS1_TID(get_tlb_tid(gtlbe)) | MAS1_TS | MAS1_VALID;
|
| MAS1_TID(get_tlb_tid(gtlbe)) | MAS1_TS | MAS1_VALID;
|
||||||
stlbe->mas2 = (gvaddr & MAS2_EPN)
|
stlbe->mas2 = (gvaddr & MAS2_EPN)
|
||||||
| e500_shadow_mas2_attrib(gtlbe->mas2,
|
| e500_shadow_mas2_attrib(gtlbe->mas2,
|
||||||
|
|
Loading…
Reference in New Issue