diff options
author | Robert Richter <robert.richter@amd.com> | 2011-09-21 05:30:18 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-10-10 00:57:16 -0400 |
commit | b716916679e72054d436afadce2f94dcad71cfad (patch) | |
tree | 86e90ea74a78fb42276868c3ecd1af714d9e5b20 /arch | |
parent | ee5789dbcc800ba7d641443e53f60d53977f9747 (diff) |
perf, x86: Implement IBS initialization
This patch implements IBS feature detection and initialzation. The
code is shared between perf and oprofile. If IBS is available on the
system for perf, a pmu is setup.
Signed-off-by: Robert Richter <robert.richter@amd.com>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1316597423-25723-3-git-send-email-robert.richter@amd.com
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/perf_event.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_amd_ibs.c | 294 | ||||
-rw-r--r-- | arch/x86/oprofile/nmi_int.c | 2 | ||||
-rw-r--r-- | arch/x86/oprofile/op_model_amd.c | 197 | ||||
-rw-r--r-- | arch/x86/oprofile/op_x86_model.h | 1 |
6 files changed, 297 insertions, 201 deletions
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index e9e277c6991b..f61c62f7d5d8 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h | |||
@@ -159,6 +159,8 @@ union cpuid10_edx { | |||
159 | #define IBS_OP_MAX_CNT 0x0000FFFFULL | 159 | #define IBS_OP_MAX_CNT 0x0000FFFFULL |
160 | #define IBS_OP_MAX_CNT_EXT 0x007FFFFFULL /* not a register bit mask */ | 160 | #define IBS_OP_MAX_CNT_EXT 0x007FFFFFULL /* not a register bit mask */ |
161 | 161 | ||
162 | extern u32 get_ibs_caps(void); | ||
163 | |||
162 | #ifdef CONFIG_PERF_EVENTS | 164 | #ifdef CONFIG_PERF_EVENTS |
163 | extern void perf_events_lapic_init(void); | 165 | extern void perf_events_lapic_init(void); |
164 | 166 | ||
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 1044fd787db8..fe6eb197f848 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile | |||
@@ -36,7 +36,7 @@ endif | |||
36 | obj-$(CONFIG_X86_MCE) += mcheck/ | 36 | obj-$(CONFIG_X86_MCE) += mcheck/ |
37 | obj-$(CONFIG_MTRR) += mtrr/ | 37 | obj-$(CONFIG_MTRR) += mtrr/ |
38 | 38 | ||
39 | obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o | 39 | obj-$(CONFIG_X86_LOCAL_APIC) += perfctr-watchdog.o perf_event_amd_ibs.o |
40 | 40 | ||
41 | quiet_cmd_mkcapflags = MKCAP $@ | 41 | quiet_cmd_mkcapflags = MKCAP $@ |
42 | cmd_mkcapflags = $(PERL) $(srctree)/$(src)/mkcapflags.pl $< $@ | 42 | cmd_mkcapflags = $(PERL) $(srctree)/$(src)/mkcapflags.pl $< $@ |
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c new file mode 100644 index 000000000000..ab6343d21825 --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | * Performance events - AMD IBS | ||
3 | * | ||
4 | * Copyright (C) 2011 Advanced Micro Devices, Inc., Robert Richter | ||
5 | * | ||
6 | * For licencing details see kernel-base/COPYING | ||
7 | */ | ||
8 | |||
9 | #include <linux/perf_event.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/pci.h> | ||
12 | |||
13 | #include <asm/apic.h> | ||
14 | |||
15 | static u32 ibs_caps; | ||
16 | |||
17 | #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) | ||
18 | |||
19 | static struct pmu perf_ibs; | ||
20 | |||
21 | static int perf_ibs_init(struct perf_event *event) | ||
22 | { | ||
23 | if (perf_ibs.type != event->attr.type) | ||
24 | return -ENOENT; | ||
25 | return 0; | ||
26 | } | ||
27 | |||
28 | static int perf_ibs_add(struct perf_event *event, int flags) | ||
29 | { | ||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | static void perf_ibs_del(struct perf_event *event, int flags) | ||
34 | { | ||
35 | } | ||
36 | |||
37 | static struct pmu perf_ibs = { | ||
38 | .event_init= perf_ibs_init, | ||
39 | .add= perf_ibs_add, | ||
40 | .del= perf_ibs_del, | ||
41 | }; | ||
42 | |||
43 | static __init int perf_event_ibs_init(void) | ||
44 | { | ||
45 | if (!ibs_caps) | ||
46 | return -ENODEV; /* ibs not supported by the cpu */ | ||
47 | |||
48 | perf_pmu_register(&perf_ibs, "ibs", -1); | ||
49 | printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps); | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | #else /* defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) */ | ||
55 | |||
56 | static __init int perf_event_ibs_init(void) { return 0; } | ||
57 | |||
58 | #endif | ||
59 | |||
60 | /* IBS - apic initialization, for perf and oprofile */ | ||
61 | |||
62 | static __init u32 __get_ibs_caps(void) | ||
63 | { | ||
64 | u32 caps; | ||
65 | unsigned int max_level; | ||
66 | |||
67 | if (!boot_cpu_has(X86_FEATURE_IBS)) | ||
68 | return 0; | ||
69 | |||
70 | /* check IBS cpuid feature flags */ | ||
71 | max_level = cpuid_eax(0x80000000); | ||
72 | if (max_level < IBS_CPUID_FEATURES) | ||
73 | return IBS_CAPS_DEFAULT; | ||
74 | |||
75 | caps = cpuid_eax(IBS_CPUID_FEATURES); | ||
76 | if (!(caps & IBS_CAPS_AVAIL)) | ||
77 | /* cpuid flags not valid */ | ||
78 | return IBS_CAPS_DEFAULT; | ||
79 | |||
80 | return caps; | ||
81 | } | ||
82 | |||
83 | u32 get_ibs_caps(void) | ||
84 | { | ||
85 | return ibs_caps; | ||
86 | } | ||
87 | |||
88 | EXPORT_SYMBOL(get_ibs_caps); | ||
89 | |||
90 | static inline int get_eilvt(int offset) | ||
91 | { | ||
92 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | ||
93 | } | ||
94 | |||
95 | static inline int put_eilvt(int offset) | ||
96 | { | ||
97 | return !setup_APIC_eilvt(offset, 0, 0, 1); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Check and reserve APIC extended interrupt LVT offset for IBS if available. | ||
102 | */ | ||
103 | static inline int ibs_eilvt_valid(void) | ||
104 | { | ||
105 | int offset; | ||
106 | u64 val; | ||
107 | int valid = 0; | ||
108 | |||
109 | preempt_disable(); | ||
110 | |||
111 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
112 | offset = val & IBSCTL_LVT_OFFSET_MASK; | ||
113 | |||
114 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | ||
115 | pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", | ||
116 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | if (!get_eilvt(offset)) { | ||
121 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", | ||
122 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
123 | goto out; | ||
124 | } | ||
125 | |||
126 | valid = 1; | ||
127 | out: | ||
128 | preempt_enable(); | ||
129 | |||
130 | return valid; | ||
131 | } | ||
132 | |||
133 | static int setup_ibs_ctl(int ibs_eilvt_off) | ||
134 | { | ||
135 | struct pci_dev *cpu_cfg; | ||
136 | int nodes; | ||
137 | u32 value = 0; | ||
138 | |||
139 | nodes = 0; | ||
140 | cpu_cfg = NULL; | ||
141 | do { | ||
142 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | ||
143 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | ||
144 | cpu_cfg); | ||
145 | if (!cpu_cfg) | ||
146 | break; | ||
147 | ++nodes; | ||
148 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | ||
149 | | IBSCTL_LVT_OFFSET_VALID); | ||
150 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | ||
151 | if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { | ||
152 | pci_dev_put(cpu_cfg); | ||
153 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | ||
154 | "IBSCTL = 0x%08x\n", value); | ||
155 | return -EINVAL; | ||
156 | } | ||
157 | } while (1); | ||
158 | |||
159 | if (!nodes) { | ||
160 | printk(KERN_DEBUG "No CPU node configured for IBS\n"); | ||
161 | return -ENODEV; | ||
162 | } | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * This runs only on the current cpu. We try to find an LVT offset and | ||
169 | * setup the local APIC. For this we must disable preemption. On | ||
170 | * success we initialize all nodes with this offset. This updates then | ||
171 | * the offset in the IBS_CTL per-node msr. The per-core APIC setup of | ||
172 | * the IBS interrupt vector is handled by perf_ibs_cpu_notifier that | ||
173 | * is using the new offset. | ||
174 | */ | ||
175 | static int force_ibs_eilvt_setup(void) | ||
176 | { | ||
177 | int offset; | ||
178 | int ret; | ||
179 | |||
180 | preempt_disable(); | ||
181 | /* find the next free available EILVT entry, skip offset 0 */ | ||
182 | for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { | ||
183 | if (get_eilvt(offset)) | ||
184 | break; | ||
185 | } | ||
186 | preempt_enable(); | ||
187 | |||
188 | if (offset == APIC_EILVT_NR_MAX) { | ||
189 | printk(KERN_DEBUG "No EILVT entry available\n"); | ||
190 | return -EBUSY; | ||
191 | } | ||
192 | |||
193 | ret = setup_ibs_ctl(offset); | ||
194 | if (ret) | ||
195 | goto out; | ||
196 | |||
197 | if (!ibs_eilvt_valid()) { | ||
198 | ret = -EFAULT; | ||
199 | goto out; | ||
200 | } | ||
201 | |||
202 | pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); | ||
203 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | ||
204 | |||
205 | return 0; | ||
206 | out: | ||
207 | preempt_disable(); | ||
208 | put_eilvt(offset); | ||
209 | preempt_enable(); | ||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | static inline int get_ibs_lvt_offset(void) | ||
214 | { | ||
215 | u64 val; | ||
216 | |||
217 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
218 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) | ||
219 | return -EINVAL; | ||
220 | |||
221 | return val & IBSCTL_LVT_OFFSET_MASK; | ||
222 | } | ||
223 | |||
224 | static void setup_APIC_ibs(void *dummy) | ||
225 | { | ||
226 | int offset; | ||
227 | |||
228 | offset = get_ibs_lvt_offset(); | ||
229 | if (offset < 0) | ||
230 | goto failed; | ||
231 | |||
232 | if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) | ||
233 | return; | ||
234 | failed: | ||
235 | pr_warn("perf: IBS APIC setup failed on cpu #%d\n", | ||
236 | smp_processor_id()); | ||
237 | } | ||
238 | |||
239 | static void clear_APIC_ibs(void *dummy) | ||
240 | { | ||
241 | int offset; | ||
242 | |||
243 | offset = get_ibs_lvt_offset(); | ||
244 | if (offset >= 0) | ||
245 | setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); | ||
246 | } | ||
247 | |||
248 | static int __cpuinit | ||
249 | perf_ibs_cpu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) | ||
250 | { | ||
251 | switch (action & ~CPU_TASKS_FROZEN) { | ||
252 | case CPU_STARTING: | ||
253 | setup_APIC_ibs(NULL); | ||
254 | break; | ||
255 | case CPU_DYING: | ||
256 | clear_APIC_ibs(NULL); | ||
257 | break; | ||
258 | default: | ||
259 | break; | ||
260 | } | ||
261 | |||
262 | return NOTIFY_OK; | ||
263 | } | ||
264 | |||
265 | static __init int amd_ibs_init(void) | ||
266 | { | ||
267 | u32 caps; | ||
268 | int ret; | ||
269 | |||
270 | caps = __get_ibs_caps(); | ||
271 | if (!caps) | ||
272 | return -ENODEV; /* ibs not supported by the cpu */ | ||
273 | |||
274 | if (!ibs_eilvt_valid()) { | ||
275 | ret = force_ibs_eilvt_setup(); | ||
276 | if (ret) { | ||
277 | pr_err("Failed to setup IBS, %d\n", ret); | ||
278 | return ret; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | get_online_cpus(); | ||
283 | ibs_caps = caps; | ||
284 | /* make ibs_caps visible to other cpus: */ | ||
285 | smp_mb(); | ||
286 | perf_cpu_notifier(perf_ibs_cpu_notifier); | ||
287 | smp_call_function(setup_APIC_ibs, NULL, 1); | ||
288 | put_online_cpus(); | ||
289 | |||
290 | return perf_event_ibs_init(); | ||
291 | } | ||
292 | |||
293 | /* Since we need the pci subsystem to init ibs we can't do this earlier: */ | ||
294 | device_initcall(amd_ibs_init); | ||
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index adf8fb31aa7b..c04dc145a4b7 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c | |||
@@ -385,8 +385,6 @@ static void nmi_cpu_shutdown(void *dummy) | |||
385 | apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); | 385 | apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); |
386 | apic_write(APIC_LVTERR, v); | 386 | apic_write(APIC_LVTERR, v); |
387 | nmi_cpu_restore_registers(msrs); | 387 | nmi_cpu_restore_registers(msrs); |
388 | if (model->cpu_down) | ||
389 | model->cpu_down(); | ||
390 | } | 388 | } |
391 | 389 | ||
392 | static void nmi_cpu_up(void *dummy) | 390 | static void nmi_cpu_up(void *dummy) |
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index e947e5cb2e61..303f08637826 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c | |||
@@ -74,27 +74,6 @@ static struct ibs_state ibs_state; | |||
74 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) | 74 | #define IBS_RANDOM_MASK ((1ULL << IBS_RANDOM_BITS) - 1) |
75 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) | 75 | #define IBS_RANDOM_MAXCNT_OFFSET (1ULL << (IBS_RANDOM_BITS - 5)) |
76 | 76 | ||
77 | static u32 get_ibs_caps(void) | ||
78 | { | ||
79 | u32 ibs_caps; | ||
80 | unsigned int max_level; | ||
81 | |||
82 | if (!boot_cpu_has(X86_FEATURE_IBS)) | ||
83 | return 0; | ||
84 | |||
85 | /* check IBS cpuid feature flags */ | ||
86 | max_level = cpuid_eax(0x80000000); | ||
87 | if (max_level < IBS_CPUID_FEATURES) | ||
88 | return IBS_CAPS_DEFAULT; | ||
89 | |||
90 | ibs_caps = cpuid_eax(IBS_CPUID_FEATURES); | ||
91 | if (!(ibs_caps & IBS_CAPS_AVAIL)) | ||
92 | /* cpuid flags not valid */ | ||
93 | return IBS_CAPS_DEFAULT; | ||
94 | |||
95 | return ibs_caps; | ||
96 | } | ||
97 | |||
98 | /* | 77 | /* |
99 | * 16-bit Linear Feedback Shift Register (LFSR) | 78 | * 16-bit Linear Feedback Shift Register (LFSR) |
100 | * | 79 | * |
@@ -285,81 +264,6 @@ static void op_amd_stop_ibs(void) | |||
285 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); | 264 | wrmsrl(MSR_AMD64_IBSOPCTL, 0); |
286 | } | 265 | } |
287 | 266 | ||
288 | static inline int get_eilvt(int offset) | ||
289 | { | ||
290 | return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); | ||
291 | } | ||
292 | |||
293 | static inline int put_eilvt(int offset) | ||
294 | { | ||
295 | return !setup_APIC_eilvt(offset, 0, 0, 1); | ||
296 | } | ||
297 | |||
298 | static inline int ibs_eilvt_valid(void) | ||
299 | { | ||
300 | int offset; | ||
301 | u64 val; | ||
302 | int valid = 0; | ||
303 | |||
304 | preempt_disable(); | ||
305 | |||
306 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
307 | offset = val & IBSCTL_LVT_OFFSET_MASK; | ||
308 | |||
309 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) { | ||
310 | pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", | ||
311 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
312 | goto out; | ||
313 | } | ||
314 | |||
315 | if (!get_eilvt(offset)) { | ||
316 | pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", | ||
317 | smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); | ||
318 | goto out; | ||
319 | } | ||
320 | |||
321 | valid = 1; | ||
322 | out: | ||
323 | preempt_enable(); | ||
324 | |||
325 | return valid; | ||
326 | } | ||
327 | |||
328 | static inline int get_ibs_offset(void) | ||
329 | { | ||
330 | u64 val; | ||
331 | |||
332 | rdmsrl(MSR_AMD64_IBSCTL, val); | ||
333 | if (!(val & IBSCTL_LVT_OFFSET_VALID)) | ||
334 | return -EINVAL; | ||
335 | |||
336 | return val & IBSCTL_LVT_OFFSET_MASK; | ||
337 | } | ||
338 | |||
339 | static void setup_APIC_ibs(void) | ||
340 | { | ||
341 | int offset; | ||
342 | |||
343 | offset = get_ibs_offset(); | ||
344 | if (offset < 0) | ||
345 | goto failed; | ||
346 | |||
347 | if (!setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 0)) | ||
348 | return; | ||
349 | failed: | ||
350 | pr_warn("oprofile: IBS APIC setup failed on cpu #%d\n", | ||
351 | smp_processor_id()); | ||
352 | } | ||
353 | |||
354 | static void clear_APIC_ibs(void) | ||
355 | { | ||
356 | int offset; | ||
357 | |||
358 | offset = get_ibs_offset(); | ||
359 | if (offset >= 0) | ||
360 | setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_FIX, 1); | ||
361 | } | ||
362 | |||
363 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | 267 | #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX |
364 | 268 | ||
365 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, | 269 | static void op_mux_switch_ctrl(struct op_x86_model_spec const *model, |
@@ -473,15 +377,6 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, | |||
473 | val |= op_x86_get_ctrl(model, &counter_config[virt]); | 377 | val |= op_x86_get_ctrl(model, &counter_config[virt]); |
474 | wrmsrl(msrs->controls[i].addr, val); | 378 | wrmsrl(msrs->controls[i].addr, val); |
475 | } | 379 | } |
476 | |||
477 | if (ibs_caps) | ||
478 | setup_APIC_ibs(); | ||
479 | } | ||
480 | |||
481 | static void op_amd_cpu_shutdown(void) | ||
482 | { | ||
483 | if (ibs_caps) | ||
484 | clear_APIC_ibs(); | ||
485 | } | 380 | } |
486 | 381 | ||
487 | static int op_amd_check_ctrs(struct pt_regs * const regs, | 382 | static int op_amd_check_ctrs(struct pt_regs * const regs, |
@@ -544,86 +439,6 @@ static void op_amd_stop(struct op_msrs const * const msrs) | |||
544 | op_amd_stop_ibs(); | 439 | op_amd_stop_ibs(); |
545 | } | 440 | } |
546 | 441 | ||
547 | static int setup_ibs_ctl(int ibs_eilvt_off) | ||
548 | { | ||
549 | struct pci_dev *cpu_cfg; | ||
550 | int nodes; | ||
551 | u32 value = 0; | ||
552 | |||
553 | nodes = 0; | ||
554 | cpu_cfg = NULL; | ||
555 | do { | ||
556 | cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, | ||
557 | PCI_DEVICE_ID_AMD_10H_NB_MISC, | ||
558 | cpu_cfg); | ||
559 | if (!cpu_cfg) | ||
560 | break; | ||
561 | ++nodes; | ||
562 | pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off | ||
563 | | IBSCTL_LVT_OFFSET_VALID); | ||
564 | pci_read_config_dword(cpu_cfg, IBSCTL, &value); | ||
565 | if (value != (ibs_eilvt_off | IBSCTL_LVT_OFFSET_VALID)) { | ||
566 | pci_dev_put(cpu_cfg); | ||
567 | printk(KERN_DEBUG "Failed to setup IBS LVT offset, " | ||
568 | "IBSCTL = 0x%08x\n", value); | ||
569 | return -EINVAL; | ||
570 | } | ||
571 | } while (1); | ||
572 | |||
573 | if (!nodes) { | ||
574 | printk(KERN_DEBUG "No CPU node configured for IBS\n"); | ||
575 | return -ENODEV; | ||
576 | } | ||
577 | |||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | /* | ||
582 | * This runs only on the current cpu. We try to find an LVT offset and | ||
583 | * setup the local APIC. For this we must disable preemption. On | ||
584 | * success we initialize all nodes with this offset. This updates then | ||
585 | * the offset in the IBS_CTL per-node msr. The per-core APIC setup of | ||
586 | * the IBS interrupt vector is called from op_amd_setup_ctrs()/op_- | ||
587 | * amd_cpu_shutdown() using the new offset. | ||
588 | */ | ||
589 | static int force_ibs_eilvt_setup(void) | ||
590 | { | ||
591 | int offset; | ||
592 | int ret; | ||
593 | |||
594 | preempt_disable(); | ||
595 | /* find the next free available EILVT entry, skip offset 0 */ | ||
596 | for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { | ||
597 | if (get_eilvt(offset)) | ||
598 | break; | ||
599 | } | ||
600 | preempt_enable(); | ||
601 | |||
602 | if (offset == APIC_EILVT_NR_MAX) { | ||
603 | printk(KERN_DEBUG "No EILVT entry available\n"); | ||
604 | return -EBUSY; | ||
605 | } | ||
606 | |||
607 | ret = setup_ibs_ctl(offset); | ||
608 | if (ret) | ||
609 | goto out; | ||
610 | |||
611 | if (!ibs_eilvt_valid()) { | ||
612 | ret = -EFAULT; | ||
613 | goto out; | ||
614 | } | ||
615 | |||
616 | pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); | ||
617 | pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); | ||
618 | |||
619 | return 0; | ||
620 | out: | ||
621 | preempt_disable(); | ||
622 | put_eilvt(offset); | ||
623 | preempt_enable(); | ||
624 | return ret; | ||
625 | } | ||
626 | |||
627 | /* | 442 | /* |
628 | * check and reserve APIC extended interrupt LVT offset for IBS if | 443 | * check and reserve APIC extended interrupt LVT offset for IBS if |
629 | * available | 444 | * available |
@@ -636,17 +451,6 @@ static void init_ibs(void) | |||
636 | if (!ibs_caps) | 451 | if (!ibs_caps) |
637 | return; | 452 | return; |
638 | 453 | ||
639 | if (ibs_eilvt_valid()) | ||
640 | goto out; | ||
641 | |||
642 | if (!force_ibs_eilvt_setup()) | ||
643 | goto out; | ||
644 | |||
645 | /* Failed to setup ibs */ | ||
646 | ibs_caps = 0; | ||
647 | return; | ||
648 | |||
649 | out: | ||
650 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); | 454 | printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); |
651 | } | 455 | } |
652 | 456 | ||
@@ -729,7 +533,6 @@ struct op_x86_model_spec op_amd_spec = { | |||
729 | .init = op_amd_init, | 533 | .init = op_amd_init, |
730 | .fill_in_addresses = &op_amd_fill_in_addresses, | 534 | .fill_in_addresses = &op_amd_fill_in_addresses, |
731 | .setup_ctrs = &op_amd_setup_ctrs, | 535 | .setup_ctrs = &op_amd_setup_ctrs, |
732 | .cpu_down = &op_amd_cpu_shutdown, | ||
733 | .check_ctrs = &op_amd_check_ctrs, | 536 | .check_ctrs = &op_amd_check_ctrs, |
734 | .start = &op_amd_start, | 537 | .start = &op_amd_start, |
735 | .stop = &op_amd_stop, | 538 | .stop = &op_amd_stop, |
diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h index 89017fa1fd63..71e8a67337e2 100644 --- a/arch/x86/oprofile/op_x86_model.h +++ b/arch/x86/oprofile/op_x86_model.h | |||
@@ -43,7 +43,6 @@ struct op_x86_model_spec { | |||
43 | int (*fill_in_addresses)(struct op_msrs * const msrs); | 43 | int (*fill_in_addresses)(struct op_msrs * const msrs); |
44 | void (*setup_ctrs)(struct op_x86_model_spec const *model, | 44 | void (*setup_ctrs)(struct op_x86_model_spec const *model, |
45 | struct op_msrs const * const msrs); | 45 | struct op_msrs const * const msrs); |
46 | void (*cpu_down)(void); | ||
47 | int (*check_ctrs)(struct pt_regs * const regs, | 46 | int (*check_ctrs)(struct pt_regs * const regs, |
48 | struct op_msrs const * const msrs); | 47 | struct op_msrs const * const msrs); |
49 | void (*start)(struct op_msrs const * const msrs); | 48 | void (*start)(struct op_msrs const * const msrs); |