Skip to contentSkip to conversation

alpha: Implement Hardware Performance Events

PATCH In review Michael Cree 3 participants 10 messages Apr 30, 2010 – Jul 28, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover alpha: Implement Hardware Performance Events

The following patches implement hardware performance events for the Alpha EV67 and later CPUs. I have had this running on a Compaq XP1000 (EV67, single CPU) for a few days now. Pretty cool -- discovered that the glibc exp2() library routine uses on average 985 cycles to execute 777 CPU instructions whereas Compaq's CPML library version of exp2() uses on average 32 cycles to execute 47 CPU instructions to achieve the same thing!

Michael Cree (3):
alpha: Add performance monitor interrupt counter.
alpha: Add wrperfmon.h header file to aid use of wrperfmon PALcall.
alpha: Implement HW performance events on the EV67 and later CPUs.

 arch/alpha/include/asm/hw_irq.h     |    1 +
 arch/alpha/include/asm/perf_event.h |    8 +-
 arch/alpha/include/asm/wrperfmon.h  |   93 ++++
 arch/alpha/kernel/Makefile          |    1 +
 arch/alpha/kernel/irq.c             |    7 +-
 arch/alpha/kernel/irq_alpha.c       |    3 +
 arch/alpha/kernel/perf_event.c      |  836 +++++++++++++++++++++++++++++++++++
 7 files changed, 946 insertions(+), 3 deletions(-)
 create mode 100644 arch/alpha/include/asm/wrperfmon.h
 create mode 100644 arch/alpha/kernel/perf_event.c
#1 lore raw

PATCH 1/3 alpha: Add performance monitor interrupt counter.

And export the count to user space via /proc/interrupts.

---
 arch/alpha/include/asm/hw_irq.h |    1 +
 arch/alpha/kernel/irq.c         |    7 +++++--
 2 files changed, 6 insertions(+), 2 deletions(-)
arch/alpha/include/asm/hw_irq.h +1 -0
index a37db0f..5050ac8 100644
@@ -3,6 +3,7 @@
 
 
 extern volatile unsigned long irq_err_count;
+DECLARE_PER_CPU(unsigned long, irq_pmi_count);
 
 #ifdef CONFIG_ALPHA_GENERIC
 #define ACTUAL_NR_IRQS	alpha_mv.nr_irqs
arch/alpha/kernel/irq.c +5 -2
index 7f912ba..fe91298 100644
@@ -31,6 +31,7 @@
 #include <asm/uaccess.h>
 
 volatile unsigned long irq_err_count;
+DEFINE_PER_CPU(unsigned long, irq_pmi_count);
 
 void ack_bad_irq(unsigned int irq)
 {
@@ -63,9 +64,7 @@ int irq_select_affinity(unsigned int irq)
 int
 show_interrupts(struct seq_file *p, void *v)
 {
-#ifdef CONFIG_SMP
 	int j;
-#endif
 	int irq = *(loff_t *) v;
 	struct irqaction * action;
 	unsigned long flags;
@@ -112,6 +111,10 @@ unlock:
 			seq_printf(p, "%10lu ", cpu_data[j].ipi_count);
 		seq_putc(p, '\n');
 #endif
+		seq_puts(p, "PMI: ");
+		for_each_online_cpu(j)
+			seq_printf(p, "%10lu ", per_cpu(irq_pmi_count, j));
+		seq_puts(p, "          Performance Monitoring\n");
 		seq_printf(p, "ERR: %10lu\n", irq_err_count);
 	}
 	return 0;
Signature · 1 line
1.7.0
#2 lore raw

PATCH 2/3 alpha: Add wrperfmon.h header file to aid use of wrperfmon PALcall.

In reply to Michael Cree
---
 arch/alpha/include/asm/wrperfmon.h |   93 ++++++++++++++++++++++++++++++++++++
 1 files changed, 93 insertions(+), 0 deletions(-)
 create mode 100644 arch/alpha/include/asm/wrperfmon.h
arch/alpha/include/asm/wrperfmon.h new file +93 -0
new file mode 100644
index 0000000..319bf67
@@ -0,0 +1,93 @@
+/*
+ * Definitions for use with the Alpha wrperfmon PAL call.
+ */
+
+#ifndef __ALPHA_WRPERFMON_H
+#define __ALPHA_WRPERFMON_H
+
+/* Following commands are implemented on all CPUs */
+#define PERFMON_CMD_DISABLE 0
+#define PERFMON_CMD_ENABLE 1
+#define PERFMON_CMD_DESIRED_EVENTS 2
+#define PERFMON_CMD_LOGGING_OPTIONS 3
+/* Following commands on EV5/EV56/PCA56 only */
+#define PERFMON_CMD_INT_FREQ 4
+#define PERFMON_CMD_ENABLE_CLEAR 7
+/* Following commands are on EV5 and better CPUs */
+#define PERFMON_CMD_READ 5
+#define PERFMON_CMD_WRITE 6
+/* Following command are on EV6 and better CPUs */
+#define PERFMON_CMD_ENABLE_WRITE 7
+/* Following command are on EV67 and better CPUs */
+#define PERFMON_CMD_I_STAT 8
+#define PERFMON_CMD_PMPC 9
+
+
+/* EV5/EV56/PCA56 Counters */
+#define EV5_PCTR_0 (1UL<<0)
+#define EV5_PCTR_1 (1UL<<1)
+#define EV5_PCTR_2 (1UL<<2)
+
+#define EV5_PCTR_0_COUNT_SHIFT 48
+#define EV5_PCTR_1_COUNT_SHIFT 32
+#define EV5_PCTR_2_COUNT_SHIFT 16
+
+#define EV5_PCTR_0_COUNT_MASK 0xffffUL
+#define EV5_PCTR_1_COUNT_MASK 0xffffUL
+#define EV5_PCTR_2_COUNT_MASK 0x3fffUL
+
+/* EV6 Counters */
+#define EV6_PCTR_0 (1UL<<0)
+#define EV6_PCTR_1 (1UL<<1)
+
+#define EV6_PCTR_0_COUNT_SHIFT 28
+#define EV6_PCTR_1_COUNT_SHIFT 6
+
+#define EV6_PCTR_0_COUNT_MASK 0xfffffUL
+#define EV6_PCTR_1_COUNT_MASK 0xfffffUL
+
+/* EV67 (and subsequent) counters */
+#define EV67_PCTR_0 (1UL<<0)
+#define EV67_PCTR_1 (1UL<<1)
+
+#define EV67_PCTR_0_COUNT_SHIFT 28
+#define EV67_PCTR_1_COUNT_SHIFT 6
+
+#define EV67_PCTR_0_COUNT_MASK 0xfffffUL
+#define EV67_PCTR_1_COUNT_MASK 0xfffffUL
+
+
+/*
+ * The Alpha Architecure Handbook, vers. 4 (1998) appears to have a misprint
+ *  in Table E-23 regarding the bits that set the event PCTR 1 counts.
+ *  Hopefully what we have here is correct.
+ */
+#define EV6_PCTR_0_EVENT_MASK 0x10UL
+#define EV6_PCTR_1_EVENT_MASK 0x0fUL
+
+/* EV6 Events */
+#define EV6_PCTR_0_CYCLES (0UL << 4)
+#define EV6_PCTR_0_INSTRUCTIONS (1UL << 4)
+
+#define EV6_PCTR_1_CYCLES 0
+#define EV6_PCTR_1_BRANCHES 1
+#define EV6_PCTR_1_BRANCH_MISPREDICTS 2
+#define EV6_PCTR_1_DTB_SINGLE_MISSES 3
+#define EV6_PCTR_1_DTB_DOUBLE_MISSES 4
+#define EV6_PCTR_1_ITB_MISSES 5
+#define EV6_PCTR_1_UNALIGNED_TRAPS 6
+#define EV6_PCTR_1_REPLY_TRAPS 7
+
+/* From the Alpha Architecture Reference Manual, 4th edn., 2002 */
+#define EV67_PCTR_MODE_MASK 0x10UL
+#define EV67_PCTR_EVENT_MASK 0x0CUL
+
+#define EV67_PCTR_MODE_PROFILEME (1UL<<4)
+#define EV67_PCTR_MODE_AGGREGATE (0UL<<4)
+
+#define EV67_PCTR_INSTR_CYCLES (0UL<<2)
+#define EV67_PCTR_CYCLES_UNDEF (1UL<<2)
+#define EV67_PCTR_INSTR_BCACHEMISS (2UL<<2)
+#define EV67_PCTR_CYCLES_MBOX (3UL<<2)
+
+#endif
Signature · 1 line
1.7.0
#3 lore raw

PATCH 3/3 alpha: Implement HW performance events on the EV67 and later CPUs.

In reply to Michael Cree

This implements hardware performance events for the EV67 and later CPUs within the Linux performance events subsystem. Only using the performance monitoring unit in HP/Compaq's so called "Aggregrate mode" is supported.

The code has been implemented in a manner that makes extension to other older Alpha CPUs relatively straightforward should some mug wish to indulge themselves.

---
 arch/alpha/include/asm/perf_event.h |    8 +-
 arch/alpha/kernel/Makefile          |    1 +
 arch/alpha/kernel/irq_alpha.c       |    3 +
 arch/alpha/kernel/perf_event.c      |  836 +++++++++++++++++++++++++++++++++++
 4 files changed, 847 insertions(+), 1 deletions(-)
 create mode 100644 arch/alpha/kernel/perf_event.c
arch/alpha/include/asm/perf_event.h +7 -1
index 3bef852..4157cd3 100644
@@ -2,8 +2,14 @@
 #define __ASM_ALPHA_PERF_EVENT_H
 
 /* Alpha only supports software events through this interface. */
-static inline void set_perf_event_pending(void) { }
+extern void set_perf_event_pending(void);
 
 #define PERF_EVENT_INDEX_OFFSET 0
 
+#ifdef CONFIG_PERF_EVENTS
+extern void init_hw_perf_events(void);
+#else
+static inline void init_hw_perf_events(void)    { }
+#endif
+
 #endif /* __ASM_ALPHA_PERF_EVENT_H */
arch/alpha/kernel/Makefile +1 -0
index 7739a62..e3dc108 100644
@@ -15,6 +15,7 @@ obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_PCI)	+= pci.o pci_iommu.o pci-sysfs.o
 obj-$(CONFIG_SRM_ENV)	+= srm_env.o
 obj-$(CONFIG_MODULES)	+= module.o
+obj-$(CONFIG_PERF_EVENTS) += perf_event.o
 
 ifdef CONFIG_ALPHA_GENERIC
 
arch/alpha/kernel/irq_alpha.c +3 -0
index cfde865..5f77afb 100644
@@ -10,6 +10,7 @@
 
 #include <asm/machvec.h>
 #include <asm/dma.h>
+#include <asm/perf_event.h>
 
 #include "proto.h"
 #include "irq_impl.h"
@@ -111,6 +112,8 @@ init_IRQ(void)
 	wrent(entInt, 0);
 
 	alpha_mv.init_irq();
+
+	init_hw_perf_events();
 }
 
 /*
arch/alpha/kernel/perf_event.c new file +836 -0

836 lines not on this page · raw message

Signature · 1 line
1.7.0
maintainer
#4 lore raw

Re: [PATCH 3/3] alpha: Implement HW performance events on the EV67 and later CPUs.

arch/alpha/kernel/perf_event.c line 810

Quoted 4 lines · Michael Cree
arch/alpha/kernel/perf_event.c new file +4 -0
+/* I wonder what this is for ??? */
+void set_perf_event_pending(void)
+{
+} 

If the PMU Interrupt comes in as an NMI we cannot take locks and such from its handler. So what we do is queue that work to be ran later. The kernel has a fallback to run this stuff from the timer hardirq, but if the PMI is IRQ context (like ARM) you can simply call perf_event_do_pending() at its tail.

Alternatively you can self-IPI like x86 does and run perf_event_do_pending() from there.

#5 lore raw

Re: [PATCH 3/3] alpha: Implement HW performance events on the EV67 and later CPUs.

Quoted 11 lines · Peter Zijlstra

arch/alpha/kernel/perf_event.c line 810

Quoted 4 lines · Michael Cree
arch/alpha/kernel/perf_event.c new file +4 -0
+/* I wonder what this is for ??? */
+void set_perf_event_pending(void)
+{
+}

If the PMU Interrupt comes in as an NMI we cannot take locks and such from its handler. So what we do is queue that work to be ran later. The kernel has a fallback to run this stuff from the timer hardirq, but if the PMI is IRQ context (like ARM) you can simply call perf_event_do_pending() at its tail.

My understanding from the Alpha HW Ref. Man. and a brief look at the kernel code is that the PMI comes in through the normal IRQ entry point on the Alpha. It is a high priority interrupt and only a machine check (i.e. detected hardware failure) can interrupt the PMU interrupt handler. I don't know whether that is an issue for taking locks or not and need advice on that.

I originally based the Alpha implementation on the Sparc implementation and would've copied the set_perf_event_pending() definition from the Sparc code, but I now see that newer Sparc code does not have that definition nor does it call perf_event_do_pending() in the interrupt handler.

So I need advice on whether to:

1) Just remove the set_perf_event_pending() definition so we are like the Sparc code,

2) Add perf_event_do_pending() to the tail of the IRQ handler so we are like the ARM code, or

3) Implement the following suggestion:

Quoted 2 lines

Alternatively you can self-IPI like x86 does and run perf_event_do_pending() from there.

Another question is what are the observeable symptoms if an incorrect option above is implemented?

Regards
Michael.

#6 lore raw

PATCH 3/3 alpha: Implement HW performance events on the EV67 and later CPUs.

In reply to Peter Zijlstra
Quoted 12 lines · Peter Zijlstra

arch/alpha/kernel/perf_event.c line 810

Quoted 4 lines · Michael Cree
arch/alpha/kernel/perf_event.c new file +4 -0
+/* I wonder what this is for ??? */
+void set_perf_event_pending(void)
+{
+}

If the PMU Interrupt comes in as an NMI we cannot take locks and such from its handler. So what we do is queue that work to be ran later. The kernel has a fallback to run this stuff from the timer hardirq, but if the PMI is IRQ context (like ARM) you can simply call perf_event_do_pending() at its tail.

Quoted 2 lines

Alternatively you can self-IPI like x86 does and run perf_event_do_pending() from there.

OK, I couldn't see how to self-IPI on the Apha (there is no obvious way in the Alpha arch code, and the Alpha HW Ref. Man. only describes such a feature being supported by the PALcode for OpenVMS which we are obviously not running) and the PMI on Alpha is at such a high priority there is no chance that the lock can be freed while in the PMI handler, so I have put the call to perf_event_do_pending() in the timer interrupt, but unlike the PowerPC code I don't know any tricks to force the timer interrupt to happen early when there is pending work, so there is a potential maximum delay of 1ms from pending work being notified to calling perf_event_do_pending(). Is this delay acceptable?

I also fix a nasty, but heretofore hidden, bug that could completely lock up a machine with continuous PMIs due to a throttled PMC being accidently re-enabled by the other PMC. It only showed up when I set the NMI flag true in the call to perf_event_overflow() and had two events counting simultaneously with a very short sample period.

I also see the x86 and Sparc code in 2.6.35 have reimplemented the call to hw_perf_group_sched_in() in terms of new functions start_txn(), cancel_txn() and commit_txn(). Is this change necessary to get the Alpha code accepted for 2.6.36? If so it would be helpful if I could have a brief description of what each function does.

The updated patch follows this message.

Cheers
Michael.

Michael Cree (1):
alpha: Implement HW performance events on the EV67 and later CPUs.

 arch/alpha/include/asm/perf_event.h |    8 +-
 arch/alpha/kernel/Makefile          |    1 +
 arch/alpha/kernel/irq_alpha.c       |    3 +
 arch/alpha/kernel/perf_event.c      |  842 +++++++++++++++++++++++++++++++++++
 arch/alpha/kernel/time.c            |   26 ++
 5 files changed, 879 insertions(+), 1 deletions(-)
 create mode 100644 arch/alpha/kernel/perf_event.c
#7 lore raw

PATCH v2 3/3 alpha: Implement HW performance events on the EV67 and later CPUs.

In reply to Peter Zijlstra

This implements hardware performance events for the EV67 and later CPUs within the Linux performance events subsystem. Only using the performance monitoring unit in HP/Compaq's so called "Aggregrate mode" is supported.

The code has been implemented in a manner that makes extension to other older Alpha CPUs relatively straightforward should some mug wish to indulge his or herself.

---
 arch/alpha/include/asm/perf_event.h |    8 +-
 arch/alpha/kernel/Makefile          |    1 +
 arch/alpha/kernel/irq_alpha.c       |    3 +
 arch/alpha/kernel/perf_event.c      |  842 +++++++++++++++++++++++++++++++++++
 arch/alpha/kernel/time.c            |   26 ++
 5 files changed, 879 insertions(+), 1 deletions(-)
 create mode 100644 arch/alpha/kernel/perf_event.c
arch/alpha/include/asm/perf_event.h +7 -1
index 3bef852..4157cd3 100644
@@ -2,8 +2,14 @@
 #define __ASM_ALPHA_PERF_EVENT_H
 
 /* Alpha only supports software events through this interface. */
-static inline void set_perf_event_pending(void) { }
+extern void set_perf_event_pending(void);
 
 #define PERF_EVENT_INDEX_OFFSET 0
 
+#ifdef CONFIG_PERF_EVENTS
+extern void init_hw_perf_events(void);
+#else
+static inline void init_hw_perf_events(void)    { }
+#endif
+
 #endif /* __ASM_ALPHA_PERF_EVENT_H */
arch/alpha/kernel/Makefile +1 -0
index 5a62fb4..1ee9b5b 100644
@@ -15,6 +15,7 @@ obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_PCI)	+= pci.o pci_iommu.o pci-sysfs.o
 obj-$(CONFIG_SRM_ENV)	+= srm_env.o
 obj-$(CONFIG_MODULES)	+= module.o
+obj-$(CONFIG_PERF_EVENTS) += perf_event.o
 
 ifdef CONFIG_ALPHA_GENERIC
 
arch/alpha/kernel/irq_alpha.c +3 -0
index cfde865..5f77afb 100644
@@ -10,6 +10,7 @@
 
 #include <asm/machvec.h>
 #include <asm/dma.h>
+#include <asm/perf_event.h>
 
 #include "proto.h"
 #include "irq_impl.h"
@@ -111,6 +112,8 @@ init_IRQ(void)
 	wrent(entInt, 0);
 
 	alpha_mv.init_irq();
+
+	init_hw_perf_events();
 }
 
 /*
arch/alpha/kernel/perf_event.c new file +842 -0

842 lines not on this page · raw message

arch/alpha/kernel/time.c +26 -0
index 1efbed8..eacceb2 100644
@@ -41,6 +41,7 @@
 #include <linux/init.h>
 #include <linux/bcd.h>
 #include <linux/profile.h>
+#include <linux/perf_event.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -82,6 +83,26 @@ static struct {
 
 unsigned long est_cycle_freq;
 
+#ifdef CONFIG_PERF_EVENTS
+
+DEFINE_PER_CPU(u8, perf_event_pending);
+
+#define set_perf_event_pending_flag()  __get_cpu_var(perf_event_pending) = 1
+#define test_perf_event_pending()      __get_cpu_var(perf_event_pending)
+#define clear_perf_event_pending()     __get_cpu_var(perf_event_pending) = 0
+
+void set_perf_event_pending(void)
+{
+	set_perf_event_pending_flag();
+}
+
+#else  /* CONFIG_PERF_EVENTS */
+
+#define test_perf_event_pending()      0
+#define clear_perf_event_pending()
+
+#endif /* CONFIG_PERF_EVENTS */
+
 
 static inline __u32 rpcc(void)
 {
@@ -175,6 +196,11 @@ irqreturn_t timer_interrupt(int irq, void *dev)
 		update_process_times(user_mode(get_irq_regs()));
 #endif
 
+	if (test_perf_event_pending()) {
+		clear_perf_event_pending();
+		perf_event_do_pending();
+	}
+
 	return IRQ_HANDLED;
 }
 
Signature · 1 line
1.7.1
#8 lore raw

Re: [PATCH 3/3 V2] alpha: Implement HW performance events on the EV67 and later CPUs.

Quoted 8 lines · On Tue, 27 Jul 2010 22:29:39 +1200 Michael Cree <[email protected]> wrote:

This implements hardware performance events for the EV67 and later CPUs within the Linux performance events subsystem. Only using the performance monitoring unit in HP/Compaq's so called "Aggregrate mode" is supported.

The code has been implemented in a manner that makes extension to other older Alpha CPUs relatively straightforward should some mug wish to indulge his or herself.

Below is the incremental diff.

I have a note here that Peter had issues with the earlier version of this patch. But I see no info here regarding what those issues were, nor whether or how they were addressed.

 arch/alpha/kernel/perf_event.c |   32 ++++++++++++++++++-------------
 arch/alpha/kernel/time.c       |   26 +++++++++++++++++++++++++
 2 files changed, 45 insertions(+), 13 deletions(-)
arch/alpha/kernel/perf_event.c renamed from arch/alpha/kernel/perf_event.c~a +19 -13
diff -puN arch/alpha/kernel/perf_event.c~a arch/alpha/kernel/perf_event.c
@@ -517,8 +517,10 @@ static void alpha_pmu_read(struct perf_e
 static void alpha_pmu_unthrottle(struct perf_event *event)
 {
 	struct hw_perf_event *hwc = &event->hw;
+	struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
 
-	wrperfmon(PERFMON_CMD_ENABLE, (1<<hwc->idx));
+	cpuc->idx_mask |= 1UL<<hwc->idx;
+	wrperfmon(PERFMON_CMD_ENABLE, (1UL<<hwc->idx));
 }
 
 
@@ -756,12 +758,19 @@ static void alpha_perf_event_irq_handler
 	__get_cpu_var(irq_pmi_count)++;
 	cpuc = &__get_cpu_var(cpu_hw_events);
 
-	/* la_ptr is the counter that overflowed. */
+	/* Completely counting through the PMC's period to trigger a new PMC
+	 * overflow interrupt while in this interrupt routine is utterly
+	 * disastrous!  The EV6 and EV67 counters are sufficiently large to
+	 * prevent this but to be really sure disable the PMCs.
+	 */
+	wrperfmon(PERFMON_CMD_DISABLE, cpuc->idx_mask);
 
-	if (la_ptr >= perf_max_events) {
+	/* la_ptr is the counter that overflowed. */
+	if (unlikely(la_ptr >= perf_max_events)) {
 		/* This should never occur! */
 		irq_err_count++;
 		pr_warning("PMI: silly index %ld\n", la_ptr);
+		wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
 		return;
 	}
 
@@ -773,17 +782,19 @@ static void alpha_perf_event_irq_handler
 			break;
 	}
 
-	if (j == cpuc->n_events) {
+	if (unlikely(j == cpuc->n_events)) {
 		/* This can occur if the event is disabled right on a PMC overflow. */
+		wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
 		return;
 	}
 
 	event = cpuc->event[j];
 
-	if (!event) {
+	if (unlikely(!event)) {
 		/* This should never occur! */
 		irq_err_count++;
 		pr_warning("PMI: No event at index %d!\n", idx);
+		wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
 		return;
 	}
 
@@ -792,24 +803,19 @@ static void alpha_perf_event_irq_handler
 	data.period = event->hw.last_period;
 
 	if (alpha_perf_event_set_period(event, hwc, idx)) {
-		if (perf_event_overflow(event, 0, &data, regs)) {
+		if (perf_event_overflow(event, 1, &data, regs)) {
 			/* Interrupts coming too quickly; "throttle" the
 			 * counter, i.e., disable it for a little while.
 			 */
-			wrperfmon(PERFMON_CMD_DISABLE, 1<<idx);
+			cpuc->idx_mask &= ~(1UL<<idx);
 		}
 	}
+	wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
 
 	return;
 }
 
 
-/* I wonder what this is for ??? */
-void set_perf_event_pending(void)
-{
-}
-
-
 
 /*
  * Init call to initialise performance events at kernel startup.
arch/alpha/kernel/time.c renamed from arch/alpha/kernel/time.c~a +26 -0
diff -puN arch/alpha/kernel/time.c~a arch/alpha/kernel/time.c
@@ -41,6 +41,7 @@
 #include <linux/init.h>
 #include <linux/bcd.h>
 #include <linux/profile.h>
+#include <linux/perf_event.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -82,6 +83,26 @@ static struct {
 
 unsigned long est_cycle_freq;
 
+#ifdef CONFIG_PERF_EVENTS
+
+DEFINE_PER_CPU(u8, perf_event_pending);
+
+#define set_perf_event_pending_flag()  __get_cpu_var(perf_event_pending) = 1
+#define test_perf_event_pending()      __get_cpu_var(perf_event_pending)
+#define clear_perf_event_pending()     __get_cpu_var(perf_event_pending) = 0
+
+void set_perf_event_pending(void)
+{
+	set_perf_event_pending_flag();
+}
+
+#else  /* CONFIG_PERF_EVENTS */
+
+#define test_perf_event_pending()      0
+#define clear_perf_event_pending()
+
+#endif /* CONFIG_PERF_EVENTS */
+
 
 static inline __u32 rpcc(void)
 {
@@ -175,6 +196,11 @@ irqreturn_t timer_interrupt(int irq, voi
 		update_process_times(user_mode(get_irq_regs()));
 #endif
 
+	if (test_perf_event_pending()) {
+		clear_perf_event_pending();
+		perf_event_do_pending();
+	}
+
 	return IRQ_HANDLED;
 }
 

_

#9 lore raw

Re: [PATCH 3/3 V2] alpha: Implement HW performance events on the EV67 and later CPUs.

Quoted 12 lines · Andrew Morton
Quoted 8 lines · On Tue, 27 Jul 2010 22:29:39 +1200 Michael Cree <[email protected]> wrote:

This implements hardware performance events for the EV67 and later CPUs within the Linux performance events subsystem. Only using the performance monitoring unit in HP/Compaq's so called "Aggregrate mode" is supported.

The code has been implemented in a manner that makes extension to other older Alpha CPUs relatively straightforward should some mug wish to indulge his or herself.

Quoted 5 lines

Below is the incremental diff.

I have a note here that Peter had issues with the earlier version of this patch. But I see no info here regarding what those issues were, nor whether or how they were addressed.

Yeah, I addressed those in the cover letter, which I include here FYI:

Quoted 12 lines · Peter Zijlstra

arch/alpha/kernel/time.c static struct { · line 97

Quoted 4 lines · Michael Cree
arch/alpha/kernel/time.c renamed from arch/alpha/kernel/time.c~a +4 -0
+/* I wonder what this is for ??? */
+void set_perf_event_pending(void)
+{
+}

If the PMU Interrupt comes in as an NMI we cannot take locks and such from its handler. So what we do is queue that work to be ran later. The kernel has a fallback to run this stuff from the timer hardirq, but if the PMI is IRQ context (like ARM) you can simply call perf_event_do_pending() at its tail.

Quoted 2 lines

Alternatively you can self-IPI like x86 does and run perf_event_do_pending() from there.

OK, I couldn't see how to self-IPI on the Apha (there is no obvious way in the Alpha arch code, and the Alpha HW Ref. Man. only describes such a feature being supported by the PALcode for OpenVMS which we are obviously not running) and the PMI on Alpha is at such a high priority there is no chance that the lock can be freed while in the PMI handler, so I have put the call to perf_event_do_pending() in the timer interrupt, but unlike the PowerPC code I don't know any tricks to force the timer interrupt to happen early when there is pending work, so there is a potential maximum delay of 1ms from pending work being notified to calling perf_event_do_pending(). Is this delay acceptable?

I also fix a nasty, but heretofore hidden, bug that could completely lock up a machine with continuous PMIs due to a throttled PMC being accidently re-enabled by the other PMC. It only showed up when I set the NMI flag true in the call to perf_event_overflow() and had two events counting simultaneously with a very short sample period.

I also see the x86 and Sparc code in 2.6.35 have reimplemented the call to hw_perf_group_sched_in() in terms of new functions start_txn(), cancel_txn() and commit_txn(). Is this change necessary to get the Alpha code accepted for 2.6.36? If so it would be helpful if I could have a brief description of what each function does.

Cheers
Michael.