From patchwork Fri Apr 22 01:58:52 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roman Gushchin X-Patchwork-Id: 12822660 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15FC3C433F5 for ; Fri, 22 Apr 2022 01:59:14 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id A9FC06B007B; Thu, 21 Apr 2022 21:59:13 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id A25886B007D; Thu, 21 Apr 2022 21:59:13 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 8EE866B007E; Thu, 21 Apr 2022 21:59:13 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (relay.hostedemail.com [64.99.140.26]) by kanga.kvack.org (Postfix) with ESMTP id 7EE0B6B007B for ; Thu, 21 Apr 2022 21:59:13 -0400 (EDT) Received: from smtpin27.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay06.hostedemail.com (Postfix) with ESMTP id 5DA3E22E5C for ; Fri, 22 Apr 2022 01:59:13 +0000 (UTC) X-FDA: 79382857386.27.E936913 Received: from out0.migadu.com (out0.migadu.com [94.23.1.103]) by imf26.hostedemail.com (Postfix) with ESMTP id 4FCF2140011 for ; Fri, 22 Apr 2022 01:59:11 +0000 (UTC) X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1650592751; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=zzvvA3SXvofoJHZNbBulanDIE6i4rnzYjcD9jlw7cIA=; b=drtHT1Ewzu6/crw875L0g+fzthtUNItM+1hYvpl0gtM6fOVI4zCGubfzJu2dO4nINPkuGh XAQIt1/o+VtjUMrIEmVcejVK7ernJVF71xiNYFRFsvSe4tj5btsWV1XtAmp3xzLZSJuiae tRIQaxo5czwvAXOBJoML3ehI16vlU+g= From: Roman Gushchin To: Andrew Morton , linux-mm@kvack.org Cc: Dave Chinner , linux-kernel@vger.kernel.org, Yang Shi , Kent Overstreet , Hillf Danton , Roman Gushchin Subject: [PATCH v1 4/5] mm: introduce numa interfaces for shrinker debugfs Date: Thu, 21 Apr 2022 18:58:52 -0700 Message-Id: <20220422015853.748291-5-roman.gushchin@linux.dev> In-Reply-To: <20220422015853.748291-1-roman.gushchin@linux.dev> References: <20220422015853.748291-1-roman.gushchin@linux.dev> MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT X-Migadu-Auth-User: linux.dev Authentication-Results: imf26.hostedemail.com; dkim=pass header.d=linux.dev header.s=key1 header.b=drtHT1Ew; spf=pass (imf26.hostedemail.com: domain of roman.gushchin@linux.dev designates 94.23.1.103 as permitted sender) smtp.mailfrom=roman.gushchin@linux.dev; dmarc=pass (policy=none) header.from=linux.dev X-Stat-Signature: fzmb8ih6ziuwk7a4h3hfir5mq6e8yzeb X-Rspamd-Queue-Id: 4FCF2140011 X-Rspamd-Server: rspam04 X-Rspam-User: X-HE-Tag: 1650592751-724042 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: 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 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 --- 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; }