* [PATCH v1 0/5] mm: introduce shrinker debugfs interface
@ 2022-04-22 1:58 Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 1/5] mm: introduce debugfs interface for kernel memory shrinkers Roman Gushchin
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Roman Gushchin @ 2022-04-22 1:58 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Dave Chinner, linux-kernel, Yang Shi, Kent Overstreet,
Hillf Danton, Roman Gushchin
There are 50+ different shrinkers in the kernel, many with their own bells and
whistles. Under the memory pressure the kernel applies some pressure on each of
them in the order of which they were created/registered in the system. Some
of them can contain only few objects, some can be quite large. Some can be
effective at reclaiming memory, some not.
The only existing debugging mechanism is a couple of tracepoints in
do_shrink_slab(): mm_shrink_slab_start and mm_shrink_slab_end. They aren't
covering everything though: shrinkers which report 0 objects will never show up,
there is no support for memcg-aware shrinkers. Shrinkers are identified by their
scan function, which is not always enough (e.g. hard to guess which super
block's shrinker it is having only "super_cache_scan"). They are a passive
mechanism: there is no way to call into counting and scanning of an individual
shrinker and profile it.
To provide a better visibility and debug options for memory shrinkers
this patchset introduces a /sys/kernel/debug/shrinker interface, to some extent
similar to /sys/kernel/slab.
For each shrinker registered in the system a directory is created. The directory
contains "count" and "scan" files, which allow to trigger count_objects()
and scan_objects() callbacks. For memcg-aware and numa-aware shrinkers
count_memcg, scan_memcg, count_node, scan_node, count_memcg_node
and scan_memcg_node are additionally provided. They allow to get per-memcg
and/or per-node object count and shrink only a specific memcg/node.
To make debugging more pleasant, the patchset also names all shrinkers,
so that debugfs entries can have more meaningful names.
Usage examples:
1) List registered shrinkers:
$ cd /sys/kernel/debug/shrinker/
$ ls
dqcache-16 sb-cgroup2-30 sb-hugetlbfs-33 sb-proc-41 sb-selinuxfs-22 sb-tmpfs-40 sb-zsmalloc-19
kfree_rcu-0 sb-configfs-23 sb-iomem-12 sb-proc-44 sb-sockfs-8 sb-tmpfs-42 shadow-18
sb-aio-20 sb-dax-11 sb-mqueue-21 sb-proc-45 sb-sysfs-26 sb-tmpfs-43 thp_deferred_split-10
sb-anon_inodefs-15 sb-debugfs-7 sb-nsfs-4 sb-proc-47 sb-tmpfs-1 sb-tmpfs-46 thp_zero-9
sb-bdev-3 sb-devpts-28 sb-pipefs-14 sb-pstore-31 sb-tmpfs-27 sb-tmpfs-49 xfs_buf-37
sb-bpf-32 sb-devtmpfs-5 sb-proc-25 sb-rootfs-2 sb-tmpfs-29 sb-tracefs-13 xfs_inodegc-38
sb-btrfs-24 sb-hugetlbfs-17 sb-proc-39 sb-securityfs-6 sb-tmpfs-35 sb-xfs-36 zspool-34
2) Get information about a specific shrinker:
$ cd sb-btrfs-24/
$ ls
count count_memcg count_memcg_node count_node scan scan_memcg scan_memcg_node scan_node
3) Count objects on the system/root cgroup level
$ cat count
212
4) Count objects on the system/root cgroup level per numa node (on a 2-node machine)
$ cat count_node
209 3
5) Count objects for each memcg (output format: cgroup inode, count)
$ cat count_memcg
1 212
20 96
53 817
2297 2
218 13
581 30
911 124
<CUT>
6) Same but with a per-node output
$ 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>
7) Scan system/root shrinker
$ cat count
212
$ echo 100 > scan
$ cat scan
97
$ cat count
115
8) Scan individual memcg
$ echo "1868 500" > scan_memcg
$ cat scan_memcg
193
9) Scan individual node
$ echo "1 200" > scan_node
$ cat scan_node
2
10) Scan individual memcg and node
$ echo "1868 0 500" > scan_memcg_node
$ cat scan_memcg_node
435
v1:
1) switched to debugfs, suggested by Mike, Andrew, Greg and others
2) switched to seq_file API for output, no PAGE_SIZE limit anymore, by Andrew
3) switched to down_read_killable(), suggested by Hillf
4) dropped stateful filtering and "freed" returning, by Kent
5) added docs, by Andrew
rfc:
https://lwn.net/Articles/891542/
Roman Gushchin (5):
mm: introduce debugfs interface for kernel memory shrinkers
mm: memcontrol: introduce mem_cgroup_ino() and
mem_cgroup_get_from_ino()
mm: introduce memcg interfaces for shrinker debugfs
mm: introduce numa interfaces for shrinker debugfs
mm: provide shrinkers with names
arch/x86/kvm/mmu/mmu.c | 2 +-
drivers/android/binder_alloc.c | 2 +-
drivers/gpu/drm/i915/gem/i915_gem_shrinker.c | 3 +-
drivers/gpu/drm/msm/msm_gem_shrinker.c | 2 +-
.../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
drivers/gpu/drm/ttm/ttm_pool.c | 2 +-
drivers/md/bcache/btree.c | 2 +-
drivers/md/dm-bufio.c | 2 +-
drivers/md/dm-zoned-metadata.c | 2 +-
drivers/md/raid5.c | 2 +-
drivers/misc/vmw_balloon.c | 2 +-
drivers/virtio/virtio_balloon.c | 2 +-
drivers/xen/xenbus/xenbus_probe_backend.c | 2 +-
fs/erofs/utils.c | 2 +-
fs/ext4/extents_status.c | 3 +-
fs/f2fs/super.c | 2 +-
fs/gfs2/glock.c | 2 +-
fs/gfs2/main.c | 2 +-
fs/jbd2/journal.c | 2 +-
fs/mbcache.c | 2 +-
fs/nfs/nfs42xattr.c | 7 +-
fs/nfs/super.c | 2 +-
fs/nfsd/filecache.c | 2 +-
fs/nfsd/nfscache.c | 2 +-
fs/quota/dquot.c | 2 +-
fs/super.c | 2 +-
fs/ubifs/super.c | 2 +-
fs/xfs/xfs_buf.c | 2 +-
fs/xfs/xfs_icache.c | 2 +-
fs/xfs/xfs_qm.c | 2 +-
include/linux/memcontrol.h | 9 +
include/linux/shrinker.h | 24 +-
kernel/rcu/tree.c | 2 +-
lib/Kconfig.debug | 9 +
mm/Makefile | 1 +
mm/huge_memory.c | 4 +-
mm/memcontrol.c | 23 +
mm/shrinker_debug.c | 511 ++++++++++++++++++
mm/vmscan.c | 66 ++-
mm/workingset.c | 2 +-
mm/zsmalloc.c | 2 +-
net/sunrpc/auth.c | 2 +-
42 files changed, 675 insertions(+), 47 deletions(-)
create mode 100644 mm/shrinker_debug.c
--
2.35.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 1/5] mm: introduce debugfs interface for kernel memory shrinkers
2022-04-22 1:58 [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
@ 2022-04-22 1:58 ` Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 2/5] mm: memcontrol: introduce mem_cgroup_ino() and mem_cgroup_get_from_ino() Roman Gushchin
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Roman Gushchin @ 2022-04-22 1:58 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Dave Chinner, linux-kernel, Yang Shi, Kent Overstreet,
Hillf Danton, Roman Gushchin
This commit introduces the /sys/kernel/debug/shrinker debugfs
interface which provides an ability to observe the state and interact
with individual kernel memory shrinkers.
Because the feature is oriented on kernel developers and adds some
memory overhead (which shouldn't be large unless there is a huge
amount of registered shrinkers), it's guarded by a config option
(disabled by default).
This commit introduces "count" and "scan" interfaces for each
shrinker registered in the system.
Basic usage:
1) Get the number of objects
$ cat count
2) Try to reclaim 500 objects
$ echo "500" > scan
Following commits in the series will add memcg- and numa-specific
features.
This commit gives debugfs entries simple numeric names, which are not
very convenient. The following commit in the series will provide
shrinkers with more meaningful names.
Signed-off-by: Roman Gushchin <roman.gushchin@linux.dev>
---
include/linux/shrinker.h | 19 +++-
lib/Kconfig.debug | 9 ++
mm/Makefile | 1 +
mm/shrinker_debug.c | 214 +++++++++++++++++++++++++++++++++++++++
mm/vmscan.c | 6 +-
5 files changed, 246 insertions(+), 3 deletions(-)
create mode 100644 mm/shrinker_debug.c
diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h
index 76fbf92b04d9..17985a890887 100644
--- a/include/linux/shrinker.h
+++ b/include/linux/shrinker.h
@@ -72,6 +72,10 @@ struct shrinker {
#ifdef CONFIG_MEMCG
/* ID in shrinker_idr */
int id;
+#endif
+#ifdef CONFIG_SHRINKER_DEBUG
+ int debugfs_id;
+ struct dentry *debugfs_entry;
#endif
/* objs pending delete, per node */
atomic_long_t *nr_deferred;
@@ -94,4 +98,17 @@ extern int register_shrinker(struct shrinker *shrinker);
extern void unregister_shrinker(struct shrinker *shrinker);
extern void free_prealloced_shrinker(struct shrinker *shrinker);
extern void synchronize_shrinkers(void);
-#endif
+
+#ifdef CONFIG_SHRINKER_DEBUG
+int shrinker_debugfs_add(struct shrinker *shrinker);
+void shrinker_debugfs_remove(struct shrinker *shrinker);
+#else /* CONFIG_SHRINKER_DEBUG */
+static inline int shrinker_debugfs_add(struct shrinker *shrinker)
+{
+ return 0;
+}
+static inline void shrinker_debugfs_remove(struct shrinker *shrinker)
+{
+}
+#endif /* CONFIG_SHRINKER_DEBUG */
+#endif /* _LINUX_SHRINKER_H */
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 6bf9cceb7d20..51910b291b81 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -733,6 +733,15 @@ config SLUB_STATS
out which slabs are relevant to a particular load.
Try running: slabinfo -DA
+config SHRINKER_DEBUG
+ default n
+ bool "Enable shrinker debugging support"
+ depends on DEBUG_FS
+ help
+ Say Y to enable the shrinker debugfs interface which provides
+ visibility into the kernel memory shrinkers subsystem.
+ Disable it to avoid an extra memory footprint.
+
config HAVE_DEBUG_KMEMLEAK
bool
diff --git a/mm/Makefile b/mm/Makefile
index 6f9ffa968a1a..9a564f836403 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -133,3 +133,4 @@ obj-$(CONFIG_PAGE_REPORTING) += page_reporting.o
obj-$(CONFIG_IO_MAPPING) += io-mapping.o
obj-$(CONFIG_HAVE_BOOTMEM_INFO_NODE) += bootmem_info.o
obj-$(CONFIG_GENERIC_IOREMAP) += ioremap.o
+obj-$(CONFIG_SHRINKER_DEBUG) += shrinker_debug.o
diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c
new file mode 100644
index 000000000000..4df7382a0737
--- /dev/null
+++ b/mm/shrinker_debug.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/idr.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/shrinker.h>
+
+/* defined in vmscan.c */
+extern struct rw_semaphore shrinker_rwsem;
+extern struct list_head shrinker_list;
+
+static DEFINE_IDA(shrinker_debugfs_ida);
+static struct dentry *shrinker_debugfs_root;
+
+static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
+{
+ struct shrinker *shrinker = (struct shrinker *)m->private;
+ unsigned long nr, total = 0;
+ int ret, nid;
+
+ ret = down_read_killable(&shrinker_rwsem);
+ if (ret)
+ return ret;
+
+ 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;
+ total += nr;
+
+ if (!(shrinker->flags & SHRINKER_NUMA_AWARE))
+ break;
+
+ cond_resched();
+ }
+ up_read(&shrinker_rwsem);
+
+ seq_printf(m, "%lu\n", total);
+
+ return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count);
+
+static ssize_t shrinker_debugfs_scan_write(struct file *file,
+ const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ struct shrinker *shrinker = (struct shrinker *)file->private_data;
+ unsigned long nr, total = 0, nr_to_scan;
+ unsigned long *count_per_node = NULL;
+ int nid;
+ char kbuf[24];
+ int read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
+ ssize_t ret;
+
+ if (copy_from_user(kbuf, buf, read_len))
+ return -EFAULT;
+ kbuf[read_len] = '\0';
+
+ if (kstrtoul(kbuf, 10, &nr_to_scan))
+ return -EINVAL;
+
+ ret = down_read_killable(&shrinker_rwsem);
+ if (ret)
+ return ret;
+
+ if (shrinker->flags & SHRINKER_NUMA_AWARE) {
+ /*
+ * If the shrinker is numa aware, distribute nr_to_scan
+ * proportionally.
+ */
+ count_per_node = kcalloc(nr_node_ids, sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!count_per_node) {
+ ret = -ENOMEM;
+ 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;
+ count_per_node[nid] = nr;
+ total += nr;
+
+ cond_resched();
+ }
+ }
+
+ for_each_node(nid) {
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ .nid = nid,
+ };
+
+ 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)
+ break;
+
+ if (!(shrinker->flags & SHRINKER_NUMA_AWARE))
+ break;
+
+ cond_resched();
+
+ }
+ ret = size;
+out:
+ up_read(&shrinker_rwsem);
+ kfree(count_per_node);
+ return ret;
+}
+
+static int shrinker_debugfs_scan_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return nonseekable_open(inode, file);
+}
+
+static const struct file_operations shrinker_debugfs_scan_fops = {
+ .owner = THIS_MODULE,
+ .open = shrinker_debugfs_scan_open,
+ .write = shrinker_debugfs_scan_write,
+};
+
+int shrinker_debugfs_add(struct shrinker *shrinker)
+{
+ struct dentry *entry;
+ char buf[256];
+ int id;
+
+ lockdep_assert_held(&shrinker_rwsem);
+
+ /* debugfs isn't initialized yet, add debugfs entries later. */
+ if (!shrinker_debugfs_root)
+ return 0;
+
+ id = ida_alloc(&shrinker_debugfs_ida, GFP_KERNEL);
+ if (id < 0)
+ return id;
+ shrinker->debugfs_id = id;
+
+ snprintf(buf, sizeof(buf), "%d", id);
+
+ /* create debugfs entry */
+ entry = debugfs_create_dir(buf, shrinker_debugfs_root);
+ if (IS_ERR(entry)) {
+ ida_free(&shrinker_debugfs_ida, id);
+ return PTR_ERR(entry);
+ }
+ shrinker->debugfs_entry = entry;
+
+ /* create generic interfaces */
+ debugfs_create_file("count", 0220, entry, shrinker,
+ &shrinker_debugfs_count_fops);
+ debugfs_create_file("scan", 0440, entry, shrinker,
+ &shrinker_debugfs_scan_fops);
+
+ return 0;
+}
+
+void shrinker_debugfs_remove(struct shrinker *shrinker)
+{
+ lockdep_assert_held(&shrinker_rwsem);
+
+ if (!shrinker->debugfs_entry)
+ return;
+
+ debugfs_remove_recursive(shrinker->debugfs_entry);
+ ida_free(&shrinker_debugfs_ida, shrinker->debugfs_id);
+}
+
+static int __init shrinker_debugfs_init(void)
+{
+ struct shrinker *shrinker;
+ int ret;
+
+ if (!debugfs_initialized())
+ return -ENODEV;
+
+ shrinker_debugfs_root = debugfs_create_dir("shrinker", NULL);
+ if (!shrinker_debugfs_root)
+ return -ENOMEM;
+
+ /* Create debugfs entries for shrinkers registered at boot */
+ ret = down_write_killable(&shrinker_rwsem);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(shrinker, &shrinker_list, list)
+ if (!shrinker->debugfs_entry)
+ ret = shrinker_debugfs_add(shrinker);
+ up_write(&shrinker_rwsem);
+
+ return ret;
+}
+late_initcall(shrinker_debugfs_init);
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 99a572f01cb4..121a54a1602b 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -190,8 +190,8 @@ static void set_task_reclaim_state(struct task_struct *task,
task->reclaim_state = rs;
}
-static LIST_HEAD(shrinker_list);
-static DECLARE_RWSEM(shrinker_rwsem);
+LIST_HEAD(shrinker_list);
+DECLARE_RWSEM(shrinker_rwsem);
#ifdef CONFIG_MEMCG
static int shrinker_nr_max;
@@ -655,6 +655,7 @@ void register_shrinker_prepared(struct shrinker *shrinker)
down_write(&shrinker_rwsem);
list_add_tail(&shrinker->list, &shrinker_list);
shrinker->flags |= SHRINKER_REGISTERED;
+ WARN_ON_ONCE(shrinker_debugfs_add(shrinker));
up_write(&shrinker_rwsem);
}
@@ -682,6 +683,7 @@ void unregister_shrinker(struct shrinker *shrinker)
shrinker->flags &= ~SHRINKER_REGISTERED;
if (shrinker->flags & SHRINKER_MEMCG_AWARE)
unregister_memcg_shrinker(shrinker);
+ shrinker_debugfs_remove(shrinker);
up_write(&shrinker_rwsem);
kfree(shrinker->nr_deferred);
--
2.35.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 2/5] mm: memcontrol: introduce mem_cgroup_ino() and mem_cgroup_get_from_ino()
2022-04-22 1:58 [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 1/5] mm: introduce debugfs interface for kernel memory shrinkers Roman Gushchin
@ 2022-04-22 1:58 ` Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 3/5] mm: introduce memcg interfaces for shrinker debugfs Roman Gushchin
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Roman Gushchin @ 2022-04-22 1:58 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Dave Chinner, linux-kernel, Yang Shi, Kent Overstreet,
Hillf Danton, Roman Gushchin
Shrinker debugfs requires a way to represent memory cgroups without
using full paths, both for displaying information and getting input
from a user.
Cgroup inode number is a perfect way, already used by e.g. bpf.
This commit adds a couple of helper functions which will be used to
represent and interact with memcg-aware shrinkers.
Signed-off-by: Roman Gushchin <roman.gushchin@linux.dev>
---
include/linux/memcontrol.h | 9 +++++++++
mm/memcontrol.c | 23 +++++++++++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 89b14729d59f..27a91dd210c9 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -831,6 +831,15 @@ static inline unsigned short mem_cgroup_id(struct mem_cgroup *memcg)
}
struct mem_cgroup *mem_cgroup_from_id(unsigned short id);
+#ifdef CONFIG_SHRINKER_DEBUG
+static inline unsigned long mem_cgroup_ino(struct mem_cgroup *memcg)
+{
+ return cgroup_ino(memcg->css.cgroup);
+}
+
+struct mem_cgroup *mem_cgroup_get_from_ino(unsigned long ino);
+#endif
+
static inline struct mem_cgroup *mem_cgroup_from_seq(struct seq_file *m)
{
return mem_cgroup_from_css(seq_css(m));
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 08cbe23a8b94..a508979177a2 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -5022,6 +5022,29 @@ struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
return idr_find(&mem_cgroup_idr, id);
}
+#ifdef CONFIG_SHRINKER_DEBUG
+struct mem_cgroup *mem_cgroup_get_from_ino(unsigned long ino)
+{
+ struct cgroup *cgrp;
+ struct cgroup_subsys_state *css;
+ struct mem_cgroup *memcg;
+
+ cgrp = cgroup_get_from_id(ino);
+ if (!cgrp)
+ return ERR_PTR(-ENOENT);
+
+ css = cgroup_get_e_css(cgrp, &memory_cgrp_subsys);
+ if (css)
+ memcg = container_of(css, struct mem_cgroup, css);
+ else
+ memcg = ERR_PTR(-ENOENT);
+
+ cgroup_put(cgrp);
+
+ return memcg;
+}
+#endif
+
static int alloc_mem_cgroup_per_node_info(struct mem_cgroup *memcg, int node)
{
struct mem_cgroup_per_node *pn;
--
2.35.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 3/5] mm: introduce memcg interfaces for shrinker debugfs
2022-04-22 1:58 [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 1/5] mm: introduce debugfs interface for kernel memory shrinkers Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 2/5] mm: memcontrol: introduce mem_cgroup_ino() and mem_cgroup_get_from_ino() Roman Gushchin
@ 2022-04-22 1:58 ` Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 4/5] mm: introduce numa " Roman Gushchin
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Roman Gushchin @ 2022-04-22 1:58 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Dave Chinner, linux-kernel, Yang Shi, Kent Overstreet,
Hillf Danton, Roman Gushchin
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.
Signed-off-by: Roman Gushchin <roman.gushchin@linux.dev>
---
mm/shrinker_debug.c | 186 +++++++++++++++++++++++++++++++++-----------
1 file changed, 139 insertions(+), 47 deletions(-)
diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c
index 4df7382a0737..002d44d6ad56 100644
--- a/mm/shrinker_debug.c
+++ b/mm/shrinker_debug.c
@@ -1,8 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/idr.h>
+#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/shrinker.h>
+#include <linux/memcontrol.h>
/* defined in vmscan.c */
extern struct rw_semaphore shrinker_rwsem;
@@ -11,25 +13,25 @@ extern struct list_head shrinker_list;
static DEFINE_IDA(shrinker_debugfs_ida);
static struct dentry *shrinker_debugfs_root;
-static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
+static unsigned long shrinker_count_objects(struct shrinker *shrinker,
+ struct mem_cgroup *memcg,
+ unsigned long *count_per_node)
{
- struct shrinker *shrinker = (struct shrinker *)m->private;
unsigned long nr, total = 0;
- int ret, nid;
-
- ret = down_read_killable(&shrinker_rwsem);
- if (ret)
- return ret;
+ int nid;
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;
+ if (count_per_node)
+ count_per_node[nid] = nr;
total += nr;
if (!(shrinker->flags & SHRINKER_NUMA_AWARE))
@@ -37,32 +39,17 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
cond_resched();
}
- up_read(&shrinker_rwsem);
-
- seq_printf(m, "%lu\n", total);
- return ret;
+ return total;
}
-DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count);
-static ssize_t shrinker_debugfs_scan_write(struct file *file,
- const char __user *buf,
- size_t size, loff_t *pos)
+static int shrinker_scan_objects(struct shrinker *shrinker,
+ struct mem_cgroup *memcg,
+ unsigned long nr_to_scan)
{
- struct shrinker *shrinker = (struct shrinker *)file->private_data;
- unsigned long nr, total = 0, nr_to_scan;
- unsigned long *count_per_node = NULL;
- int nid;
- char kbuf[24];
- int read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
- ssize_t ret;
-
- if (copy_from_user(kbuf, buf, read_len))
- return -EFAULT;
- kbuf[read_len] = '\0';
-
- if (kstrtoul(kbuf, 10, &nr_to_scan))
- return -EINVAL;
+ unsigned long *count_per_node;
+ unsigned long total, nr;
+ int ret, nid;
ret = down_read_killable(&shrinker_rwsem);
if (ret)
@@ -80,20 +67,7 @@ static ssize_t shrinker_debugfs_scan_write(struct file *file,
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;
- count_per_node[nid] = nr;
- total += nr;
-
- cond_resched();
- }
+ total = shrinker_count_objects(shrinker, memcg, count_per_node);
}
for_each_node(nid) {
@@ -102,13 +76,13 @@ static ssize_t shrinker_debugfs_scan_write(struct file *file,
.nid = nid,
};
- if (shrinker->flags & SHRINKER_NUMA_AWARE) {
+ if (count_per_node) {
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;
+ sc.nr_scanned = nr_to_scan;
}
nr = shrinker->scan_objects(shrinker, &sc);
@@ -119,15 +93,51 @@ static ssize_t shrinker_debugfs_scan_write(struct file *file,
break;
cond_resched();
-
}
- ret = size;
out:
up_read(&shrinker_rwsem);
kfree(count_per_node);
return ret;
}
+static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
+{
+ struct shrinker *shrinker = (struct shrinker *)m->private;
+ int ret;
+
+ ret = down_read_killable(&shrinker_rwsem);
+ if (!ret) {
+ unsigned long total = shrinker_count_objects(shrinker, NULL, NULL);
+
+ up_read(&shrinker_rwsem);
+ seq_printf(m, "%lu\n", total);
+ }
+ return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count);
+
+static ssize_t shrinker_debugfs_scan_write(struct file *file,
+ const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ struct shrinker *shrinker = (struct shrinker *)file->private_data;
+ unsigned long nr_to_scan;
+ char kbuf[24];
+ int read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
+ ssize_t ret;
+
+ if (copy_from_user(kbuf, buf, read_len))
+ return -EFAULT;
+ kbuf[read_len] = '\0';
+
+ if (kstrtoul(kbuf, 10, &nr_to_scan))
+ return -EINVAL;
+
+ ret = shrinker_scan_objects(shrinker, NULL, nr_to_scan);
+
+ return ret ? ret : size;
+}
+
static int shrinker_debugfs_scan_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
@@ -140,6 +150,78 @@ static const struct file_operations shrinker_debugfs_scan_fops = {
.write = shrinker_debugfs_scan_write,
};
+#ifdef CONFIG_MEMCG
+static int shrinker_debugfs_count_memcg_show(struct seq_file *m, void *v)
+{
+ struct shrinker *shrinker = (struct shrinker *)m->private;
+ struct mem_cgroup *memcg;
+ unsigned long total;
+ int ret;
+
+ ret = down_read_killable(&shrinker_rwsem);
+ if (ret)
+ return ret;
+ rcu_read_lock();
+
+ memcg = mem_cgroup_iter(NULL, NULL, NULL);
+ do {
+ if (!mem_cgroup_online(memcg))
+ continue;
+
+ total = shrinker_count_objects(shrinker, memcg, NULL);
+ if (!total)
+ continue;
+
+ seq_printf(m, "%lu %lu\n", mem_cgroup_ino(memcg), total);
+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL);
+
+ rcu_read_unlock();
+ up_read(&shrinker_rwsem);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count_memcg);
+
+static ssize_t shrinker_debugfs_scan_memcg_write(struct file *file,
+ const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ struct shrinker *shrinker = (struct shrinker *)file->private_data;
+ unsigned long nr_to_scan, ino;
+ struct mem_cgroup *memcg;
+ char kbuf[48];
+ int read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
+ ssize_t ret;
+
+ if (copy_from_user(kbuf, buf, read_len))
+ return -EFAULT;
+ kbuf[read_len] = '\0';
+
+ if (sscanf(kbuf, "%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;
+ }
+
+ ret = shrinker_scan_objects(shrinker, memcg, nr_to_scan);
+ mem_cgroup_put(memcg);
+
+ return ret ? ret : size;
+}
+
+static const struct file_operations shrinker_debugfs_scan_memcg_fops = {
+ .owner = THIS_MODULE,
+ .open = shrinker_debugfs_scan_open,
+ .write = shrinker_debugfs_scan_memcg_write,
+};
+#endif
+
int shrinker_debugfs_add(struct shrinker *shrinker)
{
struct dentry *entry;
@@ -173,6 +255,16 @@ int shrinker_debugfs_add(struct shrinker *shrinker)
debugfs_create_file("scan", 0440, entry, shrinker,
&shrinker_debugfs_scan_fops);
+#ifdef CONFIG_MEMCG
+ /* create memcg interfaces */
+ if (shrinker->flags & SHRINKER_MEMCG_AWARE) {
+ debugfs_create_file("count_memcg", 0220, entry, shrinker,
+ &shrinker_debugfs_count_memcg_fops);
+ debugfs_create_file("scan_memcg", 0440, entry, shrinker,
+ &shrinker_debugfs_scan_memcg_fops);
+ }
+#endif
+
return 0;
}
--
2.35.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 4/5] mm: introduce numa interfaces for shrinker debugfs
2022-04-22 1:58 [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
` (2 preceding siblings ...)
2022-04-22 1:58 ` [PATCH v1 3/5] mm: introduce memcg interfaces for shrinker debugfs Roman Gushchin
@ 2022-04-22 1:58 ` Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 5/5] mm: provide shrinkers with names Roman Gushchin
2022-04-22 14:57 ` [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
5 siblings, 0 replies; 7+ messages in thread
From: Roman Gushchin @ 2022-04-22 1:58 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Dave Chinner, linux-kernel, Yang Shi, Kent Overstreet,
Hillf Danton, Roman Gushchin
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 | 200 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 200 insertions(+)
diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c
index 002d44d6ad56..81350b64bf01 100644
--- a/mm/shrinker_debug.c
+++ b/mm/shrinker_debug.c
@@ -222,6 +222,185 @@ static const struct file_operations shrinker_debugfs_scan_memcg_fops = {
};
#endif
+#ifdef CONFIG_NUMA
+static int shrinker_debugfs_count_node_show(struct seq_file *m, void *v)
+{
+ struct shrinker *shrinker = (struct shrinker *)m->private;
+ unsigned long nr;
+ int ret, nid;
+
+ ret = down_read_killable(&shrinker_rwsem);
+ if (ret)
+ return ret;
+
+ 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;
+
+ seq_printf(m, "%s%lu", nid ? " " : "", nr);
+ cond_resched();
+ }
+ up_read(&shrinker_rwsem);
+ seq_puts(m, "\n");
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count_node);
+
+static ssize_t shrinker_debugfs_scan_node_write(struct file *file,
+ const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ struct shrinker *shrinker = (struct shrinker *)file->private_data;
+ unsigned long nr_to_scan = 0;
+ int nid;
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ };
+ char kbuf[48];
+ int read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
+ ssize_t ret;
+
+ if (copy_from_user(kbuf, buf, read_len))
+ return -EFAULT;
+ kbuf[read_len] = '\0';
+
+ if (sscanf(kbuf, "%d %lu", &nid, &nr_to_scan) < 2)
+ return -EINVAL;
+
+ if (nid < 0 || nid >= nr_node_ids)
+ return -EINVAL;
+
+ ret = down_read_killable(&shrinker_rwsem);
+ if (ret)
+ return ret;
+
+ sc.nid = nid;
+ sc.nr_to_scan = nr_to_scan;
+ sc.nr_scanned = nr_to_scan;
+
+ shrinker->scan_objects(shrinker, &sc);
+
+ up_read(&shrinker_rwsem);
+
+ return ret ? ret : size;
+}
+
+static const struct file_operations shrinker_debugfs_scan_node_fops = {
+ .owner = THIS_MODULE,
+ .open = shrinker_debugfs_scan_open,
+ .write = shrinker_debugfs_scan_node_write,
+};
+
+#ifdef CONFIG_MEMCG
+static int shrinker_debugfs_count_memcg_node_show(struct seq_file *m, void *v)
+{
+ struct shrinker *shrinker = (struct shrinker *)m->private;
+ unsigned long *count_per_node = NULL;
+ struct mem_cgroup *memcg;
+ unsigned long total;
+ int ret, nid;
+
+ count_per_node = kcalloc(nr_node_ids, sizeof(unsigned long), GFP_KERNEL);
+ if (!count_per_node)
+ return -ENOMEM;
+
+ ret = down_read_killable(&shrinker_rwsem);
+ if (ret) {
+ kfree(count_per_node);
+ return ret;
+ }
+ rcu_read_lock();
+
+ memcg = mem_cgroup_iter(NULL, NULL, NULL);
+ do {
+ if (!mem_cgroup_online(memcg))
+ continue;
+
+ total = shrinker_count_objects(shrinker, memcg, count_per_node);
+ if (!total)
+ continue;
+
+ seq_printf(m, "%lu", mem_cgroup_ino(memcg));
+ for_each_node(nid)
+ seq_printf(m, " %lu", count_per_node[nid]);
+ seq_puts(m, "\n");
+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL);
+
+ rcu_read_unlock();
+ up_read(&shrinker_rwsem);
+
+ kfree(count_per_node);
+ return ret;
+}
+DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count_memcg_node);
+
+static ssize_t shrinker_debugfs_scan_memcg_node_write(struct file *file,
+ const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ struct shrinker *shrinker = (struct shrinker *)file->private_data;
+ unsigned long nr_to_scan = 0, ino;
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ };
+ struct mem_cgroup *memcg;
+ int nid;
+ char kbuf[72];
+ int read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
+ ssize_t ret;
+
+ if (copy_from_user(kbuf, buf, read_len))
+ return -EFAULT;
+ kbuf[read_len] = '\0';
+
+ if (sscanf(kbuf, "%lu %d %lu", &ino, &nid, &nr_to_scan) < 2)
+ return -EINVAL;
+
+ if (nid < 0 || 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;
+ }
+
+ ret = down_read_killable(&shrinker_rwsem);
+ if (ret) {
+ mem_cgroup_put(memcg);
+ return ret;
+ }
+
+ sc.nid = nid;
+ sc.memcg = memcg;
+ sc.nr_to_scan = nr_to_scan;
+ sc.nr_scanned = nr_to_scan;
+
+ shrinker->scan_objects(shrinker, &sc);
+
+ up_read(&shrinker_rwsem);
+ mem_cgroup_put(memcg);
+
+ return ret ? ret : size;
+}
+
+static const struct file_operations shrinker_debugfs_scan_memcg_node_fops = {
+ .owner = THIS_MODULE,
+ .open = shrinker_debugfs_scan_open,
+ .write = shrinker_debugfs_scan_memcg_node_write,
+};
+#endif /* CONFIG_MEMCG */
+#endif /* CONFIG_NUMA */
+
int shrinker_debugfs_add(struct shrinker *shrinker)
{
struct dentry *entry;
@@ -265,6 +444,27 @@ int shrinker_debugfs_add(struct shrinker *shrinker)
}
#endif
+#ifdef CONFIG_NUMA
+ /* create numa and memcg/numa interfaces */
+ if ((shrinker->flags & SHRINKER_NUMA_AWARE) && nr_node_ids > 1) {
+ debugfs_create_file("count_node", 0220, entry, shrinker,
+ &shrinker_debugfs_count_node_fops);
+ debugfs_create_file("scan_node", 0440, entry, shrinker,
+ &shrinker_debugfs_scan_node_fops);
+
+#ifdef CONFIG_MEMCG
+ if (shrinker->flags & SHRINKER_MEMCG_AWARE) {
+ debugfs_create_file("count_memcg_node", 0220, entry,
+ shrinker,
+ &shrinker_debugfs_count_memcg_node_fops);
+ debugfs_create_file("scan_memcg_node", 0440, entry,
+ shrinker,
+ &shrinker_debugfs_scan_memcg_node_fops);
+ }
+#endif /* CONFIG_MEMCG */
+ }
+#endif /* CONFIG_NUMA */
+
return 0;
}
--
2.35.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 5/5] mm: provide shrinkers with names
2022-04-22 1:58 [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
` (3 preceding siblings ...)
2022-04-22 1:58 ` [PATCH v1 4/5] mm: introduce numa " Roman Gushchin
@ 2022-04-22 1:58 ` Roman Gushchin
2022-04-22 14:57 ` [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
5 siblings, 0 replies; 7+ messages in thread
From: Roman Gushchin @ 2022-04-22 1:58 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Dave Chinner, linux-kernel, Yang Shi, Kent Overstreet,
Hillf Danton, Roman Gushchin
Currently shrinkers are anonymous objects. For debugging purposes they
can be identified by count/scan function names, but it's not always
useful: e.g. for superblock's shrinkers it's nice to have at least
an idea of to which superblock the shrinker belongs.
This commit adds names to shrinkers. register_shrinker() and
prealloc_shrinker() functions are extended to take a format and
arguments to master a name. If CONFIG_SHRINKER_DEBUG is on,
the name is saved until the corresponding kobject is created,
otherwise it's simple ignored.
After this change the shrinker debugfs directory looks like:
$ cd /sys/kernel/debug/shrinker/
$ ls
dqcache-16 sb-cgroup2-30 sb-hugetlbfs-33 sb-proc-41 sb-selinuxfs-22 sb-tmpfs-40 sb-zsmalloc-19
kfree_rcu-0 sb-configfs-23 sb-iomem-12 sb-proc-44 sb-sockfs-8 sb-tmpfs-42 shadow-18
sb-aio-20 sb-dax-11 sb-mqueue-21 sb-proc-45 sb-sysfs-26 sb-tmpfs-43 thp_deferred_split-10
sb-anon_inodefs-15 sb-debugfs-7 sb-nsfs-4 sb-proc-47 sb-tmpfs-1 sb-tmpfs-46 thp_zero-9
sb-bdev-3 sb-devpts-28 sb-pipefs-14 sb-pstore-31 sb-tmpfs-27 sb-tmpfs-49 xfs_buf-37
sb-bpf-32 sb-devtmpfs-5 sb-proc-25 sb-rootfs-2 sb-tmpfs-29 sb-tracefs-13 xfs_inodegc-38
sb-btrfs-24 sb-hugetlbfs-17 sb-proc-39 sb-securityfs-6 sb-tmpfs-35 sb-xfs-36 zspool-34
Signed-off-by: Roman Gushchin <roman.gushchin@linux.dev>
---
arch/x86/kvm/mmu/mmu.c | 2 +-
drivers/android/binder_alloc.c | 2 +-
drivers/gpu/drm/i915/gem/i915_gem_shrinker.c | 3 +-
drivers/gpu/drm/msm/msm_gem_shrinker.c | 2 +-
.../gpu/drm/panfrost/panfrost_gem_shrinker.c | 2 +-
drivers/gpu/drm/ttm/ttm_pool.c | 2 +-
drivers/md/bcache/btree.c | 2 +-
drivers/md/dm-bufio.c | 2 +-
drivers/md/dm-zoned-metadata.c | 2 +-
drivers/md/raid5.c | 2 +-
drivers/misc/vmw_balloon.c | 2 +-
drivers/virtio/virtio_balloon.c | 2 +-
drivers/xen/xenbus/xenbus_probe_backend.c | 2 +-
fs/erofs/utils.c | 2 +-
fs/ext4/extents_status.c | 3 +-
fs/f2fs/super.c | 2 +-
fs/gfs2/glock.c | 2 +-
fs/gfs2/main.c | 2 +-
fs/jbd2/journal.c | 2 +-
fs/mbcache.c | 2 +-
fs/nfs/nfs42xattr.c | 7 ++-
fs/nfs/super.c | 2 +-
fs/nfsd/filecache.c | 2 +-
fs/nfsd/nfscache.c | 2 +-
fs/quota/dquot.c | 2 +-
fs/super.c | 2 +-
fs/ubifs/super.c | 2 +-
fs/xfs/xfs_buf.c | 2 +-
fs/xfs/xfs_icache.c | 2 +-
fs/xfs/xfs_qm.c | 2 +-
include/linux/shrinker.h | 5 +-
kernel/rcu/tree.c | 2 +-
mm/huge_memory.c | 4 +-
mm/shrinker_debug.c | 7 ++-
mm/vmscan.c | 60 ++++++++++++++++++-
mm/workingset.c | 2 +-
mm/zsmalloc.c | 2 +-
net/sunrpc/auth.c | 2 +-
38 files changed, 106 insertions(+), 45 deletions(-)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index c623019929a7..8cfabdd63406 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -6283,7 +6283,7 @@ int kvm_mmu_vendor_module_init(void)
if (percpu_counter_init(&kvm_total_used_mmu_pages, 0, GFP_KERNEL))
goto out;
- ret = register_shrinker(&mmu_shrinker);
+ ret = register_shrinker(&mmu_shrinker, "mmu");
if (ret)
goto out;
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index 2ac1008a5f39..951343c41ba8 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -1084,7 +1084,7 @@ int binder_alloc_shrinker_init(void)
int ret = list_lru_init(&binder_alloc_lru);
if (ret == 0) {
- ret = register_shrinker(&binder_shrinker);
+ ret = register_shrinker(&binder_shrinker, "binder");
if (ret)
list_lru_destroy(&binder_alloc_lru);
}
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
index 6a6ff98a8746..85524ef92ea4 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
@@ -426,7 +426,8 @@ void i915_gem_driver_register__shrinker(struct drm_i915_private *i915)
i915->mm.shrinker.count_objects = i915_gem_shrinker_count;
i915->mm.shrinker.seeks = DEFAULT_SEEKS;
i915->mm.shrinker.batch = 4096;
- drm_WARN_ON(&i915->drm, register_shrinker(&i915->mm.shrinker));
+ drm_WARN_ON(&i915->drm, register_shrinker(&i915->mm.shrinker,
+ "drm_i915_gem"));
i915->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom;
drm_WARN_ON(&i915->drm, register_oom_notifier(&i915->mm.oom_notifier));
diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c
index 086dacf2f26a..2d3cf4f13dfd 100644
--- a/drivers/gpu/drm/msm/msm_gem_shrinker.c
+++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c
@@ -221,7 +221,7 @@ void msm_gem_shrinker_init(struct drm_device *dev)
priv->shrinker.count_objects = msm_gem_shrinker_count;
priv->shrinker.scan_objects = msm_gem_shrinker_scan;
priv->shrinker.seeks = DEFAULT_SEEKS;
- WARN_ON(register_shrinker(&priv->shrinker));
+ WARN_ON(register_shrinker(&priv->shrinker, "drm_msm_gem"));
priv->vmap_notifier.notifier_call = msm_gem_shrinker_vmap;
WARN_ON(register_vmap_purge_notifier(&priv->vmap_notifier));
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index 77e7cb6d1ae3..0d028266ee9e 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -103,7 +103,7 @@ void panfrost_gem_shrinker_init(struct drm_device *dev)
pfdev->shrinker.count_objects = panfrost_gem_shrinker_count;
pfdev->shrinker.scan_objects = panfrost_gem_shrinker_scan;
pfdev->shrinker.seeks = DEFAULT_SEEKS;
- WARN_ON(register_shrinker(&pfdev->shrinker));
+ WARN_ON(register_shrinker(&pfdev->shrinker, "drm_panfrost"));
}
/**
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index 1bba0a0ed3f9..b8b41d242197 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -722,7 +722,7 @@ int ttm_pool_mgr_init(unsigned long num_pages)
mm_shrinker.count_objects = ttm_pool_shrinker_count;
mm_shrinker.scan_objects = ttm_pool_shrinker_scan;
mm_shrinker.seeks = 1;
- return register_shrinker(&mm_shrinker);
+ return register_shrinker(&mm_shrinker, "drm_ttm_pool");
}
/**
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index ad9f16689419..c1f734ab86b3 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -812,7 +812,7 @@ int bch_btree_cache_alloc(struct cache_set *c)
c->shrink.seeks = 4;
c->shrink.batch = c->btree_pages * 2;
- if (register_shrinker(&c->shrink))
+ if (register_shrinker(&c->shrink, "btree"))
pr_warn("bcache: %s: could not register shrinker\n",
__func__);
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 5ffa1dcf84cf..bcc95898c341 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -1806,7 +1806,7 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign
c->shrinker.scan_objects = dm_bufio_shrink_scan;
c->shrinker.seeks = 1;
c->shrinker.batch = 0;
- r = register_shrinker(&c->shrinker);
+ r = register_shrinker(&c->shrinker, "dm_bufio");
if (r)
goto bad;
diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c
index d1ea66114d14..05f2fd12066b 100644
--- a/drivers/md/dm-zoned-metadata.c
+++ b/drivers/md/dm-zoned-metadata.c
@@ -2944,7 +2944,7 @@ int dmz_ctr_metadata(struct dmz_dev *dev, int num_dev,
zmd->mblk_shrinker.seeks = DEFAULT_SEEKS;
/* Metadata cache shrinker */
- ret = register_shrinker(&zmd->mblk_shrinker);
+ ret = register_shrinker(&zmd->mblk_shrinker, "md_meta");
if (ret) {
dmz_zmd_err(zmd, "Register metadata cache shrinker failed");
goto err;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 59f91e392a2a..1b326b93155c 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -7383,7 +7383,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
conf->shrinker.count_objects = raid5_cache_count;
conf->shrinker.batch = 128;
conf->shrinker.flags = 0;
- if (register_shrinker(&conf->shrinker)) {
+ if (register_shrinker(&conf->shrinker, "md")) {
pr_warn("md/raid:%s: couldn't register shrinker.\n",
mdname(mddev));
goto abort;
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c
index f1d8ba6d4857..6c9ddf1187dd 100644
--- a/drivers/misc/vmw_balloon.c
+++ b/drivers/misc/vmw_balloon.c
@@ -1587,7 +1587,7 @@ static int vmballoon_register_shrinker(struct vmballoon *b)
b->shrinker.count_objects = vmballoon_shrinker_count;
b->shrinker.seeks = DEFAULT_SEEKS;
- r = register_shrinker(&b->shrinker);
+ r = register_shrinker(&b->shrinker, "vmw_balloon");
if (r == 0)
b->shrinker_registered = true;
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index f4c34a2a6b8e..093e06e19d0e 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -875,7 +875,7 @@ static int virtio_balloon_register_shrinker(struct virtio_balloon *vb)
vb->shrinker.count_objects = virtio_balloon_shrinker_count;
vb->shrinker.seeks = DEFAULT_SEEKS;
- return register_shrinker(&vb->shrinker);
+ return register_shrinker(&vb->shrinker, "virtio_valloon");
}
static int virtballoon_probe(struct virtio_device *vdev)
diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c
index 5abded97e1a7..a6c5e344017d 100644
--- a/drivers/xen/xenbus/xenbus_probe_backend.c
+++ b/drivers/xen/xenbus/xenbus_probe_backend.c
@@ -305,7 +305,7 @@ static int __init xenbus_probe_backend_init(void)
register_xenstore_notifier(&xenstore_notifier);
- if (register_shrinker(&backend_memory_shrinker))
+ if (register_shrinker(&backend_memory_shrinker, "xen_backend"))
pr_warn("shrinker registration failed\n");
return 0;
diff --git a/fs/erofs/utils.c b/fs/erofs/utils.c
index ec9a1d780dc1..67eb64fadd4f 100644
--- a/fs/erofs/utils.c
+++ b/fs/erofs/utils.c
@@ -282,7 +282,7 @@ static struct shrinker erofs_shrinker_info = {
int __init erofs_init_shrinker(void)
{
- return register_shrinker(&erofs_shrinker_info);
+ return register_shrinker(&erofs_shrinker_info, "erofs");
}
void erofs_exit_shrinker(void)
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index 9a3a8996aacf..a7aa79d580e5 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -1650,11 +1650,10 @@ int ext4_es_register_shrinker(struct ext4_sb_info *sbi)
err = percpu_counter_init(&sbi->s_es_stats.es_stats_shk_cnt, 0, GFP_KERNEL);
if (err)
goto err3;
-
sbi->s_es_shrinker.scan_objects = ext4_es_scan;
sbi->s_es_shrinker.count_objects = ext4_es_count;
sbi->s_es_shrinker.seeks = DEFAULT_SEEKS;
- err = register_shrinker(&sbi->s_es_shrinker);
+ err = register_shrinker(&sbi->s_es_shrinker, "ext4_es");
if (err)
goto err4;
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 4368f90571bd..2fc40a1635f3 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -4579,7 +4579,7 @@ static int __init init_f2fs_fs(void)
err = f2fs_init_sysfs();
if (err)
goto free_garbage_collection_cache;
- err = register_shrinker(&f2fs_shrinker_info);
+ err = register_shrinker(&f2fs_shrinker_info, "f2fs");
if (err)
goto free_sysfs;
err = register_filesystem(&f2fs_fs_type);
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 26169cedcefc..791c23d9f7e7 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -2549,7 +2549,7 @@ int __init gfs2_glock_init(void)
return -ENOMEM;
}
- ret = register_shrinker(&glock_shrinker);
+ ret = register_shrinker(&glock_shrinker, "gfs2_glock");
if (ret) {
destroy_workqueue(gfs2_delete_workqueue);
destroy_workqueue(glock_workqueue);
diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c
index 28d0eb23e18e..dde981b78488 100644
--- a/fs/gfs2/main.c
+++ b/fs/gfs2/main.c
@@ -150,7 +150,7 @@ static int __init init_gfs2_fs(void)
if (!gfs2_trans_cachep)
goto fail_cachep8;
- error = register_shrinker(&gfs2_qd_shrinker);
+ error = register_shrinker(&gfs2_qd_shrinker, "gfs2_qd");
if (error)
goto fail_shrinker;
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index c0cbeeaec2d1..e7786445ecc1 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -1418,7 +1418,7 @@ static journal_t *journal_init_common(struct block_device *bdev,
if (percpu_counter_init(&journal->j_checkpoint_jh_count, 0, GFP_KERNEL))
goto err_cleanup;
- if (register_shrinker(&journal->j_shrinker)) {
+ if (register_shrinker(&journal->j_shrinker, "jbd2_journal")) {
percpu_counter_destroy(&journal->j_checkpoint_jh_count);
goto err_cleanup;
}
diff --git a/fs/mbcache.c b/fs/mbcache.c
index 97c54d3a2227..379dc5b0b6ad 100644
--- a/fs/mbcache.c
+++ b/fs/mbcache.c
@@ -367,7 +367,7 @@ struct mb_cache *mb_cache_create(int bucket_bits)
cache->c_shrink.count_objects = mb_cache_count;
cache->c_shrink.scan_objects = mb_cache_scan;
cache->c_shrink.seeks = DEFAULT_SEEKS;
- if (register_shrinker(&cache->c_shrink)) {
+ if (register_shrinker(&cache->c_shrink, "mb_cache")) {
kfree(cache->c_hash);
kfree(cache);
goto err_out;
diff --git a/fs/nfs/nfs42xattr.c b/fs/nfs/nfs42xattr.c
index e7b34f7e0614..147b8a2f2dc6 100644
--- a/fs/nfs/nfs42xattr.c
+++ b/fs/nfs/nfs42xattr.c
@@ -1017,15 +1017,16 @@ int __init nfs4_xattr_cache_init(void)
if (ret)
goto out2;
- ret = register_shrinker(&nfs4_xattr_cache_shrinker);
+ ret = register_shrinker(&nfs4_xattr_cache_shrinker, "nfs_xattr_cache");
if (ret)
goto out1;
- ret = register_shrinker(&nfs4_xattr_entry_shrinker);
+ ret = register_shrinker(&nfs4_xattr_entry_shrinker, "nfs_xattr_entry");
if (ret)
goto out;
- ret = register_shrinker(&nfs4_xattr_large_entry_shrinker);
+ ret = register_shrinker(&nfs4_xattr_large_entry_shrinker,
+ "nfs_xattr_large_entry");
if (!ret)
return 0;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 6ab5eeb000dc..c7a2aef911f1 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -149,7 +149,7 @@ int __init register_nfs_fs(void)
ret = nfs_register_sysctl();
if (ret < 0)
goto error_2;
- ret = register_shrinker(&acl_shrinker);
+ ret = register_shrinker(&acl_shrinker, "nfs_acl");
if (ret < 0)
goto error_3;
#ifdef CONFIG_NFS_V4_2
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 2c1b027774d4..9c2879a3c3c0 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -666,7 +666,7 @@ nfsd_file_cache_init(void)
goto out_err;
}
- ret = register_shrinker(&nfsd_file_shrinker);
+ ret = register_shrinker(&nfsd_file_shrinker, "nfsd_filecache");
if (ret) {
pr_err("nfsd: failed to register nfsd_file_shrinker: %d\n", ret);
goto out_lru;
diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c
index 0b3f12aa37ff..f1cfb06d0be5 100644
--- a/fs/nfsd/nfscache.c
+++ b/fs/nfsd/nfscache.c
@@ -176,7 +176,7 @@ int nfsd_reply_cache_init(struct nfsd_net *nn)
nn->nfsd_reply_cache_shrinker.scan_objects = nfsd_reply_cache_scan;
nn->nfsd_reply_cache_shrinker.count_objects = nfsd_reply_cache_count;
nn->nfsd_reply_cache_shrinker.seeks = 1;
- status = register_shrinker(&nn->nfsd_reply_cache_shrinker);
+ status = register_shrinker(&nn->nfsd_reply_cache_shrinker, "nfsd_reply");
if (status)
goto out_stats_destroy;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index a74aef99bd3d..854d2b1d0914 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -2985,7 +2985,7 @@ static int __init dquot_init(void)
pr_info("VFS: Dquot-cache hash table entries: %ld (order %ld,"
" %ld bytes)\n", nr_hash, order, (PAGE_SIZE << order));
- if (register_shrinker(&dqcache_shrinker))
+ if (register_shrinker(&dqcache_shrinker, "dqcache"))
panic("Cannot register dquot shrinker");
return 0;
diff --git a/fs/super.c b/fs/super.c
index 60f57c7bc0a6..5776a3dbaf10 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -265,7 +265,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
s->s_shrink.count_objects = super_cache_count;
s->s_shrink.batch = 1024;
s->s_shrink.flags = SHRINKER_NUMA_AWARE | SHRINKER_MEMCG_AWARE;
- if (prealloc_shrinker(&s->s_shrink))
+ if (prealloc_shrinker(&s->s_shrink, "sb-%s", type->name))
goto fail;
if (list_lru_init_memcg(&s->s_dentry_lru, &s->s_shrink))
goto fail;
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index bad67455215f..a3663d201f64 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -2430,7 +2430,7 @@ static int __init ubifs_init(void)
if (!ubifs_inode_slab)
return -ENOMEM;
- err = register_shrinker(&ubifs_shrinker_info);
+ err = register_shrinker(&ubifs_shrinker_info, "ubifs");
if (err)
goto out_slab;
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index e1afb9e503e1..5645e92df0c9 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -1986,7 +1986,7 @@ xfs_alloc_buftarg(
btp->bt_shrinker.scan_objects = xfs_buftarg_shrink_scan;
btp->bt_shrinker.seeks = DEFAULT_SEEKS;
btp->bt_shrinker.flags = SHRINKER_NUMA_AWARE;
- if (register_shrinker(&btp->bt_shrinker))
+ if (register_shrinker(&btp->bt_shrinker, "xfs_buf"))
goto error_pcpu;
return btp;
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index bffd6eb0b298..d0c4e74ff763 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -2198,5 +2198,5 @@ xfs_inodegc_register_shrinker(
shrink->flags = SHRINKER_NONSLAB;
shrink->batch = XFS_INODEGC_SHRINKER_BATCH;
- return register_shrinker(shrink);
+ return register_shrinker(shrink, "xfs_inodegc");
}
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index f165d1a3de1d..93ded9e81f49 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -686,7 +686,7 @@ xfs_qm_init_quotainfo(
qinf->qi_shrinker.seeks = DEFAULT_SEEKS;
qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE;
- error = register_shrinker(&qinf->qi_shrinker);
+ error = register_shrinker(&qinf->qi_shrinker, "xfs_qm");
if (error)
goto out_free_inos;
diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h
index 17985a890887..8dffe56d6ad1 100644
--- a/include/linux/shrinker.h
+++ b/include/linux/shrinker.h
@@ -75,6 +75,7 @@ struct shrinker {
#endif
#ifdef CONFIG_SHRINKER_DEBUG
int debugfs_id;
+ char *name;
struct dentry *debugfs_entry;
#endif
/* objs pending delete, per node */
@@ -92,9 +93,9 @@ struct shrinker {
*/
#define SHRINKER_NONSLAB (1 << 3)
-extern int prealloc_shrinker(struct shrinker *shrinker);
+extern int prealloc_shrinker(struct shrinker *shrinker, const char *fmt, ...);
extern void register_shrinker_prepared(struct shrinker *shrinker);
-extern int register_shrinker(struct shrinker *shrinker);
+extern int register_shrinker(struct shrinker *shrinker, const char *fmt, ...);
extern void unregister_shrinker(struct shrinker *shrinker);
extern void free_prealloced_shrinker(struct shrinker *shrinker);
extern void synchronize_shrinkers(void);
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 5c587e00811c..b4c66916bea9 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -4978,7 +4978,7 @@ static void __init kfree_rcu_batch_init(void)
INIT_DELAYED_WORK(&krcp->page_cache_work, fill_page_cache_func);
krcp->initialized = true;
}
- if (register_shrinker(&kfree_rcu_shrinker))
+ if (register_shrinker(&kfree_rcu_shrinker, "kfree_rcu"))
pr_err("Failed to register kfree_rcu() shrinker!\n");
}
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 541c1eba072f..76086984347f 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -423,10 +423,10 @@ static int __init hugepage_init(void)
if (err)
goto err_slab;
- err = register_shrinker(&huge_zero_page_shrinker);
+ err = register_shrinker(&huge_zero_page_shrinker, "thp_zero");
if (err)
goto err_hzp_shrinker;
- err = register_shrinker(&deferred_split_shrinker);
+ err = register_shrinker(&deferred_split_shrinker, "thp_deferred_split");
if (err)
goto err_split_shrinker;
diff --git a/mm/shrinker_debug.c b/mm/shrinker_debug.c
index 81350b64bf01..e2f7ef5fecfc 100644
--- a/mm/shrinker_debug.c
+++ b/mm/shrinker_debug.c
@@ -418,7 +418,7 @@ int shrinker_debugfs_add(struct shrinker *shrinker)
return id;
shrinker->debugfs_id = id;
- snprintf(buf, sizeof(buf), "%d", id);
+ snprintf(buf, sizeof(buf), "%s-%d", shrinker->name, id);
/* create debugfs entry */
entry = debugfs_create_dir(buf, shrinker_debugfs_root);
@@ -465,6 +465,10 @@ int shrinker_debugfs_add(struct shrinker *shrinker)
}
#endif /* CONFIG_NUMA */
+ /* shrinker->name is not needed anymore, free it */
+ kfree(shrinker->name);
+ shrinker->name = NULL;
+
return 0;
}
@@ -477,6 +481,7 @@ void shrinker_debugfs_remove(struct shrinker *shrinker)
debugfs_remove_recursive(shrinker->debugfs_entry);
ida_free(&shrinker_debugfs_ida, shrinker->debugfs_id);
+ WARN_ON_ONCE(shrinker->name);
}
static int __init shrinker_debugfs_init(void)
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 121a54a1602b..6025cfda4932 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -613,7 +613,7 @@ static unsigned long lruvec_lru_size(struct lruvec *lruvec, enum lru_list lru,
/*
* Add a shrinker callback to be called from the vm.
*/
-int prealloc_shrinker(struct shrinker *shrinker)
+static int __prealloc_shrinker(struct shrinker *shrinker)
{
unsigned int size;
int err;
@@ -637,6 +637,34 @@ int prealloc_shrinker(struct shrinker *shrinker)
return 0;
}
+#ifdef CONFIG_SHRINKER_DEBUG
+int prealloc_shrinker(struct shrinker *shrinker, const char *fmt, ...)
+{
+ int err;
+ char buf[64];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vscnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ shrinker->name = kstrdup(buf, GFP_KERNEL);
+ if (!shrinker->name)
+ return -ENOMEM;
+
+ err = __prealloc_shrinker(shrinker);
+ if (err)
+ kfree(shrinker->name);
+
+ return err;
+}
+#else
+int prealloc_shrinker(struct shrinker *shrinker, const char *fmt, ...)
+{
+ return __prealloc_shrinker(shrinker);
+}
+#endif
+
void free_prealloced_shrinker(struct shrinker *shrinker)
{
if (shrinker->flags & SHRINKER_MEMCG_AWARE) {
@@ -648,6 +676,9 @@ void free_prealloced_shrinker(struct shrinker *shrinker)
kfree(shrinker->nr_deferred);
shrinker->nr_deferred = NULL;
+#ifdef CONFIG_SHRINKER_DEBUG
+ kfree(shrinker->name);
+#endif
}
void register_shrinker_prepared(struct shrinker *shrinker)
@@ -659,15 +690,38 @@ void register_shrinker_prepared(struct shrinker *shrinker)
up_write(&shrinker_rwsem);
}
-int register_shrinker(struct shrinker *shrinker)
+static int __register_shrinker(struct shrinker *shrinker)
{
- int err = prealloc_shrinker(shrinker);
+ int err = __prealloc_shrinker(shrinker);
if (err)
return err;
register_shrinker_prepared(shrinker);
return 0;
}
+
+#ifdef CONFIG_SHRINKER_DEBUG
+int register_shrinker(struct shrinker *shrinker, const char *fmt, ...)
+{
+ char buf[64];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vscnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ shrinker->name = kstrdup(buf, GFP_KERNEL);
+ if (!shrinker->name)
+ return -ENOMEM;
+
+ return __register_shrinker(shrinker);
+}
+#else
+int register_shrinker(struct shrinker *shrinker, const char *fmt, ...)
+{
+ return __register_shrinker(shrinker);
+}
+#endif
EXPORT_SYMBOL(register_shrinker);
/*
diff --git a/mm/workingset.c b/mm/workingset.c
index 592569a8974c..840986179cf3 100644
--- a/mm/workingset.c
+++ b/mm/workingset.c
@@ -625,7 +625,7 @@ static int __init workingset_init(void)
pr_info("workingset: timestamp_bits=%d max_order=%d bucket_order=%u\n",
timestamp_bits, max_order, bucket_order);
- ret = prealloc_shrinker(&workingset_shadow_shrinker);
+ ret = prealloc_shrinker(&workingset_shadow_shrinker, "shadow");
if (ret)
goto err;
ret = __list_lru_init(&shadow_nodes, true, &shadow_nodes_key,
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 9152fbde33b5..a19de176f604 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -2188,7 +2188,7 @@ static int zs_register_shrinker(struct zs_pool *pool)
pool->shrinker.batch = 0;
pool->shrinker.seeks = DEFAULT_SEEKS;
- return register_shrinker(&pool->shrinker);
+ return register_shrinker(&pool->shrinker, "zspool");
}
/**
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index 682fcd24bf43..a29742a9c3f1 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -874,7 +874,7 @@ int __init rpcauth_init_module(void)
err = rpc_init_authunix();
if (err < 0)
goto out1;
- err = register_shrinker(&rpc_cred_shrinker);
+ err = register_shrinker(&rpc_cred_shrinker, "rpc_cred");
if (err < 0)
goto out2;
return 0;
--
2.35.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 0/5] mm: introduce shrinker debugfs interface
2022-04-22 1:58 [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
` (4 preceding siblings ...)
2022-04-22 1:58 ` [PATCH v1 5/5] mm: provide shrinkers with names Roman Gushchin
@ 2022-04-22 14:57 ` Roman Gushchin
5 siblings, 0 replies; 7+ messages in thread
From: Roman Gushchin @ 2022-04-22 14:57 UTC (permalink / raw)
To: Andrew Morton, linux-mm
Cc: Dave Chinner, linux-kernel, Yang Shi, Kent Overstreet, Hillf Danton
On Thu, Apr 21, 2022 at 06:58:48PM -0700, Roman Gushchin wrote:
> There are 50+ different shrinkers in the kernel, many with their own bells and
> whistles. Under the memory pressure the kernel applies some pressure on each of
> them in the order of which they were created/registered in the system. Some
> of them can contain only few objects, some can be quite large. Some can be
> effective at reclaiming memory, some not.
>
> The only existing debugging mechanism is a couple of tracepoints in
> do_shrink_slab(): mm_shrink_slab_start and mm_shrink_slab_end. They aren't
> covering everything though: shrinkers which report 0 objects will never show up,
> there is no support for memcg-aware shrinkers. Shrinkers are identified by their
> scan function, which is not always enough (e.g. hard to guess which super
> block's shrinker it is having only "super_cache_scan"). They are a passive
> mechanism: there is no way to call into counting and scanning of an individual
> shrinker and profile it.
>
> To provide a better visibility and debug options for memory shrinkers
> this patchset introduces a /sys/kernel/debug/shrinker interface, to some extent
> similar to /sys/kernel/slab.
>
> For each shrinker registered in the system a directory is created. The directory
> contains "count" and "scan" files, which allow to trigger count_objects()
> and scan_objects() callbacks. For memcg-aware and numa-aware shrinkers
> count_memcg, scan_memcg, count_node, scan_node, count_memcg_node
> and scan_memcg_node are additionally provided. They allow to get per-memcg
> and/or per-node object count and shrink only a specific memcg/node.
>
> To make debugging more pleasant, the patchset also names all shrinkers,
> so that debugfs entries can have more meaningful names.
>
> v1:
> 1) switched to debugfs, suggested by Mike, Andrew, Greg and others
> 2) switched to seq_file API for output, no PAGE_SIZE limit anymore, by Andrew
> 3) switched to down_read_killable(), suggested by Hillf
> 4) dropped stateful filtering and "freed" returning, by Kent
> 5) added docs, by Andrew
Not quite true, gonna add it and also a cgroup tree inspection tool
and send out v2.
Thanks!
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2022-04-22 14:58 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-22 1:58 [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 1/5] mm: introduce debugfs interface for kernel memory shrinkers Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 2/5] mm: memcontrol: introduce mem_cgroup_ino() and mem_cgroup_get_from_ino() Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 3/5] mm: introduce memcg interfaces for shrinker debugfs Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 4/5] mm: introduce numa " Roman Gushchin
2022-04-22 1:58 ` [PATCH v1 5/5] mm: provide shrinkers with names Roman Gushchin
2022-04-22 14:57 ` [PATCH v1 0/5] mm: introduce shrinker debugfs interface Roman Gushchin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox