1 /**
2 * Copyright (C) ARM Limited 2010-2012. 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 */
10 #include "gator.h"
11 #include <linux/workqueue.h>
12 #include <trace/events/kmem.h>
13 #include <linux/hardirq.h>
15 #define MEMINFO_MEMFREE 0
16 #define MEMINFO_MEMUSED 1
17 #define MEMINFO_BUFFERRAM 2
18 #define MEMINFO_TOTAL 3
20 static ulong meminfo_global_enabled;
21 static ulong meminfo_enabled[MEMINFO_TOTAL];
22 static ulong meminfo_key[MEMINFO_TOTAL];
23 static unsigned long long meminfo_buffer[MEMINFO_TOTAL * 2];
24 static int meminfo_length = 0;
25 static unsigned int mem_event = 0;
26 static bool new_data_avail;
28 static void wq_sched_handler(struct work_struct *wsptr);
30 DECLARE_WORK(work, wq_sched_handler);
32 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
33 GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) {
34 #else
35 GATOR_DEFINE_PROBE(mm_page_free, TP_PROTO(struct page *page, unsigned int order)) {
36 #endif
37 mem_event++;
38 }
40 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
41 GATOR_DEFINE_PROBE(mm_pagevec_free, TP_PROTO(struct page *page, int cold)) {
42 #else
43 GATOR_DEFINE_PROBE(mm_page_free_batched, TP_PROTO(struct page *page, int cold)) {
44 #endif
45 mem_event++;
46 }
48 GATOR_DEFINE_PROBE(mm_page_alloc, TP_PROTO(struct page *page, unsigned int order, gfp_t gfp_flags, int migratetype)) {
49 mem_event++;
50 }
52 static int gator_events_meminfo_create_files(struct super_block *sb, struct dentry *root)
53 {
54 struct dentry *dir;
55 int i;
57 for (i = 0; i < MEMINFO_TOTAL; i++) {
58 switch (i) {
59 case MEMINFO_MEMFREE:
60 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memfree");
61 break;
62 case MEMINFO_MEMUSED:
63 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_memused");
64 break;
65 case MEMINFO_BUFFERRAM:
66 dir = gatorfs_mkdir(sb, root, "Linux_meminfo_bufferram");
67 break;
68 default:
69 return -1;
70 }
71 if (!dir) {
72 return -1;
73 }
74 gatorfs_create_ulong(sb, dir, "enabled", &meminfo_enabled[i]);
75 gatorfs_create_ro_ulong(sb, dir, "key", &meminfo_key[i]);
76 }
78 return 0;
79 }
81 static int gator_events_meminfo_start(void)
82 {
83 int i;
85 new_data_avail = true;
86 for (i = 0; i < MEMINFO_TOTAL; i++) {
87 if (meminfo_enabled[i]) {
88 meminfo_global_enabled = 1;
89 }
90 }
92 if (meminfo_global_enabled == 0)
93 return 0;
95 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
96 if (GATOR_REGISTER_TRACE(mm_page_free_direct))
97 #else
98 if (GATOR_REGISTER_TRACE(mm_page_free))
99 #endif
100 goto mm_page_free_exit;
101 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
102 if (GATOR_REGISTER_TRACE(mm_pagevec_free))
103 #else
104 if (GATOR_REGISTER_TRACE(mm_page_free_batched))
105 #endif
106 goto mm_page_free_batched_exit;
107 if (GATOR_REGISTER_TRACE(mm_page_alloc))
108 goto mm_page_alloc_exit;
110 return 0;
112 mm_page_alloc_exit:
113 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
114 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
115 #else
116 GATOR_UNREGISTER_TRACE(mm_page_free_batched);
117 #endif
118 mm_page_free_batched_exit:
119 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
120 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
121 #else
122 GATOR_UNREGISTER_TRACE(mm_page_free);
123 #endif
124 mm_page_free_exit:
125 return -1;
126 }
128 static void gator_events_meminfo_stop(void)
129 {
130 int i;
132 if (meminfo_global_enabled) {
133 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
134 GATOR_UNREGISTER_TRACE(mm_page_free_direct);
135 GATOR_UNREGISTER_TRACE(mm_pagevec_free);
136 #else
137 GATOR_UNREGISTER_TRACE(mm_page_free);
138 GATOR_UNREGISTER_TRACE(mm_page_free_batched);
139 #endif
140 GATOR_UNREGISTER_TRACE(mm_page_alloc);
141 }
143 meminfo_global_enabled = 0;
144 for (i = 0; i < MEMINFO_TOTAL; i++) {
145 meminfo_enabled[i] = 0;
146 }
147 }
149 // Must be run in process context (work queue) as the kernel function si_meminfo() can sleep
150 static void wq_sched_handler(struct work_struct *wsptr)
151 {
152 struct sysinfo info;
153 int i, len;
154 unsigned long long value;
156 meminfo_length = len = 0;
158 si_meminfo(&info);
159 for (i = 0; i < MEMINFO_TOTAL; i++) {
160 if (meminfo_enabled[i]) {
161 switch (i) {
162 case MEMINFO_MEMFREE:
163 value = info.freeram * PAGE_SIZE;
164 break;
165 case MEMINFO_MEMUSED:
166 value = (info.totalram - info.freeram) * PAGE_SIZE;
167 break;
168 case MEMINFO_BUFFERRAM:
169 value = info.bufferram * PAGE_SIZE;
170 break;
171 default:
172 value = 0;
173 break;
174 }
175 meminfo_buffer[len++] = (unsigned long long)meminfo_key[i];
176 meminfo_buffer[len++] = value;
177 }
178 }
180 meminfo_length = len;
181 new_data_avail = true;
182 }
184 static int gator_events_meminfo_read(long long **buffer)
185 {
186 static unsigned int last_mem_event = 0;
188 if (smp_processor_id() || !meminfo_global_enabled)
189 return 0;
191 if (last_mem_event != mem_event) {
192 last_mem_event = mem_event;
193 if (in_interrupt()) {
194 schedule_work(&work);
195 } else {
196 wq_sched_handler(NULL);
197 }
198 }
200 if (!new_data_avail)
201 return 0;
203 new_data_avail = false;
205 if (buffer)
206 *buffer = meminfo_buffer;
208 return meminfo_length;
209 }
211 static struct gator_interface gator_events_meminfo_interface = {
212 .create_files = gator_events_meminfo_create_files,
213 .start = gator_events_meminfo_start,
214 .stop = gator_events_meminfo_stop,
215 .read64 = gator_events_meminfo_read,
216 };
218 int gator_events_meminfo_init(void)
219 {
220 int i;
222 meminfo_global_enabled = 0;
223 for (i = 0; i < MEMINFO_TOTAL; i++) {
224 meminfo_enabled[i] = 0;
225 meminfo_key[i] = gator_events_get_key();
226 }
228 return gator_events_install(&gator_events_meminfo_interface);
229 }
230 gator_events_init(gator_events_meminfo_init);