cregit-Linux how code gets into the kernel

Release 4.10 arch/x86/events/core.c

Directory: arch/x86/events
/*
 * 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.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/bitops.h>
#include <linux/device.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,
};


struct static_key rdpmc_always_available = STATIC_KEY_INIT_FALSE;


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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra9670.59%433.33%
kevin winchesterkevin winchester1813.24%18.33%
stephane eranianstephane eranian107.35%18.33%
robert richterrobert richter75.15%433.33%
vince weavervince weaver53.68%216.67%
Total136100.00%12100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
andi kleenandi kleen10175.37%133.33%
stephane eranianstephane eranian2115.67%133.33%
kan liangkan liang128.96%133.33%
Total134100.00%3100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra11488.37%466.67%
robert richterrobert richter1511.63%233.33%
Total129100.00%6100.00%


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)); } }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra3784.09%360.00%
robert richterrobert richter715.91%240.00%
Total44100.00%5100.00%

#else
static bool reserve_pmc_hardware(void) { return true; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter11100.00%1100.00%
Total11100.00%1100.00%


static void release_pmc_hardware(void) {}

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter7100.00%1100.00%
Total7100.00%1100.00%

#endif
static bool check_hw_exists(void) { u64 val, val_fail, val_new= ~0; int i, reg, reg_fail, 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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra12740.45%19.09%
don zickusdon zickus7624.20%218.18%
george dunlapgeorge dunlap4113.06%19.09%
robert richterrobert richter216.69%218.18%
juergen grossjuergen gross196.05%19.09%
andre przywaraandre przywara185.73%19.09%
chen yucongchen yucong72.23%19.09%
ingo molnaringo molnar30.96%19.09%
wei huangwei huang20.64%19.09%
Total314100.00%11100.00%


static void hw_perf_event_destroy(struct perf_event *event) { x86_release_hardware(); atomic_dec(&active_events); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra1785.00%250.00%
alexander shishkinalexander shishkin315.00%250.00%
Total20100.00%4100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
alexander shishkinalexander shishkin21100.00%1100.00%
Total21100.00%1100.00%


static inline int x86_pmu_initialized(void) { return x86_pmu.handle_irq != NULL; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra16100.00%2100.00%
Total16100.00%2100.00%


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_op = (config >> 8) & 0xff; if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) return -EINVAL; cache_result = (config >> 16) & 0xff; if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) return -EINVAL; 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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra13680.47%266.67%
andi kleenandi kleen3319.53%133.33%
Total169100.00%3100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
alexander shishkinalexander shishkin76100.00%2100.00%
Total76100.00%2100.00%


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); } }

Contributors

PersonTokensPropCommitsCommitProp
alexander shishkinalexander shishkin32100.00%2100.00%
Total32100.00%2100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
alexander shishkinalexander shishkin10482.54%466.67%
peter zijlstrapeter zijlstra1713.49%116.67%
andi kleenandi kleen53.97%116.67%
Total126100.00%6100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
alexander shishkinalexander shishkin3389.19%375.00%
andi kleenandi kleen410.81%125.00%
Total37100.00%4100.00%


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; /* * The generic map: */ config = x86_pmu.event_map(attr->config); if (config == 0) return -ENOENT; if (config == -1LL) return -EINVAL; /* * Branch tracing: */ if (attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS && !attr->freq && hwc->sample_period == 1) { /* BTS is not supported by this architecture. */ if (!x86_pmu.bts_active) return -EOPNOTSUPP; /* 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; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter18782.38%222.22%
alexander shishkinalexander shishkin187.93%111.11%
peter zijlstrapeter zijlstra177.49%444.44%
franck bui-huufranck bui-huu41.76%111.11%
andi kleenandi kleen10.44%111.11%
Total227100.00%9100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
stephane eranianstephane eranian77100.00%1100.00%
Total77100.00%1100.00%


int x86_pmu_hw_config(struct perf_event *event) { if (event->attr.precise_ip) { 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++; } 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; } return x86_setup_perfctr(event); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra9630.38%531.25%
stephane eranianstephane eranian8125.63%16.25%
andi kleenandi kleen6420.25%425.00%
cyrill gorcunovcyrill gorcunov3511.08%16.25%
zheng yanzheng yan196.01%212.50%
jiri olsajiri olsa134.11%16.25%
robert richterrobert richter82.53%212.50%
Total316100.00%16100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra6461.54%436.36%
stephane eranianstephane eranian2221.15%327.27%
robert richterrobert richter1211.54%218.18%
cyrill gorcunovcyrill gorcunov54.81%19.09%
alexander shishkinalexander shishkin10.96%19.09%
Total104100.00%11100.00%


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); } }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra6674.16%116.67%
ingo molnaringo molnar1213.48%116.67%
robert richterrobert richter910.11%350.00%
christoph lameterchristoph lameter22.25%116.67%
Total89100.00%6100.00%

/* * 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(); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra4985.96%360.00%
ingo molnaringo molnar610.53%120.00%
christoph lameterchristoph lameter23.51%120.00%
Total57100.00%5100.00%


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); } }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra5371.62%233.33%
ingo molnaringo molnar1013.51%116.67%
robert richterrobert richter912.16%233.33%
christoph lameterchristoph lameter22.70%116.67%
Total74100.00%6100.00%

static struct pmu pmu;
static inline int is_x86_event(struct perf_event *event) { return event->pmu == &pmu; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra1995.00%150.00%
ingo molnaringo molnar15.00%150.00%
Total20100.00%2100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter10788.43%133.33%
peter zijlstrapeter zijlstra1411.57%266.67%
Total121100.00%3100.00%


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++; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter41100.00%1100.00%
Total41100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter59100.00%1100.00%
Total59100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter11460.96%337.50%
peter zijlstrapeter zijlstra6233.16%337.50%
zheng yanzheng yan94.81%112.50%
akinobu mitaakinobu mita21.07%112.50%
Total187100.00%8100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter35100.00%2100.00%
Total35100.00%2100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter12999.23%150.00%
peter zijlstrapeter zijlstra10.77%150.00%
Total130100.00%2100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
robert richterrobert richter8891.67%133.33%
peter zijlstrapeter zijlstra88.33%266.67%
Total96100.00%3100.00%

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 i, wmin, wmax, unsched = 0; struct hw_perf_event *hwc; bitmap_zero(used_mask, X86_PMC_IDX_MAX); if (x86_pmu.start_scheduling) x86_pmu.start_scheduling(cpuc); for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) { cpuc->event_constraint[i] = NULL; 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]; e->hw.flags |= PERF_X86_EVENT_COMMITTED; if (x86_pmu.commit_scheduling) x86_pmu.commit_scheduling(cpuc, i, assign[i]); } } else { for (i = 0; i < n; i++) { e = cpuc->event_list[i]; /* * do not put_constraint() on comitted events, * because they are good to go */ if ((e->hw.flags & PERF_X86_EVENT_COMMITTED)) continue; /* * release events that failed scheduling */ if (x86_pmu.put_event_constraints) x86_pmu.put_event_constraints(cpuc, e); } } if (x86_pmu.stop_scheduling) x86_pmu.stop_scheduling(cpuc); return unsched ? -EINVAL : 0; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra24654.42%750.00%
stephane eranianstephane eranian7115.71%214.29%
maria dimakopouloumaria dimakopoulou5111.28%214.29%
robert richterrobert richter388.41%17.14%
ingo molnaringo molnar367.96%17.14%
andrew hunterandrew hunter102.21%17.14%
Total452100.00%14100.00%

/* * 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 (is_x86_event(leader)) { if (n >= max_count) return -EINVAL; cpuc->event_list[n] = leader; n++; } if (!dogrp) return n; list_for_each_entry(event, &leader->sibling_list, group_entry) { 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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra11585.19%250.00%
ingo molnaringo molnar1813.33%125.00%
robert richterrobert richter21.48%125.00%
Total135100.00%4100.00%


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); } }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra9658.18%112.50%
vince weavervince weaver2313.94%112.50%
thomas gleixnerthomas gleixner1911.52%112.50%
robert richterrobert richter169.70%225.00%
stephane eranianstephane eranian74.24%112.50%
jacob shinjacob shin31.82%112.50%
ingo molnaringo molnar10.61%112.50%
Total165100.00%8100.00%


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]; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra4590.00%150.00%
thomas gleixnerthomas gleixner510.00%150.00%
Total50100.00%2100.00%

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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra22488.89%981.82%
thomas gleixnerthomas gleixner2610.32%19.09%
christoph lameterchristoph lameter20.79%19.09%
Total252100.00%11100.00%

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; if (!(hwc->flags & PERF_X86_EVENT_AUTO_RELOAD) || local64_read(&hwc->prev_count) != (u64)-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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra17160.00%436.36%
thomas gleixnerthomas gleixner3813.33%19.09%
zheng yanzheng yan289.82%19.09%
cyrill gorcunovcyrill gorcunov289.82%19.09%
andi kleenandi kleen175.96%19.09%
robert richterrobert richter20.70%218.18%
kevin winchesterkevin winchester10.35%19.09%
Total285100.00%11100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra1965.52%240.00%
tejun heotejun heo517.24%120.00%
thomas gleixnerthomas gleixner310.34%120.00%
robert richterrobert richter26.90%120.00%
Total29100.00%5100.00%

/* * Add a single event to the PMU. * * The event is added to the group of enabled events * but only if it can be scehduled 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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra15577.50%646.15%
thomas gleixnerthomas gleixner199.50%17.69%
lin minglin ming94.50%17.69%
stephane eranianstephane eranian84.00%17.69%
cyrill gorcunovcyrill gorcunov31.50%17.69%
christoph lameterchristoph lameter21.00%17.69%
sukadev bhattiprolusukadev bhattiprolu21.00%17.69%
ingo molnaringo molnar21.00%17.69%
Total200100.00%13100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra11786.03%457.14%
robert richterrobert richter96.62%114.29%
thomas gleixnerthomas gleixner85.88%114.29%
christoph lameterchristoph lameter21.47%114.29%
Total136100.00%7100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra25074.40%531.25%
andi kleenandi kleen4011.90%212.50%
thomas gleixnerthomas gleixner288.33%16.25%
robert richterrobert richter92.68%212.50%
jaswinder singh rajputjaswinder singh rajput41.19%212.50%
stephane eranianstephane eranian30.89%318.75%
hiroshi shimamotohiroshi shimamoto20.60%16.25%
Total336100.00%16100.00%


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_and_clear_bit(hwc->idx, cpuc->active_mask)) { x86_pmu.disable(event); 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; } }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra11097.35%466.67%
christoph lameterchristoph lameter21.77%116.67%
vince weavervince weaver10.88%116.67%
Total113100.00%6100.00%


static void x86_pmu_del(struct perf_event *event, int flags) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); int i; /* * event is descheduled */ event->hw.flags &= ~PERF_X86_EVENT_COMMITTED; /* * 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->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); } }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra17283.90%650.00%
stephane eranianstephane eranian167.80%216.67%
vince weavervince weaver115.37%18.33%
sukadev bhattiprolusukadev bhattiprolu20.98%18.33%
christoph lameterchristoph lameter20.98%18.33%
ingo molnaringo molnar20.98%18.33%
Total205100.00%12100.00%


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)) { /* * Though we deactivated the counter some cpus * might still deliver spurious interrupts still * in flight. Catch them: */ if (__test_and_clear_bit(idx, cpuc->running)) handled++; 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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra12365.43%423.53%
robert richterrobert richter3619.15%741.18%
ingo molnaringo molnar157.98%211.76%
don zickusdon zickus84.26%15.88%
christoph lameterchristoph lameter21.06%15.88%
jaswinder singh rajputjaswinder singh rajput21.06%15.88%
vince weavervince weaver21.06%15.88%
Total188100.00%17100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra2177.78%266.67%
ingo molnaringo molnar622.22%133.33%
Total27100.00%3100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra3045.45%233.33%
dave hansendave hansen2639.39%116.67%
don zickusdon zickus57.58%116.67%
stephane eranianstephane eranian46.06%116.67%
alexander shishkinalexander shishkin11.52%116.67%
Total66100.00%6100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra2741.54%233.33%
stephane eranianstephane eranian1929.23%233.33%
lin minglin ming1015.38%116.67%
thomas gleixnerthomas gleixner913.85%116.67%
Total65100.00%6100.00%


static int x86_pmu_dead_cpu(unsigned int cpu) { if (x86_pmu.cpu_dead) x86_pmu.cpu_dead(cpu); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner1557.69%125.00%
lin minglin ming519.23%125.00%
peter zijlstrapeter zijlstra415.38%125.00%
stephane eranianstephane eranian27.69%125.00%
Total26100.00%4100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner2946.03%133.33%
stephane eranianstephane eranian2742.86%133.33%
peter zijlstrapeter zijlstra711.11%133.33%
Total63100.00%3100.00%


static int x86_pmu_starting_cpu(unsigned int cpu) { if (x86_pmu.cpu_starting) x86_pmu.cpu_starting(cpu); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner1557.69%125.00%
lin minglin ming934.62%125.00%
stephane eranianstephane eranian13.85%125.00%
peter zijlstrapeter zijlstra13.85%125.00%
Total26100.00%4100.00%


static int x86_pmu_dying_cpu(unsigned int cpu) { if (x86_pmu.cpu_dying) x86_pmu.cpu_dying(cpu); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner1246.15%125.00%
lin minglin ming623.08%125.00%
stephane eranianstephane eranian519.23%125.00%
peter zijlstrapeter zijlstra311.54%125.00%
Total26100.00%4100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
lin minglin ming1435.00%116.67%
vince weavervince weaver717.50%116.67%
peter zijlstrapeter zijlstra615.00%116.67%
stephane eranianstephane eranian615.00%116.67%
borislav petkovborislav petkov410.00%116.67%
cyrill gorcunovcyrill gorcunov37.50%116.67%
Total40100.00%6100.00%

static struct attribute_group x86_pmu_format_group = { .name = "format", .attrs = NULL, }; /* * Remove all undefined events (x86_pmu.event_map(id) == 0) * out of events_attr attributes. */
static void __init filter_events(struct attribute **attrs) { struct device_attribute *d; struct perf_pmu_events_attr *pmu_attr; int offset = 0; int i, j; for (i = 0; attrs[i]; i++) { d = (struct device_attribute *)attrs[i]; pmu_attr = container_of(d, struct perf_pmu_events_attr, attr); /* str trumps id */ if (pmu_attr->event_str) continue; if (x86_pmu.event_map(i + offset)) continue; for (j = i; attrs[j]; j++) attrs[j] = attrs[j + 1]; /* Check the shifted attr. */ i--; /* * event_map() is index based, the attrs array is organized * by increasing event index. If we shift the events, then * we need to compensate for the event_map(), otherwise * we are looking up the wrong event in the map */ offset++; } }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa7458.27%133.33%
stephane eranianstephane eranian5341.73%266.67%
Total127100.00%3100.00%

/* Merge two pointer arrays */
__init struct attribute **merge_attr(struct attribute **a, struct attribute **b) { struct attribute **new; int j, i; for (j = 0; a[j]; j++) ; for (i = 0; b[i]; i++) j++; j++; new = kmalloc(sizeof(struct attribute *) * j, GFP_KERNEL); if (!new) return NULL; j = 0; for (i = 0; a[i]; i++) new[j++] = a[i]; for (i = 0; b[i]; i++) new[j++] = b[i]; new[j] = NULL; return new; }

Contributors

PersonTokensPropCommitsCommitProp
andi kleenandi kleen155100.00%2100.00%
Total155100.00%2100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa4053.33%133.33%
stephane eranianstephane eranian1925.33%133.33%
andi kleenandi kleen1621.33%133.33%
Total75100.00%3100.00%

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); }

Contributors

PersonTokensPropCommitsCommitProp
andi kleenandi kleen56100.00%1100.00%
Total56100.00%1100.00%

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, }; static struct attribute_group x86_pmu_events_group = { .name = "events", .attrs = events_attr, };
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; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa196100.00%2100.00%
Total196100.00%2100.00%


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; 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.event_attrs) x86_pmu_events_group.attrs = x86_pmu.event_attrs; if (!x86_pmu.events_sysfs_show) x86_pmu_events_group.attrs = &empty_attrs; else filter_events(x86_pmu_events_group.attrs); if (x86_pmu.cpu_events) { struct attribute **tmp; tmp = merge_attr(x86_pmu_events_group.attrs, x86_pmu.cpu_events); if (!WARN_ON(!tmp)) x86_pmu_events_group.attrs = tmp; } 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; }

Contributors

PersonTokensPropCommitsCommitProp
thomas gleixnerthomas gleixner9220.81%25.13%
ingo molnaringo molnar8920.14%512.82%
peter zijlstrapeter zijlstra8419.00%1025.64%
andi kleenandi kleen429.50%12.56%
robert richterrobert richter368.14%923.08%
jaswinder singh rajputjaswinder singh rajput337.47%25.13%
jiri olsajiri olsa306.79%37.69%
stephane eranianstephane eranian163.62%25.13%
don zickusdon zickus153.39%25.13%
cyrill gorcunovcyrill gorcunov30.68%12.56%
yong wangyong wang10.23%12.56%
yinghai luyinghai lu10.23%12.56%
Total442100.00%39100.00%

early_initcall(init_hw_perf_events);
static inline void x86_pmu_read(struct perf_event *event) { x86_perf_event_update(event); }

Contributors

PersonTokensPropCommitsCommitProp
ingo molnaringo molnar1588.24%250.00%
robert richterrobert richter211.76%250.00%
Total17100.00%4100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu3759.68%116.67%
lin minglin ming1117.74%116.67%
tejun heotejun heo69.68%116.67%
peter zijlstrapeter zijlstra58.06%233.33%
stephane eranianstephane eranian34.84%116.67%
Total62100.00%6100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
sukadev bhattiprolusukadev bhattiprolu4449.44%114.29%
tejun heotejun heo2224.72%114.29%
lin minglin ming1112.36%114.29%
peter zijlstrapeter zijlstra66.74%342.86%
stephane eranianstephane eranian66.74%114.29%
Total89100.00%7100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
lin minglin ming8767.44%116.67%
sukadev bhattiprolusukadev bhattiprolu3325.58%116.67%
peter zijlstrapeter zijlstra53.88%233.33%
christoph lameterchristoph lameter21.55%116.67%
stephane eranianstephane eranian21.55%116.67%
Total129100.00%6100.00%

/* * 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) { kfree(cpuc->shared_regs); kfree(cpuc); }

Contributors

PersonTokensPropCommitsCommitProp
stephane eranianstephane eranian23100.00%1100.00%
Total23100.00%1100.00%


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); /* only needed, if we have extra_regs */ if (x86_pmu.extra_regs) { cpuc->shared_regs = allocate_shared_regs(cpu); if (!cpuc->shared_regs) goto error; } cpuc->is_fake = 1; return cpuc; error: free_fake_cpuc(cpuc); return ERR_PTR(-ENOMEM); }

Contributors

PersonTokensPropCommitsCommitProp
stephane eranianstephane eranian9193.81%150.00%
peter zijlstrapeter zijlstra66.19%150.00%
Total97100.00%2100.00%

/* * 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, -1, 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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra8386.46%250.00%
stephane eranianstephane eranian1313.54%250.00%
Total96100.00%4100.00%

/* * 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 = n; ret = x86_pmu.schedule_events(fake_cpuc, n, NULL); out: free_fake_cpuc(fake_cpuc); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra6551.18%350.00%
stephane eranianstephane eranian5946.46%233.33%
cyrill gorcunovcyrill gorcunov32.36%116.67%
Total127100.00%6100.00%


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 (ACCESS_ONCE(x86_pmu.attr_rdpmc)) event->hw.flags |= PERF_X86_EVENT_RDPMC_ALLOWED; return err; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra7150.71%444.44%
ingo molnaringo molnar2618.57%222.22%
stephane eranianstephane eranian2517.86%111.11%
andy lutomirskiandy lutomirski1712.14%111.11%
yinghai luyinghai lu10.71%111.11%
Total140100.00%9100.00%


static void refresh_pce(void *ignored) { if (current->mm) load_mm_cr4(current->mm); }

Contributors

PersonTokensPropCommitsCommitProp
andy lutomirskiandy lutomirski23100.00%1100.00%
Total23100.00%1100.00%


static void x86_pmu_event_mapped(struct perf_event *event) { if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED)) return; if (atomic_inc_return(&current->mm->context.perf_rdpmc_allowed) == 1) on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1); }

Contributors

PersonTokensPropCommitsCommitProp
andy lutomirskiandy lutomirski57100.00%1100.00%
Total57100.00%1100.00%


static void x86_pmu_event_unmapped(struct perf_event *event) { if (!current->mm) return; if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED)) return; if (atomic_dec_and_test(&current->mm->context.perf_rdpmc_allowed)) on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1); }

Contributors

PersonTokensPropCommitsCommitProp
andy lutomirskiandy lutomirski63100.00%1100.00%
Total63100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra5384.13%250.00%
andy lutomirskiandy lutomirski812.70%125.00%
robert richterrobert richter23.17%125.00%
Total63100.00%4100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra34100.00%1100.00%
Total34100.00%1100.00%


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 == 2) != (x86_pmu.attr_rdpmc == 2)) { /* * Changing into or out of always available, aka * perf-event-bypassing mode. This path is extremely slow, * but only root can trigger it, so it's okay. */ if (val == 2) static_key_slow_inc(&rdpmc_always_available); else static_key_slow_dec(&rdpmc_always_available); on_each_cpu(refresh_pce, NULL, 1); } x86_pmu.attr_rdpmc = val; return count; }

Contributors

PersonTokensPropCommitsCommitProp
andy lutomirskiandy lutomirski5745.24%125.00%
peter zijlstrapeter zijlstra5140.48%250.00%
shuah khanshuah khan1814.29%125.00%
Total126100.00%4100.00%

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 = { .attrs = x86_pmu_attrs, }; static const struct attribute_group *x86_pmu_attr_groups[] = { &x86_pmu_attr_group, &x86_pmu_format_group, &x86_pmu_events_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); }

Contributors

PersonTokensPropCommitsCommitProp
zheng yanzheng yan1551.72%150.00%
stephane eranianstephane eranian1448.28%150.00%
Total29100.00%2100.00%


void perf_check_microcode(void) { if (x86_pmu.check_microcode) x86_pmu.check_microcode(); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra18100.00%1100.00%
Total18100.00%1100.00%

EXPORT_SYMBOL_GPL(perf_check_microcode); 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), };
void arch_perf_update_userpage(struct perf_event *event, struct perf_event_mmap_page *userpg, u64 now) { struct cyc2ns_data *data; 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 (!sched_clock_stable()) return; data = cyc2ns_read_begin(); /* * 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 = data->cyc2ns_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 = data->cyc2ns_offset; } cyc2ns_read_end(data); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra9872.59%763.64%
adrian hunteradrian hunter1813.33%19.09%
andy lutomirskiandy lutomirski1511.11%218.18%
alexander shishkinalexander shishkin42.96%19.09%
Total135100.00%11100.00%


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; for (unwind_start(&state, current, regs, NULL); !unwind_done(&state); unwind_next_frame(&state)) { addr = unwind_get_return_address(&state); if (!addr || perf_callchain_store(entry, addr)) return; } }

Contributors

PersonTokensPropCommitsCommitProp
josh poimboeufjosh poimboeuf5455.10%218.18%
peter zijlstrapeter zijlstra2525.51%436.36%
frederic weisbeckerfrederic weisbecker1616.33%327.27%
ingo molnaringo molnar22.04%19.09%
arnaldo carvalho de meloarnaldo carvalho de melo11.02%19.09%
Total98100.00%11100.00%


static inline int valid_user_frame(const void __user *fp, unsigned long size) { return (__range_not_ok(fp, size, TASK_SIZE) == 0); }

Contributors

PersonTokensPropCommitsCommitProp
arun sharmaarun sharma31100.00%1100.00%
Total31100.00%1100.00%


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; if (idx > LDT_ENTRIES) return 0; /* IRQs are off, so this synchronizes with smp_store_release */ ldt = lockless_dereference(current->active_mm->context.ldt); if (!ldt || idx > ldt->size) 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); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra8566.93%120.00%
andy lutomirskiandy lutomirski4031.50%240.00%
christoph lameterchristoph lameter10.79%120.00%
thomas gleixnerthomas gleixner10.79%120.00%
Total127100.00%5100.00%

#ifdef CONFIG_IA32_EMULATION #include <asm/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; }

Contributors

PersonTokensPropCommitsCommitProp
torok edwintorok edwin8544.50%111.11%
peter zijlstrapeter zijlstra5629.32%444.44%
andi kleenandi kleen4020.94%111.11%
johannes weinerjohannes weiner52.62%111.11%
arnaldo carvalho de meloarnaldo carvalho de melo42.09%111.11%
frederic weisbeckerfrederic weisbecker10.52%111.11%
Total191100.00%9100.00%

#else
static inline int perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry_ctx *entry) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
torok edwintorok edwin1785.00%133.33%
peter zijlstrapeter zijlstra210.00%133.33%
arnaldo carvalho de meloarnaldo carvalho de melo15.00%133.33%
Total20100.00%3100.00%

#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 (!current->mm) 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(); }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra9243.40%428.57%
andi kleenandi kleen4219.81%17.14%
torok edwintorok edwin2712.74%17.14%
josh poimboeufjosh poimboeuf188.49%17.14%
frederic weisbeckerfrederic weisbecker178.02%321.43%
andrey vaginandrey vagin83.77%17.14%
arnaldo carvalho de meloarnaldo carvalho de melo41.89%17.14%
johannes weinerjohannes weiner20.94%17.14%
ingo molnaringo molnar20.94%17.14%
Total212100.00%14100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra6878.16%120.00%
andy lutomirskiandy lutomirski910.34%240.00%
yanmin zhangyanmin zhang910.34%120.00%
ingo molnaringo molnar11.15%120.00%
Total87100.00%5100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
yanmin zhangyanmin zhang2158.33%150.00%
peter zijlstrapeter zijlstra1541.67%150.00%
Total36100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
yanmin zhangyanmin zhang7597.40%250.00%
peter zijlstrapeter zijlstra22.60%250.00%
Total77100.00%4100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
gleb natapovgleb natapov70100.00%1100.00%
Total70100.00%1100.00%

EXPORT_SYMBOL_GPL(perf_get_x86_pmu_capability);

Overall Contributors

PersonTokensPropCommitsCommitProp
peter zijlstrapeter zijlstra411341.99%7430.45%
robert richterrobert richter109811.21%3012.35%
stephane eranianstephane eranian7107.25%187.41%
andi kleenandi kleen6316.44%135.35%
jiri olsajiri olsa5525.64%62.47%
thomas gleixnerthomas gleixner3223.29%62.47%
andy lutomirskiandy lutomirski3113.18%83.29%
alexander shishkinalexander shishkin2983.04%52.06%
ingo molnaringo molnar2822.88%145.76%
lin minglin ming1641.67%20.82%
torok edwintorok edwin1371.40%10.41%
sukadev bhattiprolusukadev bhattiprolu1191.21%20.82%
yanmin zhangyanmin zhang1051.07%20.82%
don zickusdon zickus1041.06%41.65%
zheng yanzheng yan870.89%62.47%
cyrill gorcunovcyrill gorcunov800.82%31.23%
gleb natapovgleb natapov750.77%10.41%
josh poimboeufjosh poimboeuf750.77%31.23%
vince weavervince weaver570.58%31.23%
maria dimakopouloumaria dimakopoulou510.52%20.82%
jaswinder singh rajputjaswinder singh rajput420.43%31.23%
george dunlapgeorge dunlap410.42%10.41%
tejun heotejun heo360.37%20.82%
frederic weisbeckerfrederic weisbecker340.35%31.23%
arun sharmaarun sharma310.32%10.41%
dave hansendave hansen260.27%10.41%
kevin winchesterkevin winchester220.22%10.41%
christoph lameterchristoph lameter210.21%10.41%
juergen grossjuergen gross190.19%10.41%
shuah khanshuah khan180.18%10.41%
adrian hunteradrian hunter180.18%10.41%
andre przywaraandre przywara180.18%10.41%
kan liangkan liang130.13%20.82%
arnaldo carvalho de meloarnaldo carvalho de melo100.10%10.41%
andrew hunterandrew hunter100.10%10.41%
andrey vaginandrey vagin80.08%10.41%
johannes weinerjohannes weiner70.07%10.41%
chen yucongchen yucong70.07%10.41%
masami hiramatsumasami hiramatsu50.05%10.41%
borislav petkovborislav petkov50.05%20.82%
huang ruihuang rui50.05%10.41%
paul gortmakerpaul gortmaker40.04%10.41%
franck bui-huufranck bui-huu40.04%10.41%
h. peter anvinh. peter anvin30.03%10.41%
markus metzgermarkus metzger30.03%10.41%
jacob shinjacob shin30.03%10.41%
akinobu mitaakinobu mita20.02%10.41%
yinghai luyinghai lu20.02%10.41%
hiroshi shimamotohiroshi shimamoto20.02%10.41%
wei huangwei huang20.02%10.41%
yong wangyong wang10.01%10.41%
brian gerstbrian gerst10.01%10.41%
peter huewepeter huewe10.01%10.41%
Total9795100.00%243100.00%
Directory: arch/x86/events
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.