Skip to content

Commit 132194f

Browse files
jpemartinsbonzini
authored andcommitted
KVM: VMX: Do not change PID.NDST when loading a blocked vCPU
When vCPU enters block phase, pi_pre_block() inserts vCPU to a per pCPU linked list of all vCPUs that are blocked on this pCPU. Afterwards, it changes PID.NV to POSTED_INTR_WAKEUP_VECTOR which its handler (wakeup_handler()) is responsible to kick (unblock) any vCPU on that linked list that now has pending posted interrupts. While vCPU is blocked (in kvm_vcpu_block()), it may be preempted which will cause vmx_vcpu_pi_put() to set PID.SN. If later the vCPU will be scheduled to run on a different pCPU, vmx_vcpu_pi_load() will clear PID.SN but will also *overwrite PID.NDST to this different pCPU*. Instead of keeping it with original pCPU which vCPU had entered block phase on. This results in an issue because when a posted interrupt is delivered, as the wakeup_handler() will be executed and fail to find blocked vCPU on its per pCPU linked list of all vCPUs that are blocked on this pCPU. Which is due to the vCPU being placed on a *different* per pCPU linked list i.e. the original pCPU in which it entered block phase. The regression is introduced by commit c112b5f ("KVM: x86: Recompute PID.ON when clearing PID.SN"). Therefore, partially revert it and reintroduce the condition in vmx_vcpu_pi_load() responsible for avoiding changing PID.NDST when loading a blocked vCPU. Fixes: c112b5f ("KVM: x86: Recompute PID.ON when clearing PID.SN") Tested-by: Nathan Ni <[email protected]> Co-developed-by: Liran Alon <[email protected]> Signed-off-by: Liran Alon <[email protected]> Signed-off-by: Joao Martins <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 9482ae4 commit 132194f

File tree

2 files changed

+20
-0
lines changed

2 files changed

+20
-0
lines changed

arch/x86/kvm/vmx/vmx.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,18 @@ static void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
12681268
if (!pi_test_sn(pi_desc) && vcpu->cpu == cpu)
12691269
return;
12701270

1271+
/*
1272+
* If the 'nv' field is POSTED_INTR_WAKEUP_VECTOR, do not change
1273+
* PI.NDST: pi_post_block is the one expected to change PID.NDST and the
1274+
* wakeup handler expects the vCPU to be on the blocked_vcpu_list that
1275+
* matches PI.NDST. Otherwise, a vcpu may not be able to be woken up
1276+
* correctly.
1277+
*/
1278+
if (pi_desc->nv == POSTED_INTR_WAKEUP_VECTOR || vcpu->cpu == cpu) {
1279+
pi_clear_sn(pi_desc);
1280+
goto after_clear_sn;
1281+
}
1282+
12711283
/* The full case. */
12721284
do {
12731285
old.control = new.control = pi_desc->control;
@@ -1283,6 +1295,8 @@ static void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
12831295
} while (cmpxchg64(&pi_desc->control, old.control,
12841296
new.control) != old.control);
12851297

1298+
after_clear_sn:
1299+
12861300
/*
12871301
* Clear SN before reading the bitmap. The VT-d firmware
12881302
* writes the bitmap and reads SN atomically (5.2.3 in the

arch/x86/kvm/vmx/vmx.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,12 @@ static inline void pi_clear_on(struct pi_desc *pi_desc)
373373
(unsigned long *)&pi_desc->control);
374374
}
375375

376+
static inline void pi_clear_sn(struct pi_desc *pi_desc)
377+
{
378+
clear_bit(POSTED_INTR_SN,
379+
(unsigned long *)&pi_desc->control);
380+
}
381+
376382
static inline int pi_test_on(struct pi_desc *pi_desc)
377383
{
378384
return test_bit(POSTED_INTR_ON,

0 commit comments

Comments
 (0)