[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 11 * Version Perfmon-2.x is a rewrite of perfmon-1.x 12 12 * by Stephane Eranian, Hewlett Packard Co. 13 13 * 14 - * Copyright (C) 1999-2003, 2005 Hewlett Packard Co 14 + * Copyright (C) 1999-2005 Hewlett Packard Co 15 15 * Stephane Eranian <eranian@hpl.hp.com> 16 16 * David Mosberger-Tang <davidm@hpl.hp.com> 17 17 * ··· 497 497 static pfm_stats_t pfm_stats[NR_CPUS]; 498 498 static pfm_session_t pfm_sessions; /* global sessions information */ 499 499 500 + static spinlock_t pfm_alt_install_check; 501 + static pfm_intr_handler_desc_t *pfm_alt_intr_handler; 502 + 500 503 static struct proc_dir_entry *perfmon_dir; 501 504 static pfm_uuid_t pfm_null_uuid = {0,}; 502 505 ··· 609 606 DEFINE_PER_CPU(struct task_struct *, pmu_owner); 610 607 DEFINE_PER_CPU(pfm_context_t *, pmu_ctx); 611 608 DEFINE_PER_CPU(unsigned long, pmu_activation_number); 609 + EXPORT_SYMBOL_GPL(per_cpu__pfm_syst_info); 612 610 613 611 614 612 /* forward declaration */ ··· 1329 1325 error_conflict: 1330 1326 DPRINT(("system wide not possible, conflicting session [%d] on CPU%d\n", 1331 1327 pfm_sessions.pfs_sys_session[cpu]->pid, 1332 - smp_processor_id())); 1328 + cpu)); 1333 1329 abort: 1334 1330 UNLOCK_PFS(flags); 1335 1331 ··· 5559 5555 int ret; 5560 5556 5561 5557 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; 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; 5564 5561 5565 - start_cycles = ia64_get_itc(); 5562 + start_cycles = ia64_get_itc(); 5566 5563 5567 - ret = pfm_do_interrupt_handler(irq, arg, regs); 5564 + ret = pfm_do_interrupt_handler(irq, arg, regs); 5568 5565 5569 - total_cycles = ia64_get_itc(); 5566 + total_cycles = ia64_get_itc(); 5570 5567 5571 - /* 5572 - * don't measure spurious interrupts 5573 - */ 5574 - if (likely(ret == 0)) { 5575 - total_cycles -= start_cycles; 5568 + /* 5569 + * don't measure spurious interrupts 5570 + */ 5571 + if (likely(ret == 0)) { 5572 + total_cycles -= start_cycles; 5576 5573 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; 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; 5579 5576 5580 - pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles; 5577 + pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles; 5578 + } 5581 5579 } 5580 + else { 5581 + (*pfm_alt_intr_handler->handler)(irq, arg, regs); 5582 + } 5583 + 5582 5584 put_cpu_no_resched(); 5583 5585 return IRQ_HANDLED; 5584 5586 } ··· 6434 6424 .flags = SA_INTERRUPT, 6435 6425 .name = "perfmon" 6436 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); 6437 6562 6438 6563 /* 6439 6564 * perfmon initialization routine, called from the initcall() table
+8
include/asm-ia64/perfmon.h
··· 177 177 178 178 extern long perfmonctl(int fd, int cmd, void *arg, int narg); 179 179 180 + typedef struct { 181 + void (*handler)(int irq, void *arg, struct pt_regs *regs); 182 + } pfm_intr_handler_desc_t; 183 + 180 184 extern void pfm_save_regs (struct task_struct *); 181 185 extern void pfm_load_regs (struct task_struct *); 182 186 ··· 191 187 extern void pfm_inherit(struct task_struct *task, struct pt_regs *regs); 192 188 extern void pfm_init_percpu(void); 193 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 194 195 195 /* 196 196 * Reset PMD register flags