[PATCH v8 07/12] mm/demotion: Add per node memory tier attribute to sysfs

Aneesh Kumar K.V
2022-07-04T07:09:53Z

Add support to modify the memory tier for a NUMA node.

/sys/devices/system/node/nodeN/memtier

where N = node id

When read, It list the memory tier that the node belongs to.

When written, the kernel moves the node into the specified
memory tier, the tier assignment of all other nodes are not
affected.

If the memory tier does not exist, it is created.

Suggested-by: Wei Xu <weixugc@google.com>
Signed-off-by: Jagdish Gediya <jvgediya@linux.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
---
drivers/base/node.c | 42 ++++++++++++++++++++++++++++++++++++
include/linux/memory-tiers.h | 2 ++
mm/memory-tiers.c | 42 ++++++++++++++++++++++++++++++++++++
3 files changed, 86 insertions(+)

diff --git a/drivers/base/node.c b/drivers/base/node.c
index 0ac6376ef7a1..667f37eecf3a 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -20,6 +20,7 @@
#include <linux/pm_runtime.h>
#include <linux/swap.h>
#include <linux/slab.h>
+#include <linux/memory-tiers.h>

static struct bus_type node_subsys = {
.name = "node",
@@ -560,11 +561,52 @@ static ssize_t node_read_distance(struct device *dev,
}
static DEVICE_ATTR(distance, 0444, node_read_distance, NULL);

+#ifdef CONFIG_NUMA
+static ssize_t memtier_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int node = dev->id;
+ int tier_index = node_get_memory_tier_id(node);
+
+ /*
+ * CPU only NUMA node is not part of memory tiers.
+ */
+ if (tier_index != -1)
+ return sysfs_emit(buf, "%d\n", tier_index);
+ return 0;
+}
+
+static ssize_t memtier_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long tier;
+ int node = dev->id;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &tier);
+ if (ret)
+ return ret;
+
+ ret = node_update_memory_tier(node, tier);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(memtier);
+#endif
+
static struct attribute *node_dev_attrs[] = {
&dev_attr_meminfo.attr,
&dev_attr_numastat.attr,
&dev_attr_distance.attr,
&dev_attr_vmstat.attr,
+#ifdef CONFIG_NUMA
+ &dev_attr_memtier.attr,
+#endif
NULL
};

diff --git a/include/linux/memory-tiers.h b/include/linux/memory-tiers.h
index 3234301c2537..453f6e5d357c 100644
--- a/include/linux/memory-tiers.h
+++ b/include/linux/memory-tiers.h
@@ -23,6 +23,8 @@ static inline int next_demotion_node(int node)
return NUMA_NO_NODE;
}
#endif
+int node_get_memory_tier_id(int node);
+int node_update_memory_tier(int node, int tier);

#else

diff --git a/mm/memory-tiers.c b/mm/memory-tiers.c
index 4acf7570ae1b..b7cb368cb9c0 100644
--- a/mm/memory-tiers.c
+++ b/mm/memory-tiers.c
@@ -288,6 +288,48 @@ static int node_set_memory_tier(int node, int tier)
return ret;
}

+int node_get_memory_tier_id(int node)
+{
+ int tier = -1;
+ struct memory_tier *memtier;
+ /*
+ * Make sure memory tier is not unregistered
+ * while it is being read.
+ */
+ mutex_lock(&memory_tier_lock);
+ memtier = __node_get_memory_tier(node);
+ if (memtier)
+ tier = memtier->dev.id;
+ mutex_unlock(&memory_tier_lock);
+
+ return tier;
+}
+
+int node_update_memory_tier(int node, int tier)
+{
+ struct memory_tier *current_tier;
+ int ret = 0;
+
+ mutex_lock(&memory_tier_lock);
+
+ current_tier = __node_get_memory_tier(node);
+ if (!current_tier || current_tier->dev.id == tier)
+ goto out;
+
+ node_clear(node, current_tier->nodelist);
+
+ ret = __node_create_and_set_memory_tier(node, tier);
+
+ if (nodes_empty(current_tier->nodelist))
+ unregister_memory_tier(current_tier);
+
+ establish_migration_targets();
+out:
+ mutex_unlock(&memory_tier_lock);
+
+ return ret;
+}
+
#ifdef CONFIG_MIGRATION
/**
* next_demotion_node() - Get the next node in the demotion path
--
2.36.1