CoreSight currently models exception entry by changing the preceding
instruction range into a taken branch. This can give an IRQ the source
PC of an instruction that already retired and overwrite a real branch
immediately before the exception.
For an untaken B.LS followed by an IRQ, perf script currently reports:
hw int 4000f8 => ffff800080010c80 b.ls #0x400118
...
iret ffff800080012284 => 4000fc eret
The hardware trace supplies 0x4000fc as the preferred return address.
For an IRQ, this is the architectural resume PC (Arm ARM, R_VBQMV).
B.LS has retired, and the saved PC identifies the boundary before the
following MOV. Using this PC as the IRQ source reflects the
architectural state at exception entry:
hw int 4000fc => ffff800080010c80 movz x2, #0x1796
...
iret ffff800080012284 => 4000fc eret
The IRQ sample represents the transfer from this architectural execution
position to the handler. The series synthesizes exception entries from
exception packets, preserving the preceding branch and its outcome.
The supporting changes:
- Let decoders supply sample.ret_addr so later instruction fetching cannot
change thread-stack return addresses. Apply this to Intel PT
asynchronous samples as well.
- Prepare packet ISA and instruction-size handling and share sample
synthesis helpers. Mark untaken branches and break history when
instruction memory is unavailable.
- Add a thread-stack regression test and four AArch64 CoreSight tests.
IRQs and page faults must resume at the entry PC; SVC and emulated MRS
must resume four bytes later.
Based on the AI search and test on my x86 machine, this matches perf's
Intel PT handling of IRQs. Intel PT records the next instruction's IP in
the FUP packet, and Perf uses that IP as the interrupt sample's source.
This series is verified on Orion6 board with "perf test coresight".
Signed-off-by: Leo Yan <leo.yan(a)arm.com>
---
Changes in v2:
- Rework the fix around exception packets to preserve both exception
entries and preceding branches.
- Add explicit return addresses and the Intel PT asynchronous-branch fix.
- Split out packet/synthesis preparation, record not-taken branches and
handle unreadable instruction memory.
- Add thread-stack regression coverage and four CoreSight tests.
- Link to v1: https://lore.kernel.org/r/20260713-perf_cs_etm_fix_non_taken-v1-0-4561607fc…
---
Leo Yan (14):
perf sample: Allow decoders to supply branch return addresses
perf intel-pt: Preserve return addresses for asynchronous branches
perf cs-etm: Break branch history when instruction memory is unavailable
perf cs-etm: Centralize packet ISA initialization
perf cs-etm: Use the recorded instruction size for A32 and A64
perf cs-etm: Mark branches that were not taken
perf cs-etm: Factor out final instruction sample synthesis
perf cs-etm: Centralize branch sample synthesis checks
perf cs-etm: Classify exception calls using the exception packet
perf cs-etm: Synthesize exception entries separately from branches
perf tests: Check CoreSight IRQ entry and exit
perf tests: Check CoreSight syscall entry and exit
perf tests: Check CoreSight abort entry and exit
perf tests: Check CoreSight emulated instruction entry and exit
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 3 +
.../perf/tests/shell/coresight/abort_entry_exit.sh | 21 ++
tools/perf/tests/shell/coresight/irq_entry_exit.sh | 37 ++++
.../tests/shell/coresight/syscall_entry_exit.sh | 21 ++
.../perf/tests/shell/coresight/trap_entry_exit.sh | 24 +++
tools/perf/tests/shell/lib/coresight_exception.sh | 165 +++++++++++++++
tools/perf/tests/tests.h | 3 +
tools/perf/tests/thread-stack.c | 106 ++++++++++
tools/perf/tests/workloads/Build | 4 +
tools/perf/tests/workloads/branch_not_taken_loop.c | 33 +++
tools/perf/tests/workloads/page_fault_loop.c | 37 ++++
tools/perf/util/cs-etm-decoder/cs-etm-decoder.c | 106 +++++++---
tools/perf/util/cs-etm.c | 222 ++++++++++-----------
tools/perf/util/cs-etm.h | 2 +
tools/perf/util/intel-pt.c | 7 +
tools/perf/util/sample.c | 1 +
tools/perf/util/sample.h | 5 +
tools/perf/util/thread-stack.c | 5 +-
19 files changed, 656 insertions(+), 147 deletions(-)
---
base-commit: edd8a9fe2eca009599e013a29c421c7a6b5ad1b9
change-id: 20260713-perf_cs_etm_fix_non_taken-5b4d7f73f41e
Best regards,
--
Leo Yan <leo.yan(a)arm.com>
Concurrent per-thread events results in a WARN on N1SDP which leads to
the realization that per-thread events shouldn't have been sharing sinks
in the first place.
This slips through because different per-thread events will have the
same PID if owned by the same process, and we only check the PID and
nothing else. That results in unexpected WARNs because it looks like we
assumed it couldn't happen (although exclusive PMU rules allow it). But
even if it was supported it would result in trace from the wrong thread
in another event's per-thread buffer, so we should disallow it.
Fix it by determining if sharing can happen at setup time. Then remove
everywhere the same PID checking logic was copy pasted in each sink.
The first three patches fix configuration initialization, AUX cleanup
and topology locking. The remaining patches enforce the sharing rules
and simplify the sink drivers.
Signed-off-by: James Clark <james.clark(a)linaro.org>
---
Changes in v4:
- Count CPU sources in the registered topology and retain each sink's
sharing classification until its last AUX session ends.
- Serialize AUX topology access and provide locked and locking path
release functions.
- Reject unexpected path setup errors while retaining silent skips for
missing ETMs and unreachable sinks.
- Prevent all problematic sharing cases at aux setup rather than at
runtime. (Leo)
- Drop "coresight: tmc-etr: Don't stop Perf cleanup for active sysfs
reads". It was a pre-existing bug and is even more unrelated now with
the new model.
- Remove workqueue rather than fix the lifecycle, its no longer
needed. (Leo)
- Add a getter for the config manager, cscfg_get_state(). (Leo)
- Link to v3: https://lore.kernel.org/r/20260728-james-cs-multiple-per-threads-v3-0-6aee7…
Changes in v3:
- Storing and accessing event owners at runtime causes problems due to
various scenarios of: events (and sibling events) exiting, children
inheriting event FDs, PID reuse, CPU affine events that also have a
target process set but different inherit settings. Fix it by creating
a session ID in etm_setup_aux() and holding the references in it for
the duration of the whole session. (Leo)
- Make the ETR buffer allocator consistent with sink sharing rules by
not doing numeric PID comparisons there either.
- Fix up some Sashiko reports that it sees after interacting with cscfg
and taking extra references to tasks and PIDs.
- Link to v2: https://lore.kernel.org/r/20260709-james-cs-multiple-per-threads-v2-0-10ac7…
Changes in v2:
- Fix inherited events by following event->parent
- Link to v1: https://lore.kernel.org/r/20260709-james-cs-multiple-per-threads-v1-0-d384e…
---
James Clark (10):
coresight: configfs: Don't assume active until cscfg_mgr is set
coresight: etm-perf: Remove workqueue for freeing AUX data
coresight: etm-perf: Serialize AUX topology access
coresight: etm-perf: Reject event if building path fails
coresight: etm-perf: Prevent sink sharing at setup time
coresight: tmc-etr: Reflect new sharing rules in allocator
coresight: tmc-etr: Remove sharing guards from ETR
coresight: tmc-etf: Remove sharing guards from ETF
coresight: etb10: Remove sharing guards from ETB10
coresight: ultrasoc-smb: Remove sharing guards from SMB
drivers/hwtracing/coresight/coresight-core.c | 104 +++++++++++++++--
drivers/hwtracing/coresight/coresight-etb10.c | 28 +----
drivers/hwtracing/coresight/coresight-etm-perf.c | 119 ++++++++++---------
drivers/hwtracing/coresight/coresight-etm-perf.h | 2 -
drivers/hwtracing/coresight/coresight-priv.h | 4 +-
drivers/hwtracing/coresight/coresight-syscfg.c | 11 +-
drivers/hwtracing/coresight/coresight-sysfs.c | 4 +-
drivers/hwtracing/coresight/coresight-tmc-core.c | 6 +-
drivers/hwtracing/coresight/coresight-tmc-etf.c | 22 +---
drivers/hwtracing/coresight/coresight-tmc-etr.c | 142 +++++++----------------
drivers/hwtracing/coresight/coresight-tmc.h | 16 +--
drivers/hwtracing/coresight/coresight-trace-id.c | 97 ++++++++++++++--
drivers/hwtracing/coresight/coresight-trace-id.h | 26 +++--
drivers/hwtracing/coresight/ultrasoc-smb.c | 20 +---
drivers/hwtracing/coresight/ultrasoc-smb.h | 3 -
include/linux/coresight.h | 26 ++++-
16 files changed, 346 insertions(+), 284 deletions(-)
---
base-commit: 343cfcbb3ecfdb80aa576a428cfa3b65ad783842
change-id: 20260708-james-cs-multiple-per-threads-ed1d25ed1734
Best regards,
--
James Clark <james.clark(a)linaro.org>
Extracted from the last two commits from [1] as they were only
conceptually related to that patchset, not functionally. This can be
safely applied to the driver separately.
[1] https://lore.kernel.org/linux-perf-users/20260821-james-cs-hw_id-output-fai…
Signed-off-by: James Clark <james.clark(a)linaro.org>
---
James Clark (2):
perf/core: Return errors from perf_report_aux_output_id()
coresight: Retry failed HW_ID writes
drivers/hwtracing/coresight/coresight-etm-perf.c | 50 +++++++++++++++---------
include/linux/perf_event.h | 2 +-
kernel/events/core.c | 6 ++-
3 files changed, 36 insertions(+), 22 deletions(-)
---
base-commit: 75ba5c486ea071d44d3f61318c88a7793bc768d4
change-id: 20261001-james-cs-retry-hw_id-8f1ec3cf2db3
Best regards,
--
James Clark <james.clark(a)linaro.org>
On Mon, Oct 12, 2026 at 04:05:21PM -0700, Amir Ayupov wrote:
[...]
> --- a/drivers/hwtracing/coresight/coresight-config.h
> +++ b/drivers/hwtracing/coresight/coresight-config.h
> @@ -30,6 +30,12 @@
> #define CS_CFG_MATCH_CLASS_SRC_ALL 0x0001 /* match any source */
> #define CS_CFG_MATCH_CLASS_SRC_ETM4 0x0002 /* match any ETMv4 device */
It is good to introduce a marco:
#define CS_CFG_MATCH_CLASS_MASK GENMASK_U32(7, 0)
> +/*
> + * flags defining device capabilities a feature can require. A feature loads on
> + * a device that matches one of its classes and has all of its capabilities.
> + */
> +#define CS_CFG_MATCH_CAP_MASK 0x0000ff00
To make the mask explicitly 32-bit unsigned:
#define CS_CFG_MATCH_CAP_MASK GENMASK_U32(15, 8)
> +
> /* flags defining device instance matching - used in config match desc data. */
> #define CS_CFG_MATCH_INST_ANY 0x80000000 /* any instance of a class */
Not an issue with this patch, but CS_CFG_MATCH_INST_ANY appears to be
unused and could be removed in a separate cleanup patch.
> diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c
> index a816c22c87bc8..1545d64afec01 100644
> --- a/drivers/hwtracing/coresight/coresight-syscfg.c
> +++ b/drivers/hwtracing/coresight/coresight-syscfg.c
> @@ -172,7 +172,21 @@ cscfg_alloc_csdev_feat(struct coresight_device *csdev, struct cscfg_feature_desc
> }
>
> /* load one feature into one coresight device */
> +/*
> + * A feature matches a device that shares one of its class flags and has all of
> + * the capability flags it requires.
> + */
> +static bool cscfg_match_feat_csdev(u32 feat_flags, u32 csdev_flags)
> +{
> + u32 caps = feat_flags & CS_CFG_MATCH_CAP_MASK;
> +
> + return (feat_flags & csdev_flags & ~CS_CFG_MATCH_CAP_MASK) &&
> + (csdev_flags & caps) == caps;
For clearer comparison conditions:
/* Class type must be matches */
if ((feat_flags & CS_CFG_MATCH_CLASS_MASK) !=
(csdev_flags & CS_CFG_MATCH_CLASS_MASK)
return false;
/* No required capabilities miss from the device */
if (feat_flags & ~csdev_flags & CS_CFG_MATCH_CAP_MASK)
return false;
return true;
Thanks,
Leo
Hi,
On Sun, Oct 25, 2026 at 09:13:07PM +0800, Chao Xu wrote:
> Before enable funnel, priority must be set. Changing the port
> priorities in real time is not supported.
> If the priority control register is written when one or more of ports
> are enable, then the write is silently rejected.
Could you cite the relevant documentation in commit log?
The CoreSight SoC-600 TRM, section 9.11.2, PRIORITYCONTROL, a funnel
rejects priority register writes whenever any port is enabled. The
current sequence therefore prevent the configured priorities from
taking effect.
Write the priority register before enabling the first port.
> Signed-off-by: Chao Xu <xuchao066(a)163.com>
> ---
> drivers/hwtracing/coresight/coresight-funnel.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
> index af40814ce560..17f985798d7f 100644
> --- a/drivers/hwtracing/coresight/coresight-funnel.c
> +++ b/drivers/hwtracing/coresight/coresight-funnel.c
> @@ -66,8 +66,8 @@ static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
> functl &= ~FUNNEL_HOLDTIME_MASK;
> functl |= FUNNEL_HOLDTIME;
> functl |= (1 << port);
> - writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
> writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
> + writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
Please move the priority write into the first port branch:
/* Claim the device only when we enable the first slave */
if (!(functl & FUNNEL_ENSx_MASK)) {
rc = coresight_claim_device_unlocked(csdev);
if (rc)
goto done;
/* Update priorities while all ports are disabled */
writel_relaxed(drvdata->priority,
drvdata->base + FUNNEL_PRICTL);
}
Thanks,
Leo
This reverts commit 18049c8cff9cc89daadc4df6975f7d9069638926.
The original commit claimed that contiguous AUX pages unnecessarily
exacerbate fragmentation for SPE, TRBE and ETR. This is not generally
valid: larger allocations can keep pages with the same lifetime
together, while order-0 allocations can scatter them across large
blocks and prevent free pages from coalescing [1]. The follow-up
discussion [2] has not established how forcing order-0 allocations
addresses the reported problem.
TRBE and SPE write to virtually addressed buffers. The TRBE L1 TLB
has one entry on Neoverse V2 [3] and two on Neoverse V3 [4]. Small
mappings increase translation pressure, and TTW stalls can contribute
to FIFO overflow and trace loss on TRBE.
Larger allocations provide contiguous, aligned chunks for PMD or
contiguous-PTE mappings with the proposed vmap() support [5].
With the proposed vmap() support already applied, the tests in [2]
showed that preferring larger AUX allocations reduced L2 data TLB
refills by 99.68% in the TRBE test and 34.74% in the SPE test.
Restore the previous policy of trying higher-order allocations first.
The allocator already uses __GFP_NORETRY and falls back to smaller
orders, including order 0, when larger allocations fail.
Remove PERF_PMU_CAP_AUX_PREFER_LARGE, since the restored default
provides that preference. Intel PT retains its existing allocation
behaviour, with no functional change.
[1] https://lore.kernel.org/all/aPjrRkjiIt6HmXmT@casper.infradead.org/
[2] https://lore.kernel.org/linux-arm-kernel/20260810-perf_aux_trace_large_gran…
[3] https://documentation-service.arm.com/static/633fe1ec4c59b30b517730b8#page=…
[4] https://documentation-service.arm.com/static/67361bd2c7fc0d1f211da382#page=…
[5] https://lore.kernel.org/linux-mm/20260715120813.3609949-1-jiangwen6@xiaomi.…
Signed-off-by: Leo Yan <leo.yan(a)arm.com>
---
arch/x86/events/intel/pt.c | 2 --
include/linux/perf_event.h | 5 ++---
kernel/events/ring_buffer.c | 29 +++++++++--------------------
3 files changed, 11 insertions(+), 25 deletions(-)
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index 49349afee6119fb6fad815c847cb3cd9daa97604..3782af40f0832a874a7f0b60961d6652513de241 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -1896,8 +1896,6 @@ static __init int pt_init(void)
if (!intel_pt_validate_hw_cap(PT_CAP_topa_multiple_entries))
pt_pmu.pmu.capabilities = PERF_PMU_CAP_AUX_NO_SG;
- else
- pt_pmu.pmu.capabilities = PERF_PMU_CAP_AUX_PREFER_LARGE;
pt_pmu.pmu.capabilities |= PERF_PMU_CAP_EXCLUSIVE |
PERF_PMU_CAP_ITRACE |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index eb3f7bab498ca9bee9ce7193744943b24ab2e0c5..b8d8b005f5c8a7c1def7cad02230bf9ffb876bd6 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -304,9 +304,8 @@ struct perf_event_pmu_context;
#define PERF_PMU_CAP_AUX_OUTPUT 0x0080
#define PERF_PMU_CAP_EXTENDED_HW_TYPE 0x0100
#define PERF_PMU_CAP_AUX_PAUSE 0x0200
-#define PERF_PMU_CAP_AUX_PREFER_LARGE 0x0400
-#define PERF_PMU_CAP_MEDIATED_VPMU 0x0800
-#define PERF_PMU_CAP_SIMD_REGS 0x1000
+#define PERF_PMU_CAP_MEDIATED_VPMU 0x0400
+#define PERF_PMU_CAP_SIMD_REGS 0x0800
/**
* pmu::scope
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 64d4dbdc6c071b72b670dd044616f52981206ae8..95f4fbdfd2764de5b72d901d778c46302f525fad 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -684,15 +684,7 @@ int rb_alloc_aux(struct perf_buffer *rb, struct perf_event *event,
{
bool overwrite = !(flags & RING_BUFFER_WRITABLE);
int node = (event->cpu == -1) ? -1 : cpu_to_node(event->cpu);
- bool use_contiguous_pages = event->pmu->capabilities & (
- PERF_PMU_CAP_AUX_NO_SG | PERF_PMU_CAP_AUX_PREFER_LARGE);
- /*
- * Initialize max_order to 0 for page allocation. This allocates single
- * pages to minimize memory fragmentation. This is overridden if the
- * PMU needs or prefers contiguous pages (use_contiguous_pages = true).
- */
- int max_order = 0;
- int ret = -ENOMEM;
+ int ret = -ENOMEM, max_order;
if (!has_aux(event))
return -EOPNOTSUPP;
@@ -702,8 +694,8 @@ int rb_alloc_aux(struct perf_buffer *rb, struct perf_event *event,
if (!overwrite) {
/*
- * Watermark defaults to half the buffer, to aid PMU drivers
- * in double buffering.
+ * Watermark defaults to half the buffer, and so does the
+ * max_order, to aid PMU drivers in double buffering.
*/
if (!watermark)
watermark = min_t(unsigned long,
@@ -711,19 +703,16 @@ int rb_alloc_aux(struct perf_buffer *rb, struct perf_event *event,
(unsigned long)nr_pages << (PAGE_SHIFT - 1));
/*
- * If using contiguous pages, use aux_watermark as the basis
- * for chunking to help PMU drivers honor the watermark.
+ * Use aux_watermark as the basis for chunking to
+ * help PMU drivers honor the watermark.
*/
- if (use_contiguous_pages)
- max_order = get_order(watermark);
+ max_order = get_order(watermark);
} else {
/*
- * If using contiguous pages, we need to start with the
- * max_order that fits in nr_pages, not the other way around,
- * hence ilog2() and not get_order.
+ * We need to start with the max_order that fits in nr_pages,
+ * not the other way around, hence ilog2() and not get_order.
*/
- if (use_contiguous_pages)
- max_order = ilog2(nr_pages);
+ max_order = ilog2(nr_pages);
watermark = 0;
}
---
base-commit: 21a7f44fd0765fcc5194130b28767786eba1d1e7
change-id: 20261025-perf_revert_prefer_large_alloc-a0177d6379f6
Best regards,
--
Leo Yan <leo.yan(a)arm.com>