* [PATCH 0/6] page_owner: support filtering by module
@ 2023-08-15 12:52 Jinjiang Tu
2023-08-15 12:52 ` [PATCH 1/6] mm/page_owner: support identifying pages allocated by modules Jinjiang Tu
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Jinjiang Tu @ 2023-08-15 12:52 UTC (permalink / raw)
To: linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2; +Cc: tujinjiang
page_owner is a debug tool to record who allocated/freed each
page. However, it fails to record which module allocated/freed the
pages. Generally, most memory leak bugs are caused by modules,
especially third-party modules. If page_owner supports recording
and filtering pages allocated by modules, it will help us much to
find such bugs.
In this patch, we identify the module that allocate each page by
traversing the stacktrace. The module name will be dumped by
page_owner interface. We also enhance the tool/mm/page_owner_sort
tool to filter the pages allocated by modules.
Besides, when oom occurrs, we fail to know pages are allocated by
which modules. It is too late to dump page_owner after oom occurrs.
Therefore, we records the number of pages allocated by each module,
and dump the modules when oom occurrs. It will help us to find
the buggy module.
Jinjiang Tu (6):
mm/page_owner: support identifying pages allocated by modules
mm/page_owner: show modules allocating pages when oom occurred
tools/vm/page_owner_sort: support for selecting by module name
tools/vm/page_owner_sort: support for culling by module name
tools/vm/page_owner_sort: support sorting by module name
Documentation: update document for page_owner
Documentation/mm/page_owner.rst | 13 +++-
include/linux/module.h | 4 ++
kernel/module/Makefile | 1 +
kernel/module/main.c | 4 ++
kernel/module/page_owner.c | 38 ++++++++++++
mm/page_owner.c | 96 +++++++++++++++++++++++++++++
tools/mm/page_owner_sort.c | 106 ++++++++++++++++++++++++++++----
7 files changed, 248 insertions(+), 14 deletions(-)
create mode 100644 kernel/module/page_owner.c
--
2.25.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/6] mm/page_owner: support identifying pages allocated by modules
2023-08-15 12:52 [PATCH 0/6] page_owner: support filtering by module Jinjiang Tu
@ 2023-08-15 12:52 ` Jinjiang Tu
2023-08-15 12:52 ` [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred Jinjiang Tu
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Jinjiang Tu @ 2023-08-15 12:52 UTC (permalink / raw)
To: linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2; +Cc: tujinjiang
Identify if the pages are allocated by modules according to stackstrace.
By traversing the stacktrace and querying if the address of each entry is
located in a module, we gets the module who allocated/freed the page. If
several modules are found in stacktrace, we choose the one that is closet
to the allocation/free function. We record the module name in struct
page_owner and print it when a user reads from page_owner interface.
Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com>
---
mm/page_owner.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/mm/page_owner.c b/mm/page_owner.c
index c93baef0148f..ef8fe1857d42 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -12,6 +12,7 @@
#include <linux/seq_file.h>
#include <linux/memcontrol.h>
#include <linux/sched/clock.h>
+#include <linux/module.h>
#include "internal.h"
@@ -32,6 +33,9 @@ struct page_owner {
char comm[TASK_COMM_LEN];
pid_t pid;
pid_t tgid;
+#ifdef CONFIG_MODULES
+ char module_name[MODULE_NAME_LEN];
+#endif
};
static bool page_owner_enabled __initdata;
@@ -134,6 +138,78 @@ static noinline depot_stack_handle_t save_stack(gfp_t flags)
return handle;
}
+#ifdef CONFIG_MODULES
+static char *find_module_name(depot_stack_handle_t handle)
+{
+ int i;
+ struct module *mod = NULL;
+ unsigned long *entries;
+ unsigned int nr_entries;
+
+ nr_entries = stack_depot_fetch(handle, &entries);
+ for (i = 0; i < nr_entries; i++) {
+ if (core_kernel_text(entries[i]))
+ continue;
+
+ preempt_disable();
+ mod = __module_address(entries[i]);
+ preempt_enable();
+
+ if (!mod)
+ continue;
+
+ return mod->name;
+ }
+
+ return NULL;
+}
+
+static void set_module_name(struct page_owner *page_owner, char *mod_name)
+{
+ if (mod_name)
+ strscpy(page_owner->module_name, mod_name, MODULE_NAME_LEN);
+ else
+ memset(page_owner->module_name, 0, MODULE_NAME_LEN);
+}
+
+static int module_name_snprint(struct page_owner *page_owner,
+ char *kbuf, size_t size)
+{
+ if (strlen(page_owner->module_name) != 0)
+ return scnprintf(kbuf, size, "Page allocated by module %s\n",
+ page_owner->module_name);
+
+ return 0;
+}
+
+static inline void copy_module_name(struct page_owner *old_page_owner,
+ struct page_owner *new_page_owner)
+{
+ set_module_name(new_page_owner, old_page_owner->module_name);
+}
+#else
+static inline char *find_module_name(depot_stack_handle_t handle)
+{
+ return NULL;
+}
+
+static inline void set_module_name(struct page_owner *page_owner,
+ char *mod_name)
+{
+}
+
+static inline int module_name_snprint(struct page_owner *page_owner,
+ char *kbuf, size_t size)
+{
+ return 0;
+}
+
+static inline void copy_module_name(struct page_owner *old_page_owner,
+ struct page_owner *new_page_owner)
+{
+}
+#endif
+
void __reset_page_owner(struct page *page, unsigned short order)
{
int i;
@@ -141,17 +217,20 @@ void __reset_page_owner(struct page *page, unsigned short order)
depot_stack_handle_t handle;
struct page_owner *page_owner;
u64 free_ts_nsec = local_clock();
+ char *mod_name;
page_ext = page_ext_get(page);
if (unlikely(!page_ext))
return;
handle = save_stack(GFP_NOWAIT | __GFP_NOWARN);
+ mod_name = find_module_name(handle);
for (i = 0; i < (1 << order); i++) {
__clear_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags);
page_owner = get_page_owner(page_ext);
page_owner->free_handle = handle;
page_owner->free_ts_nsec = free_ts_nsec;
+ set_module_name(page_owner, mod_name);
page_ext = page_ext_next(page_ext);
}
page_ext_put(page_ext);
@@ -164,6 +243,9 @@ static inline void __set_page_owner_handle(struct page_ext *page_ext,
struct page_owner *page_owner;
int i;
u64 ts_nsec = local_clock();
+ char *mod_name;
+
+ mod_name = find_module_name(handle);
for (i = 0; i < (1 << order); i++) {
page_owner = get_page_owner(page_ext);
@@ -176,6 +258,7 @@ static inline void __set_page_owner_handle(struct page_ext *page_ext,
page_owner->ts_nsec = ts_nsec;
strscpy(page_owner->comm, current->comm,
sizeof(page_owner->comm));
+ set_module_name(page_owner, mod_name);
__set_bit(PAGE_EXT_OWNER, &page_ext->flags);
__set_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags);
@@ -256,6 +339,7 @@ void __folio_copy_owner(struct folio *newfolio, struct folio *old)
new_page_owner->ts_nsec = old_page_owner->ts_nsec;
new_page_owner->free_ts_nsec = old_page_owner->ts_nsec;
strcpy(new_page_owner->comm, old_page_owner->comm);
+ copy_module_name(new_page_owner, old_page_owner);
/*
* We don't clear the bit on the old folio as it's going to be freed
@@ -425,6 +509,8 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
migratetype_names[pageblock_mt],
&page->flags);
+ ret += module_name_snprint(page_owner, kbuf + ret, count - ret);
+
ret += stack_depot_snprint(handle, kbuf + ret, count - ret, 0);
if (ret >= count)
goto err;
--
2.25.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred
2023-08-15 12:52 [PATCH 0/6] page_owner: support filtering by module Jinjiang Tu
2023-08-15 12:52 ` [PATCH 1/6] mm/page_owner: support identifying pages allocated by modules Jinjiang Tu
@ 2023-08-15 12:52 ` Jinjiang Tu
2023-08-15 14:24 ` kernel test robot
2023-08-15 15:48 ` kernel test robot
2023-08-15 12:52 ` [PATCH 3/6] tools/vm/page_owner_sort: support for selecting by module name Jinjiang Tu
` (3 subsequent siblings)
5 siblings, 2 replies; 9+ messages in thread
From: Jinjiang Tu @ 2023-08-15 12:52 UTC (permalink / raw)
To: linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2; +Cc: tujinjiang
This patch records the pages allocated by each module and dump
the modules when oom occurred. The dump info is as follows:
--------------------------------
Modules state:
module nr_pages_allocated:
ext4 14383
mbcache 9
jbd2 94
--------------------------------
Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com>
---
include/linux/module.h | 4 ++++
kernel/module/Makefile | 1 +
kernel/module/main.c | 4 ++++
kernel/module/page_owner.c | 38 ++++++++++++++++++++++++++++++++++++++
mm/page_owner.c | 18 ++++++++++++++----
5 files changed, 61 insertions(+), 4 deletions(-)
create mode 100644 kernel/module/page_owner.c
diff --git a/include/linux/module.h b/include/linux/module.h
index a98e188cf37b..be374968cbdb 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -583,6 +583,10 @@ struct module {
#ifdef CONFIG_DYNAMIC_DEBUG_CORE
struct _ddebug_info dyndbg_info;
#endif
+
+#ifdef CONFIG_PAGE_OWNER
+ atomic_t nr_pages_allocated;
+#endif
} ____cacheline_aligned __randomize_layout;
#ifndef MODULE_ARCH_INIT
#define MODULE_ARCH_INIT {}
diff --git a/kernel/module/Makefile b/kernel/module/Makefile
index a10b2b9a6fdf..b973a3956131 100644
--- a/kernel/module/Makefile
+++ b/kernel/module/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_KGDB_KDB) += kdb.o
obj-$(CONFIG_MODVERSIONS) += version.o
obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o
obj-$(CONFIG_MODULE_STATS) += stats.o
+obj-$(CONFIG_PAGE_OWNER) += page_owner.o
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 59b1d067e528..e021c7f6dd24 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2888,6 +2888,10 @@ static int load_module(struct load_info *info, const char __user *uargs,
init_param_lock(mod);
+#ifdef CONFIG_PAGE_OWNER
+ atomic_set(&mod->nr_pages_allocated, 0);
+#endif
+
/*
* Now we've got everything in the final locations, we can
* find optional sections.
diff --git a/kernel/module/page_owner.c b/kernel/module/page_owner.c
new file mode 100644
index 000000000000..bcf2a15e7ed3
--- /dev/null
+++ b/kernel/module/page_owner.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/module.h>
+#include <linux/oom.h>
+#include "internal.h"
+
+static int po_oom_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct module *mod;
+ int nr;
+ int ret = notifier_from_errno(0);
+
+ preempt_disable();
+ pr_info("Modules state:\n");
+ pr_info("module nr_page_allocated\n");
+ list_for_each_entry_rcu(mod, &modules, list) {
+ nr = atomic_read(&mod->nr_pages_allocated);
+ if (nr <= 0)
+ continue;
+
+ pr_info("%-20s %d\n", mod->name, nr);
+ }
+ preempt_enable();
+
+ return ret;
+}
+
+static struct notifier_block po_oom_nb = {
+ .notifier_call = po_oom_notify,
+ .priority = 0
+};
+
+void po_register_oom_notifier(void)
+{
+ if (register_oom_notifier(&po_oom_nb))
+ pr_warn("Failed to register pageowner oom notifier\n");
+}
diff --git a/mm/page_owner.c b/mm/page_owner.c
index ef8fe1857d42..bbbf5a518a41 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -139,7 +139,7 @@ static noinline depot_stack_handle_t save_stack(gfp_t flags)
}
#ifdef CONFIG_MODULES
-static char *find_module_name(depot_stack_handle_t handle)
+static char *find_module_name(depot_stack_handle_t handle, int nr_pages)
{
int i;
struct module *mod = NULL;
@@ -158,6 +158,7 @@ static char *find_module_name(depot_stack_handle_t handle)
if (!mod)
continue;
+ atomic_add(nr_pages, &mod->nr_pages_allocated);
return mod->name;
}
@@ -187,8 +188,11 @@ static inline void copy_module_name(struct page_owner *old_page_owner,
{
set_module_name(new_page_owner, old_page_owner->module_name);
}
+
+void po_register_oom_notifier(void);
#else
-static inline char *find_module_name(depot_stack_handle_t handle)
+static inline char *find_module_name(depot_stack_handle_t handle,
+ int nr_pages)
{
return NULL;
}
@@ -208,6 +212,10 @@ static inline void copy_module_name(struct page_owner *old_page_owner,
struct page_owner *new_page_owner)
{
}
+
+void po_register_oom_notifier(void)
+{
+}
#endif
void __reset_page_owner(struct page *page, unsigned short order)
@@ -224,7 +232,7 @@ void __reset_page_owner(struct page *page, unsigned short order)
return;
handle = save_stack(GFP_NOWAIT | __GFP_NOWARN);
- mod_name = find_module_name(handle);
+ mod_name = find_module_name(handle, -(1 << order));
for (i = 0; i < (1 << order); i++) {
__clear_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags);
page_owner = get_page_owner(page_ext);
@@ -245,7 +253,7 @@ static inline void __set_page_owner_handle(struct page_ext *page_ext,
u64 ts_nsec = local_clock();
char *mod_name;
- mod_name = find_module_name(handle);
+ mod_name = find_module_name(handle, 1 << order);
for (i = 0; i < (1 << order); i++) {
page_owner = get_page_owner(page_ext);
@@ -809,6 +817,8 @@ static int __init pageowner_init(void)
debugfs_create_file("page_owner", 0400, NULL, NULL,
&proc_page_owner_operations);
+ po_register_oom_notifier();
+
return 0;
}
late_initcall(pageowner_init)
--
2.25.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/6] tools/vm/page_owner_sort: support for selecting by module name
2023-08-15 12:52 [PATCH 0/6] page_owner: support filtering by module Jinjiang Tu
2023-08-15 12:52 ` [PATCH 1/6] mm/page_owner: support identifying pages allocated by modules Jinjiang Tu
2023-08-15 12:52 ` [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred Jinjiang Tu
@ 2023-08-15 12:52 ` Jinjiang Tu
2023-08-15 12:52 ` [PATCH 4/6] tools/vm/page_owner_sort: support for culling " Jinjiang Tu
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Jinjiang Tu @ 2023-08-15 12:52 UTC (permalink / raw)
To: linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2; +Cc: tujinjiang
Add -M and --module options to support selecting modules. -M option
allows to filtering all the items except the ones allcating by modules.
--module option allows to only select one or more modules. For example,
--module=mod1,mod2 will only select items allocating by mod1 or mod2.
Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com>
---
tools/mm/page_owner_sort.c | 84 +++++++++++++++++++++++++++++++++-----
1 file changed, 73 insertions(+), 11 deletions(-)
diff --git a/tools/mm/page_owner_sort.c b/tools/mm/page_owner_sort.c
index 99798894b879..6fd689199789 100644
--- a/tools/mm/page_owner_sort.c
+++ b/tools/mm/page_owner_sort.c
@@ -27,6 +27,7 @@
#define true 1
#define false 0
#define TASK_COMM_LEN 16
+#define MODULE_NAME_LEN (64 - sizeof(unsigned long))
struct block_list {
char *txt;
@@ -40,12 +41,14 @@ struct block_list {
pid_t pid;
pid_t tgid;
int allocator;
+ char *module;
};
enum FILTER_BIT {
FILTER_UNRELEASE = 1<<1,
FILTER_PID = 1<<2,
FILTER_TGID = 1<<3,
- FILTER_COMM = 1<<4
+ FILTER_COMM = 1<<4,
+ FILTER_MODULE = 1<<5
};
enum CULL_BIT {
CULL_UNRELEASE = 1<<1,
@@ -74,9 +77,11 @@ struct filter_condition {
pid_t *pids;
pid_t *tgids;
char **comms;
+ char **modules;
int pids_size;
int tgids_size;
int comms_size;
+ int modules_size;
};
struct sort_condition {
int (**cmps)(const void *, const void *);
@@ -91,6 +96,7 @@ static regex_t tgid_pattern;
static regex_t comm_pattern;
static regex_t ts_nsec_pattern;
static regex_t free_ts_nsec_pattern;
+static regex_t module_pattern;
static struct block_list *list;
static int list_size;
static int max_size;
@@ -100,10 +106,12 @@ static bool debug_on;
static void set_single_cmp(int (*cmp)(const void *, const void *), int sign);
-int read_block(char *buf, char *ext_buf, int buf_size, FILE *fin)
+int read_block(char *buf, char *ext_buf, char *mod_buf, int buf_size, FILE *fin)
{
char *curr = buf, *const buf_end = buf + buf_size;
+ char *mod_string = "Page allocated by module";
+ mod_buf[0] = '\0';
while (buf_end - curr > 1 && fgets(curr, buf_end - curr, fin)) {
if (*curr == '\n') { /* empty line */
return curr - buf;
@@ -112,6 +120,10 @@ int read_block(char *buf, char *ext_buf, int buf_size, FILE *fin)
strcpy(ext_buf, curr);
continue;
}
+ if (!strncmp(curr, mod_string, strlen(mod_string))) {
+ strcpy(mod_buf, curr);
+ continue;
+ }
curr += strlen(curr);
}
@@ -401,6 +413,16 @@ static char *get_comm(char *buf)
return comm_str;
}
+static char *get_module(char *buf)
+{
+ char *mod = malloc(MODULE_NAME_LEN);
+
+ memset(mod, 0, MODULE_NAME_LEN);
+ search_pattern(&module_pattern, mod, buf);
+
+ return mod;
+}
+
static int get_arg_type(const char *arg)
{
if (!strcmp(arg, "pid") || !strcmp(arg, "p"))
@@ -469,7 +491,24 @@ static bool match_str_list(const char *str, char **list, int list_size)
return false;
}
-static bool is_need(char *buf)
+static bool is_module_filtered(char *mod_buf)
+{
+ char *mod = get_module(mod_buf);
+ int ret = true;
+
+ if (!strlen(mod))
+ goto out;
+
+ if (fc.modules_size == 0 ||
+ match_str_list(mod, fc.modules, fc.modules_size))
+ ret = false;
+
+out:
+ free(mod);
+ return ret;
+}
+
+static bool is_need(char *buf, char *mod_buf)
{
__u64 ts_nsec, free_ts_nsec;
@@ -484,6 +523,9 @@ static bool is_need(char *buf)
!match_num_list(get_tgid(buf), fc.tgids, fc.tgids_size))
return false;
+ if (filter & FILTER_MODULE && is_module_filtered(mod_buf))
+ return false;
+
char *comm = get_comm(buf);
if ((filter & FILTER_COMM) &&
@@ -495,7 +537,7 @@ static bool is_need(char *buf)
return true;
}
-static bool add_list(char *buf, int len, char *ext_buf)
+static bool add_list(char *buf, int len, char *ext_buf, char *mod_buf)
{
if (list_size != 0 &&
len == list[list_size-1].len &&
@@ -508,7 +550,7 @@ static bool add_list(char *buf, int len, char *ext_buf)
fprintf(stderr, "max_size too small??\n");
return false;
}
- if (!is_need(buf))
+ if (!is_need(buf, mod_buf))
return true;
list[list_size].pid = get_pid(buf);
list[list_size].tgid = get_tgid(buf);
@@ -530,6 +572,7 @@ static bool add_list(char *buf, int len, char *ext_buf)
list[list_size].ts_nsec = get_ts_nsec(buf);
list[list_size].free_ts_nsec = get_free_ts_nsec(buf);
list[list_size].allocator = get_allocator(buf, ext_buf);
+ list[list_size].module = get_module(mod_buf);
list_size++;
if (list_size % 1000 == 0) {
printf("loaded %d\r", list_size);
@@ -681,19 +724,21 @@ static void usage(void)
"-a\t\tSort by memory allocate time.\n"
"-r\t\tSort by memory release time.\n"
"-f\t\tFilter out the information of blocks whose memory has been released.\n"
+ "-M\t\tFilter out the information of blocks whose memory isn't allocated by modules.\n"
"-d\t\tPrint debug information.\n"
"--pid <pidlist>\tSelect by pid. This selects the information of blocks whose process ID numbers appear in <pidlist>.\n"
"--tgid <tgidlist>\tSelect by tgid. This selects the information of blocks whose Thread Group ID numbers appear in <tgidlist>.\n"
"--name <cmdlist>\n\t\tSelect by command name. This selects the information of blocks whose command name appears in <cmdlist>.\n"
"--cull <rules>\tCull by user-defined rules.<rules> is a single argument in the form of a comma-separated list with some common fields predefined\n"
"--sort <order>\tSpecify sort order as: [+|-]key[,[+|-]key[,...]]\n"
+ "--module <modulelist>\tSelect by module. This selects the information of blocks whose memory is allocated by modules appear in <modulelist>.\n"
);
}
int main(int argc, char **argv)
{
FILE *fin, *fout;
- char *buf, *ext_buf;
+ char *buf, *ext_buf, *mod_buf;
int i, count;
struct stat st;
int opt;
@@ -703,10 +748,11 @@ int main(int argc, char **argv)
{ "name", required_argument, NULL, 3 },
{ "cull", required_argument, NULL, 4 },
{ "sort", required_argument, NULL, 5 },
+ { "module", required_argument, NULL, 6 },
{ 0, 0, 0, 0},
};
- while ((opt = getopt_long(argc, argv, "adfmnprstP", longopts, NULL)) != -1)
+ while ((opt = getopt_long(argc, argv, "adfmnprstPM", longopts, NULL)) != -1)
switch (opt) {
case 'a':
set_single_cmp(compare_ts, SORT_ASC);
@@ -738,6 +784,11 @@ int main(int argc, char **argv)
case 'n':
set_single_cmp(compare_comm, SORT_ASC);
break;
+ case 'M':
+ filter = filter | FILTER_MODULE;
+ fc.modules_size = 0;
+ fc.modules = NULL;
+ break;
case 1:
filter = filter | FILTER_PID;
fc.pids = parse_nums_list(optarg, &fc.pids_size);
@@ -774,6 +825,10 @@ int main(int argc, char **argv)
exit(1);
}
break;
+ case 6:
+ filter = filter | FILTER_MODULE;
+ fc.modules = explode(',', optarg, &fc.modules_size);
+ break;
default:
usage();
exit(1);
@@ -804,6 +859,8 @@ int main(int argc, char **argv)
goto out_ts;
if (!check_regcomp(&free_ts_nsec_pattern, "free_ts\\s*([0-9]*)\\s*ns"))
goto out_free_ts;
+ if (!check_regcomp(&module_pattern, "Page allocated by module (.*)"))
+ goto out_module;
fstat(fileno(fin), &st);
max_size = st.st_size / 100; /* hack ... */
@@ -811,17 +868,18 @@ int main(int argc, char **argv)
list = malloc(max_size * sizeof(*list));
buf = malloc(BUF_SIZE);
ext_buf = malloc(BUF_SIZE);
- if (!list || !buf || !ext_buf) {
+ mod_buf = malloc(BUF_SIZE);
+ if (!list || !buf || !ext_buf || !mod_buf) {
fprintf(stderr, "Out of memory\n");
goto out_free;
}
for ( ; ; ) {
- int buf_len = read_block(buf, ext_buf, BUF_SIZE, fin);
+ int buf_len = read_block(buf, ext_buf, mod_buf, BUF_SIZE, fin);
if (buf_len < 0)
break;
- if (!add_list(buf, buf_len, ext_buf))
+ if (!add_list(buf, buf_len, ext_buf, mod_buf))
goto out_free;
}
@@ -848,8 +906,10 @@ int main(int argc, char **argv)
for (i = 0; i < count; i++) {
if (cull == 0) {
fprintf(fout, "%d times, %d pages, ", list[i].num, list[i].page_num);
+ if (strlen(list[i].module) != 0)
+ fprintf(fout, "allocated by module %s, ", list[i].module);
print_allocator(fout, list[i].allocator);
- fprintf(fout, ":\n%s\n", list[i].txt);
+ fprintf(fout, " :\n%s\n", list[i].txt);
}
else {
fprintf(fout, "%d times, %d pages",
@@ -880,6 +940,8 @@ int main(int argc, char **argv)
free(buf);
if (list)
free(list);
+out_module:
+ regfree(&module_pattern);
out_free_ts:
regfree(&free_ts_nsec_pattern);
out_ts:
--
2.25.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 4/6] tools/vm/page_owner_sort: support for culling by module name
2023-08-15 12:52 [PATCH 0/6] page_owner: support filtering by module Jinjiang Tu
` (2 preceding siblings ...)
2023-08-15 12:52 ` [PATCH 3/6] tools/vm/page_owner_sort: support for selecting by module name Jinjiang Tu
@ 2023-08-15 12:52 ` Jinjiang Tu
2023-08-15 12:52 ` [PATCH 5/6] tools/vm/page_owner_sort: support sorting " Jinjiang Tu
2023-08-15 12:52 ` [PATCH 6/6] Documentation: update document for page_owner Jinjiang Tu
5 siblings, 0 replies; 9+ messages in thread
From: Jinjiang Tu @ 2023-08-15 12:52 UTC (permalink / raw)
To: linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2; +Cc: tujinjiang
The --cull option allows the items that have the same information to
be culled, such as pid, command name, stacktrace. This patch supports
new cull arg (i.e. module) to cull the items by who allocates the page.
For example, with --cull=module option, the items will be culled as
follows:
314842 times, 334218 pages, module:
202508 times, 206389 pages, module: ext4
16711 times, 16753 pages, module: zram
For the first line, there is no string afther "module:", which means
the pages are allocated by the kernel core, not by modules.
Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com>
---
tools/mm/page_owner_sort.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/tools/mm/page_owner_sort.c b/tools/mm/page_owner_sort.c
index 6fd689199789..35acf6932d78 100644
--- a/tools/mm/page_owner_sort.c
+++ b/tools/mm/page_owner_sort.c
@@ -56,7 +56,8 @@ enum CULL_BIT {
CULL_TGID = 1<<3,
CULL_COMM = 1<<4,
CULL_STACKTRACE = 1<<5,
- CULL_ALLOCATOR = 1<<6
+ CULL_ALLOCATOR = 1<<6,
+ CULL_MODULE = 1 << 7
};
enum ALLOCATOR_BIT {
ALLOCATOR_CMA = 1<<1,
@@ -67,7 +68,7 @@ enum ALLOCATOR_BIT {
enum ARG_TYPE {
ARG_TXT, ARG_COMM, ARG_STACKTRACE, ARG_ALLOC_TS, ARG_FREE_TS,
ARG_CULL_TIME, ARG_PAGE_NUM, ARG_PID, ARG_TGID, ARG_UNKNOWN, ARG_FREE,
- ARG_ALLOCATOR
+ ARG_ALLOCATOR, ARG_MODULE
};
enum SORT_ORDER {
SORT_ASC = 1,
@@ -211,6 +212,13 @@ static int compare_release(const void *p1, const void *p2)
return l1->free_ts_nsec ? 1 : -1;
}
+static int compare_module(const void *p1, const void *p2)
+{
+ const struct block_list *l1 = p1, *l2 = p2;
+
+ return strcmp(l1->module, l2->module);
+}
+
static int compare_cull_condition(const void *p1, const void *p2)
{
if (cull == 0)
@@ -227,6 +235,8 @@ static int compare_cull_condition(const void *p1, const void *p2)
return compare_release(p1, p2);
if ((cull & CULL_ALLOCATOR) && compare_allocator(p1, p2))
return compare_allocator(p1, p2);
+ if ((cull & CULL_MODULE) && compare_module(p1, p2))
+ return compare_module(p1, p2);
return 0;
}
@@ -443,6 +453,8 @@ static int get_arg_type(const char *arg)
return ARG_ALLOC_TS;
else if (!strcmp(arg, "allocator") || !strcmp(arg, "ator"))
return ARG_ALLOCATOR;
+ else if (!strcmp(arg, "module") || !strcmp(arg, "mod"))
+ return ARG_MODULE;
else {
return ARG_UNKNOWN;
}
@@ -601,6 +613,8 @@ static bool parse_cull_args(const char *arg_str)
cull |= CULL_UNRELEASE;
else if (arg_type == ARG_ALLOCATOR)
cull |= CULL_ALLOCATOR;
+ else if (arg_type == ARG_MODULE)
+ cull |= CULL_MODULE;
else {
free_explode(args, size);
return false;
@@ -920,6 +934,8 @@ int main(int argc, char **argv)
fprintf(fout, ", TGID %d", list[i].tgid);
if (cull & CULL_COMM || filter & FILTER_COMM)
fprintf(fout, ", task_comm_name: %s", list[i].comm);
+ if (cull & CULL_MODULE || filter & FILTER_MODULE)
+ fprintf(fout, ", module: %s", list[i].module);
if (cull & CULL_ALLOCATOR) {
fprintf(fout, ", ");
print_allocator(fout, list[i].allocator);
--
2.25.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 5/6] tools/vm/page_owner_sort: support sorting by module name
2023-08-15 12:52 [PATCH 0/6] page_owner: support filtering by module Jinjiang Tu
` (3 preceding siblings ...)
2023-08-15 12:52 ` [PATCH 4/6] tools/vm/page_owner_sort: support for culling " Jinjiang Tu
@ 2023-08-15 12:52 ` Jinjiang Tu
2023-08-15 12:52 ` [PATCH 6/6] Documentation: update document for page_owner Jinjiang Tu
5 siblings, 0 replies; 9+ messages in thread
From: Jinjiang Tu @ 2023-08-15 12:52 UTC (permalink / raw)
To: linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2; +Cc: tujinjiang
This patch adds new argument (i.e. source) for --sort option,
allows the items sorted by module name.
Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com>
---
tools/mm/page_owner_sort.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tools/mm/page_owner_sort.c b/tools/mm/page_owner_sort.c
index 35acf6932d78..c2dc0b58a214 100644
--- a/tools/mm/page_owner_sort.c
+++ b/tools/mm/page_owner_sort.c
@@ -679,6 +679,8 @@ static bool parse_sort_args(const char *arg_str)
sc.cmps[i] = compare_txt;
else if (arg_type == ARG_ALLOCATOR)
sc.cmps[i] = compare_allocator;
+ else if (arg_type == ARG_MODULE)
+ sc.cmps[i] = compare_module;
else {
free_explode(args, size);
sc.size = 0;
--
2.25.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 6/6] Documentation: update document for page_owner
2023-08-15 12:52 [PATCH 0/6] page_owner: support filtering by module Jinjiang Tu
` (4 preceding siblings ...)
2023-08-15 12:52 ` [PATCH 5/6] tools/vm/page_owner_sort: support sorting " Jinjiang Tu
@ 2023-08-15 12:52 ` Jinjiang Tu
5 siblings, 0 replies; 9+ messages in thread
From: Jinjiang Tu @ 2023-08-15 12:52 UTC (permalink / raw)
To: linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2; +Cc: tujinjiang
page_owner currently supports recording and filtering
pages allocates/freed by modules. Also, page_owner
supports dumping name and the nr of pages allocated
for each module when oom occurs. Update the document for the
new features.
Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com>
---
Documentation/mm/page_owner.rst | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/Documentation/mm/page_owner.rst b/Documentation/mm/page_owner.rst
index 62e3f7ab23cc..2231c16cd993 100644
--- a/Documentation/mm/page_owner.rst
+++ b/Documentation/mm/page_owner.rst
@@ -54,6 +54,12 @@ are caught and marked, although they are mostly allocated from struct
page extension feature. Anyway, after that, no page is left in
un-tracking state.
+page owner is able to track if the pages are allocated by modules. If a page
+is allocated by a module, the information dumped from page_owner interface
+will show the module name. Users can use the user-space helper to analyze the
+allocation situation of modules. Besides, the modules that allocate pages will
+be dumped when oom occurs.
+
Usage
=====
@@ -142,6 +148,7 @@ Usage
Filter:
-f Filter out the information of blocks whose memory has been released.
+ -M Filter out the information of blocks whose memory isn't allocated by modules.
Select:
--pid <pidlist> Select by pid. This selects the blocks whose process ID
@@ -150,8 +157,10 @@ Usage
group ID numbers appear in <tgidlist>.
--name <cmdlist> Select by task command name. This selects the blocks whose
task command name appear in <cmdlist>.
+ --module <modulelist> Select by module. This selects the information of blocks whose
+ memory is allocated by modules appear in <modulelist>.
- <pidlist>, <tgidlist>, <cmdlist> are single arguments in the form of a comma-separated list,
+ <pidlist>, <tgidlist>, <cmdlist>, <modulelist> are single arguments in the form of a comma-separated list,
which offers a way to specify individual selecting rules.
@@ -175,6 +184,7 @@ STANDARD FORMAT SPECIFIERS
ft free_ts timestamp of the page when it was released
at alloc_ts timestamp of the page when it was allocated
ator allocator memory allocator for pages
+ mod module the name of the module that the page is allocated by
For --cull option:
@@ -185,3 +195,4 @@ STANDARD FORMAT SPECIFIERS
f free whether the page has been released or not
st stacktrace stack trace of the page allocation
ator allocator memory allocator for pages
+ mod module the name of the module that the page is allocated by
--
2.25.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred
2023-08-15 12:52 ` [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred Jinjiang Tu
@ 2023-08-15 14:24 ` kernel test robot
2023-08-15 15:48 ` kernel test robot
1 sibling, 0 replies; 9+ messages in thread
From: kernel test robot @ 2023-08-15 14:24 UTC (permalink / raw)
To: Jinjiang Tu, linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2
Cc: oe-kbuild-all, tujinjiang
Hi Jinjiang,
kernel test robot noticed the following build warnings:
[auto build test WARNING on akpm-mm/mm-everything]
[also build test WARNING on mcgrof/modules-next linus/master v6.5-rc6 next-20230809]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Jinjiang-Tu/mm-page_owner-support-identifying-pages-allocated-by-modules/20230815-195940
base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
patch link: https://lore.kernel.org/r/20230815125251.2865852-3-tujinjiang%40huawei.com
patch subject: [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred
config: sparc64-randconfig-r035-20230815 (https://download.01.org/0day-ci/archive/20230815/202308152233.SyFFj1kn-lkp@intel.com/config)
compiler: sparc64-linux-gcc (GCC) 12.3.0
reproduce: (https://download.01.org/0day-ci/archive/20230815/202308152233.SyFFj1kn-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202308152233.SyFFj1kn-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> kernel/module/page_owner.c:34:6: warning: no previous prototype for 'po_register_oom_notifier' [-Wmissing-prototypes]
34 | void po_register_oom_notifier(void)
| ^~~~~~~~~~~~~~~~~~~~~~~~
vim +/po_register_oom_notifier +34 kernel/module/page_owner.c
33
> 34 void po_register_oom_notifier(void)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred
2023-08-15 12:52 ` [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred Jinjiang Tu
2023-08-15 14:24 ` kernel test robot
@ 2023-08-15 15:48 ` kernel test robot
1 sibling, 0 replies; 9+ messages in thread
From: kernel test robot @ 2023-08-15 15:48 UTC (permalink / raw)
To: Jinjiang Tu, linux-mm, akpm, wangkefeng.wang, sunnanyong, liushixin2
Cc: llvm, oe-kbuild-all, tujinjiang
Hi Jinjiang,
kernel test robot noticed the following build warnings:
[auto build test WARNING on akpm-mm/mm-everything]
[also build test WARNING on mcgrof/modules-next linus/master v6.5-rc6 next-20230815]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Jinjiang-Tu/mm-page_owner-support-identifying-pages-allocated-by-modules/20230815-195940
base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-everything
patch link: https://lore.kernel.org/r/20230815125251.2865852-3-tujinjiang%40huawei.com
patch subject: [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred
config: x86_64-randconfig-x073-20230815 (https://download.01.org/0day-ci/archive/20230815/202308152353.TqoJfjIk-lkp@intel.com/config)
compiler: clang version 16.0.4 (https://github.com/llvm/llvm-project.git ae42196bc493ffe877a7e3dff8be32035dea4d07)
reproduce: (https://download.01.org/0day-ci/archive/20230815/202308152353.TqoJfjIk-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202308152353.TqoJfjIk-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> kernel/module/page_owner.c:34:6: warning: no previous prototype for function 'po_register_oom_notifier' [-Wmissing-prototypes]
void po_register_oom_notifier(void)
^
kernel/module/page_owner.c:34:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
void po_register_oom_notifier(void)
^
static
1 warning generated.
vim +/po_register_oom_notifier +34 kernel/module/page_owner.c
33
> 34 void po_register_oom_notifier(void)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2023-08-15 15:48 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-15 12:52 [PATCH 0/6] page_owner: support filtering by module Jinjiang Tu
2023-08-15 12:52 ` [PATCH 1/6] mm/page_owner: support identifying pages allocated by modules Jinjiang Tu
2023-08-15 12:52 ` [PATCH 2/6] mm/page_owner: show modules allocating pages when oom occurred Jinjiang Tu
2023-08-15 14:24 ` kernel test robot
2023-08-15 15:48 ` kernel test robot
2023-08-15 12:52 ` [PATCH 3/6] tools/vm/page_owner_sort: support for selecting by module name Jinjiang Tu
2023-08-15 12:52 ` [PATCH 4/6] tools/vm/page_owner_sort: support for culling " Jinjiang Tu
2023-08-15 12:52 ` [PATCH 5/6] tools/vm/page_owner_sort: support sorting " Jinjiang Tu
2023-08-15 12:52 ` [PATCH 6/6] Documentation: update document for page_owner Jinjiang Tu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox