Files
2026-09-22 09:38:26 +08:00

203 lines
5.4 KiB
C

/* mm/emem.c
*
* The enhance meminfo show system memory information, when processes with
* r ange of oom_score_adj values will get killed. The killed process
* oom_score_adj values is written in /proc/sys/vm/emem_trigger. If the
* written value is less then Threshold, the meminfo is shown.
* Threshold of killed process adj is set in
* /sys/module/emem/parameters/killed_proc_adj_threshold
*
* Copyright (C) 2018-2019 UNISOC, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mmzone.h>
#include <linux/sched.h>
#include <linux/swap.h>
#include <linux/oom.h>
#include <linux/rcupdate.h>
#include <linux/profile.h>
#include <linux/notifier.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/mm_inline.h>
#include <linux/proc_fs.h>
#include <linux/of_reserved_mem.h>
#include "internal.h"
#define DEFAULT_PROC_ADJ 900
#define SHOW_RESERVED_MEM 1
#ifdef CONFIG_SPRD_DEBUG
#define EMEM_SHOW_INTERVAL 2
#else
#define EMEM_SHOW_INTERVAL 5
#endif
#define EMEM_SHOW_KILL_ADJ900_INTERVAL 600
/*
* The written value is the killed process adj, then trigger to show enhance
* memory information. it's written to /proc/emem_trigger
*/
static int sysctl_emem_trigger;
static struct work_struct emem_work;
static DEFINE_SPINLOCK(emem_lock);
/* User knob to enable/disable enhance meminfo feature */
static int enable_enhance_meminfo;
/* killed process oom score adj threshold */
static int killed_proc_adj_threshold = 200;
module_param_named(enable, enable_enhance_meminfo, int, 0644);
module_param_named(killed_proc_adj_threshold, killed_proc_adj_threshold,
int, 0644);
static void dump_tasks_info(void)
{
struct task_struct *p;
struct task_struct *task;
pr_info("[ pid ] uid tgid total_vm rss swap cpu oom_score_adj name\n");
rcu_read_lock();
for_each_process(p) {
/* check unkillable tasks */
if (is_global_init(p))
continue;
if (p->flags & PF_KTHREAD)
continue;
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;
}
pr_info("[%5d] %5d %5d %8lu %8lu %6lu %3u %5d %s\n",
task->pid, from_kuid(&init_user_ns, task_uid(task)),
task->tgid, task->mm->total_vm, get_mm_rss(task->mm),
get_mm_counter(task->mm, MM_SWAPENTS),
task_cpu(task),
task->signal->oom_score_adj, task->comm);
task_unlock(task);
}
rcu_read_unlock();
}
static void enhance_meminfo(u64 interval)
{
struct timespec64 val;
static u64 last_time;
ktime_get_real_ts64(&val);
if (val.tv_sec - last_time > interval) {
pr_info("++++++++++++++++++++++E_SHOW_MEM_BEGIN++++++++++++++++++++\n");
pr_info("The killed process adj = %d\n", sysctl_emem_trigger);
enhanced_show_mem();
last_time = val.tv_sec;
pr_info("+++++++++++++++++++++++E_SHOW_MEM_END+++++++++++++++++++++\n");
}
}
static void emem_workfn(struct work_struct *work)
{
if (enable_enhance_meminfo) {
if (sysctl_emem_trigger <= killed_proc_adj_threshold)
enhance_meminfo(EMEM_SHOW_INTERVAL);
else
enhance_meminfo(EMEM_SHOW_KILL_ADJ900_INTERVAL);
if (sysctl_emem_trigger == SHOW_RESERVED_MEM) {
sysctl_emem_trigger++;
pr_info("Resrved memory info:\n");
show_reserved_memory_info();
}
}
}
static int tasks_e_show_mem_handler(struct notifier_block *nb,
unsigned long val, void *data)
{
struct sysinfo si;
si_swapinfo(&si);
pr_info("Enhanced Mem-info :TASK\n");
pr_info("Detail:\n");
dump_tasks_info();
pr_info("Total used:\n");
pr_info(" anon: %lu kB\n", ((global_node_page_state(NR_ACTIVE_ANON)
+ global_node_page_state(NR_INACTIVE_ANON)) << PAGE_SHIFT)
/ 1024);
pr_info(" swaped: %lu kB\n", ((si.totalswap - si.freeswap)
<< PAGE_SHIFT) / 1024);
return 0;
}
static struct notifier_block tasks_e_show_mem_notifier = {
.notifier_call = tasks_e_show_mem_handler,
};
static ssize_t emem_trigger_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char buffer[12];
int trigger_adj;
int ret;
memset(buffer, 0, sizeof(buffer));
if (count > sizeof(buffer) - 1)
count = sizeof(buffer) - 1;
if (copy_from_user(buffer, buf, count))
return -EFAULT;
ret = kstrtoint(buffer, 0, &trigger_adj);
if (ret)
return ret;
sysctl_emem_trigger = trigger_adj;
if (sysctl_emem_trigger <= DEFAULT_PROC_ADJ) {
spin_lock(&emem_lock);
queue_work(system_power_efficient_wq, &emem_work);
spin_unlock(&emem_lock);
}
return count;
}
const struct file_operations proc_emem_trigger_operations = {
.write = emem_trigger_write,
};
static int __init emem_init(void)
{
INIT_WORK(&emem_work, emem_workfn);
register_e_show_mem_notifier(&tasks_e_show_mem_notifier);
proc_create("emem_trigger", S_IWUSR, NULL, &proc_emem_trigger_operations);
return 0;
}
subsys_initcall(emem_init);