aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Richter <robert.richter@amd.com>2011-12-15 11:56:38 -0500
committerIngo Molnar <mingo@elte.hu>2012-03-08 05:35:22 -0500
commit4db2e8e6500d9ba6406f2714fa3968b39a325274 (patch)
tree2c4c3378fe8ca0d1ce379e826f82e28446a40903
parentb7074f1fbd6149eac1ec25063e4a364c39a85473 (diff)
perf/x86: Implement IBS pmu control ops
Add code to control the IBS pmu. We need to maintain per-cpu states. Since some states are used and changed by the nmi handler, access to these states must be atomic. Signed-off-by: Robert Richter <robert.richter@amd.com> Acked-by: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: http://lkml.kernel.org/r/1323968199-9326-4-git-send-email-robert.richter@amd.com Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_ibs.c106
1 files changed, 103 insertions, 3 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
index a7ec6bdf0a63..40a6d9d5dd23 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
@@ -24,6 +24,19 @@ static u32 ibs_caps;
24#define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) 24#define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT)
25#define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT 25#define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT
26 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
27struct perf_ibs { 40struct perf_ibs {
28 struct pmu pmu; 41 struct pmu pmu;
29 unsigned int msr; 42 unsigned int msr;
@@ -33,6 +46,7 @@ struct perf_ibs {
33 u64 valid_mask; 46 u64 valid_mask;
34 unsigned long offset_mask[1]; 47 unsigned long offset_mask[1];
35 int offset_max; 48 int offset_max;
49 struct cpu_perf_ibs __percpu *pcpu;
36}; 50};
37 51
38struct perf_ibs_data { 52struct perf_ibs_data {
@@ -97,15 +111,66 @@ static int perf_ibs_init(struct perf_event *event)
97 return 0; 111 return 0;
98} 112}
99 113
114static void perf_ibs_start(struct perf_event *event, int flags)
115{
116 struct hw_perf_event *hwc = &event->hw;
117 struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
118 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
119
120 if (test_and_set_bit(IBS_STARTED, pcpu->state))
121 return;
122
123 wrmsrl(hwc->config_base, hwc->config | perf_ibs->enable_mask);
124}
125
126static void perf_ibs_stop(struct perf_event *event, int flags)
127{
128 struct hw_perf_event *hwc = &event->hw;
129 struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
130 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
131 u64 val;
132
133 if (!test_and_clear_bit(IBS_STARTED, pcpu->state))
134 return;
135
136 set_bit(IBS_STOPPING, pcpu->state);
137
138 rdmsrl(hwc->config_base, val);
139 val &= ~perf_ibs->enable_mask;
140 wrmsrl(hwc->config_base, val);
141}
142
100static int perf_ibs_add(struct perf_event *event, int flags) 143static int perf_ibs_add(struct perf_event *event, int flags)
101{ 144{
145 struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
146 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
147
148 if (test_and_set_bit(IBS_ENABLED, pcpu->state))
149 return -ENOSPC;
150
151 pcpu->event = event;
152
153 if (flags & PERF_EF_START)
154 perf_ibs_start(event, PERF_EF_RELOAD);
155
102 return 0; 156 return 0;
103} 157}
104 158
105static void perf_ibs_del(struct perf_event *event, int flags) 159static void perf_ibs_del(struct perf_event *event, int flags)
106{ 160{
161 struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
162 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
163
164 if (!test_and_clear_bit(IBS_ENABLED, pcpu->state))
165 return;
166
167 perf_ibs_stop(event, 0);
168
169 pcpu->event = NULL;
107} 170}
108 171
172static void perf_ibs_read(struct perf_event *event) { }
173
109static struct perf_ibs perf_ibs_fetch = { 174static struct perf_ibs perf_ibs_fetch = {
110 .pmu = { 175 .pmu = {
111 .task_ctx_nr = perf_invalid_context, 176 .task_ctx_nr = perf_invalid_context,
@@ -113,6 +178,9 @@ static struct perf_ibs perf_ibs_fetch = {
113 .event_init = perf_ibs_init, 178 .event_init = perf_ibs_init,
114 .add = perf_ibs_add, 179 .add = perf_ibs_add,
115 .del = perf_ibs_del, 180 .del = perf_ibs_del,
181 .start = perf_ibs_start,
182 .stop = perf_ibs_stop,
183 .read = perf_ibs_read,
116 }, 184 },
117 .msr = MSR_AMD64_IBSFETCHCTL, 185 .msr = MSR_AMD64_IBSFETCHCTL,
118 .config_mask = IBS_FETCH_CONFIG_MASK, 186 .config_mask = IBS_FETCH_CONFIG_MASK,
@@ -130,6 +198,9 @@ static struct perf_ibs perf_ibs_op = {
130 .event_init = perf_ibs_init, 198 .event_init = perf_ibs_init,
131 .add = perf_ibs_add, 199 .add = perf_ibs_add,
132 .del = perf_ibs_del, 200 .del = perf_ibs_del,
201 .start = perf_ibs_start,
202 .stop = perf_ibs_stop,
203 .read = perf_ibs_read,
133 }, 204 },
134 .msr = MSR_AMD64_IBSOPCTL, 205 .msr = MSR_AMD64_IBSOPCTL,
135 .config_mask = IBS_OP_CONFIG_MASK, 206 .config_mask = IBS_OP_CONFIG_MASK,
@@ -142,7 +213,8 @@ static struct perf_ibs perf_ibs_op = {
142 213
143static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) 214static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
144{ 215{
145 struct perf_event *event = NULL; 216 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
217 struct perf_event *event = pcpu->event;
146 struct hw_perf_event *hwc = &event->hw; 218 struct hw_perf_event *hwc = &event->hw;
147 struct perf_sample_data data; 219 struct perf_sample_data data;
148 struct perf_raw_record raw; 220 struct perf_raw_record raw;
@@ -152,6 +224,14 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
152 unsigned int msr; 224 unsigned int msr;
153 u64 *buf; 225 u64 *buf;
154 226
227 if (!test_bit(IBS_STARTED, pcpu->state)) {
228 /* Catch spurious interrupts after stopping IBS: */
229 if (!test_and_clear_bit(IBS_STOPPING, pcpu->state))
230 return 0;
231 rdmsrl(perf_ibs->msr, *ibs_data.regs);
232 return (*ibs_data.regs & perf_ibs->valid_mask) ? 1 : 0;
233 }
234
155 msr = hwc->config_base; 235 msr = hwc->config_base;
156 buf = ibs_data.regs; 236 buf = ibs_data.regs;
157 rdmsrl(msr, *buf); 237 rdmsrl(msr, *buf);
@@ -200,13 +280,33 @@ perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs)
200 return handled; 280 return handled;
201} 281}
202 282
283static __init int perf_ibs_pmu_init(struct perf_ibs *perf_ibs, char *name)
284{
285 struct cpu_perf_ibs __percpu *pcpu;
286 int ret;
287
288 pcpu = alloc_percpu(struct cpu_perf_ibs);
289 if (!pcpu)
290 return -ENOMEM;
291
292 perf_ibs->pcpu = pcpu;
293
294 ret = perf_pmu_register(&perf_ibs->pmu, name, -1);
295 if (ret) {
296 perf_ibs->pcpu = NULL;
297 free_percpu(pcpu);
298 }
299
300 return ret;
301}
302
203static __init int perf_event_ibs_init(void) 303static __init int perf_event_ibs_init(void)
204{ 304{
205 if (!ibs_caps) 305 if (!ibs_caps)
206 return -ENODEV; /* ibs not supported by the cpu */ 306 return -ENODEV; /* ibs not supported by the cpu */
207 307
208 perf_pmu_register(&perf_ibs_fetch.pmu, "ibs_fetch", -1); 308 perf_ibs_pmu_init(&perf_ibs_fetch, "ibs_fetch");
209 perf_pmu_register(&perf_ibs_op.pmu, "ibs_op", -1); 309 perf_ibs_pmu_init(&perf_ibs_op, "ibs_op");
210 register_nmi_handler(NMI_LOCAL, &perf_ibs_nmi_handler, 0, "perf_ibs"); 310 register_nmi_handler(NMI_LOCAL, &perf_ibs_nmi_handler, 0, "perf_ibs");
211 printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps); 311 printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps);
212 312