123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- // 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 uint kswapd_threads;
- module_param_named(kswapd_threads, kswapd_threads, uint, 0644);
- 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 scan_abort_checks(void *data, bool *check_wmarks)
- {
- *check_wmarks = true;
- }
- 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;
- 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;
- }
- }
- if (IS_ENABLED(CONFIG_LRU_GEN)) {
- ret = register_trace_android_vh_scan_abort_check_wmarks(
- scan_abort_checks,
- NULL);
- if (ret) {
- pr_err("Failed to register scan_abort_check_wmarks\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");
|