diff options
Diffstat (limited to 'mali_kbase/csf/mali_kbase_csf_sync_debugfs.c')
-rw-r--r-- | mali_kbase/csf/mali_kbase_csf_sync_debugfs.c | 809 |
1 files changed, 13 insertions, 796 deletions
diff --git a/mali_kbase/csf/mali_kbase_csf_sync_debugfs.c b/mali_kbase/csf/mali_kbase_csf_sync_debugfs.c index 72c0b6f..e002ea4 100644 --- a/mali_kbase/csf/mali_kbase_csf_sync_debugfs.c +++ b/mali_kbase/csf/mali_kbase_csf_sync_debugfs.c @@ -18,802 +18,15 @@ * http://www.gnu.org/licenses/gpl-2.0.html. * */ - #include "mali_kbase_csf_sync_debugfs.h" -#include "mali_kbase_csf_csg_debugfs.h" -#include <mali_kbase.h> -#include <linux/seq_file.h> -#include <linux/version_compat_defs.h> - -#if IS_ENABLED(CONFIG_SYNC_FILE) -#include "mali_kbase_sync.h" -#endif - -#define CQS_UNREADABLE_LIVE_VALUE "(unavailable)" - -#define CSF_SYNC_DUMP_SIZE 256 - -/** - * kbasep_print() - Helper function to print to either debugfs file or dmesg. - * - * @kctx: The kbase context - * @file: The seq_file for printing to. This is NULL if printing to dmesg. - * @fmt: The message to print. - * @...: Arguments to format the message. - */ -__attribute__((format(__printf__, 3, 4))) static void -kbasep_print(struct kbase_context *kctx, struct seq_file *file, const char *fmt, ...) -{ - int len = 0; - char buffer[CSF_SYNC_DUMP_SIZE]; - va_list arglist; - - va_start(arglist, fmt); - len = vsnprintf(buffer, CSF_SYNC_DUMP_SIZE, fmt, arglist); - if (len <= 0) { - pr_err("message write to the buffer failed"); - goto exit; - } - - if (file) - seq_printf(file, buffer); - else - dev_warn(kctx->kbdev->dev, buffer); - -exit: - va_end(arglist); -} - -/** - * kbasep_csf_debugfs_get_cqs_live_u32() - Obtain live (u32) value for a CQS object. - * - * @kctx: The context of the queue. - * @obj_addr: Pointer to the CQS live 32-bit value. - * @live_val: Pointer to the u32 that will be set to the CQS object's current, live - * value. - * - * Return: 0 if successful or a negative error code on failure. - */ -static int kbasep_csf_debugfs_get_cqs_live_u32(struct kbase_context *kctx, u64 obj_addr, - u32 *live_val) -{ - struct kbase_vmap_struct *mapping; - u32 *const cpu_ptr = (u32 *)kbase_phy_alloc_mapping_get(kctx, obj_addr, &mapping); - - if (!cpu_ptr) - return -1; - - *live_val = *cpu_ptr; - kbase_phy_alloc_mapping_put(kctx, mapping); - return 0; -} - -/** - * kbasep_csf_debugfs_get_cqs_live_u64() - Obtain live (u64) value for a CQS object. - * - * @kctx: The context of the queue. - * @obj_addr: Pointer to the CQS live value (32 or 64-bit). - * @live_val: Pointer to the u64 that will be set to the CQS object's current, live - * value. - * - * Return: 0 if successful or a negative error code on failure. - */ -static int kbasep_csf_debugfs_get_cqs_live_u64(struct kbase_context *kctx, u64 obj_addr, - u64 *live_val) -{ - struct kbase_vmap_struct *mapping; - u64 *cpu_ptr = (u64 *)kbase_phy_alloc_mapping_get(kctx, obj_addr, &mapping); - - if (!cpu_ptr) - return -1; - - *live_val = *cpu_ptr; - kbase_phy_alloc_mapping_put(kctx, mapping); - return 0; -} - -/** - * kbasep_csf_sync_print_kcpu_fence_wait_or_signal() - Print details of a CSF SYNC Fence Wait - * or Fence Signal command, contained in a - * KCPU queue. - * - * @buffer: The buffer to write to. - * @length: The length of text in the buffer. - * @cmd: The KCPU Command to be printed. - * @cmd_name: The name of the command: indicates either a fence SIGNAL or WAIT. - */ -static void kbasep_csf_sync_print_kcpu_fence_wait_or_signal(char *buffer, int *length, - struct kbase_kcpu_command *cmd, - const char *cmd_name) -{ -#if (KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE) - struct fence *fence = NULL; -#else - struct dma_fence *fence = NULL; -#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) */ - struct kbase_kcpu_command_fence_info *fence_info; - struct kbase_sync_fence_info info; - const char *timeline_name = NULL; - bool is_signaled = false; - - fence_info = &cmd->info.fence; - if (kbase_kcpu_command_fence_has_force_signaled(fence_info)) - return; - - fence = kbase_fence_get(fence_info); - if (WARN_ON(!fence)) - return; - - kbase_sync_fence_info_get(fence, &info); - timeline_name = fence->ops->get_timeline_name(fence); - is_signaled = info.status > 0; - - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "cmd:%s obj:0x%pK live_value:0x%.8x | ", cmd_name, fence, is_signaled); - - /* Note: fence->seqno was u32 until 5.1 kernel, then u64 */ - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "timeline_name:%s timeline_context:0x%.16llx fence_seqno:0x%.16llx", - timeline_name, fence->context, (u64)fence->seqno); - - kbase_fence_put(fence); -} - -/** - * kbasep_csf_sync_print_kcpu_cqs_wait() - Print details of a CSF SYNC CQS Wait command, - * contained in a KCPU queue. - * - * @kctx: The kbase context. - * @buffer: The buffer to write to. - * @length: The length of text in the buffer. - * @cmd: The KCPU Command to be printed. - */ -static void kbasep_csf_sync_print_kcpu_cqs_wait(struct kbase_context *kctx, char *buffer, - int *length, struct kbase_kcpu_command *cmd) -{ - size_t i; - - for (i = 0; i < cmd->info.cqs_wait.nr_objs; i++) { - struct base_cqs_wait_info *cqs_obj = &cmd->info.cqs_wait.objs[i]; - - u32 live_val; - int ret = kbasep_csf_debugfs_get_cqs_live_u32(kctx, cqs_obj->addr, &live_val); - bool live_val_valid = (ret >= 0); - - *length += - snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "cmd:CQS_WAIT_OPERATION obj:0x%.16llx live_value:", cqs_obj->addr); - - if (live_val_valid) - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "0x%.16llx", (u64)live_val); - else - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - CQS_UNREADABLE_LIVE_VALUE); - - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - " | op:gt arg_value:0x%.8x", cqs_obj->val); - } -} - -/** - * kbasep_csf_sync_print_kcpu_cqs_set() - Print details of a CSF SYNC CQS - * Set command, contained in a KCPU queue. - * - * @kctx: The kbase context. - * @buffer: The buffer to write to. - * @length: The length of text in the buffer. - * @cmd: The KCPU Command to be printed. - */ -static void kbasep_csf_sync_print_kcpu_cqs_set(struct kbase_context *kctx, char *buffer, - int *length, struct kbase_kcpu_command *cmd) -{ - size_t i; - - for (i = 0; i < cmd->info.cqs_set.nr_objs; i++) { - struct base_cqs_set *cqs_obj = &cmd->info.cqs_set.objs[i]; - - u32 live_val; - int ret = kbasep_csf_debugfs_get_cqs_live_u32(kctx, cqs_obj->addr, &live_val); - bool live_val_valid = (ret >= 0); - - *length += - snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "cmd:CQS_SET_OPERATION obj:0x%.16llx live_value:", cqs_obj->addr); - - if (live_val_valid) - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "0x%.16llx", (u64)live_val); - else - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - CQS_UNREADABLE_LIVE_VALUE); - - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - " | op:add arg_value:0x%.8x", 1); - } -} - -/** - * kbasep_csf_sync_get_wait_op_name() - Print the name of a CQS Wait Operation. - * - * @op: The numerical value of operation. - * - * Return: const static pointer to the command name, or '??' if unknown. - */ -static const char *kbasep_csf_sync_get_wait_op_name(basep_cqs_wait_operation_op op) -{ - const char *string; - - switch (op) { - case BASEP_CQS_WAIT_OPERATION_LE: - string = "le"; - break; - case BASEP_CQS_WAIT_OPERATION_GT: - string = "gt"; - break; - default: - string = "??"; - break; - } - return string; -} - -/** - * kbasep_csf_sync_get_set_op_name() - Print the name of a CQS Set Operation. - * - * @op: The numerical value of operation. - * - * Return: const static pointer to the command name, or '??' if unknown. - */ -static const char *kbasep_csf_sync_get_set_op_name(basep_cqs_set_operation_op op) -{ - const char *string; - - switch (op) { - case BASEP_CQS_SET_OPERATION_ADD: - string = "add"; - break; - case BASEP_CQS_SET_OPERATION_SET: - string = "set"; - break; - default: - string = "???"; - break; - } - return string; -} - -/** - * kbasep_csf_sync_print_kcpu_cqs_wait_op() - Print details of a CSF SYNC CQS - * Wait Operation command, contained - * in a KCPU queue. - * - * @kctx: The kbase context. - * @buffer: The buffer to write to. - * @length: The length of text in the buffer. - * @cmd: The KCPU Command to be printed. - */ -static void kbasep_csf_sync_print_kcpu_cqs_wait_op(struct kbase_context *kctx, char *buffer, - int *length, struct kbase_kcpu_command *cmd) -{ - size_t i; - - for (i = 0; i < cmd->info.cqs_wait.nr_objs; i++) { - struct base_cqs_wait_operation_info *wait_op = - &cmd->info.cqs_wait_operation.objs[i]; - const char *op_name = kbasep_csf_sync_get_wait_op_name(wait_op->operation); - - u64 live_val; - int ret = kbasep_csf_debugfs_get_cqs_live_u64(kctx, wait_op->addr, &live_val); - - bool live_val_valid = (ret >= 0); - - *length += - snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "cmd:CQS_WAIT_OPERATION obj:0x%.16llx live_value:", wait_op->addr); - - if (live_val_valid) - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "0x%.16llx", live_val); - else - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - CQS_UNREADABLE_LIVE_VALUE); - - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - " | op:%s arg_value:0x%.16llx", op_name, wait_op->val); - } -} - -/** - * kbasep_csf_sync_print_kcpu_cqs_set_op() - Print details of a CSF SYNC CQS - * Set Operation command, contained - * in a KCPU queue. - * - * @kctx: The kbase context. - * @buffer: The buffer to write to. - * @length: The length of text in the buffer. - * @cmd: The KCPU Command to be printed. - */ -static void kbasep_csf_sync_print_kcpu_cqs_set_op(struct kbase_context *kctx, char *buffer, - int *length, struct kbase_kcpu_command *cmd) -{ - size_t i; - - for (i = 0; i < cmd->info.cqs_set_operation.nr_objs; i++) { - struct base_cqs_set_operation_info *set_op = &cmd->info.cqs_set_operation.objs[i]; - const char *op_name = kbasep_csf_sync_get_set_op_name( - (basep_cqs_set_operation_op)set_op->operation); - - u64 live_val; - int ret = kbasep_csf_debugfs_get_cqs_live_u64(kctx, set_op->addr, &live_val); - - bool live_val_valid = (ret >= 0); - - *length += - snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "cmd:CQS_SET_OPERATION obj:0x%.16llx live_value:", set_op->addr); - - if (live_val_valid) - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - "0x%.16llx", live_val); - else - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - CQS_UNREADABLE_LIVE_VALUE); - - *length += snprintf(buffer + *length, CSF_SYNC_DUMP_SIZE - *length, - " | op:%s arg_value:0x%.16llx", op_name, set_op->val); - } -} - -/** - * kbasep_csf_kcpu_debugfs_print_queue() - Print debug data for a KCPU queue - * - * @kctx: The kbase context. - * @file: The seq_file to print to. - * @queue: Pointer to the KCPU queue. - */ -static void kbasep_csf_sync_kcpu_debugfs_print_queue(struct kbase_context *kctx, - struct seq_file *file, - struct kbase_kcpu_command_queue *queue) -{ - char started_or_pending; - struct kbase_kcpu_command *cmd; - size_t i; - - if (WARN_ON(!queue)) - return; - - lockdep_assert_held(&kctx->csf.kcpu_queues.lock); - mutex_lock(&queue->lock); - - for (i = 0; i != queue->num_pending_cmds; ++i) { - char buffer[CSF_SYNC_DUMP_SIZE]; - int length = 0; - started_or_pending = ((i == 0) && queue->command_started) ? 'S' : 'P'; - length += snprintf(buffer, CSF_SYNC_DUMP_SIZE, "queue:KCPU-%d-%d exec:%c ", - kctx->id, queue->id, started_or_pending); - - cmd = &queue->commands[(u8)(queue->start_offset + i)]; - switch (cmd->type) { -#if IS_ENABLED(CONFIG_SYNC_FILE) - case BASE_KCPU_COMMAND_TYPE_FENCE_SIGNAL: - kbasep_csf_sync_print_kcpu_fence_wait_or_signal(buffer, &length, cmd, - "FENCE_SIGNAL"); - break; - case BASE_KCPU_COMMAND_TYPE_FENCE_WAIT: - kbasep_csf_sync_print_kcpu_fence_wait_or_signal(buffer, &length, cmd, - "FENCE_WAIT"); - break; -#endif - case BASE_KCPU_COMMAND_TYPE_CQS_WAIT: - kbasep_csf_sync_print_kcpu_cqs_wait(kctx, buffer, &length, cmd); - break; - case BASE_KCPU_COMMAND_TYPE_CQS_SET: - kbasep_csf_sync_print_kcpu_cqs_set(kctx, buffer, &length, cmd); - break; - case BASE_KCPU_COMMAND_TYPE_CQS_WAIT_OPERATION: - kbasep_csf_sync_print_kcpu_cqs_wait_op(kctx, buffer, &length, cmd); - break; - case BASE_KCPU_COMMAND_TYPE_CQS_SET_OPERATION: - kbasep_csf_sync_print_kcpu_cqs_set_op(kctx, buffer, &length, cmd); - break; - default: - length += snprintf(buffer + length, CSF_SYNC_DUMP_SIZE - length, - ", U, Unknown blocking command"); - break; - } - - length += snprintf(buffer + length, CSF_SYNC_DUMP_SIZE - length, "\n"); - kbasep_print(kctx, file, buffer); - } - - mutex_unlock(&queue->lock); -} - -int kbasep_csf_sync_kcpu_dump_locked(struct kbase_context *kctx, struct seq_file *file) -{ - unsigned long queue_idx; - - lockdep_assert_held(&kctx->csf.kcpu_queues.lock); - - kbasep_print(kctx, file, "KCPU queues for ctx %d:\n", kctx->id); - - queue_idx = find_first_bit(kctx->csf.kcpu_queues.in_use, KBASEP_MAX_KCPU_QUEUES); - - while (queue_idx < KBASEP_MAX_KCPU_QUEUES) { - kbasep_csf_sync_kcpu_debugfs_print_queue(kctx, file, - kctx->csf.kcpu_queues.array[queue_idx]); - - queue_idx = find_next_bit(kctx->csf.kcpu_queues.in_use, KBASEP_MAX_KCPU_QUEUES, - queue_idx + 1); - } - - return 0; -} - -int kbasep_csf_sync_kcpu_dump(struct kbase_context *kctx, struct seq_file *file) -{ - mutex_lock(&kctx->csf.kcpu_queues.lock); - kbasep_csf_sync_kcpu_dump_locked(kctx, file); - mutex_unlock(&kctx->csf.kcpu_queues.lock); - return 0; -} #if IS_ENABLED(CONFIG_DEBUG_FS) -/* GPU queue related values */ -#define GPU_CSF_MOVE_OPCODE ((u64)0x1) -#define GPU_CSF_MOVE32_OPCODE ((u64)0x2) -#define GPU_CSF_SYNC_ADD_OPCODE ((u64)0x25) -#define GPU_CSF_SYNC_SET_OPCODE ((u64)0x26) -#define GPU_CSF_SYNC_WAIT_OPCODE ((u64)0x27) -#define GPU_CSF_SYNC_ADD64_OPCODE ((u64)0x33) -#define GPU_CSF_SYNC_SET64_OPCODE ((u64)0x34) -#define GPU_CSF_SYNC_WAIT64_OPCODE ((u64)0x35) -#define GPU_CSF_CALL_OPCODE ((u64)0x20) - -#define MAX_NR_GPU_CALLS (5) -#define INSTR_OPCODE_MASK ((u64)0xFF << 56) -#define INSTR_OPCODE_GET(value) ((value & INSTR_OPCODE_MASK) >> 56) -#define MOVE32_IMM_MASK ((u64)0xFFFFFFFFFUL) -#define MOVE_DEST_MASK ((u64)0xFF << 48) -#define MOVE_DEST_GET(value) ((value & MOVE_DEST_MASK) >> 48) -#define MOVE_IMM_MASK ((u64)0xFFFFFFFFFFFFUL) -#define SYNC_SRC0_MASK ((u64)0xFF << 40) -#define SYNC_SRC1_MASK ((u64)0xFF << 32) -#define SYNC_SRC0_GET(value) (u8)((value & SYNC_SRC0_MASK) >> 40) -#define SYNC_SRC1_GET(value) (u8)((value & SYNC_SRC1_MASK) >> 32) -#define SYNC_WAIT_CONDITION_MASK ((u64)0xF << 28) -#define SYNC_WAIT_CONDITION_GET(value) (u8)((value & SYNC_WAIT_CONDITION_MASK) >> 28) - -/* Enumeration for types of GPU queue sync events for - * the purpose of dumping them through debugfs. - */ -enum debugfs_gpu_sync_type { - DEBUGFS_GPU_SYNC_WAIT, - DEBUGFS_GPU_SYNC_SET, - DEBUGFS_GPU_SYNC_ADD, - NUM_DEBUGFS_GPU_SYNC_TYPES -}; - -/** - * kbasep_csf_get_move_immediate_value() - Get the immediate values for sync operations - * from a MOVE instruction. - * - * @move_cmd: Raw MOVE instruction. - * @sync_addr_reg: Register identifier from SYNC_* instruction. - * @compare_val_reg: Register identifier from SYNC_* instruction. - * @sync_val: Pointer to store CQS object address for sync operation. - * @compare_val: Pointer to store compare value for sync operation. - * - * Return: True if value is obtained by checking for correct register identifier, - * or false otherwise. - */ -static bool kbasep_csf_get_move_immediate_value(u64 move_cmd, u64 sync_addr_reg, - u64 compare_val_reg, u64 *sync_val, - u64 *compare_val) -{ - u64 imm_mask; - - /* Verify MOVE instruction and get immediate mask */ - if (INSTR_OPCODE_GET(move_cmd) == GPU_CSF_MOVE32_OPCODE) - imm_mask = MOVE32_IMM_MASK; - else if (INSTR_OPCODE_GET(move_cmd) == GPU_CSF_MOVE_OPCODE) - imm_mask = MOVE_IMM_MASK; - else - /* Error return */ - return false; - - /* Verify value from MOVE instruction and assign to variable */ - if (sync_addr_reg == MOVE_DEST_GET(move_cmd)) - *sync_val = move_cmd & imm_mask; - else if (compare_val_reg == MOVE_DEST_GET(move_cmd)) - *compare_val = move_cmd & imm_mask; - else - /* Error return */ - return false; - - return true; -} - -/** kbasep_csf_read_ringbuffer_value() - Reads a u64 from the ringbuffer at a provided - * offset. - * - * @queue: Pointer to the queue. - * @ringbuff_offset: Ringbuffer offset. - * - * Return: the u64 in the ringbuffer at the desired offset. - */ -static u64 kbasep_csf_read_ringbuffer_value(struct kbase_queue *queue, u32 ringbuff_offset) -{ - u64 page_off = ringbuff_offset >> PAGE_SHIFT; - u64 offset_within_page = ringbuff_offset & ~PAGE_MASK; - struct page *page = as_page(queue->queue_reg->gpu_alloc->pages[page_off]); - u64 *ringbuffer = vmap(&page, 1, VM_MAP, pgprot_noncached(PAGE_KERNEL)); - u64 value; - - if (!ringbuffer) { - struct kbase_context *kctx = queue->kctx; - - dev_err(kctx->kbdev->dev, "%s failed to map the buffer page for read a command!", - __func__); - /* Return an alternative 0 for dumpping operation*/ - value = 0; - } else { - value = ringbuffer[offset_within_page / sizeof(u64)]; - vunmap(ringbuffer); - } - - return value; -} - -/** - * kbasep_csf_print_gpu_sync_op() - Print sync operation info for given sync command. - * - * @file: Pointer to debugfs seq_file file struct for writing output. - * @kctx: Pointer to kbase context. - * @queue: Pointer to the GPU command queue. - * @ringbuff_offset: Offset to index the ring buffer with, for the given sync command. - * (Useful for finding preceding MOVE commands) - * @sync_cmd: Entire u64 of the sync command, which has both sync address and - * comparison-value encoded in it. - * @type: Type of GPU sync command (e.g. SYNC_SET, SYNC_ADD, SYNC_WAIT). - * @is_64bit: Bool to indicate if operation is 64 bit (true) or 32 bit (false). - * @follows_wait: Bool to indicate if the operation follows at least one wait - * operation. Used to determine whether it's pending or started. - */ -static void kbasep_csf_print_gpu_sync_op(struct seq_file *file, struct kbase_context *kctx, - struct kbase_queue *queue, u32 ringbuff_offset, - u64 sync_cmd, enum debugfs_gpu_sync_type type, - bool is_64bit, bool follows_wait) -{ - u64 sync_addr = 0, compare_val = 0, live_val = 0; - u64 move_cmd; - u8 sync_addr_reg, compare_val_reg, wait_condition = 0; - int err; - - static const char *const gpu_sync_type_name[] = { "SYNC_WAIT", "SYNC_SET", "SYNC_ADD" }; - static const char *const gpu_sync_type_op[] = { - "wait", /* This should never be printed, only included to simplify indexing */ - "set", "add" - }; - - if (type >= NUM_DEBUGFS_GPU_SYNC_TYPES) { - dev_warn(kctx->kbdev->dev, "Expected GPU queue sync type is unknown!"); - return; - } - - /* We expect there to be at least 2 preceding MOVE instructions, and - * Base will always arrange for the 2 MOVE + SYNC instructions to be - * contiguously located, and is therefore never expected to be wrapped - * around the ringbuffer boundary. - */ - if (unlikely(ringbuff_offset < (2 * sizeof(u64)))) { - dev_warn(kctx->kbdev->dev, - "Unexpected wraparound detected between %s & MOVE instruction", - gpu_sync_type_name[type]); - return; - } - - /* 1. Get Register identifiers from SYNC_* instruction */ - sync_addr_reg = SYNC_SRC0_GET(sync_cmd); - compare_val_reg = SYNC_SRC1_GET(sync_cmd); - - /* 2. Get values from first MOVE command */ - ringbuff_offset -= sizeof(u64); - move_cmd = kbasep_csf_read_ringbuffer_value(queue, ringbuff_offset); - if (!kbasep_csf_get_move_immediate_value(move_cmd, sync_addr_reg, compare_val_reg, - &sync_addr, &compare_val)) - return; - - /* 3. Get values from next MOVE command */ - ringbuff_offset -= sizeof(u64); - move_cmd = kbasep_csf_read_ringbuffer_value(queue, ringbuff_offset); - if (!kbasep_csf_get_move_immediate_value(move_cmd, sync_addr_reg, compare_val_reg, - &sync_addr, &compare_val)) - return; - - /* 4. Get CQS object value */ - if (is_64bit) - err = kbasep_csf_debugfs_get_cqs_live_u64(kctx, sync_addr, &live_val); - else - err = kbasep_csf_debugfs_get_cqs_live_u32(kctx, sync_addr, (u32 *)(&live_val)); - - if (err) - return; - - /* 5. Print info */ - kbasep_print(kctx, file, "queue:GPU-%u-%u-%u exec:%c cmd:%s ", kctx->id, - queue->group->handle, queue->csi_index, - queue->enabled && !follows_wait ? 'S' : 'P', gpu_sync_type_name[type]); - - if (queue->group->csg_nr == KBASEP_CSG_NR_INVALID) - kbasep_print(kctx, file, "slot:-"); - else - kbasep_print(kctx, file, "slot:%d", (int)queue->group->csg_nr); - - kbasep_print(kctx, file, " obj:0x%.16llx live_value:0x%.16llx | ", sync_addr, live_val); - - if (type == DEBUGFS_GPU_SYNC_WAIT) { - wait_condition = SYNC_WAIT_CONDITION_GET(sync_cmd); - kbasep_print(kctx, file, "op:%s ", - kbasep_csf_sync_get_wait_op_name(wait_condition)); - } else - kbasep_print(kctx, file, "op:%s ", gpu_sync_type_op[type]); - - kbasep_print(kctx, file, "arg_value:0x%.16llx\n", compare_val); -} - -/** - * kbasep_csf_dump_active_queue_sync_info() - Print GPU command queue sync information. - * - * @file: seq_file for printing to. - * @queue: Address of a GPU command queue to examine. - * - * This function will iterate through each command in the ring buffer of the given GPU queue from - * CS_EXTRACT, and if is a SYNC_* instruction it will attempt to decode the sync operation and - * print relevant information to the debugfs file. - * This function will stop iterating once the CS_INSERT address is reached by the cursor (i.e. - * when there are no more commands to view) or a number of consumed GPU CALL commands have - * been observed. - */ -static void kbasep_csf_dump_active_queue_sync_info(struct seq_file *file, struct kbase_queue *queue) -{ - struct kbase_context *kctx; - u64 *addr; - u64 cs_extract, cs_insert, instr, cursor; - bool follows_wait = false; - int nr_calls = 0; - - if (!queue) - return; - - kctx = queue->kctx; - - addr = queue->user_io_addr; - cs_insert = addr[CS_INSERT_LO / sizeof(*addr)]; - - addr = queue->user_io_addr + PAGE_SIZE / sizeof(*addr); - cs_extract = addr[CS_EXTRACT_LO / sizeof(*addr)]; - - cursor = cs_extract; - - if (!is_power_of_2(queue->size)) { - dev_warn(kctx->kbdev->dev, "GPU queue %u size of %u not a power of 2", - queue->csi_index, queue->size); - return; - } - - while ((cursor < cs_insert) && (nr_calls < MAX_NR_GPU_CALLS)) { - bool instr_is_64_bit = false; - /* Calculate offset into ringbuffer from the absolute cursor, - * by finding the remainder of the cursor divided by the - * ringbuffer size. The ringbuffer size is guaranteed to be - * a power of 2, so the remainder can be calculated without an - * explicit modulo. queue->size - 1 is the ringbuffer mask. - */ - u32 cursor_ringbuff_offset = (u32)(cursor & (queue->size - 1)); - - /* Find instruction that cursor is currently on */ - instr = kbasep_csf_read_ringbuffer_value(queue, cursor_ringbuff_offset); - - switch (INSTR_OPCODE_GET(instr)) { - case GPU_CSF_SYNC_ADD64_OPCODE: - case GPU_CSF_SYNC_SET64_OPCODE: - case GPU_CSF_SYNC_WAIT64_OPCODE: - instr_is_64_bit = true; - break; - default: - break; - } - - switch (INSTR_OPCODE_GET(instr)) { - case GPU_CSF_SYNC_ADD_OPCODE: - case GPU_CSF_SYNC_ADD64_OPCODE: - kbasep_csf_print_gpu_sync_op(file, kctx, queue, cursor_ringbuff_offset, - instr, DEBUGFS_GPU_SYNC_ADD, instr_is_64_bit, - follows_wait); - break; - case GPU_CSF_SYNC_SET_OPCODE: - case GPU_CSF_SYNC_SET64_OPCODE: - kbasep_csf_print_gpu_sync_op(file, kctx, queue, cursor_ringbuff_offset, - instr, DEBUGFS_GPU_SYNC_SET, instr_is_64_bit, - follows_wait); - break; - case GPU_CSF_SYNC_WAIT_OPCODE: - case GPU_CSF_SYNC_WAIT64_OPCODE: - kbasep_csf_print_gpu_sync_op(file, kctx, queue, cursor_ringbuff_offset, - instr, DEBUGFS_GPU_SYNC_WAIT, instr_is_64_bit, - follows_wait); - follows_wait = true; /* Future commands will follow at least one wait */ - break; - case GPU_CSF_CALL_OPCODE: - nr_calls++; - break; - default: - /* Unrecognized command, skip past it */ - break; - } - - cursor += sizeof(u64); - } -} - -/** - * kbasep_csf_dump_active_group_sync_state() - Prints SYNC commands in all GPU queues of - * the provided queue group. - * - * @kctx: The kbase context - * @file: seq_file for printing to. - * @group: Address of a GPU command group to iterate through. - * - * This function will iterate through each queue in the provided GPU queue group and - * print its SYNC related commands. - */ -static void kbasep_csf_dump_active_group_sync_state(struct kbase_context *kctx, - struct seq_file *file, - struct kbase_queue_group *const group) -{ - unsigned int i; - - kbasep_print(kctx, file, "GPU queues for group %u (slot %d) of ctx %d_%d\n", group->handle, - group->csg_nr, kctx->tgid, kctx->id); - - for (i = 0; i < MAX_SUPPORTED_STREAMS_PER_GROUP; i++) - kbasep_csf_dump_active_queue_sync_info(file, group->bound_queues[i]); -} - -/** - * kbasep_csf_sync_gpu_dump() - Print CSF GPU queue sync info - * - * @kctx: The kbase context - * @file: The seq_file for printing to. - * - * Return: Negative error code or 0 on success. - */ -static int kbasep_csf_sync_gpu_dump(struct kbase_context *kctx, struct seq_file *file) -{ - u32 gr; - struct kbase_device *kbdev; - - if (WARN_ON(!kctx)) - return -EINVAL; - - kbdev = kctx->kbdev; - kbase_csf_scheduler_lock(kbdev); - kbase_csf_debugfs_update_active_groups_status(kbdev); - - for (gr = 0; gr < kbdev->csf.global_iface.group_num; gr++) { - struct kbase_queue_group *const group = - kbdev->csf.scheduler.csg_slots[gr].resident_group; - if (!group || group->kctx != kctx) - continue; - kbasep_csf_dump_active_group_sync_state(kctx, file, group); - } - - kbase_csf_scheduler_unlock(kbdev); - return 0; -} +#include "mali_kbase_csf_sync.h" +#include "mali_kbase_csf_util.h" +#include <mali_kbase.h> +#include <linux/seq_file.h> +#include <linux/version_compat_defs.h> /** * kbasep_csf_sync_debugfs_show() - Print CSF queue sync information @@ -825,13 +38,17 @@ static int kbasep_csf_sync_gpu_dump(struct kbase_context *kctx, struct seq_file */ static int kbasep_csf_sync_debugfs_show(struct seq_file *file, void *data) { + struct kbasep_printer *kbpr; struct kbase_context *kctx = file->private; + CSTD_UNUSED(data); - kbasep_print(kctx, file, "MALI_CSF_SYNC_DEBUGFS_VERSION: v%u\n", - MALI_CSF_SYNC_DEBUGFS_VERSION); + kbpr = kbasep_printer_file_init(file); + if (kbpr != NULL) { + kbasep_csf_sync_kcpu_dump_print(kctx, kbpr); + kbasep_csf_sync_gpu_dump_print(kctx, kbpr); + kbasep_printer_term(kbpr); + } - kbasep_csf_sync_kcpu_dump(kctx, file); - kbasep_csf_sync_gpu_dump(kctx, file); return 0; } |