Index: sys/vm/vm_phys.h =================================================================== --- sys/vm/vm_phys.h +++ sys/vm/vm_phys.h @@ -61,6 +61,7 @@ }; extern struct mem_affinity *mem_affinity; +extern uint8_t *mem_locality; extern int vm_ndomains; extern struct vm_phys_seg vm_phys_segs[]; extern int vm_phys_nsegs; Index: sys/vm/vm_phys.c =================================================================== --- sys/vm/vm_phys.c +++ sys/vm/vm_phys.c @@ -71,6 +71,7 @@ "Too many physsegs."); struct mem_affinity *mem_affinity; +uint8_t *mem_locality = NULL; int vm_ndomains = 1; @@ -140,6 +141,10 @@ SYSCTL_OID(_vm, OID_AUTO, phys_segs, CTLTYPE_STRING | CTLFLAG_RD, NULL, 0, sysctl_vm_phys_segs, "A", "Phys Seg Info"); +static int sysctl_vm_phys_locality(SYSCTL_HANDLER_ARGS); +SYSCTL_OID(_vm, OID_AUTO, phys_locality, CTLTYPE_STRING | CTLFLAG_RD, + NULL, 0, sysctl_vm_phys_locality, "A", "Phys Locality Info"); + SYSCTL_INT(_vm, OID_AUTO, ndomains, CTLFLAG_RD, &vm_ndomains, 0, "Number of physical memory domains available."); @@ -297,6 +302,52 @@ return (error); } +/* + * Return affinity, or -1 if there's no affinity information. + */ +static int +vm_phys_mem_affinity(int f, int t) +{ + uint8_t v; + + if (mem_locality == NULL) + return (-1); + if (f >= vm_ndomains || t >= vm_ndomains) + return (-1); + v = mem_locality[f * vm_ndomains + t]; + if (v == 255) + return (-1); + return (v);; +} + +/* + * Outputs the VM locality table. + */ +static int +sysctl_vm_phys_locality(SYSCTL_HANDLER_ARGS) +{ + struct sbuf sbuf; + int error, i, j; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + return (error); + sbuf_new_for_sysctl(&sbuf, NULL, 128, req); + + sbuf_printf(&sbuf, "\n"); + + for (i = 0; i < vm_ndomains; i++) { + sbuf_printf(&sbuf, "%d: ", i); + for (j = 0; j < vm_ndomains; j++) { + sbuf_printf(&sbuf, "%d ", vm_phys_mem_affinity(i, j)); + } + sbuf_printf(&sbuf, "\n"); + } + error = sbuf_finish(&sbuf); + sbuf_delete(&sbuf); + return (error); +} + static void vm_freelist_add(struct vm_freelist *fl, vm_page_t m, int order, int tail) { Index: sys/x86/acpica/srat.c =================================================================== --- sys/x86/acpica/srat.c +++ sys/x86/acpica/srat.c @@ -67,6 +67,101 @@ static void srat_walk_table(acpi_subtable_handler *handler, void *arg); /* + * SLIT parsing. + */ + +static ACPI_TABLE_SLIT *slit; +static vm_paddr_t slit_physaddr; +static uint8_t vm_locality_table[MAXMEMDOM * MAXMEMDOM]; + +static void +slit_walk_table(ACPI_TABLE_SLIT *s) +{ + int i, j; + int i_domain, j_domain; + int offset = 0; + uint8_t e; + + /* + * This maps the SLIT data into the VM-domain centric view. + * There may be sparse entries in the PXM namespace, so + * remap them to a VM-domain ID and if it doesn't exist, + * skip it. + * + * It should result in a packed 2d array of VM-domain + * locality information entries. + */ + + if (bootverbose) + printf("SLIT.Localities: %d\n", (int) s->LocalityCount); + for (i = 0; i < s->LocalityCount; i++) { + i_domain = acpi_map_pxm_to_vm_domainid(i); + if (i_domain < 0) + continue; + + if (bootverbose) + printf("%d: ", i); + for (j = 0; j < s->LocalityCount; j++) { + j_domain = acpi_map_pxm_to_vm_domainid(j); + if (j_domain < 0) + continue; + e = s->Entry[i * s->LocalityCount + j]; + if (bootverbose) + printf("%d ", (int) e); + vm_locality_table[offset] = e; + offset++; + } + if (bootverbose) + printf("\n"); + } +} + +/* + * Look for an ACPI System Locality Distance Information Table ("SLIT") + */ +static int +parse_slit(void) +{ + int error; + + printf("%s: called\n", __func__); + + if (resource_disabled("slit", 0)) { + printf("%s: resource disabled?\n", __func__); + return (-1); + } + + slit_physaddr = acpi_find_table(ACPI_SIG_SLIT); + if (slit_physaddr == 0) { + printf("%s: physaddr=0\n", __func__); + return (-1); + } + + /* + * Make a pass over the table to populate the cpus[] and + * mem_info[] tables. + */ + slit = acpi_map_table(slit_physaddr, ACPI_SIG_SLIT); + error = 0; + slit_walk_table(slit); + acpi_unmap_table(slit); + slit = NULL; + if (error) { + printf("%s: error mapping table\n", __func__); + slit_physaddr = 0; + return (-1); + } + + /* Tell the VM about it! */ + mem_locality = vm_locality_table; + return (0); +} + +/* + * SRAT parsing. + */ + +/* * Returns true if a memory range overlaps with at least one range in * phys_avail[]. */ @@ -301,17 +396,17 @@ /* * Look for an ACPI System Resource Affinity Table ("SRAT") */ -static void -parse_srat(void *dummy) +static int +parse_srat(void) { int error; if (resource_disabled("srat", 0)) - return; + return (-1); srat_physaddr = acpi_find_table(ACPI_SIG_SRAT); if (srat_physaddr == 0) - return; + return (-1); /* * Make a pass over the table to populate the cpus[] and @@ -325,13 +420,40 @@ if (error || check_domains() != 0 || check_phys_avail() != 0 || renumber_domains() != 0) { srat_physaddr = 0; - return; + return (-1); } /* Point vm_phys at our memory affinity table. */ mem_affinity = mem_info; + + return (0); +} + +static void +init_mem_locality(void) +{ + int i; + + /* + * For now, assume 255 == "no locality information for + * this pairing. + */ + for (i = 0; i < (MAXMEMDOM * MAXMEMDOM); i++) + vm_locality_table[i] = 255; +} + +static void +parse_acpi_tables(void *dummy) +{ + + if (parse_srat() < 0) + return; + init_mem_locality(); + (void) parse_slit(); } -SYSINIT(parse_srat, SI_SUB_VM - 1, SI_ORDER_FIRST, parse_srat, NULL); + +SYSINIT(parse_acpi_tables, SI_SUB_VM - 1, SI_ORDER_FIRST, parse_acpi_tables, + NULL); static void srat_walk_table(acpi_subtable_handler *handler, void *arg)