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