diff options
author | Xiao Guangrong <xiaoguangrong@cn.fujitsu.com> | 2011-07-11 15:24:39 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2011-07-24 04:50:27 -0400 |
commit | 640d9b0dbe9f744ac8fd517a8f6afe238f8f525b (patch) | |
tree | ff4543527ee35018f26d686e5c46a4c100dda0ef /arch/x86/kvm/paging_tmpl.h | |
parent | bebb106a5afa32efdf5332ed4a40bf4d6d06b56e (diff) |
KVM: MMU: optimize to handle dirty bit
If dirty bit is not set, we can make the pte access read-only to avoid handing
dirty bit everywhere
Signed-off-by: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86/kvm/paging_tmpl.h')
-rw-r--r-- | arch/x86/kvm/paging_tmpl.h | 46 |
1 files changed, 19 insertions, 27 deletions
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index f0fb1a4c522d..c9fe97bd8c82 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h | |||
@@ -101,11 +101,15 @@ static int FNAME(cmpxchg_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, | |||
101 | return (ret != orig_pte); | 101 | return (ret != orig_pte); |
102 | } | 102 | } |
103 | 103 | ||
104 | static unsigned FNAME(gpte_access)(struct kvm_vcpu *vcpu, pt_element_t gpte) | 104 | static unsigned FNAME(gpte_access)(struct kvm_vcpu *vcpu, pt_element_t gpte, |
105 | bool last) | ||
105 | { | 106 | { |
106 | unsigned access; | 107 | unsigned access; |
107 | 108 | ||
108 | access = (gpte & (PT_WRITABLE_MASK | PT_USER_MASK)) | ACC_EXEC_MASK; | 109 | access = (gpte & (PT_WRITABLE_MASK | PT_USER_MASK)) | ACC_EXEC_MASK; |
110 | if (last && !is_dirty_gpte(gpte)) | ||
111 | access &= ~ACC_WRITE_MASK; | ||
112 | |||
109 | #if PTTYPE == 64 | 113 | #if PTTYPE == 64 |
110 | if (vcpu->arch.mmu.nx) | 114 | if (vcpu->arch.mmu.nx) |
111 | access &= ~(gpte >> PT64_NX_SHIFT); | 115 | access &= ~(gpte >> PT64_NX_SHIFT); |
@@ -232,8 +236,6 @@ retry_walk: | |||
232 | pte |= PT_ACCESSED_MASK; | 236 | pte |= PT_ACCESSED_MASK; |
233 | } | 237 | } |
234 | 238 | ||
235 | pte_access = pt_access & FNAME(gpte_access)(vcpu, pte); | ||
236 | |||
237 | walker->ptes[walker->level - 1] = pte; | 239 | walker->ptes[walker->level - 1] = pte; |
238 | 240 | ||
239 | if (FNAME(is_last_gpte)(walker, vcpu, mmu, pte)) { | 241 | if (FNAME(is_last_gpte)(walker, vcpu, mmu, pte)) { |
@@ -268,7 +270,7 @@ retry_walk: | |||
268 | break; | 270 | break; |
269 | } | 271 | } |
270 | 272 | ||
271 | pt_access = pte_access; | 273 | pt_access &= FNAME(gpte_access)(vcpu, pte, false); |
272 | --walker->level; | 274 | --walker->level; |
273 | } | 275 | } |
274 | 276 | ||
@@ -293,6 +295,7 @@ retry_walk: | |||
293 | walker->ptes[walker->level - 1] = pte; | 295 | walker->ptes[walker->level - 1] = pte; |
294 | } | 296 | } |
295 | 297 | ||
298 | pte_access = pt_access & FNAME(gpte_access)(vcpu, pte, true); | ||
296 | walker->pt_access = pt_access; | 299 | walker->pt_access = pt_access; |
297 | walker->pte_access = pte_access; | 300 | walker->pte_access = pte_access; |
298 | pgprintk("%s: pte %llx pte_access %x pt_access %x\n", | 301 | pgprintk("%s: pte %llx pte_access %x pt_access %x\n", |
@@ -367,7 +370,7 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, | |||
367 | return; | 370 | return; |
368 | 371 | ||
369 | pgprintk("%s: gpte %llx spte %p\n", __func__, (u64)gpte, spte); | 372 | pgprintk("%s: gpte %llx spte %p\n", __func__, (u64)gpte, spte); |
370 | pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte); | 373 | pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte, true); |
371 | pfn = gfn_to_pfn_atomic(vcpu->kvm, gpte_to_gfn(gpte)); | 374 | pfn = gfn_to_pfn_atomic(vcpu->kvm, gpte_to_gfn(gpte)); |
372 | if (is_error_pfn(pfn)) { | 375 | if (is_error_pfn(pfn)) { |
373 | kvm_release_pfn_clean(pfn); | 376 | kvm_release_pfn_clean(pfn); |
@@ -379,7 +382,7 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, | |||
379 | * vcpu->arch.update_pte.pfn was fetched from get_user_pages(write = 1). | 382 | * vcpu->arch.update_pte.pfn was fetched from get_user_pages(write = 1). |
380 | */ | 383 | */ |
381 | mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0, | 384 | mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0, |
382 | is_dirty_gpte(gpte), NULL, PT_PAGE_TABLE_LEVEL, | 385 | NULL, PT_PAGE_TABLE_LEVEL, |
383 | gpte_to_gfn(gpte), pfn, true, true); | 386 | gpte_to_gfn(gpte), pfn, true, true); |
384 | } | 387 | } |
385 | 388 | ||
@@ -430,7 +433,6 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw, | |||
430 | unsigned pte_access; | 433 | unsigned pte_access; |
431 | gfn_t gfn; | 434 | gfn_t gfn; |
432 | pfn_t pfn; | 435 | pfn_t pfn; |
433 | bool dirty; | ||
434 | 436 | ||
435 | if (spte == sptep) | 437 | if (spte == sptep) |
436 | continue; | 438 | continue; |
@@ -443,18 +445,18 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw, | |||
443 | if (FNAME(prefetch_invalid_gpte)(vcpu, sp, spte, gpte)) | 445 | if (FNAME(prefetch_invalid_gpte)(vcpu, sp, spte, gpte)) |
444 | continue; | 446 | continue; |
445 | 447 | ||
446 | pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte); | 448 | pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte, |
449 | true); | ||
447 | gfn = gpte_to_gfn(gpte); | 450 | gfn = gpte_to_gfn(gpte); |
448 | dirty = is_dirty_gpte(gpte); | ||
449 | pfn = pte_prefetch_gfn_to_pfn(vcpu, gfn, | 451 | pfn = pte_prefetch_gfn_to_pfn(vcpu, gfn, |
450 | (pte_access & ACC_WRITE_MASK) && dirty); | 452 | pte_access & ACC_WRITE_MASK); |
451 | if (is_error_pfn(pfn)) { | 453 | if (is_error_pfn(pfn)) { |
452 | kvm_release_pfn_clean(pfn); | 454 | kvm_release_pfn_clean(pfn); |
453 | break; | 455 | break; |
454 | } | 456 | } |
455 | 457 | ||
456 | mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0, | 458 | mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0, |
457 | dirty, NULL, PT_PAGE_TABLE_LEVEL, gfn, | 459 | NULL, PT_PAGE_TABLE_LEVEL, gfn, |
458 | pfn, true, true); | 460 | pfn, true, true); |
459 | } | 461 | } |
460 | } | 462 | } |
@@ -470,7 +472,6 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, | |||
470 | { | 472 | { |
471 | unsigned access = gw->pt_access; | 473 | unsigned access = gw->pt_access; |
472 | struct kvm_mmu_page *sp = NULL; | 474 | struct kvm_mmu_page *sp = NULL; |
473 | bool dirty = is_dirty_gpte(gw->ptes[gw->level - 1]); | ||
474 | int top_level; | 475 | int top_level; |
475 | unsigned direct_access; | 476 | unsigned direct_access; |
476 | struct kvm_shadow_walk_iterator it; | 477 | struct kvm_shadow_walk_iterator it; |
@@ -479,8 +480,6 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, | |||
479 | return NULL; | 480 | return NULL; |
480 | 481 | ||
481 | direct_access = gw->pt_access & gw->pte_access; | 482 | direct_access = gw->pt_access & gw->pte_access; |
482 | if (!dirty) | ||
483 | direct_access &= ~ACC_WRITE_MASK; | ||
484 | 483 | ||
485 | top_level = vcpu->arch.mmu.root_level; | 484 | top_level = vcpu->arch.mmu.root_level; |
486 | if (top_level == PT32E_ROOT_LEVEL) | 485 | if (top_level == PT32E_ROOT_LEVEL) |
@@ -539,7 +538,7 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, | |||
539 | } | 538 | } |
540 | 539 | ||
541 | mmu_set_spte(vcpu, it.sptep, access, gw->pte_access & access, | 540 | mmu_set_spte(vcpu, it.sptep, access, gw->pte_access & access, |
542 | user_fault, write_fault, dirty, ptwrite, it.level, | 541 | user_fault, write_fault, ptwrite, it.level, |
543 | gw->gfn, pfn, prefault, map_writable); | 542 | gw->gfn, pfn, prefault, map_writable); |
544 | FNAME(pte_prefetch)(vcpu, gw, it.sptep); | 543 | FNAME(pte_prefetch)(vcpu, gw, it.sptep); |
545 | 544 | ||
@@ -622,17 +621,9 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, | |||
622 | return 0; | 621 | return 0; |
623 | 622 | ||
624 | /* mmio */ | 623 | /* mmio */ |
625 | if (is_error_pfn(pfn)) { | 624 | if (is_error_pfn(pfn)) |
626 | unsigned access = walker.pte_access; | ||
627 | bool dirty = is_dirty_gpte(walker.ptes[walker.level - 1]); | ||
628 | |||
629 | if (!dirty) | ||
630 | access &= ~ACC_WRITE_MASK; | ||
631 | |||
632 | return kvm_handle_bad_page(vcpu, mmu_is_nested(vcpu) ? 0 : | 625 | return kvm_handle_bad_page(vcpu, mmu_is_nested(vcpu) ? 0 : |
633 | addr, access, walker.gfn, pfn); | 626 | addr, walker.pte_access, walker.gfn, pfn); |
634 | } | ||
635 | |||
636 | spin_lock(&vcpu->kvm->mmu_lock); | 627 | spin_lock(&vcpu->kvm->mmu_lock); |
637 | if (mmu_notifier_retry(vcpu, mmu_seq)) | 628 | if (mmu_notifier_retry(vcpu, mmu_seq)) |
638 | goto out_unlock; | 629 | goto out_unlock; |
@@ -849,11 +840,12 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) | |||
849 | } | 840 | } |
850 | 841 | ||
851 | nr_present++; | 842 | nr_present++; |
852 | pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte); | 843 | pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte, |
844 | true); | ||
853 | host_writable = sp->spt[i] & SPTE_HOST_WRITEABLE; | 845 | host_writable = sp->spt[i] & SPTE_HOST_WRITEABLE; |
854 | 846 | ||
855 | set_spte(vcpu, &sp->spt[i], pte_access, 0, 0, | 847 | set_spte(vcpu, &sp->spt[i], pte_access, 0, 0, |
856 | is_dirty_gpte(gpte), PT_PAGE_TABLE_LEVEL, gfn, | 848 | PT_PAGE_TABLE_LEVEL, gfn, |
857 | spte_to_pfn(sp->spt[i]), true, false, | 849 | spte_to_pfn(sp->spt[i]), true, false, |
858 | host_writable); | 850 | host_writable); |
859 | } | 851 | } |