diff options
Diffstat (limited to 'driver')
-rw-r--r-- | driver/Makefile | 7 | ||||
-rw-r--r-- | driver/gator_annotate.c | 9 | ||||
-rw-r--r-- | driver/gator_cookies.c | 14 | ||||
-rw-r--r-- | driver/gator_ebs.c | 18 | ||||
-rw-r--r-- | driver/gator_events_mali_400.c (renamed from driver/gator_events_mali.c) | 335 | ||||
-rw-r--r-- | driver/gator_events_mali_400.h | 19 | ||||
-rw-r--r-- | driver/gator_events_mali_common.c | 79 | ||||
-rw-r--r-- | driver/gator_events_mali_common.h | 85 | ||||
-rw-r--r-- | driver/gator_events_mali_t6xx.c | 553 | ||||
-rw-r--r-- | driver/gator_events_mali_t6xx_hw.c | 571 | ||||
-rw-r--r-- | driver/gator_events_meminfo.c | 20 | ||||
-rw-r--r-- | driver/gator_events_net.c | 19 | ||||
-rw-r--r-- | driver/gator_events_perf_pmu.c | 20 | ||||
-rw-r--r-- | driver/gator_fs.c | 1 | ||||
-rw-r--r-- | driver/gator_main.c | 28 | ||||
-rw-r--r-- | driver/gator_trace_gpu.c | 152 | ||||
-rw-r--r-- | driver/mali_t6xx.mk | 24 |
17 files changed, 1720 insertions, 234 deletions
diff --git a/driver/Makefile b/driver/Makefile index 667637e..025dd9e 100644 --- a/driver/Makefile +++ b/driver/Makefile @@ -17,10 +17,13 @@ gator-y += gator_events_mmaped.o ifneq ($(GATOR_WITH_MALI_SUPPORT),) ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) -gator-y += gator_events_mali_t6xx.o +gator-y += gator_events_mali_t6xx.o \ + gator_events_mali_t6xx_hw.o +include $(M)/mali_t6xx.mk else -gator-y += gator_events_mali.o +gator-y += gator_events_mali_400.o endif +gator-y += gator_events_mali_common.o EXTRA_CFLAGS += -DMALI_SUPPORT=$(GATOR_WITH_MALI_SUPPORT) endif diff --git a/driver/gator_annotate.c b/driver/gator_annotate.c index b2288b3..b444789 100644 --- a/driver/gator_annotate.c +++ b/driver/gator_annotate.c @@ -43,9 +43,9 @@ static ssize_t annotate_write(struct file *file, char const __user *buf, size_t if (*offset) return -EINVAL; - if (!collect_annotations) { + if (!collect_annotations) + // Not collecting annotations, tell the caller everything was written return count_orig; - } cpu = 0; // Annotation only uses a single per-cpu buffer as the data must be in order to the engine @@ -64,6 +64,8 @@ static ssize_t annotate_write(struct file *file, char const __user *buf, size_t size = count < available ? count : available; if (size <= 0) { + // Buffer is full but don't return an error. Instead return 0 so the + // caller knows nothing was written and they can try again. size = 0; goto annotate_write_out; } @@ -115,8 +117,9 @@ static int annotate_release(struct inode *inode, struct file *file) // synchronize between cores spin_lock(&annotate_lock); - if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF] && buffer_check_space(cpu, ANNOTATE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { + if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF] && buffer_check_space(cpu, ANNOTATE_BUF, MAXSIZE_PACK64 + 3 * MAXSIZE_PACK32)) { uint32_t tid = current->pid; + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, smp_processor_id()); gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, tid); gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 0); // time gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, 0); // size diff --git a/driver/gator_cookies.c b/driver/gator_cookies.c index 7b50916..d7d8e84 100644 --- a/driver/gator_cookies.c +++ b/driver/gator_cookies.c @@ -25,6 +25,8 @@ static DEFINE_PER_CPU(unsigned int *, translate_buffer); static inline uint32_t get_cookie(int cpu, int buftype, struct task_struct *task, struct vm_area_struct *vma, struct module *mod, bool in_interrupt); static void wq_cookie_handler(struct work_struct *unused); DECLARE_WORK(cookie_work, wq_cookie_handler); +static struct timer_list app_process_wake_up_timer; +static void app_process_wake_up_handler(unsigned long unused_data); static uint32_t cookiemap_code(uint64_t value64) { uint32_t value = (uint32_t)((value64 >> 32) + value64); @@ -136,6 +138,12 @@ static void wq_cookie_handler(struct work_struct *unused) mutex_unlock(&start_mutex); } +static void app_process_wake_up_handler(unsigned long unused_data) +{ + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + schedule_work(&cookie_work); +} + // Retrieve full name from proc/pid/cmdline for java processes on Android static int translate_app_process(char** text, int cpu, struct task_struct * task, struct vm_area_struct *vma, bool in_interrupt) { @@ -162,7 +170,8 @@ static int translate_app_process(char** text, int cpu, struct task_struct * task translate_buffer_write_int(cpu, (unsigned int)task); translate_buffer_write_int(cpu, (unsigned int)vma); - schedule_work(&cookie_work); + + mod_timer(&app_process_wake_up_timer, jiffies + 1); goto out; } @@ -372,6 +381,8 @@ static int cookies_initialize(void) gator_crc32_table[i] = crc; } + setup_timer(&app_process_wake_up_timer, app_process_wake_up_handler, 0); + cookie_setup_error: return err; } @@ -396,6 +407,7 @@ static void cookies_release(void) per_cpu(translate_text, cpu) = NULL; } + del_timer_sync(&app_process_wake_up_timer); kfree(gator_crc32_table); gator_crc32_table = NULL; } diff --git a/driver/gator_ebs.c b/driver/gator_ebs.c index 1208d69..6abdfa4 100644 --- a/driver/gator_ebs.c +++ b/driver/gator_ebs.c @@ -23,10 +23,8 @@ extern unsigned long pmnc_event[]; extern unsigned long pmnc_count[]; extern unsigned long pmnc_key[]; -static DEFINE_PER_CPU(struct perf_event *, pevent); static DEFINE_PER_CPU(struct perf_event_attr *, pevent_attr); static DEFINE_PER_CPU(int, key); -static DEFINE_PER_CPU(unsigned int, prev_value); #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) static void ebs_overflow_handler(struct perf_event *event, int unused, struct perf_sample_data *data, struct pt_regs *regs) @@ -36,7 +34,7 @@ static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_da { int cpu = smp_processor_id(); - if (event != per_cpu(pevent, cpu)) + if (event != per_cpu(pevent_ebs, cpu)) return; // Output backtrace @@ -54,9 +52,9 @@ static void gator_event_sampling_online_dispatch(int cpu) return; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) - ev = per_cpu(pevent, cpu) = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler); + ev = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler); #else - ev = per_cpu(pevent, cpu) = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler, 0); + ev = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu), cpu, 0, ebs_overflow_handler, 0); #endif if (IS_ERR(ev)) { @@ -71,7 +69,7 @@ static void gator_event_sampling_online_dispatch(int cpu) } ev->pmu->read(ev); - per_cpu(prev_value, cpu) = local64_read(&ev->count); + per_cpu(pevent_ebs, cpu) = ev; } static void gator_event_sampling_offline_dispatch(int cpu) @@ -79,9 +77,9 @@ static void gator_event_sampling_offline_dispatch(int cpu) struct perf_event * pe = NULL; mutex_lock(&perf_mutex); - if (per_cpu(pevent, cpu)) { - pe = per_cpu(pevent, cpu); - per_cpu(pevent, cpu) = NULL; + if (per_cpu(pevent_ebs, cpu)) { + pe = per_cpu(pevent_ebs, cpu); + per_cpu(pevent_ebs, cpu) = NULL; } mutex_unlock(&perf_mutex); @@ -95,7 +93,7 @@ static int gator_event_sampling_start(void) int cnt, event = 0, count = 0, ebs_key = 0, cpu; for_each_present_cpu(cpu) { - per_cpu(pevent, cpu) = NULL; + per_cpu(pevent_ebs, cpu) = NULL; per_cpu(pevent_attr, cpu) = NULL; } diff --git a/driver/gator_events_mali.c b/driver/gator_events_mali_400.c index 21a6324..ef4cc58 100644 --- a/driver/gator_events_mali.c +++ b/driver/gator_events_mali_400.c @@ -14,6 +14,9 @@ #include "linux/mali_linux_trace.h" +#include "gator_events_mali_common.h" +#include "gator_events_mali_400.h" + /* * There are (currently) three different variants of the comms between gator and Mali: * 1 (deprecated): No software counter support @@ -27,11 +30,6 @@ #define GATOR_MALI_INTERFACE_STYLE (3) #endif -#define MALI_200 (0x0a07) -#define MALI_300 (0x0b06) //This is not actually true; Mali-300 is also 0x0b07 -#define MALI_400 (0x0b07) -#define MALI_T6xx (0x0056) - /* * List of possible actions allowing DDK to be controlled by Streamline. * The following numbers are used by DDK to control the frame buffer dumping. @@ -44,13 +42,14 @@ /* * Check that the MALI_SUPPORT define is set to one of the allowable device codes. */ -#if !defined(MALI_SUPPORT) -#error MALI_SUPPORT not defined! -#elif (MALI_SUPPORT != MALI_200) && (MALI_SUPPORT != MALI_300) && (MALI_SUPPORT != MALI_400) && (MALI_SUPPORT != MALI_T6xx) -#error MALI_SUPPORT set to an invalid device code +#if (MALI_SUPPORT != MALI_400) +#error MALI_SUPPORT set to an invalid device code: expecting MALI_400 #endif -static const char *mali_name; +/* + * The number of fragment processors. Update to suit your hardware implementation. + */ +#define NUM_FP_UNITS (4) enum counters { /* Timeline activity */ @@ -123,7 +122,9 @@ enum counters { COUNTER_GLES_STRIP_LINES_COUNT, COUNTER_GLES_LOOP_LINES_COUNT, - COUNTER_FILMSTRIP, + COUNTER_FILMSTRIP, + COUNTER_FREQUENCY, + COUNTER_VOLTAGE, NUMBER_OF_EVENTS }; @@ -138,7 +139,7 @@ enum counters { #define LAST_SW_COUNTER COUNTER_GLES_LOOP_LINES_COUNT #define FIRST_SPECIAL_COUNTER COUNTER_FILMSTRIP -#define LAST_SPECIAL_COUNTER COUNTER_FILMSTRIP +#define LAST_SPECIAL_COUNTER COUNTER_VOLTAGE /* gatorfs variables for counter enable state, * the event the counter should count and the @@ -168,13 +169,13 @@ static int trace_registered; */ static u32 get_difference(u32 start, u32 end) { - if (start - end >= 0) - { - return start - end; - } + if (start - end >= 0) + { + return start - end; + } - // Mali counters are unsigned 32 bit values that wrap. - return (4294967295u - end) + start; + // Mali counters are unsigned 32 bit values that wrap. + return (4294967295u - end) + start; } /** @@ -194,6 +195,7 @@ static inline int is_hw_counter(unsigned int event_id) return (event_id >= FIRST_HW_COUNTER && event_id <= LAST_HW_COUNTER); } +#if GATOR_MALI_INTERFACE_STYLE == 2 /** * Returns non-zero if the given counter ID is a software counter. */ @@ -201,7 +203,9 @@ static inline int is_sw_counter(unsigned int event_id) { return (event_id >= FIRST_SW_COUNTER && event_id <= LAST_SW_COUNTER); } +#endif +#if GATOR_MALI_INTERFACE_STYLE == 2 /* * The Mali DDK uses s64 types to contain software counter values, but gator * can only use a maximum of 32 bits. This function scales a software counter @@ -223,6 +227,7 @@ static u32 scale_sw_counter_value(unsigned int event_id, signed long long value) return scaled_value; } +#endif /* Probe for continuously sampled counter */ #if 0 //WE_DONT_CURRENTLY_USE_THIS_SO_SUPPRESS_WARNING @@ -263,33 +268,25 @@ GATOR_DEFINE_PROBE(mali_sw_counter, TP_PROTO(unsigned int event_id, signed long #if GATOR_MALI_INTERFACE_STYLE == 3 GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void * surface_id, unsigned int * counters)) { - u32 i; - - /* Copy over the values for those counters which are enabled. */ - for(i=FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) - { - if(counter_enabled[i]) - { - counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]); - } - } -} -#endif /* GATOR_MALI_INTERFACE_STYLE == 3 */ + u32 i; -//TODO need to work out how many fp units we have -u32 gator_mali_get_n_fp(void) { - return 4; -} - -//TODO need to work out what kind of Mali we are looking at -u32 gator_mali_get_id(void) { - return MALI_SUPPORT; + /* Copy over the values for those counters which are enabled. */ + for(i=FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) + { + if(counter_enabled[i]) + { + counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]); + } + } } +#endif /* GATOR_MALI_INTERFACE_STYLE == 3 */ -int gator_events_mali_create_files(struct super_block *sb, struct dentry *root) { +static int create_files(struct super_block *sb, struct dentry *root) { struct dentry *dir; int event; - int n_fp = gator_mali_get_n_fp(); + int n_fp = NUM_FP_UNITS; + + const char* mali_name = gator_mali_get_mali_name(); /* * Create the filesystem entries for vertex processor, fragement processor @@ -381,12 +378,27 @@ int gator_events_mali_create_files(struct super_block *sb, struct dentry *root) gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); } - /* Now set up the special counter entries */ + /* Now set up the special counter entries */ for (event = FIRST_SPECIAL_COUNTER; event <= LAST_SPECIAL_COUNTER; event++) { char buf[40]; - snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip", mali_name); + switch(event) { + case COUNTER_FILMSTRIP: + snprintf(buf, sizeof(buf), "ARM_%s_Filmstrip", mali_name); + break; + + case COUNTER_FREQUENCY: + snprintf(buf, sizeof(buf), "ARM_%s_Frequency", mali_name); + break; + + case COUNTER_VOLTAGE: + snprintf(buf, sizeof(buf), "ARM_%s_Voltage", mali_name); + break; + + default: + break; + } dir = gatorfs_mkdir(sb, root, buf); @@ -399,17 +411,14 @@ int gator_events_mali_create_files(struct super_block *sb, struct dentry *root) gatorfs_create_ro_ulong(sb, dir, "key", &counter_key[event]); } - return 0; } -//TODO -void _mali_profiling_set_event(unsigned int, unsigned int); -void _mali_osk_fb_control_set(unsigned int, unsigned int); -void _mali_profiling_control(unsigned int, unsigned int); - -void _mali_profiling_get_counters(unsigned int*, unsigned int*, unsigned int*, unsigned int*); -void (*_mali_profiling_get_counters_function_pointer)(unsigned int*, unsigned int*, unsigned int*, unsigned int*); +/* + * Local store for the get_counters entry point into the DDK. + * This is stored here since it is used very regularly. + */ +static mali_profiling_get_counters_type *mali_get_counters = NULL; /* * Examine list of software counters and determine if any one is enabled. @@ -417,17 +426,17 @@ void (*_mali_profiling_get_counters_function_pointer)(unsigned int*, unsigned in */ static int is_any_sw_counter_enabled(void) { - unsigned int i; + unsigned int i; - for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) - { - if (counter_enabled[i]) - { - return 1; /* At least one counter is enabled */ - } - } + for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) + { + if (counter_enabled[i]) + { + return 1; /* At least one counter is enabled */ + } + } - return 0; /* No s/w counters enabled */ + return 0; /* No s/w counters enabled */ } static void mali_counter_initialize(void) @@ -436,22 +445,22 @@ static void mali_counter_initialize(void) * then we can request the HW counters (of which there are only 2) * be configured to count the desired events */ - void (*set_hw_event)(unsigned int, unsigned int); - void (*set_fb_event)(unsigned int, unsigned int); - void (*mali_control)(unsigned int, unsigned int); + mali_profiling_set_event_type *mali_set_hw_event; + mali_osk_fb_control_set_type *mali_set_fb_event; + mali_profiling_control_type *mali_control; - set_hw_event = symbol_get(_mali_profiling_set_event); + mali_set_hw_event = symbol_get(_mali_profiling_set_event); - if (set_hw_event) { + if (mali_set_hw_event) { int i; - pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n",set_hw_event); + pr_debug("gator: mali online _mali_profiling_set_event symbol @ %p\n",mali_set_hw_event); for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { if (counter_enabled[i]) { - set_hw_event(i, counter_event[i]); + mali_set_hw_event(i, counter_event[i]); } else { - set_hw_event(i, 0xFFFFFFFF); + mali_set_hw_event(i, 0xFFFFFFFF); } } @@ -460,64 +469,64 @@ static void mali_counter_initialize(void) printk("gator: mali online _mali_profiling_set_event symbol not found\n"); } - set_fb_event = symbol_get(_mali_osk_fb_control_set); + mali_set_fb_event = symbol_get(_mali_osk_fb_control_set); - if (set_fb_event) { - pr_debug("gator: mali online _mali_osk_fb_control_set symbol @ %p\n", set_fb_event); + if (mali_set_fb_event) { + pr_debug("gator: mali online _mali_osk_fb_control_set symbol @ %p\n", mali_set_fb_event); - set_fb_event(0,(counter_enabled[COUNTER_FILMSTRIP]?1:0)); + mali_set_fb_event(0,(counter_enabled[COUNTER_FILMSTRIP]?1:0)); - symbol_put(_mali_osk_fb_control_set); - } else { - printk("gator: mali online _mali_osk_fb_control_set symbol not found\n"); - } + symbol_put(_mali_osk_fb_control_set); + } else { + printk("gator: mali online _mali_osk_fb_control_set symbol not found\n"); + } - /* Generic control interface for Mali DDK. */ - mali_control = symbol_get(_mali_profiling_control); - if (mali_control) { - /* The event attribute in the XML file keeps the actual frame rate. */ - unsigned int rate = counter_event[COUNTER_FILMSTRIP] & 0xff; - unsigned int resize_factor = (counter_event[COUNTER_FILMSTRIP] >> 8) & 0xff; + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + /* The event attribute in the XML file keeps the actual frame rate. */ + unsigned int rate = counter_event[COUNTER_FILMSTRIP] & 0xff; + unsigned int resize_factor = (counter_event[COUNTER_FILMSTRIP] >> 8) & 0xff; - pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); + pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); - mali_control(SW_EVENTS_ENABLE, (is_any_sw_counter_enabled()?1:0)); - mali_control(FBDUMP_CONTROL_ENABLE, (counter_enabled[COUNTER_FILMSTRIP]?1:0)); - mali_control(FBDUMP_CONTROL_RATE, rate); - mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); + mali_control(SW_EVENTS_ENABLE, (is_any_sw_counter_enabled()?1:0)); + mali_control(FBDUMP_CONTROL_ENABLE, (counter_enabled[COUNTER_FILMSTRIP]?1:0)); + mali_control(FBDUMP_CONTROL_RATE, rate); + mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); - pr_debug("gator: sent mali_control enabled=%d, rate=%d\n", (counter_enabled[COUNTER_FILMSTRIP]?1:0), rate); + pr_debug("gator: sent mali_control enabled=%d, rate=%d\n", (counter_enabled[COUNTER_FILMSTRIP]?1:0), rate); - symbol_put(_mali_profiling_control); - } else { - printk("gator: mali online _mali_profiling_control symbol not found\n"); - } + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali online _mali_profiling_control symbol not found\n"); + } - _mali_profiling_get_counters_function_pointer = symbol_get(_mali_profiling_get_counters); - if (_mali_profiling_get_counters_function_pointer){ - pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", _mali_profiling_get_counters_function_pointer); - counter_prev[COUNTER_L2_C0] = 0; - counter_prev[COUNTER_L2_C1] = 0; - } - else{ - pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined"); - } + mali_get_counters = symbol_get(_mali_profiling_get_counters); + if (mali_get_counters){ + pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", mali_get_counters); + counter_prev[COUNTER_L2_C0] = 0; + counter_prev[COUNTER_L2_C1] = 0; + } + else{ + pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined"); + } } static void mali_counter_deinitialize(void) { - void (*set_hw_event)(unsigned int, unsigned int); - void (*set_fb_event)(unsigned int, unsigned int); - void (*mali_control)(unsigned int, unsigned int); + mali_profiling_set_event_type *mali_set_hw_event; + mali_osk_fb_control_set_type *mali_set_fb_event; + mali_profiling_control_type *mali_control; - set_hw_event = symbol_get(_mali_profiling_set_event); + mali_set_hw_event = symbol_get(_mali_profiling_set_event); - if (set_hw_event) { + if (mali_set_hw_event) { int i; - pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n",set_hw_event); + pr_debug("gator: mali offline _mali_profiling_set_event symbol @ %p\n",mali_set_hw_event); for (i = FIRST_HW_COUNTER; i <= LAST_HW_COUNTER; i++) { - set_hw_event(i, 0xFFFFFFFF); + mali_set_hw_event(i, 0xFFFFFFFF); } symbol_put(_mali_profiling_set_event); @@ -525,41 +534,41 @@ static void mali_counter_deinitialize(void) printk("gator: mali offline _mali_profiling_set_event symbol not found\n"); } - set_fb_event = symbol_get(_mali_osk_fb_control_set); + mali_set_fb_event = symbol_get(_mali_osk_fb_control_set); - if (set_fb_event) { - pr_debug("gator: mali offline _mali_osk_fb_control_set symbol @ %p\n", set_fb_event); + if (mali_set_fb_event) { + pr_debug("gator: mali offline _mali_osk_fb_control_set symbol @ %p\n", mali_set_fb_event); - set_fb_event(0,0); + mali_set_fb_event(0,0); - symbol_put(_mali_osk_fb_control_set); - } else { - printk("gator: mali offline _mali_osk_fb_control_set symbol not found\n"); - } - - /* Generic control interface for Mali DDK. */ - mali_control = symbol_get(_mali_profiling_control); + symbol_put(_mali_osk_fb_control_set); + } else { + printk("gator: mali offline _mali_osk_fb_control_set symbol not found\n"); + } + + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); - if (mali_control) { - pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", set_fb_event); + if (mali_control) { + pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", mali_set_fb_event); - /* Reset the DDK state - disable counter collection */ - mali_control(SW_EVENTS_ENABLE, 0); + /* Reset the DDK state - disable counter collection */ + mali_control(SW_EVENTS_ENABLE, 0); - mali_control(FBDUMP_CONTROL_ENABLE, 0); + mali_control(FBDUMP_CONTROL_ENABLE, 0); - symbol_put(_mali_profiling_control); - } else { - printk("gator: mali offline _mali_profiling_control symbol not found\n"); - } + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali offline _mali_profiling_control symbol not found\n"); + } - if (_mali_profiling_get_counters_function_pointer){ - symbol_put(_mali_profiling_get_counters); - } + if (mali_get_counters){ + symbol_put(_mali_profiling_get_counters); + } } -static int gator_events_mali_start(void) { +static int start(void) { // register tracepoints if (GATOR_REGISTER_TRACE(mali_hw_counter)) { printk("gator: mali_hw_counter tracepoint failed to activate\n"); @@ -567,9 +576,9 @@ static int gator_events_mali_start(void) { } #if GATOR_MALI_INTERFACE_STYLE == 1 - /* None. */ + /* None. */ #elif GATOR_MALI_INTERFACE_STYLE == 2 - /* For patched Mali driver. */ + /* For patched Mali driver. */ if (GATOR_REGISTER_TRACE(mali_sw_counter)) { printk("gator: mali_sw_counter tracepoint failed to activate\n"); return -1; @@ -590,7 +599,7 @@ static int gator_events_mali_start(void) { return 0; } -static void gator_events_mali_stop(void) { +static void stop(void) { unsigned int cnt; pr_debug("gator: mali stop\n"); @@ -624,7 +633,7 @@ static void gator_events_mali_stop(void) { mali_counter_deinitialize(); } -static int gator_events_mali_read(int **buffer) { +static int read(int **buffer) { int cnt, len = 0; if (smp_processor_id()) return 0; @@ -637,8 +646,8 @@ static int gator_events_mali_read(int **buffer) { u32 val1 = 0; // Poke the driver to get the counter values - if (_mali_profiling_get_counters_function_pointer){ - _mali_profiling_get_counters_function_pointer(&src0, &val0, &src1, &val1); + if (mali_get_counters){ + mali_get_counters(&src0, &val0, &src1, &val1); } if (counter_enabled[COUNTER_L2_C0]) @@ -670,6 +679,23 @@ static int gator_events_mali_read(int **buffer) { } } + /* + * Add in the voltage and frequency counters if enabled. Note that, since these are + * actually passed as events, the counter value should not be cleared. + */ + cnt = COUNTER_FREQUENCY; + if (counter_enabled[cnt]) { + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = counter_data[cnt]; + } + + cnt = COUNTER_VOLTAGE; + if (counter_enabled[cnt]) { + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = counter_data[cnt]; + } + + if (buffer) { *buffer = (int*) counter_dump; } @@ -678,34 +704,20 @@ static int gator_events_mali_read(int **buffer) { } static struct gator_interface gator_events_mali_interface = { - .create_files = gator_events_mali_create_files, - .start = gator_events_mali_start, - .stop = gator_events_mali_stop, - .read = gator_events_mali_read, + .create_files = create_files, + .start = start, + .stop = stop, + .read = read, }; +extern void gator_events_mali_log_dvfs_event(unsigned int frequency_mhz, unsigned int voltage_mv) { + counter_data[COUNTER_FREQUENCY] = frequency_mhz; + counter_data[COUNTER_VOLTAGE] = voltage_mv; +} + int gator_events_mali_init(void) { unsigned int cnt; - u32 id = gator_mali_get_id(); - - switch (id) { - case MALI_T6xx: - mali_name = "Mali-T6xx"; - break; - case MALI_400: - mali_name = "Mali-400"; - break; - case MALI_300: - mali_name = "Mali-300"; - break; - case MALI_200: - mali_name = "Mali-200"; - break; - default: - printk("Unknown Mali ID (%d)\n", id); - return -1; - } pr_debug("gator: mali init\n"); @@ -714,11 +726,12 @@ int gator_events_mali_init(void) counter_event[cnt] = 0; counter_key[cnt] = gator_events_get_key(); counter_address[cnt] = NULL; - counter_data[cnt] = 0; + counter_data[cnt] = 0; } trace_registered = 0; return gator_events_install(&gator_events_mali_interface); } + gator_events_init(gator_events_mali_init); diff --git a/driver/gator_events_mali_400.h b/driver/gator_events_mali_400.h new file mode 100644 index 0000000..b784ae9 --- /dev/null +++ b/driver/gator_events_mali_400.h @@ -0,0 +1,19 @@ +/* + * This confidential and proprietary software may be used only as + * authorised by a licensing agreement from ARM Limited + * (C) COPYRIGHT 2011-2012 ARM Limited + * ALL RIGHTS RESERVED + * The entire notice above must be reproduced on all authorised + * copies and copies may only be made to the extent permitted + * by a licensing agreement from ARM Limited. + */ + +/* + * Header contains common definitions for the Mali-400 processors. + */ +#if !defined(GATOR_EVENTS_MALI_400_H) +#define GATOR_EVENTS_MALI_400_H + +extern void gator_events_mali_log_dvfs_event(unsigned int d0, unsigned int d1); + +#endif /* GATOR_EVENTS_MALI_400_H */ diff --git a/driver/gator_events_mali_common.c b/driver/gator_events_mali_common.c new file mode 100644 index 0000000..2dd9ad6 --- /dev/null +++ b/driver/gator_events_mali_common.c @@ -0,0 +1,79 @@ +/* + * This confidential and proprietary software may be used only as + * authorised by a licensing agreement from ARM Limited + * (C) COPYRIGHT 2011-2012 ARM Limited + * ALL RIGHTS RESERVED + * The entire notice above must be reproduced on all authorised + * copies and copies may only be made to the extent permitted + * by a licensing agreement from ARM Limited. + */ +#include "gator_events_mali_common.h" + +static u32 gator_mali_get_id(void) +{ + return MALI_SUPPORT; +} + +extern const char* gator_mali_get_mali_name(void) +{ + u32 id = gator_mali_get_id(); + + switch (id) { + case MALI_T6xx: + return "Mali-T6xx"; + case MALI_400: + return "Mali-400"; + default: + pr_debug("gator: Mali-T6xx: unknown Mali ID (%d)\n", id); + return "Mali-Unknown"; + } +} + +extern int gator_mali_create_file_system(const char* mali_name, const char* event_name, struct super_block *sb, struct dentry *root, mali_counter *counter) +{ + int err; + char buf[255]; + struct dentry *dir; + + /* If the counter name is empty ignore it*/ + if (strlen(event_name) != 0) + { + /* Set up the filesystem entry for this event. */ + snprintf(buf, sizeof(buf), "ARM_%s_%s", mali_name, event_name); + + dir = gatorfs_mkdir(sb, root, buf); + + if (dir == NULL) + { + pr_debug("gator: Mali-T6xx: error creating file system for: %s (%s)", event_name, buf); + return -1; + } + + err = gatorfs_create_ulong(sb, dir, "enabled", &counter->enabled); + if (err != 0) + { + pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ulong for: %s (%s)", event_name, buf); + return -1; + } + err = gatorfs_create_ro_ulong(sb, dir, "key", &counter->key); + if (err != 0) + { + pr_debug("gator: Mali-T6xx: error calling gatorfs_create_ro_ulong for: %s (%s)", event_name, buf); + return -1; + } + } + + return 0; +} + +extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters) +{ + unsigned int cnt; + + for (cnt = 0; cnt < n_counters; cnt++) { + mali_counter *counter = &counters[cnt]; + + counter->key = gator_events_get_key(); + counter->enabled = 0; + } +} diff --git a/driver/gator_events_mali_common.h b/driver/gator_events_mali_common.h new file mode 100644 index 0000000..cb851d5 --- /dev/null +++ b/driver/gator_events_mali_common.h @@ -0,0 +1,85 @@ +/* + * This confidential and proprietary software may be used only as + * authorised by a licensing agreement from ARM Limited + * (C) COPYRIGHT 2011-2012 ARM Limited + * ALL RIGHTS RESERVED + * The entire notice above must be reproduced on all authorised + * copies and copies may only be made to the extent permitted + * by a licensing agreement from ARM Limited. + */ +#if !defined(GATOR_EVENTS_MALI_COMMON_H) +#define GATOR_EVENTS_MALI_COMMON_H + +#include "gator.h" + +#include <linux/module.h> +#include <linux/time.h> +#include <linux/math64.h> +#include <linux/slab.h> +#include <asm/io.h> + +/* Device codes for each known GPU */ +#define MALI_400 (0x0b07) +#define MALI_T6xx (0x0056) + +/* Ensure that MALI_SUPPORT has been defined to something. */ +#ifndef MALI_SUPPORT +#error MALI_SUPPORT not defined! +#endif + +/* Values for the supported activity event types */ +#define ACTIVITY_START (1) +#define ACTIVITY_STOP (2) + +/* + * Runtime state information for a counter. + */ +typedef struct { + unsigned long key; /* 'key' (a unique id set by gatord and returned by gator.ko) */ + unsigned long enabled; /* counter enable state */ +} mali_counter; + +typedef void mali_profiling_set_event_type(unsigned int, unsigned int); +typedef void mali_osk_fb_control_set_type(unsigned int, unsigned int); +typedef void mali_profiling_control_type(unsigned int, unsigned int); +typedef void mali_profiling_get_counters_type(unsigned int*, unsigned int*, unsigned int*, unsigned int*); + +/* + * Driver entry points for functions called directly by gator. + */ +extern void _mali_profiling_set_event(unsigned int, unsigned int); +extern void _mali_osk_fb_control_set(unsigned int, unsigned int); +extern void _mali_profiling_control(unsigned int, unsigned int); +extern void _mali_profiling_get_counters(unsigned int*, unsigned int*, unsigned int*, unsigned int*); + +/** + * Returns a name which identifies the GPU type (eg Mali-400, Mali-T6xx). + * + * @return The name as a constant string. + */ +extern const char* gator_mali_get_mali_name(void); + +/** + * Creates a filesystem entry under /dev/gator relating to the specified event name and key, and + * associate the key/enable values with this entry point. + * + * @param mali_name A name related to the type of GPU, obtained from a call to gator_mali_get_mali_name() + * @param event_name The name of the event. + * @param sb Linux super block + * @param root Directory under which the entry will be created. + * @param counter_key Ptr to location which will be associated with the counter key. + * @param counter_enabled Ptr to location which will be associated with the counter enable state. + * + * @return 0 if entry point was created, non-zero if not. + */ +extern int gator_mali_create_file_system(const char* mali_name, const char* event_name, struct super_block *sb, struct dentry *root, mali_counter *counter); + +/** + * Initialises the counter array. + * + * @param keys The array of counters + * @param n_counters The number of entries in each of the arrays. + */ +extern void gator_mali_initialise_counters(mali_counter counters[], unsigned int n_counters); + +#endif /* GATOR_EVENTS_MALI_COMMON_H */ diff --git a/driver/gator_events_mali_t6xx.c b/driver/gator_events_mali_t6xx.c new file mode 100644 index 0000000..79af764 --- /dev/null +++ b/driver/gator_events_mali_t6xx.c @@ -0,0 +1,553 @@ +/* + * This confidential and proprietary software may be used only as + * authorised by a licensing agreement from ARM Limited + * (C) COPYRIGHT 2011 ARM Limited + * ALL RIGHTS RESERVED + * The entire notice above must be reproduced on all authorised + * copies and copies may only be made to the extent permitted + * by a licensing agreement from ARM Limited. + */ + +#include "gator.h" + +#include <linux/module.h> +#include <linux/time.h> +#include <linux/math64.h> +#include <linux/slab.h> +#include <asm/io.h> + +#include "linux/mali_linux_trace.h" + + +#include "gator_events_mali_common.h" + +/* + * Check that the MALI_SUPPORT define is set to one of the allowable device codes. + */ +#if (MALI_SUPPORT != MALI_T6xx) +#error MALI_SUPPORT set to an invalid device code: expecting MALI_T6xx +#endif + + +/* Counters for Mali-T6xx: + * + * - Timeline events + * They are tracepoints, but instead of reporting a number they report a START/STOP event. + * They are reported in Streamline as number of microseconds while that particular counter was active. + * + * - SW counters + * They are tracepoints reporting a particular number. + * They are accumulated in sw_counter_data array until they are passed to Streamline, then they are zeroed. + * + * - Accumulators + * They are the same as software counters but their value is not zeroed. + */ + +/* Timeline (start/stop) activity */ +static const char* timeline_event_names [] = +{ + "PM_SHADER_0", + "PM_SHADER_1", + "PM_SHADER_2", + "PM_SHADER_3", + "PM_SHADER_4", + "PM_SHADER_5", + "PM_SHADER_6", + "PM_SHADER_7", + "PM_TILER_0", + "PM_L2_0", + "PM_L2_1", + "MMU_AS_0", + "MMU_AS_1", + "MMU_AS_2", + "MMU_AS_3" +}; + +enum +{ + PM_SHADER_0 = 0, + PM_SHADER_1, + PM_SHADER_2, + PM_SHADER_3, + PM_SHADER_4, + PM_SHADER_5, + PM_SHADER_6, + PM_SHADER_7, + PM_TILER_0, + PM_L2_0, + PM_L2_1, + MMU_AS_0, + MMU_AS_1, + MMU_AS_2, + MMU_AS_3 +}; +/* The number of shader blocks in the enum above */ +#define NUM_PM_SHADER (8) + +/* Software Counters */ +static const char* software_counter_names [] = +{ + "MMU_PAGE_FAULT_0", + "MMU_PAGE_FAULT_1", + "MMU_PAGE_FAULT_2", + "MMU_PAGE_FAULT_3" +}; + +enum +{ + MMU_PAGE_FAULT_0 = 0, + MMU_PAGE_FAULT_1, + MMU_PAGE_FAULT_2, + MMU_PAGE_FAULT_3 +}; + +/* Software Counters */ +static const char* accumulators_names [] = +{ + "TOTAL_ALLOC_PAGES" +}; + +enum +{ + TOTAL_ALLOC_PAGES = 0 +}; + +#define FIRST_TIMELINE_EVENT (0) +#define NUMBER_OF_TIMELINE_EVENTS (sizeof(timeline_event_names) / sizeof(timeline_event_names[0])) +#define FIRST_SOFTWARE_COUNTER (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS) +#define NUMBER_OF_SOFTWARE_COUNTERS (sizeof(software_counter_names) / sizeof(software_counter_names[0])) +#define FIRST_ACCUMULATOR (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS) +#define NUMBER_OF_ACCUMULATORS (sizeof(accumulators_names) / sizeof(accumulators_names[0])) +#define NUMBER_OF_EVENTS (NUMBER_OF_TIMELINE_EVENTS + NUMBER_OF_SOFTWARE_COUNTERS + NUMBER_OF_ACCUMULATORS) + +/* + * gatorfs variables for counter enable state + */ +static mali_counter counters[NUMBER_OF_EVENTS]; + +/* An array used to return the data we recorded + * as key,value pairs hence the *2 + */ +static unsigned long counter_dump[NUMBER_OF_EVENTS * 2]; + +/* + * Array holding counter start times (in ns) for each counter. A zero here + * indicates that the activity monitored by this counter is not running. + */ +static struct timespec timeline_event_starttime[NUMBER_OF_TIMELINE_EVENTS]; + +/* The data we have recorded */ +static unsigned int timeline_data[NUMBER_OF_TIMELINE_EVENTS]; +static unsigned int sw_counter_data[NUMBER_OF_SOFTWARE_COUNTERS]; +static unsigned int accumulators_data[NUMBER_OF_ACCUMULATORS]; + +/* Hold the previous timestamp, used to calculate the sample interval. */ +static struct timespec prev_timestamp; + +/** + * Returns the timespan (in microseconds) between the two specified timestamps. + * + * @param start Ptr to the start timestamp + * @param end Ptr to the end timestamp + * + * @return Number of microseconds between the two timestamps (can be negative if start follows end). + */ +static inline long get_duration_us(const struct timespec *start, const struct timespec *end) +{ + long event_duration_us = (end->tv_nsec - start->tv_nsec)/1000; + event_duration_us += (end->tv_sec - start->tv_sec) * 1000000; + + return event_duration_us; +} + +static void record_timeline_event(unsigned int timeline_index, unsigned int type) +{ + struct timespec event_timestamp; + struct timespec *event_start = &timeline_event_starttime[timeline_index]; + + switch(type) + { + case ACTIVITY_START: + /* Get the event time... */ + getnstimeofday(&event_timestamp); + + /* Remember the start time if the activity is not already started */ + if(event_start->tv_sec == 0) + { + *event_start = event_timestamp; /* Structure copy */ + } + break; + + case ACTIVITY_STOP: + /* if the counter was started... */ + if(event_start->tv_sec != 0) + { + /* Get the event time... */ + getnstimeofday(&event_timestamp); + + /* Accumulate the duration in us */ + timeline_data[timeline_index] += get_duration_us(event_start, &event_timestamp); + + /* Reset the start time to indicate the activity is stopped. */ + event_start->tv_sec = 0; + } + break; + + default: + /* Other activity events are ignored. */ + break; + } +} + +/* + * Documentation about the following tracepoints is in mali_linux_trace.h + */ + +GATOR_DEFINE_PROBE(mali_pm_status, TP_PROTO(unsigned int event_id, unsigned long long value)) +{ +#define SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */ +#define TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */ +#define L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */ +#define BIT_AT(value, pos) ((value >> pos) & 1) + + static unsigned long long previous_shader_bitmask = 0; + static unsigned long long previous_tiler_bitmask = 0; + static unsigned long long previous_l2_bitmask = 0; + + switch (event_id) + { + case SHADER_PRESENT_LO: + { + unsigned long long changed_bitmask = previous_shader_bitmask ^ value; + int pos; + + for (pos = 0; pos < NUM_PM_SHADER; ++pos) + { + if (BIT_AT(changed_bitmask, pos)) + { + record_timeline_event(PM_SHADER_0 + pos, BIT_AT(value, pos) ? ACTIVITY_START : ACTIVITY_STOP); + } + } + + previous_shader_bitmask = value; + break; + } + + case TILER_PRESENT_LO: + { + unsigned long long changed = previous_tiler_bitmask ^ value; + + if (BIT_AT(changed, 0)) + { + record_timeline_event(PM_TILER_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP); + } + + previous_tiler_bitmask = value; + break; + } + + case L2_PRESENT_LO: + { + unsigned long long changed = previous_l2_bitmask ^ value; + + if (BIT_AT(changed, 0)) + { + record_timeline_event(PM_L2_0, BIT_AT(value, 0) ? ACTIVITY_START : ACTIVITY_STOP); + } + if (BIT_AT(changed, 4)) + { + record_timeline_event(PM_L2_1, BIT_AT(value, 4) ? ACTIVITY_START : ACTIVITY_STOP); + } + + previous_l2_bitmask = value; + break; + } + + default: + /* No other blocks are supported at present */ + break; + } + +#undef SHADER_PRESENT_LO +#undef TILER_PRESENT_LO +#undef L2_PRESENT_LO +#undef BIT_AT +} + +GATOR_DEFINE_PROBE(mali_page_fault_insert_pages, TP_PROTO(int event_id, unsigned long value)) +{ + /* We add to the previous since we may receive many tracepoints in one sample period */ + sw_counter_data[MMU_PAGE_FAULT_0 + event_id] += value; +} + +GATOR_DEFINE_PROBE(mali_mmu_as_in_use, TP_PROTO(int event_id)) +{ + record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_START); +} + +GATOR_DEFINE_PROBE(mali_mmu_as_released, TP_PROTO(int event_id)) +{ + record_timeline_event(MMU_AS_0 + event_id, ACTIVITY_STOP); +} + +GATOR_DEFINE_PROBE(mali_total_alloc_pages_change, TP_PROTO(long long int event_id)) +{ + accumulators_data[TOTAL_ALLOC_PAGES] = event_id; +} + +static int create_files(struct super_block *sb, struct dentry *root) +{ + int event; + /* + * Create the filesystem for all events + */ + int counter_index = 0; + const char* mali_name = gator_mali_get_mali_name(); + + for (event = FIRST_TIMELINE_EVENT; event < FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS; event++) + { + if (gator_mali_create_file_system(mali_name, timeline_event_names[counter_index], sb, root, &counters[event]) != 0) + { + return -1; + } + counter_index++; + } + counter_index = 0; + for (event = FIRST_SOFTWARE_COUNTER; event < FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS; event++) + { + if (gator_mali_create_file_system(mali_name, software_counter_names[counter_index], sb, root, &counters[event]) != 0) + { + return -1; + } + counter_index++; + } + counter_index = 0; + for (event = FIRST_ACCUMULATOR; event < FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS; event++) + { + if (gator_mali_create_file_system(mali_name, accumulators_names[counter_index], sb, root, &counters[event]) != 0) + { + return -1; + } + counter_index++; + } + + return 0; +} + +static int register_tracepoints(void) +{ + if (GATOR_REGISTER_TRACE(mali_pm_status)) + { + pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_page_fault_insert_pages)) + { + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_mmu_as_in_use)) + { + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_mmu_as_released)) + { + pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint failed to activate\n"); + return 0; + } + + if (GATOR_REGISTER_TRACE(mali_total_alloc_pages_change)) + { + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint failed to activate\n"); + return 0; + } + + pr_debug("gator: Mali-T6xx: start\n"); + pr_debug("gator: Mali-T6xx: mali_pm_status probe is at %p\n", &probe_mali_pm_status); + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages probe is at %p\n", &probe_mali_page_fault_insert_pages); + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use probe is at %p\n", &probe_mali_mmu_as_in_use); + pr_debug("gator: Mali-T6xx: mali_mmu_as_released probe is at %p\n", &probe_mali_mmu_as_released); + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change probe is at %p\n", &probe_mali_total_alloc_pages_change); + + return 1; +} + +static int start(void) +{ + unsigned int cnt; + + /* Clean all data for the next capture */ + for (cnt = 0; cnt < NUMBER_OF_TIMELINE_EVENTS; cnt++) + { + timeline_event_starttime[cnt].tv_sec = timeline_event_starttime[cnt].tv_nsec = 0; + timeline_data[cnt] = 0; + } + + for (cnt = 0; cnt < NUMBER_OF_SOFTWARE_COUNTERS; cnt++) + { + sw_counter_data[cnt] = 0; + } + + for (cnt = 0; cnt < NUMBER_OF_ACCUMULATORS; cnt++) + { + accumulators_data[cnt] = 0; + } + + /* Register tracepoints */ + if (register_tracepoints() == 0) + { + return -1; + } + + /* + * Set the first timestamp for calculating the sample interval. The first interval could be quite long, + * since it will be the time between 'start' and the first 'read'. + * This means that timeline values will be divided by a big number for the first sample. + */ + getnstimeofday(&prev_timestamp); + + return 0; +} + +static void stop(void) +{ + pr_debug("gator: Mali-T6xx: stop\n"); + + /* + * It is safe to unregister traces even if they were not successfully + * registered, so no need to check. + */ + GATOR_UNREGISTER_TRACE(mali_pm_status); + pr_debug("gator: Mali-T6xx: mali_pm_status tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_page_fault_insert_pages); + pr_debug("gator: Mali-T6xx: mali_page_fault_insert_pages tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_mmu_as_in_use); + pr_debug("gator: Mali-T6xx: mali_mmu_as_in_use tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_mmu_as_released); + pr_debug("gator: Mali-T6xx: mali_mmu_as_released tracepoint deactivated\n"); + + GATOR_UNREGISTER_TRACE(mali_total_alloc_pages_change); + pr_debug("gator: Mali-T6xx: mali_total_alloc_pages_change tracepoint deactivated\n"); +} + +static int read(int **buffer) +{ + int cnt; + int len = 0; + long sample_interval_us = 0; + struct timespec read_timestamp; + + if (smp_processor_id()!=0) + { + return 0; + } + + /* Get the start of this sample period. */ + getnstimeofday(&read_timestamp); + + /* + * Calculate the sample interval if the previous sample time is valid. + * We use tv_sec since it will not be 0. + */ + if(prev_timestamp.tv_sec != 0) { + sample_interval_us = get_duration_us(&prev_timestamp, &read_timestamp); + } + + /* Structure copy. Update the previous timestamp. */ + prev_timestamp = read_timestamp; + + /* + * Report the timeline counters (ACTIVITY_START/STOP) + */ + for (cnt = FIRST_TIMELINE_EVENT; cnt < (FIRST_TIMELINE_EVENT + NUMBER_OF_TIMELINE_EVENTS); cnt++) + { + mali_counter *counter = &counters[cnt]; + if (counter->enabled) + { + const int index = cnt - FIRST_TIMELINE_EVENT; + unsigned int value; + + /* If the activity is still running, reset its start time to the start of this sample period + * to correct the count. Add the time up to the end of the sample onto the count. */ + if(timeline_event_starttime[index].tv_sec != 0) { + const long event_duration = get_duration_us(&timeline_event_starttime[index], &read_timestamp); + timeline_data[index] += event_duration; + timeline_event_starttime[index] = read_timestamp; /* Activity is still running. */ + } + + if(sample_interval_us != 0) { + /* Convert the counter to a percent-of-sample value */ + value = (timeline_data[index] * 100) / sample_interval_us; + } else { + pr_debug("gator: Mali-T6xx: setting value to zero\n"); + value = 0; + } + + /* Clear the counter value ready for the next sample. */ + timeline_data[index] = 0; + + counter_dump[len++] = counter->key; + counter_dump[len++] = value; + } + } + + /* Report the software counters */ + for (cnt = FIRST_SOFTWARE_COUNTER; cnt < (FIRST_SOFTWARE_COUNTER + NUMBER_OF_SOFTWARE_COUNTERS); cnt++) + { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) + { + const int index = cnt - FIRST_SOFTWARE_COUNTER; + counter_dump[len++] = counter->key; + counter_dump[len++] = sw_counter_data[index]; + /* Set the value to zero for the next time */ + sw_counter_data[index] = 0; + } + } + + /* Report the accumulators */ + for (cnt = FIRST_ACCUMULATOR; cnt < (FIRST_ACCUMULATOR + NUMBER_OF_ACCUMULATORS); cnt++) + { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) + { + const int index = cnt - FIRST_ACCUMULATOR; + counter_dump[len++] = counter->key; + counter_dump[len++] = accumulators_data[index]; + /* Do not zero the accumulator */ + } + } + + /* Update the buffer */ + if (buffer) + { + *buffer = (int*) counter_dump; + } + + return len; +} + +static struct gator_interface gator_events_mali_t6xx_interface = { + .create_files = create_files, + .start = start, + .stop = stop, + .read = read +}; + +extern int gator_events_mali_t6xx_init(void) +{ + pr_debug("gator: Mali-T6xx: sw_counters init\n"); + + gator_mali_initialise_counters(counters, NUMBER_OF_EVENTS); + + return gator_events_install(&gator_events_mali_t6xx_interface); +} + +gator_events_init(gator_events_mali_t6xx_init); diff --git a/driver/gator_events_mali_t6xx_hw.c b/driver/gator_events_mali_t6xx_hw.c new file mode 100644 index 0000000..12ffebc --- /dev/null +++ b/driver/gator_events_mali_t6xx_hw.c @@ -0,0 +1,571 @@ +/* + * This confidential and proprietary software may be used only as + * authorised by a licensing agreement from ARM Limited + * (C) COPYRIGHT 2011-2012 ARM Limited + * ALL RIGHTS RESERVED + * The entire notice above must be reproduced on all authorised + * copies and copies may only be made to the extent permitted + * by a licensing agreement from ARM Limited. + */ + +#include "gator.h" + +#include <linux/module.h> +#include <linux/time.h> +#include <linux/math64.h> +#include <linux/slab.h> +#include <asm/io.h> + +/* Mali T6xx DDK includes */ +#include "linux/mali_linux_trace.h" +#include "kbase/src/common/mali_kbase.h" +#include "kbase/src/linux/mali_kbase_mem_linux.h" + +#include "gator_events_mali_common.h" + +/* Blocks for HW counters */ +enum +{ + JM_BLOCK = 0, + TILER_BLOCK, + SHADER_BLOCK, + MMU_BLOCK +}; + +/* Counters for Mali-T6xx: + * + * - HW counters, 4 blocks + * For HW counters we need strings to create /dev/gator/events files. + * Enums are not needed because the position of the HW name in the array is the same + * of the corresponding value in the received block of memory. + * HW counters are requested by calculating a bitmask, passed then to the driver. + * Every millisecond a HW counters dump is requested, and if the previous has been completed they are read. + */ + +/* Hardware Counters */ +static const char* const hardware_counter_names [] = +{ + /* Job Manager */ + "", + "", + "", + "", + "MESSAGES_SENT", + "MESSAGES_RECEIVED", + "GPU_ACTIVE", /* 6 */ + "IRQ_ACTIVE", + "JS0_JOBS", + "JS0_TASKS", + "JS0_ACTIVE", + "", + "JS0_WAIT_READ", + "JS0_WAIT_ISSUE", + "JS0_WAIT_DEPEND", + "JS0_WAIT_FINISH", + "JS1_JOBS", + "JS1_TASKS", + "JS1_ACTIVE", + "", + "JS1_WAIT_READ", + "JS1_WAIT_ISSUE", + "JS1_WAIT_DEPEND", + "JS1_WAIT_FINISH", + "JS2_JOBS", + "JS2_TASKS", + "JS2_ACTIVE", + "", + "JS2_WAIT_READ", + "JS2_WAIT_ISSUE", + "JS2_WAIT_DEPEND", + "JS2_WAIT_FINISH", + "JS3_JOBS", + "JS3_TASKS", + "JS3_ACTIVE", + "", + "JS3_WAIT_READ", + "JS3_WAIT_ISSUE", + "JS3_WAIT_DEPEND", + "JS3_WAIT_FINISH", + "JS4_JOBS", + "JS4_TASKS", + "JS4_ACTIVE", + "", + "JS4_WAIT_READ", + "JS4_WAIT_ISSUE", + "JS4_WAIT_DEPEND", + "JS4_WAIT_FINISH", + "JS5_JOBS", + "JS5_TASKS", + "JS5_ACTIVE", + "", + "JS5_WAIT_READ", + "JS5_WAIT_ISSUE", + "JS5_WAIT_DEPEND", + "JS5_WAIT_FINISH", + "JS6_JOBS", + "JS6_TASKS", + "JS6_ACTIVE", + "", + "JS6_WAIT_READ", + "JS6_WAIT_ISSUE", + "JS6_WAIT_DEPEND", + "JS6_WAIT_FINISH", + + /*Tiler*/ + "", + "", + "", + "JOBS_PROCESSED", + "TRIANGLES", + "QUADS", + "POLYGONS", + "POINTS", + "LINES", + "VCACHE_HIT", + "VCACHE_MISS", + "FRONT_FACING", + "BACK_FACING", + "PRIM_VISIBLE", + "PRIM_CULLED", + "PRIM_CLIPPED", + "LEVEL0", + "LEVEL1", + "LEVEL2", + "LEVEL3", + "LEVEL4", + "LEVEL5", + "LEVEL6", + "LEVEL7", + "COMMAND_1", + "COMMAND_2", + "COMMAND_3", + "COMMAND_4", + "COMMAND_4_7", + "COMMAND_8_15", + "COMMAND_16_63", + "COMMAND_64", + "COMPRESS_IN", + "COMPRESS_OUT", + "COMPRESS_FLUSH", + "TIMESTAMPS", + "PCACHE_HIT", + "PCACHE_MISS", + "PCACHE_LINE", + "PCACHE_STALL", + "WRBUF_HIT", + "WRBUF_MISS", + "WRBUF_LINE", + "WRBUF_PARTIAL", + "WRBUF_STALL", + "ACTIVE", + "LOADING_DESC", + "INDEX_WAIT", + "INDEX_RANGE_WAIT", + "VERTEX_WAIT", + "PCACHE_WAIT", + "WRBUF_WAIT", + "BUS_READ", + "BUS_WRITE", + "", + "", + "", + "", + "", + "UTLB_STALL", + "UTLB_REPLAY_MISS", + "UTLB_REPLAY_FULL", + "UTLB_NEW_MISS", + "UTLB_HIT", + + /* Shader Core */ + "", + "", + "", + "SHADER_CORE_ACTIVE", + "FRAG_ACTIVE", + "FRAG_PRIMATIVES", + "FRAG_PRIMATIVES_DROPPED", + "FRAG_CYCLE_DESC", + "FRAG_CYCLES_PLR", + "FRAG_CYCLES_VERT", + "FRAG_CYCLES_TRISETUP", + "FRAG_CYCLES_RAST", + "FRAG_THREADS", + "FRAG_DUMMY_THREADS", + "FRAG_QUADS_RAST", + "FRAG_QUADS_EZS_TEST", + "FRAG_QUADS_EZS_KILLED", + "FRAG_QUADS_LZS_TEST", + "FRAG_QUADS_LZS_KILLED", + "FRAG_CYCLE_NO_TILE", + "FRAG_NUM_TILES", + "FRAG_TRANS_ELIM", + "COMPUTE_ACTIVE", + "COMPUTE_TASKS", + "COMPUTE_THREADS", + "COMPUTE_CYCLES_DESC", + "TRIPIPE_ACTIVE", + "ARITH_WORDS", + "ARITH_CYCLES_REG", + "ARITH_CYCLES_L0", + "ARITH_FRAG_DEPEND", + "LS_WORDS", + "LS_ISSUES", + "LS_RESTARTS", + "LS_REISSUES_MISS", + "LS_REISSUES_VD", + "LS_REISSUE_ATTRIB_MISS", + "LS_NO_WB", + "TEX_WORDS", + "TEX_BUBBLES", + "TEX_WORDS_L0", + "TEX_WORDS_DESC", + "TEX_THREADS", + "TEX_RECIRC_FMISS", + "TEX_RECIRC_DESC", + "TEX_RECIRC_MULTI", + "TEX_RECIRC_PMISS", + "TEX_RECIRC_CONF", + "LSC_READ_HITS", + "LSC_READ_MISSES", + "LSC_WRITE_HITS", + "LSC_WRITE_MISSES", + "LSC_ATOMIC_HITS", + "LSC_ATOMIC_MISSES", + "LSC_LINE_FETCHES", + "LSC_DIRTY_LINE", + "LSC_SNOOPS", + "AXI_TLB_STALL", + "AXI_TLB_MIESS", + "AXI_TLB_TRANSACTION", + "LS_TLB_MISS", + "LS_TLB_HIT", + "AXI_BEATS_READ", + "AXI_BEATS_WRITTEN", + + /*L2 and MMU */ + "", + "", + "", + "", + "MMU_TABLE_WALK", + "MMU_REPLAY_MISS", + "MMU_REPLAY_FULL", + "MMU_NEW_MISS", + "MMU_HIT", + "", + "", + "", + "", + "", + "", + "", + "UTLB_STALL", + "UTLB_REPLAY_MISS", + "UTLB_REPLAY_FULL", + "UTLB_NEW_MISS", + "UTLB_HIT", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "L2_WRITE_BEATS", + "L2_READ_BEATS", + "L2_ANY_LOOKUP", + "L2_READ_LOOKUP", + "L2_SREAD_LOOKUP", + "L2_READ_REPLAY", + "L2_READ_SNOOP", + "L2_READ_HIT", + "L2_CLEAN_MISS", + "L2_WRITE_LOOKUP", + "L2_SWRITE_LOOKUP", + "L2_WRITE_REPLAY", + "L2_WRITE_SNOOP", + "L2_WRITE_HIT", + "L2_EXT_READ_FULL", + "L2_EXT_READ_HALF", + "L2_EXT_WRITE_FULL", + "L2_EXT_WRITE_HALF", + "L2_EXT_READ", + "L2_EXT_READ_LINE", + "L2_EXT_WRITE", + "L2_EXT_WRITE_LINE", + "L2_EXT_WRITE_SMALL", + "L2_EXT_BARRIER", + "L2_EXT_AR_STALL", + "L2_EXT_R_BUF_FULL", + "L2_EXT_RD_BUF_FULL", + "L2_EXT_R_RAW", + "L2_EXT_W_STALL", + "L2_EXT_W_BUF_FULL", + "L2_EXT_R_W_HAZARD", + "L2_TAG_HAZARD", + "L2_SNOOP_FULL", + "L2_REPLAY_FULL" +}; + +#define NUMBER_OF_HARDWARE_COUNTERS (sizeof(hardware_counter_names) / sizeof(hardware_counter_names[0])) + +#define GET_HW_BLOCK(c) (((c) >> 6) & 0x3) +#define GET_COUNTER_OFFSET(c) ((c) & 0x3f) + +/* Memory to dump hardware counters into */ +static void *kernel_dump_buffer; + +/* kbase context and device */ +static kbase_context *kbcontext = NULL; +static struct kbase_device *kbdevice = NULL; + +extern struct kbase_device *kbase_find_device(int minor); +static volatile bool kbase_device_busy = false; +static unsigned int num_hardware_counters_enabled; + +/* + * gatorfs variables for counter enable state + */ +static mali_counter counters[NUMBER_OF_HARDWARE_COUNTERS]; + +/* An array used to return the data we recorded + * as key,value pairs hence the *2 + */ +static unsigned long counter_dump[NUMBER_OF_HARDWARE_COUNTERS * 2]; + +static int start(void) +{ + kbase_uk_hwcnt_setup setup; + mali_error err; + int cnt; + u16 bitmask[] = {0, 0, 0, 0}; + + /* Setup HW counters */ + num_hardware_counters_enabled = 0; + + if(NUMBER_OF_HARDWARE_COUNTERS != 256) + { + pr_debug("Unexpected number of hardware counters defined: expecting 256, got %d\n", NUMBER_OF_HARDWARE_COUNTERS); + } + + /* Calculate enable bitmasks based on counters_enabled array */ + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) + { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) + { + int block = GET_HW_BLOCK(cnt); + int enable_bit = GET_COUNTER_OFFSET(cnt) / 4; + bitmask[block] |= (1 << enable_bit); + pr_debug("gator: Mali-T6xx: hardware counter %s selected [%d]\n", hardware_counter_names[cnt], cnt); + num_hardware_counters_enabled++; + } + } + + /* Create a kbase context for HW counters */ + if (num_hardware_counters_enabled > 0) + { + kbdevice = kbase_find_device(-1); + + if (kbcontext) + return -1; + + kbcontext = kbase_create_context(kbdevice); + if (!kbcontext) + { + pr_debug("gator: Mali-T6xx: error creating kbase context\n"); + goto out; + } + + /* + * The amount of memory needed to store the dump (bytes) + * DUMP_SIZE = number of core groups + * * number of blocks (always 8 for midgard) + * * number of counters per block (always 64 for midgard) + * * number of bytes per counter (always 4 in midgard) + * For a Mali-T6xx with a single core group = 1 * 8 * 64 * 4 + */ + kernel_dump_buffer = kbase_va_alloc(kbcontext, 2048); + if (!kernel_dump_buffer) + { + pr_debug("gator: Mali-T6xx: error trying to allocate va\n"); + goto destroy_context; + } + + setup.dump_buffer = (uintptr_t)kernel_dump_buffer; + setup.jm_bm = bitmask[JM_BLOCK]; + setup.tiler_bm = bitmask[TILER_BLOCK]; + setup.shader_bm = bitmask[SHADER_BLOCK]; + setup.mmu_l2_bm = bitmask[MMU_BLOCK]; + /* These counters do not exist on Mali-T60x */ + setup.l3_cache_bm = 0; + + /* Use kbase API to enable hardware counters and provide dump buffer */ + err = kbase_instr_hwcnt_enable(kbcontext, &setup); + if (err != MALI_ERROR_NONE) + { + pr_debug("gator: Mali-T6xx: can't setup hardware counters\n"); + goto free_buffer; + } + pr_debug("gator: Mali-T6xx: hardware counters enabled\n"); + kbase_instr_hwcnt_clear(kbcontext); + pr_debug("gator: Mali-T6xx: hardware counters cleared \n"); + + kbase_device_busy = false; + } + + return 0; + + free_buffer: + kbase_va_free(kbcontext, kernel_dump_buffer); + destroy_context: + kbase_destroy_context(kbcontext); + out: + return -1; +} + +static void stop(void) { + unsigned int cnt; + kbase_context *temp_kbcontext; + + pr_debug("gator: Mali-T6xx: stop\n"); + + /* Set all counters as disabled */ + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) { + counters[cnt].enabled = 0; + } + + /* Destroy the context for HW counters */ + if (num_hardware_counters_enabled > 0 && kbcontext != NULL) + { + /* + * Set the global variable to NULL before destroying it, because + * other function will check this before using it. + */ + temp_kbcontext = kbcontext; + kbcontext = NULL; + + kbase_instr_hwcnt_disable(temp_kbcontext); + kbase_va_free(temp_kbcontext, kernel_dump_buffer); + kbase_destroy_context(temp_kbcontext); + pr_debug("gator: Mali-T6xx: hardware counters stopped\n"); + } +} + +static int read(int **buffer) { + int cnt; + int len = 0; + u32 value = 0; + mali_bool success; + + if (smp_processor_id()!=0) + { + return 0; + } + + /* + * Report the HW counters + * Only process hardware counters if at least one of the hardware counters is enabled. + */ + if (num_hardware_counters_enabled > 0) + { + const unsigned int vithar_blocks[] = { + 0x700, /* VITHAR_JOB_MANAGER, Block 0 */ + 0x400, /* VITHAR_TILER, Block 1 */ + 0x000, /* VITHAR_SHADER_CORE, Block 2 */ + 0x500 /* VITHAR_MEMORY_SYSTEM, Block 3 */ + }; + + if (!kbcontext) + { + return -1; + } + + // TODO: SYMBOL_GET (all kbase functions) + if (kbase_instr_hwcnt_dump_complete(kbcontext, &success) == MALI_TRUE) + { + kbase_device_busy = false; + + if (success == MALI_TRUE) + { + for (cnt = 0; cnt < NUMBER_OF_HARDWARE_COUNTERS; cnt++) + { + const mali_counter *counter = &counters[cnt]; + if (counter->enabled) + { + const int block = GET_HW_BLOCK(cnt); + const int counter_offset = GET_COUNTER_OFFSET(cnt); + const u32 *counter_block = (u32 *)((uintptr_t)kernel_dump_buffer + vithar_blocks[block]); + const u32 *counter_address = counter_block + counter_offset; + + value = *counter_address; + + if(block == SHADER_BLOCK){ + /* (counter_address + 0x000) has already been accounted-for above. */ + value += *(counter_address + 0x100); + value += *(counter_address + 0x200); + value += *(counter_address + 0x300); + } + + counter_dump[len++] = counter->key; + counter_dump[len++] = value; + } + } + } + } + + if (! kbase_device_busy) + { + kbase_device_busy = true; + kbase_instr_hwcnt_dump_irq(kbcontext); + } + } + + /* Update the buffer */ + if (buffer) { + *buffer = (int*) counter_dump; + } + + return len; +} + +static int create_files(struct super_block *sb, struct dentry *root) +{ + unsigned int event; + /* + * Create the filesystem for all events + */ + int counter_index = 0; + const char* mali_name = gator_mali_get_mali_name(); + + for (event = 0; event < NUMBER_OF_HARDWARE_COUNTERS; event++) + { + if (gator_mali_create_file_system(mali_name, hardware_counter_names[counter_index], sb, root, &counters[event]) != 0) + return -1; + counter_index++; + } + + return 0; +} + + +static struct gator_interface gator_events_mali_t6xx_interface = { + .create_files = create_files, + .start = start, + .stop = stop, + .read = read +}; + +int gator_events_mali_t6xx_hw_init(void) +{ + pr_debug("gator: Mali-T6xx: sw_counters init\n"); + + gator_mali_initialise_counters(counters, NUMBER_OF_HARDWARE_COUNTERS); + + return gator_events_install(&gator_events_mali_t6xx_interface); +} + +gator_events_init(gator_events_mali_t6xx_hw_init); diff --git a/driver/gator_events_meminfo.c b/driver/gator_events_meminfo.c index ad552ef..b962641 100644 --- a/driver/gator_events_meminfo.c +++ b/driver/gator_events_meminfo.c @@ -26,8 +26,9 @@ static unsigned int mem_event = 0; static bool new_data_avail; static void wq_sched_handler(struct work_struct *wsptr); - DECLARE_WORK(work, wq_sched_handler); +static struct timer_list meminfo_wake_up_timer; +static void meminfo_wake_up_handler(unsigned long unused_data); #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0) GATOR_DEFINE_PROBE(mm_page_free_direct, TP_PROTO(struct page *page, unsigned int order)) { @@ -107,6 +108,7 @@ static int gator_events_meminfo_start(void) if (GATOR_REGISTER_TRACE(mm_page_alloc)) goto mm_page_alloc_exit; + setup_timer(&meminfo_wake_up_timer, meminfo_wake_up_handler, 0); return 0; mm_page_alloc_exit: @@ -138,6 +140,8 @@ static void gator_events_meminfo_stop(void) GATOR_UNREGISTER_TRACE(mm_page_free_batched); #endif GATOR_UNREGISTER_TRACE(mm_page_alloc); + + del_timer_sync(&meminfo_wake_up_timer); } meminfo_global_enabled = 0; @@ -146,7 +150,7 @@ static void gator_events_meminfo_stop(void) } } -// Must be run in process context (work queue) as the kernel function si_meminfo() can sleep +// Must be run in process context as the kernel function si_meminfo() can sleep static void wq_sched_handler(struct work_struct *wsptr) { struct sysinfo info; @@ -181,6 +185,12 @@ static void wq_sched_handler(struct work_struct *wsptr) new_data_avail = true; } +static void meminfo_wake_up_handler(unsigned long unused_data) +{ + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + schedule_work(&work); +} + static int gator_events_meminfo_read(long long **buffer) { static unsigned int last_mem_event = 0; @@ -190,11 +200,7 @@ static int gator_events_meminfo_read(long long **buffer) if (last_mem_event != mem_event) { last_mem_event = mem_event; - if (in_interrupt()) { - schedule_work(&work); - } else { - wq_sched_handler(NULL); - } + mod_timer(&meminfo_wake_up_timer, jiffies + 1); } if (!new_data_avail) diff --git a/driver/gator_events_net.c b/driver/gator_events_net.c index 9298905..11282b5 100644 --- a/driver/gator_events_net.c +++ b/driver/gator_events_net.c @@ -23,6 +23,9 @@ static int rx_total, tx_total; static ulong netPrev[TOTALNET]; static int netGet[TOTALNET * 4]; +static struct timer_list net_wake_up_timer; + +// Must be run in process context as the kernel function dev_get_stats() can sleep static void get_network_stats(struct work_struct *wsptr) { int rx = 0, tx = 0; struct net_device *dev; @@ -42,6 +45,12 @@ static void get_network_stats(struct work_struct *wsptr) { } DECLARE_WORK(wq_get_stats, get_network_stats); +static void net_wake_up_handler(unsigned long unused_data) +{ + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + schedule_work(&wq_get_stats); +} + static void calculate_delta(int *rx, int *tx) { int rx_calc, tx_calc; @@ -83,14 +92,16 @@ static int gator_events_net_create_files(struct super_block *sb, struct dentry * static int gator_events_net_start(void) { - get_network_stats(NULL); + get_network_stats(0); netPrev[NETRX] = rx_total; netPrev[NETTX] = tx_total; + setup_timer(&net_wake_up_timer, net_wake_up_handler, 0); return 0; } static void gator_events_net_stop(void) { + del_timer_sync(&net_wake_up_timer); netrx_enabled = 0; nettx_enabled = 0; } @@ -106,11 +117,7 @@ static int gator_events_net_read(int **buffer) if (!netrx_enabled && !nettx_enabled) return 0; - if (in_interrupt()){ - schedule_work(&wq_get_stats); - } else { - get_network_stats(NULL); - } + mod_timer(&net_wake_up_timer, jiffies + 1); calculate_delta(&rx_delta, &tx_delta); diff --git a/driver/gator_events_perf_pmu.c b/driver/gator_events_perf_pmu.c index da76a9c..9d78b46 100644 --- a/driver/gator_events_perf_pmu.c +++ b/driver/gator_events_perf_pmu.c @@ -32,6 +32,7 @@ static DEFINE_PER_CPU(int[CNTMAX], perfPrevDelta); static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); static DEFINE_PER_CPU(struct perf_event *[CNTMAX], pevent); static DEFINE_PER_CPU(struct perf_event_attr *[CNTMAX], pevent_attr); +extern DEFINE_PER_CPU(struct perf_event *, pevent_ebs); static void gator_events_perf_pmu_stop(void); @@ -74,10 +75,15 @@ static void dummy_handler(struct perf_event *event, struct perf_sample_data *dat static int gator_events_perf_pmu_online(int** buffer) { int cnt, len = 0, cpu = smp_processor_id(); + struct perf_event * ev; // read the counters and toss the invalid data, return zero instead for (cnt = 0; cnt < pmnc_counters; cnt++) { - struct perf_event * ev = per_cpu(pevent, cpu)[cnt]; + if (pmnc_count[cnt] > 0) { + ev = per_cpu(pevent_ebs, cpu); // special case for EBS + } else { + ev = per_cpu(pevent, cpu)[cnt]; + } if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { ev->pmu->read(ev); per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count); @@ -101,6 +107,9 @@ static void gator_events_perf_pmu_online_dispatch(int cpu) if (per_cpu(pevent, cpu)[cnt] != NULL || per_cpu(pevent_attr, cpu)[cnt] == 0) continue; + if (pmnc_count[cnt] > 0) + continue; // skip the EBS counter + #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) per_cpu(pevent, cpu)[cnt] = perf_event_create_kernel_counter(per_cpu(pevent_attr, cpu)[cnt], cpu, 0, dummy_handler); #else @@ -203,14 +212,19 @@ static int gator_events_perf_pmu_read(int **buffer) { int cnt, delta, len = 0; int cpu = smp_processor_id(); + struct perf_event * ev; for (cnt = 0; cnt < pmnc_counters; cnt++) { - struct perf_event * ev = per_cpu(pevent, cpu)[cnt]; + if (pmnc_count[cnt] > 0) { + ev = per_cpu(pevent_ebs, cpu); // special case for EBS + } else { + ev = per_cpu(pevent, cpu)[cnt]; + } if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { ev->pmu->read(ev); per_cpu(perfCurr, cpu)[cnt] = local64_read(&ev->count); delta = per_cpu(perfCurr, cpu)[cnt] - per_cpu(perfPrev, cpu)[cnt]; - if (delta != per_cpu(perfPrevDelta, cpu)[cnt]) { + if (delta != 0 || delta != per_cpu(perfPrevDelta, cpu)[cnt]) { per_cpu(perfPrevDelta, cpu)[cnt] = delta; per_cpu(perfPrev, cpu)[cnt] = per_cpu(perfCurr, cpu)[cnt]; per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; diff --git a/driver/gator_fs.c b/driver/gator_fs.c index 39adfbe..81b0d5b 100644 --- a/driver/gator_fs.c +++ b/driver/gator_fs.c @@ -251,7 +251,6 @@ static int gatorfs_fill_super(struct super_block *sb, void *data, int silent) gator_op_create_files(sb, root_dentry); - // FIXME: verify kill_litter_super removes our dentries return 0; } diff --git a/driver/gator_main.c b/driver/gator_main.c index 7d48812..db728ad 100644 --- a/driver/gator_main.c +++ b/driver/gator_main.c @@ -7,7 +7,7 @@ * */ -static unsigned long gator_protocol_version = 9; +static unsigned long gator_protocol_version = 10; #include <linux/slab.h> #include <linux/cpu.h> @@ -108,8 +108,12 @@ static DEFINE_MUTEX(start_mutex); static DEFINE_MUTEX(gator_buffer_mutex); bool event_based_sampling; +#if defined(__arm__) && (GATOR_PERF_PMU_SUPPORT) +DEFINE_PER_CPU(struct perf_event *, pevent_ebs); +#endif static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); +static struct timer_list gator_buffer_wake_up_timer; static LIST_HEAD(gator_events); /****************************************************************************** @@ -163,6 +167,11 @@ u32 gator_cpuid(void) } #endif +static void gator_buffer_wake_up(unsigned long data) +{ + wake_up(&gator_buffer_wait); +} + /****************************************************************************** * Commit interface ******************************************************************************/ @@ -283,7 +292,9 @@ 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); + + // had to delay scheduling work as attempting to schedule work during the context switch is illegal in kernel versions 3.5 and greater + mod_timer(&gator_buffer_wake_up_timer, jiffies + 1); } static void buffer_check(int cpu, int buftype) @@ -682,11 +693,6 @@ 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(); @@ -696,6 +702,11 @@ static void gator_stop(void) // 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(); + + // stop all events + list_for_each_entry(gi, &gator_events, list) + if (gi->stop) + gi->stop(); } /****************************************************************************** @@ -1069,11 +1080,14 @@ static int __init gator_module_init(void) return -1; } + setup_timer(&gator_buffer_wake_up_timer, gator_buffer_wake_up, 0); + return 0; } static void __exit gator_module_exit(void) { + del_timer_sync(&gator_buffer_wake_up_timer); tracepoint_synchronize_unregister(); gatorfs_unregister(); } diff --git a/driver/gator_trace_gpu.c b/driver/gator_trace_gpu.c index 921932c..d053987 100644 --- a/driver/gator_trace_gpu.c +++ b/driver/gator_trace_gpu.c @@ -18,11 +18,19 @@ #endif #include "gator_trace_gpu.h" -#define ACTIVITY_START 1 -#define ACTIVITY_STOP 2 +/* + * Taken from MALI_PROFILING_EVENT_TYPE_* items in Mali DDK. + */ +#define EVENT_TYPE_SINGLE 0 +#define EVENT_TYPE_START 1 +#define EVENT_TYPE_STOP 2 +#define EVENT_TYPE_SUSPEND 3 +#define EVENT_TYPE_RESUME 4 + /* Note whether tracepoints have been registered */ -static int mali_trace_registered; +static int mali_timeline_trace_registered; +static int mali_job_slots_trace_registered; static int gpu_trace_registered; #define GPU_START 1 @@ -32,42 +40,107 @@ static int gpu_trace_registered; #define GPU_UNIT_FP 2 #define GPU_UNIT_CL 3 -#ifdef MALI_SUPPORT +#define MALI_400 (0x0b07) +#define MALI_T6xx (0x0056) + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) +#include "gator_events_mali_400.h" -enum components { - COMPONENT_VP0 = 1, - COMPONENT_FP0 = 5, - COMPONENT_FP1, - COMPONENT_FP2, - COMPONENT_FP3, - COMPONENT_FP4, - COMPONENT_FP5, - COMPONENT_FP6, - COMPONENT_FP7, +/* + * Taken from MALI_PROFILING_EVENT_CHANNEL_* in Mali DDK. + */ +enum { + EVENT_CHANNEL_SOFTWARE = 0, + EVENT_CHANNEL_VP0 = 1, + EVENT_CHANNEL_FP0 = 5, + EVENT_CHANNEL_FP1, + EVENT_CHANNEL_FP2, + EVENT_CHANNEL_FP3, + EVENT_CHANNEL_FP4, + EVENT_CHANNEL_FP5, + EVENT_CHANNEL_FP6, + EVENT_CHANNEL_FP7, + EVENT_CHANNEL_GPU = 21 +}; + +/** + * These events are applicable when the type MALI_PROFILING_EVENT_TYPE_SINGLE is used from the GPU channel + */ +enum { + EVENT_REASON_SINGLE_GPU_NONE = 0, + EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE = 1, }; + 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; - int tgid = 0, pid = 0; // do as much work as possible before disabling interrupts component = (event_id >> 16) & 0xFF; // component is an 8-bit field state = (event_id >> 24) & 0xF; // state is a 4-bit field - 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; - if (state == ACTIVITY_START) { - tgid = d0; - pid = d1; + switch (state) { + case EVENT_TYPE_START: + if (component == EVENT_CHANNEL_VP0) { + /* tgid = d0; pid = d1; */ + marshal_sched_gpu(GPU_START, GPU_UNIT_VP, 0, d0, d1); + } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { + /* tgid = d0; pid = d1; */ + marshal_sched_gpu(GPU_START, GPU_UNIT_FP, component - EVENT_CHANNEL_FP0, d0, d1); + } + break; + + case EVENT_TYPE_STOP: + if (component == EVENT_CHANNEL_VP0) { + marshal_sched_gpu(GPU_STOP, GPU_UNIT_VP, 0, 0, 0); + } else if (component >= EVENT_CHANNEL_FP0 && component <= EVENT_CHANNEL_FP7) { + marshal_sched_gpu(GPU_STOP, GPU_UNIT_FP, component - EVENT_CHANNEL_FP0, 0, 0); + } + break; + + case EVENT_TYPE_SINGLE: + if (component == EVENT_CHANNEL_GPU) { + unsigned int reason = (event_id & 0xffff); + + if(reason == EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE) { + gator_events_mali_log_dvfs_event(d0, d1); } + } + break; - marshal_sched_gpu(type, unit, core, tgid, pid); - } - } + default: + break; + } +} +#endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) +GATOR_DEFINE_PROBE(mali_job_slots_event, TP_PROTO(unsigned int event_id, unsigned int tgid, unsigned int pid)) +{ + unsigned int component, state, type, unit = 0; + + component = (event_id >> 16) & 0xFF; // component is an 8-bit field + state = (event_id >> 24) & 0xF; // state is a 4-bit field + type = (state == EVENT_TYPE_START) ? GPU_START : GPU_STOP; + + switch (component) + { + case 0: + unit = GPU_UNIT_FP; + break; + case 1: + unit = GPU_UNIT_VP; + break; + case 2: + unit = GPU_UNIT_CL; + break; + } + + if (unit != 0) + { + marshal_sched_gpu(type, unit, 0, tgid, (pid != 0 ? pid : tgid)); + } } #endif @@ -88,15 +161,21 @@ int gator_trace_gpu_start(void) * Absence of gpu trace points is not an error */ - gpu_trace_registered = mali_trace_registered = 0; + gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; -#ifdef MALI_SUPPORT +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) if (!GATOR_REGISTER_TRACE(mali_timeline_event)) { - mali_trace_registered = 1; + mali_timeline_trace_registered = 1; } #endif - if (!mali_trace_registered) { +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + if (!GATOR_REGISTER_TRACE(mali_job_slots_event)) { + mali_job_slots_trace_registered = 1; + } +#endif + + if (!mali_timeline_trace_registered) { if (GATOR_REGISTER_TRACE(gpu_activity_start)) { return 0; } @@ -112,15 +191,22 @@ int gator_trace_gpu_start(void) void gator_trace_gpu_stop(void) { -#ifdef MALI_SUPPORT - if (mali_trace_registered) { +#if defined(MALI_SUPPORT) && (MALI_SUPPORT != MALI_T6xx) + if (mali_timeline_trace_registered) { GATOR_UNREGISTER_TRACE(mali_timeline_event); } #endif + +#if defined(MALI_SUPPORT) && (MALI_SUPPORT == MALI_T6xx) + if (mali_job_slots_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_job_slots_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; + gpu_trace_registered = mali_timeline_trace_registered = mali_job_slots_trace_registered = 0; } diff --git a/driver/mali_t6xx.mk b/driver/mali_t6xx.mk new file mode 100644 index 0000000..2e51670 --- /dev/null +++ b/driver/mali_t6xx.mk @@ -0,0 +1,24 @@ +# Defines for Mali-T6xx driver +EXTRA_CFLAGS += -DMALI_USE_UMP=1 \ + -DMALI_LICENSE_IS_GPL=1 \ + -DMALI_BASE_TRACK_MEMLEAK=0 \ + -DMALI_DEBUG=0 \ + -DMALI_ERROR_INJECT_ON=0 \ + -DMALI_CUSTOMER_RELEASE=1 \ + -DMALI_UNIT_TEST=0 \ + -DMALI_BACKEND_KERNEL=1 \ + -DMALI_NO_MALI=0 + +KBASE_DIR = $(DDK_DIR)/kernel/drivers/gpu/arm/t6xx/kbase +OSK_DIR = $(DDK_DIR)/kernel/drivers/gpu/arm/t6xx/kbase/osk +UMP_DIR = $(DDK_DIR)/kernel/drivers/gpu/arm/ump + +# Include directories in the DDK +EXTRA_CFLAGS += -I$(DDK_DIR) \ + -I$(KBASE_DIR)/.. \ + -I$(OSK_DIR)/.. \ + -I$(UMP_DIR)/.. \ + -I$(KBASE_DIR)/osk/src/linux/include \ + -I$(KBASE_DIR)/platform_dummy \ + -I$(KBASE_DIR)/src + |