// SPDX-License-Identifier: GPL-2.0 /* * mm/reclaimacct_show.c * * Copyright (c) 2022 Huawei Technologies Co., Ltd. */ #include #include #include #include #include #include "internal.h" /* Store reclaim accounting data */ static struct reclaimacct_show { u64 delay[NR_DELAY_LV][NR_RA_STUBS]; u64 count[NR_DELAY_LV][NR_RA_STUBS]; u64 max_delay; u64 max_delay_time; } *ra_show; static DEFINE_SPINLOCK(ra_show_lock); static struct reclaim_efficiency { u64 time[NR_RA_STUBS]; u64 freed[NR_RA_STUBS]; } *ra_eff; static DEFINE_SPINLOCK(ra_eff_lock); bool reclaimacct_initialize_show_data(void) { ra_show = kzalloc(sizeof(struct reclaimacct_show), GFP_KERNEL); if (!ra_show) goto fail_show; ra_eff = kzalloc(sizeof(struct reclaim_efficiency) * RECLAIM_TYPES, GFP_KERNEL); if (!ra_eff) goto fail_eff; return true; fail_eff: kfree(ra_show); ra_show = NULL; fail_show: return false; } void reclaimacct_reinitialize_show_data(void) { if (ra_show) memset(ra_show, 0, sizeof(struct reclaimacct_show)); if (ra_eff) memset(ra_eff, 0, sizeof(struct reclaim_efficiency) * RECLAIM_TYPES); } void reclaimacct_destroy_show_data(void) { kfree(ra_show); ra_show = NULL; kfree(ra_eff); ra_eff = NULL; } static void __reclaimacct_collect_data(int level, struct reclaim_acct *ra) { int i; spin_lock(&ra_show_lock); for (i = 0; i < NR_RA_STUBS; i++) { ra_show->delay[level][i] += ra->delay[i]; ra_show->count[level][i] += ra->count[i]; } if (ra->delay[RA_RECLAIM] > ra_show->max_delay) { ra_show->max_delay = ra->delay[RA_RECLAIM]; ra_show->max_delay_time = sched_clock(); } spin_unlock(&ra_show_lock); } void reclaimacct_collect_data(void) { int i; const u64 delay[NR_DELAY_LV] = { DELAY_LV0, DELAY_LV1, DELAY_LV2, DELAY_LV3, DELAY_LV4, DELAY_LV5 }; if (!ra_show || !current->reclaim_acct) return; for (i = 0; i < NR_DELAY_LV; i++) { if (current->reclaim_acct->delay[RA_RECLAIM] < delay[i]) { __reclaimacct_collect_data(i, current->reclaim_acct); break; } } } static int reclaimacct_proc_show(struct seq_file *m, void *v) { int i, j; struct reclaimacct_show show; const char *stub_name[NR_RA_STUBS] = { "direct_reclaim", "drain_all_pages", "shrink_file_list", "shrink_anon_list", "shrink_slab", }; if (!ra_show) return 0; spin_lock(&ra_show_lock); memcpy(&show, ra_show, sizeof(struct reclaimacct_show)); spin_unlock(&ra_show_lock); seq_puts(m, "watch_point(unit:ms/-)\t\t0-5ms\t\t5-10ms\t\t"); seq_puts(m, "10-50ms\t\t50-100ms\t100-2000ms\t2000-50000ms\n"); for (i = 0; i < NR_RA_STUBS; i++) { seq_printf(m, "%s_delay\t\t", stub_name[i]); for (j = 0; j < NR_DELAY_LV; j++) seq_printf(m, "%-15llu ", div_u64(show.delay[j][i], NSEC_PER_MSEC)); seq_puts(m, "\n"); seq_printf(m, "%s_count\t\t", stub_name[i]); for (j = 0; j < NR_DELAY_LV; j++) seq_printf(m, "%-15llu ", show.count[j][i]); seq_puts(m, "\n"); } seq_printf(m, "Max delay: %llu\tHappened: %llu\n", show.max_delay, show.max_delay_time); return 0; } static int reclaimacct_proc_open(struct inode *inode, struct file *file) { return single_open(file, reclaimacct_proc_show, NULL); } static const struct proc_ops reclaimacct_proc_fops = { .proc_open = reclaimacct_proc_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, }; static void __reclaimacct_collect_reclaim_efficiency( struct reclaim_acct *ra, enum reclaim_type type) { int i; ra->freed[RA_RECLAIM] = ra->freed[RA_SHRINKFILE] + ra->freed[RA_SHRINKANON]; /* system_reclaim(kswapd/zswapd) is single thread, do not need lock */ if (!is_system_reclaim(type)) spin_lock(&ra_eff_lock); for (i = 0; i < NR_RA_STUBS; i++) { ra_eff[type].time[i] += ra->delay[i]; ra_eff[type].freed[i] += ra->freed[i]; } if (!is_system_reclaim(type)) spin_unlock(&ra_eff_lock); } void reclaimacct_collect_reclaim_efficiency(void) { if (!ra_eff || !current->reclaim_acct) return; __reclaimacct_collect_reclaim_efficiency(current->reclaim_acct, current->reclaim_acct->reclaim_type); } static int reclaim_efficiency_proc_show(struct seq_file *m, void *v) { int i, j; struct reclaim_efficiency eff[RECLAIM_TYPES]; const char *stage[NR_RA_STUBS] = { "total_process", "drain_pages ", "shrink_file ", "shrink_anon ", "shrink_slab " }; const char *type[RECLAIM_TYPES] = { "direct reclaim", "kswapd ", "zswapd " }; if (!ra_eff) return 0; spin_lock(&ra_eff_lock); memcpy(&eff, ra_eff, sizeof(eff)); spin_unlock(&ra_eff_lock); for (i = 0; i < RECLAIM_TYPES; i++) { seq_printf(m, "%s time(ms) freed(page/obj)\n", type[i]); for (j = 0; j < NR_RA_STUBS; j++) seq_printf(m, "%s %-15llu %-15llu\n", stage[j], div_u64(eff[i].time[j], NSEC_PER_MSEC), eff[i].freed[j]); } return 0; } static int reclaim_efficiency_proc_open(struct inode *inode, struct file *file) { return single_open(file, reclaim_efficiency_proc_show, NULL); } static const struct proc_ops reclaim_effi_proc_fops = { .proc_open = reclaim_efficiency_proc_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, }; static int __init proc_reclaimacct_init(void) { proc_create("reclaimacct", 0440, NULL, &reclaimacct_proc_fops); proc_create("reclaim_efficiency", 0440, NULL, &reclaim_effi_proc_fops); return 0; } fs_initcall(proc_reclaimacct_init);