1 /*- 2 * Copyright (c) 2018, Mellanox Technologies, Ltd. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <dev/mlx5/mlx5_core/diag_cnt.h> 27 28 static int get_supported_cnt_ids(struct mlx5_core_dev *dev); 29 static int enable_cnt_id(struct mlx5_core_dev *dev, u16 id); 30 static void reset_cnt_id(struct mlx5_core_dev *dev); 31 static void reset_params(struct mlx5_diag_cnt *diag_cnt); 32 33 static int 34 mlx5_sysctl_counter_id(SYSCTL_HANDLER_ARGS) 35 { 36 struct mlx5_diag_cnt *diag_cnt; 37 struct mlx5_core_dev *dev; 38 uint16_t *ptr; 39 size_t max; 40 size_t num; 41 size_t x; 42 int err; 43 44 dev = arg1; 45 diag_cnt = &dev->diag_cnt; 46 47 max = MLX5_CAP_GEN(dev, num_of_diagnostic_counters); 48 49 ptr = kmalloc(sizeof(ptr[0]) * max, GFP_KERNEL); 50 51 DIAG_LOCK(diag_cnt); 52 53 for (x = num = 0; x != max; x++) { 54 if (diag_cnt->cnt_id[x].enabled) 55 ptr[num++] = diag_cnt->cnt_id[x].id; 56 } 57 58 err = SYSCTL_OUT(req, ptr, sizeof(ptr[0]) * num); 59 if (err || !req->newptr) 60 goto done; 61 62 num = req->newlen / sizeof(ptr[0]); 63 if (num > max) { 64 err = ENOMEM; 65 goto done; 66 } 67 68 err = SYSCTL_IN(req, ptr, sizeof(ptr[0]) * num); 69 70 reset_cnt_id(dev); 71 72 for (x = 0; x != num; x++) { 73 err = enable_cnt_id(dev, ptr[x]); 74 if (err) 75 goto done; 76 } 77 78 diag_cnt->num_cnt_id = num; 79 done: 80 kfree(ptr); 81 82 if (err != 0 && req->newptr != NULL) 83 reset_cnt_id(dev); 84 85 DIAG_UNLOCK(diag_cnt); 86 87 return (err); 88 } 89 90 #define NUM_OF_DIAG_PARAMS 5 91 92 static int 93 mlx5_sysctl_params(SYSCTL_HANDLER_ARGS) 94 { 95 struct mlx5_diag_cnt *diag_cnt; 96 struct mlx5_core_dev *dev; 97 uint32_t temp[NUM_OF_DIAG_PARAMS]; 98 int err; 99 100 dev = arg1; 101 diag_cnt = &dev->diag_cnt; 102 103 DIAG_LOCK(diag_cnt); 104 105 temp[0] = diag_cnt->log_num_of_samples; 106 temp[1] = diag_cnt->log_sample_period; 107 temp[2] = diag_cnt->flag; 108 temp[3] = diag_cnt->num_of_samples; 109 temp[4] = diag_cnt->sample_index; 110 111 err = SYSCTL_OUT(req, temp, sizeof(temp)); 112 if (err || !req->newptr) 113 goto done; 114 115 err = SYSCTL_IN(req, temp, sizeof(temp)); 116 if (err) 117 goto done; 118 119 reset_params(&dev->diag_cnt); 120 121 if (temp[0] > MLX5_CAP_DEBUG(dev, log_max_samples) || 122 (1U << (MLX5_CAP_DEBUG(dev, log_max_samples) - temp[0])) < 123 diag_cnt->num_cnt_id) { 124 err = ERANGE; 125 goto done; 126 } else if (temp[1] < MLX5_CAP_DEBUG(dev, log_min_sample_period)) { 127 err = ERANGE; 128 goto done; 129 } else if (temp[2] >= 0x100) { 130 err = ERANGE; 131 goto done; 132 } else if (temp[3] > (1U << diag_cnt->log_num_of_samples)) { 133 err = ERANGE; 134 goto done; 135 } else if (temp[4] > (1U << diag_cnt->log_num_of_samples)) { 136 err = ERANGE; 137 goto done; 138 } 139 140 diag_cnt->log_num_of_samples = temp[0]; 141 diag_cnt->log_sample_period = temp[1]; 142 diag_cnt->flag = temp[2]; 143 diag_cnt->num_of_samples = temp[3]; 144 diag_cnt->sample_index = temp[4]; 145 done: 146 DIAG_UNLOCK(diag_cnt); 147 148 return (err); 149 } 150 151 static void 152 decode_cnt_buffer(u32 num_of_samples, u8 *out, struct sbuf *sbuf) 153 { 154 void *cnt; 155 u64 temp; 156 u32 i; 157 158 for (i = 0; i != num_of_samples; i++) { 159 cnt = MLX5_ADDR_OF(query_diagnostic_counters_out, 160 out, diag_counter[i]); 161 temp = MLX5_GET(diagnostic_cntr_struct, cnt, counter_value_h); 162 temp = (temp << 32) | 163 MLX5_GET(diagnostic_cntr_struct, cnt, counter_value_l); 164 sbuf_printf(sbuf, 165 "0x%04x,0x%04x,0x%08x,0x%016llx\n", 166 MLX5_GET(diagnostic_cntr_struct, cnt, counter_id), 167 MLX5_GET(diagnostic_cntr_struct, cnt, sample_id), 168 MLX5_GET(diagnostic_cntr_struct, cnt, time_stamp_31_0), 169 (unsigned long long)temp); 170 } 171 } 172 173 static int 174 mlx5_sysctl_dump_set(SYSCTL_HANDLER_ARGS) 175 { 176 struct mlx5_diag_cnt *diag_cnt; 177 struct mlx5_core_dev *dev; 178 uint8_t temp; 179 int err; 180 181 dev = arg1; 182 diag_cnt = &dev->diag_cnt; 183 184 DIAG_LOCK(diag_cnt); 185 186 err = SYSCTL_OUT(req, &diag_cnt->ready, sizeof(diag_cnt->ready)); 187 if (err || !req->newptr) 188 goto done; 189 190 err = SYSCTL_IN(req, &temp, sizeof(temp)); 191 if (err) 192 goto done; 193 194 diag_cnt->ready = (temp != 0); 195 if (diag_cnt->ready != 0) 196 err = -mlx5_diag_set_params(dev); 197 done: 198 DIAG_UNLOCK(diag_cnt); 199 200 return (err); 201 } 202 203 static int 204 mlx5_sysctl_dump_get(SYSCTL_HANDLER_ARGS) 205 { 206 struct mlx5_diag_cnt *diag_cnt; 207 struct mlx5_core_dev *dev; 208 struct sbuf sbuf; 209 u8 *out; 210 int err; 211 212 dev = arg1; 213 diag_cnt = &dev->diag_cnt; 214 215 err = sysctl_wire_old_buffer(req, 0); 216 if (err != 0) 217 return (err); 218 219 DIAG_LOCK(diag_cnt); 220 221 sbuf_new_for_sysctl(&sbuf, NULL, 65536, req); 222 223 if (diag_cnt->ready != 0) { 224 err = -mlx5_diag_query_counters(dev, &out); 225 if (err) { 226 sbuf_printf(&sbuf, "\nCould not query counters: %d\n", err); 227 } else { 228 sbuf_printf(&sbuf, "\n"); 229 decode_cnt_buffer(diag_cnt->num_of_samples * 230 diag_cnt->num_cnt_id, out, &sbuf); 231 kfree(out); 232 } 233 } else { 234 sbuf_printf(&sbuf, "\nDump was not set.\n"); 235 } 236 237 err = sbuf_finish(&sbuf); 238 239 sbuf_delete(&sbuf); 240 241 DIAG_UNLOCK(diag_cnt); 242 243 return (err); 244 } 245 246 static int 247 mlx5_sysctl_cap_read(SYSCTL_HANDLER_ARGS) 248 { 249 struct mlx5_diag_cnt *diag_cnt; 250 struct mlx5_core_dev *dev; 251 struct sbuf sbuf; 252 int err; 253 u32 i; 254 255 dev = arg1; 256 diag_cnt = &dev->diag_cnt; 257 258 err = sysctl_wire_old_buffer(req, 0); 259 if (err != 0) 260 return (err); 261 262 DIAG_LOCK(diag_cnt); 263 264 sbuf_new_for_sysctl(&sbuf, NULL, 8192, req); 265 266 sbuf_printf(&sbuf, "\n"); 267 268 /* print cap */ 269 sbuf_printf(&sbuf, "log_max_samples=%d\n", 270 MLX5_CAP_DEBUG(dev, log_max_samples)); 271 sbuf_printf(&sbuf, "log_min_sample_period=%d\n", 272 MLX5_CAP_DEBUG(dev, log_min_sample_period)); 273 sbuf_printf(&sbuf, "repetitive=%d\n", 274 MLX5_CAP_DEBUG(dev, repetitive)); 275 sbuf_printf(&sbuf, "single=%d\n", 276 MLX5_CAP_DEBUG(dev, single)); 277 sbuf_printf(&sbuf, "num_of_diagnostic_counters=%d\n", 278 MLX5_CAP_GEN(dev, num_of_diagnostic_counters)); 279 280 /* print list of supported counter */ 281 sbuf_printf(&sbuf, "supported counter id:\n"); 282 for (i = 0; i != MLX5_CAP_GEN(dev, num_of_diagnostic_counters); i++) 283 sbuf_printf(&sbuf, "0x%04x,", diag_cnt->cnt_id[i].id); 284 sbuf_printf(&sbuf, "\n"); 285 286 err = sbuf_finish(&sbuf); 287 sbuf_delete(&sbuf); 288 289 DIAG_UNLOCK(diag_cnt); 290 291 return (err); 292 } 293 294 static int 295 get_supported_cnt_ids(struct mlx5_core_dev *dev) 296 { 297 u32 num_counters = MLX5_CAP_GEN(dev, num_of_diagnostic_counters); 298 struct mlx5_diag_cnt *diag_cnt = &dev->diag_cnt; 299 u32 i; 300 301 diag_cnt->cnt_id = kzalloc(sizeof(*diag_cnt->cnt_id) * num_counters, 302 GFP_KERNEL); 303 if (!diag_cnt->cnt_id) 304 return (-ENOMEM); 305 306 for (i = 0; i != num_counters; i++) { 307 diag_cnt->cnt_id[i].id = 308 MLX5_CAP_DEBUG(dev, diagnostic_counter[i].counter_id); 309 } 310 return (0); 311 } 312 313 static void 314 reset_cnt_id(struct mlx5_core_dev *dev) 315 { 316 struct mlx5_diag_cnt *diag_cnt = &dev->diag_cnt; 317 u32 i; 318 319 diag_cnt->num_cnt_id = 0; 320 for (i = 0; i != MLX5_CAP_GEN(dev, num_of_diagnostic_counters); i++) 321 diag_cnt->cnt_id[i].enabled = false; 322 } 323 324 static int 325 enable_cnt_id(struct mlx5_core_dev *dev, u16 id) 326 { 327 struct mlx5_diag_cnt *diag_cnt = &dev->diag_cnt; 328 u32 i; 329 330 for (i = 0; i != MLX5_CAP_GEN(dev, num_of_diagnostic_counters); i++) { 331 if (diag_cnt->cnt_id[i].id == id) { 332 if (diag_cnt->cnt_id[i].enabled) 333 return (EINVAL); 334 335 diag_cnt->cnt_id[i].enabled = true; 336 break; 337 } 338 } 339 340 if (i == MLX5_CAP_GEN(dev, num_of_diagnostic_counters)) 341 return (ENOENT); 342 else 343 return (0); 344 } 345 346 static void 347 reset_params(struct mlx5_diag_cnt *diag_cnt) 348 { 349 diag_cnt->log_num_of_samples = 0; 350 diag_cnt->log_sample_period = 0; 351 diag_cnt->flag = 0; 352 diag_cnt->num_of_samples = 0; 353 diag_cnt->sample_index = 0; 354 } 355 356 int 357 mlx5_diag_set_params(struct mlx5_core_dev *dev) 358 { 359 u8 out[MLX5_ST_SZ_BYTES(set_diagnostic_params_out)] = {0}; 360 struct mlx5_diag_cnt *diag_cnt = &dev->diag_cnt; 361 void *cnt_id; 362 void *ctx; 363 u16 in_sz; 364 int err; 365 u8 *in; 366 u32 i; 367 u32 j; 368 369 if (!diag_cnt->num_cnt_id) 370 return (-EINVAL); 371 372 in_sz = MLX5_ST_SZ_BYTES(set_diagnostic_params_in) + 373 diag_cnt->num_cnt_id * MLX5_ST_SZ_BYTES(counter_id); 374 in = kzalloc(in_sz, GFP_KERNEL); 375 if (!in) 376 return (-ENOMEM); 377 378 MLX5_SET(set_diagnostic_params_in, in, opcode, 379 MLX5_CMD_OP_SET_DIAGNOSTICS); 380 381 ctx = MLX5_ADDR_OF(set_diagnostic_params_in, in, 382 diagnostic_params_ctx); 383 MLX5_SET(diagnostic_params_context, ctx, num_of_counters, 384 diag_cnt->num_cnt_id); 385 MLX5_SET(diagnostic_params_context, ctx, log_num_of_samples, 386 diag_cnt->log_num_of_samples); 387 388 MLX5_SET(diagnostic_params_context, ctx, single, 389 (diag_cnt->flag >> 7) & 1); 390 MLX5_SET(diagnostic_params_context, ctx, repetitive, 391 (diag_cnt->flag >> 6) & 1); 392 MLX5_SET(diagnostic_params_context, ctx, sync, 393 (diag_cnt->flag >> 5) & 1); 394 MLX5_SET(diagnostic_params_context, ctx, clear, 395 (diag_cnt->flag >> 4) & 1); 396 MLX5_SET(diagnostic_params_context, ctx, on_demand, 397 (diag_cnt->flag >> 3) & 1); 398 MLX5_SET(diagnostic_params_context, ctx, enable, 399 (diag_cnt->flag >> 2) & 1); 400 MLX5_SET(diagnostic_params_context, ctx, log_sample_period, 401 diag_cnt->log_sample_period); 402 403 for (i = j = 0; i != MLX5_CAP_GEN(dev, num_of_diagnostic_counters); i++) { 404 if (diag_cnt->cnt_id[i].enabled) { 405 cnt_id = MLX5_ADDR_OF(diagnostic_params_context, 406 ctx, counter_id[j]); 407 MLX5_SET(counter_id, cnt_id, counter_id, 408 diag_cnt->cnt_id[i].id); 409 j++; 410 } 411 } 412 413 err = mlx5_cmd_exec(dev, in, in_sz, out, sizeof(out)); 414 415 kfree(in); 416 return (err); 417 } 418 419 /* This function is for debug purpose */ 420 int 421 mlx5_diag_query_params(struct mlx5_core_dev *dev) 422 { 423 u8 in[MLX5_ST_SZ_BYTES(query_diagnostic_params_in)] = {0}; 424 struct mlx5_diag_cnt *diag_cnt = &dev->diag_cnt; 425 void *cnt_id; 426 u16 out_sz; 427 void *ctx; 428 int err; 429 u8 *out; 430 u32 i; 431 432 out_sz = MLX5_ST_SZ_BYTES(query_diagnostic_params_out) + 433 diag_cnt->num_cnt_id * MLX5_ST_SZ_BYTES(counter_id); 434 435 out = kzalloc(out_sz, GFP_KERNEL); 436 if (!out) 437 return (-ENOMEM); 438 439 MLX5_SET(query_diagnostic_params_in, in, opcode, 440 MLX5_CMD_OP_QUERY_DIAGNOSTIC_PARAMS); 441 err = mlx5_cmd_exec(dev, in, sizeof(in), out, out_sz); 442 if (err) 443 goto out; 444 445 ctx = MLX5_ADDR_OF(query_diagnostic_params_out, out, 446 diagnostic_params_ctx); 447 mlx5_core_dbg(dev, "single=%x\n", 448 MLX5_GET(diagnostic_params_context, ctx, single)); 449 mlx5_core_dbg(dev, "repetitive=%x\n", 450 MLX5_GET(diagnostic_params_context, ctx, repetitive)); 451 mlx5_core_dbg(dev, "sync=%x\n", 452 MLX5_GET(diagnostic_params_context, ctx, sync)); 453 mlx5_core_dbg(dev, "clear=%x\n", 454 MLX5_GET(diagnostic_params_context, ctx, clear)); 455 mlx5_core_dbg(dev, "on_demand=%x\n", 456 MLX5_GET(diagnostic_params_context, ctx, on_demand)); 457 mlx5_core_dbg(dev, "enable=%x\n", 458 MLX5_GET(diagnostic_params_context, ctx, enable)); 459 mlx5_core_dbg(dev, "log_sample_period=%x\n", 460 MLX5_GET(diagnostic_params_context, ctx, 461 log_sample_period)); 462 463 for (i = 0; i != diag_cnt->num_cnt_id; i++) { 464 cnt_id = MLX5_ADDR_OF(diagnostic_params_context, 465 ctx, counter_id[i]); 466 mlx5_core_dbg(dev, "counter_id[%d]=%x\n", i, 467 MLX5_GET(counter_id, cnt_id, counter_id)); 468 } 469 out: 470 kfree(out); 471 return (err); 472 } 473 474 int 475 mlx5_diag_query_counters(struct mlx5_core_dev *dev, u8 **out_buffer) 476 { 477 u8 in[MLX5_ST_SZ_BYTES(query_diagnostic_counters_in)] = {0}; 478 struct mlx5_diag_cnt *diag_cnt = &dev->diag_cnt; 479 u16 out_sz; 480 u8 *out; 481 int err; 482 483 out_sz = MLX5_ST_SZ_BYTES(query_diagnostic_counters_out) + 484 diag_cnt->num_of_samples * diag_cnt->num_cnt_id * 485 MLX5_ST_SZ_BYTES(diagnostic_cntr_struct); 486 487 out = kzalloc(out_sz, GFP_KERNEL); 488 if (!out) 489 return (-ENOMEM); 490 491 MLX5_SET(query_diagnostic_counters_in, in, opcode, 492 MLX5_CMD_OP_QUERY_DIAGNOSTICS); 493 MLX5_SET(query_diagnostic_counters_in, in, num_of_samples, 494 diag_cnt->num_of_samples); 495 MLX5_SET(query_diagnostic_counters_in, in, sample_index, 496 diag_cnt->sample_index); 497 498 err = mlx5_cmd_exec(dev, in, sizeof(in), out, out_sz); 499 500 if (!err) 501 *out_buffer = out; 502 else 503 kfree(out); 504 505 return (err); 506 } 507 508 int 509 mlx5_diag_cnt_init(struct mlx5_core_dev *dev) 510 { 511 struct mlx5_diag_cnt *diag_cnt = &dev->diag_cnt; 512 struct sysctl_oid *diag_cnt_sysctl_node; 513 int err; 514 515 if (!MLX5_DIAG_CNT_SUPPORTED(dev)) 516 return (0); 517 518 mutex_init(&diag_cnt->lock); 519 520 /* Build private data */ 521 err = get_supported_cnt_ids(dev); 522 if (err) 523 return (err); 524 525 sysctl_ctx_init(&diag_cnt->sysctl_ctx); 526 527 diag_cnt_sysctl_node = SYSCTL_ADD_NODE(&diag_cnt->sysctl_ctx, 528 SYSCTL_CHILDREN(device_get_sysctl_tree(dev->pdev->dev.bsddev)), 529 OID_AUTO, "diag_cnt", CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 530 "Diagnostics counters"); 531 532 if (diag_cnt_sysctl_node == NULL) 533 return (-ENOMEM); 534 535 SYSCTL_ADD_PROC(&diag_cnt->sysctl_ctx, SYSCTL_CHILDREN(diag_cnt_sysctl_node), 536 OID_AUTO, "counter_id", CTLTYPE_U16 | CTLFLAG_RW | CTLFLAG_MPSAFE, 537 dev, 0, mlx5_sysctl_counter_id, "SU", "Selected counter IDs"); 538 539 SYSCTL_ADD_PROC(&diag_cnt->sysctl_ctx, SYSCTL_CHILDREN(diag_cnt_sysctl_node), 540 OID_AUTO, "params", CTLTYPE_U32 | CTLFLAG_RW | CTLFLAG_MPSAFE, 541 dev, 0, mlx5_sysctl_params, "IU", 542 "Counter parameters: log_num_of_samples, log_sample_perios, flag, num_of_samples, sample_index"); 543 544 SYSCTL_ADD_PROC(&diag_cnt->sysctl_ctx, SYSCTL_CHILDREN(diag_cnt_sysctl_node), 545 OID_AUTO, "dump_set", CTLTYPE_U8 | CTLFLAG_RW | CTLFLAG_MPSAFE, 546 dev, 0, mlx5_sysctl_dump_set, "CU", 547 "Set dump parameters by writing 1 and enable dump_get. Write 0 to disable dump."); 548 549 SYSCTL_ADD_PROC(&diag_cnt->sysctl_ctx, SYSCTL_CHILDREN(diag_cnt_sysctl_node), 550 OID_AUTO, "dump_get", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 551 dev, 0, mlx5_sysctl_dump_get, "A", 552 "Get dump parameters."); 553 554 SYSCTL_ADD_PROC(&diag_cnt->sysctl_ctx, SYSCTL_CHILDREN(diag_cnt_sysctl_node), 555 OID_AUTO, "cap", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 556 dev, 0, mlx5_sysctl_cap_read, "A", 557 "Read capabilities."); 558 559 return (0); 560 } 561 562 void 563 mlx5_diag_cnt_cleanup(struct mlx5_core_dev *dev) 564 { 565 struct mlx5_diag_cnt *diag_cnt = &dev->diag_cnt; 566 void *ptr; 567 568 if (!MLX5_DIAG_CNT_SUPPORTED(dev)) 569 return; 570 571 sysctl_ctx_free(&diag_cnt->sysctl_ctx); 572 573 ptr = diag_cnt->cnt_id; 574 diag_cnt->cnt_id = NULL; 575 576 kfree(ptr); 577 578 reset_params(diag_cnt); 579 } 580