451290d9af1759fa5759a985a44df303b98089e4
[firefly-linux-kernel-4.4.55.git] / drivers / gator / gator_events_meminfo.c
1 /**
2  * Copyright (C) ARM Limited 2010-2013. All rights reserved.
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  */
9
10 #include "gator.h"
11
12 #include <linux/hardirq.h>
13 #include <linux/kthread.h>
14 #include <linux/sched.h>
15 #include <linux/semaphore.h>
16 #include <linux/workqueue.h>
17 #include <trace/events/kmem.h>
18
19 enum {
20         MEMINFO_MEMFREE,
21         MEMINFO_MEMUSED,
22         MEMINFO_BUFFERRAM,
23         MEMINFO_TOTAL,
24 };
25
26 enum {
27         PROC_SIZE,
28         PROC_SHARE,
29         PROC_TEXT,
30         PROC_DATA,
31         PROC_COUNT,
32 };
33
34 static const char * const meminfo_names[] = {
35         "Linux_meminfo_memfree",
36         "Linux_meminfo_memused",
37         "Linux_meminfo_bufferram",
38 };
39
40 static const char * const proc_names[] = {
41         "Linux_proc_statm_size",
42         "Linux_proc_statm_share",
43         "Linux_proc_statm_text",
44         "Linux_proc_statm_data",
45 };
46
47 static bool meminfo_global_enabled;
48 static ulong meminfo_enabled[MEMINFO_TOTAL];
49 static ulong meminfo_keys[MEMINFO_TOTAL];
50 static long long meminfo_buffer[2 * (MEMINFO_TOTAL + 2)];
51 static int meminfo_length = 0;
52 static bool new_data_avail;
53
54 static bool proc_global_enabled;
55 static ulong proc_enabled[PROC_COUNT];
56 static ulong proc_keys[PROC_COUNT];
57 static DEFINE_PER_CPU(long long, proc_buffer[2 * (PROC_COUNT + 3)]);
58
59 static int gator_meminfo_func(void *data);
60 static bool gator_meminfo_run;
61 // Initialize semaphore unlocked to initialize memory values
62 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
63 static DECLARE_MUTEX(gator_meminfo_sem);
64 #else
65 static DEFINE_SEMAPHORE(gator_meminfo_sem);
66 #endif
67
68 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
69 GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order))
70 #else
71 GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order))
72 #endif
73 {
74         up(&gator_meminfo_sem);
75 }
76
77 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
78 GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold))
79 #else
80 GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold))
81 #endif
82 {
83         up(&gator_meminfo_sem);
84 }
85
86 GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype))
87 {
88         up(&gator_meminfo_sem);
89 }
90
91 static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root)
92 {
93         struct dentry *dir;
94         int i;
95
96         for (i = 0; i < MEMINFO_TOTAL; i++) {
97                 dir = gatorfs_mkdir(sb, root, meminfo_names[i]);
98                 if (!dir) {
99                         return -1;
100                 }
101                 gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]);
102                 gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_keys[i]);
103         }
104
105         for (i = 0; i < PROC_COUNT; ++i) {
106                 dir = gatorfs_mkdir(sb, root, proc_names[i]);
107                 if (!dir) {
108                         return -1;
109                 }
110                 gatorfs_create_ulong(sb, dir, "enabled", &proc_enabled[i]);
111                 gatorfs_create_ro_ulong(sb, dir, "key", &proc_keys[i]);
112         }
113
114         return 0;
115 }
116
117 static int gator_events_meminfo_start(void)
118 {
119         int i;
120
121         new_data_avail = false;
122         meminfo_global_enabled = 0;
123         for (i = 0; i < MEMINFO_TOTAL; i++) {
124                 if (meminfo_enabled[i]) {
125                         meminfo_global_enabled = 1;
126                         break;
127                 }
128         }
129
130         proc_global_enabled = 0;
131         for (i = 0; i < PROC_COUNT; ++i) {
132                 if (proc_enabled[i]) {
133                         proc_global_enabled = 1;
134                         break;
135                 }
136         }
137         if (meminfo_enabled[MEMINFO_MEMUSED]) {
138                 proc_global_enabled = 1;
139         }
140
141         if (meminfo_global_enabled == 0)
142                 return 0;
143
144 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
145         if (GATOR_REGISTER_TRACE(mm_page_free_direct))
146 #else
147         if (GATOR_REGISTER_TRACE(mm_page_free))
148 #endif
149                 goto mm_page_free_exit;
150 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
151         if (GATOR_REGISTER_TRACE(mm_pagevec_free))
152 #else
153         if (GATOR_REGISTER_TRACE(mm_page_free_batched))
154 #endif
155                 goto mm_page_free_batched_exit;
156         if (GATOR_REGISTER_TRACE(mm_page_alloc))
157                 goto mm_page_alloc_exit;
158
159         // Start worker thread
160         gator_meminfo_run = true;
161         // Since the mutex starts unlocked, memory values will be initialized
162         if (IS_ERR(kthread_run(gator_meminfo_func, NULL, "gator_meminfo")))
163                 goto kthread_run_exit;
164
165         return 0;
166
167 kthread_run_exit:
168         GATOR_UNREGISTER_TRACE(mm_page_alloc);
169 mm_page_alloc_exit:
170 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
171         GATOR_UNREGISTER_TRACE(mm_pagevec_free);
172 #else
173         GATOR_UNREGISTER_TRACE(mm_page_free_batched);
174 #endif
175 mm_page_free_batched_exit:
176 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
177         GATOR_UNREGISTER_TRACE(mm_page_free_direct);
178 #else
179         GATOR_UNREGISTER_TRACE(mm_page_free);
180 #endif
181 mm_page_free_exit:
182         return -1;
183 }
184
185 static void gator_events_meminfo_stop(void)
186 {
187         if (meminfo_global_enabled) {
188 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
189                 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
190                 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
191 #else
192                 GATOR_UNREGISTER_TRACE(mm_page_free);
193                 GATOR_UNREGISTER_TRACE(mm_page_free_batched);
194 #endif
195                 GATOR_UNREGISTER_TRACE(mm_page_alloc);
196
197                 // Stop worker thread
198                 gator_meminfo_run = false;
199                 up(&gator_meminfo_sem);
200         }
201 }
202
203 // Must be run in process context as the kernel function si_meminfo() can sleep
204 static int gator_meminfo_func(void *data)
205 {
206         struct sysinfo info;
207         int i, len;
208         unsigned long long value;
209
210         for (;;) {
211                 if (down_killable(&gator_meminfo_sem)) {
212                         break;
213                 }
214
215                 // Eat up any pending events
216                 while (!down_trylock(&gator_meminfo_sem));
217
218                 if (!gator_meminfo_run) {
219                         break;
220                 }
221
222                 meminfo_length = len = 0;
223
224                 si_meminfo(&info);
225                 for (i = 0; i < MEMINFO_TOTAL; i++) {
226                         if (meminfo_enabled[i]) {
227                                 switch (i) {
228                                 case MEMINFO_MEMFREE:
229                                         value = info.freeram * PAGE_SIZE;
230                                         break;
231                                 case MEMINFO_MEMUSED:
232                                         // pid -1 means system wide
233                                         meminfo_buffer[len++] = 1;
234                                         meminfo_buffer[len++] = -1;
235                                         // Emit value
236                                         meminfo_buffer[len++] = meminfo_keys[MEMINFO_MEMUSED];
237                                         meminfo_buffer[len++] = (info.totalram - info.freeram) * PAGE_SIZE;
238                                         // Clear pid
239                                         meminfo_buffer[len++] = 1;
240                                         meminfo_buffer[len++] = 0;
241                                         continue;
242                                 case MEMINFO_BUFFERRAM:
243                                         value = info.bufferram * PAGE_SIZE;
244                                         break;
245                                 default:
246                                         value = 0;
247                                         break;
248                                 }
249                                 meminfo_buffer[len++] = meminfo_keys[i];
250                                 meminfo_buffer[len++] = value;
251                         }
252                 }
253
254                 meminfo_length = len;
255                 new_data_avail = true;
256         }
257
258         return 0;
259 }
260
261 static int gator_events_meminfo_read(long long **buffer)
262 {
263         if (!on_primary_core() || !meminfo_global_enabled)
264                 return 0;
265
266         if (!new_data_avail)
267                 return 0;
268
269         new_data_avail = false;
270
271         if (buffer)
272                 *buffer = meminfo_buffer;
273
274         return meminfo_length;
275 }
276
277 static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task)
278 {
279         struct mm_struct *mm;
280         u64 share = 0;
281         int i;
282         long long value;
283         int len = 0;
284         int cpu = get_physical_cpu();
285         long long *buf = per_cpu(proc_buffer, cpu);
286
287         if (!proc_global_enabled) {
288                 return 0;
289         }
290
291         // Collect the memory stats of the process instead of the thread
292         if (task->group_leader != NULL) {
293                 task = task->group_leader;
294         }
295
296         // get_task_mm/mmput is not needed in this context because the task and it's mm are required as part of the sched_switch
297         mm = task->mm;
298         if (mm == NULL) {
299                 return 0;
300         }
301
302         // Derived from task_statm in fs/proc/task_mmu.c
303         if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) {
304                 share = get_mm_counter(mm,
305 #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32)
306                                                            file_rss
307 #else
308                                                            MM_FILEPAGES
309 #endif
310                                                            );
311         }
312
313         // key of 1 indicates a pid
314         buf[len++] = 1;
315         buf[len++] = task->pid;
316
317         for (i = 0; i < PROC_COUNT; ++i) {
318                 if (proc_enabled[i]) {
319                         switch (i) {
320                         case PROC_SIZE:
321                                 value = mm->total_vm;
322                                 break;
323                         case PROC_SHARE:
324                                 value = share;
325                                 break;
326                         case PROC_TEXT:
327                                 value = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT;
328                                 break;
329                         case PROC_DATA:
330                                 value = mm->total_vm - mm->shared_vm;
331                                 break;
332                         }
333
334                         buf[len++] = proc_keys[i];
335                         buf[len++] = value * PAGE_SIZE;
336                 }
337         }
338
339         if (meminfo_enabled[MEMINFO_MEMUSED]) {
340                 value = share + get_mm_counter(mm,
341 #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 32)
342                                                                            anon_rss
343 #else
344                                                                            MM_ANONPAGES
345 #endif
346                                                                            );
347                 // Send resident for this pid
348                 buf[len++] = meminfo_keys[MEMINFO_MEMUSED];
349                 buf[len++] = value * PAGE_SIZE;
350         }
351
352         // Clear pid
353         buf[len++] = 1;
354         buf[len++] = 0;
355
356         if (buffer)
357                 *buffer = buf;
358
359         return len;
360 }
361
362 static struct gator_interface gator_events_meminfo_interface = {
363         .create_files = gator_events_meminfo_create_files,
364         .start = gator_events_meminfo_start,
365         .stop = gator_events_meminfo_stop,
366         .read64 = gator_events_meminfo_read,
367         .read_proc = gator_events_meminfo_read_proc,
368 };
369
370 int gator_events_meminfo_init(void)
371 {
372         int i;
373
374         meminfo_global_enabled = 0;
375         for (i = 0; i < MEMINFO_TOTAL; i++) {
376                 meminfo_enabled[i] = 0;
377                 meminfo_keys[i] = gator_events_get_key();
378         }
379
380         proc_global_enabled = 0;
381         for (i = 0; i < PROC_COUNT; ++i) {
382                 proc_enabled[i] = 0;
383                 proc_keys[i] = gator_events_get_key();
384         }
385
386         return gator_events_install(&gator_events_meminfo_interface);
387 }