From: Sean Christopherson <seanjc@google.com>
To: Nikunj A Dadhania <nikunj@amd.com>
Cc: pbonzini@redhat.com, bp@alien8.de, joao.m.martins@oracle.com,
kai.huang@intel.com, kvm@vger.kernel.org,
thomas.lendacky@amd.com, yosry@kernel.org
Subject: Re: [PATCH v7.1] KVM: SVM: Add Page modification logging support
Date: Tue, 1 Sep 2026 17:09:39 -0700 [thread overview]
Message-ID: <apdpQ4eUkXODO-bI@google.com> (raw)
In-Reply-To: <20260529063833.1660791-1-nikunj@amd.com>
On Fri, May 29, 2026, Nikunj A Dadhania wrote:
> @@ -3331,6 +3364,53 @@ static int vmmcall_interception(struct kvm_vcpu *vcpu)
> return kvm_emulate_hypercall(vcpu);
> }
>
> +void svm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
This can be static.
> +{
> + struct vcpu_svm *svm = to_svm(vcpu);
> + struct vmcb *vmcb01 = svm->vmcb01.ptr;
> +
> + if (WARN_ON_ONCE(!vcpu->kvm->arch.cpu_dirty_log_size))
This can/should be moved to common code.
> + return;
> +
> + /*
> + * Note, nr_memslots_dirty_logging can be changed concurrent with this
> + * code, but in that case another update request will be made and so
> + * the guest will never run with a stale PML value.
> + */
Ditto with this copy+pasted comment.
> + if (atomic_read(&vcpu->kvm->nr_memslots_dirty_logging))
And this check. E.g. (incomplete, needs to be split up)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 0a8ef0b4ddda..73564619a61e 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -3377,20 +3377,12 @@ static int vmmcall_interception(struct kvm_vcpu *vcpu)
return kvm_emulate_hypercall(vcpu);
}
-void svm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+static void svm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)
{
struct vcpu_svm *svm = to_svm(vcpu);
struct vmcb *vmcb01 = svm->vmcb01.ptr;
- if (WARN_ON_ONCE(!vcpu->kvm->arch.cpu_dirty_log_size))
- return;
-
- /*
- * Note, nr_memslots_dirty_logging can be changed concurrent with this
- * code, but in that case another update request will be made and so
- * the guest will never run with a stale PML value.
- */
- if (atomic_read(&vcpu->kvm->nr_memslots_dirty_logging))
+ if (enable)
vmcb01->control.misc_ctl |= SVM_MISC_ENABLE_PML;
else
vmcb01->control.misc_ctl &= ~SVM_MISC_ENABLE_PML;
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 0052446e04d9..828a51418d83 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8416,21 +8416,13 @@ static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit
}
#endif
-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- if (WARN_ON_ONCE(!vcpu->kvm->arch.cpu_dirty_log_size))
- return;
-
guard(vmx_vmcs01)(vcpu);
- /*
- * Note, nr_memslots_dirty_logging can be changed concurrent with this
- * code, but in that case another update request will be made and so
- * the guest will never run with a stale PML value.
- */
- if (atomic_read(&vcpu->kvm->nr_memslots_dirty_logging))
+ if (enable)
secondary_exec_controls_setbit(vmx, SECONDARY_EXEC_ENABLE_PML);
else
secondary_exec_controls_clearbit(vmx, SECONDARY_EXEC_ENABLE_PML);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 33715236afc9..4443796c4426 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8073,6 +8073,21 @@ static void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu)
kvm_x86_call(set_apic_access_page_addr)(vcpu);
}
+static void kvm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+{
+ /*
+ * Note, nr_memslots_dirty_logging can be changed concurrent with this
+ * code, but in that case another update request will be made and so
+ * the guest will never run with a stale PML value.
+ */
+ bool enabled = atomic_read(&vcpu->kvm->nr_memslots_dirty_logging);
+
+ if (WARN_ON_ONCE(!vcpu->kvm->arch.cpu_dirty_log_size))
+ return;
+
+ kvm_x86_call(update_cpu_dirty_logging)(vcpu, enable);
+}
+
/*
* Called within kvm->srcu read side.
* Returns 1 to let vcpu_run() continue the guest execution loop without
@@ -8238,8 +8253,14 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
if (kvm_check_request(KVM_REQ_RECALC_INTERCEPTS, vcpu))
kvm_x86_call(recalc_intercepts)(vcpu);
- if (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu))
- kvm_x86_call(update_cpu_dirty_logging)(vcpu);
+
+ /*
+ * Note, nr_memslots_dirty_logging can be changed concurrent
+ * with this code, but in that case another update request will
+ * be made and so the guest will never run with stale PML state.
+ */
+ if (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu)
+ kvm_update_cpu_dirty_logging(vcpu);
if (kvm_check_request(KVM_REQ_UPDATE_PROTECTED_GUEST_STATE, vcpu)) {
kvm_vcpu_reset(vcpu, true);
> + vmcb01->control.misc_ctl |= SVM_MISC_ENABLE_PML;
> + else
> + vmcb01->control.misc_ctl &= ~SVM_MISC_ENABLE_PML;
> +
> + vmcb_mark_dirty(vmcb01, VMCB_NPT);
> +}
> +
> +static void svm_flush_pml_buffer(struct kvm_vcpu *vcpu)
> +{
> + struct vcpu_svm *svm = to_svm(vcpu);
> + struct vmcb_control_area *control = &svm->vmcb->control;
> +
> + /* Do nothing if PML buffer is empty */
> + if (control->pml_index == PML_HEAD_INDEX)
> + return;
> +
> + kvm_flush_pml_buffer(vcpu, control->pml_index);
> +
> + /* Reset the PML index */
> + control->pml_index = PML_HEAD_INDEX;
> +}
> +
> +static int pml_full_interception(struct kvm_vcpu *vcpu)
> +{
> + trace_kvm_pml_full(vcpu->vcpu_id);
> +
> + /*
> + * PML buffer is already flushed at the beginning of svm_handle_exit().
> + * Nothing to do here.
> + */
> + return 1;
> +}
> +
> static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
> [SVM_EXIT_READ_CR0] = cr_interception,
> [SVM_EXIT_READ_CR3] = cr_interception,
> @@ -3407,6 +3487,7 @@ static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
> #ifdef CONFIG_KVM_AMD_SEV
> [SVM_EXIT_VMGEXIT] = sev_handle_vmgexit,
> #endif
> + [SVM_EXIT_PML_FULL] = pml_full_interception,
> };
>
> static void dump_vmcb(struct kvm_vcpu *vcpu)
> @@ -3456,8 +3537,14 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
> pr_err("%-20s%016llx\n", "exit_info2:", control->exit_info_2);
> pr_err("%-20s%08x\n", "exit_int_info:", control->exit_int_info);
> pr_err("%-20s%08x\n", "exit_int_info_err:", control->exit_int_info_err);
> - pr_err("%-20s%lld\n", "misc_ctl:", control->misc_ctl);
> + pr_err("%-20s%llx\n", "misc_ctl:", control->misc_ctl);
> pr_err("%-20s%016llx\n", "nested_cr3:", control->nested_cr3);
> +
> + if (pml) {
> + pr_err("%-20s%016llx\n", "pml_addr:", control->pml_addr);
> + pr_err("%-20s%04x\n", "pml_index:", control->pml_index);
> + }
> +
> pr_err("%-20s%016llx\n", "avic_vapic_bar:", control->avic_vapic_bar);
> pr_err("%-20s%016llx\n", "ghcb:", control->ghcb_gpa);
> pr_err("%-20s%08x\n", "event_inj:", control->event_inj);
> @@ -3635,6 +3722,13 @@ int svm_invoke_exit_handler(struct kvm_vcpu *vcpu, u64 __exit_code)
> (u64)exit_code != __exit_code)
> goto unexpected_vmexit;
>
> + /*
> + * PML is never enabled when running L2, bail immediately if a PML full
> + * exit occurs as something is horribly wrong.
> + */
> + if (unlikely(is_guest_mode(vcpu) && exit_code == SVM_EXIT_PML_FULL))
> + goto unexpected_vmexit;
> +
> #ifdef CONFIG_MITIGATION_RETPOLINE
> if (exit_code == SVM_EXIT_MSR)
> return msr_interception(vcpu);
> @@ -3703,6 +3797,14 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
> struct vcpu_svm *svm = to_svm(vcpu);
> struct kvm_run *kvm_run = vcpu->run;
>
> + /*
> + * Opportunistically flush the PML buffer on VM exit. This keeps the
> + * dirty bitmap current by processing logged GPAs rather than waiting for
> + * PML_FULL exit.
> + */
> + if (vcpu->kvm->arch.cpu_dirty_log_size && !is_guest_mode(vcpu))
> + svm_flush_pml_buffer(vcpu);
> +
> if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE))
> return 0;
>
> @@ -5310,6 +5412,9 @@ static int svm_vm_init(struct kvm *kvm)
> return ret;
> }
>
> + if (pml)
> + kvm->arch.cpu_dirty_log_size = PML_LOG_NR_ENTRIES;
> +
> svm_srso_vm_init();
> return 0;
> }
> @@ -5465,6 +5570,8 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
> .gmem_prepare = sev_gmem_prepare,
> .gmem_invalidate = sev_gmem_invalidate,
> .gmem_max_mapping_level = sev_gmem_max_mapping_level,
> +
> + .update_cpu_dirty_logging = svm_update_cpu_dirty_logging,
Please keep the ordering somewhat similar to the other declarations, i.e. don't
just put this at the end. This seems like the least awful place:
.handle_exit_irqoff = svm_handle_exit_irqoff,
.update_cpu_dirty_logging = svm_update_cpu_dirty_logging,
.deliver_interrupt = svm_deliver_interrupt,
> @@ -832,6 +833,8 @@ static inline void svm_enable_intercept_for_msr(struct kvm_vcpu *vcpu,
> svm_set_intercept_for_msr(vcpu, msr, type, true);
> }
>
> +void svm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu);
And this goes away.
next prev parent reply other threads:[~2026-09-02 0:09 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-18 4:59 [PATCH v7 0/7] KVM: SVM: Add Page Modification Logging (PML) support Nikunj A Dadhania
2026-05-18 4:59 ` [PATCH v7 1/7] KVM: VMX: Pass @vcpu, not @vmx to init_vmcs() Nikunj A Dadhania
2026-05-18 11:35 ` Huang, Kai
2026-05-18 4:59 ` [PATCH v7 2/7] KVM: x86: Move PML page to common vcpu arch structure Nikunj A Dadhania
2026-05-18 4:59 ` [PATCH v7 3/7] KVM: x86: Carve out PML flush routine Nikunj A Dadhania
2026-05-18 4:59 ` [PATCH v7 4/7] KVM: VMX: Use cpu_dirty_log_size instead of enable_pml for PML checks Nikunj A Dadhania
2026-05-18 4:59 ` [PATCH v7 5/7] x86/cpufeatures: Add Page modification logging Nikunj A Dadhania
2026-05-18 4:59 ` [PATCH v7 6/7] KVM: SVM: Use BIT_ULL for 64-bit misc_ctl bit definitions Nikunj A Dadhania
2026-05-18 4:59 ` [PATCH v7 7/7] KVM: SVM: Add Page modification logging support Nikunj A Dadhania
2026-05-18 17:12 ` Yosry Ahmed
2026-05-18 18:55 ` Sean Christopherson
2026-05-18 19:14 ` Yosry Ahmed
2026-05-18 19:25 ` Yosry Ahmed
2026-05-19 14:46 ` Nikunj A. Dadhania
2026-05-29 6:38 ` [PATCH v7.1] " Nikunj A Dadhania
2026-07-13 6:12 ` Nikunj A. Dadhania
2026-08-12 23:25 ` Sean Christopherson
2026-08-13 8:24 ` Nikunj A. Dadhania
2026-09-02 0:09 ` Sean Christopherson [this message]
2026-09-02 8:52 ` Nikunj A. Dadhania
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=apdpQ4eUkXODO-bI@google.com \
--to=seanjc@google.com \
--cc=bp@alien8.de \
--cc=joao.m.martins@oracle.com \
--cc=kai.huang@intel.com \
--cc=kvm@vger.kernel.org \
--cc=nikunj@amd.com \
--cc=pbonzini@redhat.com \
--cc=thomas.lendacky@amd.com \
--cc=yosry@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox