cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

state_dump.c (18433B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3/*
      4 * Copyright 2021 HabanaLabs, Ltd.
      5 * All Rights Reserved.
      6 */
      7
      8#include <linux/vmalloc.h>
      9#include <uapi/misc/habanalabs.h>
     10#include "habanalabs.h"
     11
     12/**
     13 * hl_format_as_binary - helper function, format an integer as binary
     14 *                       using supplied scratch buffer
     15 * @buf: the buffer to use
     16 * @buf_len: buffer capacity
     17 * @n: number to format
     18 *
     19 * Returns pointer to buffer
     20 */
     21char *hl_format_as_binary(char *buf, size_t buf_len, u32 n)
     22{
     23	int i;
     24	u32 bit;
     25	bool leading0 = true;
     26	char *wrptr = buf;
     27
     28	if (buf_len > 0 && buf_len < 3) {
     29		*wrptr = '\0';
     30		return buf;
     31	}
     32
     33	wrptr[0] = '0';
     34	wrptr[1] = 'b';
     35	wrptr += 2;
     36	/* Remove 3 characters from length for '0b' and '\0' termination */
     37	buf_len -= 3;
     38
     39	for (i = 0; i < sizeof(n) * BITS_PER_BYTE && buf_len; ++i, n <<= 1) {
     40		/* Writing bit calculation in one line would cause a false
     41		 * positive static code analysis error, so splitting.
     42		 */
     43		bit = n & (1 << (sizeof(n) * BITS_PER_BYTE - 1));
     44		bit = !!bit;
     45		leading0 &= !bit;
     46		if (!leading0) {
     47			*wrptr = '0' + bit;
     48			++wrptr;
     49		}
     50	}
     51
     52	*wrptr = '\0';
     53
     54	return buf;
     55}
     56
     57/**
     58 * resize_to_fit - helper function, resize buffer to fit given amount of data
     59 * @buf: destination buffer double pointer
     60 * @size: pointer to the size container
     61 * @desired_size: size the buffer must contain
     62 *
     63 * Returns 0 on success or error code on failure.
     64 * On success, the size of buffer is at least desired_size. Buffer is allocated
     65 * via vmalloc and must be freed with vfree.
     66 */
     67static int resize_to_fit(char **buf, size_t *size, size_t desired_size)
     68{
     69	char *resized_buf;
     70	size_t new_size;
     71
     72	if (*size >= desired_size)
     73		return 0;
     74
     75	/* Not enough space to print all, have to resize */
     76	new_size = max_t(size_t, PAGE_SIZE, round_up(desired_size, PAGE_SIZE));
     77	resized_buf = vmalloc(new_size);
     78	if (!resized_buf)
     79		return -ENOMEM;
     80	memcpy(resized_buf, *buf, *size);
     81	vfree(*buf);
     82	*buf = resized_buf;
     83	*size = new_size;
     84
     85	return 1;
     86}
     87
     88/**
     89 * hl_snprintf_resize() - print formatted data to buffer, resize as needed
     90 * @buf: buffer double pointer, to be written to and resized, must be either
     91 *       NULL or allocated with vmalloc.
     92 * @size: current size of the buffer
     93 * @offset: current offset to write to
     94 * @format: format of the data
     95 *
     96 * This function will write formatted data into the buffer. If buffer is not
     97 * large enough, it will be resized using vmalloc. Size may be modified if the
     98 * buffer was resized, offset will be advanced by the number of bytes written
     99 * not including the terminating character
    100 *
    101 * Returns 0 on success or error code on failure
    102 *
    103 * Note that the buffer has to be manually released using vfree.
    104 */
    105int hl_snprintf_resize(char **buf, size_t *size, size_t *offset,
    106			   const char *format, ...)
    107{
    108	va_list args;
    109	size_t length;
    110	int rc;
    111
    112	if (*buf == NULL && (*size != 0 || *offset != 0))
    113		return -EINVAL;
    114
    115	va_start(args, format);
    116	length = vsnprintf(*buf + *offset, *size - *offset, format, args);
    117	va_end(args);
    118
    119	rc = resize_to_fit(buf, size, *offset + length + 1);
    120	if (rc < 0)
    121		return rc;
    122	else if (rc > 0) {
    123		/* Resize was needed, write again */
    124		va_start(args, format);
    125		length = vsnprintf(*buf + *offset, *size - *offset, format,
    126				   args);
    127		va_end(args);
    128	}
    129
    130	*offset += length;
    131
    132	return 0;
    133}
    134
    135/**
    136 * hl_sync_engine_to_string - convert engine type enum to string literal
    137 * @engine_type: engine type (TPC/MME/DMA)
    138 *
    139 * Return the resolved string literal
    140 */
    141const char *hl_sync_engine_to_string(enum hl_sync_engine_type engine_type)
    142{
    143	switch (engine_type) {
    144	case ENGINE_DMA:
    145		return "DMA";
    146	case ENGINE_MME:
    147		return "MME";
    148	case ENGINE_TPC:
    149		return "TPC";
    150	}
    151	return "Invalid Engine Type";
    152}
    153
    154/**
    155 * hl_print_resize_sync_engine - helper function, format engine name and ID
    156 * using hl_snprintf_resize
    157 * @buf: destination buffer double pointer to be used with hl_snprintf_resize
    158 * @size: pointer to the size container
    159 * @offset: pointer to the offset container
    160 * @engine_type: engine type (TPC/MME/DMA)
    161 * @engine_id: engine numerical id
    162 *
    163 * Returns 0 on success or error code on failure
    164 */
    165static int hl_print_resize_sync_engine(char **buf, size_t *size, size_t *offset,
    166				enum hl_sync_engine_type engine_type,
    167				u32 engine_id)
    168{
    169	return hl_snprintf_resize(buf, size, offset, "%s%u",
    170			hl_sync_engine_to_string(engine_type), engine_id);
    171}
    172
    173/**
    174 * hl_state_dump_get_sync_name - transform sync object id to name if available
    175 * @hdev: pointer to the device
    176 * @sync_id: sync object id
    177 *
    178 * Returns a name literal or NULL if not resolved.
    179 * Note: returning NULL shall not be considered as a failure, as not all
    180 * sync objects are named.
    181 */
    182const char *hl_state_dump_get_sync_name(struct hl_device *hdev, u32 sync_id)
    183{
    184	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    185	struct hl_hw_obj_name_entry *entry;
    186
    187	hash_for_each_possible(sds->so_id_to_str_tb, entry,
    188				node, sync_id)
    189		if (sync_id == entry->id)
    190			return entry->name;
    191
    192	return NULL;
    193}
    194
    195/**
    196 * hl_state_dump_get_monitor_name - transform monitor object dump to monitor
    197 * name if available
    198 * @hdev: pointer to the device
    199 * @mon: monitor state dump
    200 *
    201 * Returns a name literal or NULL if not resolved.
    202 * Note: returning NULL shall not be considered as a failure, as not all
    203 * monitors are named.
    204 */
    205const char *hl_state_dump_get_monitor_name(struct hl_device *hdev,
    206					struct hl_mon_state_dump *mon)
    207{
    208	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    209	struct hl_hw_obj_name_entry *entry;
    210
    211	hash_for_each_possible(sds->monitor_id_to_str_tb,
    212				entry, node, mon->id)
    213		if (mon->id == entry->id)
    214			return entry->name;
    215
    216	return NULL;
    217}
    218
    219/**
    220 * hl_state_dump_free_sync_to_engine_map - free sync object to engine map
    221 * @map: sync object to engine map
    222 *
    223 * Note: generic free implementation, the allocation is implemented per ASIC.
    224 */
    225void hl_state_dump_free_sync_to_engine_map(struct hl_sync_to_engine_map *map)
    226{
    227	struct hl_sync_to_engine_map_entry *entry;
    228	struct hlist_node *tmp_node;
    229	int i;
    230
    231	hash_for_each_safe(map->tb, i, tmp_node, entry, node) {
    232		hash_del(&entry->node);
    233		kfree(entry);
    234	}
    235}
    236
    237/**
    238 * hl_state_dump_get_sync_to_engine - transform sync_id to
    239 * hl_sync_to_engine_map_entry if available for current id
    240 * @map: sync object to engine map
    241 * @sync_id: sync object id
    242 *
    243 * Returns the translation entry if found or NULL if not.
    244 * Note, returned NULL shall not be considered as a failure as the map
    245 * does not cover all possible, it is a best effort sync ids.
    246 */
    247static struct hl_sync_to_engine_map_entry *
    248hl_state_dump_get_sync_to_engine(struct hl_sync_to_engine_map *map, u32 sync_id)
    249{
    250	struct hl_sync_to_engine_map_entry *entry;
    251
    252	hash_for_each_possible(map->tb, entry, node, sync_id)
    253		if (entry->sync_id == sync_id)
    254			return entry;
    255	return NULL;
    256}
    257
    258/**
    259 * hl_state_dump_read_sync_objects - read sync objects array
    260 * @hdev: pointer to the device
    261 * @index: sync manager block index starting with E_N
    262 *
    263 * Returns array of size SP_SYNC_OBJ_AMOUNT on success or NULL on failure
    264 */
    265static u32 *hl_state_dump_read_sync_objects(struct hl_device *hdev, u32 index)
    266{
    267	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    268	u32 *sync_objects;
    269	s64 base_addr; /* Base addr can be negative */
    270	int i;
    271
    272	base_addr = sds->props[SP_SYNC_OBJ_BASE_ADDR] +
    273			sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index;
    274
    275	sync_objects = vmalloc(sds->props[SP_SYNC_OBJ_AMOUNT] * sizeof(u32));
    276	if (!sync_objects)
    277		return NULL;
    278
    279	for (i = 0; i < sds->props[SP_SYNC_OBJ_AMOUNT]; ++i)
    280		sync_objects[i] = RREG32(base_addr + i * sizeof(u32));
    281
    282	return sync_objects;
    283}
    284
    285/**
    286 * hl_state_dump_free_sync_objects - free sync objects array allocated by
    287 * hl_state_dump_read_sync_objects
    288 * @sync_objects: sync objects array
    289 */
    290static void hl_state_dump_free_sync_objects(u32 *sync_objects)
    291{
    292	vfree(sync_objects);
    293}
    294
    295
    296/**
    297 * hl_state_dump_print_syncs_single_block - print active sync objects on a
    298 * single block
    299 * @hdev: pointer to the device
    300 * @index: sync manager block index starting with E_N
    301 * @buf: destination buffer double pointer to be used with hl_snprintf_resize
    302 * @size: pointer to the size container
    303 * @offset: pointer to the offset container
    304 * @map: sync engines names map
    305 *
    306 * Returns 0 on success or error code on failure
    307 */
    308static int
    309hl_state_dump_print_syncs_single_block(struct hl_device *hdev, u32 index,
    310				char **buf, size_t *size, size_t *offset,
    311				struct hl_sync_to_engine_map *map)
    312{
    313	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    314	const char *sync_name;
    315	u32 *sync_objects = NULL;
    316	int rc = 0, i;
    317
    318	if (sds->sync_namager_names) {
    319		rc = hl_snprintf_resize(
    320			buf, size, offset, "%s\n",
    321			sds->sync_namager_names[index]);
    322		if (rc)
    323			goto out;
    324	}
    325
    326	sync_objects = hl_state_dump_read_sync_objects(hdev, index);
    327	if (!sync_objects) {
    328		rc = -ENOMEM;
    329		goto out;
    330	}
    331
    332	for (i = 0; i < sds->props[SP_SYNC_OBJ_AMOUNT]; ++i) {
    333		struct hl_sync_to_engine_map_entry *entry;
    334		u64 sync_object_addr;
    335
    336		if (!sync_objects[i])
    337			continue;
    338
    339		sync_object_addr = sds->props[SP_SYNC_OBJ_BASE_ADDR] +
    340				sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index +
    341				i * sizeof(u32);
    342
    343		rc = hl_snprintf_resize(buf, size, offset, "sync id: %u", i);
    344		if (rc)
    345			goto free_sync_objects;
    346		sync_name = hl_state_dump_get_sync_name(hdev, i);
    347		if (sync_name) {
    348			rc = hl_snprintf_resize(buf, size, offset, " %s",
    349						sync_name);
    350			if (rc)
    351				goto free_sync_objects;
    352		}
    353		rc = hl_snprintf_resize(buf, size, offset, ", value: %u",
    354					sync_objects[i]);
    355		if (rc)
    356			goto free_sync_objects;
    357
    358		/* Append engine string */
    359		entry = hl_state_dump_get_sync_to_engine(map,
    360			(u32)sync_object_addr);
    361		if (entry) {
    362			rc = hl_snprintf_resize(buf, size, offset,
    363						", Engine: ");
    364			if (rc)
    365				goto free_sync_objects;
    366			rc = hl_print_resize_sync_engine(buf, size, offset,
    367						entry->engine_type,
    368						entry->engine_id);
    369			if (rc)
    370				goto free_sync_objects;
    371		}
    372
    373		rc = hl_snprintf_resize(buf, size, offset, "\n");
    374		if (rc)
    375			goto free_sync_objects;
    376	}
    377
    378free_sync_objects:
    379	hl_state_dump_free_sync_objects(sync_objects);
    380out:
    381	return rc;
    382}
    383
    384/**
    385 * hl_state_dump_print_syncs - print active sync objects
    386 * @hdev: pointer to the device
    387 * @buf: destination buffer double pointer to be used with hl_snprintf_resize
    388 * @size: pointer to the size container
    389 * @offset: pointer to the offset container
    390 *
    391 * Returns 0 on success or error code on failure
    392 */
    393static int hl_state_dump_print_syncs(struct hl_device *hdev,
    394					char **buf, size_t *size,
    395					size_t *offset)
    396
    397{
    398	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    399	struct hl_sync_to_engine_map *map;
    400	u32 index;
    401	int rc = 0;
    402
    403	map = kzalloc(sizeof(*map), GFP_KERNEL);
    404	if (!map)
    405		return -ENOMEM;
    406
    407	rc = sds->funcs.gen_sync_to_engine_map(hdev, map);
    408	if (rc)
    409		goto free_map_mem;
    410
    411	rc = hl_snprintf_resize(buf, size, offset, "Non zero sync objects:\n");
    412	if (rc)
    413		goto out;
    414
    415	if (sds->sync_namager_names) {
    416		for (index = 0; sds->sync_namager_names[index]; ++index) {
    417			rc = hl_state_dump_print_syncs_single_block(
    418				hdev, index, buf, size, offset, map);
    419			if (rc)
    420				goto out;
    421		}
    422	} else {
    423		for (index = 0; index < sds->props[SP_NUM_CORES]; ++index) {
    424			rc = hl_state_dump_print_syncs_single_block(
    425				hdev, index, buf, size, offset, map);
    426			if (rc)
    427				goto out;
    428		}
    429	}
    430
    431out:
    432	hl_state_dump_free_sync_to_engine_map(map);
    433free_map_mem:
    434	kfree(map);
    435
    436	return rc;
    437}
    438
    439/**
    440 * hl_state_dump_alloc_read_sm_block_monitors - read monitors for a specific
    441 * block
    442 * @hdev: pointer to the device
    443 * @index: sync manager block index starting with E_N
    444 *
    445 * Returns an array of monitor data of size SP_MONITORS_AMOUNT or NULL
    446 * on error
    447 */
    448static struct hl_mon_state_dump *
    449hl_state_dump_alloc_read_sm_block_monitors(struct hl_device *hdev, u32 index)
    450{
    451	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    452	struct hl_mon_state_dump *monitors;
    453	s64 base_addr; /* Base addr can be negative */
    454	int i;
    455
    456	monitors = vmalloc(sds->props[SP_MONITORS_AMOUNT] *
    457			   sizeof(struct hl_mon_state_dump));
    458	if (!monitors)
    459		return NULL;
    460
    461	base_addr = sds->props[SP_NEXT_SYNC_OBJ_ADDR] * index;
    462
    463	for (i = 0; i < sds->props[SP_MONITORS_AMOUNT]; ++i) {
    464		monitors[i].id = i;
    465		monitors[i].wr_addr_low =
    466			RREG32(base_addr + sds->props[SP_MON_OBJ_WR_ADDR_LOW] +
    467				i * sizeof(u32));
    468
    469		monitors[i].wr_addr_high =
    470			RREG32(base_addr + sds->props[SP_MON_OBJ_WR_ADDR_HIGH] +
    471				i * sizeof(u32));
    472
    473		monitors[i].wr_data =
    474			RREG32(base_addr + sds->props[SP_MON_OBJ_WR_DATA] +
    475				i * sizeof(u32));
    476
    477		monitors[i].arm_data =
    478			RREG32(base_addr + sds->props[SP_MON_OBJ_ARM_DATA] +
    479				i * sizeof(u32));
    480
    481		monitors[i].status =
    482			RREG32(base_addr + sds->props[SP_MON_OBJ_STATUS] +
    483				i * sizeof(u32));
    484	}
    485
    486	return monitors;
    487}
    488
    489/**
    490 * hl_state_dump_free_monitors - free the monitors structure
    491 * @monitors: monitors array created with
    492 *            hl_state_dump_alloc_read_sm_block_monitors
    493 */
    494static void hl_state_dump_free_monitors(struct hl_mon_state_dump *monitors)
    495{
    496	vfree(monitors);
    497}
    498
    499/**
    500 * hl_state_dump_print_monitors_single_block - print active monitors on a
    501 * single block
    502 * @hdev: pointer to the device
    503 * @index: sync manager block index starting with E_N
    504 * @buf: destination buffer double pointer to be used with hl_snprintf_resize
    505 * @size: pointer to the size container
    506 * @offset: pointer to the offset container
    507 *
    508 * Returns 0 on success or error code on failure
    509 */
    510static int hl_state_dump_print_monitors_single_block(struct hl_device *hdev,
    511						u32 index,
    512						char **buf, size_t *size,
    513						size_t *offset)
    514{
    515	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    516	struct hl_mon_state_dump *monitors = NULL;
    517	int rc = 0, i;
    518
    519	if (sds->sync_namager_names) {
    520		rc = hl_snprintf_resize(
    521			buf, size, offset, "%s\n",
    522			sds->sync_namager_names[index]);
    523		if (rc)
    524			goto out;
    525	}
    526
    527	monitors = hl_state_dump_alloc_read_sm_block_monitors(hdev, index);
    528	if (!monitors) {
    529		rc = -ENOMEM;
    530		goto out;
    531	}
    532
    533	for (i = 0; i < sds->props[SP_MONITORS_AMOUNT]; ++i) {
    534		if (!(sds->funcs.monitor_valid(&monitors[i])))
    535			continue;
    536
    537		/* Monitor is valid, dump it */
    538		rc = sds->funcs.print_single_monitor(buf, size, offset, hdev,
    539							&monitors[i]);
    540		if (rc)
    541			goto free_monitors;
    542
    543		hl_snprintf_resize(buf, size, offset, "\n");
    544	}
    545
    546free_monitors:
    547	hl_state_dump_free_monitors(monitors);
    548out:
    549	return rc;
    550}
    551
    552/**
    553 * hl_state_dump_print_monitors - print active monitors
    554 * @hdev: pointer to the device
    555 * @buf: destination buffer double pointer to be used with hl_snprintf_resize
    556 * @size: pointer to the size container
    557 * @offset: pointer to the offset container
    558 *
    559 * Returns 0 on success or error code on failure
    560 */
    561static int hl_state_dump_print_monitors(struct hl_device *hdev,
    562					char **buf, size_t *size,
    563					size_t *offset)
    564{
    565	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    566	u32 index;
    567	int rc = 0;
    568
    569	rc = hl_snprintf_resize(buf, size, offset,
    570		"Valid (armed) monitor objects:\n");
    571	if (rc)
    572		goto out;
    573
    574	if (sds->sync_namager_names) {
    575		for (index = 0; sds->sync_namager_names[index]; ++index) {
    576			rc = hl_state_dump_print_monitors_single_block(
    577				hdev, index, buf, size, offset);
    578			if (rc)
    579				goto out;
    580		}
    581	} else {
    582		for (index = 0; index < sds->props[SP_NUM_CORES]; ++index) {
    583			rc = hl_state_dump_print_monitors_single_block(
    584				hdev, index, buf, size, offset);
    585			if (rc)
    586				goto out;
    587		}
    588	}
    589
    590out:
    591	return rc;
    592}
    593
    594/**
    595 * hl_state_dump_print_engine_fences - print active fences for a specific
    596 * engine
    597 * @hdev: pointer to the device
    598 * @engine_type: engine type to use
    599 * @buf: destination buffer double pointer to be used with hl_snprintf_resize
    600 * @size: pointer to the size container
    601 * @offset: pointer to the offset container
    602 */
    603static int
    604hl_state_dump_print_engine_fences(struct hl_device *hdev,
    605				  enum hl_sync_engine_type engine_type,
    606				  char **buf, size_t *size, size_t *offset)
    607{
    608	struct hl_state_dump_specs *sds = &hdev->state_dump_specs;
    609	int rc = 0, i, n_fences;
    610	u64 base_addr, next_fence;
    611
    612	switch (engine_type) {
    613	case ENGINE_TPC:
    614		n_fences = sds->props[SP_NUM_OF_TPC_ENGINES];
    615		base_addr = sds->props[SP_TPC0_CMDQ];
    616		next_fence = sds->props[SP_NEXT_TPC];
    617		break;
    618	case ENGINE_MME:
    619		n_fences = sds->props[SP_NUM_OF_MME_ENGINES];
    620		base_addr = sds->props[SP_MME_CMDQ];
    621		next_fence = sds->props[SP_NEXT_MME];
    622		break;
    623	case ENGINE_DMA:
    624		n_fences = sds->props[SP_NUM_OF_DMA_ENGINES];
    625		base_addr = sds->props[SP_DMA_CMDQ];
    626		next_fence = sds->props[SP_DMA_QUEUES_OFFSET];
    627		break;
    628	default:
    629		return -EINVAL;
    630	}
    631	for (i = 0; i < n_fences; ++i) {
    632		rc = sds->funcs.print_fences_single_engine(
    633			hdev,
    634			base_addr + next_fence * i +
    635				sds->props[SP_FENCE0_CNT_OFFSET],
    636			base_addr + next_fence * i +
    637				sds->props[SP_CP_STS_OFFSET],
    638			engine_type, i, buf, size, offset);
    639		if (rc)
    640			goto out;
    641	}
    642out:
    643	return rc;
    644}
    645
    646/**
    647 * hl_state_dump_print_fences - print active fences
    648 * @hdev: pointer to the device
    649 * @buf: destination buffer double pointer to be used with hl_snprintf_resize
    650 * @size: pointer to the size container
    651 * @offset: pointer to the offset container
    652 */
    653static int hl_state_dump_print_fences(struct hl_device *hdev, char **buf,
    654				      size_t *size, size_t *offset)
    655{
    656	int rc = 0;
    657
    658	rc = hl_snprintf_resize(buf, size, offset, "Valid (armed) fences:\n");
    659	if (rc)
    660		goto out;
    661
    662	rc = hl_state_dump_print_engine_fences(hdev, ENGINE_TPC, buf, size, offset);
    663	if (rc)
    664		goto out;
    665
    666	rc = hl_state_dump_print_engine_fences(hdev, ENGINE_MME, buf, size, offset);
    667	if (rc)
    668		goto out;
    669
    670	rc = hl_state_dump_print_engine_fences(hdev, ENGINE_DMA, buf, size, offset);
    671	if (rc)
    672		goto out;
    673
    674out:
    675	return rc;
    676}
    677
    678/**
    679 * hl_state_dump() - dump system state
    680 * @hdev: pointer to device structure
    681 */
    682int hl_state_dump(struct hl_device *hdev)
    683{
    684	char *buf = NULL;
    685	size_t offset = 0, size = 0;
    686	int rc;
    687
    688	rc = hl_snprintf_resize(&buf, &size, &offset,
    689				"Timestamp taken on: %llu\n\n",
    690				ktime_to_ns(ktime_get()));
    691	if (rc)
    692		goto err;
    693
    694	rc = hl_state_dump_print_syncs(hdev, &buf, &size, &offset);
    695	if (rc)
    696		goto err;
    697
    698	hl_snprintf_resize(&buf, &size, &offset, "\n");
    699
    700	rc = hl_state_dump_print_monitors(hdev, &buf, &size, &offset);
    701	if (rc)
    702		goto err;
    703
    704	hl_snprintf_resize(&buf, &size, &offset, "\n");
    705
    706	rc = hl_state_dump_print_fences(hdev, &buf, &size, &offset);
    707	if (rc)
    708		goto err;
    709
    710	hl_snprintf_resize(&buf, &size, &offset, "\n");
    711
    712	hl_debugfs_set_state_dump(hdev, buf, size);
    713
    714	return 0;
    715err:
    716	vfree(buf);
    717	return rc;
    718}