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