aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/msr-index.h5
-rw-r--r--arch/x86/include/asm/perf_event.h2
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_ibs.c438
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
19static 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
27enum ibs_states {
28 IBS_ENABLED = 0,
29 IBS_STARTED = 1,
30 IBS_STOPPING = 2,
31
32 IBS_MAX_STATES,
33};
34
35struct cpu_perf_ibs {
36 struct perf_event *event;
37 unsigned long state[BITS_TO_LONGS(IBS_MAX_STATES)];
38};
39
40struct 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
54struct 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
63static int
64perf_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
98static int
99perf_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
135static struct perf_ibs perf_ibs_fetch;
136static struct perf_ibs perf_ibs_op;
137
138static 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
21static int perf_ibs_init(struct perf_event *event) 147static 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 (