forked from mirrors/linux
		
	ftrace: add mmiotrace plugin
On Sat, 22 Mar 2008 13:07:47 +0100 Ingo Molnar <mingo@elte.hu> wrote: > > > i'd suggest the following: pull x86.git and sched-devel.git into a > > > single tree [the two will combine without rejects]. Then try to add a > > > kernel/tracing/trace_mmiotrace.c ftrace plugin. The trace_sysprof.c > > > plugin might be a good example. > > > > I did this and now I have mmiotrace enabled/disabled via the tracing > > framework (what do we call this, since ftrace is one of the tracers?). > > cool! could you send the patches for that? (even if they are not fully > functional yet) Patch attached in the end. Nice to see how much code disappeared. I tried to mark all the features I had to break with XXX-comments. Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
		
							parent
							
								
									d61fc44853
								
							
						
					
					
						commit
						f984b51e07
					
				
					 5 changed files with 123 additions and 179 deletions
				
			
		| 
						 | 
					@ -173,7 +173,8 @@ config MMIOTRACE_HOOKS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MMIOTRACE
 | 
					config MMIOTRACE
 | 
				
			||||||
	bool "Memory mapped IO tracing"
 | 
						bool "Memory mapped IO tracing"
 | 
				
			||||||
	depends on DEBUG_KERNEL && RELAY && DEBUG_FS
 | 
						depends on DEBUG_KERNEL && RELAY
 | 
				
			||||||
 | 
						select TRACING
 | 
				
			||||||
	select MMIOTRACE_HOOKS
 | 
						select MMIOTRACE_HOOKS
 | 
				
			||||||
	default y
 | 
						default y
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,9 +22,8 @@
 | 
				
			||||||
#define DEBUG 1
 | 
					#define DEBUG 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/relay.h>
 | 
					 | 
				
			||||||
#include <linux/debugfs.h>
 | 
					#include <linux/debugfs.h>
 | 
				
			||||||
#include <linux/proc_fs.h>
 | 
					#include <linux/uaccess.h>
 | 
				
			||||||
#include <asm/io.h>
 | 
					#include <asm/io.h>
 | 
				
			||||||
#include <linux/version.h>
 | 
					#include <linux/version.h>
 | 
				
			||||||
#include <linux/kallsyms.h>
 | 
					#include <linux/kallsyms.h>
 | 
				
			||||||
| 
						 | 
					@ -63,18 +62,18 @@ static const size_t subbuf_size = 256*1024;
 | 
				
			||||||
static DEFINE_PER_CPU(struct trap_reason, pf_reason);
 | 
					static DEFINE_PER_CPU(struct trap_reason, pf_reason);
 | 
				
			||||||
static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace);
 | 
					static DEFINE_PER_CPU(struct mm_io_header_rw, cpu_trace);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0 /* XXX: no way gather this info anymore */
 | 
				
			||||||
/* Access to this is not per-cpu. */
 | 
					/* Access to this is not per-cpu. */
 | 
				
			||||||
static DEFINE_PER_CPU(atomic_t, dropped);
 | 
					static DEFINE_PER_CPU(atomic_t, dropped);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct dentry *dir;
 | 
					static struct dentry *dir;
 | 
				
			||||||
static struct dentry *enabled_file;
 | 
					 | 
				
			||||||
static struct dentry *marker_file;
 | 
					static struct dentry *marker_file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static DEFINE_MUTEX(mmiotrace_mutex);
 | 
					static DEFINE_MUTEX(mmiotrace_mutex);
 | 
				
			||||||
static DEFINE_SPINLOCK(trace_lock);
 | 
					static DEFINE_SPINLOCK(trace_lock);
 | 
				
			||||||
static atomic_t mmiotrace_enabled;
 | 
					static atomic_t mmiotrace_enabled;
 | 
				
			||||||
static LIST_HEAD(trace_list);		/* struct remap_trace */
 | 
					static LIST_HEAD(trace_list);		/* struct remap_trace */
 | 
				
			||||||
static struct rchan *chan;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Locking in this file:
 | 
					 * Locking in this file:
 | 
				
			||||||
| 
						 | 
					@ -93,36 +92,24 @@ static unsigned long	filter_offset;
 | 
				
			||||||
static int		nommiotrace;
 | 
					static int		nommiotrace;
 | 
				
			||||||
static int		ISA_trace;
 | 
					static int		ISA_trace;
 | 
				
			||||||
static int		trace_pc;
 | 
					static int		trace_pc;
 | 
				
			||||||
static int		enable_now;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module_param(n_subbufs, uint, 0);
 | 
					module_param(n_subbufs, uint, 0);
 | 
				
			||||||
module_param(filter_offset, ulong, 0);
 | 
					module_param(filter_offset, ulong, 0);
 | 
				
			||||||
module_param(nommiotrace, bool, 0);
 | 
					module_param(nommiotrace, bool, 0);
 | 
				
			||||||
module_param(ISA_trace, bool, 0);
 | 
					module_param(ISA_trace, bool, 0);
 | 
				
			||||||
module_param(trace_pc, bool, 0);
 | 
					module_param(trace_pc, bool, 0);
 | 
				
			||||||
module_param(enable_now, bool, 0);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128.");
 | 
					MODULE_PARM_DESC(n_subbufs, "Number of 256kB buffers, default 128.");
 | 
				
			||||||
MODULE_PARM_DESC(filter_offset, "Start address of traced mappings.");
 | 
					MODULE_PARM_DESC(filter_offset, "Start address of traced mappings.");
 | 
				
			||||||
MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing.");
 | 
					MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing.");
 | 
				
			||||||
MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range.");
 | 
					MODULE_PARM_DESC(ISA_trace, "Do not exclude the low ISA range.");
 | 
				
			||||||
MODULE_PARM_DESC(trace_pc, "Record address of faulting instructions.");
 | 
					MODULE_PARM_DESC(trace_pc, "Record address of faulting instructions.");
 | 
				
			||||||
MODULE_PARM_DESC(enable_now, "Start mmiotracing immediately on module load.");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool is_enabled(void)
 | 
					static bool is_enabled(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return atomic_read(&mmiotrace_enabled);
 | 
						return atomic_read(&mmiotrace_enabled);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void record_timestamp(struct mm_io_header *header)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct timespec now;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	getnstimeofday(&now);
 | 
					 | 
				
			||||||
	header->sec = now.tv_sec;
 | 
					 | 
				
			||||||
	header->nsec = now.tv_nsec;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Write callback for the debugfs entry:
 | 
					 * Write callback for the debugfs entry:
 | 
				
			||||||
 * Read a marker and write it to the mmio trace log
 | 
					 * Read a marker and write it to the mmio trace log
 | 
				
			||||||
| 
						 | 
					@ -141,7 +128,6 @@ static ssize_t write_marker(struct file *file, const char __user *buffer,
 | 
				
			||||||
	headp = (struct mm_io_header *)event;
 | 
						headp = (struct mm_io_header *)event;
 | 
				
			||||||
	headp->type = MMIO_MAGIC | (MMIO_MARKER << MMIO_OPCODE_SHIFT);
 | 
						headp->type = MMIO_MAGIC | (MMIO_MARKER << MMIO_OPCODE_SHIFT);
 | 
				
			||||||
	headp->data_len = len;
 | 
						headp->data_len = len;
 | 
				
			||||||
	record_timestamp(headp);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (copy_from_user(event + sizeof(*headp), buffer, len)) {
 | 
						if (copy_from_user(event + sizeof(*headp), buffer, len)) {
 | 
				
			||||||
		kfree(event);
 | 
							kfree(event);
 | 
				
			||||||
| 
						 | 
					@ -149,9 +135,11 @@ static ssize_t write_marker(struct file *file, const char __user *buffer,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock_irq(&trace_lock);
 | 
						spin_lock_irq(&trace_lock);
 | 
				
			||||||
 | 
					#if 0 /* XXX: convert this to use tracing */
 | 
				
			||||||
	if (is_enabled())
 | 
						if (is_enabled())
 | 
				
			||||||
		relay_write(chan, event, sizeof(*headp) + len);
 | 
							relay_write(chan, event, sizeof(*headp) + len);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		len = -EINVAL;
 | 
							len = -EINVAL;
 | 
				
			||||||
	spin_unlock_irq(&trace_lock);
 | 
						spin_unlock_irq(&trace_lock);
 | 
				
			||||||
	kfree(event);
 | 
						kfree(event);
 | 
				
			||||||
| 
						 | 
					@ -242,7 +230,11 @@ static void pre(struct kmmio_probe *p, struct pt_regs *regs,
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		my_trace->rw.pc = 0;
 | 
							my_trace->rw.pc = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	record_timestamp(&my_trace->header);
 | 
						/*
 | 
				
			||||||
 | 
						 * XXX: the timestamp recorded will be *after* the tracing has been
 | 
				
			||||||
 | 
						 * done, not at the time we hit the instruction. SMP implications
 | 
				
			||||||
 | 
						 * on event ordering?
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (type) {
 | 
						switch (type) {
 | 
				
			||||||
	case REG_READ:
 | 
						case REG_READ:
 | 
				
			||||||
| 
						 | 
					@ -295,77 +287,19 @@ static void post(struct kmmio_probe *p, unsigned long condition,
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	relay_write(chan, my_trace, sizeof(*my_trace));
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * XXX: Several required values are ignored:
 | 
				
			||||||
 | 
						 * - mapping id
 | 
				
			||||||
 | 
						 * - program counter
 | 
				
			||||||
 | 
						 * Also the address should be physical, not virtual.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						mmio_trace_record(my_trace->header.type, my_trace->rw.address,
 | 
				
			||||||
 | 
												my_trace->rw.value);
 | 
				
			||||||
	put_cpu_var(cpu_trace);
 | 
						put_cpu_var(cpu_trace);
 | 
				
			||||||
	put_cpu_var(pf_reason);
 | 
						put_cpu_var(pf_reason);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * subbuf_start() relay callback.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Defined so that we know when events are dropped due to the buffer-full
 | 
					 | 
				
			||||||
 * condition.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static int subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
 | 
					 | 
				
			||||||
					void *prev_subbuf, size_t prev_padding)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	unsigned int cpu = buf->cpu;
 | 
					 | 
				
			||||||
	atomic_t *drop = &per_cpu(dropped, cpu);
 | 
					 | 
				
			||||||
	int count;
 | 
					 | 
				
			||||||
	if (relay_buf_full(buf)) {
 | 
					 | 
				
			||||||
		if (atomic_inc_return(drop) == 1)
 | 
					 | 
				
			||||||
			pr_err(NAME "cpu %d buffer full!\n", cpu);
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	count = atomic_read(drop);
 | 
					 | 
				
			||||||
	if (count) {
 | 
					 | 
				
			||||||
		pr_err(NAME "cpu %d buffer no longer full, missed %d events.\n",
 | 
					 | 
				
			||||||
								cpu, count);
 | 
					 | 
				
			||||||
		atomic_sub(count, drop);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct file_operations mmio_fops = {
 | 
					 | 
				
			||||||
	.owner = THIS_MODULE,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* file_create() callback.  Creates relay file in debugfs. */
 | 
					 | 
				
			||||||
static struct dentry *create_buf_file_handler(const char *filename,
 | 
					 | 
				
			||||||
						struct dentry *parent,
 | 
					 | 
				
			||||||
						int mode,
 | 
					 | 
				
			||||||
						struct rchan_buf *buf,
 | 
					 | 
				
			||||||
						int *is_global)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct dentry *buf_file;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mmio_fops.read = relay_file_operations.read;
 | 
					 | 
				
			||||||
	mmio_fops.open = relay_file_operations.open;
 | 
					 | 
				
			||||||
	mmio_fops.poll = relay_file_operations.poll;
 | 
					 | 
				
			||||||
	mmio_fops.mmap = relay_file_operations.mmap;
 | 
					 | 
				
			||||||
	mmio_fops.release = relay_file_operations.release;
 | 
					 | 
				
			||||||
	mmio_fops.splice_read = relay_file_operations.splice_read;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	buf_file = debugfs_create_file(filename, mode, parent, buf,
 | 
					 | 
				
			||||||
								&mmio_fops);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return buf_file;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* file_remove() default callback.  Removes relay file in debugfs. */
 | 
					 | 
				
			||||||
static int remove_buf_file_handler(struct dentry *dentry)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	debugfs_remove(dentry);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct rchan_callbacks relay_callbacks = {
 | 
					 | 
				
			||||||
	.subbuf_start = subbuf_start_handler,
 | 
					 | 
				
			||||||
	.create_buf_file = create_buf_file_handler,
 | 
					 | 
				
			||||||
	.remove_buf_file = remove_buf_file_handler,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void ioremap_trace_core(unsigned long offset, unsigned long size,
 | 
					static void ioremap_trace_core(unsigned long offset, unsigned long size,
 | 
				
			||||||
							void __iomem *addr)
 | 
												void __iomem *addr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -387,7 +321,6 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size,
 | 
				
			||||||
			.pc   = 0
 | 
								.pc   = 0
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	record_timestamp(&event.header);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!trace) {
 | 
						if (!trace) {
 | 
				
			||||||
		pr_err(NAME "kmalloc failed in ioremap\n");
 | 
							pr_err(NAME "kmalloc failed in ioremap\n");
 | 
				
			||||||
| 
						 | 
					@ -410,7 +343,10 @@ static void ioremap_trace_core(unsigned long offset, unsigned long size,
 | 
				
			||||||
	if (!is_enabled())
 | 
						if (!is_enabled())
 | 
				
			||||||
		goto not_enabled;
 | 
							goto not_enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	relay_write(chan, &event, sizeof(event));
 | 
						/*
 | 
				
			||||||
 | 
						 * XXX: Insufficient data recorded!
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						mmio_trace_record(event.header.type, event.map.addr, event.map.len);
 | 
				
			||||||
	list_add_tail(&trace->list, &trace_list);
 | 
						list_add_tail(&trace->list, &trace_list);
 | 
				
			||||||
	if (!nommiotrace)
 | 
						if (!nommiotrace)
 | 
				
			||||||
		register_kmmio_probe(&trace->probe);
 | 
							register_kmmio_probe(&trace->probe);
 | 
				
			||||||
| 
						 | 
					@ -454,7 +390,6 @@ static void iounmap_trace_core(volatile void __iomem *addr)
 | 
				
			||||||
	struct remap_trace *found_trace = NULL;
 | 
						struct remap_trace *found_trace = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr_debug(NAME "Unmapping %p.\n", addr);
 | 
						pr_debug(NAME "Unmapping %p.\n", addr);
 | 
				
			||||||
	record_timestamp(&event.header);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock_irq(&trace_lock);
 | 
						spin_lock_irq(&trace_lock);
 | 
				
			||||||
	if (!is_enabled())
 | 
						if (!is_enabled())
 | 
				
			||||||
| 
						 | 
					@ -469,7 +404,8 @@ static void iounmap_trace_core(volatile void __iomem *addr)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	relay_write(chan, &event, sizeof(event));
 | 
						mmio_trace_record(event.header.type, event.map.addr,
 | 
				
			||||||
 | 
										found_trace ? found_trace->id : -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
not_enabled:
 | 
					not_enabled:
 | 
				
			||||||
	spin_unlock_irq(&trace_lock);
 | 
						spin_unlock_irq(&trace_lock);
 | 
				
			||||||
| 
						 | 
					@ -512,77 +448,23 @@ static void clear_trace_list(void)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static ssize_t read_enabled_file_bool(struct file *file,
 | 
					 | 
				
			||||||
		char __user *user_buf, size_t count, loff_t *ppos)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	char buf[3];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (is_enabled())
 | 
					 | 
				
			||||||
		buf[0] = '1';
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		buf[0] = '0';
 | 
					 | 
				
			||||||
	buf[1] = '\n';
 | 
					 | 
				
			||||||
	buf[2] = '\0';
 | 
					 | 
				
			||||||
	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void enable_mmiotrace(void);
 | 
					 | 
				
			||||||
static void disable_mmiotrace(void);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static ssize_t write_enabled_file_bool(struct file *file,
 | 
					 | 
				
			||||||
		const char __user *user_buf, size_t count, loff_t *ppos)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	char buf[32];
 | 
					 | 
				
			||||||
	int buf_size = min(count, (sizeof(buf)-1));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (copy_from_user(buf, user_buf, buf_size))
 | 
					 | 
				
			||||||
		return -EFAULT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (buf[0]) {
 | 
					 | 
				
			||||||
	case 'y':
 | 
					 | 
				
			||||||
	case 'Y':
 | 
					 | 
				
			||||||
	case '1':
 | 
					 | 
				
			||||||
		enable_mmiotrace();
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case 'n':
 | 
					 | 
				
			||||||
	case 'N':
 | 
					 | 
				
			||||||
	case '0':
 | 
					 | 
				
			||||||
		disable_mmiotrace();
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return count;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* this ripped from kernel/kprobes.c */
 | 
					 | 
				
			||||||
static struct file_operations fops_enabled = {
 | 
					 | 
				
			||||||
	.owner =	THIS_MODULE,
 | 
					 | 
				
			||||||
	.read =		read_enabled_file_bool,
 | 
					 | 
				
			||||||
	.write =	write_enabled_file_bool
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct file_operations fops_marker = {
 | 
					static struct file_operations fops_marker = {
 | 
				
			||||||
	.owner =	THIS_MODULE,
 | 
						.owner =	THIS_MODULE,
 | 
				
			||||||
	.write =	write_marker
 | 
						.write =	write_marker
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void enable_mmiotrace(void)
 | 
					void enable_mmiotrace(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	mutex_lock(&mmiotrace_mutex);
 | 
						mutex_lock(&mmiotrace_mutex);
 | 
				
			||||||
	if (is_enabled())
 | 
						if (is_enabled())
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chan = relay_open("cpu", dir, subbuf_size, n_subbufs,
 | 
					 | 
				
			||||||
						&relay_callbacks, NULL);
 | 
					 | 
				
			||||||
	if (!chan) {
 | 
					 | 
				
			||||||
		pr_err(NAME "relay app channel creation failed.\n");
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	reference_kmmio();
 | 
						reference_kmmio();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0 /* XXX: tracing does not support text entries */
 | 
				
			||||||
	marker_file = debugfs_create_file("marker", 0660, dir, NULL,
 | 
						marker_file = debugfs_create_file("marker", 0660, dir, NULL,
 | 
				
			||||||
								&fops_marker);
 | 
													&fops_marker);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	if (!marker_file)
 | 
						if (!marker_file)
 | 
				
			||||||
		pr_err(NAME "marker file creation failed.\n");
 | 
							pr_err(NAME "marker file creation failed.\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -598,7 +480,7 @@ static void enable_mmiotrace(void)
 | 
				
			||||||
	mutex_unlock(&mmiotrace_mutex);
 | 
						mutex_unlock(&mmiotrace_mutex);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void disable_mmiotrace(void)
 | 
					void disable_mmiotrace(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	mutex_lock(&mmiotrace_mutex);
 | 
						mutex_lock(&mmiotrace_mutex);
 | 
				
			||||||
	if (!is_enabled())
 | 
						if (!is_enabled())
 | 
				
			||||||
| 
						 | 
					@ -615,17 +497,13 @@ static void disable_mmiotrace(void)
 | 
				
			||||||
		debugfs_remove(marker_file);
 | 
							debugfs_remove(marker_file);
 | 
				
			||||||
		marker_file = NULL;
 | 
							marker_file = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (chan) {
 | 
					 | 
				
			||||||
		relay_close(chan);
 | 
					 | 
				
			||||||
		chan = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pr_info(NAME "disabled.\n");
 | 
						pr_info(NAME "disabled.\n");
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	mutex_unlock(&mmiotrace_mutex);
 | 
						mutex_unlock(&mmiotrace_mutex);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __init init(void)
 | 
					int __init init_mmiotrace(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pr_debug(NAME "load...\n");
 | 
						pr_debug(NAME "load...\n");
 | 
				
			||||||
	if (n_subbufs < 2)
 | 
						if (n_subbufs < 2)
 | 
				
			||||||
| 
						 | 
					@ -636,31 +514,5 @@ static int __init init(void)
 | 
				
			||||||
		pr_err(NAME "Couldn't create relay app directory.\n");
 | 
							pr_err(NAME "Couldn't create relay app directory.\n");
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	enabled_file = debugfs_create_file("enabled", 0600, dir, NULL,
 | 
					 | 
				
			||||||
								&fops_enabled);
 | 
					 | 
				
			||||||
	if (!enabled_file) {
 | 
					 | 
				
			||||||
		pr_err(NAME "Couldn't create enabled file.\n");
 | 
					 | 
				
			||||||
		debugfs_remove(dir);
 | 
					 | 
				
			||||||
		return -ENOMEM;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (enable_now)
 | 
					 | 
				
			||||||
		enable_mmiotrace();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
static void __exit cleanup(void)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	pr_debug(NAME "unload...\n");
 | 
					 | 
				
			||||||
	if (enabled_file)
 | 
					 | 
				
			||||||
		debugfs_remove(enabled_file);
 | 
					 | 
				
			||||||
	disable_mmiotrace();
 | 
					 | 
				
			||||||
	if (dir)
 | 
					 | 
				
			||||||
		debugfs_remove(dir);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module_init(init);
 | 
					 | 
				
			||||||
module_exit(cleanup);
 | 
					 | 
				
			||||||
MODULE_LICENSE("GPL");
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,6 +54,12 @@ static inline void mmiotrace_iounmap(volatile void __iomem *addr)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif /* CONFIG_MMIOTRACE_HOOKS */
 | 
					#endif /* CONFIG_MMIOTRACE_HOOKS */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* in kernel/trace/trace_mmiotrace.c */
 | 
				
			||||||
 | 
					extern int __init init_mmiotrace(void);
 | 
				
			||||||
 | 
					extern void enable_mmiotrace(void);
 | 
				
			||||||
 | 
					extern void disable_mmiotrace(void);
 | 
				
			||||||
 | 
					extern void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* __KERNEL__ */
 | 
					#endif /* __KERNEL__ */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,5 +18,6 @@ obj-$(CONFIG_FTRACE) += trace_functions.o
 | 
				
			||||||
obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o
 | 
					obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o
 | 
				
			||||||
obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o
 | 
					obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o
 | 
				
			||||||
obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o
 | 
					obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o
 | 
				
			||||||
 | 
					obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
libftrace-y := ftrace.o
 | 
					libftrace-y := ftrace.o
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										84
									
								
								kernel/trace/trace_mmiotrace.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								kernel/trace/trace_mmiotrace.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,84 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Memory mapped I/O tracing
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 Pekka Paalanen <pq@iki.fi>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DEBUG 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
 | 
					#include <linux/mmiotrace.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "trace.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern void
 | 
				
			||||||
 | 
					__trace_special(void *__tr, void *__data,
 | 
				
			||||||
 | 
							unsigned long arg1, unsigned long arg2, unsigned long arg3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct trace_array *mmio_trace_array;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mmio_trace_init(struct trace_array *tr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pr_debug("in %s\n", __func__);
 | 
				
			||||||
 | 
						mmio_trace_array = tr;
 | 
				
			||||||
 | 
						if (tr->ctrl)
 | 
				
			||||||
 | 
							enable_mmiotrace();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mmio_trace_reset(struct trace_array *tr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pr_debug("in %s\n", __func__);
 | 
				
			||||||
 | 
						if (tr->ctrl)
 | 
				
			||||||
 | 
							disable_mmiotrace();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mmio_trace_ctrl_update(struct trace_array *tr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pr_debug("in %s\n", __func__);
 | 
				
			||||||
 | 
						if (tr->ctrl)
 | 
				
			||||||
 | 
							enable_mmiotrace();
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							disable_mmiotrace();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct tracer mmio_tracer __read_mostly =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						.name		= "mmiotrace",
 | 
				
			||||||
 | 
						.init		= mmio_trace_init,
 | 
				
			||||||
 | 
						.reset		= mmio_trace_reset,
 | 
				
			||||||
 | 
						.ctrl_update	= mmio_trace_ctrl_update,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__init static int init_mmio_trace(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret = init_mmiotrace();
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						return register_tracer(&mmio_tracer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					device_initcall(init_mmio_trace);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct trace_array *tr = mmio_trace_array;
 | 
				
			||||||
 | 
						struct trace_array_cpu *data = tr->data[smp_processor_id()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!current || current->pid == 0) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * XXX: This is a problem. We need to able to record, no
 | 
				
			||||||
 | 
							 * matter what. tracing_generic_entry_update() would crash.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							static unsigned limit;
 | 
				
			||||||
 | 
							if (limit++ < 12)
 | 
				
			||||||
 | 
								pr_err("Error in %s: no current.\n", __func__);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!tr || !data) {
 | 
				
			||||||
 | 
							static unsigned limit;
 | 
				
			||||||
 | 
							if (limit++ < 12)
 | 
				
			||||||
 | 
								pr_err("%s: no tr or data\n", __func__);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						__trace_special(tr, data, type, addr, arg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in a new issue