All of lore.kernel.org
 help / color / mirror / Atom feed
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 3/5] mm: introduce memcg interfaces for shrinker sysfs
Date: Fri, 15 Apr 2022 17:27:54 -0700	[thread overview]
Message-ID: <20220416002756.4087977-4-roman.gushchin@linux.dev> (raw)
In-Reply-To: <20220416002756.4087977-1-roman.gushchin@linux.dev>

This commit introduces "count_memcg" and "scan_memcg" interfaces
for memcg-aware shrinkers.

Count_memcg using the following format:
<cgroup inode number1> <count2>
<cgroup inode number2> <count2>
...

Memory cgroups with 0 associated objects are skipped.

If the output doesn't fit into a page (sysfs limitation), a separate
line with "..." is added at the end.

It's possible to write a minimum number to the "count_memcg" interface
to filter the output.

Signed-off-by: Roman Gushchin <roman.gushchin@linux.dev>
---
 mm/shrinker_debug.c | 216 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 216 insertions(+)

diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c
index 817d578f993c..24f78f5feb22 100644
--- a/mm/shrinker_debug.c
+++ b/mm/shrinker_debug.c
@@ -3,6 +3,7 @@
 #include <linux/slab.h>
 #include <linux/kobject.h>
 #include <linux/shrinker.h>
+#include <linux/memcontrol.h>
 
 /* defined in vmscan.c */
 extern struct rw_semaphore shrinker_rwsem;
@@ -207,8 +208,223 @@ static const struct attribute_group shrinker_default_group = {
 	.attrs = shrinker_default_attrs,
 };
 
+#ifdef CONFIG_MEMCG
+static ssize_t count_memcg_show(struct shrinker_kobj *skobj,
+				struct shrinker_attribute *attr, char *buf)
+{
+	unsigned long nr, total;
+	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;
+	}
+
+	memcg = mem_cgroup_iter(NULL, NULL, NULL);
+	do {
+		if (!mem_cgroup_online(memcg))
+			continue;
+
+		/*
+		 * Display a PAGE_SIZE of data, reserve last 50 characters
+		 * for "...".
+		 */
+		if (ret > PAGE_SIZE - 50) {
+			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;
+			total += nr;
+
+			if (!(shrinker->flags & SHRINKER_NUMA_AWARE))
+				break;
+
+			cond_resched();
+		}
+
+		if (!total || total < attr->private)
+			continue;
+
+		ret += sprintf(buf + ret, "%lu %lu\n", mem_cgroup_ino(memcg),
+			       total);
+
+		cond_resched();
+	} while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL);
+out:
+	rcu_read_unlock();
+	up_read(&shrinker_rwsem);
+	return ret;
+}
+
+static ssize_t count_memcg_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_attribute = __ATTR_RW(count_memcg);
+
+static ssize_t scan_memcg_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_memcg_store(struct shrinker_kobj *skobj,
+			  struct shrinker_attribute *attr,
+			  const char *buf, size_t size)
+{
+	unsigned long nr, nr_to_scan = 0, freed = 0, total = 0, ino;
+	unsigned long *count_per_node = NULL;
+	struct mem_cgroup *memcg;
+	struct shrinker *shrinker;
+	ssize_t ret = size;
+	int nid;
+
+	if (sscanf(buf, "%lu %lu", &ino, &nr_to_scan) < 2)
+		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;
+	}
+
+	if (shrinker->flags & SHRINKER_NUMA_AWARE) {
+		count_per_node = kzalloc(sizeof(unsigned long) * nr_node_ids,
+					GFP_KERNEL);
+		if (!count_per_node) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		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;
+
+			cond_resched();
+		}
+	}
+
+	for_each_node(nid) {
+		struct shrink_control sc = {
+			.gfp_mask = GFP_KERNEL,
+			.nid = nid,
+			.memcg = memcg,
+		};
+
+		if (shrinker->flags & SHRINKER_NUMA_AWARE) {
+			sc.nr_to_scan = nr_to_scan * count_per_node[nid] /
+				(total ? total : 1);
+			sc.nr_scanned = sc.nr_to_scan;
+		} else {
+			sc.nr_to_scan = nr_to_scan;
+			sc.nr_scanned = sc.nr_to_scan;
+		}
+
+		nr = shrinker->scan_objects(shrinker, &sc);
+		if (nr == SHRINK_STOP || nr == SHRINK_EMPTY)
+			nr = 0;
+
+		freed += nr;
+
+		if (!(shrinker->flags & SHRINKER_NUMA_AWARE))
+			break;
+
+		cond_resched();
+	}
+	attr->private = freed;
+out:
+	up_read(&shrinker_rwsem);
+	mem_cgroup_put(memcg);
+	kfree(count_per_node);
+	return ret;
+}
+
+static struct shrinker_attribute scan_memcg_attribute = __ATTR_RW(scan_memcg);
+
+static struct attribute *shrinker_memcg_attrs[] = {
+	&count_memcg_attribute.attr,
+	&scan_memcg_attribute.attr,
+	NULL,
+};
+
+static umode_t memcg_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 (shrinker && (shrinker->flags & SHRINKER_MEMCG_AWARE))
+		ret = 0644;
+
+	return ret;
+}
+
+static const struct attribute_group shrinker_memcg_group = {
+	.attrs = shrinker_memcg_attrs,
+	.is_visible = memcg_attrs_visible,
+};
+#endif /* CONFIG_MEMCG */
 static const struct attribute_group *shrinker_sysfs_groups[] = {
 	&shrinker_default_group,
+#ifdef CONFIG_MEMCG
+	&shrinker_memcg_group,
+#endif
 	NULL,
 };
 
-- 
2.35.1


  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 ` Roman Gushchin [this message]
2022-04-16  0:27 ` [PATCH rfc 4/5] mm: introduce numa interfaces for shrinker sysfs Roman Gushchin
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-4-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.