From: Roman Gushchin <roman.gushchin@linux.dev>
To: linux-mm@kvack.org
Cc: Andrew Morton <akpm@linux-foundation.org>,
Dave Chinner <dchinner@redhat.com>,
linux-kernel@vger.kernel.org,
Johannes Weiner <hannes@cmpxchg.org>,
Michal Hocko <mhocko@kernel.org>,
Shakeel Butt <shakeelb@google.com>,
Yang Shi <shy828301@gmail.com>,
Roman Gushchin <roman.gushchin@linux.dev>
Subject: [PATCH rfc 4/5] mm: introduce numa interfaces for shrinker sysfs
Date: Fri, 15 Apr 2022 17:27:55 -0700 [thread overview]
Message-ID: <20220416002756.4087977-5-roman.gushchin@linux.dev> (raw)
In-Reply-To: <20220416002756.4087977-1-roman.gushchin@linux.dev>
This commit introduces "count_node", "scan_node", "count_memcg_node"
and "scan_memcg_node" interfaces for numa-aware and numa- and
memcg-aware shrinkers.
Usage examples:
1) Get per-node and per-memcg per-node counts:
$ cat count_node
209 3
$ cat count_memcg_node
1 209 3
20 96 0
53 810 7
2297 2 0
218 13 0
581 30 0
911 124 0
<CUT>
2) Scan individual node:
$ echo "1 200" > scan_node
$ cat scan_node
2
3) Scan individual memcg and node:
$ echo "1868 0 500" > scan_memcg_node
$ cat scan_memcg_node
435
Signed-off-by: Roman Gushchin <roman.gushchin@linux.dev>
---
mm/shrinker_debug.c | 279 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 279 insertions(+)
diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c
index 24f78f5feb22..ae6e434500bc 100644
--- a/mm/shrinker_debug.c
+++ b/mm/shrinker_debug.c
@@ -420,10 +420,289 @@ static const struct attribute_group shrinker_memcg_group = {
.is_visible = memcg_attrs_visible,
};
#endif /* CONFIG_MEMCG */
+
+#ifdef CONFIG_NUMA
+static ssize_t count_node_show(struct shrinker_kobj *skobj,
+ struct shrinker_attribute *attr, char *buf)
+{
+ struct shrinker *shrinker;
+ unsigned long nr;
+ int nid;
+ ssize_t ret = 0;
+
+ down_read(&shrinker_rwsem);
+
+ shrinker = skobj->shrinker;
+ if (!shrinker) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ for_each_node(nid) {
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ .nid = nid,
+ };
+
+ nr = shrinker->count_objects(shrinker, &sc);
+ if (nr == SHRINK_EMPTY)
+ nr = 0;
+
+ ret += sprintf(buf + ret, "%lu ", nr);
+
+ cond_resched();
+ }
+out:
+ up_read(&shrinker_rwsem);
+ ret += sprintf(buf + ret, "\n");
+ return ret;
+}
+
+static struct shrinker_attribute count_node_attribute = __ATTR_RO(count_node);
+
+static ssize_t scan_node_show(struct shrinker_kobj *skobj,
+ struct shrinker_attribute *attr, char *buf)
+{
+ /*
+ * Display the number of objects freed on the last scan.
+ */
+ return sprintf(buf, "%lu\n", attr->private);
+}
+
+static ssize_t scan_node_store(struct shrinker_kobj *skobj,
+ struct shrinker_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long nr, nr_to_scan = 0;
+ struct shrinker *shrinker;
+ ssize_t ret = size;
+ int nid;
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ };
+
+ if (sscanf(buf, "%d %lu", &nid, &nr_to_scan) < 2)
+ return -EINVAL;
+
+ if (nid >= nr_node_ids)
+ return -EINVAL;
+
+ if (nid < 0 || nid >= nr_node_ids)
+ return -EINVAL;
+
+ down_read(&shrinker_rwsem);
+
+ shrinker = skobj->shrinker;
+ if (!shrinker) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ sc.nid = nid;
+ sc.nr_to_scan = nr_to_scan;
+ sc.nr_scanned = nr_to_scan;
+
+ nr = shrinker->scan_objects(shrinker, &sc);
+ if (nr == SHRINK_STOP || nr == SHRINK_EMPTY)
+ nr = 0;
+
+ attr->private = nr;
+out:
+ up_read(&shrinker_rwsem);
+ return ret;
+}
+
+static struct shrinker_attribute scan_node_attribute = __ATTR_RW(scan_node);
+
+#ifdef CONFIG_MEMCG
+static ssize_t count_memcg_node_show(struct shrinker_kobj *skobj,
+ struct shrinker_attribute *attr, char *buf)
+{
+ unsigned long nr, total;
+ unsigned long *count_per_node = NULL;
+ struct shrinker *shrinker;
+ struct mem_cgroup *memcg;
+ ssize_t ret = 0;
+ int nid;
+
+ down_read(&shrinker_rwsem);
+ rcu_read_lock();
+
+ shrinker = skobj->shrinker;
+ if (!shrinker) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ count_per_node = kzalloc(sizeof(unsigned long) * nr_node_ids, GFP_KERNEL);
+ if (!count_per_node) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcg = mem_cgroup_iter(NULL, NULL, NULL);
+ do {
+ if (!mem_cgroup_online(memcg))
+ continue;
+
+ /*
+ * Display a PAGE_SIZE of data, reserve last few characters
+ * for "...".
+ */
+ if (ret > PAGE_SIZE - (nr_node_ids * 20 + 30)) {
+ ret += sprintf(buf + ret, "...\n");
+ mem_cgroup_iter_break(NULL, memcg);
+ break;
+ }
+
+ total = 0;
+ for_each_node(nid) {
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ .nid = nid,
+ .memcg = memcg,
+ };
+
+ nr = shrinker->count_objects(shrinker, &sc);
+ if (nr == SHRINK_EMPTY)
+ nr = 0;
+ count_per_node[nid] = nr;
+ total += nr;
+ }
+ if (!total || total < attr->private)
+ continue;
+
+ ret += sprintf(buf + ret, "%lu ", mem_cgroup_ino(memcg));
+ for_each_node(nid)
+ ret += sprintf(buf + ret, "%lu ", count_per_node[nid]);
+ ret += sprintf(buf + ret, "\n");
+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL);
+out:
+ rcu_read_unlock();
+ up_read(&shrinker_rwsem);
+ kfree(count_per_node);
+ return ret;
+}
+
+static ssize_t count_memcg_node_store(struct shrinker_kobj *skobj,
+ struct shrinker_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long min_count;
+
+ if (kstrtoul(buf, 10, &min_count))
+ return -EINVAL;
+
+ attr->private = min_count;
+
+ return size;
+}
+
+static struct shrinker_attribute count_memcg_node_attribute =
+ __ATTR_RW(count_memcg_node);
+
+static ssize_t scan_memcg_node_show(struct shrinker_kobj *skobj,
+ struct shrinker_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%lu\n", attr->private);
+}
+
+static ssize_t scan_memcg_node_store(struct shrinker_kobj *skobj,
+ struct shrinker_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long nr_to_scan = 0, nr, ino;
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ };
+ struct mem_cgroup *memcg;
+ struct shrinker *shrinker;
+ ssize_t ret = size;
+ int nid;
+
+ if (sscanf(buf, "%lu %d %lu", &ino, &nid, &nr_to_scan) < 2)
+ return -EINVAL;
+
+ if (nid >= nr_node_ids)
+ return -EINVAL;
+
+ memcg = mem_cgroup_get_from_ino(ino);
+ if (!memcg || IS_ERR(memcg))
+ return -ENOENT;
+
+ if (!mem_cgroup_online(memcg)) {
+ mem_cgroup_put(memcg);
+ return -ENOENT;
+ }
+
+ down_read(&shrinker_rwsem);
+
+ shrinker = skobj->shrinker;
+ if (!shrinker) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ sc.nid = nid;
+ sc.memcg = memcg;
+ sc.nr_to_scan = nr_to_scan;
+ sc.nr_scanned = nr_to_scan;
+
+ nr = shrinker->scan_objects(shrinker, &sc);
+ if (nr == SHRINK_STOP || nr == SHRINK_EMPTY)
+ nr = 0;
+
+ attr->private = nr;
+out:
+ up_read(&shrinker_rwsem);
+ mem_cgroup_put(memcg);
+ return ret;
+}
+
+static struct shrinker_attribute scan_memcg_node_attribute =
+ __ATTR_RW(scan_memcg_node);
+#endif /* CONFIG_MEMCG */
+
+static struct attribute *shrinker_node_attrs[] = {
+ &count_node_attribute.attr,
+ &scan_node_attribute.attr,
+#ifdef CONFIG_MEMCG
+ &count_memcg_node_attribute.attr,
+ &scan_memcg_node_attribute.attr,
+#endif
+ NULL,
+};
+
+static umode_t node_attrs_visible(struct kobject *kobj, struct attribute *attr,
+ int i)
+{
+ struct shrinker_kobj *skobj = to_shrinker_kobj(kobj);
+ struct shrinker *shrinker;
+ int ret = 0;
+
+ lockdep_assert_held(&shrinker_rwsem);
+
+ shrinker = skobj->shrinker;
+ if (nr_node_ids > 1 && shrinker &&
+ (shrinker->flags & SHRINKER_NUMA_AWARE))
+ ret = 0644;
+
+ return ret;
+}
+
+static const struct attribute_group shrinker_node_group = {
+ .attrs = shrinker_node_attrs,
+ .is_visible = node_attrs_visible,
+};
+#endif /* CONFIG_NUMA */
+
static const struct attribute_group *shrinker_sysfs_groups[] = {
&shrinker_default_group,
#ifdef CONFIG_MEMCG
&shrinker_memcg_group,
+#endif
+#ifdef CONFIG_NUMA
+ &shrinker_node_group,
#endif
NULL,
};
--
2.35.1
next prev parent reply other threads:[~2022-04-16 0:28 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-04-16 0:27 [PATCH rfc 0/5] mm: introduce shrinker sysfs interface Roman Gushchin
2022-04-16 0:27 ` [PATCH rfc 1/5] mm: introduce sysfs interface for debugging kernel shrinker Roman Gushchin
2022-04-16 1:35 ` Hillf Danton
2022-04-16 0:27 ` [PATCH rfc 2/5] mm: memcontrol: introduce mem_cgroup_ino() and mem_cgroup_get_from_ino() Roman Gushchin
2022-04-16 0:27 ` [PATCH rfc 3/5] mm: introduce memcg interfaces for shrinker sysfs Roman Gushchin
2022-04-16 0:27 ` Roman Gushchin [this message]
2022-04-16 0:27 ` [PATCH rfc 5/5] mm: provide shrinkers with names Roman Gushchin
2022-04-18 9:27 ` [PATCH rfc 0/5] mm: introduce shrinker sysfs interface Mike Rapoport
2022-04-18 17:27 ` Roman Gushchin
2022-04-19 6:33 ` Mike Rapoport
2022-04-19 17:58 ` Roman Gushchin
2022-04-19 4:27 ` Andrew Morton
2022-04-19 17:52 ` Roman Gushchin
2022-04-19 18:25 ` Andrew Morton
2022-04-19 18:43 ` Roman Gushchin
2022-04-19 18:33 ` Greg KH
2022-04-19 18:20 ` Kent Overstreet
2022-04-19 18:58 ` Roman Gushchin
2022-04-19 19:46 ` Kent Overstreet
2022-04-19 18:36 ` Kent Overstreet
2022-04-19 18:50 ` Roman Gushchin
2022-04-19 21:10 ` Kent Overstreet
2022-04-20 22:24 ` Yang Shi
2022-04-20 23:23 ` Roman Gushchin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220416002756.4087977-5-roman.gushchin@linux.dev \
--to=roman.gushchin@linux.dev \
--cc=akpm@linux-foundation.org \
--cc=dchinner@redhat.com \
--cc=hannes@cmpxchg.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mhocko@kernel.org \
--cc=shakeelb@google.com \
--cc=shy828301@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox