2 * Copyright (C) ARM Limited 2010-2014. All rights reserved.
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.
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>
19 #define USE_THREAD defined(CONFIG_PREEMPT_RT_FULL)
36 static const char * const meminfo_names[] = {
37 "Linux_meminfo_memfree",
38 "Linux_meminfo_memused",
39 "Linux_meminfo_bufferram",
42 static const char * const proc_names[] = {
43 "Linux_proc_statm_size",
44 "Linux_proc_statm_share",
45 "Linux_proc_statm_text",
46 "Linux_proc_statm_data",
49 static bool meminfo_global_enabled;
50 static ulong meminfo_enabled[MEMINFO_TOTAL];
51 static ulong meminfo_keys[MEMINFO_TOTAL];
52 static long long meminfo_buffer[2 * (MEMINFO_TOTAL + 2)];
53 static int meminfo_length;
54 static bool new_data_avail;
56 static bool proc_global_enabled;
57 static ulong proc_enabled[PROC_COUNT];
58 static ulong proc_keys[PROC_COUNT];
59 static DEFINE_PER_CPU(long long, proc_buffer[2 * (PROC_COUNT + 3)]);
63 static int gator_meminfo_func(void *data);
64 static bool gator_meminfo_run;
65 /* Initialize semaphore unlocked to initialize memory values */
66 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
67 static DECLARE_MUTEX(gator_meminfo_sem);
69 static DEFINE_SEMAPHORE(gator_meminfo_sem);
72 static void notify(void)
74 up(&gator_meminfo_sem);
79 static unsigned int mem_event;
80 static void wq_sched_handler(struct work_struct *wsptr);
81 DECLARE_WORK(work, wq_sched_handler);
82 static struct timer_list meminfo_wake_up_timer;
83 static void meminfo_wake_up_handler(unsigned long unused_data);
85 static void notify(void)
92 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
93 GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order))
95 GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order))
101 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
102 GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold))
104 GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold))
110 GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype))
115 static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root)
120 for (i = 0; i < MEMINFO_TOTAL; i++) {
121 dir = gatorfs_mkdir(sb, root, meminfo_names[i]);
124 gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]);
125 gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_keys[i]);
128 for (i = 0; i < PROC_COUNT; ++i) {
129 dir = gatorfs_mkdir(sb, root, proc_names[i]);
132 gatorfs_create_ulong(sb, dir, "enabled", &proc_enabled[i]);
133 gatorfs_create_ro_ulong(sb, dir, "key", &proc_keys[i]);
139 static int gator_events_meminfo_start(void)
143 new_data_avail = false;
144 meminfo_global_enabled = 0;
145 for (i = 0; i < MEMINFO_TOTAL; i++) {
146 if (meminfo_enabled[i]) {
147 meminfo_global_enabled = 1;
152 proc_global_enabled = 0;
153 for (i = 0; i < PROC_COUNT; ++i) {
154 if (proc_enabled[i]) {
155 proc_global_enabled = 1;
159 if (meminfo_enabled[MEMINFO_MEMUSED])
160 proc_global_enabled = 1;
162 if (meminfo_global_enabled == 0)
165 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
166 if (GATOR_REGISTER_TRACE(mm_page_free_direct))
168 if (GATOR_REGISTER_TRACE(mm_page_free))
170 goto mm_page_free_exit;
171 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
172 if (GATOR_REGISTER_TRACE(mm_pagevec_free))
174 if (GATOR_REGISTER_TRACE(mm_page_free_batched))
176 goto mm_page_free_batched_exit;
177 if (GATOR_REGISTER_TRACE(mm_page_alloc))
178 goto mm_page_alloc_exit;
181 /* Start worker thread */
182 gator_meminfo_run = true;
183 /* Since the mutex starts unlocked, memory values will be initialized */
184 if (IS_ERR(kthread_run(gator_meminfo_func, NULL, "gator_meminfo")))
185 goto kthread_run_exit;
187 setup_timer(&meminfo_wake_up_timer, meminfo_wake_up_handler, 0);
194 GATOR_UNREGISTER_TRACE(mm_page_alloc);
197 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
198 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
200 GATOR_UNREGISTER_TRACE(mm_page_free_batched);
202 mm_page_free_batched_exit:
203 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
204 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
206 GATOR_UNREGISTER_TRACE(mm_page_free);
212 static void gator_events_meminfo_stop(void)
214 if (meminfo_global_enabled) {
215 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
216 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
217 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
219 GATOR_UNREGISTER_TRACE(mm_page_free);
220 GATOR_UNREGISTER_TRACE(mm_page_free_batched);
222 GATOR_UNREGISTER_TRACE(mm_page_alloc);
225 /* Stop worker thread */
226 gator_meminfo_run = false;
227 up(&gator_meminfo_sem);
229 del_timer_sync(&meminfo_wake_up_timer);
234 static void do_read(void)
238 unsigned long long value;
240 meminfo_length = len = 0;
243 for (i = 0; i < MEMINFO_TOTAL; i++) {
244 if (meminfo_enabled[i]) {
246 case MEMINFO_MEMFREE:
247 value = info.freeram * PAGE_SIZE;
249 case MEMINFO_MEMUSED:
250 /* pid -1 means system wide */
251 meminfo_buffer[len++] = 1;
252 meminfo_buffer[len++] = -1;
254 meminfo_buffer[len++] = meminfo_keys[MEMINFO_MEMUSED];
255 meminfo_buffer[len++] = (info.totalram - info.freeram) * PAGE_SIZE;
257 meminfo_buffer[len++] = 1;
258 meminfo_buffer[len++] = 0;
260 case MEMINFO_BUFFERRAM:
261 value = info.bufferram * PAGE_SIZE;
267 meminfo_buffer[len++] = meminfo_keys[i];
268 meminfo_buffer[len++] = value;
272 meminfo_length = len;
273 new_data_avail = true;
278 static int gator_meminfo_func(void *data)
281 if (down_killable(&gator_meminfo_sem))
284 /* Eat up any pending events */
285 while (!down_trylock(&gator_meminfo_sem))
288 if (!gator_meminfo_run)
299 /* Must be run in process context as the kernel function si_meminfo() can sleep */
300 static void wq_sched_handler(struct work_struct *wsptr)
305 static void meminfo_wake_up_handler(unsigned long unused_data)
307 /* had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater */
308 schedule_work(&work);
313 static int gator_events_meminfo_read(long long **buffer)
316 static unsigned int last_mem_event;
319 if (!on_primary_core() || !meminfo_global_enabled)
323 if (last_mem_event != mem_event) {
324 last_mem_event = mem_event;
325 mod_timer(&meminfo_wake_up_timer, jiffies + 1);
332 new_data_avail = false;
335 *buffer = meminfo_buffer;
337 return meminfo_length;
340 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
342 static inline unsigned long gator_get_mm_counter(struct mm_struct *mm, int member)
344 #ifdef SPLIT_RSS_COUNTING
345 long val = atomic_long_read(&mm->rss_stat.count[member]);
349 return (unsigned long)val;
351 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
352 return mm->rss_stat.count[member];
354 return atomic_long_read(&mm->rss_stat.count[member]);
359 #define get_mm_counter(mm, member) gator_get_mm_counter(mm, member)
363 static int gator_events_meminfo_read_proc(long long **buffer, struct task_struct *task)
365 struct mm_struct *mm;
370 int cpu = get_physical_cpu();
371 long long *buf = per_cpu(proc_buffer, cpu);
373 if (!proc_global_enabled)
376 /* Collect the memory stats of the process instead of the thread */
377 if (task->group_leader != NULL)
378 task = task->group_leader;
380 /* 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 */
385 /* Derived from task_statm in fs/proc/task_mmu.c */
386 if (meminfo_enabled[MEMINFO_MEMUSED] || proc_enabled[PROC_SHARE]) {
387 share = get_mm_counter(mm,
388 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
396 /* key of 1 indicates a pid */
398 buf[len++] = task->pid;
400 for (i = 0; i < PROC_COUNT; ++i) {
401 if (proc_enabled[i]) {
404 value = mm->total_vm;
410 value = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> PAGE_SHIFT;
413 value = mm->total_vm - mm->shared_vm;
417 buf[len++] = proc_keys[i];
418 buf[len++] = value * PAGE_SIZE;
422 if (meminfo_enabled[MEMINFO_MEMUSED]) {
423 value = share + get_mm_counter(mm,
424 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
430 /* Send resident for this pid */
431 buf[len++] = meminfo_keys[MEMINFO_MEMUSED];
432 buf[len++] = value * PAGE_SIZE;
445 static struct gator_interface gator_events_meminfo_interface = {
446 .create_files = gator_events_meminfo_create_files,
447 .start = gator_events_meminfo_start,
448 .stop = gator_events_meminfo_stop,
449 .read64 = gator_events_meminfo_read,
450 .read_proc = gator_events_meminfo_read_proc,
453 int gator_events_meminfo_init(void)
457 meminfo_global_enabled = 0;
458 for (i = 0; i < MEMINFO_TOTAL; i++) {
459 meminfo_enabled[i] = 0;
460 meminfo_keys[i] = gator_events_get_key();
463 proc_global_enabled = 0;
464 for (i = 0; i < PROC_COUNT; ++i) {
466 proc_keys[i] = gator_events_get_key();
469 return gator_events_install(&gator_events_meminfo_interface);