Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Documentation/ABI/testing/sysfs-bus-event_source-devices-caps
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ Description:
Example output in powerpc:
grep . /sys/bus/event_source/devices/cpu/caps/*
/sys/bus/event_source/devices/cpu/caps/pmu_name:POWER9

The "branch_counter_nr" in the supported platform exposes the
maximum number of counters which can be shown in the u64 counters
of PERF_SAMPLE_BRANCH_COUNTERS, while the "branch_counter_width"
exposes the width of each counter. Both of them can be used by
the perf tool to parse the logged counters in each branch.
2 changes: 1 addition & 1 deletion arch/powerpc/perf/core-book3s.c
Original file line number Diff line number Diff line change
Expand Up @@ -2312,7 +2312,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
struct cpu_hw_events *cpuhw;
cpuhw = this_cpu_ptr(&cpu_hw_events);
power_pmu_bhrb_read(event, cpuhw);
perf_sample_save_brstack(&data, event, &cpuhw->bhrb_stack);
perf_sample_save_brstack(&data, event, &cpuhw->bhrb_stack, NULL);
}

if (event->attr.sample_type & PERF_SAMPLE_DATA_SRC &&
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/events/amd/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ static int amd_pmu_v2_handle_irq(struct pt_regs *regs)
continue;

if (has_branch_stack(event))
perf_sample_save_brstack(&data, event, &cpuc->lbr_stack);
perf_sample_save_brstack(&data, event, &cpuc->lbr_stack, NULL);

if (perf_event_overflow(event, &data, regs))
x86_pmu_stop(event, 0);
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,7 @@ int x86_pmu_hw_config(struct perf_event *event)
}
}

if (event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK)
if (branch_sample_call_stack(event))
event->attach_state |= PERF_ATTACH_TASK_DATA;

/*
Expand Down Expand Up @@ -1705,7 +1705,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs)
perf_sample_data_init(&data, 0, event->hw.last_period);

if (has_branch_stack(event))
perf_sample_save_brstack(&data, event, &cpuc->lbr_stack);
perf_sample_save_brstack(&data, event, &cpuc->lbr_stack, NULL);

if (perf_event_overflow(event, &data, regs))
x86_pmu_stop(event, 0);
Expand Down
117 changes: 108 additions & 9 deletions arch/x86/events/intel/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2527,9 +2527,14 @@ static void intel_pmu_assign_event(struct perf_event *event, int idx)
perf_report_aux_output_id(event, idx);
}

static __always_inline bool intel_pmu_needs_branch_stack(struct perf_event *event)
{
return event->hw.flags & PERF_X86_EVENT_NEEDS_BRANCH_STACK;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Use explicit boolean conversion in intel_pmu_needs_branch_stack

Use !!(event->hw.flags & PERF_X86_EVENT_NEEDS_BRANCH_STACK) to ensure the function returns a true boolean value.

Suggested change
return event->hw.flags & PERF_X86_EVENT_NEEDS_BRANCH_STACK;
return !!(event->hw.flags & PERF_X86_EVENT_NEEDS_BRANCH_STACK);

}

static void intel_pmu_del_event(struct perf_event *event)
{
if (needs_branch_stack(event))
if (intel_pmu_needs_branch_stack(event))
intel_pmu_lbr_del(event);
if (event->attr.precise_ip)
intel_pmu_pebs_del(event);
Expand Down Expand Up @@ -2792,6 +2797,7 @@ static void intel_pmu_enable_fixed(struct perf_event *event)

static void intel_pmu_enable_event(struct perf_event *event)
{
u64 enable_mask = ARCH_PERFMON_EVENTSEL_ENABLE;
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;

Expand All @@ -2800,8 +2806,10 @@ static void intel_pmu_enable_event(struct perf_event *event)

switch (idx) {
case 0 ... INTEL_PMC_IDX_FIXED - 1:
if (branch_sample_counters(event))
enable_mask |= ARCH_PERFMON_EVENTSEL_BR_CNTR;
intel_set_masks(event, idx);
__x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE);
__x86_pmu_enable_event(hwc, enable_mask);
break;
case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1:
case INTEL_PMC_IDX_METRIC_BASE ... INTEL_PMC_IDX_METRIC_END:
Expand All @@ -2825,7 +2833,7 @@ static void intel_pmu_add_event(struct perf_event *event)
{
if (event->attr.precise_ip)
intel_pmu_pebs_add(event);
if (needs_branch_stack(event))
if (intel_pmu_needs_branch_stack(event))
intel_pmu_lbr_add(event);
}

Expand Down Expand Up @@ -3052,7 +3060,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
perf_sample_data_init(&data, 0, event->hw.last_period);

if (has_branch_stack(event))
perf_sample_save_brstack(&data, event, &cpuc->lbr_stack);
intel_pmu_lbr_save_brstack(&data, cpuc, event);

if (perf_event_overflow(event, &data, regs))
x86_pmu_stop(event, 0);
Expand Down Expand Up @@ -3617,6 +3625,13 @@ intel_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
if (cpuc->excl_cntrs)
return intel_get_excl_constraints(cpuc, event, idx, c2);

/* Not all counters support the branch counter feature. */
if (branch_sample_counters(event)) {
c2 = dyn_constraint(cpuc, c2, idx);
c2->idxmsk64 &= x86_pmu.lbr_counters;
c2->weight = hweight64(c2->idxmsk64);
}

return c2;
}

Expand Down Expand Up @@ -3987,7 +4002,62 @@ static int intel_pmu_hw_config(struct perf_event *event)
x86_pmu.pebs_aliases(event);
}

if (needs_branch_stack(event)) {
if (needs_branch_stack(event) && is_sampling_event(event))
Copy link

Copilot AI Jun 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the new intel_pmu_needs_branch_stack(event) helper instead of needs_branch_stack(event) to include branch counter setups consistently.

Suggested change
if (needs_branch_stack(event) && is_sampling_event(event))
if (intel_pmu_needs_branch_stack(event) && is_sampling_event(event))

Copilot uses AI. Check for mistakes.
event->hw.flags |= PERF_X86_EVENT_NEEDS_BRANCH_STACK;

if (branch_sample_counters(event)) {
struct perf_event *leader, *sibling;
int num = 0;

if (!(x86_pmu.flags & PMU_FL_BR_CNTR) ||
(event->attr.config & ~INTEL_ARCH_EVENT_MASK))
return -EINVAL;

/*
* The branch counter logging is not supported in the call stack
* mode yet, since we cannot simply flush the LBR during e.g.,
* multiplexing. Also, there is no obvious usage with the call
* stack mode. Simply forbids it for now.
*
* If any events in the group enable the branch counter logging
* feature, the group is treated as a branch counter logging
* group, which requires the extra space to store the counters.
*/
leader = event->group_leader;
if (branch_sample_call_stack(leader))
return -EINVAL;
if (branch_sample_counters(leader))
num++;
leader->hw.flags |= PERF_X86_EVENT_BRANCH_COUNTERS;

for_each_sibling_event(sibling, leader) {
if (branch_sample_call_stack(sibling))
return -EINVAL;
if (branch_sample_counters(sibling))
num++;
}

if (num > fls(x86_pmu.lbr_counters))
return -EINVAL;
/*
* Only applying the PERF_SAMPLE_BRANCH_COUNTERS doesn't
* require any branch stack setup.
* Clear the bit to avoid unnecessary branch stack setup.
*/
if (0 == (event->attr.branch_sample_type &
~(PERF_SAMPLE_BRANCH_PLM_ALL |
PERF_SAMPLE_BRANCH_COUNTERS)))
event->hw.flags &= ~PERF_X86_EVENT_NEEDS_BRANCH_STACK;

/*
* Force the leader to be a LBR event. So LBRs can be reset
* with the leader event. See intel_pmu_lbr_del() for details.
*/
if (!intel_pmu_needs_branch_stack(leader))
return -EINVAL;
}

if (intel_pmu_needs_branch_stack(event)) {
ret = intel_pmu_setup_lbr_filter(event);
if (ret)
return ret;
Expand Down Expand Up @@ -4470,8 +4540,13 @@ cmt_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
*/
if (event->attr.precise_ip == 3) {
/* Force instruction:ppp on PMC0, 1 and Fixed counter 0 */
if (constraint_match(&fixed0_constraint, event->hw.config))
return &fixed0_counter0_1_constraint;
if (constraint_match(&fixed0_constraint, event->hw.config)) {
/* The fixed counter 0 doesn't support LBR event logging. */
if (branch_sample_counters(event))
return &counter0_1_constraint;
else
return &fixed0_counter0_1_constraint;
}

switch (c->idxmsk64 & 0x3ull) {
case 0x1:
Expand Down Expand Up @@ -4668,7 +4743,7 @@ int intel_cpuc_prepare(struct cpu_hw_events *cpuc, int cpu)
goto err;
}

if (x86_pmu.flags & (PMU_FL_EXCL_CNTRS | PMU_FL_TFA)) {
if (x86_pmu.flags & (PMU_FL_EXCL_CNTRS | PMU_FL_TFA | PMU_FL_BR_CNTR)) {
size_t sz = X86_PMC_IDX_MAX * sizeof(struct event_constraint);

cpuc->constraint_list = kzalloc_node(sz, GFP_KERNEL, cpu_to_node(cpu));
Expand Down Expand Up @@ -5645,15 +5720,39 @@ static ssize_t branches_show(struct device *cdev,

static DEVICE_ATTR_RO(branches);

static ssize_t branch_counter_nr_show(struct device *cdev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", fls(x86_pmu.lbr_counters));
}

static DEVICE_ATTR_RO(branch_counter_nr);

static ssize_t branch_counter_width_show(struct device *cdev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", LBR_INFO_BR_CNTR_BITS);
}

static DEVICE_ATTR_RO(branch_counter_width);

static struct attribute *lbr_attrs[] = {
&dev_attr_branches.attr,
&dev_attr_branch_counter_nr.attr,
&dev_attr_branch_counter_width.attr,
NULL
};

static umode_t
lbr_is_visible(struct kobject *kobj, struct attribute *attr, int i)
{
return x86_pmu.lbr_nr ? attr->mode : 0;
/* branches */
if (i == 0)
return x86_pmu.lbr_nr ? attr->mode : 0;

return (x86_pmu.flags & PMU_FL_BR_CNTR) ? attr->mode : 0;
}

static char pmu_name_str[30];
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/events/intel/ds.c
Original file line number Diff line number Diff line change
Expand Up @@ -1757,7 +1757,7 @@ static void setup_pebs_fixed_sample_data(struct perf_event *event,
setup_pebs_time(event, data, pebs->tsc);

if (has_branch_stack(event))
perf_sample_save_brstack(data, event, &cpuc->lbr_stack);
perf_sample_save_brstack(data, event, &cpuc->lbr_stack, NULL);
}

static void adaptive_pebs_save_regs(struct pt_regs *regs,
Expand Down Expand Up @@ -1918,7 +1918,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,

if (has_branch_stack(event)) {
intel_pmu_store_pebs_lbrs(lbr);
perf_sample_save_brstack(data, event, &cpuc->lbr_stack);
intel_pmu_lbr_save_brstack(data, cpuc, event);
}
}

Expand Down
85 changes: 84 additions & 1 deletion arch/x86/events/intel/lbr.c
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,25 @@ void intel_pmu_lbr_del(struct perf_event *event)
WARN_ON_ONCE(cpuc->lbr_users < 0);
WARN_ON_ONCE(cpuc->lbr_pebs_users < 0);
perf_sched_cb_dec(event->pmu);

/*
* The logged occurrences information is only valid for the
* current LBR group. If another LBR group is scheduled in
* later, the information from the stale LBRs will be wrongly
* interpreted. Reset the LBRs here.
*
* Only clear once for a branch counter group with the leader
* event. Because
* - Cannot simply reset the LBRs with the !cpuc->lbr_users.
* Because it's possible that the last LBR user is not in a
* branch counter group, e.g., a branch_counters group +
* several normal LBR events.
* - The LBR reset can be done with any one of the events in a
* branch counter group, since they are always scheduled together.
* It's easy to force the leader event an LBR event.
*/
if (is_branch_counters_group(event) && event == event->group_leader)
intel_pmu_lbr_reset();
}

static inline bool vlbr_exclude_host(void)
Expand Down Expand Up @@ -866,6 +885,8 @@ static __always_inline u16 get_lbr_cycles(u64 info)
return cycles;
}

static_assert((64 - PERF_BRANCH_ENTRY_INFO_BITS_MAX) > LBR_INFO_BR_CNTR_NUM * LBR_INFO_BR_CNTR_BITS);

static void intel_pmu_store_lbr(struct cpu_hw_events *cpuc,
struct lbr_entry *entries)
{
Expand Down Expand Up @@ -898,11 +919,67 @@ static void intel_pmu_store_lbr(struct cpu_hw_events *cpuc,
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().
Copy link

Copilot AI Jun 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix typo: change "Pleae" to "Please" in the comment.

Suggested change
* to the perf tool. Pleae see intel_pmu_lbr_counters_reorder().
* to the perf tool. Please see intel_pmu_lbr_counters_reorder().

Copilot uses AI. Check for mistakes.
*/
e->reserved = (info >> LBR_INFO_BR_CNTR_OFFSET) & LBR_INFO_BR_CNTR_FULL_MASK;
}

cpuc->lbr_stack.nr = i;
}

/*
* The enabled order may be different from the counter order.
* Update the lbr_counters with the enabled order.
*/
static void intel_pmu_lbr_counters_reorder(struct cpu_hw_events *cpuc,
struct perf_event *event)
{
int i, j, pos = 0, order[X86_PMC_IDX_MAX];
struct perf_event *leader, *sibling;
u64 src, dst, cnt;

leader = event->group_leader;
if (branch_sample_counters(leader))
order[pos++] = leader->hw.idx;

for_each_sibling_event(sibling, leader) {
if (!branch_sample_counters(sibling))
continue;
order[pos++] = sibling->hw.idx;
}

WARN_ON_ONCE(!pos);

for (i = 0; i < cpuc->lbr_stack.nr; i++) {
src = cpuc->lbr_entries[i].reserved;
dst = 0;
for (j = 0; j < pos; j++) {
cnt = (src >> (order[j] * LBR_INFO_BR_CNTR_BITS)) & LBR_INFO_BR_CNTR_MASK;
dst |= cnt << j * LBR_INFO_BR_CNTR_BITS;
}
cpuc->lbr_counters[i] = dst;
cpuc->lbr_entries[i].reserved = 0;
}
}

void intel_pmu_lbr_save_brstack(struct perf_sample_data *data,
struct cpu_hw_events *cpuc,
struct perf_event *event)
{
if (is_branch_counters_group(event)) {
intel_pmu_lbr_counters_reorder(cpuc, event);
perf_sample_save_brstack(data, event, &cpuc->lbr_stack, cpuc->lbr_counters);
return;
}

perf_sample_save_brstack(data, event, &cpuc->lbr_stack, NULL);
}

static void intel_pmu_arch_lbr_read(struct cpu_hw_events *cpuc)
{
intel_pmu_store_lbr(cpuc, NULL);
Expand Down Expand Up @@ -1173,8 +1250,10 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
for (i = 0; i < cpuc->lbr_stack.nr; ) {
if (!cpuc->lbr_entries[i].from) {
j = i;
while (++j < cpuc->lbr_stack.nr)
while (++j < cpuc->lbr_stack.nr) {
cpuc->lbr_entries[j-1] = cpuc->lbr_entries[j];
cpuc->lbr_counters[j-1] = cpuc->lbr_counters[j];
}
cpuc->lbr_stack.nr--;
if (!cpuc->lbr_entries[i].from)
continue;
Expand Down Expand Up @@ -1525,8 +1604,12 @@ void __init intel_pmu_arch_lbr_init(void)
x86_pmu.lbr_mispred = ecx.split.lbr_mispred;
x86_pmu.lbr_timed_lbr = ecx.split.lbr_timed_lbr;
x86_pmu.lbr_br_type = ecx.split.lbr_br_type;
x86_pmu.lbr_counters = ecx.split.lbr_counters;
x86_pmu.lbr_nr = lbr_nr;

if (!!x86_pmu.lbr_counters)
x86_pmu.flags |= PMU_FL_BR_CNTR;

if (x86_pmu.lbr_mispred)
static_branch_enable(&x86_lbr_mispred);
if (x86_pmu.lbr_timed_lbr)
Expand Down
Loading
Loading