[v2] EDAC: Expose node and memory controller link in sysfs if CONFIG_NUMA

Message ID 20230522152155.2614466-1-yajun.deng@linux.dev
State New
Headers
Series [v2] EDAC: Expose node and memory controller link in sysfs if CONFIG_NUMA |

Commit Message

Yajun Deng May 22, 2023, 3:21 p.m. UTC
  The node* directory in sysfs already has cpu link and memory link, memory
controller is also a resource under node. Therefore, expose the memory
controller symbolic link under the node* directory if CONFIG_NUMA.

The mc directory in sysfs can have multiple mc* directories, which can
belong to different nodes. To know which node each mc belogs to, expose
node symbolic link under the mc* directory if CONFIG_NUMA.

At the same time, change the type of EDAC from tristate to boolean
because it needs node_devices.

Signed-off-by: Yajun Deng <yajun.deng@linux.dev>
Reported-by: kernel test robot <yujie.liu@intel.com>
Closes: https://lore.kernel.org/oe-lkp/202305221451.b48d9b55-yujie.liu@intel.com
---
 Documentation/ABI/testing/sysfs-devices-edac | 26 +++++++++
 Documentation/admin-guide/ras.rst            |  2 +
 drivers/edac/Kconfig                         |  2 +-
 drivers/edac/edac_mc_sysfs.c                 | 60 ++++++++++++++++++++
 4 files changed, 89 insertions(+), 1 deletion(-)
  

Patch

diff --git a/Documentation/ABI/testing/sysfs-devices-edac b/Documentation/ABI/testing/sysfs-devices-edac
index 256a9e990c0b..2735dde61e0b 100644
--- a/Documentation/ABI/testing/sysfs-devices-edac
+++ b/Documentation/ABI/testing/sysfs-devices-edac
@@ -155,3 +155,29 @@  Description:	This attribute file displays the total count of uncorrectable
 		errors that have occurred on this DIMM. If panic_on_ue is set, this
 		counter will not have a chance to increment, since EDAC will panic the
 		system
+
+What:		/sys/devices/system/edac/mc/mc*/node*
+Date:		May 2023
+Contact:	Yajun Deng <yajun.deng@linux.dev>
+		linux-edac@vger.kernel.org
+Description:	When CONFIG_NUMA is enabled, a symbolic link that points to the
+		corresponding NUMA node directory.
+
+		For example, on node0 with two memory control directories mc0 and mc1
+		the symlinks are::
+
+		  /sys/devices/system/edac/mc/mc0/node0 -> ../../../node/node0
+		  /sys/devices/system/edac/mc/mc1/node0 -> ../../../node/node0
+
+What:		/sys/devices/system/node/node*/mc*
+Date:		May 2023
+Contact:	Yajun Deng <yajun.deng@linux.dev>
+		linux-edac@vger.kernel.org
+Description:	When CONFIG_NUMA is enabled, a symbolic link that points to the
+		corresponding memory control directory.
+
+		For example, on node0 with two memory control directories mc0 and mc1
+		the symlinks are::
+
+		  /sys/devices/system/node/node0/mc0 -> ../../edac/mc/mc0
+		  /sys/devices/system/node/node0/mc1 -> ../../edac/mc/mc1
diff --git a/Documentation/admin-guide/ras.rst b/Documentation/admin-guide/ras.rst
index 8e03751d126d..ef7cc9867fc8 100644
--- a/Documentation/admin-guide/ras.rst
+++ b/Documentation/admin-guide/ras.rst
@@ -458,6 +458,7 @@  A typical EDAC system has the following structure under
 	│   │   │   └── uevent
 	│   │   ├── max_location
 	│   │   ├── mc_name
+	│   │   ├── node0 -> ../../../node/node0  #if CONFIG_NUMA
 	│   │   ├── reset_counters
 	│   │   ├── seconds_since_reset
 	│   │   ├── size_mb
@@ -479,6 +480,7 @@  A typical EDAC system has the following structure under
 	│   │   │   └── uevent
 	│   │   ├── max_location
 	│   │   ├── mc_name
+	│   │   ├── node0 -> ../../../node/node0  #if CONFIG_NUMA
 	│   │   ├── reset_counters
 	│   │   ├── seconds_since_reset
 	│   │   ├── size_mb
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 68f576700911..e61a29e0eb28 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -10,7 +10,7 @@  config EDAC_SUPPORT
 	bool
 
 menuconfig EDAC
-	tristate "EDAC (Error Detection And Correction) reporting"
+	bool "EDAC (Error Detection And Correction) reporting"
 	depends on HAS_IOMEM && EDAC_SUPPORT && RAS
 	help
 	  EDAC is a subsystem along with hardware-specific drivers designed to
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 15f63452a9be..e0a99c5f736f 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -18,6 +18,7 @@ 
 #include <linux/bug.h>
 #include <linux/pm_runtime.h>
 #include <linux/uaccess.h>
+#include <linux/node.h>
 
 #include "edac_mc.h"
 #include "edac_module.h"
@@ -922,6 +923,61 @@  static const struct device_type mci_attr_type = {
 	.groups		= mci_attr_groups,
 };
 
+#ifdef CONFIG_NUMA
+static int edac_create_node_link(struct mem_ctl_info *mci)
+{
+	int nid = dev_to_node(mci->pdev);
+	struct node *node;
+	struct device *dev;
+	int ret;
+
+	if (nid < 0 || nid >= MAX_NUMNODES)
+		return 0;
+
+	if (!node_online(nid) || !node_devices[nid])
+		return 0;
+
+	node = node_devices[nid];
+	dev = &mci->dev;
+
+	ret = sysfs_create_link(&node->dev.kobj, &dev->kobj,
+				kobject_name(&dev->kobj));
+	if (ret)
+		return ret;
+
+	return sysfs_create_link(&dev->kobj, &node->dev.kobj,
+				 kobject_name(&node->dev.kobj));
+}
+
+static void edac_remove_node_link(struct mem_ctl_info *mci)
+{
+	int nid = dev_to_node(mci->pdev);
+	struct node *node;
+	struct device *dev;
+
+	if (nid < 0 || nid >= MAX_NUMNODES)
+		return;
+
+	if (!node_online(nid) || !node_devices[nid])
+		return;
+
+	node = node_devices[nid];
+	dev = &mci->dev;
+
+	sysfs_remove_link(&node->dev.kobj, kobject_name(&dev->kobj));
+
+	sysfs_remove_link(&dev->kobj, kobject_name(&node->dev.kobj));
+}
+#else
+static inline int edac_create_node_link(struct mem_ctl_info *mci)
+{
+	return 0;
+}
+static inline void edac_remove_node_link(struct mem_ctl_info *mci)
+{
+}
+#endif
+
 /*
  * Create a new Memory Controller kobject instance,
  *	mc<id> under the 'mc' directory
@@ -966,6 +1022,8 @@  int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
 			goto fail;
 	}
 
+	edac_create_node_link(mci);
+
 #ifdef CONFIG_EDAC_LEGACY_SYSFS
 	err = edac_create_csrow_objects(mci);
 	if (err < 0)
@@ -1000,6 +1058,8 @@  void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
 	edac_delete_csrow_objects(mci);
 #endif
 
+	edac_remove_node_link(mci);
+
 	mci_for_each_dimm(mci, dimm) {
 		if (!device_is_registered(&dimm->dev))
 			continue;