aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/kernel/perfmon.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ia64/kernel/perfmon.c')
-rw-r--r--arch/ia64/kernel/perfmon.c218
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 {
497static pfm_stats_t pfm_stats[NR_CPUS]; 497static pfm_stats_t pfm_stats[NR_CPUS];
498static pfm_session_t pfm_sessions; /* global sessions information */ 498static pfm_session_t pfm_sessions; /* global sessions information */
499 499
500static spinlock_t pfm_alt_install_check = SPIN_LOCK_UNLOCKED;
501static pfm_intr_handler_desc_t *pfm_alt_intr_handler;
502
500static struct proc_dir_entry *perfmon_dir; 503static struct proc_dir_entry *perfmon_dir;
501static pfm_uuid_t pfm_null_uuid = {0,}; 504static pfm_uuid_t pfm_null_uuid = {0,};
502 505
@@ -606,6 +609,7 @@ DEFINE_PER_CPU(unsigned long, pfm_syst_info);
606DEFINE_PER_CPU(struct task_struct *, pmu_owner); 609DEFINE_PER_CPU(struct task_struct *, pmu_owner);
607DEFINE_PER_CPU(pfm_context_t *, pmu_ctx); 610DEFINE_PER_CPU(pfm_context_t *, pmu_ctx);
608DEFINE_PER_CPU(unsigned long, pmu_activation_number); 611DEFINE_PER_CPU(unsigned long, pmu_activation_number);
612EXPORT_PER_CPU_SYMBOL_GPL(pfm_syst_info);
609 613
610 614
611/* forward declaration */ 615/* forward declaration */
@@ -1265,6 +1269,8 @@ out:
1265} 1269}
1266EXPORT_SYMBOL(pfm_unregister_buffer_fmt); 1270EXPORT_SYMBOL(pfm_unregister_buffer_fmt);
1267 1271
1272extern void update_pal_halt_status(int);
1273
1268static int 1274static int
1269pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu) 1275pfm_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)
1318error_conflict: 1329error_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));
1322abort: 1333abort:
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
6438static void
6439pfm_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
6464void
6465pfm_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
6489int
6490pfm_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
6526cleanup_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}
6538EXPORT_SYMBOL_GPL(pfm_install_alt_pmu_interrupt);
6539
6540int
6541pfm_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}
6571EXPORT_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 */