diff --git a/drivers/base/node.c b/drivers/base/node.c index 4014a98be61dc32d7f246eb7d774cc5d538454b9..aa6a7cee0ac13f6806bb22604ae8116f185b4248 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -558,6 +558,16 @@ static ssize_t type_show(struct device *dev, } static DEVICE_ATTR_RO(type); +static ssize_t peer_node_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nid = dev->id; + struct pglist_data *pgdat = NODE_DATA(nid); + + return sprintf(buf, "%d\n", pgdat->peer_node); +} +static DEVICE_ATTR_RO(peer_node); + static struct attribute *node_dev_attrs[] = { &dev_attr_cpumap.attr, &dev_attr_cpulist.attr, @@ -566,6 +576,7 @@ static struct attribute *node_dev_attrs[] = { &dev_attr_distance.attr, &dev_attr_vmstat.attr, &dev_attr_type.attr, + &dev_attr_peer_node.attr, NULL }; ATTRIBUTE_GROUPS(node_dev); diff --git a/include/linux/mm.h b/include/linux/mm.h index 5106db3ad1ce34d6e368ea6031eca1489f3e5cf0..b370c9bb9f89499db89953828254c18b14592bd0 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2414,6 +2414,7 @@ static inline unsigned long get_num_physpages(void) return phys_pages; } +int find_best_peer_node(int nid); /* * Using memblock node mappings, an architecture may initialise its * zones, allocate the backing mem_map and account for memory holes in an diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 9d0c454d23cd626107aecfd184f1e446f4d763b1..f179f07a88ff3612d6921e0c5683e0b3946cb680 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -806,6 +806,7 @@ typedef struct pglist_data { /* Per-node vmstats */ struct per_cpu_nodestat __percpu *per_cpu_nodestats; atomic_long_t vm_stat[NR_VM_NODE_STAT_ITEMS]; + int peer_node; } pg_data_t; #define node_present_pages(nid) (NODE_DATA(nid)->node_present_pages) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 0cba3f02eb3c2980ceeb687b76c12b34437463b5..6f90e717e331874d128867231367d5a54aaf163f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -7400,6 +7400,34 @@ static void check_for_memory(pg_data_t *pgdat, int nid) } } +/* + * Return the nearest peer node in terms of *locality* + * E.g. peer of node 0 is node 2 per SLIT + * node distances: + * node 0 1 2 3 + * 0: 10 21 17 28 + * 1: 21 10 28 17 + * 2: 17 28 10 28 + * 3: 28 17 28 10 + */ +int find_best_peer_node(int nid) +{ + int n, val; + int min_val = INT_MAX; + int peer = NUMA_NO_NODE; + + for_each_online_node(n) { + if (n == nid) + continue; + val = node_distance(nid, n); + if (val < min_val) { + min_val = val; + peer = n; + } + } + return peer; +} + /* * Some architecturs, e.g. ARC may have ZONE_HIGHMEM below ZONE_NORMAL. For * such cases we allow max_zone_pfn sorted in the descending order @@ -7506,6 +7534,7 @@ void __init free_area_init(unsigned long *max_zone_pfn) if (pgdat->node_present_pages) node_set_state(nid, N_MEMORY); check_for_memory(pgdat, nid); + pgdat->peer_node = find_best_peer_node(nid); } }