mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 08:58:07 +00:00
405b45376d
When you successfully write 0 to /sys/devices/cpu/rdpmc, the RDPMC instruction should be disabled unconditionally and immediately (after you close the SYSFS file) by the documentation. Instead, in the current implementation the PMU must be reloaded which happens only eventually some time in the future. Only after that the RDPMC instruction becomes disabled (on ring 3) on the respective core. This change makes the treatment of the 0 value as blocking and as unconditional as the current treatment of the 2 value, only the CR4.PCE bit is naturally set to false instead of true. Signed-off-by: Anthony Steinhauser <asteinhauser@google.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vince Weaver <vincent.weaver@maine.edu> Cc: acme@kernel.org Link: https://lkml.kernel.org/r/20191125054838.137615-1-asteinhauser@google.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
2605 lines
62 KiB
C
2605 lines
62 KiB
C
/*
|
|
* Performance events x86 architecture code
|
|
*
|
|
* Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
|
|
* Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
|
|
* Copyright (C) 2009 Jaswinder Singh Rajput
|
|
* Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
|
|
* Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra
|
|
* Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
|
|
* Copyright (C) 2009 Google, Inc., Stephane Eranian
|
|
*
|
|
* For licencing details see kernel-base/COPYING
|
|
*/
|
|
|
|
#include <linux/perf_event.h>
|
|
#include <linux/capability.h>
|
|
#include <linux/notifier.h>
|
|
#include <linux/hardirq.h>
|
|
#include <linux/kprobes.h>
|
|
#include <linux/export.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kdebug.h>
|
|
#include <linux/sched/mm.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/device.h>
|
|
#include <linux/nospec.h>
|
|
|
|
#include <asm/apic.h>
|
|
#include <asm/stacktrace.h>
|
|
#include <asm/nmi.h>
|
|
#include <asm/smp.h>
|
|
#include <asm/alternative.h>
|
|
#include <asm/mmu_context.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/timer.h>
|
|
#include <asm/desc.h>
|
|
#include <asm/ldt.h>
|
|
#include <asm/unwind.h>
|
|
|
|
#include "perf_event.h"
|
|
|
|
struct x86_pmu x86_pmu __read_mostly;
|
|
|
|
DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
|
|
.enabled = 1,
|
|
};
|
|
|
|
DEFINE_STATIC_KEY_FALSE(rdpmc_never_available_key);
|
|
DEFINE_STATIC_KEY_FALSE(rdpmc_always_available_key);
|
|
|
|
u64 __read_mostly hw_cache_event_ids
|
|
[PERF_COUNT_HW_CACHE_MAX]
|
|
[PERF_COUNT_HW_CACHE_OP_MAX]
|
|
[PERF_COUNT_HW_CACHE_RESULT_MAX];
|
|
u64 __read_mostly hw_cache_extra_regs
|
|
[PERF_COUNT_HW_CACHE_MAX]
|
|
[PERF_COUNT_HW_CACHE_OP_MAX]
|
|
[PERF_COUNT_HW_CACHE_RESULT_MAX];
|
|
|
|
/*
|
|
* Propagate event elapsed time into the generic event.
|
|
* Can only be executed on the CPU where the event is active.
|
|
* Returns the delta events processed.
|
|
*/
|
|
u64 x86_perf_event_update(struct perf_event *event)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
int shift = 64 - x86_pmu.cntval_bits;
|
|
u64 prev_raw_count, new_raw_count;
|
|
int idx = hwc->idx;
|
|
u64 delta;
|
|
|
|
if (idx == INTEL_PMC_IDX_FIXED_BTS)
|
|
return 0;
|
|
|
|
/*
|
|
* Careful: an NMI might modify the previous event value.
|
|
*
|
|
* Our tactic to handle this is to first atomically read and
|
|
* exchange a new raw count - then add that new-prev delta
|
|
* count to the generic event atomically:
|
|
*/
|
|
again:
|
|
prev_raw_count = local64_read(&hwc->prev_count);
|
|
rdpmcl(hwc->event_base_rdpmc, new_raw_count);
|
|
|
|
if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
|
|
new_raw_count) != prev_raw_count)
|
|
goto again;
|
|
|
|
/*
|
|
* Now we have the new raw value and have updated the prev
|
|
* timestamp already. We can now calculate the elapsed delta
|
|
* (event-)time and add that to the generic event.
|
|
*
|
|
* Careful, not all hw sign-extends above the physical width
|
|
* of the count.
|
|
*/
|
|
delta = (new_raw_count << shift) - (prev_raw_count << shift);
|
|
delta >>= shift;
|
|
|
|
local64_add(delta, &event->count);
|
|
local64_sub(delta, &hwc->period_left);
|
|
|
|
return new_raw_count;
|
|
}
|
|
|
|
/*
|
|
* Find and validate any extra registers to set up.
|
|
*/
|
|
static int x86_pmu_extra_regs(u64 config, struct perf_event *event)
|
|
{
|
|
struct hw_perf_event_extra *reg;
|
|
struct extra_reg *er;
|
|
|
|
reg = &event->hw.extra_reg;
|
|
|
|
if (!x86_pmu.extra_regs)
|
|
return 0;
|
|
|
|
for (er = x86_pmu.extra_regs; er->msr; er++) {
|
|
if (er->event != (config & er->config_mask))
|
|
continue;
|
|
if (event->attr.config1 & ~er->valid_mask)
|
|
return -EINVAL;
|
|
/* Check if the extra msrs can be safely accessed*/
|
|
if (!er->extra_msr_access)
|
|
return -ENXIO;
|
|
|
|
reg->idx = er->idx;
|
|
reg->config = event->attr.config1;
|
|
reg->reg = er->msr;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static atomic_t active_events;
|
|
static atomic_t pmc_refcount;
|
|
static DEFINE_MUTEX(pmc_reserve_mutex);
|
|
|
|
#ifdef CONFIG_X86_LOCAL_APIC
|
|
|
|
static bool reserve_pmc_hardware(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < x86_pmu.num_counters; i++) {
|
|
if (!reserve_perfctr_nmi(x86_pmu_event_addr(i)))
|
|
goto perfctr_fail;
|
|
}
|
|
|
|
for (i = 0; i < x86_pmu.num_counters; i++) {
|
|
if (!reserve_evntsel_nmi(x86_pmu_config_addr(i)))
|
|
goto eventsel_fail;
|
|
}
|
|
|
|
return true;
|
|
|
|
eventsel_fail:
|
|
for (i--; i >= 0; i--)
|
|
release_evntsel_nmi(x86_pmu_config_addr(i));
|
|
|
|
i = x86_pmu.num_counters;
|
|
|
|
perfctr_fail:
|
|
for (i--; i >= 0; i--)
|
|
release_perfctr_nmi(x86_pmu_event_addr(i));
|
|
|
|
return false;
|
|
}
|
|
|
|
static void release_pmc_hardware(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < x86_pmu.num_counters; i++) {
|
|
release_perfctr_nmi(x86_pmu_event_addr(i));
|
|
release_evntsel_nmi(x86_pmu_config_addr(i));
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
static bool reserve_pmc_hardware(void) { return true; }
|
|
static void release_pmc_hardware(void) {}
|
|
|
|
#endif
|
|
|
|
static bool check_hw_exists(void)
|
|
{
|
|
u64 val, val_fail = -1, val_new= ~0;
|
|
int i, reg, reg_fail = -1, ret = 0;
|
|
int bios_fail = 0;
|
|
int reg_safe = -1;
|
|
|
|
/*
|
|
* Check to see if the BIOS enabled any of the counters, if so
|
|
* complain and bail.
|
|
*/
|
|
for (i = 0; i < x86_pmu.num_counters; i++) {
|
|
reg = x86_pmu_config_addr(i);
|
|
ret = rdmsrl_safe(reg, &val);
|
|
if (ret)
|
|
goto msr_fail;
|
|
if (val & ARCH_PERFMON_EVENTSEL_ENABLE) {
|
|
bios_fail = 1;
|
|
val_fail = val;
|
|
reg_fail = reg;
|
|
} else {
|
|
reg_safe = i;
|
|
}
|
|
}
|
|
|
|
if (x86_pmu.num_counters_fixed) {
|
|
reg = MSR_ARCH_PERFMON_FIXED_CTR_CTRL;
|
|
ret = rdmsrl_safe(reg, &val);
|
|
if (ret)
|
|
goto msr_fail;
|
|
for (i = 0; i < x86_pmu.num_counters_fixed; i++) {
|
|
if (val & (0x03 << i*4)) {
|
|
bios_fail = 1;
|
|
val_fail = val;
|
|
reg_fail = reg;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If all the counters are enabled, the below test will always
|
|
* fail. The tools will also become useless in this scenario.
|
|
* Just fail and disable the hardware counters.
|
|
*/
|
|
|
|
if (reg_safe == -1) {
|
|
reg = reg_safe;
|
|
goto msr_fail;
|
|
}
|
|
|
|
/*
|
|
* Read the current value, change it and read it back to see if it
|
|
* matches, this is needed to detect certain hardware emulators
|
|
* (qemu/kvm) that don't trap on the MSR access and always return 0s.
|
|
*/
|
|
reg = x86_pmu_event_addr(reg_safe);
|
|
if (rdmsrl_safe(reg, &val))
|
|
goto msr_fail;
|
|
val ^= 0xffffUL;
|
|
ret = wrmsrl_safe(reg, val);
|
|
ret |= rdmsrl_safe(reg, &val_new);
|
|
if (ret || val != val_new)
|
|
goto msr_fail;
|
|
|
|
/*
|
|
* We still allow the PMU driver to operate:
|
|
*/
|
|
if (bios_fail) {
|
|
pr_cont("Broken BIOS detected, complain to your hardware vendor.\n");
|
|
pr_err(FW_BUG "the BIOS has corrupted hw-PMU resources (MSR %x is %Lx)\n",
|
|
reg_fail, val_fail);
|
|
}
|
|
|
|
return true;
|
|
|
|
msr_fail:
|
|
if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) {
|
|
pr_cont("PMU not available due to virtualization, using software events only.\n");
|
|
} else {
|
|
pr_cont("Broken PMU hardware detected, using software events only.\n");
|
|
pr_err("Failed to access perfctr msr (MSR %x is %Lx)\n",
|
|
reg, val_new);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void hw_perf_event_destroy(struct perf_event *event)
|
|
{
|
|
x86_release_hardware();
|
|
atomic_dec(&active_events);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static inline int
|
|
set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
|
|
{
|
|
struct perf_event_attr *attr = &event->attr;
|
|
unsigned int cache_type, cache_op, cache_result;
|
|
u64 config, val;
|
|
|
|
config = attr->config;
|
|
|
|
cache_type = (config >> 0) & 0xff;
|
|
if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
|
|
return -EINVAL;
|
|
cache_type = array_index_nospec(cache_type, PERF_COUNT_HW_CACHE_MAX);
|
|
|
|
cache_op = (config >> 8) & 0xff;
|
|
if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
|
|
return -EINVAL;
|
|
cache_op = array_index_nospec(cache_op, PERF_COUNT_HW_CACHE_OP_MAX);
|
|
|
|
cache_result = (config >> 16) & 0xff;
|
|
if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
|
|
return -EINVAL;
|
|
cache_result = array_index_nospec(cache_result, PERF_COUNT_HW_CACHE_RESULT_MAX);
|
|
|
|
val = hw_cache_event_ids[cache_type][cache_op][cache_result];
|
|
|
|
if (val == 0)
|
|
return -ENOENT;
|
|
|
|
if (val == -1)
|
|
return -EINVAL;
|
|
|
|
hwc->config |= val;
|
|
attr->config1 = hw_cache_extra_regs[cache_type][cache_op][cache_result];
|
|
return x86_pmu_extra_regs(val, event);
|
|
}
|
|
|
|
int x86_reserve_hardware(void)
|
|
{
|
|
int err = 0;
|
|
|
|
if (!atomic_inc_not_zero(&pmc_refcount)) {
|
|
mutex_lock(&pmc_reserve_mutex);
|
|
if (atomic_read(&pmc_refcount) == 0) {
|
|
if (!reserve_pmc_hardware())
|
|
err = -EBUSY;
|
|
else
|
|
reserve_ds_buffers();
|
|
}
|
|
if (!err)
|
|
atomic_inc(&pmc_refcount);
|
|
mutex_unlock(&pmc_reserve_mutex);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
void x86_release_hardware(void)
|
|
{
|
|
if (atomic_dec_and_mutex_lock(&pmc_refcount, &pmc_reserve_mutex)) {
|
|
release_pmc_hardware();
|
|
release_ds_buffers();
|
|
mutex_unlock(&pmc_reserve_mutex);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if we can create event of a certain type (that no conflicting events
|
|
* are present).
|
|
*/
|
|
int x86_add_exclusive(unsigned int what)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* When lbr_pt_coexist we allow PT to coexist with either LBR or BTS.
|
|
* LBR and BTS are still mutually exclusive.
|
|
*/
|
|
if (x86_pmu.lbr_pt_coexist && what == x86_lbr_exclusive_pt)
|
|
return 0;
|
|
|
|
if (!atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what])) {
|
|
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 fail_unlock;
|
|
}
|
|
atomic_inc(&x86_pmu.lbr_exclusive[what]);
|
|
mutex_unlock(&pmc_reserve_mutex);
|
|
}
|
|
|
|
atomic_inc(&active_events);
|
|
return 0;
|
|
|
|
fail_unlock:
|
|
mutex_unlock(&pmc_reserve_mutex);
|
|
return -EBUSY;
|
|
}
|
|
|
|
void x86_del_exclusive(unsigned int what)
|
|
{
|
|
if (x86_pmu.lbr_pt_coexist && what == x86_lbr_exclusive_pt)
|
|
return;
|
|
|
|
atomic_dec(&x86_pmu.lbr_exclusive[what]);
|
|
atomic_dec(&active_events);
|
|
}
|
|
|
|
int x86_setup_perfctr(struct perf_event *event)
|
|
{
|
|
struct perf_event_attr *attr = &event->attr;
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
u64 config;
|
|
|
|
if (!is_sampling_event(event)) {
|
|
hwc->sample_period = x86_pmu.max_period;
|
|
hwc->last_period = hwc->sample_period;
|
|
local64_set(&hwc->period_left, hwc->sample_period);
|
|
}
|
|
|
|
if (attr->type == PERF_TYPE_RAW)
|
|
return x86_pmu_extra_regs(event->attr.config, event);
|
|
|
|
if (attr->type == PERF_TYPE_HW_CACHE)
|
|
return set_ext_hw_attr(hwc, event);
|
|
|
|
if (attr->config >= x86_pmu.max_events)
|
|
return -EINVAL;
|
|
|
|
attr->config = array_index_nospec((unsigned long)attr->config, x86_pmu.max_events);
|
|
|
|
/*
|
|
* The generic map:
|
|
*/
|
|
config = x86_pmu.event_map(attr->config);
|
|
|
|
if (config == 0)
|
|
return -ENOENT;
|
|
|
|
if (config == -1LL)
|
|
return -EINVAL;
|
|
|
|
hwc->config |= config;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* check that branch_sample_type is compatible with
|
|
* settings needed for precise_ip > 1 which implies
|
|
* using the LBR to capture ALL taken branches at the
|
|
* priv levels of the measurement
|
|
*/
|
|
static inline int precise_br_compat(struct perf_event *event)
|
|
{
|
|
u64 m = event->attr.branch_sample_type;
|
|
u64 b = 0;
|
|
|
|
/* must capture all branches */
|
|
if (!(m & PERF_SAMPLE_BRANCH_ANY))
|
|
return 0;
|
|
|
|
m &= PERF_SAMPLE_BRANCH_KERNEL | PERF_SAMPLE_BRANCH_USER;
|
|
|
|
if (!event->attr.exclude_user)
|
|
b |= PERF_SAMPLE_BRANCH_USER;
|
|
|
|
if (!event->attr.exclude_kernel)
|
|
b |= PERF_SAMPLE_BRANCH_KERNEL;
|
|
|
|
/*
|
|
* ignore PERF_SAMPLE_BRANCH_HV, not supported on x86
|
|
*/
|
|
|
|
return m == b;
|
|
}
|
|
|
|
int x86_pmu_max_precise(void)
|
|
{
|
|
int precise = 0;
|
|
|
|
/* Support for constant skid */
|
|
if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) {
|
|
precise++;
|
|
|
|
/* Support for IP fixup */
|
|
if (x86_pmu.lbr_nr || x86_pmu.intel_cap.pebs_format >= 2)
|
|
precise++;
|
|
|
|
if (x86_pmu.pebs_prec_dist)
|
|
precise++;
|
|
}
|
|
return precise;
|
|
}
|
|
|
|
int x86_pmu_hw_config(struct perf_event *event)
|
|
{
|
|
if (event->attr.precise_ip) {
|
|
int precise = x86_pmu_max_precise();
|
|
|
|
if (event->attr.precise_ip > precise)
|
|
return -EOPNOTSUPP;
|
|
|
|
/* There's no sense in having PEBS for non sampling events: */
|
|
if (!is_sampling_event(event))
|
|
return -EINVAL;
|
|
}
|
|
/*
|
|
* check that PEBS LBR correction does not conflict with
|
|
* whatever the user is asking with attr->branch_sample_type
|
|
*/
|
|
if (event->attr.precise_ip > 1 && x86_pmu.intel_cap.pebs_format < 2) {
|
|
u64 *br_type = &event->attr.branch_sample_type;
|
|
|
|
if (has_branch_stack(event)) {
|
|
if (!precise_br_compat(event))
|
|
return -EOPNOTSUPP;
|
|
|
|
/* branch_sample_type is compatible */
|
|
|
|
} else {
|
|
/*
|
|
* user did not specify branch_sample_type
|
|
*
|
|
* For PEBS fixups, we capture all
|
|
* the branches at the priv level of the
|
|
* event.
|
|
*/
|
|
*br_type = PERF_SAMPLE_BRANCH_ANY;
|
|
|
|
if (!event->attr.exclude_user)
|
|
*br_type |= PERF_SAMPLE_BRANCH_USER;
|
|
|
|
if (!event->attr.exclude_kernel)
|
|
*br_type |= PERF_SAMPLE_BRANCH_KERNEL;
|
|
}
|
|
}
|
|
|
|
if (event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK)
|
|
event->attach_state |= PERF_ATTACH_TASK_DATA;
|
|
|
|
/*
|
|
* Generate PMC IRQs:
|
|
* (keep 'enabled' bit clear for now)
|
|
*/
|
|
event->hw.config = ARCH_PERFMON_EVENTSEL_INT;
|
|
|
|
/*
|
|
* Count user and OS events unless requested not to
|
|
*/
|
|
if (!event->attr.exclude_user)
|
|
event->hw.config |= ARCH_PERFMON_EVENTSEL_USR;
|
|
if (!event->attr.exclude_kernel)
|
|
event->hw.config |= ARCH_PERFMON_EVENTSEL_OS;
|
|
|
|
if (event->attr.type == PERF_TYPE_RAW)
|
|
event->hw.config |= event->attr.config & X86_RAW_EVENT_MASK;
|
|
|
|
if (event->attr.sample_period && x86_pmu.limit_period) {
|
|
if (x86_pmu.limit_period(event, event->attr.sample_period) >
|
|
event->attr.sample_period)
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* sample_regs_user never support XMM registers */
|
|
if (unlikely(event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK))
|
|
return -EINVAL;
|
|
/*
|
|
* Besides the general purpose registers, XMM registers may
|
|
* be collected in PEBS on some platforms, e.g. Icelake
|
|
*/
|
|
if (unlikely(event->attr.sample_regs_intr & PERF_REG_EXTENDED_MASK)) {
|
|
if (!(event->pmu->capabilities & PERF_PMU_CAP_EXTENDED_REGS))
|
|
return -EINVAL;
|
|
|
|
if (!event->attr.precise_ip)
|
|
return -EINVAL;
|
|
}
|
|
|
|
return x86_setup_perfctr(event);
|
|
}
|
|
|
|
/*
|
|
* Setup the hardware configuration for a given attr_type
|
|
*/
|
|
static int __x86_pmu_event_init(struct perf_event *event)
|
|
{
|
|
int err;
|
|
|
|
if (!x86_pmu_initialized())
|
|
return -ENODEV;
|
|
|
|
err = x86_reserve_hardware();
|
|
if (err)
|
|
return err;
|
|
|
|
atomic_inc(&active_events);
|
|
event->destroy = hw_perf_event_destroy;
|
|
|
|
event->hw.idx = -1;
|
|
event->hw.last_cpu = -1;
|
|
event->hw.last_tag = ~0ULL;
|
|
|
|
/* mark unused */
|
|
event->hw.extra_reg.idx = EXTRA_REG_NONE;
|
|
event->hw.branch_reg.idx = EXTRA_REG_NONE;
|
|
|
|
return x86_pmu.hw_config(event);
|
|
}
|
|
|
|
void x86_pmu_disable_all(void)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
int idx;
|
|
|
|
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
|
u64 val;
|
|
|
|
if (!test_bit(idx, cpuc->active_mask))
|
|
continue;
|
|
rdmsrl(x86_pmu_config_addr(idx), val);
|
|
if (!(val & ARCH_PERFMON_EVENTSEL_ENABLE))
|
|
continue;
|
|
val &= ~ARCH_PERFMON_EVENTSEL_ENABLE;
|
|
wrmsrl(x86_pmu_config_addr(idx), val);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* There may be PMI landing after enabled=0. The PMI hitting could be before or
|
|
* after disable_all.
|
|
*
|
|
* If PMI hits before disable_all, the PMU will be disabled in the NMI handler.
|
|
* It will not be re-enabled in the NMI handler again, because enabled=0. After
|
|
* handling the NMI, disable_all will be called, which will not change the
|
|
* state either. If PMI hits after disable_all, the PMU is already disabled
|
|
* before entering NMI handler. The NMI handler will not change the state
|
|
* either.
|
|
*
|
|
* So either situation is harmless.
|
|
*/
|
|
static void x86_pmu_disable(struct pmu *pmu)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
|
|
if (!x86_pmu_initialized())
|
|
return;
|
|
|
|
if (!cpuc->enabled)
|
|
return;
|
|
|
|
cpuc->n_added = 0;
|
|
cpuc->enabled = 0;
|
|
barrier();
|
|
|
|
x86_pmu.disable_all();
|
|
}
|
|
|
|
void x86_pmu_enable_all(int added)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
int idx;
|
|
|
|
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
|
struct hw_perf_event *hwc = &cpuc->events[idx]->hw;
|
|
|
|
if (!test_bit(idx, cpuc->active_mask))
|
|
continue;
|
|
|
|
__x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE);
|
|
}
|
|
}
|
|
|
|
static struct pmu pmu;
|
|
|
|
static inline int is_x86_event(struct perf_event *event)
|
|
{
|
|
return event->pmu == &pmu;
|
|
}
|
|
|
|
struct pmu *x86_get_pmu(void)
|
|
{
|
|
return &pmu;
|
|
}
|
|
/*
|
|
* Event scheduler state:
|
|
*
|
|
* Assign events iterating over all events and counters, beginning
|
|
* with events with least weights first. Keep the current iterator
|
|
* state in struct sched_state.
|
|
*/
|
|
struct sched_state {
|
|
int weight;
|
|
int event; /* event index */
|
|
int counter; /* counter index */
|
|
int unassigned; /* number of events to be assigned left */
|
|
int nr_gp; /* number of GP counters used */
|
|
unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
|
|
};
|
|
|
|
/* Total max is X86_PMC_IDX_MAX, but we are O(n!) limited */
|
|
#define SCHED_STATES_MAX 2
|
|
|
|
struct perf_sched {
|
|
int max_weight;
|
|
int max_events;
|
|
int max_gp;
|
|
int saved_states;
|
|
struct event_constraint **constraints;
|
|
struct sched_state state;
|
|
struct sched_state saved[SCHED_STATES_MAX];
|
|
};
|
|
|
|
/*
|
|
* Initialize interator that runs through all events and counters.
|
|
*/
|
|
static void perf_sched_init(struct perf_sched *sched, struct event_constraint **constraints,
|
|
int num, int wmin, int wmax, int gpmax)
|
|
{
|
|
int idx;
|
|
|
|
memset(sched, 0, sizeof(*sched));
|
|
sched->max_events = num;
|
|
sched->max_weight = wmax;
|
|
sched->max_gp = gpmax;
|
|
sched->constraints = constraints;
|
|
|
|
for (idx = 0; idx < num; idx++) {
|
|
if (constraints[idx]->weight == wmin)
|
|
break;
|
|
}
|
|
|
|
sched->state.event = idx; /* start with min weight */
|
|
sched->state.weight = wmin;
|
|
sched->state.unassigned = num;
|
|
}
|
|
|
|
static void perf_sched_save_state(struct perf_sched *sched)
|
|
{
|
|
if (WARN_ON_ONCE(sched->saved_states >= SCHED_STATES_MAX))
|
|
return;
|
|
|
|
sched->saved[sched->saved_states] = sched->state;
|
|
sched->saved_states++;
|
|
}
|
|
|
|
static bool perf_sched_restore_state(struct perf_sched *sched)
|
|
{
|
|
if (!sched->saved_states)
|
|
return false;
|
|
|
|
sched->saved_states--;
|
|
sched->state = sched->saved[sched->saved_states];
|
|
|
|
/* continue with next counter: */
|
|
clear_bit(sched->state.counter++, sched->state.used);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Select a counter for the current event to schedule. Return true on
|
|
* success.
|
|
*/
|
|
static bool __perf_sched_find_counter(struct perf_sched *sched)
|
|
{
|
|
struct event_constraint *c;
|
|
int idx;
|
|
|
|
if (!sched->state.unassigned)
|
|
return false;
|
|
|
|
if (sched->state.event >= sched->max_events)
|
|
return false;
|
|
|
|
c = sched->constraints[sched->state.event];
|
|
/* Prefer fixed purpose counters */
|
|
if (c->idxmsk64 & (~0ULL << INTEL_PMC_IDX_FIXED)) {
|
|
idx = INTEL_PMC_IDX_FIXED;
|
|
for_each_set_bit_from(idx, c->idxmsk, X86_PMC_IDX_MAX) {
|
|
if (!__test_and_set_bit(idx, sched->state.used))
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Grab the first unused counter starting with idx */
|
|
idx = sched->state.counter;
|
|
for_each_set_bit_from(idx, c->idxmsk, INTEL_PMC_IDX_FIXED) {
|
|
if (!__test_and_set_bit(idx, sched->state.used)) {
|
|
if (sched->state.nr_gp++ >= sched->max_gp)
|
|
return false;
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
done:
|
|
sched->state.counter = idx;
|
|
|
|
if (c->overlap)
|
|
perf_sched_save_state(sched);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool perf_sched_find_counter(struct perf_sched *sched)
|
|
{
|
|
while (!__perf_sched_find_counter(sched)) {
|
|
if (!perf_sched_restore_state(sched))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Go through all unassigned events and find the next one to schedule.
|
|
* Take events with the least weight first. Return true on success.
|
|
*/
|
|
static bool perf_sched_next_event(struct perf_sched *sched)
|
|
{
|
|
struct event_constraint *c;
|
|
|
|
if (!sched->state.unassigned || !--sched->state.unassigned)
|
|
return false;
|
|
|
|
do {
|
|
/* next event */
|
|
sched->state.event++;
|
|
if (sched->state.event >= sched->max_events) {
|
|
/* next weight */
|
|
sched->state.event = 0;
|
|
sched->state.weight++;
|
|
if (sched->state.weight > sched->max_weight)
|
|
return false;
|
|
}
|
|
c = sched->constraints[sched->state.event];
|
|
} while (c->weight != sched->state.weight);
|
|
|
|
sched->state.counter = 0; /* start with first counter */
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Assign a counter for each event.
|
|
*/
|
|
int perf_assign_events(struct event_constraint **constraints, int n,
|
|
int wmin, int wmax, int gpmax, int *assign)
|
|
{
|
|
struct perf_sched sched;
|
|
|
|
perf_sched_init(&sched, constraints, n, wmin, wmax, gpmax);
|
|
|
|
do {
|
|
if (!perf_sched_find_counter(&sched))
|
|
break; /* failed */
|
|
if (assign)
|
|
assign[sched.state.event] = sched.state.counter;
|
|
} while (perf_sched_next_event(&sched));
|
|
|
|
return sched.state.unassigned;
|
|
}
|
|
EXPORT_SYMBOL_GPL(perf_assign_events);
|
|
|
|
int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
|
|
{
|
|
struct event_constraint *c;
|
|
unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
|
|
struct perf_event *e;
|
|
int n0, i, wmin, wmax, unsched = 0;
|
|
struct hw_perf_event *hwc;
|
|
|
|
bitmap_zero(used_mask, X86_PMC_IDX_MAX);
|
|
|
|
/*
|
|
* Compute the number of events already present; see x86_pmu_add(),
|
|
* validate_group() and x86_pmu_commit_txn(). For the former two
|
|
* cpuc->n_events hasn't been updated yet, while for the latter
|
|
* cpuc->n_txn contains the number of events added in the current
|
|
* transaction.
|
|
*/
|
|
n0 = cpuc->n_events;
|
|
if (cpuc->txn_flags & PERF_PMU_TXN_ADD)
|
|
n0 -= cpuc->n_txn;
|
|
|
|
if (x86_pmu.start_scheduling)
|
|
x86_pmu.start_scheduling(cpuc);
|
|
|
|
for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) {
|
|
c = cpuc->event_constraint[i];
|
|
|
|
/*
|
|
* Previously scheduled events should have a cached constraint,
|
|
* while new events should not have one.
|
|
*/
|
|
WARN_ON_ONCE((c && i >= n0) || (!c && i < n0));
|
|
|
|
/*
|
|
* Request constraints for new events; or for those events that
|
|
* have a dynamic constraint -- for those the constraint can
|
|
* change due to external factors (sibling state, allow_tfa).
|
|
*/
|
|
if (!c || (c->flags & PERF_X86_EVENT_DYNAMIC)) {
|
|
c = x86_pmu.get_event_constraints(cpuc, i, cpuc->event_list[i]);
|
|
cpuc->event_constraint[i] = c;
|
|
}
|
|
|
|
wmin = min(wmin, c->weight);
|
|
wmax = max(wmax, c->weight);
|
|
}
|
|
|
|
/*
|
|
* fastpath, try to reuse previous register
|
|
*/
|
|
for (i = 0; i < n; i++) {
|
|
hwc = &cpuc->event_list[i]->hw;
|
|
c = cpuc->event_constraint[i];
|
|
|
|
/* never assigned */
|
|
if (hwc->idx == -1)
|
|
break;
|
|
|
|
/* constraint still honored */
|
|
if (!test_bit(hwc->idx, c->idxmsk))
|
|
break;
|
|
|
|
/* not already used */
|
|
if (test_bit(hwc->idx, used_mask))
|
|
break;
|
|
|
|
__set_bit(hwc->idx, used_mask);
|
|
if (assign)
|
|
assign[i] = hwc->idx;
|
|
}
|
|
|
|
/* slow path */
|
|
if (i != n) {
|
|
int gpmax = x86_pmu.num_counters;
|
|
|
|
/*
|
|
* Do not allow scheduling of more than half the available
|
|
* generic counters.
|
|
*
|
|
* This helps avoid counter starvation of sibling thread by
|
|
* ensuring at most half the counters cannot be in exclusive
|
|
* mode. There is no designated counters for the limits. Any
|
|
* N/2 counters can be used. This helps with events with
|
|
* specific counter constraints.
|
|
*/
|
|
if (is_ht_workaround_enabled() && !cpuc->is_fake &&
|
|
READ_ONCE(cpuc->excl_cntrs->exclusive_present))
|
|
gpmax /= 2;
|
|
|
|
unsched = perf_assign_events(cpuc->event_constraint, n, wmin,
|
|
wmax, gpmax, assign);
|
|
}
|
|
|
|
/*
|
|
* In case of success (unsched = 0), mark events as committed,
|
|
* so we do not put_constraint() in case new events are added
|
|
* and fail to be scheduled
|
|
*
|
|
* We invoke the lower level commit callback to lock the resource
|
|
*
|
|
* We do not need to do all of this in case we are called to
|
|
* validate an event group (assign == NULL)
|
|
*/
|
|
if (!unsched && assign) {
|
|
for (i = 0; i < n; i++) {
|
|
e = cpuc->event_list[i];
|
|
if (x86_pmu.commit_scheduling)
|
|
x86_pmu.commit_scheduling(cpuc, i, assign[i]);
|
|
}
|
|
} else {
|
|
for (i = n0; i < n; i++) {
|
|
e = cpuc->event_list[i];
|
|
|
|
/*
|
|
* release events that failed scheduling
|
|
*/
|
|
if (x86_pmu.put_event_constraints)
|
|
x86_pmu.put_event_constraints(cpuc, e);
|
|
|
|
cpuc->event_constraint[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if (x86_pmu.stop_scheduling)
|
|
x86_pmu.stop_scheduling(cpuc);
|
|
|
|
return unsched ? -EINVAL : 0;
|
|
}
|
|
|
|
/*
|
|
* dogrp: true if must collect siblings events (group)
|
|
* returns total number of events and error code
|
|
*/
|
|
static int collect_events(struct cpu_hw_events *cpuc, struct perf_event *leader, bool dogrp)
|
|
{
|
|
struct perf_event *event;
|
|
int n, max_count;
|
|
|
|
max_count = x86_pmu.num_counters + x86_pmu.num_counters_fixed;
|
|
|
|
/* current number of events already accepted */
|
|
n = cpuc->n_events;
|
|
if (!cpuc->n_events)
|
|
cpuc->pebs_output = 0;
|
|
|
|
if (!cpuc->is_fake && leader->attr.precise_ip) {
|
|
/*
|
|
* For PEBS->PT, if !aux_event, the group leader (PT) went
|
|
* away, the group was broken down and this singleton event
|
|
* can't schedule any more.
|
|
*/
|
|
if (is_pebs_pt(leader) && !leader->aux_event)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* pebs_output: 0: no PEBS so far, 1: PT, 2: DS
|
|
*/
|
|
if (cpuc->pebs_output &&
|
|
cpuc->pebs_output != is_pebs_pt(leader) + 1)
|
|
return -EINVAL;
|
|
|
|
cpuc->pebs_output = is_pebs_pt(leader) + 1;
|
|
}
|
|
|
|
if (is_x86_event(leader)) {
|
|
if (n >= max_count)
|
|
return -EINVAL;
|
|
cpuc->event_list[n] = leader;
|
|
n++;
|
|
}
|
|
if (!dogrp)
|
|
return n;
|
|
|
|
for_each_sibling_event(event, leader) {
|
|
if (!is_x86_event(event) ||
|
|
event->state <= PERF_EVENT_STATE_OFF)
|
|
continue;
|
|
|
|
if (n >= max_count)
|
|
return -EINVAL;
|
|
|
|
cpuc->event_list[n] = event;
|
|
n++;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static inline void x86_assign_hw_event(struct perf_event *event,
|
|
struct cpu_hw_events *cpuc, int i)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
|
|
hwc->idx = cpuc->assign[i];
|
|
hwc->last_cpu = smp_processor_id();
|
|
hwc->last_tag = ++cpuc->tags[i];
|
|
|
|
if (hwc->idx == INTEL_PMC_IDX_FIXED_BTS) {
|
|
hwc->config_base = 0;
|
|
hwc->event_base = 0;
|
|
} else if (hwc->idx >= INTEL_PMC_IDX_FIXED) {
|
|
hwc->config_base = MSR_ARCH_PERFMON_FIXED_CTR_CTRL;
|
|
hwc->event_base = MSR_ARCH_PERFMON_FIXED_CTR0 + (hwc->idx - INTEL_PMC_IDX_FIXED);
|
|
hwc->event_base_rdpmc = (hwc->idx - INTEL_PMC_IDX_FIXED) | 1<<30;
|
|
} else {
|
|
hwc->config_base = x86_pmu_config_addr(hwc->idx);
|
|
hwc->event_base = x86_pmu_event_addr(hwc->idx);
|
|
hwc->event_base_rdpmc = x86_pmu_rdpmc_index(hwc->idx);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* x86_perf_rdpmc_index - Return PMC counter used for event
|
|
* @event: the perf_event to which the PMC counter was assigned
|
|
*
|
|
* The counter assigned to this performance event may change if interrupts
|
|
* are enabled. This counter should thus never be used while interrupts are
|
|
* enabled. Before this function is used to obtain the assigned counter the
|
|
* event should be checked for validity using, for example,
|
|
* perf_event_read_local(), within the same interrupt disabled section in
|
|
* which this counter is planned to be used.
|
|
*
|
|
* Return: The index of the performance monitoring counter assigned to
|
|
* @perf_event.
|
|
*/
|
|
int x86_perf_rdpmc_index(struct perf_event *event)
|
|
{
|
|
lockdep_assert_irqs_disabled();
|
|
|
|
return event->hw.event_base_rdpmc;
|
|
}
|
|
|
|
static inline int match_prev_assignment(struct hw_perf_event *hwc,
|
|
struct cpu_hw_events *cpuc,
|
|
int i)
|
|
{
|
|
return hwc->idx == cpuc->assign[i] &&
|
|
hwc->last_cpu == smp_processor_id() &&
|
|
hwc->last_tag == cpuc->tags[i];
|
|
}
|
|
|
|
static void x86_pmu_start(struct perf_event *event, int flags);
|
|
|
|
static void x86_pmu_enable(struct pmu *pmu)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
struct perf_event *event;
|
|
struct hw_perf_event *hwc;
|
|
int i, added = cpuc->n_added;
|
|
|
|
if (!x86_pmu_initialized())
|
|
return;
|
|
|
|
if (cpuc->enabled)
|
|
return;
|
|
|
|
if (cpuc->n_added) {
|
|
int n_running = cpuc->n_events - cpuc->n_added;
|
|
/*
|
|
* apply assignment obtained either from
|
|
* hw_perf_group_sched_in() or x86_pmu_enable()
|
|
*
|
|
* step1: save events moving to new counters
|
|
*/
|
|
for (i = 0; i < n_running; i++) {
|
|
event = cpuc->event_list[i];
|
|
hwc = &event->hw;
|
|
|
|
/*
|
|
* we can avoid reprogramming counter if:
|
|
* - assigned same counter as last time
|
|
* - running on same CPU as last time
|
|
* - no other event has used the counter since
|
|
*/
|
|
if (hwc->idx == -1 ||
|
|
match_prev_assignment(hwc, cpuc, i))
|
|
continue;
|
|
|
|
/*
|
|
* Ensure we don't accidentally enable a stopped
|
|
* counter simply because we rescheduled.
|
|
*/
|
|
if (hwc->state & PERF_HES_STOPPED)
|
|
hwc->state |= PERF_HES_ARCH;
|
|
|
|
x86_pmu_stop(event, PERF_EF_UPDATE);
|
|
}
|
|
|
|
/*
|
|
* step2: reprogram moved events into new counters
|
|
*/
|
|
for (i = 0; i < cpuc->n_events; i++) {
|
|
event = cpuc->event_list[i];
|
|
hwc = &event->hw;
|
|
|
|
if (!match_prev_assignment(hwc, cpuc, i))
|
|
x86_assign_hw_event(event, cpuc, i);
|
|
else if (i < n_running)
|
|
continue;
|
|
|
|
if (hwc->state & PERF_HES_ARCH)
|
|
continue;
|
|
|
|
x86_pmu_start(event, PERF_EF_RELOAD);
|
|
}
|
|
cpuc->n_added = 0;
|
|
perf_events_lapic_init();
|
|
}
|
|
|
|
cpuc->enabled = 1;
|
|
barrier();
|
|
|
|
x86_pmu.enable_all(added);
|
|
}
|
|
|
|
static DEFINE_PER_CPU(u64 [X86_PMC_IDX_MAX], pmc_prev_left);
|
|
|
|
/*
|
|
* Set the next IRQ period, based on the hwc->period_left value.
|
|
* To be called with the event disabled in hw:
|
|
*/
|
|
int x86_perf_event_set_period(struct perf_event *event)
|
|
{
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
s64 left = local64_read(&hwc->period_left);
|
|
s64 period = hwc->sample_period;
|
|
int ret = 0, idx = hwc->idx;
|
|
|
|
if (idx == INTEL_PMC_IDX_FIXED_BTS)
|
|
return 0;
|
|
|
|
/*
|
|
* If we are way outside a reasonable range then just skip forward:
|
|
*/
|
|
if (unlikely(left <= -period)) {
|
|
left = period;
|
|
local64_set(&hwc->period_left, left);
|
|
hwc->last_period = period;
|
|
ret = 1;
|
|
}
|
|
|
|
if (unlikely(left <= 0)) {
|
|
left += period;
|
|
local64_set(&hwc->period_left, left);
|
|
hwc->last_period = period;
|
|
ret = 1;
|
|
}
|
|
/*
|
|
* Quirk: certain CPUs dont like it if just 1 hw_event is left:
|
|
*/
|
|
if (unlikely(left < 2))
|
|
left = 2;
|
|
|
|
if (left > x86_pmu.max_period)
|
|
left = x86_pmu.max_period;
|
|
|
|
if (x86_pmu.limit_period)
|
|
left = x86_pmu.limit_period(event, left);
|
|
|
|
per_cpu(pmc_prev_left[idx], smp_processor_id()) = left;
|
|
|
|
/*
|
|
* The hw event starts counting from this event offset,
|
|
* mark it to be able to extra future deltas:
|
|
*/
|
|
local64_set(&hwc->prev_count, (u64)-left);
|
|
|
|
wrmsrl(hwc->event_base, (u64)(-left) & x86_pmu.cntval_mask);
|
|
|
|
/*
|
|
* Due to erratum on certan cpu we need
|
|
* a second write to be sure the register
|
|
* is updated properly
|
|
*/
|
|
if (x86_pmu.perfctr_second_write) {
|
|
wrmsrl(hwc->event_base,
|
|
(u64)(-left) & x86_pmu.cntval_mask);
|
|
}
|
|
|
|
perf_event_update_userpage(event);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void x86_pmu_enable_event(struct perf_event *event)
|
|
{
|
|
if (__this_cpu_read(cpu_hw_events.enabled))
|
|
__x86_pmu_enable_event(&event->hw,
|
|
ARCH_PERFMON_EVENTSEL_ENABLE);
|
|
}
|
|
|
|
/*
|
|
* Add a single event to the PMU.
|
|
*
|
|
* The event is added to the group of enabled events
|
|
* but only if it can be scheduled with existing events.
|
|
*/
|
|
static int x86_pmu_add(struct perf_event *event, int flags)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
struct hw_perf_event *hwc;
|
|
int assign[X86_PMC_IDX_MAX];
|
|
int n, n0, ret;
|
|
|
|
hwc = &event->hw;
|
|
|
|
n0 = cpuc->n_events;
|
|
ret = n = collect_events(cpuc, event, false);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
|
|
if (!(flags & PERF_EF_START))
|
|
hwc->state |= PERF_HES_ARCH;
|
|
|
|
/*
|
|
* If group events scheduling transaction was started,
|
|
* skip the schedulability test here, it will be performed
|
|
* at commit time (->commit_txn) as a whole.
|
|
*
|
|
* If commit fails, we'll call ->del() on all events
|
|
* for which ->add() was called.
|
|
*/
|
|
if (cpuc->txn_flags & PERF_PMU_TXN_ADD)
|
|
goto done_collect;
|
|
|
|
ret = x86_pmu.schedule_events(cpuc, n, assign);
|
|
if (ret)
|
|
goto out;
|
|
/*
|
|
* copy new assignment, now we know it is possible
|
|
* will be used by hw_perf_enable()
|
|
*/
|
|
memcpy(cpuc->assign, assign, n*sizeof(int));
|
|
|
|
done_collect:
|
|
/*
|
|
* Commit the collect_events() state. See x86_pmu_del() and
|
|
* x86_pmu_*_txn().
|
|
*/
|
|
cpuc->n_events = n;
|
|
cpuc->n_added += n - n0;
|
|
cpuc->n_txn += n - n0;
|
|
|
|
if (x86_pmu.add) {
|
|
/*
|
|
* This is before x86_pmu_enable() will call x86_pmu_start(),
|
|
* so we enable LBRs before an event needs them etc..
|
|
*/
|
|
x86_pmu.add(event);
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void x86_pmu_start(struct perf_event *event, int flags)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
int idx = event->hw.idx;
|
|
|
|
if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
|
|
return;
|
|
|
|
if (WARN_ON_ONCE(idx == -1))
|
|
return;
|
|
|
|
if (flags & PERF_EF_RELOAD) {
|
|
WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE));
|
|
x86_perf_event_set_period(event);
|
|
}
|
|
|
|
event->hw.state = 0;
|
|
|
|
cpuc->events[idx] = event;
|
|
__set_bit(idx, cpuc->active_mask);
|
|
__set_bit(idx, cpuc->running);
|
|
x86_pmu.enable(event);
|
|
perf_event_update_userpage(event);
|
|
}
|
|
|
|
void perf_event_print_debug(void)
|
|
{
|
|
u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left, fixed;
|
|
u64 pebs, debugctl;
|
|
struct cpu_hw_events *cpuc;
|
|
unsigned long flags;
|
|
int cpu, idx;
|
|
|
|
if (!x86_pmu.num_counters)
|
|
return;
|
|
|
|
local_irq_save(flags);
|
|
|
|
cpu = smp_processor_id();
|
|
cpuc = &per_cpu(cpu_hw_events, cpu);
|
|
|
|
if (x86_pmu.version >= 2) {
|
|
rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl);
|
|
rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status);
|
|
rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow);
|
|
rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed);
|
|
|
|
pr_info("\n");
|
|
pr_info("CPU#%d: ctrl: %016llx\n", cpu, ctrl);
|
|
pr_info("CPU#%d: status: %016llx\n", cpu, status);
|
|
pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow);
|
|
pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed);
|
|
if (x86_pmu.pebs_constraints) {
|
|
rdmsrl(MSR_IA32_PEBS_ENABLE, pebs);
|
|
pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs);
|
|
}
|
|
if (x86_pmu.lbr_nr) {
|
|
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
|
pr_info("CPU#%d: debugctl: %016llx\n", cpu, debugctl);
|
|
}
|
|
}
|
|
pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask);
|
|
|
|
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
|
rdmsrl(x86_pmu_config_addr(idx), pmc_ctrl);
|
|
rdmsrl(x86_pmu_event_addr(idx), pmc_count);
|
|
|
|
prev_left = per_cpu(pmc_prev_left[idx], cpu);
|
|
|
|
pr_info("CPU#%d: gen-PMC%d ctrl: %016llx\n",
|
|
cpu, idx, pmc_ctrl);
|
|
pr_info("CPU#%d: gen-PMC%d count: %016llx\n",
|
|
cpu, idx, pmc_count);
|
|
pr_info("CPU#%d: gen-PMC%d left: %016llx\n",
|
|
cpu, idx, prev_left);
|
|
}
|
|
for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) {
|
|
rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, pmc_count);
|
|
|
|
pr_info("CPU#%d: fixed-PMC%d count: %016llx\n",
|
|
cpu, idx, pmc_count);
|
|
}
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
void x86_pmu_stop(struct perf_event *event, int flags)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
struct hw_perf_event *hwc = &event->hw;
|
|
|
|
if (test_bit(hwc->idx, cpuc->active_mask)) {
|
|
x86_pmu.disable(event);
|
|
__clear_bit(hwc->idx, cpuc->active_mask);
|
|
cpuc->events[hwc->idx] = NULL;
|
|
WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
|
|
hwc->state |= PERF_HES_STOPPED;
|
|
}
|
|
|
|
if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
|
|
/*
|
|
* Drain the remaining delta count out of a event
|
|
* that we are disabling:
|
|
*/
|
|
x86_perf_event_update(event);
|
|
hwc->state |= PERF_HES_UPTODATE;
|
|
}
|
|
}
|
|
|
|
static void x86_pmu_del(struct perf_event *event, int flags)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
int i;
|
|
|
|
/*
|
|
* If we're called during a txn, we only need to undo x86_pmu.add.
|
|
* The events never got scheduled and ->cancel_txn will truncate
|
|
* the event_list.
|
|
*
|
|
* XXX assumes any ->del() called during a TXN will only be on
|
|
* an event added during that same TXN.
|
|
*/
|
|
if (cpuc->txn_flags & PERF_PMU_TXN_ADD)
|
|
goto do_del;
|
|
|
|
/*
|
|
* Not a TXN, therefore cleanup properly.
|
|
*/
|
|
x86_pmu_stop(event, PERF_EF_UPDATE);
|
|
|
|
for (i = 0; i < cpuc->n_events; i++) {
|
|
if (event == cpuc->event_list[i])
|
|
break;
|
|
}
|
|
|
|
if (WARN_ON_ONCE(i == cpuc->n_events)) /* called ->del() without ->add() ? */
|
|
return;
|
|
|
|
/* If we have a newly added event; make sure to decrease n_added. */
|
|
if (i >= cpuc->n_events - cpuc->n_added)
|
|
--cpuc->n_added;
|
|
|
|
if (x86_pmu.put_event_constraints)
|
|
x86_pmu.put_event_constraints(cpuc, event);
|
|
|
|
/* Delete the array entry. */
|
|
while (++i < cpuc->n_events) {
|
|
cpuc->event_list[i-1] = cpuc->event_list[i];
|
|
cpuc->event_constraint[i-1] = cpuc->event_constraint[i];
|
|
}
|
|
cpuc->event_constraint[i-1] = NULL;
|
|
--cpuc->n_events;
|
|
|
|
perf_event_update_userpage(event);
|
|
|
|
do_del:
|
|
if (x86_pmu.del) {
|
|
/*
|
|
* This is after x86_pmu_stop(); so we disable LBRs after any
|
|
* event can need them etc..
|
|
*/
|
|
x86_pmu.del(event);
|
|
}
|
|
}
|
|
|
|
int x86_pmu_handle_irq(struct pt_regs *regs)
|
|
{
|
|
struct perf_sample_data data;
|
|
struct cpu_hw_events *cpuc;
|
|
struct perf_event *event;
|
|
int idx, handled = 0;
|
|
u64 val;
|
|
|
|
cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
|
|
/*
|
|
* Some chipsets need to unmask the LVTPC in a particular spot
|
|
* inside the nmi handler. As a result, the unmasking was pushed
|
|
* into all the nmi handlers.
|
|
*
|
|
* This generic handler doesn't seem to have any issues where the
|
|
* unmasking occurs so it was left at the top.
|
|
*/
|
|
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
|
|
|
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
|
if (!test_bit(idx, cpuc->active_mask))
|
|
continue;
|
|
|
|
event = cpuc->events[idx];
|
|
|
|
val = x86_perf_event_update(event);
|
|
if (val & (1ULL << (x86_pmu.cntval_bits - 1)))
|
|
continue;
|
|
|
|
/*
|
|
* event overflow
|
|
*/
|
|
handled++;
|
|
perf_sample_data_init(&data, 0, event->hw.last_period);
|
|
|
|
if (!x86_perf_event_set_period(event))
|
|
continue;
|
|
|
|
if (perf_event_overflow(event, &data, regs))
|
|
x86_pmu_stop(event, 0);
|
|
}
|
|
|
|
if (handled)
|
|
inc_irq_stat(apic_perf_irqs);
|
|
|
|
return handled;
|
|
}
|
|
|
|
void perf_events_lapic_init(void)
|
|
{
|
|
if (!x86_pmu.apic || !x86_pmu_initialized())
|
|
return;
|
|
|
|
/*
|
|
* Always use NMI for PMU
|
|
*/
|
|
apic_write(APIC_LVTPC, APIC_DM_NMI);
|
|
}
|
|
|
|
static int
|
|
perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
|
|
{
|
|
u64 start_clock;
|
|
u64 finish_clock;
|
|
int ret;
|
|
|
|
/*
|
|
* All PMUs/events that share this PMI handler should make sure to
|
|
* increment active_events for their events.
|
|
*/
|
|
if (!atomic_read(&active_events))
|
|
return NMI_DONE;
|
|
|
|
start_clock = sched_clock();
|
|
ret = x86_pmu.handle_irq(regs);
|
|
finish_clock = sched_clock();
|
|
|
|
perf_sample_event_took(finish_clock - start_clock);
|
|
|
|
return ret;
|
|
}
|
|
NOKPROBE_SYMBOL(perf_event_nmi_handler);
|
|
|
|
struct event_constraint emptyconstraint;
|
|
struct event_constraint unconstrained;
|
|
|
|
static int x86_pmu_prepare_cpu(unsigned int cpu)
|
|
{
|
|
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
|
|
int i;
|
|
|
|
for (i = 0 ; i < X86_PERF_KFREE_MAX; i++)
|
|
cpuc->kfree_on_online[i] = NULL;
|
|
if (x86_pmu.cpu_prepare)
|
|
return x86_pmu.cpu_prepare(cpu);
|
|
return 0;
|
|
}
|
|
|
|
static int x86_pmu_dead_cpu(unsigned int cpu)
|
|
{
|
|
if (x86_pmu.cpu_dead)
|
|
x86_pmu.cpu_dead(cpu);
|
|
return 0;
|
|
}
|
|
|
|
static int x86_pmu_online_cpu(unsigned int cpu)
|
|
{
|
|
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
|
|
int i;
|
|
|
|
for (i = 0 ; i < X86_PERF_KFREE_MAX; i++) {
|
|
kfree(cpuc->kfree_on_online[i]);
|
|
cpuc->kfree_on_online[i] = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int x86_pmu_starting_cpu(unsigned int cpu)
|
|
{
|
|
if (x86_pmu.cpu_starting)
|
|
x86_pmu.cpu_starting(cpu);
|
|
return 0;
|
|
}
|
|
|
|
static int x86_pmu_dying_cpu(unsigned int cpu)
|
|
{
|
|
if (x86_pmu.cpu_dying)
|
|
x86_pmu.cpu_dying(cpu);
|
|
return 0;
|
|
}
|
|
|
|
static void __init pmu_check_apic(void)
|
|
{
|
|
if (boot_cpu_has(X86_FEATURE_APIC))
|
|
return;
|
|
|
|
x86_pmu.apic = 0;
|
|
pr_info("no APIC, boot with the \"lapic\" boot parameter to force-enable it.\n");
|
|
pr_info("no hardware sampling interrupt available.\n");
|
|
|
|
/*
|
|
* If we have a PMU initialized but no APIC
|
|
* interrupts, we cannot sample hardware
|
|
* events (user-space has to fall back and
|
|
* sample via a hrtimer based software event):
|
|
*/
|
|
pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
|
|
|
|
}
|
|
|
|
static struct attribute_group x86_pmu_format_group __ro_after_init = {
|
|
.name = "format",
|
|
.attrs = NULL,
|
|
};
|
|
|
|
ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr, char *page)
|
|
{
|
|
struct perf_pmu_events_attr *pmu_attr = \
|
|
container_of(attr, struct perf_pmu_events_attr, attr);
|
|
u64 config = x86_pmu.event_map(pmu_attr->id);
|
|
|
|
/* string trumps id */
|
|
if (pmu_attr->event_str)
|
|
return sprintf(page, "%s", pmu_attr->event_str);
|
|
|
|
return x86_pmu.events_sysfs_show(page, config);
|
|
}
|
|
EXPORT_SYMBOL_GPL(events_sysfs_show);
|
|
|
|
ssize_t events_ht_sysfs_show(struct device *dev, struct device_attribute *attr,
|
|
char *page)
|
|
{
|
|
struct perf_pmu_events_ht_attr *pmu_attr =
|
|
container_of(attr, struct perf_pmu_events_ht_attr, attr);
|
|
|
|
/*
|
|
* Report conditional events depending on Hyper-Threading.
|
|
*
|
|
* This is overly conservative as usually the HT special
|
|
* handling is not needed if the other CPU thread is idle.
|
|
*
|
|
* Note this does not (and cannot) handle the case when thread
|
|
* siblings are invisible, for example with virtualization
|
|
* if they are owned by some other guest. The user tool
|
|
* has to re-read when a thread sibling gets onlined later.
|
|
*/
|
|
return sprintf(page, "%s",
|
|
topology_max_smt_threads() > 1 ?
|
|
pmu_attr->event_str_ht :
|
|
pmu_attr->event_str_noht);
|
|
}
|
|
|
|
EVENT_ATTR(cpu-cycles, CPU_CYCLES );
|
|
EVENT_ATTR(instructions, INSTRUCTIONS );
|
|
EVENT_ATTR(cache-references, CACHE_REFERENCES );
|
|
EVENT_ATTR(cache-misses, CACHE_MISSES );
|
|
EVENT_ATTR(branch-instructions, BRANCH_INSTRUCTIONS );
|
|
EVENT_ATTR(branch-misses, BRANCH_MISSES );
|
|
EVENT_ATTR(bus-cycles, BUS_CYCLES );
|
|
EVENT_ATTR(stalled-cycles-frontend, STALLED_CYCLES_FRONTEND );
|
|
EVENT_ATTR(stalled-cycles-backend, STALLED_CYCLES_BACKEND );
|
|
EVENT_ATTR(ref-cycles, REF_CPU_CYCLES );
|
|
|
|
static struct attribute *empty_attrs;
|
|
|
|
static struct attribute *events_attr[] = {
|
|
EVENT_PTR(CPU_CYCLES),
|
|
EVENT_PTR(INSTRUCTIONS),
|
|
EVENT_PTR(CACHE_REFERENCES),
|
|
EVENT_PTR(CACHE_MISSES),
|
|
EVENT_PTR(BRANCH_INSTRUCTIONS),
|
|
EVENT_PTR(BRANCH_MISSES),
|
|
EVENT_PTR(BUS_CYCLES),
|
|
EVENT_PTR(STALLED_CYCLES_FRONTEND),
|
|
EVENT_PTR(STALLED_CYCLES_BACKEND),
|
|
EVENT_PTR(REF_CPU_CYCLES),
|
|
NULL,
|
|
};
|
|
|
|
/*
|
|
* Remove all undefined events (x86_pmu.event_map(id) == 0)
|
|
* out of events_attr attributes.
|
|
*/
|
|
static umode_t
|
|
is_visible(struct kobject *kobj, struct attribute *attr, int idx)
|
|
{
|
|
struct perf_pmu_events_attr *pmu_attr;
|
|
|
|
pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr.attr);
|
|
/* str trumps id */
|
|
return pmu_attr->event_str || x86_pmu.event_map(idx) ? attr->mode : 0;
|
|
}
|
|
|
|
static struct attribute_group x86_pmu_events_group __ro_after_init = {
|
|
.name = "events",
|
|
.attrs = events_attr,
|
|
.is_visible = is_visible,
|
|
};
|
|
|
|
ssize_t x86_event_sysfs_show(char *page, u64 config, u64 event)
|
|
{
|
|
u64 umask = (config & ARCH_PERFMON_EVENTSEL_UMASK) >> 8;
|
|
u64 cmask = (config & ARCH_PERFMON_EVENTSEL_CMASK) >> 24;
|
|
bool edge = (config & ARCH_PERFMON_EVENTSEL_EDGE);
|
|
bool pc = (config & ARCH_PERFMON_EVENTSEL_PIN_CONTROL);
|
|
bool any = (config & ARCH_PERFMON_EVENTSEL_ANY);
|
|
bool inv = (config & ARCH_PERFMON_EVENTSEL_INV);
|
|
ssize_t ret;
|
|
|
|
/*
|
|
* We have whole page size to spend and just little data
|
|
* to write, so we can safely use sprintf.
|
|
*/
|
|
ret = sprintf(page, "event=0x%02llx", event);
|
|
|
|
if (umask)
|
|
ret += sprintf(page + ret, ",umask=0x%02llx", umask);
|
|
|
|
if (edge)
|
|
ret += sprintf(page + ret, ",edge");
|
|
|
|
if (pc)
|
|
ret += sprintf(page + ret, ",pc");
|
|
|
|
if (any)
|
|
ret += sprintf(page + ret, ",any");
|
|
|
|
if (inv)
|
|
ret += sprintf(page + ret, ",inv");
|
|
|
|
if (cmask)
|
|
ret += sprintf(page + ret, ",cmask=0x%02llx", cmask);
|
|
|
|
ret += sprintf(page + ret, "\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct attribute_group x86_pmu_attr_group;
|
|
static struct attribute_group x86_pmu_caps_group;
|
|
|
|
static int __init init_hw_perf_events(void)
|
|
{
|
|
struct x86_pmu_quirk *quirk;
|
|
int err;
|
|
|
|
pr_info("Performance Events: ");
|
|
|
|
switch (boot_cpu_data.x86_vendor) {
|
|
case X86_VENDOR_INTEL:
|
|
err = intel_pmu_init();
|
|
break;
|
|
case X86_VENDOR_AMD:
|
|
err = amd_pmu_init();
|
|
break;
|
|
case X86_VENDOR_HYGON:
|
|
err = amd_pmu_init();
|
|
x86_pmu.name = "HYGON";
|
|
break;
|
|
default:
|
|
err = -ENOTSUPP;
|
|
}
|
|
if (err != 0) {
|
|
pr_cont("no PMU driver, software events only.\n");
|
|
return 0;
|
|
}
|
|
|
|
pmu_check_apic();
|
|
|
|
/* sanity check that the hardware exists or is emulated */
|
|
if (!check_hw_exists())
|
|
return 0;
|
|
|
|
pr_cont("%s PMU driver.\n", x86_pmu.name);
|
|
|
|
x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */
|
|
|
|
for (quirk = x86_pmu.quirks; quirk; quirk = quirk->next)
|
|
quirk->func();
|
|
|
|
if (!x86_pmu.intel_ctrl)
|
|
x86_pmu.intel_ctrl = (1 << x86_pmu.num_counters) - 1;
|
|
|
|
perf_events_lapic_init();
|
|
register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI");
|
|
|
|
unconstrained = (struct event_constraint)
|
|
__EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1,
|
|
0, x86_pmu.num_counters, 0, 0);
|
|
|
|
x86_pmu_format_group.attrs = x86_pmu.format_attrs;
|
|
|
|
if (!x86_pmu.events_sysfs_show)
|
|
x86_pmu_events_group.attrs = &empty_attrs;
|
|
|
|
pmu.attr_update = x86_pmu.attr_update;
|
|
|
|
pr_info("... version: %d\n", x86_pmu.version);
|
|
pr_info("... bit width: %d\n", x86_pmu.cntval_bits);
|
|
pr_info("... generic registers: %d\n", x86_pmu.num_counters);
|
|
pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask);
|
|
pr_info("... max period: %016Lx\n", x86_pmu.max_period);
|
|
pr_info("... fixed-purpose events: %d\n", x86_pmu.num_counters_fixed);
|
|
pr_info("... event mask: %016Lx\n", x86_pmu.intel_ctrl);
|
|
|
|
/*
|
|
* Install callbacks. Core will call them for each online
|
|
* cpu.
|
|
*/
|
|
err = cpuhp_setup_state(CPUHP_PERF_X86_PREPARE, "perf/x86:prepare",
|
|
x86_pmu_prepare_cpu, x86_pmu_dead_cpu);
|
|
if (err)
|
|
return err;
|
|
|
|
err = cpuhp_setup_state(CPUHP_AP_PERF_X86_STARTING,
|
|
"perf/x86:starting", x86_pmu_starting_cpu,
|
|
x86_pmu_dying_cpu);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = cpuhp_setup_state(CPUHP_AP_PERF_X86_ONLINE, "perf/x86:online",
|
|
x86_pmu_online_cpu, NULL);
|
|
if (err)
|
|
goto out1;
|
|
|
|
err = perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
|
|
if (err)
|
|
goto out2;
|
|
|
|
return 0;
|
|
|
|
out2:
|
|
cpuhp_remove_state(CPUHP_AP_PERF_X86_ONLINE);
|
|
out1:
|
|
cpuhp_remove_state(CPUHP_AP_PERF_X86_STARTING);
|
|
out:
|
|
cpuhp_remove_state(CPUHP_PERF_X86_PREPARE);
|
|
return err;
|
|
}
|
|
early_initcall(init_hw_perf_events);
|
|
|
|
static inline void x86_pmu_read(struct perf_event *event)
|
|
{
|
|
if (x86_pmu.read)
|
|
return x86_pmu.read(event);
|
|
x86_perf_event_update(event);
|
|
}
|
|
|
|
/*
|
|
* Start group events scheduling transaction
|
|
* Set the flag to make pmu::enable() not perform the
|
|
* schedulability test, it will be performed at commit time
|
|
*
|
|
* We only support PERF_PMU_TXN_ADD transactions. Save the
|
|
* transaction flags but otherwise ignore non-PERF_PMU_TXN_ADD
|
|
* transactions.
|
|
*/
|
|
static void x86_pmu_start_txn(struct pmu *pmu, unsigned int txn_flags)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
|
|
WARN_ON_ONCE(cpuc->txn_flags); /* txn already in flight */
|
|
|
|
cpuc->txn_flags = txn_flags;
|
|
if (txn_flags & ~PERF_PMU_TXN_ADD)
|
|
return;
|
|
|
|
perf_pmu_disable(pmu);
|
|
__this_cpu_write(cpu_hw_events.n_txn, 0);
|
|
}
|
|
|
|
/*
|
|
* Stop group events scheduling transaction
|
|
* Clear the flag and pmu::enable() will perform the
|
|
* schedulability test.
|
|
*/
|
|
static void x86_pmu_cancel_txn(struct pmu *pmu)
|
|
{
|
|
unsigned int txn_flags;
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
|
|
WARN_ON_ONCE(!cpuc->txn_flags); /* no txn in flight */
|
|
|
|
txn_flags = cpuc->txn_flags;
|
|
cpuc->txn_flags = 0;
|
|
if (txn_flags & ~PERF_PMU_TXN_ADD)
|
|
return;
|
|
|
|
/*
|
|
* Truncate collected array by the number of events added in this
|
|
* transaction. See x86_pmu_add() and x86_pmu_*_txn().
|
|
*/
|
|
__this_cpu_sub(cpu_hw_events.n_added, __this_cpu_read(cpu_hw_events.n_txn));
|
|
__this_cpu_sub(cpu_hw_events.n_events, __this_cpu_read(cpu_hw_events.n_txn));
|
|
perf_pmu_enable(pmu);
|
|
}
|
|
|
|
/*
|
|
* Commit group events scheduling transaction
|
|
* Perform the group schedulability test as a whole
|
|
* Return 0 if success
|
|
*
|
|
* Does not cancel the transaction on failure; expects the caller to do this.
|
|
*/
|
|
static int x86_pmu_commit_txn(struct pmu *pmu)
|
|
{
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
|
int assign[X86_PMC_IDX_MAX];
|
|
int n, ret;
|
|
|
|
WARN_ON_ONCE(!cpuc->txn_flags); /* no txn in flight */
|
|
|
|
if (cpuc->txn_flags & ~PERF_PMU_TXN_ADD) {
|
|
cpuc->txn_flags = 0;
|
|
return 0;
|
|
}
|
|
|
|
n = cpuc->n_events;
|
|
|
|
if (!x86_pmu_initialized())
|
|
return -EAGAIN;
|
|
|
|
ret = x86_pmu.schedule_events(cpuc, n, assign);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* copy new assignment, now we know it is possible
|
|
* will be used by hw_perf_enable()
|
|
*/
|
|
memcpy(cpuc->assign, assign, n*sizeof(int));
|
|
|
|
cpuc->txn_flags = 0;
|
|
perf_pmu_enable(pmu);
|
|
return 0;
|
|
}
|
|
/*
|
|
* a fake_cpuc is used to validate event groups. Due to
|
|
* the extra reg logic, we need to also allocate a fake
|
|
* per_core and per_cpu structure. Otherwise, group events
|
|
* using extra reg may conflict without the kernel being
|
|
* able to catch this when the last event gets added to
|
|
* the group.
|
|
*/
|
|
static void free_fake_cpuc(struct cpu_hw_events *cpuc)
|
|
{
|
|
intel_cpuc_finish(cpuc);
|
|
kfree(cpuc);
|
|
}
|
|
|
|
static struct cpu_hw_events *allocate_fake_cpuc(void)
|
|
{
|
|
struct cpu_hw_events *cpuc;
|
|
int cpu = raw_smp_processor_id();
|
|
|
|
cpuc = kzalloc(sizeof(*cpuc), GFP_KERNEL);
|
|
if (!cpuc)
|
|
return ERR_PTR(-ENOMEM);
|
|
cpuc->is_fake = 1;
|
|
|
|
if (intel_cpuc_prepare(cpuc, cpu))
|
|
goto error;
|
|
|
|
return cpuc;
|
|
error:
|
|
free_fake_cpuc(cpuc);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
/*
|
|
* validate that we can schedule this event
|
|
*/
|
|
static int validate_event(struct perf_event *event)
|
|
{
|
|
struct cpu_hw_events *fake_cpuc;
|
|
struct event_constraint *c;
|
|
int ret = 0;
|
|
|
|
fake_cpuc = allocate_fake_cpuc();
|
|
if (IS_ERR(fake_cpuc))
|
|
return PTR_ERR(fake_cpuc);
|
|
|
|
c = x86_pmu.get_event_constraints(fake_cpuc, 0, event);
|
|
|
|
if (!c || !c->weight)
|
|
ret = -EINVAL;
|
|
|
|
if (x86_pmu.put_event_constraints)
|
|
x86_pmu.put_event_constraints(fake_cpuc, event);
|
|
|
|
free_fake_cpuc(fake_cpuc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* validate a single event group
|
|
*
|
|
* validation include:
|
|
* - check events are compatible which each other
|
|
* - events do not compete for the same counter
|
|
* - number of events <= number of counters
|
|
*
|
|
* validation ensures the group can be loaded onto the
|
|
* PMU if it was the only group available.
|
|
*/
|
|
static int validate_group(struct perf_event *event)
|
|
{
|
|
struct perf_event *leader = event->group_leader;
|
|
struct cpu_hw_events *fake_cpuc;
|
|
int ret = -EINVAL, n;
|
|
|
|
fake_cpuc = allocate_fake_cpuc();
|
|
if (IS_ERR(fake_cpuc))
|
|
return PTR_ERR(fake_cpuc);
|
|
/*
|
|
* the event is not yet connected with its
|
|
* siblings therefore we must first collect
|
|
* existing siblings, then add the new event
|
|
* before we can simulate the scheduling
|
|
*/
|
|
n = collect_events(fake_cpuc, leader, true);
|
|
if (n < 0)
|
|
goto out;
|
|
|
|
fake_cpuc->n_events = n;
|
|
n = collect_events(fake_cpuc, event, false);
|
|
if (n < 0)
|
|
goto out;
|
|
|
|
fake_cpuc->n_events = 0;
|
|
ret = x86_pmu.schedule_events(fake_cpuc, n, NULL);
|
|
|
|
out:
|
|
free_fake_cpuc(fake_cpuc);
|
|
return ret;
|
|
}
|
|
|
|
static int x86_pmu_event_init(struct perf_event *event)
|
|
{
|
|
struct pmu *tmp;
|
|
int err;
|
|
|
|
switch (event->attr.type) {
|
|
case PERF_TYPE_RAW:
|
|
case PERF_TYPE_HARDWARE:
|
|
case PERF_TYPE_HW_CACHE:
|
|
break;
|
|
|
|
default:
|
|
return -ENOENT;
|
|
}
|
|
|
|
err = __x86_pmu_event_init(event);
|
|
if (!err) {
|
|
/*
|
|
* we temporarily connect event to its pmu
|
|
* such that validate_group() can classify
|
|
* it as an x86 event using is_x86_event()
|
|
*/
|
|
tmp = event->pmu;
|
|
event->pmu = &pmu;
|
|
|
|
if (event->group_leader != event)
|
|
err = validate_group(event);
|
|
else
|
|
err = validate_event(event);
|
|
|
|
event->pmu = tmp;
|
|
}
|
|
if (err) {
|
|
if (event->destroy)
|
|
event->destroy(event);
|
|
}
|
|
|
|
if (READ_ONCE(x86_pmu.attr_rdpmc) &&
|
|
!(event->hw.flags & PERF_X86_EVENT_LARGE_PEBS))
|
|
event->hw.flags |= PERF_X86_EVENT_RDPMC_ALLOWED;
|
|
|
|
return err;
|
|
}
|
|
|
|
static void refresh_pce(void *ignored)
|
|
{
|
|
load_mm_cr4_irqsoff(this_cpu_read(cpu_tlbstate.loaded_mm));
|
|
}
|
|
|
|
static void x86_pmu_event_mapped(struct perf_event *event, struct mm_struct *mm)
|
|
{
|
|
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
|
|
return;
|
|
|
|
/*
|
|
* This function relies on not being called concurrently in two
|
|
* tasks in the same mm. Otherwise one task could observe
|
|
* perf_rdpmc_allowed > 1 and return all the way back to
|
|
* userspace with CR4.PCE clear while another task is still
|
|
* doing on_each_cpu_mask() to propagate CR4.PCE.
|
|
*
|
|
* For now, this can't happen because all callers hold mmap_sem
|
|
* for write. If this changes, we'll need a different solution.
|
|
*/
|
|
lockdep_assert_held_write(&mm->mmap_sem);
|
|
|
|
if (atomic_inc_return(&mm->context.perf_rdpmc_allowed) == 1)
|
|
on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
|
|
}
|
|
|
|
static void x86_pmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)
|
|
{
|
|
|
|
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
|
|
return;
|
|
|
|
if (atomic_dec_and_test(&mm->context.perf_rdpmc_allowed))
|
|
on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
|
|
}
|
|
|
|
static int x86_pmu_event_idx(struct perf_event *event)
|
|
{
|
|
int idx = event->hw.idx;
|
|
|
|
if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
|
|
return 0;
|
|
|
|
if (x86_pmu.num_counters_fixed && idx >= INTEL_PMC_IDX_FIXED) {
|
|
idx -= INTEL_PMC_IDX_FIXED;
|
|
idx |= 1 << 30;
|
|
}
|
|
|
|
return idx + 1;
|
|
}
|
|
|
|
static ssize_t get_attr_rdpmc(struct device *cdev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return snprintf(buf, 40, "%d\n", x86_pmu.attr_rdpmc);
|
|
}
|
|
|
|
static ssize_t set_attr_rdpmc(struct device *cdev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
unsigned long val;
|
|
ssize_t ret;
|
|
|
|
ret = kstrtoul(buf, 0, &val);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (val > 2)
|
|
return -EINVAL;
|
|
|
|
if (x86_pmu.attr_rdpmc_broken)
|
|
return -ENOTSUPP;
|
|
|
|
if (val != x86_pmu.attr_rdpmc) {
|
|
/*
|
|
* Changing into or out of never available or always available,
|
|
* aka perf-event-bypassing mode. This path is extremely slow,
|
|
* but only root can trigger it, so it's okay.
|
|
*/
|
|
if (val == 0)
|
|
static_branch_inc(&rdpmc_never_available_key);
|
|
else if (x86_pmu.attr_rdpmc == 0)
|
|
static_branch_dec(&rdpmc_never_available_key);
|
|
|
|
if (val == 2)
|
|
static_branch_inc(&rdpmc_always_available_key);
|
|
else if (x86_pmu.attr_rdpmc == 2)
|
|
static_branch_dec(&rdpmc_always_available_key);
|
|
|
|
on_each_cpu(refresh_pce, NULL, 1);
|
|
x86_pmu.attr_rdpmc = val;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static DEVICE_ATTR(rdpmc, S_IRUSR | S_IWUSR, get_attr_rdpmc, set_attr_rdpmc);
|
|
|
|
static struct attribute *x86_pmu_attrs[] = {
|
|
&dev_attr_rdpmc.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group x86_pmu_attr_group __ro_after_init = {
|
|
.attrs = x86_pmu_attrs,
|
|
};
|
|
|
|
static ssize_t max_precise_show(struct device *cdev,
|
|
struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", x86_pmu_max_precise());
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(max_precise);
|
|
|
|
static struct attribute *x86_pmu_caps_attrs[] = {
|
|
&dev_attr_max_precise.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group x86_pmu_caps_group __ro_after_init = {
|
|
.name = "caps",
|
|
.attrs = x86_pmu_caps_attrs,
|
|
};
|
|
|
|
static const struct attribute_group *x86_pmu_attr_groups[] = {
|
|
&x86_pmu_attr_group,
|
|
&x86_pmu_format_group,
|
|
&x86_pmu_events_group,
|
|
&x86_pmu_caps_group,
|
|
NULL,
|
|
};
|
|
|
|
static void x86_pmu_sched_task(struct perf_event_context *ctx, bool sched_in)
|
|
{
|
|
if (x86_pmu.sched_task)
|
|
x86_pmu.sched_task(ctx, sched_in);
|
|
}
|
|
|
|
static void x86_pmu_swap_task_ctx(struct perf_event_context *prev,
|
|
struct perf_event_context *next)
|
|
{
|
|
if (x86_pmu.swap_task_ctx)
|
|
x86_pmu.swap_task_ctx(prev, next);
|
|
}
|
|
|
|
void perf_check_microcode(void)
|
|
{
|
|
if (x86_pmu.check_microcode)
|
|
x86_pmu.check_microcode();
|
|
}
|
|
|
|
static int x86_pmu_check_period(struct perf_event *event, u64 value)
|
|
{
|
|
if (x86_pmu.check_period && x86_pmu.check_period(event, value))
|
|
return -EINVAL;
|
|
|
|
if (value && x86_pmu.limit_period) {
|
|
if (x86_pmu.limit_period(event, value) > value)
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int x86_pmu_aux_output_match(struct perf_event *event)
|
|
{
|
|
if (!(pmu.capabilities & PERF_PMU_CAP_AUX_OUTPUT))
|
|
return 0;
|
|
|
|
if (x86_pmu.aux_output_match)
|
|
return x86_pmu.aux_output_match(event);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct pmu pmu = {
|
|
.pmu_enable = x86_pmu_enable,
|
|
.pmu_disable = x86_pmu_disable,
|
|
|
|
.attr_groups = x86_pmu_attr_groups,
|
|
|
|
.event_init = x86_pmu_event_init,
|
|
|
|
.event_mapped = x86_pmu_event_mapped,
|
|
.event_unmapped = x86_pmu_event_unmapped,
|
|
|
|
.add = x86_pmu_add,
|
|
.del = x86_pmu_del,
|
|
.start = x86_pmu_start,
|
|
.stop = x86_pmu_stop,
|
|
.read = x86_pmu_read,
|
|
|
|
.start_txn = x86_pmu_start_txn,
|
|
.cancel_txn = x86_pmu_cancel_txn,
|
|
.commit_txn = x86_pmu_commit_txn,
|
|
|
|
.event_idx = x86_pmu_event_idx,
|
|
.sched_task = x86_pmu_sched_task,
|
|
.task_ctx_size = sizeof(struct x86_perf_task_context),
|
|
.swap_task_ctx = x86_pmu_swap_task_ctx,
|
|
.check_period = x86_pmu_check_period,
|
|
|
|
.aux_output_match = x86_pmu_aux_output_match,
|
|
};
|
|
|
|
void arch_perf_update_userpage(struct perf_event *event,
|
|
struct perf_event_mmap_page *userpg, u64 now)
|
|
{
|
|
struct cyc2ns_data data;
|
|
u64 offset;
|
|
|
|
userpg->cap_user_time = 0;
|
|
userpg->cap_user_time_zero = 0;
|
|
userpg->cap_user_rdpmc =
|
|
!!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED);
|
|
userpg->pmc_width = x86_pmu.cntval_bits;
|
|
|
|
if (!using_native_sched_clock() || !sched_clock_stable())
|
|
return;
|
|
|
|
cyc2ns_read_begin(&data);
|
|
|
|
offset = data.cyc2ns_offset + __sched_clock_offset;
|
|
|
|
/*
|
|
* Internal timekeeping for enabled/running/stopped times
|
|
* is always in the local_clock domain.
|
|
*/
|
|
userpg->cap_user_time = 1;
|
|
userpg->time_mult = data.cyc2ns_mul;
|
|
userpg->time_shift = data.cyc2ns_shift;
|
|
userpg->time_offset = offset - now;
|
|
|
|
/*
|
|
* cap_user_time_zero doesn't make sense when we're using a different
|
|
* time base for the records.
|
|
*/
|
|
if (!event->attr.use_clockid) {
|
|
userpg->cap_user_time_zero = 1;
|
|
userpg->time_zero = offset;
|
|
}
|
|
|
|
cyc2ns_read_end();
|
|
}
|
|
|
|
/*
|
|
* Determine whether the regs were taken from an irq/exception handler rather
|
|
* than from perf_arch_fetch_caller_regs().
|
|
*/
|
|
static bool perf_hw_regs(struct pt_regs *regs)
|
|
{
|
|
return regs->flags & X86_EFLAGS_FIXED;
|
|
}
|
|
|
|
void
|
|
perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
|
|
{
|
|
struct unwind_state state;
|
|
unsigned long addr;
|
|
|
|
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
|
|
/* TODO: We don't support guest os callchain now */
|
|
return;
|
|
}
|
|
|
|
if (perf_callchain_store(entry, regs->ip))
|
|
return;
|
|
|
|
if (perf_hw_regs(regs))
|
|
unwind_start(&state, current, regs, NULL);
|
|
else
|
|
unwind_start(&state, current, NULL, (void *)regs->sp);
|
|
|
|
for (; !unwind_done(&state); unwind_next_frame(&state)) {
|
|
addr = unwind_get_return_address(&state);
|
|
if (!addr || perf_callchain_store(entry, addr))
|
|
return;
|
|
}
|
|
}
|
|
|
|
static inline int
|
|
valid_user_frame(const void __user *fp, unsigned long size)
|
|
{
|
|
return (__range_not_ok(fp, size, TASK_SIZE) == 0);
|
|
}
|
|
|
|
static unsigned long get_segment_base(unsigned int segment)
|
|
{
|
|
struct desc_struct *desc;
|
|
unsigned int idx = segment >> 3;
|
|
|
|
if ((segment & SEGMENT_TI_MASK) == SEGMENT_LDT) {
|
|
#ifdef CONFIG_MODIFY_LDT_SYSCALL
|
|
struct ldt_struct *ldt;
|
|
|
|
/* IRQs are off, so this synchronizes with smp_store_release */
|
|
ldt = READ_ONCE(current->active_mm->context.ldt);
|
|
if (!ldt || idx >= ldt->nr_entries)
|
|
return 0;
|
|
|
|
desc = &ldt->entries[idx];
|
|
#else
|
|
return 0;
|
|
#endif
|
|
} else {
|
|
if (idx >= GDT_ENTRIES)
|
|
return 0;
|
|
|
|
desc = raw_cpu_ptr(gdt_page.gdt) + idx;
|
|
}
|
|
|
|
return get_desc_base(desc);
|
|
}
|
|
|
|
#ifdef CONFIG_IA32_EMULATION
|
|
|
|
#include <linux/compat.h>
|
|
|
|
static inline int
|
|
perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry_ctx *entry)
|
|
{
|
|
/* 32-bit process in 64-bit kernel. */
|
|
unsigned long ss_base, cs_base;
|
|
struct stack_frame_ia32 frame;
|
|
const void __user *fp;
|
|
|
|
if (!test_thread_flag(TIF_IA32))
|
|
return 0;
|
|
|
|
cs_base = get_segment_base(regs->cs);
|
|
ss_base = get_segment_base(regs->ss);
|
|
|
|
fp = compat_ptr(ss_base + regs->bp);
|
|
pagefault_disable();
|
|
while (entry->nr < entry->max_stack) {
|
|
unsigned long bytes;
|
|
frame.next_frame = 0;
|
|
frame.return_address = 0;
|
|
|
|
if (!valid_user_frame(fp, sizeof(frame)))
|
|
break;
|
|
|
|
bytes = __copy_from_user_nmi(&frame.next_frame, fp, 4);
|
|
if (bytes != 0)
|
|
break;
|
|
bytes = __copy_from_user_nmi(&frame.return_address, fp+4, 4);
|
|
if (bytes != 0)
|
|
break;
|
|
|
|
perf_callchain_store(entry, cs_base + frame.return_address);
|
|
fp = compat_ptr(ss_base + frame.next_frame);
|
|
}
|
|
pagefault_enable();
|
|
return 1;
|
|
}
|
|
#else
|
|
static inline int
|
|
perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry_ctx *entry)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
|
|
{
|
|
struct stack_frame frame;
|
|
const unsigned long __user *fp;
|
|
|
|
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
|
|
/* TODO: We don't support guest os callchain now */
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We don't know what to do with VM86 stacks.. ignore them for now.
|
|
*/
|
|
if (regs->flags & (X86_VM_MASK | PERF_EFLAGS_VM))
|
|
return;
|
|
|
|
fp = (unsigned long __user *)regs->bp;
|
|
|
|
perf_callchain_store(entry, regs->ip);
|
|
|
|
if (!nmi_uaccess_okay())
|
|
return;
|
|
|
|
if (perf_callchain_user32(regs, entry))
|
|
return;
|
|
|
|
pagefault_disable();
|
|
while (entry->nr < entry->max_stack) {
|
|
unsigned long bytes;
|
|
|
|
frame.next_frame = NULL;
|
|
frame.return_address = 0;
|
|
|
|
if (!valid_user_frame(fp, sizeof(frame)))
|
|
break;
|
|
|
|
bytes = __copy_from_user_nmi(&frame.next_frame, fp, sizeof(*fp));
|
|
if (bytes != 0)
|
|
break;
|
|
bytes = __copy_from_user_nmi(&frame.return_address, fp + 1, sizeof(*fp));
|
|
if (bytes != 0)
|
|
break;
|
|
|
|
perf_callchain_store(entry, frame.return_address);
|
|
fp = (void __user *)frame.next_frame;
|
|
}
|
|
pagefault_enable();
|
|
}
|
|
|
|
/*
|
|
* Deal with code segment offsets for the various execution modes:
|
|
*
|
|
* VM86 - the good olde 16 bit days, where the linear address is
|
|
* 20 bits and we use regs->ip + 0x10 * regs->cs.
|
|
*
|
|
* IA32 - Where we need to look at GDT/LDT segment descriptor tables
|
|
* to figure out what the 32bit base address is.
|
|
*
|
|
* X32 - has TIF_X32 set, but is running in x86_64
|
|
*
|
|
* X86_64 - CS,DS,SS,ES are all zero based.
|
|
*/
|
|
static unsigned long code_segment_base(struct pt_regs *regs)
|
|
{
|
|
/*
|
|
* For IA32 we look at the GDT/LDT segment base to convert the
|
|
* effective IP to a linear address.
|
|
*/
|
|
|
|
#ifdef CONFIG_X86_32
|
|
/*
|
|
* If we are in VM86 mode, add the segment offset to convert to a
|
|
* linear address.
|
|
*/
|
|
if (regs->flags & X86_VM_MASK)
|
|
return 0x10 * regs->cs;
|
|
|
|
if (user_mode(regs) && regs->cs != __USER_CS)
|
|
return get_segment_base(regs->cs);
|
|
#else
|
|
if (user_mode(regs) && !user_64bit_mode(regs) &&
|
|
regs->cs != __USER32_CS)
|
|
return get_segment_base(regs->cs);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
unsigned long perf_instruction_pointer(struct pt_regs *regs)
|
|
{
|
|
if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
|
|
return perf_guest_cbs->get_guest_ip();
|
|
|
|
return regs->ip + code_segment_base(regs);
|
|
}
|
|
|
|
unsigned long perf_misc_flags(struct pt_regs *regs)
|
|
{
|
|
int misc = 0;
|
|
|
|
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
|
|
if (perf_guest_cbs->is_user_mode())
|
|
misc |= PERF_RECORD_MISC_GUEST_USER;
|
|
else
|
|
misc |= PERF_RECORD_MISC_GUEST_KERNEL;
|
|
} else {
|
|
if (user_mode(regs))
|
|
misc |= PERF_RECORD_MISC_USER;
|
|
else
|
|
misc |= PERF_RECORD_MISC_KERNEL;
|
|
}
|
|
|
|
if (regs->flags & PERF_EFLAGS_EXACT)
|
|
misc |= PERF_RECORD_MISC_EXACT_IP;
|
|
|
|
return misc;
|
|
}
|
|
|
|
void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
|
|
{
|
|
cap->version = x86_pmu.version;
|
|
cap->num_counters_gp = x86_pmu.num_counters;
|
|
cap->num_counters_fixed = x86_pmu.num_counters_fixed;
|
|
cap->bit_width_gp = x86_pmu.cntval_bits;
|
|
cap->bit_width_fixed = x86_pmu.cntval_bits;
|
|
cap->events_mask = (unsigned int)x86_pmu.events_maskl;
|
|
cap->events_mask_len = x86_pmu.events_mask_len;
|
|
}
|
|
EXPORT_SYMBOL_GPL(perf_get_x86_pmu_capability);
|