[PATCH v6 3/3] perf/core: Clear the whole branch entry in perf_clear_branch_entry()
Peter Zijlstra
peterz at infradead.org
Fri Aug 7 03:29:32 PDT 2026
On Thu, Aug 06, 2026 at 06:52:23AM -0700, Puranjay Mohan wrote:
> perf_clear_branch_entry_bitfields() clears the bitfields of struct
> perf_branch_entry one by one and leaves from/to alone, since callers
> overwrite those straight away. The list has to be kept in sync with the
> struct by hand and has already fallen behind: new_type and priv were
> added to perf_branch_entry and never added here.
>
> Only BRBE writes those two, and neither is written for every record.
> brbe_set_perf_entry_type() leaves new_type alone for a branch type it
> does not recognise, and priv is not set for source-only records.
> arm_pmuv3.c allocates the per-CPU branch stack with kmalloc(), so such a
> record carries whatever the slot held: uninitialised kmalloc() data on
> the first pass over the buffer, the previous record's values after that.
> Both reach userspace through the branch stack. Nothing under
> arch/x86/events/ writes either field, so x86 is unaffected.
>
> Clear the entry with a single struct assignment instead:
>
> *br = (struct perf_branch_entry){ };
>
> The bitfields add up to exactly 64 bits, so there is no padding, and
> every caller assigns from/to immediately afterwards, so zeroing those as
> well changes nothing. PERF_BR_SPEC_NA is 0, so dropping the explicit
> spec assignment leaves the behaviour unchanged. Nothing needs keeping in
> sync when a field is added.
>
> The helper no longer touches only bitfields, so rename it to
> perf_clear_branch_entry().
Fair enough I suppose, but then why not write it like so?
---
--- a/arch/x86/events/amd/brs.c
+++ b/arch/x86/events/amd/brs.c
@@ -343,11 +343,7 @@ void amd_brs_drain(void)
if (!amd_brs_match_plm(event, from, to))
continue;
- perf_clear_branch_entry_bitfields(br+nr);
-
- br[nr].from = from;
- br[nr].to = to;
-
+ br[nr] = (struct perf_branch_entry){ from, to };
nr++;
}
empty:
--- a/arch/x86/events/amd/lbr.c
+++ b/arch/x86/events/amd/lbr.c
@@ -184,12 +184,6 @@ void amd_pmu_lbr_read(void)
entry.to.split.reserved)
continue;
- perf_clear_branch_entry_bitfields(br + out);
-
- br[out].from = sign_ext_branch_ip(entry.from.split.ip);
- br[out].to = sign_ext_branch_ip(entry.to.split.ip);
- br[out].mispred = entry.from.split.mispredict;
- br[out].predicted = !br[out].mispred;
/*
* Set branch speculation information using the status of
@@ -208,7 +202,13 @@ void amd_pmu_lbr_read(void)
* speculative and took the correct path
*/
idx = (entry.to.split.valid << 1) | entry.to.split.spec;
- br[out].spec = lbr_spec_map[idx];
+ br[out] = (struct perf_branch_entry) {
+ .from = sign_ext_branch_ip(entry.from.split.ip),
+ .to = sign_ext_branch_ip(entry.to.split.ip),
+ .mispred = entry.from.split.mispredict,
+ .predicted = !entry.from.split.mispredict,
+ .spec = lbr_spec_map[idx],
+ };
out++;
}
--- a/arch/x86/events/intel/lbr.c
+++ b/arch/x86/events/intel/lbr.c
@@ -756,10 +756,10 @@ void intel_pmu_lbr_read_32(struct cpu_hw
rdmsrq(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr);
- perf_clear_branch_entry_bitfields(br);
-
- br->from = msr_lastbranch.from;
- br->to = msr_lastbranch.to;
+ *br = (struct perf_branch_entry){
+ .from = msr_lastbranch.from,
+ .to = msr_lastbranch.to,
+ };
br++;
}
cpuc->lbr_stack.nr = i;
@@ -847,14 +847,15 @@ void intel_pmu_lbr_read_64(struct cpu_hw
if (abort && x86_pmu.lbr_double_abort && out > 0)
out--;
- perf_clear_branch_entry_bitfields(br+out);
- br[out].from = from;
- br[out].to = to;
- br[out].mispred = mis;
- br[out].predicted = pred;
- br[out].in_tx = in_tx;
- br[out].abort = abort;
- br[out].cycles = cycles;
+ br[out] = (struct perf_branch_entry) {
+ .from = from,
+ .to = to,
+ .mispred = mis,
+ .predicted = pred,
+ .in_tx = in_tx,
+ .abort = abort,
+ .cycles = cycles,
+ };
out++;
}
cpuc->lbr_stack.nr = out;
@@ -921,24 +922,25 @@ static void intel_pmu_store_lbr(struct c
to = rdlbr_to(i, lbr);
info = rdlbr_info(i, lbr);
- perf_clear_branch_entry_bitfields(e);
-
- e->from = from;
- e->to = to;
- e->mispred = get_lbr_mispred(info);
- e->predicted = !e->mispred;
- e->in_tx = !!(info & LBR_INFO_IN_TX);
- e->abort = !!(info & LBR_INFO_ABORT);
- e->cycles = get_lbr_cycles(info);
- e->type = get_lbr_br_type(info);
-
- /*
- * Leverage the reserved field of cpuc->lbr_entries[i] to
- * temporarily store the branch counters information.
- * The later code will decide what content can be disclosed
- * to the perf tool. Pleae see intel_pmu_lbr_counters_reorder().
- */
- e->reserved = (info >> LBR_INFO_BR_CNTR_OFFSET) & LBR_INFO_BR_CNTR_FULL_MASK;
+ *e = (struct perf_branch_entry){
+ .from = from,
+ .to = to,
+ .mispred = get_lbr_mispred(info),
+ .predicted = !get_lbr_mispred(info),
+ .in_tx = !!(info & LBR_INFO_IN_TX),
+ .abort = !!(info & LBR_INFO_ABORT),
+ .cycles = get_lbr_cycles(info),
+ .type = get_lbr_br_type(info),
+
+ /*
+ * Leverage the reserved field of cpuc->lbr_entries[i]
+ * to temporarily store the branch counters
+ * information. The later code will decide what
+ * content can be disclosed to the perf tool. Pleae
+ * see intel_pmu_lbr_counters_reorder().
+ */
+ .reserved = (info >> LBR_INFO_BR_CNTR_OFFSET) & LBR_INFO_BR_CNTR_FULL_MASK,
+ };
}
cpuc->lbr_stack.nr = i;
--- a/drivers/perf/arm_brbe.c
+++ b/drivers/perf/arm_brbe.c
@@ -604,7 +604,7 @@ static bool perf_entry_from_brbe_regset(
return false;
brbinf = bregs.brbinf;
- perf_clear_branch_entry_bitfields(entry);
+ *entry = (struct perf_branch_entry) { };
if (brbe_record_is_complete(brbinf)) {
entry->from = bregs.brbsrc;
entry->to = bregs.brbtgt;
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1467,23 +1467,6 @@ static inline u32 perf_sample_data_size(
return size;
}
-/*
- * Clear all bitfields in the perf_branch_entry.
- * The to and from fields are not cleared because they are
- * systematically modified by caller.
- */
-static inline void perf_clear_branch_entry_bitfields(struct perf_branch_entry *br)
-{
- br->mispred = 0;
- br->predicted = 0;
- br->in_tx = 0;
- br->abort = 0;
- br->cycles = 0;
- br->type = 0;
- br->spec = PERF_BR_SPEC_NA;
- br->reserved = 0;
-}
-
extern void perf_output_sample(struct perf_output_handle *handle,
struct perf_event_header *header,
struct perf_sample_data *data,
More information about the linux-arm-kernel
mailing list