aboutsummaryrefslogtreecommitdiff
path: root/driver/gator_trace_gpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/gator_trace_gpu.c')
-rw-r--r--driver/gator_trace_gpu.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/driver/gator_trace_gpu.c b/driver/gator_trace_gpu.c
new file mode 100644
index 0000000..bc63995
--- /dev/null
+++ b/driver/gator_trace_gpu.c
@@ -0,0 +1,250 @@
+/**
+ * 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.
+ */
+
+#include "gator.h"
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/math64.h>
+
+#ifdef MALI_SUPPORT
+#include "linux/mali_linux_trace.h"
+#endif
+#include "gator_trace_gpu.h"
+
+#define ACTIVITY_START 1
+#define ACTIVITY_STOP 2
+
+/* Note whether tracepoints have been registered */
+static int mali_trace_registered;
+static int gpu_trace_registered;
+
+#define GPU_OVERFLOW -1
+#define GPU_START 1
+#define GPU_STOP 2
+
+#define GPU_UNIT_VP 1
+#define GPU_UNIT_FP 2
+
+#define TRACESIZE (8*1024)
+
+static DEFINE_PER_CPU(uint64_t *[2], theGpuTraceBuf);
+static DEFINE_PER_CPU(int, theGpuTraceSel);
+static DEFINE_PER_CPU(int, theGpuTracePos);
+static DEFINE_PER_CPU(int, theGpuTraceErr);
+
+int gator_trace_gpu_read(long long **buffer);
+
+static void probe_gpu_write(int type, int unit, int core, struct task_struct* task)
+{
+ int tracePos;
+ unsigned long flags;
+ uint64_t *traceBuf, time;
+ int pid, tgid;
+ int cpu = smp_processor_id();
+
+ if (!per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)])
+ return;
+
+ if (task) {
+ tgid = (int)task->tgid;
+ pid = (int)task->pid;
+ } else {
+ tgid = pid = 0;
+ }
+
+ // disable interrupts to synchronize with gator_trace_gpu_read(); spinlocks not needed since percpu buffers are used
+ local_irq_save(flags);
+
+ time = gator_get_time();
+ tracePos = per_cpu(theGpuTracePos, cpu);
+ traceBuf = per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)];
+
+ if (tracePos < (TRACESIZE - 100)) {
+ // capture
+ traceBuf[tracePos++] = type;
+ traceBuf[tracePos++] = time;
+ traceBuf[tracePos++] = unit;
+ traceBuf[tracePos++] = core;
+ traceBuf[tracePos++] = tgid;
+ traceBuf[tracePos++] = pid;
+ } else if (!per_cpu(theGpuTraceErr, cpu)) {
+ per_cpu(theGpuTraceErr, cpu) = 1;
+ traceBuf[tracePos++] = GPU_OVERFLOW;
+ traceBuf[tracePos++] = time;
+ traceBuf[tracePos++] = 0;
+ traceBuf[tracePos++] = 0;
+ traceBuf[tracePos++] = 0;
+ traceBuf[tracePos++] = 0;
+ pr_debug("gator: gpu trace overflow\n");
+ }
+ per_cpu(theGpuTracePos, cpu) = tracePos;
+ local_irq_restore(flags);
+}
+
+#ifdef MALI_SUPPORT
+
+enum components {
+ COMPONENT_VP0 = 1,
+ COMPONENT_FP0 = 5,
+ COMPONENT_FP1,
+ COMPONENT_FP2,
+ COMPONENT_FP3,
+ COMPONENT_FP4,
+ COMPONENT_FP5,
+ COMPONENT_FP6,
+ COMPONENT_FP7,
+};
+
+GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4))
+{
+ unsigned int component, state;
+
+ // do as much work as possible before disabling interrupts
+ component = (event_id >> 16) & 0xF;
+ state = (event_id >> 24) & 0xF;
+
+ if ((component == COMPONENT_VP0) || (component >= COMPONENT_FP0 && component <= COMPONENT_FP7)) {
+ if (state == ACTIVITY_START || state == ACTIVITY_STOP) {
+ unsigned int type = (state == ACTIVITY_START) ? GPU_START : GPU_STOP;
+ unsigned int unit = (component < COMPONENT_FP0) ? GPU_UNIT_VP : GPU_UNIT_FP;
+ unsigned int core = (component < COMPONENT_FP0) ? component - COMPONENT_VP0 : component - COMPONENT_FP0;
+ struct task_struct* task = (state == ACTIVITY_START) ? (struct task_struct*)d2 : NULL;
+
+ probe_gpu_write(type, unit, core, task);
+ }
+ }
+}
+#endif
+
+GATOR_DEFINE_PROBE(gpu_activity_start, TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p))
+{
+ probe_gpu_write(GPU_START, gpu_unit, gpu_core, p);
+}
+
+GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core))
+{
+ probe_gpu_write(GPU_STOP, gpu_unit, gpu_core, NULL);
+}
+
+int gator_trace_gpu_start(void)
+{
+ int cpu;
+
+ /*
+ * Returns 0 for installation failed
+ * Absence of gpu trace points is not an error
+ */
+
+ gpu_trace_registered = mali_trace_registered = 0;
+
+#ifdef MALI_SUPPORT
+ if (!GATOR_REGISTER_TRACE(mali_timeline_event)) {
+ mali_trace_registered = 1;
+ }
+#endif
+
+ if (!mali_trace_registered) {
+ if (GATOR_REGISTER_TRACE(gpu_activity_start)) {
+ return 0;
+ }
+ if (GATOR_REGISTER_TRACE(gpu_activity_stop)) {
+ GATOR_UNREGISTER_TRACE(gpu_activity_start);
+ return 0;
+ }
+ gpu_trace_registered = 1;
+ }
+
+ if (!gpu_trace_registered && !mali_trace_registered) {
+ return 0;
+ }
+
+ for_each_present_cpu(cpu) {
+ per_cpu(theGpuTraceSel, cpu) = 0;
+ per_cpu(theGpuTracePos, cpu) = 0;
+ per_cpu(theGpuTraceErr, cpu) = 0;
+ per_cpu(theGpuTraceBuf, cpu)[0] = kmalloc(TRACESIZE * sizeof(uint64_t), GFP_KERNEL);
+ per_cpu(theGpuTraceBuf, cpu)[1] = kmalloc(TRACESIZE * sizeof(uint64_t), GFP_KERNEL);
+ if (!per_cpu(theGpuTraceBuf, cpu)[0] || !per_cpu(theGpuTraceBuf, cpu)[1]) {
+#ifdef MALI_SUPPORT
+ if (mali_trace_registered) {
+ GATOR_UNREGISTER_TRACE(mali_timeline_event);
+ }
+#endif
+ if (gpu_trace_registered) {
+ GATOR_UNREGISTER_TRACE(gpu_activity_stop);
+ GATOR_UNREGISTER_TRACE(gpu_activity_start);
+ }
+
+ gpu_trace_registered = mali_trace_registered = 0;
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int gator_trace_gpu_offline(long long **buffer)
+{
+ return gator_trace_gpu_read(buffer);
+}
+
+void gator_trace_gpu_stop(void)
+{
+ int cpu;
+
+ if (gpu_trace_registered || mali_trace_registered) {
+ for_each_present_cpu(cpu) {
+ kfree(per_cpu(theGpuTraceBuf, cpu)[0]);
+ kfree(per_cpu(theGpuTraceBuf, cpu)[1]);
+ per_cpu(theGpuTraceBuf, cpu)[0] = NULL;
+ per_cpu(theGpuTraceBuf, cpu)[1] = NULL;
+ }
+
+#ifdef MALI_SUPPORT
+ if (mali_trace_registered) {
+ GATOR_UNREGISTER_TRACE(mali_timeline_event);
+ }
+#endif
+ if (gpu_trace_registered) {
+ GATOR_UNREGISTER_TRACE(gpu_activity_stop);
+ GATOR_UNREGISTER_TRACE(gpu_activity_start);
+ }
+
+ gpu_trace_registered = mali_trace_registered = 0;
+ }
+}
+
+int gator_trace_gpu_read(long long **buffer)
+{
+ int cpu = smp_processor_id();
+ unsigned long flags;
+ uint64_t *traceBuf;
+ int tracePos;
+
+ if (!per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)])
+ return 0;
+
+ local_irq_save(flags);
+
+ traceBuf = per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)];
+ tracePos = per_cpu(theGpuTracePos, cpu);
+
+ per_cpu(theGpuTraceSel, cpu) = !per_cpu(theGpuTraceSel, cpu);
+ per_cpu(theGpuTracePos, cpu) = 0;
+ per_cpu(theGpuTraceErr, cpu) = 0;
+
+ local_irq_restore(flags);
+
+ if (buffer)
+ *buffer = traceBuf;
+
+ return tracePos;
+}