1/*
2 * Copyright (c) 2018, Mellanox Technologies. All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 */
32#define CREATE_TRACE_POINTS
33#include "lib/eq.h"
34#include "fw_tracer.h"
35#include "fw_tracer_tracepoint.h"
36
37static int mlx5_query_mtrc_caps(struct mlx5_fw_tracer *tracer)
38{
39	u32 *string_db_base_address_out = tracer->str_db.base_address_out;
40	u32 *string_db_size_out = tracer->str_db.size_out;
41	struct mlx5_core_dev *dev = tracer->dev;
42	u32 out[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
43	u32 in[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
44	void *mtrc_cap_sp;
45	int err, i;
46
47	err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
48				   MLX5_REG_MTRC_CAP, 0, 0);
49	if (err) {
50		mlx5_core_warn(dev, "FWTracer: Error reading tracer caps %d\n",
51			       err);
52		return err;
53	}
54
55	if (!MLX5_GET(mtrc_cap, out, trace_to_memory)) {
56		mlx5_core_dbg(dev, "FWTracer: Device does not support logging traces to memory\n");
57		return -ENOTSUPP;
58	}
59
60	tracer->trc_ver = MLX5_GET(mtrc_cap, out, trc_ver);
61	tracer->str_db.first_string_trace =
62			MLX5_GET(mtrc_cap, out, first_string_trace);
63	tracer->str_db.num_string_trace =
64			MLX5_GET(mtrc_cap, out, num_string_trace);
65	tracer->str_db.num_string_db = MLX5_GET(mtrc_cap, out, num_string_db);
66	tracer->owner = !!MLX5_GET(mtrc_cap, out, trace_owner);
67	tracer->str_db.loaded = false;
68
69	for (i = 0; i < tracer->str_db.num_string_db; i++) {
70		mtrc_cap_sp = MLX5_ADDR_OF(mtrc_cap, out, string_db_param[i]);
71		string_db_base_address_out[i] = MLX5_GET(mtrc_string_db_param,
72							 mtrc_cap_sp,
73							 string_db_base_address);
74		string_db_size_out[i] = MLX5_GET(mtrc_string_db_param,
75						 mtrc_cap_sp, string_db_size);
76	}
77
78	return err;
79}
80
81static int mlx5_set_mtrc_caps_trace_owner(struct mlx5_fw_tracer *tracer,
82					  u32 *out, u32 out_size,
83					  u8 trace_owner)
84{
85	struct mlx5_core_dev *dev = tracer->dev;
86	u32 in[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
87
88	MLX5_SET(mtrc_cap, in, trace_owner, trace_owner);
89
90	return mlx5_core_access_reg(dev, in, sizeof(in), out, out_size,
91				    MLX5_REG_MTRC_CAP, 0, 1);
92}
93
94static int mlx5_fw_tracer_ownership_acquire(struct mlx5_fw_tracer *tracer)
95{
96	struct mlx5_core_dev *dev = tracer->dev;
97	u32 out[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
98	int err;
99
100	err = mlx5_set_mtrc_caps_trace_owner(tracer, out, sizeof(out),
101					     MLX5_FW_TRACER_ACQUIRE_OWNERSHIP);
102	if (err) {
103		mlx5_core_warn(dev, "FWTracer: Acquire tracer ownership failed %d\n",
104			       err);
105		return err;
106	}
107
108	tracer->owner = !!MLX5_GET(mtrc_cap, out, trace_owner);
109
110	if (!tracer->owner)
111		return -EBUSY;
112
113	return 0;
114}
115
116static void mlx5_fw_tracer_ownership_release(struct mlx5_fw_tracer *tracer)
117{
118	u32 out[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
119
120	mlx5_set_mtrc_caps_trace_owner(tracer, out, sizeof(out),
121				       MLX5_FW_TRACER_RELEASE_OWNERSHIP);
122	tracer->owner = false;
123}
124
125static int mlx5_fw_tracer_create_log_buf(struct mlx5_fw_tracer *tracer)
126{
127	struct mlx5_core_dev *dev = tracer->dev;
128	struct device *ddev;
129	dma_addr_t dma;
130	void *buff;
131	gfp_t gfp;
132	int err;
133
134	tracer->buff.size = TRACE_BUFFER_SIZE_BYTE;
135
136	gfp = GFP_KERNEL | __GFP_ZERO;
137	buff = (void *)__get_free_pages(gfp,
138					get_order(tracer->buff.size));
139	if (!buff) {
140		err = -ENOMEM;
141		mlx5_core_warn(dev, "FWTracer: Failed to allocate pages, %d\n", err);
142		return err;
143	}
144	tracer->buff.log_buf = buff;
145
146	ddev = mlx5_core_dma_dev(dev);
147	dma = dma_map_single(ddev, buff, tracer->buff.size, DMA_FROM_DEVICE);
148	if (dma_mapping_error(ddev, dma)) {
149		mlx5_core_warn(dev, "FWTracer: Unable to map DMA: %d\n",
150			       dma_mapping_error(ddev, dma));
151		err = -ENOMEM;
152		goto free_pages;
153	}
154	tracer->buff.dma = dma;
155
156	return 0;
157
158free_pages:
159	free_pages((unsigned long)tracer->buff.log_buf, get_order(tracer->buff.size));
160
161	return err;
162}
163
164static void mlx5_fw_tracer_destroy_log_buf(struct mlx5_fw_tracer *tracer)
165{
166	struct mlx5_core_dev *dev = tracer->dev;
167	struct device *ddev;
168
169	if (!tracer->buff.log_buf)
170		return;
171
172	ddev = mlx5_core_dma_dev(dev);
173	dma_unmap_single(ddev, tracer->buff.dma, tracer->buff.size, DMA_FROM_DEVICE);
174	free_pages((unsigned long)tracer->buff.log_buf, get_order(tracer->buff.size));
175}
176
177static int mlx5_fw_tracer_create_mkey(struct mlx5_fw_tracer *tracer)
178{
179	struct mlx5_core_dev *dev = tracer->dev;
180	int err, inlen, i;
181	__be64 *mtt;
182	void *mkc;
183	u32 *in;
184
185	inlen = MLX5_ST_SZ_BYTES(create_mkey_in) +
186			sizeof(*mtt) * round_up(TRACER_BUFFER_PAGE_NUM, 2);
187
188	in = kvzalloc(inlen, GFP_KERNEL);
189	if (!in)
190		return -ENOMEM;
191
192	MLX5_SET(create_mkey_in, in, translations_octword_actual_size,
193		 DIV_ROUND_UP(TRACER_BUFFER_PAGE_NUM, 2));
194	mtt = (__be64 *)MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
195	for (i = 0 ; i < TRACER_BUFFER_PAGE_NUM ; i++)
196		mtt[i] = cpu_to_be64(tracer->buff.dma + i * PAGE_SIZE);
197
198	mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
199	MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_MTT);
200	MLX5_SET(mkc, mkc, lr, 1);
201	MLX5_SET(mkc, mkc, lw, 1);
202	MLX5_SET(mkc, mkc, pd, tracer->buff.pdn);
203	MLX5_SET(mkc, mkc, bsf_octword_size, 0);
204	MLX5_SET(mkc, mkc, qpn, 0xffffff);
205	MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT);
206	MLX5_SET(mkc, mkc, translations_octword_size,
207		 DIV_ROUND_UP(TRACER_BUFFER_PAGE_NUM, 2));
208	MLX5_SET64(mkc, mkc, start_addr, tracer->buff.dma);
209	MLX5_SET64(mkc, mkc, len, tracer->buff.size);
210	err = mlx5_core_create_mkey(dev, &tracer->buff.mkey, in, inlen);
211	if (err)
212		mlx5_core_warn(dev, "FWTracer: Failed to create mkey, %d\n", err);
213
214	kvfree(in);
215
216	return err;
217}
218
219static void mlx5_fw_tracer_free_strings_db(struct mlx5_fw_tracer *tracer)
220{
221	u32 num_string_db = tracer->str_db.num_string_db;
222	int i;
223
224	for (i = 0; i < num_string_db; i++) {
225		kfree(tracer->str_db.buffer[i]);
226		tracer->str_db.buffer[i] = NULL;
227	}
228}
229
230static int mlx5_fw_tracer_allocate_strings_db(struct mlx5_fw_tracer *tracer)
231{
232	u32 *string_db_size_out = tracer->str_db.size_out;
233	u32 num_string_db = tracer->str_db.num_string_db;
234	int i;
235
236	for (i = 0; i < num_string_db; i++) {
237		if (!string_db_size_out[i])
238			continue;
239		tracer->str_db.buffer[i] = kzalloc(string_db_size_out[i], GFP_KERNEL);
240		if (!tracer->str_db.buffer[i])
241			goto free_strings_db;
242	}
243
244	return 0;
245
246free_strings_db:
247	mlx5_fw_tracer_free_strings_db(tracer);
248	return -ENOMEM;
249}
250
251static void
252mlx5_fw_tracer_init_saved_traces_array(struct mlx5_fw_tracer *tracer)
253{
254	tracer->st_arr.saved_traces_index = 0;
255	mutex_init(&tracer->st_arr.lock);
256}
257
258static void
259mlx5_fw_tracer_clean_saved_traces_array(struct mlx5_fw_tracer *tracer)
260{
261	mutex_destroy(&tracer->st_arr.lock);
262}
263
264static void mlx5_tracer_read_strings_db(struct work_struct *work)
265{
266	struct mlx5_fw_tracer *tracer = container_of(work, struct mlx5_fw_tracer,
267						     read_fw_strings_work);
268	u32 num_of_reads, num_string_db = tracer->str_db.num_string_db;
269	struct mlx5_core_dev *dev = tracer->dev;
270	u32 in[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
271	u32 leftovers, offset;
272	int err = 0, i, j;
273	u32 *out, outlen;
274	void *out_value;
275
276	outlen = MLX5_ST_SZ_BYTES(mtrc_stdb) + STRINGS_DB_READ_SIZE_BYTES;
277	out = kzalloc(outlen, GFP_KERNEL);
278	if (!out) {
279		err = -ENOMEM;
280		goto out;
281	}
282
283	for (i = 0; i < num_string_db; i++) {
284		if (!tracer->str_db.size_out[i])
285			continue;
286		offset = 0;
287		MLX5_SET(mtrc_stdb, in, string_db_index, i);
288		num_of_reads = tracer->str_db.size_out[i] /
289				STRINGS_DB_READ_SIZE_BYTES;
290		leftovers = (tracer->str_db.size_out[i] %
291				STRINGS_DB_READ_SIZE_BYTES) /
292					STRINGS_DB_LEFTOVER_SIZE_BYTES;
293
294		MLX5_SET(mtrc_stdb, in, read_size, STRINGS_DB_READ_SIZE_BYTES);
295		for (j = 0; j < num_of_reads; j++) {
296			MLX5_SET(mtrc_stdb, in, start_offset, offset);
297
298			err = mlx5_core_access_reg(dev, in, sizeof(in), out,
299						   outlen, MLX5_REG_MTRC_STDB,
300						   0, 1);
301			if (err) {
302				mlx5_core_dbg(dev, "FWTracer: Failed to read strings DB %d\n",
303					      err);
304				goto out_free;
305			}
306
307			out_value = MLX5_ADDR_OF(mtrc_stdb, out, string_db_data);
308			memcpy(tracer->str_db.buffer[i] + offset, out_value,
309			       STRINGS_DB_READ_SIZE_BYTES);
310			offset += STRINGS_DB_READ_SIZE_BYTES;
311		}
312
313		/* Strings database is aligned to 64, need to read leftovers*/
314		MLX5_SET(mtrc_stdb, in, read_size,
315			 STRINGS_DB_LEFTOVER_SIZE_BYTES);
316		for (j = 0; j < leftovers; j++) {
317			MLX5_SET(mtrc_stdb, in, start_offset, offset);
318
319			err = mlx5_core_access_reg(dev, in, sizeof(in), out,
320						   outlen, MLX5_REG_MTRC_STDB,
321						   0, 1);
322			if (err) {
323				mlx5_core_dbg(dev, "FWTracer: Failed to read strings DB %d\n",
324					      err);
325				goto out_free;
326			}
327
328			out_value = MLX5_ADDR_OF(mtrc_stdb, out, string_db_data);
329			memcpy(tracer->str_db.buffer[i] + offset, out_value,
330			       STRINGS_DB_LEFTOVER_SIZE_BYTES);
331			offset += STRINGS_DB_LEFTOVER_SIZE_BYTES;
332		}
333	}
334
335	tracer->str_db.loaded = true;
336
337out_free:
338	kfree(out);
339out:
340	return;
341}
342
343static void mlx5_fw_tracer_arm(struct mlx5_core_dev *dev)
344{
345	u32 out[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
346	u32 in[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
347	int err;
348
349	MLX5_SET(mtrc_ctrl, in, arm_event, 1);
350
351	err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
352				   MLX5_REG_MTRC_CTRL, 0, 1);
353	if (err)
354		mlx5_core_warn(dev, "FWTracer: Failed to arm tracer event %d\n", err);
355}
356
357static const char *VAL_PARM		= "%llx";
358static const char *REPLACE_64_VAL_PARM	= "%x%x";
359static const char *PARAM_CHAR		= "%";
360
361static int mlx5_tracer_message_hash(u32 message_id)
362{
363	return jhash_1word(message_id, 0) & (MESSAGE_HASH_SIZE - 1);
364}
365
366static struct tracer_string_format *mlx5_tracer_message_insert(struct mlx5_fw_tracer *tracer,
367							       struct tracer_event *tracer_event)
368{
369	struct hlist_head *head =
370		&tracer->hash[mlx5_tracer_message_hash(tracer_event->string_event.tmsn)];
371	struct tracer_string_format *cur_string;
372
373	cur_string = kzalloc(sizeof(*cur_string), GFP_KERNEL);
374	if (!cur_string)
375		return NULL;
376
377	hlist_add_head(&cur_string->hlist, head);
378
379	return cur_string;
380}
381
382static struct tracer_string_format *mlx5_tracer_get_string(struct mlx5_fw_tracer *tracer,
383							   struct tracer_event *tracer_event)
384{
385	struct tracer_string_format *cur_string;
386	u32 str_ptr, offset;
387	int i;
388
389	str_ptr = tracer_event->string_event.string_param;
390
391	for (i = 0; i < tracer->str_db.num_string_db; i++) {
392		if (!tracer->str_db.size_out[i])
393			continue;
394		if (str_ptr > tracer->str_db.base_address_out[i] &&
395		    str_ptr < tracer->str_db.base_address_out[i] +
396		    tracer->str_db.size_out[i]) {
397			offset = str_ptr - tracer->str_db.base_address_out[i];
398			/* add it to the hash */
399			cur_string = mlx5_tracer_message_insert(tracer, tracer_event);
400			if (!cur_string)
401				return NULL;
402			cur_string->string = (char *)(tracer->str_db.buffer[i] +
403							offset);
404			return cur_string;
405		}
406	}
407
408	return NULL;
409}
410
411static void mlx5_tracer_clean_message(struct tracer_string_format *str_frmt)
412{
413	hlist_del(&str_frmt->hlist);
414	kfree(str_frmt);
415}
416
417static int mlx5_tracer_get_num_of_params(char *str)
418{
419	char *substr, *pstr = str;
420	int num_of_params = 0;
421
422	/* replace %llx with %x%x */
423	substr = strstr(pstr, VAL_PARM);
424	while (substr) {
425		memcpy(substr, REPLACE_64_VAL_PARM, 4);
426		pstr = substr;
427		substr = strstr(pstr, VAL_PARM);
428	}
429
430	/* count all the % characters */
431	substr = strstr(str, PARAM_CHAR);
432	while (substr) {
433		num_of_params += 1;
434		str = substr + 1;
435		substr = strstr(str, PARAM_CHAR);
436	}
437
438	return num_of_params;
439}
440
441static struct tracer_string_format *mlx5_tracer_message_find(struct hlist_head *head,
442							     u8 event_id, u32 tmsn)
443{
444	struct tracer_string_format *message;
445
446	hlist_for_each_entry(message, head, hlist)
447		if (message->event_id == event_id && message->tmsn == tmsn)
448			return message;
449
450	return NULL;
451}
452
453static struct tracer_string_format *mlx5_tracer_message_get(struct mlx5_fw_tracer *tracer,
454							    struct tracer_event *tracer_event)
455{
456	struct hlist_head *head =
457		&tracer->hash[mlx5_tracer_message_hash(tracer_event->string_event.tmsn)];
458
459	return mlx5_tracer_message_find(head, tracer_event->event_id, tracer_event->string_event.tmsn);
460}
461
462static void poll_trace(struct mlx5_fw_tracer *tracer,
463		       struct tracer_event *tracer_event, u64 *trace)
464{
465	u32 timestamp_low, timestamp_mid, timestamp_high, urts;
466
467	tracer_event->event_id = MLX5_GET(tracer_event, trace, event_id);
468	tracer_event->lost_event = MLX5_GET(tracer_event, trace, lost);
469	tracer_event->out = trace;
470
471	switch (tracer_event->event_id) {
472	case TRACER_EVENT_TYPE_TIMESTAMP:
473		tracer_event->type = TRACER_EVENT_TYPE_TIMESTAMP;
474		urts = MLX5_GET(tracer_timestamp_event, trace, urts);
475		if (tracer->trc_ver == 0)
476			tracer_event->timestamp_event.unreliable = !!(urts >> 2);
477		else
478			tracer_event->timestamp_event.unreliable = !!(urts & 1);
479
480		timestamp_low = MLX5_GET(tracer_timestamp_event,
481					 trace, timestamp7_0);
482		timestamp_mid = MLX5_GET(tracer_timestamp_event,
483					 trace, timestamp39_8);
484		timestamp_high = MLX5_GET(tracer_timestamp_event,
485					  trace, timestamp52_40);
486
487		tracer_event->timestamp_event.timestamp =
488				((u64)timestamp_high << 40) |
489				((u64)timestamp_mid << 8) |
490				(u64)timestamp_low;
491		break;
492	default:
493		if (tracer_event->event_id >= tracer->str_db.first_string_trace &&
494		    tracer_event->event_id <= tracer->str_db.first_string_trace +
495					      tracer->str_db.num_string_trace) {
496			tracer_event->type = TRACER_EVENT_TYPE_STRING;
497			tracer_event->string_event.timestamp =
498				MLX5_GET(tracer_string_event, trace, timestamp);
499			tracer_event->string_event.string_param =
500				MLX5_GET(tracer_string_event, trace, string_param);
501			tracer_event->string_event.tmsn =
502				MLX5_GET(tracer_string_event, trace, tmsn);
503			tracer_event->string_event.tdsn =
504				MLX5_GET(tracer_string_event, trace, tdsn);
505		} else {
506			tracer_event->type = TRACER_EVENT_TYPE_UNRECOGNIZED;
507		}
508		break;
509	}
510}
511
512static u64 get_block_timestamp(struct mlx5_fw_tracer *tracer, u64 *ts_event)
513{
514	struct tracer_event tracer_event;
515	u8 event_id;
516
517	event_id = MLX5_GET(tracer_event, ts_event, event_id);
518
519	if (event_id == TRACER_EVENT_TYPE_TIMESTAMP)
520		poll_trace(tracer, &tracer_event, ts_event);
521	else
522		tracer_event.timestamp_event.timestamp = 0;
523
524	return tracer_event.timestamp_event.timestamp;
525}
526
527static void mlx5_fw_tracer_clean_print_hash(struct mlx5_fw_tracer *tracer)
528{
529	struct tracer_string_format *str_frmt;
530	struct hlist_node *n;
531	int i;
532
533	for (i = 0; i < MESSAGE_HASH_SIZE; i++) {
534		hlist_for_each_entry_safe(str_frmt, n, &tracer->hash[i], hlist)
535			mlx5_tracer_clean_message(str_frmt);
536	}
537}
538
539static void mlx5_fw_tracer_clean_ready_list(struct mlx5_fw_tracer *tracer)
540{
541	struct tracer_string_format *str_frmt, *tmp_str;
542
543	list_for_each_entry_safe(str_frmt, tmp_str, &tracer->ready_strings_list,
544				 list)
545		list_del(&str_frmt->list);
546}
547
548static void mlx5_fw_tracer_save_trace(struct mlx5_fw_tracer *tracer,
549				      u64 timestamp, bool lost,
550				      u8 event_id, char *msg)
551{
552	struct mlx5_fw_trace_data *trace_data;
553
554	mutex_lock(&tracer->st_arr.lock);
555	trace_data = &tracer->st_arr.straces[tracer->st_arr.saved_traces_index];
556	trace_data->timestamp = timestamp;
557	trace_data->lost = lost;
558	trace_data->event_id = event_id;
559	strscpy_pad(trace_data->msg, msg, TRACE_STR_MSG);
560
561	tracer->st_arr.saved_traces_index =
562		(tracer->st_arr.saved_traces_index + 1) & (SAVED_TRACES_NUM - 1);
563	mutex_unlock(&tracer->st_arr.lock);
564}
565
566static noinline
567void mlx5_tracer_print_trace(struct tracer_string_format *str_frmt,
568			     struct mlx5_core_dev *dev,
569			     u64 trace_timestamp)
570{
571	char	tmp[512];
572
573	snprintf(tmp, sizeof(tmp), str_frmt->string,
574		 str_frmt->params[0],
575		 str_frmt->params[1],
576		 str_frmt->params[2],
577		 str_frmt->params[3],
578		 str_frmt->params[4],
579		 str_frmt->params[5],
580		 str_frmt->params[6]);
581
582	trace_mlx5_fw(dev->tracer, trace_timestamp, str_frmt->lost,
583		      str_frmt->event_id, tmp);
584
585	mlx5_fw_tracer_save_trace(dev->tracer, trace_timestamp,
586				  str_frmt->lost, str_frmt->event_id, tmp);
587
588	/* remove it from hash */
589	mlx5_tracer_clean_message(str_frmt);
590}
591
592static int mlx5_tracer_handle_raw_string(struct mlx5_fw_tracer *tracer,
593					 struct tracer_event *tracer_event)
594{
595	struct tracer_string_format *cur_string;
596
597	cur_string = mlx5_tracer_message_insert(tracer, tracer_event);
598	if (!cur_string)
599		return -1;
600
601	cur_string->event_id = tracer_event->event_id;
602	cur_string->timestamp = tracer_event->string_event.timestamp;
603	cur_string->lost = tracer_event->lost_event;
604	cur_string->string = "0x%08x%08x";
605	cur_string->num_of_params = 2;
606	cur_string->params[0] = upper_32_bits(*tracer_event->out);
607	cur_string->params[1] = lower_32_bits(*tracer_event->out);
608	list_add_tail(&cur_string->list, &tracer->ready_strings_list);
609	return 0;
610}
611
612static int mlx5_tracer_handle_string_trace(struct mlx5_fw_tracer *tracer,
613					   struct tracer_event *tracer_event)
614{
615	struct tracer_string_format *cur_string;
616
617	if (tracer_event->string_event.tdsn == 0) {
618		cur_string = mlx5_tracer_get_string(tracer, tracer_event);
619		if (!cur_string)
620			return mlx5_tracer_handle_raw_string(tracer, tracer_event);
621
622		cur_string->num_of_params = mlx5_tracer_get_num_of_params(cur_string->string);
623		cur_string->last_param_num = 0;
624		cur_string->event_id = tracer_event->event_id;
625		cur_string->tmsn = tracer_event->string_event.tmsn;
626		cur_string->timestamp = tracer_event->string_event.timestamp;
627		cur_string->lost = tracer_event->lost_event;
628		if (cur_string->num_of_params == 0) /* trace with no params */
629			list_add_tail(&cur_string->list, &tracer->ready_strings_list);
630	} else {
631		cur_string = mlx5_tracer_message_get(tracer, tracer_event);
632		if (!cur_string) {
633			pr_debug("%s Got string event for unknown string tmsn: %d\n",
634				 __func__, tracer_event->string_event.tmsn);
635			return mlx5_tracer_handle_raw_string(tracer, tracer_event);
636		}
637		cur_string->last_param_num += 1;
638		if (cur_string->last_param_num > TRACER_MAX_PARAMS) {
639			pr_debug("%s Number of params exceeds the max (%d)\n",
640				 __func__, TRACER_MAX_PARAMS);
641			list_add_tail(&cur_string->list, &tracer->ready_strings_list);
642			return 0;
643		}
644		/* keep the new parameter */
645		cur_string->params[cur_string->last_param_num - 1] =
646			tracer_event->string_event.string_param;
647		if (cur_string->last_param_num == cur_string->num_of_params)
648			list_add_tail(&cur_string->list, &tracer->ready_strings_list);
649	}
650
651	return 0;
652}
653
654static void mlx5_tracer_handle_timestamp_trace(struct mlx5_fw_tracer *tracer,
655					       struct tracer_event *tracer_event)
656{
657	struct tracer_timestamp_event timestamp_event =
658						tracer_event->timestamp_event;
659	struct tracer_string_format *str_frmt, *tmp_str;
660	struct mlx5_core_dev *dev = tracer->dev;
661	u64 trace_timestamp;
662
663	list_for_each_entry_safe(str_frmt, tmp_str, &tracer->ready_strings_list, list) {
664		list_del(&str_frmt->list);
665		if (str_frmt->timestamp < (timestamp_event.timestamp & MASK_6_0))
666			trace_timestamp = (timestamp_event.timestamp & MASK_52_7) |
667					  (str_frmt->timestamp & MASK_6_0);
668		else
669			trace_timestamp = ((timestamp_event.timestamp - 1) & MASK_52_7) |
670					  (str_frmt->timestamp & MASK_6_0);
671
672		mlx5_tracer_print_trace(str_frmt, dev, trace_timestamp);
673	}
674}
675
676static int mlx5_tracer_handle_trace(struct mlx5_fw_tracer *tracer,
677				    struct tracer_event *tracer_event)
678{
679	if (tracer_event->type == TRACER_EVENT_TYPE_STRING) {
680		mlx5_tracer_handle_string_trace(tracer, tracer_event);
681	} else if (tracer_event->type == TRACER_EVENT_TYPE_TIMESTAMP) {
682		if (!tracer_event->timestamp_event.unreliable)
683			mlx5_tracer_handle_timestamp_trace(tracer, tracer_event);
684	} else {
685		pr_debug("%s Got unrecognised type %d for parsing, exiting..\n",
686			 __func__, tracer_event->type);
687	}
688	return 0;
689}
690
691static void mlx5_fw_tracer_handle_traces(struct work_struct *work)
692{
693	struct mlx5_fw_tracer *tracer =
694			container_of(work, struct mlx5_fw_tracer, handle_traces_work);
695	u64 block_timestamp, last_block_timestamp, tmp_trace_block[TRACES_PER_BLOCK];
696	u32 block_count, start_offset, prev_start_offset, prev_consumer_index;
697	u32 trace_event_size = MLX5_ST_SZ_BYTES(tracer_event);
698	struct mlx5_core_dev *dev = tracer->dev;
699	struct tracer_event tracer_event;
700	int i;
701
702	mlx5_core_dbg(dev, "FWTracer: Handle Trace event, owner=(%d)\n", tracer->owner);
703	if (!tracer->owner)
704		return;
705
706	if (unlikely(!tracer->str_db.loaded))
707		goto arm;
708
709	block_count = tracer->buff.size / TRACER_BLOCK_SIZE_BYTE;
710	start_offset = tracer->buff.consumer_index * TRACER_BLOCK_SIZE_BYTE;
711
712	/* Copy the block to local buffer to avoid HW override while being processed */
713	memcpy(tmp_trace_block, tracer->buff.log_buf + start_offset,
714	       TRACER_BLOCK_SIZE_BYTE);
715
716	block_timestamp =
717		get_block_timestamp(tracer, &tmp_trace_block[TRACES_PER_BLOCK - 1]);
718
719	while (block_timestamp > tracer->last_timestamp) {
720		/* Check block override if it's not the first block */
721		if (tracer->last_timestamp) {
722			u64 *ts_event;
723			/* To avoid block override be the HW in case of buffer
724			 * wraparound, the time stamp of the previous block
725			 * should be compared to the last timestamp handled
726			 * by the driver.
727			 */
728			prev_consumer_index =
729				(tracer->buff.consumer_index - 1) & (block_count - 1);
730			prev_start_offset = prev_consumer_index * TRACER_BLOCK_SIZE_BYTE;
731
732			ts_event = tracer->buff.log_buf + prev_start_offset +
733				   (TRACES_PER_BLOCK - 1) * trace_event_size;
734			last_block_timestamp = get_block_timestamp(tracer, ts_event);
735			/* If previous timestamp different from last stored
736			 * timestamp then there is a good chance that the
737			 * current buffer is overwritten and therefore should
738			 * not be parsed.
739			 */
740			if (tracer->last_timestamp != last_block_timestamp) {
741				mlx5_core_warn(dev, "FWTracer: Events were lost\n");
742				tracer->last_timestamp = block_timestamp;
743				tracer->buff.consumer_index =
744					(tracer->buff.consumer_index + 1) & (block_count - 1);
745				break;
746			}
747		}
748
749		/* Parse events */
750		for (i = 0; i < TRACES_PER_BLOCK ; i++) {
751			poll_trace(tracer, &tracer_event, &tmp_trace_block[i]);
752			mlx5_tracer_handle_trace(tracer, &tracer_event);
753		}
754
755		tracer->buff.consumer_index =
756			(tracer->buff.consumer_index + 1) & (block_count - 1);
757
758		tracer->last_timestamp = block_timestamp;
759		start_offset = tracer->buff.consumer_index * TRACER_BLOCK_SIZE_BYTE;
760		memcpy(tmp_trace_block, tracer->buff.log_buf + start_offset,
761		       TRACER_BLOCK_SIZE_BYTE);
762		block_timestamp = get_block_timestamp(tracer,
763						      &tmp_trace_block[TRACES_PER_BLOCK - 1]);
764	}
765
766arm:
767	mlx5_fw_tracer_arm(dev);
768}
769
770static int mlx5_fw_tracer_set_mtrc_conf(struct mlx5_fw_tracer *tracer)
771{
772	struct mlx5_core_dev *dev = tracer->dev;
773	u32 out[MLX5_ST_SZ_DW(mtrc_conf)] = {0};
774	u32 in[MLX5_ST_SZ_DW(mtrc_conf)] = {0};
775	int err;
776
777	MLX5_SET(mtrc_conf, in, trace_mode, TRACE_TO_MEMORY);
778	MLX5_SET(mtrc_conf, in, log_trace_buffer_size,
779		 ilog2(TRACER_BUFFER_PAGE_NUM));
780	MLX5_SET(mtrc_conf, in, trace_mkey, tracer->buff.mkey);
781
782	err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
783				   MLX5_REG_MTRC_CONF, 0, 1);
784	if (err)
785		mlx5_core_warn(dev, "FWTracer: Failed to set tracer configurations %d\n", err);
786
787	tracer->buff.consumer_index = 0;
788	return err;
789}
790
791static int mlx5_fw_tracer_set_mtrc_ctrl(struct mlx5_fw_tracer *tracer, u8 status, u8 arm)
792{
793	struct mlx5_core_dev *dev = tracer->dev;
794	u32 out[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
795	u32 in[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
796	int err;
797
798	MLX5_SET(mtrc_ctrl, in, modify_field_select, TRACE_STATUS);
799	MLX5_SET(mtrc_ctrl, in, trace_status, status);
800	MLX5_SET(mtrc_ctrl, in, arm_event, arm);
801
802	err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
803				   MLX5_REG_MTRC_CTRL, 0, 1);
804
805	if (!err && status)
806		tracer->last_timestamp = 0;
807
808	return err;
809}
810
811static int mlx5_fw_tracer_start(struct mlx5_fw_tracer *tracer)
812{
813	struct mlx5_core_dev *dev = tracer->dev;
814	int err;
815
816	err = mlx5_fw_tracer_ownership_acquire(tracer);
817	if (err) {
818		mlx5_core_dbg(dev, "FWTracer: Ownership was not granted %d\n", err);
819		/* Don't fail since ownership can be acquired on a later FW event */
820		return 0;
821	}
822
823	err = mlx5_fw_tracer_set_mtrc_conf(tracer);
824	if (err) {
825		mlx5_core_warn(dev, "FWTracer: Failed to set tracer configuration %d\n", err);
826		goto release_ownership;
827	}
828
829	/* enable tracer & trace events */
830	err = mlx5_fw_tracer_set_mtrc_ctrl(tracer, 1, 1);
831	if (err) {
832		mlx5_core_warn(dev, "FWTracer: Failed to enable tracer %d\n", err);
833		goto release_ownership;
834	}
835
836	mlx5_core_dbg(dev, "FWTracer: Ownership granted and active\n");
837	return 0;
838
839release_ownership:
840	mlx5_fw_tracer_ownership_release(tracer);
841	return err;
842}
843
844static void mlx5_fw_tracer_ownership_change(struct work_struct *work)
845{
846	struct mlx5_fw_tracer *tracer =
847		container_of(work, struct mlx5_fw_tracer, ownership_change_work);
848
849	mlx5_core_dbg(tracer->dev, "FWTracer: ownership changed, current=(%d)\n", tracer->owner);
850	if (tracer->owner) {
851		mlx5_fw_tracer_ownership_acquire(tracer);
852		return;
853	}
854
855	mlx5_fw_tracer_start(tracer);
856}
857
858static int mlx5_fw_tracer_set_core_dump_reg(struct mlx5_core_dev *dev,
859					    u32 *in, int size_in)
860{
861	u32 out[MLX5_ST_SZ_DW(core_dump_reg)] = {};
862
863	if (!MLX5_CAP_DEBUG(dev, core_dump_general) &&
864	    !MLX5_CAP_DEBUG(dev, core_dump_qp))
865		return -EOPNOTSUPP;
866
867	return mlx5_core_access_reg(dev, in, size_in, out, sizeof(out),
868				    MLX5_REG_CORE_DUMP, 0, 1);
869}
870
871int mlx5_fw_tracer_trigger_core_dump_general(struct mlx5_core_dev *dev)
872{
873	struct mlx5_fw_tracer *tracer = dev->tracer;
874	u32 in[MLX5_ST_SZ_DW(core_dump_reg)] = {};
875	int err;
876
877	if (!MLX5_CAP_DEBUG(dev, core_dump_general) || !tracer)
878		return -EOPNOTSUPP;
879	if (!tracer->owner)
880		return -EPERM;
881
882	MLX5_SET(core_dump_reg, in, core_dump_type, 0x0);
883
884	err =  mlx5_fw_tracer_set_core_dump_reg(dev, in, sizeof(in));
885	if (err)
886		return err;
887	queue_work(tracer->work_queue, &tracer->handle_traces_work);
888	flush_workqueue(tracer->work_queue);
889	return 0;
890}
891
892static void
893mlx5_devlink_fmsg_fill_trace(struct devlink_fmsg *fmsg,
894			     struct mlx5_fw_trace_data *trace_data)
895{
896	devlink_fmsg_obj_nest_start(fmsg);
897	devlink_fmsg_u64_pair_put(fmsg, "timestamp", trace_data->timestamp);
898	devlink_fmsg_bool_pair_put(fmsg, "lost", trace_data->lost);
899	devlink_fmsg_u8_pair_put(fmsg, "event_id", trace_data->event_id);
900	devlink_fmsg_string_pair_put(fmsg, "msg", trace_data->msg);
901	devlink_fmsg_obj_nest_end(fmsg);
902}
903
904int mlx5_fw_tracer_get_saved_traces_objects(struct mlx5_fw_tracer *tracer,
905					    struct devlink_fmsg *fmsg)
906{
907	struct mlx5_fw_trace_data *straces = tracer->st_arr.straces;
908	u32 index, start_index, end_index;
909	u32 saved_traces_index;
910
911	if (!straces[0].timestamp)
912		return -ENOMSG;
913
914	mutex_lock(&tracer->st_arr.lock);
915	saved_traces_index = tracer->st_arr.saved_traces_index;
916	if (straces[saved_traces_index].timestamp)
917		start_index = saved_traces_index;
918	else
919		start_index = 0;
920	end_index = (saved_traces_index - 1) & (SAVED_TRACES_NUM - 1);
921
922	devlink_fmsg_arr_pair_nest_start(fmsg, "dump fw traces");
923	index = start_index;
924	while (index != end_index) {
925		mlx5_devlink_fmsg_fill_trace(fmsg, &straces[index]);
926
927		index = (index + 1) & (SAVED_TRACES_NUM - 1);
928	}
929
930	devlink_fmsg_arr_pair_nest_end(fmsg);
931	mutex_unlock(&tracer->st_arr.lock);
932
933	return 0;
934}
935
936static void mlx5_fw_tracer_update_db(struct work_struct *work)
937{
938	struct mlx5_fw_tracer *tracer =
939			container_of(work, struct mlx5_fw_tracer, update_db_work);
940
941	mlx5_fw_tracer_reload(tracer);
942}
943
944/* Create software resources (Buffers, etc ..) */
945struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
946{
947	struct mlx5_fw_tracer *tracer = NULL;
948	int err;
949
950	if (!MLX5_CAP_MCAM_REG(dev, tracer_registers)) {
951		mlx5_core_dbg(dev, "FWTracer: Tracer capability not present\n");
952		return NULL;
953	}
954
955	tracer = kvzalloc(sizeof(*tracer), GFP_KERNEL);
956	if (!tracer)
957		return ERR_PTR(-ENOMEM);
958
959	tracer->work_queue = create_singlethread_workqueue("mlx5_fw_tracer");
960	if (!tracer->work_queue) {
961		err = -ENOMEM;
962		goto free_tracer;
963	}
964
965	tracer->dev = dev;
966
967	INIT_LIST_HEAD(&tracer->ready_strings_list);
968	INIT_WORK(&tracer->ownership_change_work, mlx5_fw_tracer_ownership_change);
969	INIT_WORK(&tracer->read_fw_strings_work, mlx5_tracer_read_strings_db);
970	INIT_WORK(&tracer->handle_traces_work, mlx5_fw_tracer_handle_traces);
971	INIT_WORK(&tracer->update_db_work, mlx5_fw_tracer_update_db);
972	mutex_init(&tracer->state_lock);
973
974
975	err = mlx5_query_mtrc_caps(tracer);
976	if (err) {
977		mlx5_core_dbg(dev, "FWTracer: Failed to query capabilities %d\n", err);
978		goto destroy_workqueue;
979	}
980
981	err = mlx5_fw_tracer_create_log_buf(tracer);
982	if (err) {
983		mlx5_core_warn(dev, "FWTracer: Create log buffer failed %d\n", err);
984		goto destroy_workqueue;
985	}
986
987	err = mlx5_fw_tracer_allocate_strings_db(tracer);
988	if (err) {
989		mlx5_core_warn(dev, "FWTracer: Allocate strings database failed %d\n", err);
990		goto free_log_buf;
991	}
992
993	mlx5_fw_tracer_init_saved_traces_array(tracer);
994	mlx5_core_dbg(dev, "FWTracer: Tracer created\n");
995
996	return tracer;
997
998free_log_buf:
999	mlx5_fw_tracer_destroy_log_buf(tracer);
1000destroy_workqueue:
1001	tracer->dev = NULL;
1002	destroy_workqueue(tracer->work_queue);
1003free_tracer:
1004	kvfree(tracer);
1005	return ERR_PTR(err);
1006}
1007
1008static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data);
1009
1010/* Create HW resources + start tracer */
1011int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer)
1012{
1013	struct mlx5_core_dev *dev;
1014	int err;
1015
1016	if (IS_ERR_OR_NULL(tracer))
1017		return 0;
1018
1019	if (!tracer->str_db.loaded)
1020		queue_work(tracer->work_queue, &tracer->read_fw_strings_work);
1021
1022	mutex_lock(&tracer->state_lock);
1023	if (test_and_set_bit(MLX5_TRACER_STATE_UP, &tracer->state))
1024		goto unlock;
1025
1026	dev = tracer->dev;
1027
1028	err = mlx5_core_alloc_pd(dev, &tracer->buff.pdn);
1029	if (err) {
1030		mlx5_core_warn(dev, "FWTracer: Failed to allocate PD %d\n", err);
1031		goto err_cancel_work;
1032	}
1033
1034	err = mlx5_fw_tracer_create_mkey(tracer);
1035	if (err) {
1036		mlx5_core_warn(dev, "FWTracer: Failed to create mkey %d\n", err);
1037		goto err_dealloc_pd;
1038	}
1039
1040	MLX5_NB_INIT(&tracer->nb, fw_tracer_event, DEVICE_TRACER);
1041	mlx5_eq_notifier_register(dev, &tracer->nb);
1042
1043	err = mlx5_fw_tracer_start(tracer);
1044	if (err) {
1045		mlx5_core_warn(dev, "FWTracer: Failed to start tracer %d\n", err);
1046		goto err_notifier_unregister;
1047	}
1048unlock:
1049	mutex_unlock(&tracer->state_lock);
1050	return 0;
1051
1052err_notifier_unregister:
1053	mlx5_eq_notifier_unregister(dev, &tracer->nb);
1054	mlx5_core_destroy_mkey(dev, tracer->buff.mkey);
1055err_dealloc_pd:
1056	mlx5_core_dealloc_pd(dev, tracer->buff.pdn);
1057err_cancel_work:
1058	cancel_work_sync(&tracer->read_fw_strings_work);
1059	mutex_unlock(&tracer->state_lock);
1060	return err;
1061}
1062
1063/* Stop tracer + Cleanup HW resources */
1064void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer)
1065{
1066	if (IS_ERR_OR_NULL(tracer))
1067		return;
1068
1069	mutex_lock(&tracer->state_lock);
1070	if (!test_and_clear_bit(MLX5_TRACER_STATE_UP, &tracer->state))
1071		goto unlock;
1072
1073	mlx5_core_dbg(tracer->dev, "FWTracer: Cleanup, is owner ? (%d)\n",
1074		      tracer->owner);
1075	mlx5_eq_notifier_unregister(tracer->dev, &tracer->nb);
1076	cancel_work_sync(&tracer->ownership_change_work);
1077	cancel_work_sync(&tracer->handle_traces_work);
1078	/* It is valid to get here from update_db_work. Hence, don't wait for
1079	 * update_db_work to finished.
1080	 */
1081	cancel_work(&tracer->update_db_work);
1082
1083	if (tracer->owner)
1084		mlx5_fw_tracer_ownership_release(tracer);
1085
1086	mlx5_core_destroy_mkey(tracer->dev, tracer->buff.mkey);
1087	mlx5_core_dealloc_pd(tracer->dev, tracer->buff.pdn);
1088unlock:
1089	mutex_unlock(&tracer->state_lock);
1090}
1091
1092/* Free software resources (Buffers, etc ..) */
1093void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer)
1094{
1095	if (IS_ERR_OR_NULL(tracer))
1096		return;
1097
1098	mlx5_core_dbg(tracer->dev, "FWTracer: Destroy\n");
1099
1100	cancel_work_sync(&tracer->read_fw_strings_work);
1101	mlx5_fw_tracer_clean_ready_list(tracer);
1102	mlx5_fw_tracer_clean_print_hash(tracer);
1103	mlx5_fw_tracer_clean_saved_traces_array(tracer);
1104	mlx5_fw_tracer_free_strings_db(tracer);
1105	mlx5_fw_tracer_destroy_log_buf(tracer);
1106	mutex_destroy(&tracer->state_lock);
1107	destroy_workqueue(tracer->work_queue);
1108	kvfree(tracer);
1109}
1110
1111static int mlx5_fw_tracer_recreate_strings_db(struct mlx5_fw_tracer *tracer)
1112{
1113	struct mlx5_core_dev *dev;
1114	int err;
1115
1116	if (test_and_set_bit(MLX5_TRACER_RECREATE_DB, &tracer->state))
1117		return 0;
1118	cancel_work_sync(&tracer->read_fw_strings_work);
1119	mlx5_fw_tracer_clean_ready_list(tracer);
1120	mlx5_fw_tracer_clean_print_hash(tracer);
1121	mlx5_fw_tracer_clean_saved_traces_array(tracer);
1122	mlx5_fw_tracer_free_strings_db(tracer);
1123
1124	dev = tracer->dev;
1125	err = mlx5_query_mtrc_caps(tracer);
1126	if (err) {
1127		mlx5_core_dbg(dev, "FWTracer: Failed to query capabilities %d\n", err);
1128		goto out;
1129	}
1130
1131	err = mlx5_fw_tracer_allocate_strings_db(tracer);
1132	if (err) {
1133		mlx5_core_warn(dev, "FWTracer: Allocate strings DB failed %d\n", err);
1134		goto out;
1135	}
1136	mlx5_fw_tracer_init_saved_traces_array(tracer);
1137out:
1138	clear_bit(MLX5_TRACER_RECREATE_DB, &tracer->state);
1139	return err;
1140}
1141
1142int mlx5_fw_tracer_reload(struct mlx5_fw_tracer *tracer)
1143{
1144	struct mlx5_core_dev *dev;
1145	int err;
1146
1147	if (IS_ERR_OR_NULL(tracer))
1148		return 0;
1149
1150	dev = tracer->dev;
1151	mlx5_fw_tracer_cleanup(tracer);
1152	err = mlx5_fw_tracer_recreate_strings_db(tracer);
1153	if (err) {
1154		mlx5_core_warn(dev, "Failed to recreate FW tracer strings DB\n");
1155		return err;
1156	}
1157	err = mlx5_fw_tracer_init(tracer);
1158	if (err) {
1159		mlx5_core_warn(dev, "Failed to re-initialize FW tracer\n");
1160		return err;
1161	}
1162
1163	return 0;
1164}
1165
1166static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data)
1167{
1168	struct mlx5_fw_tracer *tracer = mlx5_nb_cof(nb, struct mlx5_fw_tracer, nb);
1169	struct mlx5_core_dev *dev = tracer->dev;
1170	struct mlx5_eqe *eqe = data;
1171
1172	switch (eqe->sub_type) {
1173	case MLX5_TRACER_SUBTYPE_OWNERSHIP_CHANGE:
1174		queue_work(tracer->work_queue, &tracer->ownership_change_work);
1175		break;
1176	case MLX5_TRACER_SUBTYPE_TRACES_AVAILABLE:
1177		queue_work(tracer->work_queue, &tracer->handle_traces_work);
1178		break;
1179	case MLX5_TRACER_SUBTYPE_STRINGS_DB_UPDATE:
1180		queue_work(tracer->work_queue, &tracer->update_db_work);
1181		break;
1182	default:
1183		mlx5_core_dbg(dev, "FWTracer: Event with unrecognized subtype: sub_type %d\n",
1184			      eqe->sub_type);
1185	}
1186
1187	return NOTIFY_OK;
1188}
1189
1190EXPORT_TRACEPOINT_SYMBOL(mlx5_fw);
1191