git: 2450da67764c - main - x86/xen: use x{2}APIC if virtualized by hardware

From: Roger Pau Monné <royger_at_FreeBSD.org>
Date: Tue, 18 Jan 2022 09:19:18 UTC
The branch main has been updated by royger:

URL: https://cgit.FreeBSD.org/src/commit/?id=2450da67764c975868167db49106fb335af19f0d

commit 2450da67764c975868167db49106fb335af19f0d
Author:     Roger Pau Monné <royger@FreeBSD.org>
AuthorDate: 2022-01-13 13:57:07 +0000
Commit:     Roger Pau Monné <royger@FreeBSD.org>
CommitDate: 2022-01-18 09:18:22 +0000

    x86/xen: use x{2}APIC if virtualized by hardware
    
    Instead of using event channels or hypercalls to deal with IPIs and
    NMIs.
    
    Using a hardware virtualized APIC should be faster than using any PV
    interface, since the VM exit can be avoided.
    
    Xen exposes whether the domain is using hardware assisted x{2}APIC
    emulation in a CPUID bit.
    
    Sponsored by: Citrix Systems R&D
---
 sys/x86/include/xen/xen-os.h |  2 ++
 sys/x86/xen/hvm.c            | 16 ++++++++--------
 sys/x86/xen/xen_apic.c       | 13 +++++++++++++
 3 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/sys/x86/include/xen/xen-os.h b/sys/x86/include/xen/xen-os.h
index 655961da22fd..4d4aa64955ba 100644
--- a/sys/x86/include/xen/xen-os.h
+++ b/sys/x86/include/xen/xen-os.h
@@ -49,6 +49,8 @@ extern int xen_disable_pv_disks;
 /* tunable for disabling PV nics */
 extern int xen_disable_pv_nics;
 
+extern uint32_t xen_cpuid_base;
+
 static inline bool
 xen_has_percpu_evtchn(void)
 {
diff --git a/sys/x86/xen/hvm.c b/sys/x86/xen/hvm.c
index 569b113364b1..6eb16c3098fd 100644
--- a/sys/x86/xen/hvm.c
+++ b/sys/x86/xen/hvm.c
@@ -110,7 +110,7 @@ TUNABLE_INT("hw.xen.disable_pv_nics", &xen_disable_pv_nics);
 
 /*---------------------- XEN Hypervisor Probe and Setup ----------------------*/
 
-static uint32_t cpuid_base;
+uint32_t xen_cpuid_base;
 
 static uint32_t
 xen_hvm_cpuid_base(void)
@@ -153,7 +153,7 @@ hypervisor_version(void)
 	uint32_t regs[4];
 	int major, minor;
 
-	do_cpuid(cpuid_base + 1, regs);
+	do_cpuid(xen_cpuid_base + 1, regs);
 
 	major = regs[0] >> 16;
 	minor = regs[0] & 0xffff;
@@ -171,9 +171,9 @@ xen_hvm_init_hypercall_stubs(enum xen_hvm_init_type init_type)
 	uint32_t regs[4];
 
 	/* Legacy PVH will get here without the cpuid leaf being set. */
-	if (cpuid_base == 0)
-		cpuid_base = xen_hvm_cpuid_base();
-	if (cpuid_base == 0)
+	if (xen_cpuid_base == 0)
+		xen_cpuid_base = xen_hvm_cpuid_base();
+	if (xen_cpuid_base == 0)
 		return (ENXIO);
 
 	if (xen_domain() && init_type == XEN_HVM_INIT_LATE) {
@@ -192,7 +192,7 @@ xen_hvm_init_hypercall_stubs(enum xen_hvm_init_type init_type)
 	/*
 	 * Find the hypercall pages.
 	 */
-	do_cpuid(cpuid_base + 2, regs);
+	do_cpuid(xen_cpuid_base + 2, regs);
 	if (regs[0] != 1)
 		return (EINVAL);
 
@@ -448,8 +448,8 @@ xen_hvm_cpu_init(void)
 	 * Set vCPU ID. If available fetch the ID from CPUID, if not just use
 	 * the ACPI ID.
 	 */
-	KASSERT(cpuid_base != 0, ("Invalid base Xen CPUID leaf"));
-	cpuid_count(cpuid_base + 4, 0, regs);
+	KASSERT(xen_cpuid_base != 0, ("Invalid base Xen CPUID leaf"));
+	cpuid_count(xen_cpuid_base + 4, 0, regs);
 	KASSERT((regs[0] & XEN_HVM_CPUID_VCPU_ID_PRESENT) ||
 	    !xen_pv_domain(),
 	    ("Xen PV domain without vcpu_id in cpuid"));
diff --git a/sys/x86/xen/xen_apic.c b/sys/x86/xen/xen_apic.c
index 2efa5a3c2345..c268e747f44a 100644
--- a/sys/x86/xen/xen_apic.c
+++ b/sys/x86/xen/xen_apic.c
@@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
 #include <xen/hvm.h>
 #include <xen/xen_intr.h>
 
+#include <xen/interface/arch-x86/cpuid.h>
 #include <xen/interface/vcpu.h>
 
 /*--------------------------------- Macros -----------------------------------*/
@@ -601,11 +602,23 @@ xen_cpu_ipi_init(int cpu)
 static void
 xen_setup_cpus(void)
 {
+	uint32_t regs[4];
 	int i;
 
 	if (!xen_vector_callback_enabled)
 		return;
 
+	/*
+	 * Check whether the APIC virtualization is hardware assisted, as
+	 * that's faster than using event channels because it avoids the VM
+	 * exit.
+	 */
+	KASSERT(xen_cpuid_base != 0, ("Invalid base Xen CPUID leaf"));
+	cpuid_count(xen_cpuid_base + 4, 0, regs);
+	if ((x2apic_mode && (regs[0] & XEN_HVM_CPUID_X2APIC_VIRT)) ||
+	    (!x2apic_mode && (regs[0] & XEN_HVM_CPUID_APIC_ACCESS_VIRT)))
+		return;
+
 	CPU_FOREACH(i)
 		xen_cpu_ipi_init(i);