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