// SPDX-License-Identifier: GPL-2.0 /* * mm/reclaim_acct.c * * Copyright (c) 2022 Huawei Technologies Co., Ltd. */ #include #include #include "internal.h" const char *stub_name[NR_RA_STUBS] = { "direct_reclaim", "drain_all_pages", "shrink_file_list", "shrink_anon_list", "shrink_slab", }; /* Once initialized, the variable should never be changed */ static bool reclaimacct_is_off = true; static int reclaimacct_disable = 1; static void reclaimacct_free(struct reclaim_acct *ra, enum reclaim_type type) { memset(ra, 0, sizeof(struct reclaim_acct)); } static void __reclaimacct_end(struct reclaim_acct *ra, u64 freed, enum reclaimacct_stubs stub, const struct shrinker *shrinker) { u64 now, delay, start; start = ra->start[stub]; now = ktime_get_ns(); if (now < start) return; delay = now - start; if (delay < DELAY_LV5 || is_system_reclaim(ra->reclaim_type)) { ra->delay[stub] += delay; ra->count[stub]++; ra->freed[stub] += freed; } if (delay > DELAY_LV4 && delay < DELAY_LV5) { pr_warn_ratelimited("%s timeout:%llu\n", stub_name[stub], delay); if (shrinker) pr_warn_ratelimited("shrinker = %pF\n", shrinker); } } void reclaimacct_tsk_init(struct task_struct *tsk) { if (tsk) tsk->reclaim_acct = NULL; } /* Reinitialize in case parent's non-null pointer was duped */ void reclaimacct_init(void) { reclaimacct_tsk_init(&init_task); } void reclaimacct_substage_start(enum reclaimacct_stubs stub) { if (!current->reclaim_acct) return; current->reclaim_acct->start[stub] = ktime_get_ns(); } void reclaimacct_substage_end(enum reclaimacct_stubs stub, unsigned long freed, const struct shrinker *shrinker) { if (!current->reclaim_acct) return; __reclaimacct_end(current->reclaim_acct, freed, stub, shrinker); } static void reclaimacct_directreclaim_end(struct reclaim_acct *ra) { int i; if (ra->delay[RA_RECLAIM] > DELAY_LV4) { pr_warn_ratelimited("Summary"); for (i = 0; i < NR_RA_STUBS; i++) pr_warn_ratelimited(" %s=%llu %llu", stub_name[i], ra->delay[i], ra->count[i]); pr_warn_ratelimited("\n"); } reclaimacct_collect_data(); reclaimacct_free(ra, ra->reclaim_type); } static void reclaimacct_system_reclaim_end(struct reclaim_acct *ra) { reclaimacct_free(ra, ra->reclaim_type); } void reclaimacct_start(enum reclaim_type type, struct reclaim_acct *ra) { if (reclaimacct_disable || reclaimacct_is_off) return; if (!current->reclaim_acct) current->reclaim_acct = ra; current->reclaim_acct->reclaim_type = type; current->reclaim_acct->start[RA_RECLAIM] = ktime_get_ns(); } void reclaimacct_end(enum reclaim_type type) { if (!current->reclaim_acct) return; __reclaimacct_end(current->reclaim_acct, 0, RA_RECLAIM, NULL); reclaimacct_collect_reclaim_efficiency(); if (is_system_reclaim(type)) reclaimacct_system_reclaim_end(current->reclaim_acct); else reclaimacct_directreclaim_end(current->reclaim_acct); current->reclaim_acct = NULL; } /* Reclaim accounting module initialize */ static int reclaimacct_init_handle(void *p) { if (!reclaimacct_initialize_show_data()) goto alloc_show_failed; reclaimacct_is_off = false; pr_info("enabled\n"); return 0; alloc_show_failed: reclaimacct_is_off = true; pr_err("disabled\n"); return 0; } static int __init reclaimacct_module_init(void) { struct task_struct *task = NULL; task = kthread_run(reclaimacct_init_handle, NULL, "reclaimacct_init"); if (IS_ERR(task)) pr_err("run reclaimacct_init failed\n"); else pr_info("run reclaimacct_init successfully\n"); return 0; } late_initcall(reclaimacct_module_init); static int reclaimacct_disable_set(const char *val, const struct kernel_param *kp) { int ret; ret = param_set_int(val, kp); if (ret) return ret; if (!reclaimacct_disable) reclaimacct_reinitialize_show_data(); return 0; } static const struct kernel_param_ops reclaimacct_disable_ops = { .set = reclaimacct_disable_set, .get = param_get_int, }; module_param_cb(disable, &reclaimacct_disable_ops, &reclaimacct_disable, 0644);