git: 5f78d024695b - main - acpi_pci: Cache PCI proximity domains

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Thu, 27 Aug 2026 22:50:43 UTC
The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=5f78d024695b39208a6c92f6a96017bae53cdf2c

commit 5f78d024695b39208a6c92f6a96017bae53cdf2c
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-27 06:22:56 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-27 22:49:56 +0000

    acpi_pci: Cache PCI proximity domains
    
    A PCI function's _PXM is stable for the lifetime of its device
    instance, but CPU and DMA locality queries may evaluate it repeatedly.
    SR-IOV amplifies this because every VF resolves locality through the
    same PF.
    
    Cache successful mappings and the stable absence of _PXM on the
    locality source device, and share that result between CPU and domain
    queries. Continue to retry generic evaluation or mapping errors rather
    than making a potentially transient failure permanent.
    
    Reviewed by:    jhb
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
    Differential Revision:  https://reviews.freebsd.org/D59207
---
 sys/dev/acpica/acpi.c     | 22 +++++++++++++++-------
 sys/dev/acpica/acpi_pci.c | 37 ++++++++++++++++++++++++++++++++-----
 sys/dev/acpica/acpivar.h  |  3 +++
 3 files changed, 50 insertions(+), 12 deletions(-)

diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
index 5469df01fd35..8b897defe6ce 100644
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -1402,20 +1402,19 @@ acpi_pxm_parse(device_t dev)
 }
 
 int
-acpi_get_cpus(device_t dev, device_t child, enum cpu_sets op, size_t setsize,
-    cpuset_t *cpuset)
+acpi_get_cpus_for_domain(device_t dev, device_t child, int domain,
+    enum cpu_sets op, size_t setsize, cpuset_t *cpuset)
 {
-	int d, error;
+	int error;
 
-	d = acpi_pxm_parse(child);
-	if (d < 0)
+	if (domain < 0)
 		return (bus_generic_get_cpus(dev, child, op, setsize, cpuset));
 
 	switch (op) {
 	case LOCAL_CPUS:
 		if (setsize != sizeof(cpuset_t))
 			return (EINVAL);
-		*cpuset = cpuset_domain[d];
+		*cpuset = cpuset_domain[domain];
 		return (0);
 	case INTR_CPUS:
 		error = bus_generic_get_cpus(dev, child, op, setsize, cpuset);
@@ -1423,13 +1422,22 @@ acpi_get_cpus(device_t dev, device_t child, enum cpu_sets op, size_t setsize,
 			return (error);
 		if (setsize != sizeof(cpuset_t))
 			return (EINVAL);
-		CPU_AND(cpuset, cpuset, &cpuset_domain[d]);
+		CPU_AND(cpuset, cpuset, &cpuset_domain[domain]);
 		return (0);
 	default:
 		return (bus_generic_get_cpus(dev, child, op, setsize, cpuset));
 	}
 }
 
+int
+acpi_get_cpus(device_t dev, device_t child, enum cpu_sets op, size_t setsize,
+    cpuset_t *cpuset)
+{
+
+	return (acpi_get_cpus_for_domain(dev, child, acpi_pxm_parse(child), op,
+	    setsize, cpuset));
+}
+
 static int
 acpi_get_domain_method(device_t dev, device_t child, int *domain)
 {
diff --git a/sys/dev/acpica/acpi_pci.c b/sys/dev/acpica/acpi_pci.c
index 8b6aeb69f593..1f04de54ac44 100644
--- a/sys/dev/acpica/acpi_pci.c
+++ b/sys/dev/acpica/acpi_pci.c
@@ -61,8 +61,12 @@ struct acpi_pci_devinfo {
 	struct pci_devinfo	ap_dinfo;
 	ACPI_HANDLE		ap_handle;
 	int			ap_flags;
+	int			ap_domain;
 };
 
+/* acpi_pxm_parse() returns -2, -1, or a non-negative domain. */
+#define	ACPI_PCI_DOMAIN_UNSET	(-3)
+
 ACPI_SERIAL_DECL(pci_powerstate, "ACPI PCI power methods");
 
 /* Be sure that ACPI and PCI power states are equivalent. */
@@ -132,6 +136,7 @@ acpi_pci_alloc_devinfo(device_t dev)
 	struct acpi_pci_devinfo *dinfo;
 
 	dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
+	dinfo->ap_domain = ACPI_PCI_DOMAIN_UNSET;
 	return (&dinfo->ap_dinfo);
 }
 
@@ -217,16 +222,38 @@ acpi_pci_get_locality_device(device_t child)
 	return (pf != NULL ? pf : child);
 }
 
+/* Cache locality on its source device; all of a PF's VFs share its result. */
+static int
+acpi_pci_get_locality_domain(device_t child)
+{
+	struct acpi_pci_devinfo *dinfo;
+	device_t locality;
+	int domain;
+
+	locality = acpi_pci_get_locality_device(child);
+	dinfo = device_get_ivars(locality);
+	domain = dinfo->ap_domain;
+	if (domain == ACPI_PCI_DOMAIN_UNSET) {
+		domain = acpi_pxm_parse(locality);
+		/* Do not make a generic evaluation or mapping error permanent. */
+		if (domain != -1)
+			dinfo->ap_domain = domain;
+	}
+	return (domain);
+}
+
 static int
 acpi_pci_get_cpus(device_t dev, device_t child, enum cpu_sets op,
     size_t setsize, cpuset_t *cpuset)
 {
+	device_t locality;
 
 	/* BUS_GET_CPUS may preserve a descendant below the PCI function. */
 	if (device_get_parent(child) != dev)
 		return (acpi_get_cpus(dev, child, op, setsize, cpuset));
-	child = acpi_pci_get_locality_device(child);
-	return (acpi_get_cpus(dev, child, op, setsize, cpuset));
+	locality = acpi_pci_get_locality_device(child);
+	return (acpi_get_cpus_for_domain(dev, locality,
+	    acpi_pci_get_locality_domain(locality), op, setsize, cpuset));
 }
 
 /*
@@ -242,8 +269,7 @@ acpi_pci_get_domain(device_t dev, device_t child, int *domain)
 {
 	int d;
 
-	child = acpi_pci_get_locality_device(child);
-	d = acpi_pxm_parse(child);
+	d = acpi_pci_get_locality_domain(child);
 	if (d >= 0) {
 		*domain = d;
 		return (0);
@@ -252,7 +278,8 @@ acpi_pci_get_domain(device_t dev, device_t child, int *domain)
 		return (ENOENT);
 
 	/* No _PXM node; go up a level */
-	return (bus_generic_get_domain(dev, child, domain));
+	return (bus_generic_get_domain(dev,
+	    acpi_pci_get_locality_device(child), domain));
 }
 
 /*
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
index 27300a00b8d1..028b39637a45 100644
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -603,6 +603,9 @@ void		acpi_pxm_set_mem_locality(void);
 void		acpi_pxm_set_cpu_locality(void);
 int		acpi_pxm_get_cpu_locality(int apic_id);
 int		acpi_pxm_parse(device_t dev);
+int		acpi_get_cpus_for_domain(device_t dev, device_t child,
+		    int domain, enum cpu_sets op, size_t setsize,
+		    cpuset_t *cpuset);
 
 /*
  * Map a PXM to a VM domain.