Offering: HULK hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9GSSR
----------------------------------------
Identify if the pages are allocated by modules according to stackstrace. Record the module name in struct page_owner and print it when a user reads from /sys/kernel/debug/page_owner.
Signed-off-by: Jinjiang Tu tujinjiang@huawei.com --- mm/Kconfig.debug | 9 ++++++ mm/Makefile | 1 + mm/page_owner.c | 24 +++++++-------- mm/page_owner.h | 59 +++++++++++++++++++++++++++++++++++ mm/page_owner_module.c | 70 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 mm/page_owner.h create mode 100644 mm/page_owner_module.c
diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug index 864f129f1937..154ece4e7fc5 100644 --- a/mm/Kconfig.debug +++ b/mm/Kconfig.debug @@ -62,6 +62,15 @@ config PAGE_OWNER
If unsure, say N.
+config PAGE_OWNER_MODULE_STAT + bool "Track module allocation with page owner" + depends on PAGE_OWNER && MODULES + help + This tracks if a page is allocated by modules, may help to find the + alloc_page(s) problem in modules. Even if you include this feature + on your build, it is disabled in default. You should pass "page_owner=on" + to boot parameter in order to enable it. + config PAGE_POISONING bool "Poison pages after freeing" select PAGE_POISONING_NO_SANITY if HIBERNATION diff --git a/mm/Makefile b/mm/Makefile index a014a5e08f7b..7194a39e2a90 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o obj-$(CONFIG_DEBUG_RODATA_TEST) += rodata_test.o obj-$(CONFIG_DEBUG_VM_PGTABLE) += debug_vm_pgtable.o obj-$(CONFIG_PAGE_OWNER) += page_owner.o +obj-$(CONFIG_PAGE_OWNER_MODULE_STAT) += page_owner_module.o obj-$(CONFIG_CLEANCACHE) += cleancache.o obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o obj-$(CONFIG_ZPOOL) += zpool.o diff --git a/mm/page_owner.c b/mm/page_owner.c index 119f028ef559..3e3cf55d270e 100644 --- a/mm/page_owner.c +++ b/mm/page_owner.c @@ -13,6 +13,7 @@ #include <linux/memcontrol.h> #include <linux/sched/clock.h>
+#include "page_owner.h" #include "internal.h"
/* @@ -21,19 +22,6 @@ */ #define PAGE_OWNER_STACK_DEPTH (16)
-struct page_owner { - unsigned short order; - short last_migrate_reason; - gfp_t gfp_mask; - depot_stack_handle_t handle; - depot_stack_handle_t free_handle; - u64 ts_nsec; - u64 free_ts_nsec; - char comm[TASK_COMM_LEN]; - pid_t pid; - pid_t tgid; -}; - static bool page_owner_enabled = false; DEFINE_STATIC_KEY_FALSE(page_owner_inited);
@@ -144,8 +132,10 @@ void __reset_page_owner(struct page *page, unsigned int order) depot_stack_handle_t handle = 0; struct page_owner *page_owner; u64 free_ts_nsec = local_clock(); + char mod_name[MODULE_NAME_LEN] = {0};
handle = save_stack(GFP_NOWAIT | __GFP_NOWARN); + po_find_module_name_with_update(handle, mod_name, MODULE_NAME_LEN);
page_ext = page_ext_get(page); if (unlikely(!page_ext)) @@ -155,6 +145,7 @@ void __reset_page_owner(struct page *page, unsigned int order) page_owner = get_page_owner(page_ext); page_owner->free_handle = handle; page_owner->free_ts_nsec = free_ts_nsec; + po_set_module_name(page_owner, mod_name); page_ext = page_ext_next(page_ext); } page_ext_put(page_ext); @@ -167,6 +158,9 @@ static inline void __set_page_owner_handle(struct page *page, struct page_owner *page_owner; int i; u64 ts_nsec = local_clock(); + char mod_name[MODULE_NAME_LEN] = {0}; + + po_find_module_name_with_update(handle, mod_name, MODULE_NAME_LEN);
for (i = 0; i < (1 << order); i++) { page_owner = get_page_owner(page_ext); @@ -179,6 +173,7 @@ static inline void __set_page_owner_handle(struct page *page, page_owner->ts_nsec = ts_nsec; strscpy(page_owner->comm, current->comm, sizeof(page_owner->comm)); + po_set_module_name(page_owner, mod_name); __set_bit(PAGE_EXT_OWNER, &page_ext->flags); __set_bit(PAGE_EXT_OWNER_ALLOCATED, &page_ext->flags);
@@ -260,6 +255,7 @@ void __copy_page_owner(struct page *oldpage, struct page *newpage) 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); + po_copy_module_name(new_page_owner, old_page_owner);
/* * We don't clear the bit on the oldpage as it's going to be freed @@ -434,6 +430,8 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn, migratetype_names[pageblock_mt], page->flags, &page->flags);
+ ret += po_module_name_snprint(page_owner, kbuf + ret, count - ret); + nr_entries = stack_depot_fetch(handle, &entries); ret += stack_trace_snprint(kbuf + ret, count - ret, entries, nr_entries, 0); if (ret >= count) diff --git a/mm/page_owner.h b/mm/page_owner.h new file mode 100644 index 000000000000..18ea05e999e7 --- /dev/null +++ b/mm/page_owner.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved. + */ + +#ifndef __MM_PAGE_OWNER_H +#define __MM_PAGE_OWNER_H + +#include <linux/stackdepot.h> + +struct page_owner { + unsigned short order; + short last_migrate_reason; + gfp_t gfp_mask; + depot_stack_handle_t handle; + depot_stack_handle_t free_handle; + u64 ts_nsec; + u64 free_ts_nsec; + char comm[TASK_COMM_LEN]; + pid_t pid; + pid_t tgid; +#ifdef CONFIG_PAGE_OWNER_MODULE_STAT + char module_name[MODULE_NAME_LEN]; +#endif +}; + +#ifdef CONFIG_PAGE_OWNER_MODULE_STAT +void po_find_module_name_with_update(depot_stack_handle_t handle, char *mod_name, size_t size); +void po_set_module_name(struct page_owner *page_owner, char *mod_name); +int po_module_name_snprint(struct page_owner *page_owner, char *kbuf, size_t size); + +static inline void po_copy_module_name(struct page_owner *dst, + struct page_owner *src) +{ + po_set_module_name(dst, src->module_name); +} + +#else +static void po_find_module_name_with_update(depot_stack_handle_t handle, char *mod_name, + size_t size) +{ +} + +static void po_set_module_name(struct page_owner *page_owner, char *mod_name) +{ +} + +static inline int po_module_name_snprint(struct page_owner *page_owner, + char *kbuf, size_t size) +{ + return 0; +} + +static inline void po_copy_module_name(struct page_owner *dst, struct page_owner *src) +{ +} +#endif + +#endif diff --git a/mm/page_owner_module.c b/mm/page_owner_module.c new file mode 100644 index 000000000000..2a2becf975da --- /dev/null +++ b/mm/page_owner_module.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved. + * + * page_owner_module core file + */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/ctype.h> + +#include "page_owner.h" + +void po_find_module_name_with_update(depot_stack_handle_t handle, char *mod_name, size_t size) +{ + int i; + struct module *mod = NULL; + unsigned long *entries; + unsigned int nr_entries; + + if (unlikely(!mod_name)) + return; + + nr_entries = stack_depot_fetch(handle, &entries); + if (!in_task()) + nr_entries = filter_irq_stacks(entries, nr_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; + + strscpy(mod_name, mod->name, size); + return; + } +} + +void po_set_module_name(struct page_owner *page_owner, char *mod_name) +{ + if (unlikely(!page_owner || !mod_name)) + return; + + if (strlen(mod_name) != 0) + strscpy(page_owner->module_name, mod_name, MODULE_NAME_LEN); + else + memset(page_owner->module_name, 0, MODULE_NAME_LEN); +} + +static inline bool po_is_module(struct page_owner *page_owner) +{ + return strlen(page_owner->module_name) != 0; +} + +int po_module_name_snprint(struct page_owner *page_owner, + char *kbuf, size_t size) +{ + if (unlikely(!page_owner || !kbuf)) + return 0; + + if (po_is_module(page_owner)) + return scnprintf(kbuf, size, "Page allocated by module %s\n", + page_owner->module_name); + + return 0; +}