KVM: arm64: Support enabling dirty log gradually in small chunks
authorKeqian Zhu <zhukeqian1@huawei.com>
Mon, 13 Apr 2020 12:20:23 +0000 (20:20 +0800)
committerMarc Zyngier <maz@kernel.org>
Sat, 16 May 2020 14:05:02 +0000 (15:05 +0100)
There is already support of enabling dirty log gradually in small chunks
for x86 in commit 3c9bd4006bfc ("KVM: x86: enable dirty log gradually in
small chunks"). This adds support for arm64.

x86 still writes protect all huge pages when DIRTY_LOG_INITIALLY_ALL_SET
is enabled. However, for arm64, both huge pages and normal pages can be
write protected gradually by userspace.

Under the Huawei Kunpeng 920 2.6GHz platform, I did some tests on 128G
Linux VMs with different page size. The memory pressure is 127G in each
case. The time taken of memory_global_dirty_log_start in QEMU is listed
below:

Page Size      Before    After Optimization
  4K            650ms         1.8ms
  2M             4ms          1.8ms
  1G             2ms          1.8ms

Besides the time reduction, the biggest improvement is that we will minimize
the performance side effect (because of dissolving huge pages and marking
memslots dirty) on guest after enabling dirty log.

Signed-off-by: Keqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20200413122023.52583-1-zhukeqian1@huawei.com
Documentation/virt/kvm/api.rst
arch/arm64/include/asm/kvm_host.h
arch/arm64/kvm/mmu.c

index efbbe570aa9b7b3bf581e89e1508fb611294bf81..0017f63fa44f85cc5a4ef48724eb184d1a1c2604 100644 (file)
@@ -5777,7 +5777,7 @@ will be initialized to 1 when created.  This also improves performance because
 dirty logging can be enabled gradually in small chunks on the first call
 to KVM_CLEAR_DIRTY_LOG.  KVM_DIRTY_LOG_INITIALLY_SET depends on
 KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE (it is also only available on
-x86 for now).
+x86 and arm64 for now).
 
 KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 was previously available under the name
 KVM_CAP_MANUAL_DIRTY_LOG_PROTECT, but the implementation had bugs that make
index 32c8a675e5a4a33c89a12cdfd69d3d558b92f95f..a723f84fab83514961c839a2edf9f75124763bed 100644 (file)
@@ -46,6 +46,9 @@
 #define KVM_REQ_RECORD_STEAL   KVM_ARCH_REQ(3)
 #define KVM_REQ_RELOAD_GICv4   KVM_ARCH_REQ(4)
 
+#define KVM_DIRTY_LOG_MANUAL_CAPS   (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \
+                                    KVM_DIRTY_LOG_INITIALLY_SET)
+
 DECLARE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
 
 extern unsigned int kvm_sve_max_vl;
index 66eb8e3f6e8c67de4fc080127078c459806cbf8d..ddf85bf218973f60f5f1d0edec02d6a894782261 100644 (file)
@@ -2277,8 +2277,16 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
         * allocated dirty_bitmap[], dirty pages will be tracked while the
         * memory slot is write protected.
         */
-       if (change != KVM_MR_DELETE && mem->flags & KVM_MEM_LOG_DIRTY_PAGES)
-               kvm_mmu_wp_memory_region(kvm, mem->slot);
+       if (change != KVM_MR_DELETE && mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
+               /*
+                * If we're with initial-all-set, we don't need to write
+                * protect any pages because they're all reported as dirty.
+                * Huge pages and normal pages will be write protect gradually.
+                */
+               if (!kvm_dirty_log_manual_protect_and_init_set(kvm)) {
+                       kvm_mmu_wp_memory_region(kvm, mem->slot);
+               }
+       }
 }
 
 int kvm_arch_prepare_memory_region(struct kvm *kvm,