Add initial memory locality cost awareness to the VM, and include

a basic ACPI SLIT table parser.

For now this just exports the map via sysctl; it'll eventually be useful
to userland when there's more useful NUMA support in -HEAD.

* Add an optional mem_locality map;
* add a mapping function taking from/to domain and returning the
  relative cost, or -1 if it's not available;
* Add a very basic SLIT parser to x86 ACPI.

Differential Revision:	https://reviews.freebsd.org/D2460
Reviewed by:	rpaulo, stas, jhb
Sponsored by:	Norse Corp, Inc (hardware, coding); Dell (hardware)
This commit is contained in:
Adrian Chadd 2015-05-08 00:56:56 +00:00
parent 8c54dbfb9b
commit 415d7ccab2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=282617
3 changed files with 168 additions and 6 deletions

View File

@ -71,6 +71,7 @@ _Static_assert(sizeof(long) * NBBY >= VM_PHYSSEG_MAX,
"Too many physsegs.");
struct mem_affinity *mem_affinity;
int *mem_locality;
int vm_ndomains = 1;
@ -140,6 +141,10 @@ static int sysctl_vm_phys_segs(SYSCTL_HANDLER_ARGS);
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,48 @@ sysctl_vm_phys_segs(SYSCTL_HANDLER_ARGS)
return (error);
}
/*
* Return affinity, or -1 if there's no affinity information.
*/
static int
vm_phys_mem_affinity(int f, int t)
{
if (mem_locality == NULL)
return (-1);
if (f >= vm_ndomains || t >= vm_ndomains)
return (-1);
return (mem_locality[f * vm_ndomains + t]);
}
/*
* 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)
{

View File

@ -61,6 +61,7 @@ struct vm_phys_seg {
};
extern struct mem_affinity *mem_affinity;
int *mem_locality;
extern int vm_ndomains;
extern struct vm_phys_seg vm_phys_segs[];
extern int vm_phys_nsegs;

View File

@ -64,8 +64,96 @@ static vm_paddr_t srat_physaddr;
static int vm_domains[VM_PHYSSEG_MAX];
static ACPI_TABLE_SLIT *slit;
static vm_paddr_t slit_physaddr;
static int vm_locality_table[MAXMEMDOM * MAXMEMDOM];
static void srat_walk_table(acpi_subtable_handler *handler, void *arg);
/*
* SLIT parsing.
*/
static void
slit_parse_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);
/* 255 == "no locality information" */
if (e == 255)
vm_locality_table[offset] = -1;
else
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)
{
if (resource_disabled("slit", 0)) {
return (-1);
}
slit_physaddr = acpi_find_table(ACPI_SIG_SLIT);
if (slit_physaddr == 0) {
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);
slit_parse_table(slit);
acpi_unmap_table(slit);
slit = NULL;
/* 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 +389,17 @@ renumber_domains(void)
/*
* 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 +413,39 @@ parse_srat(void *dummy)
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);
}
SYSINIT(parse_srat, SI_SUB_VM - 1, SI_ORDER_FIRST, parse_srat, NULL);
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] = -1;
}
static void
parse_acpi_tables(void *dummy)
{
if (parse_srat() < 0)
return;
init_mem_locality();
(void) parse_slit();
}
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)