1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2020 Intel Corporation 3 */ 4 5 #include <ethdev_driver.h> 6 #include <rte_string_fns.h> 7 #ifdef RTE_LIB_TELEMETRY 8 #include <telemetry_internal.h> 9 #endif 10 11 #include "rte_metrics.h" 12 #include "rte_metrics_telemetry.h" 13 14 #ifdef RTE_HAS_JANSSON 15 16 struct telemetry_metrics_data tel_met_data; 17 18 int metrics_log_level; 19 #define RTE_LOGTYPE_METRICS metrics_log_level 20 21 /* Logging Macros */ 22 #define METRICS_LOG(level, fmt, args...) \ 23 RTE_LOG_LINE(level, METRICS, "%s(): "fmt, __func__, ## args) 24 25 #define METRICS_LOG_ERR(fmt, args...) \ 26 METRICS_LOG(ERR, fmt, ## args) 27 28 #define METRICS_LOG_WARN(fmt, args...) \ 29 METRICS_LOG(WARNING, fmt, ## args) 30 31 static int32_t 32 rte_metrics_tel_reg_port_ethdev_to_metrics(uint16_t port_id) 33 { 34 int ret, num_xstats, i; 35 struct rte_eth_xstat_name *eth_xstats_names; 36 const char **xstats_names; 37 38 num_xstats = rte_eth_xstats_get(port_id, NULL, 0); 39 if (num_xstats < 0) { 40 METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", 41 port_id, num_xstats); 42 return -EPERM; 43 } 44 45 xstats_names = malloc(sizeof(*xstats_names) * num_xstats); 46 eth_xstats_names = malloc(sizeof(struct rte_eth_xstat_name) 47 * num_xstats); 48 if (eth_xstats_names == NULL || xstats_names == NULL) { 49 METRICS_LOG_ERR("Failed to malloc memory for xstats_names"); 50 ret = -ENOMEM; 51 goto free_xstats; 52 } 53 54 if (rte_eth_xstats_get_names(port_id, 55 eth_xstats_names, num_xstats) != num_xstats) { 56 METRICS_LOG_ERR("rte_eth_xstats_get_names(%u) len %d failed", 57 port_id, num_xstats); 58 ret = -EPERM; 59 goto free_xstats; 60 } 61 62 for (i = 0; i < num_xstats; i++) 63 xstats_names[i] = eth_xstats_names[i].name; 64 ret = rte_metrics_reg_names(xstats_names, num_xstats); 65 if (ret < 0) 66 METRICS_LOG_ERR("rte_metrics_reg_names failed - metrics may already be registered"); 67 68 free_xstats: 69 free(eth_xstats_names); 70 free(xstats_names); 71 return ret; 72 } 73 74 int32_t 75 rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, int *reg_index_list) 76 { 77 struct driver_index { 78 const void *dev_ops; 79 int reg_index; 80 } drv_idx[RTE_MAX_ETHPORTS] = { {0} }; 81 int ret, nb_drv_idx = 0; 82 uint16_t d; 83 84 rte_metrics_init(rte_socket_id()); 85 RTE_ETH_FOREACH_DEV(d) { 86 int i; 87 /* Different device types have different numbers of stats, so 88 * first check if the stats for this type of device have 89 * already been registered 90 */ 91 for (i = 0; i < nb_drv_idx; i++) { 92 if (rte_eth_devices[d].dev_ops == drv_idx[i].dev_ops) { 93 reg_index_list[d] = drv_idx[i].reg_index; 94 break; 95 } 96 } 97 if (i < nb_drv_idx) 98 continue; /* we found a match, go to next port */ 99 100 /* No match, register a new set of xstats for this port */ 101 ret = rte_metrics_tel_reg_port_ethdev_to_metrics(d); 102 if (ret < 0) { 103 METRICS_LOG_ERR("Failed to register ethdev to metrics"); 104 return ret; 105 } 106 reg_index_list[d] = ret; 107 drv_idx[nb_drv_idx].dev_ops = rte_eth_devices[d].dev_ops; 108 drv_idx[nb_drv_idx].reg_index = ret; 109 nb_drv_idx++; 110 } 111 *metrics_register_done = 1; 112 return 0; 113 } 114 115 static int32_t 116 rte_metrics_tel_update_metrics_ethdev(uint16_t port_id, int reg_start_index) 117 { 118 int ret, num_xstats, i; 119 struct rte_eth_xstat *eth_xstats; 120 121 num_xstats = rte_eth_xstats_get(port_id, NULL, 0); 122 if (num_xstats < 0) { 123 METRICS_LOG_ERR("rte_eth_xstats_get(%u) failed: %d", port_id, 124 num_xstats); 125 return -EPERM; 126 } 127 eth_xstats = malloc(sizeof(struct rte_eth_xstat) * num_xstats); 128 if (eth_xstats == NULL) { 129 METRICS_LOG_ERR("Failed to malloc memory for xstats"); 130 return -ENOMEM; 131 } 132 ret = rte_eth_xstats_get(port_id, eth_xstats, num_xstats); 133 if (ret < 0 || ret > num_xstats) { 134 free(eth_xstats); 135 METRICS_LOG_ERR("rte_eth_xstats_get(%u) len%i failed: %d", 136 port_id, num_xstats, ret); 137 return -EPERM; 138 } 139 140 uint64_t xstats_values[num_xstats]; 141 for (i = 0; i < num_xstats; i++) 142 xstats_values[i] = eth_xstats[i].value; 143 if (rte_metrics_update_values(port_id, reg_start_index, xstats_values, 144 num_xstats) < 0) { 145 METRICS_LOG_ERR("Could not update metrics values"); 146 free(eth_xstats); 147 return -EPERM; 148 } 149 free(eth_xstats); 150 return 0; 151 } 152 153 static int32_t 154 rte_metrics_tel_format_port(uint32_t pid, json_t *ports, 155 uint32_t *metric_ids, int num_metric_ids) 156 { 157 struct rte_metric_value *metrics = NULL; 158 struct rte_metric_name *names = NULL; 159 int num_metrics, i, ret = -EPERM; /* most error cases return EPERM */ 160 json_t *port, *stats; 161 162 num_metrics = rte_metrics_get_names(NULL, 0); 163 if (num_metrics < 0) { 164 METRICS_LOG_ERR("Cannot get metrics count"); 165 return -EINVAL; 166 } else if (num_metrics == 0) { 167 METRICS_LOG_ERR("No metrics to display (none have been registered)"); 168 return -EPERM; 169 } 170 171 metrics = malloc(sizeof(struct rte_metric_value) * num_metrics); 172 names = malloc(sizeof(struct rte_metric_name) * num_metrics); 173 if (metrics == NULL || names == NULL) { 174 METRICS_LOG_ERR("Cannot allocate memory"); 175 ret = -ENOMEM; 176 goto fail; 177 } 178 179 if (rte_metrics_get_names(names, num_metrics) != num_metrics || 180 rte_metrics_get_values(pid, metrics, num_metrics) 181 != num_metrics) { 182 METRICS_LOG_ERR("Error getting metrics"); 183 goto fail; 184 } 185 186 stats = json_array(); 187 if (stats == NULL) { 188 METRICS_LOG_ERR("Could not create stats JSON object"); 189 goto fail; 190 } 191 192 for (i = 0; i < num_metrics; i++) { 193 int32_t j; 194 for (j = 0; j < num_metric_ids; j++) 195 if (metrics[i].key == metric_ids[j]) 196 break; 197 198 if (num_metric_ids > 0 && j == num_metric_ids) 199 continue; /* can't find this id */ 200 201 json_t *stat = json_pack("{s,s,s,I}", 202 "name", names[metrics[i].key].name, 203 "value", metrics[i].value); 204 if (stat == NULL || json_array_append_new(stats, stat) < 0) { 205 METRICS_LOG_ERR("Format stat with id: %u failed", 206 metrics[i].key); 207 goto fail; 208 } 209 } 210 211 port = json_pack("{s,i,s,o}", "port", pid, "stats", 212 json_array_size(stats) ? stats : json_null()); 213 if (port == NULL || json_array_append_new(ports, port) < 0) { 214 METRICS_LOG_ERR("Error creating port and adding to ports"); 215 goto fail; 216 } 217 218 free(metrics); 219 free(names); 220 return 0; 221 222 fail: 223 free(metrics); 224 free(names); 225 return ret; 226 } 227 228 int32_t 229 rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep, 230 char **json_buffer) 231 { 232 json_t *root, *ports; 233 int ret, i; 234 235 ports = json_array(); 236 if (ports == NULL) { 237 METRICS_LOG_ERR("Could not create ports JSON array"); 238 return -EPERM; 239 } 240 241 if (ep->type == PORT_STATS) { 242 if (ep->pp.num_port_ids <= 0) { 243 METRICS_LOG_ERR("Please provide port/metric ids"); 244 return -EINVAL; 245 } 246 247 for (i = 0; i < ep->pp.num_port_ids; i++) { 248 ret = rte_metrics_tel_format_port(ep->pp.port_ids[i], 249 ports, &ep->pp.metric_ids[0], 250 ep->pp.num_metric_ids); 251 if (ret < 0) { 252 METRICS_LOG_ERR("Format port in JSON failed"); 253 return ret; 254 } 255 } 256 } else if (ep->type == GLOBAL_STATS) { 257 /* Request Global Metrics */ 258 ret = rte_metrics_tel_format_port(RTE_METRICS_GLOBAL, 259 ports, NULL, 0); 260 if (ret < 0) { 261 METRICS_LOG_ERR("Request Global Metrics Failed"); 262 return ret; 263 } 264 } else { 265 METRICS_LOG_ERR("Invalid metrics type in encode params"); 266 return -EINVAL; 267 } 268 269 root = json_pack("{s,s,s,o}", "status_code", "Status OK: 200", 270 "data", ports); 271 if (root == NULL) { 272 METRICS_LOG_ERR("Root, Status or data field cannot be set"); 273 return -EPERM; 274 } 275 276 *json_buffer = json_dumps(root, JSON_INDENT(2)); 277 json_decref(root); 278 return 0; 279 } 280 281 int32_t 282 rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep, 283 int *reg_index, char **json_buffer) 284 { 285 int ret, i; 286 uint32_t port_id; 287 288 for (i = 0; i < ep->pp.num_port_ids; i++) { 289 port_id = ep->pp.port_ids[i]; 290 if (!rte_eth_dev_is_valid_port(port_id)) { 291 METRICS_LOG_ERR("Port: %d invalid", port_id); 292 return -EINVAL; 293 } 294 295 ret = rte_metrics_tel_update_metrics_ethdev(port_id, 296 reg_index[i]); 297 if (ret < 0) { 298 METRICS_LOG_ERR("Failed to update ethdev metrics"); 299 return ret; 300 } 301 } 302 303 ret = rte_metrics_tel_encode_json_format(ep, json_buffer); 304 if (ret < 0) { 305 METRICS_LOG_ERR("JSON encode function failed"); 306 return ret; 307 } 308 return 0; 309 } 310 311 int32_t 312 rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep) 313 { 314 int p, num_port_ids = 0; 315 316 RTE_ETH_FOREACH_DEV(p) { 317 ep->pp.port_ids[num_port_ids] = p; 318 num_port_ids++; 319 } 320 321 if (!num_port_ids) { 322 METRICS_LOG_ERR("No active ports"); 323 return -EINVAL; 324 } 325 326 ep->pp.num_port_ids = num_port_ids; 327 ep->pp.num_metric_ids = 0; 328 ep->type = PORT_STATS; 329 return 0; 330 } 331 332 static int32_t 333 rte_metrics_tel_stat_names_to_ids(const char * const *stat_names, 334 uint32_t *stat_ids, int num_stat_names) 335 { 336 struct rte_metric_name *names; 337 int num_metrics; 338 int i, j, nb_stat_ids = 0; 339 340 num_metrics = rte_metrics_get_names(NULL, 0); 341 if (num_metrics <= 0) { 342 METRICS_LOG_ERR("Error getting metrics count - no metrics may be registered"); 343 return -EPERM; 344 } 345 346 names = malloc(sizeof(struct rte_metric_name) * num_metrics); 347 if (names == NULL) { 348 METRICS_LOG_ERR("Cannot allocate memory for names"); 349 return -ENOMEM; 350 } 351 352 if (rte_metrics_get_names(names, num_metrics) != num_metrics) { 353 METRICS_LOG_ERR("Cannot get metrics names"); 354 free(names); 355 return -EPERM; 356 } 357 358 for (i = 0; i < num_stat_names; i++) { 359 for (j = 0; j < num_metrics; j++) { 360 if (strcmp(stat_names[i], names[j].name) == 0) { 361 stat_ids[nb_stat_ids++] = j; 362 break; 363 } 364 } 365 if (j == num_metrics) { 366 METRICS_LOG_WARN("Invalid stat name %s", 367 stat_names[i]); 368 free(names); 369 return -EINVAL; 370 } 371 } 372 373 free(names); 374 return 0; 375 } 376 377 int32_t 378 rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data) 379 { 380 int ret; 381 json_t *port_ids_json = json_object_get(data, "ports"); 382 json_t *stat_names_json = json_object_get(data, "stats"); 383 uint64_t num_stat_names = json_array_size(stat_names_json); 384 const char *stat_names[num_stat_names]; 385 size_t index; 386 json_t *value; 387 388 memset(ep, 0, sizeof(*ep)); 389 ep->pp.num_port_ids = json_array_size(port_ids_json); 390 ep->pp.num_metric_ids = num_stat_names; 391 if (!json_is_object(data) || !json_is_array(port_ids_json) || 392 !json_is_array(stat_names_json)) { 393 METRICS_LOG_WARN("Invalid data provided for this command"); 394 return -EINVAL; 395 } 396 397 json_array_foreach(port_ids_json, index, value) { 398 if (!json_is_integer(value)) { 399 METRICS_LOG_WARN("Port ID given is not valid"); 400 return -EINVAL; 401 } 402 ep->pp.port_ids[index] = json_integer_value(value); 403 if (rte_eth_dev_is_valid_port(ep->pp.port_ids[index]) < 1) 404 return -EINVAL; 405 } 406 json_array_foreach(stat_names_json, index, value) { 407 if (!json_is_string(value)) { 408 METRICS_LOG_WARN("Stat Name given is not a string"); 409 return -EINVAL; 410 } 411 stat_names[index] = json_string_value(value); 412 } 413 414 ret = rte_metrics_tel_stat_names_to_ids(stat_names, ep->pp.metric_ids, 415 num_stat_names); 416 if (ret < 0) { 417 METRICS_LOG_ERR("Could not convert stat names to IDs"); 418 return ret; 419 } 420 421 ep->type = PORT_STATS; 422 return 0; 423 } 424 425 static int 426 rte_metrics_tel_initial_metrics_setup(void) 427 { 428 int ret; 429 rte_metrics_init(rte_socket_id()); 430 431 if (!tel_met_data.metrics_register_done) { 432 ret = rte_metrics_tel_reg_all_ethdev( 433 &tel_met_data.metrics_register_done, 434 tel_met_data.reg_index); 435 if (ret < 0) 436 return ret; 437 } 438 return 0; 439 } 440 441 static int 442 handle_ports_all_stats_values(const char *cmd __rte_unused, 443 const char *params __rte_unused, 444 char *buffer, int buf_len) 445 { 446 struct telemetry_encode_param ep; 447 int ret, used = 0; 448 char *json_buffer = NULL; 449 450 ret = rte_metrics_tel_initial_metrics_setup(); 451 if (ret < 0) 452 return ret; 453 454 memset(&ep, 0, sizeof(ep)); 455 ret = rte_metrics_tel_get_port_stats_ids(&ep); 456 if (ret < 0) 457 return ret; 458 459 ret = rte_metrics_tel_get_ports_stats_json(&ep, tel_met_data.reg_index, 460 &json_buffer); 461 if (ret < 0) 462 return ret; 463 464 used += strlcpy(buffer, json_buffer, buf_len); 465 return used; 466 } 467 468 static int 469 handle_global_stats_values(const char *cmd __rte_unused, 470 const char *params __rte_unused, 471 char *buffer, int buf_len) 472 { 473 char *json_buffer = NULL; 474 struct telemetry_encode_param ep = { .type = GLOBAL_STATS }; 475 int ret, used = 0; 476 477 ret = rte_metrics_tel_initial_metrics_setup(); 478 if (ret < 0) 479 return ret; 480 481 ret = rte_metrics_tel_encode_json_format(&ep, &json_buffer); 482 if (ret < 0) { 483 METRICS_LOG_ERR("JSON encode function failed"); 484 return ret; 485 } 486 used += strlcpy(buffer, json_buffer, buf_len); 487 return used; 488 } 489 490 static int 491 handle_ports_stats_values_by_name(const char *cmd __rte_unused, 492 const char *params, 493 char *buffer, int buf_len) 494 { 495 char *json_buffer = NULL; 496 struct telemetry_encode_param ep; 497 int ret, used = 0; 498 json_t *data; 499 json_error_t error; 500 501 ret = rte_metrics_tel_initial_metrics_setup(); 502 if (ret < 0) 503 return ret; 504 505 data = json_loads(params, 0, &error); 506 if (!data) { 507 METRICS_LOG_WARN("Could not load JSON object from data passed in : %s", 508 error.text); 509 return -EPERM; 510 } else if (!json_is_object(data)) { 511 METRICS_LOG_WARN("JSON Request data is not a JSON object"); 512 json_decref(data); 513 return -EINVAL; 514 } 515 516 ret = rte_metrics_tel_extract_data(&ep, data); 517 if (ret < 0) { 518 METRICS_LOG_ERR("Extract data function failed"); 519 return ret; 520 } 521 522 ret = rte_metrics_tel_encode_json_format(&ep, &json_buffer); 523 if (ret < 0) { 524 METRICS_LOG_ERR("JSON encode function failed"); 525 return ret; 526 } 527 used += strlcpy(buffer, json_buffer, buf_len); 528 return used; 529 } 530 531 RTE_LOG_REGISTER_DEFAULT(metrics_log_level, ERR); 532 533 RTE_INIT(metrics_ctor) 534 { 535 #ifdef RTE_LIB_TELEMETRY 536 rte_telemetry_legacy_register("ports_all_stat_values", DATA_NOT_REQ, 537 handle_ports_all_stats_values); 538 rte_telemetry_legacy_register("global_stat_values", DATA_NOT_REQ, 539 handle_global_stats_values); 540 rte_telemetry_legacy_register("ports_stats_values_by_name", DATA_REQ, 541 handle_ports_stats_values_by_name); 542 #endif 543 } 544 545 #else /* !RTE_HAS_JANSSON */ 546 547 int32_t 548 rte_metrics_tel_reg_all_ethdev(int *metrics_register_done, int *reg_index_list) 549 { 550 RTE_SET_USED(metrics_register_done); 551 RTE_SET_USED(reg_index_list); 552 553 return -ENOTSUP; 554 } 555 556 int32_t 557 rte_metrics_tel_encode_json_format(struct telemetry_encode_param *ep, 558 char **json_buffer) 559 { 560 RTE_SET_USED(ep); 561 RTE_SET_USED(json_buffer); 562 563 return -ENOTSUP; 564 } 565 566 int32_t 567 rte_metrics_tel_get_ports_stats_json(struct telemetry_encode_param *ep, 568 int *reg_index, char **json_buffer) 569 { 570 RTE_SET_USED(ep); 571 RTE_SET_USED(reg_index); 572 RTE_SET_USED(json_buffer); 573 574 return -ENOTSUP; 575 } 576 577 int32_t 578 rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep) 579 { 580 RTE_SET_USED(ep); 581 582 return -ENOTSUP; 583 } 584 585 int32_t 586 rte_metrics_tel_extract_data(struct telemetry_encode_param *ep, json_t *data) 587 { 588 RTE_SET_USED(ep); 589 RTE_SET_USED(data); 590 591 return -ENOTSUP; 592 } 593 594 int32_t 595 rte_metrics_tel_get_global_stats(struct telemetry_encode_param *ep) 596 { 597 RTE_SET_USED(ep); 598 599 return -ENOTSUP; 600 } 601 602 #endif /* !RTE_HAS_JANSSON */ 603