Rtwo/kernel/motorola/sm8550/drivers/soc/qcom/mem-hooks.c

223 lines
5 KiB
C
Raw Normal View History

2025-09-30 20:22:48 -04:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Took is_el1_instruction_abort() from arch/arm64/mm/fault.c
* Copyright (C) 2012 ARM Ltd
*/
#include <linux/module.h>
#include <linux/oom.h>
#include <trace/hooks/mm.h>
#include <trace/hooks/vmscan.h>
#include <linux/printk.h>
#include <linux/nodemask.h>
#include <linux/kthread.h>
#include <linux/swap.h>
#include <trace/hooks/fault.h>
#include <asm/esr.h>
#include <asm/ptrace.h>
static unsigned long panic_on_oom_timeout;
struct task_struct *saved_tsk;
static uint kswapd_threads;
module_param_named(kswapd_threads, kswapd_threads, uint, 0644);
#define PANIC_ON_OOM_DEFER_TIMEOUT (5*HZ)
static void readahead_set(void *data, gfp_t *flag)
{
if (*flag & __GFP_MOVABLE) {
*flag |= __GFP_CMA;
*flag &= ~__GFP_HIGHMEM;
}
}
static void gfp_zone_set(void *data, gfp_t *flag)
{
if (!IS_ENABLED(CONFIG_HIGHMEM)) {
if ((*flag & __GFP_MOVABLE) && !(*flag & __GFP_CMA))
*flag &= ~__GFP_HIGHMEM;
}
}
static void set_swap_cache(void *data, gfp_t *flag)
{
*flag |= __GFP_CMA;
}
static void __maybe_unused __oom_panic_defer(void *data, struct oom_control *oc, int *val)
{
int ret = 0;
struct task_struct *p;
if (oc->chosen)
goto out;
rcu_read_lock();
for_each_process(p) {
if (tsk_is_oom_victim(p))
break;
}
rcu_read_unlock();
if (p == &init_task)
goto out;
if (p != saved_tsk) {
panic_on_oom_timeout = jiffies + PANIC_ON_OOM_DEFER_TIMEOUT;
saved_tsk = p;
ret = -1;
} else if (time_before_eq(jiffies, panic_on_oom_timeout)) {
ret = -1;
}
out:
*val = ret;
}
static void balance_reclaim(void *unused, bool *balance_anon_file_reclaim)
{
*balance_anon_file_reclaim = true;
}
static int kswapd_per_node_run(int nid, unsigned int kswapd_threads)
{
pg_data_t *pgdat = NODE_DATA(nid);
unsigned int hid, start = 0;
int ret = 0;
if (pgdat->kswapd) {
start = 1;
pgdat->mkswapd[0] = pgdat->kswapd;
}
for (hid = start; hid < kswapd_threads; ++hid) {
pgdat->mkswapd[hid] = kthread_run(kswapd, pgdat, "kswapd%d:%d",
nid, hid);
if (IS_ERR(pgdat->mkswapd[hid])) {
/* failure at boot is fatal */
WARN_ON(system_state < SYSTEM_RUNNING);
pr_err("Failed to start kswapd%d on node %d\n",
hid, nid);
ret = PTR_ERR(pgdat->mkswapd[hid]);
pgdat->mkswapd[hid] = NULL;
continue;
}
if (!pgdat->kswapd)
pgdat->kswapd = pgdat->mkswapd[hid];
}
return ret;
}
static void kswapd_per_node_stop(int nid, unsigned int kswapd_threads)
{
int hid = 0;
struct task_struct *kswapd;
for (hid = 0; hid < kswapd_threads; hid++) {
kswapd = NODE_DATA(nid)->mkswapd[hid];
if (kswapd) {
kthread_stop(kswapd);
NODE_DATA(nid)->mkswapd[hid] = NULL;
}
}
NODE_DATA(nid)->kswapd = NULL;
}
static void kswapd_threads_set(void *unused, int nid, bool *skip, bool run)
{
*skip = true;
if (run)
kswapd_per_node_run(nid, kswapd_threads);
else
kswapd_per_node_stop(nid, kswapd_threads);
}
static int init_kswapd_per_node_hook(void)
{
int ret = 0;
int nid;
if (kswapd_threads > MAX_KSWAPD_THREADS) {
pr_err("Failed to set kswapd_threads to %d ,Max limit is %d\n",
kswapd_threads, MAX_KSWAPD_THREADS);
return ret;
} else if (kswapd_threads > 1) {
ret = register_trace_android_vh_kswapd_per_node(kswapd_threads_set, NULL);
if (ret) {
pr_err("Failed to register kswapd_per_node hooks\n");
return ret;
}
for_each_node_state(nid, N_MEMORY)
kswapd_per_node_run(nid, kswapd_threads);
}
return ret;
}
static bool is_el1_instruction_abort(unsigned long esr)
{
return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_CUR;
}
static void can_fixup_sea(void *unused, unsigned long addr, unsigned long esr,
struct pt_regs *regs, bool *can_fixup)
{
if (!user_mode(regs) && !is_el1_instruction_abort(esr))
*can_fixup = true;
else
*can_fixup = false;
}
static int __init init_mem_hooks(void)
{
int ret;
ret = init_kswapd_per_node_hook();
if (ret)
return ret;
ret = register_trace_android_rvh_set_readahead_gfp_mask(readahead_set, NULL);
if (ret) {
pr_err("Failed to register readahead_gfp_mask hooks\n");
return ret;
}
ret = register_trace_android_rvh_set_gfp_zone_flags(gfp_zone_set, NULL);
if (ret) {
pr_err("Failed to register gfp_zone_flags hooks\n");
return ret;
}
ret = register_trace_android_rvh_set_skip_swapcache_flags(set_swap_cache, NULL);
if (ret) {
pr_err("Failed to register skip_swapcache_flags hooks\n");
return ret;
}
if (IS_ENABLED(CONFIG_QCOM_BALANCE_ANON_FILE_RECLAIM)) {
ret = register_trace_android_rvh_set_balance_anon_file_reclaim(balance_reclaim,
NULL);
if (ret) {
pr_err("Failed to register balance_anon_file_reclaim hooks\n");
return ret;
}
}
ret = register_trace_android_vh_try_fixup_sea(can_fixup_sea, NULL);
if (ret) {
pr_err("Failed to register try_fixup_sea\n");
return ret;
}
return 0;
}
module_init(init_mem_hooks);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Memory Trace Hook Call-Back Registration");
MODULE_LICENSE("GPL v2");