perf: Fix GPF when writing fixed trigger counters
[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 /* Helper to set a fixed perfcounter to trigger/overflow after count events.
113  * Anytime you set a perfcounter to something non-zero, you need to use this
114  * helper. */
115 static void perfmon_set_fixed_trigger(unsigned int idx, uint64_t count)
116 {
117         int64_t write_val = -(int64_t)count;
118
119         write_val &= (1ULL << cpu_caps.bits_x_fix_counter) - 1;
120         write_msr(MSR_CORE_PERF_FIXED_CTR0 + idx, write_val);
121 }
122
123 /* Helper to set a regular perfcounter to trigger/overflow after count events.
124  * Anytime you set a perfcounter to something non-zero, you ought to use this
125  * helper. */
126 static void perfmon_set_unfixed_trigger(unsigned int idx, uint64_t count)
127 {
128         int64_t write_val = -(int64_t)count;
129
130         write_val &= (1ULL << cpu_caps.bits_x_counter) - 1;
131         write_msr(MSR_IA32_PERFCTR0 + idx, write_val);
132 }
133
134 static void perfmon_do_cores_alloc(void *opaque)
135 {
136         struct perfmon_alloc *pa = (struct perfmon_alloc *) opaque;
137         struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
138         int i;
139
140         spin_lock_irqsave(&cctx->lock);
141         if (perfmon_is_fixed_event(&pa->ev)) {
142                 uint64_t fxctrl_value = read_msr(MSR_CORE_PERF_FIXED_CTR_CTRL);
143                 uint64_t tmp;
144
145                 i = PMEV_GET_EVENT(pa->ev.event);
146                 if (i >= (int) cpu_caps.fix_counters_x_proc) {
147                         i = -EINVAL;
148                 } else if (fxctrl_value & (FIXCNTR_MASK << (i * FIXCNTR_NBITS))) {
149                         i = -EBUSY;
150                 } else {
151                         cctx->fixed_counters[i] = pa->ev;
152                         PMEV_SET_EN(cctx->fixed_counters[i].event, 1);
153
154                         tmp = perfmon_get_fixevent_mask(&pa->ev, i, fxctrl_value);
155
156                         perfmon_enable_fix_event(i, TRUE);
157
158                         perfmon_set_fixed_trigger(i, pa->ev.trigger_count);
159                         write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL, tmp);
160                 }
161         } else {
162                 for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
163                         if (cctx->counters[i].event == 0) {
164                                 if (!perfmon_event_available(i))
165                                         warn_once("Counter %d is free but not available", i);
166                                 else
167                                         break;
168                         }
169                 }
170                 if (i < (int) cpu_caps.counters_x_proc) {
171                         cctx->counters[i] = pa->ev;
172                         PMEV_SET_EN(cctx->counters[i].event, 1);
173
174                         perfmon_enable_event(i, TRUE);
175
176                         perfmon_set_unfixed_trigger(i, pa->ev.trigger_count);
177                         write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + i,
178                                   cctx->counters[i].event);
179                 } else {
180                         i = -ENOSPC;
181                 }
182         }
183         spin_unlock_irqsave(&cctx->lock);
184
185         pa->cores_counters[core_id()] = (counter_t) i;
186 }
187
188 static void perfmon_do_cores_free(void *opaque)
189 {
190         struct perfmon_alloc *pa = (struct perfmon_alloc *) opaque;
191         struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
192         int err = 0, coreno = core_id();
193         counter_t ccno = pa->cores_counters[coreno];
194
195         spin_lock_irqsave(&cctx->lock);
196         if (perfmon_is_fixed_event(&pa->ev)) {
197                 unsigned int ccbitsh = ccno * FIXCNTR_NBITS;
198                 uint64_t fxctrl_value = read_msr(MSR_CORE_PERF_FIXED_CTR_CTRL);
199
200                 if ((ccno >= cpu_caps.fix_counters_x_proc) ||
201                         !(fxctrl_value & (FIXCNTR_MASK << ccbitsh))) {
202                         err = -ENOENT;
203                 } else {
204                         perfmon_init_event(&cctx->fixed_counters[ccno]);
205
206                         perfmon_enable_fix_event((int) ccno, FALSE);
207
208                         write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL,
209                                   fxctrl_value & ~(FIXCNTR_MASK << ccbitsh));
210                         write_msr(MSR_CORE_PERF_FIXED_CTR0 + ccno, 0);
211                 }
212         } else {
213                 if (ccno < (int) cpu_caps.counters_x_proc) {
214                         perfmon_init_event(&cctx->counters[ccno]);
215
216                         perfmon_enable_event((int) ccno, FALSE);
217
218                         write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + ccno, 0);
219                         write_msr(MSR_IA32_PERFCTR0 + ccno, 0);
220                 } else {
221                         err = -ENOENT;
222                 }
223         }
224         spin_unlock_irqsave(&cctx->lock);
225
226         pa->cores_counters[coreno] = (counter_t) err;
227 }
228
229 static void perfmon_do_cores_status(void *opaque)
230 {
231         struct perfmon_status_env *env = (struct perfmon_status_env *) opaque;
232         struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
233         int coreno = core_id();
234         counter_t ccno = env->pa->cores_counters[coreno];
235
236         spin_lock_irqsave(&cctx->lock);
237         if (perfmon_is_fixed_event(&env->pa->ev))
238                 env->pef->cores_values[coreno] =
239                     read_msr(MSR_CORE_PERF_FIXED_CTR0 + ccno);
240         else
241                 env->pef->cores_values[coreno] =
242                     read_msr(MSR_IA32_PERFCTR0 + ccno);
243         spin_unlock_irqsave(&cctx->lock);
244 }
245
246 static void perfmon_setup_alloc_core_set(const struct perfmon_alloc *pa,
247                                          struct core_set *cset)
248 {
249         int i;
250
251         core_set_init(cset);
252         for (i = 0; i < num_cores; i++) {
253                 if (pa->cores_counters[i] >= 0)
254                         core_set_setcpu(cset, i);
255         }
256 }
257
258 static void perfmon_cleanup_cores_alloc(struct perfmon_alloc *pa)
259 {
260         struct core_set cset;
261
262         perfmon_setup_alloc_core_set(pa, &cset);
263         smp_do_in_cores(&cset, perfmon_do_cores_free, pa);
264 }
265
266 static void perfmon_free_alloc(struct perfmon_alloc *pa)
267 {
268         kfree(pa);
269 }
270
271 static void perfmon_destroy_alloc(struct perfmon_alloc *pa)
272 {
273         if (pa) {
274                 perfmon_cleanup_cores_alloc(pa);
275                 perfmon_free_alloc(pa);
276         }
277 }
278
279 static void perfmon_release_alloc(struct kref *kref)
280 {
281         struct perfmon_alloc *pa = container_of(kref, struct perfmon_alloc, ref);
282
283         perfmon_destroy_alloc(pa);
284 }
285
286 static struct perfmon_alloc *perfmon_create_alloc(const struct perfmon_event *pev)
287 {
288         int i;
289         struct perfmon_alloc *pa = kzmalloc(sizeof(struct perfmon_alloc) +
290                                                 num_cores * sizeof(counter_t),
291                                             MEM_WAIT);
292
293         kref_init(&pa->ref, perfmon_release_alloc, 1);
294         pa->ev = *pev;
295         for (i = 0; i < num_cores; i++)
296                 pa->cores_counters[i] = INVALID_COUNTER;
297
298         return pa;
299 }
300
301 static struct perfmon_status *perfmon_alloc_status(void)
302 {
303         struct perfmon_status *pef = kzmalloc(sizeof(struct perfmon_status) +
304                                                   num_cores * sizeof(uint64_t),
305                                               MEM_WAIT);
306
307         return pef;
308 }
309
310 static void perfmon_arm_irq(void)
311 {
312         apicrput(MSR_LAPIC_LVT_PERFMON, IdtLAPIC_PCINT);
313 }
314
315 bool perfmon_supported(void)
316 {
317         return cpu_caps.perfmon_version >= 2;
318 }
319
320 void perfmon_global_init(void)
321 {
322         perfmon_read_cpu_caps(&cpu_caps);
323 }
324
325 void perfmon_pcpu_init(void)
326 {
327         int i;
328
329         if (!perfmon_supported())
330                 return;
331         /* Enable user level access to the performance counters */
332         lcr4(rcr4() | CR4_PCE);
333
334         /* Reset all the counters and selectors to zero.
335          */
336         write_msr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
337         for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
338                 write_msr(MSR_ARCH_PERFMON_EVENTSEL0 + i, 0);
339                 write_msr(MSR_IA32_PERFCTR0 + i, 0);
340         }
341         write_msr(MSR_CORE_PERF_FIXED_CTR_CTRL, 0);
342         for (i = 0; i < (int) cpu_caps.fix_counters_x_proc; i++)
343                 write_msr(MSR_CORE_PERF_FIXED_CTR0 + i, 0);
344
345         perfmon_arm_irq();
346 }
347
348 static uint64_t perfmon_make_sample_event(const struct perfmon_event *pev)
349 {
350         uint64_t ei = ((uint64_t) PMEV_GET_MASK(pev->event) << 8) |
351                 PMEV_GET_EVENT(pev->event);
352
353         if (perfmon_is_fixed_event(pev))
354                 ei |= 1 << 16;
355
356         return PROF_MKINFO(PROF_DOM_PMU, ei);
357 }
358
359 void perfmon_interrupt(struct hw_trapframe *hw_tf, void *data)
360 {
361         int i;
362         struct perfmon_cpu_context *cctx = PERCPU_VARPTR(counters_env);
363         uint64_t gctrl, status;
364
365         spin_lock_irqsave(&cctx->lock);
366         /* We need to save the global control status, because we need to disable
367          * counters in order to be able to reset their values.
368          * We will restore the global control status on exit.
369          */
370         status = read_msr(MSR_CORE_PERF_GLOBAL_STATUS);
371         gctrl = read_msr(MSR_CORE_PERF_GLOBAL_CTRL);
372         write_msr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
373         for (i = 0; i < (int) cpu_caps.counters_x_proc; i++) {
374                 if (status & ((uint64_t) 1 << i)) {
375                         if (cctx->counters[i].event) {
376                                 profiler_add_hw_sample(
377                                     hw_tf, perfmon_make_sample_event(cctx->counters + i));
378                                 perfmon_set_unfixed_trigger(i, cctx->counters[i].trigger_count);
379                         }
380                 }
381         }
382         for (i = 0; i < (int) cpu_caps.fix_counters_x_proc; i++) {
383                 if (status & ((uint64_t) 1 << (32 + i))) {
384                         if (cctx->fixed_counters[i].event) {
385                                 profiler_add_hw_sample(
386                                     hw_tf, perfmon_make_sample_event(cctx->fixed_counters + i));
387                                 perfmon_set_fixed_trigger(i,
388                                         cctx->fixed_counters[i].trigger_count);
389                         }
390                 }
391         }
392         write_msr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status);
393         write_msr(MSR_CORE_PERF_GLOBAL_CTRL, gctrl);
394         spin_unlock_irqsave(&cctx->lock);
395
396         /* We need to re-arm the IRQ as the PFM IRQ gets masked on trigger.
397          * Note that KVM and real HW seems to be doing two different things WRT
398          * re-arming the IRQ. KVM re-arms does not mask the IRQ, while real HW does.
399          */
400         perfmon_arm_irq();
401 }
402
403 void perfmon_get_cpu_caps(struct perfmon_cpu_caps *pcc)
404 {
405         memcpy(pcc, &cpu_caps, sizeof(*pcc));
406 }
407
408 static int perfmon_install_session_alloc(struct perfmon_session *ps,
409                                          struct perfmon_alloc *pa)
410 {
411         int i;
412
413         spin_lock(&ps->lock);
414         for (i = 0; (i < ARRAY_SIZE(ps->allocs)) && (ps->allocs[i] != NULL); i++)
415                 ;
416         if (likely(i < ARRAY_SIZE(ps->allocs)))
417                 ps->allocs[i] = pa;
418         else
419                 i = -ENFILE;
420         spin_unlock(&ps->lock);
421         if (unlikely(i < 0))
422                 error(-i, ERROR_FIXME);
423
424         return i;
425 }
426
427 int perfmon_open_event(const struct core_set *cset, struct perfmon_session *ps,
428                        const struct perfmon_event *pev)
429 {
430         ERRSTACK(1);
431         int i;
432         struct perfmon_alloc *pa = perfmon_create_alloc(pev);
433
434         if (waserror()) {
435                 perfmon_destroy_alloc(pa);
436                 nexterror();
437         }
438         smp_do_in_cores(cset, perfmon_do_cores_alloc, pa);
439
440         for (i = 0; i < num_cores; i++) {
441                 if (core_set_getcpu(cset, i)) {
442                         counter_t ccno = pa->cores_counters[i];
443
444                         if (unlikely(ccno < 0)) {
445                                 perfmon_destroy_alloc(pa);
446                                 return (int) ccno;
447                         }
448                 }
449         }
450         /* The perfmon_alloc data structure will not be visible to userspace,
451          * until the perfmon_install_session_alloc() completes, and at that
452          * time the smp_do_in_cores(perfmon_do_cores_alloc) will have run on
453          * all cores.
454          * The perfmon_alloc data structure will never be changed once published.
455          */
456         i = perfmon_install_session_alloc(ps, pa);
457         poperror();
458
459         return i;
460 }
461
462 static void perfmon_alloc_get(struct perfmon_session *ps, int ped, bool reset,
463                               struct perfmon_alloc **ppa)
464 {
465         struct perfmon_alloc *pa;
466
467         if (unlikely((ped < 0) || (ped >= ARRAY_SIZE(ps->allocs))))
468                 error(EBADFD, ERROR_FIXME);
469         spin_lock(&ps->lock);
470         pa = ps->allocs[ped];
471         if (likely(pa)) {
472                 if (reset)
473                         ps->allocs[ped] = NULL;
474                 else
475                         kref_get(&pa->ref, 1);
476         }
477         spin_unlock(&ps->lock);
478         if (unlikely(!pa))
479                 error(ENOENT, ERROR_FIXME);
480         *ppa = pa;
481 }
482
483 void perfmon_close_event(struct perfmon_session *ps, int ped)
484 {
485         struct perfmon_alloc *pa;
486
487         perfmon_alloc_get(ps, ped, TRUE, &pa);
488         kref_put(&pa->ref);
489 }
490
491 struct perfmon_status *perfmon_get_event_status(struct perfmon_session *ps,
492                                                 int ped)
493 {
494         struct core_set cset;
495         struct perfmon_status_env env;
496
497         perfmon_alloc_get(ps, ped, FALSE, &env.pa);
498         env.pef = perfmon_alloc_status();
499         perfmon_setup_alloc_core_set(env.pa, &cset);
500
501         smp_do_in_cores(&cset, perfmon_do_cores_status, &env);
502
503         kref_put(&env.pa->ref);
504
505         return env.pef;
506 }
507
508 void perfmon_free_event_status(struct perfmon_status *pef)
509 {
510         kfree(pef);
511 }
512
513 static void perfmon_release_session(struct kref *kref)
514 {
515         struct perfmon_session *ps =
516             container_of(kref, struct perfmon_session, ref);
517
518         for (int i = 0; i < ARRAY_SIZE(ps->allocs); i++) {
519                 struct perfmon_alloc *pa = ps->allocs[i];
520
521                 if (pa)
522                         kref_put(&pa->ref);
523         }
524         kfree(ps);
525 }
526
527 struct perfmon_session *perfmon_create_session(void)
528 {
529         struct perfmon_session *ps = kzmalloc(sizeof(struct perfmon_session),
530                                               MEM_WAIT);
531
532         kref_init(&ps->ref, perfmon_release_session, 1);
533         spinlock_init(&ps->lock);
534
535         return ps;
536 }
537
538 void perfmon_get_session(struct perfmon_session *ps)
539 {
540         kref_get(&ps->ref, 1);
541 }
542
543 void perfmon_close_session(struct perfmon_session *ps)
544 {
545         if (likely(ps))
546                 kref_put(&ps->ref);
547 }