blob: 055824e4c02a775dadf3f30d71266cacfa6a79d7 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (C) 2018 MediaTek Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/mm.h>
15#include <linux/slab.h>
16#include <linux/sched.h>
17#include <linux/cpu.h>
18#include <linux/module.h>
19#include <linux/fs.h>
20
21#include "met_drv.h"
22#include "mem_stat.h"
23#include "trace.h"
24
25
26/* define MEMSTAT_DEBUG */
27#ifdef MEMSTAT_DEBUG
28#define debug_memstat(fmt, arg...) pr_debug(fmt, ##arg)
29#else
30#define debug_memstat(fmt, arg...) do {} while (0)
31#endif
32
33struct metdevice met_memstat;
34
35unsigned int phy_memstat_mask;
36unsigned int vir_memstat_mask;
37
38#define MAX_PHY_MEMSTAT_EVENT_AMOUNT 6
39#define MAX_VIR_MEMSTAT_EVENT_AMOUNT 6
40
41struct mem_event phy_memstat_table[] = {
42 {FREE_MEM, "free_mem", "Free Memory"}
43};
44
45#define PHY_MEMSTAT_TABLE_SIZE (sizeof(phy_memstat_table) / sizeof(struct mem_event))
46
47struct mem_event vir_memstat_table[] = {
48 {FILE_PAGES, "file_pages", "File Pages"},
49 {FILE_DIRTY, "file_dirty", "FD APP->FS(KB)"},
50 {NUM_DIRTIED, "num_dirtied", "Num Dirtied"},
51 {WRITE_BACK, "write_back", "WB. FS->Block IO(KB)"},
52 {NUM_WRITTEN, "num_written", "Num Written"},
53 {PG_FAULT_CNT, "pg_fault_cnt", "Page Fault Count"}
54};
55
56#define VIR_MEMSTAT_TABLE_SIZE (sizeof(vir_memstat_table) / sizeof(struct mem_event))
57
58int vm_event_counters_enable;
59unsigned long *vm_status;
60static struct delayed_work dwork;
61
62noinline void memstat(unsigned int cnt, unsigned int *value)
63{
64 MET_GENERAL_PRINT(MET_TRACE, cnt, value);
65}
66
67static int get_phy_memstat(unsigned int *value)
68{
69 int i, cnt = 0;
70 struct sysinfo info;
71
72#define K(x) ((x) << (PAGE_SHIFT - 10))
73
74 si_meminfo(&info);
75
76 for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
77 if (phy_memstat_mask & (1 << i)) {
78 switch (i) {
79 case FREE_MEM:
80 value[cnt] = K(info.freeram);
81 break;
82 }
83
84 cnt++;
85 }
86 }
87
88 return cnt;
89}
90
91static int get_vir_memstat(unsigned int *value)
92{
93 int i, cnt = 0;
94
95 for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
96 vm_status[i] = global_page_state(i);
97
98 all_vm_events(vm_status + NR_VM_ZONE_STAT_ITEMS);
99
100 for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
101 if (vir_memstat_mask & (1 << i)) {
102 switch (i) {
103 case FILE_PAGES:
104 value[cnt] = vm_status[NR_FILE_PAGES] << (PAGE_SHIFT - 10);
105 break;
106 case FILE_DIRTY:
107 value[cnt] = vm_status[NR_FILE_DIRTY] << (PAGE_SHIFT - 10);
108 break;
109 case NUM_DIRTIED:
110 value[cnt] = vm_status[NR_DIRTIED] << (PAGE_SHIFT - 10);
111 break;
112 case WRITE_BACK:
113 value[cnt] = vm_status[NR_WRITEBACK] << (PAGE_SHIFT - 10);
114 break;
115 case NUM_WRITTEN:
116 value[cnt] = vm_status[NR_WRITTEN] << (PAGE_SHIFT - 10);
117 break;
118 case PG_FAULT_CNT:
119 value[cnt] = vm_status[NR_VM_ZONE_STAT_ITEMS + PGFAULT];
120 break;
121 }
122
123 cnt++;
124 }
125 }
126
127 return cnt;
128}
129
130static void wq_get_memstat(struct work_struct *work)
131{
132 int total_event_amount = 0, phy_event_amount = 0;
133 unsigned int stat_val[MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT];
134
135 memset(stat_val, 0, sizeof(unsigned int) * (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT));
136 total_event_amount = phy_event_amount = get_phy_memstat(stat_val);
137
138 if (vm_event_counters_enable)
139 total_event_amount += get_vir_memstat(&(stat_val[phy_event_amount]));
140
141 if (total_event_amount <= (MAX_PHY_MEMSTAT_EVENT_AMOUNT + MAX_VIR_MEMSTAT_EVENT_AMOUNT))
142 memstat(total_event_amount-1, stat_val);
143}
144
145void met_memstat_polling(unsigned long long stamp, int cpu)
146{
147 schedule_delayed_work(&dwork, 0);
148}
149
150static void met_memstat_start(void)
151{
152 int stat_items_size = 0;
153
154 stat_items_size = NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long);
155
156#ifdef CONFIG_VM_EVENT_COUNTERS
157 stat_items_size += sizeof(struct vm_event_state);
158#endif
159
160 vm_status = kmalloc(stat_items_size, GFP_KERNEL);
161 if (vm_status == NULL)
162 return;
163 INIT_DELAYED_WORK(&dwork, wq_get_memstat);
164}
165
166static void met_memstat_stop(void)
167{
168 kfree(vm_status);
169 cancel_delayed_work_sync(&dwork);
170}
171
172static const char help[] =
173" --memstat=[phy_mem_stat|vir_mem_stat]:event_name enable sampling physical & virtual memory status\n";
174
175static int met_memstat_print_help(char *buf, int len)
176{
177 int i, l;
178
179 l = snprintf(buf, PAGE_SIZE, help);
180
181 for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++)
182 l += snprintf(buf + l, PAGE_SIZE - l, " --memstat=phy_mem_stat:%s\n",
183 phy_memstat_table[i].name);
184
185#ifdef CONFIG_VM_EVENT_COUNTERS
186 for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++)
187 l += snprintf(buf + l, PAGE_SIZE - l, " --memstat=vir_mem_stat:%s\n",
188 vir_memstat_table[i].name);
189#endif
190
191 return l;
192}
193
194static const char header[] = "met-info [000] 0.0: ms_ud_sys_header: memstat,";
195
196
197static int met_memstat_print_header(char *buf, int len)
198{
199 int i, l;
200 int event_amount = 0;
201
202 l = snprintf(buf, PAGE_SIZE, header);
203
204 for (i = 0; i < MAX_PHY_MEMSTAT_EVENT_AMOUNT; i++) {
205 if ((phy_memstat_mask & (1 << i)) && (i < PHY_MEMSTAT_TABLE_SIZE)) {
206 l += snprintf(buf + l, PAGE_SIZE - l, phy_memstat_table[i].header_name);
207 l += snprintf(buf + l, PAGE_SIZE - l, ",");
208 event_amount++;
209 }
210 }
211
212#ifdef CONFIG_VM_EVENT_COUNTERS
213 for (i = 0; i < MAX_VIR_MEMSTAT_EVENT_AMOUNT; i++) {
214 if ((vir_memstat_mask & (1 << i)) && (i < VIR_MEMSTAT_TABLE_SIZE)) {
215 l += snprintf(buf + l, PAGE_SIZE - l, vir_memstat_table[i].header_name);
216 l += snprintf(buf + l, PAGE_SIZE - l, ",");
217 event_amount++;
218 }
219 }
220#endif
221
222 for (i = 0; i < event_amount; i++) {
223 l += snprintf(buf + l, PAGE_SIZE - l, "x");
224 l += snprintf(buf + l, PAGE_SIZE - l, ",");
225 }
226
227 phy_memstat_mask = 0;
228 vir_memstat_mask = 0;
229
230 l += snprintf(buf + l, PAGE_SIZE - l, "\n");
231
232 return l;
233}
234
235static int met_memstat_process_argument(const char *arg, int len)
236{
237 int i, found_event = 0;
238 char choice[16], event[32];
239 char *pch;
240 int str_len;
241
242
243#ifdef CONFIG_VM_EVENT_COUNTERS
244 vm_event_counters_enable = 1;
245#endif
246
247 pch = strchr(arg, ':');
248 if (pch == NULL)
249 goto error;
250
251 memset(choice, 0, sizeof(choice));
252 memset(event, 0, sizeof(event));
253
254 str_len = (int)(pch - arg);
255 memcpy(choice, arg, str_len);
256 memcpy(event, arg + str_len + 1, len - (str_len + 1));
257
258 if (strncmp(choice, "phy_mem_stat", 12) == 0) {
259 for (i = 0; i < PHY_MEMSTAT_TABLE_SIZE; i++) {
260 if (strncmp(event, phy_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
261 phy_memstat_mask |= (1 << phy_memstat_table[i].id);
262 found_event = 1;
263
264 break;
265 }
266 }
267 } else if (strncmp(choice, "vir_mem_stat", 12) == 0) {
268 if (!vm_event_counters_enable) {
269 pr_debug("[%s] %d: CONFIG_VM_EVENT_COUNTERS is not configured\n", __func__,
270 __LINE__);
271 goto error;
272 }
273
274 for (i = 0; i < VIR_MEMSTAT_TABLE_SIZE; i++) {
275 if (strncmp(event, vir_memstat_table[i].name, MAX_EVENT_NAME_LEN) == 0) {
276 vir_memstat_mask |= (1 << vir_memstat_table[i].id);
277 found_event = 1;
278
279 break;
280 }
281 }
282 } else {
283 pr_debug("[%s] %d: only support phy_mem_stat & vir_mem_stat keyword\n", __func__,
284 __LINE__);
285 goto error;
286 }
287
288 if (!found_event) {
289 pr_debug("[%s] %d: input event name error\n", __func__, __LINE__);
290 goto error;
291 }
292
293 met_memstat.mode = 1;
294 return 0;
295
296error:
297 met_memstat.mode = 0;
298 return -EINVAL;
299}
300
301struct metdevice met_memstat = {
302 .name = "memstat",
303 .type = MET_TYPE_PMU,
304 .cpu_related = 0,
305 .start = met_memstat_start,
306 .stop = met_memstat_stop,
307 .polling_interval = 1,
308 .timed_polling = met_memstat_polling,
309 .tagged_polling = met_memstat_polling,
310 .print_help = met_memstat_print_help,
311 .print_header = met_memstat_print_header,
312 .process_argument = met_memstat_process_argument
313};