[PATCH 14/21] EDAC, ghes: Extract numa node information for each dimm
From: Robert Richter
Date: Wed May 29 2019 - 04:48:23 EST
In a later patch we want to have one mc device per node. This patch
extracts the numa node information for each dimm. This is done by
collecting the physical address ranges from the DMI table (Memory
Array Mapped Address - Type 19 of SMBIOS spec). The node information
for a physical address is already know to a numa aware system (e.g. by
using the ACPI _PXM method or the ACPI SRAT table), so based on the PA
we can assign the node id to the dimms.
A fallback that disables numa is implemented in case the node
information is inconsistent.
E.g., on a ThunderX2 system the following node mappings are found
based on the DMI table:
EDAC DEBUG: mem_info_setup: DIMM0: Found mem range [0x0000008800000000-0x0000009ffcffffff] on node 0
EDAC DEBUG: mem_info_setup: DIMM1: Found mem range [0x0000008800000000-0x0000009ffcffffff] on node 0
EDAC DEBUG: mem_info_setup: DIMM2: Found mem range [0x0000008800000000-0x0000009ffcffffff] on node 0
EDAC DEBUG: mem_info_setup: DIMM3: Found mem range [0x0000008800000000-0x0000009ffcffffff] on node 0
EDAC DEBUG: mem_info_setup: DIMM4: Found mem range [0x0000008800000000-0x0000009ffcffffff] on node 0
EDAC DEBUG: mem_info_setup: DIMM5: Found mem range [0x0000008800000000-0x0000009ffcffffff] on node 0
EDAC DEBUG: mem_info_setup: DIMM6: Found mem range [0x0000008800000000-0x0000009ffcffffff] on node 0
EDAC DEBUG: mem_info_setup: DIMM7: Found mem range [0x0000008800000000-0x0000009ffcffffff] on node 0
EDAC DEBUG: mem_info_setup: DIMM8: Found mem range [0x0000009ffd000000-0x000000bffcffffff] on node 1
EDAC DEBUG: mem_info_setup: DIMM9: Found mem range [0x0000009ffd000000-0x000000bffcffffff] on node 1
EDAC DEBUG: mem_info_setup: DIMM10: Found mem range [0x0000009ffd000000-0x000000bffcffffff] on node 1
EDAC DEBUG: mem_info_setup: DIMM11: Found mem range [0x0000009ffd000000-0x000000bffcffffff] on node 1
EDAC DEBUG: mem_info_setup: DIMM12: Found mem range [0x0000009ffd000000-0x000000bffcffffff] on node 1
EDAC DEBUG: mem_info_setup: DIMM13: Found mem range [0x0000009ffd000000-0x000000bffcffffff] on node 1
EDAC DEBUG: mem_info_setup: DIMM14: Found mem range [0x0000009ffd000000-0x000000bffcffffff] on node 1
EDAC DEBUG: mem_info_setup: DIMM15: Found mem range [0x0000009ffd000000-0x000000bffcffffff] on node 1
Signed-off-by: Robert Richter <rrichter@xxxxxxxxxxx>
---
drivers/edac/ghes_edac.c | 104 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 103 insertions(+), 1 deletion(-)
diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
index 50f4ee36b755..083452a48b42 100644
--- a/drivers/edac/ghes_edac.c
+++ b/drivers/edac/ghes_edac.c
@@ -67,14 +67,34 @@ struct memdev_dmi_entry {
u16 conf_mem_clk_speed;
} __attribute__((__packed__));
+/* Memory Array Mapped Address - Type 19 of SMBIOS spec */
+struct memarr_dmi_entry {
+ u8 type;
+ u8 length;
+ u16 handle;
+ u32 start;
+ u32 end;
+ u16 phys_mem_array_handle;
+ u8 partition_width;
+ u64 ext_start;
+ u64 ext_end;
+} __attribute__((__packed__));
+
struct ghes_dimm_info {
struct dimm_info dimm_info;
int idx;
+ int numa_node;
+ phys_addr_t start;
+ phys_addr_t end;
+ u16 phys_handle;
};
struct ghes_mem_info {
- int num_dimm;
+ int num_dimm;
struct ghes_dimm_info *dimms;
+ int num_nodes;
+ int num_per_node[MAX_NUMNODES];
+ bool enable_numa;
};
struct ghes_mem_info mem_info;
@@ -97,10 +117,50 @@ static void ghes_dimm_info_init(void)
for_each_dimm(dimm) {
dimm->idx = idx;
+ dimm->numa_node = NUMA_NO_NODE;
idx++;
}
}
+static void ghes_edac_set_nid(const struct dmi_header *dh, void *arg)
+{
+ struct memarr_dmi_entry *entry = (struct memarr_dmi_entry *)dh;
+ struct ghes_dimm_info *dimm;
+ phys_addr_t start, end;
+ int nid;
+
+ if (dh->type != DMI_ENTRY_MEM_ARRAY_MAPPED_ADDR)
+ return;
+
+ /* only support SMBIOS 2.7+ */
+ if (entry->length < sizeof(*entry))
+ return;
+
+ if (entry->start == 0xffffffff)
+ start = entry->ext_start;
+ else
+ start = entry->start;
+ if (entry->end == 0xffffffff)
+ end = entry->ext_end;
+ else
+ end = entry->end;
+
+ if (!pfn_valid(PHYS_PFN(start)))
+ return;
+
+ nid = pfn_to_nid(PHYS_PFN(start));
+ if (nid < 0 || nid >= MAX_NUMNODES || !node_possible(nid))
+ nid = NUMA_NO_NODE;
+
+ for_each_dimm(dimm) {
+ if (entry->phys_mem_array_handle == dimm->phys_handle) {
+ dimm->numa_node = nid;
+ dimm->start = start;
+ dimm->end = end;
+ }
+ }
+}
+
static int get_dimm_smbios_index(u16 handle)
{
struct mem_ctl_info *mci = ghes_pvt->mci;
@@ -213,8 +273,25 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg)
}
}
+static void mem_info_disable_numa(void)
+{
+ struct ghes_dimm_info *dimm;
+
+ for_each_dimm(dimm) {
+ if (dimm->numa_node != NUMA_NO_NODE)
+ mem_info.num_per_node[dimm->numa_node] = 0;
+ dimm->numa_node = 0;
+ }
+
+ mem_info.num_per_node[0] = mem_info.num_dimm;
+ mem_info.num_nodes = 1;
+ mem_info.enable_numa = false;
+}
+
static int mem_info_setup(void)
{
+ struct ghes_dimm_info *dimm;
+ bool enable_numa = true;
int idx = 0;
memset(&mem_info, 0, sizeof(mem_info));
@@ -231,6 +308,29 @@ static int mem_info_setup(void)
ghes_dimm_info_init();
dmi_walk(ghes_edac_dmidecode, &idx);
+ dmi_walk(ghes_edac_set_nid, NULL);
+
+ for_each_dimm(dimm) {
+ if (dimm->numa_node == NUMA_NO_NODE) {
+ enable_numa = false;
+ } else {
+ if (!mem_info.num_per_node[dimm->numa_node])
+ mem_info.num_nodes++;
+ mem_info.num_per_node[dimm->numa_node]++;
+ }
+
+ edac_dbg(1, "DIMM%i: Found mem range [%pa-%pa] on node %d\n",
+ dimm->idx, &dimm->start, &dimm->end, dimm->numa_node);
+ }
+
+ mem_info.enable_numa = enable_numa;
+ if (enable_numa)
+ return 0;
+
+ /* something went wrong, disable numa */
+ if (num_possible_nodes() > 1)
+ pr_warn("Can't get numa info, disabling numa\n");
+ mem_info_disable_numa();
return 0;
}
@@ -258,6 +358,8 @@ static int mem_info_setup_fake(void)
dimm->dtype = DEV_UNKNOWN;
dimm->edac_mode = EDAC_SECDED;
+ mem_info_disable_numa();
+
return 0;
}
--
2.20.1