dpdk/lib/metrics/rte_metrics_telemetry.c
<<
>>
Prefs
   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
  16struct telemetry_metrics_data tel_met_data;
  17
  18int metrics_log_level;
  19
  20/* Logging Macros */
  21#define METRICS_LOG(level, fmt, args...) \
  22        rte_log(RTE_LOG_ ##level, metrics_log_level, "%s(): "fmt "\n", \
  23                __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
  31static int32_t
  32rte_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
  68free_xstats:
  69        free(eth_xstats_names);
  70        free(xstats_names);
  71        return ret;
  72}
  73
  74int32_t
  75rte_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
 115static int32_t
 116rte_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
 153static int32_t
 154rte_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
 222fail:
 223        free(metrics);
 224        free(names);
 225        return ret;
 226}
 227
 228int32_t
 229rte_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
 281int32_t
 282rte_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
 311int32_t
 312rte_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
 332static int32_t
 333rte_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\n",
 367                                        stat_names[i]);
 368                        free(names);
 369                        return -EINVAL;
 370                }
 371        }
 372
 373        free(names);
 374        return 0;
 375}
 376
 377int32_t
 378rte_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
 425static int
 426rte_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
 441static int
 442handle_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
 468static int
 469handle_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
 490static int
 491handle_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
 531RTE_LOG_REGISTER_DEFAULT(metrics_log_level, ERR);
 532
 533RTE_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
 547int32_t
 548rte_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
 556int32_t
 557rte_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
 566int32_t
 567rte_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
 577int32_t
 578rte_metrics_tel_get_port_stats_ids(struct telemetry_encode_param *ep)
 579{
 580        RTE_SET_USED(ep);
 581
 582        return -ENOTSUP;
 583}
 584
 585int32_t
 586rte_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
 594int32_t
 595rte_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