| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | #include <linux/perf_event.h> |
|---|
| 2 | 3 | #include <linux/export.h> |
|---|
| 3 | 4 | #include <linux/types.h> |
|---|
| .. | .. |
|---|
| 12 | 13 | |
|---|
| 13 | 14 | static DEFINE_PER_CPU(unsigned long, perf_nmi_tstamp); |
|---|
| 14 | 15 | static unsigned long perf_nmi_window; |
|---|
| 16 | + |
|---|
| 17 | +/* AMD Event 0xFFF: Merge. Used with Large Increment per Cycle events */ |
|---|
| 18 | +#define AMD_MERGE_EVENT ((0xFULL << 32) | 0xFFULL) |
|---|
| 19 | +#define AMD_MERGE_EVENT_ENABLE (AMD_MERGE_EVENT | ARCH_PERFMON_EVENTSEL_ENABLE) |
|---|
| 15 | 20 | |
|---|
| 16 | 21 | static __initconst const u64 amd_hw_cache_event_ids |
|---|
| 17 | 22 | [PERF_COUNT_HW_CACHE_MAX] |
|---|
| .. | .. |
|---|
| 301 | 306 | return offset; |
|---|
| 302 | 307 | } |
|---|
| 303 | 308 | |
|---|
| 309 | +/* |
|---|
| 310 | + * AMD64 events are detected based on their event codes. |
|---|
| 311 | + */ |
|---|
| 312 | +static inline unsigned int amd_get_event_code(struct hw_perf_event *hwc) |
|---|
| 313 | +{ |
|---|
| 314 | + return ((hwc->config >> 24) & 0x0f00) | (hwc->config & 0x00ff); |
|---|
| 315 | +} |
|---|
| 316 | + |
|---|
| 317 | +static inline bool amd_is_pair_event_code(struct hw_perf_event *hwc) |
|---|
| 318 | +{ |
|---|
| 319 | + if (!(x86_pmu.flags & PMU_FL_PAIR)) |
|---|
| 320 | + return false; |
|---|
| 321 | + |
|---|
| 322 | + switch (amd_get_event_code(hwc)) { |
|---|
| 323 | + case 0x003: return true; /* Retired SSE/AVX FLOPs */ |
|---|
| 324 | + default: return false; |
|---|
| 325 | + } |
|---|
| 326 | +} |
|---|
| 327 | + |
|---|
| 304 | 328 | static int amd_core_hw_config(struct perf_event *event) |
|---|
| 305 | 329 | { |
|---|
| 306 | 330 | if (event->attr.exclude_host && event->attr.exclude_guest) |
|---|
| .. | .. |
|---|
| 316 | 340 | else if (event->attr.exclude_guest) |
|---|
| 317 | 341 | event->hw.config |= AMD64_EVENTSEL_HOSTONLY; |
|---|
| 318 | 342 | |
|---|
| 319 | | - return 0; |
|---|
| 320 | | -} |
|---|
| 343 | + if ((x86_pmu.flags & PMU_FL_PAIR) && amd_is_pair_event_code(&event->hw)) |
|---|
| 344 | + event->hw.flags |= PERF_X86_EVENT_PAIR; |
|---|
| 321 | 345 | |
|---|
| 322 | | -/* |
|---|
| 323 | | - * AMD64 events are detected based on their event codes. |
|---|
| 324 | | - */ |
|---|
| 325 | | -static inline unsigned int amd_get_event_code(struct hw_perf_event *hwc) |
|---|
| 326 | | -{ |
|---|
| 327 | | - return ((hwc->config >> 24) & 0x0f00) | (hwc->config & 0x00ff); |
|---|
| 346 | + return 0; |
|---|
| 328 | 347 | } |
|---|
| 329 | 348 | |
|---|
| 330 | 349 | static inline int amd_is_nb_event(struct hw_perf_event *hwc) |
|---|
| .. | .. |
|---|
| 652 | 671 | */ |
|---|
| 653 | 672 | static int amd_pmu_handle_irq(struct pt_regs *regs) |
|---|
| 654 | 673 | { |
|---|
| 655 | | - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); |
|---|
| 656 | | - int active, handled; |
|---|
| 657 | | - |
|---|
| 658 | | - /* |
|---|
| 659 | | - * Obtain the active count before calling x86_pmu_handle_irq() since |
|---|
| 660 | | - * it is possible that x86_pmu_handle_irq() may make a counter |
|---|
| 661 | | - * inactive (through x86_pmu_stop). |
|---|
| 662 | | - */ |
|---|
| 663 | | - active = __bitmap_weight(cpuc->active_mask, X86_PMC_IDX_MAX); |
|---|
| 674 | + int handled; |
|---|
| 664 | 675 | |
|---|
| 665 | 676 | /* Process any counter overflows */ |
|---|
| 666 | 677 | handled = x86_pmu_handle_irq(regs); |
|---|
| .. | .. |
|---|
| 670 | 681 | * NMIs will be claimed if arriving within that window. |
|---|
| 671 | 682 | */ |
|---|
| 672 | 683 | if (handled) { |
|---|
| 673 | | - this_cpu_write(perf_nmi_tstamp, |
|---|
| 674 | | - jiffies + perf_nmi_window); |
|---|
| 684 | + this_cpu_write(perf_nmi_tstamp, jiffies + perf_nmi_window); |
|---|
| 675 | 685 | |
|---|
| 676 | 686 | return handled; |
|---|
| 677 | 687 | } |
|---|
| .. | .. |
|---|
| 864 | 874 | } |
|---|
| 865 | 875 | } |
|---|
| 866 | 876 | |
|---|
| 877 | +static struct event_constraint pair_constraint; |
|---|
| 878 | + |
|---|
| 879 | +static struct event_constraint * |
|---|
| 880 | +amd_get_event_constraints_f17h(struct cpu_hw_events *cpuc, int idx, |
|---|
| 881 | + struct perf_event *event) |
|---|
| 882 | +{ |
|---|
| 883 | + struct hw_perf_event *hwc = &event->hw; |
|---|
| 884 | + |
|---|
| 885 | + if (amd_is_pair_event_code(hwc)) |
|---|
| 886 | + return &pair_constraint; |
|---|
| 887 | + |
|---|
| 888 | + return &unconstrained; |
|---|
| 889 | +} |
|---|
| 890 | + |
|---|
| 891 | +static void amd_put_event_constraints_f17h(struct cpu_hw_events *cpuc, |
|---|
| 892 | + struct perf_event *event) |
|---|
| 893 | +{ |
|---|
| 894 | + struct hw_perf_event *hwc = &event->hw; |
|---|
| 895 | + |
|---|
| 896 | + if (is_counter_pair(hwc)) |
|---|
| 897 | + --cpuc->n_pair; |
|---|
| 898 | +} |
|---|
| 899 | + |
|---|
| 867 | 900 | static ssize_t amd_event_sysfs_show(char *page, u64 config) |
|---|
| 868 | 901 | { |
|---|
| 869 | 902 | u64 event = (config & ARCH_PERFMON_EVENTSEL_EVENT) | |
|---|
| .. | .. |
|---|
| 907 | 940 | |
|---|
| 908 | 941 | static int __init amd_core_pmu_init(void) |
|---|
| 909 | 942 | { |
|---|
| 943 | + u64 even_ctr_mask = 0ULL; |
|---|
| 944 | + int i; |
|---|
| 945 | + |
|---|
| 910 | 946 | if (!boot_cpu_has(X86_FEATURE_PERFCTR_CORE)) |
|---|
| 911 | 947 | return 0; |
|---|
| 912 | 948 | |
|---|
| 913 | | - /* Avoid calulating the value each time in the NMI handler */ |
|---|
| 949 | + /* Avoid calculating the value each time in the NMI handler */ |
|---|
| 914 | 950 | perf_nmi_window = msecs_to_jiffies(100); |
|---|
| 915 | | - |
|---|
| 916 | | - switch (boot_cpu_data.x86) { |
|---|
| 917 | | - case 0x15: |
|---|
| 918 | | - pr_cont("Fam15h "); |
|---|
| 919 | | - x86_pmu.get_event_constraints = amd_get_event_constraints_f15h; |
|---|
| 920 | | - break; |
|---|
| 921 | | - case 0x17: |
|---|
| 922 | | - pr_cont("Fam17h "); |
|---|
| 923 | | - /* |
|---|
| 924 | | - * In family 17h, there are no event constraints in the PMC hardware. |
|---|
| 925 | | - * We fallback to using default amd_get_event_constraints. |
|---|
| 926 | | - */ |
|---|
| 927 | | - break; |
|---|
| 928 | | - default: |
|---|
| 929 | | - pr_err("core perfctr but no constraints; unknown hardware!\n"); |
|---|
| 930 | | - return -ENODEV; |
|---|
| 931 | | - } |
|---|
| 932 | 951 | |
|---|
| 933 | 952 | /* |
|---|
| 934 | 953 | * If core performance counter extensions exists, we must use |
|---|
| .. | .. |
|---|
| 944 | 963 | */ |
|---|
| 945 | 964 | x86_pmu.amd_nb_constraints = 0; |
|---|
| 946 | 965 | |
|---|
| 966 | + if (boot_cpu_data.x86 == 0x15) { |
|---|
| 967 | + pr_cont("Fam15h "); |
|---|
| 968 | + x86_pmu.get_event_constraints = amd_get_event_constraints_f15h; |
|---|
| 969 | + } |
|---|
| 970 | + if (boot_cpu_data.x86 >= 0x17) { |
|---|
| 971 | + pr_cont("Fam17h+ "); |
|---|
| 972 | + /* |
|---|
| 973 | + * Family 17h and compatibles have constraints for Large |
|---|
| 974 | + * Increment per Cycle events: they may only be assigned an |
|---|
| 975 | + * even numbered counter that has a consecutive adjacent odd |
|---|
| 976 | + * numbered counter following it. |
|---|
| 977 | + */ |
|---|
| 978 | + for (i = 0; i < x86_pmu.num_counters - 1; i += 2) |
|---|
| 979 | + even_ctr_mask |= 1 << i; |
|---|
| 980 | + |
|---|
| 981 | + pair_constraint = (struct event_constraint) |
|---|
| 982 | + __EVENT_CONSTRAINT(0, even_ctr_mask, 0, |
|---|
| 983 | + x86_pmu.num_counters / 2, 0, |
|---|
| 984 | + PERF_X86_EVENT_PAIR); |
|---|
| 985 | + |
|---|
| 986 | + x86_pmu.get_event_constraints = amd_get_event_constraints_f17h; |
|---|
| 987 | + x86_pmu.put_event_constraints = amd_put_event_constraints_f17h; |
|---|
| 988 | + x86_pmu.perf_ctr_pair_en = AMD_MERGE_EVENT_ENABLE; |
|---|
| 989 | + x86_pmu.flags |= PMU_FL_PAIR; |
|---|
| 990 | + } |
|---|
| 991 | + |
|---|
| 947 | 992 | pr_cont("core perfctr, "); |
|---|
| 948 | 993 | return 0; |
|---|
| 949 | 994 | } |
|---|