Commit db7cb5b9 authored by Aneesh Kumar K.V's avatar Aneesh Kumar K.V Committed by Benjamin Herrenschmidt

powerpc/kvm: Handle transparent hugepage in KVM

We can find pte that are splitting while walking page tables. Return
None pte in that case.
Signed-off-by: default avatarAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 12bc9f6f
...@@ -159,36 +159,46 @@ static inline int hpte_cache_flags_ok(unsigned long ptel, unsigned long io_type) ...@@ -159,36 +159,46 @@ static inline int hpte_cache_flags_ok(unsigned long ptel, unsigned long io_type)
} }
/* /*
* Lock and read a linux PTE. If it's present and writable, atomically * If it's present and writable, atomically set dirty and referenced bits and
* set dirty and referenced bits and return the PTE, otherwise return 0. * return the PTE, otherwise return 0. If we find a transparent hugepage
* and if it is marked splitting we return 0;
*/ */
static inline pte_t kvmppc_read_update_linux_pte(pte_t *p, int writing) static inline pte_t kvmppc_read_update_linux_pte(pte_t *ptep, int writing,
unsigned int hugepage)
{ {
pte_t pte, tmp; pte_t old_pte, new_pte = __pte(0);
/* wait until _PAGE_BUSY is clear then set it atomically */ while (1) {
__asm__ __volatile__ ( old_pte = pte_val(*ptep);
"1: ldarx %0,0,%3\n" /*
" andi. %1,%0,%4\n" * wait until _PAGE_BUSY is clear then set it atomically
" bne- 1b\n" */
" ori %1,%0,%4\n" if (unlikely(old_pte & _PAGE_BUSY)) {
" stdcx. %1,0,%3\n" cpu_relax();
" bne- 1b" continue;
: "=&r" (pte), "=&r" (tmp), "=m" (*p)
: "r" (p), "i" (_PAGE_BUSY)
: "cc");
if (pte_present(pte)) {
pte = pte_mkyoung(pte);
if (writing && pte_write(pte))
pte = pte_mkdirty(pte);
} }
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
/* If hugepage and is trans splitting return None */
if (unlikely(hugepage &&
pmd_trans_splitting(pte_pmd(old_pte))))
return __pte(0);
#endif
/* If pte is not present return None */
if (unlikely(!(old_pte & _PAGE_PRESENT)))
return __pte(0);
*p = pte; /* clears _PAGE_BUSY */ new_pte = pte_mkyoung(old_pte);
if (writing && pte_write(old_pte))
new_pte = pte_mkdirty(new_pte);
return pte; if (old_pte == __cmpxchg_u64((unsigned long *)ptep, old_pte,
new_pte))
break;
}
return new_pte;
} }
/* Return HPTE cache control bits corresponding to Linux pte bits */ /* Return HPTE cache control bits corresponding to Linux pte bits */
static inline unsigned long hpte_cache_bits(unsigned long pte_val) static inline unsigned long hpte_cache_bits(unsigned long pte_val)
{ {
......
...@@ -675,6 +675,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, ...@@ -675,6 +675,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
} }
/* if the guest wants write access, see if that is OK */ /* if the guest wants write access, see if that is OK */
if (!writing && hpte_is_writable(r)) { if (!writing && hpte_is_writable(r)) {
unsigned int hugepage_shift;
pte_t *ptep, pte; pte_t *ptep, pte;
/* /*
...@@ -683,9 +684,10 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, ...@@ -683,9 +684,10 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
*/ */
rcu_read_lock_sched(); rcu_read_lock_sched();
ptep = find_linux_pte_or_hugepte(current->mm->pgd, ptep = find_linux_pte_or_hugepte(current->mm->pgd,
hva, NULL); hva, &hugepage_shift);
if (ptep && pte_present(*ptep)) { if (ptep) {
pte = kvmppc_read_update_linux_pte(ptep, 1); pte = kvmppc_read_update_linux_pte(ptep, 1,
hugepage_shift);
if (pte_write(pte)) if (pte_write(pte))
write_ok = 1; write_ok = 1;
} }
......
...@@ -139,20 +139,18 @@ static pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva, ...@@ -139,20 +139,18 @@ static pte_t lookup_linux_pte(pgd_t *pgdir, unsigned long hva,
{ {
pte_t *ptep; pte_t *ptep;
unsigned long ps = *pte_sizep; unsigned long ps = *pte_sizep;
unsigned int shift; unsigned int hugepage_shift;
ptep = find_linux_pte_or_hugepte(pgdir, hva, &shift); ptep = find_linux_pte_or_hugepte(pgdir, hva, &hugepage_shift);
if (!ptep) if (!ptep)
return __pte(0); return __pte(0);
if (shift) if (hugepage_shift)
*pte_sizep = 1ul << shift; *pte_sizep = 1ul << hugepage_shift;
else else
*pte_sizep = PAGE_SIZE; *pte_sizep = PAGE_SIZE;
if (ps > *pte_sizep) if (ps > *pte_sizep)
return __pte(0); return __pte(0);
if (!pte_present(*ptep)) return kvmppc_read_update_linux_pte(ptep, writing, hugepage_shift);
return __pte(0);
return kvmppc_read_update_linux_pte(ptep, writing);
} }
static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v) static inline void unlock_hpte(unsigned long *hpte, unsigned long hpte_v)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment