Skip to content

Commit c085fb8

Browse files
Kan LiangPeter Zijlstra
authored andcommitted
perf/x86/intel/lbr: Support XSAVES for arch LBR read
Reading LBR registers in a perf NMI handler for a non-PEBS event causes a high overhead because the number of LBR registers is huge. To reduce the overhead, the XSAVES instruction should be used to replace the LBR registers' reading method. The XSAVES buffer used for LBR read has to be per-CPU because the NMI handler invoked the lbr_read(). The existing task_ctx_data buffer cannot be used which is per-task and only be allocated for the LBR call stack mode. A new lbr_xsave pointer is introduced in the cpu_hw_events as an XSAVES buffer for LBR read. The XSAVES buffer should be allocated only when LBR is used by a non-PEBS event on the CPU because the total size of the lbr_xsave is not small (~1.4KB). The XSAVES buffer is allocated when a non-PEBS event is added, but it is lazily released in x86_release_hardware() when perf releases the entire PMU hardware resource, because perf may frequently schedule the event, e.g. high context switch. The lazy release method reduces the overhead of frequently allocate/free the buffer. If the lbr_xsave fails to be allocated, roll back to normal Arch LBR lbr_read(). Signed-off-by: Kan Liang <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Dave Hansen <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent ce711ea commit c085fb8

File tree

3 files changed

+47
-1
lines changed

3 files changed

+47
-1
lines changed

arch/x86/events/core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ void x86_release_hardware(void)
358358
if (atomic_dec_and_mutex_lock(&pmc_refcount, &pmc_reserve_mutex)) {
359359
release_pmc_hardware();
360360
release_ds_buffers();
361+
release_lbr_buffers();
361362
mutex_unlock(&pmc_reserve_mutex);
362363
}
363364
}

arch/x86/events/intel/lbr.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,7 @@ static inline bool branch_user_callstack(unsigned br_sel)
658658

659659
void intel_pmu_lbr_add(struct perf_event *event)
660660
{
661+
struct kmem_cache *kmem_cache = event->pmu->task_ctx_cache;
661662
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
662663

663664
if (!x86_pmu.lbr_nr)
@@ -695,6 +696,29 @@ void intel_pmu_lbr_add(struct perf_event *event)
695696
perf_sched_cb_inc(event->ctx->pmu);
696697
if (!cpuc->lbr_users++ && !event->total_time_running)
697698
intel_pmu_lbr_reset();
699+
700+
if (static_cpu_has(X86_FEATURE_ARCH_LBR) &&
701+
kmem_cache && !cpuc->lbr_xsave &&
702+
(cpuc->lbr_users != cpuc->lbr_pebs_users))
703+
cpuc->lbr_xsave = kmem_cache_alloc(kmem_cache, GFP_KERNEL);
704+
}
705+
706+
void release_lbr_buffers(void)
707+
{
708+
struct kmem_cache *kmem_cache = x86_get_pmu()->task_ctx_cache;
709+
struct cpu_hw_events *cpuc;
710+
int cpu;
711+
712+
if (!static_cpu_has(X86_FEATURE_ARCH_LBR))
713+
return;
714+
715+
for_each_possible_cpu(cpu) {
716+
cpuc = per_cpu_ptr(&cpu_hw_events, cpu);
717+
if (kmem_cache && cpuc->lbr_xsave) {
718+
kmem_cache_free(kmem_cache, cpuc->lbr_xsave);
719+
cpuc->lbr_xsave = NULL;
720+
}
721+
}
698722
}
699723

700724
void intel_pmu_lbr_del(struct perf_event *event)
@@ -945,6 +969,19 @@ static void intel_pmu_arch_lbr_read(struct cpu_hw_events *cpuc)
945969
intel_pmu_store_lbr(cpuc, NULL);
946970
}
947971

972+
static void intel_pmu_arch_lbr_read_xsave(struct cpu_hw_events *cpuc)
973+
{
974+
struct x86_perf_task_context_arch_lbr_xsave *xsave = cpuc->lbr_xsave;
975+
976+
if (!xsave) {
977+
intel_pmu_store_lbr(cpuc, NULL);
978+
return;
979+
}
980+
copy_dynamic_supervisor_to_kernel(&xsave->xsave, XFEATURE_MASK_LBR);
981+
982+
intel_pmu_store_lbr(cpuc, xsave->lbr.entries);
983+
}
984+
948985
void intel_pmu_lbr_read(void)
949986
{
950987
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
@@ -1767,14 +1804,15 @@ void __init intel_pmu_arch_lbr_init(void)
17671804
x86_pmu.lbr_ctl_map = NULL;
17681805

17691806
x86_pmu.lbr_reset = intel_pmu_arch_lbr_reset;
1770-
x86_pmu.lbr_read = intel_pmu_arch_lbr_read;
17711807
if (arch_lbr_xsave) {
17721808
x86_pmu.lbr_save = intel_pmu_arch_lbr_xsaves;
17731809
x86_pmu.lbr_restore = intel_pmu_arch_lbr_xrstors;
1810+
x86_pmu.lbr_read = intel_pmu_arch_lbr_read_xsave;
17741811
pr_cont("XSAVE ");
17751812
} else {
17761813
x86_pmu.lbr_save = intel_pmu_arch_lbr_save;
17771814
x86_pmu.lbr_restore = intel_pmu_arch_lbr_restore;
1815+
x86_pmu.lbr_read = intel_pmu_arch_lbr_read;
17781816
}
17791817

17801818
pr_cont("Architectural LBR, ");

arch/x86/events/perf_event.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ struct cpu_hw_events {
253253
void *last_task_ctx;
254254
int last_log_id;
255255
int lbr_select;
256+
void *lbr_xsave;
256257

257258
/*
258259
* Intel host/guest exclude bits
@@ -1066,6 +1067,8 @@ void release_ds_buffers(void);
10661067

10671068
void reserve_ds_buffers(void);
10681069

1070+
void release_lbr_buffers(void);
1071+
10691072
extern struct event_constraint bts_constraint;
10701073
extern struct event_constraint vlbr_constraint;
10711074

@@ -1207,6 +1210,10 @@ static inline void release_ds_buffers(void)
12071210
{
12081211
}
12091212

1213+
static inline void release_lbr_buffers(void)
1214+
{
1215+
}
1216+
12101217
static inline int intel_pmu_init(void)
12111218
{
12121219
return 0;

0 commit comments

Comments
 (0)