[PATCH v2 17/18] x86/numa: Provide a range-to-target_node lookup facility
From: Dan Williams
Date: Sun Nov 17 2019 - 13:00:39 EST
The DEV_DAX_KMEM facility is a generic mechanism to allow device-dax
instances, fronting performance-differentiated-memory like pmem, to be
added to the System RAM pool. The numa node for that hot-added memory is
derived from the device-dax instance's 'target_node' attribute.
Recall that the 'target_node' is the ACPI-PXM-to-node translation for
memory when it comes online whereas the 'numa_node' attribute of the
device represents the closest online cpu node.
Presently useful target_node information from the ACPI SRAT is discarded
with the expectation that "Reserved" memory will never be onlined. Now,
DEV_DAX_KMEM violates that assumption, there is a need to retain the
translation. Move, rather than discard, numa_memblk data to a secondary
array that memory_add_physaddr_to_target_node() may consider at a later
point in time.
Note that memory_add_physaddr_to_nid() is currently only available on
CONFIG_MEMORY_HOTPLUG=y platforms whereas the target node information
may be useful on CONFIG_MEMORY_HOTPLUG=n builds, hence why it is calling
phys_to_target_node() and optionally defined by asm/io.h rather than a
memory_add_physaddr_to_target_nid() helper that lives in
include/linux/memory_hotplug.h.
Cc: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
Cc: Andy Lutomirski <luto@xxxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Borislav Petkov <bp@xxxxxxxxx>
Cc: "H. Peter Anvin" <hpa@xxxxxxxxx>
Cc: <x86@xxxxxxxxxx>
Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc: David Hildenbrand <david@xxxxxxxxxx>
Cc: Michal Hocko <mhocko@xxxxxxxx>
Reported-by: kbuild test robot <lkp@xxxxxxxxx>
Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
---
arch/x86/mm/numa.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++---
include/linux/numa.h | 8 +++++
mm/mempolicy.c | 5 +++
3 files changed, 83 insertions(+), 6 deletions(-)
diff --git a/arch/x86/mm/numa.c b/arch/x86/mm/numa.c
index 4123100e0eaf..f4f02ac0c465 100644
--- a/arch/x86/mm/numa.c
+++ b/arch/x86/mm/numa.c
@@ -31,6 +31,24 @@ __initdata
#endif
;
+/*
+ * Presently, DEV_DAX_KMEM is the only kernel facility that might
+ * convert Reserved or Soft Reserved memory to System RAM.
+ */
+#if IS_ENABLED(CONFIG_DEV_DAX_KMEM)
+static struct numa_meminfo __numa_reserved_meminfo;
+
+static struct numa_meminfo *numa_reserved_meminfo(void)
+{
+ return &__numa_reserved_meminfo;
+}
+#else
+static struct numa_meminfo *numa_reserved_meminfo(void)
+{
+ return NULL;
+}
+#endif
+
static int numa_distance_cnt;
static u8 *numa_distance;
@@ -168,6 +186,26 @@ void __init numa_remove_memblk_from(int idx, struct numa_meminfo *mi)
(mi->nr_blks - idx) * sizeof(mi->blk[0]));
}
+/**
+ * numa_move_memblk - Move one numa_memblk from one numa_meminfo to another
+ * @dst: numa_meminfo to move block to
+ * @idx: Index of memblk to remove
+ * @src: numa_meminfo to remove memblk from
+ *
+ * If @dst is non-NULL add it at the @dst->nr_blks index and increment
+ * @dst->nr_blks, then remove it from @src.
+ */
+static void __init numa_move_memblk(struct numa_meminfo *dst, int idx,
+ struct numa_meminfo *src)
+{
+ if (dst) {
+ memcpy(&dst->blk[dst->nr_blks], &src->blk[idx],
+ sizeof(struct numa_memblk));
+ dst->nr_blks++;
+ }
+ numa_remove_memblk_from(idx, src);
+}
+
/**
* numa_add_memblk - Add one numa_memblk to numa_meminfo
* @nid: NUMA node ID of the new memblk
@@ -245,7 +283,7 @@ int __init numa_cleanup_meminfo(struct numa_meminfo *mi)
if (bi->start >= bi->end ||
!memblock_overlaps_region(&memblock.memory,
bi->start, bi->end - bi->start))
- numa_remove_memblk_from(i--, mi);
+ numa_move_memblk(numa_reserved_meminfo(), i--, mi);
}
/* merge neighboring / overlapping entries */
@@ -881,16 +919,44 @@ EXPORT_SYMBOL(cpumask_of_node);
#endif /* !CONFIG_DEBUG_PER_CPU_MAPS */
+static int meminfo_to_nid(struct numa_meminfo *mi, u64 start, int *nid)
+{
+ int i;
+
+ for (i = 0; mi && i < mi->nr_blks; i++)
+ if (mi->blk[i].start <= start && mi->blk[i].end > start) {
+ *nid = mi->blk[i].nid;
+ break;
+ }
+ return i;
+}
+
+int phys_to_target_node(phys_addr_t start)
+{
+ struct numa_meminfo *mi = &numa_meminfo;
+ int nid = mi->blk[0].nid;
+ int i = meminfo_to_nid(mi, start, &nid);
+
+ /*
+ * Prefer online nodes, but if reserved memory might be
+ * hot-added continue the search with reserved ranges.
+ */
+ if (i < mi->nr_blks)
+ return nid;
+
+ mi = numa_reserved_meminfo();
+ meminfo_to_nid(mi, start, &nid);
+ return nid;
+}
+EXPORT_SYMBOL_GPL(phys_to_target_node);
+
#ifdef CONFIG_MEMORY_HOTPLUG
int memory_add_physaddr_to_nid(u64 start)
{
struct numa_meminfo *mi = &numa_meminfo;
int nid = mi->blk[0].nid;
- int i;
- for (i = 0; i < mi->nr_blks; i++)
- if (mi->blk[i].start <= start && mi->blk[i].end > start)
- nid = mi->blk[i].nid;
+ meminfo_to_nid(mi, start, &nid);
return nid;
}
EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
diff --git a/include/linux/numa.h b/include/linux/numa.h
index 20f4e44b186c..941790a0765b 100644
--- a/include/linux/numa.h
+++ b/include/linux/numa.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_NUMA_H
#define _LINUX_NUMA_H
-
+#include <linux/types.h>
#ifdef CONFIG_NODES_SHIFT
#define NODES_SHIFT CONFIG_NODES_SHIFT
@@ -15,11 +15,17 @@
#ifdef CONFIG_NUMA
int numa_map_to_online_node(int node);
+int phys_to_target_node(phys_addr_t addr);
#else
static inline int numa_map_to_online_node(int node)
{
return NUMA_NO_NODE;
}
+
+static inline int phys_to_target_node(phys_addr_t addr)
+{
+ return NUMA_NO_NODE;
+}
#endif
#endif /* _LINUX_NUMA_H */
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index d618121bcc17..0db8b446e23e 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2996,3 +2996,8 @@ void mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol)
p += scnprintf(p, buffer + maxlen - p, ":%*pbl",
nodemask_pr_args(&nodes));
}
+
+__weak int phys_to_target_node(phys_addr_t addr)
+{
+ return NUMA_NO_NODE;
+}