mem-hooks.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
  4. *
  5. * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  6. *
  7. * Took is_el1_instruction_abort() from arch/arm64/mm/fault.c
  8. * Copyright (C) 2012 ARM Ltd
  9. */
  10. #include <linux/module.h>
  11. #include <linux/oom.h>
  12. #include <trace/hooks/mm.h>
  13. #include <trace/hooks/vmscan.h>
  14. #include <linux/printk.h>
  15. #include <linux/nodemask.h>
  16. #include <linux/kthread.h>
  17. #include <linux/swap.h>
  18. #include <trace/hooks/fault.h>
  19. #include <asm/esr.h>
  20. #include <asm/ptrace.h>
  21. static uint kswapd_threads;
  22. module_param_named(kswapd_threads, kswapd_threads, uint, 0644);
  23. static void balance_reclaim(void *unused, bool *balance_anon_file_reclaim)
  24. {
  25. *balance_anon_file_reclaim = true;
  26. }
  27. static int kswapd_per_node_run(int nid, unsigned int kswapd_threads)
  28. {
  29. pg_data_t *pgdat = NODE_DATA(nid);
  30. unsigned int hid, start = 0;
  31. int ret = 0;
  32. if (pgdat->kswapd) {
  33. start = 1;
  34. pgdat->mkswapd[0] = pgdat->kswapd;
  35. }
  36. for (hid = start; hid < kswapd_threads; ++hid) {
  37. pgdat->mkswapd[hid] = kthread_run(kswapd, pgdat, "kswapd%d:%d",
  38. nid, hid);
  39. if (IS_ERR(pgdat->mkswapd[hid])) {
  40. /* failure at boot is fatal */
  41. WARN_ON(system_state < SYSTEM_RUNNING);
  42. pr_err("Failed to start kswapd%d on node %d\n",
  43. hid, nid);
  44. ret = PTR_ERR(pgdat->mkswapd[hid]);
  45. pgdat->mkswapd[hid] = NULL;
  46. continue;
  47. }
  48. if (!pgdat->kswapd)
  49. pgdat->kswapd = pgdat->mkswapd[hid];
  50. }
  51. return ret;
  52. }
  53. static void kswapd_per_node_stop(int nid, unsigned int kswapd_threads)
  54. {
  55. int hid = 0;
  56. struct task_struct *kswapd;
  57. for (hid = 0; hid < kswapd_threads; hid++) {
  58. kswapd = NODE_DATA(nid)->mkswapd[hid];
  59. if (kswapd) {
  60. kthread_stop(kswapd);
  61. NODE_DATA(nid)->mkswapd[hid] = NULL;
  62. }
  63. }
  64. NODE_DATA(nid)->kswapd = NULL;
  65. }
  66. static void scan_abort_checks(void *data, bool *check_wmarks)
  67. {
  68. *check_wmarks = true;
  69. }
  70. static void kswapd_threads_set(void *unused, int nid, bool *skip, bool run)
  71. {
  72. *skip = true;
  73. if (run)
  74. kswapd_per_node_run(nid, kswapd_threads);
  75. else
  76. kswapd_per_node_stop(nid, kswapd_threads);
  77. }
  78. static int init_kswapd_per_node_hook(void)
  79. {
  80. int ret = 0;
  81. int nid;
  82. if (kswapd_threads > MAX_KSWAPD_THREADS) {
  83. pr_err("Failed to set kswapd_threads to %d ,Max limit is %d\n",
  84. kswapd_threads, MAX_KSWAPD_THREADS);
  85. return ret;
  86. } else if (kswapd_threads > 1) {
  87. ret = register_trace_android_vh_kswapd_per_node(kswapd_threads_set, NULL);
  88. if (ret) {
  89. pr_err("Failed to register kswapd_per_node hooks\n");
  90. return ret;
  91. }
  92. for_each_node_state(nid, N_MEMORY)
  93. kswapd_per_node_run(nid, kswapd_threads);
  94. }
  95. return ret;
  96. }
  97. static bool is_el1_instruction_abort(unsigned long esr)
  98. {
  99. return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_CUR;
  100. }
  101. static void can_fixup_sea(void *unused, unsigned long addr, unsigned long esr,
  102. struct pt_regs *regs, bool *can_fixup)
  103. {
  104. if (!user_mode(regs) && !is_el1_instruction_abort(esr))
  105. *can_fixup = true;
  106. else
  107. *can_fixup = false;
  108. }
  109. static int __init init_mem_hooks(void)
  110. {
  111. int ret;
  112. ret = init_kswapd_per_node_hook();
  113. if (ret)
  114. return ret;
  115. if (IS_ENABLED(CONFIG_QCOM_BALANCE_ANON_FILE_RECLAIM)) {
  116. ret = register_trace_android_rvh_set_balance_anon_file_reclaim(
  117. balance_reclaim,
  118. NULL);
  119. if (ret) {
  120. pr_err("Failed to register balance_anon_file_reclaim hooks\n");
  121. return ret;
  122. }
  123. }
  124. if (IS_ENABLED(CONFIG_LRU_GEN)) {
  125. ret = register_trace_android_vh_scan_abort_check_wmarks(
  126. scan_abort_checks,
  127. NULL);
  128. if (ret) {
  129. pr_err("Failed to register scan_abort_check_wmarks\n");
  130. return ret;
  131. }
  132. }
  133. ret = register_trace_android_vh_try_fixup_sea(can_fixup_sea, NULL);
  134. if (ret) {
  135. pr_err("Failed to register try_fixup_sea\n");
  136. return ret;
  137. }
  138. return 0;
  139. }
  140. module_init(init_mem_hooks);
  141. MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Memory Trace Hook Call-Back Registration");
  142. MODULE_LICENSE("GPL");