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