trace_mmiotrace.c 8.9 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
static enum print_line_t mmio_print_rw(struct trace_iterator *iter)
P
Pekka Paalanen 已提交
179 180
{
	struct trace_entry *entry = iter->ent;
S
Steven Rostedt 已提交
181 182
	struct trace_mmiotrace_rw *field;
	struct mmiotrace_rw *rw;
P
Pekka Paalanen 已提交
183
	struct trace_seq *s	= &iter->seq;
184
	unsigned long long t	= ns2usecs(iter->ts);
P
Pekka Paalanen 已提交
185 186 187 188
	unsigned long usec_rem	= do_div(t, 1000000ULL);
	unsigned secs		= (unsigned long)t;
	int ret = 1;

S
Steven Rostedt 已提交
189 190 191
	trace_assign_type(field, entry);
	rw = &field->rw;

192
	switch (rw->opcode) {
P
Pekka Paalanen 已提交
193 194
	case MMIO_READ:
		ret = trace_seq_printf(s,
195 196 197
			"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,
198
			rw->value, rw->pc, 0);
P
Pekka Paalanen 已提交
199 200 201
		break;
	case MMIO_WRITE:
		ret = trace_seq_printf(s,
202 203 204
			"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,
205
			rw->value, rw->pc, 0);
P
Pekka Paalanen 已提交
206 207 208
		break;
	case MMIO_UNKNOWN_OP:
		ret = trace_seq_printf(s,
209 210 211
			"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 已提交
212
			(rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff,
213
			(rw->value >> 0) & 0xff, rw->pc, 0);
P
Pekka Paalanen 已提交
214 215 216 217 218 219
		break;
	default:
		ret = trace_seq_printf(s, "rw what?\n");
		break;
	}
	if (ret)
220 221
		return TRACE_TYPE_HANDLED;
	return TRACE_TYPE_PARTIAL_LINE;
P
Pekka Paalanen 已提交
222 223
}

224
static enum print_line_t mmio_print_map(struct trace_iterator *iter)
P
Pekka Paalanen 已提交
225 226
{
	struct trace_entry *entry = iter->ent;
S
Steven Rostedt 已提交
227 228
	struct trace_mmiotrace_map *field;
	struct mmiotrace_map *m;
P
Pekka Paalanen 已提交
229
	struct trace_seq *s	= &iter->seq;
230
	unsigned long long t	= ns2usecs(iter->ts);
P
Pekka Paalanen 已提交
231 232
	unsigned long usec_rem	= do_div(t, 1000000ULL);
	unsigned secs		= (unsigned long)t;
233
	int ret;
P
Pekka Paalanen 已提交
234

S
Steven Rostedt 已提交
235 236 237
	trace_assign_type(field, entry);
	m = &field->map;

238
	switch (m->opcode) {
P
Pekka Paalanen 已提交
239 240
	case MMIO_PROBE:
		ret = trace_seq_printf(s,
241 242 243
			"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,
244
			0UL, 0);
P
Pekka Paalanen 已提交
245 246 247 248
		break;
	case MMIO_UNPROBE:
		ret = trace_seq_printf(s,
			"UNMAP %lu.%06lu %d 0x%lx %d\n",
249
			secs, usec_rem, m->map_id, 0UL, 0);
P
Pekka Paalanen 已提交
250 251 252 253 254 255
		break;
	default:
		ret = trace_seq_printf(s, "map what?\n");
		break;
	}
	if (ret)
256 257
		return TRACE_TYPE_HANDLED;
	return TRACE_TYPE_PARTIAL_LINE;
P
Pekka Paalanen 已提交
258 259
}

260
static enum print_line_t mmio_print_mark(struct trace_iterator *iter)
261 262
{
	struct trace_entry *entry = iter->ent;
263 264
	struct print_entry *print = (struct print_entry *)entry;
	const char *msg		= print->buf;
265
	struct trace_seq *s	= &iter->seq;
266
	unsigned long long t	= ns2usecs(iter->ts);
267 268 269 270 271 272 273
	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)
274
		return TRACE_TYPE_PARTIAL_LINE;
275

276
	if (entry->flags & TRACE_FLAG_CONT)
277 278
		trace_seq_print_cont(s, iter);

279
	return TRACE_TYPE_HANDLED;
280 281
}

282
static enum print_line_t mmio_print_line(struct trace_iterator *iter)
P
Pekka Paalanen 已提交
283 284 285 286 287 288
{
	switch (iter->ent->type) {
	case TRACE_MMIO_RW:
		return mmio_print_rw(iter);
	case TRACE_MMIO_MAP:
		return mmio_print_map(iter);
289 290
	case TRACE_PRINT:
		return mmio_print_mark(iter);
P
Pekka Paalanen 已提交
291
	default:
292
		return TRACE_TYPE_HANDLED; /* ignore unknown entries */
P
Pekka Paalanen 已提交
293
	}
P
Pekka Paalanen 已提交
294 295 296 297 298 299 300
}

static struct tracer mmio_tracer __read_mostly =
{
	.name		= "mmiotrace",
	.init		= mmio_trace_init,
	.reset		= mmio_trace_reset,
301 302 303
	.pipe_open	= mmio_pipe_open,
	.close		= mmio_close,
	.read		= mmio_read,
P
Pekka Paalanen 已提交
304
	.ctrl_update	= mmio_trace_ctrl_update,
P
Pekka Paalanen 已提交
305
	.print_line	= mmio_print_line,
P
Pekka Paalanen 已提交
306 307 308 309 310 311 312 313
};

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

314 315 316 317
static void __trace_mmiotrace_rw(struct trace_array *tr,
				struct trace_array_cpu *data,
				struct mmiotrace_rw *rw)
{
318
	struct ring_buffer_event *event;
319
	struct trace_mmiotrace_rw *entry;
320 321
	unsigned long irq_flags;

322 323 324 325 326
	event	= ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
					   &irq_flags);
	if (!event)
		return;
	entry	= ring_buffer_event_data(event);
327
	tracing_generic_entry_update(&entry->ent, 0, preempt_count());
328 329
	entry->ent.type			= TRACE_MMIO_RW;
	entry->rw			= *rw;
330
	ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
331 332 333 334

	trace_wake_up();
}

P
Pekka Paalanen 已提交
335
void mmio_trace_rw(struct mmiotrace_rw *rw)
P
Pekka Paalanen 已提交
336 337 338
{
	struct trace_array *tr = mmio_trace_array;
	struct trace_array_cpu *data = tr->data[smp_processor_id()];
P
Pekka Paalanen 已提交
339 340
	__trace_mmiotrace_rw(tr, data, rw);
}
P
Pekka Paalanen 已提交
341

342 343 344 345
static void __trace_mmiotrace_map(struct trace_array *tr,
				struct trace_array_cpu *data,
				struct mmiotrace_map *map)
{
346
	struct ring_buffer_event *event;
347
	struct trace_mmiotrace_map *entry;
348 349
	unsigned long irq_flags;

350 351 352 353 354
	event	= ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
					   &irq_flags);
	if (!event)
		return;
	entry	= ring_buffer_event_data(event);
355
	tracing_generic_entry_update(&entry->ent, 0, preempt_count());
356 357
	entry->ent.type			= TRACE_MMIO_MAP;
	entry->map			= *map;
358
	ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
359 360 361 362

	trace_wake_up();
}

P
Pekka Paalanen 已提交
363 364 365 366 367 368 369 370 371
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 已提交
372
}
373 374 375 376 377

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