Merge branch 'perf-counters-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/peterz/linux-2.6-perf
* 'perf-counters-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/peterz/linux-2.6-perf: (31 commits) perf_counter tools: Give perf top inherit option perf_counter tools: Fix vmlinux symbol generation breakage perf_counter: Detect debugfs location perf_counter: Add tracepoint support to perf list, perf stat perf symbol: C++ demangling perf: avoid structure size confusion by using a fixed size perf_counter: Fix throttle/unthrottle event logging perf_counter: Improve perf stat and perf record option parsing perf_counter: PERF_SAMPLE_ID and inherited counters perf_counter: Plug more stack leaks perf: Fix stack data leak perf_counter: Remove unused variables perf_counter: Make call graph option consistent perf_counter: Add perf record option to log addresses perf_counter: Log vfork as a fork event perf_counter: Synthesize VDSO mmap event perf_counter: Make sure we dont leak kernel memory to userspace perf_counter tools: Fix index boundary check perf_counter: Fix the tracepoint channel to perfcounters perf_counter, x86: Extend perf_counter Pentium M support ...
Šī revīzija ir iekļauta:
@@ -65,6 +65,52 @@ static DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters) = {
|
||||
.enabled = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
* Not sure about some of these
|
||||
*/
|
||||
static const u64 p6_perfmon_event_map[] =
|
||||
{
|
||||
[PERF_COUNT_HW_CPU_CYCLES] = 0x0079,
|
||||
[PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0,
|
||||
[PERF_COUNT_HW_CACHE_REFERENCES] = 0x0000,
|
||||
[PERF_COUNT_HW_CACHE_MISSES] = 0x0000,
|
||||
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4,
|
||||
[PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5,
|
||||
[PERF_COUNT_HW_BUS_CYCLES] = 0x0062,
|
||||
};
|
||||
|
||||
static u64 p6_pmu_event_map(int event)
|
||||
{
|
||||
return p6_perfmon_event_map[event];
|
||||
}
|
||||
|
||||
/*
|
||||
* Counter setting that is specified not to count anything.
|
||||
* We use this to effectively disable a counter.
|
||||
*
|
||||
* L2_RQSTS with 0 MESI unit mask.
|
||||
*/
|
||||
#define P6_NOP_COUNTER 0x0000002EULL
|
||||
|
||||
static u64 p6_pmu_raw_event(u64 event)
|
||||
{
|
||||
#define P6_EVNTSEL_EVENT_MASK 0x000000FFULL
|
||||
#define P6_EVNTSEL_UNIT_MASK 0x0000FF00ULL
|
||||
#define P6_EVNTSEL_EDGE_MASK 0x00040000ULL
|
||||
#define P6_EVNTSEL_INV_MASK 0x00800000ULL
|
||||
#define P6_EVNTSEL_COUNTER_MASK 0xFF000000ULL
|
||||
|
||||
#define P6_EVNTSEL_MASK \
|
||||
(P6_EVNTSEL_EVENT_MASK | \
|
||||
P6_EVNTSEL_UNIT_MASK | \
|
||||
P6_EVNTSEL_EDGE_MASK | \
|
||||
P6_EVNTSEL_INV_MASK | \
|
||||
P6_EVNTSEL_COUNTER_MASK)
|
||||
|
||||
return event & P6_EVNTSEL_MASK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Intel PerfMon v3. Used on Core2 and later.
|
||||
*/
|
||||
@@ -666,6 +712,7 @@ static int __hw_perf_counter_init(struct perf_counter *counter)
|
||||
{
|
||||
struct perf_counter_attr *attr = &counter->attr;
|
||||
struct hw_perf_counter *hwc = &counter->hw;
|
||||
u64 config;
|
||||
int err;
|
||||
|
||||
if (!x86_pmu_initialized())
|
||||
@@ -718,14 +765,40 @@ static int __hw_perf_counter_init(struct perf_counter *counter)
|
||||
|
||||
if (attr->config >= x86_pmu.max_events)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The generic map:
|
||||
*/
|
||||
hwc->config |= x86_pmu.event_map(attr->config);
|
||||
config = x86_pmu.event_map(attr->config);
|
||||
|
||||
if (config == 0)
|
||||
return -ENOENT;
|
||||
|
||||
if (config == -1LL)
|
||||
return -EINVAL;
|
||||
|
||||
hwc->config |= config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void p6_pmu_disable_all(void)
|
||||
{
|
||||
struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters);
|
||||
u64 val;
|
||||
|
||||
if (!cpuc->enabled)
|
||||
return;
|
||||
|
||||
cpuc->enabled = 0;
|
||||
barrier();
|
||||
|
||||
/* p6 only has one enable register */
|
||||
rdmsrl(MSR_P6_EVNTSEL0, val);
|
||||
val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE;
|
||||
wrmsrl(MSR_P6_EVNTSEL0, val);
|
||||
}
|
||||
|
||||
static void intel_pmu_disable_all(void)
|
||||
{
|
||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0);
|
||||
@@ -767,6 +840,23 @@ void hw_perf_disable(void)
|
||||
return x86_pmu.disable_all();
|
||||
}
|
||||
|
||||
static void p6_pmu_enable_all(void)
|
||||
{
|
||||
struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters);
|
||||
unsigned long val;
|
||||
|
||||
if (cpuc->enabled)
|
||||
return;
|
||||
|
||||
cpuc->enabled = 1;
|
||||
barrier();
|
||||
|
||||
/* p6 only has one enable register */
|
||||
rdmsrl(MSR_P6_EVNTSEL0, val);
|
||||
val |= ARCH_PERFMON_EVENTSEL0_ENABLE;
|
||||
wrmsrl(MSR_P6_EVNTSEL0, val);
|
||||
}
|
||||
|
||||
static void intel_pmu_enable_all(void)
|
||||
{
|
||||
wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl);
|
||||
@@ -784,13 +874,13 @@ static void amd_pmu_enable_all(void)
|
||||
barrier();
|
||||
|
||||
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
||||
struct perf_counter *counter = cpuc->counters[idx];
|
||||
u64 val;
|
||||
|
||||
if (!test_bit(idx, cpuc->active_mask))
|
||||
continue;
|
||||
rdmsrl(MSR_K7_EVNTSEL0 + idx, val);
|
||||
if (val & ARCH_PERFMON_EVENTSEL0_ENABLE)
|
||||
continue;
|
||||
|
||||
val = counter->hw.config;
|
||||
val |= ARCH_PERFMON_EVENTSEL0_ENABLE;
|
||||
wrmsrl(MSR_K7_EVNTSEL0 + idx, val);
|
||||
}
|
||||
@@ -819,16 +909,13 @@ static inline void intel_pmu_ack_status(u64 ack)
|
||||
|
||||
static inline void x86_pmu_enable_counter(struct hw_perf_counter *hwc, int idx)
|
||||
{
|
||||
int err;
|
||||
err = checking_wrmsrl(hwc->config_base + idx,
|
||||
(void)checking_wrmsrl(hwc->config_base + idx,
|
||||
hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE);
|
||||
}
|
||||
|
||||
static inline void x86_pmu_disable_counter(struct hw_perf_counter *hwc, int idx)
|
||||
{
|
||||
int err;
|
||||
err = checking_wrmsrl(hwc->config_base + idx,
|
||||
hwc->config);
|
||||
(void)checking_wrmsrl(hwc->config_base + idx, hwc->config);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -836,13 +923,24 @@ intel_pmu_disable_fixed(struct hw_perf_counter *hwc, int __idx)
|
||||
{
|
||||
int idx = __idx - X86_PMC_IDX_FIXED;
|
||||
u64 ctrl_val, mask;
|
||||
int err;
|
||||
|
||||
mask = 0xfULL << (idx * 4);
|
||||
|
||||
rdmsrl(hwc->config_base, ctrl_val);
|
||||
ctrl_val &= ~mask;
|
||||
err = checking_wrmsrl(hwc->config_base, ctrl_val);
|
||||
(void)checking_wrmsrl(hwc->config_base, ctrl_val);
|
||||
}
|
||||
|
||||
static inline void
|
||||
p6_pmu_disable_counter(struct hw_perf_counter *hwc, int idx)
|
||||
{
|
||||
struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters);
|
||||
u64 val = P6_NOP_COUNTER;
|
||||
|
||||
if (cpuc->enabled)
|
||||
val |= ARCH_PERFMON_EVENTSEL0_ENABLE;
|
||||
|
||||
(void)checking_wrmsrl(hwc->config_base + idx, val);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -943,6 +1041,19 @@ intel_pmu_enable_fixed(struct hw_perf_counter *hwc, int __idx)
|
||||
err = checking_wrmsrl(hwc->config_base, ctrl_val);
|
||||
}
|
||||
|
||||
static void p6_pmu_enable_counter(struct hw_perf_counter *hwc, int idx)
|
||||
{
|
||||
struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters);
|
||||
u64 val;
|
||||
|
||||
val = hwc->config;
|
||||
if (cpuc->enabled)
|
||||
val |= ARCH_PERFMON_EVENTSEL0_ENABLE;
|
||||
|
||||
(void)checking_wrmsrl(hwc->config_base + idx, val);
|
||||
}
|
||||
|
||||
|
||||
static void intel_pmu_enable_counter(struct hw_perf_counter *hwc, int idx)
|
||||
{
|
||||
if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) {
|
||||
@@ -959,8 +1070,6 @@ static void amd_pmu_enable_counter(struct hw_perf_counter *hwc, int idx)
|
||||
|
||||
if (cpuc->enabled)
|
||||
x86_pmu_enable_counter(hwc, idx);
|
||||
else
|
||||
x86_pmu_disable_counter(hwc, idx);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1176,6 +1285,49 @@ static void intel_pmu_reset(void)
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static int p6_pmu_handle_irq(struct pt_regs *regs)
|
||||
{
|
||||
struct perf_sample_data data;
|
||||
struct cpu_hw_counters *cpuc;
|
||||
struct perf_counter *counter;
|
||||
struct hw_perf_counter *hwc;
|
||||
int idx, handled = 0;
|
||||
u64 val;
|
||||
|
||||
data.regs = regs;
|
||||
data.addr = 0;
|
||||
|
||||
cpuc = &__get_cpu_var(cpu_hw_counters);
|
||||
|
||||
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
||||
if (!test_bit(idx, cpuc->active_mask))
|
||||
continue;
|
||||
|
||||
counter = cpuc->counters[idx];
|
||||
hwc = &counter->hw;
|
||||
|
||||
val = x86_perf_counter_update(counter, hwc, idx);
|
||||
if (val & (1ULL << (x86_pmu.counter_bits - 1)))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* counter overflow
|
||||
*/
|
||||
handled = 1;
|
||||
data.period = counter->hw.last_period;
|
||||
|
||||
if (!x86_perf_counter_set_period(counter, hwc, idx))
|
||||
continue;
|
||||
|
||||
if (perf_counter_overflow(counter, 1, &data))
|
||||
p6_pmu_disable_counter(hwc, idx);
|
||||
}
|
||||
|
||||
if (handled)
|
||||
inc_irq_stat(apic_perf_irqs);
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
/*
|
||||
* This handler is triggered by the local APIC, so the APIC IRQ handling
|
||||
@@ -1185,14 +1337,13 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
|
||||
{
|
||||
struct perf_sample_data data;
|
||||
struct cpu_hw_counters *cpuc;
|
||||
int bit, cpu, loops;
|
||||
int bit, loops;
|
||||
u64 ack, status;
|
||||
|
||||
data.regs = regs;
|
||||
data.addr = 0;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
cpuc = &per_cpu(cpu_hw_counters, cpu);
|
||||
cpuc = &__get_cpu_var(cpu_hw_counters);
|
||||
|
||||
perf_disable();
|
||||
status = intel_pmu_get_status();
|
||||
@@ -1249,14 +1400,13 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
|
||||
struct cpu_hw_counters *cpuc;
|
||||
struct perf_counter *counter;
|
||||
struct hw_perf_counter *hwc;
|
||||
int cpu, idx, handled = 0;
|
||||
int idx, handled = 0;
|
||||
u64 val;
|
||||
|
||||
data.regs = regs;
|
||||
data.addr = 0;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
cpuc = &per_cpu(cpu_hw_counters, cpu);
|
||||
cpuc = &__get_cpu_var(cpu_hw_counters);
|
||||
|
||||
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
|
||||
if (!test_bit(idx, cpuc->active_mask))
|
||||
@@ -1353,6 +1503,32 @@ static __read_mostly struct notifier_block perf_counter_nmi_notifier = {
|
||||
.priority = 1
|
||||
};
|
||||
|
||||
static struct x86_pmu p6_pmu = {
|
||||
.name = "p6",
|
||||
.handle_irq = p6_pmu_handle_irq,
|
||||
.disable_all = p6_pmu_disable_all,
|
||||
.enable_all = p6_pmu_enable_all,
|
||||
.enable = p6_pmu_enable_counter,
|
||||
.disable = p6_pmu_disable_counter,
|
||||
.eventsel = MSR_P6_EVNTSEL0,
|
||||
.perfctr = MSR_P6_PERFCTR0,
|
||||
.event_map = p6_pmu_event_map,
|
||||
.raw_event = p6_pmu_raw_event,
|
||||
.max_events = ARRAY_SIZE(p6_perfmon_event_map),
|
||||
.max_period = (1ULL << 31) - 1,
|
||||
.version = 0,
|
||||
.num_counters = 2,
|
||||
/*
|
||||
* Counters have 40 bits implemented. However they are designed such
|
||||
* that bits [32-39] are sign extensions of bit 31. As such the
|
||||
* effective width of a counter for P6-like PMU is 32 bits only.
|
||||
*
|
||||
* See IA-32 Intel Architecture Software developer manual Vol 3B
|
||||
*/
|
||||
.counter_bits = 32,
|
||||
.counter_mask = (1ULL << 32) - 1,
|
||||
};
|
||||
|
||||
static struct x86_pmu intel_pmu = {
|
||||
.name = "Intel",
|
||||
.handle_irq = intel_pmu_handle_irq,
|
||||
@@ -1392,6 +1568,37 @@ static struct x86_pmu amd_pmu = {
|
||||
.max_period = (1ULL << 47) - 1,
|
||||
};
|
||||
|
||||
static int p6_pmu_init(void)
|
||||
{
|
||||
switch (boot_cpu_data.x86_model) {
|
||||
case 1:
|
||||
case 3: /* Pentium Pro */
|
||||
case 5:
|
||||
case 6: /* Pentium II */
|
||||
case 7:
|
||||
case 8:
|
||||
case 11: /* Pentium III */
|
||||
break;
|
||||
case 9:
|
||||
case 13:
|
||||
/* Pentium M */
|
||||
break;
|
||||
default:
|
||||
pr_cont("unsupported p6 CPU model %d ",
|
||||
boot_cpu_data.x86_model);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!cpu_has_apic) {
|
||||
pr_info("no Local APIC, try rebooting with lapic");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
x86_pmu = p6_pmu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pmu_init(void)
|
||||
{
|
||||
union cpuid10_edx edx;
|
||||
@@ -1400,8 +1607,14 @@ static int intel_pmu_init(void)
|
||||
unsigned int ebx;
|
||||
int version;
|
||||
|
||||
if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON))
|
||||
if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
|
||||
/* check for P6 processor family */
|
||||
if (boot_cpu_data.x86 == 6) {
|
||||
return p6_pmu_init();
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the Architectural PerfMon supports
|
||||
|
Atsaukties uz šo jaunā problēmā
Block a user