|
@@ -26,11 +26,14 @@ static void *real_vmalloc_addr(void *x)
|
|
|
{
|
|
|
unsigned long addr = (unsigned long) x;
|
|
|
pte_t *p;
|
|
|
-
|
|
|
- p = find_linux_pte_or_hugepte(swapper_pg_dir, addr, NULL);
|
|
|
+ /*
|
|
|
+ * assume we don't have huge pages in vmalloc space...
|
|
|
+ * So don't worry about THP collapse/split. Called
|
|
|
+ * Only in realmode, hence won't need irq_save/restore.
|
|
|
+ */
|
|
|
+ p = __find_linux_pte_or_hugepte(swapper_pg_dir, addr, NULL);
|
|
|
if (!p || !pte_present(*p))
|
|
|
return NULL;
|
|
|
- /* assume we don't have huge pages in vmalloc space... */
|
|
|
addr = (pte_pfn(*p) << PAGE_SHIFT) | (addr & ~PAGE_MASK);
|
|
|
return __va(addr);
|
|
|
}
|
|
@@ -131,25 +134,6 @@ static void remove_revmap_chain(struct kvm *kvm, long pte_index,
|
|
|
unlock_rmap(rmap);
|
|
|
}
|
|
|
|
|
|
-static pte_t lookup_linux_pte_and_update(pgd_t *pgdir, unsigned long hva,
|
|
|
- int writing, unsigned long *pte_sizep)
|
|
|
-{
|
|
|
- pte_t *ptep;
|
|
|
- unsigned long ps = *pte_sizep;
|
|
|
- unsigned int hugepage_shift;
|
|
|
-
|
|
|
- ptep = find_linux_pte_or_hugepte(pgdir, hva, &hugepage_shift);
|
|
|
- if (!ptep)
|
|
|
- return __pte(0);
|
|
|
- if (hugepage_shift)
|
|
|
- *pte_sizep = 1ul << hugepage_shift;
|
|
|
- else
|
|
|
- *pte_sizep = PAGE_SIZE;
|
|
|
- if (ps > *pte_sizep)
|
|
|
- return __pte(0);
|
|
|
- return kvmppc_read_update_linux_pte(ptep, writing, hugepage_shift);
|
|
|
-}
|
|
|
-
|
|
|
long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
|
|
|
long pte_index, unsigned long pteh, unsigned long ptel,
|
|
|
pgd_t *pgdir, bool realmode, unsigned long *pte_idx_ret)
|
|
@@ -160,13 +144,13 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
|
|
|
struct revmap_entry *rev;
|
|
|
unsigned long g_ptel;
|
|
|
struct kvm_memory_slot *memslot;
|
|
|
- unsigned long pte_size;
|
|
|
+ unsigned hpage_shift;
|
|
|
unsigned long is_io;
|
|
|
unsigned long *rmap;
|
|
|
- pte_t pte;
|
|
|
+ pte_t *ptep;
|
|
|
unsigned int writing;
|
|
|
unsigned long mmu_seq;
|
|
|
- unsigned long rcbits;
|
|
|
+ unsigned long rcbits, irq_flags = 0;
|
|
|
|
|
|
psize = hpte_page_size(pteh, ptel);
|
|
|
if (!psize)
|
|
@@ -202,22 +186,46 @@ long kvmppc_do_h_enter(struct kvm *kvm, unsigned long flags,
|
|
|
|
|
|
/* Translate to host virtual address */
|
|
|
hva = __gfn_to_hva_memslot(memslot, gfn);
|
|
|
-
|
|
|
- /* Look up the Linux PTE for the backing page */
|
|
|
- pte_size = psize;
|
|
|
- pte = lookup_linux_pte_and_update(pgdir, hva, writing, &pte_size);
|
|
|
- if (pte_present(pte) && !pte_protnone(pte)) {
|
|
|
- if (writing && !pte_write(pte))
|
|
|
- /* make the actual HPTE be read-only */
|
|
|
- ptel = hpte_make_readonly(ptel);
|
|
|
- is_io = hpte_cache_bits(pte_val(pte));
|
|
|
- pa = pte_pfn(pte) << PAGE_SHIFT;
|
|
|
- pa |= hva & (pte_size - 1);
|
|
|
- pa |= gpa & ~PAGE_MASK;
|
|
|
+ /*
|
|
|
+ * If we had a page table table change after lookup, we would
|
|
|
+ * retry via mmu_notifier_retry.
|
|
|
+ */
|
|
|
+ if (realmode)
|
|
|
+ ptep = __find_linux_pte_or_hugepte(pgdir, hva, &hpage_shift);
|
|
|
+ else {
|
|
|
+ local_irq_save(irq_flags);
|
|
|
+ ptep = find_linux_pte_or_hugepte(pgdir, hva, &hpage_shift);
|
|
|
}
|
|
|
+ if (ptep) {
|
|
|
+ pte_t pte;
|
|
|
+ unsigned int host_pte_size;
|
|
|
|
|
|
- if (pte_size < psize)
|
|
|
- return H_PARAMETER;
|
|
|
+ if (hpage_shift)
|
|
|
+ host_pte_size = 1ul << hpage_shift;
|
|
|
+ else
|
|
|
+ host_pte_size = PAGE_SIZE;
|
|
|
+ /*
|
|
|
+ * We should always find the guest page size
|
|
|
+ * to <= host page size, if host is using hugepage
|
|
|
+ */
|
|
|
+ if (host_pte_size < psize) {
|
|
|
+ if (!realmode)
|
|
|
+ local_irq_restore(flags);
|
|
|
+ return H_PARAMETER;
|
|
|
+ }
|
|
|
+ pte = kvmppc_read_update_linux_pte(ptep, writing);
|
|
|
+ if (pte_present(pte) && !pte_protnone(pte)) {
|
|
|
+ if (writing && !pte_write(pte))
|
|
|
+ /* make the actual HPTE be read-only */
|
|
|
+ ptel = hpte_make_readonly(ptel);
|
|
|
+ is_io = hpte_cache_bits(pte_val(pte));
|
|
|
+ pa = pte_pfn(pte) << PAGE_SHIFT;
|
|
|
+ pa |= hva & (host_pte_size - 1);
|
|
|
+ pa |= gpa & ~PAGE_MASK;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!realmode)
|
|
|
+ local_irq_restore(irq_flags);
|
|
|
|
|
|
ptel &= ~(HPTE_R_PP0 - psize);
|
|
|
ptel |= pa;
|