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