]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - arch/i386/oprofile/nmi_int.c
[PATCH] i386: Utilize performance counter reservation framework in oprofile
[mirror_ubuntu-zesty-kernel.git] / arch / i386 / oprofile / nmi_int.c
CommitLineData
1da177e4
LT
1/**
2 * @file nmi_int.c
3 *
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author John Levon <levon@movementarian.org>
8 */
9
10#include <linux/init.h>
11#include <linux/notifier.h>
12#include <linux/smp.h>
13#include <linux/oprofile.h>
14#include <linux/sysdev.h>
15#include <linux/slab.h>
1cfcea1b 16#include <linux/moduleparam.h>
1da177e4
LT
17#include <asm/nmi.h>
18#include <asm/msr.h>
19#include <asm/apic.h>
20
21#include "op_counter.h"
22#include "op_x86_model.h"
23
24static struct op_x86_model_spec const * model;
25static struct op_msrs cpu_msrs[NR_CPUS];
26static unsigned long saved_lvtpc[NR_CPUS];
27
28static int nmi_start(void);
29static void nmi_stop(void);
30
31/* 0 == registered but off, 1 == registered and on */
32static int nmi_enabled = 0;
33
34#ifdef CONFIG_PM
35
438510f6 36static int nmi_suspend(struct sys_device *dev, pm_message_t state)
1da177e4
LT
37{
38 if (nmi_enabled == 1)
39 nmi_stop();
40 return 0;
41}
42
43
44static int nmi_resume(struct sys_device *dev)
45{
46 if (nmi_enabled == 1)
47 nmi_start();
48 return 0;
49}
50
51
52static struct sysdev_class oprofile_sysclass = {
53 set_kset_name("oprofile"),
54 .resume = nmi_resume,
55 .suspend = nmi_suspend,
56};
57
58
59static struct sys_device device_oprofile = {
60 .id = 0,
61 .cls = &oprofile_sysclass,
62};
63
64
65static int __init init_driverfs(void)
66{
67 int error;
68 if (!(error = sysdev_class_register(&oprofile_sysclass)))
69 error = sysdev_register(&device_oprofile);
70 return error;
71}
72
73
74static void exit_driverfs(void)
75{
76 sysdev_unregister(&device_oprofile);
77 sysdev_class_unregister(&oprofile_sysclass);
78}
79
80#else
81#define init_driverfs() do { } while (0)
82#define exit_driverfs() do { } while (0)
83#endif /* CONFIG_PM */
84
85
86static int nmi_callback(struct pt_regs * regs, int cpu)
87{
88 return model->check_ctrs(regs, &cpu_msrs[cpu]);
89}
90
91
92static void nmi_cpu_save_registers(struct op_msrs * msrs)
93{
94 unsigned int const nr_ctrs = model->num_counters;
95 unsigned int const nr_ctrls = model->num_controls;
96 struct op_msr * counters = msrs->counters;
97 struct op_msr * controls = msrs->controls;
98 unsigned int i;
99
100 for (i = 0; i < nr_ctrs; ++i) {
cb9c448c
DZ
101 if (counters[i].addr){
102 rdmsr(counters[i].addr,
103 counters[i].saved.low,
104 counters[i].saved.high);
105 }
1da177e4
LT
106 }
107
108 for (i = 0; i < nr_ctrls; ++i) {
cb9c448c
DZ
109 if (controls[i].addr){
110 rdmsr(controls[i].addr,
111 controls[i].saved.low,
112 controls[i].saved.high);
113 }
1da177e4
LT
114 }
115}
116
117
118static void nmi_save_registers(void * dummy)
119{
120 int cpu = smp_processor_id();
121 struct op_msrs * msrs = &cpu_msrs[cpu];
122 model->fill_in_addresses(msrs);
123 nmi_cpu_save_registers(msrs);
124}
125
126
127static void free_msrs(void)
128{
129 int i;
c8912599 130 for_each_possible_cpu(i) {
1da177e4
LT
131 kfree(cpu_msrs[i].counters);
132 cpu_msrs[i].counters = NULL;
133 kfree(cpu_msrs[i].controls);
134 cpu_msrs[i].controls = NULL;
135 }
136}
137
138
139static int allocate_msrs(void)
140{
141 int success = 1;
142 size_t controls_size = sizeof(struct op_msr) * model->num_controls;
143 size_t counters_size = sizeof(struct op_msr) * model->num_counters;
144
145 int i;
394e3902 146 for_each_online_cpu(i) {
1da177e4
LT
147 cpu_msrs[i].counters = kmalloc(counters_size, GFP_KERNEL);
148 if (!cpu_msrs[i].counters) {
149 success = 0;
150 break;
151 }
152 cpu_msrs[i].controls = kmalloc(controls_size, GFP_KERNEL);
153 if (!cpu_msrs[i].controls) {
154 success = 0;
155 break;
156 }
157 }
158
159 if (!success)
160 free_msrs();
161
162 return success;
163}
164
165
166static void nmi_cpu_setup(void * dummy)
167{
168 int cpu = smp_processor_id();
169 struct op_msrs * msrs = &cpu_msrs[cpu];
170 spin_lock(&oprofilefs_lock);
171 model->setup_ctrs(msrs);
172 spin_unlock(&oprofilefs_lock);
173 saved_lvtpc[cpu] = apic_read(APIC_LVTPC);
174 apic_write(APIC_LVTPC, APIC_DM_NMI);
175}
176
177
178static int nmi_setup(void)
179{
180 if (!allocate_msrs())
181 return -ENOMEM;
182
183 /* We walk a thin line between law and rape here.
184 * We need to be careful to install our NMI handler
185 * without actually triggering any NMIs as this will
186 * break the core code horrifically.
187 */
188 if (reserve_lapic_nmi() < 0) {
189 free_msrs();
190 return -EBUSY;
191 }
192 /* We need to serialize save and setup for HT because the subset
193 * of msrs are distinct for save and setup operations
194 */
195 on_each_cpu(nmi_save_registers, NULL, 0, 1);
196 on_each_cpu(nmi_cpu_setup, NULL, 0, 1);
197 set_nmi_callback(nmi_callback);
198 nmi_enabled = 1;
199 return 0;
200}
201
202
203static void nmi_restore_registers(struct op_msrs * msrs)
204{
205 unsigned int const nr_ctrs = model->num_counters;
206 unsigned int const nr_ctrls = model->num_controls;
207 struct op_msr * counters = msrs->counters;
208 struct op_msr * controls = msrs->controls;
209 unsigned int i;
210
211 for (i = 0; i < nr_ctrls; ++i) {
cb9c448c
DZ
212 if (controls[i].addr){
213 wrmsr(controls[i].addr,
214 controls[i].saved.low,
215 controls[i].saved.high);
216 }
1da177e4
LT
217 }
218
219 for (i = 0; i < nr_ctrs; ++i) {
cb9c448c
DZ
220 if (counters[i].addr){
221 wrmsr(counters[i].addr,
222 counters[i].saved.low,
223 counters[i].saved.high);
224 }
1da177e4
LT
225 }
226}
227
228
229static void nmi_cpu_shutdown(void * dummy)
230{
231 unsigned int v;
232 int cpu = smp_processor_id();
233 struct op_msrs * msrs = &cpu_msrs[cpu];
234
235 /* restoring APIC_LVTPC can trigger an apic error because the delivery
236 * mode and vector nr combination can be illegal. That's by design: on
237 * power on apic lvt contain a zero vector nr which are legal only for
238 * NMI delivery mode. So inhibit apic err before restoring lvtpc
239 */
240 v = apic_read(APIC_LVTERR);
241 apic_write(APIC_LVTERR, v | APIC_LVT_MASKED);
242 apic_write(APIC_LVTPC, saved_lvtpc[cpu]);
243 apic_write(APIC_LVTERR, v);
244 nmi_restore_registers(msrs);
cb9c448c 245 model->shutdown(msrs);
1da177e4
LT
246}
247
248
249static void nmi_shutdown(void)
250{
251 nmi_enabled = 0;
252 on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1);
253 unset_nmi_callback();
254 release_lapic_nmi();
255 free_msrs();
256}
257
258
259static void nmi_cpu_start(void * dummy)
260{
261 struct op_msrs const * msrs = &cpu_msrs[smp_processor_id()];
262 model->start(msrs);
263}
264
265
266static int nmi_start(void)
267{
268 on_each_cpu(nmi_cpu_start, NULL, 0, 1);
269 return 0;
270}
271
272
273static void nmi_cpu_stop(void * dummy)
274{
275 struct op_msrs const * msrs = &cpu_msrs[smp_processor_id()];
276 model->stop(msrs);
277}
278
279
280static void nmi_stop(void)
281{
282 on_each_cpu(nmi_cpu_stop, NULL, 0, 1);
283}
284
285
286struct op_counter_config counter_config[OP_MAX_COUNTER];
287
288static int nmi_create_files(struct super_block * sb, struct dentry * root)
289{
290 unsigned int i;
291
292 for (i = 0; i < model->num_counters; ++i) {
293 struct dentry * dir;
0c6856f7 294 char buf[4];
1da177e4 295
cb9c448c
DZ
296 /* quick little hack to _not_ expose a counter if it is not
297 * available for use. This should protect userspace app.
298 * NOTE: assumes 1:1 mapping here (that counters are organized
299 * sequentially in their struct assignment).
300 */
301 if (unlikely(!avail_to_resrv_perfctr_nmi_bit(i)))
302 continue;
303
0c6856f7 304 snprintf(buf, sizeof(buf), "%d", i);
1da177e4
LT
305 dir = oprofilefs_mkdir(sb, root, buf);
306 oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled);
307 oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event);
308 oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count);
309 oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask);
310 oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel);
311 oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user);
312 }
313
314 return 0;
315}
316
1cfcea1b
AK
317static int p4force;
318module_param(p4force, int, 0);
1da177e4
LT
319
320static int __init p4_init(char ** cpu_type)
321{
322 __u8 cpu_model = boot_cpu_data.x86_model;
323
1cfcea1b 324 if (!p4force && (cpu_model > 6 || cpu_model == 5))
1da177e4
LT
325 return 0;
326
327#ifndef CONFIG_SMP
328 *cpu_type = "i386/p4";
329 model = &op_p4_spec;
330 return 1;
331#else
332 switch (smp_num_siblings) {
333 case 1:
334 *cpu_type = "i386/p4";
335 model = &op_p4_spec;
336 return 1;
337
338 case 2:
339 *cpu_type = "i386/p4-ht";
340 model = &op_p4_ht2_spec;
341 return 1;
342 }
343#endif
344
345 printk(KERN_INFO "oprofile: P4 HyperThreading detected with > 2 threads\n");
346 printk(KERN_INFO "oprofile: Reverting to timer mode.\n");
347 return 0;
348}
349
350
351static int __init ppro_init(char ** cpu_type)
352{
353 __u8 cpu_model = boot_cpu_data.x86_model;
354
64471ebe
BL
355 if (cpu_model == 14)
356 *cpu_type = "i386/core";
f04b92e9
BL
357 else if (cpu_model == 15)
358 *cpu_type = "i386/core_2";
64471ebe 359 else if (cpu_model > 0xd)
1da177e4 360 return 0;
64471ebe 361 else if (cpu_model == 9) {
1da177e4
LT
362 *cpu_type = "i386/p6_mobile";
363 } else if (cpu_model > 5) {
364 *cpu_type = "i386/piii";
365 } else if (cpu_model > 2) {
366 *cpu_type = "i386/pii";
367 } else {
368 *cpu_type = "i386/ppro";
369 }
370
371 model = &op_ppro_spec;
372 return 1;
373}
374
375/* in order to get driverfs right */
376static int using_nmi;
377
96d0821c 378int __init op_nmi_init(struct oprofile_operations *ops)
1da177e4
LT
379{
380 __u8 vendor = boot_cpu_data.x86_vendor;
381 __u8 family = boot_cpu_data.x86;
382 char *cpu_type;
383
384 if (!cpu_has_apic)
385 return -ENODEV;
386
387 switch (vendor) {
388 case X86_VENDOR_AMD:
389 /* Needs to be at least an Athlon (or hammer in 32bit mode) */
390
391 switch (family) {
392 default:
393 return -ENODEV;
394 case 6:
395 model = &op_athlon_spec;
396 cpu_type = "i386/athlon";
397 break;
398 case 0xf:
399 model = &op_athlon_spec;
400 /* Actually it could be i386/hammer too, but give
401 user space an consistent name. */
402 cpu_type = "x86-64/hammer";
403 break;
404 }
405 break;
406
407 case X86_VENDOR_INTEL:
408 switch (family) {
409 /* Pentium IV */
410 case 0xf:
411 if (!p4_init(&cpu_type))
412 return -ENODEV;
413 break;
414
415 /* A P6-class processor */
416 case 6:
417 if (!ppro_init(&cpu_type))
418 return -ENODEV;
419 break;
420
421 default:
422 return -ENODEV;
423 }
424 break;
425
426 default:
427 return -ENODEV;
428 }
429
430 init_driverfs();
431 using_nmi = 1;
432 ops->create_files = nmi_create_files;
433 ops->setup = nmi_setup;
434 ops->shutdown = nmi_shutdown;
435 ops->start = nmi_start;
436 ops->stop = nmi_stop;
437 ops->cpu_type = cpu_type;
438 printk(KERN_INFO "oprofile: using NMI interrupt.\n");
439 return 0;
440}
441
442
96d0821c 443void op_nmi_exit(void)
1da177e4
LT
444{
445 if (using_nmi)
446 exit_driverfs();
447}