ia64/xen-unstable

view linux-2.6-xen-sparse/arch/i386/oprofile/xenoprof.c @ 10069:3dca5b4add2b

Fixes to the xenoprofile Linux driver.

The active_domains code has race conditions:

* oprofile_set_active() calls set_active() method without holding
start_sem. This is clearly wrong, as xenoprof_set_active() makes
several hypercalls. oprofile_start(), for instance, could run in
the middle of xenoprof_set_active().

* adomain_write(), adomain_read() and xenoprof_set_active() access
global active_domains[] and adomains without synchronization. I
went for a simple, obvious fix and created another mutex. Instead,
one could move the shared data into oprof.c and protect it with
start_sem, but that's more invasive.

Also clean up the code dealing with /dev/oprofile/active_domains:

* Use parameters instead of global variables to pass domain ids
around. Give those globals internal linkage.

* Allocate buffers dynamically to conserve stack space.

* Treat writes with size zero exactly like a write containing no
domain id. Before, zero-sized write was ignored, which is not the
same.

* Parse domain ids as unsigned numbers. Before, the first one was
parsed as signed number.

Because ispunct()-punctuation is ignored between domain ids, signs
are still silently ignored except for the first number. Hmm.

* Make parser accept whitespace as domain separator, because that's
what you get when reading the file.

* EINVAL on domain ids overflowing domid_t. Before, they were
silently truncated.

* EINVAL on too many domain ids. Before, the excess ones were
silently ignored.

* Reset active domains on failure halfway through setting them.

* Fix potential buffer overflow in adomain_read(). Couldn't really
happen because buffer was sufficient for current value of
MAX_OPROF_DOMAINS.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
author kaf24@firebug.cl.cam.ac.uk
date Tue May 16 14:07:56 2006 +0100 (2006-05-16)
parents 4fd6ba0e3336
children 23591d2c46aa
line source
1 /**
2 * @file xenoprof.c
3 *
4 * @remark Copyright 2002 OProfile authors
5 * @remark Read the file COPYING
6 *
7 * @author John Levon <levon@movementarian.org>
8 *
9 * Modified by Aravind Menon and Jose Renato Santos for Xen
10 * These modifications are:
11 * Copyright (C) 2005 Hewlett-Packard Co.
12 */
14 #include <linux/init.h>
15 #include <linux/notifier.h>
16 #include <linux/smp.h>
17 #include <linux/oprofile.h>
18 #include <linux/sysdev.h>
19 #include <linux/slab.h>
20 #include <linux/interrupt.h>
21 #include <linux/vmalloc.h>
22 #include <asm/nmi.h>
23 #include <asm/msr.h>
24 #include <asm/apic.h>
25 #include <asm/pgtable.h>
26 #include <xen/evtchn.h>
27 #include "op_counter.h"
29 #include <xen/interface/xen.h>
30 #include <xen/interface/xenoprof.h>
32 static int xenoprof_start(void);
33 static void xenoprof_stop(void);
35 void * vm_map_xen_pages(unsigned long maddr, int vm_size, pgprot_t prot);
37 static int xenoprof_enabled = 0;
38 static unsigned int num_events = 0;
39 static int is_primary = 0;
40 static int active_defined;
42 /* sample buffers shared with Xen */
43 xenoprof_buf_t * xenoprof_buf[MAX_VIRT_CPUS];
44 /* Shared buffer area */
45 char * shared_buffer;
46 /* Number of buffers in shared area (one per VCPU) */
47 int nbuf;
48 /* Mappings of VIRQ_XENOPROF to irq number (per cpu) */
49 int ovf_irq[NR_CPUS];
50 /* cpu model type string - copied from Xen memory space on XENOPROF_init command */
51 char cpu_type[XENOPROF_CPU_TYPE_SIZE];
53 #ifdef CONFIG_PM
55 static int xenoprof_suspend(struct sys_device * dev, pm_message_t state)
56 {
57 if (xenoprof_enabled == 1)
58 xenoprof_stop();
59 return 0;
60 }
63 static int xenoprof_resume(struct sys_device * dev)
64 {
65 if (xenoprof_enabled == 1)
66 xenoprof_start();
67 return 0;
68 }
71 static struct sysdev_class oprofile_sysclass = {
72 set_kset_name("oprofile"),
73 .resume = xenoprof_resume,
74 .suspend = xenoprof_suspend
75 };
78 static struct sys_device device_oprofile = {
79 .id = 0,
80 .cls = &oprofile_sysclass,
81 };
84 static int __init init_driverfs(void)
85 {
86 int error;
87 if (!(error = sysdev_class_register(&oprofile_sysclass)))
88 error = sysdev_register(&device_oprofile);
89 return error;
90 }
93 static void __exit exit_driverfs(void)
94 {
95 sysdev_unregister(&device_oprofile);
96 sysdev_class_unregister(&oprofile_sysclass);
97 }
99 #else
100 #define init_driverfs() do { } while (0)
101 #define exit_driverfs() do { } while (0)
102 #endif /* CONFIG_PM */
104 unsigned long long oprofile_samples = 0;
106 static irqreturn_t
107 xenoprof_ovf_interrupt(int irq, void * dev_id, struct pt_regs * regs)
108 {
109 int head, tail, size;
110 struct xenoprof_buf * buf;
111 int cpu;
113 cpu = smp_processor_id();
114 buf = xenoprof_buf[cpu];
116 head = buf->event_head;
117 tail = buf->event_tail;
118 size = buf->event_size;
120 if (tail > head) {
121 while (tail < size) {
122 oprofile_add_pc(buf->event_log[tail].eip,
123 buf->event_log[tail].mode,
124 buf->event_log[tail].event);
125 oprofile_samples++;
126 tail++;
127 }
128 tail = 0;
129 }
130 while (tail < head) {
131 oprofile_add_pc(buf->event_log[tail].eip,
132 buf->event_log[tail].mode,
133 buf->event_log[tail].event);
134 oprofile_samples++;
135 tail++;
136 }
138 buf->event_tail = tail;
140 return IRQ_HANDLED;
141 }
144 static void unbind_virq(void)
145 {
146 int i;
148 for_each_cpu(i) {
149 if (ovf_irq[i] >= 0) {
150 unbind_from_irqhandler(ovf_irq[i], NULL);
151 ovf_irq[i] = -1;
152 }
153 }
154 }
157 static int bind_virq(void)
158 {
159 int i, result;
161 for_each_cpu(i) {
162 result = bind_virq_to_irqhandler(VIRQ_XENOPROF,
163 i,
164 xenoprof_ovf_interrupt,
165 SA_INTERRUPT,
166 "xenoprof",
167 NULL);
169 if (result < 0) {
170 unbind_virq();
171 return result;
172 }
174 ovf_irq[i] = result;
175 }
177 return 0;
178 }
181 static int xenoprof_setup(void)
182 {
183 int ret;
184 int i;
186 ret = bind_virq();
187 if (ret)
188 return ret;
190 if (is_primary) {
191 struct xenoprof_counter counter;
193 /* Define dom0 as an active domain if not done yet */
194 if (!active_defined) {
195 domid_t domid;
196 ret = HYPERVISOR_xenoprof_op(XENOPROF_reset_active_list, NULL);
197 if (ret)
198 goto err;
199 domid = 0;
200 ret = HYPERVISOR_xenoprof_op(XENOPROF_set_active, &domid);
201 if (ret)
202 goto err;
203 active_defined = 1;
204 }
206 ret = HYPERVISOR_xenoprof_op(XENOPROF_reserve_counters, NULL);
207 if (ret)
208 goto err;
209 for (i=0; i<num_events; i++) {
210 counter.ind = i;
211 counter.count = (uint64_t)counter_config[i].count;
212 counter.enabled = (uint32_t)counter_config[i].enabled;
213 counter.event = (uint32_t)counter_config[i].event;
214 counter.kernel = (uint32_t)counter_config[i].kernel;
215 counter.user = (uint32_t)counter_config[i].user;
216 counter.unit_mask = (uint64_t)counter_config[i].unit_mask;
217 HYPERVISOR_xenoprof_op(XENOPROF_counter,
218 &counter);
219 }
220 ret = HYPERVISOR_xenoprof_op(XENOPROF_setup_events, NULL);
222 if (ret)
223 goto err;
224 }
226 ret = HYPERVISOR_xenoprof_op(XENOPROF_enable_virq, NULL);
227 if (ret)
228 goto err;
230 xenoprof_enabled = 1;
231 return 0;
232 err:
233 unbind_virq();
234 return ret;
235 }
238 static void xenoprof_shutdown(void)
239 {
240 xenoprof_enabled = 0;
242 HYPERVISOR_xenoprof_op(XENOPROF_disable_virq, NULL);
244 if (is_primary) {
245 HYPERVISOR_xenoprof_op(XENOPROF_release_counters, NULL);
246 active_defined = 0;
247 }
249 unbind_virq();
251 }
254 static int xenoprof_start(void)
255 {
256 int ret = 0;
258 if (is_primary)
259 ret = HYPERVISOR_xenoprof_op(XENOPROF_start, NULL);
261 return ret;
262 }
265 static void xenoprof_stop(void)
266 {
267 if (is_primary)
268 HYPERVISOR_xenoprof_op(XENOPROF_stop, NULL);
269 }
272 static int xenoprof_set_active(int * active_domains,
273 unsigned int adomains)
274 {
275 int ret = 0;
276 int i;
277 int set_dom0 = 0;
278 domid_t domid;
280 if (!is_primary)
281 return 0;
283 if (adomains > MAX_OPROF_DOMAINS)
284 return -E2BIG;
286 ret = HYPERVISOR_xenoprof_op(XENOPROF_reset_active_list, NULL);
287 if (ret)
288 return ret;
290 for (i=0; i<adomains; i++) {
291 domid = active_domains[i];
292 if (domid != active_domains[i]) {
293 ret = -EINVAL;
294 goto out;
295 }
296 ret = HYPERVISOR_xenoprof_op(XENOPROF_set_active, &domid);
297 if (ret)
298 goto out;
299 if (active_domains[i] == 0)
300 set_dom0 = 1;
301 }
302 /* dom0 must always be active but may not be in the list */
303 if (!set_dom0) {
304 domid = 0;
305 ret = HYPERVISOR_xenoprof_op(XENOPROF_set_active, &domid);
306 }
308 out:
309 if (ret)
310 HYPERVISOR_xenoprof_op(XENOPROF_reset_active_list, NULL);
311 active_defined = !ret;
312 return ret;
313 }
316 struct op_counter_config counter_config[OP_MAX_COUNTER];
318 static int xenoprof_create_files(struct super_block * sb, struct dentry * root)
319 {
320 unsigned int i;
322 for (i = 0; i < num_events; ++i) {
323 struct dentry * dir;
324 char buf[2];
326 snprintf(buf, 2, "%d", i);
327 dir = oprofilefs_mkdir(sb, root, buf);
328 oprofilefs_create_ulong(sb, dir, "enabled",
329 &counter_config[i].enabled);
330 oprofilefs_create_ulong(sb, dir, "event",
331 &counter_config[i].event);
332 oprofilefs_create_ulong(sb, dir, "count",
333 &counter_config[i].count);
334 oprofilefs_create_ulong(sb, dir, "unit_mask",
335 &counter_config[i].unit_mask);
336 oprofilefs_create_ulong(sb, dir, "kernel",
337 &counter_config[i].kernel);
338 oprofilefs_create_ulong(sb, dir, "user",
339 &counter_config[i].user);
340 }
342 return 0;
343 }
346 struct oprofile_operations xenoprof_ops = {
347 .create_files = xenoprof_create_files,
348 .set_active = xenoprof_set_active,
349 .setup = xenoprof_setup,
350 .shutdown = xenoprof_shutdown,
351 .start = xenoprof_start,
352 .stop = xenoprof_stop
353 };
356 /* in order to get driverfs right */
357 static int using_xenoprof;
359 int __init oprofile_arch_init(struct oprofile_operations * ops)
360 {
361 struct xenoprof_init init;
362 struct xenoprof_buf * buf;
363 int vm_size;
364 int npages;
365 int ret;
366 int i;
368 init.max_samples = 16;
369 ret = HYPERVISOR_xenoprof_op(XENOPROF_init, &init);
371 if (!ret) {
372 pgprot_t prot = __pgprot(_KERNPG_TABLE);
374 num_events = init.num_events;
375 is_primary = init.is_primary;
376 nbuf = init.nbuf;
378 /* just in case - make sure we do not overflow event list
379 (i.e. counter_config list) */
380 if (num_events > OP_MAX_COUNTER)
381 num_events = OP_MAX_COUNTER;
383 npages = (init.bufsize * nbuf - 1) / PAGE_SIZE + 1;
384 vm_size = npages * PAGE_SIZE;
386 shared_buffer = (char *)vm_map_xen_pages(init.buf_maddr,
387 vm_size, prot);
388 if (!shared_buffer) {
389 ret = -ENOMEM;
390 goto out;
391 }
393 for (i=0; i< nbuf; i++) {
394 buf = (struct xenoprof_buf*)
395 &shared_buffer[i * init.bufsize];
396 BUG_ON(buf->vcpu_id >= MAX_VIRT_CPUS);
397 xenoprof_buf[buf->vcpu_id] = buf;
398 }
400 /* cpu_type is detected by Xen */
401 cpu_type[XENOPROF_CPU_TYPE_SIZE-1] = 0;
402 strncpy(cpu_type, init.cpu_type, XENOPROF_CPU_TYPE_SIZE - 1);
403 xenoprof_ops.cpu_type = cpu_type;
405 init_driverfs();
406 using_xenoprof = 1;
407 *ops = xenoprof_ops;
409 for (i=0; i<NR_CPUS; i++)
410 ovf_irq[i] = -1;
412 active_defined = 0;
413 }
414 out:
415 printk(KERN_INFO "oprofile_arch_init: ret %d, events %d, "
416 "is_primary %d\n", ret, num_events, is_primary);
417 return ret;
418 }
421 void __exit oprofile_arch_exit(void)
422 {
423 if (using_xenoprof)
424 exit_driverfs();
426 if (shared_buffer) {
427 vunmap(shared_buffer);
428 shared_buffer = NULL;
429 }
430 if (is_primary)
431 HYPERVISOR_xenoprof_op(XENOPROF_shutdown, NULL);
432 }