trace_mmiotrace.c 7.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 30 31
	tr->time_start = ftrace_now(tr->cpu);

	for_each_online_cpu(cpu)
		tracing_reset(tr->data[cpu]);
}
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 133 134 135 136 137 138 139
static unsigned long count_overruns(struct trace_iterator *iter)
{
	int cpu;
	unsigned long cnt = 0;
	for_each_online_cpu(cpu) {
		cnt += iter->overrun[cpu];
		iter->overrun[cpu] = 0;
	}
	return cnt;
}

140 141 142 143 144 145
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 已提交
146 147 148 149 150 151 152 153 154 155 156
	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;
	}
157 158 159 160 161 162 163 164 165 166 167 168

	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 已提交
169
print_out:
170 171
	ret = trace_seq_to_user(s, ubuf, cnt);
	return (ret == -EBUSY) ? 0 : ret;
P
Pekka Paalanen 已提交
172 173 174 175 176
}

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

184
	switch (entry->field.mmiorw.opcode) {
P
Pekka Paalanen 已提交
185 186
	case MMIO_READ:
		ret = trace_seq_printf(s,
187 188 189
			"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,
190
			rw->value, rw->pc, 0);
P
Pekka Paalanen 已提交
191 192 193
		break;
	case MMIO_WRITE:
		ret = trace_seq_printf(s,
194 195 196
			"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,
197
			rw->value, rw->pc, 0);
P
Pekka Paalanen 已提交
198 199 200
		break;
	case MMIO_UNKNOWN_OP:
		ret = trace_seq_printf(s,
201 202 203
			"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 已提交
204
			(rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff,
205
			(rw->value >> 0) & 0xff, rw->pc, 0);
P
Pekka Paalanen 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218
		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;
219
	struct mmiotrace_map *m	= &entry->field.mmiomap;
P
Pekka Paalanen 已提交
220
	struct trace_seq *s	= &iter->seq;
221
	unsigned long long t	= ns2usecs(entry->field.t);
P
Pekka Paalanen 已提交
222 223 224 225
	unsigned long usec_rem	= do_div(t, 1000000ULL);
	unsigned secs		= (unsigned long)t;
	int ret = 1;

226
	switch (entry->field.mmiorw.opcode) {
P
Pekka Paalanen 已提交
227 228
	case MMIO_PROBE:
		ret = trace_seq_printf(s,
229 230 231
			"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,
232
			0UL, 0);
P
Pekka Paalanen 已提交
233 234 235 236
		break;
	case MMIO_UNPROBE:
		ret = trace_seq_printf(s,
			"UNMAP %lu.%06lu %d 0x%lx %d\n",
237
			secs, usec_rem, m->map_id, 0UL, 0);
P
Pekka Paalanen 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
		break;
	default:
		ret = trace_seq_printf(s, "map what?\n");
		break;
	}
	if (ret)
		return 1;
	return 0;
}

/* 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);
	default:
		return 1; /* ignore unknown entries */
	}
P
Pekka Paalanen 已提交
259 260 261 262 263 264 265
}

static struct tracer mmio_tracer __read_mostly =
{
	.name		= "mmiotrace",
	.init		= mmio_trace_init,
	.reset		= mmio_trace_reset,
266 267 268
	.pipe_open	= mmio_pipe_open,
	.close		= mmio_close,
	.read		= mmio_read,
P
Pekka Paalanen 已提交
269
	.ctrl_update	= mmio_trace_ctrl_update,
P
Pekka Paalanen 已提交
270
	.print_line	= mmio_print_line,
P
Pekka Paalanen 已提交
271 272 273 274 275 276 277 278
};

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

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
static void __trace_mmiotrace_rw(struct trace_array *tr,
				struct trace_array_cpu *data,
				struct mmiotrace_rw *rw)
{
	struct trace_entry *entry;
	unsigned long irq_flags;

	raw_local_irq_save(irq_flags);
	__raw_spin_lock(&data->lock);

	entry				= tracing_get_trace_entry(tr, data);
	tracing_generic_entry_update(entry, 0);
	entry->type			= TRACE_MMIO_RW;
	entry->field.mmiorw		= *rw;

	__raw_spin_unlock(&data->lock);
	raw_local_irq_restore(irq_flags);

	trace_wake_up();
}

P
Pekka Paalanen 已提交
300
void mmio_trace_rw(struct mmiotrace_rw *rw)
P
Pekka Paalanen 已提交
301 302 303
{
	struct trace_array *tr = mmio_trace_array;
	struct trace_array_cpu *data = tr->data[smp_processor_id()];
P
Pekka Paalanen 已提交
304 305
	__trace_mmiotrace_rw(tr, data, rw);
}
P
Pekka Paalanen 已提交
306

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
static void __trace_mmiotrace_map(struct trace_array *tr,
				struct trace_array_cpu *data,
				struct mmiotrace_map *map)
{
	struct trace_entry *entry;
	unsigned long irq_flags;

	raw_local_irq_save(irq_flags);
	__raw_spin_lock(&data->lock);

	entry				= tracing_get_trace_entry(tr, data);
	tracing_generic_entry_update(entry, 0);
	entry->type			= TRACE_MMIO_MAP;
	entry->field.mmiomap		= *map;

	__raw_spin_unlock(&data->lock);
	raw_local_irq_restore(irq_flags);

	trace_wake_up();
}

P
Pekka Paalanen 已提交
328 329 330 331 332 333 334 335 336
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 已提交
337
}
338 339 340 341 342

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