Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


PMU Host Interface and initial implementation for Linux and AMD/Intel
[palacios.git] / linux_module / iface-pmu.c
1 /*
2  * PMU
3  * (c) Chang S. Bae, 2013
4  */
5
6 #include <linux/cdev.h>
7 #include <linux/errno.h>
8 #include <asm/msr.h>
9 #include <asm/msr-index.h>
10
11 #include <palacios/vmm_types.h>
12 #include <palacios/vmm_util.h>
13 #include <interfaces/vmm_pmu.h>
14
15 #include "vm.h"
16 #include "palacios.h"
17 #include "iface-pmu-intel.h"
18 #include "iface-pmu-amd.h"
19 #include "util-queue.h"
20 #include "linux-exts.h"
21
22
23 // Number of inits/deinits we have seen (inc on init, dec on deinit)
24 // This is per CPU - init/deinit mean init/deinit PMU
25 // tracking ON THE CURRENT CORE
26 static DEFINE_PER_CPU(u32, pmu_refcount) = 0;
27
28
29 /*
30  * some macros may be commonly used
31  */
32 #define MSR_READ(msrs, c) do {rdmsrl((c), (msrs).q);} while (0)
33 #define MSR_WRITE(msrs, c) do {wrmsrl((c), (msrs).q);} while (0)
34 #define SET_BIT(val, i) ((val) |= (1 << i))
35 #define CLEAR_BIT(val, u, i) ((val) &= ~((u&1) << i))
36 #define SET_BYTE(val, u, i) ((val) |= ((u&255) << i))
37 #define CHECK_BIT(val, i) ((val) & (1U << i))
38
39
40 static inline void cpuid_string(u32 id, u32 dest[4]) {
41   asm volatile("cpuid"
42                :"=a"(dest),"=b"(*(dest+1)),"=c"(*(dest+2)),"=d"(*(dest+3))
43                :"a"(id));
44 }
45
46
47 static int get_cpu_vendor(char name[13])
48 {
49   u32 dest[4];
50   u32 maxid;
51
52   cpuid_string(0,dest);
53   maxid=dest[0];
54   ((u32*)name)[0]=dest[1];
55   ((u32*)name)[1]=dest[3];
56   ((u32*)name)[2]=dest[2];
57   name[12]=0;
58    
59   return maxid;
60 }
61
62 static int is_intel(void)
63 {
64   char name[13];
65   get_cpu_vendor(name);
66   return !strcmp(name,"GenuineIntel");
67 }
68
69 static int is_amd(void)
70 {
71   char name[13];
72   get_cpu_vendor(name);
73   return !strcmp(name,"AuthenticAMD");
74 }
75
76
77
78 /*
79  * AMD and Intel implementations are distinguished by prefix: INTEL or AMD
80  */
81
82 /*
83  * name: *_get_slot
84  * description: check available slots in pmu
85  * return: -1 if none, else returns index: 0 ... 3
86  */
87
88 static int intel_get_slot(void) {
89
90   int i, slot;
91   struct msr control;
92
93   slot = -1;
94   control.q = 0x0;
95
96   for (i=0; i<INTEL_NUM_PMU_CONTROLS; i++) {
97     INTEL_CTRL_READ(control, i);
98     if(control.q & (0x1<<INTEL_EN_BIT)) {
99       continue;
100     } else {
101       slot = i;
102       break;
103     }
104   }
105
106   return slot;
107 }
108
109 static int amd_get_slot(void) {
110   int i, slot;
111   struct msr control;
112
113   slot = -1;
114   control.q = 0x0;
115
116   for (i=0; i<AMD_NUM_PMU_CONTROLS; i++) {
117     AMD_CTRL_READ(control, i);
118     if(control.q & (0x1<<AMD_EN_BIT)) {
119       continue;
120     } else {
121       slot = i;
122       break;
123     }
124   }
125
126   return slot;
127   return -1;
128 }
129
130 /*
131  * name: *_find_idx
132  * description: find index of pmu register that is available
133  */
134 static int intel_find_idx(uint8_t event, uint8_t mask) {
135   int i;
136
137   struct msr control;
138
139   control.q = 0x0;
140
141   for (i=0; i<INTEL_NUM_PMU_COUNTERS; i++) {
142     INTEL_CTRL_READ(control, i);
143     if((((control.l>>INTEL_EVENT_BIT) & 0xff) == event) &&
144        (((control.l>>INTEL_UMASK_BIT) & 0xff) == mask)) {
145       return i;
146     }
147   }
148
149   return -1;
150 }
151
152
153 /*
154  * following implementations : init, deinit, start_tracking, stop_track and get_value
155  * specifically fit into the pmu interface
156  */
157
158 static uint64_t intel_get_value(v3_pmon_ctr_t ctr) {
159   /*
160    * local variables
161    */
162   int ctr_idx;
163   struct msr count;
164
165   count.q = 0x0;
166
167   switch(ctr) {
168   case V3_PMON_CLOCK_COUNT:
169     INTEL_FIXED_CTR_READ(count, INTEL_IDX_CLK_IN_FPMU);
170     break;
171   case V3_PMON_RETIRED_INST_COUNT:
172     INTEL_FIXED_CTR_READ(count, INTEL_IDX_INST_IN_FPMU);
173     break;
174   case V3_PMON_MEM_LOAD_COUNT:
175     if((ctr_idx = intel_find_idx(INTEL_MEM_INST_RETIRED, INTEL_LOADS)) >= 0) {
176       INTEL_CTR_READ(count, ctr_idx);
177     } else {
178       goto INTEL_READ_FAILED;
179     }
180     break;
181   case V3_PMON_MEM_STORE_COUNT:
182     if((ctr_idx = intel_find_idx(INTEL_MEM_INST_RETIRED, INTEL_STORES)) >= 0) {
183       INTEL_CTR_READ(count, ctr_idx);
184     } else {
185       goto INTEL_READ_FAILED;
186     }
187     break;
188   case V3_PMON_CACHE_MISS_COUNT:
189     if((ctr_idx = intel_find_idx(INTEL_MEM_LOAD_RETIRED, INTEL_L3_MISS)) >= 0) {
190       INTEL_CTR_READ(count, ctr_idx);
191     } else {
192       goto INTEL_READ_FAILED;
193     }
194     break;
195   case V3_PMON_TLB_MISS_COUNT:
196     if((ctr_idx = intel_find_idx(INTEL_MEM_LOAD_RETIRED, INTEL_DTLB_MISS)) >= 0) {
197       INTEL_CTR_READ(count, ctr_idx);
198     } else {
199       goto INTEL_READ_FAILED;
200     }
201     break;
202   }
203
204   return (uint64_t)count.q;
205
206  INTEL_READ_FAILED:
207   return 0;
208 }
209
210
211 static int intel_start_tracking(v3_pmon_ctr_t ctr) {
212   /*
213    * local variables
214    */
215   int ctr_idx;
216   struct msr msrs;
217
218   /*
219    * check if available slot in PMU, except for fixed counters (Intel specific)
220    */
221
222   switch(ctr) {
223   case V3_PMON_CLOCK_COUNT:
224     INTEL_FIXED_CTRL_READ(msrs);
225     msrs.l |= 0x3<<8;
226     INTEL_FIXED_CTRL_WRITE(msrs);
227     break;
228   case V3_PMON_RETIRED_INST_COUNT:
229     INTEL_FIXED_CTRL_READ(msrs);
230     msrs.l |= 0x3;
231     INTEL_FIXED_CTRL_WRITE(msrs);
232     break;
233   case V3_PMON_MEM_LOAD_COUNT:
234     if((ctr_idx = intel_get_slot()) >= 0) {
235       INTEL_CTRL_START(INTEL_MEM_INST_RETIRED, INTEL_LOADS, ctr_idx);
236     } else {
237       goto INTEL_START_FAILED;
238     }
239     break;
240   case V3_PMON_MEM_STORE_COUNT:
241     if((ctr_idx = intel_get_slot()) >= 0) {
242       INTEL_CTRL_START(INTEL_MEM_INST_RETIRED, INTEL_STORES, ctr_idx);
243     } else {
244       goto INTEL_START_FAILED;
245     }
246     break;
247   case V3_PMON_CACHE_MISS_COUNT:
248     if((ctr_idx = intel_get_slot()) >= 0) {
249       INTEL_CTRL_START(INTEL_MEM_LOAD_RETIRED, INTEL_L3_MISS, ctr_idx);
250     } else {
251       goto INTEL_START_FAILED;
252     }
253     break;
254   case V3_PMON_TLB_MISS_COUNT:
255     if((ctr_idx = intel_get_slot()) >= 0) {
256       INTEL_CTRL_START(INTEL_MEM_LOAD_RETIRED, INTEL_DTLB_MISS, ctr_idx);
257     } else {
258       goto INTEL_START_FAILED;
259     }
260     break;
261   }
262
263   return 0;
264
265  INTEL_START_FAILED:
266   ERROR("ERROR: no more slot remains for pmon events\n");
267   return -1;
268 }
269
270 /*
271  * descript: disabling pmu event counts
272  */
273
274 static int intel_stop_tracking(v3_pmon_ctr_t ctr) {
275   /*
276    * local variables
277    */
278   int ctr_idx = -1;
279   struct msr msrs;
280
281   /*
282    * check if available slot in PMU, except
283    */
284
285   switch(ctr) {
286   case V3_PMON_CLOCK_COUNT:
287     INTEL_FIXED_CTRL_READ(msrs);
288     msrs.l &= ~(0xf<<8);
289     INTEL_FIXED_CTRL_WRITE(msrs);
290     break;
291   case V3_PMON_RETIRED_INST_COUNT:
292     INTEL_FIXED_CTRL_READ(msrs);
293     msrs.l &= ~(0xf);
294     INTEL_FIXED_CTRL_WRITE(msrs);
295     break;
296   case V3_PMON_MEM_LOAD_COUNT:
297     if((ctr_idx = intel_find_idx(INTEL_MEM_INST_RETIRED, INTEL_LOADS)) >= 0) {
298       INTEL_CTRL_STOP(ctr_idx);
299     } else {
300       goto INTEL_STOP_FAILED;
301     }
302     break;
303   case V3_PMON_MEM_STORE_COUNT:
304     if((ctr_idx = intel_find_idx(INTEL_MEM_INST_RETIRED, INTEL_STORES)) >= 0) {
305       INTEL_CTRL_STOP(ctr_idx);
306     } else {
307       goto INTEL_STOP_FAILED;
308     }
309     break;
310   case V3_PMON_CACHE_MISS_COUNT:
311     if((ctr_idx = intel_find_idx(INTEL_MEM_LOAD_RETIRED, INTEL_L3_MISS)) >= 0) {
312       INTEL_CTRL_STOP(ctr_idx);
313     } else {
314       goto INTEL_STOP_FAILED;
315     }
316     break;
317   case V3_PMON_TLB_MISS_COUNT:
318     if((ctr_idx = intel_find_idx(INTEL_MEM_LOAD_RETIRED, INTEL_DTLB_MISS)) >= 0) {
319       INTEL_CTRL_STOP(ctr_idx);
320     } else {
321       goto INTEL_STOP_FAILED;
322     }
323     break;
324   }
325
326   return 0;
327
328  INTEL_STOP_FAILED:
329   ERROR("ERROR: no more slot remains for pmon events\n");
330   return -1;
331 }
332
333 static void intel_pmu_init(void) {
334   int i;
335   struct msr control;
336
337   if ((get_cpu_var(pmu_refcount)++) > 1) {
338     put_cpu_var(pmu_refcount);
339     // only the first init clears the pmu
340     return;
341   }
342   put_cpu_var(pmu_refcount);
343     
344
345   control.q=0x0;
346
347   /*
348    * per Intel PMU architecture,
349    * there are two class of counters
350    * fixed ones (3 counters) and programmable ones (4 counters)
351    * events for fixed coutners are determined, so enabling or not is the option
352    * whereas, programmable ones are litterally programmable.
353    */
354
355   /*
356    * enable fixed counters in global
357    */
358   MSR_READ(control, INTEL_IA32_PERF_GLOBAL_CTRL);
359   control.q |= 0x70000000f; // enable fix counters (3 for the intel model)
360   MSR_WRITE(control, INTEL_IA32_PERF_GLOBAL_CTRL);
361
362   /*
363    * disable in fixed counters control
364    */
365
366   INTEL_FIXED_CTRL_WRITE(control);
367
368   /*
369    * clean up programmable counter control
370    */
371   for (i=0; i<INTEL_NUM_PMU_CONTROLS; i++) {
372     INTEL_CTRL_WRITE(control, i);
373   }
374 }
375
376 static void intel_pmu_deinit(void) {
377   if ((get_cpu_var(pmu_refcount)--)==0) {
378     put_cpu_var(pmu_refcount);
379     // actually deinit
380   }
381   put_cpu_var(pmu_refcount);
382 }
383
384
385
386
387
388 static int amd_find_idx(uint8_t event, uint8_t mask) {
389   int i;
390
391   struct msr control;
392
393   control.q = 0x0;
394
395   for (i=0; i<AMD_NUM_PMU_COUNTERS; i++) {
396     AMD_CTRL_READ(control, i);
397     if((((control.l>>AMD_EVENT_BIT) & 0xff) == event) &&
398        (((control.l>>AMD_UMASK_BIT) & 0xff) == mask)) {
399       return i;
400     }
401   }
402
403   return -1;
404 }
405
406
407 static uint64_t amd_get_value(v3_pmon_ctr_t ctr) {
408   int ctr_idx;
409   struct msr count;
410
411   count.q = 0x0;
412
413   switch(ctr) {
414   case V3_PMON_CLOCK_COUNT:
415     if((ctr_idx = amd_find_idx(AMD_CLK_NOT_HALTED, 0x0)) >= 0) {
416       AMD_CTR_READ(count, ctr_idx);
417     } else {
418       goto AMD_READ_FAILED;
419     }
420     break;
421   case V3_PMON_RETIRED_INST_COUNT:
422     if((ctr_idx = amd_find_idx(AMD_RETIRED_INSTRUCTIONS, 0x0)) >= 0) {
423       AMD_CTR_READ(count, ctr_idx);
424     } else {
425       goto AMD_READ_FAILED;
426     }
427     break;
428   case V3_PMON_MEM_LOAD_COUNT:
429     if((ctr_idx = amd_find_idx(AMD_PREFETCH_INST_DISPATCHED, AMD_LOAD)) >= 0) {
430       AMD_CTR_READ(count, ctr_idx);
431     } else {
432       goto AMD_READ_FAILED;
433     }
434     break;
435   case V3_PMON_MEM_STORE_COUNT:
436     if((ctr_idx = amd_find_idx(AMD_PREFETCH_INST_DISPATCHED, AMD_STORE)) >= 0) {
437       AMD_CTR_READ(count, ctr_idx);
438     } else {
439       goto AMD_READ_FAILED;
440     }
441     break;
442   case V3_PMON_CACHE_MISS_COUNT:
443     if((ctr_idx = amd_find_idx(AMD_DATA_CACHE_MISSES, 0x0)) >= 0) {
444       AMD_CTR_READ(count, ctr_idx);
445     } else {
446       goto AMD_READ_FAILED;
447     }
448     break;
449   case V3_PMON_TLB_MISS_COUNT:
450     if((ctr_idx = amd_find_idx(AMD_L1_DTLB_AND_L2_DTLB_MISS, 0x0)) >= 0) {
451       AMD_CTR_READ(count, ctr_idx);
452     } else {
453       goto AMD_READ_FAILED;
454     }
455     break;
456   }
457
458   return (uint64_t)count.q;
459
460  AMD_READ_FAILED:
461   return 0;
462 }
463
464 static int amd_start_tracking(v3_pmon_ctr_t ctr) {
465
466   int ctr_idx;
467
468   switch(ctr) {
469   case V3_PMON_CLOCK_COUNT:
470     if((ctr_idx = amd_get_slot()) >= 0) {
471       AMD_CTRL_START(AMD_CLK_NOT_HALTED, 0x0, ctr_idx);
472     } else {
473       goto AMD_START_FAILED;
474     }
475     break;
476   case V3_PMON_RETIRED_INST_COUNT:
477     if((ctr_idx = amd_get_slot()) >= 0) {
478       AMD_CTRL_START(AMD_RETIRED_INSTRUCTIONS, 0x0, ctr_idx);
479     } else {
480       goto AMD_START_FAILED;
481     }
482     break;
483   case V3_PMON_MEM_LOAD_COUNT:
484     if((ctr_idx = amd_get_slot()) >= 0) {
485       AMD_CTRL_START(AMD_PREFETCH_INST_DISPATCHED, AMD_LOAD, ctr_idx);
486     } else {
487       goto AMD_START_FAILED;
488     }
489     break;
490   case V3_PMON_MEM_STORE_COUNT:
491     if((ctr_idx = amd_get_slot()) >= 0) {
492       AMD_CTRL_START(AMD_PREFETCH_INST_DISPATCHED, AMD_STORE, ctr_idx);
493     } else {
494       goto AMD_START_FAILED;
495     }
496     break;
497   case V3_PMON_CACHE_MISS_COUNT:
498     if((ctr_idx = amd_get_slot()) >= 0) {
499       AMD_CTRL_START(AMD_DATA_CACHE_MISSES, 0x0, ctr_idx);
500     } else {
501       goto AMD_START_FAILED;
502     }
503     break;
504   case V3_PMON_TLB_MISS_COUNT:
505     if((ctr_idx = amd_get_slot()) >= 0) {
506       AMD_CTRL_START(AMD_L1_DTLB_AND_L2_DTLB_MISS, 0x0, ctr_idx);
507     } else {
508       goto AMD_START_FAILED;
509     }
510     break;
511   }
512
513   return 0;
514
515  AMD_START_FAILED:
516   ERROR("ERROR: no more slot remains for pmon events\n");
517   return -1;
518 }
519
520
521 static int amd_stop_tracking(v3_pmon_ctr_t ctr) {
522
523   int ctr_idx = -1;
524
525
526   switch(ctr) {
527   case V3_PMON_CLOCK_COUNT:
528     if((ctr_idx = amd_find_idx(AMD_CLK_NOT_HALTED, 0x0)) >= 0) {
529       AMD_CTRL_STOP(ctr_idx);
530     } else {
531       goto AMD_STOP_FAILED;
532     }
533     break;
534   case V3_PMON_RETIRED_INST_COUNT:
535     if((ctr_idx = amd_find_idx(AMD_RETIRED_INSTRUCTIONS, 0x0)) >= 0) {
536       AMD_CTRL_STOP(ctr_idx);
537     } else {
538       goto AMD_STOP_FAILED;
539     }
540     break;
541   case V3_PMON_MEM_LOAD_COUNT:
542     if((ctr_idx = amd_find_idx(AMD_PREFETCH_INST_DISPATCHED, AMD_LOAD)) >= 0) {
543       AMD_CTRL_STOP(ctr_idx);
544     } else {
545       goto AMD_STOP_FAILED;
546     }
547     break;
548   case V3_PMON_MEM_STORE_COUNT:
549     if((ctr_idx = amd_find_idx(AMD_PREFETCH_INST_DISPATCHED, AMD_STORE)) >= 0) {
550       AMD_CTRL_STOP(ctr_idx);
551     } else {
552       goto AMD_STOP_FAILED;
553     }
554     break;
555   case V3_PMON_CACHE_MISS_COUNT:
556     if((ctr_idx = amd_find_idx(AMD_DATA_CACHE_MISSES, 0x0)) >= 0) {
557       AMD_CTRL_STOP(ctr_idx);
558     } else {
559       goto AMD_STOP_FAILED;
560     }
561     break;
562   case V3_PMON_TLB_MISS_COUNT:
563     if((ctr_idx = amd_find_idx(AMD_L1_DTLB_AND_L2_DTLB_MISS, 0x0)) >= 0) {
564       AMD_CTRL_STOP(ctr_idx);
565     } else {
566       goto AMD_STOP_FAILED;
567     }
568     break;
569   }
570
571   return 0;
572
573  AMD_STOP_FAILED:
574   ERROR("ERROR: no more slot remains for pmon events\n");
575   return -1;
576 }
577
578
579 static void amd_pmu_init(void) {
580
581   int i;
582   struct msr control;
583
584   
585   if ((get_cpu_var(pmu_refcount)++) > 1) {
586     put_cpu_var(pmu_refcount);
587     // only the first init clears the pmu
588     return;
589   }
590   put_cpu_var(pmu_refcount);
591   
592   
593
594   // initialize variables
595   control.q=0x0;
596
597   /*
598    * clean up programmable counter control
599    */
600   for (i=0; i<AMD_NUM_PMU_CONTROLS; i++) {
601     AMD_CTRL_WRITE(control, i);
602   }
603 }
604
605 static void amd_pmu_deinit(void) {
606   if ((get_cpu_var(pmu_refcount)--)==0) {
607     put_cpu_var(pmu_refcount);
608     // actually deinit
609   }
610   put_cpu_var(pmu_refcount);
611 }
612
613
614 static struct v3_pmu_iface palacios_pmu_intel = {
615   .init = intel_pmu_init,
616   .deinit = intel_pmu_deinit,
617   .start_tracking = intel_start_tracking,
618   .stop_tracking = intel_stop_tracking,
619   .get_value = intel_get_value
620 };
621
622 static struct v3_pmu_iface palacios_pmu_amd = {
623   .init = amd_pmu_init,
624   .deinit = amd_pmu_deinit,
625   .start_tracking = amd_start_tracking,
626   .stop_tracking = amd_stop_tracking,
627   .get_value = amd_get_value
628 };
629
630 static int pmu_init( void ) {
631   if (is_intel()) {
632     INFO("Intel PMU featureset detected\n");
633     V3_Init_PMU(&palacios_pmu_intel);
634   } else if (is_amd()) { 
635     INFO("AMD PMU featureset detected\n");
636     V3_Init_PMU(&palacios_pmu_amd);
637   } else {
638     ERROR("This is neither an Intel nor AMD machine - No PMU functionality configured\n");
639     return -1;
640   }
641
642   return 0;
643 }
644
645
646 static struct linux_ext pmu_ext = {
647   .name = "PMU",
648   .init = pmu_init,
649   .deinit = NULL,
650   .guest_init = NULL,
651   .guest_deinit = NULL
652 };
653
654 register_extension(&pmu_ext);
655
656
657
658