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 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 588 static void mlx5_tracer_handle_timestamp_trace(struct mlx5_fw_tracer *tracer, 589 struct tracer_event *tracer_event) 590 { 591 struct tracer_timestamp_event timestamp_event = 592 tracer_event->timestamp_event; 593 struct tracer_string_format *str_frmt, *tmp_str; 594 struct mlx5_core_dev *dev = tracer->dev; 595 u64 trace_timestamp; 596 597 list_for_each_entry_safe(str_frmt, tmp_str, &tracer->ready_strings_list, list) { 598 list_del(&str_frmt->list); 599 if (str_frmt->timestamp < (timestamp_event.timestamp & MASK_6_0)) 600 trace_timestamp = (timestamp_event.timestamp & MASK_52_7) | 601 (str_frmt->timestamp & MASK_6_0); 602 else 603 trace_timestamp = ((timestamp_event.timestamp & MASK_52_7) - 1) | 604 (str_frmt->timestamp & MASK_6_0); 605 606 mlx5_tracer_print_trace(str_frmt, dev, trace_timestamp); 607 } 608 } 609 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 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 700 static int mlx5_fw_tracer_set_mtrc_conf(struct mlx5_fw_tracer *tracer) 701 { 702 struct mlx5_core_dev *dev = tracer->dev; 703 u32 out[MLX5_ST_SZ_DW(mtrc_conf)] = {0}; 704 u32 in[MLX5_ST_SZ_DW(mtrc_conf)] = {0}; 705 int err; 706 707 MLX5_SET(mtrc_conf, in, trace_mode, TRACE_TO_MEMORY); 708 MLX5_SET(mtrc_conf, in, log_trace_buffer_size, 709 ilog2(TRACER_BUFFER_PAGE_NUM)); 710 MLX5_SET(mtrc_conf, in, trace_mkey, tracer->buff.mkey.key); 711 712 err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), 713 MLX5_REG_MTRC_CONF, 0, 1); 714 if (err) 715 mlx5_core_warn(dev, "FWTracer: Failed to set tracer configurations %d\n", err); 716 717 return err; 718 } 719 720 static int mlx5_fw_tracer_set_mtrc_ctrl(struct mlx5_fw_tracer *tracer, u8 status, u8 arm) 721 { 722 struct mlx5_core_dev *dev = tracer->dev; 723 u32 out[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0}; 724 u32 in[MLX5_ST_SZ_DW(mtrc_ctrl)] = {0}; 725 int err; 726 727 MLX5_SET(mtrc_ctrl, in, modify_field_select, TRACE_STATUS); 728 MLX5_SET(mtrc_ctrl, in, trace_status, status); 729 MLX5_SET(mtrc_ctrl, in, arm_event, arm); 730 731 err = mlx5_core_access_reg(dev, in, sizeof(in), out, sizeof(out), 732 MLX5_REG_MTRC_CTRL, 0, 1); 733 734 if (!err && status) 735 tracer->last_timestamp = 0; 736 737 return err; 738 } 739 740 static int mlx5_fw_tracer_start(struct mlx5_fw_tracer *tracer) 741 { 742 struct mlx5_core_dev *dev = tracer->dev; 743 int err; 744 745 err = mlx5_fw_tracer_ownership_acquire(tracer); 746 if (err) { 747 mlx5_core_dbg(dev, "FWTracer: Ownership was not granted %d\n", err); 748 /* Don't fail since ownership can be acquired on a later FW event */ 749 return 0; 750 } 751 752 err = mlx5_fw_tracer_set_mtrc_conf(tracer); 753 if (err) { 754 mlx5_core_warn(dev, "FWTracer: Failed to set tracer configuration %d\n", err); 755 goto release_ownership; 756 } 757 758 /* enable tracer & trace events */ 759 err = mlx5_fw_tracer_set_mtrc_ctrl(tracer, 1, 1); 760 if (err) { 761 mlx5_core_warn(dev, "FWTracer: Failed to enable tracer %d\n", err); 762 goto release_ownership; 763 } 764 765 mlx5_core_dbg(dev, "FWTracer: Ownership granted and active\n"); 766 return 0; 767 768 release_ownership: 769 mlx5_fw_tracer_ownership_release(tracer); 770 return err; 771 } 772 773 static void mlx5_fw_tracer_ownership_change(struct work_struct *work) 774 { 775 struct mlx5_fw_tracer *tracer = 776 container_of(work, struct mlx5_fw_tracer, ownership_change_work); 777 778 mlx5_core_dbg(tracer->dev, "FWTracer: ownership changed, current=(%d)\n", tracer->owner); 779 if (tracer->owner) { 780 tracer->owner = false; 781 tracer->buff.consumer_index = 0; 782 return; 783 } 784 785 mlx5_fw_tracer_start(tracer); 786 } 787 788 /* Create software resources (Buffers, etc ..) */ 789 struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev) 790 { 791 struct mlx5_fw_tracer *tracer = NULL; 792 int err; 793 794 if (!MLX5_CAP_MCAM_REG(dev, tracer_registers)) { 795 mlx5_core_dbg(dev, "FWTracer: Tracer capability not present\n"); 796 return NULL; 797 } 798 799 tracer = kzalloc(sizeof(*tracer), GFP_KERNEL); 800 if (!tracer) 801 return ERR_PTR(-ENOMEM); 802 803 tracer->work_queue = create_singlethread_workqueue("mlx5_fw_tracer"); 804 if (!tracer->work_queue) { 805 err = -ENOMEM; 806 goto free_tracer; 807 } 808 809 tracer->dev = dev; 810 811 INIT_LIST_HEAD(&tracer->ready_strings_list); 812 INIT_WORK(&tracer->ownership_change_work, mlx5_fw_tracer_ownership_change); 813 INIT_WORK(&tracer->read_fw_strings_work, mlx5_tracer_read_strings_db); 814 INIT_WORK(&tracer->handle_traces_work, mlx5_fw_tracer_handle_traces); 815 816 817 err = mlx5_query_mtrc_caps(tracer); 818 if (err) { 819 mlx5_core_dbg(dev, "FWTracer: Failed to query capabilities %d\n", err); 820 goto destroy_workqueue; 821 } 822 823 err = mlx5_fw_tracer_create_log_buf(tracer); 824 if (err) { 825 mlx5_core_warn(dev, "FWTracer: Create log buffer failed %d\n", err); 826 goto destroy_workqueue; 827 } 828 829 err = mlx5_fw_tracer_allocate_strings_db(tracer); 830 if (err) { 831 mlx5_core_warn(dev, "FWTracer: Allocate strings database failed %d\n", err); 832 goto free_log_buf; 833 } 834 835 mlx5_core_dbg(dev, "FWTracer: Tracer created\n"); 836 837 return tracer; 838 839 free_log_buf: 840 mlx5_fw_tracer_destroy_log_buf(tracer); 841 destroy_workqueue: 842 tracer->dev = NULL; 843 destroy_workqueue(tracer->work_queue); 844 free_tracer: 845 kfree(tracer); 846 return ERR_PTR(err); 847 } 848 849 /* Create HW resources + start tracer 850 * must be called before Async EQ is created 851 */ 852 int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer) 853 { 854 struct mlx5_core_dev *dev; 855 int err; 856 857 if (IS_ERR_OR_NULL(tracer)) 858 return 0; 859 860 dev = tracer->dev; 861 862 if (!tracer->str_db.loaded) 863 queue_work(tracer->work_queue, &tracer->read_fw_strings_work); 864 865 err = mlx5_core_alloc_pd(dev, &tracer->buff.pdn); 866 if (err) { 867 mlx5_core_warn(dev, "FWTracer: Failed to allocate PD %d\n", err); 868 return err; 869 } 870 871 err = mlx5_fw_tracer_create_mkey(tracer); 872 if (err) { 873 mlx5_core_warn(dev, "FWTracer: Failed to create mkey %d\n", err); 874 goto err_dealloc_pd; 875 } 876 877 mlx5_fw_tracer_start(tracer); 878 879 return 0; 880 881 err_dealloc_pd: 882 mlx5_core_dealloc_pd(dev, tracer->buff.pdn); 883 return err; 884 } 885 886 /* Stop tracer + Cleanup HW resources 887 * must be called after Async EQ is destroyed 888 */ 889 void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer) 890 { 891 if (IS_ERR_OR_NULL(tracer)) 892 return; 893 894 mlx5_core_dbg(tracer->dev, "FWTracer: Cleanup, is owner ? (%d)\n", 895 tracer->owner); 896 897 cancel_work_sync(&tracer->ownership_change_work); 898 cancel_work_sync(&tracer->handle_traces_work); 899 900 if (tracer->owner) 901 mlx5_fw_tracer_ownership_release(tracer); 902 903 mlx5_core_destroy_mkey(tracer->dev, &tracer->buff.mkey); 904 mlx5_core_dealloc_pd(tracer->dev, tracer->buff.pdn); 905 } 906 907 /* Free software resources (Buffers, etc ..) */ 908 void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer) 909 { 910 if (IS_ERR_OR_NULL(tracer)) 911 return; 912 913 mlx5_core_dbg(tracer->dev, "FWTracer: Destroy\n"); 914 915 cancel_work_sync(&tracer->read_fw_strings_work); 916 mlx5_fw_tracer_clean_ready_list(tracer); 917 mlx5_fw_tracer_clean_print_hash(tracer); 918 mlx5_fw_tracer_free_strings_db(tracer); 919 mlx5_fw_tracer_destroy_log_buf(tracer); 920 flush_workqueue(tracer->work_queue); 921 destroy_workqueue(tracer->work_queue); 922 kfree(tracer); 923 } 924 925 void mlx5_fw_tracer_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe) 926 { 927 struct mlx5_fw_tracer *tracer = dev->tracer; 928 929 if (!tracer) 930 return; 931 932 switch (eqe->sub_type) { 933 case MLX5_TRACER_SUBTYPE_OWNERSHIP_CHANGE: 934 if (test_bit(MLX5_INTERFACE_STATE_UP, &dev->intf_state)) 935 queue_work(tracer->work_queue, &tracer->ownership_change_work); 936 break; 937 case MLX5_TRACER_SUBTYPE_TRACES_AVAILABLE: 938 if (likely(tracer->str_db.loaded)) 939 queue_work(tracer->work_queue, &tracer->handle_traces_work); 940 break; 941 default: 942 mlx5_core_dbg(dev, "FWTracer: Event with unrecognized subtype: sub_type %d\n", 943 eqe->sub_type); 944 } 945 } 946 947 EXPORT_TRACEPOINT_SYMBOL(mlx5_fw); 948