Re: [RFC][PATCH 08/11] perf, x86: Implement simple LBR support

From: Stephane Eranian
Date: Wed Mar 03 2010 - 16:52:43 EST


On Wed, Mar 3, 2010 at 5:39 PM, Peter Zijlstra <a.p.zijlstra@xxxxxxxxx> wrote:
> Implement support for Intel LBR stacks that support
> FREEZE_LBRS_ON_PMI. We do not (yet?) support the LBR config register
> because that is SMT wide and would also put undue restraints on the
> PEBS users.
>
You're saying PEBS users have priorities over pure LBR users?
Why is that?

Without coding this, how would you expose LBR configuration to userland
given you're using the PERF_SAMPLE_BRANCH_STACK approach?


> Signed-off-by: Peter Zijlstra <a.p.zijlstra@xxxxxxxxx>
> ---
> Âarch/x86/kernel/cpu/perf_event.c      |  22 ++
> Âarch/x86/kernel/cpu/perf_event_intel.c   |  13 +
> Âarch/x86/kernel/cpu/perf_event_intel_lbr.c | Â228 +++++++++++++++++++++++++++++
> Â3 files changed, 263 insertions(+)
>
> Index: linux-2.6/arch/x86/kernel/cpu/perf_event.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/cpu/perf_event.c
> +++ linux-2.6/arch/x86/kernel/cpu/perf_event.c
> @@ -48,6 +48,12 @@ struct amd_nb {
> Â Â Â Âstruct event_constraint event_constraints[X86_PMC_IDX_MAX];
> Â};
>
> +#define MAX_LBR_ENTRIES Â Â Â Â Â Â Â Â16
> +
> +struct lbr_entry {
> + Â Â Â u64 Â Â from, to, flags;
> +};
> +
> Âstruct cpu_hw_events {
> Â Â Â Â/*
> Â Â Â Â * Generic x86 PMC bits
> @@ -70,6 +76,14 @@ struct cpu_hw_events {
> Â Â Â Âu64 Â Â Â Â Â Â Â Â Â Â pebs_enabled;
>
> Â Â Â Â/*
> + Â Â Â Â* Intel LBR bits
> + Â Â Â Â*/
> +    int           lbr_users;
> +    int           lbr_entries;
> +    struct lbr_entry    Âlbr_stack[MAX_LBR_ENTRIES];
> +    void          Â*lbr_context;
> +
> + Â Â Â /*
> Â Â Â Â * AMD specific bits
> Â Â Â Â */
>    Âstruct amd_nb      *amd_nb;
> @@ -154,6 +168,13 @@ struct x86_pmu {
>    Âint       pebs_record_size;
>    Âvoid      Â(*drain_pebs)(void);
> Â Â Â Âstruct event_constraint *pebs_constraints;
> +
> + Â Â Â /*
> + Â Â Â Â* Intel LBR
> + Â Â Â Â*/
> +    unsigned long  lbr_tos, lbr_from, lbr_to; /* MSR base regs    */
> +    int       lbr_nr;          Â/* hardware stack size */
> +    int       lbr_format;        Â/* hardware format   */
> Â};
>
> Âstatic struct x86_pmu x86_pmu __read_mostly;
> @@ -1238,6 +1259,7 @@ undo:
>
> Â#include "perf_event_amd.c"
> Â#include "perf_event_p6.c"
> +#include "perf_event_intel_lbr.c"
> Â#include "perf_event_intel_ds.c"
> Â#include "perf_event_intel.c"
>
> Index: linux-2.6/arch/x86/kernel/cpu/perf_event_intel.c
> ===================================================================
> --- linux-2.6.orig/arch/x86/kernel/cpu/perf_event_intel.c
> +++ linux-2.6/arch/x86/kernel/cpu/perf_event_intel.c
> @@ -480,6 +480,7 @@ static void intel_pmu_disable_all(void)
> Â Â Â Â Â Â Â Âintel_pmu_disable_bts();
>
> Â Â Â Âintel_pmu_pebs_disable_all();
> + Â Â Â intel_pmu_lbr_disable_all();
> Â}
>
> Âstatic void intel_pmu_enable_all(void)
> @@ -499,6 +500,7 @@ static void intel_pmu_enable_all(void)
> Â Â Â Â}
>
> Â Â Â Âintel_pmu_pebs_enable_all();
> + Â Â Â intel_pmu_lbr_enable_all();
> Â}
>
> Âstatic inline u64 intel_pmu_get_status(void)
> @@ -675,6 +677,8 @@ again:
> Â Â Â Âinc_irq_stat(apic_perf_irqs);
> Â Â Â Âack = status;
>
> + Â Â Â intel_pmu_lbr_read();
> +
> Â Â Â Â/*
> Â Â Â Â * PEBS overflow sets bit 62 in the global status register
> Â Â Â Â */
> @@ -847,6 +851,8 @@ static __init int intel_pmu_init(void)
> Â Â Â Â Â Â Â Âmemcpy(hw_cache_event_ids, core2_hw_cache_event_ids,
> Â Â Â Â Â Â Â Â Â Â Â sizeof(hw_cache_event_ids));
>
> + Â Â Â Â Â Â Â intel_pmu_lbr_init_core();
> +
> Â Â Â Â Â Â Â Âx86_pmu.event_constraints = intel_core2_event_constraints;
> Â Â Â Â Â Â Â Âpr_cont("Core2 events, ");
> Â Â Â Â Â Â Â Âbreak;
> @@ -856,13 +862,18 @@ static __init int intel_pmu_init(void)
> Â Â Â Â Â Â Â Âmemcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids,
> Â Â Â Â Â Â Â Â Â Â Â sizeof(hw_cache_event_ids));
>
> + Â Â Â Â Â Â Â intel_pmu_lbr_init_nhm();
> +
> Â Â Â Â Â Â Â Âx86_pmu.event_constraints = intel_nehalem_event_constraints;
> Â Â Â Â Â Â Â Âpr_cont("Nehalem/Corei7 events, ");
> Â Â Â Â Â Â Â Âbreak;
> +
> Â Â Â Âcase 28: /* Atom */
> Â Â Â Â Â Â Â Âmemcpy(hw_cache_event_ids, atom_hw_cache_event_ids,
> Â Â Â Â Â Â Â Â Â Â Â sizeof(hw_cache_event_ids));
>
> + Â Â Â Â Â Â Â intel_pmu_lbr_init_atom();
> +
> Â Â Â Â Â Â Â Âx86_pmu.event_constraints = intel_gen_event_constraints;
> Â Â Â Â Â Â Â Âpr_cont("Atom events, ");
> Â Â Â Â Â Â Â Âbreak;
> @@ -872,6 +883,8 @@ static __init int intel_pmu_init(void)
> Â Â Â Â Â Â Â Âmemcpy(hw_cache_event_ids, westmere_hw_cache_event_ids,
> Â Â Â Â Â Â Â Â Â Â Â sizeof(hw_cache_event_ids));
>
> + Â Â Â Â Â Â Â intel_pmu_lbr_init_nhm();
> +
> Â Â Â Â Â Â Â Âx86_pmu.event_constraints = intel_westmere_event_constraints;
> Â Â Â Â Â Â Â Âpr_cont("Westmere events, ");
> Â Â Â Â Â Â Â Âbreak;
> Index: linux-2.6/arch/x86/kernel/cpu/perf_event_intel_lbr.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6/arch/x86/kernel/cpu/perf_event_intel_lbr.c
> @@ -0,0 +1,228 @@
> +#ifdef CONFIG_CPU_SUP_INTEL
> +
> +enum {
> + Â Â Â LBR_FORMAT_32 Â Â Â Â Â = 0x00,
> + Â Â Â LBR_FORMAT_LIP Â Â Â Â Â= 0x01,
> + Â Â Â LBR_FORMAT_EIP Â Â Â Â Â= 0x02,
> + Â Â Â LBR_FORMAT_EIP_FLAGS Â Â= 0x03,
> +};
> +
> +/*
> + * We only support LBR implementations that have FREEZE_LBRS_ON_PMI
> + * otherwise it becomes near impossible to get a reliable stack.
> + */
> +
> +#define X86_DEBUGCTL_LBR Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â (1 << 0)
> +#define X86_DEBUGCTL_FREEZE_LBRS_ON_PMI Â Â Â Â Â Â Â Â(1 << 11)
> +
> +static void __intel_pmu_lbr_enable(void)
> +{
> + Â Â Â u64 debugctl;
> +
> + Â Â Â rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
> + Â Â Â debugctl |= (X86_DEBUGCTL_LBR | X86_DEBUGCTL_FREEZE_LBRS_ON_PMI);
> + Â Â Â wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
> +}
> +
> +static void __intel_pmu_lbr_disable(void)
> +{
> + Â Â Â u64 debugctl;
> +
> + Â Â Â rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
> + Â Â Â debugctl &= ~(X86_DEBUGCTL_LBR | X86_DEBUGCTL_FREEZE_LBRS_ON_PMI);
> + Â Â Â wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
> +}
> +
> +static void intel_pmu_lbr_reset_32(void)
> +{
> + Â Â Â int i;
> +
> + Â Â Â for (i = 0; i < x86_pmu.lbr_nr; i++)
> + Â Â Â Â Â Â Â wrmsrl(x86_pmu.lbr_from + i, 0);
> +}
> +
> +static void intel_pmu_lbr_reset_64(void)
> +{
> + Â Â Â int i;
> +
> + Â Â Â for (i = 0; i < x86_pmu.lbr_nr; i++) {
> + Â Â Â Â Â Â Â wrmsrl(x86_pmu.lbr_from + i, 0);
> +        wrmsrl(x86_pmu.lbr_to  + i, 0);
> + Â Â Â }
> +}
> +
> +static void intel_pmu_lbr_reset(void)
> +{
> + Â Â Â if (x86_pmu.lbr_format == LBR_FORMAT_32)
> + Â Â Â Â Â Â Â intel_pmu_lbr_reset_32();
> + Â Â Â else
> + Â Â Â Â Â Â Â intel_pmu_lbr_reset_64();
> +}
> +
> +static void intel_pmu_lbr_enable(struct perf_event *event)
> +{
> + Â Â Â struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +
> + Â Â Â if (!x86_pmu.lbr_nr)
> + Â Â Â Â Â Â Â return;
> +
> + Â Â Â WARN_ON(cpuc->enabled);
> +
> + Â Â Â /*
> + Â Â Â Â* Reset the LBR stack if this is the first LBR user or
> + Â Â Â Â* we changed task context so as to avoid data leaks.
> + Â Â Â Â*/
> +
> + Â Â Â if (!cpuc->lbr_users ||
> + Â Â Â Â Â (event->ctx->task && cpuc->lbr_context != event->ctx)) {
> + Â Â Â Â Â Â Â intel_pmu_lbr_reset();
> + Â Â Â Â Â Â Â cpuc->lbr_context = event->ctx;
> + Â Â Â }
> +
> + Â Â Â cpuc->lbr_users++;
> +}
> +
> +static void intel_pmu_lbr_disable(struct perf_event *event)
> +{
> + Â Â Â struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +
> + Â Â Â if (!x86_pmu.lbr_nr)
> + Â Â Â Â Â Â Â return;
> +
> + Â Â Â cpuc->lbr_users--;
> +
> + Â Â Â BUG_ON(cpuc->lbr_users < 0);
> + Â Â Â WARN_ON(cpuc->enabled);
> +}
> +
> +static void intel_pmu_lbr_enable_all(void)
> +{
> + Â Â Â struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +
> + Â Â Â if (cpuc->lbr_users)
> + Â Â Â Â Â Â Â __intel_pmu_lbr_enable();
> +}
> +
> +static void intel_pmu_lbr_disable_all(void)
> +{
> + Â Â Â struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +
> + Â Â Â if (cpuc->lbr_users)
> + Â Â Â Â Â Â Â __intel_pmu_lbr_disable();
> +}
> +
> +static inline u64 intel_pmu_lbr_tos(void)
> +{
> + Â Â Â u64 tos;
> +
> + Â Â Â rdmsrl(x86_pmu.lbr_tos, tos);
> +
> + Â Â Â return tos;
> +}
> +
> +static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc)
> +{
> + Â Â Â unsigned long mask = x86_pmu.lbr_nr - 1;
> + Â Â Â u64 tos = intel_pmu_lbr_tos();
> + Â Â Â int i;
> +
> + Â Â Â for (i = 0; i < x86_pmu.lbr_nr; i++, tos--) {
> + Â Â Â Â Â Â Â unsigned long lbr_idx = (tos - i) & mask;
> + Â Â Â Â Â Â Â union {
> + Â Â Â Â Â Â Â Â Â Â Â struct {
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â u32 from;
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â u32 to;
> + Â Â Â Â Â Â Â Â Â Â Â };
> + Â Â Â Â Â Â Â Â Â Â Â u64 Â Â lbr;
> + Â Â Â Â Â Â Â } msr_lastbranch;
> +
> + Â Â Â Â Â Â Â rdmsrl(x86_pmu.lbr_from + lbr_idx, msr_lastbranch.lbr);
> +
> + Â Â Â Â Â Â Â cpuc->lbr_stack[i].from Â= msr_lastbranch.from;
> +        cpuc->lbr_stack[i].to  Â= msr_lastbranch.to;
> + Â Â Â Â Â Â Â cpuc->lbr_stack[i].flags = 0;
> + Â Â Â }
> + Â Â Â cpuc->lbr_entries = i;
> +}
> +
> +#define LBR_FROM_FLAG_MISPRED Â(1ULL << 63)
> +
> +/*
> + * Due to lack of segmentation in Linux the effective address (offset)
> + * is the same as the linear address, allowing us to merge the LIP and EIP
> + * LBR formats.
> + */
> +static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc)
> +{
> + Â Â Â unsigned long mask = x86_pmu.lbr_nr - 1;
> + Â Â Â u64 tos = intel_pmu_lbr_tos();
> + Â Â Â int i;
> +
> + Â Â Â for (i = 0; i < x86_pmu.lbr_nr; i++, tos--) {
> + Â Â Â Â Â Â Â unsigned long lbr_idx = (tos - i) & mask;
> + Â Â Â Â Â Â Â u64 from, to, flags = 0;
> +
> + Â Â Â Â Â Â Â rdmsrl(x86_pmu.lbr_from + lbr_idx, from);
> +        rdmsrl(x86_pmu.lbr_to  + lbr_idx, to);
> +
> + Â Â Â Â Â Â Â if (x86_pmu.lbr_format == LBR_FORMAT_EIP_FLAGS) {
> + Â Â Â Â Â Â Â Â Â Â Â flags = !!(from & LBR_FROM_FLAG_MISPRED);
> + Â Â Â Â Â Â Â Â Â Â Â from = (u64)((((s64)from) << 1) >> 1);
> + Â Â Â Â Â Â Â }
> +
> + Â Â Â Â Â Â Â cpuc->lbr_stack[i].from Â= from;
> +        cpuc->lbr_stack[i].to  Â= to;
> + Â Â Â Â Â Â Â cpuc->lbr_stack[i].flags = flags;
> + Â Â Â }
> + Â Â Â cpuc->lbr_entries = i;
> +}
> +
> +static void intel_pmu_lbr_read(void)
> +{
> + Â Â Â struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
> +
> + Â Â Â if (!cpuc->lbr_users)
> + Â Â Â Â Â Â Â return;
> +
> + Â Â Â if (x86_pmu.lbr_format == LBR_FORMAT_32)
> + Â Â Â Â Â Â Â intel_pmu_lbr_read_32(cpuc);
> + Â Â Â else
> + Â Â Â Â Â Â Â intel_pmu_lbr_read_64(cpuc);
> +}
> +
> +static int intel_pmu_lbr_format(void)
> +{
> + Â Â Â u64 capabilities;
> +
> + Â Â Â rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities);
> + Â Â Â return capabilities & 0x1f;
> +}
> +
> +static void intel_pmu_lbr_init_core(void)
> +{
> + Â Â Â x86_pmu.lbr_format = intel_pmu_lbr_format();
> +    x86_pmu.lbr_nr   = 4;
> +    x86_pmu.lbr_tos  Â= 0x01c9;
> +    x86_pmu.lbr_from  = 0x40;
> +    x86_pmu.lbr_to   = 0x60;
> +}
> +
> +static void intel_pmu_lbr_init_nhm(void)
> +{
> + Â Â Â x86_pmu.lbr_format = intel_pmu_lbr_format();
> +    x86_pmu.lbr_nr   = 16;
> +    x86_pmu.lbr_tos  Â= 0x01c9;
> +    x86_pmu.lbr_from  = 0x680;
> +    x86_pmu.lbr_to   = 0x6c0;
> +}
> +
> +static void intel_pmu_lbr_init_atom(void)
> +{
> + Â Â Â x86_pmu.lbr_format = intel_pmu_lbr_format();
> +    x86_pmu.lbr_nr   = 8;
> +    x86_pmu.lbr_tos  Â= 0x01c9;
> +    x86_pmu.lbr_from  = 0x40;
> +    x86_pmu.lbr_to   = 0x60;
> +}
> +
> +#endif /* CONFIG_CPU_SUP_INTEL */
>
> --
>
>



--
Stephane Eranian | EMEA Software Engineering
Google France | 38 avenue de l'OpÃra | 75002 Paris
Tel : +33 (0) 1 42 68 53 00
This email may be confidential or privileged. If you received this
communication by mistake, please
don't forward it to anyone else, please erase all copies and
attachments, and please let me know that
it went to the wrong person. Thanks
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/