// SPDX-License-Identifier: GPL-2.0 /* * mm/reclaim_acct.c * * Copyright (c) 2022 Huawei Technologies Co., Ltd. */ #include #include #include "internal.h" /* 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); current->reclaim_acct = NULL; } 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); } /* Reclaim accounting module initialize */ static int reclaimacct_init_handle(void *p) { int i; 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); module_param_named(disable, reclaimacct_disable, int, 0644);