op_nmi.c revision 8cfa702f803c5ef6a2b062a489a1b2cf66b45b5e
1/**
2 * @file op_nmi.c
3 * Setup and handling of NMI PMC interrupts
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author John Levon
9 * @author Philippe Elie
10 */
11
12#include "oprofile.h"
13#include "op_msr.h"
14#include "op_apic.h"
15#include "op_util.h"
16#include "op_x86_model.h"
17
18static struct op_msrs cpu_msrs[NR_CPUS];
19static struct op_x86_model_spec const * model = NULL;
20
21static struct op_x86_model_spec const * get_model(void)
22{
23	if (!model) {
24		/* pick out our per-model function table */
25		switch (sysctl.cpu_type) {
26		case CPU_ATHLON:
27		case CPU_HAMMER:
28			model = &op_athlon_spec;
29			break;
30		case CPU_P4:
31			model = &op_p4_spec;
32			break;
33#ifdef HT_SUPPORT
34		case CPU_P4_HT2:
35			model = &op_p4_ht2_spec;
36			break;
37#endif
38		default:
39			model = &op_ppro_spec;
40			break;
41		}
42	}
43	return model;
44}
45
46asmlinkage void op_do_nmi(struct pt_regs * regs)
47{
48	uint const cpu = op_cpu_id();
49	struct op_msrs const * const msrs = &cpu_msrs[cpu];
50
51	model->check_ctrs(cpu, msrs, regs);
52}
53
54/* ---------------- PMC setup ------------------ */
55
56static void pmc_setup_ctr(void * dummy)
57{
58	uint const cpu = op_cpu_id();
59	struct op_msrs const * const msrs = &cpu_msrs[cpu];
60	get_model()->setup_ctrs(msrs);
61}
62
63
64static int pmc_setup_all(void)
65{
66	if (smp_call_function(pmc_setup_ctr, NULL, 0, 1))
67		return -EFAULT;
68	pmc_setup_ctr(NULL);
69	return 0;
70}
71
72
73static void pmc_start(void * info)
74{
75	uint const cpu = op_cpu_id();
76	struct op_msrs const * const msrs = &cpu_msrs[cpu];
77
78	if (info && (*((uint *)info) != cpu))
79		return;
80
81	get_model()->start(msrs);
82}
83
84
85static void pmc_stop(void * info)
86{
87	uint const cpu = op_cpu_id();
88	struct op_msrs const * const msrs = &cpu_msrs[cpu];
89
90	if (info && (*((uint *)info) != cpu))
91		return;
92
93	get_model()->stop(msrs);
94}
95
96
97static void pmc_select_start(uint cpu)
98{
99	if (cpu == op_cpu_id())
100		pmc_start(NULL);
101	else
102		smp_call_function(pmc_start, &cpu, 0, 1);
103}
104
105
106static void pmc_select_stop(uint cpu)
107{
108	if (cpu == op_cpu_id())
109		pmc_stop(NULL);
110	else
111		smp_call_function(pmc_stop, &cpu, 0, 1);
112}
113
114
115static void pmc_start_all(void)
116{
117	int cpu, i;
118
119	for (cpu = 0 ; cpu < smp_num_cpus; cpu++) {
120		struct _oprof_data * data = &oprof_data[cpu];
121
122		for (i = 0 ; i < get_model()->num_counters ; ++i) {
123			if (sysctl.ctr[i].enabled)
124				data->ctr_count[i] = sysctl.ctr[i].count;
125			else
126				data->ctr_count[i] = 0;
127		}
128	}
129
130	install_nmi();
131	smp_call_function(pmc_start, NULL, 0, 1);
132	pmc_start(NULL);
133}
134
135
136static void pmc_stop_all(void)
137{
138	smp_call_function(pmc_stop, NULL, 0, 1);
139	pmc_stop(NULL);
140	restore_nmi();
141}
142
143
144static int pmc_check_params(void)
145{
146	int i;
147	int enabled = 0;
148
149	for (i = 0; i < get_model()->num_counters; i++) {
150		if (!sysctl.ctr[i].enabled)
151			continue;
152
153		enabled = 1;
154
155		if (!sysctl.ctr[i].user && !sysctl.ctr[i].kernel) {
156			printk(KERN_ERR "oprofile: neither kernel nor user "
157			       "set for counter %d\n", i);
158			return -EINVAL;
159		}
160
161		if (check_range(sysctl.ctr[i].count, 1, OP_MAX_PERF_COUNT,
162			"ctr count value %d not in range (%d %ld)\n"))
163			return -EINVAL;
164
165	}
166
167	if (!enabled) {
168		printk(KERN_ERR "oprofile: no counters have been enabled.\n");
169		return -EINVAL;
170	}
171
172	return 0;
173}
174
175
176static void free_msr_group(struct op_msr_group * group)
177{
178	if (group->addrs)
179		kfree(group->addrs);
180	if (group->saved)
181		kfree(group->saved);
182	group->addrs = NULL;
183	group->saved = NULL;
184}
185
186
187static void pmc_save_registers(void * dummy)
188{
189	uint i;
190	uint const cpu = op_cpu_id();
191	uint const nr_ctrs = get_model()->num_counters;
192	uint const nr_ctrls = get_model()->num_controls;
193	struct op_msr_group * counters = &cpu_msrs[cpu].counters;
194	struct op_msr_group * controls = &cpu_msrs[cpu].controls;
195
196	counters->addrs = NULL;
197	counters->saved = NULL;
198	controls->addrs = NULL;
199	controls->saved = NULL;
200
201	counters->addrs = kmalloc(nr_ctrs * sizeof(uint), GFP_KERNEL);
202	if (!counters->addrs)
203		goto fault;
204
205	counters->saved = kmalloc(
206		nr_ctrs * sizeof(struct op_saved_msr), GFP_KERNEL);
207	if (!counters->saved)
208		goto fault;
209
210	controls->addrs = kmalloc(nr_ctrls * sizeof(uint), GFP_KERNEL);
211	if (!controls->addrs)
212		goto fault;
213
214	controls->saved = kmalloc(
215		nr_ctrls * sizeof(struct op_saved_msr), GFP_KERNEL);
216	if (!controls->saved)
217		goto fault;
218
219	model->fill_in_addresses(&cpu_msrs[cpu]);
220
221	for (i = 0; i < nr_ctrs; ++i) {
222		rdmsr(counters->addrs[i],
223			counters->saved[i].low,
224			counters->saved[i].high);
225	}
226
227	for (i = 0; i < nr_ctrls; ++i) {
228		rdmsr(controls->addrs[i],
229			controls->saved[i].low,
230			controls->saved[i].high);
231	}
232	return;
233
234fault:
235	free_msr_group(counters);
236	free_msr_group(controls);
237}
238
239
240static void pmc_restore_registers(void * dummy)
241{
242	uint i;
243	uint const cpu = op_cpu_id();
244	uint const nr_ctrs = get_model()->num_counters;
245	uint const nr_ctrls = get_model()->num_controls;
246	struct op_msr_group * counters = &cpu_msrs[cpu].counters;
247	struct op_msr_group * controls = &cpu_msrs[cpu].controls;
248
249	if (controls->addrs) {
250		for (i = 0; i < nr_ctrls; ++i) {
251			wrmsr(controls->addrs[i],
252				controls->saved[i].low,
253				controls->saved[i].high);
254		}
255	}
256
257	if (counters->addrs) {
258		for (i = 0; i < nr_ctrs; ++i) {
259			wrmsr(counters->addrs[i],
260				counters->saved[i].low,
261				counters->saved[i].high);
262		}
263	}
264
265	free_msr_group(counters);
266	free_msr_group(controls);
267}
268
269
270static int pmc_init(void)
271{
272	int err = 0;
273
274	if ((err = smp_call_function(pmc_save_registers, NULL, 0, 1)))
275		goto out;
276
277	pmc_save_registers(NULL);
278
279	if ((err = apic_setup()))
280		goto out_restore;
281
282	if ((err = smp_call_function(lvtpc_apic_setup, NULL, 0, 1))) {
283		lvtpc_apic_restore(NULL);
284		goto out_restore;
285	}
286
287out:
288	return err;
289out_restore:
290	smp_call_function(pmc_restore_registers, NULL, 0, 1);
291	pmc_restore_registers(NULL);
292	goto out;
293}
294
295
296static void pmc_deinit(void)
297{
298	smp_call_function(lvtpc_apic_restore, NULL, 0, 1);
299	lvtpc_apic_restore(NULL);
300
301	apic_restore();
302
303	smp_call_function(pmc_restore_registers, NULL, 0, 1);
304	pmc_restore_registers(NULL);
305}
306
307
308static char * names[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8"};
309
310static int pmc_add_sysctls(ctl_table * next)
311{
312	ctl_table * start = next;
313	ctl_table * tab;
314	int i, j;
315
316	/* now init the sysctls */
317	for (i=0; i < get_model()->num_counters; i++) {
318		next->ctl_name = 1;
319		next->procname = names[i];
320		next->mode = 0755;
321
322		if (!(tab = kmalloc(sizeof(ctl_table)*7, GFP_KERNEL)))
323			goto cleanup;
324
325		next->child = tab;
326
327		memset(tab, 0, sizeof(ctl_table)*7);
328		tab[0] = ((ctl_table) { 1, "enabled", &sysctl_parms.ctr[i].enabled, sizeof(int), 0644, NULL, lproc_dointvec, NULL, });
329		tab[1] = ((ctl_table) { 1, "event", &sysctl_parms.ctr[i].event, sizeof(int), 0644, NULL, lproc_dointvec, NULL,  });
330		tab[2] = ((ctl_table) { 1, "count", &sysctl_parms.ctr[i].count, sizeof(int), 0644, NULL, lproc_dointvec, NULL, });
331		tab[3] = ((ctl_table) { 1, "unit_mask", &sysctl_parms.ctr[i].unit_mask, sizeof(int), 0644, NULL, lproc_dointvec, NULL, });
332		tab[4] = ((ctl_table) { 1, "kernel", &sysctl_parms.ctr[i].kernel, sizeof(int), 0644, NULL, lproc_dointvec, NULL, });
333		tab[5] = ((ctl_table) { 1, "user", &sysctl_parms.ctr[i].user, sizeof(int), 0644, NULL, lproc_dointvec, NULL, });
334		next++;
335	}
336
337	return 0;
338
339cleanup:
340	next = start;
341	for (j = 0; j < i; j++) {
342		kfree(next->child);
343		next++;
344	}
345	return -EFAULT;
346}
347
348
349static void pmc_remove_sysctls(ctl_table * next)
350{
351	int i;
352	for (i=0; i < get_model()->num_counters; i++) {
353		kfree(next->child);
354		next++;
355	}
356}
357
358
359static struct op_int_operations op_nmi_ops = {
360	init: pmc_init,
361	deinit: pmc_deinit,
362	add_sysctls: pmc_add_sysctls,
363	remove_sysctls: pmc_remove_sysctls,
364	check_params: pmc_check_params,
365	setup: pmc_setup_all,
366	start: pmc_start_all,
367	stop: pmc_stop_all,
368	start_cpu: pmc_select_start,
369	stop_cpu: pmc_select_stop,
370};
371
372
373struct op_int_operations const * op_int_interface()
374{
375	return &op_nmi_ops;
376}
377