]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/arm-ds5-gator.git/blob - driver/gator_events_perf_pmu.c
Merge branch 'master' into android
[android-sdk/arm-ds5-gator.git] / driver / gator_events_perf_pmu.c
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         }
124 static void gator_events_perf_pmu_offline_dispatch(int cpu)
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         }
144 static int gator_events_perf_pmu_start(void)
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;
182 static void gator_events_perf_pmu_stop(void)
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         }
202 static int gator_events_perf_pmu_read(int **buffer)
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;
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)
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);
305 gator_events_init(gator_events_perf_pmu_init);
306 #endif