The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=5f78d024695b39208a6c92f6a96017bae53cdf2c
commit 5f78d024695b39208a6c92f6a96017bae53cdf2c Author: Kevin Bowling <[email protected]> AuthorDate: 2026-08-27 06:22:56 +0000 Commit: Kevin Bowling <[email protected]> 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.
