public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] LTT core implementation
@ 2004-12-15  4:05 Karim Yaghmour
  0 siblings, 0 replies; only message in thread
From: Karim Yaghmour @ 2004-12-15  4:05 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, LTT-Dev


Andrew,

Per our discussion a while ago, this is an updated set of
patches for LTT with a cleanup of the namespace to avoid the
use of upper-case macros. All four patches are against
rc3-bk8. Your feedback is greatly appreciated.

This is based on the 2.6.9 set of patches shipped with LTT
0.9.6pre4 which was released earlier today.

Karim

Signed-off-by: Karim Yaghmour <karim@opersys.com>

diff -urpN linux-2.6.10-rc3-bk8-relayfs/kernel/ltt-core.c linux-2.6.10-rc3-bk8-relayfs-ltt/kernel/ltt-core.c
--- linux-2.6.10-rc3-bk8-relayfs/kernel/ltt-core.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.10-rc3-bk8-relayfs-ltt/kernel/ltt-core.c	2004-12-14 17:58:05.000000000 -0500
@@ -0,0 +1,2589 @@
+/*
+ * ltt-core.c
+ *
+ * (C) Copyright, 1999, 2000, 2001, 2002, 2003, 2004 -
+ *              Karim Yaghmour (karim@opersys.com)
+ *
+ * Contains the kernel code for the Linux Trace Toolkit.
+ *
+ * Author:
+ *	Karim Yaghmour (karim@opersys.com)
+ *
+ * Changelog:
+ *	14/12/04, Renamed trace macros and variables to avoid namespace
+ *		pollution (i.e. TRACE_XXX is now ltt_ev_xxx, etc.)
+ *	24/01/04, Revamped tracer to rely entirely on relayfs, no sys_trace.
+ *		Renamed all relevant files and functions from trace* to ltt*.
+ *	14/03/03, Modified to use relayfs (zanussi@us.ibm.com)
+ *	15/10/02, Changed tracer from device to kernel subsystem and added
+ *		custom trace system call (sys_trace).
+ *	01/10/02, Coding style change to fit with kernel coding style.
+ *	16/02/02, Added Tom Zanussi's implementation of K42's lockless logging.
+ *		K42 tracing guru Robert Wisniewski participated in the
+ *		discussions surrounding this implementation. A big thanks to
+ *		the IBM folks.
+ *	03/12/01, Added user event support.
+ *	05/01/01, Modified PPC bit manipulation functions for x86
+ *		compatibility (andy_lowe@mvista.com).
+ *	15/11/00, Finally fixed memory allocation and remapping method. Now
+ *		using BTTV-driver-inspired code.
+ *	13/03/00, Modified tracer so that the daemon mmaps the tracer's buffers
+ *		in it's address space rather than use "read".
+ *	26/01/00, Added support for standardized buffer sizes and extensibility
+ *		of events.
+ *	01/10/99, Modified tracer in order to used double-buffering.
+ *	28/09/99, Adding tracer configuration support.
+ *	09/09/99, Changing the format of an event record in order to reduce the
+ *		size of the traces.
+ *	04/03/99, Initial typing.
+ *
+ * Note:
+ *	The sizes of the variables used to store the details of an event are
+ *	planned for a system who gets at least one clock tick every 10
+ *	milli-seconds. There has to be at least one event every 2^32-1
+ *	microseconds, otherwise the size of the variable holding the time
+ *	doesn't work anymore.
+ */
+
+#include <linux/init.h>
+#include <linux/ltt-core.h>
+#include <linux/ltt-events.h>
+#include <linux/errno.h>
+#include <linux/stddef.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+
+#include <asm/io.h>
+#include <asm/current.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/relay.h>
+#include <asm/ltt.h>
+
+/* Tracer configuration */
+static int		num_cpus;
+
+/* System call tracing behavior */
+unsigned int		syscall_entry_trace_active = 0;
+unsigned int		syscall_exit_trace_active = 0;
+static int		use_syscall_eip_bounds;
+static int		lower_eip_bound_set;
+static int		upper_eip_bound_set;
+static void*		lower_eip_bound;
+static void*		upper_eip_bound;
+static int		fetch_syscall_eip_use_depth;
+static int		fetch_syscall_eip_use_bounds;
+static void*		syscall_lower_eip_bound;
+static void*		syscall_upper_eip_bound;
+static int		syscall_eip_depth;
+static int		syscall_eip_depth_set;
+
+/* Data buffer management */
+static struct ltt_trace_struct	current_traces[NR_TRACES];
+static u32			start_reserve = LTT_TRACER_FIRST_EVENT_SIZE;
+static u32			end_reserve = LTT_TRACER_LAST_EVENT_SIZE;
+static u32			trace_start_reserve = LTT_TRACER_START_TRACE_EVENT_SIZE;
+static struct ltt_arch_info	ltt_arch_info;
+static struct ltt_buf_control_info	shared_buf_ctl;
+static char *			user_event_data = NULL;
+
+/* Relayfs interaction */
+static struct rchan_callbacks	ltt_callbacks;			/* relayfs callbacks */
+static char			relay_file_name[PATH_MAX];	/* scratch area */
+
+/* Timer management */
+static struct timer_list	heartbeat_timer;
+static struct timer_list	percpu_timer[NR_CPUS] __cacheline_aligned;
+
+/* /proc variables */
+static struct proc_dir_entry *	ltt_proc_root_entry; /* proc/ltt */
+static int			tmp_rchan_handles[NR_CPUS];
+static char			relayfs_path[PATH_MAX];	/* path to attribs */
+static int			control_channel; /* LTT control channel */
+
+/* Forward declarations */
+static struct proc_dir_entry *create_handle_proc_dir(unsigned trace_handle);
+static void remove_handle_proc_dir(struct proc_dir_entry *handle_dir,
+				   unsigned trace_handle);
+
+/* Size of statically defined events */
+int event_struct_size[LTT_EV_MAX + 1] =
+{
+	sizeof(ltt_trace_start),
+	sizeof(ltt_syscall_entry),
+	0,				/* LTT_SYSCALL_EXIT */
+	sizeof(ltt_trap_entry),
+	0,				/* LTT_TRAP_EXIT */
+	sizeof(ltt_irq_entry),
+	0,				/* LTT_IRQ_EXIT */
+	sizeof(ltt_schedchange),
+	0,				/* LTT_KERNEL_TIMER */
+	sizeof(ltt_soft_irq),
+	sizeof(ltt_process),
+	sizeof(ltt_file_system),
+	sizeof(ltt_timer),
+	sizeof(ltt_memory),
+	sizeof(ltt_socket),
+	sizeof(ltt_ipc),
+	sizeof(ltt_network),
+	sizeof(ltt_buffer_start),
+	sizeof(ltt_buffer_end),
+	sizeof(ltt_new_event),
+	sizeof(ltt_custom),
+	sizeof(ltt_change_mask),
+	0				/* LTT_HEARTBEAT */
+};
+
+/* Custom event description */
+struct custom_event_desc {
+	ltt_new_event event;
+
+	pid_t owner_pid;
+
+	struct custom_event_desc *next;
+	struct custom_event_desc *prev;
+};
+
+/* Custom event management */
+static int			next_event_id = LTT_EV_MAX + 1;
+static rwlock_t			custom_list_lock = RW_LOCK_UNLOCKED;
+static rwlock_t			trace_handle_table_lock = RW_LOCK_UNLOCKED;
+static struct custom_event_desc	custom_events_head;
+static struct custom_event_desc	*custom_events = NULL;
+
+/* Handle management */
+struct trace_handle_struct{
+	struct task_struct	*owner;
+};
+static struct trace_handle_struct trace_handle_table[LTT_MAX_HANDLES];
+
+/*
+ * Helper functions
+ */
+
+/**
+ *	set_waiting_for_cpu_async: - Utility function for setting wait flags
+ *	@cpu_id: which CPU to set flag on
+ *	@bit: which bit to set
+ */
+static inline void set_waiting_for_cpu_async(unsigned int trace_handle, u8 cpu_id, int bit)
+{
+	atomic_set(&waiting_for_cpu_async(trace_handle, cpu_id),
+		   atomic_read(&waiting_for_cpu_async(trace_handle, cpu_id)) | bit);
+}
+
+/**
+ *	clear_waiting_for_cpu_async: - Utility function for clearing wait flags
+ *	@cpu_id: which CPU to clear flag on
+ *	@bit: which bit to clear
+ */
+static inline void clear_waiting_for_cpu_async(unsigned int trace_handle, u8 cpu_id, int bit)
+{
+	atomic_set(&waiting_for_cpu_async(trace_handle, cpu_id),
+		   atomic_read(&waiting_for_cpu_async(trace_handle, cpu_id)) & ~bit);
+}
+
+/*
+ * Trace heartbeat
+ */
+
+/**
+ *	write_heartbeat_event: - Timer function generating hearbeat event.
+ *	@data: unused
+ *
+ *	Guarantees at least 1 event is logged before low word of TSC wraps.
+ */
+static void write_heartbeat_event(unsigned long data)
+{
+	unsigned long int flags;
+	int i, j;
+	
+	local_irq_save(flags);
+	for (i = 0; i < NR_TRACES; i++) {
+		if (current_traces[i].active && current_traces[i].using_tsc) {
+			for (j =  0; j < num_cpus; j++)
+				set_waiting_for_cpu_async(i, j, LTT_TRACE_HEARTBEAT);
+		}
+	}
+	local_irq_restore(flags);
+
+	del_timer(&heartbeat_timer);
+	heartbeat_timer.expires = jiffies + 0xffffffffUL/loops_per_jiffy - 1;
+	add_timer(&heartbeat_timer);
+}
+
+/**
+ *	need_heartbeat: - If any active trace uses TSC timestamping, return 1
+ *
+ *	Returns the number of active traces using TSC timestamping
+ *
+ *	Needed for starting/stopping the heartbeat timer depending on whether
+ *	any trace needs it or not.
+ */
+int need_heartbeat(void)
+{
+	int i, retval = 0;
+	struct ltt_trace_struct *trace;
+	
+	for (i = 0; i < NR_TRACES; i++) {
+		trace = &current_traces[i];
+		if(trace->active && trace->using_tsc)
+			retval++;
+	}
+
+	return retval;
+}
+
+/**
+ *	init_heartbeat_timer: - Start timer generating hearbeat events.
+ */
+static void init_heartbeat_timer(void)
+{
+	if (loops_per_jiffy > 0) {
+		init_timer(&heartbeat_timer);
+		heartbeat_timer.function = write_heartbeat_event;
+		heartbeat_timer.expires = jiffies
+			+ 0xffffffffUL/loops_per_jiffy - 1;
+		add_timer(&heartbeat_timer);
+	}
+	else
+		printk(KERN_ALERT "LTT: No TSC for heartbeat timer - continuing without one \n");
+}
+
+/**
+ *	delete_heartbeat_timer: - Stop timer generating hearbeat events.
+ */
+static void delete_heartbeat_timer(void)
+{
+	if (loops_per_jiffy > 0)
+		del_timer(&heartbeat_timer);
+}
+
+/*
+ * Tasks and timers for trace finalization
+ */
+
+/**
+ *	all_channels_finalized: - Verify that all channels have been finalized.
+ *	@trace_handle: the trace containing the channels to be tested
+ *
+ *	Returns 1 if channels on all CPUs are complete, 0 otherwise.
+ */
+static int all_channels_finalized(unsigned int trace_handle)
+{
+	int i;
+	
+	for (i = 0; i < num_cpus; i++)
+		if (atomic_read(&waiting_for_cpu_async(trace_handle, i)) & LTT_FINALIZE_TRACE)
+			return 0;
+
+	return 1;
+}
+
+/**
+ *	active_traces: - The number of currently active traces
+ *
+ *	Returns the number of active traces
+ */
+static inline int active_traces(void)
+{
+	int i, nr_active = 0;
+
+	for (i = 0; i < NR_TRACES; i++)
+		if (current_traces[i].active)
+			nr_active++;
+
+	return nr_active;
+}
+
+/**
+ *	del_percpu_timers: - Delete all per_cpu timers.
+ */
+static inline void del_percpu_timers(void)
+{
+	int i;
+
+	for (i =  0; i < num_cpus; i++)
+		del_timer_sync(&percpu_timer[i]);
+}
+
+/**
+ *	remove_readers_async: - Remove all map readers asynchronously.
+ *	@private: the trace_handle containing the readers to be removed
+ */
+static void remove_readers_async(void *private)
+{
+	int i;
+	unsigned int trace_handle = (unsigned int)private;
+
+	for (i = 0; i < num_cpus; i++) {
+		remove_map_reader(trace_channel_reader(trace_handle, i));
+		trace_channel_reader(trace_handle, i) = NULL;
+	}
+}
+
+/**
+ *	remove_readers: - Remove all map readers.
+ *	@trace_handle: the trace containing the readers to be removed
+ */
+static inline void remove_readers(unsigned int trace_handle)
+{
+	int i;
+	
+	for (i = 0; i < num_cpus; i++) {
+		remove_map_reader(trace_channel_reader(trace_handle, i));
+		trace_channel_reader(trace_handle, i) = NULL;
+	}
+}
+
+/**
+ *	do_waiting_async_tasks: - perform asynchronous per-CPU tasks.
+ *	@trace_handle: the trace handle
+ *	@cpu_id: the CPU the tasks should be executed on
+ */
+static void do_waiting_async_tasks(unsigned int trace_handle, u8 cpu_id)
+{
+	unsigned long int flags;
+	int tasks;
+	struct ltt_trace_struct *trace;
+	
+	trace = &current_traces[trace_handle];
+
+	local_irq_save(flags);
+	tasks = atomic_read(&waiting_for_cpu_async(trace_handle, cpu_id));
+
+	if (tasks == 0) {
+		local_irq_restore(flags);
+		return;
+	}
+
+	if (trace->using_tsc && trace->tracer_started && (tasks & LTT_TRACE_HEARTBEAT)) {
+                clear_waiting_for_cpu_async(trace_handle, cpu_id, LTT_TRACE_HEARTBEAT);
+		ltt_ev_heartbeat();
+	}
+
+	if (trace->tracer_stopping && (tasks & LTT_FINALIZE_TRACE)) {
+                clear_waiting_for_cpu_async(trace_handle, cpu_id, LTT_FINALIZE_TRACE);
+		if (relay_close(trace_channel_handle(trace_handle, cpu_id)) != 0)
+			printk(KERN_ALERT "LTT: Couldn't close trace channel %d\n", trace_channel_handle(trace_handle, cpu_id));
+
+		set_bit(cpu_id, &trace->buffer_switches_pending);
+
+		if (all_channels_finalized(trace_handle)) {
+			PREPARE_WORK(&trace->work, remove_readers_async, (void *)trace_handle);
+			schedule_work(&trace->work);
+
+			trace->tracer_stopping = 0;
+		}
+	}
+
+	local_irq_restore(flags);
+}
+
+/**
+ *	check_waiting_async_tasks: - Timer function checking for async tasks.
+ *	@data: unused
+ */
+static void check_waiting_async_tasks(unsigned long data)
+{
+	int i;
+	int cpu = smp_processor_id();
+
+	for (i = 0; i < NR_TRACES; i++) {
+		if (atomic_read(&waiting_for_cpu_async(i, cpu)) != 0)
+			do_waiting_async_tasks(i, cpu);
+	}
+
+	del_timer(&percpu_timer[cpu]);
+	percpu_timer[cpu].expires = jiffies + LTT_PERCPU_TIMER_FREQ;
+	add_timer(&percpu_timer[cpu]);
+}
+
+/**
+ *	_init_percpu_timer: - Start timer checking for async tasks.
+ */
+void _init_percpu_timer(void * dummy)
+{
+	int cpu = smp_processor_id();
+
+	init_timer(&percpu_timer[cpu]);
+	percpu_timer[cpu].function = check_waiting_async_tasks;
+	percpu_timer[cpu].expires = jiffies + LTT_PERCPU_TIMER_FREQ;
+	add_timer(&percpu_timer[cpu]);
+}
+
+static inline void init_percpu_timers(void)
+{
+	_init_percpu_timer(NULL);
+
+	if (smp_call_function(_init_percpu_timer, NULL, 1, 1) != 0)
+		printk(KERN_ALERT "LTT: Couldn't initialize all per-CPU timers\n");
+}
+
+/*
+ * User-kernel interface for tracer
+ */
+
+/**
+ *	update_shared_buffer_control: - prepare for GET_BUFFER_CONTROL ioctl
+ *	@trace: the trace instance
+ *	@cpu_id: the CPU associated with the ioctl
+ */
+static inline void update_shared_buffer_control(struct ltt_trace_struct *trace, u8 cpu_id)
+{
+	struct rchan_info channel_info;
+	int i;
+	int channel_handle = trace_channel_handle(trace->trace_handle, cpu_id);
+	
+	if (relay_info(channel_handle, &channel_info) == -1) {
+		shared_buf_ctl.buffer_control_valid = 0;
+		return;
+	}
+
+	shared_buf_ctl.cpu_id =				cpu_id;
+	shared_buf_ctl.buffer_switches_pending =	trace->buffer_switches_pending & ~(1UL << cpu_id);
+	shared_buf_ctl.buffer_control_valid =		1;
+	shared_buf_ctl.buf_size =			channel_info.buf_size;
+	shared_buf_ctl.n_buffers =			channel_info.n_bufs;
+	shared_buf_ctl.cur_idx =			channel_info.cur_idx;
+	shared_buf_ctl.buffers_produced =		channel_info.bufs_produced;
+	shared_buf_ctl.buffers_consumed =		channel_info.bufs_consumed;
+
+	if (channel_info.flags & RELAY_SCHEME_LOCKLESS) {
+		for (i = 0; i < channel_info.n_bufs; i++) {
+			shared_buf_ctl.buffer_complete[i] =
+				channel_info.buffer_complete[i];
+		}
+	}
+}
+
+/**
+ *	ltt_flight_pause: - pause the flight recorder
+ *
+ *	Allows for external control of flight recorder e.g. for crashdump
+ */
+void ltt_flight_pause(void)
+{
+	struct ltt_trace_struct *trace;
+
+	trace = &current_traces[FLIGHT_HANDLE];
+	if (!trace->active)
+		return;
+	
+	trace->paused = 1;
+}
+
+/**
+ *	ltt_flight_unpause: - unpause the flight recorder
+ *
+ *	Allows for external control of flight recorder e.g. for crashdump
+ */
+void ltt_flight_unpause(void)
+{
+	struct ltt_trace_struct *trace;
+
+	trace = &current_traces[FLIGHT_HANDLE];
+	if (!trace->active)
+		return;
+	
+	trace->paused = 0;
+}
+
+/**
+ *      ltt_ioctl: - Tracing kernel-user control interface
+ *      @rchan_id: rchan id ioctl occurred on
+ *      @tracer_command: command given by the caller
+ *      @command_arg: argument to the command
+ *
+ *      Returns:
+ *      >0, In case the caller requested the number of events lost.
+ *      0, Everything went OK
+ *      -ENOSYS, no such command
+ *      -EINVAL, tracer not properly configured
+ *      -EBUSY, tracer can't be reconfigured while in operation
+ *      -ENOMEM, no more memory
+ *      -EFAULT, unable to access user space memory
+ *      -EACCES, invalid tracer handle
+ */
+static int ltt_ioctl(int rchan_id,
+		     unsigned int tracer_command,
+		     unsigned long arg)
+{
+	int retval;
+	int new_user_event_id;
+	unsigned long int flags;
+	u8 cpu_id;
+	u8 i;
+	u32 buffers_consumed;
+	ltt_custom user_event;
+	ltt_change_mask trace_mask;
+	ltt_new_event new_user_event;
+	struct ltt_buffers_committed buffers_committed;
+	struct ltt_trace_struct *trace = NULL;
+	struct ltt_tracer_status tracer_status;
+	unsigned int tracer_handle;
+	unsigned long command_arg;
+
+	if (copy_from_user(&tracer_handle, (void *)arg, sizeof(unsigned int)))
+		return -EFAULT;
+
+	if (copy_from_user(&command_arg, (void*)(arg + sizeof(tracer_handle)), sizeof(unsigned long)))
+		return -EFAULT;
+
+	if (tracer_command == LTT_TRACER_ALLOC_HANDLE)
+		return ltt_alloc_trace_handle(tracer_handle);
+
+	if (!ltt_valid_trace_handle(tracer_handle))
+		return -EACCES;
+
+	if (tracer_handle < NR_TRACES)
+		trace = &current_traces[tracer_handle];
+	else if (tracer_handle >= NR_TRACES) {
+		if (current_traces[TRACE_HANDLE].active)
+			trace = &current_traces[TRACE_HANDLE];
+		if (trace == NULL && tracer_command != LTT_TRACER_GET_STATUS)
+			return -EACCES;
+	}
+
+	if ((tracer_handle < NR_TRACES)
+	    && (trace->tracer_started == 1)
+	    && (tracer_command != LTT_TRACER_STOP)
+	    && (tracer_command != LTT_TRACER_PAUSE)
+	    && (tracer_command != LTT_TRACER_UNPAUSE)
+	    && (tracer_command != LTT_TRACER_DATA_COMITTED)
+	    && (tracer_command != LTT_TRACER_GET_ARCH_INFO)
+	    && (tracer_command != LTT_TRACER_GET_BUFFER_CONTROL)
+	    && (tracer_command != LTT_TRACER_GET_START_INFO))
+		return -EBUSY;
+
+	if ((tracer_handle >= NR_TRACES)
+	    && (tracer_command != LTT_TRACER_CREATE_USER_EVENT)
+	    && (tracer_command != LTT_TRACER_DESTROY_USER_EVENT)
+	    && (tracer_command != LTT_TRACER_TRACE_USER_EVENT)
+	    && (tracer_command != LTT_TRACER_FREE_HANDLE)
+	    && (tracer_command != LTT_TRACER_GET_STATUS)
+	    && (tracer_command != LTT_TRACER_SET_EVENT_MASK)
+	    && (tracer_command != LTT_TRACER_GET_EVENT_MASK))
+		return -ENOSYS;
+
+	switch (tracer_command) {
+	case LTT_TRACER_START:
+		if (trace->using_tsc && (need_heartbeat() == 1))
+			init_heartbeat_timer();
+		if (active_traces() == 1)
+			init_percpu_timers();
+
+		if (((use_syscall_eip_bounds == 1)
+		     && (syscall_eip_depth_set == 1))
+		    || ((use_syscall_eip_bounds == 1)
+			&& ((lower_eip_bound_set != 1)
+			    || (upper_eip_bound_set != 1)))
+		    || ((trace->tracing_pid == 1)
+			&& (trace->tracing_pgrp == 1)))
+			return -EINVAL;
+
+		if (ltt_set_trace_config(syscall_eip_depth_set,
+					 use_syscall_eip_bounds,
+					 syscall_eip_depth,
+					 lower_eip_bound,
+					 upper_eip_bound) < 0)
+			return -EINVAL;
+
+		if (trace->flight_recorder)
+			ltt_set_flight_recorder_config(trace);
+		
+		ltt_set_bit(LTT_EV_BUFFER_START, &trace->traced_events);
+		ltt_set_bit(LTT_EV_BUFFER_START, &trace->log_event_details_mask);
+		ltt_set_bit(LTT_EV_START, &trace->traced_events);
+		ltt_set_bit(LTT_EV_START, &trace->log_event_details_mask);
+		ltt_set_bit(LTT_EV_CHANGE_MASK, &trace->traced_events);
+		ltt_set_bit(LTT_EV_CHANGE_MASK, &trace->log_event_details_mask);
+
+		syscall_entry_trace_active = ltt_syscall_active(LTT_EV_SYSCALL_ENTRY);
+		syscall_exit_trace_active  = ltt_syscall_active(LTT_EV_SYSCALL_EXIT);
+
+		trace->tracer_stopping = 0;
+		trace->tracer_started = 1;
+
+		ltt_reregister_custom_events();
+		break;
+
+	case LTT_TRACER_STOP:
+		if (trace->flight_recorder) {
+			for (i = 0; i < num_cpus; i++)
+				tmp_rchan_handles[i] = trace_channel_handle(tracer_handle, i);
+			ltt_free_all_handles(NULL);
+		} else {
+			trace->tracer_stopping = 1;
+			trace->tracer_started = 0;
+		}
+
+		syscall_entry_trace_active = ltt_syscall_active(LTT_EV_SYSCALL_ENTRY);
+		syscall_exit_trace_active  = ltt_syscall_active(LTT_EV_SYSCALL_EXIT);
+
+		if (trace->flight_recorder) {
+			for (i = 0; i < num_cpus; i++) {
+				if (relay_close(tmp_rchan_handles[i]) != 0)
+					printk(KERN_ALERT "LTT: Couldn't close trace channel %d\n", trace_channel_handle(tracer_handle, i));
+			}
+			remove_readers(tracer_handle);
+		} else {
+			for (i = 0; i < num_cpus; i++)
+				set_waiting_for_cpu_async(tracer_handle, i, LTT_FINALIZE_TRACE);
+		}
+		break;
+
+	case LTT_TRACER_PAUSE:
+		trace->paused = 1;
+		break;
+
+	case LTT_TRACER_UNPAUSE:
+		trace->paused = 0;
+		break;
+
+	case LTT_TRACER_CONFIG_DEFAULT:
+		ltt_set_default_config(trace);
+		break;
+
+	case LTT_TRACER_CONFIG_MEMORY_BUFFERS:
+		if (trace->use_locking == 1) {
+			if (command_arg < LTT_TRACER_MIN_BUF_SIZE)
+				return -EINVAL;
+		}
+		else {
+			if ((command_arg < LTT_TRACER_LOCKLESS_MIN_BUF_SIZE) ||
+			    (command_arg > LTT_TRACER_LOCKLESS_MAX_BUF_SIZE))
+				return -EINVAL;
+		}
+
+		return ltt_set_buffer_size(trace, command_arg, relayfs_path);
+		break;
+
+	case LTT_TRACER_CONFIG_N_MEMORY_BUFFERS:
+		if (command_arg < LTT_TRACER_MIN_BUFFERS ||
+		    command_arg > LTT_TRACER_MAX_BUFFERS)
+			return -EINVAL;
+
+		return ltt_set_n_buffers(trace, command_arg);
+		break;
+
+	case LTT_TRACER_CONFIG_USE_LOCKING:
+		trace->use_locking = command_arg;
+
+		if ((trace->use_locking == 0) && (have_cmpxchg() == 0))
+			return -EINVAL;
+		break;
+
+	case LTT_TRACER_CONFIG_EVENTS:
+		if (copy_from_user(&trace->traced_events, (void *) command_arg, sizeof(trace->traced_events)))
+			return -EFAULT;
+		break;
+
+	case LTT_TRACER_CONFIG_TIMESTAMP:
+		trace->using_tsc = command_arg;
+
+		if ((trace->using_tsc == 1) && (have_tsc() == 0)) {
+			trace->using_tsc = 0;
+			return -EINVAL;
+		}
+
+		break;
+
+	case LTT_TRACER_CONFIG_DETAILS:
+		if (copy_from_user(&trace->log_event_details_mask, (void *) command_arg, sizeof(trace->log_event_details_mask)))
+			return -EFAULT;
+		break;
+
+	case LTT_TRACER_CONFIG_CPUID:
+		trace->log_cpuid = 0; /* disabled*/
+		break;
+
+	case LTT_TRACER_CONFIG_PID:
+		trace->tracing_pid = 1;
+		trace->traced_pid = command_arg;
+		break;
+
+	case LTT_TRACER_CONFIG_PGRP:
+		trace->tracing_pgrp = 1;
+		trace->traced_pgrp = command_arg;
+		break;
+
+	case LTT_TRACER_CONFIG_GID:
+		trace->tracing_gid = 1;
+		trace->traced_gid = command_arg;
+		break;
+
+	case LTT_TRACER_CONFIG_UID:
+		trace->tracing_uid = 1;
+		trace->traced_uid = command_arg;
+		break;
+
+	case LTT_TRACER_CONFIG_SYSCALL_EIP_DEPTH:
+		syscall_eip_depth_set = 1;
+		syscall_eip_depth = command_arg;
+		break;
+
+	case LTT_TRACER_CONFIG_SYSCALL_EIP_LOWER:
+		use_syscall_eip_bounds = 1;
+		lower_eip_bound = (void *) command_arg;
+		lower_eip_bound_set = 1;
+		break;
+
+	case LTT_TRACER_CONFIG_SYSCALL_EIP_UPPER:
+		use_syscall_eip_bounds = 1;
+		upper_eip_bound = (void *) command_arg;
+		upper_eip_bound_set = 1;
+		break;
+
+	case LTT_TRACER_DATA_COMITTED:
+		if (copy_from_user(&buffers_committed, (void *)command_arg,
+				   sizeof(buffers_committed)))
+			return -EFAULT;
+
+		cpu_id = buffers_committed.cpu_id;
+		buffers_consumed = buffers_committed.buffers_consumed;
+		clear_bit(cpu_id, &trace->buffer_switches_pending);
+
+		local_irq_save(flags);
+		relay_buffers_consumed(trace_channel_reader(tracer_handle, cpu_id),
+				       buffers_consumed);
+		local_irq_restore(flags);
+
+		break;
+
+	case LTT_TRACER_GET_EVENTS_LOST:
+		return events_lost(tracer_handle, command_arg);
+		break;
+
+	case LTT_TRACER_CREATE_USER_EVENT:
+		if (copy_from_user(&new_user_event, (void *) command_arg, sizeof(new_user_event)))
+			return -EFAULT;
+
+		new_user_event_id = ltt_create_owned_event(new_user_event.type,
+							     new_user_event.desc,
+							     new_user_event.format_type,
+							     new_user_event.form,
+							     current->pid);
+		if (new_user_event_id >= 0) {
+			new_user_event.id = new_user_event_id;
+			if (copy_to_user((void *) command_arg, &new_user_event, sizeof(new_user_event))) {
+				ltt_destroy_event(new_user_event_id);
+				return -EFAULT;
+			}
+		}
+		else
+			return new_user_event_id;
+		break;
+
+	case LTT_TRACER_DESTROY_USER_EVENT:
+		ltt_destroy_event((int) command_arg);
+		break;
+
+	case LTT_TRACER_TRACE_USER_EVENT:
+		if (copy_from_user(&user_event, (void *) command_arg, sizeof(user_event)))
+			return -EFAULT;
+
+		if ((user_event_data == NULL)
+		    && (user_event_data = vmalloc(LTT_CUSTOM_EV_MAX_SIZE)) < 0)
+			return -ENOMEM;
+
+		if (copy_from_user(user_event_data, user_event.data, user_event.data_size))
+			return -EFAULT;
+
+		retval = ltt_log_raw_event(user_event.id,
+					   user_event.data_size,
+					   user_event_data);
+
+		if (retval < 0)
+			return retval;
+		break;
+
+	case LTT_TRACER_SET_EVENT_MASK:
+		if (copy_from_user(&(trace_mask.mask), (void *) command_arg, sizeof(trace_mask.mask)))
+			return -EFAULT;
+
+		retval = _ltt_log_event(trace,
+					LTT_EV_CHANGE_MASK,
+					&trace_mask,
+					smp_processor_id());
+
+		memcpy(&trace->traced_events, &(trace_mask.mask), sizeof(trace_mask.mask));
+
+		syscall_entry_trace_active = ltt_syscall_active(LTT_EV_SYSCALL_ENTRY);
+		syscall_exit_trace_active  = ltt_syscall_active(LTT_EV_SYSCALL_EXIT);
+
+		ltt_set_bit(LTT_EV_BUFFER_START, &trace->traced_events);
+		ltt_set_bit(LTT_EV_START, &trace->traced_events);
+		ltt_set_bit(LTT_EV_CHANGE_MASK, &trace->traced_events);
+
+		return retval;
+		break;
+
+	case LTT_TRACER_GET_EVENT_MASK:
+		if (copy_to_user((void *) command_arg, &trace->traced_events, sizeof(trace->traced_events)))
+			return -EFAULT;
+		break;
+
+	case LTT_TRACER_GET_ARCH_INFO:
+		ltt_arch_info.n_cpus = num_cpus;
+		ltt_arch_info.page_shift = PAGE_SHIFT;
+
+		if (copy_to_user((void *) command_arg,
+				&ltt_arch_info,
+				sizeof(ltt_arch_info)))
+			return -EFAULT;
+		break;
+
+	case LTT_TRACER_GET_START_INFO:
+		if (trace->trace_start_data) {
+			if (copy_to_user((void *)command_arg,
+					 trace->trace_start_data,
+					 sizeof(ltt_trace_start)))
+				return -EFAULT;
+		} else
+			return -EINVAL;
+		break;
+
+	case LTT_TRACER_GET_STATUS:
+		if (ltt_get_status(&tracer_status))
+			return -EINVAL;
+		
+		if (copy_to_user((void *)command_arg,
+				 &tracer_status,
+				 sizeof(struct ltt_tracer_status)))
+			return -EFAULT;
+		break;
+
+	case LTT_TRACER_GET_BUFFER_CONTROL:
+		if (copy_from_user(&shared_buf_ctl, (void *)command_arg, sizeof(shared_buf_ctl)))
+			return -EFAULT;
+
+		if (shared_buf_ctl.cpu_id == -1) {
+			for (i = 0; i < num_cpus; i++) {
+				if (trace->buffer_switches_pending & (1UL << i)) {
+					update_shared_buffer_control(trace, i);
+					if (copy_to_user((void *)command_arg,
+							 &shared_buf_ctl,
+							 sizeof(struct ltt_buf_control_info)))
+						return -EFAULT;
+					return 0;
+				}
+			}
+		}
+		else {
+			update_shared_buffer_control(trace, (u8)shared_buf_ctl.cpu_id);
+			if (copy_to_user((void *)command_arg,
+					 &shared_buf_ctl,
+					 sizeof(struct ltt_buf_control_info)))
+				return -EFAULT;
+			return 0;
+		}
+
+		shared_buf_ctl.cpu_id = 0;
+		shared_buf_ctl.buffer_control_valid = 0;
+
+		if (copy_to_user((void *) command_arg,
+				&shared_buf_ctl,
+				sizeof(struct ltt_buf_control_info)))
+			return -EFAULT;
+		break;
+
+	case LTT_TRACER_FREE_HANDLE:
+		return ltt_free_trace_handle(tracer_handle);
+		break;
+
+	case LTT_TRACER_FREE_DAEMON_HANDLE:
+		return ltt_free_daemon_handle(trace);
+		break;
+
+	case LTT_TRACER_FREE_ALL_HANDLES:
+		ltt_free_all_handles(current);
+		break;
+
+	case LTT_TRACER_MAP_BUFFER:
+		return -EFAULT;
+		break;
+
+	default:
+		return -ENOSYS;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Trace Handles
+ */
+
+/**
+ *	ltt_valid_trace_handle: - Validate tracer handle.
+ *	@tracer_handle: handle to be validated
+ *
+ *	Returns:
+ *	1, if handle is valid
+ *	0, if handle is invalid
+ */
+int ltt_valid_trace_handle(unsigned int tracer_handle)
+{
+	int retval = 0;
+	struct ltt_trace_struct *trace;
+
+	if (tracer_handle < NR_TRACES) {
+		trace = &current_traces[tracer_handle];
+		if (!trace->active)
+			retval = 0;
+		else if (!trace->flight_recorder) {
+			if (trace->daemon_task_struct == current)
+				retval = 1;
+		} else
+			retval = 1;
+	} else {
+		read_lock(&trace_handle_table_lock);
+		if (trace_handle_table[tracer_handle - NR_TRACES].owner == current)
+			retval = 1;
+		else
+			retval = 0;
+		read_unlock(&trace_handle_table_lock);
+	}
+
+	return retval;
+}
+
+/**
+ *	ltt_alloc_trace_handle: - Allocate trace handle to caller.
+ *	@tracer_handle: handle requested by process
+ *
+ *	Returns:
+ *	Handle ID, everything went OK
+ *	-ENODEV, no more free handles.
+ *	-EBUSY, daemon handle already in use.
+ */
+int ltt_alloc_trace_handle(unsigned int tracer_handle)
+{
+	int i;
+	int retval;
+	struct ltt_trace_struct *trace = NULL;
+	
+	if (tracer_handle < NR_TRACES) {
+		trace = &current_traces[tracer_handle];
+		if (trace == NULL)
+			return -ENODEV;
+		if (trace->active)
+			return -EBUSY;
+	}
+
+	if (tracer_handle >= NR_TRACES) {
+		write_lock(&trace_handle_table_lock);
+		for (i = 0; i < LTT_MAX_HANDLES; i++)
+			if (trace_handle_table[i].owner == NULL) {
+				trace_handle_table[i].owner = current;
+				break;
+			}
+		write_unlock(&trace_handle_table_lock);
+		if (i == LTT_MAX_HANDLES)
+			retval = -ENODEV;
+		else
+			retval = (i + NR_TRACES);
+	}
+	else {
+		trace->active = trace;
+		trace->tracer_started = 0;
+		trace->tracer_stopping = 0;
+		if (tracer_handle == TRACE_HANDLE) {
+			trace->flight_recorder = 0;
+			trace->daemon_task_struct = current;
+		} else {
+			if ((trace->trace_start_data = (struct _ltt_trace_start *) kmalloc(sizeof(struct _ltt_trace_start), GFP_ATOMIC)) == NULL)
+				return -ENOMEM;
+		}
+
+		trace->proc_dir_entry = create_handle_proc_dir(tracer_handle);
+		ltt_set_default_config(trace);
+		retval = trace->trace_handle = tracer_handle;
+	}
+
+	return retval;
+}
+
+/**
+ *	ltt_free_trace_handle: - Free a single handle.
+ *	tracer_handle: handle to be freed.
+ *
+ *	Returns:
+ *	0, everything went OK
+ *	-ENODEV, no such handle.
+ *	-EACCES, handle doesn't belong to caller.
+ */
+int ltt_free_trace_handle(unsigned int tracer_handle)
+{
+	int retval;
+
+	if ((tracer_handle < NR_TRACES) || (tracer_handle >= LTT_MAX_HANDLES))
+		return -ENODEV;
+
+	write_lock(&trace_handle_table_lock);
+
+	if (trace_handle_table[tracer_handle - NR_TRACES].owner == current) {
+		trace_handle_table[tracer_handle - NR_TRACES].owner = NULL;
+		retval = 0;
+	}
+	else
+		retval = -EACCES;
+
+	write_unlock(&trace_handle_table_lock);
+
+	return retval;
+}
+
+/**
+ *	ltt_free_daemon_handle: - Free the daemon's handle.
+ *	@trace: the trace instance
+ *
+ *	Returns:
+ *	0, everything went OK
+ *	-EACCES, handle doesn't belong to caller.
+ *	-EBUSY, there are still event writes in progress so the buffer can't
+ *	be released.
+ */
+int ltt_free_daemon_handle(struct ltt_trace_struct *trace)
+{
+	int i;
+
+	if (!trace->flight_recorder) {
+		if (trace->daemon_task_struct != current)
+			return -EACCES;
+
+		for (i = 0; i < num_cpus; i++) {
+			if (events_lost(trace->trace_handle, i) > 0)
+				printk(KERN_ALERT "LTT: Lost %d events on cpu %d\n",
+				       events_lost(trace->trace_handle, i), i);
+		}
+		trace->daemon_task_struct = NULL;
+	}
+	
+	trace->active = NULL;
+
+	if (!active_traces())
+		del_percpu_timers();
+
+	if(trace->using_tsc && !need_heartbeat())
+		delete_heartbeat_timer();
+
+	for (i = 0; i < num_cpus; i++) {
+		if (trace_channel_handle(trace->trace_handle, i) != -1) {
+				trace_channel_handle(trace->trace_handle, i) = -1;
+				trace_channel_reader(trace->trace_handle, i) = NULL;
+		}
+	}
+
+	remove_handle_proc_dir(trace->proc_dir_entry, 0);
+
+	trace->use_locking = 1;
+	ltt_set_default_config(trace);
+	trace->tracer_started = 0;
+	trace->tracer_stopping = 0;
+	if (trace->trace_start_data)
+		kfree(trace->trace_start_data);
+
+	return 0;
+}
+
+/**
+ *	ltt_free_all_handles: - Free all handles taken.
+ *	@task_ptr: pointer to exiting task.
+ *
+ *	Free all handles taken against a given channel.  If task_ptr is NULL,
+ *	it means there is no daemon, i.e. free all handles taken agains the
+ *	flight recorder channel, otherwise task_ptr refers to a trace daemon.
+ */
+void ltt_free_all_handles(struct task_struct* task_ptr)
+{
+	int i;
+	struct ltt_trace_struct *trace;
+
+	if (task_ptr == NULL) {
+		if (current_traces[FLIGHT_HANDLE].active) {
+			ltt_free_daemon_handle(&current_traces[FLIGHT_HANDLE]);
+			return;
+		}
+	}
+	else {
+		trace = &current_traces[TRACE_HANDLE];
+		if (trace->active && trace->daemon_task_struct == task_ptr)
+			ltt_free_daemon_handle(trace);
+	}
+
+	write_lock(&trace_handle_table_lock);
+	for (i = 0; i < LTT_MAX_HANDLES; i++)
+		if (trace_handle_table[i].owner == current)
+			trace_handle_table[i].owner = NULL;
+	write_unlock(&trace_handle_table_lock);
+}
+
+/*
+ * Tracer Configuration
+ */
+
+/**
+ *	init_trace: - Initialize a trace/flight recorder instance
+ *	@trace_struct: trace/flight recorder struct
+ *
+ *	Initialize a trace instance to default values.
+ */
+static void init_trace(struct ltt_trace_struct *trace)
+{
+	trace->trace_handle = 0;
+	
+	trace->active = NULL;
+	trace->paused = 0;
+	trace->flight_recorder = 1;
+	trace->daemon_task_struct = NULL;
+	trace->trace_start_data = NULL;
+	
+	trace->tracer_started = 0;
+	trace->tracer_stopping = 0;
+	trace->proc_dir_entry = NULL;
+	trace->traced_events = 0;
+	trace->log_event_details_mask = 0;
+	trace->log_cpuid = 0;
+	trace->tracing_pid = 0;
+	trace->tracing_pgrp = 0;
+	trace->tracing_gid = 0;
+	trace->tracing_uid = 0;
+	trace->traced_pid = 0;
+	trace->traced_pgrp = 0;
+	trace->traced_gid = 0;
+	trace->traced_uid = 0;
+	trace->use_locking = 1;
+	trace->n_buffers = 0;
+	trace->buf_size = 0;
+	trace->using_tsc = 0;
+
+	trace->buffer_switches_pending = 0;
+	INIT_WORK(&trace->work, NULL, NULL);
+}
+
+/**
+ *	ltt_syscall_active: - If any active trace is logging syscalls, return 1
+ *	@syscall_type: either SYSCALL_ENTRY or SYSCALL_EXIT
+ *
+ *	Returns 1 if any channel is tracing syscalls, 0 otherwise
+ *
+ *	Needed for setting/clearing the global syscall...active variables
+ *	in order to reflect the needs of all traces.
+ */
+int ltt_syscall_active(int syscall_type)
+{
+	int i, retval = 0;
+	struct ltt_trace_struct *trace;
+	
+	for (i = 0; i < NR_TRACES; i++) {
+		trace = &current_traces[i];
+		if (!trace->active)
+			continue;
+		if(ltt_test_bit(syscall_type, &trace->traced_events))
+			retval = 1;
+	}
+
+	return retval;
+}
+
+/**
+ *	init_channel_data: - Init channel-associated data for new tracing run.
+ *	@trace: the trace to be initialized
+ */
+static void init_channel_data(struct ltt_trace_struct *trace)
+{
+	unsigned i;
+	
+	trace->buffer_switches_pending = 0;
+
+	for (i = 0; i < num_cpus; i++) {
+		trace_channel_handle(trace->trace_handle, i) = -1;
+		trace_channel_reader(trace->trace_handle, i) = NULL;
+		atomic_set(&waiting_for_cpu_async(trace->trace_handle, i), LTT_NOTHING_TO_DO);
+		events_lost(trace->trace_handle, i) = 0;
+	}
+}
+
+/**
+ *	ltt_set_n_buffers: - Sets the number of buffers.
+ *      @trace: the trace instance
+ *	@no_buffers: number of buffers
+ *
+ *	For lockless only, must be a power of 2.
+ *
+ *	Returns:
+ *
+ *	0, Size setting went OK
+ *	-EINVAL, not a power of 2
+ */
+int ltt_set_n_buffers(struct ltt_trace_struct *trace, int no_buffers)
+{
+	if (hweight32(no_buffers) != 1)
+		return -EINVAL;
+
+	trace->n_buffers = no_buffers;
+
+	return 0;
+}
+
+/**
+ *	ltt_set_buffer_size: - Sets size of and creates buffers.
+ *	@buf_size: Size of sub-buffers
+ *	@dirname: name of the relayfs directory to contain trace files
+ *
+ *	Note: dirname should be well-formed before it gets here e.g.
+ *	trailing slashes should be removed.
+ *
+ *	Returns:
+ *	0, Size setting went OK
+ *	-ENOMEM, unable to get a hold of memory for tracer
+ *	-EINVAL, tracer not properly configured
+ */
+int ltt_set_buffer_size(struct ltt_trace_struct *trace, int buffer_size, char * dirname)
+{
+	int i;
+	u32 flags;
+
+	if (trace->flight_recorder)
+		flags = RELAY_DELIVERY_BULK | RELAY_USAGE_SMP | RELAY_MODE_CONTINUOUS;
+	else
+		flags = RELAY_DELIVERY_BULK | RELAY_USAGE_SMP | RELAY_MODE_NO_OVERWRITE;
+
+	if ((dirname == NULL) || (strlen(dirname) == 0))
+		return  -EINVAL;
+
+	if (trace->using_tsc)
+		flags |= RELAY_TIMESTAMP_TSC;
+	else
+		flags |= RELAY_TIMESTAMP_GETTIMEOFDAY;
+	
+	if (trace->use_locking)
+		flags |= RELAY_SCHEME_LOCKING;
+	else
+		flags |= RELAY_SCHEME_LOCKLESS;
+	
+	num_cpus = num_online_cpus();
+
+	init_channel_data(trace);
+
+	trace->buf_size = buffer_size;
+
+	for (i = 0; i < num_cpus; i++) {
+		sprintf(relay_file_name, "%s/cpu%d", dirname, i);
+		trace_channel_handle(trace->trace_handle, i) = relay_open(relay_file_name,
+							  buffer_size,
+							  trace->n_buffers,
+							  flags,
+							  &ltt_callbacks,
+							  start_reserve,
+							  end_reserve,
+							  trace_start_reserve,
+							  0,
+							  0,
+							  0,
+							  NULL,
+							  0);
+		if (trace_channel_handle(trace->trace_handle, i) < 0)
+			return -ENOMEM;
+	}
+	
+	return 0;
+}
+
+/**
+ *	ltt_set_default_config: - Sets the tracer in its default config
+ *
+ *	Returns:
+ *	0, everything went OK
+ *	-ENOMEM, unable to get a hold of memory for tracer
+ */
+int ltt_set_default_config(struct ltt_trace_struct *trace)
+{
+	int i;
+	int retval = 0;
+
+	trace->traced_events = 0;
+
+	for (i = 0; i <= LTT_EV_MAX; i++) {
+		ltt_set_bit(i, &trace->traced_events);
+		ltt_set_bit(i, &trace->log_event_details_mask);
+	}
+
+	syscall_entry_trace_active = ltt_syscall_active(LTT_EV_SYSCALL_ENTRY);
+	syscall_exit_trace_active  = ltt_syscall_active(LTT_EV_SYSCALL_EXIT);
+
+	trace->log_cpuid = 0;
+	trace->tracing_pid = 0;
+	trace->tracing_pgrp = 0;
+	trace->tracing_gid = 0;
+	trace->tracing_uid = 0;
+	trace->using_tsc = 0;
+
+	syscall_eip_depth_set = 0;
+	use_syscall_eip_bounds = 0;
+	lower_eip_bound_set = 0;
+	upper_eip_bound_set = 0;
+
+	ltt_set_trace_config(syscall_eip_depth_set,
+			 use_syscall_eip_bounds,
+			 0,
+			 0,
+			 0);
+
+	return retval;
+}
+
+/**
+ *	ltt_set_trace_config: - Set the tracing configuration
+ *	@do_syscall_depth: Use depth to fetch eip
+ *	@do_syscall_bounds: Use bounds to fetch eip
+ *	@eip_depth: Detph to fetch eip
+ *	@eip_lower_bound: Lower bound eip address
+ *	@eip_upper_bound: Upper bound eip address
+ *
+ *	Returns:
+ *	0, all is OK
+ *	-ENOMEDIUM, there isn't a registered tracer
+ *	-ENXIO, wrong tracer
+ *	-EINVAL, invalid configuration
+ */
+int ltt_set_trace_config(int do_syscall_depth,
+		     int do_syscall_bounds,
+		     int eip_depth,
+		     void *eip_lower_bound,
+		     void *eip_upper_bound)
+{
+	if ((do_syscall_depth && do_syscall_bounds)
+	    || (eip_lower_bound > eip_upper_bound)
+	    || (eip_depth < 0))
+		return -EINVAL;
+
+	fetch_syscall_eip_use_depth = do_syscall_depth;
+	fetch_syscall_eip_use_bounds = do_syscall_bounds;
+
+	syscall_eip_depth = eip_depth;
+	syscall_lower_eip_bound = eip_lower_bound;
+	syscall_upper_eip_bound = eip_upper_bound;
+
+	return 0;
+}
+
+/**
+ *	ltt_get_trace_config: - Get the tracing configuration
+ *	@do_syscall_depth: Use depth to fetch eip
+ *	@do_syscall_bounds: Use bounds to fetch eip
+ *	@eip_depth: Detph to fetch eip
+ *	@eip_lower_bound: Lower bound eip address
+ *	@eip_upper_bound: Upper bound eip address
+ *
+ *	Returns:
+ *	0, all is OK
+ *	-ENOMEDIUM, there isn't a registered tracer
+ */
+int ltt_get_trace_config(int *do_syscall_depth,
+		     int *do_syscall_bounds,
+		     int *eip_depth,
+		     void **eip_lower_bound,
+		     void **eip_upper_bound)
+{
+	*do_syscall_depth = fetch_syscall_eip_use_depth;
+	*do_syscall_bounds = fetch_syscall_eip_use_bounds;
+	*eip_depth = syscall_eip_depth;
+	*eip_lower_bound = syscall_lower_eip_bound;
+	*eip_upper_bound = syscall_upper_eip_bound;
+
+	return 0;
+}
+
+/**
+ *	ltt_set_flight_recorder_config: - set flight recorder defaults
+ *	@trace: the trace struct
+ */
+void ltt_set_flight_recorder_config(struct ltt_trace_struct *trace)
+{
+	trace->traced_events = 0;
+	trace->log_event_details_mask = 0;
+	
+	ltt_set_bit(LTT_EV_BUFFER_START, &trace->traced_events);
+	ltt_set_bit(LTT_EV_BUFFER_START, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_START, &trace->traced_events);
+	ltt_set_bit(LTT_EV_START, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_CHANGE_MASK, &trace->traced_events);
+	ltt_set_bit(LTT_EV_CHANGE_MASK, &trace->log_event_details_mask);
+
+	ltt_set_bit(LTT_EV_SYSCALL_ENTRY, &trace->traced_events);
+	ltt_set_bit(LTT_EV_SYSCALL_ENTRY, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_SYSCALL_EXIT, &trace->traced_events);
+	ltt_set_bit(LTT_EV_SYSCALL_EXIT, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_TRAP_ENTRY, &trace->traced_events);
+	ltt_set_bit(LTT_EV_TRAP_ENTRY, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_TRAP_EXIT, &trace->traced_events);
+	ltt_set_bit(LTT_EV_TRAP_EXIT, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_IRQ_ENTRY, &trace->traced_events);
+	ltt_set_bit(LTT_EV_IRQ_ENTRY, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_IRQ_EXIT, &trace->traced_events);
+	ltt_set_bit(LTT_EV_IRQ_EXIT, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_SCHEDCHANGE, &trace->traced_events);
+	ltt_set_bit(LTT_EV_SCHEDCHANGE, &trace->log_event_details_mask);
+
+	ltt_set_bit(LTT_EV_KERNEL_TIMER, &trace->traced_events);
+	ltt_set_bit(LTT_EV_KERNEL_TIMER, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_SOFT_IRQ, &trace->traced_events);
+	ltt_set_bit(LTT_EV_SOFT_IRQ, &trace->log_event_details_mask);
+	ltt_set_bit(LTT_EV_PROCESS, &trace->traced_events);
+	ltt_set_bit(LTT_EV_PROCESS, &trace->log_event_details_mask);
+}
+
+/**
+ *	ltt_get_status: - fill in a status struct covering all traces
+ *	@tracer_status: the tracer_status struct
+ */
+int ltt_get_status(struct ltt_tracer_status *tracer_status)
+{
+	int i, j, rchan_handle, retval = 0;
+	struct ltt_trace_struct *trace;
+	struct ltt_trace_info *info;
+	struct rchan_info rchan_info;
+	
+	tracer_status->num_cpus = num_cpus;
+
+	for (i = 0; i < NR_TRACES; i++) {
+		trace = &current_traces[i];
+		info = &tracer_status->traces[i];
+		info->active = trace->active && trace->tracer_started ? 1 : 0;
+		if (!info->active)
+			continue;
+		info->trace_handle = trace->trace_handle;
+		info->paused = trace->paused;
+		info->flight_recorder = trace->flight_recorder;
+		info->use_locking = trace->use_locking;
+		info->using_tsc = trace->using_tsc;
+		info->n_buffers = trace->n_buffers;
+		info->buf_size = trace->buf_size;
+		info->traced_events = trace->traced_events;
+		info->log_event_details_mask = trace->log_event_details_mask;
+		for (j = 0; j < num_cpus; j++) {
+			rchan_handle = trace_channel_handle(trace->trace_handle, j);
+			retval = relay_info(rchan_handle, &rchan_info);
+			if (retval)
+				return retval;
+			info->buffers_produced[j] = rchan_info.bufs_produced;
+		}
+	}
+
+	return retval;
+}
+
+/*
+ * Custom Events
+ */
+
+/**
+ *	init_custom_events: - Initialize custom events
+ */
+static inline void init_custom_events(void)
+{
+	custom_events = &custom_events_head;
+	custom_events->next = custom_events;
+	custom_events->prev = custom_events;
+}
+
+/**
+ *	_ltt_create_event: - Create a new traceable event type
+ *	@event_type: string describing event type
+ *	@event_desc: string used for standard formatting
+ *	@format_type: type of formatting used to log event data
+ *	@format_data: data specific to format
+ *	@owner_pid: PID of event's owner (0 if none)
+ *
+ *	Returns:
+ *	New Event ID if all is OK
+ *	-ENOMEM, Unable to allocate new event
+ */
+int _ltt_create_event(char *event_type,
+		      char *event_desc,
+		      int format_type,
+		      char *format_data,
+		      pid_t owner_pid)
+{
+	ltt_new_event *new_event;
+	struct custom_event_desc *new_event_desc;
+
+	if ((new_event_desc = (struct custom_event_desc *) kmalloc(sizeof(struct custom_event_desc), GFP_ATOMIC)) == NULL)
+		 return -ENOMEM;
+
+	new_event = &(new_event_desc->event);
+	new_event->type[0] = '\0';
+	new_event->desc[0] = '\0';
+	new_event->form[0] = '\0';
+
+	if (event_type != NULL)
+		strncpy(new_event->type, event_type, LTT_CUSTOM_EV_TYPE_STR_LEN);
+	if (event_desc != NULL)
+		strncpy(new_event->desc, event_desc, LTT_CUSTOM_EV_DESC_STR_LEN);
+	if (format_data != NULL)
+		strncpy(new_event->form, format_data, LTT_CUSTOM_EV_FORM_STR_LEN);
+
+	new_event->type[LTT_CUSTOM_EV_TYPE_STR_LEN - 1] = '\0';
+	new_event->desc[LTT_CUSTOM_EV_DESC_STR_LEN - 1] = '\0';
+	new_event->form[LTT_CUSTOM_EV_FORM_STR_LEN - 1] = '\0';
+
+	new_event->format_type = format_type;
+	new_event->id = next_event_id;
+
+	next_event_id++;
+
+	new_event_desc->owner_pid = owner_pid;
+
+	write_lock(&custom_list_lock);
+
+	if (custom_events == NULL)
+		init_custom_events();
+
+	new_event_desc->next = custom_events;
+	new_event_desc->prev = custom_events->prev;
+	custom_events->prev->next = new_event_desc;
+	custom_events->prev = new_event_desc;
+	write_unlock(&custom_list_lock);
+
+	ltt_log_event(LTT_EV_NEW_EVENT, &(new_event_desc->event));
+
+	return new_event->id;
+}
+
+int ltt_create_event(char *event_type,
+		     char *event_desc,
+		     int format_type,
+		     char *format_data)
+{
+	return _ltt_create_event(event_type, event_desc, format_type, format_data, 0);
+}
+
+int ltt_create_owned_event(char *event_type,
+			   char *event_desc,
+			   int format_type,
+			   char *format_data,
+			   pid_t owner_pid)
+{
+	return _ltt_create_event(event_type, event_desc, format_type, format_data, owner_pid);
+}
+
+/**
+ *	ltt_destroy_event: - Destroy a created event type
+ *	@event_id, the Id returned by ltt_create_event()
+ */
+void ltt_destroy_event(int event_id)
+{
+	struct custom_event_desc *event_desc;
+
+	write_lock(&custom_list_lock);
+
+	if (custom_events == NULL)
+		init_custom_events();
+
+	for (event_desc = custom_events->next;
+	     event_desc != custom_events;
+	     event_desc = event_desc->next)
+		if (event_desc->event.id == event_id)
+			break;
+
+	if (event_desc != custom_events) {
+		event_desc->next->prev = event_desc->prev;
+		event_desc->prev->next = event_desc->next;
+		kfree(event_desc);
+	}
+
+	write_unlock(&custom_list_lock);
+}
+
+/**
+ *	ltt_destroy_owners_events: Destroy an owner's events
+ *	@owner_pid: the PID of the owner who's events are to be deleted.
+ */
+void ltt_destroy_owners_events(pid_t owner_pid)
+{
+	struct custom_event_desc *temp_event;
+	struct custom_event_desc *event_desc;
+
+	write_lock(&custom_list_lock);
+
+	if (custom_events == NULL)
+		init_custom_events();
+
+	event_desc = custom_events->next;
+
+	while (event_desc != custom_events) {
+		temp_event = event_desc->next;
+		if (event_desc->owner_pid == owner_pid) {
+			event_desc->next->prev = event_desc->prev;
+			event_desc->prev->next = event_desc->next;
+			kfree(event_desc);
+		}
+		event_desc = temp_event;
+	}
+
+	write_unlock(&custom_list_lock);
+}
+
+/**
+ *	ltt_reregister_custom_events: - Relogs event creations.
+ */
+void ltt_reregister_custom_events(void)
+{
+	struct custom_event_desc *event_desc;
+
+	read_lock(&custom_list_lock);
+
+	if (custom_events == NULL)
+		init_custom_events();
+
+	for (event_desc = custom_events->next;
+	     event_desc != custom_events;
+	     event_desc = event_desc->next)
+		ltt_log_event(LTT_EV_NEW_EVENT, &(event_desc->event));
+
+	read_unlock(&custom_list_lock);
+}
+
+/*
+ * Event logging primitives
+ */
+
+/**
+ *	_ltt_log_event: - Tracing function per se.
+ *	@trace: the trace instance
+ *	@event_id: ID of event as defined in linux/ltt.h
+ *	@event_struct: struct describing the event
+ *	@cpu_id: the CPU associated with the event
+ *
+ *	Returns:
+ *	0, if everything went OK (event got registered)
+ *	-ENODEV, no tracing daemon opened the driver.
+ *	-ENOMEM, no more memory to store events.
+ *	-EBUSY, tracer not started yet.
+ */
+int _ltt_log_event(struct ltt_trace_struct *trace,
+		   u8 event_id,
+		   void *event_struct,
+		   u8 cpu_id)
+{
+	int var_data_len = 0;
+	void *var_data_beg = NULL;
+	uint16_t data_size;
+	struct task_struct *incoming_process = NULL;
+	unsigned long flags;
+	char * reserved;
+	int bytes_written = 0;
+	int reserve_code, interrupting;
+	struct timeval time_stamp;
+	u32 time_delta;
+	int channel_handle;
+	struct rchan *rchan;
+	unsigned int tracer_handle;
+	
+	if (!trace)
+		return -ENOMEDIUM;
+
+	if (trace->paused)
+		return -EBUSY;
+
+	tracer_handle = trace->trace_handle;
+	
+	if (!trace->flight_recorder && (trace->daemon_task_struct == NULL))
+		return -ENODEV;
+
+	channel_handle = trace_channel_handle(tracer_handle, cpu_id);
+
+	if ((trace->tracer_started == 1) || (event_id == LTT_EV_START) || (event_id == LTT_EV_BUFFER_START))
+		goto trace_event;
+
+	return -EBUSY;
+
+trace_event:
+	if (!ltt_test_bit(event_id, &trace->traced_events))
+		return 0;
+
+	if ((event_id != LTT_EV_START) && (event_id != LTT_EV_BUFFER_START)) {
+		if (event_id == LTT_EV_SCHEDCHANGE)
+			incoming_process = (struct task_struct *) (((ltt_schedchange *) event_struct)->in);
+		if ((trace->tracing_pid == 1) && (current->pid != trace->traced_pid)) {
+			if (incoming_process == NULL)
+				return 0;
+			else if (incoming_process->pid != trace->traced_pid)
+				return 0;
+		}
+		if ((trace->tracing_pgrp == 1) && (process_group(current) != trace->traced_pgrp)) {
+			if (incoming_process == NULL)
+				return 0;
+			else if (process_group(incoming_process) != trace->traced_pgrp)
+				return 0;
+		}
+		if ((trace->tracing_gid == 1) && (current->egid != trace->traced_gid)) {
+			if (incoming_process == NULL)
+				return 0;
+			else if (incoming_process->egid != trace->traced_gid)
+				return 0;
+		}
+		if ((trace->tracing_uid == 1) && (current->euid != trace->traced_uid)) {
+			if (incoming_process == NULL)
+				return 0;
+			else if (incoming_process->euid != trace->traced_uid)
+				return 0;
+		}
+		if (event_id == LTT_EV_SCHEDCHANGE)
+			(((ltt_schedchange *) event_struct)->in) = incoming_process->pid;
+	}
+
+	data_size = sizeof(event_id) + sizeof(time_delta) + sizeof(data_size);
+
+	if (ltt_test_bit(event_id, &trace->log_event_details_mask)) {
+		data_size += event_struct_size[event_id];
+		switch (event_id) {
+		case LTT_EV_FILE_SYSTEM:
+			if ((((ltt_file_system *) event_struct)->event_sub_id == LTT_EV_FILE_SYSTEM_EXEC)
+			    || (((ltt_file_system *) event_struct)->event_sub_id == LTT_EV_FILE_SYSTEM_OPEN)) {
+				var_data_beg = ((ltt_file_system *) event_struct)->file_name;
+				var_data_len = ((ltt_file_system *) event_struct)->event_data2 + 1;
+				data_size += (uint16_t) var_data_len;
+			}
+			break;
+		case LTT_EV_CUSTOM:
+			var_data_beg = ((ltt_custom *) event_struct)->data;
+			var_data_len = ((ltt_custom *) event_struct)->data_size;
+			data_size += (uint16_t) var_data_len;
+			break;
+		}
+	}
+
+	if ((trace->log_cpuid == 1) && (event_id != LTT_EV_START) && (event_id != LTT_EV_BUFFER_START))
+		data_size += sizeof(cpu_id);
+
+	rchan = rchan_get(channel_handle);
+	if (rchan == NULL)
+		return -ENODEV;
+
+	relay_lock_channel(rchan, flags); /* nop for lockless */
+	reserved = relay_reserve(rchan, data_size, &time_stamp, &time_delta, &reserve_code, &interrupting);
+	
+	if (reserve_code & RELAY_WRITE_DISCARD) {
+		events_lost(trace->trace_handle, cpu_id)++;
+		bytes_written = 0;
+		goto check_buffer_switch;
+	}
+
+	if ((trace->log_cpuid == 1) && (event_id != LTT_EV_START)
+	    && (event_id != LTT_EV_BUFFER_START))
+		relay_write_direct(reserved,
+				   &cpu_id,
+				   sizeof(cpu_id));
+
+	relay_write_direct(reserved,
+			   &event_id,
+			   sizeof(event_id));
+
+	relay_write_direct(reserved,
+			   &time_delta,
+			   sizeof(time_delta));
+
+	if (ltt_test_bit(event_id, &trace->log_event_details_mask)) {
+		relay_write_direct(reserved,
+				   event_struct,
+				   event_struct_size[event_id]);
+		if (var_data_len)
+			relay_write_direct(reserved,
+					   var_data_beg,
+					   var_data_len);
+	}
+
+	relay_write_direct(reserved,
+			   &data_size,
+			   sizeof(data_size));
+
+	bytes_written = data_size;
+
+check_buffer_switch:
+	if ((event_id == LTT_EV_SCHEDCHANGE) && (tracer_handle == TRACE_HANDLE) && current_traces[FLIGHT_HANDLE].active)
+		(((ltt_schedchange *) event_struct)->in) = (u32)incoming_process;
+	
+	/* We need to commit even if we didn't write anything because
+	   that's how the deliver callback is invoked. */
+	relay_commit(rchan, reserved, bytes_written, reserve_code, interrupting);
+
+	relay_unlock_channel(rchan, flags);
+	rchan_put(rchan);
+
+	return 0;
+}
+
+/**
+ *	ltt_log_event: - Trace an event
+ *	@event_id, the event's ID (check out ltt.h)
+ *	@event_struct, the structure describing the event
+ *
+ *	Returns:
+ *	Trace fct return code if OK.
+ *	-ENOMEDIUM, there is no registered tracer
+ *	-ENOMEM, couldn't access ltt_info
+ */
+int ltt_log_event(u8 event_id,
+		void *event_struct)
+{
+	int i;
+	static int err[NR_TRACES];
+	struct ltt_trace_struct *trace;
+	u32 cpu = smp_processor_id();
+
+	for (i = 0; i < NR_TRACES; i++) {
+		trace = current_traces[i].active;
+		err[i] = _ltt_log_event(trace,
+				     event_id,
+				     event_struct,
+				     cpu);
+	}
+
+	return err[0] == -ENOMEDIUM ? err[1] : err[0];
+}
+
+/**
+ *	ltt_log_std_formatted_event: - Trace a formatted event
+ *	@event_id: the event Id provided upon creation
+ *	@...: printf-like data that will be used to fill the event string.
+ *
+ *	Returns:
+ *	Trace fct return code if OK.
+ *	-ENOMEDIUM, there is no registered tracer or event doesn't exist.
+ */
+int ltt_log_std_formatted_event(int event_id,...)
+{
+	int string_size;
+	char final_string[LTT_CUSTOM_EV_FINAL_STR_LEN];
+	va_list vararg_list;
+        ltt_custom custom_event;
+	struct custom_event_desc *event_desc;
+
+	read_lock(&custom_list_lock);
+
+	if (custom_events == NULL)
+		init_custom_events();
+
+	for (event_desc = custom_events->next;
+	     event_desc != custom_events;
+	     event_desc = event_desc->next)
+		if (event_desc->event.id == event_id)
+			break;
+
+	if (event_desc == custom_events) {
+		read_unlock(&custom_list_lock);
+		return -ENOMEDIUM;
+	}
+
+	custom_event.id = event_id;
+
+	va_start(vararg_list, event_id);
+	string_size = vsprintf(final_string, event_desc->event.desc, vararg_list);
+	read_unlock(&custom_list_lock);
+	va_end(vararg_list);
+
+	custom_event.data_size = (u32) (string_size + 1);
+	custom_event.data = final_string;
+
+	return ltt_log_event(LTT_EV_CUSTOM, &custom_event);
+}
+
+/**
+ *	ltt_log_raw_event: - Trace a raw event
+ *	@event_id, the event Id provided upon creation
+ *	@event_size, the size of the data provided
+ *	@event_data, data buffer describing event
+ *
+ *	Returns:
+ *	Trace fct return code if OK.
+ *	-ENOMEDIUM, there is no registered tracer or event doesn't exist.
+ */
+int ltt_log_raw_event(int event_id, int event_size, void *event_data)
+{
+	ltt_custom custom_event;
+	struct custom_event_desc *event_desc;
+
+	read_lock(&custom_list_lock);
+
+	if (custom_events == NULL)
+		init_custom_events();
+
+	for (event_desc = custom_events->next;
+	     event_desc != custom_events;
+	     event_desc = event_desc->next)
+		if (event_desc->event.id == event_id)
+			break;
+
+	read_unlock(&custom_list_lock);
+
+	if (event_desc == custom_events)
+		return -ENOMEDIUM;
+
+	custom_event.id = event_id;
+
+	if (event_size <= LTT_CUSTOM_EV_MAX_SIZE)
+		custom_event.data_size = (u32) event_size;
+	else
+		custom_event.data_size = (u32) LTT_CUSTOM_EV_MAX_SIZE;
+
+	custom_event.data = event_data;
+
+	return ltt_log_event(LTT_EV_CUSTOM, &custom_event);
+}
+
+/*
+ * Relayfs callback implementations.
+ */
+
+/**
+ *	_ltt_channel_cpuid: - Get CPU id given channel handle, for given trace.
+ *	@tracer_handle: trace handle.
+ *	@channel_handle: relay channel handle.
+ *
+ *	Returns:
+ *
+ *	CPU id
+ *	-1, channel_handle, thus CPU id, not found
+ */
+static int _ltt_channel_cpuid(int tracer_handle, int channel_handle)
+{
+	int i;
+	
+	for (i = 0; i < num_cpus; i++)
+		if (trace_channel_handle(tracer_handle, i) == channel_handle)
+			return i;
+	
+	return -1;
+}
+
+/**
+ *	ltt_channel_cpuid: - Get CPU id given channel handle.
+ *	@channel_handle: relay channel handle.
+ *
+ *	Returns:
+ *
+ *	CPU id
+ *	-1, channel_handle, thus CPU id, not found
+ */
+static int ltt_channel_cpuid(int channel_handle)
+{
+	int i, cpuid;
+	
+	for (i = 0; i < NR_TRACES; i++) {
+		cpuid = _ltt_channel_cpuid(i, channel_handle);
+		if (cpuid != -1)
+			return cpuid;
+	}
+	
+	return -1;
+}
+
+/**
+ *	ltt_channel_trace: - Get trace struct given channel handle.
+ *	@channel_handle: relay channel handle.
+ *
+ *	Returns:
+ *
+ *	trace struct *
+ *	NULL, channel_handle, thus trace_struct *, not found
+ */
+static struct ltt_trace_struct *ltt_channel_trace(int channel_handle)
+{
+	int i;
+	
+	for (i = 0; i < NR_TRACES; i++) {
+		if (_ltt_channel_cpuid(i, channel_handle) != -1)
+			return &current_traces[i];
+	}
+	
+	return NULL;
+}
+
+/**
+ *	ltt_channel_trace_handle: - Get trace handle given channel handle.
+ *	@channel_handle: relay channel handle.
+ *
+ *	Returns:
+ *
+ *	trace handle
+ *	-1, channel_handle, thus trace handle, not found
+ */
+static int ltt_channel_trace_handle(int channel_handle)
+{
+	unsigned int i;
+	
+	for (i = 0; i < NR_TRACES; i++)
+		if (_ltt_channel_cpuid(i, channel_handle) != -1)
+			return i;
+	
+	return -1;
+}
+
+/**
+ *	write_start_event: - Initialize a trace session for a given CPU.
+ *	@cpu_id: the CPU id to initialize a trace for
+ */
+static inline int write_start_event(struct ltt_trace_struct *trace,
+				    int channel,
+				    char * current_write_pos,
+				    u32 start_tsc,
+				    int using_tsc)
+{
+	struct rchan_info channel_info;
+	u32 time_delta;
+        ltt_trace_start start_event;
+	u8 event_id;
+	uint16_t data_size;
+
+	relay_info(channel, &channel_info);
+
+	start_event.magic_number =	LTT_TRACER_MAGIC_NUMBER;
+	start_event.arch_type =		LTT_ARCH_TYPE;
+	start_event.arch_variant =	LTT_ARCH_VARIANT;
+	start_event.system_type =	LTT_SYS_TYPE_VANILLA_LINUX;
+	start_event.major_version =	LTT_TRACER_VERSION_MAJOR;
+	start_event.minor_version =	LTT_TRACER_VERSION_MINOR;
+	start_event.buffer_size =	channel_info.buf_size;
+	start_event.event_mask = 	trace->traced_events;
+	start_event.details_mask =	trace->log_event_details_mask;
+	start_event.log_cpuid =		trace->log_cpuid;
+	start_event.use_tsc =		trace->using_tsc;
+	start_event.flight_recorder =	trace->flight_recorder;
+
+	event_id = LTT_EV_START;
+	relay_write_direct(current_write_pos,
+			   &event_id,
+			   sizeof(event_id));
+
+	time_delta = switch_time_delta(start_tsc, using_tsc);
+	relay_write_direct(current_write_pos,
+			   &time_delta,
+			   sizeof(time_delta));
+
+	relay_write_direct(current_write_pos,
+			   &start_event,
+			   sizeof(ltt_trace_start));
+
+	data_size = sizeof(event_id)
+		+ sizeof(time_delta)
+		+ sizeof(ltt_trace_start)
+		+ sizeof(data_size);
+
+	relay_write_direct(current_write_pos,
+			   &data_size,
+			   sizeof(data_size));
+
+	if (trace->trace_start_data)
+		memcpy(trace->trace_start_data, &start_event, sizeof(start_event));
+
+	return (int)data_size;
+}
+
+/**
+ *	buffer_start_callback: - Write start-buffer event to start of buffer.
+ *	@channel_handle: the channel id
+ *	@current_write_pos: position in sub-buffer client should write to
+ *	@buffer_id: the id of the new sub-buffer
+ *	@start_time: the timestamp associated with the start of sub-buffer
+ *	@start_tsc: the TSC associated with the timestamp, if using_tsc
+ *	@using_tsc: boolean, indicates whether start_tsc is valid
+ *
+ *	This is the relayfs buffer_start() callback implementation for
+ *	the tracer.  We write the start event directly to the address
+ *	contained in the current_write_pos param.  If this is the first
+ *	sub-buffer, we also write the start event.  Of course we reserved
+ *	the number of bytes we're writing when we opened the channel, which
+ *	is the number we return.
+ */
+static int buffer_start_callback(int channel_handle,
+				 char * current_write_pos,
+				 u32 buffer_id,
+				 struct timeval start_time,
+				 u32 start_tsc,
+				 int using_tsc)
+{
+	ltt_buffer_start start_buffer_event;
+	u8 event_id;
+	u32 time_delta;
+	uint16_t data_size;
+	struct ltt_trace_struct *trace = ltt_channel_trace(channel_handle);
+
+	if (!trace)
+		return 0;
+	
+	start_buffer_event.id = buffer_id;
+	start_buffer_event.time = start_time;
+	start_buffer_event.tsc = start_tsc;
+
+	event_id = LTT_EV_BUFFER_START;
+	relay_write_direct(current_write_pos,
+			   &event_id,
+			   sizeof(event_id));
+
+	time_delta = switch_time_delta(start_tsc, using_tsc);
+	relay_write_direct(current_write_pos,
+			   &time_delta,
+			   sizeof(time_delta));
+
+	relay_write_direct(current_write_pos,
+			   &start_buffer_event,
+			   sizeof(start_buffer_event));
+
+	data_size = sizeof(event_id)
+	    + sizeof(time_delta)
+	    + sizeof(start_buffer_event)
+	    + sizeof(data_size);
+
+	relay_write_direct(current_write_pos,
+			   &data_size,
+			   sizeof(data_size));
+
+	if (buffer_id == 0) /* first buffer */
+		data_size += write_start_event(trace, channel_handle, current_write_pos, start_tsc, using_tsc);
+	
+	return (int)data_size;
+}
+
+/**
+ *	buffer_end_callback - called at the end of a sub-buffer
+ *	@channel_handle: the channel id
+ *	@current_write_pos: position in sub-buffer of end of data
+ *	@end_of_buffer: the position of the end of the sub-buffer
+ *	@end_time: the timestamp associated with the end of the sub-buffer
+ *	@end_tsc: the TSC associated with the end_time, if using_tsc
+ *	@using_tsc: boolean, indicates whether end_tsc is valid
+ *
+ *	This is the relayfs buffer_end() callback implementation for
+ *	the tracer.  We write the end event directly to the address
+ *	contained in the current_write_pos param.  We also calculate
+ *	the 'size_lost' or unused bytes at the end of the sub-buffer
+ *	and write that value to the very end of the sub-buffer for
+ *	post-processing.  Of course we reserved	the number of bytes
+ *	we're writing when we opened the channel, which is the number
+ *	we return.
+ */
+static int buffer_end_callback(int channel_handle,
+			       char * current_write_pos,
+			       char * end_of_buffer,
+			       struct timeval end_time,
+			       u32 end_tsc,
+			       int using_tsc)
+{
+ 	ltt_buffer_end end_buffer_event;
+	u8 event_id;
+	u32 time_delta;
+	char* init_write_pos = current_write_pos;
+	uint16_t data_size;
+	u32 size_lost;
+	u8 cpu_id;
+	struct ltt_trace_struct *trace;
+
+	end_buffer_event.time = end_time;
+	end_buffer_event.tsc = end_tsc;
+
+	cpu_id = (u8)ltt_channel_cpuid(channel_handle);
+	trace = ltt_channel_trace(channel_handle);
+	if (!trace)
+		return 0;
+
+	if (trace->log_cpuid == 1)
+		relay_write_direct(current_write_pos,
+				   &cpu_id,
+				   sizeof(cpu_id));
+
+	event_id = LTT_EV_BUFFER_END;
+	relay_write_direct(current_write_pos,
+			   &event_id,
+			   sizeof(event_id));
+
+	time_delta = switch_time_delta(end_tsc, using_tsc);
+	relay_write_direct(current_write_pos,
+			   &time_delta,
+			   sizeof(time_delta));
+
+	relay_write_direct(current_write_pos,
+			   &end_buffer_event,
+			   sizeof(end_buffer_event));
+
+	data_size = sizeof(event_id)
+		+ sizeof(time_delta)
+		+ sizeof(end_buffer_event)
+		+ sizeof(data_size);
+
+	relay_write_direct(current_write_pos,
+			   &data_size,
+			   sizeof(data_size));
+
+	/* size lost includes size of end buffer event */
+	size_lost = end_of_buffer - init_write_pos;
+	*((u32 *) (end_of_buffer - sizeof(size_lost))) = size_lost;
+
+	return (int)data_size;
+}
+
+/**
+ *	deliver_callback - called when data is ready for the tracer
+ *	@channel_handle: the channel id
+ *	@from: the start of the delivered data
+ *	@len: the length of the delivered data
+ *
+ *	This is the relayfs deliver() callback implementation for
+ *	the tracer.  We simply set the send_signal flag, which will
+ *	be checked when the current write is finished, at which
+ *	point the daemon will be signaled to read the buffer.
+ */
+void deliver_callback(int channel_handle,
+		      char * from,
+		      u32 len)
+{
+	struct ltt_trace_struct *trace;
+	int cpu_id;
+
+	trace = ltt_channel_trace(channel_handle);
+	if (!trace)
+		return;
+	
+	cpu_id = ltt_channel_cpuid(channel_handle);
+	if (cpu_id == -1)
+		return;
+
+	set_bit(cpu_id, &trace->buffer_switches_pending);
+}
+
+/**
+ *	fileop_notify - called when change to trace file status
+ *	@rchan_id: the rchan id
+ *	@filp: the file
+ *	@fileop: the file operation
+ *
+ *	This is the relayfs fileop_notify() callback implementation for
+ *	the tracer.  We use it to take care of trace file mapping and
+ *	unmapping tasks.
+ */
+static int fileop_notify (int rchan_id,
+			  struct file *filp,
+			  enum relay_fileop fileop)
+{
+	struct rchan_reader *map_reader;
+	struct rchan_reader *open_file_reader;
+	struct rchan *rchan;
+	u8 cpu_id;
+	int trace_handle;
+
+	trace_handle = ltt_channel_trace_handle(rchan_id);
+	if (trace_handle == -1)
+		return 0;
+	
+	if (fileop == RELAY_FILE_MAP) {
+		cpu_id = (u8)ltt_channel_cpuid(rchan_id);
+		open_file_reader = (struct rchan_reader *)filp->private_data;
+		rchan = open_file_reader->rchan;
+		if (atomic_read(&rchan->mapped))
+			return -EBUSY;
+		map_reader = add_map_reader(rchan_id);
+		trace_channel_reader(trace_handle, cpu_id) = map_reader;
+	}
+	else if (fileop == RELAY_FILE_UNMAP) {
+		cpu_id = (u8)ltt_channel_cpuid(rchan_id);
+		remove_map_reader(trace_channel_reader(trace_handle, cpu_id));
+		trace_channel_reader(trace_handle, cpu_id) = NULL;
+	}
+
+	return 0;
+}
+
+static struct rchan_callbacks ltt_callbacks = {
+	.buffer_start = buffer_start_callback,
+	.buffer_end = buffer_end_callback,
+	.deliver = deliver_callback,
+	.fileop_notify = fileop_notify,
+	.ioctl = ltt_ioctl,
+};
+
+/*
+ * Procfs kernel-user interface
+ */
+
+/**
+ *	proc_read_relayfs_path - procfs read callback for relayfs_path attr
+ */
+static int proc_read_relayfs_path(char *page, char **start, off_t off,
+				  int count, int *eof, void *data)
+{
+	return sprintf(page, "%s", relayfs_path);
+}
+
+/**
+ *	proc_write_relayfs_path - procfs write callback for relayfs_path attr
+ *
+ *	Sets the path to the trace files within relayfs for the current trace.
+ */
+static int proc_write_relayfs_path(struct file *filp, const char *buffer,
+				   unsigned long count, void *data)
+{
+	unsigned long len;
+
+	if (count > PATH_MAX)
+		len = PATH_MAX;
+	else
+		len = count;
+	
+	if (copy_from_user(relayfs_path, buffer, len))
+		return -EFAULT;
+
+	if (len != PATH_MAX)
+		relayfs_path[len] = '\0';
+
+	return len;
+}
+
+/**
+ *	populate_handle_proc_dir - populate proc dir with trace attributes
+ *	@trace_handle: the trace handle for this trace run
+ *	@handle_dir: the directory to populate
+ *
+ *	This function populates the handle dir with attribute files.
+ *
+ *	Returns 0 if successful, negative if not.
+ */
+static int populate_handle_proc_dir(unsigned int trace_handle,
+				    struct proc_dir_entry *handle_dir)
+{
+	struct proc_dir_entry * file_entry;
+	int err = 0;
+
+	file_entry = create_proc_entry("relayfs_path", 0666, handle_dir);
+
+	if (file_entry == NULL) {
+		err = -ENOMEM;
+		return err;
+	}
+
+	file_entry->read_proc = proc_read_relayfs_path;
+	file_entry->write_proc = proc_write_relayfs_path;
+	file_entry->data = (void *)trace_handle;
+	file_entry->owner = THIS_MODULE;
+
+	return err;
+}
+
+/**
+ *	create_handle_proc_dir - create proc dir for trace attributes
+ *	@trace_handle: the trace handle for this trace run
+ *
+ *	This function creates a proc dir to communicate trace attribute
+ *	values between the daemon and the tracer.  It also populates the
+ *	new dir with the attribute files.
+ *
+ *	Retruns the proc dir entry if successful, NULL otherwise.
+ */
+static struct proc_dir_entry *create_handle_proc_dir(unsigned int trace_handle)
+{
+	char handle_dir_name[22];
+	struct proc_dir_entry *handle_dir;
+
+	sprintf(handle_dir_name, "%u", trace_handle);
+
+	handle_dir = proc_mkdir(handle_dir_name, ltt_proc_root_entry);
+
+	if (handle_dir == NULL)
+		return NULL;
+	else
+		handle_dir->owner = THIS_MODULE;
+
+	if (populate_handle_proc_dir(trace_handle, handle_dir)) {
+		remove_proc_entry(handle_dir_name, ltt_proc_root_entry);
+		handle_dir = NULL;
+	}
+		
+	return handle_dir;
+}
+
+/**
+ *	depopulate_handle_proc_dir - remove proc dir entries for handle_dir
+ *	@handle_dir: the directory to depopulate
+ *
+ *	This function removes the attribute files from the handle dir.
+ */
+static void depopulate_handle_proc_dir(struct proc_dir_entry *handle_dir)
+{
+	remove_proc_entry("relayfs_path", handle_dir);
+}
+
+/**
+ *	remove_handle_proc_dir - remove proc dir for trace attributes
+ *	@handle_dir: the directory
+ *	@trace_handle: the trace handle for this trace run
+ *
+ *	This function removes a trace handle's proc dir.  It first
+ *	depopulates the dir of attribute files.
+ */
+static void remove_handle_proc_dir(struct proc_dir_entry *handle_dir,
+				   unsigned int trace_handle)
+{
+	char handle_dir_name[22];
+
+	depopulate_handle_proc_dir(handle_dir);
+	
+	sprintf(handle_dir_name, "%u", trace_handle);
+	remove_proc_entry(handle_dir_name, ltt_proc_root_entry);
+}
+
+
+/*
+ * Initialization and finalization
+ */
+
+static struct rchan_callbacks control_callbacks = {
+	.ioctl = ltt_ioctl,
+};
+
+/**
+ *	create_control_channel - creates channel /mnt/relay/ltt/control
+ *
+ *	Returns channel id on success, negative otherwise.
+ */
+static int create_control_channel(void)
+{
+	u32 bufsize, nbufs;
+	u32 channel_flags;
+	int control;
+
+	sprintf(relay_file_name, "%s/%s", LTT_RELAYFS_ROOT, LTT_CONTROL_FILE);
+
+	channel_flags = RELAY_DELIVERY_PACKET | RELAY_USAGE_GLOBAL;
+	channel_flags |= RELAY_SCHEME_ANY | RELAY_TIMESTAMP_ANY;
+
+	bufsize = 4096;
+	nbufs = 4;
+
+	control = relay_open(relay_file_name,
+			     bufsize,
+			     nbufs,
+			     channel_flags,
+			     &control_callbacks,
+			     0,
+			     0,
+			     0,
+			     0,
+			     0,
+			     0,
+			     NULL,
+			     0);
+
+	return control;
+}
+
+/**
+ *	proc_read_init_ltt - procfs read callback for init attr
+ */
+static int proc_read_init_ltt(char *page, char **start, off_t off,
+			      int count, int *eof, void *data)
+{
+	return sprintf(page, "%d", control_channel == -1 ? 0 : 1);
+}
+
+/**
+ *	proc_write_init_ltt - procfs write callback for init attr
+ */
+static int proc_write_init_ltt(struct file *filp, const char *buffer,
+			       unsigned long count, void *data)
+{
+	if (control_channel == -1) {
+		control_channel = create_control_channel();
+	
+		if (control_channel < 0)
+			printk("LTT control channel creation failed, errcode: %d\n", control_channel);
+		else
+			printk("LTT control channel created\n");
+	}
+
+	return 1;
+}
+
+/**
+ *	remove_control_channel - destroys channel /mnt/relay/ltt/control
+ *
+ *	Returns 0, negative otherwise.
+ */
+static int remove_control_channel(void)
+{
+	if (control_channel != -1)
+		return relay_close(control_channel);
+
+	return -ENODEV;
+}
+
+static int __init init_ltt(void)
+{
+	int i;
+	int err = 0;
+	struct proc_dir_entry *init_entry;
+
+	ltt_proc_root_entry = proc_mkdir("ltt", NULL);
+
+	if (ltt_proc_root_entry == NULL)
+		err = -ENOMEM;
+	else
+		ltt_proc_root_entry->owner = THIS_MODULE;
+
+	control_channel = -1;
+	
+	init_entry = create_proc_entry("init", 0666, ltt_proc_root_entry);
+	if (init_entry == NULL) {
+		err = -ENOMEM;
+		return err;
+	}
+
+	init_entry->read_proc = proc_read_init_ltt;
+	init_entry->write_proc = proc_write_init_ltt;
+	init_entry->owner = THIS_MODULE;
+
+	for (i = 0; i < NR_TRACES; i++)
+		init_trace(&current_traces[i]);
+		
+	return err;
+}
+
+static void __exit exit_ltt(void)
+{
+	remove_proc_entry("init", ltt_proc_root_entry);
+	remove_proc_entry("ltt", NULL);
+
+	remove_control_channel();
+}
+
+module_init(init_ltt)
+module_exit(exit_ltt)
+
+EXPORT_SYMBOL(ltt_set_trace_config);
+EXPORT_SYMBOL(ltt_get_trace_config);
+EXPORT_SYMBOL(ltt_create_event);
+EXPORT_SYMBOL(ltt_create_owned_event);
+EXPORT_SYMBOL(ltt_destroy_event);
+EXPORT_SYMBOL(ltt_destroy_owners_events);
+EXPORT_SYMBOL(ltt_log_std_formatted_event);
+EXPORT_SYMBOL(ltt_log_raw_event);
+EXPORT_SYMBOL(ltt_log_event);
+EXPORT_SYMBOL(syscall_entry_trace_active);
+EXPORT_SYMBOL(syscall_exit_trace_active);
+EXPORT_SYMBOL(ltt_flight_pause);
+EXPORT_SYMBOL(ltt_flight_unpause);
+
+MODULE_AUTHOR("Karim Yaghmour, Tom Zanussi, Bob Wisniewski")
+MODULE_DESCRIPTION("Linux Trace Toolkit kernel core")
+MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2004-12-15  4:01 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-12-15  4:05 [PATCH 1/4] LTT core implementation Karim Yaghmour

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox