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