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:24:53 -0400
commitbd8ac686c73c7e925fcfe0b02dc4e7b947127864 (patch)
tree7a675c4919429b507bb8e7b09677d96ae30ec561
parentf984b51e0779a6dd30feedc41404013ca54e5d05 (diff)
ftrace: mmiotrace, updates
here is a patch that makes mmiotrace work almost well within the tracing framework. The patch applies on top of my previous patch. I have my own output formatting in place now. Summary of changes: - fix the NULL dereference that was due to not calling tracing_reset() - add print_line() callback into struct tracer - implement print_line() for mmiotrace, producing up-to-spec text - add my output header, but that is not really called in the right place - rewrote the main structs in mmiotrace - added two new trace entry types: TRACE_MMIO_RW and TRACE_MMIO_MAP - made some functions in trace.c non-static - check current==NULL in tracing_generic_entry_update() - fix(?) comparison in trace_seq_printf() Things seem to work fine except a few issues. Markers (text lines injected into mmiotrace log) are missing, I did not feel hacking them in before we have variable length entries. My output header is printed only for 'trace' file, but not 'trace_pipe'. For some reason, despite my quick fix, iter->trace is NULL in print_trace_line() when called from 'trace_pipe' file, which means I don't get proper output formatting. I only tried by loading nouveau.ko, which just detects the card, and that is traced fine. I didn't try further. Map, two reads and unmap. Works perfectly. I am missing the information about overflows, I'd prefer to have a counter for lost events. I didn't try, but I guess currently there is no way of knowning when it overflows? So, not too far from being fully operational, it seems :-) And looking at the diffstat, there also is some 700-900 lines of user space code that just became obsolete. Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r--arch/x86/Kconfig.debug2
-rw-r--r--arch/x86/kernel/mmiotrace/mmio-mod.c140
-rw-r--r--include/linux/mmiotrace.h85
-rw-r--r--kernel/trace/trace.c34
-rw-r--r--kernel/trace/trace.h14
-rw-r--r--kernel/trace/trace_mmiotrace.c151
6 files changed, 238 insertions, 188 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 7e4b8494078e..1d6de0d67f99 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -173,7 +173,7 @@ 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 176 depends on DEBUG_KERNEL
177 select TRACING 177 select TRACING
178 select MMIOTRACE_HOOKS 178 select MMIOTRACE_HOOKS
179 default y 179 default y
diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c
index c7a67d7e482b..62abc281a512 100644
--- a/arch/x86/kernel/mmiotrace/mmio-mod.c
+++ b/arch/x86/kernel/mmiotrace/mmio-mod.c
@@ -37,11 +37,6 @@
37 37
38#define NAME "mmiotrace: " 38#define NAME "mmiotrace: "
39 39
40/* This app's relay channel files will appear in /debug/mmio-trace */
41static const char APP_DIR[] = "mmio-trace";
42/* the marker injection file in /debug/APP_DIR */
43static const char MARKER_FILE[] = "mmio-marker";
44
45struct trap_reason { 40struct trap_reason {
46 unsigned long addr; 41 unsigned long addr;
47 unsigned long ip; 42 unsigned long ip;
@@ -56,18 +51,15 @@ struct remap_trace {
56 unsigned long id; 51 unsigned long id;
57}; 52};
58 53
59static const size_t subbuf_size = 256*1024;
60
61/* Accessed per-cpu. */ 54/* Accessed per-cpu. */
62static DEFINE_PER_CPU(struct trap_reason, pf_reason); 55static DEFINE_PER_CPU(struct trap_reason, pf_reason);
63static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace); 56static DEFINE_PER_CPU(struct mmiotrace_rw, cpu_trace);
64 57
65#if 0 /* XXX: no way gather this info anymore */ 58#if 0 /* XXX: no way gather this info anymore */
66/* Access to this is not per-cpu. */ 59/* Access to this is not per-cpu. */
67static DEFINE_PER_CPU(atomic_t, dropped); 60static DEFINE_PER_CPU(atomic_t, dropped);
68#endif 61#endif
69 62
70static struct dentry *dir;
71static struct dentry *marker_file; 63static struct dentry *marker_file;
72 64
73static DEFINE_MUTEX(mmiotrace_mutex); 65static DEFINE_MUTEX(mmiotrace_mutex);
@@ -82,24 +74,21 @@ static LIST_HEAD(trace_list); /* struct remap_trace */
82 * and trace_lock. 74 * and trace_lock.
83 * - Routines depending on is_enabled() must take trace_lock. 75 * - Routines depending on is_enabled() must take trace_lock.
84 * - trace_list users must hold trace_lock. 76 * - trace_list users must hold trace_lock.
85 * - is_enabled() guarantees that chan is valid. 77 * - is_enabled() guarantees that mmio_trace_record is allowed.
86 * - pre/post callbacks assume the effect of is_enabled() being true. 78 * - pre/post callbacks assume the effect of is_enabled() being true.
87 */ 79 */
88 80
89/* module parameters */ 81/* module parameters */
90static unsigned int n_subbufs = 32*4;
91static unsigned long filter_offset; 82static unsigned long filter_offset;
92static int nommiotrace; 83static int nommiotrace;
93static int ISA_trace; 84static int ISA_trace;
94static int trace_pc; 85static int trace_pc;
95 86
96module_param(n_subbufs, uint, 0);
97module_param(filter_offset, ulong, 0); 87module_param(filter_offset, ulong, 0);
98module_param(nommiotrace, bool, 0); 88module_param(nommiotrace, bool, 0);
99module_param(ISA_trace, bool, 0); 89module_param(ISA_trace, bool, 0);
100module_param(trace_pc, bool, 0); 90module_param(trace_pc, bool, 0);
101 91
102MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128.");
103MODULE_PARM_DESC(filter_offset, "Start address of traced mappings."); 92MODULE_PARM_DESC(filter_offset, "Start address of traced mappings.");
104MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing."); 93MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing.");
105MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range."); 94MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range.");
@@ -110,6 +99,7 @@ static bool is_enabled(void)
110 return atomic_read(&mmiotrace_enabled); 99 return atomic_read(&mmiotrace_enabled);
111} 100}
112 101
102#if 0 /* XXX: needs rewrite */
113/* 103/*
114 * Write callback for the debugfs entry: 104 * Write callback for the debugfs entry:
115 * Read a marker and write it to the mmio trace log 105 * Read a marker and write it to the mmio trace log
@@ -145,6 +135,7 @@ static ssize_t write_marker(struct file *file, const char __user *buffer,
145 kfree(event); 135 kfree(event);
146 return len; 136 return len;
147} 137}
138#endif
148 139
149static void print_pte(unsigned long address) 140static void print_pte(unsigned long address)
150{ 141{
@@ -198,9 +189,10 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs,
198 unsigned long addr) 189 unsigned long addr)
199{ 190{
200 struct trap_reason *my_reason = &get_cpu_var(pf_reason); 191 struct trap_reason *my_reason = &get_cpu_var(pf_reason);
201 struct mm_io_header_rw *my_trace = &get_cpu_var(cpu_trace); 192 struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace);
202 const unsigned long instptr = instruction_pointer(regs); 193 const unsigned long instptr = instruction_pointer(regs);
203 const enum reason_type type = get_ins_type(instptr); 194 const enum reason_type type = get_ins_type(instptr);
195 struct remap_trace *trace = p->user_data;
204 196
205 /* it doesn't make sense to have more than one active trace per cpu */ 197 /* it doesn't make sense to have more than one active trace per cpu */
206 if (my_reason->active_traces) 198 if (my_reason->active_traces)
@@ -212,23 +204,17 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs,
212 my_reason->addr = addr; 204 my_reason->addr = addr;
213 my_reason->ip = instptr; 205 my_reason->ip = instptr;
214 206
215 my_trace->header.type = MMIO_MAGIC; 207 my_trace->phys = addr - trace->probe.addr + trace->phys;
216 my_trace->header.pid = 0; 208 my_trace->map_id = trace->id;
217 my_trace->header.data_len = sizeof(struct mm_io_rw);
218 my_trace->rw.address = addr;
219 /*
220 * struct remap_trace *trace = p->user_data;
221 * phys = addr - trace->probe.addr + trace->phys;
222 */
223 209
224 /* 210 /*
225 * Only record the program counter when requested. 211 * Only record the program counter when requested.
226 * It may taint clean-room reverse engineering. 212 * It may taint clean-room reverse engineering.
227 */ 213 */
228 if (trace_pc) 214 if (trace_pc)
229 my_trace->rw.pc = instptr; 215 my_trace->pc = instptr;
230 else 216 else
231 my_trace->rw.pc = 0; 217 my_trace->pc = 0;
232 218
233 /* 219 /*
234 * XXX: the timestamp recorded will be *after* the tracing has been 220 * XXX: the timestamp recorded will be *after* the tracing has been
@@ -238,28 +224,25 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs,
238 224
239 switch (type) { 225 switch (type) {
240 case REG_READ: 226 case REG_READ:
241 my_trace->header.type |= 227 my_trace->opcode = MMIO_READ;
242 (MMIO_READ << MMIO_OPCODE_SHIFT) | 228 my_trace->width = get_ins_mem_width(instptr);
243 (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT);
244 break; 229 break;
245 case REG_WRITE: 230 case REG_WRITE:
246 my_trace->header.type |= 231 my_trace->opcode = MMIO_WRITE;
247 (MMIO_WRITE << MMIO_OPCODE_SHIFT) | 232 my_trace->width = get_ins_mem_width(instptr);
248 (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); 233 my_trace->value = get_ins_reg_val(instptr, regs);
249 my_trace->rw.value = get_ins_reg_val(instptr, regs);
250 break; 234 break;
251 case IMM_WRITE: 235 case IMM_WRITE:
252 my_trace->header.type |= 236 my_trace->opcode = MMIO_WRITE;
253 (MMIO_WRITE << MMIO_OPCODE_SHIFT) | 237 my_trace->width = get_ins_mem_width(instptr);
254 (get_ins_mem_width(instptr) << MMIO_WIDTH_SHIFT); 238 my_trace->value = get_ins_imm_val(instptr);
255 my_trace->rw.value = get_ins_imm_val(instptr);
256 break; 239 break;
257 default: 240 default:
258 { 241 {
259 unsigned char *ip = (unsigned char *)instptr; 242 unsigned char *ip = (unsigned char *)instptr;
260 my_trace->header.type |= 243 my_trace->opcode = MMIO_UNKNOWN_OP;
261 (MMIO_UNKNOWN_OP << MMIO_OPCODE_SHIFT); 244 my_trace->width = 0;
262 my_trace->rw.value = (*ip) << 16 | *(ip + 1) << 8 | 245 my_trace->value = (*ip) << 16 | *(ip + 1) << 8 |
263 *(ip + 2); 246 *(ip + 2);
264 } 247 }
265 } 248 }
@@ -271,7 +254,7 @@ static void post(struct kmmio_probe *p, unsigned long condition,
271 struct pt_regs *regs) 254 struct pt_regs *regs)
272{ 255{
273 struct trap_reason *my_reason = &get_cpu_var(pf_reason); 256 struct trap_reason *my_reason = &get_cpu_var(pf_reason);
274 struct mm_io_header_rw *my_trace = &get_cpu_var(cpu_trace); 257 struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace);
275 258
276 /* this should always return the active_trace count to 0 */ 259 /* this should always return the active_trace count to 0 */
277 my_reason->active_traces--; 260 my_reason->active_traces--;
@@ -282,20 +265,13 @@ static void post(struct kmmio_probe *p, unsigned long condition,
282 265
283 switch (my_reason->type) { 266 switch (my_reason->type) {
284 case REG_READ: 267 case REG_READ:
285 my_trace->rw.value = get_ins_reg_val(my_reason->ip, regs); 268 my_trace->value = get_ins_reg_val(my_reason->ip, regs);
286 break; 269 break;
287 default: 270 default:
288 break; 271 break;
289 } 272 }
290 273
291 /* 274 mmio_trace_rw(my_trace);
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); 275 put_cpu_var(cpu_trace);
300 put_cpu_var(pf_reason); 276 put_cpu_var(pf_reason);
301} 277}
@@ -305,21 +281,11 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size,
305{ 281{
306 static atomic_t next_id; 282 static atomic_t next_id;
307 struct remap_trace *trace = kmalloc(sizeof(*trace), GFP_KERNEL); 283 struct remap_trace *trace = kmalloc(sizeof(*trace), GFP_KERNEL);
308 struct mm_io_header_map event = { 284 struct mmiotrace_map map = {
309 .header = { 285 .phys = offset,
310 .type = MMIO_MAGIC | 286 .virt = (unsigned long)addr,
311 (MMIO_PROBE << MMIO_OPCODE_SHIFT), 287 .len = size,
312 .sec = 0, 288 .opcode = MMIO_PROBE
313 .nsec = 0,
314 .pid = 0,
315 .data_len = sizeof(struct mm_io_map)
316 },
317 .map = {
318 .phys = offset,
319 .addr = (unsigned long)addr,
320 .len = size,
321 .pc = 0
322 }
323 }; 289 };
324 290
325 if (!trace) { 291 if (!trace) {
@@ -338,15 +304,13 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size,
338 .phys = offset, 304 .phys = offset,
339 .id = atomic_inc_return(&next_id) 305 .id = atomic_inc_return(&next_id)
340 }; 306 };
307 map.map_id = trace->id;
341 308
342 spin_lock_irq(&trace_lock); 309 spin_lock_irq(&trace_lock);
343 if (!is_enabled()) 310 if (!is_enabled())
344 goto not_enabled; 311 goto not_enabled;
345 312
346 /* 313 mmio_trace_mapping(&map);
347 * XXX: Insufficient data recorded!
348 */
349 mmio_trace_record(event.header.type, event.map.addr, event.map.len);
350 list_add_tail(&trace->list, &trace_list); 314 list_add_tail(&trace->list, &trace_list);
351 if (!nommiotrace) 315 if (!nommiotrace)
352 register_kmmio_probe(&trace->probe); 316 register_kmmio_probe(&trace->probe);
@@ -369,21 +333,11 @@ mmiotrace_ioremap(unsigned long offset, unsigned long size, void __iomem *addr)
369 333
370static void iounmap_trace_core(volatile void __iomem *addr) 334static void iounmap_trace_core(volatile void __iomem *addr)
371{ 335{
372 struct mm_io_header_map event = { 336 struct mmiotrace_map map = {
373 .header = { 337 .phys = 0,
374 .type = MMIO_MAGIC | 338 .virt = (unsigned long)addr,
375 (MMIO_UNPROBE << MMIO_OPCODE_SHIFT), 339 .len = 0,
376 .sec = 0, 340 .opcode = MMIO_UNPROBE
377 .nsec = 0,
378 .pid = 0,
379 .data_len = sizeof(struct mm_io_map)
380 },
381 .map = {
382 .phys = 0,
383 .addr = (unsigned long)addr,
384 .len = 0,
385 .pc = 0
386 }
387 }; 341 };
388 struct remap_trace *trace; 342 struct remap_trace *trace;
389 struct remap_trace *tmp; 343 struct remap_trace *tmp;
@@ -404,8 +358,8 @@ static void iounmap_trace_core(volatile void __iomem *addr)
404 break; 358 break;
405 } 359 }
406 } 360 }
407 mmio_trace_record(event.header.type, event.map.addr, 361 map.map_id = (found_trace) ? found_trace->id : -1;
408 found_trace ? found_trace->id : -1); 362 mmio_trace_mapping(&map);
409 363
410not_enabled: 364not_enabled:
411 spin_unlock_irq(&trace_lock); 365 spin_unlock_irq(&trace_lock);
@@ -448,10 +402,12 @@ static void clear_trace_list(void)
448 } 402 }
449} 403}
450 404
405#if 0 /* XXX: out of order */
451static struct file_operations fops_marker = { 406static struct file_operations fops_marker = {
452 .owner = THIS_MODULE, 407 .owner = THIS_MODULE,
453 .write = write_marker 408 .write = write_marker
454}; 409};
410#endif
455 411
456void enable_mmiotrace(void) 412void enable_mmiotrace(void)
457{ 413{
@@ -464,9 +420,9 @@ void enable_mmiotrace(void)
464#if 0 /* XXX: tracing does not support text entries */ 420#if 0 /* XXX: tracing does not support text entries */
465 marker_file = debugfs_create_file("marker", 0660, dir, NULL, 421 marker_file = debugfs_create_file("marker", 0660, dir, NULL,
466 &fops_marker); 422 &fops_marker);
467#endif
468 if (!marker_file) 423 if (!marker_file)
469 pr_err(NAME "marker file creation failed.\n"); 424 pr_err(NAME "marker file creation failed.\n");
425#endif
470 426
471 if (nommiotrace) 427 if (nommiotrace)
472 pr_info(NAME "MMIO tracing disabled.\n"); 428 pr_info(NAME "MMIO tracing disabled.\n");
@@ -502,17 +458,3 @@ void disable_mmiotrace(void)
502out: 458out:
503 mutex_unlock(&mmiotrace_mutex); 459 mutex_unlock(&mmiotrace_mutex);
504} 460}
505
506int __init init_mmiotrace(void)
507{
508 pr_debug(NAME "load...\n");
509 if (n_subbufs < 2)
510 return -EINVAL;
511
512 dir = debugfs_create_dir(APP_DIR, NULL);
513 if (!dir) {
514 pr_err(NAME "Couldn't create relay app directory.\n");
515 return -ENOMEM;
516 }
517 return 0;
518}
diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h
index 579b3b06c90e..c88a9c197d22 100644
--- a/include/linux/mmiotrace.h
+++ b/include/linux/mmiotrace.h
@@ -54,73 +54,38 @@ 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 */ 57enum mm_io_opcode {
58extern int __init init_mmiotrace(void); 58 MMIO_READ = 0x1, /* struct mmiotrace_rw */
59extern void enable_mmiotrace(void); 59 MMIO_WRITE = 0x2, /* struct mmiotrace_rw */
60extern void disable_mmiotrace(void); 60 MMIO_PROBE = 0x3, /* struct mmiotrace_map */
61extern void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg); 61 MMIO_UNPROBE = 0x4, /* struct mmiotrace_map */
62
63#endif /* __KERNEL__ */
64
65
66/*
67 * If you change anything here, you must bump MMIO_VERSION.
68 * This is the relay data format for user space.
69 */
70#define MMIO_VERSION 0x04
71
72/* mm_io_header.type */
73#define MMIO_OPCODE_MASK 0xff
74#define MMIO_OPCODE_SHIFT 0
75#define MMIO_WIDTH_MASK 0xff00
76#define MMIO_WIDTH_SHIFT 8
77#define MMIO_MAGIC (0x6f000000 | (MMIO_VERSION<<16))
78#define MMIO_MAGIC_MASK 0xffff0000
79
80enum mm_io_opcode { /* payload type: */
81 MMIO_READ = 0x1, /* struct mm_io_rw */
82 MMIO_WRITE = 0x2, /* struct mm_io_rw */
83 MMIO_PROBE = 0x3, /* struct mm_io_map */
84 MMIO_UNPROBE = 0x4, /* struct mm_io_map */
85 MMIO_MARKER = 0x5, /* raw char data */ 62 MMIO_MARKER = 0x5, /* raw char data */
86 MMIO_UNKNOWN_OP = 0x6, /* struct mm_io_rw */ 63 MMIO_UNKNOWN_OP = 0x6, /* struct mmiotrace_rw */
87}; 64};
88 65
89struct mm_io_header { 66struct mmiotrace_rw {
90 __u32 type; /* see MMIO_* macros above */ 67 unsigned long phys; /* PCI address of register */
91 __u32 sec; /* timestamp */ 68 unsigned long value;
92 __u32 nsec; 69 unsigned long pc; /* optional program counter */
93 __u32 pid; /* PID of the process, or 0 for kernel core */ 70 int map_id;
94 __u16 data_len; /* length of the following payload */ 71 unsigned char opcode; /* one of MMIO_{READ,WRITE,UNKNOWN_OP} */
72 unsigned char width; /* size of register access in bytes */
95}; 73};
96 74
97struct mm_io_rw { 75struct mmiotrace_map {
98 __u64 address; /* virtual address of register */ 76 unsigned long phys; /* base address in PCI space */
99 __u64 value; 77 unsigned long virt; /* base virtual address */
100 __u64 pc; /* optional program counter */ 78 unsigned long len; /* mapping size */
79 int map_id;
80 unsigned char opcode; /* MMIO_PROBE or MMIO_UNPROBE */
101}; 81};
102 82
103struct mm_io_map { 83/* in kernel/trace/trace_mmiotrace.c */
104 __u64 phys; /* base address in PCI space */ 84extern void enable_mmiotrace(void);
105 __u64 addr; /* base virtual address */ 85extern void disable_mmiotrace(void);
106 __u64 len; /* mapping size */ 86extern void mmio_trace_rw(struct mmiotrace_rw *rw);
107 __u64 pc; /* optional program counter */ 87extern void mmio_trace_mapping(struct mmiotrace_map *map);
108};
109
110
111/*
112 * These structures are used to allow a single relay_write()
113 * call to write a full packet.
114 */
115
116struct mm_io_header_rw {
117 struct mm_io_header header;
118 struct mm_io_rw rw;
119} __attribute__((packed));
120 88
121struct mm_io_header_map { 89#endif /* __KERNEL__ */
122 struct mm_io_header header;
123 struct mm_io_map map;
124} __attribute__((packed));
125 90
126#endif /* MMIOTRACE_H */ 91#endif /* MMIOTRACE_H */
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 3271916ff033..d14fe49e9638 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -831,6 +831,40 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data,
831 trace_function(tr, data, ip, parent_ip, flags); 831 trace_function(tr, data, ip, parent_ip, flags);
832} 832}
833 833
834#ifdef CONFIG_MMIOTRACE
835void __trace_mmiotrace_rw(struct trace_array *tr, struct trace_array_cpu *data,
836 struct mmiotrace_rw *rw)
837{
838 struct trace_entry *entry;
839 unsigned long irq_flags;
840
841 spin_lock_irqsave(&data->lock, irq_flags);
842 entry = tracing_get_trace_entry(tr, data);
843 tracing_generic_entry_update(entry, 0);
844 entry->type = TRACE_MMIO_RW;
845 entry->mmiorw = *rw;
846 spin_unlock_irqrestore(&data->lock, irq_flags);
847
848 trace_wake_up();
849}
850
851void __trace_mmiotrace_map(struct trace_array *tr, struct trace_array_cpu *data,
852 struct mmiotrace_map *map)
853{
854 struct trace_entry *entry;
855 unsigned long irq_flags;
856
857 spin_lock_irqsave(&data->lock, irq_flags);
858 entry = tracing_get_trace_entry(tr, data);
859 tracing_generic_entry_update(entry, 0);
860 entry->type = TRACE_MMIO_MAP;
861 entry->mmiomap = *map;
862 spin_unlock_irqrestore(&data->lock, irq_flags);
863
864 trace_wake_up();
865}
866#endif
867
834void __trace_stack(struct trace_array *tr, 868void __trace_stack(struct trace_array *tr,
835 struct trace_array_cpu *data, 869 struct trace_array_cpu *data,
836 unsigned long flags, 870 unsigned long flags,
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index c460e85e94ed..0ef9ef74c806 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -5,6 +5,7 @@
5#include <asm/atomic.h> 5#include <asm/atomic.h>
6#include <linux/sched.h> 6#include <linux/sched.h>
7#include <linux/clocksource.h> 7#include <linux/clocksource.h>
8#include <linux/mmiotrace.h>
8 9
9enum trace_type { 10enum trace_type {
10 __TRACE_FIRST_TYPE = 0, 11 __TRACE_FIRST_TYPE = 0,
@@ -14,6 +15,8 @@ enum trace_type {
14 TRACE_WAKE, 15 TRACE_WAKE,
15 TRACE_STACK, 16 TRACE_STACK,
16 TRACE_SPECIAL, 17 TRACE_SPECIAL,
18 TRACE_MMIO_RW,
19 TRACE_MMIO_MAP,
17 20
18 __TRACE_LAST_TYPE 21 __TRACE_LAST_TYPE
19}; 22};
@@ -75,6 +78,8 @@ struct trace_entry {
75 struct ctx_switch_entry ctx; 78 struct ctx_switch_entry ctx;
76 struct special_entry special; 79 struct special_entry special;
77 struct stack_entry stack; 80 struct stack_entry stack;
81 struct mmiotrace_rw mmiorw;
82 struct mmiotrace_map mmiomap;
78 }; 83 };
79}; 84};
80 85
@@ -255,6 +260,15 @@ extern unsigned long ftrace_update_tot_cnt;
255extern int DYN_FTRACE_TEST_NAME(void); 260extern int DYN_FTRACE_TEST_NAME(void);
256#endif 261#endif
257 262
263#ifdef CONFIG_MMIOTRACE
264extern void __trace_mmiotrace_rw(struct trace_array *tr,
265 struct trace_array_cpu *data,
266 struct mmiotrace_rw *rw);
267extern void __trace_mmiotrace_map(struct trace_array *tr,
268 struct trace_array_cpu *data,
269 struct mmiotrace_map *map);
270#endif
271
258#ifdef CONFIG_FTRACE_STARTUP_TEST 272#ifdef CONFIG_FTRACE_STARTUP_TEST
259#ifdef CONFIG_FTRACE 273#ifdef CONFIG_FTRACE
260extern int trace_selftest_startup_function(struct tracer *trace, 274extern int trace_selftest_startup_function(struct tracer *trace,
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
index e4dd03cc5aa6..3a12b1ad0c63 100644
--- a/kernel/trace/trace_mmiotrace.c
+++ b/kernel/trace/trace_mmiotrace.c
@@ -11,19 +11,26 @@
11 11
12#include "trace.h" 12#include "trace.h"
13 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; 14static struct trace_array *mmio_trace_array;
19 15
16static void mmio_reset_data(struct trace_array *tr)
17{
18 int cpu;
19
20 tr->time_start = ftrace_now(tr->cpu);
21
22 for_each_online_cpu(cpu)
23 tracing_reset(tr->data[cpu]);
24}
20 25
21static void mmio_trace_init(struct trace_array *tr) 26static void mmio_trace_init(struct trace_array *tr)
22{ 27{
23 pr_debug("in %s\n", __func__); 28 pr_debug("in %s\n", __func__);
24 mmio_trace_array = tr; 29 mmio_trace_array = tr;
25 if (tr->ctrl) 30 if (tr->ctrl) {
31 mmio_reset_data(tr);
26 enable_mmiotrace(); 32 enable_mmiotrace();
33 }
27} 34}
28 35
29static void mmio_trace_reset(struct trace_array *tr) 36static void mmio_trace_reset(struct trace_array *tr)
@@ -31,15 +38,110 @@ static void mmio_trace_reset(struct trace_array *tr)
31 pr_debug("in %s\n", __func__); 38 pr_debug("in %s\n", __func__);
32 if (tr->ctrl) 39 if (tr->ctrl)
33 disable_mmiotrace(); 40 disable_mmiotrace();
41 mmio_reset_data(tr);
42 mmio_trace_array = NULL;
34} 43}
35 44
36static void mmio_trace_ctrl_update(struct trace_array *tr) 45static void mmio_trace_ctrl_update(struct trace_array *tr)
37{ 46{
38 pr_debug("in %s\n", __func__); 47 pr_debug("in %s\n", __func__);
39 if (tr->ctrl) 48 if (tr->ctrl) {
49 mmio_reset_data(tr);
40 enable_mmiotrace(); 50 enable_mmiotrace();
41 else 51 } else {
42 disable_mmiotrace(); 52 disable_mmiotrace();
53 }
54}
55
56/* XXX: This is not called for trace_pipe file! */
57void mmio_print_header(struct trace_iterator *iter)
58{
59 struct trace_seq *s = &iter->seq;
60 trace_seq_printf(s, "VERSION broken 20070824\n");
61 /* TODO: print /proc/bus/pci/devices contents as PCIDEV lines */
62}
63
64static int mmio_print_rw(struct trace_iterator *iter)
65{
66 struct trace_entry *entry = iter->ent;
67 struct mmiotrace_rw *rw = &entry->mmiorw;
68 struct trace_seq *s = &iter->seq;
69 unsigned long long t = ns2usecs(entry->t);
70 unsigned long usec_rem = do_div(t, 1000000ULL);
71 unsigned secs = (unsigned long)t;
72 int ret = 1;
73
74 switch (entry->mmiorw.opcode) {
75 case MMIO_READ:
76 ret = trace_seq_printf(s,
77 "R %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n",
78 rw->width, secs, usec_rem, rw->map_id, rw->phys,
79 rw->value, rw->pc, entry->pid);
80 break;
81 case MMIO_WRITE:
82 ret = trace_seq_printf(s,
83 "W %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n",
84 rw->width, secs, usec_rem, rw->map_id, rw->phys,
85 rw->value, rw->pc, entry->pid);
86 break;
87 case MMIO_UNKNOWN_OP:
88 ret = trace_seq_printf(s,
89 "UNKNOWN %lu.%06lu %d 0x%lx %02x,%02x,%02x 0x%lx %d\n",
90 secs, usec_rem, rw->map_id, rw->phys,
91 (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff,
92 (rw->value >> 0) & 0xff, rw->pc, entry->pid);
93 break;
94 default:
95 ret = trace_seq_printf(s, "rw what?\n");
96 break;
97 }
98 if (ret)
99 return 1;
100 return 0;
101}
102
103static int mmio_print_map(struct trace_iterator *iter)
104{
105 struct trace_entry *entry = iter->ent;
106 struct mmiotrace_map *m = &entry->mmiomap;
107 struct trace_seq *s = &iter->seq;
108 unsigned long long t = ns2usecs(entry->t);
109 unsigned long usec_rem = do_div(t, 1000000ULL);
110 unsigned secs = (unsigned long)t;
111 int ret = 1;
112
113 switch (entry->mmiorw.opcode) {
114 case MMIO_PROBE:
115 ret = trace_seq_printf(s,
116 "MAP %lu.%06lu %d 0x%lx 0x%lx 0x%lx 0x%lx %d\n",
117 secs, usec_rem, m->map_id, m->phys, m->virt, m->len,
118 0UL, entry->pid);
119 break;
120 case MMIO_UNPROBE:
121 ret = trace_seq_printf(s,
122 "UNMAP %lu.%06lu %d 0x%lx %d\n",
123 secs, usec_rem, m->map_id, 0UL, entry->pid);
124 break;
125 default:
126 ret = trace_seq_printf(s, "map what?\n");
127 break;
128 }
129 if (ret)
130 return 1;
131 return 0;
132}
133
134/* return 0 to abort printing without consuming current entry in pipe mode */
135static int mmio_print_line(struct trace_iterator *iter)
136{
137 switch (iter->ent->type) {
138 case TRACE_MMIO_RW:
139 return mmio_print_rw(iter);
140 case TRACE_MMIO_MAP:
141 return mmio_print_map(iter);
142 default:
143 return 1; /* ignore unknown entries */
144 }
43} 145}
44 146
45static struct tracer mmio_tracer __read_mostly = 147static struct tracer mmio_tracer __read_mostly =
@@ -47,38 +149,31 @@ static struct tracer mmio_tracer __read_mostly =
47 .name = "mmiotrace", 149 .name = "mmiotrace",
48 .init = mmio_trace_init, 150 .init = mmio_trace_init,
49 .reset = mmio_trace_reset, 151 .reset = mmio_trace_reset,
152 .open = mmio_print_header,
50 .ctrl_update = mmio_trace_ctrl_update, 153 .ctrl_update = mmio_trace_ctrl_update,
154 .print_line = mmio_print_line,
51}; 155};
52 156
53__init static int init_mmio_trace(void) 157__init static int init_mmio_trace(void)
54{ 158{
55 int ret = init_mmiotrace();
56 if (ret)
57 return ret;
58 return register_tracer(&mmio_tracer); 159 return register_tracer(&mmio_tracer);
59} 160}
60device_initcall(init_mmio_trace); 161device_initcall(init_mmio_trace);
61 162
62void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg) 163void mmio_trace_rw(struct mmiotrace_rw *rw)
63{ 164{
64 struct trace_array *tr = mmio_trace_array; 165 struct trace_array *tr = mmio_trace_array;
65 struct trace_array_cpu *data = tr->data[smp_processor_id()]; 166 struct trace_array_cpu *data = tr->data[smp_processor_id()];
167 __trace_mmiotrace_rw(tr, data, rw);
168}
66 169
67 if (!current || current->pid == 0) { 170void mmio_trace_mapping(struct mmiotrace_map *map)
68 /* 171{
69 * XXX: This is a problem. We need to able to record, no 172 struct trace_array *tr = mmio_trace_array;
70 * matter what. tracing_generic_entry_update() would crash. 173 struct trace_array_cpu *data;
71 */ 174
72 static unsigned limit; 175 preempt_disable();
73 if (limit++ < 12) 176 data = tr->data[smp_processor_id()];
74 pr_err("Error in %s: no current.\n", __func__); 177 __trace_mmiotrace_map(tr, data, map);
75 return; 178 preempt_enable();
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} 179}