aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/kvm/book3s_64_mmu_radix.c42
1 files changed, 27 insertions, 15 deletions
diff --git a/arch/powerpc/kvm/book3s_64_mmu_radix.c b/arch/powerpc/kvm/book3s_64_mmu_radix.c
index 5cb4e4687107..ed62164f8474 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_radix.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_radix.c
@@ -160,6 +160,17 @@ static void kvmppc_radix_tlbie_page(struct kvm *kvm, unsigned long addr,
160 asm volatile("ptesync": : :"memory"); 160 asm volatile("ptesync": : :"memory");
161} 161}
162 162
163static void kvmppc_radix_flush_pwc(struct kvm *kvm, unsigned long addr)
164{
165 unsigned long rb = 0x2 << PPC_BITLSHIFT(53); /* IS = 2 */
166
167 asm volatile("ptesync": : :"memory");
168 /* RIC=1 PRS=0 R=1 IS=2 */
169 asm volatile(PPC_TLBIE_5(%0, %1, 1, 0, 1)
170 : : "r" (rb), "r" (kvm->arch.lpid) : "memory");
171 asm volatile("ptesync": : :"memory");
172}
173
163unsigned long kvmppc_radix_update_pte(struct kvm *kvm, pte_t *ptep, 174unsigned long kvmppc_radix_update_pte(struct kvm *kvm, pte_t *ptep,
164 unsigned long clr, unsigned long set, 175 unsigned long clr, unsigned long set,
165 unsigned long addr, unsigned int shift) 176 unsigned long addr, unsigned int shift)
@@ -261,6 +272,11 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa,
261 ret = -EAGAIN; 272 ret = -EAGAIN;
262 goto out_unlock; 273 goto out_unlock;
263 } 274 }
275 /* Check if we raced and someone else has set the same thing */
276 if (level == 1 && pmd_raw(*pmd) == pte_raw(pte)) {
277 ret = 0;
278 goto out_unlock;
279 }
264 /* Valid 2MB page here already, remove it */ 280 /* Valid 2MB page here already, remove it */
265 old = kvmppc_radix_update_pte(kvm, pmdp_ptep(pmd), 281 old = kvmppc_radix_update_pte(kvm, pmdp_ptep(pmd),
266 ~0UL, 0, lgpa, PMD_SHIFT); 282 ~0UL, 0, lgpa, PMD_SHIFT);
@@ -275,12 +291,13 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa,
275 } 291 }
276 } else if (level == 1 && !pmd_none(*pmd)) { 292 } else if (level == 1 && !pmd_none(*pmd)) {
277 /* 293 /*
278 * There's a page table page here, but we wanted 294 * There's a page table page here, but we wanted to
279 * to install a large page. Tell the caller and let 295 * install a large page, so remove and free the page
280 * it try installing a normal page if it wants. 296 * table page. new_ptep will be NULL since level == 1.
281 */ 297 */
282 ret = -EBUSY; 298 new_ptep = pte_offset_kernel(pmd, 0);
283 goto out_unlock; 299 pmd_clear(pmd);
300 kvmppc_radix_flush_pwc(kvm, gpa);
284 } 301 }
285 if (level == 0) { 302 if (level == 0) {
286 if (pmd_none(*pmd)) { 303 if (pmd_none(*pmd)) {
@@ -291,6 +308,11 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa,
291 } 308 }
292 ptep = pte_offset_kernel(pmd, gpa); 309 ptep = pte_offset_kernel(pmd, gpa);
293 if (pte_present(*ptep)) { 310 if (pte_present(*ptep)) {
311 /* Check if someone else set the same thing */
312 if (pte_raw(*ptep) == pte_raw(pte)) {
313 ret = 0;
314 goto out_unlock;
315 }
294 /* PTE was previously valid, so invalidate it */ 316 /* PTE was previously valid, so invalidate it */
295 old = kvmppc_radix_update_pte(kvm, ptep, _PAGE_PRESENT, 317 old = kvmppc_radix_update_pte(kvm, ptep, _PAGE_PRESENT,
296 0, gpa, 0); 318 0, gpa, 0);
@@ -469,16 +491,6 @@ int kvmppc_book3s_radix_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,
469 491
470 /* Allocate space in the tree and write the PTE */ 492 /* Allocate space in the tree and write the PTE */
471 ret = kvmppc_create_pte(kvm, pte, gpa, level, mmu_seq); 493 ret = kvmppc_create_pte(kvm, pte, gpa, level, mmu_seq);
472 if (ret == -EBUSY) {
473 /*
474 * There's already a PMD where wanted to install a large page;
475 * for now, fall back to installing a small page.
476 */
477 level = 0;
478 pfn |= gfn & ((PMD_SIZE >> PAGE_SHIFT) - 1);
479 pte = pfn_pte(pfn, __pgprot(pgflags));
480 ret = kvmppc_create_pte(kvm, pte, gpa, level, mmu_seq);
481 }
482 494
483 if (page) { 495 if (page) {
484 if (!ret && (pgflags & _PAGE_WRITE)) 496 if (!ret && (pgflags & _PAGE_WRITE))