Path: blob/master/drivers/accel/habanalabs/common/state_dump.c
26450 views
// SPDX-License-Identifier: GPL-2.012/*3* Copyright 2021 HabanaLabs, Ltd.4* All Rights Reserved.5*/67#include <linux/vmalloc.h>8#include <uapi/drm/habanalabs_accel.h>9#include "habanalabs.h"1011/**12* hl_format_as_binary - helper function, format an integer as binary13* using supplied scratch buffer14* @buf: the buffer to use15* @buf_len: buffer capacity16* @n: number to format17*18* Returns pointer to buffer19*/20char *hl_format_as_binary(char *buf, size_t buf_len, u32 n)21{22int i;23u32 bit;24bool leading0 = true;25char *wrptr = buf;2627if (buf_len > 0 && buf_len < 3) {28*wrptr = '\0';29return buf;30}3132wrptr[0] = '0';33wrptr[1] = 'b';34wrptr += 2;35/* Remove 3 characters from length for '0b' and '\0' termination */36buf_len -= 3;3738for (i = 0; i < sizeof(n) * BITS_PER_BYTE && buf_len; ++i, n <<= 1) {39/* Writing bit calculation in one line would cause a false40* positive static code analysis error, so splitting.41*/42bit = n & (1 << (sizeof(n) * BITS_PER_BYTE - 1));43bit = !!bit;44leading0 &= !bit;45if (!leading0) {46*wrptr = '0' + bit;47++wrptr;48}49}5051*wrptr = '\0';5253return buf;54}5556/**57* resize_to_fit - helper function, resize buffer to fit given amount of data58* @buf: destination buffer double pointer59* @size: pointer to the size container60* @desired_size: size the buffer must contain61*62* Returns 0 on success or error code on failure.63* On success, the size of buffer is at least desired_size. Buffer is allocated64* via vmalloc and must be freed with vfree.65*/66static int resize_to_fit(char **buf, size_t *size, size_t desired_size)67{68char *resized_buf;69size_t new_size;7071if (*size >= desired_size)72return 0;7374/* Not enough space to print all, have to resize */75new_size = max_t(size_t, PAGE_SIZE, round_up(desired_size, PAGE_SIZE));76resized_buf = vmalloc(new_size);77if (!resized_buf)78return -ENOMEM;79memcpy(resized_buf, *buf, *size);80vfree(*buf);81*buf = resized_buf;82*size = new_size;8384return 1;85}8687/**88* hl_snprintf_resize() - print formatted data to buffer, resize as needed89* @buf: buffer double pointer, to be written to and resized, must be either90* NULL or allocated with vmalloc.91* @size: current size of the buffer92* @offset: current offset to write to93* @format: format of the data94*95* This function will write formatted data into the buffer. If buffer is not96* large enough, it will be resized using vmalloc. Size may be modified if the97* buffer was resized, offset will be advanced by the number of bytes written98* not including the terminating character99*100* Returns 0 on success or error code on failure101*102* Note that the buffer has to be manually released using vfree.103*/104int hl_snprintf_resize(char **buf, size_t *size, size_t *offset,105const char *format, ...)106{107va_list args;108size_t length;109int rc;110111if (*buf == NULL && (*size != 0 || *offset != 0))112return -EINVAL;113114va_start(args, format);115length = vsnprintf(*buf + *offset, *size - *offset, format, args);116va_end(args);117118rc = resize_to_fit(buf, size, *offset + length + 1);119if (rc < 0)120return rc;121else if (rc > 0) {122/* Resize was needed, write again */123va_start(args, format);124length = vsnprintf(*buf + *offset, *size - *offset, format,125args);126va_end(args);127}128129*offset += length;130131return 0;132}133134/**135* hl_sync_engine_to_string - convert engine type enum to string literal136* @engine_type: engine type (TPC/MME/DMA)137*138* Return the resolved string literal139*/140const char *hl_sync_engine_to_string(enum hl_sync_engine_type engine_type)141{142switch (engine_type) {143case ENGINE_DMA:144return "DMA";145case ENGINE_MME:146return "MME";147case ENGINE_TPC:148return "TPC";149}150return "Invalid Engine Type";151}152153/**154* hl_print_resize_sync_engine - helper function, format engine name and ID155* using hl_snprintf_resize156* @buf: destination buffer double pointer to be used with hl_snprintf_resize157* @size: pointer to the size container158* @offset: pointer to the offset container159* @engine_type: engine type (TPC/MME/DMA)160* @engine_id: engine numerical id161*162* Returns 0 on success or error code on failure163*/164static int hl_print_resize_sync_engine(char **buf, size_t *size, size_t *offset,165enum hl_sync_engine_type engine_type,166u32 engine_id)167{168return hl_snprintf_resize(buf, size, offset, "%s%u",169hl_sync_engine_to_string(engine_type), engine_id);170}171172/**173* hl_state_dump_get_sync_name - transform sync object id to name if available174* @hdev: pointer to the device175* @sync_id: sync object id176*177* Returns a name literal or NULL if not resolved.178* Note: returning NULL shall not be considered as a failure, as not all179* sync objects are named.180*/181const char *hl_state_dump_get_sync_name(struct hl_device *hdev, u32 sync_id)182{183struct hl_state_dump_specs *sds = &hdev->state_dump_specs;184struct hl_hw_obj_name_entry *entry;185186hash_for_each_possible(sds->so_id_to_str_tb, entry,187node, sync_id)188if (sync_id == entry->id)189return entry->name;190191return NULL;192}193194/**195* hl_state_dump_get_monitor_name - transform monitor object dump to monitor196* name if available197* @hdev: pointer to the device198* @mon: monitor state dump199*200* Returns a name literal or NULL if not resolved.201* Note: returning NULL shall not be considered as a failure, as not all202* monitors are named.203*/204const char *hl_state_dump_get_monitor_name(struct hl_device *hdev,205struct hl_mon_state_dump *mon)206{207struct hl_state_dump_specs *sds = &hdev->state_dump_specs;208struct hl_hw_obj_name_entry *entry;209210hash_for_each_possible(sds->monitor_id_to_str_tb,211entry, node, mon->id)212if (mon->id == entry->id)213return entry->name;214215return NULL;216}217218/**219* hl_state_dump_free_sync_to_engine_map - free sync object to engine map220* @map: sync object to engine map221*222* Note: generic free implementation, the allocation is implemented per ASIC.223*/224void hl_state_dump_free_sync_to_engine_map(struct hl_sync_to_engine_map *map)225{226struct hl_sync_to_engine_map_entry *entry;227struct hlist_node *tmp_node;228int i;229230hash_for_each_safe(map->tb, i, tmp_node, entry, node) {231hash_del(&entry->node);232kfree(entry);233}234}235236/**237* hl_state_dump_get_sync_to_engine - transform sync_id to238* hl_sync_to_engine_map_entry if available for current id239* @map: sync object to engine map240* @sync_id: sync object id241*242* Returns the translation entry if found or NULL if not.243* Note, returned NULL shall not be considered as a failure as the map244* does not cover all possible, it is a best effort sync ids.245*/246static struct hl_sync_to_engine_map_entry *247hl_state_dump_get_sync_to_engine(struct hl_sync_to_engine_map *map, u32 sync_id)248{249struct hl_sync_to_engine_map_entry *entry;250251hash_for_each_possible(map->tb, entry, node, sync_id)252if (entry->sync_id == sync_id)253return entry;254return NULL;255}256257/**258* hl_state_dump_read_sync_objects - read sync objects array259* @hdev: pointer to the device260* @index: sync manager block index starting with E_N261*262* Returns array of size SP_SYNC_OBJ_AMOUNT on success or NULL on failure263*/264static u32 *hl_state_dump_read_sync_objects(struct hl_device *hdev, u32 index)265{266struct hl_state_dump_specs *sds = &hdev->state_dump_specs;267u32 *sync_objects;268s64 base_addr; /* Base addr can be negative */269int i;270271base_addr = sds->props[SP_SYNC_OBJ_BASE_ADDR] +272sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index;273274sync_objects = vmalloc(sds->props[SP_SYNC_OBJ_AMOUNT] * sizeof(u32));275if (!sync_objects)276return NULL;277278for (i = 0; i < sds->props[SP_SYNC_OBJ_AMOUNT]; ++i)279sync_objects[i] = RREG32(base_addr + i * sizeof(u32));280281return sync_objects;282}283284/**285* hl_state_dump_free_sync_objects - free sync objects array allocated by286* hl_state_dump_read_sync_objects287* @sync_objects: sync objects array288*/289static void hl_state_dump_free_sync_objects(u32 *sync_objects)290{291vfree(sync_objects);292}293294295/**296* hl_state_dump_print_syncs_single_block - print active sync objects on a297* single block298* @hdev: pointer to the device299* @index: sync manager block index starting with E_N300* @buf: destination buffer double pointer to be used with hl_snprintf_resize301* @size: pointer to the size container302* @offset: pointer to the offset container303* @map: sync engines names map304*305* Returns 0 on success or error code on failure306*/307static int308hl_state_dump_print_syncs_single_block(struct hl_device *hdev, u32 index,309char **buf, size_t *size, size_t *offset,310struct hl_sync_to_engine_map *map)311{312struct hl_state_dump_specs *sds = &hdev->state_dump_specs;313const char *sync_name;314u32 *sync_objects = NULL;315int rc = 0, i;316317if (sds->sync_namager_names) {318rc = hl_snprintf_resize(319buf, size, offset, "%s\n",320sds->sync_namager_names[index]);321if (rc)322goto out;323}324325sync_objects = hl_state_dump_read_sync_objects(hdev, index);326if (!sync_objects) {327rc = -ENOMEM;328goto out;329}330331for (i = 0; i < sds->props[SP_SYNC_OBJ_AMOUNT]; ++i) {332struct hl_sync_to_engine_map_entry *entry;333u64 sync_object_addr;334335if (!sync_objects[i])336continue;337338sync_object_addr = sds->props[SP_SYNC_OBJ_BASE_ADDR] +339sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index +340i * sizeof(u32);341342rc = hl_snprintf_resize(buf, size, offset, "sync id: %u", i);343if (rc)344goto free_sync_objects;345sync_name = hl_state_dump_get_sync_name(hdev, i);346if (sync_name) {347rc = hl_snprintf_resize(buf, size, offset, " %s",348sync_name);349if (rc)350goto free_sync_objects;351}352rc = hl_snprintf_resize(buf, size, offset, ", value: %u",353sync_objects[i]);354if (rc)355goto free_sync_objects;356357/* Append engine string */358entry = hl_state_dump_get_sync_to_engine(map,359(u32)sync_object_addr);360if (entry) {361rc = hl_snprintf_resize(buf, size, offset,362", Engine: ");363if (rc)364goto free_sync_objects;365rc = hl_print_resize_sync_engine(buf, size, offset,366entry->engine_type,367entry->engine_id);368if (rc)369goto free_sync_objects;370}371372rc = hl_snprintf_resize(buf, size, offset, "\n");373if (rc)374goto free_sync_objects;375}376377free_sync_objects:378hl_state_dump_free_sync_objects(sync_objects);379out:380return rc;381}382383/**384* hl_state_dump_print_syncs - print active sync objects385* @hdev: pointer to the device386* @buf: destination buffer double pointer to be used with hl_snprintf_resize387* @size: pointer to the size container388* @offset: pointer to the offset container389*390* Returns 0 on success or error code on failure391*/392static int hl_state_dump_print_syncs(struct hl_device *hdev,393char **buf, size_t *size,394size_t *offset)395396{397struct hl_state_dump_specs *sds = &hdev->state_dump_specs;398struct hl_sync_to_engine_map *map;399u32 index;400int rc = 0;401402map = kzalloc(sizeof(*map), GFP_KERNEL);403if (!map)404return -ENOMEM;405406rc = sds->funcs.gen_sync_to_engine_map(hdev, map);407if (rc)408goto free_map_mem;409410rc = hl_snprintf_resize(buf, size, offset, "Non zero sync objects:\n");411if (rc)412goto out;413414if (sds->sync_namager_names) {415for (index = 0; sds->sync_namager_names[index]; ++index) {416rc = hl_state_dump_print_syncs_single_block(417hdev, index, buf, size, offset, map);418if (rc)419goto out;420}421} else {422for (index = 0; index < sds->props[SP_NUM_CORES]; ++index) {423rc = hl_state_dump_print_syncs_single_block(424hdev, index, buf, size, offset, map);425if (rc)426goto out;427}428}429430out:431hl_state_dump_free_sync_to_engine_map(map);432free_map_mem:433kfree(map);434435return rc;436}437438/**439* hl_state_dump_alloc_read_sm_block_monitors - read monitors for a specific440* block441* @hdev: pointer to the device442* @index: sync manager block index starting with E_N443*444* Returns an array of monitor data of size SP_MONITORS_AMOUNT or NULL445* on error446*/447static struct hl_mon_state_dump *448hl_state_dump_alloc_read_sm_block_monitors(struct hl_device *hdev, u32 index)449{450struct hl_state_dump_specs *sds = &hdev->state_dump_specs;451struct hl_mon_state_dump *monitors;452s64 base_addr; /* Base addr can be negative */453int i;454455monitors = vmalloc(sds->props[SP_MONITORS_AMOUNT] *456sizeof(struct hl_mon_state_dump));457if (!monitors)458return NULL;459460base_addr = sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index;461462for (i = 0; i < sds->props[SP_MONITORS_AMOUNT]; ++i) {463monitors[i].id = i;464monitors[i].wr_addr_low =465RREG32(base_addr + sds->props[SP_MON_OBJ_WR_ADDR_LOW] +466i * sizeof(u32));467468monitors[i].wr_addr_high =469RREG32(base_addr + sds->props[SP_MON_OBJ_WR_ADDR_HIGH] +470i * sizeof(u32));471472monitors[i].wr_data =473RREG32(base_addr + sds->props[SP_MON_OBJ_WR_DATA] +474i * sizeof(u32));475476monitors[i].arm_data =477RREG32(base_addr + sds->props[SP_MON_OBJ_ARM_DATA] +478i * sizeof(u32));479480monitors[i].status =481RREG32(base_addr + sds->props[SP_MON_OBJ_STATUS] +482i * sizeof(u32));483}484485return monitors;486}487488/**489* hl_state_dump_free_monitors - free the monitors structure490* @monitors: monitors array created with491* hl_state_dump_alloc_read_sm_block_monitors492*/493static void hl_state_dump_free_monitors(struct hl_mon_state_dump *monitors)494{495vfree(monitors);496}497498/**499* hl_state_dump_print_monitors_single_block - print active monitors on a500* single block501* @hdev: pointer to the device502* @index: sync manager block index starting with E_N503* @buf: destination buffer double pointer to be used with hl_snprintf_resize504* @size: pointer to the size container505* @offset: pointer to the offset container506*507* Returns 0 on success or error code on failure508*/509static int hl_state_dump_print_monitors_single_block(struct hl_device *hdev,510u32 index,511char **buf, size_t *size,512size_t *offset)513{514struct hl_state_dump_specs *sds = &hdev->state_dump_specs;515struct hl_mon_state_dump *monitors = NULL;516int rc = 0, i;517518if (sds->sync_namager_names) {519rc = hl_snprintf_resize(520buf, size, offset, "%s\n",521sds->sync_namager_names[index]);522if (rc)523goto out;524}525526monitors = hl_state_dump_alloc_read_sm_block_monitors(hdev, index);527if (!monitors) {528rc = -ENOMEM;529goto out;530}531532for (i = 0; i < sds->props[SP_MONITORS_AMOUNT]; ++i) {533if (!(sds->funcs.monitor_valid(&monitors[i])))534continue;535536/* Monitor is valid, dump it */537rc = sds->funcs.print_single_monitor(buf, size, offset, hdev,538&monitors[i]);539if (rc)540goto free_monitors;541542hl_snprintf_resize(buf, size, offset, "\n");543}544545free_monitors:546hl_state_dump_free_monitors(monitors);547out:548return rc;549}550551/**552* hl_state_dump_print_monitors - print active monitors553* @hdev: pointer to the device554* @buf: destination buffer double pointer to be used with hl_snprintf_resize555* @size: pointer to the size container556* @offset: pointer to the offset container557*558* Returns 0 on success or error code on failure559*/560static int hl_state_dump_print_monitors(struct hl_device *hdev,561char **buf, size_t *size,562size_t *offset)563{564struct hl_state_dump_specs *sds = &hdev->state_dump_specs;565u32 index;566int rc = 0;567568rc = hl_snprintf_resize(buf, size, offset,569"Valid (armed) monitor objects:\n");570if (rc)571goto out;572573if (sds->sync_namager_names) {574for (index = 0; sds->sync_namager_names[index]; ++index) {575rc = hl_state_dump_print_monitors_single_block(576hdev, index, buf, size, offset);577if (rc)578goto out;579}580} else {581for (index = 0; index < sds->props[SP_NUM_CORES]; ++index) {582rc = hl_state_dump_print_monitors_single_block(583hdev, index, buf, size, offset);584if (rc)585goto out;586}587}588589out:590return rc;591}592593/**594* hl_state_dump_print_engine_fences - print active fences for a specific595* engine596* @hdev: pointer to the device597* @engine_type: engine type to use598* @buf: destination buffer double pointer to be used with hl_snprintf_resize599* @size: pointer to the size container600* @offset: pointer to the offset container601*/602static int603hl_state_dump_print_engine_fences(struct hl_device *hdev,604enum hl_sync_engine_type engine_type,605char **buf, size_t *size, size_t *offset)606{607struct hl_state_dump_specs *sds = &hdev->state_dump_specs;608int rc = 0, i, n_fences;609u64 base_addr, next_fence;610611switch (engine_type) {612case ENGINE_TPC:613n_fences = sds->props[SP_NUM_OF_TPC_ENGINES];614base_addr = sds->props[SP_TPC0_CMDQ];615next_fence = sds->props[SP_NEXT_TPC];616break;617case ENGINE_MME:618n_fences = sds->props[SP_NUM_OF_MME_ENGINES];619base_addr = sds->props[SP_MME_CMDQ];620next_fence = sds->props[SP_NEXT_MME];621break;622case ENGINE_DMA:623n_fences = sds->props[SP_NUM_OF_DMA_ENGINES];624base_addr = sds->props[SP_DMA_CMDQ];625next_fence = sds->props[SP_DMA_QUEUES_OFFSET];626break;627default:628return -EINVAL;629}630for (i = 0; i < n_fences; ++i) {631rc = sds->funcs.print_fences_single_engine(632hdev,633base_addr + next_fence * i +634sds->props[SP_FENCE0_CNT_OFFSET],635base_addr + next_fence * i +636sds->props[SP_CP_STS_OFFSET],637engine_type, i, buf, size, offset);638if (rc)639goto out;640}641out:642return rc;643}644645/**646* hl_state_dump_print_fences - print active fences647* @hdev: pointer to the device648* @buf: destination buffer double pointer to be used with hl_snprintf_resize649* @size: pointer to the size container650* @offset: pointer to the offset container651*/652static int hl_state_dump_print_fences(struct hl_device *hdev, char **buf,653size_t *size, size_t *offset)654{655int rc = 0;656657rc = hl_snprintf_resize(buf, size, offset, "Valid (armed) fences:\n");658if (rc)659goto out;660661rc = hl_state_dump_print_engine_fences(hdev, ENGINE_TPC, buf, size, offset);662if (rc)663goto out;664665rc = hl_state_dump_print_engine_fences(hdev, ENGINE_MME, buf, size, offset);666if (rc)667goto out;668669rc = hl_state_dump_print_engine_fences(hdev, ENGINE_DMA, buf, size, offset);670if (rc)671goto out;672673out:674return rc;675}676677/**678* hl_state_dump() - dump system state679* @hdev: pointer to device structure680*/681int hl_state_dump(struct hl_device *hdev)682{683char *buf = NULL;684size_t offset = 0, size = 0;685int rc;686687rc = hl_snprintf_resize(&buf, &size, &offset,688"Timestamp taken on: %llu\n\n",689ktime_to_ns(ktime_get()));690if (rc)691goto err;692693rc = hl_state_dump_print_syncs(hdev, &buf, &size, &offset);694if (rc)695goto err;696697hl_snprintf_resize(&buf, &size, &offset, "\n");698699rc = hl_state_dump_print_monitors(hdev, &buf, &size, &offset);700if (rc)701goto err;702703hl_snprintf_resize(&buf, &size, &offset, "\n");704705rc = hl_state_dump_print_fences(hdev, &buf, &size, &offset);706if (rc)707goto err;708709hl_snprintf_resize(&buf, &size, &offset, "\n");710711hl_debugfs_set_state_dump(hdev, buf, size);712713return 0;714err:715vfree(buf);716return rc;717}718719720