trace_mmiotrace.c 8.6 KB
Newer Older
P
Pekka Paalanen 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * Memory mapped I/O tracing
 *
 * Copyright (C) 2008 Pekka Paalanen <pq@iki.fi>
 */

#define DEBUG 1

#include <linux/kernel.h>
#include <linux/mmiotrace.h>
P
Pekka Paalanen 已提交
11
#include <linux/pci.h>
P
Pekka Paalanen 已提交
12 13 14

#include "trace.h"

15 16 17 18
struct header_iter {
	struct pci_dev *dev;
};

P
Pekka Paalanen 已提交
19
static struct trace_array *mmio_trace_array;
P
Pekka Paalanen 已提交
20
static bool overrun_detected;
P
Pekka Paalanen 已提交
21

P
Pekka Paalanen 已提交
22 23 24 25
static void mmio_reset_data(struct trace_array *tr)
{
	int cpu;

P
Pekka Paalanen 已提交
26
	overrun_detected = false;
P
Pekka Paalanen 已提交
27 28 29
	tr->time_start = ftrace_now(tr->cpu);

	for_each_online_cpu(cpu)
30
		tracing_reset(tr, cpu);
P
Pekka Paalanen 已提交
31
}
P
Pekka Paalanen 已提交
32 33 34 35 36

static void mmio_trace_init(struct trace_array *tr)
{
	pr_debug("in %s\n", __func__);
	mmio_trace_array = tr;
P
Pekka Paalanen 已提交
37 38
	if (tr->ctrl) {
		mmio_reset_data(tr);
P
Pekka Paalanen 已提交
39
		enable_mmiotrace();
P
Pekka Paalanen 已提交
40
	}
P
Pekka Paalanen 已提交
41 42 43 44 45 46 47
}

static void mmio_trace_reset(struct trace_array *tr)
{
	pr_debug("in %s\n", __func__);
	if (tr->ctrl)
		disable_mmiotrace();
P
Pekka Paalanen 已提交
48 49
	mmio_reset_data(tr);
	mmio_trace_array = NULL;
P
Pekka Paalanen 已提交
50 51 52 53 54
}

static void mmio_trace_ctrl_update(struct trace_array *tr)
{
	pr_debug("in %s\n", __func__);
P
Pekka Paalanen 已提交
55 56
	if (tr->ctrl) {
		mmio_reset_data(tr);
P
Pekka Paalanen 已提交
57
		enable_mmiotrace();
P
Pekka Paalanen 已提交
58
	} else {
P
Pekka Paalanen 已提交
59
		disable_mmiotrace();
P
Pekka Paalanen 已提交
60 61 62
	}
}

P
Pekka Paalanen 已提交
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev)
{
	int ret = 0;
	int i;
	resource_size_t start, end;
	const struct pci_driver *drv = pci_dev_driver(dev);

	/* XXX: incomplete checks for trace_seq_printf() return value */
	ret += trace_seq_printf(s, "PCIDEV %02x%02x %04x%04x %x",
				dev->bus->number, dev->devfn,
				dev->vendor, dev->device, dev->irq);
	/*
	 * XXX: is pci_resource_to_user() appropriate, since we are
	 * supposed to interpret the __ioremap() phys_addr argument based on
	 * these printed values?
	 */
	for (i = 0; i < 7; i++) {
		pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
		ret += trace_seq_printf(s, " %llx",
			(unsigned long long)(start |
			(dev->resource[i].flags & PCI_REGION_FLAG_MASK)));
	}
	for (i = 0; i < 7; i++) {
		pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
		ret += trace_seq_printf(s, " %llx",
			dev->resource[i].start < dev->resource[i].end ?
			(unsigned long long)(end - start) + 1 : 0);
	}
	if (drv)
		ret += trace_seq_printf(s, " %s\n", drv->name);
	else
		ret += trace_seq_printf(s, " \n");
	return ret;
}

98 99 100 101 102 103 104 105 106
static void destroy_header_iter(struct header_iter *hiter)
{
	if (!hiter)
		return;
	pci_dev_put(hiter->dev);
	kfree(hiter);
}

static void mmio_pipe_open(struct trace_iterator *iter)
P
Pekka Paalanen 已提交
107
{
108
	struct header_iter *hiter;
P
Pekka Paalanen 已提交
109
	struct trace_seq *s = &iter->seq;
P
Pekka Paalanen 已提交
110 111 112

	trace_seq_printf(s, "VERSION 20070824\n");

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
	hiter = kzalloc(sizeof(*hiter), GFP_KERNEL);
	if (!hiter)
		return;

	hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
	iter->private = hiter;
}

/* XXX: This is not called when the pipe is closed! */
static void mmio_close(struct trace_iterator *iter)
{
	struct header_iter *hiter = iter->private;
	destroy_header_iter(hiter);
	iter->private = NULL;
}

P
Pekka Paalanen 已提交
129 130 131 132
static unsigned long count_overruns(struct trace_iterator *iter)
{
	int cpu;
	unsigned long cnt = 0;
133 134
/* FIXME: */
#if 0
P
Pekka Paalanen 已提交
135 136 137 138
	for_each_online_cpu(cpu) {
		cnt += iter->overrun[cpu];
		iter->overrun[cpu] = 0;
	}
139 140
#endif
	(void)cpu;
P
Pekka Paalanen 已提交
141 142 143
	return cnt;
}

144 145 146 147 148 149
static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp,
				char __user *ubuf, size_t cnt, loff_t *ppos)
{
	ssize_t ret;
	struct header_iter *hiter = iter->private;
	struct trace_seq *s = &iter->seq;
P
Pekka Paalanen 已提交
150 151 152 153 154 155 156 157 158 159 160
	unsigned long n;

	n = count_overruns(iter);
	if (n) {
		/* XXX: This is later than where events were lost. */
		trace_seq_printf(s, "MARK 0.000000 Lost %lu events.\n", n);
		if (!overrun_detected)
			pr_warning("mmiotrace has lost events.\n");
		overrun_detected = true;
		goto print_out;
	}
161 162 163 164 165 166 167 168 169 170 171 172

	if (!hiter)
		return 0;

	mmio_print_pcidev(s, hiter->dev);
	hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, hiter->dev);

	if (!hiter->dev) {
		destroy_header_iter(hiter);
		iter->private = NULL;
	}

P
Pekka Paalanen 已提交
173
print_out:
174 175
	ret = trace_seq_to_user(s, ubuf, cnt);
	return (ret == -EBUSY) ? 0 : ret;
P
Pekka Paalanen 已提交
176 177 178 179 180
}

static int mmio_print_rw(struct trace_iterator *iter)
{
	struct trace_entry *entry = iter->ent;
181
	struct mmiotrace_rw *rw	= &entry->field.mmiorw;
P
Pekka Paalanen 已提交
182
	struct trace_seq *s	= &iter->seq;
183
	unsigned long long t	= ns2usecs(iter->ts);
P
Pekka Paalanen 已提交
184 185 186 187
	unsigned long usec_rem	= do_div(t, 1000000ULL);
	unsigned secs		= (unsigned long)t;
	int ret = 1;

188
	switch (entry->field.mmiorw.opcode) {
P
Pekka Paalanen 已提交
189 190
	case MMIO_READ:
		ret = trace_seq_printf(s,
191 192 193
			"R %d %lu.%06lu %d 0x%llx 0x%lx 0x%lx %d\n",
			rw->width, secs, usec_rem, rw->map_id,
			(unsigned long long)rw->phys,
194
			rw->value, rw->pc, 0);
P
Pekka Paalanen 已提交
195 196 197
		break;
	case MMIO_WRITE:
		ret = trace_seq_printf(s,
198 199 200
			"W %d %lu.%06lu %d 0x%llx 0x%lx 0x%lx %d\n",
			rw->width, secs, usec_rem, rw->map_id,
			(unsigned long long)rw->phys,
201
			rw->value, rw->pc, 0);
P
Pekka Paalanen 已提交
202 203 204
		break;
	case MMIO_UNKNOWN_OP:
		ret = trace_seq_printf(s,
205 206 207
			"UNKNOWN %lu.%06lu %d 0x%llx %02x,%02x,%02x 0x%lx %d\n",
			secs, usec_rem, rw->map_id,
			(unsigned long long)rw->phys,
P
Pekka Paalanen 已提交
208
			(rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff,
209
			(rw->value >> 0) & 0xff, rw->pc, 0);
P
Pekka Paalanen 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222
		break;
	default:
		ret = trace_seq_printf(s, "rw what?\n");
		break;
	}
	if (ret)
		return 1;
	return 0;
}

static int mmio_print_map(struct trace_iterator *iter)
{
	struct trace_entry *entry = iter->ent;
223
	struct mmiotrace_map *m	= &entry->field.mmiomap;
P
Pekka Paalanen 已提交
224
	struct trace_seq *s	= &iter->seq;
225
	unsigned long long t	= ns2usecs(iter->ts);
P
Pekka Paalanen 已提交
226 227 228 229
	unsigned long usec_rem	= do_div(t, 1000000ULL);
	unsigned secs		= (unsigned long)t;
	int ret = 1;

230
	switch (entry->field.mmiorw.opcode) {
P
Pekka Paalanen 已提交
231 232
	case MMIO_PROBE:
		ret = trace_seq_printf(s,
233 234 235
			"MAP %lu.%06lu %d 0x%llx 0x%lx 0x%lx 0x%lx %d\n",
			secs, usec_rem, m->map_id,
			(unsigned long long)m->phys, m->virt, m->len,
236
			0UL, 0);
P
Pekka Paalanen 已提交
237 238 239 240
		break;
	case MMIO_UNPROBE:
		ret = trace_seq_printf(s,
			"UNMAP %lu.%06lu %d 0x%lx %d\n",
241
			secs, usec_rem, m->map_id, 0UL, 0);
P
Pekka Paalanen 已提交
242 243 244 245 246 247 248 249 250 251
		break;
	default:
		ret = trace_seq_printf(s, "map what?\n");
		break;
	}
	if (ret)
		return 1;
	return 0;
}

252 253 254 255 256
static int mmio_print_mark(struct trace_iterator *iter)
{
	struct trace_entry *entry = iter->ent;
	const char *msg		= entry->field.print.buf;
	struct trace_seq *s	= &iter->seq;
257
	unsigned long long t	= ns2usecs(iter->ts);
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
	unsigned long usec_rem	= do_div(t, 1000000ULL);
	unsigned secs		= (unsigned long)t;
	int ret;

	/* The trailing newline must be in the message. */
	ret = trace_seq_printf(s, "MARK %lu.%06lu %s", secs, usec_rem, msg);
	if (!ret)
		return 0;

	if (entry->field.flags & TRACE_FLAG_CONT)
		trace_seq_print_cont(s, iter);

	return 1;
}

P
Pekka Paalanen 已提交
273 274 275 276 277 278 279 280
/* return 0 to abort printing without consuming current entry in pipe mode */
static int mmio_print_line(struct trace_iterator *iter)
{
	switch (iter->ent->type) {
	case TRACE_MMIO_RW:
		return mmio_print_rw(iter);
	case TRACE_MMIO_MAP:
		return mmio_print_map(iter);
281 282
	case TRACE_PRINT:
		return mmio_print_mark(iter);
P
Pekka Paalanen 已提交
283 284 285
	default:
		return 1; /* ignore unknown entries */
	}
P
Pekka Paalanen 已提交
286 287 288 289 290 291 292
}

static struct tracer mmio_tracer __read_mostly =
{
	.name		= "mmiotrace",
	.init		= mmio_trace_init,
	.reset		= mmio_trace_reset,
293 294 295
	.pipe_open	= mmio_pipe_open,
	.close		= mmio_close,
	.read		= mmio_read,
P
Pekka Paalanen 已提交
296
	.ctrl_update	= mmio_trace_ctrl_update,
P
Pekka Paalanen 已提交
297
	.print_line	= mmio_print_line,
P
Pekka Paalanen 已提交
298 299 300 301 302 303 304 305
};

__init static int init_mmio_trace(void)
{
	return register_tracer(&mmio_tracer);
}
device_initcall(init_mmio_trace);

306 307 308 309
static void __trace_mmiotrace_rw(struct trace_array *tr,
				struct trace_array_cpu *data,
				struct mmiotrace_rw *rw)
{
310
	struct ring_buffer_event *event;
311 312 313
	struct trace_entry *entry;
	unsigned long irq_flags;

314 315 316 317 318
	event	= ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
					   &irq_flags);
	if (!event)
		return;
	entry	= ring_buffer_event_data(event);
319 320 321
	tracing_generic_entry_update(entry, 0);
	entry->type			= TRACE_MMIO_RW;
	entry->field.mmiorw		= *rw;
322
	ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
323 324 325 326

	trace_wake_up();
}

P
Pekka Paalanen 已提交
327
void mmio_trace_rw(struct mmiotrace_rw *rw)
P
Pekka Paalanen 已提交
328 329 330
{
	struct trace_array *tr = mmio_trace_array;
	struct trace_array_cpu *data = tr->data[smp_processor_id()];
P
Pekka Paalanen 已提交
331 332
	__trace_mmiotrace_rw(tr, data, rw);
}
P
Pekka Paalanen 已提交
333

334 335 336 337
static void __trace_mmiotrace_map(struct trace_array *tr,
				struct trace_array_cpu *data,
				struct mmiotrace_map *map)
{
338
	struct ring_buffer_event *event;
339 340 341
	struct trace_entry *entry;
	unsigned long irq_flags;

342 343 344 345 346
	event	= ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
					   &irq_flags);
	if (!event)
		return;
	entry	= ring_buffer_event_data(event);
347 348 349
	tracing_generic_entry_update(entry, 0);
	entry->type			= TRACE_MMIO_MAP;
	entry->field.mmiomap		= *map;
350
	ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
351 352 353 354

	trace_wake_up();
}

P
Pekka Paalanen 已提交
355 356 357 358 359 360 361 362 363
void mmio_trace_mapping(struct mmiotrace_map *map)
{
	struct trace_array *tr = mmio_trace_array;
	struct trace_array_cpu *data;

	preempt_disable();
	data = tr->data[smp_processor_id()];
	__trace_mmiotrace_map(tr, data, map);
	preempt_enable();
P
Pekka Paalanen 已提交
364
}
365 366 367 368 369

int mmio_trace_printk(const char *fmt, va_list args)
{
	return trace_vprintk(0, fmt, args);
}