Skip to content

Commit 41e90a6

Browse files
committed
KVM: x86: Retry APIC optimized map recalc if vCPU is added/enabled
Retry the optimized APIC map recalculation if an APIC-enabled vCPU shows up between allocating the map and filling in the map data. Conditionally reschedule before retrying even though the number of vCPUs that can be created is bounded by KVM. Retrying a few thousand times isn't so slow as to be hugely problematic, but it's not blazing fast either. Reset xapic_id_mistach on each retry as a vCPU could change its xAPIC ID between loops, but do NOT reset max_id. The map size also factors in whether or not a vCPU's local APIC is hardware-enabled, i.e. userspace and/or the guest can theoretically keep KVM retrying indefinitely. The only downside is that KVM will allocate more memory than is strictly necessary if the vCPU with the highest x2APIC ID disabled its APIC while the recalculation was in-progress. Refresh kvm->arch.apic_map_dirty to opportunistically change it from DIRTY => UPDATE_IN_PROGRESS to avoid an unnecessary recalc from a different task, i.e. if another task is waiting to attempt an update (which is likely since a retry happens if and only if an update is required). Link: https://lore.kernel.org/r/20230602233250.1014316-3-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 550ba57 commit 41e90a6

File tree

1 file changed

+25
-4
lines changed

1 file changed

+25
-4
lines changed

arch/x86/kvm/lapic.c

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,8 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
376376
struct kvm_vcpu *vcpu;
377377
unsigned long i;
378378
u32 max_id = 255; /* enough space for any xAPIC ID */
379-
bool xapic_id_mismatch = false;
379+
bool xapic_id_mismatch;
380+
int r;
380381

381382
/* Read kvm->arch.apic_map_dirty before kvm->arch.apic_map. */
382383
if (atomic_read_acquire(&kvm->arch.apic_map_dirty) == CLEAN)
@@ -386,9 +387,14 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
386387
"Dirty APIC map without an in-kernel local APIC");
387388

388389
mutex_lock(&kvm->arch.apic_map_lock);
390+
391+
retry:
389392
/*
390-
* Read kvm->arch.apic_map_dirty before kvm->arch.apic_map
391-
* (if clean) or the APIC registers (if dirty).
393+
* Read kvm->arch.apic_map_dirty before kvm->arch.apic_map (if clean)
394+
* or the APIC registers (if dirty). Note, on retry the map may have
395+
* not yet been marked dirty by whatever task changed a vCPU's x2APIC
396+
* ID, i.e. the map may still show up as in-progress. In that case
397+
* this task still needs to retry and complete its calculation.
392398
*/
393399
if (atomic_cmpxchg_acquire(&kvm->arch.apic_map_dirty,
394400
DIRTY, UPDATE_IN_PROGRESS) == CLEAN) {
@@ -397,6 +403,15 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
397403
return;
398404
}
399405

406+
/*
407+
* Reset the mismatch flag between attempts so that KVM does the right
408+
* thing if a vCPU changes its xAPIC ID, but do NOT reset max_id, i.e.
409+
* keep max_id strictly increasing. Disallowing max_id from shrinking
410+
* ensures KVM won't get stuck in an infinite loop, e.g. if the vCPU
411+
* with the highest x2APIC ID is toggling its APIC on and off.
412+
*/
413+
xapic_id_mismatch = false;
414+
400415
kvm_for_each_vcpu(i, vcpu, kvm)
401416
if (kvm_apic_present(vcpu))
402417
max_id = max(max_id, kvm_x2apic_id(vcpu->arch.apic));
@@ -415,9 +430,15 @@ void kvm_recalculate_apic_map(struct kvm *kvm)
415430
if (!kvm_apic_present(vcpu))
416431
continue;
417432

418-
if (kvm_recalculate_phys_map(new, vcpu, &xapic_id_mismatch)) {
433+
r = kvm_recalculate_phys_map(new, vcpu, &xapic_id_mismatch);
434+
if (r) {
419435
kvfree(new);
420436
new = NULL;
437+
if (r == -E2BIG) {
438+
cond_resched();
439+
goto retry;
440+
}
441+
421442
goto out;
422443
}
423444

0 commit comments

Comments
 (0)