2022-06-06 18:44:18

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 0/7] KVM: x86: AVIC/APICv patch queue

This patch series contains a few fixes that I worked on

recently.



Also included another attempt to add inhibit

when the guest had changed apic id and/or apic base.



I also tested AVIC with full preemption and

found few bugs, which are now hopefully fixed.



Best regards,

Maxim Levitsky



Maxim Levitsky (7):

KVM: x86: document AVIC/APICv inhibit reasons

KVM: x86: inhibit APICv/AVIC when the guest and/or host changes either

apic id or the apic base from their default values.

KVM: x86: SVM: remove avic's broken code that updated APIC ID

KVM: x86: SVM: fix avic_kick_target_vcpus_fast

KVM: x86: disable preemption while updating apicv inhibition

KVM: x86: disable preemption around the call to

kvm_arch_vcpu_{un|}blocking

KVM: x86: SVM: there is no need for preempt safe wrappers for

avic_vcpu_load/put



arch/x86/include/asm/kvm_host.h | 68 ++++++++++++-

arch/x86/kvm/lapic.c | 27 ++++-

arch/x86/kvm/svm/avic.c | 171 ++++++++++++++------------------

arch/x86/kvm/svm/svm.c | 4 +-

arch/x86/kvm/svm/svm.h | 4 +-

arch/x86/kvm/vmx/vmx.c | 4 +-

arch/x86/kvm/x86.c | 2 +

virt/kvm/kvm_main.c | 8 +-

8 files changed, 180 insertions(+), 108 deletions(-)



--

2.26.3





2022-06-06 19:01:02

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 2/7] KVM: x86: inhibit APICv/AVIC when the guest and/or host changes either apic id or the apic base from their default values.

Neither of these settings should be changed by the guest and it is
a burden to support it in the acceleration code, so just inhibit
this code instead.

Signed-off-by: Maxim Levitsky <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 9 +++++++++
arch/x86/kvm/lapic.c | 27 +++++++++++++++++++++++----
arch/x86/kvm/svm/avic.c | 4 +++-
arch/x86/kvm/vmx/vmx.c | 4 +++-
4 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index d7fb3ade44501..971db02e8ed86 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1094,6 +1094,15 @@ enum kvm_apicv_inhibit {
*/
APICV_INHIBIT_REASON_BLOCKIRQ,

+ /*
+ * For simplicity, the APIC acceleration is inhibited
+ * first time either APIC ID or APIC base are changed by the guest
+ * from their reset values.
+ */
+ APICV_INHIBIT_REASON_APIC_ID_MODIFIED,
+ APICV_INHIBIT_REASON_APIC_BASE_MODIFIED,
+
+
/******************************************************/
/* INHIBITs that are relevant only to the AMD's AVIC. */
/******************************************************/
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index e69b83708f050..a413a1d8df4c1 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -2040,6 +2040,19 @@ static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val)
}
}

+static void kvm_lapic_xapic_id_updated(struct kvm_lapic *apic)
+{
+ struct kvm *kvm = apic->vcpu->kvm;
+
+ if (KVM_BUG_ON(apic_x2apic_mode(apic), kvm))
+ return;
+
+ if (kvm_xapic_id(apic) == apic->vcpu->vcpu_id)
+ return;
+
+ kvm_set_apicv_inhibit(apic->vcpu->kvm, APICV_INHIBIT_REASON_APIC_ID_MODIFIED);
+}
+
static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
{
int ret = 0;
@@ -2048,10 +2061,12 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)

switch (reg) {
case APIC_ID: /* Local APIC ID */
- if (!apic_x2apic_mode(apic))
+ if (!apic_x2apic_mode(apic)) {
kvm_apic_set_xapic_id(apic, val >> 24);
- else
+ kvm_lapic_xapic_id_updated(apic);
+ } else {
ret = 1;
+ }
break;

case APIC_TASKPRI:
@@ -2354,8 +2369,10 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
MSR_IA32_APICBASE_BASE;

if ((value & MSR_IA32_APICBASE_ENABLE) &&
- apic->base_address != APIC_DEFAULT_PHYS_BASE)
- pr_warn_once("APIC base relocation is unsupported by KVM");
+ apic->base_address != APIC_DEFAULT_PHYS_BASE) {
+ kvm_set_apicv_inhibit(apic->vcpu->kvm,
+ APICV_INHIBIT_REASON_APIC_BASE_MODIFIED);
+ }
}

void kvm_apic_update_apicv(struct kvm_vcpu *vcpu)
@@ -2666,6 +2683,8 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu,
icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR);
__kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32);
}
+ } else {
+ kvm_lapic_xapic_id_updated(vcpu->arch.apic);
}

return 0;
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 54fe03714f8a6..8dffd67f60862 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -910,7 +910,9 @@ bool avic_check_apicv_inhibit_reasons(enum kvm_apicv_inhibit reason)
BIT(APICV_INHIBIT_REASON_PIT_REINJ) |
BIT(APICV_INHIBIT_REASON_X2APIC) |
BIT(APICV_INHIBIT_REASON_BLOCKIRQ) |
- BIT(APICV_INHIBIT_REASON_SEV);
+ BIT(APICV_INHIBIT_REASON_SEV |
+ BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) |
+ BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED));

return supported & BIT(reason);
}
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index fd2e707faf2bf..48440f73c3352 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -7860,7 +7860,9 @@ static bool vmx_check_apicv_inhibit_reasons(enum kvm_apicv_inhibit reason)
ulong supported = BIT(APICV_INHIBIT_REASON_DISABLE) |
BIT(APICV_INHIBIT_REASON_ABSENT) |
BIT(APICV_INHIBIT_REASON_HYPERV) |
- BIT(APICV_INHIBIT_REASON_BLOCKIRQ);
+ BIT(APICV_INHIBIT_REASON_BLOCKIRQ) |
+ BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) |
+ BIT(APICV_INHIBIT_REASON_APIC_BASE_MODIFIED);

return supported & BIT(reason);
}
--
2.26.3

2022-06-06 19:07:41

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 1/7] KVM: x86: document AVIC/APICv inhibit reasons

These days there are too many AVIC/APICv inhibit
reasons, and it doesn't hurt to have some documentation
for them.

Signed-off-by: Maxim Levitsky <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 59 +++++++++++++++++++++++++++++++--
1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 6cf5d77d78969..d7fb3ade44501 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1065,14 +1065,69 @@ struct kvm_x86_msr_filter {
};

enum kvm_apicv_inhibit {
+
+ /********************************************************************/
+ /* INHIBITs that are relevant to both Intel's APICv and AMD's AVIC. */
+ /********************************************************************/
+
+ /*
+ * APIC acceleration is disabled by a module parameter
+ * and/or not supported in hardware.
+ */
APICV_INHIBIT_REASON_DISABLE,
+
+ /*
+ * APIC acceleration is inhibited because AutoEOI feature is
+ * being used by a HyperV guest.
+ */
APICV_INHIBIT_REASON_HYPERV,
+
+ /*
+ * APIC acceleration is inhibited because the userspace didn't yet
+ * enable the kernel/split irqchip.
+ */
+ APICV_INHIBIT_REASON_ABSENT,
+
+ /* APIC acceleration is inhibited because KVM_GUESTDBG_BLOCKIRQ
+ * (out of band, debug measure of blocking all interrupts on this vCPU)
+ * was enabled, to avoid AVIC/APICv bypassing it.
+ */
+ APICV_INHIBIT_REASON_BLOCKIRQ,
+
+ /******************************************************/
+ /* INHIBITs that are relevant only to the AMD's AVIC. */
+ /******************************************************/
+
+ /*
+ * AVIC is inhibited on a vCPU because it runs a nested guest.
+ *
+ * This is needed because unlike APICv, the peers of this vCPU
+ * cannot use the doorbell mechanism to signal interrupts via AVIC when
+ * a vCPU runs nested.
+ */
APICV_INHIBIT_REASON_NESTED,
+
+ /*
+ * On SVM, the wait for the IRQ window is implemented with pending vIRQ,
+ * which cannot be injected when the AVIC is enabled, thus AVIC
+ * is inhibited while KVM waits for IRQ window.
+ */
APICV_INHIBIT_REASON_IRQWIN,
+
+ /*
+ * PIT (i8254) 're-inject' mode, relies on EOI intercept,
+ * which AVIC doesn't support for edge triggered interrupts.
+ */
APICV_INHIBIT_REASON_PIT_REINJ,
+
+ /*
+ * AVIC is inhibited because the guest has x2apic in its CPUID.
+ */
APICV_INHIBIT_REASON_X2APIC,
- APICV_INHIBIT_REASON_BLOCKIRQ,
- APICV_INHIBIT_REASON_ABSENT,
+
+ /*
+ * AVIC is disabled because SEV doesn't support it.
+ */
APICV_INHIBIT_REASON_SEV,
};

--
2.26.3

2022-06-06 19:21:12

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 4/7] KVM: x86: SVM: fix avic_kick_target_vcpus_fast

There are two issues in avic_kick_target_vcpus_fast

1. It is legal to issue an IPI request with APIC_DEST_NOSHORT
and a physical destination of 0xFF (or 0xFFFFFFFF in case of x2apic),
which must be treated as a broadcast destination.

Fix this by explicitly checking for it.
Also don’t use ‘index’ in this case as it gives no new information.

2. It is legal to issue a logical IPI request to more than one target.
Index field only provides index in physical id table of first
such target and therefore can't be used before we are sure
that only a single target was addressed.

Instead, parse the ICRL/ICRH, double check that a unicast interrupt
was requested, and use that info to figure out the physical id
of the target vCPU.
At that point there is no need to use the index field as well.


In addition to fixing the above issues, also skip the call to
kvm_apic_match_dest.

It is possible to do this now, because now as long as AVIC is not
inhibited, it is guaranteed that none of the vCPUs changed their
apic id from its default value.


This fixes boot of windows guest with AVIC enabled because it uses
IPI with 0xFF destination and no destination shorthand.

Fixes: 7223fd2d5338 ("KVM: SVM: Use target APIC ID to complete AVIC IRQs when possible")
Cc: [email protected]

Signed-off-by: Maxim Levitsky <[email protected]>
---
arch/x86/kvm/svm/avic.c | 105 ++++++++++++++++++++++++++--------------
1 file changed, 69 insertions(+), 36 deletions(-)

diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 072e2c8cc66aa..5d98ac575dedc 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -291,58 +291,91 @@ void avic_ring_doorbell(struct kvm_vcpu *vcpu)
static int avic_kick_target_vcpus_fast(struct kvm *kvm, struct kvm_lapic *source,
u32 icrl, u32 icrh, u32 index)
{
- u32 dest, apic_id;
- struct kvm_vcpu *vcpu;
+ u32 l1_physical_id, dest;
+ struct kvm_vcpu *target_vcpu;
int dest_mode = icrl & APIC_DEST_MASK;
int shorthand = icrl & APIC_SHORT_MASK;
struct kvm_svm *kvm_svm = to_kvm_svm(kvm);
- u32 *avic_logical_id_table = page_address(kvm_svm->avic_logical_id_table_page);

if (shorthand != APIC_DEST_NOSHORT)
return -EINVAL;

- /*
- * The AVIC incomplete IPI #vmexit info provides index into
- * the physical APIC ID table, which can be used to derive
- * guest physical APIC ID.
- */
+ if (apic_x2apic_mode(source))
+ dest = icrh;
+ else
+ dest = GET_APIC_DEST_FIELD(icrh);
+
if (dest_mode == APIC_DEST_PHYSICAL) {
- apic_id = index;
+ /* broadcast destination, use slow path */
+ if (apic_x2apic_mode(source) && dest == X2APIC_BROADCAST)
+ return -EINVAL;
+ if (!apic_x2apic_mode(source) && dest == APIC_BROADCAST)
+ return -EINVAL;
+
+ l1_physical_id = dest;
+
+ if (WARN_ON_ONCE(l1_physical_id != index))
+ return -EINVAL;
+
} else {
- if (!apic_x2apic_mode(source)) {
- /* For xAPIC logical mode, the index is for logical APIC table. */
- apic_id = avic_logical_id_table[index] & 0x1ff;
+ u32 bitmap, cluster;
+ int logid_index;
+
+ if (apic_x2apic_mode(source)) {
+ /* 16 bit dest mask, 16 bit cluster id */
+ bitmap = dest & 0xFFFF0000;
+ cluster = (dest >> 16) << 4;
+ } else if (kvm_lapic_get_reg(source, APIC_DFR) == APIC_DFR_FLAT) {
+ /* 8 bit dest mask*/
+ bitmap = dest;
+ cluster = 0;
} else {
- return -EINVAL;
+ /* 4 bit desk mask, 4 bit cluster id */
+ bitmap = dest & 0xF;
+ cluster = (dest >> 4) << 2;
}
- }

- /*
- * Assuming vcpu ID is the same as physical apic ID,
- * and use it to retrieve the target vCPU.
- */
- vcpu = kvm_get_vcpu_by_id(kvm, apic_id);
- if (!vcpu)
- return -EINVAL;
+ if (unlikely(!bitmap))
+ /* guest bug: nobody to send the logical interrupt to */
+ return 0;

- if (apic_x2apic_mode(vcpu->arch.apic))
- dest = icrh;
- else
- dest = GET_APIC_DEST_FIELD(icrh);
+ if (!is_power_of_2(bitmap))
+ /* multiple logical destinations, use slow path */
+ return -EINVAL;

- /*
- * Try matching the destination APIC ID with the vCPU.
- */
- if (kvm_apic_match_dest(vcpu, source, shorthand, dest, dest_mode)) {
- vcpu->arch.apic->irr_pending = true;
- svm_complete_interrupt_delivery(vcpu,
- icrl & APIC_MODE_MASK,
- icrl & APIC_INT_LEVELTRIG,
- icrl & APIC_VECTOR_MASK);
- return 0;
+ logid_index = cluster + __ffs(bitmap);
+
+ if (apic_x2apic_mode(source)) {
+ l1_physical_id = logid_index;
+ } else {
+ u32 *avic_logical_id_table =
+ page_address(kvm_svm->avic_logical_id_table_page);
+
+ u32 logid_entry = avic_logical_id_table[logid_index];
+
+ if (WARN_ON_ONCE(index != logid_index))
+ return -EINVAL;
+
+ /* guest bug: non existing/reserved logical destination */
+ if (unlikely(!(logid_entry & AVIC_LOGICAL_ID_ENTRY_VALID_MASK)))
+ return 0;
+
+ l1_physical_id = logid_entry &
+ AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK;
+ }
}

- return -EINVAL;
+ target_vcpu = kvm_get_vcpu_by_id(kvm, l1_physical_id);
+ if (unlikely(!target_vcpu))
+ /* guest bug: non existing vCPU is a target of this IPI*/
+ return 0;
+
+ target_vcpu->arch.apic->irr_pending = true;
+ svm_complete_interrupt_delivery(target_vcpu,
+ icrl & APIC_MODE_MASK,
+ icrl & APIC_INT_LEVELTRIG,
+ icrl & APIC_VECTOR_MASK);
+ return 0;
}

static void avic_kick_target_vcpus(struct kvm *kvm, struct kvm_lapic *source,
--
2.26.3

2022-06-06 19:44:18

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 6/7] KVM: x86: disable preemption around the call to kvm_arch_vcpu_{un|}blocking

On SVM, if preemption happens right after the call to finish_rcuwait
but before call to kvm_arch_vcpu_unblocking on SVM/AVIC, it itself
will re-enable AVIC, and then we will try to re-enable it again
in kvm_arch_vcpu_unblocking which will lead to a warning
in __avic_vcpu_load.

The same problem can happen if the vCPU is preempted right after the call
to kvm_arch_vcpu_blocking but before the call to prepare_to_rcuwait
and in this case, we will end up with AVIC enabled during sleep -
Ooops.

Signed-off-by: Maxim Levitsky <[email protected]>
---
virt/kvm/kvm_main.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 5e511fcec80d7..642dba492c65d 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3337,9 +3337,11 @@ bool kvm_vcpu_block(struct kvm_vcpu *vcpu)

vcpu->stat.generic.blocking = 1;

+ preempt_disable();
kvm_arch_vcpu_blocking(vcpu);
-
prepare_to_rcuwait(wait);
+ preempt_enable();
+
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);

@@ -3349,9 +3351,11 @@ bool kvm_vcpu_block(struct kvm_vcpu *vcpu)
waited = true;
schedule();
}
- finish_rcuwait(wait);

+ preempt_disable();
+ finish_rcuwait(wait);
kvm_arch_vcpu_unblocking(vcpu);
+ preempt_enable();

vcpu->stat.generic.blocking = 0;

--
2.26.3

2022-06-06 20:00:28

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 7/7] KVM: x86: SVM: there is no need for preempt safe wrappers for avic_vcpu_load/put

Now that these functions are always called with preemption disabled -
remove them.

No functional change intended.

Signed-off-by: Maxim Levitsky <[email protected]>
---
arch/x86/kvm/svm/avic.c | 27 ++++-----------------------
arch/x86/kvm/svm/svm.c | 4 ++--
arch/x86/kvm/svm/svm.h | 4 ++--
3 files changed, 8 insertions(+), 27 deletions(-)

diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index 5d98ac575dedc..5542d8959e114 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -946,7 +946,7 @@ avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, int cpu, bool r)
return ret;
}

-void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
u64 entry;
int h_physical_id = kvm_cpu_get_apicid(cpu);
@@ -978,7 +978,7 @@ void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, true);
}

-void __avic_vcpu_put(struct kvm_vcpu *vcpu)
+void avic_vcpu_put(struct kvm_vcpu *vcpu)
{
u64 entry;
struct vcpu_svm *svm = to_svm(vcpu);
@@ -997,25 +997,6 @@ void __avic_vcpu_put(struct kvm_vcpu *vcpu)
WRITE_ONCE(*(svm->avic_physical_id_cache), entry);
}

-static void avic_vcpu_load(struct kvm_vcpu *vcpu)
-{
- int cpu = get_cpu();
-
- WARN_ON(cpu != vcpu->cpu);
-
- __avic_vcpu_load(vcpu, cpu);
-
- put_cpu();
-}
-
-static void avic_vcpu_put(struct kvm_vcpu *vcpu)
-{
- preempt_disable();
-
- __avic_vcpu_put(vcpu);
-
- preempt_enable();
-}

void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)
{
@@ -1042,7 +1023,7 @@ void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)
vmcb_mark_dirty(vmcb, VMCB_AVIC);

if (activated)
- avic_vcpu_load(vcpu);
+ avic_vcpu_load(vcpu, vcpu->cpu);
else
avic_vcpu_put(vcpu);

@@ -1075,5 +1056,5 @@ void avic_vcpu_unblocking(struct kvm_vcpu *vcpu)
if (!kvm_vcpu_apicv_active(vcpu))
return;

- avic_vcpu_load(vcpu);
+ avic_vcpu_load(vcpu, vcpu->cpu);
}
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 4aea82f668fb1..b909769c73f03 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -1439,13 +1439,13 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
indirect_branch_prediction_barrier();
}
if (kvm_vcpu_apicv_active(vcpu))
- __avic_vcpu_load(vcpu, cpu);
+ avic_vcpu_load(vcpu, cpu);
}

static void svm_vcpu_put(struct kvm_vcpu *vcpu)
{
if (kvm_vcpu_apicv_active(vcpu))
- __avic_vcpu_put(vcpu);
+ avic_vcpu_put(vcpu);

svm_prepare_host_switch(vcpu);

diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index cd92f43437539..035020d073477 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -614,8 +614,8 @@ void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb);
int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu);
int avic_unaccelerated_access_interception(struct kvm_vcpu *vcpu);
int avic_init_vcpu(struct vcpu_svm *svm);
-void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu);
-void __avic_vcpu_put(struct kvm_vcpu *vcpu);
+void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu);
+void avic_vcpu_put(struct kvm_vcpu *vcpu);
void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu);
void avic_set_virtual_apic_mode(struct kvm_vcpu *vcpu);
void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu);
--
2.26.3

2022-06-06 20:48:48

by Maxim Levitsky

[permalink] [raw]
Subject: [PATCH 5/7] KVM: x86: disable preemption while updating apicv inhibition

Currently nothing prevents preemption in kvm_vcpu_update_apicv.

On SVM, If the preemption happens after we update the
vcpu->arch.apicv_active, the preemption itself will
'update' the inhibition since the AVIC will be first disabled
on vCPU unload and then enabled, when the current task
is loaded again.

Then we will try to update it again, which will lead to a warning
in __avic_vcpu_load, that the AVIC is already enabled.

Fix this by disabling preemption in this code.

Signed-off-by: Maxim Levitsky <[email protected]>
---
arch/x86/kvm/x86.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 2db6f0373fa3f..9bbe6144d82ae 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -9893,6 +9893,7 @@ void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu)
return;

down_read(&vcpu->kvm->arch.apicv_update_lock);
+ preempt_disable();

activate = kvm_vcpu_apicv_activated(vcpu);

@@ -9913,6 +9914,7 @@ void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu)
kvm_make_request(KVM_REQ_EVENT, vcpu);

out:
+ preempt_enable();
up_read(&vcpu->kvm->arch.apicv_update_lock);
}
EXPORT_SYMBOL_GPL(kvm_vcpu_update_apicv);
--
2.26.3

2022-06-07 09:17:53

by Chao Gao

[permalink] [raw]
Subject: Re: [PATCH 2/7] KVM: x86: inhibit APICv/AVIC when the guest and/or host changes either apic id or the apic base from their default values.

On Mon, Jun 06, 2022 at 09:08:24PM +0300, Maxim Levitsky wrote:
>+ /*
>+ * For simplicity, the APIC acceleration is inhibited
>+ * first time either APIC ID or APIC base are changed by the guest
>+ * from their reset values.
>+ */
>+ APICV_INHIBIT_REASON_APIC_ID_MODIFIED,
>+ APICV_INHIBIT_REASON_APIC_BASE_MODIFIED,
>+
>+

Remove one newline.

> void kvm_apic_update_apicv(struct kvm_vcpu *vcpu)
>@@ -2666,6 +2683,8 @@ static int kvm_apic_state_fixup(struct kvm_vcpu *vcpu,
> icr = __kvm_lapic_get_reg64(s->regs, APIC_ICR);
> __kvm_lapic_set_reg(s->regs, APIC_ICR2, icr >> 32);
> }
>+ } else {
>+ kvm_lapic_xapic_id_updated(vcpu->arch.apic);

Strictly speaking, this is needed only for "set" cases.

2022-06-08 05:05:01

by Maxim Levitsky

[permalink] [raw]
Subject: Re: [PATCH 2/7] KVM: x86: inhibit APICv/AVIC when the guest and/or host changes either apic id or the apic base from their default values.

On Tue, 2022-06-07 at 15:05 +0800, Chao Gao wrote:
> On Mon, Jun 06, 2022 at 09:08:24PM +0300, Maxim Levitsky wrote:
> > +       /*
> > +        * For simplicity, the APIC acceleration is inhibited
> > +        * first time either APIC ID or APIC base are changed by
> > the guest
> > +        * from their reset values.
> > +        */
> > +       APICV_INHIBIT_REASON_APIC_ID_MODIFIED,
> > +       APICV_INHIBIT_REASON_APIC_BASE_MODIFIED,
> > +
> > +
>
> Remove one newline.
Will do.
>
> > void kvm_apic_update_apicv(struct kvm_vcpu *vcpu)
> > @@ -2666,6 +2683,8 @@ static int kvm_apic_state_fixup(struct
> > kvm_vcpu *vcpu,
> >                         icr = __kvm_lapic_get_reg64(s->regs,
> > APIC_ICR);
> >                         __kvm_lapic_set_reg(s->regs, APIC_ICR2, icr
> > >> 32);
> >                 }
> > +       } else {
> > +               kvm_lapic_xapic_id_updated(vcpu->arch.apic);
>
> Strictly speaking, this is needed only for "set" cases.
>
True, thanks!


Best regards,
Maxim Levitsky

2022-06-08 13:47:15

by Paolo Bonzini

[permalink] [raw]
Subject: Re: [PATCH 4/7] KVM: x86: SVM: fix avic_kick_target_vcpus_fast

On 6/6/22 20:08, Maxim Levitsky wrote:
> There are two issues in avic_kick_target_vcpus_fast
>
> 1. It is legal to issue an IPI request with APIC_DEST_NOSHORT
> and a physical destination of 0xFF (or 0xFFFFFFFF in case of x2apic),
> which must be treated as a broadcast destination.
>
> Fix this by explicitly checking for it.
> Also don’t use ‘index’ in this case as it gives no new information.
>
> 2. It is legal to issue a logical IPI request to more than one target.
> Index field only provides index in physical id table of first
> such target and therefore can't be used before we are sure
> that only a single target was addressed.
>
> Instead, parse the ICRL/ICRH, double check that a unicast interrupt
> was requested, and use that info to figure out the physical id
> of the target vCPU.
> At that point there is no need to use the index field as well.
>
>
> In addition to fixing the above issues, also skip the call to
> kvm_apic_match_dest.
>
> It is possible to do this now, because now as long as AVIC is not
> inhibited, it is guaranteed that none of the vCPUs changed their
> apic id from its default value.
>
>
> This fixes boot of windows guest with AVIC enabled because it uses
> IPI with 0xFF destination and no destination shorthand.
>
> Fixes: 7223fd2d5338 ("KVM: SVM: Use target APIC ID to complete AVIC IRQs when possible")
> Cc: [email protected]
>
> Signed-off-by: Maxim Levitsky <[email protected]>

Is it possible to use kvm_intr_is_single_vcpu_fast, or am I missing
something?

Series queued, thanks.

Paolo

2022-06-09 08:26:34

by Maxim Levitsky

[permalink] [raw]
Subject: Re: [PATCH 4/7] KVM: x86: SVM: fix avic_kick_target_vcpus_fast

On Wed, 2022-06-08 at 15:21 +0200, Paolo Bonzini wrote:
> On 6/6/22 20:08, Maxim Levitsky wrote:
> > There are two issues in avic_kick_target_vcpus_fast
> >
> > 1. It is legal to issue an IPI request with APIC_DEST_NOSHORT
> > and a physical destination of 0xFF (or 0xFFFFFFFF in case of x2apic),
> > which must be treated as a broadcast destination.
> >
> > Fix this by explicitly checking for it.
> > Also don’t use ‘index’ in this case as it gives no new information.
> >
> > 2. It is legal to issue a logical IPI request to more than one target.
> > Index field only provides index in physical id table of first
> > such target and therefore can't be used before we are sure
> > that only a single target was addressed.
> >
> > Instead, parse the ICRL/ICRH, double check that a unicast interrupt
> > was requested, and use that info to figure out the physical id
> > of the target vCPU.
> > At that point there is no need to use the index field as well.
> >
> >
> > In addition to fixing the above issues, also skip the call to
> > kvm_apic_match_dest.
> >
> > It is possible to do this now, because now as long as AVIC is not
> > inhibited, it is guaranteed that none of the vCPUs changed their
> > apic id from its default value.
> >
> >
> > This fixes boot of windows guest with AVIC enabled because it uses
> > IPI with 0xFF destination and no destination shorthand.
> >
> > Fixes: 7223fd2d5338 ("KVM: SVM: Use target APIC ID to complete AVIC IRQs when possible")
> > Cc: [email protected]
> >
> > Signed-off-by: Maxim Levitsky <[email protected]>
>
> Is it possible to use kvm_intr_is_single_vcpu_fast, or am I missing
> something?

Yes, except that it needs 'struct kvm_lapic_irq' which we won't have when
we emulate guest<->guest interrupts, and also it goes over apic map and such,
which can be be skipped.

It also does more unneeded things like dealing with low priority mode for example,
which thankfully AVIC doenst' support and if attempted will still VM exit
with 'incomplete IPI' but with AVIC_IPI_FAILURE_INVALID_INT_TYPE subreason,
which goes through full APIC register emulation.

I do think about the fact that ICRL/H parsing in the case of logical ID,
(which depends on cluser mode and x2apic mode) can be moved to some common
code, but I wasn't able yet to find a clean way to do it.

BTW: there is another case where AVIC must be inhibited: in xapic mode,
logical ids, don't have to have a single bit set in the mask area of the logical id,
(low 4 bits in cluster mode and all 8 bits in flat mode)
and neither there is a guarnantee that multilple CPUs don't share these bits.

AVIC however has a logical ID table which maps each (bit x cluster value) to a physical id,
and therefore a single vCPU, so tha later is not possible to support with AVIC.

I haven't studied the code that is responsible for this, I will do this soon.


Thankfully IPIv only supports physical IPI mode (this is what I heard, don't know for sure).

I also will write a unit test for this very soon, to test various logical id
IPIs, messing with logical id registers, etc, etc.

Best regards,
Maxim Levitsky


>
> Series queued, thanks.
>
> Paolo
>