aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPekka Paalanen <pq@iki.fi>2008-05-12 15:20:57 -0400
committerThomas Gleixner <tglx@linutronix.de>2008-05-24 05:22:43 -0400
commitf984b51e0779a6dd30feedc41404013ca54e5d05 (patch)
treeae7e80f53707e9069a9607072a554b51c660075b
parentd61fc44853f46fb002228b18aa5f30db21fcd4ac (diff)
ftrace: add mmiotrace plugin
On Sat, 22 Mar 2008 13:07:47 +0100 Ingo Molnar <mingo@elte.hu> wrote: > > > i'd suggest the following: pull x86.git and sched-devel.git into a > > > single tree [the two will combine without rejects]. Then try to add a > > > kernel/tracing/trace_mmiotrace.c ftrace plugin. The trace_sysprof.c > > > plugin might be a good example. > > > > I did this and now I have mmiotrace enabled/disabled via the tracing > > framework (what do we call this, since ftrace is one of the tracers?). > > cool! could you send the patches for that? (even if they are not fully > functional yet) Patch attached in the end. Nice to see how much code disappeared. I tried to mark all the features I had to break with XXX-comments. Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/x86/Kconfig.debug3
-rw-r--r--arch/x86/kernel/mmiotrace/mmio-mod.c208
-rw-r--r--include/linux/mmiotrace.h6
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/trace_mmiotrace.c84
5 files changed, 123 insertions, 179 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index aa0d6462b1fc..7e4b8494078e 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -173,7 +173,8 @@ config MMIOTRACE_HOOKS
173 173
174config MMIOTRACE 174config MMIOTRACE
175 bool "Memory mapped IO tracing" 175 bool "Memory mapped IO tracing"
176 depends on DEBUG_KERNEL && RELAY && DEBUG_FS 176 depends on DEBUG_KERNEL && RELAY
177 select TRACING
177 select MMIOTRACE_HOOKS 178 select MMIOTRACE_HOOKS
178 default y 179 default y
179 help 180 help
diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c
index 738644061e4e..c7a67d7e482b 100644
--- a/arch/x86/kernel/mmiotrace/mmio-mod.c
+++ b/arch/x86/kernel/mmiotrace/mmio-mod.c
@@ -22,9 +22,8 @@
22#define DEBUG 1 22#define DEBUG 1
23 23
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/relay.h>
26#include <linux/debugfs.h> 25#include <linux/debugfs.h>
27#include <linux/proc_fs.h> 26#include <linux/uaccess.h>
28#include <asm/io.h> 27#include <asm/io.h>
29#include <linux/version.h> 28#include <linux/version.h>
30#include <linux/kallsyms.h> 29#include <linux/kallsyms.h>
@@ -63,18 +62,18 @@ static const size_t subbuf_size = 256*1024;
63static DEFINE_PER_CPU(struct trap_reason, pf_reason); 62static DEFINE_PER_CPU(struct trap_reason, pf_reason);
64static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace); 63static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace);
65 64
65#if 0 /* XXX: no way gather this info anymore */
66/* Access to this is not per-cpu. */ 66/* Access to this is not per-cpu. */
67static DEFINE_PER_CPU(atomic_t, dropped); 67static DEFINE_PER_CPU(atomic_t, dropped);
68#endif
68 69
69static struct dentry *dir; 70static struct dentry *dir;
70static struct dentry *enabled_file;
71static struct dentry *marker_file; 71static struct dentry *marker_file;
72 72
73static DEFINE_MUTEX(mmiotrace_mutex); 73static DEFINE_MUTEX(mmiotrace_mutex);
74static DEFINE_SPINLOCK(trace_lock); 74static DEFINE_SPINLOCK(trace_lock);
75static atomic_t mmiotrace_enabled; 75static atomic_t mmiotrace_enabled;
76static LIST_HEAD(trace_list); /* struct remap_trace */ 76static LIST_HEAD(trace_list); /* struct remap_trace */
77static struct rchan *chan;
78 77
79/* 78/*
80 * Locking in this file: 79 * Locking in this file:
@@ -93,36 +92,24 @@ static unsigned long filter_offset;
93static int nommiotrace; 92static int nommiotrace;
94static int ISA_trace; 93static int ISA_trace;
95static int trace_pc; 94static int trace_pc;
96static int enable_now;
97 95
98module_param(n_subbufs, uint, 0); 96module_param(n_subbufs, uint, 0);
99module_param(filter_offset, ulong, 0); 97module_param(filter_offset, ulong, 0);
100module_param(nommiotrace, bool, 0); 98module_param(nommiotrace, bool, 0);
101module_param(ISA_trace, bool, 0); 99module_param(ISA_trace, bool, 0);
102module_param(trace_pc, bool, 0); 100module_param(trace_pc, bool, 0);
103module_param(enable_now, bool, 0);
104 101
105MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128."); 102MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128.");
106MODULE_PARM_DESC(filter_offset, "Start address of traced mappings."); 103MODULE_PARM_DESC(filter_offset, "Start address of traced mappings.");
107MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing."); 104MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing.");
108MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range."); 105MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range.");
109MODULE_PARM_DESC(trace_pc, "Record address of faulting instructions."); 106MODULE_PARM_DESC(trace_pc, "Record address of faulting instructions.");
110MODULE_PARM_DESC(enable_now, "Start mmiotracing immediately on module load.");
111 107
112static bool is_enabled(void) 108static bool is_enabled(void)
113{ 109{
114 return atomic_read(&mmiotrace_enabled); 110 return atomic_read(&mmiotrace_enabled);
115} 111}
116 112
117static void record_timestamp(struct mm_io_header *header)
118{
119 struct timespec now;
120
121 getnstimeofday(&now);
122 header->sec = now.tv_sec;
123 header->nsec = now.tv_nsec;
124}
125
126/* 113/*
127 * Write callback for the debugfs entry: 114 * Write callback for the debugfs entry:
128 * Read a marker and write it to the mmio trace log 115 * Read a marker and write it to the mmio trace log
@@ -141,7 +128,6 @@ static ssize_t write_marker(struct file *file, const char __user *buffer,
141 headp = (struct mm_io_header *)event; 128 headp = (struct mm_io_header *)event;
142 headp->type = MMIO_MAGIC | (MMIO_MARKER << MMIO_OPCODE_SHIFT); 129 headp->type = MMIO_MAGIC | (MMIO_MARKER << MMIO_OPCODE_SHIFT);
143 headp->data_len = len; 130 headp->data_len = len;
144 record_timestamp(headp);
145 131
146 if (copy_from_user(event + sizeof(*headp), buffer, len)) { 132 if (copy_from_user(event + sizeof(*headp), buffer, len)) {
147 kfree(event); 133 kfree(event);
@@ -149,9 +135,11 @@ static ssize_t write_marker(struct file *file, const char __user *buffer,
149 } 135 }
150 136
151 spin_lock_irq(&trace_lock); 137 spin_lock_irq(&trace_lock);
138#if 0 /* XXX: convert this to use tracing */
152 if (is_enabled()) 139 if (is_enabled())
153 relay_write(chan, event, sizeof(*headp) + len); 140 relay_write(chan, event, sizeof(*headp) + len);
154 else 141 else
142#endif
155 len = -EINVAL; 143 len = -EINVAL;
156 spin_unlock_irq(&trace_lock); 144 spin_unlock_irq(&trace_lock);
157 kfree(event); 145 kfree(event);
@@ -242,7 +230,11 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs,
242 else 230 else
243 my_trace->rw.pc = 0; 231 my_trace->rw.pc = 0;
244 232
245 record_timestamp(&my_trace->header); 233 /*
234 * XXX: the timestamp recorded will be *after* the tracing has been
235 * done, not at the time we hit the instruction. SMP implications
236 * on event ordering?
237 */
246 238
247 switch (type) { 239 switch (type) {
248 case REG_READ: 240 case REG_READ:
@@ -295,77 +287,19 @@ static void post(struct kmmio_probe *p, unsigned long condition,
295 default: 287 default:
296 break; 288 break;
297 } 289 }
298 relay_write(chan, my_trace, sizeof(*my_trace)); 290
291 /*
292 * XXX: Several required values are ignored:
293 * - mapping id
294 * - program counter
295 * Also the address should be physical, not virtual.
296 */
297 mmio_trace_record(my_trace->header.type, my_trace->rw.address,
298 my_trace->rw.value);
299 put_cpu_var(cpu_trace); 299 put_cpu_var(cpu_trace);
300 put_cpu_var(pf_reason); 300 put_cpu_var(pf_reason);
301} 301}
302 302
303/*
304 * subbuf_start() relay callback.
305 *
306 * Defined so that we know when events are dropped due to the buffer-full
307 * condition.
308 */
309static int subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
310 void *prev_subbuf, size_t prev_padding)
311{
312 unsigned int cpu = buf->cpu;
313 atomic_t *drop = &per_cpu(dropped, cpu);
314 int count;
315 if (relay_buf_full(buf)) {
316 if (atomic_inc_return(drop) == 1)
317 pr_err(NAME "cpu %d buffer full!\n", cpu);
318 return 0;
319 }
320 count = atomic_read(drop);
321 if (count) {
322 pr_err(NAME "cpu %d buffer no longer full, missed %d events.\n",
323 cpu, count);
324 atomic_sub(count, drop);
325 }
326
327 return 1;
328}
329
330static struct file_operations mmio_fops = {
331 .owner = THIS_MODULE,
332};
333
334/* file_create() callback. Creates relay file in debugfs. */
335static struct dentry *create_buf_file_handler(const char *filename,
336 struct dentry *parent,
337 int mode,
338 struct rchan_buf *buf,
339 int *is_global)
340{
341 struct dentry *buf_file;
342
343 mmio_fops.read = relay_file_operations.read;
344 mmio_fops.open = relay_file_operations.open;
345 mmio_fops.poll = relay_file_operations.poll;
346 mmio_fops.mmap = relay_file_operations.mmap;
347 mmio_fops.release = relay_file_operations.release;
348 mmio_fops.splice_read = relay_file_operations.splice_read;
349
350 buf_file = debugfs_create_file(filename, mode, parent, buf,
351 &mmio_fops);
352
353 return buf_file;
354}
355
356/* file_remove() default callback. Removes relay file in debugfs. */
357static int remove_buf_file_handler(struct dentry *dentry)
358{
359 debugfs_remove(dentry);
360 return 0;
361}
362
363static struct rchan_callbacks relay_callbacks = {
364 .subbuf_start = subbuf_start_handler,
365 .create_buf_file = create_buf_file_handler,
366 .remove_buf_file = remove_buf_file_handler,
367};
368
369static void ioremap_trace_core(unsigned long offset, unsigned long size, 303static void ioremap_trace_core(unsigned long offset, unsigned long size,
370 void __iomem *addr) 304 void __iomem *addr)
371{ 305{
@@ -387,7 +321,6 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size,
387 .pc = 0 321 .pc = 0
388 } 322 }
389 }; 323 };
390 record_timestamp(&event.header);
391 324
392 if (!trace) { 325 if (!trace) {
393 pr_err(NAME "kmalloc failed in ioremap\n"); 326 pr_err(NAME "kmalloc failed in ioremap\n");
@@ -410,7 +343,10 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size,
410 if (!is_enabled()) 343 if (!is_enabled())
411 goto not_enabled; 344 goto not_enabled;
412 345
413 relay_write(chan, &event, sizeof(event)); 346 /*
347 * XXX: Insufficient data recorded!
348 */
349 mmio_trace_record(event.header.type, event.map.addr, event.map.len);
414 list_add_tail(&trace->list, &trace_list); 350 list_add_tail(&trace->list, &trace_list);
415 if (!nommiotrace) 351 if (!nommiotrace)
416 register_kmmio_probe(&trace->probe); 352 register_kmmio_probe(&trace->probe);
@@ -454,7 +390,6 @@ static void iounmap_trace_core(volatile void __iomem *addr)
454 struct remap_trace *found_trace = NULL; 390 struct remap_trace *found_trace = NULL;
455 391
456 pr_debug(NAME "Unmapping %p.\n", addr); 392 pr_debug(NAME "Unmapping %p.\n", addr);
457 record_timestamp(&event.header);
458 393
459 spin_lock_irq(&trace_lock); 394 spin_lock_irq(&trace_lock);
460 if (!is_enabled()) 395 if (!is_enabled())
@@ -469,7 +404,8 @@ static void iounmap_trace_core(volatile void __iomem *addr)
469 break; 404 break;
470 } 405 }
471 } 406 }
472 relay_write(chan, &event, sizeof(event)); 407 mmio_trace_record(event.header.type, event.map.addr,
408 found_trace ? found_trace->id : -1);
473 409
474not_enabled: 410not_enabled:
475 spin_unlock_irq(&trace_lock); 411 spin_unlock_irq(&trace_lock);
@@ -512,77 +448,23 @@ static void clear_trace_list(void)
512 } 448 }
513} 449}
514 450
515static ssize_t read_enabled_file_bool(struct file *file,
516 char __user *user_buf, size_t count, loff_t *ppos)
517{
518 char buf[3];
519
520 if (is_enabled())
521 buf[0] = '1';
522 else
523 buf[0] = '0';
524 buf[1] = '\n';
525 buf[2] = '\0';
526 return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
527}
528
529static void enable_mmiotrace(void);
530static void disable_mmiotrace(void);
531
532static ssize_t write_enabled_file_bool(struct file *file,
533 const char __user *user_buf, size_t count, loff_t *ppos)
534{
535 char buf[32];
536 int buf_size = min(count, (sizeof(buf)-1));
537
538 if (copy_from_user(buf, user_buf, buf_size))
539 return -EFAULT;
540
541 switch (buf[0]) {
542 case 'y':
543 case 'Y':
544 case '1':
545 enable_mmiotrace();
546 break;
547 case 'n':
548 case 'N':
549 case '0':
550 disable_mmiotrace();
551 break;
552 }
553
554 return count;
555}
556
557/* this ripped from kernel/kprobes.c */
558static struct file_operations fops_enabled = {
559 .owner = THIS_MODULE,
560 .read = read_enabled_file_bool,
561 .write = write_enabled_file_bool
562};
563
564static struct file_operations fops_marker = { 451static struct file_operations fops_marker = {
565 .owner = THIS_MODULE, 452 .owner = THIS_MODULE,
566 .write = write_marker 453 .write = write_marker
567}; 454};
568 455
569static void enable_mmiotrace(void) 456void enable_mmiotrace(void)
570{ 457{
571 mutex_lock(&mmiotrace_mutex); 458 mutex_lock(&mmiotrace_mutex);
572 if (is_enabled()) 459 if (is_enabled())
573 goto out; 460 goto out;
574 461
575 chan = relay_open("cpu", dir, subbuf_size, n_subbufs,
576 &relay_callbacks, NULL);
577 if (!chan) {
578 pr_err(NAME "relay app channel creation failed.\n");
579 goto out;
580 }
581
582 reference_kmmio(); 462 reference_kmmio();
583 463
464#if 0 /* XXX: tracing does not support text entries */
584 marker_file = debugfs_create_file("marker", 0660, dir, NULL, 465 marker_file = debugfs_create_file("marker", 0660, dir, NULL,
585 &fops_marker); 466 &fops_marker);
467#endif
586 if (!marker_file) 468 if (!marker_file)
587 pr_err(NAME "marker file creation failed.\n"); 469 pr_err(NAME "marker file creation failed.\n");
588 470
@@ -598,7 +480,7 @@ out:
598 mutex_unlock(&mmiotrace_mutex); 480 mutex_unlock(&mmiotrace_mutex);
599} 481}
600 482
601static void disable_mmiotrace(void) 483void disable_mmiotrace(void)
602{ 484{
603 mutex_lock(&mmiotrace_mutex); 485 mutex_lock(&mmiotrace_mutex);
604 if (!is_enabled()) 486 if (!is_enabled())
@@ -615,17 +497,13 @@ static void disable_mmiotrace(void)
615 debugfs_remove(marker_file); 497 debugfs_remove(marker_file);
616 marker_file = NULL; 498 marker_file = NULL;
617 } 499 }
618 if (chan) {
619 relay_close(chan);
620 chan = NULL;
621 }
622 500
623 pr_info(NAME "disabled.\n"); 501 pr_info(NAME "disabled.\n");
624out: 502out:
625 mutex_unlock(&mmiotrace_mutex); 503 mutex_unlock(&mmiotrace_mutex);
626} 504}
627 505
628static int __init init(void) 506int __init init_mmiotrace(void)
629{ 507{
630 pr_debug(NAME "load...\n"); 508 pr_debug(NAME "load...\n");
631 if (n_subbufs < 2) 509 if (n_subbufs < 2)
@@ -636,31 +514,5 @@ static int __init init(void)
636 pr_err(NAME "Couldn't create relay app directory.\n"); 514 pr_err(NAME "Couldn't create relay app directory.\n");
637 return -ENOMEM; 515 return -ENOMEM;
638 } 516 }
639
640 enabled_file = debugfs_create_file("enabled", 0600, dir, NULL,
641 &fops_enabled);
642 if (!enabled_file) {
643 pr_err(NAME "Couldn't create enabled file.\n");
644 debugfs_remove(dir);
645 return -ENOMEM;
646 }
647
648 if (enable_now)
649 enable_mmiotrace();
650
651 return 0; 517 return 0;
652} 518}
653
654static void __exit cleanup(void)
655{
656 pr_debug(NAME "unload...\n");
657 if (enabled_file)
658 debugfs_remove(enabled_file);
659 disable_mmiotrace();
660 if (dir)
661 debugfs_remove(dir);
662}
663
664module_init(init);
665module_exit(cleanup);
666MODULE_LICENSE("GPL");
diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h
index cb5efd0c7f51..579b3b06c90e 100644
--- a/include/linux/mmiotrace.h
+++ b/include/linux/mmiotrace.h
@@ -54,6 +54,12 @@ static inline void mmiotrace_iounmap(volatile void __iomem *addr)
54} 54}
55#endif /* CONFIG_MMIOTRACE_HOOKS */ 55#endif /* CONFIG_MMIOTRACE_HOOKS */
56 56
57/* in kernel/trace/trace_mmiotrace.c */
58extern int __init init_mmiotrace(void);
59extern void enable_mmiotrace(void);
60extern void disable_mmiotrace(void);
61extern void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg);
62
57#endif /* __KERNEL__ */ 63#endif /* __KERNEL__ */
58 64
59 65
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index d9efbbfa2bdf..c44a7dce9086 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -18,5 +18,6 @@ obj-$(CONFIG_FTRACE) += trace_functions.o
18obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o 18obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o
19obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o 19obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o
20obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o 20obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o
21obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
21 22
22libftrace-y := ftrace.o 23libftrace-y := ftrace.o
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
new file mode 100644
index 000000000000..e4dd03cc5aa6
--- /dev/null
+++ b/kernel/trace/trace_mmiotrace.c
@@ -0,0 +1,84 @@
1/*
2 * Memory mapped I/O tracing
3 *
4 * Copyright (C) 2008 Pekka Paalanen <pq@iki.fi>
5 */
6
7#define DEBUG 1
8
9#include <linux/kernel.h>
10#include <linux/mmiotrace.h>
11
12#include "trace.h"
13
14extern void
15__trace_special(void *__tr, void *__data,
16 unsigned long arg1, unsigned long arg2, unsigned long arg3);
17
18static struct trace_array *mmio_trace_array;
19
20
21static void mmio_trace_init(struct trace_array *tr)
22{
23 pr_debug("in %s\n", __func__);
24 mmio_trace_array = tr;
25 if (tr->ctrl)
26 enable_mmiotrace();
27}
28
29static void mmio_trace_reset(struct trace_array *tr)
30{
31 pr_debug("in %s\n", __func__);
32 if (tr->ctrl)
33 disable_mmiotrace();
34}
35
36static void mmio_trace_ctrl_update(struct trace_array *tr)
37{
38 pr_debug("in %s\n", __func__);
39 if (tr->ctrl)
40 enable_mmiotrace();
41 else
42 disable_mmiotrace();
43}
44
45static struct tracer mmio_tracer __read_mostly =
46{
47 .name = "mmiotrace",
48 .init = mmio_trace_init,
49 .reset = mmio_trace_reset,
50 .ctrl_update = mmio_trace_ctrl_update,
51};
52
53__init static int init_mmio_trace(void)
54{
55 int ret = init_mmiotrace();
56 if (ret)
57 return ret;
58 return register_tracer(&mmio_tracer);
59}
60device_initcall(init_mmio_trace);
61
62void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg)
63{
64 struct trace_array *tr = mmio_trace_array;
65 struct trace_array_cpu *data = tr->data[smp_processor_id()];
66
67 if (!current || current->pid == 0) {
68 /*
69 * XXX: This is a problem. We need to able to record, no
70 * matter what. tracing_generic_entry_update() would crash.
71 */
72 static unsigned limit;
73 if (limit++ < 12)
74 pr_err("Error in %s: no current.\n", __func__);
75 return;
76 }
77 if (!tr || !data) {
78 static unsigned limit;
79 if (limit++ < 12)
80 pr_err("%s: no tr or data\n", __func__);
81 return;
82 }
83 __trace_special(tr, data, type, addr, arg);
84}