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