Commit 89212919 authored by Keqian Zhu's avatar Keqian Zhu Committed by Paolo Bonzini

KVM: x86: Do not write protect huge page in initially-all-set mode

Currently, when dirty logging is started in initially-all-set mode,
we write protect huge pages to prepare for splitting them into
4K pages, and leave normal pages untouched as the logging will
be enabled lazily as dirty bits are cleared.

However, enabling dirty logging lazily is also feasible for huge pages.
This not only reduces the time of start dirty logging, but it also
greatly reduces side-effect on guest when there is high dirty rate.
Signed-off-by: default avatarKeqian Zhu <zhukeqian1@huawei.com>
Message-Id: <20210429034115.35560-3-zhukeqian1@huawei.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 3ad93562
...@@ -1172,8 +1172,7 @@ static bool __rmap_clear_dirty(struct kvm *kvm, struct kvm_rmap_head *rmap_head, ...@@ -1172,8 +1172,7 @@ static bool __rmap_clear_dirty(struct kvm *kvm, struct kvm_rmap_head *rmap_head,
* @gfn_offset: start of the BITS_PER_LONG pages we care about * @gfn_offset: start of the BITS_PER_LONG pages we care about
* @mask: indicates which pages we should protect * @mask: indicates which pages we should protect
* *
* Used when we do not need to care about huge page mappings: e.g. during dirty * Used when we do not need to care about huge page mappings.
* logging we do not have any such mappings.
*/ */
static void kvm_mmu_write_protect_pt_masked(struct kvm *kvm, static void kvm_mmu_write_protect_pt_masked(struct kvm *kvm,
struct kvm_memory_slot *slot, struct kvm_memory_slot *slot,
...@@ -1230,13 +1229,36 @@ static void kvm_mmu_clear_dirty_pt_masked(struct kvm *kvm, ...@@ -1230,13 +1229,36 @@ static void kvm_mmu_clear_dirty_pt_masked(struct kvm *kvm,
* It calls kvm_mmu_write_protect_pt_masked to write protect selected pages to * It calls kvm_mmu_write_protect_pt_masked to write protect selected pages to
* enable dirty logging for them. * enable dirty logging for them.
* *
* Used when we do not need to care about huge page mappings: e.g. during dirty * We need to care about huge page mappings: e.g. during dirty logging we may
* logging we do not have any such mappings. * have such mappings.
*/ */
void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
struct kvm_memory_slot *slot, struct kvm_memory_slot *slot,
gfn_t gfn_offset, unsigned long mask) gfn_t gfn_offset, unsigned long mask)
{ {
/*
* Huge pages are NOT write protected when we start dirty logging in
* initially-all-set mode; must write protect them here so that they
* are split to 4K on the first write.
*
* The gfn_offset is guaranteed to be aligned to 64, but the base_gfn
* of memslot has no such restriction, so the range can cross two large
* pages.
*/
if (kvm_dirty_log_manual_protect_and_init_set(kvm)) {
gfn_t start = slot->base_gfn + gfn_offset + __ffs(mask);
gfn_t end = slot->base_gfn + gfn_offset + __fls(mask);
kvm_mmu_slot_gfn_write_protect(kvm, slot, start, PG_LEVEL_2M);
/* Cross two large pages? */
if (ALIGN(start << PAGE_SHIFT, PMD_SIZE) !=
ALIGN(end << PAGE_SHIFT, PMD_SIZE))
kvm_mmu_slot_gfn_write_protect(kvm, slot, end,
PG_LEVEL_2M);
}
/* Now handle 4K PTEs. */
if (kvm_x86_ops.cpu_dirty_log_size) if (kvm_x86_ops.cpu_dirty_log_size)
kvm_mmu_clear_dirty_pt_masked(kvm, slot, gfn_offset, mask); kvm_mmu_clear_dirty_pt_masked(kvm, slot, gfn_offset, mask);
else else
......
...@@ -11103,36 +11103,19 @@ static void kvm_mmu_slot_apply_flags(struct kvm *kvm, ...@@ -11103,36 +11103,19 @@ static void kvm_mmu_slot_apply_flags(struct kvm *kvm,
*/ */
kvm_mmu_zap_collapsible_sptes(kvm, new); kvm_mmu_zap_collapsible_sptes(kvm, new);
} else { } else {
/* By default, write-protect everything to log writes. */
int level = PG_LEVEL_4K;
if (kvm_x86_ops.cpu_dirty_log_size) {
/* /*
* Clear all dirty bits, unless pages are treated as * Initially-all-set does not require write protecting any page,
* dirty from the get-go. * because they're all assumed to be dirty.
*/ */
if (!kvm_dirty_log_manual_protect_and_init_set(kvm)) if (kvm_dirty_log_manual_protect_and_init_set(kvm))
kvm_mmu_slot_leaf_clear_dirty(kvm, new); return;
/* if (kvm_x86_ops.cpu_dirty_log_size) {
* Write-protect large pages on write so that dirty kvm_mmu_slot_leaf_clear_dirty(kvm, new);
* logging happens at 4k granularity. No need to kvm_mmu_slot_remove_write_access(kvm, new, PG_LEVEL_2M);
* write-protect small SPTEs since write accesses are } else {
* logged by the CPU via dirty bits. kvm_mmu_slot_remove_write_access(kvm, new, PG_LEVEL_4K);
*/
level = PG_LEVEL_2M;
} else if (kvm_dirty_log_manual_protect_and_init_set(kvm)) {
/*
* If we're with initial-all-set, we don't need
* to write protect any small page because
* they're reported as dirty already. However
* we still need to write-protect huge pages
* so that the page split can happen lazily on
* the first write to the huge page.
*/
level = PG_LEVEL_2M;
} }
kvm_mmu_slot_remove_write_access(kvm, new, level);
} }
} }
......
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