Messages in this thread Patch in this message | | | Date | Tue, 14 Dec 2004 23:05:13 -0500 | From | Karim Yaghmour <> | Subject | [PATCH 1/4] LTT core implementation |
| |
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 = ¤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"); - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |