lkml.org 
[lkml]   [2015]   [Apr]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[tip:perf/core] perf/x86: Mark Intel PT and LBR/ BTS as mutually exclusive
    Commit-ID:  4807034248bed58d49a4f9f450c024e3b0f58577
    Gitweb: http://git.kernel.org/tip/4807034248bed58d49a4f9f450c024e3b0f58577
    Author: Alexander Shishkin <alexander.shishkin@linux.intel.com>
    AuthorDate: Wed, 14 Jan 2015 14:18:20 +0200
    Committer: Ingo Molnar <mingo@kernel.org>
    CommitDate: Thu, 2 Apr 2015 17:14:19 +0200

    perf/x86: Mark Intel PT and LBR/BTS as mutually exclusive

    Intel PT cannot be used at the same time as LBR or BTS and will cause a
    general protection fault if they are used together. In order to avoid
    fixing up GPs in the fast path, instead we disallow creating LBR/BTS
    events when PT events are present and vice versa.

    Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
    Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Frederic Weisbecker <fweisbec@gmail.com>
    Cc: H. Peter Anvin <hpa@zytor.com>
    Cc: Kaixu Xia <kaixu.xia@linaro.org>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Mike Galbraith <efault@gmx.de>
    Cc: Paul Mackerras <paulus@samba.org>
    Cc: Robert Richter <rric@kernel.org>
    Cc: Stephane Eranian <eranian@google.com>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: acme@infradead.org
    Cc: adrian.hunter@intel.com
    Cc: kan.liang@intel.com
    Cc: markus.t.metzger@intel.com
    Cc: mathieu.poirier@linaro.org
    Link: http://lkml.kernel.org/r/1421237903-181015-12-git-send-email-alexander.shishkin@linux.intel.com
    Signed-off-by: Ingo Molnar <mingo@kernel.org>
    ---
    arch/x86/kernel/cpu/perf_event.c | 43 ++++++++++++++++++++++++++++++++++
    arch/x86/kernel/cpu/perf_event.h | 40 +++++++++++++++++++++++++++++++
    arch/x86/kernel/cpu/perf_event_intel.c | 11 +++++++++
    3 files changed, 94 insertions(+)

    diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
    index 0420ebc..549d01d 100644
    --- a/arch/x86/kernel/cpu/perf_event.c
    +++ b/arch/x86/kernel/cpu/perf_event.c
    @@ -263,6 +263,14 @@ static void hw_perf_event_destroy(struct perf_event *event)
    }
    }

    +void hw_perf_lbr_event_destroy(struct perf_event *event)
    +{
    + hw_perf_event_destroy(event);
    +
    + /* undo the lbr/bts event accounting */
    + x86_del_exclusive(x86_lbr_exclusive_lbr);
    +}
    +
    static inline int x86_pmu_initialized(void)
    {
    return x86_pmu.handle_irq != NULL;
    @@ -302,6 +310,35 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
    return x86_pmu_extra_regs(val, event);
    }

    +/*
    + * Check if we can create event of a certain type (that no conflicting events
    + * are present).
    + */
    +int x86_add_exclusive(unsigned int what)
    +{
    + int ret = -EBUSY, i;
    +
    + if (atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what]))
    + return 0;
    +
    + mutex_lock(&pmc_reserve_mutex);
    + for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++)
    + if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i]))
    + goto out;
    +
    + atomic_inc(&x86_pmu.lbr_exclusive[what]);
    + ret = 0;
    +
    +out:
    + mutex_unlock(&pmc_reserve_mutex);
    + return ret;
    +}
    +
    +void x86_del_exclusive(unsigned int what)
    +{
    + atomic_dec(&x86_pmu.lbr_exclusive[what]);
    +}
    +
    int x86_setup_perfctr(struct perf_event *event)
    {
    struct perf_event_attr *attr = &event->attr;
    @@ -346,6 +383,12 @@ int x86_setup_perfctr(struct perf_event *event)
    /* BTS is currently only allowed for user-mode. */
    if (!attr->exclude_kernel)
    return -EOPNOTSUPP;
    +
    + /* disallow bts if conflicting events are present */
    + if (x86_add_exclusive(x86_lbr_exclusive_lbr))
    + return -EBUSY;
    +
    + event->destroy = hw_perf_lbr_event_destroy;
    }

    hwc->config |= config;
    diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
    index 87e5081..4749966 100644
    --- a/arch/x86/kernel/cpu/perf_event.h
    +++ b/arch/x86/kernel/cpu/perf_event.h
    @@ -408,6 +408,12 @@ union x86_pmu_config {

    #define X86_CONFIG(args...) ((union x86_pmu_config){.bits = {args}}).value

    +enum {
    + x86_lbr_exclusive_lbr,
    + x86_lbr_exclusive_pt,
    + x86_lbr_exclusive_max,
    +};
    +
    /*
    * struct x86_pmu - generic x86 pmu
    */
    @@ -506,6 +512,11 @@ struct x86_pmu {
    bool lbr_double_abort; /* duplicated lbr aborts */

    /*
    + * Intel PT/LBR/BTS are exclusive
    + */
    + atomic_t lbr_exclusive[x86_lbr_exclusive_max];
    +
    + /*
    * Extra registers for events
    */
    struct extra_reg *extra_regs;
    @@ -603,6 +614,12 @@ static inline int x86_pmu_rdpmc_index(int index)
    return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index;
    }

    +int x86_add_exclusive(unsigned int what);
    +
    +void x86_del_exclusive(unsigned int what);
    +
    +void hw_perf_lbr_event_destroy(struct perf_event *event);
    +
    int x86_setup_perfctr(struct perf_event *event);

    int x86_pmu_hw_config(struct perf_event *event);
    @@ -689,6 +706,29 @@ static inline int amd_pmu_init(void)

    #ifdef CONFIG_CPU_SUP_INTEL

    +static inline bool intel_pmu_needs_lbr_smpl(struct perf_event *event)
    +{
    + /* user explicitly requested branch sampling */
    + if (has_branch_stack(event))
    + return true;
    +
    + /* implicit branch sampling to correct PEBS skid */
    + if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1 &&
    + x86_pmu.intel_cap.pebs_format < 2)
    + return true;
    +
    + return false;
    +}
    +
    +static inline bool intel_pmu_has_bts(struct perf_event *event)
    +{
    + if (event->attr.config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS &&
    + !event->attr.freq && event->hw.sample_period == 1)
    + return true;
    +
    + return false;
    +}
    +
    int intel_pmu_save_and_restart(struct perf_event *event);

    struct event_constraint *
    diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
    index fc6dbc4..b7b3ff2 100644
    --- a/arch/x86/kernel/cpu/perf_event_intel.c
    +++ b/arch/x86/kernel/cpu/perf_event_intel.c
    @@ -1942,6 +1942,17 @@ static int intel_pmu_hw_config(struct perf_event *event)
    ret = intel_pmu_setup_lbr_filter(event);
    if (ret)
    return ret;
    +
    + /*
    + * BTS is set up earlier in this path, so don't account twice
    + */
    + if (!intel_pmu_has_bts(event)) {
    + /* disallow lbr if conflicting events are present */
    + if (x86_add_exclusive(x86_lbr_exclusive_lbr))
    + return -EBUSY;
    +
    + event->destroy = hw_perf_lbr_event_destroy;
    + }
    }

    if (event->attr.type != PERF_TYPE_RAW)

    \
     
     \ /
      Last update: 2015-04-02 21:21    [W:3.097 / U:1.692 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site