Skip to content

Commit

Permalink
mm: shrinkers: add scan interface for shrinker debugfs
Browse files Browse the repository at this point in the history
Add a scan interface which allows to trigger scanning of a particular
shrinker and specify memcg and numa node.  It's useful for testing,
debugging and profiling of a specific scan_objects() callback.  Unlike
alternatives (creating a real memory pressure and dropping caches via
/proc/sys/vm/drop_caches) this interface allows to interact with only one
shrinker at once.  Also, if a shrinker is misreporting the number of
objects (as some do), it doesn't affect scanning.

[[email protected]: improve typing, fix arg count checking]
  Link: https://lkml.kernel.org/r/YpgKttTowT22mKPQ@carbon
[[email protected]: fix arg count checking]
Link: https://lkml.kernel.org/r/[email protected]
Signed-off-by: Roman Gushchin <[email protected]>
Acked-by: Muchun Song <[email protected]>
Cc: Christophe JAILLET <[email protected]>
Cc: Dave Chinner <[email protected]>
Cc: Hillf Danton <[email protected]>
Cc: Kent Overstreet <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
  • Loading branch information
rgushchin authored and akpm00 committed Jul 4, 2022
1 parent d261ea2 commit bbf535f
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 4 deletions.
39 changes: 35 additions & 4 deletions Documentation/admin-guide/mm/shrinker_debugfs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ Shrinker Debugfs Interface
==========================

Shrinker debugfs interface provides a visibility into the kernel memory
shrinkers subsystem and allows to get information about individual shrinkers.
shrinkers subsystem and allows to get information about individual shrinkers
and interact with them.

For each shrinker registered in the system a directory in **<debugfs>/shrinker/**
is created. The directory's name is composed from the shrinker's name and an
unique id: e.g. *kfree_rcu-0* or *sb-xfs:vda1-36*.

Each shrinker directory contains the **count** file, which allows to trigger
the *count_objects()* callback for each memcg and numa node (if applicable).
Each shrinker directory contains **count** and **scan** files, which allow to
trigger *count_objects()* and *scan_objects()* callbacks for each memcg and
numa node (if applicable).

Usage:
------
Expand Down Expand Up @@ -43,7 +45,7 @@ Usage:

$ cd sb-btrfs\:vda2-24/
$ ls
count
count scan

3. *Count objects*

Expand Down Expand Up @@ -102,3 +104,32 @@ Usage:
2877 84 0
293 1 0
735 8 0

4. *Scan objects*

The expected input format::

<cgroup inode id> <numa id> <number of objects to scan>

For a non-memcg-aware shrinker or on a system with no memory
cgrups **0** should be passed as cgroup id.
::

$ cd /sys/kernel/debug/shrinker/
$ cd sb-btrfs\:vda2-24/

$ cat count | head -n 5
1 212 0
21 97 0
55 802 5
2367 2 0
225 13 0

$ echo "55 0 200" > scan

$ cat count | head -n 5
1 212 0
21 96 0
55 752 5
2367 2 0
225 13 0
74 changes: 74 additions & 0 deletions mm/shrinker_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,78 @@ static int shrinker_debugfs_count_show(struct seq_file *m, void *v)
}
DEFINE_SHOW_ATTRIBUTE(shrinker_debugfs_count);

static int shrinker_debugfs_scan_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
return nonseekable_open(inode, file);
}

static ssize_t shrinker_debugfs_scan_write(struct file *file,
const char __user *buf,
size_t size, loff_t *pos)
{
struct shrinker *shrinker = file->private_data;
unsigned long nr_to_scan = 0, ino, read_len;
struct shrink_control sc = {
.gfp_mask = GFP_KERNEL,
};
struct mem_cgroup *memcg = NULL;
int nid;
char kbuf[72];
ssize_t ret;

read_len = size < (sizeof(kbuf) - 1) ? size : (sizeof(kbuf) - 1);
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) != 3)
return -EINVAL;

if (nid < 0 || nid >= nr_node_ids)
return -EINVAL;

if (nr_to_scan == 0)
return size;

if (shrinker->flags & SHRINKER_MEMCG_AWARE) {
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;
}
} else if (ino != 0) {
return -EINVAL;
}

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 size;
}

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;
Expand Down Expand Up @@ -128,6 +200,8 @@ int shrinker_debugfs_add(struct shrinker *shrinker)

debugfs_create_file("count", 0220, entry, shrinker,
&shrinker_debugfs_count_fops);
debugfs_create_file("scan", 0440, entry, shrinker,
&shrinker_debugfs_scan_fops);
return 0;
}

Expand Down

0 comments on commit bbf535f

Please sign in to comment.