All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] alpha/perf: add ProfileMe sampling
@ 2026-10-28 18:09 Matt Turner
  2026-10-28 18:09 ` [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call Matt Turner
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Matt Turner @ 2026-10-28 18:09 UTC (permalink / raw)
  To: Richard Henderson, Magnus Lindholm, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark
  Cc: linux-alpha, linux-kernel, linux-perf-users, Matt Turner

The 21264 from EV67 on, and the 21364, have a second performance counter
mode, ProfileMe, alongside the Aggregate mode that the driver uses today.
An Aggregate mode sample reports the PC where the overflow interrupt was
taken. On an EV7 that is anywhere from 15 instructions before to 12
after the one that overflowed the counter, which makes perf annotate
output close to useless. In ProfileMe mode the CPU picks one fetched
instruction, follows it down the pipeline, and reports its PC along with
what happened to it: whether it retired, whether it trapped and why,
whether it was a mispredicted branch, stalled before map, or was killed
on a wrong path, and what PCTR1 counted while it was in flight.

Patch 1 fixes a constant passed to the wrong PALcode call, with no
functional change. Patch 2 adds the sysfs format and event attributes
the perf tool needs to name raw events. Patch 3 adds ProfileMe sampling,
selected with precise_ip or a new profileme config bit, so cycles:p and
perf record's default event use it. Patch 4 attaches the status
registers to each sample as PERF_SAMPLE_RAW data, and patch 5 adds an
option to randomize the sampling interval so that it cannot lock onto a
loop.

A separate series for perf-tools-next adds a decoder for the raw data
to perf report -D and perf script -D, and a script that sums the status
by symbol or by instruction, in the style of DCPI.

The two modes cannot run at the same time, because PCTR_CTL[SL0] sets
the mode for both counters. A ProfileMe event is scheduled alone, and
perf multiplexes it with any other hardware events. perf stat does not
ask for precise events, so it keeps using Aggregate mode.

To try it:

  perf record -e cycles:p -- ./workload
  perf annotate

I tested this on an AlphaServer ES47 (EV7). Samples from a
16-instruction loop land on all 16 instructions rather than on 4 PCs.
DTB misses and unaligned traps are attributed to the faulting load. A
branch on random data shows up as mispredicted in 21% of its samples,
and in 0% with sorted data. A 12 minute perf record -a while building
git took 4.45 million samples with no warnings.

Signed-off-by: Matt Turner <mattst88@gmail.com>
---
Matt Turner (5):
      alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call
      alpha/perf: add sysfs format and event attributes
      alpha/perf: implement ProfileMe sampling
      alpha/perf: attach ProfileMe status as PERF_SAMPLE_RAW data
      alpha/perf: optionally randomize the ProfileMe sampling interval

 arch/alpha/include/asm/perf_event.h      |   2 +
 arch/alpha/include/asm/wrperfmon.h       |  14 ++
 arch/alpha/include/uapi/asm/perf_event.h |  27 ++++
 arch/alpha/kernel/perf_event.c           | 246 +++++++++++++++++++++++++++++--
 4 files changed, 278 insertions(+), 11 deletions(-)
---
base-commit: 330517c63aa9b48227b6ab45f01003effac77ae4
change-id: 20261024-alpha-profileme-6e98a977cb54

Best regards,
--  
Matt Turner <mattst88@gmail.com>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call
  2026-10-28 18:09 [PATCH 0/5] alpha/perf: add ProfileMe sampling Matt Turner
@ 2026-10-28 18:09 ` Matt Turner
  2026-10-28 18:12   ` sashiko-bot
  2026-10-29 17:38   ` Magnus Lindholm
  2026-10-28 18:09 ` [PATCH 2/5] alpha/perf: add sysfs format and event attributes Matt Turner
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 16+ messages in thread
From: Matt Turner @ 2026-10-28 18:09 UTC (permalink / raw)
  To: Richard Henderson, Magnus Lindholm, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark
  Cc: linux-alpha, linux-kernel, linux-perf-users, Matt Turner

alpha_pmu_enable() passes EV67_PCTR_MODE_AGGREGATE to
PERFMON_CMD_LOGGING_OPTIONS. That constant is the counting mode bit for
PCTR_CTL, which belongs in the PERFMON_CMD_DESIRED_EVENTS argument. The
logging options call takes a flag in bit 0 where 0 means "count all
processes". It works only because EV67_PCTR_MODE_AGGREGATE happens to be
0.

Add a name for that argument and pass it instead. No functional change.

Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/include/asm/wrperfmon.h | 2 ++
 arch/alpha/kernel/perf_event.c     | 2 +-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/alpha/include/asm/wrperfmon.h b/arch/alpha/include/asm/wrperfmon.h
index c97b82a1f7db..1fa9bc813263 100644
--- a/arch/alpha/include/asm/wrperfmon.h
+++ b/arch/alpha/include/asm/wrperfmon.h
@@ -23,6 +23,8 @@
 #define PERFMON_CMD_I_STAT 8
 #define PERFMON_CMD_PMPC 9
 
+/* PERFMON_CMD_LOGGING_OPTIONS arguments */
+#define PERFMON_CMD_LOGGING_ALL 0
 
 /* EV5/EV56/PCA56 Counters */
 #define EV5_PCTR_0 (1UL<<0)
diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
index a3eaab094ece..2d9383425357 100644
--- a/arch/alpha/kernel/perf_event.c
+++ b/arch/alpha/kernel/perf_event.c
@@ -728,7 +728,7 @@ static void alpha_pmu_enable(struct pmu *pmu)
 		maybe_change_configuration(cpuc);
 
 		/* Start counting the desired events. */
-		wrperfmon(PERFMON_CMD_LOGGING_OPTIONS, EV67_PCTR_MODE_AGGREGATE);
+		wrperfmon(PERFMON_CMD_LOGGING_OPTIONS, PERFMON_CMD_LOGGING_ALL);
 		wrperfmon(PERFMON_CMD_DESIRED_EVENTS, cpuc->config);
 		wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
 	}

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/5] alpha/perf: add sysfs format and event attributes
  2026-10-28 18:09 [PATCH 0/5] alpha/perf: add ProfileMe sampling Matt Turner
  2026-10-28 18:09 ` [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call Matt Turner
@ 2026-10-28 18:09 ` Matt Turner
  2026-10-28 18:13   ` sashiko-bot
  2026-10-28 18:09 ` [PATCH 3/5] alpha/perf: implement ProfileMe sampling Matt Turner
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Matt Turner @ 2026-10-28 18:09 UTC (permalink / raw)
  To: Richard Henderson, Magnus Lindholm, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark
  Cc: linux-alpha, linux-kernel, linux-perf-users, Matt Turner

The Alpha PMU registers with no attribute groups, so the perf tool has no
way to learn the raw config layout, and the PMU's own events can only be
named as raw hex numbers.

Describe the event field in format/event and export aliases for the four
EV67 event types. mbox-replay has no generic hardware event, and can now
be named as cpu/mbox-replay/.

Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/perf_event.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
index 2d9383425357..70840c05d39b 100644
--- a/arch/alpha/kernel/perf_event.c
+++ b/arch/alpha/kernel/perf_event.c
@@ -212,6 +212,44 @@ static int ev67_raw_event_valid(u64 config)
 };
 
 
+PMU_FORMAT_ATTR(event, "config:0-7");
+
+static struct attribute *ev67_format_attrs[] = {
+	&format_attr_event.attr,
+	NULL,
+};
+
+static const struct attribute_group ev67_format_group = {
+	.name = "format",
+	.attrs = ev67_format_attrs,
+};
+
+/* Values must match enum ev67_pmc_event_type. */
+PMU_EVENT_ATTR_STRING(cycles, ev67_event_cycles, "event=0x01");
+PMU_EVENT_ATTR_STRING(instructions, ev67_event_instructions, "event=0x02");
+PMU_EVENT_ATTR_STRING(bcache-misses, ev67_event_bcache_misses, "event=0x03");
+PMU_EVENT_ATTR_STRING(mbox-replay, ev67_event_mbox_replay, "event=0x04");
+
+static struct attribute *ev67_event_attrs[] = {
+	&ev67_event_cycles.attr.attr,
+	&ev67_event_instructions.attr.attr,
+	&ev67_event_bcache_misses.attr.attr,
+	&ev67_event_mbox_replay.attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ev67_events_group = {
+	.name = "events",
+	.attrs = ev67_event_attrs,
+};
+
+static const struct attribute_group *ev67_attr_groups[] = {
+	&ev67_format_group,
+	&ev67_events_group,
+	NULL,
+};
+
+
 static const struct alpha_pmu_t ev67_pmu = {
 	.event_map = ev67_perfmon_event_map,
 	.max_events = ARRAY_SIZE(ev67_perfmon_event_map),
@@ -761,6 +799,7 @@ static struct pmu pmu = {
 	.start		= alpha_pmu_start,
 	.stop		= alpha_pmu_stop,
 	.read		= alpha_pmu_read,
+	.attr_groups	= ev67_attr_groups,
 	.capabilities	= PERF_PMU_CAP_NO_EXCLUDE,
 };
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/5] alpha/perf: implement ProfileMe sampling
  2026-10-28 18:09 [PATCH 0/5] alpha/perf: add ProfileMe sampling Matt Turner
  2026-10-28 18:09 ` [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call Matt Turner
  2026-10-28 18:09 ` [PATCH 2/5] alpha/perf: add sysfs format and event attributes Matt Turner
@ 2026-10-28 18:09 ` Matt Turner
  2026-10-28 18:28   ` sashiko-bot
  2026-10-29 15:18   ` Magnus Lindholm
  2026-10-28 18:09 ` [PATCH 4/5] alpha/perf: attach ProfileMe status as PERF_SAMPLE_RAW data Matt Turner
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 16+ messages in thread
From: Matt Turner @ 2026-10-28 18:09 UTC (permalink / raw)
  To: Richard Henderson, Magnus Lindholm, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark
  Cc: linux-alpha, linux-kernel, linux-perf-users, Matt Turner

The 21264 (EV67 and later) and 21364 have two performance counting modes,
and the driver only uses Aggregate mode. Its samples report the PC at
which the counter interrupt was taken, which lands a variable number of
instructions after the one that overflowed the counter. On an EV7 a
16-instruction user loop shows up at only 4 PCs, anywhere from 15
instructions before to 12 after the real one.

In ProfileMe mode, the instruction that overflows PCTR0 opens a window
during which PCTR1 counts, and when it closes PALcode delivers the
interrupt with that instruction's PC latched in PMPC.

Select ProfileMe with the new profileme config bit or with precise_ip,
since PMPC has no skid. PCTR_CTL[SL0] selects the mode for both
counters, so a ProfileMe event is scheduled alone and perf core
multiplexes it with other hardware events. The event type chooses the
SL1 setting, which decides what PCTR0 samples on and what PCTR1 counts
in the window:

  event          PCTR0         PCTR1
  instructions   instructions  cycles
  bcache-misses  instructions  Bcache misses
  cycles         cycles        inum retire delay
  mbox-replay    cycles        Mbox replay traps

Every interrupt rearms both counters, since PCTR0 stops when the window
closes. The instruction count is corrected by the overcount I_STAT
reports. The sample is attributed to PMPC with the mode bit in regs
adjusted to match, since the interrupt can be taken in a different mode
than the profiled instruction: in an unaligned access test on EV7 that
was true of three quarters of the samples. PALmode PMPCs are physical
addresses, so those samples keep the interrupted PC.

Three behaviors differ from Aggregate mode. perf record's default event
is cycles:P, so it now records a ProfileMe profile. In ProfileMe mode
PCTR0 counts valid fetched instructions rather than retired ones, so the
instructions count includes the wrong path, squashed NOPs, and both
halves of each CMOV: on an EV7 loop with a randomly mispredicted branch
it is about twice the Aggregate count. And instructions killed on a
wrong path are sampled at their own PC, as ProfileMe defines them.

Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/include/asm/wrperfmon.h |  12 +++
 arch/alpha/kernel/perf_event.c     | 157 ++++++++++++++++++++++++++++++++++---
 2 files changed, 159 insertions(+), 10 deletions(-)

diff --git a/arch/alpha/include/asm/wrperfmon.h b/arch/alpha/include/asm/wrperfmon.h
index 1fa9bc813263..7fbbfd74926a 100644
--- a/arch/alpha/include/asm/wrperfmon.h
+++ b/arch/alpha/include/asm/wrperfmon.h
@@ -92,5 +92,17 @@
 #define EV67_PCTR_CYCLES_UNDEF (1UL<<2)
 #define EV67_PCTR_INSTR_BCACHEMISS (2UL<<2)
 #define EV67_PCTR_CYCLES_MBOX (3UL<<2)
+/* In ProfileMe mode, SL1 = 1 makes PCTR1 count the inum retire delay. */
+#define EV67_PCTR_CYCLES_RETIRE_DELAY (1UL<<2)
+/* Set in both SL settings where PCTR0 counts cycles. */
+#define EV67_PCTR_PCTR0_CYCLES (1UL<<2)
+
+/* ProfileMe status, from the EV67/EV68 Hardware Reference Manuals. */
+#define EV67_I_STAT_OVR_SHIFT 30
+#define EV67_I_STAT_OVR_MASK 0x7UL
+
+/* PMPC bits 63:2 hold the profiled PC. Bit 0 set means a PALmode address. */
+#define EV67_PMPC_PAL (1UL<<0)
+#define EV67_PMPC_PC_MASK (~3UL)
 
 #endif
diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
index 70840c05d39b..24c96feda942 100644
--- a/arch/alpha/kernel/perf_event.c
+++ b/arch/alpha/kernel/perf_event.c
@@ -30,6 +30,13 @@
 #define MAX_HWEVENTS 3
 #define PMC_NO_INDEX -1
 
+/* Layout of attr.config for PERF_TYPE_RAW events; see format/ in sysfs. */
+#define ALPHA_PERF_CONFIG_EVENT		0xffULL
+#define ALPHA_PERF_CONFIG_PROFILEME	BIT_ULL(8)
+
+/* hw_perf_event.flags */
+#define ALPHA_PERF_FLAG_PROFILEME	0x1
+
 /* For tracking PMCs and the hw events they monitor on each CPU. */
 struct cpu_hw_events {
 	int			enabled;
@@ -145,6 +152,19 @@ static const struct ev67_mapping_t ev67_mapping[] = {
 };
 
 
+/*
+ * In ProfileMe mode PCTR0 selects the profiled instruction and PCTR1 counts
+ * during its window. PCTR0 counts instructions or cycles, and each choice
+ * pairs with two PCTR1 inputs, so each event type picks one SL1 setting.
+ * Indexed by enum ev67_pmc_event_type.
+ */
+static const unsigned long ev67_profileme_sl1[] = {
+	[EV67_CYCLES]		= EV67_PCTR_CYCLES_RETIRE_DELAY, /* PCTR1: retire delay */
+	[EV67_INSTRUCTIONS]	= EV67_PCTR_INSTR_CYCLES,	/* PCTR1: cycles */
+	[EV67_BCACHEMISS]	= EV67_PCTR_INSTR_BCACHEMISS,	/* PCTR1: Bcache misses */
+	[EV67_MBOXREPLAY]	= EV67_PCTR_CYCLES_MBOX,	/* PCTR1: Mbox replays */
+};
+
 /*
  * Check that a group of events can be simultaneously scheduled on to the
  * EV67 PMU.  Also allocate counter indices and config.
@@ -152,9 +172,21 @@ static const struct ev67_mapping_t ev67_mapping[] = {
 static int ev67_check_constraints(struct perf_event **event,
 				unsigned long *evtype, int n_ev)
 {
-	int idx0;
+	int idx0, j;
 	unsigned long config;
 
+	/* A ProfileMe event uses both counters, so it must be alone. */
+	for (j = 0; j < n_ev; j++) {
+		if (!(event[j]->hw.flags & ALPHA_PERF_FLAG_PROFILEME))
+			continue;
+		if (n_ev != 1)
+			return -1;
+		event[0]->hw.idx = 0;
+		event[0]->hw.config_base = EV67_PCTR_MODE_PROFILEME |
+					   ev67_profileme_sl1[evtype[0]];
+		return 0;
+	}
+
 	idx0 = ev67_mapping[evtype[0]-1].idx;
 	config = ev67_mapping[evtype[0]-1].config;
 	if (n_ev == 1)
@@ -213,9 +245,11 @@ static int ev67_raw_event_valid(u64 config)
 
 
 PMU_FORMAT_ATTR(event, "config:0-7");
+PMU_FORMAT_ATTR(profileme, "config:8");
 
 static struct attribute *ev67_format_attrs[] = {
 	&format_attr_event.attr,
+	&format_attr_profileme.attr,
 	NULL,
 };
 
@@ -276,6 +310,24 @@ static inline void alpha_write_pmc(int idx, unsigned long val)
 	wrperfmon(PERFMON_CMD_WRITE, val);
 }
 
+/* Write PCTR0 and clear PCTR1 in one call, as ProfileMe mode requires. */
+static inline void alpha_write_pmc_profileme(unsigned long val)
+{
+	val &= alpha_pmu->pmc_count_mask[0];
+	val <<= alpha_pmu->pmc_count_shift[0];
+	val |= EV67_PCTR_0 | EV67_PCTR_1;
+	wrperfmon(PERFMON_CMD_WRITE, val);
+}
+
+/* The counters an event occupies, for PERFMON_CMD_ENABLE and DISABLE. */
+static inline unsigned long alpha_event_idx_mask(struct hw_perf_event *hwc,
+						 int idx)
+{
+	if (hwc->flags & ALPHA_PERF_FLAG_PROFILEME)
+		return EV67_PCTR_0 | EV67_PCTR_1;
+	return 1UL<<idx;
+}
+
 static inline unsigned long alpha_read_pmc(int idx)
 {
 	unsigned long val;
@@ -320,7 +372,10 @@ static int alpha_perf_event_set_period(struct perf_event *event,
 
 	local64_set(&hwc->prev_count, (unsigned long)(-left));
 
-	alpha_write_pmc(idx, (unsigned long)(-left));
+	if (hwc->flags & ALPHA_PERF_FLAG_PROFILEME)
+		alpha_write_pmc_profileme((unsigned long)(-left));
+	else
+		alpha_write_pmc(idx, (unsigned long)(-left));
 
 	perf_event_update_userpage(event);
 
@@ -457,7 +512,8 @@ static void maybe_change_configuration(struct cpu_hw_events *cpuc)
 		}
 
 		if (!(hwc->state & PERF_HES_STOPPED))
-			cpuc->idx_mask |= (1<<cpuc->current_idx[j]);
+			cpuc->idx_mask |= alpha_event_idx_mask(hwc,
+							cpuc->current_idx[j]);
 	}
 	cpuc->config = cpuc->event[0]->hw.config_base;
 }
@@ -548,7 +604,7 @@ static void alpha_pmu_del(struct perf_event *event, int flags)
 			alpha_perf_event_update(event, hwc, idx, 0);
 			perf_event_update_userpage(event);
 
-			cpuc->idx_mask &= ~(1UL<<idx);
+			cpuc->idx_mask &= ~alpha_event_idx_mask(hwc, idx);
 			cpuc->n_events--;
 			break;
 		}
@@ -573,7 +629,7 @@ static void alpha_pmu_stop(struct perf_event *event, int flags)
 	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
 
 	if (!(hwc->state & PERF_HES_STOPPED)) {
-		cpuc->idx_mask &= ~(1UL<<hwc->idx);
+		cpuc->idx_mask &= ~alpha_event_idx_mask(hwc, hwc->idx);
 		hwc->state |= PERF_HES_STOPPED;
 	}
 
@@ -583,7 +639,7 @@ static void alpha_pmu_stop(struct perf_event *event, int flags)
 	}
 
 	if (cpuc->enabled)
-		wrperfmon(PERFMON_CMD_DISABLE, (1UL<<hwc->idx));
+		wrperfmon(PERFMON_CMD_DISABLE, alpha_event_idx_mask(hwc, hwc->idx));
 }
 
 
@@ -602,9 +658,9 @@ static void alpha_pmu_start(struct perf_event *event, int flags)
 
 	hwc->state = 0;
 
-	cpuc->idx_mask |= 1UL<<hwc->idx;
+	cpuc->idx_mask |= alpha_event_idx_mask(hwc, hwc->idx);
 	if (cpuc->enabled)
-		wrperfmon(PERFMON_CMD_ENABLE, (1UL<<hwc->idx));
+		wrperfmon(PERFMON_CMD_ENABLE, alpha_event_idx_mask(hwc, hwc->idx));
 }
 
 
@@ -644,6 +700,7 @@ static int __hw_perf_event_init(struct perf_event *event)
 	struct perf_event *evts[MAX_HWEVENTS];
 	unsigned long evtypes[MAX_HWEVENTS];
 	int idx_rubbish_bin[MAX_HWEVENTS];
+	bool profileme = false;
 	int ev;
 	int n;
 
@@ -657,9 +714,13 @@ static int __hw_perf_event_init(struct perf_event *event)
 	} else if (attr->type == PERF_TYPE_HW_CACHE) {
 		return -EOPNOTSUPP;
 	} else if (attr->type == PERF_TYPE_RAW) {
-		if (!alpha_pmu->raw_event_valid(attr->config))
+		if (attr->config & ~(ALPHA_PERF_CONFIG_EVENT |
+				     ALPHA_PERF_CONFIG_PROFILEME))
 			return -EINVAL;
-		ev = attr->config;
+		ev = attr->config & ALPHA_PERF_CONFIG_EVENT;
+		if (!alpha_pmu->raw_event_valid(ev))
+			return -EINVAL;
+		profileme = attr->config & ALPHA_PERF_CONFIG_PROFILEME;
 	} else {
 		return -EOPNOTSUPP;
 	}
@@ -668,6 +729,17 @@ static int __hw_perf_event_init(struct perf_event *event)
 		return ev;
 	}
 
+	/*
+	 * ProfileMe samples report the exact PC of the profiled instruction,
+	 * so it also serves any request for precise samples. It only makes
+	 * sense for sampling events.
+	 */
+	if (attr->precise_ip)
+		profileme = true;
+	if (profileme && !is_sampling_event(event))
+		return -EINVAL;
+	hwc->flags = profileme ? ALPHA_PERF_FLAG_PROFILEME : 0;
+
 	/*
 	 * We place the event type in event_base here and leave calculation
 	 * of the codes to programme the PMU for alpha_pmu_enable() because
@@ -832,6 +904,61 @@ void perf_event_print_debug(void)
 }
 
 
+/*
+ * Handle the close of a ProfileMe window on PCTR0.
+ *
+ * PCTR0 stops counting when the window closes and does not start again
+ * until PCTR_CTL is written, so the status must be read first and the
+ * counters then rearmed unconditionally.
+ */
+static void alpha_perf_event_profileme(struct perf_event *event,
+				       struct hw_perf_event *hwc,
+				       struct pt_regs *regs)
+{
+	struct perf_sample_data data;
+	struct pt_regs pm_regs;
+	unsigned long i_stat, pmpc, ovr;
+
+	i_stat = wrperfmon(PERFMON_CMD_I_STAT, 0);
+	pmpc = wrperfmon(PERFMON_CMD_PMPC, 0);
+
+	alpha_perf_event_update(event, hwc, 0, alpha_pmu->pmc_max_period[0]+1);
+
+	/*
+	 * When PCTR0 counts instructions, up to 7 of them are counted after
+	 * the overflow but before the window opens. I_STAT reports how many.
+	 */
+	if (!(hwc->config_base & EV67_PCTR_PCTR0_CYCLES)) {
+		ovr = (i_stat >> EV67_I_STAT_OVR_SHIFT) & EV67_I_STAT_OVR_MASK;
+		local64_sub(ovr, &event->count);
+		local64_add(ovr, &hwc->period_left);
+	}
+
+	/*
+	 * Attribute the sample to the profiled instruction rather than to
+	 * wherever the interrupt landed, including the user/kernel mode that
+	 * perf derives from regs. A PALmode PMPC is a physical address that
+	 * perf cannot resolve. That happens for ITB miss, Istream access
+	 * violation and interrupt redirects, and for instructions inside
+	 * PALcode. Keep the interrupted PC, which for redirects is the
+	 * instruction that trapped.
+	 */
+	pm_regs = *regs;
+	if (!(pmpc & EV67_PMPC_PAL)) {
+		pm_regs.pc = pmpc & EV67_PMPC_PC_MASK;
+		/* PS bit 3 is the mode bit that user_mode() tests. */
+		if (pm_regs.pc < TASK_SIZE)
+			pm_regs.ps |= 8UL;
+		else
+			pm_regs.ps &= ~8UL;
+	}
+
+	perf_sample_data_init(&data, 0, hwc->last_period);
+
+	if (alpha_perf_event_set_period(event, hwc, 0))
+		perf_event_overflow(event, &data, &pm_regs);
+}
+
 /*
  * Performance Monitoring Interrupt Service Routine called when a PMC
  * overflows.  The PMC that overflowed is passed in la_ptr.
@@ -866,6 +993,10 @@ static void alpha_perf_event_irq_handler(unsigned long la_ptr,
 
 	idx = la_ptr;
 
+	/* A ProfileMe event owns both counters but lives at index 0. */
+	if (cpuc->config & EV67_PCTR_MODE_PROFILEME)
+		idx = 0;
+
 	for (j = 0; j < cpuc->n_events; j++) {
 		if (cpuc->current_idx[j] == idx)
 			break;
@@ -888,6 +1019,12 @@ static void alpha_perf_event_irq_handler(unsigned long la_ptr,
 	}
 
 	hwc = &event->hw;
+	if (hwc->flags & ALPHA_PERF_FLAG_PROFILEME) {
+		alpha_perf_event_profileme(event, hwc, regs);
+		wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
+		return;
+	}
+
 	alpha_perf_event_update(event, hwc, idx, alpha_pmu->pmc_max_period[idx]+1);
 	perf_sample_data_init(&data, 0, hwc->last_period);
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/5] alpha/perf: attach ProfileMe status as PERF_SAMPLE_RAW data
  2026-10-28 18:09 [PATCH 0/5] alpha/perf: add ProfileMe sampling Matt Turner
                   ` (2 preceding siblings ...)
  2026-10-28 18:09 ` [PATCH 3/5] alpha/perf: implement ProfileMe sampling Matt Turner
@ 2026-10-28 18:09 ` Matt Turner
  2026-10-28 18:26   ` sashiko-bot
  2026-10-28 18:09 ` [PATCH 5/5] alpha/perf: optionally randomize the ProfileMe sampling interval Matt Turner
  2026-10-29  9:11 ` [PATCH 0/5] alpha/perf: add ProfileMe sampling Peter Zijlstra
  5 siblings, 1 reply; 16+ messages in thread
From: Matt Turner @ 2026-10-28 18:09 UTC (permalink / raw)
  To: Richard Henderson, Magnus Lindholm, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark
  Cc: linux-alpha, linux-kernel, linux-perf-users, Matt Turner

A ProfileMe sample's PC alone does not say what happened to the profiled
instruction. I_STAT and PCTR_CTL do: whether it trapped and why, was
mispredicted, stalled, or was killed on a wrong path, and what PCTR1
counted during the window.

With PERF_SAMPLE_RAW, attach PMPC, I_STAT and PCTR_CTL to each ProfileMe
sample in a versioned struct alpha_profileme_sample. PMPC is included
because a PALmode PMPC is not the sample's IP. Killed instructions can be
told apart by PM_KILLED_BM and VAL.

Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/include/asm/perf_event.h      |  2 ++
 arch/alpha/include/uapi/asm/perf_event.h | 27 +++++++++++++++++++++++++++
 arch/alpha/kernel/perf_event.c           | 21 +++++++++++++++++++--
 3 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/arch/alpha/include/asm/perf_event.h b/arch/alpha/include/asm/perf_event.h
index 72536566014a..f498c1d47c07 100644
--- a/arch/alpha/include/asm/perf_event.h
+++ b/arch/alpha/include/asm/perf_event.h
@@ -1,6 +1,8 @@
 #ifndef __ASM_ALPHA_PERF_EVENT_H
 #define __ASM_ALPHA_PERF_EVENT_H
 
+#include <uapi/asm/perf_event.h>
+
 /*
  * Software events have no trap frame, so perf_fetch_caller_regs() fills
  * in a zeroed pt_regs. A zero ps already means kernel mode; only pc is
diff --git a/arch/alpha/include/uapi/asm/perf_event.h b/arch/alpha/include/uapi/asm/perf_event.h
new file mode 100644
index 000000000000..0cb7d6f3cb15
--- /dev/null
+++ b/arch/alpha/include/uapi/asm/perf_event.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_ASM_ALPHA_PERF_EVENT_H
+#define _UAPI_ASM_ALPHA_PERF_EVENT_H
+
+#include <linux/types.h>
+
+/*
+ * PERF_SAMPLE_RAW payload of a ProfileMe sample from the cpu PMU.
+ *
+ * pmpc, i_stat and pctr_ctl are the values of the PMPC, I_STAT and
+ * PCTR_CTL registers when the ProfileMe window closed. i_stat holds only
+ * bits 40:30 and pctr_ctl only bits 47:0. See the EV67/EV68 Hardware
+ * Reference Manuals for the field layout.
+ *
+ * Fields may be added to the end in later versions.
+ */
+#define ALPHA_PROFILEME_SAMPLE_VERSION	1
+
+struct alpha_profileme_sample {
+	__u32 version;
+	__u32 reserved;
+	__u64 pmpc;
+	__u64 i_stat;
+	__u64 pctr_ctl;
+};
+
+#endif /* _UAPI_ASM_ALPHA_PERF_EVENT_H */
diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
index 24c96feda942..06c9afeb7c05 100644
--- a/arch/alpha/kernel/perf_event.c
+++ b/arch/alpha/kernel/perf_event.c
@@ -915,10 +915,13 @@ static void alpha_perf_event_profileme(struct perf_event *event,
 				       struct hw_perf_event *hwc,
 				       struct pt_regs *regs)
 {
+	struct alpha_profileme_sample pm;
+	struct perf_raw_record raw;
 	struct perf_sample_data data;
 	struct pt_regs pm_regs;
-	unsigned long i_stat, pmpc, ovr;
+	unsigned long pctr_ctl, i_stat, pmpc, ovr;
 
+	pctr_ctl = wrperfmon(PERFMON_CMD_READ, 0);
 	i_stat = wrperfmon(PERFMON_CMD_I_STAT, 0);
 	pmpc = wrperfmon(PERFMON_CMD_PMPC, 0);
 
@@ -941,7 +944,7 @@ static void alpha_perf_event_profileme(struct perf_event *event,
 	 * perf cannot resolve. That happens for ITB miss, Istream access
 	 * violation and interrupt redirects, and for instructions inside
 	 * PALcode. Keep the interrupted PC, which for redirects is the
-	 * instruction that trapped.
+	 * instruction that trapped, and report the real PMPC in the raw data.
 	 */
 	pm_regs = *regs;
 	if (!(pmpc & EV67_PMPC_PAL)) {
@@ -955,6 +958,20 @@ static void alpha_perf_event_profileme(struct perf_event *event,
 
 	perf_sample_data_init(&data, 0, hwc->last_period);
 
+	pm.version = ALPHA_PROFILEME_SAMPLE_VERSION;
+	pm.reserved = 0;
+	pm.pmpc = pmpc;
+	pm.i_stat = i_stat;
+	/* PALcode sign-extends bit 47 of PCTR_CTL. */
+	pm.pctr_ctl = pctr_ctl & GENMASK(47, 0);
+	raw = (struct perf_raw_record) {
+		.frag = {
+			.size = sizeof(pm),
+			.data = &pm,
+		},
+	};
+	perf_sample_save_raw_data(&data, event, &raw);
+
 	if (alpha_perf_event_set_period(event, hwc, 0))
 		perf_event_overflow(event, &data, &pm_regs);
 }

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 5/5] alpha/perf: optionally randomize the ProfileMe sampling interval
  2026-10-28 18:09 [PATCH 0/5] alpha/perf: add ProfileMe sampling Matt Turner
                   ` (3 preceding siblings ...)
  2026-10-28 18:09 ` [PATCH 4/5] alpha/perf: attach ProfileMe status as PERF_SAMPLE_RAW data Matt Turner
@ 2026-10-28 18:09 ` Matt Turner
  2026-10-28 18:21   ` sashiko-bot
  2026-10-29 15:01   ` Magnus Lindholm
  2026-10-29  9:11 ` [PATCH 0/5] alpha/perf: add ProfileMe sampling Peter Zijlstra
  5 siblings, 2 replies; 16+ messages in thread
From: Matt Turner @ 2026-10-28 18:09 UTC (permalink / raw)
  To: Richard Henderson, Magnus Lindholm, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark
  Cc: linux-alpha, linux-kernel, linux-perf-users, Matt Turner

A fixed sampling interval can phase-lock with a loop whose length divides
it, so that the same few instructions are profiled over and over while
the rest of the loop is never seen. DCPI avoided this by randomizing the
interval.

Add a randomize config bit that varies each interval by up to 1/16 of
the period in either direction. Only ProfileMe events accept it, since
Aggregate mode samples already land a variable distance from the
instruction that overflowed. Counts are unaffected, because prev_count
already records the value actually written to the counter.

The jitter comes from a per-CPU prandom state rather than
get_random_u32_below(), which takes the batched entropy local lock and
triggers an invalid wait context report from lockdep in the PMI.

Signed-off-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/perf_event.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
index 06c9afeb7c05..da8262c74d04 100644
--- a/arch/alpha/kernel/perf_event.c
+++ b/arch/alpha/kernel/perf_event.c
@@ -16,6 +16,7 @@
 #include <linux/kdebug.h>
 #include <linux/mutex.h>
 #include <linux/init.h>
+#include <linux/prandom.h>
 
 #include <asm/hwrpb.h>
 #include <linux/atomic.h>
@@ -33,9 +34,11 @@
 /* Layout of attr.config for PERF_TYPE_RAW events; see format/ in sysfs. */
 #define ALPHA_PERF_CONFIG_EVENT		0xffULL
 #define ALPHA_PERF_CONFIG_PROFILEME	BIT_ULL(8)
+#define ALPHA_PERF_CONFIG_RANDOMIZE	BIT_ULL(9)
 
 /* hw_perf_event.flags */
 #define ALPHA_PERF_FLAG_PROFILEME	0x1
+#define ALPHA_PERF_FLAG_RANDOMIZE	0x2
 
 /* For tracking PMCs and the hw events they monitor on each CPU. */
 struct cpu_hw_events {
@@ -59,6 +62,9 @@ struct cpu_hw_events {
 };
 DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
 
+/* For randomizing sample intervals from the PMI, where locks are not allowed. */
+static DEFINE_PER_CPU(struct rnd_state, alpha_perf_rnd);
+
 
 
 /*
@@ -246,10 +252,12 @@ static int ev67_raw_event_valid(u64 config)
 
 PMU_FORMAT_ATTR(event, "config:0-7");
 PMU_FORMAT_ATTR(profileme, "config:8");
+PMU_FORMAT_ATTR(randomize, "config:9");
 
 static struct attribute *ev67_format_attrs[] = {
 	&format_attr_event.attr,
 	&format_attr_profileme.attr,
+	&format_attr_randomize.attr,
 	NULL,
 };
 
@@ -360,6 +368,20 @@ static int alpha_perf_event_set_period(struct perf_event *event,
 		ret = 1;
 	}
 
+	/*
+	 * Vary each new interval by up to 1/16 of the period either way, so
+	 * the samples cannot phase-lock with a loop whose length divides the
+	 * period. The count stays exact because prev_count records the value
+	 * actually written.
+	 */
+	if (ret && (hwc->flags & ALPHA_PERF_FLAG_RANDOMIZE) && period >= 8) {
+		long jitter = period / 8;
+		u32 r = prandom_u32_state(this_cpu_ptr(&alpha_perf_rnd));
+
+		left += (long)((u64)r * jitter >> 32) - jitter / 2;
+		local64_set(&hwc->period_left, left);
+	}
+
 	/*
 	 * Hardware restrictions require that the counters must not be
 	 * written with values that are too close to the maximum period.
@@ -715,7 +737,8 @@ static int __hw_perf_event_init(struct perf_event *event)
 		return -EOPNOTSUPP;
 	} else if (attr->type == PERF_TYPE_RAW) {
 		if (attr->config & ~(ALPHA_PERF_CONFIG_EVENT |
-				     ALPHA_PERF_CONFIG_PROFILEME))
+				     ALPHA_PERF_CONFIG_PROFILEME |
+				     ALPHA_PERF_CONFIG_RANDOMIZE))
 			return -EINVAL;
 		ev = attr->config & ALPHA_PERF_CONFIG_EVENT;
 		if (!alpha_pmu->raw_event_valid(ev))
@@ -740,6 +763,14 @@ static int __hw_perf_event_init(struct perf_event *event)
 		return -EINVAL;
 	hwc->flags = profileme ? ALPHA_PERF_FLAG_PROFILEME : 0;
 
+	if (attr->type == PERF_TYPE_RAW &&
+	    (attr->config & ALPHA_PERF_CONFIG_RANDOMIZE)) {
+		if (!profileme)
+			return -EINVAL;
+		prandom_init_once(&alpha_perf_rnd);
+		hwc->flags |= ALPHA_PERF_FLAG_RANDOMIZE;
+	}
+
 	/*
 	 * We place the event type in event_base here and leave calculation
 	 * of the codes to programme the PMU for alpha_pmu_enable() because

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call
  2026-10-28 18:09 ` [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call Matt Turner
@ 2026-10-28 18:12   ` sashiko-bot
  2026-10-29 17:38   ` Magnus Lindholm
  1 sibling, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-28 18:12 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-perf-users

> alpha_pmu_enable() passes EV67_PCTR_MODE_AGGREGATE to
> PERFMON_CMD_LOGGING_OPTIONS. That constant is the counting mode bit for
> PCTR_CTL, which belongs in the PERFMON_CMD_DESIRED_EVENTS argument. The
> logging options call takes a flag in bit 0 where 0 means "count all
> processes". It works only because EV67_PCTR_MODE_AGGREGATE happens to be
> 0.
> 
> Add a name for that argument and pass it instead. No functional change.
> 
> Signed-off-by: Matt Turner <mattst88@gmail.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261028-alpha-profileme-v1-0-d44b52bf2500@gmail.com?part=1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/5] alpha/perf: add sysfs format and event attributes
  2026-10-28 18:09 ` [PATCH 2/5] alpha/perf: add sysfs format and event attributes Matt Turner
@ 2026-10-28 18:13   ` sashiko-bot
  0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-28 18:13 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-perf-users

> The Alpha PMU registers with no attribute groups, so the perf tool has no
> way to learn the raw config layout, and the PMU's own events can only be
> named as raw hex numbers.
> 
> Describe the event field in format/event and export aliases for the four
> EV67 event types. mbox-replay has no generic hardware event, and can now
> be named as cpu/mbox-replay/.
> 
> Signed-off-by: Matt Turner <mattst88@gmail.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261028-alpha-profileme-v1-0-d44b52bf2500@gmail.com?part=2


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 5/5] alpha/perf: optionally randomize the ProfileMe sampling interval
  2026-10-28 18:09 ` [PATCH 5/5] alpha/perf: optionally randomize the ProfileMe sampling interval Matt Turner
@ 2026-10-28 18:21   ` sashiko-bot
  2026-10-29 15:01   ` Magnus Lindholm
  1 sibling, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-28 18:21 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] alpha/perf: 64-bit overflow in alpha_perf_event_set_period() jitter math
--

commit f5fe32a707f112845a6641dd8d8b1fcd7a1ddb70
Author: Matt Turner <mattst88@gmail.com>

alpha/perf: optionally randomize the ProfileMe sampling interval

This commit adds a randomize configuration bit for Alpha ProfileMe raw
perf events to vary each sampling interval by up to 1/16 of the period in
either direction using a per-CPU prandom state.

> diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
> index 06c9afeb7c05e..da8262c74d043 100644
> --- a/arch/alpha/kernel/perf_event.c
> +++ b/arch/alpha/kernel/perf_event.c

[ ... ]

> @@ -360,6 +368,20 @@ static int alpha_perf_event_set_period(struct perf_event *event,
>  		ret = 1;
>  	}
>  
> +	/*
> +	 * Vary each new interval by up to 1/16 of the period either way, so
> +	 * the samples cannot phase-lock with a loop whose length divides the
> +	 * period. The count stays exact because prev_count records the value
> +	 * actually written.
> +	 */
> +	if (ret && (hwc->flags & ALPHA_PERF_FLAG_RANDOMIZE) && period >= 8) {
> +		long jitter = period / 8;
> +		u32 r = prandom_u32_state(this_cpu_ptr(&alpha_perf_rnd));
> +
> +		left += (long)((u64)r * jitter >> 32) - jitter / 2;
> +		local64_set(&hwc->period_left, left);
> +	}

[Severity: Medium]
Can the multiplication (u64)r * jitter overflow 64 bits in
alpha_perf_event_set_period() in arch/alpha/kernel/perf_event.c when a user
specifies a large 64-bit sample_period?

In __perf_event_open() in kernel/events/core.c, userspace can supply any
63-bit attr.sample_period:

	if (attr.freq) {
		if (attr.sample_freq > sysctl_perf_event_sample_rate)
			return -EINVAL;
	} else {
		if (attr.sample_period & (1ULL << 63))
			return -EINVAL;
	}

When hwc->sample_period is at least 1UL << 35, jitter (period / 8) is at
least 1UL << 32, so (u64)r * jitter overflows 64-bit arithmetic and wraps.
Because the 64-bit product is then shifted right by 32, the positive term
is bounded to U32_MAX while jitter / 2 remains a full 64-bit value, causing
every interval to be systematically shortened by roughly period / 16 rather
than varying symmetrically within [-period / 16, +period / 16). In addition,
if sample_period is near LONG_MAX, adding a positive 1/16 offset to left can
overflow signed long into a negative period_left.

Would using mul_u64_u32_shr(jitter, r, 32) avoid the multiplication overflow?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261028-alpha-profileme-v1-0-d44b52bf2500@gmail.com?part=5

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/5] alpha/perf: attach ProfileMe status as PERF_SAMPLE_RAW data
  2026-10-28 18:09 ` [PATCH 4/5] alpha/perf: attach ProfileMe status as PERF_SAMPLE_RAW data Matt Turner
@ 2026-10-28 18:26   ` sashiko-bot
  0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-28 18:26 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] alpha/perf: misaligned u64 fields and unwritten pad in alpha_profileme_sample
--

commit 6d712b2296772549d16528b224255d9e1a7bde43
Author: Matt Turner <mattst88@gmail.com>

alpha/perf: attach ProfileMe status as PERF_SAMPLE_RAW data

This commit attaches the PMPC, I_STAT, and PCTR_CTL register values to
ProfileMe samples as PERF_SAMPLE_RAW data using a versioned UAPI struct
alpha_profileme_sample.

> diff --git a/arch/alpha/include/uapi/asm/perf_event.h b/arch/alpha/include/uapi/asm/perf_event.h
> new file mode 100644
> index 0000000000000..0cb7d6f3cb154
> --- /dev/null
> +++ b/arch/alpha/include/uapi/asm/perf_event.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +#ifndef _UAPI_ASM_ALPHA_PERF_EVENT_H
> +#define _UAPI_ASM_ALPHA_PERF_EVENT_H
> +
> +#include <linux/types.h>
> +
> +/*
> + * PERF_SAMPLE_RAW payload of a ProfileMe sample from the cpu PMU.
> + *
> + * pmpc, i_stat and pctr_ctl are the values of the PMPC, I_STAT and
> + * PCTR_CTL registers when the ProfileMe window closed. i_stat holds only
> + * bits 40:30 and pctr_ctl only bits 47:0. See the EV67/EV68 Hardware
> + * Reference Manuals for the field layout.
> + *
> + * Fields may be added to the end in later versions.
> + */
> +#define ALPHA_PROFILEME_SAMPLE_VERSION	1
> +
> +struct alpha_profileme_sample {
> +	__u32 version;
> +	__u32 reserved;
> +	__u64 pmpc;
> +	__u64 i_stat;
> +	__u64 pctr_ctl;
> +};

[ ... ]

> diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
> index 24c96feda9424..06c9afeb7c05e 100644
> --- a/arch/alpha/kernel/perf_event.c
> +++ b/arch/alpha/kernel/perf_event.c

[ ... ]

> @@ -955,6 +958,20 @@ static void alpha_perf_event_profileme(struct perf_event *event,
>  
>  	perf_sample_data_init(&data, 0, hwc->last_period);
>  
> +	pm.version = ALPHA_PROFILEME_SAMPLE_VERSION;
> +	pm.reserved = 0;
> +	pm.pmpc = pmpc;
> +	pm.i_stat = i_stat;
> +	/* PALcode sign-extends bit 47 of PCTR_CTL. */
> +	pm.pctr_ctl = pctr_ctl & GENMASK(47, 0);
> +	raw = (struct perf_raw_record) {
> +		.frag = {
> +			.size = sizeof(pm),
> +			.data = &pm,
> +		},
> +	};
> +	perf_sample_save_raw_data(&data, event, &raw);

[Severity: Medium]
Does including the __u32 reserved field in struct alpha_profileme_sample
misalign pmpc, i_stat, and pctr_ctl in the perf ring buffer and leave 4 bytes
of trailing padding unwritten?

In a PERF_RECORD_SAMPLE record, all fields preceding PERF_SAMPLE_RAW are
multiples of sizeof(u64). When perf_output_sample() writes the
PERF_SAMPLE_RAW section, it begins on an 8-byte boundary by outputting the
4-byte u32 raw->size field, which places the start of the raw payload at a 4
(mod 8) byte offset in the ring buffer.

Because struct alpha_profileme_sample has both __u32 version and __u32
reserved before __u64 pmpc, its total size is 32 bytes and pmpc is at offset
8 within the struct. Placed after the 4-byte raw->size field in the sample
record, pmpc, i_stat, and pctr_ctl land at 4 (mod 8) offsets rather than
8-byte aligned offsets, triggering unaligned 64-bit load traps on Alpha when
dereferenced in place and breaking 64-bit byte-swapping in
evsel__parse_sample().

In addition, when alpha_perf_event_profileme() passes sizeof(pm) (32 bytes) to
perf_sample_save_raw_data():

include/linux/perf_event.h:perf_sample_save_raw_data() {
	...
	size = round_up(sum + sizeof(u32), sizeof(u64));
	raw->size = size - sizeof(u32);
	frag->pad = raw->size - sum;
	...
}

round_up(32 + 4, 8) evaluates to 40, which sets raw->size to 36 and
frag->pad to 4. When perf_element overflows into
alpha_perf_event_profileme() -> perf_event_overflow() ->
__perf_event_overflow() -> perf_output_sample():

kernel/events/core.c:perf_output_sample() {
	...
			if (frag->pad)
				__output_skip(handle, NULL, frag->pad);
	...
}

__output_skip() advances the ring buffer position by 4 bytes without writing
to the buffer, leaving 4 trailing bytes of the 36-byte raw payload unwritten
(exposing stale ring buffer data after wrap-around).

Would it make sense to drop __u32 reserved from struct alpha_profileme_sample
so that the struct is 28 bytes (4 mod 8), keeping pmpc, i_stat, and pctr_ctl
8-byte aligned in the sample record with raw->size == 28 and frag->pad == 0?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261028-alpha-profileme-v1-0-d44b52bf2500@gmail.com?part=4

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/5] alpha/perf: implement ProfileMe sampling
  2026-10-28 18:09 ` [PATCH 3/5] alpha/perf: implement ProfileMe sampling Matt Turner
@ 2026-10-28 18:28   ` sashiko-bot
  2026-10-29 15:18   ` Magnus Lindholm
  1 sibling, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-28 18:28 UTC (permalink / raw)
  To: Matt Turner; +Cc: linux-perf-users

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] alpha/perf: wrong register and stack state in alpha_perf_event_profileme()
--

commit 4ba1c82f19c556ae6c582ff386ff198b05c6a1eb
Author: Matt Turner <mattst88@gmail.com>

alpha/perf: implement ProfileMe sampling

This commit implements ProfileMe performance counter sampling on Alpha
EV67 and later processors, selecting it via the profileme config attribute
or precise_ip and attributing samples to the PC latched in PMPC.

> diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
> index 70840c05d39bf..24c96feda9424 100644
> --- a/arch/alpha/kernel/perf_event.c
> +++ b/arch/alpha/kernel/perf_event.c

[ ... ]

> @@ -832,6 +904,61 @@ void perf_event_print_debug(void)
>  }
>  
>  
> +/*
> + * Handle the close of a ProfileMe window on PCTR0.
> + *
> + * PCTR0 stops counting when the window closes and does not start again
> + * until PCTR_CTL is written, so the status must be read first and the
> + * counters then rearmed unconditionally.
> + */
> +static void alpha_perf_event_profileme(struct perf_event *event,
> +				       struct hw_perf_event *hwc,
> +				       struct pt_regs *regs)
> +{
> +	struct perf_sample_data data;
> +	struct pt_regs pm_regs;
> +	unsigned long i_stat, pmpc, ovr;
> +
> +	i_stat = wrperfmon(PERFMON_CMD_I_STAT, 0);
> +	pmpc = wrperfmon(PERFMON_CMD_PMPC, 0);
> +
> +	alpha_perf_event_update(event, hwc, 0, alpha_pmu->pmc_max_period[0]+1);
> +
> +	/*
> +	 * When PCTR0 counts instructions, up to 7 of them are counted after
> +	 * the overflow but before the window opens. I_STAT reports how many.
> +	 */
> +	if (!(hwc->config_base & EV67_PCTR_PCTR0_CYCLES)) {
> +		ovr = (i_stat >> EV67_I_STAT_OVR_SHIFT) & EV67_I_STAT_OVR_MASK;
> +		local64_sub(ovr, &event->count);
> +		local64_add(ovr, &hwc->period_left);
> +	}
> +
> +	/*
> +	 * Attribute the sample to the profiled instruction rather than to
> +	 * wherever the interrupt landed, including the user/kernel mode that
> +	 * perf derives from regs. A PALmode PMPC is a physical address that
> +	 * perf cannot resolve. That happens for ITB miss, Istream access
> +	 * violation and interrupt redirects, and for instructions inside
> +	 * PALcode. Keep the interrupted PC, which for redirects is the
> +	 * instruction that trapped.
> +	 */
> +	pm_regs = *regs;
> +	if (!(pmpc & EV67_PMPC_PAL)) {
> +		pm_regs.pc = pmpc & EV67_PMPC_PC_MASK;
> +		/* PS bit 3 is the mode bit that user_mode() tests. */
> +		if (pm_regs.pc < TASK_SIZE)
> +			pm_regs.ps |= 8UL;
> +		else
> +			pm_regs.ps &= ~8UL;
> +	}
> +
> +	perf_sample_data_init(&data, 0, hwc->last_period);
> +
> +	if (alpha_perf_event_set_period(event, hwc, 0))
> +		perf_event_overflow(event, &data, &pm_regs);
> +}

[Severity: High]
When a user-mode instruction is profiled (pm_regs.pc < TASK_SIZE), but the
PMI is delivered after the CPU has entered kernel mode (!user_mode(regs)),
regs is the kernel interrupt trap frame containing live kernel GPRs, while
the user task's registers live in task_pt_regs(current).

By copying *regs into pm_regs and setting pm_regs.ps |= 8UL before calling
perf_event_overflow(), user_mode(&pm_regs) evaluates to true in
kernel/events/core.c:perf_sample_regs_user():

	if (user_mode(regs)) {
		regs_user->abi = perf_reg_abi(current);
		regs_user->regs = regs;
	} else if (is_user_task(current)) {
		perf_get_regs_user(regs_user, regs);
	}

and in kernel/events/core.c:perf_sample_regs_intr():

	if (exclude_kernel && !user_mode(regs)) {
		regs_intr->abi = PERF_SAMPLE_REGS_ABI_NONE;
		regs_intr->regs = NULL;
	} else {
		regs_intr->regs = regs;
		regs_intr->abi = perf_reg_abi(current);
	}

Doesn't this cause PERF_SAMPLE_REGS_USER and PERF_SAMPLE_REGS_INTR (even
when attr.exclude_kernel is set) to record live kernel registers from the
kernel interrupt trap frame into the user-visible perf ring buffer instead
of fetching task_pt_regs(current), while also corrupting user DWARF
callgraph unwinding?

Additionally, if current is a kernel thread when this happens,
perf_sample_regs_user() sets regs_user->regs = &pm_regs while
perf_sample_ustack_size() returns 0 (since current->mm is NULL), which
causes perf_prepare_sample() and perf_output_sample_ustack() to disagree
on the PERF_SAMPLE_STACK_USER record size.

Similarly, when !user_mode(&pm_regs) is true (pm_regs.pc >= TASK_SIZE),
passing the stack-local &pm_regs to perf_event_overflow() affects
arch/alpha/kernel/perf_regs.c:perf_reg_value():

	if (idx == PERF_REG_ALPHA_SP) {
		if (user_mode(regs))
			return user_stack_pointer(regs);
		if (!object_is_on_stack(regs))
			return 0;
		/* Same kernel sp approximation as stacktrace.c. */
		return (unsigned long)(regs + 1);
	}

and arch/alpha/kernel/stacktrace.c:arch_stack_walk():

	if (regs && task == current) {
		ksp = (unsigned long)(regs + 1);
	}

Because pm_regs is a local variable on the stack, object_is_on_stack(&pm_regs)
is true, so &pm_regs + 1 points inside alpha_perf_event_profileme()'s own
stack frame rather than the end of the interrupted trap frame (regs + 1).
And if the PMI landed in user mode after a profiled kernel instruction,
clearing bit 3 of pm_regs.ps causes perf_callchain_kernel() and
PERF_SAMPLE_REGS_INTR to treat the user register frame as a kernel frame
and unwind the kernel stack starting from &pm_regs + 1.

Could the PC and PS adjustments be made in-place on regs (and restored
after perf_event_overflow()) only when the PMPC mode matches the
interrupted mode in regs, or how should mode-mismatched samples handle register
and stack sampling?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261028-alpha-profileme-v1-0-d44b52bf2500@gmail.com?part=3

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/5] alpha/perf: add ProfileMe sampling
  2026-10-28 18:09 [PATCH 0/5] alpha/perf: add ProfileMe sampling Matt Turner
                   ` (4 preceding siblings ...)
  2026-10-28 18:09 ` [PATCH 5/5] alpha/perf: optionally randomize the ProfileMe sampling interval Matt Turner
@ 2026-10-29  9:11 ` Peter Zijlstra
  2026-10-29 15:34   ` Matt Turner
  5 siblings, 1 reply; 16+ messages in thread
From: Peter Zijlstra @ 2026-10-29  9:11 UTC (permalink / raw)
  To: Matt Turner
  Cc: Richard Henderson, Magnus Lindholm, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, linux-alpha, linux-kernel, linux-perf-users

On Wed, Oct 28, 2026 at 02:09:29PM -0400, Matt Turner wrote:
> The 21264 from EV67 on, and the 21364, have a second performance counter
> mode, ProfileMe, alongside the Aggregate mode that the driver uses today.
> An Aggregate mode sample reports the PC where the overflow interrupt was
> taken. On an EV7 that is anywhere from 15 instructions before to 12
> after the one that overflowed the counter, which makes perf annotate
> output close to useless. In ProfileMe mode the CPU picks one fetched
> instruction, follows it down the pipeline, and reports its PC along with
> what happened to it: whether it retired, whether it trapped and why,
> whether it was a mispredicted branch, stalled before map, or was killed
> on a wrong path, and what PCTR1 counted while it was in flight.

Isn't this the thing that ended up as AMD's IBS?

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 5/5] alpha/perf: optionally randomize the ProfileMe sampling interval
  2026-10-28 18:09 ` [PATCH 5/5] alpha/perf: optionally randomize the ProfileMe sampling interval Matt Turner
  2026-10-28 18:21   ` sashiko-bot
@ 2026-10-29 15:01   ` Magnus Lindholm
  1 sibling, 0 replies; 16+ messages in thread
From: Magnus Lindholm @ 2026-10-29 15:01 UTC (permalink / raw)
  To: Matt Turner
  Cc: Richard Henderson, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, linux-alpha, linux-kernel, linux-perf-users

Hi Matt,

On Wed, Oct 28, 2026 at 7:09 PM Matt Turner <mattst88@gmail.com> wrote:
>
> A fixed sampling interval can phase-lock with a loop whose length divides
> it, so that the same few instructions are profiled over and over while
> the rest of the loop is never seen. DCPI avoided this by randomizing the
> interval.
>
> Add a randomize config bit that varies each interval by up to 1/16 of
> the period in either direction. Only ProfileMe events accept it, since
> Aggregate mode samples already land a variable distance from the
> instruction that overflowed. Counts are unaffected, because prev_count
> already records the value actually written to the counter.
>
> The jitter comes from a per-CPU prandom state rather than
> get_random_u32_below(), which takes the batched entropy local lock and
> triggers an invalid wait context report from lockdep in the PMI.
>
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
>  arch/alpha/kernel/perf_event.c | 33 ++++++++++++++++++++++++++++++++-
>  1 file changed, 32 insertions(+), 1 deletion(-)
>
> diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
> index 06c9afeb7c05..da8262c74d04 100644
> --- a/arch/alpha/kernel/perf_event.c
> +++ b/arch/alpha/kernel/perf_event.c
> @@ -16,6 +16,7 @@
>  #include <linux/kdebug.h>
>  #include <linux/mutex.h>
>  #include <linux/init.h>
> +#include <linux/prandom.h>
>
>  #include <asm/hwrpb.h>
>  #include <linux/atomic.h>
> @@ -33,9 +34,11 @@
>  /* Layout of attr.config for PERF_TYPE_RAW events; see format/ in sysfs. */
>  #define ALPHA_PERF_CONFIG_EVENT                0xffULL
>  #define ALPHA_PERF_CONFIG_PROFILEME    BIT_ULL(8)
> +#define ALPHA_PERF_CONFIG_RANDOMIZE    BIT_ULL(9)
>
>  /* hw_perf_event.flags */
>  #define ALPHA_PERF_FLAG_PROFILEME      0x1
> +#define ALPHA_PERF_FLAG_RANDOMIZE      0x2
>
>  /* For tracking PMCs and the hw events they monitor on each CPU. */
>  struct cpu_hw_events {
> @@ -59,6 +62,9 @@ struct cpu_hw_events {
>  };
>  DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
>
> +/* For randomizing sample intervals from the PMI, where locks are not allowed. */
> +static DEFINE_PER_CPU(struct rnd_state, alpha_perf_rnd);
> +
>
>
>  /*
> @@ -246,10 +252,12 @@ static int ev67_raw_event_valid(u64 config)
>
>  PMU_FORMAT_ATTR(event, "config:0-7");
>  PMU_FORMAT_ATTR(profileme, "config:8");
> +PMU_FORMAT_ATTR(randomize, "config:9");
>
>  static struct attribute *ev67_format_attrs[] = {
>         &format_attr_event.attr,
>         &format_attr_profileme.attr,
> +       &format_attr_randomize.attr,
>         NULL,
>  };
>
> @@ -360,6 +368,20 @@ static int alpha_perf_event_set_period(struct perf_event *event,
>                 ret = 1;
>         }
>
> +       /*
> +        * Vary each new interval by up to 1/16 of the period either way, so
> +        * the samples cannot phase-lock with a loop whose length divides the
> +        * period. The count stays exact because prev_count records the value
> +        * actually written.
> +        */
> +       if (ret && (hwc->flags & ALPHA_PERF_FLAG_RANDOMIZE) && period >= 8) {
> +               long jitter = period / 8;
> +               u32 r = prandom_u32_state(this_cpu_ptr(&alpha_perf_rnd));
> +
> +               left += (long)((u64)r * jitter >> 32) - jitter / 2;
> +               local64_set(&hwc->period_left, left);
> +       }
> +

One minor corner case? This  multiplication can overflow for very
large  sample periods. I guess normal sampling periods are not
affected, is it worth using overflow-safe multiply/shift to avoid this
limitation?

>         /*
>          * Hardware restrictions require that the counters must not be
>          * written with values that are too close to the maximum period.
> @@ -715,7 +737,8 @@ static int __hw_perf_event_init(struct perf_event *event)
>                 return -EOPNOTSUPP;
>         } else if (attr->type == PERF_TYPE_RAW) {
>                 if (attr->config & ~(ALPHA_PERF_CONFIG_EVENT |
> -                                    ALPHA_PERF_CONFIG_PROFILEME))
> +                                    ALPHA_PERF_CONFIG_PROFILEME |
> +                                    ALPHA_PERF_CONFIG_RANDOMIZE))
>                         return -EINVAL;
>                 ev = attr->config & ALPHA_PERF_CONFIG_EVENT;
>                 if (!alpha_pmu->raw_event_valid(ev))
> @@ -740,6 +763,14 @@ static int __hw_perf_event_init(struct perf_event *event)
>                 return -EINVAL;
>         hwc->flags = profileme ? ALPHA_PERF_FLAG_PROFILEME : 0;
>
> +       if (attr->type == PERF_TYPE_RAW &&
> +           (attr->config & ALPHA_PERF_CONFIG_RANDOMIZE)) {
> +               if (!profileme)
> +                       return -EINVAL;
> +               prandom_init_once(&alpha_perf_rnd);
> +               hwc->flags |= ALPHA_PERF_FLAG_RANDOMIZE;
> +       }
> +
>         /*
>          * We place the event type in event_base here and leave calculation
>          * of the codes to programme the PMU for alpha_pmu_enable() because
>
> --
> 2.55.0
>

I'll report back once I've done some testing on real hardware.

Thanks

Magnus

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/5] alpha/perf: implement ProfileMe sampling
  2026-10-28 18:09 ` [PATCH 3/5] alpha/perf: implement ProfileMe sampling Matt Turner
  2026-10-28 18:28   ` sashiko-bot
@ 2026-10-29 15:18   ` Magnus Lindholm
  1 sibling, 0 replies; 16+ messages in thread
From: Magnus Lindholm @ 2026-10-29 15:18 UTC (permalink / raw)
  To: Matt Turner
  Cc: Richard Henderson, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, linux-alpha, linux-kernel, linux-perf-users

Hi Matt,

On Wed, Oct 28, 2026 at 7:09 PM Matt Turner <mattst88@gmail.com> wrote:
>
> The 21264 (EV67 and later) and 21364 have two performance counting modes,
> and the driver only uses Aggregate mode. Its samples report the PC at
> which the counter interrupt was taken, which lands a variable number of
> instructions after the one that overflowed the counter. On an EV7 a
> 16-instruction user loop shows up at only 4 PCs, anywhere from 15
> instructions before to 12 after the real one.
>
> In ProfileMe mode, the instruction that overflows PCTR0 opens a window
> during which PCTR1 counts, and when it closes PALcode delivers the
> interrupt with that instruction's PC latched in PMPC.
>
> Select ProfileMe with the new profileme config bit or with precise_ip,
> since PMPC has no skid. PCTR_CTL[SL0] selects the mode for both
> counters, so a ProfileMe event is scheduled alone and perf core
> multiplexes it with other hardware events. The event type chooses the
> SL1 setting, which decides what PCTR0 samples on and what PCTR1 counts
> in the window:
>
>   event          PCTR0         PCTR1
>   instructions   instructions  cycles
>   bcache-misses  instructions  Bcache misses
>   cycles         cycles        inum retire delay
>   mbox-replay    cycles        Mbox replay traps
>
> Every interrupt rearms both counters, since PCTR0 stops when the window
> closes. The instruction count is corrected by the overcount I_STAT
> reports. The sample is attributed to PMPC with the mode bit in regs
> adjusted to match, since the interrupt can be taken in a different mode
> than the profiled instruction: in an unaligned access test on EV7 that
> was true of three quarters of the samples. PALmode PMPCs are physical
> addresses, so those samples keep the interrupted PC.
>
> Three behaviors differ from Aggregate mode. perf record's default event
> is cycles:P, so it now records a ProfileMe profile. In ProfileMe mode
> PCTR0 counts valid fetched instructions rather than retired ones, so the
> instructions count includes the wrong path, squashed NOPs, and both
> halves of each CMOV: on an EV7 loop with a randomly mispredicted branch
> it is about twice the Aggregate count. And instructions killed on a
> wrong path are sampled at their own PC, as ProfileMe defines them.
>
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
>  arch/alpha/include/asm/wrperfmon.h |  12 +++
>  arch/alpha/kernel/perf_event.c     | 157 ++++++++++++++++++++++++++++++++++---
>  2 files changed, 159 insertions(+), 10 deletions(-)
>
> diff --git a/arch/alpha/include/asm/wrperfmon.h b/arch/alpha/include/asm/wrperfmon.h
> index 1fa9bc813263..7fbbfd74926a 100644
> --- a/arch/alpha/include/asm/wrperfmon.h
> +++ b/arch/alpha/include/asm/wrperfmon.h
> @@ -92,5 +92,17 @@
>  #define EV67_PCTR_CYCLES_UNDEF (1UL<<2)
>  #define EV67_PCTR_INSTR_BCACHEMISS (2UL<<2)
>  #define EV67_PCTR_CYCLES_MBOX (3UL<<2)
> +/* In ProfileMe mode, SL1 = 1 makes PCTR1 count the inum retire delay. */
> +#define EV67_PCTR_CYCLES_RETIRE_DELAY (1UL<<2)
> +/* Set in both SL settings where PCTR0 counts cycles. */
> +#define EV67_PCTR_PCTR0_CYCLES (1UL<<2)
> +
> +/* ProfileMe status, from the EV67/EV68 Hardware Reference Manuals. */
> +#define EV67_I_STAT_OVR_SHIFT 30
> +#define EV67_I_STAT_OVR_MASK 0x7UL
> +
> +/* PMPC bits 63:2 hold the profiled PC. Bit 0 set means a PALmode address. */
> +#define EV67_PMPC_PAL (1UL<<0)
> +#define EV67_PMPC_PC_MASK (~3UL)
>
>  #endif
> diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
> index 70840c05d39b..24c96feda942 100644
> --- a/arch/alpha/kernel/perf_event.c
> +++ b/arch/alpha/kernel/perf_event.c
> @@ -30,6 +30,13 @@
>  #define MAX_HWEVENTS 3
>  #define PMC_NO_INDEX -1
>
> +/* Layout of attr.config for PERF_TYPE_RAW events; see format/ in sysfs. */
> +#define ALPHA_PERF_CONFIG_EVENT                0xffULL
> +#define ALPHA_PERF_CONFIG_PROFILEME    BIT_ULL(8)
> +
> +/* hw_perf_event.flags */
> +#define ALPHA_PERF_FLAG_PROFILEME      0x1
> +
>  /* For tracking PMCs and the hw events they monitor on each CPU. */
>  struct cpu_hw_events {
>         int                     enabled;
> @@ -145,6 +152,19 @@ static const struct ev67_mapping_t ev67_mapping[] = {
>  };
>
>
> +/*
> + * In ProfileMe mode PCTR0 selects the profiled instruction and PCTR1 counts
> + * during its window. PCTR0 counts instructions or cycles, and each choice
> + * pairs with two PCTR1 inputs, so each event type picks one SL1 setting.
> + * Indexed by enum ev67_pmc_event_type.
> + */
> +static const unsigned long ev67_profileme_sl1[] = {
> +       [EV67_CYCLES]           = EV67_PCTR_CYCLES_RETIRE_DELAY, /* PCTR1: retire delay */
> +       [EV67_INSTRUCTIONS]     = EV67_PCTR_INSTR_CYCLES,       /* PCTR1: cycles */
> +       [EV67_BCACHEMISS]       = EV67_PCTR_INSTR_BCACHEMISS,   /* PCTR1: Bcache misses */
> +       [EV67_MBOXREPLAY]       = EV67_PCTR_CYCLES_MBOX,        /* PCTR1: Mbox replays */
> +};
> +
>  /*
>   * Check that a group of events can be simultaneously scheduled on to the
>   * EV67 PMU.  Also allocate counter indices and config.
> @@ -152,9 +172,21 @@ static const struct ev67_mapping_t ev67_mapping[] = {
>  static int ev67_check_constraints(struct perf_event **event,
>                                 unsigned long *evtype, int n_ev)
>  {
> -       int idx0;
> +       int idx0, j;
>         unsigned long config;
>
> +       /* A ProfileMe event uses both counters, so it must be alone. */
> +       for (j = 0; j < n_ev; j++) {
> +               if (!(event[j]->hw.flags & ALPHA_PERF_FLAG_PROFILEME))
> +                       continue;
> +               if (n_ev != 1)
> +                       return -1;
> +               event[0]->hw.idx = 0;
> +               event[0]->hw.config_base = EV67_PCTR_MODE_PROFILEME |
> +                                          ev67_profileme_sl1[evtype[0]];
> +               return 0;
> +       }
> +
>         idx0 = ev67_mapping[evtype[0]-1].idx;
>         config = ev67_mapping[evtype[0]-1].config;
>         if (n_ev == 1)
> @@ -213,9 +245,11 @@ static int ev67_raw_event_valid(u64 config)
>
>
>  PMU_FORMAT_ATTR(event, "config:0-7");
> +PMU_FORMAT_ATTR(profileme, "config:8");
>
>  static struct attribute *ev67_format_attrs[] = {
>         &format_attr_event.attr,
> +       &format_attr_profileme.attr,
>         NULL,
>  };
>
> @@ -276,6 +310,24 @@ static inline void alpha_write_pmc(int idx, unsigned long val)
>         wrperfmon(PERFMON_CMD_WRITE, val);
>  }
>
> +/* Write PCTR0 and clear PCTR1 in one call, as ProfileMe mode requires. */
> +static inline void alpha_write_pmc_profileme(unsigned long val)
> +{
> +       val &= alpha_pmu->pmc_count_mask[0];
> +       val <<= alpha_pmu->pmc_count_shift[0];
> +       val |= EV67_PCTR_0 | EV67_PCTR_1;
> +       wrperfmon(PERFMON_CMD_WRITE, val);
> +}
> +
> +/* The counters an event occupies, for PERFMON_CMD_ENABLE and DISABLE. */
> +static inline unsigned long alpha_event_idx_mask(struct hw_perf_event *hwc,
> +                                                int idx)
> +{
> +       if (hwc->flags & ALPHA_PERF_FLAG_PROFILEME)
> +               return EV67_PCTR_0 | EV67_PCTR_1;
> +       return 1UL<<idx;
> +}
> +
>  static inline unsigned long alpha_read_pmc(int idx)
>  {
>         unsigned long val;
> @@ -320,7 +372,10 @@ static int alpha_perf_event_set_period(struct perf_event *event,
>
>         local64_set(&hwc->prev_count, (unsigned long)(-left));
>
> -       alpha_write_pmc(idx, (unsigned long)(-left));
> +       if (hwc->flags & ALPHA_PERF_FLAG_PROFILEME)
> +               alpha_write_pmc_profileme((unsigned long)(-left));
> +       else
> +               alpha_write_pmc(idx, (unsigned long)(-left));
>
>         perf_event_update_userpage(event);
>
> @@ -457,7 +512,8 @@ static void maybe_change_configuration(struct cpu_hw_events *cpuc)
>                 }
>
>                 if (!(hwc->state & PERF_HES_STOPPED))
> -                       cpuc->idx_mask |= (1<<cpuc->current_idx[j]);
> +                       cpuc->idx_mask |= alpha_event_idx_mask(hwc,
> +                                                       cpuc->current_idx[j]);
>         }
>         cpuc->config = cpuc->event[0]->hw.config_base;
>  }
> @@ -548,7 +604,7 @@ static void alpha_pmu_del(struct perf_event *event, int flags)
>                         alpha_perf_event_update(event, hwc, idx, 0);
>                         perf_event_update_userpage(event);
>
> -                       cpuc->idx_mask &= ~(1UL<<idx);
> +                       cpuc->idx_mask &= ~alpha_event_idx_mask(hwc, idx);
>                         cpuc->n_events--;
>                         break;
>                 }
> @@ -573,7 +629,7 @@ static void alpha_pmu_stop(struct perf_event *event, int flags)
>         struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
>
>         if (!(hwc->state & PERF_HES_STOPPED)) {
> -               cpuc->idx_mask &= ~(1UL<<hwc->idx);
> +               cpuc->idx_mask &= ~alpha_event_idx_mask(hwc, hwc->idx);
>                 hwc->state |= PERF_HES_STOPPED;
>         }
>
> @@ -583,7 +639,7 @@ static void alpha_pmu_stop(struct perf_event *event, int flags)
>         }
>
>         if (cpuc->enabled)
> -               wrperfmon(PERFMON_CMD_DISABLE, (1UL<<hwc->idx));
> +               wrperfmon(PERFMON_CMD_DISABLE, alpha_event_idx_mask(hwc, hwc->idx));
>  }
>
>
> @@ -602,9 +658,9 @@ static void alpha_pmu_start(struct perf_event *event, int flags)
>
>         hwc->state = 0;
>
> -       cpuc->idx_mask |= 1UL<<hwc->idx;
> +       cpuc->idx_mask |= alpha_event_idx_mask(hwc, hwc->idx);
>         if (cpuc->enabled)
> -               wrperfmon(PERFMON_CMD_ENABLE, (1UL<<hwc->idx));
> +               wrperfmon(PERFMON_CMD_ENABLE, alpha_event_idx_mask(hwc, hwc->idx));
>  }
>
>
> @@ -644,6 +700,7 @@ static int __hw_perf_event_init(struct perf_event *event)
>         struct perf_event *evts[MAX_HWEVENTS];
>         unsigned long evtypes[MAX_HWEVENTS];
>         int idx_rubbish_bin[MAX_HWEVENTS];
> +       bool profileme = false;
>         int ev;
>         int n;
>
> @@ -657,9 +714,13 @@ static int __hw_perf_event_init(struct perf_event *event)
>         } else if (attr->type == PERF_TYPE_HW_CACHE) {
>                 return -EOPNOTSUPP;
>         } else if (attr->type == PERF_TYPE_RAW) {
> -               if (!alpha_pmu->raw_event_valid(attr->config))
> +               if (attr->config & ~(ALPHA_PERF_CONFIG_EVENT |
> +                                    ALPHA_PERF_CONFIG_PROFILEME))
>                         return -EINVAL;
> -               ev = attr->config;
> +               ev = attr->config & ALPHA_PERF_CONFIG_EVENT;
> +               if (!alpha_pmu->raw_event_valid(ev))
> +                       return -EINVAL;
> +               profileme = attr->config & ALPHA_PERF_CONFIG_PROFILEME;
>         } else {
>                 return -EOPNOTSUPP;
>         }
> @@ -668,6 +729,17 @@ static int __hw_perf_event_init(struct perf_event *event)
>                 return ev;
>         }
>
> +       /*
> +        * ProfileMe samples report the exact PC of the profiled instruction,
> +        * so it also serves any request for precise samples. It only makes
> +        * sense for sampling events.
> +        */
> +       if (attr->precise_ip)
> +               profileme = true;
> +       if (profileme && !is_sampling_event(event))
> +               return -EINVAL;
> +       hwc->flags = profileme ? ALPHA_PERF_FLAG_PROFILEME : 0;
> +
>         /*
>          * We place the event type in event_base here and leave calculation
>          * of the codes to programme the PMU for alpha_pmu_enable() because
> @@ -832,6 +904,61 @@ void perf_event_print_debug(void)
>  }
>
>
> +/*
> + * Handle the close of a ProfileMe window on PCTR0.
> + *
> + * PCTR0 stops counting when the window closes and does not start again
> + * until PCTR_CTL is written, so the status must be read first and the
> + * counters then rearmed unconditionally.
> + */
> +static void alpha_perf_event_profileme(struct perf_event *event,
> +                                      struct hw_perf_event *hwc,
> +                                      struct pt_regs *regs)
> +{
> +       struct perf_sample_data data;
> +       struct pt_regs pm_regs;
> +       unsigned long i_stat, pmpc, ovr;
> +
> +       i_stat = wrperfmon(PERFMON_CMD_I_STAT, 0);
> +       pmpc = wrperfmon(PERFMON_CMD_PMPC, 0);
> +
> +       alpha_perf_event_update(event, hwc, 0, alpha_pmu->pmc_max_period[0]+1);
> +
> +       /*
> +        * When PCTR0 counts instructions, up to 7 of them are counted after
> +        * the overflow but before the window opens. I_STAT reports how many.
> +        */
> +       if (!(hwc->config_base & EV67_PCTR_PCTR0_CYCLES)) {
> +               ovr = (i_stat >> EV67_I_STAT_OVR_SHIFT) & EV67_I_STAT_OVR_MASK;
> +               local64_sub(ovr, &event->count);
> +               local64_add(ovr, &hwc->period_left);
> +       }
> +
> +       /*
> +        * Attribute the sample to the profiled instruction rather than to
> +        * wherever the interrupt landed, including the user/kernel mode that
> +        * perf derives from regs. A PALmode PMPC is a physical address that
> +        * perf cannot resolve. That happens for ITB miss, Istream access
> +        * violation and interrupt redirects, and for instructions inside
> +        * PALcode. Keep the interrupted PC, which for redirects is the
> +        * instruction that trapped.
> +        */
> +       pm_regs = *regs;

On Alpha, perf_reg_value(SP) derives kernel SP from regs + 1.
I think passing this copy therefore reports an address past the local copy,
rather than past the original interrupt frame.

> +       if (!(pmpc & EV67_PMPC_PAL)) {
> +               pm_regs.pc = pmpc & EV67_PMPC_PC_MASK;
> +               /* PS bit 3 is the mode bit that user_mode() tests. */
> +               if (pm_regs.pc < TASK_SIZE)

If PMPC is in userspace but the interrupt arrived in kernel mode,
this also makes perf_sample_regs_user() select the copied kernel
registers as user registers, bypassing perf_get_regs_user().

The EV67 HRM, section 6.10.3.2 (pages 6-21/6-22), describes PMPC
as the profiled PC; the interrupt follows window closure (also
Architecture Reference Manual, section E.2.5.1, page E-22).
That does not make the interrupt registers the profiled user state.

Could we preserve the original frame for register/stack capture
and supply the profiled IP and mode separately?


> +                       pm_regs.ps |= 8UL;
> +               else
> +                       pm_regs.ps &= ~8UL;
> +       }
> +
> +       perf_sample_data_init(&data, 0, hwc->last_period);
> +
> +       if (alpha_perf_event_set_period(event, hwc, 0))
> +               perf_event_overflow(event, &data, &pm_regs);
> +}
> +
>  /*
>   * Performance Monitoring Interrupt Service Routine called when a PMC
>   * overflows.  The PMC that overflowed is passed in la_ptr.
> @@ -866,6 +993,10 @@ static void alpha_perf_event_irq_handler(unsigned long la_ptr,
>
>         idx = la_ptr;
>
> +       /* A ProfileMe event owns both counters but lives at index 0. */
> +       if (cpuc->config & EV67_PCTR_MODE_PROFILEME)
> +               idx = 0;
> +
>         for (j = 0; j < cpuc->n_events; j++) {
>                 if (cpuc->current_idx[j] == idx)
>                         break;
> @@ -888,6 +1019,12 @@ static void alpha_perf_event_irq_handler(unsigned long la_ptr,
>         }
>
>         hwc = &event->hw;
> +       if (hwc->flags & ALPHA_PERF_FLAG_PROFILEME) {
> +               alpha_perf_event_profileme(event, hwc, regs);
> +               wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
> +               return;
> +       }
> +
>         alpha_perf_event_update(event, hwc, idx, alpha_pmu->pmc_max_period[idx]+1);
>         perf_sample_data_init(&data, 0, hwc->last_period);
>
>
> --
> 2.55.0
>


I'll test this on my ES40 later.

Thanks

Magnus

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/5] alpha/perf: add ProfileMe sampling
  2026-10-29  9:11 ` [PATCH 0/5] alpha/perf: add ProfileMe sampling Peter Zijlstra
@ 2026-10-29 15:34   ` Matt Turner
  0 siblings, 0 replies; 16+ messages in thread
From: Matt Turner @ 2026-10-29 15:34 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Richard Henderson, Magnus Lindholm, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, linux-alpha, linux-kernel, linux-perf-users

On Thu, Oct 29, 2026 at 5:11 AM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Wed, Oct 28, 2026 at 02:09:29PM -0400, Matt Turner wrote:
> > The 21264 from EV67 on, and the 21364, have a second performance counter
> > mode, ProfileMe, alongside the Aggregate mode that the driver uses today.
> > An Aggregate mode sample reports the PC where the overflow interrupt was
> > taken. On an EV7 that is anywhere from 15 instructions before to 12
> > after the one that overflowed the counter, which makes perf annotate
> > output close to useless. In ProfileMe mode the CPU picks one fetched
> > instruction, follows it down the pipeline, and reports its PC along with
> > what happened to it: whether it retired, whether it trapped and why,
> > whether it was a mispredicted branch, stalled before map, or was killed
> > on a wrong path, and what PCTR1 counted while it was in flight.
>
> Isn't this the thing that ended up as AMD's IBS?

I have no idea, but it wouldn't surprise me too much :)

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call
  2026-10-28 18:09 ` [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call Matt Turner
  2026-10-28 18:12   ` sashiko-bot
@ 2026-10-29 17:38   ` Magnus Lindholm
  1 sibling, 0 replies; 16+ messages in thread
From: Magnus Lindholm @ 2026-10-29 17:38 UTC (permalink / raw)
  To: Matt Turner
  Cc: Richard Henderson, Peter Zijlstra, Ingo Molnar,
	Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
	Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
	James Clark, linux-alpha, linux-kernel, linux-perf-users

Hi Matt,

On Wed, Oct 28, 2026 at 7:09 PM Matt Turner <mattst88@gmail.com> wrote:
>
> alpha_pmu_enable() passes EV67_PCTR_MODE_AGGREGATE to
> PERFMON_CMD_LOGGING_OPTIONS. That constant is the counting mode bit for
> PCTR_CTL, which belongs in the PERFMON_CMD_DESIRED_EVENTS argument. The
> logging options call takes a flag in bit 0 where 0 means "count all
> processes". It works only because EV67_PCTR_MODE_AGGREGATE happens to be
> 0.
>
> Add a name for that argument and pass it instead. No functional change.
>
> Signed-off-by: Matt Turner <mattst88@gmail.com>
> ---
>  arch/alpha/include/asm/wrperfmon.h | 2 ++
>  arch/alpha/kernel/perf_event.c     | 2 +-
>  2 files changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/arch/alpha/include/asm/wrperfmon.h b/arch/alpha/include/asm/wrperfmon.h
> index c97b82a1f7db..1fa9bc813263 100644
> --- a/arch/alpha/include/asm/wrperfmon.h
> +++ b/arch/alpha/include/asm/wrperfmon.h
> @@ -23,6 +23,8 @@
>  #define PERFMON_CMD_I_STAT 8
>  #define PERFMON_CMD_PMPC 9
>
> +/* PERFMON_CMD_LOGGING_OPTIONS arguments */
> +#define PERFMON_CMD_LOGGING_ALL 0

The Alpha Architecture Reference Manual, Table E-18 (page E-24),
describes bit 0 as:

  "set = log all processes"
  "clear = log only selected processes"

That appears opposite to the changelog's description of zero as
"count all processes". Is the manual incorrect for the PALcode
implementation we use?

This preserves the existing value, so there is no functional change,
but a short explanation would help reconcile the name and changelog
with the manual.


>
>  /* EV5/EV56/PCA56 Counters */
>  #define EV5_PCTR_0 (1UL<<0)
> diff --git a/arch/alpha/kernel/perf_event.c b/arch/alpha/kernel/perf_event.c
> index a3eaab094ece..2d9383425357 100644
> --- a/arch/alpha/kernel/perf_event.c
> +++ b/arch/alpha/kernel/perf_event.c
> @@ -728,7 +728,7 @@ static void alpha_pmu_enable(struct pmu *pmu)
>                 maybe_change_configuration(cpuc);
>
>                 /* Start counting the desired events. */
> -               wrperfmon(PERFMON_CMD_LOGGING_OPTIONS, EV67_PCTR_MODE_AGGREGATE);
> +               wrperfmon(PERFMON_CMD_LOGGING_OPTIONS, PERFMON_CMD_LOGGING_ALL);
>                 wrperfmon(PERFMON_CMD_DESIRED_EVENTS, cpuc->config);
>                 wrperfmon(PERFMON_CMD_ENABLE, cpuc->idx_mask);
>         }
>
> --
> 2.55.0
>


WIll test this all later on my ES40

Magnus

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2026-10-29 17:38 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 18:09 [PATCH 0/5] alpha/perf: add ProfileMe sampling Matt Turner
2026-10-28 18:09 ` [PATCH 1/5] alpha/perf: pass PERFMON_CMD_LOGGING_ALL to the logging options call Matt Turner
2026-10-28 18:12   ` sashiko-bot
2026-10-29 17:38   ` Magnus Lindholm
2026-10-28 18:09 ` [PATCH 2/5] alpha/perf: add sysfs format and event attributes Matt Turner
2026-10-28 18:13   ` sashiko-bot
2026-10-28 18:09 ` [PATCH 3/5] alpha/perf: implement ProfileMe sampling Matt Turner
2026-10-28 18:28   ` sashiko-bot
2026-10-29 15:18   ` Magnus Lindholm
2026-10-28 18:09 ` [PATCH 4/5] alpha/perf: attach ProfileMe status as PERF_SAMPLE_RAW data Matt Turner
2026-10-28 18:26   ` sashiko-bot
2026-10-28 18:09 ` [PATCH 5/5] alpha/perf: optionally randomize the ProfileMe sampling interval Matt Turner
2026-10-28 18:21   ` sashiko-bot
2026-10-29 15:01   ` Magnus Lindholm
2026-10-29  9:11 ` [PATCH 0/5] alpha/perf: add ProfileMe sampling Peter Zijlstra
2026-10-29 15:34   ` Matt Turner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.