diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 959f1808c240fa8f472914ef74503338eb8763de..5bec748f1dab11a57394b0961e7c6aa93fda3790 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -23,12 +23,23 @@ #include #include #include +#if defined(CONFIG_DEBUG_FS) +#include +#include +#endif #include "of_private.h" #define MAX_RESERVED_REGIONS 64 static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; static int reserved_mem_count; +#if defined(CONFIG_DEBUG_FS) +#define DT_RESERVED_MEM "dt_reserved_mem" +static int dynamic_reserved_mem_count; +static const char *dynamic_reserved_mem_array[MAX_RESERVED_REGIONS]; +static int cma_reserved_mem_count; +static const char *cma_reserved_mem_array[MAX_RESERVED_REGIONS]; +#endif static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, @@ -73,6 +84,13 @@ void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, rmem->name = uname; rmem->base = base; rmem->size = size; +#if defined(CONFIG_DEBUG_FS) + if ((of_get_flat_dt_prop(node, "reusable", NULL)) + && (cma_reserved_mem_count < MAX_RESERVED_REGIONS)) { + cma_reserved_mem_array[cma_reserved_mem_count] = uname; + cma_reserved_mem_count++; + } +#endif reserved_mem_count++; return; @@ -214,6 +232,13 @@ static int __init __reserved_mem_alloc_size(unsigned long node, return -ENOMEM; } +#if defined(CONFIG_DEBUG_FS) + if (dynamic_reserved_mem_count < MAX_RESERVED_REGIONS) { + dynamic_reserved_mem_array[dynamic_reserved_mem_count] = uname; + dynamic_reserved_mem_count++; + } +#endif + *res_base = base; *res_size = size; @@ -514,3 +539,78 @@ struct reserved_mem *of_reserved_mem_lookup(struct device_node *np) return NULL; } EXPORT_SYMBOL_GPL(of_reserved_mem_lookup); + +#if defined(CONFIG_DEBUG_FS) +static int dt_reserved_memory_debug_show(struct seq_file *m, void *private) +{ + struct reserved_mem *dt_reserved_mem = m->private; + struct reserved_mem *rmem = NULL; + int i = 0; + int j = 0; + int cma = 0; + int dynamic = 0; + + seq_printf(m, " num [start .... end] [size]" + " [d/s] [cma] [name]\n"); + + for (i = 0; i < reserved_mem_count; i++) { + cma = 0; + dynamic = 0; + rmem = &(dt_reserved_mem[i]); + + /* find out dynamic reserved memory node */ + for (j = 0; j < dynamic_reserved_mem_count; j++) { + if (!strcmp(rmem->name, dynamic_reserved_mem_array[j])) { + dynamic = 1; + break; + } + } + + /* find out cma reserved memory node */ + for (j = 0; j < cma_reserved_mem_count; j++) { + if (!strcmp(rmem->name, cma_reserved_mem_array[j])) { + cma = 1; + break; + } + } + + seq_printf(m, "%4d: [0x%016llx..0x%016llx] %8llukB %8s %8s %8s\n", + i, + (unsigned long long)rmem->base, + (unsigned long long)(rmem->base + rmem->size - 1), + (unsigned long long)rmem->size / SZ_1K, + (dynamic == 1) ? "d" : "s", + (cma == 1) ? "y" : "n", + rmem->name); + } + + return 0; +} + +static int dt_reserved_memory_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, dt_reserved_memory_debug_show, inode->i_private); +} + +static const struct file_operations dt_reserved_memory_debug_fops = { + .open = dt_reserved_memory_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init dt_reserved_memory_init_debugfs(void) +{ + struct dentry *root = debugfs_create_dir(DT_RESERVED_MEM, NULL); + if (!root) + return -ENXIO; + debugfs_create_file("dt_reserved_memory", + S_IRUGO, + root, + reserved_mem, + &dt_reserved_memory_debug_fops); + + return 0; +} +__initcall(dt_reserved_memory_init_debugfs); +#endif /*CONFIG_DEBUG_FS*/ \ No newline at end of file diff --git a/fs/proc/base.c b/fs/proc/base.c index f1e1a0b2cae88c5f1c09a0895009ee817a10c2c3..6a35dd810ac3439b407c9adcc5b2574f6ede5b1c 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3510,6 +3510,10 @@ static const struct pid_entry tgid_base_stuff[] = { #endif #ifdef CONFIG_TIME_NS REG("timens_offsets", S_IRUGO|S_IWUSR, proc_timens_offsets_operations), +#endif +#ifdef CONFIG_RSS_THRESHOLD + ONE("rss", S_IRUGO, proc_pid_rss), + REG("rss_threshold", S_IRUGO|S_IWUSR, proc_pid_rss_threshold_operations), #endif REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations), #ifdef CONFIG_HAVE_ARCH_TRACEHOOK diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 445c74a39a931f22762bd6034159e8e16ccacd11..c39cac8f7efa071324dd65bf6805ae6bf8e3daf1 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -187,6 +187,12 @@ extern int pid_delete_dentry(const struct dentry *); extern int proc_pid_readdir(struct file *, struct dir_context *); struct dentry *proc_pid_lookup(struct dentry *, unsigned int); extern loff_t mem_lseek(struct file *, loff_t, int); +#ifdef CONFIG_RSS_THRESHOLD +extern int proc_pid_rss(struct seq_file *, struct pid_namespace *, + struct pid *, struct task_struct *); +extern void listen_rss_threshold(struct mm_struct *mm); +extern const struct file_operations proc_pid_rss_threshold_operations; +#endif /* Lookups */ typedef struct dentry *instantiate_t(struct dentry *, diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index 875306ca2b397a12eda150c7a79b13b529b4c8ab..4d81eccc768ef837199eff582b319a0b6e20722a 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -160,6 +160,10 @@ static int meminfo_proc_show(struct seq_file *m, void *v) show_val_kb(m, "VmallocChunk: ", 0ul); show_val_kb(m, "Percpu: ", pcpu_nr_pages()); +#ifdef CONFIG_PAGE_TRACING + show_val_kb(m, "Skb: ", global_zone_page_state(NR_SKB_PAGES)); +#endif + memtest_report_meminfo(m); #ifdef CONFIG_MEMORY_FAILURE @@ -186,6 +190,11 @@ static int meminfo_proc_show(struct seq_file *m, void *v) global_zone_page_state(NR_FREE_CMA_PAGES)); #endif +#ifdef CONFIG_PAGE_TRACING + seq_puts(m, "GLTrack: - kB\n"); + show_val_kb(m, "ZspageUsed: ", global_zone_page_state(NR_ZSPAGES)); +#endif + #ifdef CONFIG_UNACCEPTED_MEMORY show_val_kb(m, "Unaccepted: ", global_zone_page_state(NR_UNACCEPTED)); diff --git a/include/linux/mm.h b/include/linux/mm.h index 4176fa18f692dff4761c742186486e88153239c4..8fd0fc27fe09ae1309417cf981fab07341fbf22f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2584,10 +2584,18 @@ static inline unsigned long get_mm_counter_sum(struct mm_struct *mm, int member) void mm_trace_rss_stat(struct mm_struct *mm, int member); +#ifdef CONFIG_RSS_THRESHOLD +void listen_rss_threshold(struct mm_struct *mm); +#endif + static inline void add_mm_counter(struct mm_struct *mm, int member, long value) { percpu_counter_add(&mm->rss_stat[member], value); +#ifdef CONFIG_RSS_THRESHOLD + listen_rss_threshold(mm); +#endif + mm_trace_rss_stat(mm, member); } @@ -2595,6 +2603,10 @@ static inline void inc_mm_counter(struct mm_struct *mm, int member) { percpu_counter_inc(&mm->rss_stat[member]); +#ifdef CONFIG_RSS_THRESHOLD + listen_rss_threshold(mm); +#endif + mm_trace_rss_stat(mm, member); } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 2b2616008a49212af52fc5a8b3153979a2229985..98850f8ca34dd97c529126cc514ecbabf6f1056d 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -853,6 +853,9 @@ struct mm_struct { unsigned long hiwater_rss; /* High-watermark of RSS usage */ unsigned long hiwater_vm; /* High-water virtual memory usage */ +#ifdef CONFIG_RSS_THRESHOLD + unsigned long rss_threshold; /* A threshold monitor RSS */ +#endif unsigned long total_vm; /* Total pages mapped */ unsigned long locked_vm; /* Pages that have PG_mlocked set */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index c378c11c2ee6f5ea97377501c5dd724da7ce6f48..c40f72d98bfc91b9286f0aa0ce799117e5d4ddd3 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -161,6 +161,9 @@ enum zone_stat_item { NR_ZSPAGES, /* allocated in zsmalloc */ #endif NR_FREE_CMA_PAGES, +#ifdef CONFIG_PAGE_TRACING + NR_SKB_PAGES, +#endif #ifdef CONFIG_UNACCEPTED_MEMORY NR_UNACCEPTED, #endif diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 1893a7916475520ab39a3b5756f9d28afff5178f..aa37e689bae101d39c166fb98f26ada1110fc2ee 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -136,6 +136,10 @@ enum pageflags { PG_arch_2, PG_arch_3, #endif +#ifdef CONFIG_PAGE_TRACING + PG_skb, + PG_zspage, +#endif #ifdef CONFIG_MEM_PURGEABLE PG_purgeable, #endif @@ -631,6 +635,11 @@ TESTCLEARFLAG(Young, young, PF_ANY) PAGEFLAG(Idle, idle, PF_ANY) #endif +#ifdef CONFIG_PAGE_TRACING + PAGEFLAG(SKB, skb, PF_ANY) + PAGEFLAG(Zspage, zspage, PF_ANY) +#endif + /* * PageReported() is used to track reported free pages within the Buddy * allocator. We can use the non-atomic version of the test and set diff --git a/kernel/fork.c b/kernel/fork.c index 0907f94005c842af29e28933e4a60c40a0f11337..a61c17928555b5526fc4c92e163f0c2fc9791e19 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1283,6 +1283,9 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, { mt_init_flags(&mm->mm_mt, MM_MT_FLAGS); mt_set_external_lock(&mm->mm_mt, &mm->mmap_lock); +#ifdef CONFIG_RSS_THRESHOLD + mm->rss_threshold = 0; +#endif atomic_set(&mm->mm_users, 1); atomic_set(&mm->mm_count, 1); seqcount_init(&mm->write_protect_seq); diff --git a/mm/Kconfig b/mm/Kconfig index 11f2f83389e57cc2ed350c11130eabfb15bd0c40..52a747aec2d20a49e1734fc57d4e89b4a25b86d6 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -516,6 +516,12 @@ config HYPERHOLD_ZSWAPD and the refault of anonymous pages is high, the content of zram will exchanged to eswap by a certain percentage. +config PAGE_TRACING + bool "Enable Page Tracing" + default n + help + This option enables page tracing. + config RECLAIM_ACCT bool "Memory reclaim delay accounting" default n @@ -1264,6 +1270,31 @@ config ANON_VMA_NAME area from being merged with adjacent virtual memory areas due to the difference in their name. +# +# For lmkd to trigger in-kernel lowmem info +# +config LOWMEM + bool "Low Memory Killer" + default n + help + Enables lowmem killer parameter tuning + +config LMKD_DBG + bool "Low Memory Killer Debug" + default n + help + print processes info when lmk happen per several seconds + +# +# Show the process ashmem for debug +# +config MEMTRACE_ASHMEM + bool "Ashmem Process Info Show" + depends on ASHMEM + default n + help + Enable the Ashmem Process Info Show + config USERFAULTFD bool "Enable userfaultfd() system call" depends on MMU @@ -1358,4 +1389,14 @@ config PURGEABLE_ASHMEM source "mm/damon/Kconfig" +# +# Use rss_threshold to monitoring RSS +# +config RSS_THRESHOLD + bool "Enable /proc//rss and /proc//rss_threshold to monitoring RSS" + default n + depends on PROC_FS && MEMCG + help + Set a threshold to monitoring RSS in per pid + endmenu diff --git a/mm/Makefile b/mm/Makefile index 94ea4fb431637fdb8c465637ddc4211279e1102d..8ae15e50c59cc5e4b284c71499afc446900d554a 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -71,6 +71,10 @@ ifdef CONFIG_MMU obj-$(CONFIG_ADVISE_SYSCALLS) += madvise.o endif +obj-$(CONFIG_MEMTRACE_ASHMEM) += memtrace_ashmem.o +obj-$(CONFIG_LMKD_DBG) += lmkd_dbg_trigger.o +obj-$(CONFIG_LOWMEM) += lowmem_dbg.o +obj-$(CONFIG_RSS_THRESHOLD) += rss_threshold.o obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o swap_slots.o obj-$(CONFIG_ZSWAP) += zswap.o obj-$(CONFIG_HAS_DMA) += dmapool.o diff --git a/mm/debug.c b/mm/debug.c index ee533a5ceb79d3335e28d4c17530208dbe17fa0c..49b2dd7ac886a144d50c9d875017cdc8cc3dd9b8 100644 --- a/mm/debug.c +++ b/mm/debug.c @@ -33,6 +33,10 @@ const char *migrate_reason_names[MR_TYPES] = { const struct trace_print_flags pageflag_names[] = { __def_pageflag_names, +#ifdef CONFIG_PAGE_TRACING + {1UL << PG_skb, "skb"}, + {1UL << PG_zspage, "zspage"}, +#endif {0, NULL} }; diff --git a/mm/lmkd_dbg_trigger.c b/mm/lmkd_dbg_trigger.c new file mode 100644 index 0000000000000000000000000000000000000000..bd97331e0e5abab9b5beebf71e6c5aa4f857e7b0 --- /dev/null +++ b/mm/lmkd_dbg_trigger.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mm/lmkd_dbg_trigger.c + * + * Copyright (c) 2020-2022 Huawei Technologies Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define PROC_NUMBUF 8 + +static int lmkd_oom_score_adj; +static atomic64_t lmkd_no_cma_cnt = ATOMIC64_INIT(0); + +static int lmkd_dbg_trigger_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "lmkd_oom_score_adj: %d\n", lmkd_oom_score_adj); + seq_printf(m, "lmkd_no_cma_cnt: %lld\n", + atomic64_read(&lmkd_no_cma_cnt)); + return 0; +} + +static int lmkd_dbg_trigger_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, lmkd_dbg_trigger_proc_show, NULL); +} + +static ssize_t lmkd_dbg_trigger_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char buffer[PROC_NUMBUF]; + int oom_score_adj; + int err; + + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count)) { + err = -EFAULT; + goto out; + } + + err = kstrtoint(strstrip(buffer), 0, &oom_score_adj); + if (err) + goto out; + + if (oom_score_adj < OOM_SCORE_ADJ_MIN || + oom_score_adj > OOM_SCORE_ADJ_MAX) { + err = -EINVAL; + goto out; + } + + lmkd_oom_score_adj = oom_score_adj; + lowmem_dbg(oom_score_adj); + +out: + return err < 0 ? err : count; +} + +static const struct proc_ops lmkd_dbg_trigger_proc_fops = { + .proc_open = lmkd_dbg_trigger_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = lmkd_dbg_trigger_write, +}; + +static int __init proc_lmkd_dbg_trigger_init(void) +{ + proc_create("lmkd_dbg_trigger", 0660, NULL, + &lmkd_dbg_trigger_proc_fops); + return 0; +} + +fs_initcall(proc_lmkd_dbg_trigger_init); + diff --git a/mm/lowmem_dbg.c b/mm/lowmem_dbg.c new file mode 100644 index 0000000000000000000000000000000000000000..c5e4477a8d265c321fb246f3067cddbe5fd5e053 --- /dev/null +++ b/mm/lowmem_dbg.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mm/lowmem_dbg.c + * + * Copyright (c) 2020-2022 Huawei Technologies Co., Ltd. + */ +#define pr_fmt(fmt) "lowmem:" fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#define LMK_PRT_TSK_RSS 0 +#define LMK_INTERVAL 15 + +/* SERVICE_ADJ(5) * OOM_SCORE_ADJ_MAX / -OOM_DISABLE */ +#define LMK_SERVICE_ADJ 1000 +/* defiine TASK STATE String */ +#define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWPNn" + +static unsigned long long last_jiffs; +static const char state_to_char[] = TASK_STATE_TO_CHAR_STR; +static void lowmem_dump(struct work_struct *work); + +static DEFINE_MUTEX(lowmem_dump_mutex); +static DECLARE_WORK(lowmem_dbg_verbose_wk, lowmem_dump); + +static int task_state_char(unsigned long state) +{ + int bit = state ? __ffs(state) + 1 : 0; + + return bit < sizeof(state_to_char) - 1 ? state_to_char[bit] : '?'; +} + +static void tasks_dump(bool verbose) +{ + struct task_struct *p = NULL; + struct task_struct *task = NULL; + short tsk_oom_adj = 0; + unsigned long tsk_nr_ptes = 0; + char frozen_mark = ' '; + + pr_info("[ pid ] uid tgid total_vm rss nptes swap adj s name\n"); + + rcu_read_lock(); + for_each_process(p) { + task = find_lock_task_mm(p); + if (!task) { + /* + * This is a kthread or all of p's threads have already + * detached their mm's. There's no need to report + * them; they can't be oom killed anyway. + */ + continue; + } + + tsk_oom_adj = task->signal->oom_score_adj; + if (!verbose && tsk_oom_adj && + (tsk_oom_adj <= LMK_SERVICE_ADJ) && + (get_mm_rss(task->mm) < LMK_PRT_TSK_RSS)) { + task_unlock(task); + continue; + } + + tsk_nr_ptes = mm_pgtables_bytes(task->mm); + + frozen_mark = frozen(task) ? '*' : ' '; + + pr_info("[%5d] %5d %5d %8lu %6lu %5lu %5lu %5hd %c %s%c\n", + task->pid, from_kuid(&init_user_ns, task_uid(task)), + task->tgid, task->mm->total_vm, get_mm_rss(task->mm), + tsk_nr_ptes, + get_mm_counter(task->mm, MM_SWAPENTS), + tsk_oom_adj, + task_state_char(task->__state), + task->comm, + frozen_mark); /*lint !e1058*/ + task_unlock(task); + } + rcu_read_unlock(); +} + +static void lowmem_dump(struct work_struct *work) +{ + bool verbose = (work == &lowmem_dbg_verbose_wk) ? true : false; + + mutex_lock(&lowmem_dump_mutex); + show_mem(); + tasks_dump(verbose); + mutex_unlock(&lowmem_dump_mutex); +} + +void lowmem_dbg(short oom_score_adj) +{ + unsigned long long jiffs = get_jiffies_64(); + + if (oom_score_adj == 0) { + schedule_work(&lowmem_dbg_verbose_wk); + } else if (time_after64(jiffs, (last_jiffs + LMK_INTERVAL * HZ))) { + last_jiffs = get_jiffies_64(); + schedule_work(&lowmem_dbg_verbose_wk); + } +} + diff --git a/mm/memtrace_ashmem.c b/mm/memtrace_ashmem.c new file mode 100644 index 0000000000000000000000000000000000000000..9d61f32d4f7840f3a9ff93e750e0bac26a3d4acd --- /dev/null +++ b/mm/memtrace_ashmem.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mm/memtrace_ashmem.c + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include "../drivers/staging/android/ashmem.h" + +static int ashmem_debug_process_info_open(struct inode *inode, + struct file *file); + +struct ashmem_debug_process_info_args { + struct seq_file *seq; + struct task_struct *tsk; +}; + +static const struct proc_ops debug_process_ashmem_info_fops = { + .proc_open = ashmem_debug_process_info_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static int ashmem_debug_process_info_cb(const void *data, + struct file *f, unsigned int fd) +{ + const struct ashmem_debug_process_info_args *args = data; + struct task_struct *tsk = args->tsk; + + if (!is_ashmem_file(f)) + return 0; + seq_printf(args->seq, + "%s %u %u %s %zu\n", + tsk->comm, tsk->pid, fd, + get_ashmem_name_by_file(f), + get_ashmem_size_by_file(f)); + return 0; +} + +static int ashmem_debug_process_info_show(struct seq_file *s, void *d) +{ + struct task_struct *tsk = NULL; + struct ashmem_debug_process_info_args cb_args; + + seq_puts(s, "Process ashmem detail info:\n"); + seq_puts(s, "----------------------------------------------------\n"); + seq_printf(s, "%s %s %s %s %s\n", + "Process name", "Process ID", + "fd", "ashmem_name", "size"); + + ashmem_mutex_lock(); + rcu_read_lock(); + for_each_process(tsk) { + if (tsk->flags & PF_KTHREAD) + continue; + cb_args.seq = s; + cb_args.tsk = tsk; + + task_lock(tsk); + iterate_fd(tsk->files, 0, + ashmem_debug_process_info_cb, (void *)&cb_args); + task_unlock(tsk); + } + rcu_read_unlock(); + ashmem_mutex_unlock(); + seq_puts(s, "----------------------------------------------------\n"); + return 0; +} + +static int ashmem_debug_process_info_open(struct inode *inode, + struct file *file) +{ + return single_open(file, ashmem_debug_process_info_show, + inode->i_private); +} + +void init_ashmem_process_info(void) +{ + struct proc_dir_entry *entry = NULL; + + entry = proc_create_data("ashmem_process_info", 0444, + NULL, &debug_process_ashmem_info_fops, NULL); + if (!entry) + pr_err("Failed to create ashmem debug info\n"); +} + diff --git a/mm/page_alloc.c b/mm/page_alloc.c index e9357c1a5443139e4e7ed8886d003deda8c7a3d1..e931fcc656f0f4c35a0b951271aa74d361f951fa 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4637,6 +4637,22 @@ static struct page *__page_frag_cache_refill(struct page_frag_cache *nc, page = alloc_pages_node(NUMA_NO_NODE, gfp, 0); nc->va = page ? page_address(page) : NULL; +#ifdef CONFIG_PAGE_TRACING + if (likely(page)) { + int order = get_order(nc->size); + int i; + struct page *newpage = page; + unsigned int deta = 1U << (unsigned int)order; + + for (i = 0; i < (1 << order); i++) { + if (!newpage) + break; + SetPageSKB(newpage); + newpage++; + } + mod_zone_page_state(page_zone(page), NR_SKB_PAGES, (long)deta); + } +#endif return page; } @@ -4645,8 +4661,15 @@ void __page_frag_cache_drain(struct page *page, unsigned int count) { VM_BUG_ON_PAGE(page_ref_count(page) == 0, page); - if (page_ref_sub_and_test(page, count)) + if (page_ref_sub_and_test(page, count)) { +#ifdef CONFIG_PAGE_TRACING + if (likely(page)) { + unsigned int deta = 1U << compound_order(page); + mod_zone_page_state(page_zone(page), NR_SKB_PAGES, -(long)deta); + } +#endif free_the_page(page, compound_order(page)); + } } EXPORT_SYMBOL(__page_frag_cache_drain); @@ -4730,8 +4753,15 @@ void page_frag_free(void *addr) { struct page *page = virt_to_head_page(addr); - if (unlikely(put_page_testzero(page))) + if (unlikely(put_page_testzero(page))) { +#ifdef CONFIG_PAGE_TRACING + if (likely(page)) { + unsigned int deta = 1U << compound_order(page); + mod_zone_page_state(page_zone(page), NR_SKB_PAGES, -(long)deta); + } +#endif free_the_page(page, compound_order(page)); + } } EXPORT_SYMBOL(page_frag_free); diff --git a/mm/rss_threshold.c b/mm/rss_threshold.c new file mode 100644 index 0000000000000000000000000000000000000000..c9d915998dbda2d7ae27fdb5759f234997d24ceb --- /dev/null +++ b/mm/rss_threshold.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mm/rss_threshold.c + * + * Copyright (c) 2022 Huawei Technologies Co., Ltd. + */ +#include +#include +#include +#include "../fs/proc/internal.h" + +int proc_pid_rss(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *task) +{ + struct mm_struct *mm = get_task_mm(task); + unsigned long total_rss; + + if (mm) { + total_rss = get_mm_rss(mm); + seq_printf(m, "VMRss:%lu KB\n", total_rss << (PAGE_SHIFT - 10)); + mmput(mm); + } + return 0; +} + +void listen_rss_threshold(struct mm_struct *mm) +{ + unsigned long total_rss; + + total_rss = get_mm_rss(mm); + + if (!(mm->owner) || mm->rss_threshold == 0) + return; + + total_rss = total_rss << (PAGE_SHIFT - 10); + + if (likely(total_rss <= mm->rss_threshold)) + return; + + if (mm->owner->comm) + pr_err("rss_threshold monitor:Pid:%d [%s] rss size:%lu KB is out of range:%lu KB\n", + mm->owner->pid, mm->owner->comm, + total_rss, + mm->rss_threshold); + else + pr_err("rss_threshold monitor:Pid:%d [NULL] rss size:%lu KB is out of range:%lu KB\n", + mm->owner->pid, + total_rss, + mm->rss_threshold); +} + +static ssize_t rss_threshold_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file_inode(file); + struct task_struct *p; + struct mm_struct *mm = NULL; + unsigned long mem_total; + unsigned long rss_threshold; + int err; + + err = kstrtoul_from_user(buf, count, 0, &rss_threshold); + if (err < 0) + return err; + + mem_total = totalram_pages() << (PAGE_SHIFT - 10); + if (rss_threshold < 0 || rss_threshold > mem_total) + return -EINVAL; + + p = get_proc_task(inode); + if (!p) + return -ESRCH; + + mm = get_task_mm(p); + if (mm) { + mm->rss_threshold = rss_threshold; + listen_rss_threshold(mm); + mmput(mm); + } + + put_task_struct(p); + + return count; +} + +static int rss_threshold_show(struct seq_file *m, void *v) +{ + struct inode *inode = m->private; + struct task_struct *p; + struct mm_struct *mm = NULL; + + p = get_proc_task(inode); + if (!p) + return -ESRCH; + + mm = get_task_mm(p); + if (mm) { + seq_printf(m, "Threshold:%lu KB\n", mm->rss_threshold); + mmput(mm); + } + put_task_struct(p); + + return 0; +} + +static int rss_threshold_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, rss_threshold_show, inode); +} + +const struct file_operations proc_pid_rss_threshold_operations = { + .open = rss_threshold_open, + .read = seq_read, + .write = rss_threshold_write, + .llseek = seq_lseek, + .release = single_release, +}; diff --git a/mm/vmstat.c b/mm/vmstat.c index 3d8ffcc4072af505d760029461e8f05f31290952..9a3e746e1f69400b997b9d2920ac2326eb69fb80 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -1187,7 +1187,9 @@ const char * const vmstat_text[] = { #ifdef CONFIG_UNACCEPTED_MEMORY "nr_unaccepted", #endif - +#ifdef CONFIG_PAGE_TRACING + "nr_skb_pages", +#endif /* enum numa_stat_item counters */ #ifdef CONFIG_NUMA "numa_hit", diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index c82070167d8aa8a1d304affbce7625dd0c851e24..cb2c3964782680fe4aeffe7e23bb025d9fda9937 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c @@ -1008,6 +1008,9 @@ static struct zspage *alloc_zspage(struct zs_pool *pool, } inc_zone_page_state(page, NR_ZSPAGES); +#ifdef CONFIG_PAGE_TRACING + SetPageZspage(page); +#endif pages[i] = page; }