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