| /* |
| * Copyright (C) 2019 MediaTek Inc. |
| * |
| * 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. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <asm/page.h> |
| #include "interface.h" |
| #include "met_drv.h" |
| |
| #ifdef CONFIG_GPU_TRACEPOINTS |
| #include <trace/events/gpu.h> |
| |
| #define show_secs_from_ns(ns) \ |
| ({ \ |
| u64 t = ns + (NSEC_PER_USEC / 2); \ |
| do_div(t, NSEC_PER_SEC); \ |
| t; \ |
| }) |
| |
| #define show_usecs_from_ns(ns) \ |
| ({ \ |
| u64 t = ns + (NSEC_PER_USEC / 2) ; \ |
| u32 rem; \ |
| do_div(t, NSEC_PER_USEC); \ |
| rem = do_div(t, USEC_PER_SEC); \ |
| }) |
| |
| static int event_gpu_registered; |
| static int event_gpu_enabled; |
| |
| noinline void gpu_sched_switch(const char *gpu_name, u64 timestamp, |
| u32 next_ctx_id, s32 next_prio, u32 next_job_id) |
| { |
| MET_TRACE("gpu_name=%s ts=%llu.%06lu next_ctx_id=%lu next_prio=%ld next_job_id=%lu\n", |
| gpu_name, |
| (unsigned long long)show_secs_from_ns(timestamp), |
| (unsigned long)show_usecs_from_ns(timestamp), |
| (unsigned long)next_ctx_id, (long)next_prio, (unsigned long)next_job_id); |
| } |
| |
| MET_DEFINE_PROBE(gpu_sched_switch, TP_PROTO(const char *gpu_name, u64 timestamp, |
| u32 next_ctx_id, s32 next_prio, u32 next_job_id)) |
| { |
| gpu_sched_switch(gpu_name, timestamp, next_ctx_id, next_prio, next_job_id); |
| } |
| |
| noinline void gpu_job_enqueue(u32 ctx_id, u32 job_id, const char *type) |
| { |
| MET_TRACE("ctx_id=%lu job_id=%lu type=%s", |
| (unsigned long)ctx_id, (unsigned long)job_id, type); |
| } |
| |
| MET_DEFINE_PROBE(gpu_job_enqueue, TP_PROTO(u32 ctx_id, u32 job_id, const char *type)) |
| { |
| gpu_job_enqueue(ctx_id, job_id, type); |
| } |
| #endif |
| |
| |
| #ifdef MET_EVENT_POWER_SUPPORT |
| #include "met_power.h" |
| #include "met_kernel_symbol.h" |
| |
| static int event_power_registered; |
| static int event_power_enabled; |
| |
| const char * |
| met_trace_print_symbols_seq(char* pclass_name, unsigned long val, |
| const struct trace_print_flags *symbol_array) |
| { |
| int i; |
| size_t new_fsize=0; |
| char _buf[32]; |
| const char *ret = pclass_name; |
| |
| for (i = 0; symbol_array[i].name; i++) { |
| |
| if (val != symbol_array[i].mask) |
| continue; |
| |
| new_fsize = sprintf(pclass_name, symbol_array[i].name, strlen(symbol_array[i].name)); |
| break; |
| } |
| |
| if (new_fsize == 0) { |
| snprintf(_buf, 32, "0x%lx", val); |
| new_fsize = sprintf(pclass_name, _buf, strlen(_buf)); |
| } |
| |
| return ret; |
| } |
| |
| #define __print_symbolic(pclass_name, value, symbol_array...) \ |
| ({ \ |
| static const struct trace_print_flags symbols[] = \ |
| { symbol_array, { -1, NULL }}; \ |
| met_trace_print_symbols_seq(pclass_name, value, symbols); \ |
| }) |
| |
| #ifdef pm_qos_update_request |
| #undef pm_qos_update_request |
| #endif |
| void pm_qos_update_request(int pm_qos_class, s32 value, char *owner) |
| { |
| char class_name[64]; |
| MET_TRACE("pm_qos_class=%s value=%d owner=%s\n", |
| __print_symbolic(class_name, pm_qos_class, |
| { _PM_QOS_CPU_DMA_LATENCY, "CPU_DMA_LATENCY" }, |
| { _PM_QOS_NETWORK_LATENCY, "NETWORK_LATENCY" }, |
| { _PM_QOS_NETWORK_THROUGHPUT, "NETWORK_THROUGHPUT" }), |
| value, owner); |
| } |
| //#endif |
| |
| #ifdef pm_qos_update_target |
| #undef pm_qos_update_target |
| #endif |
| void pm_qos_update_target(unsigned int action, int prev_value, int curr_value) |
| { |
| char class_name[64]; |
| |
| MET_TRACE("action=%s prev_value=%d curr_value=%d\n", |
| __print_symbolic(class_name, action, |
| { _PM_QOS_ADD_REQ, "ADD_REQ" }, |
| { _PM_QOS_UPDATE_REQ, "UPDATE_REQ" }, |
| { _PM_QOS_REMOVE_REQ, "REMOVE_REQ" }), |
| prev_value, curr_value); |
| } |
| #endif |
| //#endif |
| |
| static int reset_driver_stat(void) |
| { |
| #ifdef CONFIG_GPU_TRACEPOINTS |
| event_gpu_enabled = 0; |
| #endif |
| #ifdef MET_EVENT_POWER_SUPPORT |
| event_power_enabled = 0; |
| #endif |
| |
| met_trace_event.mode = 0; |
| return 0; |
| } |
| |
| |
| |
| static void met_event_start(void) |
| { |
| #ifdef CONFIG_GPU_TRACEPOINTS |
| /* register trace event for gpu */ |
| do { |
| if (!event_gpu_enabled) |
| break; |
| if (MET_REGISTER_TRACE(gpu_sched_switch)) { |
| pr_debug("can not register callback of gpu_sched_switch\n"); |
| break; |
| } |
| if (MET_REGISTER_TRACE(gpu_job_enqueue)) { |
| pr_debug("can not register callback of gpu_job_enqueue\n"); |
| MET_UNREGISTER_TRACE(gpu_sched_switch); |
| break; |
| } |
| event_gpu_registered = 1; |
| } while (0); |
| #endif |
| |
| #ifdef MET_EVENT_POWER_SUPPORT |
| /* register trace event for power */ |
| do { |
| if (!event_power_enabled) |
| break; |
| if (met_reg_event_power_symbol) |
| if (met_reg_event_power_symbol()) { |
| pr_debug("can not register callback of met_reg_event_power\n"); |
| break; |
| } |
| event_power_registered = 1; |
| } while (0); |
| #endif |
| |
| } |
| |
| static void met_event_stop(void) |
| { |
| #ifdef CONFIG_GPU_TRACEPOINTS |
| /* unregister trace event for gpu */ |
| if (event_gpu_registered) { |
| MET_UNREGISTER_TRACE(gpu_job_enqueue); |
| MET_UNREGISTER_TRACE(gpu_sched_switch); |
| event_gpu_registered = 0; |
| } |
| #endif |
| |
| #ifdef MET_EVENT_POWER_SUPPORT |
| /* unregister trace event for power */ |
| if (event_power_registered) { |
| if (met_unreg_event_power_symbol) |
| met_unreg_event_power_symbol(); |
| event_power_registered = 0; |
| } |
| #endif |
| } |
| |
| static int met_event_process_argument(const char *arg, int len) |
| { |
| int ret = -1; |
| |
| #ifdef CONFIG_GPU_TRACEPOINTS |
| if (strcasecmp(arg, "gpu") == 0) { |
| event_gpu_enabled = 1; |
| met_trace_event.mode = 1; |
| ret = 0; |
| } |
| #endif |
| #ifdef MET_EVENT_POWER_SUPPORT |
| if (strcasecmp(arg, "power") == 0) { |
| event_power_enabled = 1; |
| met_trace_event.mode = 1; |
| ret = 0; |
| } |
| #endif |
| return ret; |
| } |
| |
| static const char help[] = "\b" |
| #ifdef CONFIG_GPU_TRACEPOINTS |
| " --event=gpu output gpu trace events\n" |
| #endif |
| #ifdef MET_EVENT_POWER_SUPPORT |
| " --event=power output pmqos trace events\n" |
| #endif |
| ; |
| |
| static int met_event_print_help(char *buf, int len) |
| { |
| return snprintf(buf, PAGE_SIZE, help); |
| } |
| |
| static const char header[] = |
| "met-info [000] 0.0: met_ftrace_event:" |
| #ifdef CONFIG_GPU_TRACEPOINTS |
| " gpu:gpu_sched_switch gpu:gpu_job_enqueue" |
| #endif |
| #ifdef MET_EVENT_POWER_SUPPORT |
| " power:pm_qos_update_request power:pm_qos_update_target" |
| #endif |
| "\n"; |
| |
| static int met_event_print_header(char *buf, int len) |
| { |
| int ret; |
| |
| ret = snprintf(buf, PAGE_SIZE, header); |
| return ret; |
| } |
| |
| struct metdevice met_trace_event = { |
| .name = "event", |
| .type = MET_TYPE_PMU, |
| .start = met_event_start, |
| .stop = met_event_stop, |
| .reset = reset_driver_stat, |
| .process_argument = met_event_process_argument, |
| .print_help = met_event_print_help, |
| .print_header = met_event_print_header, |
| }; |