diff options
author | Paul Mackerras <paulus@samba.org> | 2009-03-30 13:07:07 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-04-06 03:30:39 -0400 |
commit | 7595d63b3a9ce65d14c4fbd0e7de448a343d7215 (patch) | |
tree | 09d9c4defe8805fc3c5cd645c045f6bdab90f748 /arch/powerpc/kernel | |
parent | 3c1ba6fafecaed295017881f8863a18602f32c1d (diff) |
perf_counter: powerpc: only reserve PMU hardware when we need it
Impact: cooperate with oprofile
At present, on PowerPC, if you have perf_counters compiled in, oprofile
doesn't work. There is code to allow the PMU to be shared between
competing subsystems, such as perf_counters and oprofile, but currently
the perf_counter subsystem reserves the PMU for itself at boot time,
and never releases it.
This makes perf_counter play nicely with oprofile. Now we keep a count
of how many perf_counter instances are counting hardware events, and
reserve the PMU when that count becomes non-zero, and release the PMU
when that count becomes zero. This means that it is possible to have
perf_counters compiled in and still use oprofile, as long as there are
no hardware perf_counters active. This also means that if oprofile is
active, sys_perf_counter_open will fail if the hw_event specifies a
hardware event.
To avoid races with other tasks creating and destroying perf_counters,
we use a mutex. We use atomic_inc_not_zero and atomic_add_unless to
avoid having to take the mutex unless there is a possibility of the
count going between 0 and 1.
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Orig-LKML-Reference: <20090330171023.627912475@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/perf_counter.c | 47 |
1 files changed, 42 insertions, 5 deletions
diff --git a/arch/powerpc/kernel/perf_counter.c b/arch/powerpc/kernel/perf_counter.c index cde720fc495c..560dd1e7b524 100644 --- a/arch/powerpc/kernel/perf_counter.c +++ b/arch/powerpc/kernel/perf_counter.c | |||
@@ -41,6 +41,8 @@ struct power_pmu *ppmu; | |||
41 | */ | 41 | */ |
42 | static unsigned int freeze_counters_kernel = MMCR0_FCS; | 42 | static unsigned int freeze_counters_kernel = MMCR0_FCS; |
43 | 43 | ||
44 | static void perf_counter_interrupt(struct pt_regs *regs); | ||
45 | |||
44 | void perf_counter_print_debug(void) | 46 | void perf_counter_print_debug(void) |
45 | { | 47 | { |
46 | } | 48 | } |
@@ -594,6 +596,24 @@ struct hw_perf_counter_ops power_perf_ops = { | |||
594 | .read = power_perf_read | 596 | .read = power_perf_read |
595 | }; | 597 | }; |
596 | 598 | ||
599 | /* Number of perf_counters counting hardware events */ | ||
600 | static atomic_t num_counters; | ||
601 | /* Used to avoid races in calling reserve/release_pmc_hardware */ | ||
602 | static DEFINE_MUTEX(pmc_reserve_mutex); | ||
603 | |||
604 | /* | ||
605 | * Release the PMU if this is the last perf_counter. | ||
606 | */ | ||
607 | static void hw_perf_counter_destroy(struct perf_counter *counter) | ||
608 | { | ||
609 | if (!atomic_add_unless(&num_counters, -1, 1)) { | ||
610 | mutex_lock(&pmc_reserve_mutex); | ||
611 | if (atomic_dec_return(&num_counters) == 0) | ||
612 | release_pmc_hardware(); | ||
613 | mutex_unlock(&pmc_reserve_mutex); | ||
614 | } | ||
615 | } | ||
616 | |||
597 | const struct hw_perf_counter_ops * | 617 | const struct hw_perf_counter_ops * |
598 | hw_perf_counter_init(struct perf_counter *counter) | 618 | hw_perf_counter_init(struct perf_counter *counter) |
599 | { | 619 | { |
@@ -601,6 +621,7 @@ hw_perf_counter_init(struct perf_counter *counter) | |||
601 | struct perf_counter *ctrs[MAX_HWCOUNTERS]; | 621 | struct perf_counter *ctrs[MAX_HWCOUNTERS]; |
602 | unsigned int events[MAX_HWCOUNTERS]; | 622 | unsigned int events[MAX_HWCOUNTERS]; |
603 | int n; | 623 | int n; |
624 | int err; | ||
604 | 625 | ||
605 | if (!ppmu) | 626 | if (!ppmu) |
606 | return NULL; | 627 | return NULL; |
@@ -646,6 +667,27 @@ hw_perf_counter_init(struct perf_counter *counter) | |||
646 | 667 | ||
647 | counter->hw.config = events[n]; | 668 | counter->hw.config = events[n]; |
648 | atomic64_set(&counter->hw.period_left, counter->hw_event.irq_period); | 669 | atomic64_set(&counter->hw.period_left, counter->hw_event.irq_period); |
670 | |||
671 | /* | ||
672 | * See if we need to reserve the PMU. | ||
673 | * If no counters are currently in use, then we have to take a | ||
674 | * mutex to ensure that we don't race with another task doing | ||
675 | * reserve_pmc_hardware or release_pmc_hardware. | ||
676 | */ | ||
677 | err = 0; | ||
678 | if (!atomic_inc_not_zero(&num_counters)) { | ||
679 | mutex_lock(&pmc_reserve_mutex); | ||
680 | if (atomic_read(&num_counters) == 0 && | ||
681 | reserve_pmc_hardware(perf_counter_interrupt)) | ||
682 | err = -EBUSY; | ||
683 | else | ||
684 | atomic_inc(&num_counters); | ||
685 | mutex_unlock(&pmc_reserve_mutex); | ||
686 | } | ||
687 | counter->destroy = hw_perf_counter_destroy; | ||
688 | |||
689 | if (err) | ||
690 | return NULL; | ||
649 | return &power_perf_ops; | 691 | return &power_perf_ops; |
650 | } | 692 | } |
651 | 693 | ||
@@ -769,11 +811,6 @@ static int init_perf_counters(void) | |||
769 | { | 811 | { |
770 | unsigned long pvr; | 812 | unsigned long pvr; |
771 | 813 | ||
772 | if (reserve_pmc_hardware(perf_counter_interrupt)) { | ||
773 | printk(KERN_ERR "Couldn't init performance monitor subsystem\n"); | ||
774 | return -EBUSY; | ||
775 | } | ||
776 | |||
777 | /* XXX should get this from cputable */ | 814 | /* XXX should get this from cputable */ |
778 | pvr = mfspr(SPRN_PVR); | 815 | pvr = mfspr(SPRN_PVR); |
779 | switch (PVR_VER(pvr)) { | 816 | switch (PVR_VER(pvr)) { |