mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-05-02 13:23:45 +00:00
KVM: VMX: avoid double list add with VT-d posted interrupts
In some cases, for example involving hot-unplug of assigned devices, pi_post_block can forget to remove the vCPU from the blocked_vcpu_list. When this happens, the next call to pi_pre_block corrupts the list. Fix this in two ways. First, check vcpu->pre_pcpu in pi_pre_block and WARN instead of adding the element twice in the list. Second, always do the list removal in pi_post_block if vcpu->pre_pcpu is set (not -1). The new code keeps interrupts disabled for the whole duration of pi_pre_block/pi_post_block. This is not strictly necessary, but easier to follow. For the same reason, PI.ON is checked only after the cmpxchg, and to handle it we just call the post-block code. This removes duplication of the list removal code. Cc: Huangweidong <weidong.huang@huawei.com> Cc: Gonglei <arei.gonglei@huawei.com> Cc: wangxin <wangxinxin.wang@huawei.com> Cc: Radim Krčmář <rkrcmar@redhat.com> Tested-by: Longpeng (Mike) <longpeng2@huawei.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
cd39e1176d
commit
8b306e2f3c
1 changed files with 25 additions and 37 deletions
|
@ -11710,10 +11710,11 @@ static void __pi_post_block(struct kvm_vcpu *vcpu)
|
||||||
struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
|
struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
|
||||||
struct pi_desc old, new;
|
struct pi_desc old, new;
|
||||||
unsigned int dest;
|
unsigned int dest;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
old.control = new.control = pi_desc->control;
|
old.control = new.control = pi_desc->control;
|
||||||
|
WARN(old.nv != POSTED_INTR_WAKEUP_VECTOR,
|
||||||
|
"Wakeup handler not enabled while the VCPU is blocked\n");
|
||||||
|
|
||||||
dest = cpu_physical_id(vcpu->cpu);
|
dest = cpu_physical_id(vcpu->cpu);
|
||||||
|
|
||||||
|
@ -11730,14 +11731,10 @@ static void __pi_post_block(struct kvm_vcpu *vcpu)
|
||||||
} while (cmpxchg(&pi_desc->control, old.control,
|
} while (cmpxchg(&pi_desc->control, old.control,
|
||||||
new.control) != old.control);
|
new.control) != old.control);
|
||||||
|
|
||||||
if(vcpu->pre_pcpu != -1) {
|
if (!WARN_ON_ONCE(vcpu->pre_pcpu == -1)) {
|
||||||
spin_lock_irqsave(
|
spin_lock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
|
||||||
&per_cpu(blocked_vcpu_on_cpu_lock,
|
|
||||||
vcpu->pre_pcpu), flags);
|
|
||||||
list_del(&vcpu->blocked_vcpu_list);
|
list_del(&vcpu->blocked_vcpu_list);
|
||||||
spin_unlock_irqrestore(
|
spin_unlock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
|
||||||
&per_cpu(blocked_vcpu_on_cpu_lock,
|
|
||||||
vcpu->pre_pcpu), flags);
|
|
||||||
vcpu->pre_pcpu = -1;
|
vcpu->pre_pcpu = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11757,7 +11754,6 @@ static void __pi_post_block(struct kvm_vcpu *vcpu)
|
||||||
*/
|
*/
|
||||||
static int pi_pre_block(struct kvm_vcpu *vcpu)
|
static int pi_pre_block(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
unsigned int dest;
|
unsigned int dest;
|
||||||
struct pi_desc old, new;
|
struct pi_desc old, new;
|
||||||
struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
|
struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
|
||||||
|
@ -11767,34 +11763,20 @@ static int pi_pre_block(struct kvm_vcpu *vcpu)
|
||||||
!kvm_vcpu_apicv_active(vcpu))
|
!kvm_vcpu_apicv_active(vcpu))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
vcpu->pre_pcpu = vcpu->cpu;
|
WARN_ON(irqs_disabled());
|
||||||
spin_lock_irqsave(&per_cpu(blocked_vcpu_on_cpu_lock,
|
local_irq_disable();
|
||||||
vcpu->pre_pcpu), flags);
|
if (!WARN_ON_ONCE(vcpu->pre_pcpu != -1)) {
|
||||||
list_add_tail(&vcpu->blocked_vcpu_list,
|
vcpu->pre_pcpu = vcpu->cpu;
|
||||||
&per_cpu(blocked_vcpu_on_cpu,
|
spin_lock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
|
||||||
vcpu->pre_pcpu));
|
list_add_tail(&vcpu->blocked_vcpu_list,
|
||||||
spin_unlock_irqrestore(&per_cpu(blocked_vcpu_on_cpu_lock,
|
&per_cpu(blocked_vcpu_on_cpu,
|
||||||
vcpu->pre_pcpu), flags);
|
vcpu->pre_pcpu));
|
||||||
|
spin_unlock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
old.control = new.control = pi_desc->control;
|
old.control = new.control = pi_desc->control;
|
||||||
|
|
||||||
/*
|
|
||||||
* We should not block the vCPU if
|
|
||||||
* an interrupt is posted for it.
|
|
||||||
*/
|
|
||||||
if (pi_test_on(pi_desc) == 1) {
|
|
||||||
spin_lock_irqsave(&per_cpu(blocked_vcpu_on_cpu_lock,
|
|
||||||
vcpu->pre_pcpu), flags);
|
|
||||||
list_del(&vcpu->blocked_vcpu_list);
|
|
||||||
spin_unlock_irqrestore(
|
|
||||||
&per_cpu(blocked_vcpu_on_cpu_lock,
|
|
||||||
vcpu->pre_pcpu), flags);
|
|
||||||
vcpu->pre_pcpu = -1;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN((pi_desc->sn == 1),
|
WARN((pi_desc->sn == 1),
|
||||||
"Warning: SN field of posted-interrupts "
|
"Warning: SN field of posted-interrupts "
|
||||||
"is set before blocking\n");
|
"is set before blocking\n");
|
||||||
|
@ -11819,7 +11801,12 @@ static int pi_pre_block(struct kvm_vcpu *vcpu)
|
||||||
} while (cmpxchg(&pi_desc->control, old.control,
|
} while (cmpxchg(&pi_desc->control, old.control,
|
||||||
new.control) != old.control);
|
new.control) != old.control);
|
||||||
|
|
||||||
return 0;
|
/* We should not block the vCPU if an interrupt is posted for it. */
|
||||||
|
if (pi_test_on(pi_desc) == 1)
|
||||||
|
__pi_post_block(vcpu);
|
||||||
|
|
||||||
|
local_irq_enable();
|
||||||
|
return (vcpu->pre_pcpu == -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vmx_pre_block(struct kvm_vcpu *vcpu)
|
static int vmx_pre_block(struct kvm_vcpu *vcpu)
|
||||||
|
@ -11835,12 +11822,13 @@ static int vmx_pre_block(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
static void pi_post_block(struct kvm_vcpu *vcpu)
|
static void pi_post_block(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
if (!kvm_arch_has_assigned_device(vcpu->kvm) ||
|
if (vcpu->pre_pcpu == -1)
|
||||||
!irq_remapping_cap(IRQ_POSTING_CAP) ||
|
|
||||||
!kvm_vcpu_apicv_active(vcpu))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
WARN_ON(irqs_disabled());
|
||||||
|
local_irq_disable();
|
||||||
__pi_post_block(vcpu);
|
__pi_post_block(vcpu);
|
||||||
|
local_irq_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vmx_post_block(struct kvm_vcpu *vcpu)
|
static void vmx_post_block(struct kvm_vcpu *vcpu)
|
||||||
|
|
Loading…
Add table
Reference in a new issue