1f984b51eSPekka Paalanen /* 2f984b51eSPekka Paalanen * Memory mapped I/O tracing 3f984b51eSPekka Paalanen * 4f984b51eSPekka Paalanen * Copyright (C) 2008 Pekka Paalanen <pq@iki.fi> 5f984b51eSPekka Paalanen */ 6f984b51eSPekka Paalanen 7f984b51eSPekka Paalanen #define DEBUG 1 8f984b51eSPekka Paalanen 9f984b51eSPekka Paalanen #include <linux/kernel.h> 10f984b51eSPekka Paalanen #include <linux/mmiotrace.h> 1113829537SPekka Paalanen #include <linux/pci.h> 12f984b51eSPekka Paalanen 13f984b51eSPekka Paalanen #include "trace.h" 14f984b51eSPekka Paalanen 15d0a7e8caSPekka Paalanen struct header_iter { 16d0a7e8caSPekka Paalanen struct pci_dev *dev; 17d0a7e8caSPekka Paalanen }; 18d0a7e8caSPekka Paalanen 19f984b51eSPekka Paalanen static struct trace_array *mmio_trace_array; 202039238bSPekka Paalanen static bool overrun_detected; 21f984b51eSPekka Paalanen 22bd8ac686SPekka Paalanen static void mmio_reset_data(struct trace_array *tr) 23bd8ac686SPekka Paalanen { 24bd8ac686SPekka Paalanen int cpu; 25bd8ac686SPekka Paalanen 262039238bSPekka Paalanen overrun_detected = false; 27bd8ac686SPekka Paalanen tr->time_start = ftrace_now(tr->cpu); 28bd8ac686SPekka Paalanen 29bd8ac686SPekka Paalanen for_each_online_cpu(cpu) 30bd8ac686SPekka Paalanen tracing_reset(tr->data[cpu]); 31bd8ac686SPekka Paalanen } 32f984b51eSPekka Paalanen 33f984b51eSPekka Paalanen static void mmio_trace_init(struct trace_array *tr) 34f984b51eSPekka Paalanen { 35f984b51eSPekka Paalanen pr_debug("in %s\n", __func__); 36f984b51eSPekka Paalanen mmio_trace_array = tr; 37bd8ac686SPekka Paalanen if (tr->ctrl) { 38bd8ac686SPekka Paalanen mmio_reset_data(tr); 39f984b51eSPekka Paalanen enable_mmiotrace(); 40f984b51eSPekka Paalanen } 41bd8ac686SPekka Paalanen } 42f984b51eSPekka Paalanen 43f984b51eSPekka Paalanen static void mmio_trace_reset(struct trace_array *tr) 44f984b51eSPekka Paalanen { 45f984b51eSPekka Paalanen pr_debug("in %s\n", __func__); 46f984b51eSPekka Paalanen if (tr->ctrl) 47f984b51eSPekka Paalanen disable_mmiotrace(); 48bd8ac686SPekka Paalanen mmio_reset_data(tr); 49bd8ac686SPekka Paalanen mmio_trace_array = NULL; 50f984b51eSPekka Paalanen } 51f984b51eSPekka Paalanen 52f984b51eSPekka Paalanen static void mmio_trace_ctrl_update(struct trace_array *tr) 53f984b51eSPekka Paalanen { 54f984b51eSPekka Paalanen pr_debug("in %s\n", __func__); 55bd8ac686SPekka Paalanen if (tr->ctrl) { 56bd8ac686SPekka Paalanen mmio_reset_data(tr); 57f984b51eSPekka Paalanen enable_mmiotrace(); 58bd8ac686SPekka Paalanen } else { 59f984b51eSPekka Paalanen disable_mmiotrace(); 60f984b51eSPekka Paalanen } 61bd8ac686SPekka Paalanen } 62bd8ac686SPekka Paalanen 6313829537SPekka Paalanen static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev) 6413829537SPekka Paalanen { 6513829537SPekka Paalanen int ret = 0; 6613829537SPekka Paalanen int i; 6713829537SPekka Paalanen resource_size_t start, end; 6813829537SPekka Paalanen const struct pci_driver *drv = pci_dev_driver(dev); 6913829537SPekka Paalanen 7013829537SPekka Paalanen /* XXX: incomplete checks for trace_seq_printf() return value */ 7113829537SPekka Paalanen ret += trace_seq_printf(s, "PCIDEV %02x%02x %04x%04x %x", 7213829537SPekka Paalanen dev->bus->number, dev->devfn, 7313829537SPekka Paalanen dev->vendor, dev->device, dev->irq); 7413829537SPekka Paalanen /* 7513829537SPekka Paalanen * XXX: is pci_resource_to_user() appropriate, since we are 7613829537SPekka Paalanen * supposed to interpret the __ioremap() phys_addr argument based on 7713829537SPekka Paalanen * these printed values? 7813829537SPekka Paalanen */ 7913829537SPekka Paalanen for (i = 0; i < 7; i++) { 8013829537SPekka Paalanen pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 8113829537SPekka Paalanen ret += trace_seq_printf(s, " %llx", 8213829537SPekka Paalanen (unsigned long long)(start | 8313829537SPekka Paalanen (dev->resource[i].flags & PCI_REGION_FLAG_MASK))); 8413829537SPekka Paalanen } 8513829537SPekka Paalanen for (i = 0; i < 7; i++) { 8613829537SPekka Paalanen pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); 8713829537SPekka Paalanen ret += trace_seq_printf(s, " %llx", 8813829537SPekka Paalanen dev->resource[i].start < dev->resource[i].end ? 8913829537SPekka Paalanen (unsigned long long)(end - start) + 1 : 0); 9013829537SPekka Paalanen } 9113829537SPekka Paalanen if (drv) 9213829537SPekka Paalanen ret += trace_seq_printf(s, " %s\n", drv->name); 9313829537SPekka Paalanen else 9413829537SPekka Paalanen ret += trace_seq_printf(s, " \n"); 9513829537SPekka Paalanen return ret; 9613829537SPekka Paalanen } 9713829537SPekka Paalanen 98d0a7e8caSPekka Paalanen static void destroy_header_iter(struct header_iter *hiter) 99bd8ac686SPekka Paalanen { 100d0a7e8caSPekka Paalanen if (!hiter) 101d0a7e8caSPekka Paalanen return; 102d0a7e8caSPekka Paalanen pci_dev_put(hiter->dev); 103d0a7e8caSPekka Paalanen kfree(hiter); 104d0a7e8caSPekka Paalanen } 105d0a7e8caSPekka Paalanen 106d0a7e8caSPekka Paalanen static void mmio_pipe_open(struct trace_iterator *iter) 107d0a7e8caSPekka Paalanen { 108d0a7e8caSPekka Paalanen struct header_iter *hiter; 109bd8ac686SPekka Paalanen struct trace_seq *s = &iter->seq; 11013829537SPekka Paalanen 11113829537SPekka Paalanen trace_seq_printf(s, "VERSION 20070824\n"); 11213829537SPekka Paalanen 113d0a7e8caSPekka Paalanen hiter = kzalloc(sizeof(*hiter), GFP_KERNEL); 114d0a7e8caSPekka Paalanen if (!hiter) 115d0a7e8caSPekka Paalanen return; 116d0a7e8caSPekka Paalanen 117d0a7e8caSPekka Paalanen hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 118d0a7e8caSPekka Paalanen iter->private = hiter; 119d0a7e8caSPekka Paalanen } 120d0a7e8caSPekka Paalanen 121d0a7e8caSPekka Paalanen /* XXX: This is not called when the pipe is closed! */ 122d0a7e8caSPekka Paalanen static void mmio_close(struct trace_iterator *iter) 123d0a7e8caSPekka Paalanen { 124d0a7e8caSPekka Paalanen struct header_iter *hiter = iter->private; 125d0a7e8caSPekka Paalanen destroy_header_iter(hiter); 126d0a7e8caSPekka Paalanen iter->private = NULL; 127d0a7e8caSPekka Paalanen } 128d0a7e8caSPekka Paalanen 1292039238bSPekka Paalanen static unsigned long count_overruns(struct trace_iterator *iter) 1302039238bSPekka Paalanen { 1312039238bSPekka Paalanen int cpu; 1322039238bSPekka Paalanen unsigned long cnt = 0; 1332039238bSPekka Paalanen for_each_online_cpu(cpu) { 1342039238bSPekka Paalanen cnt += iter->overrun[cpu]; 1352039238bSPekka Paalanen iter->overrun[cpu] = 0; 1362039238bSPekka Paalanen } 1372039238bSPekka Paalanen return cnt; 1382039238bSPekka Paalanen } 1392039238bSPekka Paalanen 140d0a7e8caSPekka Paalanen static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp, 141d0a7e8caSPekka Paalanen char __user *ubuf, size_t cnt, loff_t *ppos) 142d0a7e8caSPekka Paalanen { 143d0a7e8caSPekka Paalanen ssize_t ret; 144d0a7e8caSPekka Paalanen struct header_iter *hiter = iter->private; 145d0a7e8caSPekka Paalanen struct trace_seq *s = &iter->seq; 1462039238bSPekka Paalanen unsigned long n; 1472039238bSPekka Paalanen 1482039238bSPekka Paalanen n = count_overruns(iter); 1492039238bSPekka Paalanen if (n) { 1502039238bSPekka Paalanen /* XXX: This is later than where events were lost. */ 1512039238bSPekka Paalanen trace_seq_printf(s, "MARK 0.000000 Lost %lu events.\n", n); 1522039238bSPekka Paalanen if (!overrun_detected) 1532039238bSPekka Paalanen pr_warning("mmiotrace has lost events.\n"); 1542039238bSPekka Paalanen overrun_detected = true; 1552039238bSPekka Paalanen goto print_out; 1562039238bSPekka Paalanen } 157d0a7e8caSPekka Paalanen 158d0a7e8caSPekka Paalanen if (!hiter) 159d0a7e8caSPekka Paalanen return 0; 160d0a7e8caSPekka Paalanen 161d0a7e8caSPekka Paalanen mmio_print_pcidev(s, hiter->dev); 162d0a7e8caSPekka Paalanen hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, hiter->dev); 163d0a7e8caSPekka Paalanen 164d0a7e8caSPekka Paalanen if (!hiter->dev) { 165d0a7e8caSPekka Paalanen destroy_header_iter(hiter); 166d0a7e8caSPekka Paalanen iter->private = NULL; 167d0a7e8caSPekka Paalanen } 168d0a7e8caSPekka Paalanen 1692039238bSPekka Paalanen print_out: 170d0a7e8caSPekka Paalanen ret = trace_seq_to_user(s, ubuf, cnt); 171d0a7e8caSPekka Paalanen return (ret == -EBUSY) ? 0 : ret; 172bd8ac686SPekka Paalanen } 173bd8ac686SPekka Paalanen 174bd8ac686SPekka Paalanen static int mmio_print_rw(struct trace_iterator *iter) 175bd8ac686SPekka Paalanen { 176bd8ac686SPekka Paalanen struct trace_entry *entry = iter->ent; 177bd8ac686SPekka Paalanen struct mmiotrace_rw *rw = &entry->mmiorw; 178bd8ac686SPekka Paalanen struct trace_seq *s = &iter->seq; 179bd8ac686SPekka Paalanen unsigned long long t = ns2usecs(entry->t); 180bd8ac686SPekka Paalanen unsigned long usec_rem = do_div(t, 1000000ULL); 181bd8ac686SPekka Paalanen unsigned secs = (unsigned long)t; 182bd8ac686SPekka Paalanen int ret = 1; 183bd8ac686SPekka Paalanen 184bd8ac686SPekka Paalanen switch (entry->mmiorw.opcode) { 185bd8ac686SPekka Paalanen case MMIO_READ: 186bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 187bd8ac686SPekka Paalanen "R %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", 188bd8ac686SPekka Paalanen rw->width, secs, usec_rem, rw->map_id, rw->phys, 189736ca61fSPekka Paalanen rw->value, rw->pc, 0); 190bd8ac686SPekka Paalanen break; 191bd8ac686SPekka Paalanen case MMIO_WRITE: 192bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 193bd8ac686SPekka Paalanen "W %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", 194bd8ac686SPekka Paalanen rw->width, secs, usec_rem, rw->map_id, rw->phys, 195736ca61fSPekka Paalanen rw->value, rw->pc, 0); 196bd8ac686SPekka Paalanen break; 197bd8ac686SPekka Paalanen case MMIO_UNKNOWN_OP: 198bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 199bd8ac686SPekka Paalanen "UNKNOWN %lu.%06lu %d 0x%lx %02x,%02x,%02x 0x%lx %d\n", 200bd8ac686SPekka Paalanen secs, usec_rem, rw->map_id, rw->phys, 201bd8ac686SPekka Paalanen (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff, 202736ca61fSPekka Paalanen (rw->value >> 0) & 0xff, rw->pc, 0); 203bd8ac686SPekka Paalanen break; 204bd8ac686SPekka Paalanen default: 205bd8ac686SPekka Paalanen ret = trace_seq_printf(s, "rw what?\n"); 206bd8ac686SPekka Paalanen break; 207bd8ac686SPekka Paalanen } 208bd8ac686SPekka Paalanen if (ret) 209bd8ac686SPekka Paalanen return 1; 210bd8ac686SPekka Paalanen return 0; 211bd8ac686SPekka Paalanen } 212bd8ac686SPekka Paalanen 213bd8ac686SPekka Paalanen static int mmio_print_map(struct trace_iterator *iter) 214bd8ac686SPekka Paalanen { 215bd8ac686SPekka Paalanen struct trace_entry *entry = iter->ent; 216bd8ac686SPekka Paalanen struct mmiotrace_map *m = &entry->mmiomap; 217bd8ac686SPekka Paalanen struct trace_seq *s = &iter->seq; 218bd8ac686SPekka Paalanen unsigned long long t = ns2usecs(entry->t); 219bd8ac686SPekka Paalanen unsigned long usec_rem = do_div(t, 1000000ULL); 220bd8ac686SPekka Paalanen unsigned secs = (unsigned long)t; 221bd8ac686SPekka Paalanen int ret = 1; 222bd8ac686SPekka Paalanen 223bd8ac686SPekka Paalanen switch (entry->mmiorw.opcode) { 224bd8ac686SPekka Paalanen case MMIO_PROBE: 225bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 226bd8ac686SPekka Paalanen "MAP %lu.%06lu %d 0x%lx 0x%lx 0x%lx 0x%lx %d\n", 227bd8ac686SPekka Paalanen secs, usec_rem, m->map_id, m->phys, m->virt, m->len, 228*e0fd5c2fSPekka Paalanen 0UL, 0); 229bd8ac686SPekka Paalanen break; 230bd8ac686SPekka Paalanen case MMIO_UNPROBE: 231bd8ac686SPekka Paalanen ret = trace_seq_printf(s, 232bd8ac686SPekka Paalanen "UNMAP %lu.%06lu %d 0x%lx %d\n", 233*e0fd5c2fSPekka Paalanen secs, usec_rem, m->map_id, 0UL, 0); 234bd8ac686SPekka Paalanen break; 235bd8ac686SPekka Paalanen default: 236bd8ac686SPekka Paalanen ret = trace_seq_printf(s, "map what?\n"); 237bd8ac686SPekka Paalanen break; 238bd8ac686SPekka Paalanen } 239bd8ac686SPekka Paalanen if (ret) 240bd8ac686SPekka Paalanen return 1; 241bd8ac686SPekka Paalanen return 0; 242bd8ac686SPekka Paalanen } 243bd8ac686SPekka Paalanen 244bd8ac686SPekka Paalanen /* return 0 to abort printing without consuming current entry in pipe mode */ 245bd8ac686SPekka Paalanen static int mmio_print_line(struct trace_iterator *iter) 246bd8ac686SPekka Paalanen { 247bd8ac686SPekka Paalanen switch (iter->ent->type) { 248bd8ac686SPekka Paalanen case TRACE_MMIO_RW: 249bd8ac686SPekka Paalanen return mmio_print_rw(iter); 250bd8ac686SPekka Paalanen case TRACE_MMIO_MAP: 251bd8ac686SPekka Paalanen return mmio_print_map(iter); 252bd8ac686SPekka Paalanen default: 253bd8ac686SPekka Paalanen return 1; /* ignore unknown entries */ 254bd8ac686SPekka Paalanen } 255bd8ac686SPekka Paalanen } 256f984b51eSPekka Paalanen 257f984b51eSPekka Paalanen static struct tracer mmio_tracer __read_mostly = 258f984b51eSPekka Paalanen { 259f984b51eSPekka Paalanen .name = "mmiotrace", 260f984b51eSPekka Paalanen .init = mmio_trace_init, 261f984b51eSPekka Paalanen .reset = mmio_trace_reset, 262d0a7e8caSPekka Paalanen .pipe_open = mmio_pipe_open, 263d0a7e8caSPekka Paalanen .close = mmio_close, 264d0a7e8caSPekka Paalanen .read = mmio_read, 265f984b51eSPekka Paalanen .ctrl_update = mmio_trace_ctrl_update, 266bd8ac686SPekka Paalanen .print_line = mmio_print_line, 267f984b51eSPekka Paalanen }; 268f984b51eSPekka Paalanen 269f984b51eSPekka Paalanen __init static int init_mmio_trace(void) 270f984b51eSPekka Paalanen { 271f984b51eSPekka Paalanen return register_tracer(&mmio_tracer); 272f984b51eSPekka Paalanen } 273f984b51eSPekka Paalanen device_initcall(init_mmio_trace); 274f984b51eSPekka Paalanen 275bd8ac686SPekka Paalanen void mmio_trace_rw(struct mmiotrace_rw *rw) 276f984b51eSPekka Paalanen { 277f984b51eSPekka Paalanen struct trace_array *tr = mmio_trace_array; 278f984b51eSPekka Paalanen struct trace_array_cpu *data = tr->data[smp_processor_id()]; 279bd8ac686SPekka Paalanen __trace_mmiotrace_rw(tr, data, rw); 280bd8ac686SPekka Paalanen } 281f984b51eSPekka Paalanen 282bd8ac686SPekka Paalanen void mmio_trace_mapping(struct mmiotrace_map *map) 283bd8ac686SPekka Paalanen { 284bd8ac686SPekka Paalanen struct trace_array *tr = mmio_trace_array; 285bd8ac686SPekka Paalanen struct trace_array_cpu *data; 286bd8ac686SPekka Paalanen 287bd8ac686SPekka Paalanen preempt_disable(); 288bd8ac686SPekka Paalanen data = tr->data[smp_processor_id()]; 289bd8ac686SPekka Paalanen __trace_mmiotrace_map(tr, data, map); 290bd8ac686SPekka Paalanen preempt_enable(); 291f984b51eSPekka Paalanen } 292