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 <rrich...@marvell.com>
---
 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

Reply via email to