• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * mm/reclaimacct_show.c
4  *
5  * Copyright (c) 2022 Huawei Technologies Co., Ltd.
6  */
7 
8 #include <linux/fs.h>
9 #include <linux/proc_fs.h>
10 #include <linux/seq_file.h>
11 #include <linux/slab.h>
12 #include <linux/time64.h>
13 
14 #include "internal.h"
15 
16 /* Store reclaim accounting data */
17 static struct reclaimacct_show {
18 	u64 delay[NR_DELAY_LV][NR_RA_STUBS];
19 	u64 count[NR_DELAY_LV][NR_RA_STUBS];
20 	u64 max_delay;
21 	u64 max_delay_time;
22 } *ra_show;
23 static DEFINE_SPINLOCK(ra_show_lock);
24 
25 static struct reclaim_efficiency {
26 	u64 time[NR_RA_STUBS];
27 	u64 freed[NR_RA_STUBS];
28 } *ra_eff;
29 static DEFINE_SPINLOCK(ra_eff_lock);
30 
reclaimacct_initialize_show_data(void)31 bool reclaimacct_initialize_show_data(void)
32 {
33 	ra_show = kzalloc(sizeof(struct reclaimacct_show), GFP_KERNEL);
34 	if (!ra_show)
35 		goto fail_show;
36 
37 	ra_eff = kzalloc(sizeof(struct reclaim_efficiency) * RECLAIM_TYPES, GFP_KERNEL);
38 	if (!ra_eff)
39 		goto fail_eff;
40 	return true;
41 
42 fail_eff:
43 	kfree(ra_show);
44 	ra_show = NULL;
45 
46 fail_show:
47 	return false;
48 }
49 
reclaimacct_reinitialize_show_data(void)50 void reclaimacct_reinitialize_show_data(void)
51 {
52 	if (ra_show)
53 		memset(ra_show, 0, sizeof(struct reclaimacct_show));
54 
55 	if (ra_eff)
56 		memset(ra_eff, 0, sizeof(struct reclaim_efficiency) * RECLAIM_TYPES);
57 }
58 
reclaimacct_destroy_show_data(void)59 void reclaimacct_destroy_show_data(void)
60 {
61 	kfree(ra_show);
62 	ra_show = NULL;
63 
64 	kfree(ra_eff);
65 	ra_eff = NULL;
66 }
67 
__reclaimacct_collect_data(int level,struct reclaim_acct * ra)68 static void __reclaimacct_collect_data(int level, struct reclaim_acct *ra)
69 {
70 	int i;
71 
72 	spin_lock(&ra_show_lock);
73 	for (i = 0; i < NR_RA_STUBS; i++) {
74 		ra_show->delay[level][i] += ra->delay[i];
75 		ra_show->count[level][i] += ra->count[i];
76 	}
77 
78 	if (ra->delay[RA_RECLAIM] > ra_show->max_delay) {
79 		ra_show->max_delay = ra->delay[RA_RECLAIM];
80 		ra_show->max_delay_time = sched_clock();
81 	}
82 	spin_unlock(&ra_show_lock);
83 }
84 
reclaimacct_collect_data(void)85 void reclaimacct_collect_data(void)
86 {
87 	int i;
88 	const u64 delay[NR_DELAY_LV] = {
89 		DELAY_LV0, DELAY_LV1, DELAY_LV2, DELAY_LV3, DELAY_LV4, DELAY_LV5
90 	};
91 
92 	if (!ra_show || !current->reclaim_acct)
93 		return;
94 
95 	for (i = 0; i < NR_DELAY_LV; i++) {
96 		if (current->reclaim_acct->delay[RA_RECLAIM] < delay[i]) {
97 			__reclaimacct_collect_data(i, current->reclaim_acct);
98 			break;
99 		}
100 	}
101 }
102 
reclaimacct_proc_show(struct seq_file * m,void * v)103 static int reclaimacct_proc_show(struct seq_file *m, void *v)
104 {
105 	int i, j;
106 	struct reclaimacct_show show;
107 	const char *stub_name[NR_RA_STUBS] = {
108 		"direct_reclaim",
109 		"drain_all_pages",
110 		"shrink_file_list",
111 		"shrink_anon_list",
112 		"shrink_slab",
113 	};
114 
115 	if (!ra_show)
116 		return 0;
117 
118 	spin_lock(&ra_show_lock);
119 	memcpy(&show, ra_show, sizeof(struct reclaimacct_show));
120 	spin_unlock(&ra_show_lock);
121 
122 	seq_puts(m, "watch_point(unit:ms/-)\t\t0-5ms\t\t5-10ms\t\t");
123 	seq_puts(m, "10-50ms\t\t50-100ms\t100-2000ms\t2000-50000ms\n");
124 	for (i = 0; i < NR_RA_STUBS; i++) {
125 		seq_printf(m, "%s_delay\t\t", stub_name[i]);
126 		for (j = 0; j < NR_DELAY_LV; j++)
127 			seq_printf(m, "%-15llu ", div_u64(show.delay[j][i], NSEC_PER_MSEC));
128 		seq_puts(m, "\n");
129 
130 		seq_printf(m, "%s_count\t\t", stub_name[i]);
131 		for (j = 0; j < NR_DELAY_LV; j++)
132 			seq_printf(m, "%-15llu ", show.count[j][i]);
133 		seq_puts(m, "\n");
134 	}
135 	seq_printf(m, "Max delay: %llu\tHappened: %llu\n", show.max_delay, show.max_delay_time);
136 
137 	return 0;
138 }
139 
reclaimacct_proc_open(struct inode * inode,struct file * file)140 static int reclaimacct_proc_open(struct inode *inode, struct file *file)
141 {
142 	return single_open(file, reclaimacct_proc_show, NULL);
143 }
144 
145 static const struct proc_ops reclaimacct_proc_fops = {
146 	.proc_open = reclaimacct_proc_open,
147 	.proc_read = seq_read,
148 	.proc_lseek = seq_lseek,
149 	.proc_release = single_release,
150 };
151 
__reclaimacct_collect_reclaim_efficiency(struct reclaim_acct * ra,enum reclaim_type type)152 static void __reclaimacct_collect_reclaim_efficiency(
153 	struct reclaim_acct *ra, enum reclaim_type type)
154 {
155 	int i;
156 
157 	ra->freed[RA_RECLAIM] = ra->freed[RA_SHRINKFILE] + ra->freed[RA_SHRINKANON];
158 
159 	/* system_reclaim(kswapd/zswapd) is single thread, do not need lock */
160 	if (!is_system_reclaim(type))
161 		spin_lock(&ra_eff_lock);
162 
163 	for (i = 0; i < NR_RA_STUBS; i++) {
164 		ra_eff[type].time[i] += ra->delay[i];
165 		ra_eff[type].freed[i] += ra->freed[i];
166 	}
167 
168 	if (!is_system_reclaim(type))
169 		spin_unlock(&ra_eff_lock);
170 }
171 
reclaimacct_collect_reclaim_efficiency(void)172 void reclaimacct_collect_reclaim_efficiency(void)
173 {
174 	if (!ra_eff || !current->reclaim_acct)
175 		return;
176 
177 	__reclaimacct_collect_reclaim_efficiency(current->reclaim_acct,
178 		current->reclaim_acct->reclaim_type);
179 }
180 
reclaim_efficiency_proc_show(struct seq_file * m,void * v)181 static int reclaim_efficiency_proc_show(struct seq_file *m, void *v)
182 {
183 	int i, j;
184 	struct reclaim_efficiency eff[RECLAIM_TYPES];
185 	const char *stage[NR_RA_STUBS] = {
186 		"total_process",
187 		"drain_pages  ",
188 		"shrink_file  ",
189 		"shrink_anon  ",
190 		"shrink_slab  "
191 	};
192 	const char *type[RECLAIM_TYPES] = {
193 		"direct reclaim",
194 		"kswapd        ",
195 		"zswapd        "
196 	};
197 
198 	if (!ra_eff)
199 		return 0;
200 
201 	spin_lock(&ra_eff_lock);
202 	memcpy(&eff, ra_eff, sizeof(eff));
203 	spin_unlock(&ra_eff_lock);
204 
205 	for (i = 0; i < RECLAIM_TYPES; i++) {
206 		seq_printf(m, "%s time(ms)        freed(page/obj)\n", type[i]);
207 		for (j = 0; j < NR_RA_STUBS; j++)
208 			seq_printf(m, "%s  %-15llu %-15llu\n", stage[j],
209 				div_u64(eff[i].time[j], NSEC_PER_MSEC),
210 				eff[i].freed[j]);
211 	}
212 
213 	return 0;
214 }
215 
reclaim_efficiency_proc_open(struct inode * inode,struct file * file)216 static int reclaim_efficiency_proc_open(struct inode *inode, struct file *file)
217 {
218 	return single_open(file, reclaim_efficiency_proc_show, NULL);
219 }
220 
221 static const struct proc_ops reclaim_effi_proc_fops = {
222 	.proc_open = reclaim_efficiency_proc_open,
223 	.proc_read = seq_read,
224 	.proc_lseek = seq_lseek,
225 	.proc_release = single_release,
226 };
227 
proc_reclaimacct_init(void)228 static int __init proc_reclaimacct_init(void)
229 {
230 	proc_create("reclaimacct", 0440, NULL, &reclaimacct_proc_fops);
231 	proc_create("reclaim_efficiency", 0440, NULL, &reclaim_effi_proc_fops);
232 	return 0;
233 }
234 fs_initcall(proc_reclaimacct_init);
235