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 19 struct header_iter { 20 struct pci_dev *dev; 21 }; 22 23 static struct trace_array *mmio_trace_array; 24 static bool overrun_detected; 25 static unsigned long prev_overruns; 26 static atomic_t dropped_count; 27 28 static 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 36 static 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 46 static 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 55 static void mmio_trace_start(struct trace_array *tr) 56 { 57 pr_debug("in %s\n", __func__); 58 mmio_reset_data(tr); 59 } 60 61 static 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 89 static 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 97 static 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_obj(*hiter); 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 static void mmio_close(struct trace_iterator *iter) 113 { 114 struct header_iter *hiter = iter->private; 115 destroy_header_iter(hiter); 116 iter->private = NULL; 117 } 118 119 static unsigned long count_overruns(struct trace_iterator *iter) 120 { 121 unsigned long cnt = atomic_xchg(&dropped_count, 0); 122 unsigned long over = ring_buffer_overruns(iter->array_buffer->buffer); 123 124 if (over > prev_overruns) 125 cnt += over - prev_overruns; 126 prev_overruns = over; 127 return cnt; 128 } 129 130 static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp, 131 char __user *ubuf, size_t cnt, loff_t *ppos) 132 { 133 ssize_t ret; 134 struct header_iter *hiter = iter->private; 135 struct trace_seq *s = &iter->seq; 136 unsigned long n; 137 138 n = count_overruns(iter); 139 if (n) { 140 /* XXX: This is later than where events were lost. */ 141 trace_seq_printf(s, "MARK 0.000000 Lost %lu events.\n", n); 142 if (!overrun_detected) 143 pr_warn("mmiotrace has lost events\n"); 144 overrun_detected = true; 145 goto print_out; 146 } 147 148 if (!hiter || !hiter->dev) 149 return 0; 150 151 mmio_print_pcidev(s, hiter->dev); 152 hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, hiter->dev); 153 154 if (!hiter->dev) { 155 destroy_header_iter(hiter); 156 iter->private = NULL; 157 } 158 159 print_out: 160 ret = trace_seq_to_user(s, ubuf, cnt); 161 return (ret == -EBUSY) ? 0 : ret; 162 } 163 164 static enum print_line_t mmio_print_rw(struct trace_iterator *iter) 165 { 166 struct trace_entry *entry = iter->ent; 167 struct trace_mmiotrace_rw *field; 168 struct mmiotrace_rw *rw; 169 struct trace_seq *s = &iter->seq; 170 unsigned long long t = ns2usecs(iter->ts); 171 unsigned long usec_rem = do_div(t, USEC_PER_SEC); 172 unsigned secs = (unsigned long)t; 173 174 trace_assign_type(field, entry); 175 rw = &field->rw; 176 177 switch (rw->opcode) { 178 case MMIO_READ: 179 trace_seq_printf(s, 180 "R %d %u.%06lu %d 0x%llx 0x%lx 0x%lx %d\n", 181 rw->width, secs, usec_rem, rw->map_id, 182 (unsigned long long)rw->phys, 183 rw->value, rw->pc, 0); 184 break; 185 case MMIO_WRITE: 186 trace_seq_printf(s, 187 "W %d %u.%06lu %d 0x%llx 0x%lx 0x%lx %d\n", 188 rw->width, secs, usec_rem, rw->map_id, 189 (unsigned long long)rw->phys, 190 rw->value, rw->pc, 0); 191 break; 192 case MMIO_UNKNOWN_OP: 193 trace_seq_printf(s, 194 "UNKNOWN %u.%06lu %d 0x%llx %02lx,%02lx," 195 "%02lx 0x%lx %d\n", 196 secs, usec_rem, rw->map_id, 197 (unsigned long long)rw->phys, 198 (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff, 199 (rw->value >> 0) & 0xff, rw->pc, 0); 200 break; 201 default: 202 trace_seq_puts(s, "rw what?\n"); 203 break; 204 } 205 206 return trace_handle_return(s); 207 } 208 209 static enum print_line_t mmio_print_map(struct trace_iterator *iter) 210 { 211 struct trace_entry *entry = iter->ent; 212 struct trace_mmiotrace_map *field; 213 struct mmiotrace_map *m; 214 struct trace_seq *s = &iter->seq; 215 unsigned long long t = ns2usecs(iter->ts); 216 unsigned long usec_rem = do_div(t, USEC_PER_SEC); 217 unsigned secs = (unsigned long)t; 218 219 trace_assign_type(field, entry); 220 m = &field->map; 221 222 switch (m->opcode) { 223 case MMIO_PROBE: 224 trace_seq_printf(s, 225 "MAP %u.%06lu %d 0x%llx 0x%lx 0x%lx 0x%lx %d\n", 226 secs, usec_rem, m->map_id, 227 (unsigned long long)m->phys, m->virt, m->len, 228 0UL, 0); 229 break; 230 case MMIO_UNPROBE: 231 trace_seq_printf(s, 232 "UNMAP %u.%06lu %d 0x%lx %d\n", 233 secs, usec_rem, m->map_id, 0UL, 0); 234 break; 235 default: 236 trace_seq_puts(s, "map what?\n"); 237 break; 238 } 239 240 return trace_handle_return(s); 241 } 242 243 static enum print_line_t mmio_print_mark(struct trace_iterator *iter) 244 { 245 struct trace_entry *entry = iter->ent; 246 struct print_entry *print = (struct print_entry *)entry; 247 const char *msg = print->buf; 248 struct trace_seq *s = &iter->seq; 249 unsigned long long t = ns2usecs(iter->ts); 250 unsigned long usec_rem = do_div(t, USEC_PER_SEC); 251 unsigned secs = (unsigned long)t; 252 253 /* The trailing newline must be in the message. */ 254 trace_seq_printf(s, "MARK %u.%06lu %s", secs, usec_rem, msg); 255 256 return trace_handle_return(s); 257 } 258 259 static enum print_line_t mmio_print_line(struct trace_iterator *iter) 260 { 261 switch (iter->ent->type) { 262 case TRACE_MMIO_RW: 263 return mmio_print_rw(iter); 264 case TRACE_MMIO_MAP: 265 return mmio_print_map(iter); 266 case TRACE_PRINT: 267 return mmio_print_mark(iter); 268 default: 269 return TRACE_TYPE_HANDLED; /* ignore unknown entries */ 270 } 271 } 272 273 static struct tracer mmio_tracer __read_mostly = 274 { 275 .name = "mmiotrace", 276 .init = mmio_trace_init, 277 .reset = mmio_trace_reset, 278 .start = mmio_trace_start, 279 .pipe_open = mmio_pipe_open, 280 .close = mmio_close, 281 .pipe_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 } 291 device_initcall(init_mmio_trace); 292 293 static void __trace_mmiotrace_rw(struct trace_array *tr, 294 struct mmiotrace_rw *rw) 295 { 296 struct trace_buffer *buffer = tr->array_buffer.buffer; 297 struct ring_buffer_event *event; 298 struct trace_mmiotrace_rw *entry; 299 unsigned int trace_ctx; 300 301 trace_ctx = tracing_gen_ctx_flags(0); 302 event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW, 303 sizeof(*entry), trace_ctx); 304 if (!event) { 305 atomic_inc(&dropped_count); 306 return; 307 } 308 entry = ring_buffer_event_data(event); 309 entry->rw = *rw; 310 311 trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); 312 } 313 314 void mmio_trace_rw(struct mmiotrace_rw *rw) 315 { 316 struct trace_array *tr = mmio_trace_array; 317 __trace_mmiotrace_rw(tr, rw); 318 } 319 320 static void __trace_mmiotrace_map(struct trace_array *tr, 321 struct mmiotrace_map *map) 322 { 323 struct trace_buffer *buffer = tr->array_buffer.buffer; 324 struct ring_buffer_event *event; 325 struct trace_mmiotrace_map *entry; 326 unsigned int trace_ctx; 327 328 trace_ctx = tracing_gen_ctx_flags(0); 329 event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP, 330 sizeof(*entry), trace_ctx); 331 if (!event) { 332 atomic_inc(&dropped_count); 333 return; 334 } 335 entry = ring_buffer_event_data(event); 336 entry->map = *map; 337 338 trace_buffer_unlock_commit(tr, buffer, event, trace_ctx); 339 } 340 341 void mmio_trace_mapping(struct mmiotrace_map *map) 342 { 343 struct trace_array *tr = mmio_trace_array; 344 __trace_mmiotrace_map(tr, map); 345 } 346 347 int mmio_trace_printk(const char *fmt, va_list args) 348 { 349 return trace_vprintk(0, fmt, args); 350 } 351