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 "fw_tracer.h"
34 #include "fw_tracer_tracepoint.h"
35
mlx5_query_mtrc_caps(struct mlx5_fw_tracer * tracer)36 static int mlx5_query_mtrc_caps(struct mlx5_fw_tracer *tracer)
37 {
38 u32 *string_db_base_address_out = tracer->str_db.base_address_out;
39 u32 *string_db_size_out = tracer->str_db.size_out;
40 struct mlx5_core_dev *dev = tracer->dev;
41 u32 out[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
42 u32 in[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
43 void *mtrc_cap_sp;
44 int err, i;
45
46 err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
47 MLX5_REG_MTRC_CAP, 0, 0);
48 if (err) {
49 mlx5_core_warn(dev, "FWTracer: Error reading tracer caps %d\n",
50 err);
51 return err;
52 }
53
54 if (!MLX5_GET(mtrc_cap, out, trace_to_memory)) {
55 mlx5_core_dbg(dev, "FWTracer: Device does not support logging traces to memory\n");
56 return -ENOTSUPP;
57 }
58
59 tracer->trc_ver = MLX5_GET(mtrc_cap, out, trc_ver);
60 tracer->str_db.first_string_trace =
61 MLX5_GET(mtrc_cap, out, first_string_trace);
62 tracer->str_db.num_string_trace =
63 MLX5_GET(mtrc_cap, out, num_string_trace);
64 tracer->str_db.num_string_db = MLX5_GET(mtrc_cap, out, num_string_db);
65 tracer->owner = !!MLX5_GET(mtrc_cap, out, trace_owner);
66
67 for (i = 0; i < tracer->str_db.num_string_db; i++) {
68 mtrc_cap_sp = MLX5_ADDR_OF(mtrc_cap, out, string_db_param[i]);
69 string_db_base_address_out[i] = MLX5_GET(mtrc_string_db_param,
70 mtrc_cap_sp,
71 string_db_base_address);
72 string_db_size_out[i] = MLX5_GET(mtrc_string_db_param,
73 mtrc_cap_sp, string_db_size);
74 }
75
76 return err;
77 }
78
mlx5_set_mtrc_caps_trace_owner(struct mlx5_fw_tracer * tracer,u32 * out,u32 out_size,u8 trace_owner)79 static int mlx5_set_mtrc_caps_trace_owner(struct mlx5_fw_tracer *tracer,
80 u32 *out, u32 out_size,
81 u8 trace_owner)
82 {
83 struct mlx5_core_dev *dev = tracer->dev;
84 u32 in[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
85
86 MLX5_SET(mtrc_cap, in, trace_owner, trace_owner);
87
88 return mlx5_core_access_reg(dev, in, sizeof(in), out, out_size,
89 MLX5_REG_MTRC_CAP, 0, 1);
90 }
91
mlx5_fw_tracer_ownership_acquire(struct mlx5_fw_tracer * tracer)92 static int mlx5_fw_tracer_ownership_acquire(struct mlx5_fw_tracer *tracer)
93 {
94 struct mlx5_core_dev *dev = tracer->dev;
95 u32 out[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
96 int err;
97
98 err = mlx5_set_mtrc_caps_trace_owner(tracer, out, sizeof(out),
99 MLX5_FW_TRACER_ACQUIRE_OWNERSHIP);
100 if (err) {
101 mlx5_core_warn(dev, "FWTracer: Acquire tracer ownership failed %d\n",
102 err);
103 return err;
104 }
105
106 tracer->owner = !!MLX5_GET(mtrc_cap, out, trace_owner);
107
108 if (!tracer->owner)
109 return -EBUSY;
110
111 return 0;
112 }
113
mlx5_fw_tracer_ownership_release(struct mlx5_fw_tracer * tracer)114 static void mlx5_fw_tracer_ownership_release(struct mlx5_fw_tracer *tracer)
115 {
116 u32 out[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
117
118 mlx5_set_mtrc_caps_trace_owner(tracer, out, sizeof(out),
119 MLX5_FW_TRACER_RELEASE_OWNERSHIP);
120 tracer->owner = false;
121 }
122
mlx5_fw_tracer_create_log_buf(struct mlx5_fw_tracer * tracer)123 static int mlx5_fw_tracer_create_log_buf(struct mlx5_fw_tracer *tracer)
124 {
125 struct mlx5_core_dev *dev = tracer->dev;
126 struct device *ddev = &dev->pdev->dev;
127 dma_addr_t dma;
128 void *buff;
129 gfp_t gfp;
130 int err;
131
132 tracer->buff.size = TRACE_BUFFER_SIZE_BYTE;
133
134 gfp = GFP_KERNEL | __GFP_ZERO;
135 buff = (void *)__get_free_pages(gfp,
136 get_order(tracer->buff.size));
137 if (!buff) {
138 err = -ENOMEM;
139 mlx5_core_warn(dev, "FWTracer: Failed to allocate pages, %d\n", err);
140 return err;
141 }
142 tracer->buff.log_buf = buff;
143
144 dma = dma_map_single(ddev, buff, tracer->buff.size, DMA_FROM_DEVICE);
145 if (dma_mapping_error(ddev, dma)) {
146 mlx5_core_warn(dev, "FWTracer: Unable to map DMA: %d\n",
147 dma_mapping_error(ddev, dma));
148 err = -ENOMEM;
149 goto free_pages;
150 }
151 tracer->buff.dma = dma;
152
153 return 0;
154
155 free_pages:
156 free_pages((unsigned long)tracer->buff.log_buf, get_order(tracer->buff.size));
157
158 return err;
159 }
160
mlx5_fw_tracer_destroy_log_buf(struct mlx5_fw_tracer * tracer)161 static void mlx5_fw_tracer_destroy_log_buf(struct mlx5_fw_tracer *tracer)
162 {
163 struct mlx5_core_dev *dev = tracer->dev;
164 struct device *ddev = &dev->pdev->dev;
165
166 if (!tracer->buff.log_buf)
167 return;
168
169 dma_unmap_single(ddev, tracer->buff.dma, tracer->buff.size, DMA_FROM_DEVICE);
170 free_pages((unsigned long)tracer->buff.log_buf, get_order(tracer->buff.size));
171 }
172
mlx5_fw_tracer_create_mkey(struct mlx5_fw_tracer * tracer)173 static int mlx5_fw_tracer_create_mkey(struct mlx5_fw_tracer *tracer)
174 {
175 struct mlx5_core_dev *dev = tracer->dev;
176 int err, inlen, i;
177 __be64 *mtt;
178 void *mkc;
179 u32 *in;
180
181 inlen = MLX5_ST_SZ_BYTES(create_mkey_in) +
182 sizeof(*mtt) * round_up(TRACER_BUFFER_PAGE_NUM, 2);
183
184 in = kvzalloc(inlen, GFP_KERNEL);
185 if (!in)
186 return -ENOMEM;
187
188 MLX5_SET(create_mkey_in, in, translations_octword_actual_size,
189 DIV_ROUND_UP(TRACER_BUFFER_PAGE_NUM, 2));
190 mtt = (u64 *)MLX5_ADDR_OF(create_mkey_in, in, klm_pas_mtt);
191 for (i = 0 ; i < TRACER_BUFFER_PAGE_NUM ; i++)
192 mtt[i] = cpu_to_be64(tracer->buff.dma + i * PAGE_SIZE);
193
194 mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry);
195 MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_MTT);
196 MLX5_SET(mkc, mkc, lr, 1);
197 MLX5_SET(mkc, mkc, lw, 1);
198 MLX5_SET(mkc, mkc, pd, tracer->buff.pdn);
199 MLX5_SET(mkc, mkc, bsf_octword_size, 0);
200 MLX5_SET(mkc, mkc, qpn, 0xffffff);
201 MLX5_SET(mkc, mkc, log_page_size, PAGE_SHIFT);
202 MLX5_SET(mkc, mkc, translations_octword_size,
203 DIV_ROUND_UP(TRACER_BUFFER_PAGE_NUM, 2));
204 MLX5_SET64(mkc, mkc, start_addr, tracer->buff.dma);
205 MLX5_SET64(mkc, mkc, len, tracer->buff.size);
206 err = mlx5_core_create_mkey(dev, &tracer->buff.mkey, in, inlen);
207 if (err)
208 mlx5_core_warn(dev, "FWTracer: Failed to create mkey, %d\n", err);
209
210 kvfree(in);
211
212 return err;
213 }
214
mlx5_fw_tracer_free_strings_db(struct mlx5_fw_tracer * tracer)215 static void mlx5_fw_tracer_free_strings_db(struct mlx5_fw_tracer *tracer)
216 {
217 u32 num_string_db = tracer->str_db.num_string_db;
218 int i;
219
220 for (i = 0; i < num_string_db; i++) {
221 kfree(tracer->str_db.buffer[i]);
222 tracer->str_db.buffer[i] = NULL;
223 }
224 }
225
mlx5_fw_tracer_allocate_strings_db(struct mlx5_fw_tracer * tracer)226 static int mlx5_fw_tracer_allocate_strings_db(struct mlx5_fw_tracer *tracer)
227 {
228 u32 *string_db_size_out = tracer->str_db.size_out;
229 u32 num_string_db = tracer->str_db.num_string_db;
230 int i;
231
232 for (i = 0; i < num_string_db; i++) {
233 tracer->str_db.buffer[i] = kzalloc(string_db_size_out[i], GFP_KERNEL);
234 if (!tracer->str_db.buffer[i])
235 goto free_strings_db;
236 }
237
238 return 0;
239
240 free_strings_db:
241 mlx5_fw_tracer_free_strings_db(tracer);
242 return -ENOMEM;
243 }
244
mlx5_tracer_read_strings_db(struct work_struct * work)245 static void mlx5_tracer_read_strings_db(struct work_struct *work)
246 {
247 struct mlx5_fw_tracer *tracer = container_of(work, struct mlx5_fw_tracer,
248 read_fw_strings_work);
249 u32 num_of_reads, num_string_db = tracer->str_db.num_string_db;
250 struct mlx5_core_dev *dev = tracer->dev;
251 u32 in[MLX5_ST_SZ_DW(mtrc_cap)] = {0};
252 u32 leftovers, offset;
253 int err = 0, i, j;
254 u32 *out, outlen;
255 void *out_value;
256
257 outlen = MLX5_ST_SZ_BYTES(mtrc_stdb) + STRINGS_DB_READ_SIZE_BYTES;
258 out = kzalloc(outlen, GFP_KERNEL);
259 if (!out) {
260 err = -ENOMEM;
261 goto out;
262 }
263
264 for (i = 0; i < num_string_db; i++) {
265 offset = 0;
266 MLX5_SET(mtrc_stdb, in, string_db_index, i);
267 num_of_reads = tracer->str_db.size_out[i] /
268 STRINGS_DB_READ_SIZE_BYTES;
269 leftovers = (tracer->str_db.size_out[i] %
270 STRINGS_DB_READ_SIZE_BYTES) /
271 STRINGS_DB_LEFTOVER_SIZE_BYTES;
272
273 MLX5_SET(mtrc_stdb, in, read_size, STRINGS_DB_READ_SIZE_BYTES);
274 for (j = 0; j < num_of_reads; j++) {
275 MLX5_SET(mtrc_stdb, in, start_offset, offset);
276
277 err = mlx5_core_access_reg(dev, in, sizeof(in), out,
278 outlen, MLX5_REG_MTRC_STDB,
279 0, 1);
280 if (err) {
281 mlx5_core_dbg(dev, "FWTracer: Failed to read strings DB %d\n",
282 err);
283 goto out_free;
284 }
285
286 out_value = MLX5_ADDR_OF(mtrc_stdb, out, string_db_data);
287 memcpy(tracer->str_db.buffer[i] + offset, out_value,
288 STRINGS_DB_READ_SIZE_BYTES);
289 offset += STRINGS_DB_READ_SIZE_BYTES;
290 }
291
292 /* Strings database is aligned to 64, need to read leftovers*/
293 MLX5_SET(mtrc_stdb, in, read_size,
294 STRINGS_DB_LEFTOVER_SIZE_BYTES);
295 for (j = 0; j < leftovers; 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_LEFTOVER_SIZE_BYTES);
310 offset += STRINGS_DB_LEFTOVER_SIZE_BYTES;
311 }
312 }
313
314 tracer->str_db.loaded = true;
315
316 out_free:
317 kfree(out);
318 out:
319 return;
320 }
321
mlx5_fw_tracer_arm(struct mlx5_core_dev * dev)322 static void mlx5_fw_tracer_arm(struct mlx5_core_dev *dev)
323 {
324 u32 out[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
325 u32 in[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
326 int err;
327
328 MLX5_SET(mtrc_ctrl, in, arm_event, 1);
329
330 err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
331 MLX5_REG_MTRC_CTRL, 0, 1);
332 if (err)
333 mlx5_core_warn(dev, "FWTracer: Failed to arm tracer event %d\n", err);
334 }
335
336 static const char *VAL_PARM = "%llx";
337 static const char *REPLACE_64_VAL_PARM = "%x%x";
338 static const char *PARAM_CHAR = "%";
339
mlx5_tracer_message_hash(u32 message_id)340 static int mlx5_tracer_message_hash(u32 message_id)
341 {
342 return jhash_1word(message_id, 0) & (MESSAGE_HASH_SIZE - 1);
343 }
344
mlx5_tracer_message_insert(struct mlx5_fw_tracer * tracer,struct tracer_event * tracer_event)345 static struct tracer_string_format *mlx5_tracer_message_insert(struct mlx5_fw_tracer *tracer,
346 struct tracer_event *tracer_event)
347 {
348 struct hlist_head *head =
349 &tracer->hash[mlx5_tracer_message_hash(tracer_event->string_event.tmsn)];
350 struct tracer_string_format *cur_string;
351
352 cur_string = kzalloc(sizeof(*cur_string), GFP_KERNEL);
353 if (!cur_string)
354 return NULL;
355
356 hlist_add_head(&cur_string->hlist, head);
357
358 return cur_string;
359 }
360
mlx5_tracer_get_string(struct mlx5_fw_tracer * tracer,struct tracer_event * tracer_event)361 static struct tracer_string_format *mlx5_tracer_get_string(struct mlx5_fw_tracer *tracer,
362 struct tracer_event *tracer_event)
363 {
364 struct tracer_string_format *cur_string;
365 u32 str_ptr, offset;
366 int i;
367
368 str_ptr = tracer_event->string_event.string_param;
369
370 for (i = 0; i < tracer->str_db.num_string_db; i++) {
371 if (str_ptr > tracer->str_db.base_address_out[i] &&
372 str_ptr < tracer->str_db.base_address_out[i] +
373 tracer->str_db.size_out[i]) {
374 offset = str_ptr - tracer->str_db.base_address_out[i];
375 /* add it to the hash */
376 cur_string = mlx5_tracer_message_insert(tracer, tracer_event);
377 if (!cur_string)
378 return NULL;
379 cur_string->string = (char *)(tracer->str_db.buffer[i] +
380 offset);
381 return cur_string;
382 }
383 }
384
385 return NULL;
386 }
387
mlx5_tracer_clean_message(struct tracer_string_format * str_frmt)388 static void mlx5_tracer_clean_message(struct tracer_string_format *str_frmt)
389 {
390 hlist_del(&str_frmt->hlist);
391 kfree(str_frmt);
392 }
393
mlx5_tracer_get_num_of_params(char * str)394 static int mlx5_tracer_get_num_of_params(char *str)
395 {
396 char *substr, *pstr = str;
397 int num_of_params = 0;
398
399 /* replace %llx with %x%x */
400 substr = strstr(pstr, VAL_PARM);
401 while (substr) {
402 memcpy(substr, REPLACE_64_VAL_PARM, 4);
403 pstr = substr;
404 substr = strstr(pstr, VAL_PARM);
405 }
406
407 /* count all the % characters */
408 substr = strstr(str, PARAM_CHAR);
409 while (substr) {
410 num_of_params += 1;
411 str = substr + 1;
412 substr = strstr(str, PARAM_CHAR);
413 }
414
415 return num_of_params;
416 }
417
mlx5_tracer_message_find(struct hlist_head * head,u8 event_id,u32 tmsn)418 static struct tracer_string_format *mlx5_tracer_message_find(struct hlist_head *head,
419 u8 event_id, u32 tmsn)
420 {
421 struct tracer_string_format *message;
422
423 hlist_for_each_entry(message, head, hlist)
424 if (message->event_id == event_id && message->tmsn == tmsn)
425 return message;
426
427 return NULL;
428 }
429
mlx5_tracer_message_get(struct mlx5_fw_tracer * tracer,struct tracer_event * tracer_event)430 static struct tracer_string_format *mlx5_tracer_message_get(struct mlx5_fw_tracer *tracer,
431 struct tracer_event *tracer_event)
432 {
433 struct hlist_head *head =
434 &tracer->hash[mlx5_tracer_message_hash(tracer_event->string_event.tmsn)];
435
436 return mlx5_tracer_message_find(head, tracer_event->event_id, tracer_event->string_event.tmsn);
437 }
438
poll_trace(struct mlx5_fw_tracer * tracer,struct tracer_event * tracer_event,u64 * trace)439 static void poll_trace(struct mlx5_fw_tracer *tracer,
440 struct tracer_event *tracer_event, u64 *trace)
441 {
442 u32 timestamp_low, timestamp_mid, timestamp_high, urts;
443
444 tracer_event->event_id = MLX5_GET(tracer_event, trace, event_id);
445 tracer_event->lost_event = MLX5_GET(tracer_event, trace, lost);
446
447 switch (tracer_event->event_id) {
448 case TRACER_EVENT_TYPE_TIMESTAMP:
449 tracer_event->type = TRACER_EVENT_TYPE_TIMESTAMP;
450 urts = MLX5_GET(tracer_timestamp_event, trace, urts);
451 if (tracer->trc_ver == 0)
452 tracer_event->timestamp_event.unreliable = !!(urts >> 2);
453 else
454 tracer_event->timestamp_event.unreliable = !!(urts & 1);
455
456 timestamp_low = MLX5_GET(tracer_timestamp_event,
457 trace, timestamp7_0);
458 timestamp_mid = MLX5_GET(tracer_timestamp_event,
459 trace, timestamp39_8);
460 timestamp_high = MLX5_GET(tracer_timestamp_event,
461 trace, timestamp52_40);
462
463 tracer_event->timestamp_event.timestamp =
464 ((u64)timestamp_high << 40) |
465 ((u64)timestamp_mid << 8) |
466 (u64)timestamp_low;
467 break;
468 default:
469 if (tracer_event->event_id >= tracer->str_db.first_string_trace ||
470 tracer_event->event_id <= tracer->str_db.first_string_trace +
471 tracer->str_db.num_string_trace) {
472 tracer_event->type = TRACER_EVENT_TYPE_STRING;
473 tracer_event->string_event.timestamp =
474 MLX5_GET(tracer_string_event, trace, timestamp);
475 tracer_event->string_event.string_param =
476 MLX5_GET(tracer_string_event, trace, string_param);
477 tracer_event->string_event.tmsn =
478 MLX5_GET(tracer_string_event, trace, tmsn);
479 tracer_event->string_event.tdsn =
480 MLX5_GET(tracer_string_event, trace, tdsn);
481 } else {
482 tracer_event->type = TRACER_EVENT_TYPE_UNRECOGNIZED;
483 }
484 break;
485 }
486 }
487
get_block_timestamp(struct mlx5_fw_tracer * tracer,u64 * ts_event)488 static u64 get_block_timestamp(struct mlx5_fw_tracer *tracer, u64 *ts_event)
489 {
490 struct tracer_event tracer_event;
491 u8 event_id;
492
493 event_id = MLX5_GET(tracer_event, ts_event, event_id);
494
495 if (event_id == TRACER_EVENT_TYPE_TIMESTAMP)
496 poll_trace(tracer, &tracer_event, ts_event);
497 else
498 tracer_event.timestamp_event.timestamp = 0;
499
500 return tracer_event.timestamp_event.timestamp;
501 }
502
mlx5_fw_tracer_clean_print_hash(struct mlx5_fw_tracer * tracer)503 static void mlx5_fw_tracer_clean_print_hash(struct mlx5_fw_tracer *tracer)
504 {
505 struct tracer_string_format *str_frmt;
506 struct hlist_node *n;
507 int i;
508
509 for (i = 0; i < MESSAGE_HASH_SIZE; i++) {
510 hlist_for_each_entry_safe(str_frmt, n, &tracer->hash[i], hlist)
511 mlx5_tracer_clean_message(str_frmt);
512 }
513 }
514
mlx5_fw_tracer_clean_ready_list(struct mlx5_fw_tracer * tracer)515 static void mlx5_fw_tracer_clean_ready_list(struct mlx5_fw_tracer *tracer)
516 {
517 struct tracer_string_format *str_frmt, *tmp_str;
518
519 list_for_each_entry_safe(str_frmt, tmp_str, &tracer->ready_strings_list,
520 list)
521 list_del(&str_frmt->list);
522 }
523
mlx5_tracer_print_trace(struct tracer_string_format * str_frmt,struct mlx5_core_dev * dev,u64 trace_timestamp)524 static void mlx5_tracer_print_trace(struct tracer_string_format *str_frmt,
525 struct mlx5_core_dev *dev,
526 u64 trace_timestamp)
527 {
528 char tmp[512];
529
530 snprintf(tmp, sizeof(tmp), str_frmt->string,
531 str_frmt->params[0],
532 str_frmt->params[1],
533 str_frmt->params[2],
534 str_frmt->params[3],
535 str_frmt->params[4],
536 str_frmt->params[5],
537 str_frmt->params[6]);
538
539 trace_mlx5_fw(dev->tracer, trace_timestamp, str_frmt->lost,
540 str_frmt->event_id, tmp);
541
542 /* remove it from hash */
543 mlx5_tracer_clean_message(str_frmt);
544 }
545
mlx5_tracer_handle_string_trace(struct mlx5_fw_tracer * tracer,struct tracer_event * tracer_event)546 static int mlx5_tracer_handle_string_trace(struct mlx5_fw_tracer *tracer,
547 struct tracer_event *tracer_event)
548 {
549 struct tracer_string_format *cur_string;
550
551 if (tracer_event->string_event.tdsn == 0) {
552 cur_string = mlx5_tracer_get_string(tracer, tracer_event);
553 if (!cur_string)
554 return -1;
555
556 cur_string->num_of_params = mlx5_tracer_get_num_of_params(cur_string->string);
557 cur_string->last_param_num = 0;
558 cur_string->event_id = tracer_event->event_id;
559 cur_string->tmsn = tracer_event->string_event.tmsn;
560 cur_string->timestamp = tracer_event->string_event.timestamp;
561 cur_string->lost = tracer_event->lost_event;
562 if (cur_string->num_of_params == 0) /* trace with no params */
563 list_add_tail(&cur_string->list, &tracer->ready_strings_list);
564 } else {
565 cur_string = mlx5_tracer_message_get(tracer, tracer_event);
566 if (!cur_string) {
567 pr_debug("%s Got string event for unknown string tdsm: %d\n",
568 __func__, tracer_event->string_event.tmsn);
569 return -1;
570 }
571 cur_string->last_param_num += 1;
572 if (cur_string->last_param_num > TRACER_MAX_PARAMS) {
573 pr_debug("%s Number of params exceeds the max (%d)\n",
574 __func__, TRACER_MAX_PARAMS);
575 list_add_tail(&cur_string->list, &tracer->ready_strings_list);
576 return 0;
577 }
578 /* keep the new parameter */
579 cur_string->params[cur_string->last_param_num - 1] =
580 tracer_event->string_event.string_param;
581 if (cur_string->last_param_num == cur_string->num_of_params)
582 list_add_tail(&cur_string->list, &tracer->ready_strings_list);
583 }
584
585 return 0;
586 }
587
mlx5_tracer_handle_timestamp_trace(struct mlx5_fw_tracer * tracer,struct tracer_event * tracer_event)588 static void mlx5_tracer_handle_timestamp_trace(struct mlx5_fw_tracer *tracer,
589 struct tracer_event *tracer_event)
590 {
591 struct tracer_timestamp_event timestamp_event =
592 tracer_event->timestamp_event;
593 struct tracer_string_format *str_frmt, *tmp_str;
594 struct mlx5_core_dev *dev = tracer->dev;
595 u64 trace_timestamp;
596
597 list_for_each_entry_safe(str_frmt, tmp_str, &tracer->ready_strings_list, list) {
598 list_del(&str_frmt->list);
599 if (str_frmt->timestamp < (timestamp_event.timestamp & MASK_6_0))
600 trace_timestamp = (timestamp_event.timestamp & MASK_52_7) |
601 (str_frmt->timestamp & MASK_6_0);
602 else
603 trace_timestamp = ((timestamp_event.timestamp & MASK_52_7) - 1) |
604 (str_frmt->timestamp & MASK_6_0);
605
606 mlx5_tracer_print_trace(str_frmt, dev, trace_timestamp);
607 }
608 }
609
mlx5_tracer_handle_trace(struct mlx5_fw_tracer * tracer,struct tracer_event * tracer_event)610 static int mlx5_tracer_handle_trace(struct mlx5_fw_tracer *tracer,
611 struct tracer_event *tracer_event)
612 {
613 if (tracer_event->type == TRACER_EVENT_TYPE_STRING) {
614 mlx5_tracer_handle_string_trace(tracer, tracer_event);
615 } else if (tracer_event->type == TRACER_EVENT_TYPE_TIMESTAMP) {
616 if (!tracer_event->timestamp_event.unreliable)
617 mlx5_tracer_handle_timestamp_trace(tracer, tracer_event);
618 } else {
619 pr_debug("%s Got unrecognised type %d for parsing, exiting..\n",
620 __func__, tracer_event->type);
621 }
622 return 0;
623 }
624
mlx5_fw_tracer_handle_traces(struct work_struct * work)625 static void mlx5_fw_tracer_handle_traces(struct work_struct *work)
626 {
627 struct mlx5_fw_tracer *tracer =
628 container_of(work, struct mlx5_fw_tracer, handle_traces_work);
629 u64 block_timestamp, last_block_timestamp, tmp_trace_block[TRACES_PER_BLOCK];
630 u32 block_count, start_offset, prev_start_offset, prev_consumer_index;
631 u32 trace_event_size = MLX5_ST_SZ_BYTES(tracer_event);
632 struct mlx5_core_dev *dev = tracer->dev;
633 struct tracer_event tracer_event;
634 int i;
635
636 mlx5_core_dbg(dev, "FWTracer: Handle Trace event, owner=(%d)\n", tracer->owner);
637 if (!tracer->owner)
638 return;
639
640 block_count = tracer->buff.size / TRACER_BLOCK_SIZE_BYTE;
641 start_offset = tracer->buff.consumer_index * TRACER_BLOCK_SIZE_BYTE;
642
643 /* Copy the block to local buffer to avoid HW override while being processed*/
644 memcpy(tmp_trace_block, tracer->buff.log_buf + start_offset,
645 TRACER_BLOCK_SIZE_BYTE);
646
647 block_timestamp =
648 get_block_timestamp(tracer, &tmp_trace_block[TRACES_PER_BLOCK - 1]);
649
650 while (block_timestamp > tracer->last_timestamp) {
651 /* Check block override if its not the first block */
652 if (!tracer->last_timestamp) {
653 u64 *ts_event;
654 /* To avoid block override be the HW in case of buffer
655 * wraparound, the time stamp of the previous block
656 * should be compared to the last timestamp handled
657 * by the driver.
658 */
659 prev_consumer_index =
660 (tracer->buff.consumer_index - 1) & (block_count - 1);
661 prev_start_offset = prev_consumer_index * TRACER_BLOCK_SIZE_BYTE;
662
663 ts_event = tracer->buff.log_buf + prev_start_offset +
664 (TRACES_PER_BLOCK - 1) * trace_event_size;
665 last_block_timestamp = get_block_timestamp(tracer, ts_event);
666 /* If previous timestamp different from last stored
667 * timestamp then there is a good chance that the
668 * current buffer is overwritten and therefore should
669 * not be parsed.
670 */
671 if (tracer->last_timestamp != last_block_timestamp) {
672 mlx5_core_warn(dev, "FWTracer: Events were lost\n");
673 tracer->last_timestamp = block_timestamp;
674 tracer->buff.consumer_index =
675 (tracer->buff.consumer_index + 1) & (block_count - 1);
676 break;
677 }
678 }
679
680 /* Parse events */
681 for (i = 0; i < TRACES_PER_BLOCK ; i++) {
682 poll_trace(tracer, &tracer_event, &tmp_trace_block[i]);
683 mlx5_tracer_handle_trace(tracer, &tracer_event);
684 }
685
686 tracer->buff.consumer_index =
687 (tracer->buff.consumer_index + 1) & (block_count - 1);
688
689 tracer->last_timestamp = block_timestamp;
690 start_offset = tracer->buff.consumer_index * TRACER_BLOCK_SIZE_BYTE;
691 memcpy(tmp_trace_block, tracer->buff.log_buf + start_offset,
692 TRACER_BLOCK_SIZE_BYTE);
693 block_timestamp = get_block_timestamp(tracer,
694 &tmp_trace_block[TRACES_PER_BLOCK - 1]);
695 }
696
697 mlx5_fw_tracer_arm(dev);
698 }
699
mlx5_fw_tracer_set_mtrc_conf(struct mlx5_fw_tracer * tracer)700 static int mlx5_fw_tracer_set_mtrc_conf(struct mlx5_fw_tracer *tracer)
701 {
702 struct mlx5_core_dev *dev = tracer->dev;
703 u32 out[MLX5_ST_SZ_DW(mtrc_conf)] = {0};
704 u32 in[MLX5_ST_SZ_DW(mtrc_conf)] = {0};
705 int err;
706
707 MLX5_SET(mtrc_conf, in, trace_mode, TRACE_TO_MEMORY);
708 MLX5_SET(mtrc_conf, in, log_trace_buffer_size,
709 ilog2(TRACER_BUFFER_PAGE_NUM));
710 MLX5_SET(mtrc_conf, in, trace_mkey, tracer->buff.mkey.key);
711
712 err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
713 MLX5_REG_MTRC_CONF, 0, 1);
714 if (err)
715 mlx5_core_warn(dev, "FWTracer: Failed to set tracer configurations %d\n", err);
716
717 return err;
718 }
719
mlx5_fw_tracer_set_mtrc_ctrl(struct mlx5_fw_tracer * tracer,u8 status,u8 arm)720 static int mlx5_fw_tracer_set_mtrc_ctrl(struct mlx5_fw_tracer *tracer, u8 status, u8 arm)
721 {
722 struct mlx5_core_dev *dev = tracer->dev;
723 u32 out[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
724 u32 in[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
725 int err;
726
727 MLX5_SET(mtrc_ctrl, in, modify_field_select, TRACE_STATUS);
728 MLX5_SET(mtrc_ctrl, in, trace_status, status);
729 MLX5_SET(mtrc_ctrl, in, arm_event, arm);
730
731 err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
732 MLX5_REG_MTRC_CTRL, 0, 1);
733
734 if (!err && status)
735 tracer->last_timestamp = 0;
736
737 return err;
738 }
739
mlx5_fw_tracer_start(struct mlx5_fw_tracer * tracer)740 static int mlx5_fw_tracer_start(struct mlx5_fw_tracer *tracer)
741 {
742 struct mlx5_core_dev *dev = tracer->dev;
743 int err;
744
745 err = mlx5_fw_tracer_ownership_acquire(tracer);
746 if (err) {
747 mlx5_core_dbg(dev, "FWTracer: Ownership was not granted %d\n", err);
748 /* Don't fail since ownership can be acquired on a later FW event */
749 return 0;
750 }
751
752 err = mlx5_fw_tracer_set_mtrc_conf(tracer);
753 if (err) {
754 mlx5_core_warn(dev, "FWTracer: Failed to set tracer configuration %d\n", err);
755 goto release_ownership;
756 }
757
758 /* enable tracer & trace events */
759 err = mlx5_fw_tracer_set_mtrc_ctrl(tracer, 1, 1);
760 if (err) {
761 mlx5_core_warn(dev, "FWTracer: Failed to enable tracer %d\n", err);
762 goto release_ownership;
763 }
764
765 mlx5_core_dbg(dev, "FWTracer: Ownership granted and active\n");
766 return 0;
767
768 release_ownership:
769 mlx5_fw_tracer_ownership_release(tracer);
770 return err;
771 }
772
mlx5_fw_tracer_ownership_change(struct work_struct * work)773 static void mlx5_fw_tracer_ownership_change(struct work_struct *work)
774 {
775 struct mlx5_fw_tracer *tracer =
776 container_of(work, struct mlx5_fw_tracer, ownership_change_work);
777
778 mlx5_core_dbg(tracer->dev, "FWTracer: ownership changed, current=(%d)\n", tracer->owner);
779 if (tracer->owner) {
780 tracer->owner = false;
781 tracer->buff.consumer_index = 0;
782 return;
783 }
784
785 mlx5_fw_tracer_start(tracer);
786 }
787
788 /* Create software resources (Buffers, etc ..) */
mlx5_fw_tracer_create(struct mlx5_core_dev * dev)789 struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
790 {
791 struct mlx5_fw_tracer *tracer = NULL;
792 int err;
793
794 if (!MLX5_CAP_MCAM_REG(dev, tracer_registers)) {
795 mlx5_core_dbg(dev, "FWTracer: Tracer capability not present\n");
796 return NULL;
797 }
798
799 tracer = kzalloc(sizeof(*tracer), GFP_KERNEL);
800 if (!tracer)
801 return ERR_PTR(-ENOMEM);
802
803 tracer->work_queue = create_singlethread_workqueue("mlx5_fw_tracer");
804 if (!tracer->work_queue) {
805 err = -ENOMEM;
806 goto free_tracer;
807 }
808
809 tracer->dev = dev;
810
811 INIT_LIST_HEAD(&tracer->ready_strings_list);
812 INIT_WORK(&tracer->ownership_change_work, mlx5_fw_tracer_ownership_change);
813 INIT_WORK(&tracer->read_fw_strings_work, mlx5_tracer_read_strings_db);
814 INIT_WORK(&tracer->handle_traces_work, mlx5_fw_tracer_handle_traces);
815
816
817 err = mlx5_query_mtrc_caps(tracer);
818 if (err) {
819 mlx5_core_dbg(dev, "FWTracer: Failed to query capabilities %d\n", err);
820 goto destroy_workqueue;
821 }
822
823 err = mlx5_fw_tracer_create_log_buf(tracer);
824 if (err) {
825 mlx5_core_warn(dev, "FWTracer: Create log buffer failed %d\n", err);
826 goto destroy_workqueue;
827 }
828
829 err = mlx5_fw_tracer_allocate_strings_db(tracer);
830 if (err) {
831 mlx5_core_warn(dev, "FWTracer: Allocate strings database failed %d\n", err);
832 goto free_log_buf;
833 }
834
835 mlx5_core_dbg(dev, "FWTracer: Tracer created\n");
836
837 return tracer;
838
839 free_log_buf:
840 mlx5_fw_tracer_destroy_log_buf(tracer);
841 destroy_workqueue:
842 tracer->dev = NULL;
843 destroy_workqueue(tracer->work_queue);
844 free_tracer:
845 kfree(tracer);
846 return ERR_PTR(err);
847 }
848
849 /* Create HW resources + start tracer
850 * must be called before Async EQ is created
851 */
mlx5_fw_tracer_init(struct mlx5_fw_tracer * tracer)852 int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer)
853 {
854 struct mlx5_core_dev *dev;
855 int err;
856
857 if (IS_ERR_OR_NULL(tracer))
858 return 0;
859
860 dev = tracer->dev;
861
862 if (!tracer->str_db.loaded)
863 queue_work(tracer->work_queue, &tracer->read_fw_strings_work);
864
865 err = mlx5_core_alloc_pd(dev, &tracer->buff.pdn);
866 if (err) {
867 mlx5_core_warn(dev, "FWTracer: Failed to allocate PD %d\n", err);
868 return err;
869 }
870
871 err = mlx5_fw_tracer_create_mkey(tracer);
872 if (err) {
873 mlx5_core_warn(dev, "FWTracer: Failed to create mkey %d\n", err);
874 goto err_dealloc_pd;
875 }
876
877 mlx5_fw_tracer_start(tracer);
878
879 return 0;
880
881 err_dealloc_pd:
882 mlx5_core_dealloc_pd(dev, tracer->buff.pdn);
883 return err;
884 }
885
886 /* Stop tracer + Cleanup HW resources
887 * must be called after Async EQ is destroyed
888 */
mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer * tracer)889 void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer)
890 {
891 if (IS_ERR_OR_NULL(tracer))
892 return;
893
894 mlx5_core_dbg(tracer->dev, "FWTracer: Cleanup, is owner ? (%d)\n",
895 tracer->owner);
896
897 cancel_work_sync(&tracer->ownership_change_work);
898 cancel_work_sync(&tracer->handle_traces_work);
899
900 if (tracer->owner)
901 mlx5_fw_tracer_ownership_release(tracer);
902
903 mlx5_core_destroy_mkey(tracer->dev, &tracer->buff.mkey);
904 mlx5_core_dealloc_pd(tracer->dev, tracer->buff.pdn);
905 }
906
907 /* Free software resources (Buffers, etc ..) */
mlx5_fw_tracer_destroy(struct mlx5_fw_tracer * tracer)908 void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer)
909 {
910 if (IS_ERR_OR_NULL(tracer))
911 return;
912
913 mlx5_core_dbg(tracer->dev, "FWTracer: Destroy\n");
914
915 cancel_work_sync(&tracer->read_fw_strings_work);
916 mlx5_fw_tracer_clean_ready_list(tracer);
917 mlx5_fw_tracer_clean_print_hash(tracer);
918 mlx5_fw_tracer_free_strings_db(tracer);
919 mlx5_fw_tracer_destroy_log_buf(tracer);
920 flush_workqueue(tracer->work_queue);
921 destroy_workqueue(tracer->work_queue);
922 kfree(tracer);
923 }
924
mlx5_fw_tracer_event(struct mlx5_core_dev * dev,struct mlx5_eqe * eqe)925 void mlx5_fw_tracer_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe)
926 {
927 struct mlx5_fw_tracer *tracer = dev->tracer;
928
929 if (!tracer)
930 return;
931
932 switch (eqe->sub_type) {
933 case MLX5_TRACER_SUBTYPE_OWNERSHIP_CHANGE:
934 if (test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state))
935 queue_work(tracer->work_queue, &tracer->ownership_change_work);
936 break;
937 case MLX5_TRACER_SUBTYPE_TRACES_AVAILABLE:
938 if (likely(tracer->str_db.loaded))
939 queue_work(tracer->work_queue, &tracer->handle_traces_work);
940 break;
941 default:
942 mlx5_core_dbg(dev, "FWTracer: Event with unrecognized subtype: sub_type %d\n",
943 eqe->sub_type);
944 }
945 }
946
947 EXPORT_TRACEPOINT_SYMBOL(mlx5_fw);
948