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 | |
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')
-rw-r--r-- | arch/x86/kvm/mmu.c | 13 | ||||
-rw-r--r-- | arch/x86/kvm/paging_tmpl.h | 46 |
2 files changed, 25 insertions, 34 deletions
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index d1986b7dcec7..98812c25727b 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c | |||
@@ -1923,7 +1923,7 @@ static int mmu_need_write_protect(struct kvm_vcpu *vcpu, gfn_t gfn, | |||
1923 | 1923 | ||
1924 | static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, | 1924 | static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, |
1925 | unsigned pte_access, int user_fault, | 1925 | unsigned pte_access, int user_fault, |
1926 | int write_fault, int dirty, int level, | 1926 | int write_fault, int level, |
1927 | gfn_t gfn, pfn_t pfn, bool speculative, | 1927 | gfn_t gfn, pfn_t pfn, bool speculative, |
1928 | bool can_unsync, bool host_writable) | 1928 | bool can_unsync, bool host_writable) |
1929 | { | 1929 | { |
@@ -1938,8 +1938,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, | |||
1938 | spte = PT_PRESENT_MASK; | 1938 | spte = PT_PRESENT_MASK; |
1939 | if (!speculative) | 1939 | if (!speculative) |
1940 | spte |= shadow_accessed_mask; | 1940 | spte |= shadow_accessed_mask; |
1941 | if (!dirty) | 1941 | |
1942 | pte_access &= ~ACC_WRITE_MASK; | ||
1943 | if (pte_access & ACC_EXEC_MASK) | 1942 | if (pte_access & ACC_EXEC_MASK) |
1944 | spte |= shadow_x_mask; | 1943 | spte |= shadow_x_mask; |
1945 | else | 1944 | else |
@@ -2023,7 +2022,7 @@ done: | |||
2023 | 2022 | ||
2024 | static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, | 2023 | static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, |
2025 | unsigned pt_access, unsigned pte_access, | 2024 | unsigned pt_access, unsigned pte_access, |
2026 | int user_fault, int write_fault, int dirty, | 2025 | int user_fault, int write_fault, |
2027 | int *ptwrite, int level, gfn_t gfn, | 2026 | int *ptwrite, int level, gfn_t gfn, |
2028 | pfn_t pfn, bool speculative, | 2027 | pfn_t pfn, bool speculative, |
2029 | bool host_writable) | 2028 | bool host_writable) |
@@ -2059,7 +2058,7 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, | |||
2059 | } | 2058 | } |
2060 | 2059 | ||
2061 | if (set_spte(vcpu, sptep, pte_access, user_fault, write_fault, | 2060 | if (set_spte(vcpu, sptep, pte_access, user_fault, write_fault, |
2062 | dirty, level, gfn, pfn, speculative, true, | 2061 | level, gfn, pfn, speculative, true, |
2063 | host_writable)) { | 2062 | host_writable)) { |
2064 | if (write_fault) | 2063 | if (write_fault) |
2065 | *ptwrite = 1; | 2064 | *ptwrite = 1; |
@@ -2129,7 +2128,7 @@ static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu, | |||
2129 | 2128 | ||
2130 | for (i = 0; i < ret; i++, gfn++, start++) | 2129 | for (i = 0; i < ret; i++, gfn++, start++) |
2131 | mmu_set_spte(vcpu, start, ACC_ALL, | 2130 | mmu_set_spte(vcpu, start, ACC_ALL, |
2132 | access, 0, 0, 1, NULL, | 2131 | access, 0, 0, NULL, |
2133 | sp->role.level, gfn, | 2132 | sp->role.level, gfn, |
2134 | page_to_pfn(pages[i]), true, true); | 2133 | page_to_pfn(pages[i]), true, true); |
2135 | 2134 | ||
@@ -2193,7 +2192,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write, | |||
2193 | unsigned pte_access = ACC_ALL; | 2192 | unsigned pte_access = ACC_ALL; |
2194 | 2193 | ||
2195 | mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, pte_access, | 2194 | mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, pte_access, |
2196 | 0, write, 1, &pt_write, | 2195 | 0, write, &pt_write, |
2197 | level, gfn, pfn, prefault, map_writable); | 2196 | level, gfn, pfn, prefault, map_writable); |
2198 | direct_pte_prefetch(vcpu, iterator.sptep); | 2197 | direct_pte_prefetch(vcpu, iterator.sptep); |
2199 | ++vcpu->stat.pf_fixed; | 2198 | ++vcpu->stat.pf_fixed; |
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 | } |