diff options
| -rw-r--r-- | Documentation/admin-guide/kernel-parameters.txt | 5 | ||||
| -rw-r--r-- | arch/x86/events/intel/core.c | 113 | ||||
| -rw-r--r-- | arch/x86/events/perf_event.h | 4 | ||||
| -rw-r--r-- | arch/x86/include/asm/msr-index.h | 1 |
4 files changed, 122 insertions, 1 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 92eb1f42240d..6795dedcbd1e 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt | |||
| @@ -856,6 +856,11 @@ | |||
| 856 | causing system reset or hang due to sending | 856 | causing system reset or hang due to sending |
| 857 | INIT from AP to BSP. | 857 | INIT from AP to BSP. |
| 858 | 858 | ||
| 859 | disable_counter_freezing [HW] | ||
| 860 | Disable Intel PMU counter freezing feature. | ||
| 861 | The feature only exists starting from | ||
| 862 | Arch Perfmon v4 (Skylake and newer). | ||
| 863 | |||
| 859 | disable_ddw [PPC/PSERIES] | 864 | disable_ddw [PPC/PSERIES] |
| 860 | Disable Dynamic DMA Window support. Use this if | 865 | Disable Dynamic DMA Window support. Use this if |
| 861 | to workaround buggy firmware. | 866 | to workaround buggy firmware. |
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index 9b320a51f82f..bd3b8f3600b2 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c | |||
| @@ -1995,6 +1995,18 @@ static void intel_pmu_nhm_enable_all(int added) | |||
| 1995 | intel_pmu_enable_all(added); | 1995 | intel_pmu_enable_all(added); |
| 1996 | } | 1996 | } |
| 1997 | 1997 | ||
| 1998 | static void enable_counter_freeze(void) | ||
| 1999 | { | ||
| 2000 | update_debugctlmsr(get_debugctlmsr() | | ||
| 2001 | DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI); | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | static void disable_counter_freeze(void) | ||
| 2005 | { | ||
| 2006 | update_debugctlmsr(get_debugctlmsr() & | ||
| 2007 | ~DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI); | ||
| 2008 | } | ||
| 2009 | |||
| 1998 | static inline u64 intel_pmu_get_status(void) | 2010 | static inline u64 intel_pmu_get_status(void) |
| 1999 | { | 2011 | { |
| 2000 | u64 status; | 2012 | u64 status; |
| @@ -2290,6 +2302,91 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status) | |||
| 2290 | return handled; | 2302 | return handled; |
| 2291 | } | 2303 | } |
| 2292 | 2304 | ||
| 2305 | static bool disable_counter_freezing; | ||
| 2306 | static int __init intel_perf_counter_freezing_setup(char *s) | ||
| 2307 | { | ||
| 2308 | disable_counter_freezing = true; | ||
| 2309 | pr_info("Intel PMU Counter freezing feature disabled\n"); | ||
| 2310 | return 1; | ||
| 2311 | } | ||
| 2312 | __setup("disable_counter_freezing", intel_perf_counter_freezing_setup); | ||
| 2313 | |||
| 2314 | /* | ||
| 2315 | * Simplified handler for Arch Perfmon v4: | ||
| 2316 | * - We rely on counter freezing/unfreezing to enable/disable the PMU. | ||
| 2317 | * This is done automatically on PMU ack. | ||
| 2318 | * - Ack the PMU only after the APIC. | ||
| 2319 | */ | ||
| 2320 | |||
| 2321 | static int intel_pmu_handle_irq_v4(struct pt_regs *regs) | ||
| 2322 | { | ||
| 2323 | struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); | ||
| 2324 | int handled = 0; | ||
| 2325 | bool bts = false; | ||
| 2326 | u64 status; | ||
| 2327 | int pmu_enabled = cpuc->enabled; | ||
| 2328 | int loops = 0; | ||
| 2329 | |||
| 2330 | /* PMU has been disabled because of counter freezing */ | ||
| 2331 | cpuc->enabled = 0; | ||
| 2332 | if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { | ||
| 2333 | bts = true; | ||
| 2334 | intel_bts_disable_local(); | ||
| 2335 | handled = intel_pmu_drain_bts_buffer(); | ||
| 2336 | handled += intel_bts_interrupt(); | ||
| 2337 | } | ||
| 2338 | status = intel_pmu_get_status(); | ||
| 2339 | if (!status) | ||
| 2340 | goto done; | ||
| 2341 | again: | ||
| 2342 | intel_pmu_lbr_read(); | ||
| 2343 | if (++loops > 100) { | ||
| 2344 | static bool warned; | ||
| 2345 | |||
| 2346 | if (!warned) { | ||
| 2347 | WARN(1, "perfevents: irq loop stuck!\n"); | ||
| 2348 | perf_event_print_debug(); | ||
| 2349 | warned = true; | ||
| 2350 | } | ||
| 2351 | intel_pmu_reset(); | ||
| 2352 | goto done; | ||
| 2353 | } | ||
| 2354 | |||
| 2355 | |||
| 2356 | handled += handle_pmi_common(regs, status); | ||
| 2357 | done: | ||
| 2358 | /* Ack the PMI in the APIC */ | ||
| 2359 | apic_write(APIC_LVTPC, APIC_DM_NMI); | ||
| 2360 | |||
| 2361 | /* | ||
| 2362 | * The counters start counting immediately while ack the status. | ||
| 2363 | * Make it as close as possible to IRET. This avoids bogus | ||
| 2364 | * freezing on Skylake CPUs. | ||
| 2365 | */ | ||
| 2366 | if (status) { | ||
| 2367 | intel_pmu_ack_status(status); | ||
| 2368 | } else { | ||
| 2369 | /* | ||
| 2370 | * CPU may issues two PMIs very close to each other. | ||
| 2371 | * When the PMI handler services the first one, the | ||
| 2372 | * GLOBAL_STATUS is already updated to reflect both. | ||
| 2373 | * When it IRETs, the second PMI is immediately | ||
| 2374 | * handled and it sees clear status. At the meantime, | ||
| 2375 | * there may be a third PMI, because the freezing bit | ||
| 2376 | * isn't set since the ack in first PMI handlers. | ||
| 2377 | * Double check if there is more work to be done. | ||
| 2378 | */ | ||
| 2379 | status = intel_pmu_get_status(); | ||
| 2380 | if (status) | ||
| 2381 | goto again; | ||
| 2382 | } | ||
| 2383 | |||
| 2384 | if (bts) | ||
| 2385 | intel_bts_enable_local(); | ||
| 2386 | cpuc->enabled = pmu_enabled; | ||
| 2387 | return handled; | ||
| 2388 | } | ||
| 2389 | |||
| 2293 | /* | 2390 | /* |
| 2294 | * This handler is triggered by the local APIC, so the APIC IRQ handling | 2391 | * This handler is triggered by the local APIC, so the APIC IRQ handling |
| 2295 | * rules apply: | 2392 | * rules apply: |
| @@ -3361,6 +3458,9 @@ static void intel_pmu_cpu_starting(int cpu) | |||
| 3361 | if (x86_pmu.version > 1) | 3458 | if (x86_pmu.version > 1) |
| 3362 | flip_smm_bit(&x86_pmu.attr_freeze_on_smi); | 3459 | flip_smm_bit(&x86_pmu.attr_freeze_on_smi); |
| 3363 | 3460 | ||
| 3461 | if (x86_pmu.counter_freezing) | ||
| 3462 | enable_counter_freeze(); | ||
| 3463 | |||
| 3364 | if (!cpuc->shared_regs) | 3464 | if (!cpuc->shared_regs) |
| 3365 | return; | 3465 | return; |
| 3366 | 3466 | ||
| @@ -3432,6 +3532,9 @@ static void intel_pmu_cpu_dying(int cpu) | |||
| 3432 | free_excl_cntrs(cpu); | 3532 | free_excl_cntrs(cpu); |
| 3433 | 3533 | ||
| 3434 | fini_debug_store_on_cpu(cpu); | 3534 | fini_debug_store_on_cpu(cpu); |
| 3535 | |||
| 3536 | if (x86_pmu.counter_freezing) | ||
| 3537 | disable_counter_freeze(); | ||
| 3435 | } | 3538 | } |
| 3436 | 3539 | ||
| 3437 | static void intel_pmu_sched_task(struct perf_event_context *ctx, | 3540 | static void intel_pmu_sched_task(struct perf_event_context *ctx, |
| @@ -3946,6 +4049,9 @@ __init int intel_pmu_init(void) | |||
| 3946 | max((int)edx.split.num_counters_fixed, assume); | 4049 | max((int)edx.split.num_counters_fixed, assume); |
| 3947 | } | 4050 | } |
| 3948 | 4051 | ||
| 4052 | if (version >= 4) | ||
| 4053 | x86_pmu.counter_freezing = !disable_counter_freezing; | ||
| 4054 | |||
| 3949 | if (boot_cpu_has(X86_FEATURE_PDCM)) { | 4055 | if (boot_cpu_has(X86_FEATURE_PDCM)) { |
| 3950 | u64 capabilities; | 4056 | u64 capabilities; |
| 3951 | 4057 | ||
| @@ -4442,6 +4548,13 @@ __init int intel_pmu_init(void) | |||
| 4442 | pr_cont("full-width counters, "); | 4548 | pr_cont("full-width counters, "); |
| 4443 | } | 4549 | } |
| 4444 | 4550 | ||
| 4551 | /* | ||
| 4552 | * For arch perfmon 4 use counter freezing to avoid | ||
| 4553 | * several MSR accesses in the PMI. | ||
| 4554 | */ | ||
| 4555 | if (x86_pmu.counter_freezing) | ||
| 4556 | x86_pmu.handle_irq = intel_pmu_handle_irq_v4; | ||
| 4557 | |||
| 4445 | kfree(to_free); | 4558 | kfree(to_free); |
| 4446 | return 0; | 4559 | return 0; |
| 4447 | } | 4560 | } |
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h index 156286335351..adae087cecdd 100644 --- a/arch/x86/events/perf_event.h +++ b/arch/x86/events/perf_event.h | |||
| @@ -560,9 +560,11 @@ struct x86_pmu { | |||
| 560 | struct event_constraint *event_constraints; | 560 | struct event_constraint *event_constraints; |
| 561 | struct x86_pmu_quirk *quirks; | 561 | struct x86_pmu_quirk *quirks; |
| 562 | int perfctr_second_write; | 562 | int perfctr_second_write; |
| 563 | bool late_ack; | ||
| 564 | u64 (*limit_period)(struct perf_event *event, u64 l); | 563 | u64 (*limit_period)(struct perf_event *event, u64 l); |
| 565 | 564 | ||
| 565 | /* PMI handler bits */ | ||
| 566 | unsigned int late_ack :1, | ||
| 567 | counter_freezing :1; | ||
| 566 | /* | 568 | /* |
| 567 | * sysfs attrs | 569 | * sysfs attrs |
| 568 | */ | 570 | */ |
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 4731f0cf97c5..80f4a4f38c79 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h | |||
| @@ -164,6 +164,7 @@ | |||
| 164 | #define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9) | 164 | #define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9) |
| 165 | #define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10) | 165 | #define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10) |
| 166 | #define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11) | 166 | #define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11) |
| 167 | #define DEBUGCTLMSR_FREEZE_PERFMON_ON_PMI (1UL << 12) | ||
| 167 | #define DEBUGCTLMSR_FREEZE_IN_SMM_BIT 14 | 168 | #define DEBUGCTLMSR_FREEZE_IN_SMM_BIT 14 |
| 168 | #define DEBUGCTLMSR_FREEZE_IN_SMM (1UL << DEBUGCTLMSR_FREEZE_IN_SMM_BIT) | 169 | #define DEBUGCTLMSR_FREEZE_IN_SMM (1UL << DEBUGCTLMSR_FREEZE_IN_SMM_BIT) |
| 169 | 170 | ||
