Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4074b30 authored by Vince Weaver's avatar Vince Weaver Committed by Ingo Molnar
Browse files

perf/x86: Enable overflow on Intel KNC with a custom knc_pmu_handle_irq()



Although based on the Intel P6 design, the interrupt mechnanism
for KNC more closely resembles the Intel architectural
perfmon one.

We can't just re-use that code though, because KNC has different
MSR numbers for the status and ack registers.

In this case we just cut-and paste from perf_event_intel.c
with some minor changes, as it looks like it would not be
worth the trouble to change that code to be MSR-configurable.

Signed-off-by: default avatarVince Weaver <vincent.weaver@maine.edu>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Cc: eranian@gmail.com
Cc: Meadows Lawrence F <lawrence.f.meadows@intel.com>
Link: http://lkml.kernel.org/r/alpine.DEB.2.02.1210171304410.23243@vincent-weaver-1.um.maine.edu


[ Small stylistic edits. ]
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 7d011962
Loading
Loading
Loading
Loading
+77 −1
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@
#include <linux/perf_event.h>
#include <linux/types.h>

#include <asm/hardirq.h>

#include "perf_event.h"

static const u64 knc_perfmon_event_map[] =
@@ -193,6 +195,80 @@ static void knc_pmu_enable_event(struct perf_event *event)
	(void)wrmsrl_safe(hwc->config_base + hwc->idx, val);
}

static inline u64 knc_pmu_get_status(void)
{
	u64 status;

	rdmsrl(MSR_KNC_IA32_PERF_GLOBAL_STATUS, status);

	return status;
}

static inline void knc_pmu_ack_status(u64 ack)
{
	wrmsrl(MSR_KNC_IA32_PERF_GLOBAL_OVF_CONTROL, ack);
}

static int knc_pmu_handle_irq(struct pt_regs *regs)
{
	struct perf_sample_data data;
	struct cpu_hw_events *cpuc;
	int handled = 0;
	int bit, loops;
	u64 status;

	cpuc = &__get_cpu_var(cpu_hw_events);

	knc_pmu_disable_all();

	status = knc_pmu_get_status();
	if (!status) {
		knc_pmu_enable_all(0);
		return handled;
	}

	loops = 0;
again:
	knc_pmu_ack_status(status);
	if (++loops > 100) {
		WARN_ONCE(1, "perf: irq loop stuck!\n");
		perf_event_print_debug();
		goto done;
	}

	inc_irq_stat(apic_perf_irqs);

	for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) {
		struct perf_event *event = cpuc->events[bit];

		handled++;

		if (!test_bit(bit, cpuc->active_mask))
			continue;

		if (!intel_pmu_save_and_restart(event))
			continue;

		perf_sample_data_init(&data, 0, event->hw.last_period);

		if (perf_event_overflow(event, &data, regs))
			x86_pmu_stop(event, 0);
	}

	/*
	 * Repeat if there is more work to be done:
	 */
	status = knc_pmu_get_status();
	if (status)
		goto again;

done:
	knc_pmu_enable_all(0);

	return handled;
}


PMU_FORMAT_ATTR(event,	"config:0-7"	);
PMU_FORMAT_ATTR(umask,	"config:8-15"	);
PMU_FORMAT_ATTR(edge,	"config:18"	);
@@ -210,7 +286,7 @@ static struct attribute *intel_knc_formats_attr[] = {

static __initconst struct x86_pmu knc_pmu = {
	.name			= "knc",
	.handle_irq		= x86_pmu_handle_irq,
	.handle_irq		= knc_pmu_handle_irq,
	.disable_all		= knc_pmu_disable_all,
	.enable_all		= knc_pmu_enable_all,
	.enable			= knc_pmu_enable_event,