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