]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: x86: Only allow "fast" IPIs in fastpath WRMSR(X2APIC_ICR) handler
authorSean Christopherson <seanjc@google.com>
Tue, 5 Aug 2025 19:05:11 +0000 (12:05 -0700)
committerSean Christopherson <seanjc@google.com>
Tue, 19 Aug 2025 18:59:32 +0000 (11:59 -0700)
Explicitly restrict fastpath ICR writes to IPIs that are "fast", i.e. can
be delivered without having to walk all vCPUs, and that target at most 16
vCPUs.  Artificially restricting ICR writes to physical mode guarantees
at most one vCPU will receive in IPI (because x2APIC IDs are read-only),
but that delivery might not be "fast".  E.g. even if the vCPU exists, KVM
might have to iterate over 4096 vCPUs to find the right one.

Limiting delivery to fast IPIs aligns the WRMSR fastpath with
kvm_arch_set_irq_inatomic() (which also runs with IRQs disabled), and will
allow dropping the semi-arbitrary restrictions on delivery mode and type.

Link: https://lore.kernel.org/r/20250805190526.1453366-4-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/lapic.c
arch/x86/kvm/lapic.h
arch/x86/kvm/x86.c

index 68fda104195cf3225c74cde29934c4fed4ec4f49..252e77c88ae2283946b0ec5a0220f7bd85fd1f1e 100644 (file)
@@ -2432,7 +2432,7 @@ EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi);
 
 #define X2APIC_ICR_RESERVED_BITS (GENMASK_ULL(31, 20) | GENMASK_ULL(17, 16) | BIT(13))
 
-int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
+static int __kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data, bool fast)
 {
        if (data & X2APIC_ICR_RESERVED_BITS)
                return 1;
@@ -2447,7 +2447,20 @@ int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
         */
        data &= ~APIC_ICR_BUSY;
 
-       kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
+       if (fast) {
+               struct kvm_lapic_irq irq;
+               int ignored;
+
+               kvm_icr_to_lapic_irq(apic, (u32)data, (u32)(data >> 32), &irq);
+
+               if (!kvm_irq_delivery_to_apic_fast(apic->vcpu->kvm, apic, &irq,
+                                                  &ignored, NULL))
+                       return -EWOULDBLOCK;
+
+               trace_kvm_apic_ipi((u32)data, irq.dest_id);
+       } else {
+               kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32));
+       }
        if (kvm_x86_ops.x2apic_icr_is_split) {
                kvm_lapic_set_reg(apic, APIC_ICR, data);
                kvm_lapic_set_reg(apic, APIC_ICR2, data >> 32);
@@ -2458,6 +2471,16 @@ int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
        return 0;
 }
 
+static int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data)
+{
+       return __kvm_x2apic_icr_write(apic, data, false);
+}
+
+int kvm_x2apic_icr_write_fast(struct kvm_lapic *apic, u64 data)
+{
+       return __kvm_x2apic_icr_write(apic, data, true);
+}
+
 static u64 kvm_x2apic_icr_read(struct kvm_lapic *apic)
 {
        if (kvm_x86_ops.x2apic_icr_is_split)
index 72de14527698af96664020b2ba1d233722049bde..1b2d408816aaaa6d707d44ee841bb1215948c425 100644 (file)
@@ -137,7 +137,7 @@ int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr);
 void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu);
 void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu);
 
-int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data);
+int kvm_x2apic_icr_write_fast(struct kvm_lapic *apic, u64 data);
 int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data);
 int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
 
index 5dc32f2fe391922c9815e007b844ed64c066c682..1b64c71458a27d5c906e5e51be3b063837a5ff3e 100644 (file)
@@ -2150,7 +2150,7 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data
            ((data & APIC_DEST_MASK) == APIC_DEST_PHYSICAL) &&
            ((data & APIC_MODE_MASK) == APIC_DM_FIXED) &&
            ((u32)(data >> 32) != X2APIC_BROADCAST))
-               return kvm_x2apic_icr_write(vcpu->arch.apic, data);
+               return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data);
 
        return 1;
 }