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
	
	 Pekka Paalanen
						Pekka Paalanen