diff --git a/mm/Kconfig b/mm/Kconfig index 11f2f83389e57cc2ed350c11130eabfb15bd0c40..2acd0e298db61072dbb9e1e7638b0f17be263a54 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -1263,6 +1263,20 @@ config ANON_VMA_NAME Assigning a name to anonymous virtual memory area might prevent that 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 config USERFAULTFD bool "Enable userfaultfd() system call" diff --git a/mm/Makefile b/mm/Makefile index 94ea4fb431637fdb8c465637ddc4211279e1102d..2aaf330799e6e00fc9f7579050e7e8112b120ffb 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -71,6 +71,8 @@ ifdef CONFIG_MMU obj-$(CONFIG_ADVISE_SYSCALLS) += madvise.o endif +obj-$(CONFIG_LMKD_DBG) += lmkd_dbg_trigger.o +obj-$(CONFIG_LOWMEM) += lowmem_dbg.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/lmkd_dbg_trigger.c b/mm/lmkd_dbg_trigger.c new file mode 100644 index 0000000000000000000000000000000000000000..1ebc3418bb2cd325c0e26e01b7c2bfcb686e64f4 --- /dev/null +++ b/mm/lmkd_dbg_trigger.c @@ -0,0 +1,82 @@ +// 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..6262f4bf3df648d411aeac9a16fb2ae35ac5dff1 --- /dev/null +++ b/mm/lowmem_dbg.c @@ -0,0 +1,109 @@ +// 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); + } +}