> diff --git a/drivers/perf/arm_brbe.c b/drivers/perf/arm_brbe.c
> index ba554e0c846c..aede95e27784 100644
> --- a/drivers/perf/arm_brbe.c
> +++ b/drivers/perf/arm_brbe.c
[ ... ]
> @@ -774,32 +795,96 @@ void brbe_read_filtered_entries(struct
> perf_branch_stack *branch_stack,
[ ... ]
> +/*
> + * Best-effort BRBE snapshot for BPF tracing. Pause BRBE to avoid
> + * self-recording and return 0 if the snapshot state appears disturbed.
> + */
> +int arm_brbe_snapshot_branch_stack(struct perf_branch_entry *entries,
> unsigned int cnt)
> +{
> + unsigned long flags;
> + int nr_hw, nr_copied = 0;
> + u64 brbfcr, brbcr;
> +
> + if (!cnt)
> + return 0;
> +
> + /* Guard against running on a CPU without BRBE (e.g. big.LITTLE). */
> + if (!cpu_has_brbe())
> + return 0;
> +
> + /*
> + * Pause BRBE first to avoid recording our own branches. The
> + * sysreg read/write and ISB are branchless, so pausing before
> + * checking BRBCR avoids polluting the buffer with our own
> + * conditional branches.
> + */
> + brbfcr = read_sysreg_s(SYS_BRBFCR_EL1);
> + brbcr = read_sysreg_s(SYS_BRBCR_EL1);
> + write_sysreg_s(brbfcr | BRBFCR_EL1_PAUSED, SYS_BRBFCR_EL1);
> + isb();
> +
> + /* Bail out if BRBE is not enabled (BRBCR_EL1 == 0). */
> + if (!brbcr) {
> + write_sysreg_s(brbfcr, SYS_BRBFCR_EL1);
> + return 0;
> + }
A subsystem pattern flags this as potentially concerning: when
BRBCR_EL1 is not enabled, the function restores BRBFCR_EL1 and returns
immediately. Should the write_sysreg_s that restores BRBFCR_EL1 be
followed by an isb() before the return?
Looking at other code paths in this function, both the success path and
the PMU-overflow-detection path restore BRBFCR_EL1 followed by isb()
before returning. The early-exit path here omits the barrier, meaning
there is a function return (RET branch) right after the control register
write without an intervening isb().
ARM64 guidelines typically require an isb() after writes to control
system registers to ensure the change takes effect before subsequent
operations. While BRBE is disabled in this path, and eventual exception
entry/exit will provide synchronization, the inconsistency with the
other exit paths is worth checking.
> +
> + /* Block local exception delivery while reading the buffer. */
> + flags = local_daif_save();
> +
> + /*
> + * A PMU overflow before local_daif_save() could have re-enabled
> + * BRBE, clearing the PAUSED bit. The overflow handler already
> + * restored BRBE to its correct state, so just bail out.
> + */
> + if (!(read_sysreg_s(SYS_BRBFCR_EL1) & BRBFCR_EL1_PAUSED)) {
> + local_daif_restore(flags);
> + return 0;
> + }
[ ... ]
> + brbe_invalidate();
> +
> + /* Restore BRBCR before unpausing via BRBFCR, matching brbe_enable(). */
> + write_sysreg_s(brbcr, SYS_BRBCR_EL1);
> + isb();
> + write_sysreg_s(brbfcr, SYS_BRBFCR_EL1);
> + /* Ensure BRBE is unpaused before returning to the caller. */
> + isb();
> + local_daif_restore(flags);
> +
> + return nr_copied;
> +}
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/26510917353