diff options
Diffstat (limited to 'drivers/gator/driver/gator_main.c')
-rw-r--r-- | drivers/gator/driver/gator_main.c | 1109 |
1 files changed, 0 insertions, 1109 deletions
diff --git a/drivers/gator/driver/gator_main.c b/drivers/gator/driver/gator_main.c deleted file mode 100644 index 6341ef6f051..00000000000 --- a/drivers/gator/driver/gator_main.c +++ /dev/null @@ -1,1109 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2012. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ - -static unsigned long gator_protocol_version = 8; - -#include <linux/slab.h> -#include <linux/cpu.h> -#include <linux/sched.h> -#include <linux/irq.h> -#include <linux/vmalloc.h> -#include <linux/hardirq.h> -#include <linux/highmem.h> -#include <linux/pagemap.h> -#include <linux/suspend.h> -#include <linux/module.h> -#include <linux/perf_event.h> -#include <asm/stacktrace.h> -#include <asm/uaccess.h> - -#include "gator.h" -#include "gator_events.h" - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) -#error kernels prior to 2.6.32 are not supported -#endif - -#if !defined(CONFIG_GENERIC_TRACER) && !defined(CONFIG_TRACING) -#error gator requires the kernel to have CONFIG_GENERIC_TRACER or CONFIG_TRACING defined -#endif - -#ifndef CONFIG_PROFILING -#error gator requires the kernel to have CONFIG_PROFILING defined -#endif - -#ifndef CONFIG_HIGH_RES_TIMERS -#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined -#endif - -#if defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS) -#error gator requires the kernel to have CONFIG_LOCAL_TIMERS defined on SMP systems -#endif - -#if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT)) -#ifndef CONFIG_PERF_EVENTS -#warning gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters -#elif !defined CONFIG_HW_PERF_EVENTS -#warning gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters -#endif -#endif - -#if (!(GATOR_CPU_FREQ_SUPPORT)) -#warning gator requires kernel version 2.6.38 or greater and CONFIG_CPU_FREQ defined in order to enable the CPU Freq timeline chart -#endif - -/****************************************************************************** - * DEFINES - ******************************************************************************/ -#define TIMER_BUFFER_SIZE_DEFAULT (512*1024) -#define EVENT_BUFFER_SIZE_DEFAULT (128*1024) - -#define NO_COOKIE 0UL -#define INVALID_COOKIE ~0UL - -#define FRAME_HRTIMER 1 -#define FRAME_EVENT 2 -#define FRAME_ANNOTATE 3 - -#define MESSAGE_COOKIE 1 -#define MESSAGE_COUNTERS 3 -#define MESSAGE_START_BACKTRACE 5 -#define MESSAGE_END_BACKTRACE 7 -#define MESSAGE_SCHEDULER_TRACE 9 -#define MESSAGE_PID_NAME 11 -#define MESSAGE_GPU_TRACE 13 -#define MESSAGE_OVERFLOW 127 - -#define MAXSIZE_PACK32 5 -#define MAXSIZE_PACK64 9 - -#if defined(__arm__) -#define PC_REG regs->ARM_pc -#else -#define PC_REG regs->ip -#endif - -enum {TIMER_BUF, EVENT_BUF, NUM_GATOR_BUFS}; - -/****************************************************************************** - * Globals - ******************************************************************************/ -static unsigned long gator_cpu_cores; -static unsigned long userspace_buffer_size; -static unsigned long gator_backtrace_depth; - -static unsigned long gator_started; -static unsigned long gator_buffer_opened; -static unsigned long gator_timer_count; -static unsigned long gator_streaming; -static DEFINE_MUTEX(start_mutex); -static DEFINE_MUTEX(gator_buffer_mutex); - -bool event_based_sampling; - -static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); - -static void buffer_check(int cpu, int buftype); - -/****************************************************************************** - * Prototypes - ******************************************************************************/ -static bool buffer_check_space(int cpu, int buftype, int bytes); -static void gator_buffer_write_packed_int(int cpu, int buftype, unsigned int x); -static void gator_buffer_write_packed_int64(int cpu, int buftype, unsigned long long x); -static void gator_buffer_write_string(int cpu, int buftype, char *x); -static int gator_write_packed_int(char *buffer, unsigned int x); -static int gator_write_packed_int64(char *buffer, unsigned long long x); -static void gator_add_trace(int cpu, int buftype, unsigned int address); -static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs); -static uint64_t gator_get_time(void); - -static uint32_t gator_buffer_size[NUM_GATOR_BUFS]; -static uint32_t gator_buffer_mask[NUM_GATOR_BUFS]; -static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_read); -static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_write); -static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_commit); -static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], buffer_space_available); -static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer); -static DEFINE_PER_CPU(uint64_t, emit_overflow); - -/****************************************************************************** - * Application Includes - ******************************************************************************/ -#include "gator_hrtimer_perf.c" -#include "gator_hrtimer_gator.c" -#include "gator_cookies.c" -#include "gator_trace_sched.c" -#include "gator_trace_gpu.c" -#include "gator_backtrace.c" -#include "gator_annotate.c" -#include "gator_fs.c" -#include "gator_ebs.c" -#include "gator_pack.c" - -/****************************************************************************** - * Misc - ******************************************************************************/ -#if defined(__arm__) -u32 gator_cpuid(void) -{ - u32 val; - asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (val)); - return (val >> 4) & 0xfff; -} -#endif - -/****************************************************************************** - * Commit interface - ******************************************************************************/ -static bool buffer_commit_ready(int* cpu, int* buftype) -{ - int cpu_x, x; - for_each_present_cpu(cpu_x) { - for (x = 0; x < NUM_GATOR_BUFS; x++) - if (per_cpu(gator_buffer_commit, cpu_x)[x] != per_cpu(gator_buffer_read, cpu_x)[x]) { - *cpu = cpu_x; - *buftype = x; - return true; - } - } - return false; -} - -/****************************************************************************** - * Buffer management - ******************************************************************************/ -static bool buffer_check_space(int cpu, int buftype, int bytes) -{ - int remaining, filled; - - filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype]; - if (filled < 0) { - filled += gator_buffer_size[buftype]; - } - - remaining = gator_buffer_size[buftype] - filled; - - if (per_cpu(buffer_space_available, cpu)[buftype]) { - // Give some extra room; also allows space to insert the overflow error packet - remaining -= 200; - } else { - // Hysteresis, prevents multiple overflow messages - remaining -= 2000; - } - - if (remaining < bytes) { - if (per_cpu(buffer_space_available, cpu)[buftype] == true) { - // overflow packet to be emitted at a later time, as we may be in the middle of writing a message, e.g. counters - per_cpu(emit_overflow, cpu) = gator_get_time(); - pr_err("overflow: remaining = %d\n", gator_buffer_size[buftype] - filled); - } - per_cpu(buffer_space_available, cpu)[buftype] = false; - } else { - per_cpu(buffer_space_available, cpu)[buftype] = true; - } - - return per_cpu(buffer_space_available, cpu)[buftype]; -} - -static void gator_buffer_write_bytes(int cpu, int buftype, char *x, int len) -{ - int i; - u32 write = per_cpu(gator_buffer_write, cpu)[buftype]; - u32 mask = gator_buffer_mask[buftype]; - char* buffer = per_cpu(gator_buffer, cpu)[buftype]; - - for (i = 0; i < len; i++) { - buffer[write] = x[i]; - write = (write + 1) & mask; - } - - per_cpu(gator_buffer_write, cpu)[buftype] = write; -} - -static void gator_buffer_write_string(int cpu, int buftype, char *x) -{ - int len = strlen(x); - gator_buffer_write_packed_int(cpu, buftype, len); - gator_buffer_write_bytes(cpu, buftype, x, len); -} - -static void gator_buffer_header(int cpu, int buftype) -{ - int frame; - - if (buftype == TIMER_BUF) - frame = FRAME_HRTIMER; - else if (buftype == EVENT_BUF) - frame = FRAME_EVENT; - else - frame = -1; - - gator_buffer_write_packed_int(cpu, buftype, frame); - gator_buffer_write_packed_int(cpu, buftype, cpu); -} - -static void gator_commit_buffer(int cpu, int buftype) -{ - per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype]; - gator_buffer_header(cpu, buftype); - wake_up(&gator_buffer_wait); -} - -static void buffer_check(int cpu, int buftype) -{ - int filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_commit, cpu)[buftype]; - if (filled < 0) { - filled += gator_buffer_size[buftype]; - } - if (filled >= ((gator_buffer_size[buftype] * 3) / 4)) { - gator_commit_buffer(cpu, buftype); - } -} - -static void gator_add_trace(int cpu, int buftype, unsigned int address) -{ - off_t offset = 0; - unsigned long cookie = get_address_cookie(cpu, buftype, current, address & ~1, &offset); - - if (cookie == NO_COOKIE || cookie == INVALID_COOKIE) { - offset = address; - } - - gator_buffer_write_packed_int(cpu, buftype, offset & ~1); - gator_buffer_write_packed_int(cpu, buftype, cookie); -} - -static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs) -{ - int inKernel = regs ? !user_mode(regs) : 1; - unsigned long exec_cookie = inKernel ? NO_COOKIE : get_exec_cookie(cpu, buftype, current); - - if (!regs) - return; - - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_START_BACKTRACE); - gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); - gator_buffer_write_packed_int(cpu, buftype, exec_cookie); - gator_buffer_write_packed_int(cpu, buftype, (unsigned int)current->tgid); - gator_buffer_write_packed_int(cpu, buftype, (unsigned int)current->pid); - gator_buffer_write_packed_int(cpu, buftype, inKernel); - - if (inKernel) { - kernel_backtrace(cpu, buftype, regs); - } else { - // Cookie+PC - gator_add_trace(cpu, buftype, PC_REG); - - // Backtrace - if (gator_backtrace_depth) - arm_backtrace_eabi(cpu, buftype, regs, gator_backtrace_depth); - } - - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_END_BACKTRACE); -} - -/****************************************************************************** - * hrtimer interrupt processing - ******************************************************************************/ -static LIST_HEAD(gator_events); - -static void gator_timer_interrupt(void) -{ - struct pt_regs * const regs = get_irq_regs(); - int cpu = smp_processor_id(); - int *buffer, len, i, buftype = TIMER_BUF; - long long *buffer64; - struct gator_interface *gi; - - // Output scheduler trace - len = gator_trace_sched_read(&buffer64); - if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_SCHEDULER_TRACE); - gator_buffer_write_packed_int(cpu, buftype, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); - } - } - - // Output GPU trace - len = gator_trace_gpu_read(&buffer64); - if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_GPU_TRACE); - gator_buffer_write_packed_int(cpu, buftype, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); - } - } - - // Output counters - if (buffer_check_space(cpu, buftype, MAXSIZE_PACK32 * 2 + MAXSIZE_PACK64)) { - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); - gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); - list_for_each_entry(gi, &gator_events, list) { - if (gi->read) { - len = gi->read(&buffer); - if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, buftype, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int(cpu, buftype, buffer[i]); - } - } - } else if (gi->read64) { - len = gi->read64(&buffer64); - if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, buftype, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); - } - } - } - } - gator_buffer_write_packed_int(cpu, buftype, 0); - } - - // Output backtrace - if (!event_based_sampling && buffer_check_space(cpu, buftype, gator_backtrace_depth * 2 * MAXSIZE_PACK32)) - gator_add_sample(cpu, buftype, regs); - - // Overflow message - if (per_cpu(emit_overflow, cpu)) { - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_OVERFLOW); - gator_buffer_write_packed_int64(cpu, buftype, per_cpu(emit_overflow, cpu)); - per_cpu(emit_overflow, cpu) = 0; - } - - // Check and commit; generally, commit is set to occur once per second - buffer_check(cpu, buftype); -} - -DEFINE_PER_CPU(int, hrtimer_is_active); -static int hrtimer_running; - -// This function runs in interrupt context and on the appropriate core -static void gator_timer_offline(void* unused) -{ - int i, len, cpu = smp_processor_id(); - int* buffer; - long long* buffer64; - - if (per_cpu(hrtimer_is_active, cpu)) { - struct gator_interface *gi; - gator_hrtimer_offline(cpu); - per_cpu(hrtimer_is_active, cpu) = 0; - - // Output scheduler trace - len = gator_trace_sched_offline(&buffer64); - if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_SCHEDULER_TRACE); - gator_buffer_write_packed_int(cpu, TIMER_BUF, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, TIMER_BUF, buffer64[i]); - } - } - - // Output GPU trace - len = gator_trace_gpu_offline(&buffer64); - if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_GPU_TRACE); - gator_buffer_write_packed_int(cpu, TIMER_BUF, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, TIMER_BUF, buffer64[i]); - } - } - - // offline any events and output counters - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_COUNTERS); - gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time()); - list_for_each_entry(gi, &gator_events, list) { - if (gi->offline) { - len = gi->offline(&buffer); - if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, TIMER_BUF, len); - for (i = 0; i < len; i++) - gator_buffer_write_packed_int(cpu, TIMER_BUF, buffer[i]); - } - } - } - gator_buffer_write_packed_int(cpu, TIMER_BUF, 0); - - gator_commit_buffer(cpu, TIMER_BUF); - } - - if (event_based_sampling) { - gator_commit_buffer(cpu, EVENT_BUF); - } -} - -// This function runs in interrupt context and may be running on a core other than core 'cpu' -static void gator_timer_offline_dispatch(int cpu) -{ - struct gator_interface *gi; - - list_for_each_entry(gi, &gator_events, list) - if (gi->offline_dispatch) - gi->offline_dispatch(cpu); - - gator_event_sampling_offline_dispatch(cpu); -} - -static void gator_timer_stop(void) -{ - int cpu; - - if (hrtimer_running) { - on_each_cpu(gator_timer_offline, NULL, 1); - for_each_online_cpu(cpu) { - gator_timer_offline_dispatch(cpu); - } - - hrtimer_running = 0; - gator_hrtimer_shutdown(); - } -} - -// This function runs in interrupt context and on the appropriate core -static void gator_timer_online(void* unused) -{ - int i, len, cpu = smp_processor_id(); - int* buffer; - - if (!per_cpu(hrtimer_is_active, cpu)) { - struct gator_interface *gi; - - // online any events and output counters - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_COUNTERS); - gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time()); - list_for_each_entry(gi, &gator_events, list) { - if (gi->online) { - len = gi->online(&buffer); - if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, TIMER_BUF, len); - for (i = 0; i < len; i++) - gator_buffer_write_packed_int(cpu, TIMER_BUF, buffer[i]); - } - } - } - gator_buffer_write_packed_int(cpu, TIMER_BUF, 0); - - gator_event_sampling_online(); - - gator_hrtimer_online(cpu); - per_cpu(hrtimer_is_active, cpu) = 1; - } -} - -// This function runs in interrupt context and may be running on a core other than core 'cpu' -static void gator_timer_online_dispatch(int cpu) -{ - struct gator_interface *gi; - - list_for_each_entry(gi, &gator_events, list) - if (gi->online_dispatch) - gi->online_dispatch(cpu); - - gator_event_sampling_online_dispatch(cpu); -} - -int gator_timer_start(unsigned long setup) -{ - int cpu; - - if (!setup) { - pr_err("gator: cannot start due to a system tick value of zero\n"); - return -1; - } else if (hrtimer_running) { - pr_notice("gator: high res timer already running\n"); - return 0; - } - - hrtimer_running = 1; - - if (gator_hrtimer_init(setup, gator_timer_interrupt) == -1) - return -1; - - for_each_online_cpu(cpu) { - gator_timer_online_dispatch(cpu); - } - on_each_cpu(gator_timer_online, NULL, 1); - - return 0; -} - -static uint64_t gator_get_time(void) -{ - struct timespec ts; - uint64_t timestamp; - - getnstimeofday(&ts); - timestamp = timespec_to_ns(&ts); - - return timestamp; -} - -/****************************************************************************** - * cpu hotplug and pm notifiers - ******************************************************************************/ -static int __cpuinit gator_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) -{ - long cpu = (long)hcpu; - - switch (action) { - case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - smp_call_function_single(cpu, gator_timer_offline, NULL, 1); - gator_timer_offline_dispatch(cpu); - break; - case CPU_ONLINE: - case CPU_ONLINE_FROZEN: - gator_timer_online_dispatch(cpu); - smp_call_function_single(cpu, gator_timer_online, NULL, 1); - break; - } - - return NOTIFY_OK; -} - -static struct notifier_block __refdata gator_cpu_notifier = { - .notifier_call = gator_cpu_notify, -}; - -// n.b. calling "on_each_cpu" only runs on those that are online -// Registered linux events are not disabled, so their counters will continue to collect -static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) -{ - int cpu; - - switch (event) { - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - unregister_hotcpu_notifier(&gator_cpu_notifier); - unregister_scheduler_tracepoints(); - on_each_cpu(gator_timer_offline, NULL, 1); - for_each_online_cpu(cpu) { - gator_timer_offline_dispatch(cpu); - } - break; - case PM_POST_HIBERNATION: - case PM_POST_SUSPEND: - for_each_online_cpu(cpu) { - gator_timer_online_dispatch(cpu); - } - on_each_cpu(gator_timer_online, NULL, 1); - register_scheduler_tracepoints(); - register_hotcpu_notifier(&gator_cpu_notifier); - break; - } - - return NOTIFY_OK; -} - -static struct notifier_block gator_pm_notifier = { - .notifier_call = gator_pm_notify, -}; - -static int gator_notifier_start(void) -{ - int retval; - retval = register_hotcpu_notifier(&gator_cpu_notifier); - if (retval == 0) - retval = register_pm_notifier(&gator_pm_notifier); - return retval; -} - -static void gator_notifier_stop(void) -{ - unregister_pm_notifier(&gator_pm_notifier); - unregister_hotcpu_notifier(&gator_cpu_notifier); -} - -/****************************************************************************** - * Main - ******************************************************************************/ -int gator_events_install(struct gator_interface *interface) -{ - list_add_tail(&interface->list, &gator_events); - - return 0; -} - -int gator_events_get_key(void) -{ - static int key; - - return key++; -} - -static int gator_init(void) -{ - int i; - - if (gator_annotate_init()) - return -1; - - // events sources (gator_events.h, generated by gator_events.sh) - for (i = 0; i < ARRAY_SIZE(gator_events_list); i++) - if (gator_events_list[i]) - gator_events_list[i](); - - return 0; -} - -static int gator_start(void) -{ - struct gator_interface *gi; - - // start all events - list_for_each_entry(gi, &gator_events, list) { - if (gi->start && gi->start() != 0) { - struct list_head *ptr = gi->list.prev; - - while (ptr != &gator_events) { - gi = list_entry(ptr, struct gator_interface, list); - - if (gi->stop) - gi->stop(); - - ptr = ptr->prev; - } - goto events_failure; - } - } - - // cookies shall be initialized before trace_sched_start() and gator_timer_start() - if (cookies_initialize()) - goto cookies_failure; - if (gator_annotate_start()) - goto annotate_failure; - if (gator_trace_sched_start()) - goto sched_failure; - if (gator_trace_gpu_start()) - goto gpu_failure; - if (gator_event_sampling_start()) - goto event_sampling_failure; - if (gator_timer_start(gator_timer_count)) - goto timer_failure; - if (gator_notifier_start()) - goto notifier_failure; - - return 0; - -notifier_failure: - gator_timer_stop(); -timer_failure: - gator_event_sampling_stop(); -event_sampling_failure: - gator_trace_gpu_stop(); -gpu_failure: - gator_trace_sched_stop(); -sched_failure: - gator_annotate_stop(); -annotate_failure: - cookies_release(); -cookies_failure: - // stop all events - list_for_each_entry(gi, &gator_events, list) - if (gi->stop) - gi->stop(); -events_failure: - - return -1; -} - -static void gator_stop(void) -{ - struct gator_interface *gi; - - // stop all events - list_for_each_entry(gi, &gator_events, list) - if (gi->stop) - gi->stop(); - - gator_annotate_stop(); - gator_trace_sched_stop(); - gator_trace_gpu_stop(); - gator_event_sampling_stop(); - - // stop all interrupt callback reads before tearing down other interfaces - gator_notifier_stop(); // should be called before gator_timer_stop to avoid re-enabling the hrtimer after it has been offlined - gator_timer_stop(); -} - -static void gator_exit(void) -{ - gator_annotate_exit(); -} - -/****************************************************************************** - * Filesystem - ******************************************************************************/ -/* fopen("buffer") */ -static int gator_op_setup(void) -{ - int err = 0; - int cpu, i; - - mutex_lock(&start_mutex); - - gator_buffer_size[TIMER_BUF] = userspace_buffer_size; - gator_buffer_mask[TIMER_BUF] = userspace_buffer_size - 1; - - // must be a power of 2 - if (gator_buffer_size[TIMER_BUF] & (gator_buffer_size[TIMER_BUF] - 1)) { - err = -ENOEXEC; - goto setup_error; - } - - gator_buffer_size[EVENT_BUF] = EVENT_BUFFER_SIZE_DEFAULT; - gator_buffer_mask[EVENT_BUF] = gator_buffer_size[EVENT_BUF] - 1; - - // Initialize percpu per buffer variables - for (i = 0; i < NUM_GATOR_BUFS; i++) { - for_each_present_cpu(cpu) { - per_cpu(gator_buffer, cpu)[i] = vmalloc(gator_buffer_size[i]); - if (!per_cpu(gator_buffer, cpu)[i]) { - err = -ENOMEM; - goto setup_error; - } - - per_cpu(gator_buffer_read, cpu)[i] = 0; - per_cpu(gator_buffer_write, cpu)[i] = 0; - per_cpu(gator_buffer_commit, cpu)[i] = 0; - per_cpu(buffer_space_available, cpu)[i] = true; - per_cpu(emit_overflow, cpu) = 0; - gator_buffer_header(cpu, i); - } - } - -setup_error: - mutex_unlock(&start_mutex); - return err; -} - -/* Actually start profiling (echo 1>/dev/gator/driver/enable) */ -static int gator_op_start(void) -{ - int err = 0; - - mutex_lock(&start_mutex); - - if (gator_started || gator_start()) - err = -EINVAL; - else - gator_started = 1; - - mutex_unlock(&start_mutex); - - return err; -} - -/* echo 0>/dev/gator/driver/enable */ -static void gator_op_stop(void) -{ - mutex_lock(&start_mutex); - - if (gator_started) { - gator_stop(); - - mutex_lock(&gator_buffer_mutex); - - gator_started = 0; - cookies_release(); - wake_up(&gator_buffer_wait); - - mutex_unlock(&gator_buffer_mutex); - } - - mutex_unlock(&start_mutex); -} - -static void gator_shutdown(void) -{ - int cpu, i; - - mutex_lock(&start_mutex); - - gator_annotate_shutdown(); - - for_each_present_cpu(cpu) { - mutex_lock(&gator_buffer_mutex); - for (i = 0; i < NUM_GATOR_BUFS; i++) { - vfree(per_cpu(gator_buffer, cpu)[i]); - per_cpu(gator_buffer, cpu)[i] = NULL; - per_cpu(gator_buffer_read, cpu)[i] = 0; - per_cpu(gator_buffer_write, cpu)[i] = 0; - per_cpu(gator_buffer_commit, cpu)[i] = 0; - per_cpu(buffer_space_available, cpu)[i] = true; - per_cpu(emit_overflow, cpu) = 0; - } - mutex_unlock(&gator_buffer_mutex); - } - - mutex_unlock(&start_mutex); -} - -static int gator_set_backtrace(unsigned long val) -{ - int err = 0; - - mutex_lock(&start_mutex); - - if (gator_started) - err = -EBUSY; - else - gator_backtrace_depth = val; - - mutex_unlock(&start_mutex); - - return err; -} - -static ssize_t enable_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - return gatorfs_ulong_to_user(gator_started, buf, count, offset); -} - -static ssize_t enable_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) -{ - unsigned long val; - int retval; - - if (*offset) - return -EINVAL; - - retval = gatorfs_ulong_from_user(&val, buf, count); - if (retval) - return retval; - - if (val) - retval = gator_op_start(); - else - gator_op_stop(); - - if (retval) - return retval; - return count; -} - -static const struct file_operations enable_fops = { - .read = enable_read, - .write = enable_write, -}; - -static int userspace_buffer_open(struct inode *inode, struct file *file) -{ - int err = -EPERM; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (test_and_set_bit_lock(0, &gator_buffer_opened)) - return -EBUSY; - - if ((err = gator_op_setup())) - goto fail; - - /* NB: the actual start happens from userspace - * echo 1 >/dev/gator/driver/enable - */ - - return 0; - -fail: - __clear_bit_unlock(0, &gator_buffer_opened); - return err; -} - -static int userspace_buffer_release(struct inode *inode, struct file *file) -{ - gator_op_stop(); - gator_shutdown(); - __clear_bit_unlock(0, &gator_buffer_opened); - return 0; -} - -static ssize_t userspace_buffer_read(struct file *file, char __user *buf, - size_t count, loff_t *offset) -{ - int retval = -EINVAL; - int commit = 0, length1, length2, read; - char *buffer1; - char *buffer2 = NULL; - int cpu, buftype; - - /* do not handle partial reads */ - if (count != userspace_buffer_size || *offset) - return -EINVAL; - - // sleep until the condition is true or a signal is received - // the condition is checked each time gator_buffer_wait is woken up - buftype = cpu = -1; - wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || gator_annotate_ready() || !gator_started); - - if (signal_pending(current)) - return -EINTR; - - length2 = 0; - retval = -EFAULT; - - mutex_lock(&gator_buffer_mutex); - - if (buftype != -1 && cpu != -1) { - read = per_cpu(gator_buffer_read, cpu)[buftype]; - commit = per_cpu(gator_buffer_commit, cpu)[buftype]; - - /* May happen if the buffer is freed during pending reads. */ - if (!per_cpu(gator_buffer, cpu)[buftype]) { - retval = -EFAULT; - goto out; - } - - /* determine the size of two halves */ - length1 = commit - read; - buffer1 = &(per_cpu(gator_buffer, cpu)[buftype][read]); - buffer2 = &(per_cpu(gator_buffer, cpu)[buftype][0]); - if (length1 < 0) { - length1 = gator_buffer_size[buftype] - read; - length2 = commit; - } - } else if (gator_annotate_ready()) { - length1 = gator_annotate_read(&buffer1); - if (!length1) - goto out; - } else { - retval = 0; - goto out; - } - - /* start, middle or end */ - if (length1 > 0) { - if (copy_to_user(&buf[0], buffer1, length1)) { - goto out; - } - } - - /* possible wrap around */ - if (length2 > 0) { - if (copy_to_user(&buf[length1], buffer2, length2)) { - goto out; - } - } - - if (buftype != -1 && cpu != -1) - per_cpu(gator_buffer_read, cpu)[buftype] = commit; - - retval = length1 + length2; - - /* kick just in case we've lost an SMP event */ - wake_up(&gator_buffer_wait); - -out: - mutex_unlock(&gator_buffer_mutex); - return retval; -} - -const struct file_operations gator_event_buffer_fops = { - .open = userspace_buffer_open, - .release = userspace_buffer_release, - .read = userspace_buffer_read, -}; - -static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset) -{ - return gatorfs_ulong_to_user(gator_backtrace_depth, buf, count, - offset); -} - -static ssize_t depth_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) -{ - unsigned long val; - int retval; - - if (*offset) - return -EINVAL; - - retval = gatorfs_ulong_from_user(&val, buf, count); - if (retval) - return retval; - - retval = gator_set_backtrace(val); - - if (retval) - return retval; - return count; -} - -static const struct file_operations depth_fops = { - .read = depth_read, - .write = depth_write -}; - -void gator_op_create_files(struct super_block *sb, struct dentry *root) -{ - struct dentry *dir; - struct gator_interface *gi; - int cpu; - - /* reinitialize default values */ - gator_cpu_cores = 0; - for_each_present_cpu(cpu) { - gator_cpu_cores++; - } - userspace_buffer_size = TIMER_BUFFER_SIZE_DEFAULT; - gator_streaming = 1; - - gatorfs_create_file(sb, root, "enable", &enable_fops); - gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops); - gatorfs_create_file(sb, root, "backtrace_depth", &depth_fops); - gatorfs_create_ulong(sb, root, "cpu_cores", &gator_cpu_cores); - gatorfs_create_ulong(sb, root, "buffer_size", &userspace_buffer_size); - gatorfs_create_ulong(sb, root, "tick", &gator_timer_count); - gatorfs_create_ulong(sb, root, "streaming", &gator_streaming); - gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version); - - // Annotate interface - gator_annotate_create_files(sb, root); - - // Linux Events - dir = gatorfs_mkdir(sb, root, "events"); - list_for_each_entry(gi, &gator_events, list) - if (gi->create_files) - gi->create_files(sb, dir); -} - -/****************************************************************************** - * Module - ******************************************************************************/ -static int __init gator_module_init(void) -{ - if (gatorfs_register()) { - return -1; - } - - if (gator_init()) { - gatorfs_unregister(); - return -1; - } - - return 0; -} - -static void __exit gator_module_exit(void) -{ - tracepoint_synchronize_unregister(); - gatorfs_unregister(); - gator_exit(); -} - -module_init(gator_module_init); -module_exit(gator_module_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("ARM Ltd"); -MODULE_DESCRIPTION("Gator system profiler"); |