You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf changes from Ingo Molnar:
"Core kernel changes:
- One of the more interesting features in this cycle is the ability
to attach eBPF programs (user-defined, sandboxed bytecode executed
by the kernel) to kprobes.
This allows user-defined instrumentation on a live kernel image
that can never crash, hang or interfere with the kernel negatively.
(Right now it's limited to root-only, but in the future we might
allow unprivileged use as well.)
(Alexei Starovoitov)
- Another non-trivial feature is per event clockid support: this
allows, amongst other things, the selection of different clock
sources for event timestamps traced via perf.
This feature is sought by people who'd like to merge perf generated
events with external events that were measured with different
clocks:
- cluster wide profiling
- for system wide tracing with user-space events,
- JIT profiling events
etc. Matching perf tooling support is added as well, available via
the -k, --clockid <clockid> parameter to perf record et al.
(Peter Zijlstra)
Hardware enablement kernel changes:
- x86 Intel Processor Trace (PT) support: which is a hardware tracer
on steroids, available on Broadwell CPUs.
The hardware trace stream is directly output into the user-space
ring-buffer, using the 'AUX' data format extension that was added
to the perf core to support hardware constraints such as the
necessity to have the tracing buffer physically contiguous.
This patch-set was developed for two years and this is the result.
A simple way to make use of this is to use BTS tracing, the PT
driver emulates BTS output - available via the 'intel_bts' PMU.
More explicit PT specific tooling support is in the works as well -
will probably be ready by 4.2.
(Alexander Shishkin, Peter Zijlstra)
- x86 Intel Cache QoS Monitoring (CQM) support: this is a hardware
feature of Intel Xeon CPUs that allows the measurement and
allocation/partitioning of caches to individual workloads.
These kernel changes expose the measurement side as a new PMU
driver, which exposes various QoS related PMU events. (The
partitioning change is work in progress and is planned to be merged
as a cgroup extension.)
(Matt Fleming, Peter Zijlstra; CPU feature detection by Peter P
Waskiewicz Jr)
- x86 Intel Haswell LBR call stack support: this is a new Haswell
feature that allows the hardware recording of call chains, plus
tooling support. To activate this feature you have to enable it
via the new 'lbr' call-graph recording option:
perf record --call-graph lbr
perf report
or:
perf top --call-graph lbr
This hardware feature is a lot faster than stack walk or dwarf
based unwinding, but has some limitations:
- It reuses the current LBR facility, so LBR call stack and
branch record can not be enabled at the same time.
- It is only available for user-space callchains.
(Yan, Zheng)
- x86 Intel Broadwell CPU support and various event constraints and
event table fixes for earlier models.
(Andi Kleen)
- x86 Intel HT CPUs event scheduling workarounds. This is a complex
CPU bug affecting the SNB,IVB,HSW families that results in counter
value corruption. The mitigation code is automatically enabled and
is transparent.
(Maria Dimakopoulou, Stephane Eranian)
The perf tooling side had a ton of changes in this cycle as well, so
I'm only able to list the user visible changes here, in addition to
the tooling changes outlined above:
User visible changes affecting all tools:
- Improve support of compressed kernel modules (Jiri Olsa)
- Save DSO loading errno to better report errors (Arnaldo Carvalho de Melo)
- Bash completion for subcommands (Yunlong Song)
- Add 'I' event modifier for perf_event_attr.exclude_idle bit (Jiri Olsa)
- Support missing -f to override perf.data file ownership. (Yunlong Song)
- Show the first event with an invalid filter (David Ahern, Arnaldo Carvalho de Melo)
User visible changes in individual tools:
'perf data':
New tool for converting perf.data to other formats, initially
for the CTF (Common Trace Format) from LTTng (Jiri Olsa,
Sebastian Siewior)
'perf diff':
Add --kallsyms option (David Ahern)
'perf list':
Allow listing events with 'tracepoint' prefix (Yunlong Song)
Sort the output of the command (Yunlong Song)
'perf kmem':
Respect -i option (Jiri Olsa)
Print big numbers using thousands' group (Namhyung Kim)
Allow -v option (Namhyung Kim)
Fix alignment of slab result table (Namhyung Kim)
'perf probe':
Support multiple probes on different binaries on the same command line (Masami Hiramatsu)
Support unnamed union/structure members data collection. (Masami Hiramatsu)
Check kprobes blacklist when adding new events. (Masami Hiramatsu)
'perf record':
Teach 'perf record' about perf_event_attr.clockid (Peter Zijlstra)
Support recording running/enabled time (Andi Kleen)
'perf sched':
Improve the performance of 'perf sched replay' on high CPU core count machines (Yunlong Song)
'perf report' and 'perf top':
Allow annotating entries in callchains in the hists browser (Arnaldo Carvalho de Melo)
Indicate which callchain entries are annotated in the
TUI hists browser (Arnaldo Carvalho de Melo)
Add pid/tid filtering to 'report' and 'script' commands (David Ahern)
Consider PERF_RECORD_ events with cpumode == 0 in 'perf top', removing one
cause of long term memory usage buildup, i.e. not processing PERF_RECORD_EXIT
events (Arnaldo Carvalho de Melo)
'perf stat':
Report unsupported events properly (Suzuki K. Poulose)
Output running time and run/enabled ratio in CSV mode (Andi Kleen)
'perf trace':
Handle legacy syscalls tracepoints (David Ahern, Arnaldo Carvalho de Melo)
Only insert blank duration bracket when tracing syscalls (Arnaldo Carvalho de Melo)
Filter out the trace pid when no threads are specified (Arnaldo Carvalho de Melo)
Dump stack on segfaults (Arnaldo Carvalho de Melo)
No need to explicitely enable evsels for workload started from perf, let it
be enabled via perf_event_attr.enable_on_exec, removing some events that take
place in the 'perf trace' before a workload is really started by it.
(Arnaldo Carvalho de Melo)
Allow mixing with tracepoints and suppressing plain syscalls. (Arnaldo Carvalho de Melo)
There's also been a ton of infrastructure work done, such as the
split-out of perf's build system into tools/build/ and other changes -
see the shortlog and changelog for details"
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (358 commits)
perf/x86/intel/pt: Clean up the control flow in pt_pmu_hw_init()
perf evlist: Fix type for references to data_head/tail
perf probe: Check the orphaned -x option
perf probe: Support multiple probes on different binaries
perf buildid-list: Fix segfault when show DSOs with hits
perf tools: Fix cross-endian analysis
perf tools: Fix error path to do closedir() when synthesizing threads
perf tools: Fix synthesizing fork_event.ppid for non-main thread
perf tools: Add 'I' event modifier for exclude_idle bit
perf report: Don't call map__kmap if map is NULL.
perf tests: Fix attr tests
perf probe: Fix ARM 32 building error
perf tools: Merge all perf_event_attr print functions
perf record: Add clockid parameter
perf sched replay: Use replay_repeat to calculate the runavg of cpu usage instead of the default value 10
perf sched replay: Support using -f to override perf.data file ownership
perf sched replay: Fix the EMFILE error caused by the limitation of the maximum open files
perf sched replay: Handle the dead halt of sem_wait when create_tasks() fails for any task
perf sched replay: Fix the segmentation fault problem caused by pr_err in threads
perf sched replay: Realloc the memory of pid_to_task stepwise to adapt to the different pid_max configurations
...
This commit is contained in:
@@ -648,7 +648,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
|
||||
* Per-cpu breakpoints are not supported by our stepping
|
||||
* mechanism.
|
||||
*/
|
||||
if (!bp->hw.bp_target)
|
||||
if (!bp->hw.target)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
|
||||
@@ -527,7 +527,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
|
||||
* Disallow per-task kernel breakpoints since these would
|
||||
* complicate the stepping code.
|
||||
*/
|
||||
if (info->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.bp_target)
|
||||
if (info->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.target)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -124,7 +124,7 @@ static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw)
|
||||
|
||||
static inline void power_pmu_bhrb_enable(struct perf_event *event) {}
|
||||
static inline void power_pmu_bhrb_disable(struct perf_event *event) {}
|
||||
static void power_pmu_flush_branch_stack(void) {}
|
||||
static void power_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) {}
|
||||
static inline void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) {}
|
||||
static void pmao_restore_workaround(bool ebb) { }
|
||||
#endif /* CONFIG_PPC32 */
|
||||
@@ -350,6 +350,7 @@ static void power_pmu_bhrb_enable(struct perf_event *event)
|
||||
cpuhw->bhrb_context = event->ctx;
|
||||
}
|
||||
cpuhw->bhrb_users++;
|
||||
perf_sched_cb_inc(event->ctx->pmu);
|
||||
}
|
||||
|
||||
static void power_pmu_bhrb_disable(struct perf_event *event)
|
||||
@@ -361,6 +362,7 @@ static void power_pmu_bhrb_disable(struct perf_event *event)
|
||||
|
||||
cpuhw->bhrb_users--;
|
||||
WARN_ON_ONCE(cpuhw->bhrb_users < 0);
|
||||
perf_sched_cb_dec(event->ctx->pmu);
|
||||
|
||||
if (!cpuhw->disabled && !cpuhw->bhrb_users) {
|
||||
/* BHRB cannot be turned off when other
|
||||
@@ -375,9 +377,12 @@ static void power_pmu_bhrb_disable(struct perf_event *event)
|
||||
/* Called from ctxsw to prevent one process's branch entries to
|
||||
* mingle with the other process's entries during context switch.
|
||||
*/
|
||||
static void power_pmu_flush_branch_stack(void)
|
||||
static void power_pmu_sched_task(struct perf_event_context *ctx, bool sched_in)
|
||||
{
|
||||
if (ppmu->bhrb_nr)
|
||||
if (!ppmu->bhrb_nr)
|
||||
return;
|
||||
|
||||
if (sched_in)
|
||||
power_pmu_bhrb_reset();
|
||||
}
|
||||
/* Calculate the to address for a branch */
|
||||
@@ -1901,7 +1906,7 @@ static struct pmu power_pmu = {
|
||||
.cancel_txn = power_pmu_cancel_txn,
|
||||
.commit_txn = power_pmu_commit_txn,
|
||||
.event_idx = power_pmu_event_idx,
|
||||
.flush_branch_stack = power_pmu_flush_branch_stack,
|
||||
.sched_task = power_pmu_sched_task,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <asm/disabled-features.h>
|
||||
#endif
|
||||
|
||||
#define NCAPINTS 11 /* N 32-bit words worth of info */
|
||||
#define NCAPINTS 13 /* N 32-bit words worth of info */
|
||||
#define NBUGINTS 1 /* N 32-bit bug flags */
|
||||
|
||||
/*
|
||||
@@ -195,6 +195,7 @@
|
||||
#define X86_FEATURE_HWP_ACT_WINDOW ( 7*32+ 12) /* Intel HWP_ACT_WINDOW */
|
||||
#define X86_FEATURE_HWP_EPP ( 7*32+13) /* Intel HWP_EPP */
|
||||
#define X86_FEATURE_HWP_PKG_REQ ( 7*32+14) /* Intel HWP_PKG_REQ */
|
||||
#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */
|
||||
|
||||
/* Virtualization flags: Linux defined, word 8 */
|
||||
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
|
||||
@@ -226,6 +227,7 @@
|
||||
#define X86_FEATURE_ERMS ( 9*32+ 9) /* Enhanced REP MOVSB/STOSB */
|
||||
#define X86_FEATURE_INVPCID ( 9*32+10) /* Invalidate Processor Context ID */
|
||||
#define X86_FEATURE_RTM ( 9*32+11) /* Restricted Transactional Memory */
|
||||
#define X86_FEATURE_CQM ( 9*32+12) /* Cache QoS Monitoring */
|
||||
#define X86_FEATURE_MPX ( 9*32+14) /* Memory Protection Extension */
|
||||
#define X86_FEATURE_AVX512F ( 9*32+16) /* AVX-512 Foundation */
|
||||
#define X86_FEATURE_RDSEED ( 9*32+18) /* The RDSEED instruction */
|
||||
@@ -244,6 +246,12 @@
|
||||
#define X86_FEATURE_XGETBV1 (10*32+ 2) /* XGETBV with ECX = 1 */
|
||||
#define X86_FEATURE_XSAVES (10*32+ 3) /* XSAVES/XRSTORS */
|
||||
|
||||
/* Intel-defined CPU QoS Sub-leaf, CPUID level 0x0000000F:0 (edx), word 11 */
|
||||
#define X86_FEATURE_CQM_LLC (11*32+ 1) /* LLC QoS if 1 */
|
||||
|
||||
/* Intel-defined CPU QoS Sub-leaf, CPUID level 0x0000000F:1 (edx), word 12 */
|
||||
#define X86_FEATURE_CQM_OCCUP_LLC (12*32+ 0) /* LLC occupancy monitoring if 1 */
|
||||
|
||||
/*
|
||||
* BUG word(s)
|
||||
*/
|
||||
|
||||
@@ -109,6 +109,9 @@ struct cpuinfo_x86 {
|
||||
/* in KB - valid for CPUS which support this call: */
|
||||
int x86_cache_size;
|
||||
int x86_cache_alignment; /* In bytes */
|
||||
/* Cache QoS architectural values: */
|
||||
int x86_cache_max_rmid; /* max index */
|
||||
int x86_cache_occ_scale; /* scale to bytes */
|
||||
int x86_power;
|
||||
unsigned long loops_per_jiffy;
|
||||
/* cpuid returned max cores value: */
|
||||
|
||||
@@ -74,6 +74,24 @@
|
||||
#define MSR_IA32_PERF_CAPABILITIES 0x00000345
|
||||
#define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6
|
||||
|
||||
#define MSR_IA32_RTIT_CTL 0x00000570
|
||||
#define RTIT_CTL_TRACEEN BIT(0)
|
||||
#define RTIT_CTL_OS BIT(2)
|
||||
#define RTIT_CTL_USR BIT(3)
|
||||
#define RTIT_CTL_CR3EN BIT(7)
|
||||
#define RTIT_CTL_TOPA BIT(8)
|
||||
#define RTIT_CTL_TSC_EN BIT(10)
|
||||
#define RTIT_CTL_DISRETC BIT(11)
|
||||
#define RTIT_CTL_BRANCH_EN BIT(13)
|
||||
#define MSR_IA32_RTIT_STATUS 0x00000571
|
||||
#define RTIT_STATUS_CONTEXTEN BIT(1)
|
||||
#define RTIT_STATUS_TRIGGEREN BIT(2)
|
||||
#define RTIT_STATUS_ERROR BIT(4)
|
||||
#define RTIT_STATUS_STOPPED BIT(5)
|
||||
#define MSR_IA32_RTIT_CR3_MATCH 0x00000572
|
||||
#define MSR_IA32_RTIT_OUTPUT_BASE 0x00000560
|
||||
#define MSR_IA32_RTIT_OUTPUT_MASK 0x00000561
|
||||
|
||||
#define MSR_MTRRfix64K_00000 0x00000250
|
||||
#define MSR_MTRRfix16K_80000 0x00000258
|
||||
#define MSR_MTRRfix16K_A0000 0x00000259
|
||||
|
||||
@@ -39,7 +39,8 @@ obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd_iommu.o
|
||||
endif
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_knc.o perf_event_p4.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_rapl.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_rapl.o perf_event_intel_cqm.o
|
||||
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_pt.o perf_event_intel_bts.o
|
||||
|
||||
obj-$(CONFIG_PERF_EVENTS_INTEL_UNCORE) += perf_event_intel_uncore.o \
|
||||
perf_event_intel_uncore_snb.o \
|
||||
|
||||
@@ -646,6 +646,30 @@ void get_cpu_cap(struct cpuinfo_x86 *c)
|
||||
c->x86_capability[10] = eax;
|
||||
}
|
||||
|
||||
/* Additional Intel-defined flags: level 0x0000000F */
|
||||
if (c->cpuid_level >= 0x0000000F) {
|
||||
u32 eax, ebx, ecx, edx;
|
||||
|
||||
/* QoS sub-leaf, EAX=0Fh, ECX=0 */
|
||||
cpuid_count(0x0000000F, 0, &eax, &ebx, &ecx, &edx);
|
||||
c->x86_capability[11] = edx;
|
||||
if (cpu_has(c, X86_FEATURE_CQM_LLC)) {
|
||||
/* will be overridden if occupancy monitoring exists */
|
||||
c->x86_cache_max_rmid = ebx;
|
||||
|
||||
/* QoS sub-leaf, EAX=0Fh, ECX=1 */
|
||||
cpuid_count(0x0000000F, 1, &eax, &ebx, &ecx, &edx);
|
||||
c->x86_capability[12] = edx;
|
||||
if (cpu_has(c, X86_FEATURE_CQM_OCCUP_LLC)) {
|
||||
c->x86_cache_max_rmid = ecx;
|
||||
c->x86_cache_occ_scale = ebx;
|
||||
}
|
||||
} else {
|
||||
c->x86_cache_max_rmid = -1;
|
||||
c->x86_cache_occ_scale = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* AMD-defined flags: level 0x80000001 */
|
||||
xlvl = cpuid_eax(0x80000000);
|
||||
c->extended_cpuid_level = xlvl;
|
||||
@@ -834,6 +858,20 @@ static void generic_identify(struct cpuinfo_x86 *c)
|
||||
detect_nopl(c);
|
||||
}
|
||||
|
||||
static void x86_init_cache_qos(struct cpuinfo_x86 *c)
|
||||
{
|
||||
/*
|
||||
* The heavy lifting of max_rmid and cache_occ_scale are handled
|
||||
* in get_cpu_cap(). Here we just set the max_rmid for the boot_cpu
|
||||
* in case CQM bits really aren't there in this CPU.
|
||||
*/
|
||||
if (c != &boot_cpu_data) {
|
||||
boot_cpu_data.x86_cache_max_rmid =
|
||||
min(boot_cpu_data.x86_cache_max_rmid,
|
||||
c->x86_cache_max_rmid);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This does the hard work of actually picking apart the CPU stuff...
|
||||
*/
|
||||
@@ -923,6 +961,7 @@ static void identify_cpu(struct cpuinfo_x86 *c)
|
||||
|
||||
init_hypervisor(c);
|
||||
x86_init_rdrand(c);
|
||||
x86_init_cache_qos(c);
|
||||
|
||||
/*
|
||||
* Clear/Set all flags overriden by options, need do it
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Intel(R) Processor Trace PMU driver for perf
|
||||
* Copyright (c) 2013-2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* Intel PT is specified in the Intel Architecture Instruction Set Extensions
|
||||
* Programming Reference:
|
||||
* http://software.intel.com/en-us/intel-isa-extensions
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_PT_H__
|
||||
#define __INTEL_PT_H__
|
||||
|
||||
/*
|
||||
* Single-entry ToPA: when this close to region boundary, switch
|
||||
* buffers to avoid losing data.
|
||||
*/
|
||||
#define TOPA_PMI_MARGIN 512
|
||||
|
||||
/*
|
||||
* Table of Physical Addresses bits
|
||||
*/
|
||||
enum topa_sz {
|
||||
TOPA_4K = 0,
|
||||
TOPA_8K,
|
||||
TOPA_16K,
|
||||
TOPA_32K,
|
||||
TOPA_64K,
|
||||
TOPA_128K,
|
||||
TOPA_256K,
|
||||
TOPA_512K,
|
||||
TOPA_1MB,
|
||||
TOPA_2MB,
|
||||
TOPA_4MB,
|
||||
TOPA_8MB,
|
||||
TOPA_16MB,
|
||||
TOPA_32MB,
|
||||
TOPA_64MB,
|
||||
TOPA_128MB,
|
||||
TOPA_SZ_END,
|
||||
};
|
||||
|
||||
static inline unsigned int sizes(enum topa_sz tsz)
|
||||
{
|
||||
return 1 << (tsz + 12);
|
||||
};
|
||||
|
||||
struct topa_entry {
|
||||
u64 end : 1;
|
||||
u64 rsvd0 : 1;
|
||||
u64 intr : 1;
|
||||
u64 rsvd1 : 1;
|
||||
u64 stop : 1;
|
||||
u64 rsvd2 : 1;
|
||||
u64 size : 4;
|
||||
u64 rsvd3 : 2;
|
||||
u64 base : 36;
|
||||
u64 rsvd4 : 16;
|
||||
};
|
||||
|
||||
#define TOPA_SHIFT 12
|
||||
#define PT_CPUID_LEAVES 2
|
||||
|
||||
enum pt_capabilities {
|
||||
PT_CAP_max_subleaf = 0,
|
||||
PT_CAP_cr3_filtering,
|
||||
PT_CAP_topa_output,
|
||||
PT_CAP_topa_multiple_entries,
|
||||
PT_CAP_payloads_lip,
|
||||
};
|
||||
|
||||
struct pt_pmu {
|
||||
struct pmu pmu;
|
||||
u32 caps[4 * PT_CPUID_LEAVES];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pt_buffer - buffer configuration; one buffer per task_struct or
|
||||
* cpu, depending on perf event configuration
|
||||
* @cpu: cpu for per-cpu allocation
|
||||
* @tables: list of ToPA tables in this buffer
|
||||
* @first: shorthand for first topa table
|
||||
* @last: shorthand for last topa table
|
||||
* @cur: current topa table
|
||||
* @nr_pages: buffer size in pages
|
||||
* @cur_idx: current output region's index within @cur table
|
||||
* @output_off: offset within the current output region
|
||||
* @data_size: running total of the amount of data in this buffer
|
||||
* @lost: if data was lost/truncated
|
||||
* @head: logical write offset inside the buffer
|
||||
* @snapshot: if this is for a snapshot/overwrite counter
|
||||
* @stop_pos: STOP topa entry in the buffer
|
||||
* @intr_pos: INT topa entry in the buffer
|
||||
* @data_pages: array of pages from perf
|
||||
* @topa_index: table of topa entries indexed by page offset
|
||||
*/
|
||||
struct pt_buffer {
|
||||
int cpu;
|
||||
struct list_head tables;
|
||||
struct topa *first, *last, *cur;
|
||||
unsigned int cur_idx;
|
||||
size_t output_off;
|
||||
unsigned long nr_pages;
|
||||
local_t data_size;
|
||||
local_t lost;
|
||||
local64_t head;
|
||||
bool snapshot;
|
||||
unsigned long stop_pos, intr_pos;
|
||||
void **data_pages;
|
||||
struct topa_entry *topa_index[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pt - per-cpu pt context
|
||||
* @handle: perf output handle
|
||||
* @handle_nmi: do handle PT PMI on this cpu, there's an active event
|
||||
*/
|
||||
struct pt {
|
||||
struct perf_output_handle handle;
|
||||
int handle_nmi;
|
||||
};
|
||||
|
||||
#endif /* __INTEL_PT_H__ */
|
||||
@@ -263,6 +263,14 @@ static void hw_perf_event_destroy(struct perf_event *event)
|
||||
}
|
||||
}
|
||||
|
||||
void hw_perf_lbr_event_destroy(struct perf_event *event)
|
||||
{
|
||||
hw_perf_event_destroy(event);
|
||||
|
||||
/* undo the lbr/bts event accounting */
|
||||
x86_del_exclusive(x86_lbr_exclusive_lbr);
|
||||
}
|
||||
|
||||
static inline int x86_pmu_initialized(void)
|
||||
{
|
||||
return x86_pmu.handle_irq != NULL;
|
||||
@@ -302,6 +310,35 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
|
||||
return x86_pmu_extra_regs(val, event);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we can create event of a certain type (that no conflicting events
|
||||
* are present).
|
||||
*/
|
||||
int x86_add_exclusive(unsigned int what)
|
||||
{
|
||||
int ret = -EBUSY, i;
|
||||
|
||||
if (atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what]))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&pmc_reserve_mutex);
|
||||
for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++)
|
||||
if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i]))
|
||||
goto out;
|
||||
|
||||
atomic_inc(&x86_pmu.lbr_exclusive[what]);
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&pmc_reserve_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void x86_del_exclusive(unsigned int what)
|
||||
{
|
||||
atomic_dec(&x86_pmu.lbr_exclusive[what]);
|
||||
}
|
||||
|
||||
int x86_setup_perfctr(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_attr *attr = &event->attr;
|
||||
@@ -346,6 +383,12 @@ int x86_setup_perfctr(struct perf_event *event)
|
||||
/* BTS is currently only allowed for user-mode. */
|
||||
if (!attr->exclude_kernel)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* disallow bts if conflicting events are present */
|
||||
if (x86_add_exclusive(x86_lbr_exclusive_lbr))
|
||||
return -EBUSY;
|
||||
|
||||
event->destroy = hw_perf_lbr_event_destroy;
|
||||
}
|
||||
|
||||
hwc->config |= config;
|
||||
@@ -399,39 +442,41 @@ int x86_pmu_hw_config(struct perf_event *event)
|
||||
|
||||
if (event->attr.precise_ip > precise)
|
||||
return -EOPNOTSUPP;
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
if (has_branch_stack(event)) {
|
||||
if (!precise_br_compat(event))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* branch_sample_type is compatible */
|
||||
/* 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;
|
||||
} 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_user)
|
||||
*br_type |= PERF_SAMPLE_BRANCH_USER;
|
||||
|
||||
if (!event->attr.exclude_kernel)
|
||||
*br_type |= PERF_SAMPLE_BRANCH_KERNEL;
|
||||
}
|
||||
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)
|
||||
@@ -449,6 +494,12 @@ int x86_pmu_hw_config(struct perf_event *event)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -728,14 +779,17 @@ 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, num = 0;
|
||||
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++) {
|
||||
hwc = &cpuc->event_list[i]->hw;
|
||||
c = x86_pmu.get_event_constraints(cpuc, cpuc->event_list[i]);
|
||||
c = x86_pmu.get_event_constraints(cpuc, i, cpuc->event_list[i]);
|
||||
hwc->constraint = c;
|
||||
|
||||
wmin = min(wmin, c->weight);
|
||||
@@ -768,24 +822,30 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
|
||||
|
||||
/* slow path */
|
||||
if (i != n)
|
||||
num = perf_assign_events(cpuc->event_list, n, wmin,
|
||||
wmax, assign);
|
||||
unsched = perf_assign_events(cpuc->event_list, n, wmin,
|
||||
wmax, assign);
|
||||
|
||||
/*
|
||||
* Mark the event as committed, so we do not put_constraint()
|
||||
* in case new events are added and fail scheduling.
|
||||
* 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 (!num && assign) {
|
||||
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, e, assign[i]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* scheduling failed or is just a simulation,
|
||||
* free resources if necessary
|
||||
*/
|
||||
if (!assign || num) {
|
||||
|
||||
if (!assign || unsched) {
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
e = cpuc->event_list[i];
|
||||
/*
|
||||
@@ -795,11 +855,18 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
|
||||
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);
|
||||
}
|
||||
}
|
||||
return num ? -EINVAL : 0;
|
||||
|
||||
if (x86_pmu.stop_scheduling)
|
||||
x86_pmu.stop_scheduling(cpuc);
|
||||
|
||||
return unsched ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -986,6 +1053,9 @@ int x86_perf_event_set_period(struct perf_event *event)
|
||||
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;
|
||||
|
||||
/*
|
||||
@@ -1033,7 +1103,6 @@ static int x86_pmu_add(struct perf_event *event, int flags)
|
||||
|
||||
hwc = &event->hw;
|
||||
|
||||
perf_pmu_disable(event->pmu);
|
||||
n0 = cpuc->n_events;
|
||||
ret = n = collect_events(cpuc, event, false);
|
||||
if (ret < 0)
|
||||
@@ -1071,7 +1140,6 @@ done_collect:
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
perf_pmu_enable(event->pmu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1103,7 +1171,7 @@ static void x86_pmu_start(struct perf_event *event, int flags)
|
||||
void perf_event_print_debug(void)
|
||||
{
|
||||
u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left, fixed;
|
||||
u64 pebs;
|
||||
u64 pebs, debugctl;
|
||||
struct cpu_hw_events *cpuc;
|
||||
unsigned long flags;
|
||||
int cpu, idx;
|
||||
@@ -1121,14 +1189,20 @@ void perf_event_print_debug(void)
|
||||
rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status);
|
||||
rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow);
|
||||
rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed);
|
||||
rdmsrl(MSR_IA32_PEBS_ENABLE, pebs);
|
||||
|
||||
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);
|
||||
pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs);
|
||||
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);
|
||||
|
||||
@@ -1321,11 +1395,12 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
|
||||
{
|
||||
unsigned int cpu = (long)hcpu;
|
||||
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
|
||||
int ret = NOTIFY_OK;
|
||||
int i, ret = NOTIFY_OK;
|
||||
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_UP_PREPARE:
|
||||
cpuc->kfree_on_online = NULL;
|
||||
for (i = 0 ; i < X86_PERF_KFREE_MAX; i++)
|
||||
cpuc->kfree_on_online[i] = NULL;
|
||||
if (x86_pmu.cpu_prepare)
|
||||
ret = x86_pmu.cpu_prepare(cpu);
|
||||
break;
|
||||
@@ -1336,7 +1411,10 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu)
|
||||
break;
|
||||
|
||||
case CPU_ONLINE:
|
||||
kfree(cpuc->kfree_on_online);
|
||||
for (i = 0 ; i < X86_PERF_KFREE_MAX; i++) {
|
||||
kfree(cpuc->kfree_on_online[i]);
|
||||
cpuc->kfree_on_online[i] = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU_DYING:
|
||||
@@ -1712,7 +1790,7 @@ static int validate_event(struct perf_event *event)
|
||||
if (IS_ERR(fake_cpuc))
|
||||
return PTR_ERR(fake_cpuc);
|
||||
|
||||
c = x86_pmu.get_event_constraints(fake_cpuc, event);
|
||||
c = x86_pmu.get_event_constraints(fake_cpuc, -1, event);
|
||||
|
||||
if (!c || !c->weight)
|
||||
ret = -EINVAL;
|
||||
@@ -1914,10 +1992,10 @@ static const struct attribute_group *x86_pmu_attr_groups[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void x86_pmu_flush_branch_stack(void)
|
||||
static void x86_pmu_sched_task(struct perf_event_context *ctx, bool sched_in)
|
||||
{
|
||||
if (x86_pmu.flush_branch_stack)
|
||||
x86_pmu.flush_branch_stack();
|
||||
if (x86_pmu.sched_task)
|
||||
x86_pmu.sched_task(ctx, sched_in);
|
||||
}
|
||||
|
||||
void perf_check_microcode(void)
|
||||
@@ -1949,7 +2027,8 @@ static struct pmu pmu = {
|
||||
.commit_txn = x86_pmu_commit_txn,
|
||||
|
||||
.event_idx = x86_pmu_event_idx,
|
||||
.flush_branch_stack = x86_pmu_flush_branch_stack,
|
||||
.sched_task = x86_pmu_sched_task,
|
||||
.task_ctx_size = sizeof(struct x86_perf_task_context),
|
||||
};
|
||||
|
||||
void arch_perf_update_userpage(struct perf_event *event,
|
||||
@@ -1968,13 +2047,23 @@ void arch_perf_update_userpage(struct perf_event *event,
|
||||
|
||||
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;
|
||||
|
||||
userpg->cap_user_time_zero = 1;
|
||||
userpg->time_zero = data->cyc2ns_offset;
|
||||
/*
|
||||
* cap_user_time_zero doesn't make sense when we're using a different
|
||||
* time base for the records.
|
||||
*/
|
||||
if (event->clock == &local_clock) {
|
||||
userpg->cap_user_time_zero = 1;
|
||||
userpg->time_zero = data->cyc2ns_offset;
|
||||
}
|
||||
|
||||
cyc2ns_read_end(data);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,8 @@ struct event_constraint {
|
||||
#define PERF_X86_EVENT_COMMITTED 0x8 /* event passed commit_txn */
|
||||
#define PERF_X86_EVENT_PEBS_LD_HSW 0x10 /* haswell style datala, load */
|
||||
#define PERF_X86_EVENT_PEBS_NA_HSW 0x20 /* haswell style datala, unknown */
|
||||
#define PERF_X86_EVENT_EXCL 0x40 /* HT exclusivity on counter */
|
||||
#define PERF_X86_EVENT_DYNAMIC 0x80 /* dynamic alloc'd constraint */
|
||||
#define PERF_X86_EVENT_RDPMC_ALLOWED 0x40 /* grant rdpmc permission */
|
||||
|
||||
|
||||
@@ -123,8 +125,37 @@ struct intel_shared_regs {
|
||||
unsigned core_id; /* per-core: core id */
|
||||
};
|
||||
|
||||
enum intel_excl_state_type {
|
||||
INTEL_EXCL_UNUSED = 0, /* counter is unused */
|
||||
INTEL_EXCL_SHARED = 1, /* counter can be used by both threads */
|
||||
INTEL_EXCL_EXCLUSIVE = 2, /* counter can be used by one thread only */
|
||||
};
|
||||
|
||||
struct intel_excl_states {
|
||||
enum intel_excl_state_type init_state[X86_PMC_IDX_MAX];
|
||||
enum intel_excl_state_type state[X86_PMC_IDX_MAX];
|
||||
int num_alloc_cntrs;/* #counters allocated */
|
||||
int max_alloc_cntrs;/* max #counters allowed */
|
||||
bool sched_started; /* true if scheduling has started */
|
||||
};
|
||||
|
||||
struct intel_excl_cntrs {
|
||||
raw_spinlock_t lock;
|
||||
|
||||
struct intel_excl_states states[2];
|
||||
|
||||
int refcnt; /* per-core: #HT threads */
|
||||
unsigned core_id; /* per-core: core id */
|
||||
};
|
||||
|
||||
#define MAX_LBR_ENTRIES 16
|
||||
|
||||
enum {
|
||||
X86_PERF_KFREE_SHARED = 0,
|
||||
X86_PERF_KFREE_EXCL = 1,
|
||||
X86_PERF_KFREE_MAX
|
||||
};
|
||||
|
||||
struct cpu_hw_events {
|
||||
/*
|
||||
* Generic x86 PMC bits
|
||||
@@ -179,6 +210,12 @@ struct cpu_hw_events {
|
||||
* used on Intel NHM/WSM/SNB
|
||||
*/
|
||||
struct intel_shared_regs *shared_regs;
|
||||
/*
|
||||
* manage exclusive counter access between hyperthread
|
||||
*/
|
||||
struct event_constraint *constraint_list; /* in enable order */
|
||||
struct intel_excl_cntrs *excl_cntrs;
|
||||
int excl_thread_id; /* 0 or 1 */
|
||||
|
||||
/*
|
||||
* AMD specific bits
|
||||
@@ -187,7 +224,7 @@ struct cpu_hw_events {
|
||||
/* Inverted mask of bits to clear in the perf_ctr ctrl registers */
|
||||
u64 perf_ctr_virt_mask;
|
||||
|
||||
void *kfree_on_online;
|
||||
void *kfree_on_online[X86_PERF_KFREE_MAX];
|
||||
};
|
||||
|
||||
#define __EVENT_CONSTRAINT(c, n, m, w, o, f) {\
|
||||
@@ -202,6 +239,10 @@ struct cpu_hw_events {
|
||||
#define EVENT_CONSTRAINT(c, n, m) \
|
||||
__EVENT_CONSTRAINT(c, n, m, HWEIGHT(n), 0, 0)
|
||||
|
||||
#define INTEL_EXCLEVT_CONSTRAINT(c, n) \
|
||||
__EVENT_CONSTRAINT(c, n, ARCH_PERFMON_EVENTSEL_EVENT, HWEIGHT(n),\
|
||||
0, PERF_X86_EVENT_EXCL)
|
||||
|
||||
/*
|
||||
* The overlap flag marks event constraints with overlapping counter
|
||||
* masks. This is the case if the counter mask of such an event is not
|
||||
@@ -259,6 +300,10 @@ struct cpu_hw_events {
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT(c, n) \
|
||||
EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS)
|
||||
|
||||
#define INTEL_EXCLUEVT_CONSTRAINT(c, n) \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_EXCL)
|
||||
|
||||
#define INTEL_PLD_CONSTRAINT(c, n) \
|
||||
__EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LDLAT)
|
||||
@@ -283,22 +328,40 @@ struct cpu_hw_events {
|
||||
|
||||
/* Check flags and event code, and set the HSW load flag */
|
||||
#define INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
ARCH_PERFMON_EVENTSEL_EVENT|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LD_HSW)
|
||||
|
||||
#define INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_XLD(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
ARCH_PERFMON_EVENTSEL_EVENT|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, \
|
||||
PERF_X86_EVENT_PEBS_LD_HSW|PERF_X86_EVENT_EXCL)
|
||||
|
||||
/* Check flags and event code/umask, and set the HSW store flag */
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_ST_HSW)
|
||||
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XST(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, \
|
||||
PERF_X86_EVENT_PEBS_ST_HSW|PERF_X86_EVENT_EXCL)
|
||||
|
||||
/* Check flags and event code/umask, and set the HSW load flag */
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LD_HSW)
|
||||
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \
|
||||
HWEIGHT(n), 0, \
|
||||
PERF_X86_EVENT_PEBS_LD_HSW|PERF_X86_EVENT_EXCL)
|
||||
|
||||
/* Check flags and event code/umask, and set the HSW N/A flag */
|
||||
#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(code, n) \
|
||||
__EVENT_CONSTRAINT(code, n, \
|
||||
@@ -408,6 +471,13 @@ union x86_pmu_config {
|
||||
|
||||
#define X86_CONFIG(args...) ((union x86_pmu_config){.bits = {args}}).value
|
||||
|
||||
enum {
|
||||
x86_lbr_exclusive_lbr,
|
||||
x86_lbr_exclusive_bts,
|
||||
x86_lbr_exclusive_pt,
|
||||
x86_lbr_exclusive_max,
|
||||
};
|
||||
|
||||
/*
|
||||
* struct x86_pmu - generic x86 pmu
|
||||
*/
|
||||
@@ -443,14 +513,25 @@ struct x86_pmu {
|
||||
u64 max_period;
|
||||
struct event_constraint *
|
||||
(*get_event_constraints)(struct cpu_hw_events *cpuc,
|
||||
int idx,
|
||||
struct perf_event *event);
|
||||
|
||||
void (*put_event_constraints)(struct cpu_hw_events *cpuc,
|
||||
struct perf_event *event);
|
||||
|
||||
void (*commit_scheduling)(struct cpu_hw_events *cpuc,
|
||||
struct perf_event *event,
|
||||
int cntr);
|
||||
|
||||
void (*start_scheduling)(struct cpu_hw_events *cpuc);
|
||||
|
||||
void (*stop_scheduling)(struct cpu_hw_events *cpuc);
|
||||
|
||||
struct event_constraint *event_constraints;
|
||||
struct x86_pmu_quirk *quirks;
|
||||
int perfctr_second_write;
|
||||
bool late_ack;
|
||||
unsigned (*limit_period)(struct perf_event *event, unsigned l);
|
||||
|
||||
/*
|
||||
* sysfs attrs
|
||||
@@ -472,7 +553,8 @@ struct x86_pmu {
|
||||
void (*cpu_dead)(int cpu);
|
||||
|
||||
void (*check_microcode)(void);
|
||||
void (*flush_branch_stack)(void);
|
||||
void (*sched_task)(struct perf_event_context *ctx,
|
||||
bool sched_in);
|
||||
|
||||
/*
|
||||
* Intel Arch Perfmon v2+
|
||||
@@ -503,11 +585,16 @@ struct x86_pmu {
|
||||
const int *lbr_sel_map; /* lbr_select mappings */
|
||||
bool lbr_double_abort; /* duplicated lbr aborts */
|
||||
|
||||
/*
|
||||
* Intel PT/LBR/BTS are exclusive
|
||||
*/
|
||||
atomic_t lbr_exclusive[x86_lbr_exclusive_max];
|
||||
|
||||
/*
|
||||
* Extra registers for events
|
||||
*/
|
||||
struct extra_reg *extra_regs;
|
||||
unsigned int er_flags;
|
||||
unsigned int flags;
|
||||
|
||||
/*
|
||||
* Intel host/guest support (KVM)
|
||||
@@ -515,6 +602,13 @@ struct x86_pmu {
|
||||
struct perf_guest_switch_msr *(*guest_get_msrs)(int *nr);
|
||||
};
|
||||
|
||||
struct x86_perf_task_context {
|
||||
u64 lbr_from[MAX_LBR_ENTRIES];
|
||||
u64 lbr_to[MAX_LBR_ENTRIES];
|
||||
int lbr_callstack_users;
|
||||
int lbr_stack_state;
|
||||
};
|
||||
|
||||
#define x86_add_quirk(func_) \
|
||||
do { \
|
||||
static struct x86_pmu_quirk __quirk __initdata = { \
|
||||
@@ -524,8 +618,13 @@ do { \
|
||||
x86_pmu.quirks = &__quirk; \
|
||||
} while (0)
|
||||
|
||||
#define ERF_NO_HT_SHARING 1
|
||||
#define ERF_HAS_RSP_1 2
|
||||
/*
|
||||
* x86_pmu flags
|
||||
*/
|
||||
#define PMU_FL_NO_HT_SHARING 0x1 /* no hyper-threading resource sharing */
|
||||
#define PMU_FL_HAS_RSP_1 0x2 /* has 2 equivalent offcore_rsp regs */
|
||||
#define PMU_FL_EXCL_CNTRS 0x4 /* has exclusive counter requirements */
|
||||
#define PMU_FL_EXCL_ENABLED 0x8 /* exclusive counter active */
|
||||
|
||||
#define EVENT_VAR(_id) event_attr_##_id
|
||||
#define EVENT_PTR(_id) &event_attr_##_id.attr.attr
|
||||
@@ -546,6 +645,12 @@ static struct perf_pmu_events_attr event_attr_##v = { \
|
||||
|
||||
extern struct x86_pmu x86_pmu __read_mostly;
|
||||
|
||||
static inline bool x86_pmu_has_lbr_callstack(void)
|
||||
{
|
||||
return x86_pmu.lbr_sel_map &&
|
||||
x86_pmu.lbr_sel_map[PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT] > 0;
|
||||
}
|
||||
|
||||
DECLARE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
|
||||
|
||||
int x86_perf_event_set_period(struct perf_event *event);
|
||||
@@ -588,6 +693,12 @@ static inline int x86_pmu_rdpmc_index(int index)
|
||||
return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index;
|
||||
}
|
||||
|
||||
int x86_add_exclusive(unsigned int what);
|
||||
|
||||
void x86_del_exclusive(unsigned int what);
|
||||
|
||||
void hw_perf_lbr_event_destroy(struct perf_event *event);
|
||||
|
||||
int x86_setup_perfctr(struct perf_event *event);
|
||||
|
||||
int x86_pmu_hw_config(struct perf_event *event);
|
||||
@@ -674,10 +785,34 @@ static inline int amd_pmu_init(void)
|
||||
|
||||
#ifdef CONFIG_CPU_SUP_INTEL
|
||||
|
||||
static inline bool intel_pmu_needs_lbr_smpl(struct perf_event *event)
|
||||
{
|
||||
/* user explicitly requested branch sampling */
|
||||
if (has_branch_stack(event))
|
||||
return true;
|
||||
|
||||
/* implicit branch sampling to correct PEBS skid */
|
||||
if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1 &&
|
||||
x86_pmu.intel_cap.pebs_format < 2)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool intel_pmu_has_bts(struct perf_event *event)
|
||||
{
|
||||
if (event->attr.config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS &&
|
||||
!event->attr.freq && event->hw.sample_period == 1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int intel_pmu_save_and_restart(struct perf_event *event);
|
||||
|
||||
struct event_constraint *
|
||||
x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event);
|
||||
x86_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
|
||||
struct perf_event *event);
|
||||
|
||||
struct intel_shared_regs *allocate_shared_regs(int cpu);
|
||||
|
||||
@@ -727,13 +862,15 @@ void intel_pmu_pebs_disable_all(void);
|
||||
|
||||
void intel_ds_init(void);
|
||||
|
||||
void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in);
|
||||
|
||||
void intel_pmu_lbr_reset(void);
|
||||
|
||||
void intel_pmu_lbr_enable(struct perf_event *event);
|
||||
|
||||
void intel_pmu_lbr_disable(struct perf_event *event);
|
||||
|
||||
void intel_pmu_lbr_enable_all(void);
|
||||
void intel_pmu_lbr_enable_all(bool pmi);
|
||||
|
||||
void intel_pmu_lbr_disable_all(void);
|
||||
|
||||
@@ -747,8 +884,18 @@ void intel_pmu_lbr_init_atom(void);
|
||||
|
||||
void intel_pmu_lbr_init_snb(void);
|
||||
|
||||
void intel_pmu_lbr_init_hsw(void);
|
||||
|
||||
int intel_pmu_setup_lbr_filter(struct perf_event *event);
|
||||
|
||||
void intel_pt_interrupt(void);
|
||||
|
||||
int intel_bts_interrupt(void);
|
||||
|
||||
void intel_bts_enable_local(void);
|
||||
|
||||
void intel_bts_disable_local(void);
|
||||
|
||||
int p4_pmu_init(void);
|
||||
|
||||
int p6_pmu_init(void);
|
||||
@@ -758,6 +905,10 @@ int knc_pmu_init(void);
|
||||
ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr,
|
||||
char *page);
|
||||
|
||||
static inline int is_ht_workaround_enabled(void)
|
||||
{
|
||||
return !!(x86_pmu.flags & PMU_FL_EXCL_ENABLED);
|
||||
}
|
||||
#else /* CONFIG_CPU_SUP_INTEL */
|
||||
|
||||
static inline void reserve_ds_buffers(void)
|
||||
|
||||
@@ -382,6 +382,7 @@ static int amd_pmu_cpu_prepare(int cpu)
|
||||
static void amd_pmu_cpu_starting(int cpu)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
|
||||
void **onln = &cpuc->kfree_on_online[X86_PERF_KFREE_SHARED];
|
||||
struct amd_nb *nb;
|
||||
int i, nb_id;
|
||||
|
||||
@@ -399,7 +400,7 @@ static void amd_pmu_cpu_starting(int cpu)
|
||||
continue;
|
||||
|
||||
if (nb->nb_id == nb_id) {
|
||||
cpuc->kfree_on_online = cpuc->amd_nb;
|
||||
*onln = cpuc->amd_nb;
|
||||
cpuc->amd_nb = nb;
|
||||
break;
|
||||
}
|
||||
@@ -429,7 +430,8 @@ static void amd_pmu_cpu_dead(int cpu)
|
||||
}
|
||||
|
||||
static struct event_constraint *
|
||||
amd_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event)
|
||||
amd_get_event_constraints(struct cpu_hw_events *cpuc, int idx,
|
||||
struct perf_event *event)
|
||||
{
|
||||
/*
|
||||
* if not NB event or no NB, then no constraints
|
||||
@@ -537,7 +539,8 @@ static struct event_constraint amd_f15_PMC50 = EVENT_CONSTRAINT(0, 0x3F, 0);
|
||||
static struct event_constraint amd_f15_PMC53 = EVENT_CONSTRAINT(0, 0x38, 0);
|
||||
|
||||
static struct event_constraint *
|
||||
amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, struct perf_event *event)
|
||||
amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, int idx,
|
||||
struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
unsigned int event_code = amd_get_event_code(hwc);
|
||||
|
||||
@@ -796,7 +796,7 @@ static int setup_ibs_ctl(int ibs_eilvt_off)
|
||||
* the IBS interrupt vector is handled by perf_ibs_cpu_notifier that
|
||||
* is using the new offset.
|
||||
*/
|
||||
static int force_ibs_eilvt_setup(void)
|
||||
static void force_ibs_eilvt_setup(void)
|
||||
{
|
||||
int offset;
|
||||
int ret;
|
||||
@@ -811,26 +811,24 @@ static int force_ibs_eilvt_setup(void)
|
||||
|
||||
if (offset == APIC_EILVT_NR_MAX) {
|
||||
printk(KERN_DEBUG "No EILVT entry available\n");
|
||||
return -EBUSY;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = setup_ibs_ctl(offset);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (!ibs_eilvt_valid()) {
|
||||
ret = -EFAULT;
|
||||
if (!ibs_eilvt_valid())
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_info("IBS: LVT offset %d assigned\n", offset);
|
||||
|
||||
return 0;
|
||||
return;
|
||||
out:
|
||||
preempt_disable();
|
||||
put_eilvt(offset);
|
||||
preempt_enable();
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
static void ibs_eilvt_setup(void)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -461,7 +461,8 @@ void intel_pmu_enable_bts(u64 config)
|
||||
|
||||
debugctlmsr |= DEBUGCTLMSR_TR;
|
||||
debugctlmsr |= DEBUGCTLMSR_BTS;
|
||||
debugctlmsr |= DEBUGCTLMSR_BTINT;
|
||||
if (config & ARCH_PERFMON_EVENTSEL_INT)
|
||||
debugctlmsr |= DEBUGCTLMSR_BTINT;
|
||||
|
||||
if (!(config & ARCH_PERFMON_EVENTSEL_OS))
|
||||
debugctlmsr |= DEBUGCTLMSR_BTS_OFF_OS;
|
||||
@@ -611,6 +612,10 @@ struct event_constraint intel_snb_pebs_event_constraints[] = {
|
||||
INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */
|
||||
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
|
||||
INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */
|
||||
INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
|
||||
INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
|
||||
INTEL_EXCLEVT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_ALL_EVENT_CONSTRAINT(0, 0xf),
|
||||
EVENT_CONSTRAINT_END
|
||||
@@ -622,6 +627,10 @@ struct event_constraint intel_ivb_pebs_event_constraints[] = {
|
||||
INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */
|
||||
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
|
||||
INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */
|
||||
INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
|
||||
INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
|
||||
INTEL_EXCLEVT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_ALL_EVENT_CONSTRAINT(0, 0xf),
|
||||
EVENT_CONSTRAINT_END
|
||||
@@ -633,16 +642,16 @@ struct event_constraint intel_hsw_pebs_event_constraints[] = {
|
||||
/* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf),
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x21d0, 0xf), /* MEM_UOPS_RETIRED.LOCK_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x41d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x81d0, 0xf), /* MEM_UOPS_RETIRED.ALL_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x12d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x42d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x82d0, 0xf), /* MEM_UOPS_RETIRED.ALL_STORES */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd2, 0xf), /* MEM_LOAD_UOPS_L3_HIT_RETIRED.* */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd3, 0xf), /* MEM_LOAD_UOPS_L3_MISS_RETIRED.* */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x11d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x21d0, 0xf), /* MEM_UOPS_RETIRED.LOCK_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x41d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x81d0, 0xf), /* MEM_UOPS_RETIRED.ALL_LOADS */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XST(0x12d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XST(0x42d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_STORES */
|
||||
INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XST(0x82d0, 0xf), /* MEM_UOPS_RETIRED.ALL_STORES */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_XLD(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_XLD(0xd2, 0xf), /* MEM_LOAD_UOPS_L3_HIT_RETIRED.* */
|
||||
INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_XLD(0xd3, 0xf), /* MEM_LOAD_UOPS_L3_MISS_RETIRED.* */
|
||||
/* Allow all events as PEBS with no flags */
|
||||
INTEL_ALL_EVENT_CONSTRAINT(0, 0xf),
|
||||
EVENT_CONSTRAINT_END
|
||||
|
||||
@@ -39,6 +39,7 @@ static enum {
|
||||
#define LBR_IND_JMP_BIT 6 /* do not capture indirect jumps */
|
||||
#define LBR_REL_JMP_BIT 7 /* do not capture relative jumps */
|
||||
#define LBR_FAR_BIT 8 /* do not capture far branches */
|
||||
#define LBR_CALL_STACK_BIT 9 /* enable call stack */
|
||||
|
||||
#define LBR_KERNEL (1 << LBR_KERNEL_BIT)
|
||||
#define LBR_USER (1 << LBR_USER_BIT)
|
||||
@@ -49,6 +50,7 @@ static enum {
|
||||
#define LBR_REL_JMP (1 << LBR_REL_JMP_BIT)
|
||||
#define LBR_IND_JMP (1 << LBR_IND_JMP_BIT)
|
||||
#define LBR_FAR (1 << LBR_FAR_BIT)
|
||||
#define LBR_CALL_STACK (1 << LBR_CALL_STACK_BIT)
|
||||
|
||||
#define LBR_PLM (LBR_KERNEL | LBR_USER)
|
||||
|
||||
@@ -69,33 +71,31 @@ static enum {
|
||||
#define LBR_FROM_FLAG_IN_TX (1ULL << 62)
|
||||
#define LBR_FROM_FLAG_ABORT (1ULL << 61)
|
||||
|
||||
#define for_each_branch_sample_type(x) \
|
||||
for ((x) = PERF_SAMPLE_BRANCH_USER; \
|
||||
(x) < PERF_SAMPLE_BRANCH_MAX; (x) <<= 1)
|
||||
|
||||
/*
|
||||
* x86control flow change classification
|
||||
* x86control flow changes include branches, interrupts, traps, faults
|
||||
*/
|
||||
enum {
|
||||
X86_BR_NONE = 0, /* unknown */
|
||||
X86_BR_NONE = 0, /* unknown */
|
||||
|
||||
X86_BR_USER = 1 << 0, /* branch target is user */
|
||||
X86_BR_KERNEL = 1 << 1, /* branch target is kernel */
|
||||
X86_BR_USER = 1 << 0, /* branch target is user */
|
||||
X86_BR_KERNEL = 1 << 1, /* branch target is kernel */
|
||||
|
||||
X86_BR_CALL = 1 << 2, /* call */
|
||||
X86_BR_RET = 1 << 3, /* return */
|
||||
X86_BR_SYSCALL = 1 << 4, /* syscall */
|
||||
X86_BR_SYSRET = 1 << 5, /* syscall return */
|
||||
X86_BR_INT = 1 << 6, /* sw interrupt */
|
||||
X86_BR_IRET = 1 << 7, /* return from interrupt */
|
||||
X86_BR_JCC = 1 << 8, /* conditional */
|
||||
X86_BR_JMP = 1 << 9, /* jump */
|
||||
X86_BR_IRQ = 1 << 10,/* hw interrupt or trap or fault */
|
||||
X86_BR_IND_CALL = 1 << 11,/* indirect calls */
|
||||
X86_BR_ABORT = 1 << 12,/* transaction abort */
|
||||
X86_BR_IN_TX = 1 << 13,/* in transaction */
|
||||
X86_BR_NO_TX = 1 << 14,/* not in transaction */
|
||||
X86_BR_CALL = 1 << 2, /* call */
|
||||
X86_BR_RET = 1 << 3, /* return */
|
||||
X86_BR_SYSCALL = 1 << 4, /* syscall */
|
||||
X86_BR_SYSRET = 1 << 5, /* syscall return */
|
||||
X86_BR_INT = 1 << 6, /* sw interrupt */
|
||||
X86_BR_IRET = 1 << 7, /* return from interrupt */
|
||||
X86_BR_JCC = 1 << 8, /* conditional */
|
||||
X86_BR_JMP = 1 << 9, /* jump */
|
||||
X86_BR_IRQ = 1 << 10,/* hw interrupt or trap or fault */
|
||||
X86_BR_IND_CALL = 1 << 11,/* indirect calls */
|
||||
X86_BR_ABORT = 1 << 12,/* transaction abort */
|
||||
X86_BR_IN_TX = 1 << 13,/* in transaction */
|
||||
X86_BR_NO_TX = 1 << 14,/* not in transaction */
|
||||
X86_BR_ZERO_CALL = 1 << 15,/* zero length call */
|
||||
X86_BR_CALL_STACK = 1 << 16,/* call stack */
|
||||
};
|
||||
|
||||
#define X86_BR_PLM (X86_BR_USER | X86_BR_KERNEL)
|
||||
@@ -112,13 +112,15 @@ enum {
|
||||
X86_BR_JMP |\
|
||||
X86_BR_IRQ |\
|
||||
X86_BR_ABORT |\
|
||||
X86_BR_IND_CALL)
|
||||
X86_BR_IND_CALL |\
|
||||
X86_BR_ZERO_CALL)
|
||||
|
||||
#define X86_BR_ALL (X86_BR_PLM | X86_BR_ANY)
|
||||
|
||||
#define X86_BR_ANY_CALL \
|
||||
(X86_BR_CALL |\
|
||||
X86_BR_IND_CALL |\
|
||||
X86_BR_ZERO_CALL |\
|
||||
X86_BR_SYSCALL |\
|
||||
X86_BR_IRQ |\
|
||||
X86_BR_INT)
|
||||
@@ -130,17 +132,32 @@ static void intel_pmu_lbr_filter(struct cpu_hw_events *cpuc);
|
||||
* otherwise it becomes near impossible to get a reliable stack.
|
||||
*/
|
||||
|
||||
static void __intel_pmu_lbr_enable(void)
|
||||
static void __intel_pmu_lbr_enable(bool pmi)
|
||||
{
|
||||
u64 debugctl;
|
||||
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||
u64 debugctl, lbr_select = 0, orig_debugctl;
|
||||
|
||||
if (cpuc->lbr_sel)
|
||||
wrmsrl(MSR_LBR_SELECT, cpuc->lbr_sel->config);
|
||||
/*
|
||||
* No need to reprogram LBR_SELECT in a PMI, as it
|
||||
* did not change.
|
||||
*/
|
||||
if (cpuc->lbr_sel && !pmi) {
|
||||
lbr_select = cpuc->lbr_sel->config;
|
||||
wrmsrl(MSR_LBR_SELECT, lbr_select);
|
||||
}
|
||||
|
||||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||
debugctl |= (DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||
orig_debugctl = debugctl;
|
||||
debugctl |= DEBUGCTLMSR_LBR;
|
||||
/*
|
||||
* LBR callstack does not work well with FREEZE_LBRS_ON_PMI.
|
||||
* If FREEZE_LBRS_ON_PMI is set, PMI near call/return instructions
|
||||
* may cause superfluous increase/decrease of LBR_TOS.
|
||||
*/
|
||||
if (!(lbr_select & LBR_CALL_STACK))
|
||||
debugctl |= DEBUGCTLMSR_FREEZE_LBRS_ON_PMI;
|
||||
if (orig_debugctl != debugctl)
|
||||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
||||
}
|
||||
|
||||
static void __intel_pmu_lbr_disable(void)
|
||||
@@ -181,9 +198,116 @@ void intel_pmu_lbr_reset(void)
|
||||
intel_pmu_lbr_reset_64();
|
||||
}
|
||||
|
||||
/*
|
||||
* TOS = most recently recorded branch
|
||||
*/
|
||||
static inline u64 intel_pmu_lbr_tos(void)
|
||||
{
|
||||
u64 tos;
|
||||
|
||||
rdmsrl(x86_pmu.lbr_tos, tos);
|
||||
return tos;
|
||||
}
|
||||
|
||||
enum {
|
||||
LBR_NONE,
|
||||
LBR_VALID,
|
||||
};
|
||||
|
||||
static void __intel_pmu_lbr_restore(struct x86_perf_task_context *task_ctx)
|
||||
{
|
||||
int i;
|
||||
unsigned lbr_idx, mask;
|
||||
u64 tos;
|
||||
|
||||
if (task_ctx->lbr_callstack_users == 0 ||
|
||||
task_ctx->lbr_stack_state == LBR_NONE) {
|
||||
intel_pmu_lbr_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
mask = x86_pmu.lbr_nr - 1;
|
||||
tos = intel_pmu_lbr_tos();
|
||||
for (i = 0; i < x86_pmu.lbr_nr; i++) {
|
||||
lbr_idx = (tos - i) & mask;
|
||||
wrmsrl(x86_pmu.lbr_from + lbr_idx, task_ctx->lbr_from[i]);
|
||||
wrmsrl(x86_pmu.lbr_to + lbr_idx, task_ctx->lbr_to[i]);
|
||||
}
|
||||
task_ctx->lbr_stack_state = LBR_NONE;
|
||||
}
|
||||
|
||||
static void __intel_pmu_lbr_save(struct x86_perf_task_context *task_ctx)
|
||||
{
|
||||
int i;
|
||||
unsigned lbr_idx, mask;
|
||||
u64 tos;
|
||||
|
||||
if (task_ctx->lbr_callstack_users == 0) {
|
||||
task_ctx->lbr_stack_state = LBR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
mask = x86_pmu.lbr_nr - 1;
|
||||
tos = intel_pmu_lbr_tos();
|
||||
for (i = 0; i < x86_pmu.lbr_nr; i++) {
|
||||
lbr_idx = (tos - i) & mask;
|
||||
rdmsrl(x86_pmu.lbr_from + lbr_idx, task_ctx->lbr_from[i]);
|
||||
rdmsrl(x86_pmu.lbr_to + lbr_idx, task_ctx->lbr_to[i]);
|
||||
}
|
||||
task_ctx->lbr_stack_state = LBR_VALID;
|
||||
}
|
||||
|
||||
void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||
struct x86_perf_task_context *task_ctx;
|
||||
|
||||
if (!x86_pmu.lbr_nr)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If LBR callstack feature is enabled and the stack was saved when
|
||||
* the task was scheduled out, restore the stack. Otherwise flush
|
||||
* the LBR stack.
|
||||
*/
|
||||
task_ctx = ctx ? ctx->task_ctx_data : NULL;
|
||||
if (task_ctx) {
|
||||
if (sched_in) {
|
||||
__intel_pmu_lbr_restore(task_ctx);
|
||||
cpuc->lbr_context = ctx;
|
||||
} else {
|
||||
__intel_pmu_lbr_save(task_ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* When sampling the branck stack in system-wide, it may be
|
||||
* necessary to flush the stack on context switch. This happens
|
||||
* when the branch stack does not tag its entries with the pid
|
||||
* of the current task. Otherwise it becomes impossible to
|
||||
* associate a branch entry with a task. This ambiguity is more
|
||||
* likely to appear when the branch stack supports priv level
|
||||
* filtering and the user sets it to monitor only at the user
|
||||
* level (which could be a useful measurement in system-wide
|
||||
* mode). In that case, the risk is high of having a branch
|
||||
* stack with branch from multiple tasks.
|
||||
*/
|
||||
if (sched_in) {
|
||||
intel_pmu_lbr_reset();
|
||||
cpuc->lbr_context = ctx;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool branch_user_callstack(unsigned br_sel)
|
||||
{
|
||||
return (br_sel & X86_BR_USER) && (br_sel & X86_BR_CALL_STACK);
|
||||
}
|
||||
|
||||
void intel_pmu_lbr_enable(struct perf_event *event)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||
struct x86_perf_task_context *task_ctx;
|
||||
|
||||
if (!x86_pmu.lbr_nr)
|
||||
return;
|
||||
@@ -198,18 +322,33 @@ void intel_pmu_lbr_enable(struct perf_event *event)
|
||||
}
|
||||
cpuc->br_sel = event->hw.branch_reg.reg;
|
||||
|
||||
if (branch_user_callstack(cpuc->br_sel) && event->ctx &&
|
||||
event->ctx->task_ctx_data) {
|
||||
task_ctx = event->ctx->task_ctx_data;
|
||||
task_ctx->lbr_callstack_users++;
|
||||
}
|
||||
|
||||
cpuc->lbr_users++;
|
||||
perf_sched_cb_inc(event->ctx->pmu);
|
||||
}
|
||||
|
||||
void intel_pmu_lbr_disable(struct perf_event *event)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||
struct x86_perf_task_context *task_ctx;
|
||||
|
||||
if (!x86_pmu.lbr_nr)
|
||||
return;
|
||||
|
||||
if (branch_user_callstack(cpuc->br_sel) && event->ctx &&
|
||||
event->ctx->task_ctx_data) {
|
||||
task_ctx = event->ctx->task_ctx_data;
|
||||
task_ctx->lbr_callstack_users--;
|
||||
}
|
||||
|
||||
cpuc->lbr_users--;
|
||||
WARN_ON_ONCE(cpuc->lbr_users < 0);
|
||||
perf_sched_cb_dec(event->ctx->pmu);
|
||||
|
||||
if (cpuc->enabled && !cpuc->lbr_users) {
|
||||
__intel_pmu_lbr_disable();
|
||||
@@ -218,12 +357,12 @@ void intel_pmu_lbr_disable(struct perf_event *event)
|
||||
}
|
||||
}
|
||||
|
||||
void intel_pmu_lbr_enable_all(void)
|
||||
void intel_pmu_lbr_enable_all(bool pmi)
|
||||
{
|
||||
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||
|
||||
if (cpuc->lbr_users)
|
||||
__intel_pmu_lbr_enable();
|
||||
__intel_pmu_lbr_enable(pmi);
|
||||
}
|
||||
|
||||
void intel_pmu_lbr_disable_all(void)
|
||||
@@ -234,18 +373,6 @@ void intel_pmu_lbr_disable_all(void)
|
||||
__intel_pmu_lbr_disable();
|
||||
}
|
||||
|
||||
/*
|
||||
* TOS = most recently recorded branch
|
||||
*/
|
||||
static inline u64 intel_pmu_lbr_tos(void)
|
||||
{
|
||||
u64 tos;
|
||||
|
||||
rdmsrl(x86_pmu.lbr_tos, tos);
|
||||
|
||||
return tos;
|
||||
}
|
||||
|
||||
static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc)
|
||||
{
|
||||
unsigned long mask = x86_pmu.lbr_nr - 1;
|
||||
@@ -350,7 +477,7 @@ void intel_pmu_lbr_read(void)
|
||||
* - in case there is no HW filter
|
||||
* - in case the HW filter has errata or limitations
|
||||
*/
|
||||
static void intel_pmu_setup_sw_lbr_filter(struct perf_event *event)
|
||||
static int intel_pmu_setup_sw_lbr_filter(struct perf_event *event)
|
||||
{
|
||||
u64 br_type = event->attr.branch_sample_type;
|
||||
int mask = 0;
|
||||
@@ -387,11 +514,21 @@ static void intel_pmu_setup_sw_lbr_filter(struct perf_event *event)
|
||||
if (br_type & PERF_SAMPLE_BRANCH_COND)
|
||||
mask |= X86_BR_JCC;
|
||||
|
||||
if (br_type & PERF_SAMPLE_BRANCH_CALL_STACK) {
|
||||
if (!x86_pmu_has_lbr_callstack())
|
||||
return -EOPNOTSUPP;
|
||||
if (mask & ~(X86_BR_USER | X86_BR_KERNEL))
|
||||
return -EINVAL;
|
||||
mask |= X86_BR_CALL | X86_BR_IND_CALL | X86_BR_RET |
|
||||
X86_BR_CALL_STACK;
|
||||
}
|
||||
|
||||
/*
|
||||
* stash actual user request into reg, it may
|
||||
* be used by fixup code for some CPU
|
||||
*/
|
||||
event->hw.branch_reg.reg = mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -403,14 +540,14 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event)
|
||||
{
|
||||
struct hw_perf_event_extra *reg;
|
||||
u64 br_type = event->attr.branch_sample_type;
|
||||
u64 mask = 0, m;
|
||||
u64 v;
|
||||
u64 mask = 0, v;
|
||||
int i;
|
||||
|
||||
for_each_branch_sample_type(m) {
|
||||
if (!(br_type & m))
|
||||
for (i = 0; i < PERF_SAMPLE_BRANCH_MAX_SHIFT; i++) {
|
||||
if (!(br_type & (1ULL << i)))
|
||||
continue;
|
||||
|
||||
v = x86_pmu.lbr_sel_map[m];
|
||||
v = x86_pmu.lbr_sel_map[i];
|
||||
if (v == LBR_NOT_SUPP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -420,8 +557,12 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event)
|
||||
reg = &event->hw.branch_reg;
|
||||
reg->idx = EXTRA_REG_LBR;
|
||||
|
||||
/* LBR_SELECT operates in suppress mode so invert mask */
|
||||
reg->config = ~mask & x86_pmu.lbr_sel_mask;
|
||||
/*
|
||||
* The first 9 bits (LBR_SEL_MASK) in LBR_SELECT operate
|
||||
* in suppress mode. So LBR_SELECT should be set to
|
||||
* (~mask & LBR_SEL_MASK) | (mask & ~LBR_SEL_MASK)
|
||||
*/
|
||||
reg->config = mask ^ x86_pmu.lbr_sel_mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -439,7 +580,9 @@ int intel_pmu_setup_lbr_filter(struct perf_event *event)
|
||||
/*
|
||||
* setup SW LBR filter
|
||||
*/
|
||||
intel_pmu_setup_sw_lbr_filter(event);
|
||||
ret = intel_pmu_setup_sw_lbr_filter(event);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* setup HW LBR filter, if any
|
||||
@@ -568,6 +711,12 @@ static int branch_type(unsigned long from, unsigned long to, int abort)
|
||||
ret = X86_BR_INT;
|
||||
break;
|
||||
case 0xe8: /* call near rel */
|
||||
insn_get_immediate(&insn);
|
||||
if (insn.immediate1.value == 0) {
|
||||
/* zero length call */
|
||||
ret = X86_BR_ZERO_CALL;
|
||||
break;
|
||||
}
|
||||
case 0x9a: /* call far absolute */
|
||||
ret = X86_BR_CALL;
|
||||
break;
|
||||
@@ -678,35 +827,49 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
|
||||
/*
|
||||
* Map interface branch filters onto LBR filters
|
||||
*/
|
||||
static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
|
||||
[PERF_SAMPLE_BRANCH_ANY] = LBR_ANY,
|
||||
[PERF_SAMPLE_BRANCH_USER] = LBR_USER,
|
||||
[PERF_SAMPLE_BRANCH_KERNEL] = LBR_KERNEL,
|
||||
[PERF_SAMPLE_BRANCH_HV] = LBR_IGN,
|
||||
[PERF_SAMPLE_BRANCH_ANY_RETURN] = LBR_RETURN | LBR_REL_JMP
|
||||
| LBR_IND_JMP | LBR_FAR,
|
||||
static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
|
||||
[PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY,
|
||||
[PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER,
|
||||
[PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL,
|
||||
[PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGN,
|
||||
[PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_REL_JMP
|
||||
| LBR_IND_JMP | LBR_FAR,
|
||||
/*
|
||||
* NHM/WSM erratum: must include REL_JMP+IND_JMP to get CALL branches
|
||||
*/
|
||||
[PERF_SAMPLE_BRANCH_ANY_CALL] =
|
||||
[PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] =
|
||||
LBR_REL_CALL | LBR_IND_CALL | LBR_REL_JMP | LBR_IND_JMP | LBR_FAR,
|
||||
/*
|
||||
* NHM/WSM erratum: must include IND_JMP to capture IND_CALL
|
||||
*/
|
||||
[PERF_SAMPLE_BRANCH_IND_CALL] = LBR_IND_CALL | LBR_IND_JMP,
|
||||
[PERF_SAMPLE_BRANCH_COND] = LBR_JCC,
|
||||
[PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL | LBR_IND_JMP,
|
||||
[PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC,
|
||||
};
|
||||
|
||||
static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = {
|
||||
[PERF_SAMPLE_BRANCH_ANY] = LBR_ANY,
|
||||
[PERF_SAMPLE_BRANCH_USER] = LBR_USER,
|
||||
[PERF_SAMPLE_BRANCH_KERNEL] = LBR_KERNEL,
|
||||
[PERF_SAMPLE_BRANCH_HV] = LBR_IGN,
|
||||
[PERF_SAMPLE_BRANCH_ANY_RETURN] = LBR_RETURN | LBR_FAR,
|
||||
[PERF_SAMPLE_BRANCH_ANY_CALL] = LBR_REL_CALL | LBR_IND_CALL
|
||||
| LBR_FAR,
|
||||
[PERF_SAMPLE_BRANCH_IND_CALL] = LBR_IND_CALL,
|
||||
[PERF_SAMPLE_BRANCH_COND] = LBR_JCC,
|
||||
static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
|
||||
[PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY,
|
||||
[PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER,
|
||||
[PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL,
|
||||
[PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGN,
|
||||
[PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_FAR,
|
||||
[PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] = LBR_REL_CALL | LBR_IND_CALL
|
||||
| LBR_FAR,
|
||||
[PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL,
|
||||
[PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC,
|
||||
};
|
||||
|
||||
static const int hsw_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = {
|
||||
[PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY,
|
||||
[PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER,
|
||||
[PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL,
|
||||
[PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGN,
|
||||
[PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_FAR,
|
||||
[PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] = LBR_REL_CALL | LBR_IND_CALL
|
||||
| LBR_FAR,
|
||||
[PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL,
|
||||
[PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC,
|
||||
[PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT] = LBR_REL_CALL | LBR_IND_CALL
|
||||
| LBR_RETURN | LBR_CALL_STACK,
|
||||
};
|
||||
|
||||
/* core */
|
||||
@@ -765,6 +928,20 @@ void __init intel_pmu_lbr_init_snb(void)
|
||||
pr_cont("16-deep LBR, ");
|
||||
}
|
||||
|
||||
/* haswell */
|
||||
void intel_pmu_lbr_init_hsw(void)
|
||||
{
|
||||
x86_pmu.lbr_nr = 16;
|
||||
x86_pmu.lbr_tos = MSR_LBR_TOS;
|
||||
x86_pmu.lbr_from = MSR_LBR_NHM_FROM;
|
||||
x86_pmu.lbr_to = MSR_LBR_NHM_TO;
|
||||
|
||||
x86_pmu.lbr_sel_mask = LBR_SEL_MASK;
|
||||
x86_pmu.lbr_sel_map = hsw_lbr_sel_map;
|
||||
|
||||
pr_cont("16-deep LBR, ");
|
||||
}
|
||||
|
||||
/* atom */
|
||||
void __init intel_pmu_lbr_init_atom(void)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1132,8 +1132,7 @@ static int snbep_pci2phy_map_init(int devid)
|
||||
}
|
||||
}
|
||||
|
||||
if (ubox_dev)
|
||||
pci_dev_put(ubox_dev);
|
||||
pci_dev_put(ubox_dev);
|
||||
|
||||
return err ? pcibios_err_to_errno(err) : 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user