diff mbox series

[1/6] mm/page_owner: support identifying pages allocated by modules

Message ID 20230815125251.2865852-2-tujinjiang@huawei.com (mailing list archive)
State New
Headers show
Series page_owner: support filtering by module | expand

Commit Message

Jinjiang Tu Aug. 15, 2023, 12:52 p.m. UTC
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 mbox series

Patch

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;