diff options
Diffstat (limited to 'arch/ia64/kernel/perfmon.c')
-rw-r--r-- | arch/ia64/kernel/perfmon.c | 218 |
1 files changed, 193 insertions, 25 deletions
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 376fcbc3f8da..6407bff6bfd7 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c | |||
@@ -11,7 +11,7 @@ | |||
11 | * Version Perfmon-2.x is a rewrite of perfmon-1.x | 11 | * Version Perfmon-2.x is a rewrite of perfmon-1.x |
12 | * by Stephane Eranian, Hewlett Packard Co. | 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 | * Stephane Eranian <eranian@hpl.hp.com> | 15 | * Stephane Eranian <eranian@hpl.hp.com> |
16 | * David Mosberger-Tang <davidm@hpl.hp.com> | 16 | * David Mosberger-Tang <davidm@hpl.hp.com> |
17 | * | 17 | * |
@@ -497,6 +497,9 @@ typedef struct { | |||
497 | static pfm_stats_t pfm_stats[NR_CPUS]; | 497 | static pfm_stats_t pfm_stats[NR_CPUS]; |
498 | static pfm_session_t pfm_sessions; /* global sessions information */ | 498 | static pfm_session_t pfm_sessions; /* global sessions information */ |
499 | 499 | ||
500 | static spinlock_t pfm_alt_install_check = SPIN_LOCK_UNLOCKED; | ||
501 | static pfm_intr_handler_desc_t *pfm_alt_intr_handler; | ||
502 | |||
500 | static struct proc_dir_entry *perfmon_dir; | 503 | static struct proc_dir_entry *perfmon_dir; |
501 | static pfm_uuid_t pfm_null_uuid = {0,}; | 504 | static pfm_uuid_t pfm_null_uuid = {0,}; |
502 | 505 | ||
@@ -606,6 +609,7 @@ DEFINE_PER_CPU(unsigned long, pfm_syst_info); | |||
606 | DEFINE_PER_CPU(struct task_struct *, pmu_owner); | 609 | DEFINE_PER_CPU(struct task_struct *, pmu_owner); |
607 | DEFINE_PER_CPU(pfm_context_t *, pmu_ctx); | 610 | DEFINE_PER_CPU(pfm_context_t *, pmu_ctx); |
608 | DEFINE_PER_CPU(unsigned long, pmu_activation_number); | 611 | DEFINE_PER_CPU(unsigned long, pmu_activation_number); |
612 | EXPORT_PER_CPU_SYMBOL_GPL(pfm_syst_info); | ||
609 | 613 | ||
610 | 614 | ||
611 | /* forward declaration */ | 615 | /* forward declaration */ |
@@ -1265,6 +1269,8 @@ out: | |||
1265 | } | 1269 | } |
1266 | EXPORT_SYMBOL(pfm_unregister_buffer_fmt); | 1270 | EXPORT_SYMBOL(pfm_unregister_buffer_fmt); |
1267 | 1271 | ||
1272 | extern void update_pal_halt_status(int); | ||
1273 | |||
1268 | static int | 1274 | static int |
1269 | pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu) | 1275 | pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu) |
1270 | { | 1276 | { |
@@ -1311,6 +1317,11 @@ pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu) | |||
1311 | is_syswide, | 1317 | is_syswide, |
1312 | cpu)); | 1318 | cpu)); |
1313 | 1319 | ||
1320 | /* | ||
1321 | * disable default_idle() to go to PAL_HALT | ||
1322 | */ | ||
1323 | update_pal_halt_status(0); | ||
1324 | |||
1314 | UNLOCK_PFS(flags); | 1325 | UNLOCK_PFS(flags); |
1315 | 1326 | ||
1316 | return 0; | 1327 | return 0; |
@@ -1318,7 +1329,7 @@ pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu) | |||
1318 | error_conflict: | 1329 | error_conflict: |
1319 | DPRINT(("system wide not possible, conflicting session [%d] on CPU%d\n", | 1330 | DPRINT(("system wide not possible, conflicting session [%d] on CPU%d\n", |
1320 | pfm_sessions.pfs_sys_session[cpu]->pid, | 1331 | pfm_sessions.pfs_sys_session[cpu]->pid, |
1321 | smp_processor_id())); | 1332 | cpu)); |
1322 | abort: | 1333 | abort: |
1323 | UNLOCK_PFS(flags); | 1334 | UNLOCK_PFS(flags); |
1324 | 1335 | ||
@@ -1366,6 +1377,12 @@ pfm_unreserve_session(pfm_context_t *ctx, int is_syswide, unsigned int cpu) | |||
1366 | is_syswide, | 1377 | is_syswide, |
1367 | cpu)); | 1378 | cpu)); |
1368 | 1379 | ||
1380 | /* | ||
1381 | * if possible, enable default_idle() to go into PAL_HALT | ||
1382 | */ | ||
1383 | if (pfm_sessions.pfs_task_sessions == 0 && pfm_sessions.pfs_sys_sessions == 0) | ||
1384 | update_pal_halt_status(1); | ||
1385 | |||
1369 | UNLOCK_PFS(flags); | 1386 | UNLOCK_PFS(flags); |
1370 | 1387 | ||
1371 | return 0; | 1388 | return 0; |
@@ -4202,7 +4219,7 @@ pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) | |||
4202 | DPRINT(("cannot load to [%d], invalid ctx_state=%d\n", | 4219 | DPRINT(("cannot load to [%d], invalid ctx_state=%d\n", |
4203 | req->load_pid, | 4220 | req->load_pid, |
4204 | ctx->ctx_state)); | 4221 | ctx->ctx_state)); |
4205 | return -EINVAL; | 4222 | return -EBUSY; |
4206 | } | 4223 | } |
4207 | 4224 | ||
4208 | DPRINT(("load_pid [%d] using_dbreg=%d\n", req->load_pid, ctx->ctx_fl_using_dbreg)); | 4225 | DPRINT(("load_pid [%d] using_dbreg=%d\n", req->load_pid, ctx->ctx_fl_using_dbreg)); |
@@ -4704,16 +4721,26 @@ recheck: | |||
4704 | if (task == current || ctx->ctx_fl_system) return 0; | 4721 | if (task == current || ctx->ctx_fl_system) return 0; |
4705 | 4722 | ||
4706 | /* | 4723 | /* |
4707 | * if context is UNLOADED we are safe to go | 4724 | * we are monitoring another thread |
4708 | */ | 4725 | */ |
4709 | if (state == PFM_CTX_UNLOADED) return 0; | 4726 | switch(state) { |
4710 | 4727 | case PFM_CTX_UNLOADED: | |
4711 | /* | 4728 | /* |
4712 | * no command can operate on a zombie context | 4729 | * if context is UNLOADED we are safe to go |
4713 | */ | 4730 | */ |
4714 | if (state == PFM_CTX_ZOMBIE) { | 4731 | return 0; |
4715 | DPRINT(("cmd %d state zombie cannot operate on context\n", cmd)); | 4732 | case PFM_CTX_ZOMBIE: |
4716 | return -EINVAL; | 4733 | /* |
4734 | * no command can operate on a zombie context | ||
4735 | */ | ||
4736 | DPRINT(("cmd %d state zombie cannot operate on context\n", cmd)); | ||
4737 | return -EINVAL; | ||
4738 | case PFM_CTX_MASKED: | ||
4739 | /* | ||
4740 | * PMU state has been saved to software even though | ||
4741 | * the thread may still be running. | ||
4742 | */ | ||
4743 | if (cmd != PFM_UNLOAD_CONTEXT) return 0; | ||
4717 | } | 4744 | } |
4718 | 4745 | ||
4719 | /* | 4746 | /* |
@@ -5532,26 +5559,32 @@ pfm_interrupt_handler(int irq, void *arg, struct pt_regs *regs) | |||
5532 | int ret; | 5559 | int ret; |
5533 | 5560 | ||
5534 | this_cpu = get_cpu(); | 5561 | this_cpu = get_cpu(); |
5535 | min = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min; | 5562 | if (likely(!pfm_alt_intr_handler)) { |
5536 | max = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max; | 5563 | min = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min; |
5564 | max = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max; | ||
5537 | 5565 | ||
5538 | start_cycles = ia64_get_itc(); | 5566 | start_cycles = ia64_get_itc(); |
5539 | 5567 | ||
5540 | ret = pfm_do_interrupt_handler(irq, arg, regs); | 5568 | ret = pfm_do_interrupt_handler(irq, arg, regs); |
5541 | 5569 | ||
5542 | total_cycles = ia64_get_itc(); | 5570 | total_cycles = ia64_get_itc(); |
5543 | 5571 | ||
5544 | /* | 5572 | /* |
5545 | * don't measure spurious interrupts | 5573 | * don't measure spurious interrupts |
5546 | */ | 5574 | */ |
5547 | if (likely(ret == 0)) { | 5575 | if (likely(ret == 0)) { |
5548 | total_cycles -= start_cycles; | 5576 | total_cycles -= start_cycles; |
5549 | 5577 | ||
5550 | if (total_cycles < min) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min = total_cycles; | 5578 | if (total_cycles < min) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min = total_cycles; |
5551 | if (total_cycles > max) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max = total_cycles; | 5579 | if (total_cycles > max) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max = total_cycles; |
5552 | 5580 | ||
5553 | pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles; | 5581 | pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles; |
5582 | } | ||
5554 | } | 5583 | } |
5584 | else { | ||
5585 | (*pfm_alt_intr_handler->handler)(irq, arg, regs); | ||
5586 | } | ||
5587 | |||
5555 | put_cpu_no_resched(); | 5588 | put_cpu_no_resched(); |
5556 | return IRQ_HANDLED; | 5589 | return IRQ_HANDLED; |
5557 | } | 5590 | } |
@@ -6402,6 +6435,141 @@ static struct irqaction perfmon_irqaction = { | |||
6402 | .name = "perfmon" | 6435 | .name = "perfmon" |
6403 | }; | 6436 | }; |
6404 | 6437 | ||
6438 | static void | ||
6439 | pfm_alt_save_pmu_state(void *data) | ||
6440 | { | ||
6441 | struct pt_regs *regs; | ||
6442 | |||
6443 | regs = ia64_task_regs(current); | ||
6444 | |||
6445 | DPRINT(("called\n")); | ||
6446 | |||
6447 | /* | ||
6448 | * should not be necessary but | ||
6449 | * let's take not risk | ||
6450 | */ | ||
6451 | pfm_clear_psr_up(); | ||
6452 | pfm_clear_psr_pp(); | ||
6453 | ia64_psr(regs)->pp = 0; | ||
6454 | |||
6455 | /* | ||
6456 | * This call is required | ||
6457 | * May cause a spurious interrupt on some processors | ||
6458 | */ | ||
6459 | pfm_freeze_pmu(); | ||
6460 | |||
6461 | ia64_srlz_d(); | ||
6462 | } | ||
6463 | |||
6464 | void | ||
6465 | pfm_alt_restore_pmu_state(void *data) | ||
6466 | { | ||
6467 | struct pt_regs *regs; | ||
6468 | |||
6469 | regs = ia64_task_regs(current); | ||
6470 | |||
6471 | DPRINT(("called\n")); | ||
6472 | |||
6473 | /* | ||
6474 | * put PMU back in state expected | ||
6475 | * by perfmon | ||
6476 | */ | ||
6477 | pfm_clear_psr_up(); | ||
6478 | pfm_clear_psr_pp(); | ||
6479 | ia64_psr(regs)->pp = 0; | ||
6480 | |||
6481 | /* | ||
6482 | * perfmon runs with PMU unfrozen at all times | ||
6483 | */ | ||
6484 | pfm_unfreeze_pmu(); | ||
6485 | |||
6486 | ia64_srlz_d(); | ||
6487 | } | ||
6488 | |||
6489 | int | ||
6490 | pfm_install_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) | ||
6491 | { | ||
6492 | int ret, i; | ||
6493 | int reserve_cpu; | ||
6494 | |||
6495 | /* some sanity checks */ | ||
6496 | if (hdl == NULL || hdl->handler == NULL) return -EINVAL; | ||
6497 | |||
6498 | /* do the easy test first */ | ||
6499 | if (pfm_alt_intr_handler) return -EBUSY; | ||
6500 | |||
6501 | /* one at a time in the install or remove, just fail the others */ | ||
6502 | if (!spin_trylock(&pfm_alt_install_check)) { | ||
6503 | return -EBUSY; | ||
6504 | } | ||
6505 | |||
6506 | /* reserve our session */ | ||
6507 | for_each_online_cpu(reserve_cpu) { | ||
6508 | ret = pfm_reserve_session(NULL, 1, reserve_cpu); | ||
6509 | if (ret) goto cleanup_reserve; | ||
6510 | } | ||
6511 | |||
6512 | /* save the current system wide pmu states */ | ||
6513 | ret = on_each_cpu(pfm_alt_save_pmu_state, NULL, 0, 1); | ||
6514 | if (ret) { | ||
6515 | DPRINT(("on_each_cpu() failed: %d\n", ret)); | ||
6516 | goto cleanup_reserve; | ||
6517 | } | ||
6518 | |||
6519 | /* officially change to the alternate interrupt handler */ | ||
6520 | pfm_alt_intr_handler = hdl; | ||
6521 | |||
6522 | spin_unlock(&pfm_alt_install_check); | ||
6523 | |||
6524 | return 0; | ||
6525 | |||
6526 | cleanup_reserve: | ||
6527 | for_each_online_cpu(i) { | ||
6528 | /* don't unreserve more than we reserved */ | ||
6529 | if (i >= reserve_cpu) break; | ||
6530 | |||
6531 | pfm_unreserve_session(NULL, 1, i); | ||
6532 | } | ||
6533 | |||
6534 | spin_unlock(&pfm_alt_install_check); | ||
6535 | |||
6536 | return ret; | ||
6537 | } | ||
6538 | EXPORT_SYMBOL_GPL(pfm_install_alt_pmu_interrupt); | ||
6539 | |||
6540 | int | ||
6541 | pfm_remove_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) | ||
6542 | { | ||
6543 | int i; | ||
6544 | int ret; | ||
6545 | |||
6546 | if (hdl == NULL) return -EINVAL; | ||
6547 | |||
6548 | /* cannot remove someone else's handler! */ | ||
6549 | if (pfm_alt_intr_handler != hdl) return -EINVAL; | ||
6550 | |||
6551 | /* one at a time in the install or remove, just fail the others */ | ||
6552 | if (!spin_trylock(&pfm_alt_install_check)) { | ||
6553 | return -EBUSY; | ||
6554 | } | ||
6555 | |||
6556 | pfm_alt_intr_handler = NULL; | ||
6557 | |||
6558 | ret = on_each_cpu(pfm_alt_restore_pmu_state, NULL, 0, 1); | ||
6559 | if (ret) { | ||
6560 | DPRINT(("on_each_cpu() failed: %d\n", ret)); | ||
6561 | } | ||
6562 | |||
6563 | for_each_online_cpu(i) { | ||
6564 | pfm_unreserve_session(NULL, 1, i); | ||
6565 | } | ||
6566 | |||
6567 | spin_unlock(&pfm_alt_install_check); | ||
6568 | |||
6569 | return 0; | ||
6570 | } | ||
6571 | EXPORT_SYMBOL_GPL(pfm_remove_alt_pmu_interrupt); | ||
6572 | |||
6405 | /* | 6573 | /* |
6406 | * perfmon initialization routine, called from the initcall() table | 6574 | * perfmon initialization routine, called from the initcall() table |
6407 | */ | 6575 | */ |