]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: nVMX: Switch to vmcs01 to update PML controls on-demand if L2 is active
authorSean Christopherson <seanjc@google.com>
Fri, 9 Jan 2026 03:45:26 +0000 (19:45 -0800)
committerSean Christopherson <seanjc@google.com>
Wed, 14 Jan 2026 01:35:31 +0000 (17:35 -0800)
If KVM toggles "CPU dirty logging", a.k.a. Page-Modification Logging (PML),
while L2 is active, temporarily load vmcs01 and immediately update the
relevant controls instead of deferring the update until the next nested
VM-Exit.  For PML, deferring the update is relatively straightforward, but
for several APICv related updates, deferring updates creates ordering and
state consistency problems, e.g. KVM at-large thinks APICv is enabled, but
vmcs01 is still running with stale (and effectively unknown) state.

Convert PML first precisely because it's the simplest case to handle: if
something is broken with the vmcs01 <=> vmcs02 dance, then hopefully bugs
will bisect here.

Reviewed-by: Chao Gao <chao.gao@intel.com>
Link: https://patch.msgid.link/20260109034532.1012993-3-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/vmx/vmx.h

index 6dd1da94f00cfd825c9b4c57849fe215a8b40a54..e8adff614bd5bad7c0eadb7157cc617b6c3f99cf 100644 (file)
@@ -5131,11 +5131,6 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
                vmx_set_virtual_apic_mode(vcpu);
        }
 
-       if (vmx->nested.update_vmcs01_cpu_dirty_logging) {
-               vmx->nested.update_vmcs01_cpu_dirty_logging = false;
-               vmx_update_cpu_dirty_logging(vcpu);
-       }
-
        nested_put_vmcs12_pages(vcpu);
 
        if (vmx->nested.reload_vmcs01_apic_access_page) {
index 1f4d15cc898ed320f83e14854821ad6c019e1d06..3aa0e055e2b06d5d9d69bd79de2d2d09f2a1dbea 100644 (file)
@@ -1594,6 +1594,41 @@ void vmx_vcpu_put(struct kvm_vcpu *vcpu)
        vmx_prepare_switch_to_host(to_vmx(vcpu));
 }
 
+static void vmx_switch_loaded_vmcs(struct kvm_vcpu *vcpu,
+                                  struct loaded_vmcs *vmcs)
+{
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+       int cpu;
+
+       cpu = get_cpu();
+       vmx->loaded_vmcs = vmcs;
+       vmx_vcpu_load_vmcs(vcpu, cpu);
+       put_cpu();
+}
+
+static void vmx_load_vmcs01(struct kvm_vcpu *vcpu)
+{
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+
+       if (!is_guest_mode(vcpu)) {
+               WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01);
+               return;
+       }
+
+       WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->nested.vmcs02);
+       vmx_switch_loaded_vmcs(vcpu, &vmx->vmcs01);
+}
+
+static void vmx_put_vmcs01(struct kvm_vcpu *vcpu)
+{
+       if (!is_guest_mode(vcpu))
+               return;
+
+       vmx_switch_loaded_vmcs(vcpu, &to_vmx(vcpu)->nested.vmcs02);
+}
+DEFINE_GUARD(vmx_vmcs01, struct kvm_vcpu *,
+            vmx_load_vmcs01(_T), vmx_put_vmcs01(_T))
+
 bool vmx_emulation_required(struct kvm_vcpu *vcpu)
 {
        return emulate_invalid_guest_state && !vmx_guest_state_valid(vcpu);
@@ -8276,10 +8311,7 @@ void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
        if (WARN_ON_ONCE(!enable_pml))
                return;
 
-       if (is_guest_mode(vcpu)) {
-               vmx->nested.update_vmcs01_cpu_dirty_logging = true;
-               return;
-       }
+       guard(vmx_vmcs01)(vcpu);
 
        /*
         * Note, nr_memslots_dirty_logging can be changed concurrent with this
index bc3ed3145d7e837f1eac90275652e61579b44ab5..b44eda6225f453da21c371f5803e19899a0cab02 100644 (file)
@@ -133,7 +133,6 @@ struct nested_vmx {
 
        bool change_vmcs01_virtual_apic_mode;
        bool reload_vmcs01_apic_access_page;
-       bool update_vmcs01_cpu_dirty_logging;
        bool update_vmcs01_apicv_status;
        bool update_vmcs01_hwapic_isr;