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