[IA64] alternate perfmon handler

Patch from Charles Spirakis

Some linux customers want to optimize their applications on the latest
hardware but are not yet willing to upgrade to the latest kernel. This
patch provides a way to plug in an alternate, basic, and GPL'ed PMU
subsystem to help with their monitoring needs or for specialty work. It
can also be used in case of serious unexpected bugs in perfmon. Mutual
exclusion between the two subsystems is guaranteed, hence no conflict
can arise from both subsystem being present.

Acked-by: Stephane Eranian <eranian@hpl.hp.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>

Tony Luck a1ecf7f6 6872ec54

+168 -15
+160 -15
arch/ia64/kernel/perfmon.c
··· 11 * Version Perfmon-2.x is a rewrite of perfmon-1.x 12 * by Stephane Eranian, Hewlett Packard Co. 13 * 14 - * Copyright (C) 1999-2003, 2005 Hewlett Packard Co 15 * Stephane Eranian <eranian@hpl.hp.com> 16 * David Mosberger-Tang <davidm@hpl.hp.com> 17 * ··· 497 static pfm_stats_t pfm_stats[NR_CPUS]; 498 static pfm_session_t pfm_sessions; /* global sessions information */ 499 500 static struct proc_dir_entry *perfmon_dir; 501 static pfm_uuid_t pfm_null_uuid = {0,}; 502 ··· 609 DEFINE_PER_CPU(struct task_struct *, pmu_owner); 610 DEFINE_PER_CPU(pfm_context_t *, pmu_ctx); 611 DEFINE_PER_CPU(unsigned long, pmu_activation_number); 612 613 614 /* forward declaration */ ··· 1329 error_conflict: 1330 DPRINT(("system wide not possible, conflicting session [%d] on CPU%d\n", 1331 pfm_sessions.pfs_sys_session[cpu]->pid, 1332 - smp_processor_id())); 1333 abort: 1334 UNLOCK_PFS(flags); 1335 ··· 5559 int ret; 5560 5561 this_cpu = get_cpu(); 5562 - min = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min; 5563 - max = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max; 5564 5565 - start_cycles = ia64_get_itc(); 5566 5567 - ret = pfm_do_interrupt_handler(irq, arg, regs); 5568 5569 - total_cycles = ia64_get_itc(); 5570 5571 - /* 5572 - * don't measure spurious interrupts 5573 - */ 5574 - if (likely(ret == 0)) { 5575 - total_cycles -= start_cycles; 5576 5577 - if (total_cycles < min) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min = total_cycles; 5578 - if (total_cycles > max) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max = total_cycles; 5579 5580 - pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles; 5581 } 5582 put_cpu_no_resched(); 5583 return IRQ_HANDLED; 5584 } ··· 6434 .flags = SA_INTERRUPT, 6435 .name = "perfmon" 6436 }; 6437 6438 /* 6439 * perfmon initialization routine, called from the initcall() table
··· 11 * Version Perfmon-2.x is a rewrite of perfmon-1.x 12 * by Stephane Eranian, Hewlett Packard Co. 13 * 14 + * Copyright (C) 1999-2005 Hewlett Packard Co 15 * Stephane Eranian <eranian@hpl.hp.com> 16 * David Mosberger-Tang <davidm@hpl.hp.com> 17 * ··· 497 static pfm_stats_t pfm_stats[NR_CPUS]; 498 static pfm_session_t pfm_sessions; /* global sessions information */ 499 500 + static spinlock_t pfm_alt_install_check; 501 + static pfm_intr_handler_desc_t *pfm_alt_intr_handler; 502 + 503 static struct proc_dir_entry *perfmon_dir; 504 static pfm_uuid_t pfm_null_uuid = {0,}; 505 ··· 606 DEFINE_PER_CPU(struct task_struct *, pmu_owner); 607 DEFINE_PER_CPU(pfm_context_t *, pmu_ctx); 608 DEFINE_PER_CPU(unsigned long, pmu_activation_number); 609 + EXPORT_SYMBOL_GPL(per_cpu__pfm_syst_info); 610 611 612 /* forward declaration */ ··· 1325 error_conflict: 1326 DPRINT(("system wide not possible, conflicting session [%d] on CPU%d\n", 1327 pfm_sessions.pfs_sys_session[cpu]->pid, 1328 + cpu)); 1329 abort: 1330 UNLOCK_PFS(flags); 1331 ··· 5555 int ret; 5556 5557 this_cpu = get_cpu(); 5558 + if (likely(!pfm_alt_intr_handler)) { 5559 + min = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min; 5560 + max = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max; 5561 5562 + start_cycles = ia64_get_itc(); 5563 5564 + ret = pfm_do_interrupt_handler(irq, arg, regs); 5565 5566 + total_cycles = ia64_get_itc(); 5567 5568 + /* 5569 + * don't measure spurious interrupts 5570 + */ 5571 + if (likely(ret == 0)) { 5572 + total_cycles -= start_cycles; 5573 5574 + if (total_cycles < min) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min = total_cycles; 5575 + if (total_cycles > max) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max = total_cycles; 5576 5577 + pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles; 5578 + } 5579 } 5580 + else { 5581 + (*pfm_alt_intr_handler->handler)(irq, arg, regs); 5582 + } 5583 + 5584 put_cpu_no_resched(); 5585 return IRQ_HANDLED; 5586 } ··· 6424 .flags = SA_INTERRUPT, 6425 .name = "perfmon" 6426 }; 6427 + 6428 + static void 6429 + pfm_alt_save_pmu_state(void *data) 6430 + { 6431 + struct pt_regs *regs; 6432 + 6433 + regs = ia64_task_regs(current); 6434 + 6435 + DPRINT(("called\n")); 6436 + 6437 + /* 6438 + * should not be necessary but 6439 + * let's take not risk 6440 + */ 6441 + pfm_clear_psr_up(); 6442 + pfm_clear_psr_pp(); 6443 + ia64_psr(regs)->pp = 0; 6444 + 6445 + /* 6446 + * This call is required 6447 + * May cause a spurious interrupt on some processors 6448 + */ 6449 + pfm_freeze_pmu(); 6450 + 6451 + ia64_srlz_d(); 6452 + } 6453 + 6454 + void 6455 + pfm_alt_restore_pmu_state(void *data) 6456 + { 6457 + struct pt_regs *regs; 6458 + 6459 + regs = ia64_task_regs(current); 6460 + 6461 + DPRINT(("called\n")); 6462 + 6463 + /* 6464 + * put PMU back in state expected 6465 + * by perfmon 6466 + */ 6467 + pfm_clear_psr_up(); 6468 + pfm_clear_psr_pp(); 6469 + ia64_psr(regs)->pp = 0; 6470 + 6471 + /* 6472 + * perfmon runs with PMU unfrozen at all times 6473 + */ 6474 + pfm_unfreeze_pmu(); 6475 + 6476 + ia64_srlz_d(); 6477 + } 6478 + 6479 + int 6480 + pfm_install_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) 6481 + { 6482 + int ret, i; 6483 + int reserve_cpu; 6484 + 6485 + /* some sanity checks */ 6486 + if (hdl == NULL || hdl->handler == NULL) return -EINVAL; 6487 + 6488 + /* do the easy test first */ 6489 + if (pfm_alt_intr_handler) return -EBUSY; 6490 + 6491 + /* one at a time in the install or remove, just fail the others */ 6492 + if (!spin_trylock(&pfm_alt_install_check)) { 6493 + return -EBUSY; 6494 + } 6495 + 6496 + /* reserve our session */ 6497 + for_each_online_cpu(reserve_cpu) { 6498 + ret = pfm_reserve_session(NULL, 1, reserve_cpu); 6499 + if (ret) goto cleanup_reserve; 6500 + } 6501 + 6502 + /* save the current system wide pmu states */ 6503 + ret = on_each_cpu(pfm_alt_save_pmu_state, NULL, 0, 1); 6504 + if (ret) { 6505 + DPRINT(("on_each_cpu() failed: %d\n", ret)); 6506 + goto cleanup_reserve; 6507 + } 6508 + 6509 + /* officially change to the alternate interrupt handler */ 6510 + pfm_alt_intr_handler = hdl; 6511 + 6512 + spin_unlock(&pfm_alt_install_check); 6513 + 6514 + return 0; 6515 + 6516 + cleanup_reserve: 6517 + for_each_online_cpu(i) { 6518 + /* don't unreserve more than we reserved */ 6519 + if (i >= reserve_cpu) break; 6520 + 6521 + pfm_unreserve_session(NULL, 1, i); 6522 + } 6523 + 6524 + spin_unlock(&pfm_alt_install_check); 6525 + 6526 + return ret; 6527 + } 6528 + EXPORT_SYMBOL_GPL(pfm_install_alt_pmu_interrupt); 6529 + 6530 + int 6531 + pfm_remove_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) 6532 + { 6533 + int i; 6534 + int ret; 6535 + 6536 + if (hdl == NULL) return -EINVAL; 6537 + 6538 + /* cannot remove someone else's handler! */ 6539 + if (pfm_alt_intr_handler != hdl) return -EINVAL; 6540 + 6541 + /* one at a time in the install or remove, just fail the others */ 6542 + if (!spin_trylock(&pfm_alt_install_check)) { 6543 + return -EBUSY; 6544 + } 6545 + 6546 + pfm_alt_intr_handler = NULL; 6547 + 6548 + ret = on_each_cpu(pfm_alt_restore_pmu_state, NULL, 0, 1); 6549 + if (ret) { 6550 + DPRINT(("on_each_cpu() failed: %d\n", ret)); 6551 + } 6552 + 6553 + for_each_online_cpu(i) { 6554 + pfm_unreserve_session(NULL, 1, i); 6555 + } 6556 + 6557 + spin_unlock(&pfm_alt_install_check); 6558 + 6559 + return 0; 6560 + } 6561 + EXPORT_SYMBOL_GPL(pfm_remove_alt_pmu_interrupt); 6562 6563 /* 6564 * perfmon initialization routine, called from the initcall() table
+8
include/asm-ia64/perfmon.h
··· 177 178 extern long perfmonctl(int fd, int cmd, void *arg, int narg); 179 180 extern void pfm_save_regs (struct task_struct *); 181 extern void pfm_load_regs (struct task_struct *); 182 ··· 191 extern void pfm_inherit(struct task_struct *task, struct pt_regs *regs); 192 extern void pfm_init_percpu(void); 193 extern void pfm_handle_work(void); 194 195 /* 196 * Reset PMD register flags
··· 177 178 extern long perfmonctl(int fd, int cmd, void *arg, int narg); 179 180 + typedef struct { 181 + void (*handler)(int irq, void *arg, struct pt_regs *regs); 182 + } pfm_intr_handler_desc_t; 183 + 184 extern void pfm_save_regs (struct task_struct *); 185 extern void pfm_load_regs (struct task_struct *); 186 ··· 187 extern void pfm_inherit(struct task_struct *task, struct pt_regs *regs); 188 extern void pfm_init_percpu(void); 189 extern void pfm_handle_work(void); 190 + extern int pfm_install_alt_pmu_interrupt(pfm_intr_handler_desc_t *h); 191 + extern int pfm_remove_alt_pmu_interrupt(pfm_intr_handler_desc_t *h); 192 + 193 + 194 195 /* 196 * Reset PMD register flags