From: Karim Yaghmour Still to be done: - Remove /proc entry for writing relayfs path, use normal ltt control channel instead (whether it be ioctl or syscall.) - Use system calls instead of ioctl for communication between kernel tracing infrastructure and userpace. I'm still not sure about this one since all relayfs clients must, in one way or another, be able to have kernel/user-space interaction. In this regard, the use of an ioctl is easy to generalize to all relayfs clients. Then again, it all depends on the fate of relayfs ... so I'll keep this one on my list for now. A special thanks to Tim Bird for putting in the bulk of the effort to get a 2.6.10 patch ready. The unsplit patch can be obtained here: http://www.opersys.com/ftp/pub/LTT/ExtraPatches/patch-ltt-linux-2.6.10-vanilla-050113-2.2.bz2 This set of patches has been compile-tested, but should still work with 0.9.6pre4 released last month. Depends on relayfs. signed-off-by: Karim Yaghmour Signed-off-by: Andrew Morton --- 25-akpm/kernel/ltt-core.c | 2505 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 2505 insertions(+) diff -puN /dev/null kernel/ltt-core.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/kernel/ltt-core.c 2005-01-13 23:01:09.928687760 -0800 @@ -0,0 +1,2505 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* 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 = ¤t_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 = ¤t_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 = ¤t_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 = ¤t_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 = ¤t_traces[tracer_handle]; + else if (tracer_handle >= NR_TRACES) { + if (current_traces[TRACE_HANDLE].active) + trace = ¤t_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, <t_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 = ¤t_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 = ¤t_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(¤t_traces[FLIGHT_HANDLE]); + return; + } + } else { + trace = ¤t_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 = ¤t_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, <t_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 = ¤t_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 ¤t_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(¤t_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"); _