iproute2/rdma/stat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
   2/*
   3 * rdma.c       RDMA tool
   4 * Authors:     Mark Zhang <markz@mellanox.com>
   5 */
   6
   7#include "rdma.h"
   8#include "res.h"
   9#include "stat.h"
  10#include <inttypes.h>
  11
  12static int stat_help(struct rd *rd)
  13{
  14        pr_out("Usage: %s [ OPTIONS ] statistic { COMMAND | help }\n", rd->filename);
  15        pr_out("       %s statistic OBJECT show\n", rd->filename);
  16        pr_out("       %s statistic OBJECT show link [ DEV/PORT_INDEX ] [ FILTER-NAME FILTER-VALUE ]\n", rd->filename);
  17        pr_out("       %s statistic OBJECT mode\n", rd->filename);
  18        pr_out("       %s statistic OBJECT set COUNTER_SCOPE [DEV/PORT_INDEX] auto {CRITERIA | off}\n", rd->filename);
  19        pr_out("       %s statistic OBJECT bind COUNTER_SCOPE [DEV/PORT_INDEX] [OBJECT-ID] [COUNTER-ID]\n", rd->filename);
  20        pr_out("       %s statistic OBJECT unbind COUNTER_SCOPE [DEV/PORT_INDEX] [COUNTER-ID]\n", rd->filename);
  21        pr_out("       %s statistic show\n", rd->filename);
  22        pr_out("       %s statistic show link [ DEV/PORT_INDEX ]\n", rd->filename);
  23        pr_out("       %s statistic mode [ supported ]\n", rd->filename);
  24        pr_out("       %s statistic mode [ supported ] link [ DEV/PORT_INDEX ]\n", rd->filename);
  25        pr_out("       %s statistic set link [ DEV/PORT_INDEX ] optional-counters [ OPTIONAL-COUNTERS ]\n", rd->filename);
  26        pr_out("       %s statistic unset link [ DEV/PORT_INDEX ] optional-counters\n", rd->filename);
  27        pr_out("where  OBJECT: = { qp }\n");
  28        pr_out("       CRITERIA : = { type }\n");
  29        pr_out("       COUNTER_SCOPE: = { link | dev }\n");
  30        pr_out("       FILTER_NAME: = { cntn | lqpn | pid }\n");
  31        pr_out("Examples:\n");
  32        pr_out("       %s statistic qp show\n", rd->filename);
  33        pr_out("       %s statistic qp show link mlx5_2/1\n", rd->filename);
  34        pr_out("       %s statistic qp mode\n", rd->filename);
  35        pr_out("       %s statistic qp mode link mlx5_0\n", rd->filename);
  36        pr_out("       %s statistic qp set link mlx5_2/1 auto type on\n", rd->filename);
  37        pr_out("       %s statistic qp set link mlx5_2/1 auto off\n", rd->filename);
  38        pr_out("       %s statistic qp bind link mlx5_2/1 lqpn 178\n", rd->filename);
  39        pr_out("       %s statistic qp bind link mlx5_2/1 lqpn 178 cntn 4\n", rd->filename);
  40        pr_out("       %s statistic qp unbind link mlx5_2/1 cntn 4\n", rd->filename);
  41        pr_out("       %s statistic qp unbind link mlx5_2/1 cntn 4 lqpn 178\n", rd->filename);
  42        pr_out("       %s statistic show\n", rd->filename);
  43        pr_out("       %s statistic show link mlx5_2/1\n", rd->filename);
  44        pr_out("       %s statistic mode\n", rd->filename);
  45        pr_out("       %s statistic mode link mlx5_2/1\n", rd->filename);
  46        pr_out("       %s statistic mode supported\n", rd->filename);
  47        pr_out("       %s statistic mode supported link mlx5_2/1\n", rd->filename);
  48        pr_out("       %s statistic set link mlx5_2/1 optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts\n", rd->filename);
  49        pr_out("       %s statistic unset link mlx5_2/1 optional-counters\n", rd->filename);
  50
  51        return 0;
  52}
  53
  54struct counter_param {
  55        char *name;
  56        uint32_t attr;
  57};
  58
  59static struct counter_param auto_params[] = {
  60        { "type", RDMA_COUNTER_MASK_QP_TYPE, },
  61        { "pid", RDMA_COUNTER_MASK_PID, },
  62        { NULL },
  63};
  64
  65static int prepare_auto_mode_str(struct nlattr **tb, uint32_t mask,
  66                                 char *output, int len)
  67{
  68        char s[] = "qp auto";
  69        int i, outlen = strlen(s);
  70        bool first = true;
  71
  72        memset(output, 0, len);
  73        snprintf(output, len, "%s", s);
  74
  75        if (mask) {
  76                for (i = 0; auto_params[i].name != NULL; i++) {
  77                        if (mask & auto_params[i].attr) {
  78                                outlen += strlen(auto_params[i].name) + 1;
  79                                if (outlen >= len)
  80                                        return -EINVAL;
  81                                if (first) {
  82                                        strcat(output, " ");
  83                                        first = false;
  84                                } else
  85                                        strcat(output, ",");
  86
  87                                strcat(output, auto_params[i].name);
  88                        }
  89                }
  90
  91                if (outlen + strlen(" on") >= len)
  92                        return -EINVAL;
  93                strcat(output, " on");
  94        } else {
  95                if (outlen + strlen(" off") >= len)
  96                        return -EINVAL;
  97                strcat(output, " off");
  98        }
  99
 100        return 0;
 101}
 102
 103static int qp_link_get_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
 104{
 105        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 106        uint32_t mode = 0, mask = 0;
 107        char output[128] = {};
 108        struct rd *rd = data;
 109        uint32_t idx, port;
 110        const char *name;
 111
 112        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 113        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME])
 114                return MNL_CB_ERROR;
 115
 116        if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
 117                pr_err("This tool doesn't support switches yet\n");
 118                return MNL_CB_ERROR;
 119        }
 120
 121        idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
 122        port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
 123        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 124        if (tb[RDMA_NLDEV_ATTR_STAT_MODE])
 125                mode = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_MODE]);
 126
 127        if (mode == RDMA_COUNTER_MODE_AUTO) {
 128                if (!tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK])
 129                        return MNL_CB_ERROR;
 130                mask = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]);
 131                prepare_auto_mode_str(tb, mask, output, sizeof(output));
 132        } else {
 133                snprintf(output, sizeof(output), "qp auto off");
 134        }
 135
 136        open_json_object(NULL);
 137        print_link(rd, idx, name, port, tb);
 138        print_color_string(PRINT_ANY, COLOR_NONE, "mode", "mode %s ", output);
 139        newline(rd);
 140        return MNL_CB_OK;
 141}
 142
 143static int stat_one_qp_link_get_mode(struct rd *rd)
 144{
 145        uint32_t seq;
 146        int ret;
 147
 148        if (!rd->port_idx)
 149                return 0;
 150
 151        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET,
 152                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 153
 154        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 155        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 156        /* Make RDMA_NLDEV_ATTR_STAT_MODE valid so that kernel knows
 157         * return only mode instead of all counters
 158         */
 159        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 160                         RDMA_COUNTER_MODE_MANUAL);
 161        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 162        ret = rd_send_msg(rd);
 163        if (ret)
 164                return ret;
 165
 166        ret = rd_recv_msg(rd, qp_link_get_mode_parse_cb, rd, seq);
 167        return ret;
 168}
 169
 170static int stat_qp_link_get_mode(struct rd *rd)
 171{
 172        return rd_exec_link(rd, stat_one_qp_link_get_mode, false);
 173}
 174
 175static int stat_qp_get_mode(struct rd *rd)
 176{
 177        const struct rd_cmd cmds[] = {
 178                { NULL,         stat_qp_link_get_mode },
 179                { "link",       stat_qp_link_get_mode },
 180                { "help",       stat_help },
 181                { 0 }
 182        };
 183
 184        return rd_exec_cmd(rd, cmds, "parameter");
 185}
 186
 187int res_get_hwcounters(struct rd *rd, struct nlattr *hwc_table, bool print)
 188{
 189        struct nlattr *nla_entry;
 190        const char *nm;
 191        uint64_t v;
 192        int err;
 193
 194        mnl_attr_for_each_nested(nla_entry, hwc_table) {
 195                struct nlattr *hw_line[RDMA_NLDEV_ATTR_MAX] = {};
 196
 197                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, hw_line);
 198                if (err != MNL_CB_OK)
 199                        return -EINVAL;
 200
 201                if (!hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] ||
 202                    !hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]) {
 203                        return -EINVAL;
 204                }
 205
 206                if (!print)
 207                        continue;
 208
 209                nm = mnl_attr_get_str(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
 210                v = mnl_attr_get_u64(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]);
 211                if (rd->pretty_output && !rd->json_output)
 212                        newline_indent(rd);
 213                res_print_u64(rd, nm, v, hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
 214        }
 215
 216        return MNL_CB_OK;
 217}
 218
 219static int res_counter_line(struct rd *rd, const char *name, int index,
 220                       struct nlattr **nla_line)
 221{
 222        uint32_t cntn, port = 0, pid = 0, qpn, qp_type = 0;
 223        struct nlattr *hwc_table, *qp_table;
 224        struct nlattr *nla_entry;
 225        const char *comm = NULL;
 226        bool isfirst;
 227        int err;
 228
 229        if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX])
 230                port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]);
 231
 232        hwc_table = nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS];
 233        qp_table = nla_line[RDMA_NLDEV_ATTR_RES_QP];
 234        if (!hwc_table || !qp_table ||
 235            !nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
 236                return MNL_CB_ERROR;
 237
 238        cntn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
 239        if (rd_is_filtered_attr(rd, "cntn", cntn,
 240                                nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]))
 241                return MNL_CB_OK;
 242
 243        if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
 244                qp_type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]);
 245
 246        if (rd_is_string_filtered_attr(rd, "qp-type", qp_types_to_str(qp_type),
 247                                       nla_line[RDMA_NLDEV_ATTR_RES_TYPE]))
 248                return MNL_CB_OK;
 249
 250        if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
 251                SPRINT_BUF(b);
 252
 253                pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 254                if (!get_task_name(pid, b, sizeof(b)))
 255                        comm = b;
 256        } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
 257                /* discard const from mnl_attr_get_str */
 258                comm = (char *)mnl_attr_get_str(
 259                        nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 260        }
 261
 262        if (rd_is_filtered_attr(rd, "pid", pid,
 263                                nla_line[RDMA_NLDEV_ATTR_RES_PID]))
 264                return MNL_CB_OK;
 265
 266        mnl_attr_for_each_nested(nla_entry, qp_table) {
 267                struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
 268
 269                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
 270                if (err != MNL_CB_OK)
 271                        return -EINVAL;
 272
 273                if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
 274                        return -EINVAL;
 275
 276                qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 277                if (rd_is_filtered_attr(rd, "lqpn", qpn,
 278                                        qp_line[RDMA_NLDEV_ATTR_RES_LQPN]))
 279                        return MNL_CB_OK;
 280        }
 281
 282        err = res_get_hwcounters(rd, hwc_table, false);
 283        if (err != MNL_CB_OK)
 284                return err;
 285        open_json_object(NULL);
 286        print_link(rd, index, name, port, nla_line);
 287        print_color_uint(PRINT_ANY, COLOR_NONE, "cntn", "cntn %u ", cntn);
 288        if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
 289                print_qp_type(rd, qp_type);
 290        res_print_u64(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 291        print_comm(rd, comm, nla_line);
 292        res_get_hwcounters(rd, hwc_table, true);
 293        isfirst = true;
 294        open_json_array(PRINT_JSON, "lqpn");
 295        print_color_string(PRINT_FP, COLOR_NONE, NULL, "\n    LQPN: <", NULL);
 296        mnl_attr_for_each_nested(nla_entry, qp_table) {
 297                struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
 298                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
 299                if (err != MNL_CB_OK)
 300                        return -EINVAL;
 301
 302                if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
 303                        return -EINVAL;
 304
 305                qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 306                if (!isfirst)
 307                        print_color_string(PRINT_FP, COLOR_NONE, NULL, ",",
 308                                           NULL);
 309                print_color_uint(PRINT_ANY, COLOR_NONE, NULL, "%d", qpn);
 310                isfirst = false;
 311        }
 312        close_json_array(PRINT_ANY, ">");
 313        newline(rd);
 314        return MNL_CB_OK;
 315}
 316
 317static int stat_qp_show_parse_cb(const struct nlmsghdr *nlh, void *data)
 318{
 319        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 320        struct nlattr *nla_table, *nla_entry;
 321        struct rd *rd = data;
 322        const char *name;
 323        uint32_t idx;
 324        int ret = MNL_CB_OK;
 325
 326        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 327        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
 328            !tb[RDMA_NLDEV_ATTR_STAT_COUNTER])
 329                return MNL_CB_ERROR;
 330
 331        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 332        idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
 333        nla_table = tb[RDMA_NLDEV_ATTR_STAT_COUNTER];
 334
 335        mnl_attr_for_each_nested(nla_entry, nla_table) {
 336                struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
 337
 338                ret = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
 339                if (ret != MNL_CB_OK)
 340                        break;
 341
 342                ret = res_counter_line(rd, name, idx, nla_line);
 343                if (ret != MNL_CB_OK)
 344                        break;
 345        }
 346
 347        return ret;
 348}
 349
 350static const struct filters stat_valid_filters[MAX_NUMBER_OF_FILTERS] = {
 351        { .name = "cntn", .is_number = true },
 352        { .name = "lqpn", .is_number = true },
 353        { .name = "pid", .is_number = true },
 354        { .name = "qp-type", .is_number = false },
 355};
 356
 357static int stat_qp_show_one_link(struct rd *rd)
 358{
 359        int flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
 360        uint32_t seq;
 361        int ret;
 362
 363        if (!rd->port_idx)
 364                return 0;
 365
 366        ret = rd_build_filter(rd, stat_valid_filters);
 367        if (ret)
 368                return ret;
 369
 370        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
 371        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 372        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 373        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 374        ret = rd_send_msg(rd);
 375        if (ret)
 376                return ret;
 377
 378        ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq);
 379        return ret;
 380}
 381
 382static int stat_qp_show_link(struct rd *rd)
 383{
 384        return rd_exec_link(rd, stat_qp_show_one_link, false);
 385}
 386
 387static int stat_qp_show(struct rd *rd)
 388{
 389        const struct rd_cmd cmds[] = {
 390                { NULL,         stat_qp_show_link },
 391                { "link",       stat_qp_show_link },
 392                { "help",       stat_help },
 393                { 0 }
 394        };
 395
 396        return rd_exec_cmd(rd, cmds, "parameter");
 397}
 398
 399static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask)
 400{
 401        uint32_t seq;
 402
 403        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
 404                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 405
 406        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 407        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 408        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 409        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 410                         RDMA_COUNTER_MODE_AUTO);
 411        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask);
 412
 413        return rd_sendrecv_msg(rd, seq);
 414}
 415
 416static int stat_get_auto_mode_mask(struct rd *rd)
 417{
 418        char *modes = rd_argv(rd), *mode, *saved_ptr;
 419        const char *delim = ",";
 420        int mask = 0, found, i;
 421
 422        if (!modes)
 423                return mask;
 424
 425        mode = strtok_r(modes, delim, &saved_ptr);
 426        do {
 427                if (!mode)
 428                        break;
 429
 430                found = false;
 431                for (i = 0;  auto_params[i].name != NULL; i++) {
 432                        if (!strcmp(mode, auto_params[i].name)) {
 433                                mask |= auto_params[i].attr;
 434                                found = true;
 435                                break;
 436                        }
 437                }
 438
 439                if (!found) {
 440                        pr_err("Unknown auto mode '%s'.\n", mode);
 441                        mask = 0;
 442                        break;
 443                }
 444
 445                mode = strtok_r(NULL, delim, &saved_ptr);
 446        } while(1);
 447
 448        if (mask)
 449                rd_arg_inc(rd);
 450
 451        return mask;
 452}
 453
 454static int stat_one_qp_set_link_auto(struct rd *rd)
 455{
 456        int auto_mask = 0;
 457
 458        if (!rd_argc(rd))
 459                return -EINVAL;
 460
 461        if (!strcmpx(rd_argv(rd), "off")) {
 462                rd_arg_inc(rd);
 463                return stat_qp_set_link_auto_sendmsg(rd, 0);
 464        }
 465
 466        auto_mask = stat_get_auto_mode_mask(rd);
 467        if (!auto_mask || !rd_argc(rd))
 468                return -EINVAL;
 469
 470        if (!strcmpx(rd_argv(rd), "on")) {
 471                rd_arg_inc(rd);
 472                return stat_qp_set_link_auto_sendmsg(rd, auto_mask);
 473        } else {
 474                pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
 475                return -EINVAL;
 476        }
 477}
 478
 479static int stat_one_qp_set_link(struct rd *rd)
 480{
 481        const struct rd_cmd cmds[] = {
 482                { NULL,         stat_one_qp_link_get_mode },
 483                { "auto",       stat_one_qp_set_link_auto },
 484                { 0 }
 485        };
 486
 487        if (!rd->port_idx)
 488                return 0;
 489
 490        return rd_exec_cmd(rd, cmds, "parameter");
 491}
 492
 493static int stat_qp_set_link(struct rd *rd)
 494{
 495        return rd_exec_link(rd, stat_one_qp_set_link, false);
 496}
 497
 498static int stat_qp_set(struct rd *rd)
 499{
 500        const struct rd_cmd cmds[] = {
 501                { NULL,         stat_help },
 502                { "link",       stat_qp_set_link },
 503                { "help",       stat_help },
 504                { 0 }
 505        };
 506
 507        return rd_exec_cmd(rd, cmds, "parameter");
 508}
 509
 510static int stat_get_arg_str(struct rd *rd, const char *arg, char **value, bool allow_empty)
 511{
 512        int len = 0;
 513
 514        if (strcmpx(rd_argv(rd), arg) != 0) {
 515                pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
 516                return -EINVAL;
 517        }
 518
 519        rd_arg_inc(rd);
 520        if (!rd_no_arg(rd)) {
 521                *value = strdup(rd_argv(rd));
 522                len = strlen(*value);
 523                rd_arg_inc(rd);
 524        }
 525
 526        if ((allow_empty && len) || (!allow_empty && !len)) {
 527                stat_help(rd);
 528                return -EINVAL;
 529        }
 530
 531        return 0;
 532}
 533
 534static int stat_get_arg(struct rd *rd, const char *arg)
 535{
 536        int value = 0;
 537        char *endp;
 538
 539        if (strcmpx(rd_argv(rd), arg) != 0)
 540                return -EINVAL;
 541
 542        rd_arg_inc(rd);
 543
 544        if (rd_is_multiarg(rd)) {
 545                pr_err("The parameter %s shouldn't include range\n", arg);
 546                return -EINVAL;
 547        }
 548
 549        value = strtol(rd_argv(rd), &endp, 10);
 550        rd_arg_inc(rd);
 551
 552        return value;
 553}
 554
 555static int stat_one_qp_bind(struct rd *rd)
 556{
 557        int lqpn = 0, cntn = 0, ret;
 558        uint32_t seq;
 559
 560        if (rd_no_arg(rd)) {
 561                stat_help(rd);
 562                return -EINVAL;
 563        }
 564
 565        ret = rd_build_filter(rd, stat_valid_filters);
 566        if (ret)
 567                return ret;
 568
 569        lqpn = stat_get_arg(rd, "lqpn");
 570        if (lqpn < 0)
 571                return lqpn;
 572
 573        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
 574                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 575
 576        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 577                         RDMA_COUNTER_MODE_MANUAL);
 578
 579        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 580        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 581        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 582        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
 583
 584        if (rd_argc(rd)) {
 585                cntn = stat_get_arg(rd, "cntn");
 586                if (cntn < 0)
 587                        return cntn;
 588
 589                mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID,
 590                                 cntn);
 591        }
 592
 593        return rd_sendrecv_msg(rd, seq);
 594}
 595
 596static int do_stat_qp_unbind_lqpn(struct rd *rd, uint32_t cntn, uint32_t lqpn)
 597{
 598        uint32_t seq;
 599
 600        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_DEL,
 601                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 602
 603        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 604                         RDMA_COUNTER_MODE_MANUAL);
 605        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 606        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 607        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 608        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
 609        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
 610
 611        return rd_sendrecv_msg(rd, seq);
 612}
 613
 614static int stat_get_counter_parse_cb(const struct nlmsghdr *nlh, void *data)
 615{
 616        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 617        struct nlattr *nla_table, *nla_entry;
 618        struct rd *rd = data;
 619        uint32_t lqpn, cntn;
 620        int err;
 621
 622        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 623
 624        if (!tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
 625                return MNL_CB_ERROR;
 626        cntn = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
 627
 628        nla_table = tb[RDMA_NLDEV_ATTR_RES_QP];
 629        if (!nla_table)
 630                return MNL_CB_ERROR;
 631
 632        mnl_attr_for_each_nested(nla_entry, nla_table) {
 633                struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
 634
 635                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
 636                if (err != MNL_CB_OK)
 637                        return -EINVAL;
 638
 639                if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN])
 640                        return -EINVAL;
 641
 642                lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 643                err = do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
 644                if (err)
 645                        return MNL_CB_ERROR;
 646        }
 647
 648        return MNL_CB_OK;
 649}
 650
 651static int stat_one_qp_unbind(struct rd *rd)
 652{
 653        int flags = NLM_F_REQUEST | NLM_F_ACK, ret;
 654        char buf[MNL_SOCKET_BUFFER_SIZE];
 655        int lqpn = 0, cntn = 0;
 656        unsigned int portid;
 657        uint32_t seq;
 658
 659        if (rd_no_arg(rd)) {
 660                stat_help(rd);
 661                return -EINVAL;
 662        }
 663
 664        ret = rd_build_filter(rd, stat_valid_filters);
 665        if (ret)
 666                return ret;
 667
 668        cntn = stat_get_arg(rd, "cntn");
 669        if (cntn < 0)
 670                return cntn;
 671
 672        if (rd_argc(rd)) {
 673                lqpn = stat_get_arg(rd, "lqpn");
 674                if (lqpn < 0)
 675                        return lqpn;
 676                return do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
 677        }
 678
 679        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
 680        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 681        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 682        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 683        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
 684        ret = rd_send_msg(rd);
 685        if (ret)
 686                return ret;
 687
 688
 689        /* Can't use rd_recv_msg() since the callback also calls it (recursively),
 690         * then rd_recv_msg() always return -1 here
 691         */
 692        portid = mnl_socket_get_portid(rd->nl);
 693        ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
 694        if (ret <= 0)
 695                return ret;
 696
 697        ret = mnl_cb_run(buf, ret, seq, portid, stat_get_counter_parse_cb, rd);
 698        mnl_socket_close(rd->nl);
 699        if (ret != MNL_CB_OK)
 700                return ret;
 701
 702        return 0;
 703}
 704
 705static int stat_qp_bind_link(struct rd *rd)
 706{
 707        return rd_exec_link(rd, stat_one_qp_bind, true);
 708}
 709
 710static int stat_qp_bind(struct rd *rd)
 711{
 712        const struct rd_cmd cmds[] = {
 713                { NULL,         stat_help },
 714                { "link",       stat_qp_bind_link },
 715                { "help",       stat_help },
 716                { 0 },
 717        };
 718
 719        return rd_exec_cmd(rd, cmds, "parameter");
 720}
 721
 722static int stat_qp_unbind_link(struct rd *rd)
 723{
 724        return rd_exec_link(rd, stat_one_qp_unbind, true);
 725}
 726
 727static int stat_qp_unbind(struct rd *rd)
 728{
 729        const struct rd_cmd cmds[] = {
 730                { NULL,         stat_help },
 731                { "link",       stat_qp_unbind_link },
 732                { "help",       stat_help },
 733                { 0 },
 734        };
 735
 736        return rd_exec_cmd(rd, cmds, "parameter");
 737}
 738
 739static int stat_qp(struct rd *rd)
 740{
 741        const struct rd_cmd cmds[] =  {
 742                { NULL,         stat_qp_show },
 743                { "show",       stat_qp_show },
 744                { "list",       stat_qp_show },
 745                { "mode",       stat_qp_get_mode },
 746                { "set",        stat_qp_set },
 747                { "bind",       stat_qp_bind },
 748                { "unbind",     stat_qp_unbind },
 749                { "help",       stat_help },
 750                { 0 }
 751        };
 752
 753        return rd_exec_cmd(rd, cmds, "parameter");
 754}
 755
 756static int do_stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data,
 757                                 bool supported)
 758{
 759        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 760        struct nlattr *nla_entry;
 761        const char *dev, *name;
 762        struct rd *rd = data;
 763        int enabled, err = 0;
 764        bool isfirst = true;
 765        uint32_t port;
 766
 767        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 768        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
 769            !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
 770            !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
 771                return MNL_CB_ERROR;
 772
 773        dev = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 774        port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
 775
 776        mnl_attr_for_each_nested(nla_entry,
 777                                 tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
 778                struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {};
 779
 780                err  = mnl_attr_parse_nested(nla_entry, rd_attr_cb, cnt);
 781                if ((err != MNL_CB_OK) ||
 782                    (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]))
 783                        return -EINVAL;
 784
 785                if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
 786                        continue;
 787
 788                enabled = mnl_attr_get_u8(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC]);
 789                name = mnl_attr_get_str(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
 790                if (supported || enabled) {
 791                        if (isfirst) {
 792                                open_json_object(NULL);
 793                                print_color_string(PRINT_ANY, COLOR_NONE,
 794                                                   "ifname", "link %s/", dev);
 795                                print_color_uint(PRINT_ANY, COLOR_NONE, "port",
 796                                                 "%u ", port);
 797                                if (supported)
 798                                        open_json_array(PRINT_ANY,
 799                                                "supported optional-counters");
 800                                else
 801                                        open_json_array(PRINT_ANY,
 802                                                        "optional-counters");
 803                                print_color_string(PRINT_FP, COLOR_NONE, NULL,
 804                                                   " ", NULL);
 805                                isfirst = false;
 806                        } else {
 807                                print_color_string(PRINT_FP, COLOR_NONE, NULL,
 808                                                   ",", NULL);
 809                        }
 810                        if (rd->pretty_output && !rd->json_output)
 811                                newline_indent(rd);
 812
 813                        print_color_string(PRINT_ANY, COLOR_NONE, NULL, "%s",
 814                                           name);
 815                }
 816        }
 817
 818        if (!isfirst) {
 819                close_json_array(PRINT_JSON, NULL);
 820                newline(rd);
 821        }
 822
 823        return 0;
 824}
 825
 826static int stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
 827{
 828        return do_stat_mode_parse_cb(nlh, data, false);
 829}
 830
 831static int stat_mode_parse_cb_supported(const struct nlmsghdr *nlh, void *data)
 832{
 833        return do_stat_mode_parse_cb(nlh, data, true);
 834}
 835
 836static int stat_one_link_get_status_req(struct rd *rd, uint32_t *seq)
 837{
 838        int flags = NLM_F_REQUEST | NLM_F_ACK;
 839
 840        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET_STATUS, seq,  flags);
 841        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 842        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 843
 844        return rd_send_msg(rd);
 845}
 846
 847static int stat_one_link_get_mode(struct rd *rd)
 848{
 849        uint32_t seq;
 850        int err;
 851
 852        if (!rd->port_idx)
 853                return 0;
 854
 855        err = stat_one_link_get_status_req(rd, &seq);
 856        if (err)
 857                return err;
 858
 859        return rd_recv_msg(rd, stat_mode_parse_cb, rd, seq);
 860}
 861
 862static int stat_one_link_get_mode_supported(struct rd *rd)
 863{
 864        uint32_t seq;
 865        int err;
 866
 867        if (!rd->port_idx)
 868                return 0;
 869
 870        err = stat_one_link_get_status_req(rd, &seq);
 871        if (err)
 872                return err;
 873
 874        return rd_recv_msg(rd, stat_mode_parse_cb_supported, rd, seq);
 875}
 876
 877static int stat_link_get_mode(struct rd *rd)
 878{
 879        return rd_exec_link(rd, stat_one_link_get_mode, false);
 880}
 881
 882static int stat_link_get_mode_supported(struct rd *rd)
 883{
 884        return rd_exec_link(rd, stat_one_link_get_mode_supported, false);
 885}
 886
 887static int stat_mode_supported(struct rd *rd)
 888{
 889        const struct rd_cmd cmds[] = {
 890                { NULL,         stat_link_get_mode_supported },
 891                { "link",       stat_link_get_mode_supported },
 892                { "help",       stat_help },
 893                { 0 },
 894        };
 895        return rd_exec_cmd(rd, cmds, "parameter");
 896}
 897
 898static int stat_mode(struct rd *rd)
 899{
 900        const struct rd_cmd cmds[] = {
 901                { NULL,         stat_link_get_mode },
 902                { "link",       stat_link_get_mode },
 903                { "show",       stat_link_get_mode },
 904                { "supported",  stat_mode_supported },
 905                { "help",       stat_help },
 906                { 0 },
 907        };
 908
 909        return rd_exec_cmd(rd, cmds, "parameter");
 910}
 911
 912static int stat_one_set_link_opcounters(const struct nlmsghdr *nlh, void *data)
 913{
 914        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 915        struct nlattr *nla_entry, *tb_set;
 916        int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
 917        char *opcnt, *opcnts;
 918        struct rd *rd = data;
 919        uint32_t seq;
 920        bool found;
 921
 922        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 923        if (!tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
 924                return MNL_CB_ERROR;
 925
 926        if (rd_no_arg(rd)) {
 927                stat_help(rd);
 928                return -EINVAL;
 929        }
 930
 931        ret = stat_get_arg_str(rd, "optional-counters", &opcnts, false);
 932        if (ret)
 933                return ret;
 934
 935        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
 936        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
 937                         rd->dev_idx);
 938        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
 939                         rd->port_idx);
 940
 941        tb_set = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
 942
 943        opcnt = strtok(opcnts, ",");
 944        while (opcnt) {
 945                found = false;
 946                mnl_attr_for_each_nested(nla_entry,
 947                                         tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
 948                        struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {}, *nm, *id;
 949
 950                        if (mnl_attr_parse_nested(nla_entry, rd_attr_cb,
 951                                                  cnt) != MNL_CB_OK)
 952                                return -EINVAL;
 953
 954                        nm = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME];
 955                        id = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX];
 956                        if (!nm || ! id)
 957                                return -EINVAL;
 958
 959                        if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
 960                                continue;
 961
 962                        if (strcmp(opcnt, mnl_attr_get_str(nm)) == 0) {
 963                                mnl_attr_put_u32(rd->nlh,
 964                                                 RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX,
 965                                                 mnl_attr_get_u32(id));
 966                                found = true;
 967                        }
 968                }
 969
 970                if (!found)
 971                        return -EINVAL;
 972
 973                opcnt = strtok(NULL, ",");
 974        }
 975        mnl_attr_nest_end(rd->nlh, tb_set);
 976
 977        return rd_sendrecv_msg(rd, seq);
 978}
 979
 980static int stat_one_set_link(struct rd *rd)
 981{
 982        uint32_t seq;
 983        int err;
 984
 985        if (!rd->port_idx)
 986                return 0;
 987
 988        err = stat_one_link_get_status_req(rd, &seq);
 989        if (err)
 990                return err;
 991
 992        return rd_recv_msg(rd, stat_one_set_link_opcounters, rd, seq);
 993}
 994
 995static int stat_set_link(struct rd *rd)
 996{
 997        return rd_exec_link(rd, stat_one_set_link, true);
 998}
 999
1000static int stat_set(struct rd *rd)
1001{
1002        const struct rd_cmd cmds[] = {
1003                { NULL,         stat_help },
1004                { "link",       stat_set_link },
1005                { "help",       stat_help },
1006                { 0 },
1007        };
1008        return rd_exec_cmd(rd, cmds, "parameter");
1009}
1010
1011static int stat_one_unset_link_opcounters(struct rd *rd)
1012{
1013        int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
1014        struct nlattr *tbl;
1015        uint32_t seq;
1016        char *opcnts;
1017
1018        if (rd_no_arg(rd)) {
1019                stat_help(rd);
1020                return -EINVAL;
1021        }
1022
1023        ret = stat_get_arg_str(rd, "optional-counters", &opcnts, true);
1024        if (ret)
1025                return ret;
1026
1027        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
1028        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
1029                         rd->dev_idx);
1030        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
1031                         rd->port_idx);
1032
1033        tbl = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
1034        mnl_attr_nest_end(rd->nlh, tbl);
1035
1036        return rd_sendrecv_msg(rd, seq);
1037}
1038
1039static int stat_one_unset_link(struct rd *rd)
1040{
1041        return stat_one_unset_link_opcounters(rd);
1042}
1043
1044static int stat_unset_link(struct rd *rd)
1045{
1046        return rd_exec_link(rd, stat_one_unset_link, true);
1047}
1048
1049static int stat_unset(struct rd *rd)
1050{
1051        const struct rd_cmd cmds[] = {
1052                { NULL,         stat_help },
1053                { "link",       stat_unset_link },
1054                { "help",       stat_help },
1055                { 0 },
1056        };
1057        return rd_exec_cmd(rd, cmds, "parameter");
1058}
1059
1060static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
1061{
1062        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
1063        struct rd *rd = data;
1064        const char *name;
1065        uint32_t port;
1066        int ret;
1067
1068        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
1069        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
1070            !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
1071            !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
1072                return MNL_CB_ERROR;
1073
1074        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
1075        port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
1076        open_json_object(NULL);
1077        print_color_string(PRINT_ANY, COLOR_NONE, "ifname", "link %s/", name);
1078        print_color_uint(PRINT_ANY, COLOR_NONE, "port", "%u ", port);
1079        ret = res_get_hwcounters(rd, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true);
1080
1081        newline(rd);
1082        return ret;
1083}
1084
1085static int stat_show_one_link(struct rd *rd)
1086{
1087        int flags = NLM_F_REQUEST | NLM_F_ACK;
1088        uint32_t seq;
1089        int ret;
1090
1091        if (!rd->port_idx)
1092                return 0;
1093
1094        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq,  flags);
1095        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
1096        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
1097        ret = rd_send_msg(rd);
1098        if (ret)
1099                return ret;
1100
1101        return rd_recv_msg(rd, stat_show_parse_cb, rd, seq);
1102}
1103
1104static int stat_show_link(struct rd *rd)
1105{
1106        return rd_exec_link(rd, stat_show_one_link, false);
1107}
1108
1109static int stat_show(struct rd *rd)
1110{
1111        const struct rd_cmd cmds[] = {
1112                { NULL,         stat_show_link },
1113                { "link",       stat_show_link },
1114                { "mr",         stat_mr },
1115                { "help",       stat_help },
1116                { 0 }
1117        };
1118
1119        return rd_exec_cmd(rd, cmds, "parameter");
1120}
1121
1122int cmd_stat(struct rd *rd)
1123{
1124        const struct rd_cmd cmds[] =  {
1125                { NULL,         stat_show },
1126                { "show",       stat_show },
1127                { "list",       stat_show },
1128                { "help",       stat_help },
1129                { "qp",         stat_qp },
1130                { "mr",         stat_mr },
1131                { "mode",       stat_mode },
1132                { "set",        stat_set },
1133                { "unset",      stat_unset },
1134                { 0 }
1135        };
1136
1137        return rd_exec_cmd(rd, cmds, "statistic command");
1138}
1139