perf: Fix fixed counter busy detection
[akaros.git] / kern / arch / x86 / perfmon.c
1 /* Copyright (c) 2015 Google Inc
2  * Davide Libenzi <dlibenzi@google.com>
3  * See LICENSE for details.
4  */
5
6 #include <sys/types.h>
7 #include <arch/ros/msr-index.h>
8 #include <arch/ros/membar.h>
9 #include <arch/x86.h>
10 #include <arch/msr.h>
11 #include <arch/uaccess.h>
12 #include <ros/errno.h>
13 #include <assert.h>
14 #include <trap.h>
15 #include <smp.h>
16 #include <atomic.h>
17 #include <core_set.h>
18 #include <kref.h>
19 #include <percpu.h>
20 #include <kmalloc.h>
21 #include <err.h>
22 #include <string.h>
23 #include <profiler.h>
24 #include <arch/perfmon.h>
25
26 #define FIXCNTR_NBITS 4
27 #define FIXCNTR_MASK (((uint64_t) 1 << FIXCNTR_NBITS) - 1)
28
29 struct perfmon_cpu_context {
30         spinlock_t lock;
31         struct perfmon_event counters[MAX_VAR_COUNTERS];
32         struct perfmon_event fixed_counters[MAX_FIX_COUNTERS];
33 };
34
35 struct perfmon_status_env {
36         struct perfmon_alloc *pa;
37         struct perfmon_status *pef;
38 };
39
40 static struct perfmon_cpu_caps cpu_caps;
41 static DEFINE_PERCPU(struct perfmon_cpu_context, counters_env);
42 DEFINE_PERCPU_INIT(perfmon_counters_env_init);
43
44 static void perfmon_counters_env_init(void)
45 {
46         for (int i = 0; i < num_cores; i++) {
47                 struct perfmon_cpu_context *cctx = _PERCPU_VARPTR(counters_env, i);
48
49                 spinlock_init_irqsave(&cctx->lock);
50         }
51 }
52
53 static void perfmon_read_cpu_caps(struct perfmon_cpu_caps *pcc)
54 {
55         uint32_t a, b, c, d;
56
57         cpuid(0x0a, 0, &a, &b, &c, &d);
58
59         pcc->proc_arch_events = a >> 24;
60         pcc->bits_x_counter = (a >> 16) & 0xff;
61         pcc->counters_x_proc = (a >> 8) & 0xff;
62         pcc->bits_x_fix_counter = (d >> 5) & 0xff;
63         pcc->fix_counters_x_proc = d & 0x1f;
64         pcc->perfmon_version = a & 0xff;
65 }
66
67 static void perfmon_enable_event(int event, bool enable)
68 {
69         uint64_t gctrl = read_msr(MSR_CORE_PERF_GLOBAL_CTRL);
70
71         if (enable)
72                 write_msr(MSR_CORE_PERF_GLOBAL_CTRL, gctrl | (1 << event));
73         else
74                 write_msr(MSR_CORE_PERF_GLOBAL_CTRL, gctrl & ~(1 << event));
75 }
76
77 static void perfmon_enable_fix_event(int event, bool enable)
78 {
79         uint64_t gctrl = read_msr(MSR_CORE_PERF_GLOBAL_CTRL);
80
81         if (enable)
82                 write_msr(MSR_CORE_PERF_GLOBAL_CTRL,
83                           gctrl | ((uint64_t) 1 << (32 + event)));
84         else
85                 write_msr(MSR_CORE_PERF_GLOBAL_CTRL,
86                           gctrl & ~((uint64_t) 1 << (32 + event)));
87 }
88
89 static bool perfmon_event_available(uint32_t event)
90 {
91         return read_msr(MSR_ARCH_PERFMON_EVENTSEL0 + event) == 0;
92 }
93
94 static uint64_t perfmon_get_fixevent_mask(const struct perfmon_event *pev,
95                                           int eventno, uint64_t base)
96 {
97         uint64_t m = 0;
98
99         if (PMEV_GET_EN(pev->event))
100                 m |= 1 << 3;
101         if (PMEV_GET_OS(pev->event))
102                 m |= (1 << 0);
103         if (PMEV_GET_USR(pev->event))
104                 m |= (1 << 1);
105
106         m <<= eventno * FIXCNTR_NBITS;
107         m |= base & ~(FIXCNTR_MASK << (eventno * FIXCNTR_NBITS));
108
109         return m;
110 }
111
112 static void perfmon_do_cores_alloc(void *opaque)
113 {
114         struct perfmon_alloc *pa = (struct perfmon_alloc *) opaque;
115         struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
116         int i;
117
118         spin_lock_irqsave(&cctx->lock);
119         if (perfmon_is_fixed_event(&pa->ev)) {
120                 uint64_t fxctrl_value = read_msr(MSR_CORE_PERF_FIXED_CTR_CTRL);
121                 uint64_t tmp;
122
123                 i = PMEV_GET_EVENT(pa->ev.event);
124                 if (i >= (int) cpu_caps.fix_counters_x_proc) {
125                         i = -EINVAL;
126                 } else if (fxctrl_value & (FIXCNTR_MASK << (i * FIXCNTR_NBITS))) {
127                         i = -EBUSY;
128                 } else {
129                         cctx->fixed_counters[i] = pa->ev;
130                         PMEV_SET_EN(cctx->fixed_counters[i].event, 1);
131
132                         tmp = perfmon_get_fixevent_mask(&pa->ev, i, fxctrl_value);
133
134                         perfmon_enable_fix_event(i, TRUE);
135
136                         write_msr(MSR_CORE_PERF_FIXED_CTR0 + i,
137                                   -(int64_t) pa->ev.trigger_count);
138                         write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL, tmp);
139                 }
140         } else {
141                 for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
142                         if (cctx->counters[i].event == 0) {
143                                 if (!perfmon_event_available(i))
144                                         warn_once("Counter %d is free but not available", i);
145                                 else
146                                         break;
147                         }
148                 }
149                 if (i < (int) cpu_caps.counters_x_proc) {
150                         cctx->counters[i] = pa->ev;
151                         PMEV_SET_EN(cctx->counters[i].event, 1);
152
153                         perfmon_enable_event(i, TRUE);
154
155                         write_msr(MSR_IA32_PERFCTR0 + i, -(int64_t) pa->ev.trigger_count);
156                         write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + i,
157                                   cctx->counters[i].event);
158                 } else {
159                         i = -ENOSPC;
160                 }
161         }
162         spin_unlock_irqsave(&cctx->lock);
163
164         pa->cores_counters[core_id()] = (counter_t) i;
165 }
166
167 static void perfmon_do_cores_free(void *opaque)
168 {
169         struct perfmon_alloc *pa = (struct perfmon_alloc *) opaque;
170         struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
171         int err = 0, coreno = core_id();
172         counter_t ccno = pa->cores_counters[coreno];
173
174         spin_lock_irqsave(&cctx->lock);
175         if (perfmon_is_fixed_event(&pa->ev)) {
176                 unsigned int ccbitsh = ccno * FIXCNTR_NBITS;
177                 uint64_t fxctrl_value = read_msr(MSR_CORE_PERF_FIXED_CTR_CTRL);
178
179                 if ((ccno >= cpu_caps.fix_counters_x_proc) ||
180                         !(fxctrl_value & (FIXCNTR_MASK << ccbitsh))) {
181                         err = -ENOENT;
182                 } else {
183                         perfmon_init_event(&cctx->fixed_counters[ccno]);
184
185                         perfmon_enable_fix_event((int) ccno, FALSE);
186
187                         write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL,
188                                   fxctrl_value & ~(FIXCNTR_MASK << ccbitsh));
189                         write_msr(MSR_CORE_PERF_FIXED_CTR0 + ccno, 0);
190                 }
191         } else {
192                 if (ccno < (int) cpu_caps.counters_x_proc) {
193                         perfmon_init_event(&cctx->counters[ccno]);
194
195                         perfmon_enable_event((int) ccno, FALSE);
196
197                         write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + ccno, 0);
198                         write_msr(MSR_IA32_PERFCTR0 + ccno, 0);
199                 } else {
200                         err = -ENOENT;
201                 }
202         }
203         spin_unlock_irqsave(&cctx->lock);
204
205         pa->cores_counters[coreno] = (counter_t) err;
206 }
207
208 static void perfmon_do_cores_status(void *opaque)
209 {
210         struct perfmon_status_env *env = (struct perfmon_status_env *) opaque;
211         struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
212         int coreno = core_id();
213         counter_t ccno = env->pa->cores_counters[coreno];
214
215         spin_lock_irqsave(&cctx->lock);
216         if (perfmon_is_fixed_event(&env->pa->ev))
217                 env->pef->cores_values[coreno] =
218                     read_msr(MSR_CORE_PERF_FIXED_CTR0 + ccno);
219         else
220                 env->pef->cores_values[coreno] =
221                     read_msr(MSR_IA32_PERFCTR0 + ccno);
222         spin_unlock_irqsave(&cctx->lock);
223 }
224
225 static void perfmon_setup_alloc_core_set(const struct perfmon_alloc *pa,
226                                          struct core_set *cset)
227 {
228         int i;
229
230         core_set_init(cset);
231         for (i = 0; i < num_cores; i++) {
232                 if (pa->cores_counters[i] >= 0)
233                         core_set_setcpu(cset, i);
234         }
235 }
236
237 static void perfmon_cleanup_cores_alloc(struct perfmon_alloc *pa)
238 {
239         struct core_set cset;
240
241         perfmon_setup_alloc_core_set(pa, &cset);
242         smp_do_in_cores(&cset, perfmon_do_cores_free, pa);
243 }
244
245 static void perfmon_free_alloc(struct perfmon_alloc *pa)
246 {
247         kfree(pa);
248 }
249
250 static void perfmon_destroy_alloc(struct perfmon_alloc *pa)
251 {
252         if (pa) {
253                 perfmon_cleanup_cores_alloc(pa);
254                 perfmon_free_alloc(pa);
255         }
256 }
257
258 static void perfmon_release_alloc(struct kref *kref)
259 {
260         struct perfmon_alloc *pa = container_of(kref, struct perfmon_alloc, ref);
261
262         perfmon_destroy_alloc(pa);
263 }
264
265 static struct perfmon_alloc *perfmon_create_alloc(const struct perfmon_event *pev)
266 {
267         int i;
268         struct perfmon_alloc *pa = kzmalloc(sizeof(struct perfmon_alloc) +
269                                                 num_cores * sizeof(counter_t),
270                                             MEM_WAIT);
271
272         kref_init(&pa->ref, perfmon_release_alloc, 1);
273         pa->ev = *pev;
274         for (i = 0; i < num_cores; i++)
275                 pa->cores_counters[i] = INVALID_COUNTER;
276
277         return pa;
278 }
279
280 static struct perfmon_status *perfmon_alloc_status(void)
281 {
282         struct perfmon_status *pef = kzmalloc(sizeof(struct perfmon_status) +
283                                                   num_cores * sizeof(uint64_t),
284                                               MEM_WAIT);
285
286         return pef;
287 }
288
289 static void perfmon_arm_irq(void)
290 {
291         apicrput(MSR_LAPIC_LVT_PERFMON, IdtLAPIC_PCINT);
292 }
293
294 bool perfmon_supported(void)
295 {
296         return cpu_caps.perfmon_version >= 2;
297 }
298
299 void perfmon_global_init(void)
300 {
301         perfmon_read_cpu_caps(&cpu_caps);
302 }
303
304 void perfmon_pcpu_init(void)
305 {
306         int i;
307
308         if (!perfmon_supported())
309                 return;
310         /* Enable user level access to the performance counters */
311         lcr4(rcr4() | CR4_PCE);
312
313         /* Reset all the counters and selectors to zero.
314          */
315         write_msr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
316         for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
317                 write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + i, 0);
318                 write_msr(MSR_IA32_PERFCTR0 + i, 0);
319         }
320         write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);
321         for (i = 0; i < (int) cpu_caps.fix_counters_x_proc; i++)
322                 write_msr(MSR_CORE_PERF_FIXED_CTR0 + i, 0);
323
324         perfmon_arm_irq();
325 }
326
327 static uint64_t perfmon_make_sample_event(const struct perfmon_event *pev)
328 {
329         uint64_t ei = ((uint64_t) PMEV_GET_MASK(pev->event) << 8) |
330                 PMEV_GET_EVENT(pev->event);
331
332         if (perfmon_is_fixed_event(pev))
333                 ei |= 1 << 16;
334
335         return PROF_MKINFO(PROF_DOM_PMU, ei);
336 }
337
338 void perfmon_interrupt(struct hw_trapframe *hw_tf, void *data)
339 {
340         int i;
341         struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
342         uint64_t gctrl, status;
343
344         spin_lock_irqsave(&cctx->lock);
345         /* We need to save the global control status, because we need to disable
346          * counters in order to be able to reset their values.
347          * We will restore the global control status on exit.
348          */
349         status = read_msr(MSR_CORE_PERF_GLOBAL_STATUS);
350         gctrl = read_msr(MSR_CORE_PERF_GLOBAL_CTRL);
351         write_msr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
352         for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
353                 if (status & ((uint64_t) 1 << i)) {
354                         if (cctx->counters[i].event) {
355                                 profiler_add_hw_sample(
356                                     hw_tf, perfmon_make_sample_event(cctx->counters + i));
357                                 write_msr(MSR_IA32_PERFCTR0 + i,
358                                           -(int64_t) cctx->counters[i].trigger_count);
359                         }
360                 }
361         }
362         for (i = 0; i < (int) cpu_caps.fix_counters_x_proc; i++) {
363                 if (status & ((uint64_t) 1 << (32 + i))) {
364                         if (cctx->fixed_counters[i].event) {
365                                 profiler_add_hw_sample(
366                                     hw_tf, perfmon_make_sample_event(cctx->fixed_counters + i));
367                                 write_msr(MSR_CORE_PERF_FIXED_CTR0 + i,
368                                           -(int64_t) cctx->fixed_counters[i].trigger_count);
369                         }
370                 }
371         }
372         write_msr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status);
373         write_msr(MSR_CORE_PERF_GLOBAL_CTRL, gctrl);
374         spin_unlock_irqsave(&cctx->lock);
375
376         /* We need to re-arm the IRQ as the PFM IRQ gets masked on trigger.
377          * Note that KVM and real HW seems to be doing two different things WRT
378          * re-arming the IRQ. KVM re-arms does not mask the IRQ, while real HW does.
379          */
380         perfmon_arm_irq();
381 }
382
383 void perfmon_get_cpu_caps(struct perfmon_cpu_caps *pcc)
384 {
385         memcpy(pcc, &cpu_caps, sizeof(*pcc));
386 }
387
388 static int perfmon_install_session_alloc(struct perfmon_session *ps,
389                                          struct perfmon_alloc *pa)
390 {
391         int i;
392
393         spin_lock(&ps->lock);
394         for (i = 0; (i < ARRAY_SIZE(ps->allocs)) && (ps->allocs[i] != NULL); i++)
395                 ;
396         if (likely(i < ARRAY_SIZE(ps->allocs)))
397                 ps->allocs[i] = pa;
398         else
399                 i = -ENFILE;
400         spin_unlock(&ps->lock);
401         if (unlikely(i < 0))
402                 error(-i, ERROR_FIXME);
403
404         return i;
405 }
406
407 int perfmon_open_event(const struct core_set *cset, struct perfmon_session *ps,
408                        const struct perfmon_event *pev)
409 {
410         ERRSTACK(1);
411         int i;
412         struct perfmon_alloc *pa = perfmon_create_alloc(pev);
413
414         if (waserror()) {
415                 perfmon_destroy_alloc(pa);
416                 nexterror();
417         }
418         smp_do_in_cores(cset, perfmon_do_cores_alloc, pa);
419
420         for (i = 0; i < num_cores; i++) {
421                 if (core_set_getcpu(cset, i)) {
422                         counter_t ccno = pa->cores_counters[i];
423
424                         if (unlikely(ccno < 0)) {
425                                 perfmon_destroy_alloc(pa);
426                                 return (int) ccno;
427                         }
428                 }
429         }
430         /* The perfmon_alloc data structure will not be visible to userspace,
431          * until the perfmon_install_session_alloc() completes, and at that
432          * time the smp_do_in_cores(perfmon_do_cores_alloc) will have run on
433          * all cores.
434          * The perfmon_alloc data structure will never be changed once published.
435          */
436         i = perfmon_install_session_alloc(ps, pa);
437         poperror();
438
439         return i;
440 }
441
442 static void perfmon_alloc_get(struct perfmon_session *ps, int ped, bool reset,
443                               struct perfmon_alloc **ppa)
444 {
445         struct perfmon_alloc *pa;
446
447         if (unlikely((ped < 0) || (ped >= ARRAY_SIZE(ps->allocs))))
448                 error(EBADFD, ERROR_FIXME);
449         spin_lock(&ps->lock);
450         pa = ps->allocs[ped];
451         if (likely(pa)) {
452                 if (reset)
453                         ps->allocs[ped] = NULL;
454                 else
455                         kref_get(&pa->ref, 1);
456         }
457         spin_unlock(&ps->lock);
458         if (unlikely(!pa))
459                 error(ENOENT, ERROR_FIXME);
460         *ppa = pa;
461 }
462
463 void perfmon_close_event(struct perfmon_session *ps, int ped)
464 {
465         struct perfmon_alloc *pa;
466
467         perfmon_alloc_get(ps, ped, TRUE, &pa);
468         kref_put(&pa->ref);
469 }
470
471 struct perfmon_status *perfmon_get_event_status(struct perfmon_session *ps,
472                                                 int ped)
473 {
474         struct core_set cset;
475         struct perfmon_status_env env;
476
477         perfmon_alloc_get(ps, ped, FALSE, &env.pa);
478         env.pef = perfmon_alloc_status();
479         perfmon_setup_alloc_core_set(env.pa, &cset);
480
481         smp_do_in_cores(&cset, perfmon_do_cores_status, &env);
482
483         kref_put(&env.pa->ref);
484
485         return env.pef;
486 }
487
488 void perfmon_free_event_status(struct perfmon_status *pef)
489 {
490         kfree(pef);
491 }
492
493 static void perfmon_release_session(struct kref *kref)
494 {
495         struct perfmon_session *ps =
496             container_of(kref, struct perfmon_session, ref);
497
498         for (int i = 0; i < ARRAY_SIZE(ps->allocs); i++) {
499                 struct perfmon_alloc *pa = ps->allocs[i];
500
501                 if (pa)
502                         kref_put(&pa->ref);
503         }
504         kfree(ps);
505 }
506
507 struct perfmon_session *perfmon_create_session(void)
508 {
509         struct perfmon_session *ps = kzmalloc(sizeof(struct perfmon_session),
510                                               MEM_WAIT);
511
512         kref_init(&ps->ref, perfmon_release_session, 1);
513         spinlock_init(&ps->lock);
514
515         return ps;
516 }
517
518 void perfmon_get_session(struct perfmon_session *ps)
519 {
520         kref_get(&ps->ref, 1);
521 }
522
523 void perfmon_close_session(struct perfmon_session *ps)
524 {
525         if (likely(ps))
526                 kref_put(&ps->ref);
527 }