1 /*
2 * Code to configure and utilize the ppc64 pmc hardware. Generally, the
3 * following functions are supported:
4 * 1. Kernel profile based on decrementer ticks
5 * 2. Kernel profile based on PMC execution cycles
6 * 3. Trace based on arbitrary PMC counter
7 * 4. Timeslice data capture of arbitrary collections of PMCs
8 *
9 * Copyright (C) 2002 David Engebretsen <engebret@us.ibm.com>
10 */
11
12 #include <asm/proc_fs.h>
13 #include <asm/paca.h>
14 #include <asm/iSeries/ItLpPaca.h>
15 #include <asm/iSeries/ItLpQueue.h>
16 #include <asm/processor.h>
17 #include <linux/proc_fs.h>
18 #include <linux/spinlock.h>
19 #include <asm/pmc.h>
20 #include <asm/uaccess.h>
21 #include <asm/naca.h>
22 #include <asm/perfmon.h>
23 #include <asm/iSeries/HvCall.h>
24 #include <asm/hvcall.h>
25 #include <asm/cputable.h>
26
27 extern char _stext[], _etext[], _end[];
28 struct perfmon_base_struct perfmon_base = {0, 0, 0, 0, 0, 0, 0, PMC_STATE_INITIAL};
29
30 int alloc_perf_buffer(int size);
31 int free_perf_buffer(void);
32 int clear_buffers(void);
33 void pmc_stop(void *data);
34 void pmc_clear(void *data);
35 void pmc_start(void *data);
36 void pmc_touch_bolted(void *data);
37 void dump_pmc_struct(struct perfmon_struct *perfdata);
38 void dump_hardware_pmc_struct(void *perfdata);
39 int decr_profile(void *data);
40 int pmc_profile(struct perfmon_struct *perfdata);
41 int pmc_set_general(struct perfmon_struct *perfdata);
42 int pmc_set_user_general(struct perfmon_struct *perfdata);
43 void pmc_configure(void *data);
44 int pmc_timeslice_enable(struct perfmon_struct *perfdata);
45 int pmc_timeslice_disable(struct perfmon_struct *perfdata);
46 int pmc_timeslice_set(struct perfmon_struct *perfdata);
47 void pmc_dump_timeslice(struct perfmon_struct *perfdata);
48 void pmc_trace_rec_type(unsigned long type);
49 void pmc_timeslice_data_collect(void *data);
50 int pmc_timeslice_tick(void);
51 int perfmon_buffer_ctl(void *data);
52 int perfmon_dump_ctl(void *data);
53 int perfmon_profile_ctl(void *data);
54 int perfmon_trace_ctl(void *data);
55 int perfmon_timeslice_ctl(void *data);
56
57 #define PMC_MAX_CPUS 48
58 #define PMC_MAX_COUNTERS 8
59 #define PMC_SLICES_STAR 64
60 #define PMC_SLICES_GP 28
61 #define PMC_SLICES_MAX 64
62 #define PMC_TICK_FACTOR 10
63
64 int pmc_timeslice_enabled = 0, pmc_timeslice_top = 0;
65 int pmc_tick_count[PMC_MAX_CPUS], pmc_timeslice[PMC_MAX_CPUS];
66
67 unsigned long pmc_timeslice_data[PMC_SLICES_MAX*PMC_MAX_COUNTERS*PMC_MAX_CPUS];
68
69 /*
70 * DRENG: todo:
71 * add api to add config entries (entry, values), and bump pmc_timeslice_top
72 * value
73 * add api to get data from kernel (entry, values)
74 */
75 unsigned long pmc_timeslice_config[PMC_SLICES_MAX * 5];
76
77 static spinlock_t pmc_lock = SPIN_LOCK_UNLOCKED;
78 asmlinkage int
sys_perfmonctl(int cmd,void * data)79 sys_perfmonctl (int cmd, void *data)
80 {
81 struct perfmon_struct *pdata;
82 int err;
83
84 printk("sys_perfmonctl: cmd = 0x%x\n", cmd);
85 pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER);
86 err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct));
87 switch(cmd) {
88 case PMC_CMD_BUFFER:
89 perfmon_buffer_ctl(data);
90 break;
91 case PMC_CMD_DUMP:
92 perfmon_dump_ctl(data);
93 break;
94 case PMC_CMD_DECR_PROFILE: /* NIA time sampling */
95 decr_profile(data);
96 break;
97 case PMC_CMD_PROFILE:
98 perfmon_profile_ctl(pdata);
99 break;
100 case PMC_CMD_TRACE:
101 perfmon_trace_ctl(pdata);
102 break;
103 case PMC_CMD_TIMESLICE:
104 perfmon_timeslice_ctl(pdata);
105 break;
106 #if 0
107 case PMC_OP_TIMESLICE:
108 pmc_enable_timeslice(pdata);
109 break;
110 case PMC_OP_DUMP_TIMESLICE:
111 pmc_dump_timeslice(pdata);
112 smp_call_function(pmc_dump_timeslice, (void *)pdata, 0, 1);
113 break;
114 #endif
115 default:
116 printk("Perfmon: Unknown command\n");
117 break;
118 }
119
120 kfree(pdata);
121 return 0;
122 }
123
perfmon_buffer_ctl(void * data)124 int perfmon_buffer_ctl(void *data) {
125 struct perfmon_struct *pdata;
126 int err;
127
128 pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER);
129 err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct));
130
131 switch(pdata->header.subcmd) {
132 case PMC_SUBCMD_BUFFER_ALLOC:
133 alloc_perf_buffer(0);
134 break;
135 case PMC_SUBCMD_BUFFER_FREE:
136 free_perf_buffer();
137 break;
138 case PMC_SUBCMD_BUFFER_CLEAR:
139 clear_buffers();
140 break;
141 default:
142 return(-1);
143 }
144
145 return(0);
146 }
147
alloc_perf_buffer(int size)148 int alloc_perf_buffer(int size)
149 {
150 int i;
151
152 printk("Perfmon: allocate buffer\n");
153 if(perfmon_base.state == PMC_STATE_INITIAL) {
154 perfmon_base.profile_length = (((unsigned long) &_etext -
155 (unsigned long) &_stext) >> 2) * sizeof(int);
156 perfmon_base.profile_buffer = (unsigned long)btmalloc(perfmon_base.profile_length);
157 perfmon_base.trace_length = 1024*1024*16;
158 perfmon_base.trace_buffer = (unsigned long)btmalloc(perfmon_base.trace_length);
159
160 perfmon_base.timeslice_length = PMC_SLICES_MAX*PMC_MAX_COUNTERS*PMC_MAX_CPUS;
161 perfmon_base.timeslice_buffer = (unsigned long)pmc_timeslice_data;
162
163 if(perfmon_base.profile_buffer && perfmon_base.trace_buffer) {
164 memset((char *)perfmon_base.profile_buffer, 0, perfmon_base.profile_length);
165 printk("Profile buffer created at address 0x%lx of length 0x%lx\n",
166 perfmon_base.profile_buffer, perfmon_base.profile_length);
167 } else {
168 printk("Profile buffer creation failed\n");
169 return 0;
170 }
171
172 /* Fault in the first bolted segment - it then remains in the stab for all time */
173 pmc_touch_bolted(NULL);
174 smp_call_function(pmc_touch_bolted, (void *)NULL, 0, 1);
175
176 for (i=0; i<MAX_PACAS; ++i) {
177 paca[i].prof_shift = 2;
178 paca[i].prof_len = perfmon_base.profile_length;
179 paca[i].prof_buffer = (unsigned *)(perfmon_base.profile_buffer);
180 paca[i].prof_stext = (unsigned *)&_stext;
181
182 paca[i].prof_etext = (unsigned *)&_etext;
183 mb();
184 }
185
186 perfmon_base.state = PMC_STATE_READY;
187 }
188
189 return 0;
190 }
191
free_perf_buffer()192 int free_perf_buffer()
193 {
194 printk("Perfmon: free buffer\n");
195
196 if(perfmon_base.state == PMC_STATE_INITIAL) {
197 printk("Perfmon: free buffer failed - no buffer was allocated.\n");
198 return -1;
199 }
200
201 btfree((void *)perfmon_base.profile_buffer);
202 btfree((void *)perfmon_base.trace_buffer);
203
204 perfmon_base.profile_length = 0;
205 perfmon_base.profile_buffer = 0;
206 perfmon_base.trace_buffer = 0;
207 perfmon_base.trace_length = 0;
208 perfmon_base.trace_end = 0;
209 perfmon_base.state = PMC_STATE_INITIAL;
210
211 return(0);
212 }
213
clear_buffers()214 int clear_buffers()
215 {
216 int i, j;
217
218 if(perfmon_base.state == PMC_STATE_INITIAL) {
219 printk("Perfmon: clear buffer failed - no buffer was allocated.\n");
220 return -1;
221 }
222
223 printk("Perfmon: clear buffer\n");
224
225 /* Stop counters on all [PMC_MAX_CPUS]processors -- blocking */
226 pmc_stop(NULL);
227 smp_call_function(pmc_stop, (void *)NULL, 0, 1);
228
229 /* Clear the buffers */
230 memset((char *)perfmon_base.profile_buffer, 0, perfmon_base.profile_length);
231 memset((char *)perfmon_base.trace_buffer, 0, perfmon_base.trace_length);
232 memset((char *)perfmon_base.timeslice_buffer, 0, perfmon_base.timeslice_length);
233
234 /* Reset the trace buffer point */
235 perfmon_base.trace_end = 0;
236
237 for (i=0; i<MAX_PACAS; ++i) {
238 for(j=0; j<8; j++) {
239 paca[i].pmcc[j] = 0;
240 }
241 }
242 #if 0
243 /* Reset the timeslice data */
244 for(i=0; i<(PMC_MAX_CPUS*PMC_MAX_COUNTERS*PMC_SLICES_MAX); i++) {
245 pmc_timeslice_data[i] = 0;
246 }
247 #endif
248 /* Restart counters on all processors -- blocking */
249 pmc_start(NULL);
250 smp_call_function(pmc_start, (void *)NULL, 0, 1);
251
252 return(0);
253 }
254
pmc_stop(void * data)255 void pmc_stop(void *data)
256 {
257 /* Freeze all counters, leave everything else alone */
258 mtspr(MMCR0, mfspr( MMCR0 ) | 0x80000000);
259 }
260
pmc_clear(void * data)261 void pmc_clear(void *data)
262 {
263 mtspr(PMC1, 0); mtspr(PMC2, 0);
264 mtspr(PMC3, 0); mtspr(PMC4, 0);
265 mtspr(PMC5, 0); mtspr(PMC6, 0);
266 mtspr(PMC7, 0); mtspr(PMC8, 0);
267 mtspr(MMCR0, 0); mtspr(MMCR1, 0); mtspr(MMCRA, 0);
268 }
269
pmc_start(void * data)270 void pmc_start(void *data)
271 {
272 /* Free all counters, leave everything else alone */
273 mtspr(MMCR0, mfspr( MMCR0 ) & 0x7fffffff);
274 }
275
pmc_touch_bolted(void * data)276 void pmc_touch_bolted(void *data)
277 {
278 volatile int touch;
279
280 /* Hack to fault the buffer into the segment table */
281 touch = *((int *)(perfmon_base.profile_buffer));
282 }
283
perfmon_dump_ctl(void * data)284 int perfmon_dump_ctl(void *data) {
285 struct perfmon_struct *pdata;
286 int err;
287
288 pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER);
289 err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct));
290
291 switch(pdata->header.subcmd) {
292 case PMC_SUBCMD_DUMP_COUNTERS:
293 dump_pmc_struct(pdata);
294 // copy_to_user(data, pdata, sizeof(struct perfmon_struct));
295 break;
296 case PMC_SUBCMD_DUMP_HARDWARE:
297 dump_hardware_pmc_struct(pdata);
298 smp_call_function(dump_hardware_pmc_struct, (void *)pdata, 0, 1);
299 break;
300 }
301
302 return(0);
303 }
304
dump_pmc_struct(struct perfmon_struct * perfdata)305 void dump_pmc_struct(struct perfmon_struct *perfdata)
306 {
307 unsigned int cpu = perfdata->vdata.pmc_info.cpu, i;
308
309 if(cpu > MAX_PACAS) return;
310
311 printk("PMC Control Mode: 0x%lx\n", perfmon_base.state);
312 printk("PMC[1 - 2] = 0x%16.16lx 0x%16.16lx\n",
313 paca[cpu].pmcc[0], paca[cpu].pmcc[1]);
314 printk("PMC[3 - 4] = 0x%16.16lx 0x%16.16lx\n",
315 paca[cpu].pmcc[2], paca[cpu].pmcc[3]);
316 printk("PMC[5 - 6] = 0x%16.16lx 0x%16.16lx\n",
317 paca[cpu].pmcc[4], paca[cpu].pmcc[5]);
318 printk("PMC[7 - 8] = 0x%16.16lx 0x%16.16lx\n",
319 paca[cpu].pmcc[6], paca[cpu].pmcc[7]);
320
321 perfdata->vdata.pmc_info.mode = perfmon_base.state;
322 for(i = 0; i < 11; i++)
323 perfdata->vdata.pmc_info.pmc_base[i] = paca[cpu].pmc[i];
324
325 for(i = 0; i < 8; i++)
326 perfdata->vdata.pmc_info.pmc_cumulative[i] = paca[cpu].pmcc[i];
327 }
328
dump_hardware_pmc_struct(void * perfdata)329 void dump_hardware_pmc_struct(void *perfdata)
330 {
331 unsigned int cpu = smp_processor_id();
332
333 printk("PMC[%2.2d][1 - 4] = 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
334 cpu, (u32) mfspr(PMC1),(u32) mfspr(PMC2),(u32) mfspr(PMC3),(u32) mfspr(PMC4));
335 printk("PMC[%2.2d][5 - 8] = 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
336 cpu, (u32) mfspr(PMC5),(u32) mfspr(PMC6),(u32) mfspr(PMC7),(u32) mfspr(PMC8));
337 printk("MMCR[%2.2d][0,1,A] = 0x%8.8x 0x%8.8x 0x%8.8x\n",
338 cpu, (u32) mfspr(MMCR0),(u32) mfspr(MMCR1),(u32) mfspr(MMCRA));
339 }
340
decr_profile(void * data)341 int decr_profile(void *data)
342 {
343 int i;
344
345 printk("Perfmon: NIA decrementer profile\n");
346
347 if(perfmon_base.state == PMC_STATE_INITIAL) {
348 printk("Perfmon: failed - no buffer was allocated.\n");
349 return -1;
350 }
351
352 /* Stop counters on all processors -- blocking */
353 pmc_stop(NULL);
354 smp_call_function(pmc_stop, (void *)NULL, 0, 1);
355
356 for (i=0; i<MAX_PACAS; ++i) {
357 paca[i].prof_mode = PMC_STATE_DECR_PROFILE;
358 }
359
360 perfmon_base.state = PMC_STATE_DECR_PROFILE;
361 mb();
362
363 return 0;
364 }
365
perfmon_profile_ctl(void * data)366 int perfmon_profile_ctl(void *data) {
367 struct perfmon_struct *pdata;
368 int err;
369
370 pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER);
371 err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct));
372
373 switch(pdata->header.subcmd) {
374 case PMC_SUBCMD_PROFILE_CYCLE:
375 pmc_profile(pdata);
376 break;
377 default:
378 return(-1);
379 }
380
381 return(0);
382 }
383
perfmon_trace_ctl(void * data)384 int perfmon_trace_ctl(void *data) {
385 struct perfmon_struct *pdata;
386 int err;
387
388 pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER);
389 err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct));
390
391 pmc_set_general(pdata);
392
393 #if 0
394 /* Reimplement at some point ... */
395 pmc_set_user_general(pdata);
396 #endif
397 return(0);
398 }
399
perfmon_timeslice_ctl(void * data)400 int perfmon_timeslice_ctl(void *data) {
401 struct perfmon_struct *pdata;
402 int err;
403
404 pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER);
405 err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct));
406
407 switch(pdata->header.subcmd) {
408 case PMC_SUBCMD_TIMESLICE_ENABLE:
409 pmc_timeslice_enable(pdata);
410 break;
411 case PMC_SUBCMD_TIMESLICE_DISABLE:
412 pmc_timeslice_disable(pdata);
413 break;
414 case PMC_SUBCMD_TIMESLICE_SET:
415 pmc_timeslice_set(pdata);
416 break;
417 default:
418 return(-1);
419 }
420
421 return(0);
422 }
423
plpar_perfmon(int mode)424 static long plpar_perfmon(int mode)
425 {
426 return plpar_hcall_norets(H_PERFMON, mode, 0);
427 }
428
pmc_configure_hardware()429 static void pmc_configure_hardware() {
430 /*
431 * Debug bus enabled is required on GP for timeslice mode.
432 * Flood enabled is required on GP for PMC cycle profile mode
433 * iSeries SP sets this by default. pSeries requires the OS to enable.
434 */
435 if (cur_cpu_spec->cpu_features & CPU_FTR_SLB) {
436 /* Set up the debug bus to pmc mode - a feature of GP */
437 switch(systemcfg->platform) {
438 case PLATFORM_ISERIES_LPAR:
439 HvCall_setDebugBus(1);
440 break;
441 case PLATFORM_PSERIES_LPAR:
442 plpar_perfmon(1);
443 break;
444 case PLATFORM_PSERIES:
445 mtspr(HID0, mfspr(HID0) | 0x0000080000000000);
446 }
447 }
448 }
449
450 /*
451 * pmc_profile
452 *
453 * Profile the kernel based on cycle counts. This is made a special case of the more
454 * general trace functions because it is high use. Benefits of special casing this
455 * include a buffer of sufficient size to profile the entire kernel is available, and CPI
456 * can be collected along with the profile.
457 */
pmc_profile(struct perfmon_struct * perfdata)458 int pmc_profile(struct perfmon_struct *perfdata)
459 {
460 struct pmc_struct *pdata = &(perfdata->vdata.pmc);
461 int i;
462
463 printk("Perfmon: NIA PMC profile\n");
464
465 pmc_timeslice_disable(NULL); // fixme
466
467 if(perfmon_base.state == PMC_STATE_INITIAL) {
468 printk("Perfmon: failed - no buffer was allocated.\n");
469 return -1;
470 }
471
472 /* Stop counters on all processors -- blocking */
473 pmc_stop(NULL);
474 smp_call_function(pmc_stop, (void *)NULL, 0, 1);
475
476 for (i=0; i<MAX_PACAS; ++i) {
477 paca[i].prof_mode = PMC_STATE_PROFILE_KERN;
478 }
479 perfmon_base.state = PMC_STATE_PROFILE_KERN;
480
481 if (cur_cpu_spec->cpu_features & CPU_FTR_SLB) {
482 for(i = 0; i < 8; i++)
483 pdata->pmc[i] = 0x0;
484 pdata->pmc[1] = 0x7f000000;
485 /* Freeze in problem state, exception enable, freeze on event, */
486 /* enable counter negative condition, PMC1, PMC2 */
487 pdata->pmc[8] = 0x2600cd0e;
488 pdata->pmc[9] = 0x4a5675ac;
489
490 /* Freeze while in wait state */
491 pdata->pmc[10] = 0x00022002;
492 } else {
493 pdata->pmc[0] = 0x7f000000;
494 for(i = 1; i < 8; i++)
495 pdata->pmc[i] = 0x0;
496 pdata->pmc[8] = 0x26000000 | (0x01 << (31 - 25) | (0x1));
497 pdata->pmc[9] = (0x3 << (31-4)); /* Instr completed */
498
499 /* Freeze while in wait state */
500 pdata->pmc[10] = 0x00000000 | (0x1 << (31 - 30));
501 }
502
503 pmc_configure_hardware();
504
505 mb();
506
507 pmc_trace_rec_type(perfdata->header.vdata.type);
508 pmc_configure((void *)perfdata);
509 smp_call_function(pmc_configure, (void *)perfdata, 0, 0);
510
511 return 0;
512 }
513
pmc_set_general(struct perfmon_struct * perfdata)514 int pmc_set_general(struct perfmon_struct *perfdata)
515 {
516 int i;
517
518 printk("Perfmon: PMC sampling - General\n");
519
520 if(perfmon_base.state == PMC_STATE_INITIAL) {
521 printk("Perfmon: failed - no buffer was allocated.\n");
522 return -1;
523 }
524
525 /* Stop counters on all processors -- blocking */
526 pmc_stop(NULL);
527 smp_call_function(pmc_stop, (void *)NULL, 0, 1);
528
529 for (i=0; i<MAX_PACAS; ++i) {
530 paca[i].prof_mode = PMC_STATE_TRACE_KERN;
531 }
532 perfmon_base.state = PMC_STATE_TRACE_KERN;
533 mb();
534
535 pmc_trace_rec_type(perfdata->header.vdata.type);
536 pmc_configure((void *)perfdata);
537 smp_call_function(pmc_configure, (void *)perfdata, 0, 0);
538
539 return 0;
540 }
541
pmc_set_user_general(struct perfmon_struct * perfdata)542 int pmc_set_user_general(struct perfmon_struct *perfdata)
543 {
544 #if 0
545 struct pmc_struct *pdata = &(perfdata->vdata.pmc);
546 #endif
547 int pid = perfdata->header.vdata.pid;
548 struct task_struct *task;
549 int i;
550
551 printk("Perfmon: PMC sampling - general user\n");
552
553 if(perfmon_base.state == PMC_STATE_INITIAL) {
554 printk("Perfmon: failed - no buffer was allocated.\n");
555 return -1;
556 }
557
558 if(pid) {
559 printk("Perfmon: pid = 0x%x\n", pid);
560 read_lock(&tasklist_lock);
561 task = find_task_by_pid(pid);
562 if (task) {
563 printk("Perfmon: task = 0x%lx\n", (u64) task);
564 task->thread.regs->msr |= 0x4;
565 #if 0
566 for(i = 0; i < 11; i++)
567 task->thread.pmc[i] = pdata->pmc[i];
568 #endif
569 } else {
570 printk("Perfmon: task not found\n");
571 read_unlock(&tasklist_lock);
572 return -1;
573 }
574 }
575 read_unlock(&tasklist_lock);
576
577 /* Stop counters on all processors -- blocking */
578 pmc_stop(NULL);
579 smp_call_function(pmc_stop, (void *)NULL, 0, 1);
580
581 for (i=0; i<MAX_PACAS; ++i) {
582 paca[i].prof_mode = PMC_STATE_TRACE_USER;
583 }
584 perfmon_base.state = PMC_STATE_TRACE_USER;
585 mb();
586
587 pmc_trace_rec_type(perfdata->header.vdata.type);
588 pmc_configure((void *)perfdata);
589 smp_call_function(pmc_configure, (void *)perfdata, 0, 0);
590
591 return 0;
592 }
593
pmc_trace_rec_type(unsigned long type)594 void pmc_trace_rec_type(unsigned long type)
595 {
596 unsigned long cmd_rec;
597
598 cmd_rec = 0xFFUL << 56;
599 cmd_rec |= type;
600 *((unsigned long *)(perfmon_base.trace_buffer +
601 perfmon_base.trace_end)) = cmd_rec;
602 perfmon_base.trace_end += 8;
603 if(perfmon_base.trace_end >= perfmon_base.trace_length)
604 perfmon_base.trace_end = 0;
605 }
606
pmc_configure(void * data)607 void pmc_configure(void *data)
608 {
609 struct paca_struct *lpaca = get_paca();
610 struct perfmon_struct *perfdata = (struct perfmon_struct *)data;
611 struct pmc_struct *pdata = &(perfdata->vdata.pmc);
612 unsigned long i;
613
614 /* Indicate to hypervisor that we are using the PMCs */
615 if(systemcfg->platform == PLATFORM_ISERIES_LPAR)
616 lpaca->xLpPacaPtr->xPMCRegsInUse = 1;
617
618 /* Freeze all counters */
619 mtspr(MMCR0, 0x80000000); mtspr(MMCR1, 0x00000000);
620
621 /* Clear all the PMCs */
622 mtspr(PMC1, 0); mtspr(PMC2, 0); mtspr(PMC3, 0); mtspr(PMC4, 0);
623 mtspr(PMC5, 0); mtspr(PMC6, 0); mtspr(PMC7, 0); mtspr(PMC8, 0);
624
625 /* Set the PMCs to the new values */
626 for(i = 0; i < 11; i++) {
627 lpaca->pmc[i] = pdata->pmc[i];
628 printk("pmc_configure: 0x%lx\n", pdata->pmc[i]);
629 }
630
631 mtspr(PMC1, lpaca->pmc[0]); mtspr(PMC2, lpaca->pmc[1]);
632 mtspr(PMC3, lpaca->pmc[2]); mtspr(PMC4, lpaca->pmc[3]);
633 mtspr(PMC5, lpaca->pmc[4]); mtspr(PMC6, lpaca->pmc[5]);
634 mtspr(PMC7, lpaca->pmc[6]); mtspr(PMC8, lpaca->pmc[7]);
635 mtspr(MMCR1, lpaca->pmc[9]); mtspr(MMCRA, lpaca->pmc[10]);
636
637 mb();
638
639 /* Start all counters - MMCR0 contains the PMC enable control bit */
640 mtspr(MMCR0, lpaca->pmc[8]);
641 }
642
643 /* Increment the timeslice counters for the current timeslice */
pmc_timeslice_data_collect(void * data)644 void pmc_timeslice_data_collect(void *data) {
645 struct paca_struct *lpaca = get_paca();
646 int i, cpu = smp_processor_id();
647 int slice_rec = cpu*PMC_MAX_COUNTERS*PMC_SLICES_MAX +
648 pmc_timeslice[cpu]*PMC_MAX_COUNTERS;
649
650 /* First get any cumulative data that may have accrued */
651 for(i=0; i<8; i++) {
652 pmc_timeslice_data[slice_rec + i] += lpaca->pmcc[i];
653 lpaca->pmcc[i] = 0;
654 lpaca->pmc[i] = 0;
655 }
656
657 /* Next get current hardware values */
658 pmc_timeslice_data[slice_rec + 0] += ((u32)mfspr(PMC1));
659 pmc_timeslice_data[slice_rec + 1] += ((u32)mfspr(PMC2));
660 pmc_timeslice_data[slice_rec + 2] += ((u32)mfspr(PMC3));
661 pmc_timeslice_data[slice_rec + 3] += ((u32)mfspr(PMC4));
662 pmc_timeslice_data[slice_rec + 4] += ((u32)mfspr(PMC5));
663 pmc_timeslice_data[slice_rec + 5] += ((u32)mfspr(PMC6));
664 pmc_timeslice_data[slice_rec + 6] += ((u32)mfspr(PMC7));
665 pmc_timeslice_data[slice_rec + 7] += ((u32)mfspr(PMC8));
666 }
667
668 /* Handle a timeslice tick. Each processor executes independantly */
pmc_timeslice_tick()669 int pmc_timeslice_tick() {
670 int i;
671 struct perfmon_struct perfdata;
672 unsigned int cpu = smp_processor_id();
673
674 /* Switch timeslice every decrementer, reduced by some factor */
675 pmc_tick_count[cpu]++;
676 if(!pmc_timeslice_enabled || pmc_tick_count[cpu] < PMC_TICK_FACTOR)
677 return 0;
678 pmc_tick_count[cpu] = 0;
679
680 /* Stop counters and collect current state */
681 pmc_stop(NULL);
682 pmc_timeslice_data_collect(NULL);
683
684 /* Move on to the next timeslice */
685 pmc_timeslice[cpu]++;
686 if(pmc_timeslice[cpu] >= pmc_timeslice_top) pmc_timeslice[cpu] = 0;
687
688 /* Set up the counters to reflect the new data to collect */
689 for(i = 0; i < 8; i++) {
690 perfdata.vdata.pmc.pmc[i] = 0;
691 }
692 for(i = 0; i < 3; i++) {
693 perfdata.vdata.pmc.pmc[i+8] =
694 pmc_timeslice_config[pmc_timeslice[cpu]*5+i];
695 }
696 mb();
697
698 /* Configure the new counters */
699 pmc_configure((void *)&perfdata);
700
701 return 0;
702 }
703
pmc_timeslice_set(struct perfmon_struct * perfdata)704 int pmc_timeslice_set(struct perfmon_struct *perfdata)
705 {
706 int slice;
707
708 printk("Perfmon: Timeslice set\n");
709
710 slice = perfdata->header.vdata.slice;
711 if(slice >= PMC_SLICES_MAX) {
712 printk("Perfmon: invalid timeslice specified %d\n", slice);
713 return(-1);
714 }
715
716 if(slice > pmc_timeslice_top) pmc_timeslice_top = slice;
717
718 pmc_timeslice_config[slice * 5 + 0] = perfdata->vdata.pmc.pmc[0];
719 pmc_timeslice_config[slice * 5 + 1] = perfdata->vdata.pmc.pmc[1];
720 pmc_timeslice_config[slice * 5 + 2] = perfdata->vdata.pmc.pmc[2];
721 pmc_timeslice_config[slice * 5 + 3] = perfdata->vdata.pmc.pmc[3];
722 pmc_timeslice_config[slice * 5 + 4] = perfdata->vdata.pmc.pmc[4];
723 }
724
pmc_timeslice_enable(struct perfmon_struct * perfdata)725 int pmc_timeslice_enable(struct perfmon_struct *perfdata)
726 {
727 int i, j;
728
729 printk("Perfmon: Timeslice mode\n");
730
731 if(perfmon_base.state == PMC_STATE_INITIAL) {
732 printk("Perfmon: failed - no buffer was allocated.\n");
733 return -1;
734 }
735
736 pmc_timeslice_disable(NULL); // fixme
737 mb();
738
739 for (i=0; i<MAX_PACAS; ++i) {
740 paca[i].prof_mode = PMC_STATE_TIMESLICE;
741 }
742 perfmon_base.state = PMC_STATE_TIMESLICE;
743
744 pmc_configure_hardware();
745
746 for(i=0; i<PMC_MAX_CPUS; i++) {
747 pmc_tick_count[i] = 0;
748 pmc_timeslice[i] = 0;
749 }
750
751 /* Stop counters on all processors -- blocking */
752 pmc_stop(NULL);
753 smp_call_function(pmc_stop, (void *)NULL, 0, 1);
754
755 /* Clear out the PMC counters, cumulative and current */
756 for (i=0; i<MAX_PACAS; ++i) {
757 paca[i].prof_mode = PMC_STATE_TIMESLICE;
758 for(j=0; j<8; j++) {
759 paca[i].pmcc[j] = 0;
760 }
761 for(j=0; j<11; j++) {
762 paca[i].pmc[j] = 0;
763 }
764 }
765
766 memset((char *)perfmon_base.timeslice_buffer,
767 0, perfmon_base.timeslice_length);
768
769 pmc_trace_rec_type(PMC_TYPE_TIMESLICE);
770
771 /* Clear counters on all processors -- blocking */
772 pmc_clear(NULL);
773 smp_call_function(pmc_clear, (void *)NULL, 0, 1);
774
775 mb();
776
777 pmc_timeslice_enabled = 1;
778
779 /*
780 * We do not actually setup the PMC hardware here. That occurs
781 * after the first timeslice tick occurs. Close enough.
782 */
783 return 0;
784 }
785
pmc_timeslice_disable(struct perfmon_struct * perfdata)786 int pmc_timeslice_disable(struct perfmon_struct *perfdata)
787 {
788 pmc_timeslice_enabled = 0;
789 }
790
pmc_dump_timeslice(struct perfmon_struct * perfdata)791 void pmc_dump_timeslice(struct perfmon_struct *perfdata)
792 {
793 unsigned long rec, i, j, idx;
794 int cpu = smp_processor_id();
795
796 spin_lock(&pmc_lock);
797
798 pmc_trace_rec_type(PMC_TYPE_TIMESLICE_DUMP); /* DRENG put cpu num in */
799 for(i=0; i<pmc_timeslice_top; i++) {
800 for(j=0; j<8; j++) {
801 idx = cpu*PMC_MAX_COUNTERS*PMC_SLICES_MAX +
802 i*PMC_MAX_COUNTERS + j;
803 rec = pmc_timeslice_data[idx];
804 *((unsigned long *)(perfmon_base.trace_buffer +
805 perfmon_base.trace_end)) = rec;
806 perfmon_base.trace_end += 8;
807 if(perfmon_base.trace_end >= perfmon_base.trace_length)
808 perfmon_base.trace_end = 0;
809 }
810 }
811 spin_unlock(&pmc_lock);
812 }
813