linux/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
<<
>>
Prefs
   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
  36static 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
  79static 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
  92static 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
 114static 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
 123static 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
 155free_pages:
 156        free_pages((unsigned long)tracer->buff.log_buf, get_order(tracer->buff.size));
 157
 158        return err;
 159}
 160
 161static 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
 173static 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
 215static 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
 226static 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
 240free_strings_db:
 241        mlx5_fw_tracer_free_strings_db(tracer);
 242        return -ENOMEM;
 243}
 244
 245static 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
 316out_free:
 317        kfree(out);
 318out:
 319        return;
 320}
 321
 322static 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
 336static const char *VAL_PARM             = "%llx";
 337static const char *REPLACE_64_VAL_PARM  = "%x%x";
 338static const char *PARAM_CHAR           = "%";
 339
 340static int mlx5_tracer_message_hash(u32 message_id)
 341{
 342        return jhash_1word(message_id, 0) & (MESSAGE_HASH_SIZE - 1);
 343}
 344
 345static 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
 361static 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
 388static void mlx5_tracer_clean_message(struct tracer_string_format *str_frmt)
 389{
 390        hlist_del(&str_frmt->hlist);
 391        kfree(str_frmt);
 392}
 393
 394static 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
 418static 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
 430static 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
 439static 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
 488static 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
 503static 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
 515static 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
 524static 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
 546static 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
 588static 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
 610static 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
 625static 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
 700static 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
 720static 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
 740static 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
 768release_ownership:
 769        mlx5_fw_tracer_ownership_release(tracer);
 770        return err;
 771}
 772
 773static 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 ..) */
 789struct 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
 839free_log_buf:
 840        mlx5_fw_tracer_destroy_log_buf(tracer);
 841destroy_workqueue:
 842        tracer->dev = NULL;
 843        destroy_workqueue(tracer->work_queue);
 844free_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 */
 852int 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
 881err_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 */
 889void 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 ..) */
 908void 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
 925void 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
 947EXPORT_TRACEPOINT_SYMBOL(mlx5_fw);
 948