diff options
Diffstat (limited to 'drivers/misc/habanalabs/common/state_dump.c')
-rw-r--r-- | drivers/misc/habanalabs/common/state_dump.c | 718 |
1 files changed, 0 insertions, 718 deletions
diff --git a/drivers/misc/habanalabs/common/state_dump.c b/drivers/misc/habanalabs/common/state_dump.c deleted file mode 100644 index 74726907c95e..000000000000 --- a/drivers/misc/habanalabs/common/state_dump.c +++ /dev/null @@ -1,718 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -/* - * Copyright 2021 HabanaLabs, Ltd. - * All Rights Reserved. - */ - -#include <linux/vmalloc.h> -#include <uapi/misc/habanalabs.h> -#include "habanalabs.h" - -/** - * hl_format_as_binary - helper function, format an integer as binary - * using supplied scratch buffer - * @buf: the buffer to use - * @buf_len: buffer capacity - * @n: number to format - * - * Returns pointer to buffer - */ -char *hl_format_as_binary(char *buf, size_t buf_len, u32 n) -{ - int i; - u32 bit; - bool leading0 = true; - char *wrptr = buf; - - if (buf_len > 0 && buf_len < 3) { - *wrptr = '\0'; - return buf; - } - - wrptr[0] = '0'; - wrptr[1] = 'b'; - wrptr += 2; - /* Remove 3 characters from length for '0b' and '\0' termination */ - buf_len -= 3; - - for (i = 0; i < sizeof(n) * BITS_PER_BYTE && buf_len; ++i, n <<= 1) { - /* Writing bit calculation in one line would cause a false - * positive static code analysis error, so splitting. - */ - bit = n & (1 << (sizeof(n) * BITS_PER_BYTE - 1)); - bit = !!bit; - leading0 &= !bit; - if (!leading0) { - *wrptr = '0' + bit; - ++wrptr; - } - } - - *wrptr = '\0'; - - return buf; -} - -/** - * resize_to_fit - helper function, resize buffer to fit given amount of data - * @buf: destination buffer double pointer - * @size: pointer to the size container - * @desired_size: size the buffer must contain - * - * Returns 0 on success or error code on failure. - * On success, the size of buffer is at least desired_size. Buffer is allocated - * via vmalloc and must be freed with vfree. - */ -static int resize_to_fit(char **buf, size_t *size, size_t desired_size) -{ - char *resized_buf; - size_t new_size; - - if (*size >= desired_size) - return 0; - - /* Not enough space to print all, have to resize */ - new_size = max_t(size_t, PAGE_SIZE, round_up(desired_size, PAGE_SIZE)); - resized_buf = vmalloc(new_size); - if (!resized_buf) - return -ENOMEM; - memcpy(resized_buf, *buf, *size); - vfree(*buf); - *buf = resized_buf; - *size = new_size; - - return 1; -} - -/** - * hl_snprintf_resize() - print formatted data to buffer, resize as needed - * @buf: buffer double pointer, to be written to and resized, must be either - * NULL or allocated with vmalloc. - * @size: current size of the buffer - * @offset: current offset to write to - * @format: format of the data - * - * This function will write formatted data into the buffer. If buffer is not - * large enough, it will be resized using vmalloc. Size may be modified if the - * buffer was resized, offset will be advanced by the number of bytes written - * not including the terminating character - * - * Returns 0 on success or error code on failure - * - * Note that the buffer has to be manually released using vfree. - */ -int hl_snprintf_resize(char **buf, size_t *size, size_t *offset, - const char *format, ...) -{ - va_list args; - size_t length; - int rc; - - if (*buf == NULL && (*size != 0 || *offset != 0)) - return -EINVAL; - - va_start(args, format); - length = vsnprintf(*buf + *offset, *size - *offset, format, args); - va_end(args); - - rc = resize_to_fit(buf, size, *offset + length + 1); - if (rc < 0) - return rc; - else if (rc > 0) { - /* Resize was needed, write again */ - va_start(args, format); - length = vsnprintf(*buf + *offset, *size - *offset, format, - args); - va_end(args); - } - - *offset += length; - - return 0; -} - -/** - * hl_sync_engine_to_string - convert engine type enum to string literal - * @engine_type: engine type (TPC/MME/DMA) - * - * Return the resolved string literal - */ -const char *hl_sync_engine_to_string(enum hl_sync_engine_type engine_type) -{ - switch (engine_type) { - case ENGINE_DMA: - return "DMA"; - case ENGINE_MME: - return "MME"; - case ENGINE_TPC: - return "TPC"; - } - return "Invalid Engine Type"; -} - -/** - * hl_print_resize_sync_engine - helper function, format engine name and ID - * using hl_snprintf_resize - * @buf: destination buffer double pointer to be used with hl_snprintf_resize - * @size: pointer to the size container - * @offset: pointer to the offset container - * @engine_type: engine type (TPC/MME/DMA) - * @engine_id: engine numerical id - * - * Returns 0 on success or error code on failure - */ -static int hl_print_resize_sync_engine(char **buf, size_t *size, size_t *offset, - enum hl_sync_engine_type engine_type, - u32 engine_id) -{ - return hl_snprintf_resize(buf, size, offset, "%s%u", - hl_sync_engine_to_string(engine_type), engine_id); -} - -/** - * hl_state_dump_get_sync_name - transform sync object id to name if available - * @hdev: pointer to the device - * @sync_id: sync object id - * - * Returns a name literal or NULL if not resolved. - * Note: returning NULL shall not be considered as a failure, as not all - * sync objects are named. - */ -const char *hl_state_dump_get_sync_name(struct hl_device *hdev, u32 sync_id) -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - struct hl_hw_obj_name_entry *entry; - - hash_for_each_possible(sds->so_id_to_str_tb, entry, - node, sync_id) - if (sync_id == entry->id) - return entry->name; - - return NULL; -} - -/** - * hl_state_dump_get_monitor_name - transform monitor object dump to monitor - * name if available - * @hdev: pointer to the device - * @mon: monitor state dump - * - * Returns a name literal or NULL if not resolved. - * Note: returning NULL shall not be considered as a failure, as not all - * monitors are named. - */ -const char *hl_state_dump_get_monitor_name(struct hl_device *hdev, - struct hl_mon_state_dump *mon) -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - struct hl_hw_obj_name_entry *entry; - - hash_for_each_possible(sds->monitor_id_to_str_tb, - entry, node, mon->id) - if (mon->id == entry->id) - return entry->name; - - return NULL; -} - -/** - * hl_state_dump_free_sync_to_engine_map - free sync object to engine map - * @map: sync object to engine map - * - * Note: generic free implementation, the allocation is implemented per ASIC. - */ -void hl_state_dump_free_sync_to_engine_map(struct hl_sync_to_engine_map *map) -{ - struct hl_sync_to_engine_map_entry *entry; - struct hlist_node *tmp_node; - int i; - - hash_for_each_safe(map->tb, i, tmp_node, entry, node) { - hash_del(&entry->node); - kfree(entry); - } -} - -/** - * hl_state_dump_get_sync_to_engine - transform sync_id to - * hl_sync_to_engine_map_entry if available for current id - * @map: sync object to engine map - * @sync_id: sync object id - * - * Returns the translation entry if found or NULL if not. - * Note, returned NULL shall not be considered as a failure as the map - * does not cover all possible, it is a best effort sync ids. - */ -static struct hl_sync_to_engine_map_entry * -hl_state_dump_get_sync_to_engine(struct hl_sync_to_engine_map *map, u32 sync_id) -{ - struct hl_sync_to_engine_map_entry *entry; - - hash_for_each_possible(map->tb, entry, node, sync_id) - if (entry->sync_id == sync_id) - return entry; - return NULL; -} - -/** - * hl_state_dump_read_sync_objects - read sync objects array - * @hdev: pointer to the device - * @index: sync manager block index starting with E_N - * - * Returns array of size SP_SYNC_OBJ_AMOUNT on success or NULL on failure - */ -static u32 *hl_state_dump_read_sync_objects(struct hl_device *hdev, u32 index) -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - u32 *sync_objects; - s64 base_addr; /* Base addr can be negative */ - int i; - - base_addr = sds->props[SP_SYNC_OBJ_BASE_ADDR] + - sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index; - - sync_objects = vmalloc(sds->props[SP_SYNC_OBJ_AMOUNT] * sizeof(u32)); - if (!sync_objects) - return NULL; - - for (i = 0; i < sds->props[SP_SYNC_OBJ_AMOUNT]; ++i) - sync_objects[i] = RREG32(base_addr + i * sizeof(u32)); - - return sync_objects; -} - -/** - * hl_state_dump_free_sync_objects - free sync objects array allocated by - * hl_state_dump_read_sync_objects - * @sync_objects: sync objects array - */ -static void hl_state_dump_free_sync_objects(u32 *sync_objects) -{ - vfree(sync_objects); -} - - -/** - * hl_state_dump_print_syncs_single_block - print active sync objects on a - * single block - * @hdev: pointer to the device - * @index: sync manager block index starting with E_N - * @buf: destination buffer double pointer to be used with hl_snprintf_resize - * @size: pointer to the size container - * @offset: pointer to the offset container - * @map: sync engines names map - * - * Returns 0 on success or error code on failure - */ -static int -hl_state_dump_print_syncs_single_block(struct hl_device *hdev, u32 index, - char **buf, size_t *size, size_t *offset, - struct hl_sync_to_engine_map *map) -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - const char *sync_name; - u32 *sync_objects = NULL; - int rc = 0, i; - - if (sds->sync_namager_names) { - rc = hl_snprintf_resize( - buf, size, offset, "%s\n", - sds->sync_namager_names[index]); - if (rc) - goto out; - } - - sync_objects = hl_state_dump_read_sync_objects(hdev, index); - if (!sync_objects) { - rc = -ENOMEM; - goto out; - } - - for (i = 0; i < sds->props[SP_SYNC_OBJ_AMOUNT]; ++i) { - struct hl_sync_to_engine_map_entry *entry; - u64 sync_object_addr; - - if (!sync_objects[i]) - continue; - - sync_object_addr = sds->props[SP_SYNC_OBJ_BASE_ADDR] + - sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index + - i * sizeof(u32); - - rc = hl_snprintf_resize(buf, size, offset, "sync id: %u", i); - if (rc) - goto free_sync_objects; - sync_name = hl_state_dump_get_sync_name(hdev, i); - if (sync_name) { - rc = hl_snprintf_resize(buf, size, offset, " %s", - sync_name); - if (rc) - goto free_sync_objects; - } - rc = hl_snprintf_resize(buf, size, offset, ", value: %u", - sync_objects[i]); - if (rc) - goto free_sync_objects; - - /* Append engine string */ - entry = hl_state_dump_get_sync_to_engine(map, - (u32)sync_object_addr); - if (entry) { - rc = hl_snprintf_resize(buf, size, offset, - ", Engine: "); - if (rc) - goto free_sync_objects; - rc = hl_print_resize_sync_engine(buf, size, offset, - entry->engine_type, - entry->engine_id); - if (rc) - goto free_sync_objects; - } - - rc = hl_snprintf_resize(buf, size, offset, "\n"); - if (rc) - goto free_sync_objects; - } - -free_sync_objects: - hl_state_dump_free_sync_objects(sync_objects); -out: - return rc; -} - -/** - * hl_state_dump_print_syncs - print active sync objects - * @hdev: pointer to the device - * @buf: destination buffer double pointer to be used with hl_snprintf_resize - * @size: pointer to the size container - * @offset: pointer to the offset container - * - * Returns 0 on success or error code on failure - */ -static int hl_state_dump_print_syncs(struct hl_device *hdev, - char **buf, size_t *size, - size_t *offset) - -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - struct hl_sync_to_engine_map *map; - u32 index; - int rc = 0; - - map = kzalloc(sizeof(*map), GFP_KERNEL); - if (!map) - return -ENOMEM; - - rc = sds->funcs.gen_sync_to_engine_map(hdev, map); - if (rc) - goto free_map_mem; - - rc = hl_snprintf_resize(buf, size, offset, "Non zero sync objects:\n"); - if (rc) - goto out; - - if (sds->sync_namager_names) { - for (index = 0; sds->sync_namager_names[index]; ++index) { - rc = hl_state_dump_print_syncs_single_block( - hdev, index, buf, size, offset, map); - if (rc) - goto out; - } - } else { - for (index = 0; index < sds->props[SP_NUM_CORES]; ++index) { - rc = hl_state_dump_print_syncs_single_block( - hdev, index, buf, size, offset, map); - if (rc) - goto out; - } - } - -out: - hl_state_dump_free_sync_to_engine_map(map); -free_map_mem: - kfree(map); - - return rc; -} - -/** - * hl_state_dump_alloc_read_sm_block_monitors - read monitors for a specific - * block - * @hdev: pointer to the device - * @index: sync manager block index starting with E_N - * - * Returns an array of monitor data of size SP_MONITORS_AMOUNT or NULL - * on error - */ -static struct hl_mon_state_dump * -hl_state_dump_alloc_read_sm_block_monitors(struct hl_device *hdev, u32 index) -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - struct hl_mon_state_dump *monitors; - s64 base_addr; /* Base addr can be negative */ - int i; - - monitors = vmalloc(sds->props[SP_MONITORS_AMOUNT] * - sizeof(struct hl_mon_state_dump)); - if (!monitors) - return NULL; - - base_addr = sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index; - - for (i = 0; i < sds->props[SP_MONITORS_AMOUNT]; ++i) { - monitors[i].id = i; - monitors[i].wr_addr_low = - RREG32(base_addr + sds->props[SP_MON_OBJ_WR_ADDR_LOW] + - i * sizeof(u32)); - - monitors[i].wr_addr_high = - RREG32(base_addr + sds->props[SP_MON_OBJ_WR_ADDR_HIGH] + - i * sizeof(u32)); - - monitors[i].wr_data = - RREG32(base_addr + sds->props[SP_MON_OBJ_WR_DATA] + - i * sizeof(u32)); - - monitors[i].arm_data = - RREG32(base_addr + sds->props[SP_MON_OBJ_ARM_DATA] + - i * sizeof(u32)); - - monitors[i].status = - RREG32(base_addr + sds->props[SP_MON_OBJ_STATUS] + - i * sizeof(u32)); - } - - return monitors; -} - -/** - * hl_state_dump_free_monitors - free the monitors structure - * @monitors: monitors array created with - * hl_state_dump_alloc_read_sm_block_monitors - */ -static void hl_state_dump_free_monitors(struct hl_mon_state_dump *monitors) -{ - vfree(monitors); -} - -/** - * hl_state_dump_print_monitors_single_block - print active monitors on a - * single block - * @hdev: pointer to the device - * @index: sync manager block index starting with E_N - * @buf: destination buffer double pointer to be used with hl_snprintf_resize - * @size: pointer to the size container - * @offset: pointer to the offset container - * - * Returns 0 on success or error code on failure - */ -static int hl_state_dump_print_monitors_single_block(struct hl_device *hdev, - u32 index, - char **buf, size_t *size, - size_t *offset) -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - struct hl_mon_state_dump *monitors = NULL; - int rc = 0, i; - - if (sds->sync_namager_names) { - rc = hl_snprintf_resize( - buf, size, offset, "%s\n", - sds->sync_namager_names[index]); - if (rc) - goto out; - } - - monitors = hl_state_dump_alloc_read_sm_block_monitors(hdev, index); - if (!monitors) { - rc = -ENOMEM; - goto out; - } - - for (i = 0; i < sds->props[SP_MONITORS_AMOUNT]; ++i) { - if (!(sds->funcs.monitor_valid(&monitors[i]))) - continue; - - /* Monitor is valid, dump it */ - rc = sds->funcs.print_single_monitor(buf, size, offset, hdev, - &monitors[i]); - if (rc) - goto free_monitors; - - hl_snprintf_resize(buf, size, offset, "\n"); - } - -free_monitors: - hl_state_dump_free_monitors(monitors); -out: - return rc; -} - -/** - * hl_state_dump_print_monitors - print active monitors - * @hdev: pointer to the device - * @buf: destination buffer double pointer to be used with hl_snprintf_resize - * @size: pointer to the size container - * @offset: pointer to the offset container - * - * Returns 0 on success or error code on failure - */ -static int hl_state_dump_print_monitors(struct hl_device *hdev, - char **buf, size_t *size, - size_t *offset) -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - u32 index; - int rc = 0; - - rc = hl_snprintf_resize(buf, size, offset, - "Valid (armed) monitor objects:\n"); - if (rc) - goto out; - - if (sds->sync_namager_names) { - for (index = 0; sds->sync_namager_names[index]; ++index) { - rc = hl_state_dump_print_monitors_single_block( - hdev, index, buf, size, offset); - if (rc) - goto out; - } - } else { - for (index = 0; index < sds->props[SP_NUM_CORES]; ++index) { - rc = hl_state_dump_print_monitors_single_block( - hdev, index, buf, size, offset); - if (rc) - goto out; - } - } - -out: - return rc; -} - -/** - * hl_state_dump_print_engine_fences - print active fences for a specific - * engine - * @hdev: pointer to the device - * @engine_type: engine type to use - * @buf: destination buffer double pointer to be used with hl_snprintf_resize - * @size: pointer to the size container - * @offset: pointer to the offset container - */ -static int -hl_state_dump_print_engine_fences(struct hl_device *hdev, - enum hl_sync_engine_type engine_type, - char **buf, size_t *size, size_t *offset) -{ - struct hl_state_dump_specs *sds = &hdev->state_dump_specs; - int rc = 0, i, n_fences; - u64 base_addr, next_fence; - - switch (engine_type) { - case ENGINE_TPC: - n_fences = sds->props[SP_NUM_OF_TPC_ENGINES]; - base_addr = sds->props[SP_TPC0_CMDQ]; - next_fence = sds->props[SP_NEXT_TPC]; - break; - case ENGINE_MME: - n_fences = sds->props[SP_NUM_OF_MME_ENGINES]; - base_addr = sds->props[SP_MME_CMDQ]; - next_fence = sds->props[SP_NEXT_MME]; - break; - case ENGINE_DMA: - n_fences = sds->props[SP_NUM_OF_DMA_ENGINES]; - base_addr = sds->props[SP_DMA_CMDQ]; - next_fence = sds->props[SP_DMA_QUEUES_OFFSET]; - break; - default: - return -EINVAL; - } - for (i = 0; i < n_fences; ++i) { - rc = sds->funcs.print_fences_single_engine( - hdev, - base_addr + next_fence * i + - sds->props[SP_FENCE0_CNT_OFFSET], - base_addr + next_fence * i + - sds->props[SP_CP_STS_OFFSET], - engine_type, i, buf, size, offset); - if (rc) - goto out; - } -out: - return rc; -} - -/** - * hl_state_dump_print_fences - print active fences - * @hdev: pointer to the device - * @buf: destination buffer double pointer to be used with hl_snprintf_resize - * @size: pointer to the size container - * @offset: pointer to the offset container - */ -static int hl_state_dump_print_fences(struct hl_device *hdev, char **buf, - size_t *size, size_t *offset) -{ - int rc = 0; - - rc = hl_snprintf_resize(buf, size, offset, "Valid (armed) fences:\n"); - if (rc) - goto out; - - rc = hl_state_dump_print_engine_fences(hdev, ENGINE_TPC, buf, size, offset); - if (rc) - goto out; - - rc = hl_state_dump_print_engine_fences(hdev, ENGINE_MME, buf, size, offset); - if (rc) - goto out; - - rc = hl_state_dump_print_engine_fences(hdev, ENGINE_DMA, buf, size, offset); - if (rc) - goto out; - -out: - return rc; -} - -/** - * hl_state_dump() - dump system state - * @hdev: pointer to device structure - */ -int hl_state_dump(struct hl_device *hdev) -{ - char *buf = NULL; - size_t offset = 0, size = 0; - int rc; - - rc = hl_snprintf_resize(&buf, &size, &offset, - "Timestamp taken on: %llu\n\n", - ktime_to_ns(ktime_get())); - if (rc) - goto err; - - rc = hl_state_dump_print_syncs(hdev, &buf, &size, &offset); - if (rc) - goto err; - - hl_snprintf_resize(&buf, &size, &offset, "\n"); - - rc = hl_state_dump_print_monitors(hdev, &buf, &size, &offset); - if (rc) - goto err; - - hl_snprintf_resize(&buf, &size, &offset, "\n"); - - rc = hl_state_dump_print_fences(hdev, &buf, &size, &offset); - if (rc) - goto err; - - hl_snprintf_resize(&buf, &size, &offset, "\n"); - - hl_debugfs_set_state_dump(hdev, buf, size); - - return 0; -err: - vfree(buf); - return rc; -} |