diff mbox series

[07/10] kvm: x86: mmu: Per-VM dirty logging mode

Message ID 20181020031543.124399-8-junaids@google.com (mailing list archive)
State New, archived
Headers show
Series [01/10] kvm: mmu: spte_write_protect optimization | expand

Commit Message

Junaid Shahid Oct. 20, 2018, 3:15 a.m. UTC
Add the ability to have different VMs use different dirty logging modes.

Signed-off-by: Junaid Shahid <junaids@google.com>
---
 arch/x86/include/asm/kvm_host.h |  4 +++
 arch/x86/include/uapi/asm/kvm.h |  4 +++
 arch/x86/kvm/mmu.c              | 30 +++++++++++++++++------
 arch/x86/kvm/vmx.c              |  3 ++-
 arch/x86/kvm/x86.c              | 43 ++++++++++++++++++++++++---------
 5 files changed, 63 insertions(+), 21 deletions(-)
diff mbox series

Patch

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 78187944494a..3da22c92a5d6 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -920,6 +920,8 @@  struct kvm_arch {
 
 	bool guest_can_read_msr_platform_info;
 	bool exception_payload_enabled;
+
+	u8 dirty_logging_mode;
 };
 
 struct kvm_vm_stat {
@@ -1199,6 +1201,8 @@  struct kvm_arch_async_pf {
 
 extern struct kvm_x86_ops *kvm_x86_ops;
 
+extern u8 kvm_default_dirty_log_mode;
+
 #define __KVM_HAVE_ARCH_VM_ALLOC
 static inline struct kvm *kvm_arch_alloc_vm(void)
 {
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index dabfcf7c3941..2b1c442bffe6 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -420,4 +420,8 @@  struct kvm_nested_state {
 	__u8 data[0];
 };
 
+#define KVM_DIRTY_LOG_MODE_WRPROT	1
+#define KVM_DIRTY_LOG_MODE_DBIT		2
+#define KVM_DIRTY_LOG_MODE_PML		4
+
 #endif /* _ASM_X86_KVM_H */
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 3f782d33e078..2714dbea2d88 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -262,6 +262,8 @@  static const u64 shadow_nonpresent_or_rsvd_mask_len = 5;
  */
 static u64 __read_mostly shadow_nonpresent_or_rsvd_lower_gfn_mask;
 
+u8 __read_mostly kvm_default_dirty_log_mode;
+EXPORT_SYMBOL_GPL(kvm_default_dirty_log_mode);
 
 static void mmu_spte_set(u64 *sptep, u64 spte);
 static union kvm_mmu_page_role
@@ -432,8 +434,12 @@  void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask,
 	shadow_acc_track_mask = acc_track_mask;
 	shadow_me_mask = me_mask;
 
-	if (shadow_dirty_mask == 0)
+	if (shadow_dirty_mask == 0) {
 		enable_d_bit_logging = false;
+
+		if (kvm_default_dirty_log_mode == KVM_DIRTY_LOG_MODE_DBIT)
+			kvm_default_dirty_log_mode = KVM_DIRTY_LOG_MODE_WRPROT;
+	}
 }
 EXPORT_SYMBOL_GPL(kvm_mmu_set_mask_ptes);
 
@@ -719,7 +725,7 @@  static void spte_dirty_mask_cleared(struct kvm *kvm, u64 *sptep)
 	 * the D bit would result in those pages being unnecessarily reported as
 	 * dirty again in the next round.
 	 */
-	if (enable_d_bit_logging) {
+	if (kvm->arch.dirty_logging_mode == KVM_DIRTY_LOG_MODE_DBIT) {
 		gfn_t gfn;
 		struct kvm_mmu_page *sp = page_header(__pa(sptep));
 
@@ -1722,14 +1728,19 @@  void kvm_arch_mmu_get_and_reset_log_dirty(struct kvm *kvm,
 				struct kvm_memory_slot *slot,
 				gfn_t gfn_offset, unsigned long *mask)
 {
-	if (kvm_x86_ops->get_and_reset_log_dirty)
-		kvm_x86_ops->get_and_reset_log_dirty(kvm, slot, gfn_offset,
-						     mask);
-	else if (enable_d_bit_logging)
+	switch (kvm->arch.dirty_logging_mode) {
+	case KVM_DIRTY_LOG_MODE_WRPROT:
+		kvm_mmu_write_protect_pt_masked(kvm, slot, gfn_offset, *mask);
+		break;
+	case KVM_DIRTY_LOG_MODE_DBIT:
 		*mask |= kvm_mmu_shadow_dirty_mask_test_and_clear(kvm, slot,
 								  gfn_offset);
-	else
-		kvm_mmu_write_protect_pt_masked(kvm, slot, gfn_offset, *mask);
+		break;
+	default:
+		if (kvm_x86_ops->get_and_reset_log_dirty)
+			kvm_x86_ops->get_and_reset_log_dirty(kvm, slot,
+							     gfn_offset, mask);
+	}
 }
 
 /**
@@ -6057,6 +6068,9 @@  int kvm_mmu_module_init(void)
 	BUILD_BUG_ON(sizeof(union kvm_mmu_role) != sizeof(u64));
 
 	kvm_mmu_reset_all_pte_masks();
+	kvm_default_dirty_log_mode = enable_d_bit_logging
+				     ? KVM_DIRTY_LOG_MODE_DBIT
+				     : KVM_DIRTY_LOG_MODE_WRPROT;
 
 	pte_list_desc_cache = kmem_cache_create("pte_list_desc",
 					    sizeof(struct pte_list_desc),
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index d9be8e631f17..c6b0477c855e 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -7992,7 +7992,8 @@  static __init int hardware_setup(void)
 		kvm_x86_ops->slot_disable_log_dirty = NULL;
 		kvm_x86_ops->flush_log_dirty = NULL;
 		kvm_x86_ops->get_and_reset_log_dirty = NULL;
-	}
+	} else
+		kvm_default_dirty_log_mode = KVM_DIRTY_LOG_MODE_PML;
 
 	if (!cpu_has_vmx_preemption_timer())
 		kvm_x86_ops->request_immediate_exit = __kvm_request_immediate_exit;
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index ab710956b8a2..9d22d4eeb5dc 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4404,11 +4404,17 @@  int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log)
 
 	mutex_lock(&kvm->slots_lock);
 
-	/*
-	 * Flush potentially hardware-cached dirty pages to dirty_bitmap.
-	 */
-	if (kvm_x86_ops->flush_log_dirty)
-		kvm_x86_ops->flush_log_dirty(kvm);
+	switch (kvm->arch.dirty_logging_mode) {
+	case KVM_DIRTY_LOG_MODE_WRPROT:
+	case KVM_DIRTY_LOG_MODE_DBIT:
+		break;
+	default:
+		/*
+		 * Flush potentially hardware-cached dirty pages to dirty_bitmap.
+		 */
+		if (kvm_x86_ops->flush_log_dirty)
+			kvm_x86_ops->flush_log_dirty(kvm);
+	}
 
 	r = kvm_get_dirty_log_protect(kvm, log, &is_dirty);
 
@@ -9020,6 +9026,8 @@  int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
 
 	kvm->arch.guest_can_read_msr_platform_info = true;
 
+	kvm->arch.dirty_logging_mode = kvm_default_dirty_log_mode;
+
 	INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
 	INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
 
@@ -9302,15 +9310,26 @@  static void kvm_mmu_slot_apply_flags(struct kvm *kvm,
 	 * See the comments in fast_page_fault().
 	 */
 	if (new->flags & KVM_MEM_LOG_DIRTY_PAGES) {
-		if (kvm_x86_ops->slot_enable_log_dirty)
-			kvm_x86_ops->slot_enable_log_dirty(kvm, new);
-		else if (enable_d_bit_logging)
-			kvm_mmu_slot_wrprot_lpage_and_clear_dirty(kvm, new);
-		else
+		switch (kvm->arch.dirty_logging_mode) {
+		case KVM_DIRTY_LOG_MODE_WRPROT:
 			kvm_mmu_slot_remove_write_access(kvm, new);
+			break;
+		case KVM_DIRTY_LOG_MODE_DBIT:
+			kvm_mmu_slot_wrprot_lpage_and_clear_dirty(kvm, new);
+			break;
+		default:
+			if (kvm_x86_ops->slot_enable_log_dirty)
+				kvm_x86_ops->slot_enable_log_dirty(kvm, new);
+		}
 	} else {
-		if (kvm_x86_ops->slot_disable_log_dirty)
-			kvm_x86_ops->slot_disable_log_dirty(kvm, new);
+		switch (kvm->arch.dirty_logging_mode) {
+		case KVM_DIRTY_LOG_MODE_WRPROT:
+		case KVM_DIRTY_LOG_MODE_DBIT:
+			break;
+		default:
+			if (kvm_x86_ops->slot_disable_log_dirty)
+				kvm_x86_ops->slot_disable_log_dirty(kvm, new);
+		}
 	}
 }