summaryrefslogtreecommitdiff
path: root/drivers/gator/gator_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gator/gator_main.c')
-rw-r--r--drivers/gator/gator_main.c1086
1 files changed, 0 insertions, 1086 deletions
diff --git a/drivers/gator/gator_main.c b/drivers/gator/gator_main.c
deleted file mode 100644
index 988045f187d..00000000000
--- a/drivers/gator/gator_main.c
+++ /dev/null
@@ -1,1086 +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 = 9;
-
-#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 to support PC sampling
-#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
-
-/******************************************************************************
- * DEFINES
- ******************************************************************************/
-#define BACKTRACE_BUFFER_SIZE (128*1024)
-#define COUNTER_BUFFER_SIZE (128*1024)
-#define ANNOTATE_BUFFER_SIZE (64*1024) // annotate counters have the core as part of the data and the core value in the frame header may be discarded
-#define SCHED_TRACE_BUFFER_SIZE (128*1024)
-#define GPU_TRACE_BUFFER_SIZE (64*1024)
-#define COUNTER2_BUFFER_SIZE (64*1024) // counters2 counters have the core as part of the data and the core value in the frame header may be discarded
-#define WFI_BUFFER_SIZE (32*1024) // wfi counters have the core as part of the data and the core value in the frame header may be discarded
-
-#define NO_COOKIE 0UL
-#define INVALID_COOKIE ~0UL
-
-#define FRAME_BACKTRACE 1
-#define FRAME_COUNTER 2
-#define FRAME_ANNOTATE 3
-#define FRAME_SCHED_TRACE 4
-#define FRAME_GPU_TRACE 5
-#define FRAME_COUNTER2 6
-#define FRAME_WFI 7
-
-#define MESSAGE_COOKIE 1
-#define MESSAGE_START_BACKTRACE 5
-#define MESSAGE_END_BACKTRACE 7
-#define MESSAGE_SUMMARY 9
-#define MESSAGE_PID_NAME 11
-
-#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 {BACKTRACE_BUF, COUNTER_BUF, SCHED_TRACE_BUF, GPU_TRACE_BUF, ANNOTATE_BUF, COUNTER2_BUF, WFI_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_response_type;
-static DEFINE_MUTEX(start_mutex);
-static DEFINE_MUTEX(gator_buffer_mutex);
-
-bool event_based_sampling;
-
-static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait);
-static LIST_HEAD(gator_events);
-
-/******************************************************************************
- * Prototypes
- ******************************************************************************/
-static void buffer_check(int cpu, int buftype);
-static int buffer_bytes_available(int cpu, int buftype);
-static bool buffer_check_space(int cpu, int buftype, int bytes);
-static int contiguous_space_available(int cpu, int bufytpe);
-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_bytes(int cpu, int buftype, char *x, int len);
-static void gator_buffer_write_string(int cpu, int buftype, char *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);
-
-/******************************************************************************
- * Application Includes
- ******************************************************************************/
-#include "gator_marshaling.c"
-#include "gator_hrtimer_perf.c"
-#include "gator_hrtimer_gator.c"
-#include "gator_cookies.c"
-#include "gator_trace_sched.c"
-#include "gator_trace_power.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 int buffer_bytes_available(int cpu, int buftype)
-{
- 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;
- }
-
- return remaining;
-}
-
-static int contiguous_space_available(int cpu, int buftype)
-{
- int remaining = buffer_bytes_available(cpu, buftype);
- int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype];
- if (remaining < contiguous)
- return remaining;
- else
- return contiguous;
-}
-
-static bool buffer_check_space(int cpu, int buftype, int bytes)
-{
- int remaining = buffer_bytes_available(cpu, buftype);
-
- if (remaining < bytes) {
- 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 == BACKTRACE_BUF)
- frame = FRAME_BACKTRACE;
- else if (buftype == COUNTER_BUF)
- frame = FRAME_COUNTER;
- else if (buftype == ANNOTATE_BUF)
- frame = FRAME_ANNOTATE;
- else if (buftype == SCHED_TRACE_BUF)
- frame = FRAME_SCHED_TRACE;
- else if (buftype == GPU_TRACE_BUF)
- frame = FRAME_GPU_TRACE;
- else if (buftype == COUNTER2_BUF)
- frame = FRAME_COUNTER2;
- else if (buftype == WFI_BUF)
- frame = FRAME_WFI;
- else
- frame = -1;
-
- if (per_cpu(gator_buffer, cpu)[buftype]) {
- marshal_frame(cpu, buftype, frame);
- }
-}
-
-static void gator_commit_buffer(int cpu, int buftype)
-{
- if (!per_cpu(gator_buffer, cpu)[buftype])
- return;
-
- 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;
- }
-
- marshal_backtrace(offset & ~1, 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;
-
- if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, inKernel))
- return;
-
- 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);
- }
-
- marshal_backtrace_footer();
-}
-
-/******************************************************************************
- * hrtimer interrupt processing
- ******************************************************************************/
-static void gator_timer_interrupt(void)
-{
- struct pt_regs * const regs = get_irq_regs();
- int cpu = smp_processor_id();
-
- // Output backtrace
- gator_add_sample(cpu, BACKTRACE_BUF, regs);
-
- // Collect counters
- collect_counters();
-}
-
-static int gator_running;
-
-// This function runs in interrupt context and on the appropriate core
-static void gator_timer_offline(void* unused)
-{
- struct gator_interface *gi;
- int i, len, cpu = smp_processor_id();
- int* buffer;
-
- gator_trace_sched_offline();
- gator_trace_power_offline();
-
- gator_hrtimer_offline(cpu);
-
- // Offline any events and output counters
- if (marshal_event_header()) {
- list_for_each_entry(gi, &gator_events, list) {
- if (gi->offline) {
- len = gi->offline(&buffer);
- marshal_event(len, buffer);
- }
- }
- }
-
- // Flush all buffers on this core
- for (i = 0; i < NUM_GATOR_BUFS; i++)
- gator_commit_buffer(cpu, i);
-}
-
-// This function runs in process 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 (gator_running) {
- on_each_cpu(gator_timer_offline, NULL, 1);
- for_each_online_cpu(cpu) {
- gator_timer_offline_dispatch(cpu);
- }
-
- gator_running = 0;
- gator_hrtimer_shutdown();
- }
-}
-
-// This function runs in interrupt context and on the appropriate core
-static void gator_timer_online(void* unused)
-{
- struct gator_interface *gi;
- int len, cpu = smp_processor_id();
- int* buffer;
-
- gator_trace_power_online();
-
- // online any events and output counters
- if (marshal_event_header()) {
- list_for_each_entry(gi, &gator_events, list) {
- if (gi->online) {
- len = gi->online(&buffer);
- marshal_event(len, buffer);
- }
- }
- }
-
- gator_hrtimer_online(cpu);
-}
-
-// 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 sample_rate)
-{
- int cpu;
-
- if (gator_running) {
- pr_notice("gator: already running\n");
- return 0;
- }
-
- gator_running = 1;
-
- // event based sampling trumps hr timer based sampling
- if (event_based_sampling)
- sample_rate = 0;
-
- if (gator_hrtimer_init(sample_rate, 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_hotcpu_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_hotcpu_notifier = {
- .notifier_call = gator_hotcpu_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_hotcpu_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_hotcpu_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_hotcpu_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_hotcpu_notifier);
-}
-
-/******************************************************************************
- * Main
- ******************************************************************************/
-static void gator_summary(void)
-{
- uint64_t timestamp, uptime = 0;
- struct timespec uptime_ts;
- void (*m2b)(struct timespec *ts);
-
- timestamp = gator_get_time();
-
- do_posix_clock_monotonic_gettime(&uptime_ts);
- m2b = symbol_get(monotonic_to_bootbased);
- if (m2b) {
- m2b(&uptime_ts);
- uptime = (long long)uptime_ts.tv_sec * 1000000000 + uptime_ts.tv_nsec;
- }
-
- marshal_summary(timestamp, uptime);
-}
-
-int gator_events_install(struct gator_interface *interface)
-{
- list_add_tail(&interface->list, &gator_events);
-
- return 0;
-}
-
-int gator_events_get_key(void)
-{
- // key of zero is reserved as a timestamp
- static int key = 1;
-
- return key++;
-}
-
-static int gator_init(void)
-{
- int i;
-
- // 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]();
-
- gator_trace_power_init();
-
- return 0;
-}
-
-static int gator_start(void)
-{
- unsigned long cpu, i;
- struct gator_interface *gi;
-
- // Initialize the buffer with the frame type and core
- for_each_present_cpu(cpu) {
- for (i = 0; i < NUM_GATOR_BUFS; i++) {
- gator_buffer_header(cpu, i);
- }
- }
-
- // Capture the start time
- gator_summary();
-
- // 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_power_start())
- goto power_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_power_stop();
-power_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_power_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();
-}
-
-/******************************************************************************
- * Filesystem
- ******************************************************************************/
-/* fopen("buffer") */
-static int gator_op_setup(void)
-{
- int err = 0;
- int cpu, i;
-
- mutex_lock(&start_mutex);
-
- gator_buffer_size[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE;
- gator_buffer_mask[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE - 1;
-
- gator_buffer_size[COUNTER_BUF] = COUNTER_BUFFER_SIZE;
- gator_buffer_mask[COUNTER_BUF] = COUNTER_BUFFER_SIZE - 1;
-
- gator_buffer_size[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE;
- gator_buffer_mask[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE - 1;
-
- gator_buffer_size[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE;
- gator_buffer_mask[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE - 1;
-
- gator_buffer_size[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE;
- gator_buffer_mask[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE - 1;
-
- gator_buffer_size[COUNTER2_BUF] = COUNTER2_BUFFER_SIZE;
- gator_buffer_mask[COUNTER2_BUF] = COUNTER2_BUFFER_SIZE - 1;
-
- gator_buffer_size[WFI_BUF] = WFI_BUFFER_SIZE;
- gator_buffer_mask[WFI_BUF] = WFI_BUFFER_SIZE - 1;
-
- // Initialize percpu per buffer variables
- for (i = 0; i < NUM_GATOR_BUFS; i++) {
- // Verify buffers are a power of 2
- if (gator_buffer_size[i] & (gator_buffer_size[i] - 1)) {
- err = -ENOEXEC;
- goto setup_error;
- }
-
- for_each_present_cpu(cpu) {
- 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;
-
- // Annotation is a special case that only uses a single buffer
- if (cpu > 0 && i == ANNOTATE_BUF) {
- per_cpu(gator_buffer, cpu)[i] = NULL;
- continue;
- }
-
- per_cpu(gator_buffer, cpu)[i] = vmalloc(gator_buffer_size[i]);
- if (!per_cpu(gator_buffer, cpu)[i]) {
- err = -ENOMEM;
- goto setup_error;
- }
- }
- }
-
-setup_error:
- mutex_unlock(&start_mutex);
- return err;
-}
-
-/* Actually start profiling (echo 1>/dev/gator/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/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);
-
- 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;
- }
- 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/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, length, length1, length2, read, byte, type_length;
- 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_started);
-
- if (signal_pending(current))
- return -EINTR;
-
- length2 = 0;
- retval = -EFAULT;
-
- mutex_lock(&gator_buffer_mutex);
-
- if (buftype == -1 || cpu == -1) {
- retval = 0;
- goto out;
- }
-
- 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;
- }
-
- // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload
- type_length = gator_response_type ? 1 : 0;
- length = length1 + length2 - type_length - sizeof(int);
- for (byte = 0; byte < sizeof(int); byte++) {
- per_cpu(gator_buffer, cpu)[buftype][(read + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF;
- }
-
- /* 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;
- }
- }
-
- 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 = BACKTRACE_BUFFER_SIZE;
- gator_response_type = 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, "response_type", &gator_response_type);
- 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);
-
- // Power interface
- gator_trace_power_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();
-}
-
-module_init(gator_module_init);
-module_exit(gator_module_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("ARM Ltd");
-MODULE_DESCRIPTION("Gator system profiler");