diff options
| -rw-r--r-- | arch/powerpc/kvm/book3s_64_mmu_radix.c | 69 |
1 files changed, 43 insertions, 26 deletions
diff --git a/arch/powerpc/kvm/book3s_64_mmu_radix.c b/arch/powerpc/kvm/book3s_64_mmu_radix.c index 0c854816e653..5cb4e4687107 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_radix.c +++ b/arch/powerpc/kvm/book3s_64_mmu_radix.c | |||
| @@ -195,6 +195,12 @@ static void kvmppc_pte_free(pte_t *ptep) | |||
| 195 | kmem_cache_free(kvm_pte_cache, ptep); | 195 | kmem_cache_free(kvm_pte_cache, ptep); |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | /* Like pmd_huge() and pmd_large(), but works regardless of config options */ | ||
| 199 | static inline int pmd_is_leaf(pmd_t pmd) | ||
| 200 | { | ||
| 201 | return !!(pmd_val(pmd) & _PAGE_PTE); | ||
| 202 | } | ||
| 203 | |||
| 198 | static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa, | 204 | static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa, |
| 199 | unsigned int level, unsigned long mmu_seq) | 205 | unsigned int level, unsigned long mmu_seq) |
| 200 | { | 206 | { |
| @@ -219,7 +225,7 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa, | |||
| 219 | else | 225 | else |
| 220 | new_pmd = pmd_alloc_one(kvm->mm, gpa); | 226 | new_pmd = pmd_alloc_one(kvm->mm, gpa); |
| 221 | 227 | ||
| 222 | if (level == 0 && !(pmd && pmd_present(*pmd))) | 228 | if (level == 0 && !(pmd && pmd_present(*pmd) && !pmd_is_leaf(*pmd))) |
| 223 | new_ptep = kvmppc_pte_alloc(); | 229 | new_ptep = kvmppc_pte_alloc(); |
| 224 | 230 | ||
| 225 | /* Check if we might have been invalidated; let the guest retry if so */ | 231 | /* Check if we might have been invalidated; let the guest retry if so */ |
| @@ -244,12 +250,30 @@ static int kvmppc_create_pte(struct kvm *kvm, pte_t pte, unsigned long gpa, | |||
| 244 | new_pmd = NULL; | 250 | new_pmd = NULL; |
| 245 | } | 251 | } |
| 246 | pmd = pmd_offset(pud, gpa); | 252 | pmd = pmd_offset(pud, gpa); |
| 247 | if (pmd_large(*pmd)) { | 253 | if (pmd_is_leaf(*pmd)) { |
| 248 | /* Someone else has instantiated a large page here; retry */ | 254 | unsigned long lgpa = gpa & PMD_MASK; |
| 249 | ret = -EAGAIN; | 255 | |
| 250 | goto out_unlock; | 256 | /* |
| 251 | } | 257 | * If we raced with another CPU which has just put |
| 252 | if (level == 1 && !pmd_none(*pmd)) { | 258 | * a 2MB pte in after we saw a pte page, try again. |
| 259 | */ | ||
| 260 | if (level == 0 && !new_ptep) { | ||
| 261 | ret = -EAGAIN; | ||
| 262 | goto out_unlock; | ||
| 263 | } | ||
| 264 | /* Valid 2MB page here already, remove it */ | ||
| 265 | old = kvmppc_radix_update_pte(kvm, pmdp_ptep(pmd), | ||
| 266 | ~0UL, 0, lgpa, PMD_SHIFT); | ||
| 267 | kvmppc_radix_tlbie_page(kvm, lgpa, PMD_SHIFT); | ||
| 268 | if (old & _PAGE_DIRTY) { | ||
| 269 | unsigned long gfn = lgpa >> PAGE_SHIFT; | ||
| 270 | struct kvm_memory_slot *memslot; | ||
| 271 | memslot = gfn_to_memslot(kvm, gfn); | ||
| 272 | if (memslot && memslot->dirty_bitmap) | ||
| 273 | kvmppc_update_dirty_map(memslot, | ||
| 274 | gfn, PMD_SIZE); | ||
| 275 | } | ||
| 276 | } else if (level == 1 && !pmd_none(*pmd)) { | ||
| 253 | /* | 277 | /* |
| 254 | * There's a page table page here, but we wanted | 278 | * There's a page table page here, but we wanted |
| 255 | * to install a large page. Tell the caller and let | 279 | * to install a large page. Tell the caller and let |
| @@ -412,28 +436,24 @@ int kvmppc_book3s_radix_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
| 412 | } else { | 436 | } else { |
| 413 | page = pages[0]; | 437 | page = pages[0]; |
| 414 | pfn = page_to_pfn(page); | 438 | pfn = page_to_pfn(page); |
| 415 | if (PageHuge(page)) { | 439 | if (PageCompound(page)) { |
| 416 | page = compound_head(page); | 440 | pte_size <<= compound_order(compound_head(page)); |
| 417 | pte_size <<= compound_order(page); | ||
| 418 | /* See if we can insert a 2MB large-page PTE here */ | 441 | /* See if we can insert a 2MB large-page PTE here */ |
| 419 | if (pte_size >= PMD_SIZE && | 442 | if (pte_size >= PMD_SIZE && |
| 420 | (gpa & PMD_MASK & PAGE_MASK) == | 443 | (gpa & (PMD_SIZE - PAGE_SIZE)) == |
| 421 | (hva & PMD_MASK & PAGE_MASK)) { | 444 | (hva & (PMD_SIZE - PAGE_SIZE))) { |
| 422 | level = 1; | 445 | level = 1; |
| 423 | pfn &= ~((PMD_SIZE >> PAGE_SHIFT) - 1); | 446 | pfn &= ~((PMD_SIZE >> PAGE_SHIFT) - 1); |
| 424 | } | 447 | } |
| 425 | } | 448 | } |
| 426 | /* See if we can provide write access */ | 449 | /* See if we can provide write access */ |
| 427 | if (writing) { | 450 | if (writing) { |
| 428 | /* | ||
| 429 | * We assume gup_fast has set dirty on the host PTE. | ||
| 430 | */ | ||
| 431 | pgflags |= _PAGE_WRITE; | 451 | pgflags |= _PAGE_WRITE; |
| 432 | } else { | 452 | } else { |
| 433 | local_irq_save(flags); | 453 | local_irq_save(flags); |
| 434 | ptep = find_current_mm_pte(current->mm->pgd, | 454 | ptep = find_current_mm_pte(current->mm->pgd, |
| 435 | hva, NULL, NULL); | 455 | hva, NULL, NULL); |
| 436 | if (ptep && pte_write(*ptep) && pte_dirty(*ptep)) | 456 | if (ptep && pte_write(*ptep)) |
| 437 | pgflags |= _PAGE_WRITE; | 457 | pgflags |= _PAGE_WRITE; |
| 438 | local_irq_restore(flags); | 458 | local_irq_restore(flags); |
| 439 | } | 459 | } |
| @@ -459,18 +479,15 @@ int kvmppc_book3s_radix_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
| 459 | pte = pfn_pte(pfn, __pgprot(pgflags)); | 479 | pte = pfn_pte(pfn, __pgprot(pgflags)); |
| 460 | ret = kvmppc_create_pte(kvm, pte, gpa, level, mmu_seq); | 480 | ret = kvmppc_create_pte(kvm, pte, gpa, level, mmu_seq); |
| 461 | } | 481 | } |
| 462 | if (ret == 0 || ret == -EAGAIN) | ||
| 463 | ret = RESUME_GUEST; | ||
| 464 | 482 | ||
| 465 | if (page) { | 483 | if (page) { |
| 466 | /* | 484 | if (!ret && (pgflags & _PAGE_WRITE)) |
| 467 | * We drop pages[0] here, not page because page might | 485 | set_page_dirty_lock(page); |
| 468 | * have been set to the head page of a compound, but | 486 | put_page(page); |
| 469 | * we have to drop the reference on the correct tail | ||
| 470 | * page to match the get inside gup() | ||
| 471 | */ | ||
| 472 | put_page(pages[0]); | ||
| 473 | } | 487 | } |
| 488 | |||
| 489 | if (ret == 0 || ret == -EAGAIN) | ||
| 490 | ret = RESUME_GUEST; | ||
| 474 | return ret; | 491 | return ret; |
| 475 | } | 492 | } |
| 476 | 493 | ||
| @@ -644,7 +661,7 @@ void kvmppc_free_radix(struct kvm *kvm) | |||
| 644 | continue; | 661 | continue; |
| 645 | pmd = pmd_offset(pud, 0); | 662 | pmd = pmd_offset(pud, 0); |
| 646 | for (im = 0; im < PTRS_PER_PMD; ++im, ++pmd) { | 663 | for (im = 0; im < PTRS_PER_PMD; ++im, ++pmd) { |
| 647 | if (pmd_huge(*pmd)) { | 664 | if (pmd_is_leaf(*pmd)) { |
| 648 | pmd_clear(pmd); | 665 | pmd_clear(pmd); |
| 649 | continue; | 666 | continue; |
| 650 | } | 667 | } |
