diff options
| -rw-r--r-- | arch/x86/include/asm/msr-index.h | 5 | ||||
| -rw-r--r-- | arch/x86/include/asm/perf_event.h | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event_amd_ibs.c | 438 |
3 files changed, 438 insertions, 7 deletions
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index ccb805966f68..957ec87385af 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h | |||
| @@ -134,6 +134,8 @@ | |||
| 134 | #define MSR_AMD64_IBSFETCHCTL 0xc0011030 | 134 | #define MSR_AMD64_IBSFETCHCTL 0xc0011030 |
| 135 | #define MSR_AMD64_IBSFETCHLINAD 0xc0011031 | 135 | #define MSR_AMD64_IBSFETCHLINAD 0xc0011031 |
| 136 | #define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032 | 136 | #define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032 |
| 137 | #define MSR_AMD64_IBSFETCH_REG_COUNT 3 | ||
| 138 | #define MSR_AMD64_IBSFETCH_REG_MASK ((1UL<<MSR_AMD64_IBSFETCH_REG_COUNT)-1) | ||
| 137 | #define MSR_AMD64_IBSOPCTL 0xc0011033 | 139 | #define MSR_AMD64_IBSOPCTL 0xc0011033 |
| 138 | #define MSR_AMD64_IBSOPRIP 0xc0011034 | 140 | #define MSR_AMD64_IBSOPRIP 0xc0011034 |
| 139 | #define MSR_AMD64_IBSOPDATA 0xc0011035 | 141 | #define MSR_AMD64_IBSOPDATA 0xc0011035 |
| @@ -141,8 +143,11 @@ | |||
| 141 | #define MSR_AMD64_IBSOPDATA3 0xc0011037 | 143 | #define MSR_AMD64_IBSOPDATA3 0xc0011037 |
| 142 | #define MSR_AMD64_IBSDCLINAD 0xc0011038 | 144 | #define MSR_AMD64_IBSDCLINAD 0xc0011038 |
| 143 | #define MSR_AMD64_IBSDCPHYSAD 0xc0011039 | 145 | #define MSR_AMD64_IBSDCPHYSAD 0xc0011039 |
| 146 | #define MSR_AMD64_IBSOP_REG_COUNT 7 | ||
| 147 | #define MSR_AMD64_IBSOP_REG_MASK ((1UL<<MSR_AMD64_IBSOP_REG_COUNT)-1) | ||
| 144 | #define MSR_AMD64_IBSCTL 0xc001103a | 148 | #define MSR_AMD64_IBSCTL 0xc001103a |
| 145 | #define MSR_AMD64_IBSBRTARGET 0xc001103b | 149 | #define MSR_AMD64_IBSBRTARGET 0xc001103b |
| 150 | #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ | ||
| 146 | 151 | ||
| 147 | /* Fam 15h MSRs */ | 152 | /* Fam 15h MSRs */ |
| 148 | #define MSR_F15H_PERF_CTL 0xc0010200 | 153 | #define MSR_F15H_PERF_CTL 0xc0010200 |
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 2291895b1836..8a3c75d824b7 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h | |||
| @@ -178,6 +178,8 @@ struct x86_pmu_capability { | |||
| 178 | #define IBS_FETCH_MAX_CNT 0x0000FFFFULL | 178 | #define IBS_FETCH_MAX_CNT 0x0000FFFFULL |
| 179 | 179 | ||
| 180 | /* IbsOpCtl bits */ | 180 | /* IbsOpCtl bits */ |
| 181 | /* lower 4 bits of the current count are ignored: */ | ||
| 182 | #define IBS_OP_CUR_CNT (0xFFFF0ULL<<32) | ||
| 181 | #define IBS_OP_CNT_CTL (1ULL<<19) | 183 | #define IBS_OP_CNT_CTL (1ULL<<19) |
| 182 | #define IBS_OP_VAL (1ULL<<18) | 184 | #define IBS_OP_VAL (1ULL<<18) |
| 183 | #define IBS_OP_ENABLE (1ULL<<17) | 185 | #define IBS_OP_ENABLE (1ULL<<17) |
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index 3b8a2d30d14e..8ff74d439041 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c | |||
| @@ -16,36 +16,460 @@ static u32 ibs_caps; | |||
| 16 | 16 | ||
| 17 | #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) | 17 | #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) |
| 18 | 18 | ||
| 19 | static struct pmu perf_ibs; | 19 | #include <linux/kprobes.h> |
| 20 | #include <linux/hardirq.h> | ||
| 21 | |||
| 22 | #include <asm/nmi.h> | ||
| 23 | |||
| 24 | #define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) | ||
| 25 | #define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT | ||
| 26 | |||
| 27 | enum ibs_states { | ||
| 28 | IBS_ENABLED = 0, | ||
| 29 | IBS_STARTED = 1, | ||
| 30 | IBS_STOPPING = 2, | ||
| 31 | |||
| 32 | IBS_MAX_STATES, | ||
| 33 | }; | ||
| 34 | |||
| 35 | struct cpu_perf_ibs { | ||
| 36 | struct perf_event *event; | ||
| 37 | unsigned long state[BITS_TO_LONGS(IBS_MAX_STATES)]; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct perf_ibs { | ||
| 41 | struct pmu pmu; | ||
| 42 | unsigned int msr; | ||
| 43 | u64 config_mask; | ||
| 44 | u64 cnt_mask; | ||
| 45 | u64 enable_mask; | ||
| 46 | u64 valid_mask; | ||
| 47 | u64 max_period; | ||
| 48 | unsigned long offset_mask[1]; | ||
| 49 | int offset_max; | ||
| 50 | struct cpu_perf_ibs __percpu *pcpu; | ||
| 51 | u64 (*get_count)(u64 config); | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct perf_ibs_data { | ||
| 55 | u32 size; | ||
| 56 | union { | ||
| 57 | u32 data[0]; /* data buffer starts here */ | ||
| 58 | u32 caps; | ||
| 59 | }; | ||
| 60 | u64 regs[MSR_AMD64_IBS_REG_COUNT_MAX]; | ||
| 61 | }; | ||
| 62 | |||
| 63 | static int | ||
| 64 | perf_event_set_period(struct hw_perf_event *hwc, u64 min, u64 max, u64 *count) | ||
| 65 | { | ||
| 66 | s64 left = local64_read(&hwc->period_left); | ||
| 67 | s64 period = hwc->sample_period; | ||
| 68 | int overflow = 0; | ||
| 69 | |||
| 70 | /* | ||
| 71 | * If we are way outside a reasonable range then just skip forward: | ||
| 72 | */ | ||
| 73 | if (unlikely(left <= -period)) { | ||
| 74 | left = period; | ||
| 75 | local64_set(&hwc->period_left, left); | ||
| 76 | hwc->last_period = period; | ||
| 77 | overflow = 1; | ||
| 78 | } | ||
| 79 | |||
| 80 | if (unlikely(left <= 0)) { | ||
| 81 | left += period; | ||
| 82 | local64_set(&hwc->period_left, left); | ||
| 83 | hwc->last_period = period; | ||
| 84 | overflow = 1; | ||
| 85 | } | ||
| 86 | |||
| 87 | if (unlikely(left < min)) | ||
| 88 | left = min; | ||
| 89 | |||
| 90 | if (left > max) | ||
| 91 | left = max; | ||
| 92 | |||
| 93 | *count = (u64)left; | ||
| 94 | |||
| 95 | return overflow; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int | ||
| 99 | perf_event_try_update(struct perf_event *event, u64 new_raw_count, int width) | ||
| 100 | { | ||
| 101 | struct hw_perf_event *hwc = &event->hw; | ||
| 102 | int shift = 64 - width; | ||
| 103 | u64 prev_raw_count; | ||
| 104 | u64 delta; | ||
| 105 | |||
| 106 | /* | ||
| 107 | * Careful: an NMI might modify the previous event value. | ||
| 108 | * | ||
| 109 | * Our tactic to handle this is to first atomically read and | ||
| 110 | * exchange a new raw count - then add that new-prev delta | ||
| 111 | * count to the generic event atomically: | ||
| 112 | */ | ||
| 113 | prev_raw_count = local64_read(&hwc->prev_count); | ||
| 114 | if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, | ||
| 115 | new_raw_count) != prev_raw_count) | ||
| 116 | return 0; | ||
| 117 | |||
| 118 | /* | ||
| 119 | * Now we have the new raw value and have updated the prev | ||
| 120 | * timestamp already. We can now calculate the elapsed delta | ||
| 121 | * (event-)time and add that to the generic event. | ||
| 122 | * | ||
| 123 | * Careful, not all hw sign-extends above the physical width | ||
| 124 | * of the count. | ||
| 125 | */ | ||
| 126 | delta = (new_raw_count << shift) - (prev_raw_count << shift); | ||
| 127 | delta >>= shift; | ||
| 128 | |||
| 129 | local64_add(delta, &event->count); | ||
| 130 | local64_sub(delta, &hwc->period_left); | ||
| 131 | |||
| 132 | return 1; | ||
| 133 | } | ||
| 134 | |||
| 135 | static struct perf_ibs perf_ibs_fetch; | ||
| 136 | static struct perf_ibs perf_ibs_op; | ||
| 137 | |||
| 138 | static struct perf_ibs *get_ibs_pmu(int type) | ||
| 139 | { | ||
| 140 | if (perf_ibs_fetch.pmu.type == type) | ||
| 141 | return &perf_ibs_fetch; | ||
| 142 | if (perf_ibs_op.pmu.type == type) | ||
| 143 | return &perf_ibs_op; | ||
| 144 | return NULL; | ||
| 145 | } | ||
| 20 | 146 | ||
| 21 | static int perf_ibs_init(struct perf_event *event) | 147 | static int perf_ibs_init(struct perf_event *event) |
| 22 | { | 148 | { |
| 23 | if (perf_ibs.type != event->attr.type) | 149 | struct hw_perf_event *hwc = &event->hw; |
| 150 | struct perf_ibs *perf_ibs; | ||
| 151 | u64 max_cnt, config; | ||
| 152 | |||
| 153 | perf_ibs = get_ibs_pmu(event->attr.type); | ||
| 154 | if ( | ||
