GNU Linux-libre 4.19.304-gnu1
[releases.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 "fw_tracer.h"
34 #include "fw_tracer_tracepoint.h"
35
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
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
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
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
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
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
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
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
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
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
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
340 static int mlx5_tracer_message_hash(u32 message_id)
341 {
342         return jhash_1word(message_id, 0) & (MESSAGE_HASH_SIZE - 1);
343 }
344
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
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
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
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
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
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
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
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
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
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
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
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 tmsn: %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
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 - 1) & MASK_52_7) |
604                                           (str_frmt->timestamp & MASK_6_0);
605
606                 mlx5_tracer_print_trace(str_frmt, dev, trace_timestamp);
607         }
608 }
609
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
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         if (unlikely(!tracer->str_db.loaded))
641                 goto arm;
642
643         block_count = tracer->buff.size / TRACER_BLOCK_SIZE_BYTE;
644         start_offset = tracer->buff.consumer_index * TRACER_BLOCK_SIZE_BYTE;
645
646         /* Copy the block to local buffer to avoid HW override while being processed*/
647         memcpy(tmp_trace_block, tracer->buff.log_buf + start_offset,
648                TRACER_BLOCK_SIZE_BYTE);
649
650         block_timestamp =
651                 get_block_timestamp(tracer, &tmp_trace_block[TRACES_PER_BLOCK - 1]);
652
653         while (block_timestamp > tracer->last_timestamp) {
654                 /* Check block override if it's not the first block */
655                 if (tracer->last_timestamp) {
656                         u64 *ts_event;
657                         /* To avoid block override be the HW in case of buffer
658                          * wraparound, the time stamp of the previous block
659                          * should be compared to the last timestamp handled
660                          * by the driver.
661                          */
662                         prev_consumer_index =
663                                 (tracer->buff.consumer_index - 1) & (block_count - 1);
664                         prev_start_offset = prev_consumer_index * TRACER_BLOCK_SIZE_BYTE;
665
666                         ts_event = tracer->buff.log_buf + prev_start_offset +
667                                    (TRACES_PER_BLOCK - 1) * trace_event_size;
668                         last_block_timestamp = get_block_timestamp(tracer, ts_event);
669                         /* If previous timestamp different from last stored
670                          * timestamp then there is a good chance that the
671                          * current buffer is overwritten and therefore should
672                          * not be parsed.
673                          */
674                         if (tracer->last_timestamp != last_block_timestamp) {
675                                 mlx5_core_warn(dev, "FWTracer: Events were lost\n");
676                                 tracer->last_timestamp = block_timestamp;
677                                 tracer->buff.consumer_index =
678                                         (tracer->buff.consumer_index + 1) & (block_count - 1);
679                                 break;
680                         }
681                 }
682
683                 /* Parse events */
684                 for (i = 0; i < TRACES_PER_BLOCK ; i++) {
685                         poll_trace(tracer, &tracer_event, &tmp_trace_block[i]);
686                         mlx5_tracer_handle_trace(tracer, &tracer_event);
687                 }
688
689                 tracer->buff.consumer_index =
690                         (tracer->buff.consumer_index + 1) & (block_count - 1);
691
692                 tracer->last_timestamp = block_timestamp;
693                 start_offset = tracer->buff.consumer_index * TRACER_BLOCK_SIZE_BYTE;
694                 memcpy(tmp_trace_block, tracer->buff.log_buf + start_offset,
695                        TRACER_BLOCK_SIZE_BYTE);
696                 block_timestamp = get_block_timestamp(tracer,
697                                                       &tmp_trace_block[TRACES_PER_BLOCK - 1]);
698         }
699
700 arm:
701         mlx5_fw_tracer_arm(dev);
702 }
703
704 static int mlx5_fw_tracer_set_mtrc_conf(struct mlx5_fw_tracer *tracer)
705 {
706         struct mlx5_core_dev *dev = tracer->dev;
707         u32 out[MLX5_ST_SZ_DW(mtrc_conf)] = {0};
708         u32 in[MLX5_ST_SZ_DW(mtrc_conf)] = {0};
709         int err;
710
711         MLX5_SET(mtrc_conf, in, trace_mode, TRACE_TO_MEMORY);
712         MLX5_SET(mtrc_conf, in, log_trace_buffer_size,
713                  ilog2(TRACER_BUFFER_PAGE_NUM));
714         MLX5_SET(mtrc_conf, in, trace_mkey, tracer->buff.mkey.key);
715
716         err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
717                                    MLX5_REG_MTRC_CONF, 0, 1);
718         if (err)
719                 mlx5_core_warn(dev, "FWTracer: Failed to set tracer configurations %d\n", err);
720
721         return err;
722 }
723
724 static int mlx5_fw_tracer_set_mtrc_ctrl(struct mlx5_fw_tracer *tracer, u8 status, u8 arm)
725 {
726         struct mlx5_core_dev *dev = tracer->dev;
727         u32 out[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
728         u32 in[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0};
729         int err;
730
731         MLX5_SET(mtrc_ctrl, in, modify_field_select, TRACE_STATUS);
732         MLX5_SET(mtrc_ctrl, in, trace_status, status);
733         MLX5_SET(mtrc_ctrl, in, arm_event, arm);
734
735         err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out),
736                                    MLX5_REG_MTRC_CTRL, 0, 1);
737
738         if (!err && status)
739                 tracer->last_timestamp = 0;
740
741         return err;
742 }
743
744 static int mlx5_fw_tracer_start(struct mlx5_fw_tracer *tracer)
745 {
746         struct mlx5_core_dev *dev = tracer->dev;
747         int err;
748
749         err = mlx5_fw_tracer_ownership_acquire(tracer);
750         if (err) {
751                 mlx5_core_dbg(dev, "FWTracer: Ownership was not granted %d\n", err);
752                 /* Don't fail since ownership can be acquired on a later FW event */
753                 return 0;
754         }
755
756         err = mlx5_fw_tracer_set_mtrc_conf(tracer);
757         if (err) {
758                 mlx5_core_warn(dev, "FWTracer: Failed to set tracer configuration %d\n", err);
759                 goto release_ownership;
760         }
761
762         /* enable tracer & trace events */
763         err = mlx5_fw_tracer_set_mtrc_ctrl(tracer, 1, 1);
764         if (err) {
765                 mlx5_core_warn(dev, "FWTracer: Failed to enable tracer %d\n", err);
766                 goto release_ownership;
767         }
768
769         mlx5_core_dbg(dev, "FWTracer: Ownership granted and active\n");
770         return 0;
771
772 release_ownership:
773         mlx5_fw_tracer_ownership_release(tracer);
774         return err;
775 }
776
777 static void mlx5_fw_tracer_ownership_change(struct work_struct *work)
778 {
779         struct mlx5_fw_tracer *tracer =
780                 container_of(work, struct mlx5_fw_tracer, ownership_change_work);
781
782         mlx5_core_dbg(tracer->dev, "FWTracer: ownership changed, current=(%d)\n", tracer->owner);
783         if (tracer->owner) {
784                 tracer->owner = false;
785                 tracer->buff.consumer_index = 0;
786                 return;
787         }
788
789         mlx5_fw_tracer_start(tracer);
790 }
791
792 /* Create software resources (Buffers, etc ..) */
793 struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev)
794 {
795         struct mlx5_fw_tracer *tracer = NULL;
796         int err;
797
798         if (!MLX5_CAP_MCAM_REG(dev, tracer_registers)) {
799                 mlx5_core_dbg(dev, "FWTracer: Tracer capability not present\n");
800                 return NULL;
801         }
802
803         tracer = kvzalloc(sizeof(*tracer), GFP_KERNEL);
804         if (!tracer)
805                 return ERR_PTR(-ENOMEM);
806
807         tracer->work_queue = create_singlethread_workqueue("mlx5_fw_tracer");
808         if (!tracer->work_queue) {
809                 err = -ENOMEM;
810                 goto free_tracer;
811         }
812
813         tracer->dev = dev;
814
815         INIT_LIST_HEAD(&tracer->ready_strings_list);
816         INIT_WORK(&tracer->ownership_change_work, mlx5_fw_tracer_ownership_change);
817         INIT_WORK(&tracer->read_fw_strings_work, mlx5_tracer_read_strings_db);
818         INIT_WORK(&tracer->handle_traces_work, mlx5_fw_tracer_handle_traces);
819
820
821         err = mlx5_query_mtrc_caps(tracer);
822         if (err) {
823                 mlx5_core_dbg(dev, "FWTracer: Failed to query capabilities %d\n", err);
824                 goto destroy_workqueue;
825         }
826
827         err = mlx5_fw_tracer_create_log_buf(tracer);
828         if (err) {
829                 mlx5_core_warn(dev, "FWTracer: Create log buffer failed %d\n", err);
830                 goto destroy_workqueue;
831         }
832
833         err = mlx5_fw_tracer_allocate_strings_db(tracer);
834         if (err) {
835                 mlx5_core_warn(dev, "FWTracer: Allocate strings database failed %d\n", err);
836                 goto free_log_buf;
837         }
838
839         mlx5_core_dbg(dev, "FWTracer: Tracer created\n");
840
841         return tracer;
842
843 free_log_buf:
844         mlx5_fw_tracer_destroy_log_buf(tracer);
845 destroy_workqueue:
846         tracer->dev = NULL;
847         destroy_workqueue(tracer->work_queue);
848 free_tracer:
849         kvfree(tracer);
850         return ERR_PTR(err);
851 }
852
853 /* Create HW resources + start tracer
854  * must be called before Async EQ is created
855  */
856 int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer)
857 {
858         struct mlx5_core_dev *dev;
859         int err;
860
861         if (IS_ERR_OR_NULL(tracer))
862                 return 0;
863
864         dev = tracer->dev;
865
866         if (!tracer->str_db.loaded)
867                 queue_work(tracer->work_queue, &tracer->read_fw_strings_work);
868
869         err = mlx5_core_alloc_pd(dev, &tracer->buff.pdn);
870         if (err) {
871                 mlx5_core_warn(dev, "FWTracer: Failed to allocate PD %d\n", err);
872                 return err;
873         }
874
875         err = mlx5_fw_tracer_create_mkey(tracer);
876         if (err) {
877                 mlx5_core_warn(dev, "FWTracer: Failed to create mkey %d\n", err);
878                 goto err_dealloc_pd;
879         }
880
881         mlx5_fw_tracer_start(tracer);
882
883         return 0;
884
885 err_dealloc_pd:
886         mlx5_core_dealloc_pd(dev, tracer->buff.pdn);
887         return err;
888 }
889
890 /* Stop tracer + Cleanup HW resources
891  * must be called after Async EQ is destroyed
892  */
893 void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer)
894 {
895         if (IS_ERR_OR_NULL(tracer))
896                 return;
897
898         mlx5_core_dbg(tracer->dev, "FWTracer: Cleanup, is owner ? (%d)\n",
899                       tracer->owner);
900
901         cancel_work_sync(&tracer->ownership_change_work);
902         cancel_work_sync(&tracer->handle_traces_work);
903
904         if (tracer->owner)
905                 mlx5_fw_tracer_ownership_release(tracer);
906
907         mlx5_core_destroy_mkey(tracer->dev, &tracer->buff.mkey);
908         mlx5_core_dealloc_pd(tracer->dev, tracer->buff.pdn);
909 }
910
911 /* Free software resources (Buffers, etc ..) */
912 void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer)
913 {
914         if (IS_ERR_OR_NULL(tracer))
915                 return;
916
917         mlx5_core_dbg(tracer->dev, "FWTracer: Destroy\n");
918
919         cancel_work_sync(&tracer->read_fw_strings_work);
920         mlx5_fw_tracer_clean_ready_list(tracer);
921         mlx5_fw_tracer_clean_print_hash(tracer);
922         mlx5_fw_tracer_free_strings_db(tracer);
923         mlx5_fw_tracer_destroy_log_buf(tracer);
924         flush_workqueue(tracer->work_queue);
925         destroy_workqueue(tracer->work_queue);
926         kvfree(tracer);
927 }
928
929 void mlx5_fw_tracer_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe)
930 {
931         struct mlx5_fw_tracer *tracer = dev->tracer;
932
933         if (!tracer)
934                 return;
935
936         switch (eqe->sub_type) {
937         case MLX5_TRACER_SUBTYPE_OWNERSHIP_CHANGE:
938                 if (test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state))
939                         queue_work(tracer->work_queue, &tracer->ownership_change_work);
940                 break;
941         case MLX5_TRACER_SUBTYPE_TRACES_AVAILABLE:
942                 queue_work(tracer->work_queue, &tracer->handle_traces_work);
943                 break;
944         default:
945                 mlx5_core_dbg(dev, "FWTracer: Event with unrecognized subtype: sub_type %d\n",
946                               eqe->sub_type);
947         }
948 }
949
950 EXPORT_TRACEPOINT_SYMBOL(mlx5_fw);