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 */
9 #include <linux/slab.h>
10 #include <linux/perf_event.h>
11 #include "gator.h"
13 // gator_events_armvX.c is used for Linux 2.6.x
14 #if GATOR_PERF_PMU_SUPPORT
16 static const char *pmnc_name;
17 int pmnc_counters;
18 int ccnt = 0;
20 #define CNTMAX (6+1)
22 static DEFINE_MUTEX(perf_mutex);
24 unsigned long pmnc_enabled[CNTMAX];
25 unsigned long pmnc_event[CNTMAX];
26 unsigned long pmnc_count[CNTMAX];
27 unsigned long pmnc_key[CNTMAX];
29 static DEFINE_PER_CPU(int[CNTMAX], perfCurr);
30 static DEFINE_PER_CPU(int[CNTMAX], perfPrev);
31 static DEFINE_PER_CPU(int[CNTMAX], perfPrevDelta);
32 static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt);
33 static DEFINE_PER_CPU(struct perf_event *[CNTMAX], pevent);
34 static DEFINE_PER_CPU(struct perf_event_attr *[CNTMAX], pevent_attr);
36 static void gator_events_perf_pmu_stop(void);
38 static int gator_events_perf_pmu_create_files(struct super_block *sb, struct dentry *root)
39 {
40 struct dentry *dir;
41 int i;
43 for (i = 0; i < pmnc_counters; i++) {
44 char buf[40];
45 if (i == 0) {
46 snprintf(buf, sizeof buf, "%s_ccnt", pmnc_name);
47 } else {
48 snprintf(buf, sizeof buf, "%s_cnt%d", pmnc_name, i-1);
49 }
50 dir = gatorfs_mkdir(sb, root, buf);
51 if (!dir) {
52 return -1;
53 }
54 gatorfs_create_ulong(sb, dir, "enabled", &pmnc_enabled[i]);
55 gatorfs_create_ulong(sb, dir, "count", &pmnc_count[i]);
56 gatorfs_create_ro_ulong(sb, dir, "key", &pmnc_key[i]);
57 if (i > 0) {
58 gatorfs_create_ulong(sb, dir, "event", &pmnc_event[i]);
59 }
60 }
62 return 0;
63 }
65 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
66 static void dummy_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs)
67 #else
68 static void dummy_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs)
69 #endif
70 {
71 // Required as perf_event_create_kernel_counter() requires an overflow handler, even though all we do is poll
72 }
74 static int gator_events_perf_pmu_online(int** buffer)
75 {
76 int cnt, len = 0, cpu = smp_processor_id();
78 // read the counters and toss the invalid data, return zero instead
79 for (cnt = 0; cnt < pmnc_counters; cnt++) {
80 struct perf_event * ev = per_cpu(pevent, cpu)[cnt];
81 if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) {
82 ev->pmu->read(ev);
83 per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count);
84 per_cpu(perfPrevDelta, cpu)[cnt] = 0;
85 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
86 per_cpu(perfCnt, cpu)[len++] = 0;
87 }
88 }
90 if (buffer)
91 *buffer = per_cpu(perfCnt, cpu);
93 return len;
94 }
96 static void gator_events_perf_pmu_online_dispatch(int cpu)
97 {
98 int cnt;
100 for (cnt = 0; cnt < pmnc_counters; cnt++) {
101 if (per_cpu(pevent, cpu)[cnt] != NULL || per_cpu(pevent_attr, cpu)[cnt] == 0)
102 continue;
104 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0)
105 per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, dummy_handler);
106 #else
107 per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, dummy_handler, 0);
108 #endif
109 if (IS_ERR(per_cpu(pevent, cpu)[cnt])) {
110 pr_debug("gator: unable to online a counter on cpu %d\n", cpu);
111 per_cpu(pevent, cpu)[cnt] = NULL;
112 continue;
113 }
115 if (per_cpu(pevent, cpu)[cnt]->state != PERF_EVENT_STATE_ACTIVE) {
116 pr_debug("gator: inactive counter on cpu %d\n", cpu);
117 perf_event_release_kernel(per_cpu(pevent, cpu)[cnt]);
118 per_cpu(pevent, cpu)[cnt] = NULL;
119 continue;
120 }
121 }
122 }
124 static void gator_events_perf_pmu_offline_dispatch(int cpu)
125 {
126 int cnt;
127 struct perf_event * pe;
129 for (cnt = 0; cnt < pmnc_counters; cnt++) {
130 pe = NULL;
131 mutex_lock(&perf_mutex);
132 if (per_cpu(pevent, cpu)[cnt]) {
133 pe = per_cpu(pevent, cpu)[cnt];
134 per_cpu(pevent, cpu)[cnt] = NULL;
135 }
136 mutex_unlock(&perf_mutex);
138 if (pe) {
139 perf_event_release_kernel(pe);
140 }
141 }
142 }
144 static int gator_events_perf_pmu_start(void)
145 {
146 int cnt, cpu;
147 u32 size = sizeof(struct perf_event_attr);
149 for_each_present_cpu(cpu) {
150 for (cnt = 0; cnt < pmnc_counters; cnt++) {
151 per_cpu(pevent, cpu)[cnt] = NULL;
152 if (!pmnc_enabled[cnt]) // Skip disabled counters
153 continue;
155 per_cpu(perfPrev, cpu)[cnt] = 0;
156 per_cpu(perfCurr, cpu)[cnt] = 0;
157 per_cpu(perfPrevDelta, cpu)[cnt] = 0;
158 per_cpu(pevent_attr, cpu)[cnt] = kmalloc(size, GFP_KERNEL);
159 if (!per_cpu(pevent_attr, cpu)[cnt]) {
160 gator_events_perf_pmu_stop();
161 return -1;
162 }
164 memset(per_cpu(pevent_attr, cpu)[cnt], 0, size);
165 per_cpu(pevent_attr, cpu)[cnt]->type = PERF_TYPE_RAW;
166 per_cpu(pevent_attr, cpu)[cnt]->size = size;
167 per_cpu(pevent_attr, cpu)[cnt]->config = pmnc_event[cnt];
168 per_cpu(pevent_attr, cpu)[cnt]->sample_period = 0;
169 per_cpu(pevent_attr, cpu)[cnt]->pinned = 1;
171 // handle special case for ccnt
172 if (cnt == ccnt) {
173 per_cpu(pevent_attr, cpu)[cnt]->type = PERF_TYPE_HARDWARE;
174 per_cpu(pevent_attr, cpu)[cnt]->config = PERF_COUNT_HW_CPU_CYCLES;
175 }
176 }
177 }
179 return 0;
180 }
182 static void gator_events_perf_pmu_stop(void)
183 {
184 unsigned int cnt, cpu;
186 for_each_present_cpu(cpu) {
187 for (cnt = 0; cnt < pmnc_counters; cnt++) {
188 if (per_cpu(pevent_attr, cpu)[cnt]) {
189 kfree(per_cpu(pevent_attr, cpu)[cnt]);
190 per_cpu(pevent_attr, cpu)[cnt] = NULL;
191 }
192 }
193 }
195 for (cnt = 0; cnt < pmnc_counters; cnt++) {
196 pmnc_enabled[cnt] = 0;
197 pmnc_event[cnt] = 0;
198 pmnc_count[cnt] = 0;
199 }
200 }
202 static int gator_events_perf_pmu_read(int **buffer)
203 {
204 int cnt, delta, len = 0;
205 int cpu = smp_processor_id();
207 for (cnt = 0; cnt < pmnc_counters; cnt++) {
208 struct perf_event * ev = per_cpu(pevent, cpu)[cnt];
209 if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) {
210 ev->pmu->read(ev);
211 per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count);
212 delta = per_cpu(perfCurr, cpu)[cnt] - per_cpu(perfPrev, cpu)[cnt];
213 if (delta != per_cpu(perfPrevDelta, cpu)[cnt]) {
214 per_cpu(perfPrevDelta, cpu)[cnt] = delta;
215 per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt];
216 per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt];
217 if (delta < 0)
218 delta *= -1;
219 per_cpu(perfCnt, cpu)[len++] = delta;
220 }
221 }
222 }
224 if (buffer)
225 *buffer = per_cpu(perfCnt, cpu);
227 return len;
228 }
230 static struct gator_interface gator_events_perf_pmu_interface = {
231 .create_files = gator_events_perf_pmu_create_files,
232 .start = gator_events_perf_pmu_start,
233 .stop = gator_events_perf_pmu_stop,
234 .online = gator_events_perf_pmu_online,
235 .online_dispatch = gator_events_perf_pmu_online_dispatch,
236 .offline_dispatch = gator_events_perf_pmu_offline_dispatch,
237 .read = gator_events_perf_pmu_read,
238 };
240 int gator_events_perf_pmu_init(void)
241 {
242 unsigned int cnt;
244 switch (gator_cpuid()) {
245 case ARM1136:
246 case ARM1156:
247 case ARM1176:
248 pmnc_name = "ARM_ARM11";
249 pmnc_counters = 3;
250 ccnt = 2;
251 break;
252 case ARM11MPCORE:
253 pmnc_name = "ARM_ARM11MPCore";
254 pmnc_counters = 3;
255 break;
256 case CORTEX_A5:
257 pmnc_name = "ARM_Cortex-A5";
258 pmnc_counters = 2;
259 break;
260 case CORTEX_A7:
261 pmnc_name = "ARM_Cortex-A7";
262 pmnc_counters = 4;
263 break;
264 case CORTEX_A8:
265 pmnc_name = "ARM_Cortex-A8";
266 pmnc_counters = 4;
267 break;
268 case CORTEX_A9:
269 pmnc_name = "ARM_Cortex-A9";
270 pmnc_counters = 6;
271 break;
272 case CORTEX_A15:
273 pmnc_name = "ARM_Cortex-A15";
274 pmnc_counters = 6;
275 break;
276 case SCORPION:
277 pmnc_name = "Scorpion";
278 pmnc_counters = 4;
279 break;
280 case SCORPIONMP:
281 pmnc_name = "ScorpionMP";
282 pmnc_counters = 4;
283 break;
284 case KRAITSIM:
285 case KRAIT:
286 pmnc_name = "Krait";
287 pmnc_counters = 4;
288 break;
289 default:
290 return -1;
291 }
293 pmnc_counters++; // CNT[n] + CCNT
295 for (cnt = 0; cnt < CNTMAX; cnt++) {
296 pmnc_enabled[cnt] = 0;
297 pmnc_event[cnt] = 0;
298 pmnc_count[cnt] = 0;
299 pmnc_key[cnt] = gator_events_get_key();
300 }
302 return gator_events_install(&gator_events_perf_pmu_interface);
303 }
305 gator_events_init(gator_events_perf_pmu_init);
306 #endif