[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250401161106.790710-19-pbonzini@redhat.com>
Date: Tue, 1 Apr 2025 18:10:55 +0200
From: Paolo Bonzini <pbonzini@...hat.com>
To: linux-kernel@...r.kernel.org,
kvm@...r.kernel.org
Cc: roy.hopkins@...e.com,
seanjc@...gle.com,
thomas.lendacky@....com,
ashish.kalra@....com,
michael.roth@....com,
jroedel@...e.de,
nsaenz@...zon.com,
anelkz@...zon.de,
James.Bottomley@...senPartnership.com
Subject: [PATCH 18/29] KVM: x86: track APICv inhibits per plane
As a first step towards per-plane APIC maps, track APICv inhibits per
plane. Most of the inhibits are set or cleared when building the map,
and the virtual machine as a whole will have the OR of the inhibits
of the individual plane.
Signed-off-by: Paolo Bonzini <pbonzini@...hat.com>
---
arch/x86/include/asm/kvm_host.h | 21 +++++----
arch/x86/kvm/hyperv.c | 2 +-
arch/x86/kvm/i8254.c | 4 +-
arch/x86/kvm/lapic.c | 15 +++---
arch/x86/kvm/svm/sev.c | 2 +-
arch/x86/kvm/svm/svm.c | 3 +-
arch/x86/kvm/x86.c | 83 +++++++++++++++++++++++++--------
include/linux/kvm_host.h | 2 +-
8 files changed, 90 insertions(+), 42 deletions(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index e29694a97a19..d07ab048d7cc 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1087,6 +1087,7 @@ struct kvm_arch_memory_slot {
};
struct kvm_arch_plane {
+ unsigned long apicv_inhibit_reasons;
};
/*
@@ -1299,11 +1300,13 @@ enum kvm_apicv_inhibit {
/*
* PIT (i8254) 're-inject' mode, relies on EOI intercept,
* which AVIC doesn't support for edge triggered interrupts.
+ * Applied only to plane 0.
*/
APICV_INHIBIT_REASON_PIT_REINJ,
/*
- * AVIC is disabled because SEV doesn't support it.
+ * AVIC is disabled because SEV doesn't support it. Sticky and applied
+ * only to plane 0.
*/
APICV_INHIBIT_REASON_SEV,
@@ -2232,21 +2235,21 @@ gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva,
bool kvm_apicv_activated(struct kvm *kvm);
bool kvm_vcpu_apicv_activated(struct kvm_vcpu *vcpu);
void __kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu);
-void __kvm_set_or_clear_apicv_inhibit(struct kvm *kvm,
+void __kvm_set_or_clear_apicv_inhibit(struct kvm_plane *plane,
enum kvm_apicv_inhibit reason, bool set);
-void kvm_set_or_clear_apicv_inhibit(struct kvm *kvm,
+void kvm_set_or_clear_apicv_inhibit(struct kvm_plane *plane,
enum kvm_apicv_inhibit reason, bool set);
-static inline void kvm_set_apicv_inhibit(struct kvm *kvm,
+static inline void kvm_set_apicv_inhibit(struct kvm_plane *plane,
enum kvm_apicv_inhibit reason)
{
- kvm_set_or_clear_apicv_inhibit(kvm, reason, true);
+ kvm_set_or_clear_apicv_inhibit(plane, reason, true);
}
-static inline void kvm_clear_apicv_inhibit(struct kvm *kvm,
+static inline void kvm_clear_apicv_inhibit(struct kvm_plane *plane,
enum kvm_apicv_inhibit reason)
{
- kvm_set_or_clear_apicv_inhibit(kvm, reason, false);
+ kvm_set_or_clear_apicv_inhibit(plane, reason, false);
}
int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code,
@@ -2360,8 +2363,8 @@ void kvm_make_scan_ioapic_request(struct kvm *kvm);
void kvm_make_scan_ioapic_request_mask(struct kvm *kvm,
unsigned long *vcpu_bitmap);
-static inline void kvm_arch_init_plane(struct kvm_plane *plane) {}
-static inline void kvm_arch_free_plane(struct kvm_plane *plane) {}
+void kvm_arch_init_plane(struct kvm_plane *plane);
+void kvm_arch_free_plane(struct kvm_plane *plane);
bool kvm_arch_async_page_not_present(struct kvm_vcpu *vcpu,
struct kvm_async_pf *work);
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index c6592e7f40a2..a522b467be48 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -145,7 +145,7 @@ static void synic_update_vector(struct kvm_vcpu_hv_synic *synic,
* Inhibit APICv if any vCPU is using SynIC's AutoEOI, which relies on
* the hypervisor to manually inject IRQs.
*/
- __kvm_set_or_clear_apicv_inhibit(vcpu->kvm,
+ __kvm_set_or_clear_apicv_inhibit(vcpu_to_plane(vcpu),
APICV_INHIBIT_REASON_HYPERV,
!!hv->synic_auto_eoi_used);
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c
index e3a3e7b90c26..ded1a9565c36 100644
--- a/arch/x86/kvm/i8254.c
+++ b/arch/x86/kvm/i8254.c
@@ -306,13 +306,13 @@ void kvm_pit_set_reinject(struct kvm_pit *pit, bool reinject)
* So, deactivate APICv when PIT is in reinject mode.
*/
if (reinject) {
- kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_PIT_REINJ);
+ kvm_set_apicv_inhibit(kvm->planes[0], APICV_INHIBIT_REASON_PIT_REINJ);
/* The initial state is preserved while ps->reinject == 0. */
kvm_pit_reset_reinject(pit);
kvm_register_irq_ack_notifier(kvm, &ps->irq_ack_notifier);
kvm_register_irq_mask_notifier(kvm, 0, &pit->mask_notifier);
} else {
- kvm_clear_apicv_inhibit(kvm, APICV_INHIBIT_REASON_PIT_REINJ);
+ kvm_clear_apicv_inhibit(kvm->planes[0], APICV_INHIBIT_REASON_PIT_REINJ);
kvm_unregister_irq_ack_notifier(kvm, &ps->irq_ack_notifier);
kvm_unregister_irq_mask_notifier(kvm, 0, &pit->mask_notifier);
}
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index c078269f7b1d..4077c8d1e37e 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -377,6 +377,7 @@ enum {
static void kvm_recalculate_apic_map(struct kvm *kvm)
{
+ struct kvm_plane *plane = kvm->planes[0];
struct kvm_apic_map *new, *old = NULL;
struct kvm_vcpu *vcpu;
unsigned long i;
@@ -456,19 +457,19 @@ static void kvm_recalculate_apic_map(struct kvm *kvm)
* map also applies to APICv.
*/
if (!new)
- kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED);
+ kvm_set_apicv_inhibit(plane, APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED);
else
- kvm_clear_apicv_inhibit(kvm, APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED);
+ kvm_clear_apicv_inhibit(plane, APICV_INHIBIT_REASON_PHYSICAL_ID_ALIASED);
if (!new || new->logical_mode == KVM_APIC_MODE_MAP_DISABLED)
- kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED);
+ kvm_set_apicv_inhibit(plane, APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED);
else
- kvm_clear_apicv_inhibit(kvm, APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED);
+ kvm_clear_apicv_inhibit(plane, APICV_INHIBIT_REASON_LOGICAL_ID_ALIASED);
if (xapic_id_mismatch)
- kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_APIC_ID_MODIFIED);
+ kvm_set_apicv_inhibit(plane, APICV_INHIBIT_REASON_APIC_ID_MODIFIED);
else
- kvm_clear_apicv_inhibit(kvm, APICV_INHIBIT_REASON_APIC_ID_MODIFIED);
+ kvm_clear_apicv_inhibit(plane, APICV_INHIBIT_REASON_APIC_ID_MODIFIED);
old = rcu_dereference_protected(kvm->arch.apic_map,
lockdep_is_held(&kvm->arch.apic_map_lock));
@@ -2630,7 +2631,7 @@ static void __kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value)
if ((value & MSR_IA32_APICBASE_ENABLE) &&
apic->base_address != APIC_DEFAULT_PHYS_BASE) {
- kvm_set_apicv_inhibit(apic->vcpu->kvm,
+ kvm_set_apicv_inhibit(vcpu_to_plane(vcpu),
APICV_INHIBIT_REASON_APIC_BASE_MODIFIED);
}
}
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 827dbe4d2b3b..130d895f1d95 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -458,7 +458,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
INIT_LIST_HEAD(&sev->mirror_vms);
sev->need_init = false;
- kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_SEV);
+ kvm_set_apicv_inhibit(kvm->planes[0], APICV_INHIBIT_REASON_SEV);
return 0;
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index f6a435ff7e2d..917bfe8db101 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -3926,7 +3926,8 @@ static void svm_enable_irq_window(struct kvm_vcpu *vcpu)
* the VM wide AVIC inhibition.
*/
if (!is_guest_mode(vcpu))
- kvm_set_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_IRQWIN);
+ kvm_set_apicv_inhibit(vcpu_to_plane(vcpu),
+ APICV_INHIBIT_REASON_IRQWIN);
svm_set_vintr(svm);
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 653886e6e1c8..382d8ace131f 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -6567,7 +6567,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
smp_wmb();
kvm->arch.irqchip_mode = KVM_IRQCHIP_SPLIT;
kvm->arch.nr_reserved_ioapic_pins = cap->args[0];
- kvm_clear_apicv_inhibit(kvm, APICV_INHIBIT_REASON_ABSENT);
+ kvm_clear_apicv_inhibit(kvm->planes[0], APICV_INHIBIT_REASON_ABSENT);
r = 0;
split_irqchip_unlock:
mutex_unlock(&kvm->lock);
@@ -7109,7 +7109,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
/* Write kvm->irq_routing before enabling irqchip_in_kernel. */
smp_wmb();
kvm->arch.irqchip_mode = KVM_IRQCHIP_KERNEL;
- kvm_clear_apicv_inhibit(kvm, APICV_INHIBIT_REASON_ABSENT);
+ kvm_clear_apicv_inhibit(kvm->planes[0], APICV_INHIBIT_REASON_ABSENT);
create_irqchip_unlock:
mutex_unlock(&kvm->lock);
break;
@@ -9996,14 +9996,18 @@ static void set_or_clear_apicv_inhibit(unsigned long *inhibits,
trace_kvm_apicv_inhibit_changed(reason, set, *inhibits);
}
-static void kvm_apicv_init(struct kvm *kvm)
+static void kvm_apicv_init(struct kvm *kvm, unsigned long *apicv_inhibit_reasons)
{
- enum kvm_apicv_inhibit reason = enable_apicv ? APICV_INHIBIT_REASON_ABSENT :
- APICV_INHIBIT_REASON_DISABLED;
+ enum kvm_apicv_inhibit reason;
- set_or_clear_apicv_inhibit(&kvm->arch.apicv_inhibit_reasons, reason, true);
+ if (!enable_apicv)
+ reason = APICV_INHIBIT_REASON_DISABLED;
+ else if (!irqchip_kernel(kvm))
+ reason = APICV_INHIBIT_REASON_ABSENT;
+ else
+ return;
- init_rwsem(&kvm->arch.apicv_update_lock);
+ set_or_clear_apicv_inhibit(apicv_inhibit_reasons, reason, true);
}
static void kvm_sched_yield(struct kvm_vcpu *vcpu, unsigned long dest_id)
@@ -10633,10 +10637,22 @@ static void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu)
__kvm_vcpu_update_apicv(vcpu);
}
-void __kvm_set_or_clear_apicv_inhibit(struct kvm *kvm,
+static bool kvm_compute_apicv_inhibit(struct kvm *kvm,
+ enum kvm_apicv_inhibit reason)
+{
+ int i;
+ for (i = 0; i < KVM_MAX_VCPU_PLANES; i++)
+ if (test_bit(reason, &kvm->planes[i]->arch.apicv_inhibit_reasons))
+ return true;
+
+ return false;
+}
+
+void __kvm_set_or_clear_apicv_inhibit(struct kvm_plane *plane,
enum kvm_apicv_inhibit reason, bool set)
{
- unsigned long old, new;
+ struct kvm *kvm = plane->kvm;
+ unsigned long local, global;
bool changed;
lockdep_assert_held_write(&kvm->arch.apicv_update_lock);
@@ -10644,9 +10660,24 @@ void __kvm_set_or_clear_apicv_inhibit(struct kvm *kvm,
if (!(kvm_x86_ops.required_apicv_inhibits & BIT(reason)))
return;
- old = new = kvm->arch.apicv_inhibit_reasons;
- set_or_clear_apicv_inhibit(&new, reason, set);
- changed = (!!old != !!new);
+ local = plane->arch.apicv_inhibit_reasons;
+ set_or_clear_apicv_inhibit(&local, reason, set);
+
+ /* Could this flip change the global state? */
+ global = kvm->arch.apicv_inhibit_reasons;
+ if ((local & BIT(reason)) == (global & BIT(reason))) {
+ /* Easy case 1, the bit is now the same as for the whole VM. */
+ changed = false;
+ } else if (set) {
+ /* Easy case 2, maybe the bit flipped globally from clear to set? */
+ changed = !global;
+ set_or_clear_apicv_inhibit(&global, reason, set);
+ } else {
+ /* Harder case, check if no other plane had this inhibit. */
+ set = kvm_compute_apicv_inhibit(kvm, reason);
+ set_or_clear_apicv_inhibit(&global, reason, set);
+ changed = !global;
+ }
if (changed) {
/*
@@ -10664,7 +10695,8 @@ void __kvm_set_or_clear_apicv_inhibit(struct kvm *kvm,
kvm_make_all_cpus_request(kvm, KVM_REQ_APICV_UPDATE);
}
- kvm->arch.apicv_inhibit_reasons = new;
+ plane->arch.apicv_inhibit_reasons = local;
+ kvm->arch.apicv_inhibit_reasons = global;
if (changed && set) {
unsigned long gfn = gpa_to_gfn(APIC_DEFAULT_PHYS_BASE);
@@ -10675,14 +10707,17 @@ void __kvm_set_or_clear_apicv_inhibit(struct kvm *kvm,
}
}
-void kvm_set_or_clear_apicv_inhibit(struct kvm *kvm,
+void kvm_set_or_clear_apicv_inhibit(struct kvm_plane *plane,
enum kvm_apicv_inhibit reason, bool set)
{
+ struct kvm *kvm;
+
if (!enable_apicv)
return;
+ kvm = plane->kvm;
down_write(&kvm->arch.apicv_update_lock);
- __kvm_set_or_clear_apicv_inhibit(kvm, reason, set);
+ __kvm_set_or_clear_apicv_inhibit(plane, reason, set);
up_write(&kvm->arch.apicv_update_lock);
}
EXPORT_SYMBOL_GPL(kvm_set_or_clear_apicv_inhibit);
@@ -12083,24 +12118,26 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
return ret;
}
-static void kvm_arch_vcpu_guestdbg_update_apicv_inhibit(struct kvm *kvm)
+static void kvm_arch_vcpu_guestdbg_update_apicv_inhibit(struct kvm_plane *plane)
{
bool set = false;
+ struct kvm *kvm;
struct kvm_vcpu *vcpu;
unsigned long i;
if (!enable_apicv)
return;
+ kvm = plane->kvm;
down_write(&kvm->arch.apicv_update_lock);
- kvm_for_each_vcpu(i, vcpu, kvm) {
+ kvm_for_each_plane_vcpu(i, vcpu, plane) {
if (vcpu->guest_debug & KVM_GUESTDBG_BLOCKIRQ) {
set = true;
break;
}
}
- __kvm_set_or_clear_apicv_inhibit(kvm, APICV_INHIBIT_REASON_BLOCKIRQ, set);
+ __kvm_set_or_clear_apicv_inhibit(plane, APICV_INHIBIT_REASON_BLOCKIRQ, set);
up_write(&kvm->arch.apicv_update_lock);
}
@@ -12156,7 +12193,7 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
kvm_x86_call(update_exception_bitmap)(vcpu);
- kvm_arch_vcpu_guestdbg_update_apicv_inhibit(vcpu->kvm);
+ kvm_arch_vcpu_guestdbg_update_apicv_inhibit(vcpu_to_plane(vcpu));
r = 0;
@@ -12732,6 +12769,11 @@ void kvm_arch_free_vm(struct kvm *kvm)
}
+void kvm_arch_init_plane(struct kvm_plane *plane)
+{
+ kvm_apicv_init(plane->kvm, &plane->arch.apicv_inhibit_reasons);
+}
+
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
{
int ret;
@@ -12767,6 +12809,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
set_bit(KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID,
&kvm->arch.irq_sources_bitmap);
+ init_rwsem(&kvm->arch.apicv_update_lock);
raw_spin_lock_init(&kvm->arch.tsc_write_lock);
mutex_init(&kvm->arch.apic_map_lock);
seqcount_raw_spinlock_init(&kvm->arch.pvclock_sc, &kvm->arch.tsc_write_lock);
@@ -12789,7 +12832,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
- kvm_apicv_init(kvm);
+ kvm_apicv_init(kvm, &kvm->arch.apicv_inhibit_reasons);
kvm_hv_init_vm(kvm);
kvm_xen_init_vm(kvm);
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 152dc5845309..5cade1c04646 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -943,7 +943,7 @@ static inline struct kvm_plane *vcpu_to_plane(struct kvm_vcpu *vcpu)
#else
static inline struct kvm_plane *vcpu_to_plane(struct kvm_vcpu *vcpu)
{
- return vcpu->kvm->planes[vcpu->plane_id];
+ return vcpu->kvm->planes[vcpu->plane];
}
#endif
--
2.49.0
Powered by blists - more mailing lists