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 #include <linux/pci.h> 12 #include <linux/time.h> 13 14 #include <asm/atomic.h> 15 16 #include "trace.h" 17 #include "trace_output.h" 18 19 struct header_iter { 20 struct pci_dev *dev; 21 }; 22 23 static struct trace_array *mmio_trace_array; 24 static bool overrun_detected; 25 static unsigned long prev_overruns; 26 static atomic_t dropped_count; 27 28 static void mmio_reset_data(struct trace_array *tr) 29 { 30 overrun_detected = false; 31 prev_overruns = 0; 32 33 tracing_reset_online_cpus(tr); 34 } 35 36 static int mmio_trace_init(struct trace_array *tr) 37 { 38 pr_debug("in %s\n", __func__); 39 mmio_trace_array = tr; 40 41 mmio_reset_data(tr); 42 enable_mmiotrace(); 43 return 0; 44 } 45 46 static void mmio_trace_reset(struct trace_array *tr) 47 { 48 pr_debug("in %s\n", __func__); 49 50 disable_mmiotrace(); 51 mmio_reset_data(tr); 52 mmio_trace_array = NULL; 53 } 54 55 static void mmio_trace_start(struct trace_array *tr) 56 { 57 pr_debug("in %s\n", __func__); 58 mmio_reset_data(tr); 59 } 60 61 static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev) 62 { 63 int ret = 0; 64 int i; 65 resource_size_t start, end; 66 const struct pci_driver *drv = pci_dev_driver(dev); 67 68 /* XXX: incomplete checks for trace_seq_printf() return value */ 69 ret += trace_seq_printf(s, "PCIDEV %02x%02x %04x%04x %x", 70 dev->bus->number, dev->devfn, 71 dev->vendor, dev->device, dev->irq); 72 /* 73 * XXX: is pci_resource_to_user() appropriate, since we are 74 * supposed to interpret the __ioremap() phys_addr argument based on 75 * these printed values? 76 */ 77 for (i = 0; i < 7; i++) { 78 pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 79 ret += trace_seq_printf(s, " %llx", 80 (unsigned long long)(start | 81 (dev->resource[i].flags & PCI_REGION_FLAG_MASK))); 82 } 83 for (i = 0; i < 7; i++) { 84 pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 85 ret += trace_seq_printf(s, " %llx", 86 dev->resource[i].start < dev->resource[i].end ? 87 (unsigned long long)(end - start) + 1 : 0); 88 } 89 if (drv) 90 ret += trace_seq_printf(s, " %s\n", drv->name); 91 else 92 ret += trace_seq_printf(s, " \n"); 93 return ret; 94 } 95 96 static void destroy_header_iter(struct header_iter *hiter) 97 { 98 if (!hiter) 99 return; 100 pci_dev_put(hiter->dev); 101 kfree(hiter); 102 } 103 104 static void mmio_pipe_open(struct trace_iterator *iter) 105 { 106 struct header_iter *hiter; 107 struct trace_seq *s = &iter->seq; 108 109 trace_seq_printf(s, "VERSION 20070824\n"); 110 111 hiter = kzalloc(sizeof(*hiter), GFP_KERNEL); 112 if (!hiter) 113 return; 114 115 hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 116 iter->private = hiter; 117 } 118 119 /* XXX: This is not called when the pipe is closed! */ 120 static void mmio_close(struct trace_iterator *iter) 121 { 122 struct header_iter *hiter = iter->private; 123 destroy_header_iter(hiter); 124 iter->private = NULL; 125 } 126 127 static unsigned long count_overruns(struct trace_iterator *iter) 128 { 129 unsigned long cnt = atomic_xchg(&dropped_count, 0); 130 unsigned long over = ring_buffer_overruns(iter->tr->buffer); 131 132 if (over > prev_overruns) 133 cnt += over - prev_overruns; 134 prev_overruns = over; 135 return cnt; 136 } 137 138 static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp, 139 char __user *ubuf, size_t cnt, loff_t *ppos) 140 { 141 ssize_t ret; 142 struct header_iter *hiter = iter->private; 143 struct trace_seq *s = &iter->seq; 144 unsigned long n; 145 146 n = count_overruns(iter); 147 if (n) { 148 /* XXX: This is later than where events were lost. */ 149 trace_seq_printf(s, "MARK 0.000000 Lost %lu events.\n", n); 150 if (!overrun_detected) 151 pr_warning("mmiotrace has lost events.\n"); 152 overrun_detected = true; 153 goto print_out; 154 } 155 156 if (!hiter) 157 return 0; 158 159 mmio_print_pcidev(s, hiter->dev); 160 hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, hiter->dev); 161 162 if (!hiter->dev) { 163 destroy_header_iter(hiter); 164 iter->private = NULL; 165 } 166 167 print_out: 168 ret = trace_seq_to_user(s, ubuf, cnt); 169 return (ret == -EBUSY) ? 0 : ret; 170 } 171 172 static enum print_line_t mmio_print_rw(struct trace_iterator *iter) 173 { 174 struct trace_entry *entry = iter->ent; 175 struct trace_mmiotrace_rw *field; 176 struct mmiotrace_rw *rw; 177 struct trace_seq *s = &iter->seq; 178 unsigned long long t = ns2usecs(iter->ts); 179 unsigned long usec_rem = do_div(t, USEC_PER_SEC); 180 unsigned secs = (unsigned long)t; 181 int ret = 1; 182 183 trace_assign_type(field, entry); 184 rw = &field->rw; 185 186 switch (rw->opcode) { 187 case MMIO_READ: 188 ret = trace_seq_printf(s, 189 "R %d %u.%06lu %d 0x%llx 0x%lx 0x%lx %d\n", 190 rw->width, secs, usec_rem, rw->map_id, 191 (unsigned long long)rw->phys, 192 rw->value, rw->pc, 0); 193 break; 194 case MMIO_WRITE: 195 ret = trace_seq_printf(s, 196 "W %d %u.%06lu %d 0x%llx 0x%lx 0x%lx %d\n", 197 rw->width, secs, usec_rem, rw->map_id, 198 (unsigned long long)rw->phys, 199 rw->value, rw->pc, 0); 200 break; 201 case MMIO_UNKNOWN_OP: 202 ret = trace_seq_printf(s, 203 "UNKNOWN %u.%06lu %d 0x%llx %02lx,%02lx," 204 "%02lx 0x%lx %d\n", 205 secs, usec_rem, rw->map_id, 206 (unsigned long long)rw->phys, 207 (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff, 208 (rw->value >> 0) & 0xff, rw->pc, 0); 209 break; 210 default: 211 ret = trace_seq_printf(s, "rw what?\n"); 212 break; 213 } 214 if (ret) 215 return TRACE_TYPE_HANDLED; 216 return TRACE_TYPE_PARTIAL_LINE; 217 } 218 219 static enum print_line_t mmio_print_map(struct trace_iterator *iter) 220 { 221 struct trace_entry *entry = iter->ent; 222 struct trace_mmiotrace_map *field; 223 struct mmiotrace_map *m; 224 struct trace_seq *s = &iter->seq; 225 unsigned long long t = ns2usecs(iter->ts); 226 unsigned long usec_rem = do_div(t, USEC_PER_SEC); 227 unsigned secs = (unsigned long)t; 228 int ret; 229 230 trace_assign_type(field, entry); 231 m = &field->map; 232 233 switch (m->opcode) { 234 case MMIO_PROBE: 235 ret = trace_seq_printf(s, 236 "MAP %u.%06lu %d 0x%llx 0x%lx 0x%lx 0x%lx %d\n", 237 secs, usec_rem, m->map_id, 238 (unsigned long long)m->phys, m->virt, m->len, 239 0UL, 0); 240 break; 241 case MMIO_UNPROBE: 242 ret = trace_seq_printf(s, 243 "UNMAP %u.%06lu %d 0x%lx %d\n", 244 secs, usec_rem, m->map_id, 0UL, 0); 245 break; 246 default: 247 ret = trace_seq_printf(s, "map what?\n"); 248 break; 249 } 250 if (ret) 251 return TRACE_TYPE_HANDLED; 252 return TRACE_TYPE_PARTIAL_LINE; 253 } 254 255 static enum print_line_t mmio_print_mark(struct trace_iterator *iter) 256 { 257 struct trace_entry *entry = iter->ent; 258 struct print_entry *print = (struct print_entry *)entry; 259 const char *msg = print->buf; 260 struct trace_seq *s = &iter->seq; 261 unsigned long long t = ns2usecs(iter->ts); 262 unsigned long usec_rem = do_div(t, USEC_PER_SEC); 263 unsigned secs = (unsigned long)t; 264 int ret; 265 266 /* The trailing newline must be in the message. */ 267 ret = trace_seq_printf(s, "MARK %u.%06lu %s", secs, usec_rem, msg); 268 if (!ret) 269 return TRACE_TYPE_PARTIAL_LINE; 270 271 return TRACE_TYPE_HANDLED; 272 } 273 274 static enum print_line_t mmio_print_line(struct trace_iterator *iter) 275 { 276 switch (iter->ent->type) { 277 case TRACE_MMIO_RW: 278 return mmio_print_rw(iter); 279 case TRACE_MMIO_MAP: 280 return mmio_print_map(iter); 281 case TRACE_PRINT: 282 return mmio_print_mark(iter); 283 default: 284 return TRACE_TYPE_HANDLED; /* ignore unknown entries */ 285 } 286 } 287 288 static struct tracer mmio_tracer __read_mostly = 289 { 290 .name = "mmiotrace", 291 .init = mmio_trace_init, 292 .reset = mmio_trace_reset, 293 .start = mmio_trace_start, 294 .pipe_open = mmio_pipe_open, 295 .close = mmio_close, 296 .read = mmio_read, 297 .print_line = mmio_print_line, 298 }; 299 300 __init static int init_mmio_trace(void) 301 { 302 return register_tracer(&mmio_tracer); 303 } 304 device_initcall(init_mmio_trace); 305 306 static void __trace_mmiotrace_rw(struct trace_array *tr, 307 struct trace_array_cpu *data, 308 struct mmiotrace_rw *rw) 309 { 310 struct ftrace_event_call *call = &event_mmiotrace_rw; 311 struct ring_buffer *buffer = tr->buffer; 312 struct ring_buffer_event *event; 313 struct trace_mmiotrace_rw *entry; 314 int pc = preempt_count(); 315 316 event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW, 317 sizeof(*entry), 0, pc); 318 if (!event) { 319 atomic_inc(&dropped_count); 320 return; 321 } 322 entry = ring_buffer_event_data(event); 323 entry->rw = *rw; 324 325 if (!filter_check_discard(call, entry, buffer, event)) 326 trace_buffer_unlock_commit(buffer, event, 0, pc); 327 } 328 329 void mmio_trace_rw(struct mmiotrace_rw *rw) 330 { 331 struct trace_array *tr = mmio_trace_array; 332 struct trace_array_cpu *data = tr->data[smp_processor_id()]; 333 __trace_mmiotrace_rw(tr, data, rw); 334 } 335 336 static void __trace_mmiotrace_map(struct trace_array *tr, 337 struct trace_array_cpu *data, 338 struct mmiotrace_map *map) 339 { 340 struct ftrace_event_call *call = &event_mmiotrace_map; 341 struct ring_buffer *buffer = tr->buffer; 342 struct ring_buffer_event *event; 343 struct trace_mmiotrace_map *entry; 344 int pc = preempt_count(); 345 346 event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP, 347 sizeof(*entry), 0, pc); 348 if (!event) { 349 atomic_inc(&dropped_count); 350 return; 351 } 352 entry = ring_buffer_event_data(event); 353 entry->map = *map; 354 355 if (!filter_check_discard(call, entry, buffer, event)) 356 trace_buffer_unlock_commit(buffer, event, 0, pc); 357 } 358 359 void mmio_trace_mapping(struct mmiotrace_map *map) 360 { 361 struct trace_array *tr = mmio_trace_array; 362 struct trace_array_cpu *data; 363 364 preempt_disable(); 365 data = tr->data[smp_processor_id()]; 366 __trace_mmiotrace_map(tr, data, map); 367 preempt_enable(); 368 } 369 370 int mmio_trace_printk(const char *fmt, va_list args) 371 { 372 return trace_vprintk(0, fmt, args); 373 } 374