Skip to content

Commit

Permalink
perf_counter: pmc arbitration
Browse files Browse the repository at this point in the history
Follow the example set by powerpc and try to play nice with oprofile
and the nmi watchdog.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Acked-by: Paul Mackerras <paulus@samba.org>
Orig-LKML-Reference: <20090330171024.459968444@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
  • Loading branch information
Peter Zijlstra authored and Ingo Molnar committed Apr 6, 2009
1 parent d7d59fb commit 4e935e4
Showing 1 changed file with 75 additions and 0 deletions.
75 changes: 75 additions & 0 deletions arch/x86/kernel/cpu/perf_counter.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <asm/apic.h>
#include <asm/stacktrace.h>
#include <asm/nmi.h>

static bool perf_counters_initialized __read_mostly;

Expand Down Expand Up @@ -172,17 +173,89 @@ x86_perf_counter_update(struct perf_counter *counter,
atomic64_sub(delta, &hwc->period_left);
}

static atomic_t num_counters;
static DEFINE_MUTEX(pmc_reserve_mutex);

static bool reserve_pmc_hardware(void)
{
int i;

if (nmi_watchdog == NMI_LOCAL_APIC)
disable_lapic_nmi_watchdog();

for (i = 0; i < nr_counters_generic; i++) {
if (!reserve_perfctr_nmi(pmc_ops->perfctr + i))
goto perfctr_fail;
}

for (i = 0; i < nr_counters_generic; i++) {
if (!reserve_evntsel_nmi(pmc_ops->eventsel + i))
goto eventsel_fail;
}

return true;

eventsel_fail:
for (i--; i >= 0; i--)
release_evntsel_nmi(pmc_ops->eventsel + i);

i = nr_counters_generic;

perfctr_fail:
for (i--; i >= 0; i--)
release_perfctr_nmi(pmc_ops->perfctr + i);

if (nmi_watchdog == NMI_LOCAL_APIC)
enable_lapic_nmi_watchdog();

return false;
}

static void release_pmc_hardware(void)
{
int i;

for (i = 0; i < nr_counters_generic; i++) {
release_perfctr_nmi(pmc_ops->perfctr + i);
release_evntsel_nmi(pmc_ops->eventsel + i);
}

if (nmi_watchdog == NMI_LOCAL_APIC)
enable_lapic_nmi_watchdog();
}

static void hw_perf_counter_destroy(struct perf_counter *counter)
{
if (atomic_dec_and_mutex_lock(&num_counters, &pmc_reserve_mutex)) {
release_pmc_hardware();
mutex_unlock(&pmc_reserve_mutex);
}
}

/*
* Setup the hardware configuration for a given hw_event_type
*/
static int __hw_perf_counter_init(struct perf_counter *counter)
{
struct perf_counter_hw_event *hw_event = &counter->hw_event;
struct hw_perf_counter *hwc = &counter->hw;
int err;

if (unlikely(!perf_counters_initialized))
return -EINVAL;

err = 0;
if (atomic_inc_not_zero(&num_counters)) {
mutex_lock(&pmc_reserve_mutex);
if (atomic_read(&num_counters) == 0 && !reserve_pmc_hardware())
err = -EBUSY;
else
atomic_inc(&num_counters);
mutex_unlock(&pmc_reserve_mutex);
}
if (err)
return err;

/*
* Generate PMC IRQs:
* (keep 'enabled' bit clear for now)
Expand Down Expand Up @@ -230,6 +303,8 @@ static int __hw_perf_counter_init(struct perf_counter *counter)
hwc->config |= pmc_ops->event_map(perf_event_id(hw_event));
}

counter->destroy = hw_perf_counter_destroy;

return 0;
}

Expand Down

0 comments on commit 4e935e4

Please sign in to comment.