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_uint(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                pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 252                comm = get_task_name(pid);
 253        }
 254        if (rd_is_filtered_attr(rd, "pid", pid,
 255                                nla_line[RDMA_NLDEV_ATTR_RES_PID]))
 256                return MNL_CB_OK;
 257
 258        if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
 259                comm = (char *)mnl_attr_get_str(
 260                        nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 261
 262        mnl_attr_for_each_nested(nla_entry, qp_table) {
 263                struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
 264
 265                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
 266                if (err != MNL_CB_OK)
 267                        return -EINVAL;
 268
 269                if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
 270                        return -EINVAL;
 271
 272                qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 273                if (rd_is_filtered_attr(rd, "lqpn", qpn,
 274                                        qp_line[RDMA_NLDEV_ATTR_RES_LQPN]))
 275                        return MNL_CB_OK;
 276        }
 277
 278        err = res_get_hwcounters(rd, hwc_table, false);
 279        if (err != MNL_CB_OK)
 280                return err;
 281        open_json_object(NULL);
 282        print_link(rd, index, name, port, nla_line);
 283        print_color_uint(PRINT_ANY, COLOR_NONE, "cntn", "cntn %u ", cntn);
 284        if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
 285                print_qp_type(rd, qp_type);
 286        res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 287        print_comm(rd, comm, nla_line);
 288        res_get_hwcounters(rd, hwc_table, true);
 289        isfirst = true;
 290        open_json_array(PRINT_JSON, "lqpn");
 291        print_color_string(PRINT_FP, COLOR_NONE, NULL, "\n    LQPN: <", NULL);
 292        mnl_attr_for_each_nested(nla_entry, qp_table) {
 293                struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
 294                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
 295                if (err != MNL_CB_OK)
 296                        return -EINVAL;
 297
 298                if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
 299                        return -EINVAL;
 300
 301                qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 302                if (!isfirst)
 303                        print_color_string(PRINT_FP, COLOR_NONE, NULL, ",",
 304                                           NULL);
 305                print_color_uint(PRINT_ANY, COLOR_NONE, NULL, "%d", qpn);
 306                isfirst = false;
 307        }
 308        close_json_array(PRINT_ANY, ">");
 309        newline(rd);
 310        return MNL_CB_OK;
 311}
 312
 313static int stat_qp_show_parse_cb(const struct nlmsghdr *nlh, void *data)
 314{
 315        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 316        struct nlattr *nla_table, *nla_entry;
 317        struct rd *rd = data;
 318        const char *name;
 319        uint32_t idx;
 320        int ret = MNL_CB_OK;
 321
 322        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 323        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
 324            !tb[RDMA_NLDEV_ATTR_STAT_COUNTER])
 325                return MNL_CB_ERROR;
 326
 327        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 328        idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
 329        nla_table = tb[RDMA_NLDEV_ATTR_STAT_COUNTER];
 330
 331        mnl_attr_for_each_nested(nla_entry, nla_table) {
 332                struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
 333
 334                ret = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
 335                if (ret != MNL_CB_OK)
 336                        break;
 337
 338                ret = res_counter_line(rd, name, idx, nla_line);
 339                if (ret != MNL_CB_OK)
 340                        break;
 341        }
 342
 343        return ret;
 344}
 345
 346static const struct filters stat_valid_filters[MAX_NUMBER_OF_FILTERS] = {
 347        { .name = "cntn", .is_number = true },
 348        { .name = "lqpn", .is_number = true },
 349        { .name = "pid", .is_number = true },
 350        { .name = "qp-type", .is_number = false },
 351};
 352
 353static int stat_qp_show_one_link(struct rd *rd)
 354{
 355        int flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
 356        uint32_t seq;
 357        int ret;
 358
 359        if (!rd->port_idx)
 360                return 0;
 361
 362        ret = rd_build_filter(rd, stat_valid_filters);
 363        if (ret)
 364                return ret;
 365
 366        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
 367        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 368        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 369        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 370        ret = rd_send_msg(rd);
 371        if (ret)
 372                return ret;
 373
 374        ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq);
 375        return ret;
 376}
 377
 378static int stat_qp_show_link(struct rd *rd)
 379{
 380        return rd_exec_link(rd, stat_qp_show_one_link, false);
 381}
 382
 383static int stat_qp_show(struct rd *rd)
 384{
 385        const struct rd_cmd cmds[] = {
 386                { NULL,         stat_qp_show_link },
 387                { "link",       stat_qp_show_link },
 388                { "help",       stat_help },
 389                { 0 }
 390        };
 391
 392        return rd_exec_cmd(rd, cmds, "parameter");
 393}
 394
 395static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask)
 396{
 397        uint32_t seq;
 398
 399        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
 400                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 401
 402        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 403        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 404        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 405        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 406                         RDMA_COUNTER_MODE_AUTO);
 407        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask);
 408
 409        return rd_sendrecv_msg(rd, seq);
 410}
 411
 412static int stat_get_auto_mode_mask(struct rd *rd)
 413{
 414        char *modes = rd_argv(rd), *mode, *saved_ptr;
 415        const char *delim = ",";
 416        int mask = 0, found, i;
 417
 418        if (!modes)
 419                return mask;
 420
 421        mode = strtok_r(modes, delim, &saved_ptr);
 422        do {
 423                if (!mode)
 424                        break;
 425
 426                found = false;
 427                for (i = 0;  auto_params[i].name != NULL; i++) {
 428                        if (!strcmp(mode, auto_params[i].name)) {
 429                                mask |= auto_params[i].attr;
 430                                found = true;
 431                                break;
 432                        }
 433                }
 434
 435                if (!found) {
 436                        pr_err("Unknown auto mode '%s'.\n", mode);
 437                        mask = 0;
 438                        break;
 439                }
 440
 441                mode = strtok_r(NULL, delim, &saved_ptr);
 442        } while(1);
 443
 444        if (mask)
 445                rd_arg_inc(rd);
 446
 447        return mask;
 448}
 449
 450static int stat_one_qp_set_link_auto(struct rd *rd)
 451{
 452        int auto_mask = 0;
 453
 454        if (!rd_argc(rd))
 455                return -EINVAL;
 456
 457        if (!strcmpx(rd_argv(rd), "off")) {
 458                rd_arg_inc(rd);
 459                return stat_qp_set_link_auto_sendmsg(rd, 0);
 460        }
 461
 462        auto_mask = stat_get_auto_mode_mask(rd);
 463        if (!auto_mask || !rd_argc(rd))
 464                return -EINVAL;
 465
 466        if (!strcmpx(rd_argv(rd), "on")) {
 467                rd_arg_inc(rd);
 468                return stat_qp_set_link_auto_sendmsg(rd, auto_mask);
 469        } else {
 470                pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
 471                return -EINVAL;
 472        }
 473}
 474
 475static int stat_one_qp_set_link(struct rd *rd)
 476{
 477        const struct rd_cmd cmds[] = {
 478                { NULL,         stat_one_qp_link_get_mode },
 479                { "auto",       stat_one_qp_set_link_auto },
 480                { 0 }
 481        };
 482
 483        if (!rd->port_idx)
 484                return 0;
 485
 486        return rd_exec_cmd(rd, cmds, "parameter");
 487}
 488
 489static int stat_qp_set_link(struct rd *rd)
 490{
 491        return rd_exec_link(rd, stat_one_qp_set_link, false);
 492}
 493
 494static int stat_qp_set(struct rd *rd)
 495{
 496        const struct rd_cmd cmds[] = {
 497                { NULL,         stat_help },
 498                { "link",       stat_qp_set_link },
 499                { "help",       stat_help },
 500                { 0 }
 501        };
 502
 503        return rd_exec_cmd(rd, cmds, "parameter");
 504}
 505
 506static int stat_get_arg_str(struct rd *rd, const char *arg, char **value, bool allow_empty)
 507{
 508        int len = 0;
 509
 510        if (strcmpx(rd_argv(rd), arg) != 0) {
 511                pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
 512                return -EINVAL;
 513        }
 514
 515        rd_arg_inc(rd);
 516        if (!rd_no_arg(rd)) {
 517                *value = strdup(rd_argv(rd));
 518                len = strlen(*value);
 519                rd_arg_inc(rd);
 520        }
 521
 522        if ((allow_empty && len) || (!allow_empty && !len)) {
 523                stat_help(rd);
 524                return -EINVAL;
 525        }
 526
 527        return 0;
 528}
 529
 530static int stat_get_arg(struct rd *rd, const char *arg)
 531{
 532        int value = 0;
 533        char *endp;
 534
 535        if (strcmpx(rd_argv(rd), arg) != 0)
 536                return -EINVAL;
 537
 538        rd_arg_inc(rd);
 539
 540        if (rd_is_multiarg(rd)) {
 541                pr_err("The parameter %s shouldn't include range\n", arg);
 542                return -EINVAL;
 543        }
 544
 545        value = strtol(rd_argv(rd), &endp, 10);
 546        rd_arg_inc(rd);
 547
 548        return value;
 549}
 550
 551static int stat_one_qp_bind(struct rd *rd)
 552{
 553        int lqpn = 0, cntn = 0, ret;
 554        uint32_t seq;
 555
 556        if (rd_no_arg(rd)) {
 557                stat_help(rd);
 558                return -EINVAL;
 559        }
 560
 561        ret = rd_build_filter(rd, stat_valid_filters);
 562        if (ret)
 563                return ret;
 564
 565        lqpn = stat_get_arg(rd, "lqpn");
 566        if (lqpn < 0)
 567                return lqpn;
 568
 569        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
 570                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 571
 572        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 573                         RDMA_COUNTER_MODE_MANUAL);
 574
 575        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 576        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 577        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 578        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
 579
 580        if (rd_argc(rd)) {
 581                cntn = stat_get_arg(rd, "cntn");
 582                if (cntn < 0)
 583                        return cntn;
 584
 585                mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID,
 586                                 cntn);
 587        }
 588
 589        return rd_sendrecv_msg(rd, seq);
 590}
 591
 592static int do_stat_qp_unbind_lqpn(struct rd *rd, uint32_t cntn, uint32_t lqpn)
 593{
 594        uint32_t seq;
 595
 596        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_DEL,
 597                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 598
 599        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 600                         RDMA_COUNTER_MODE_MANUAL);
 601        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 602        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 603        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 604        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
 605        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
 606
 607        return rd_sendrecv_msg(rd, seq);
 608}
 609
 610static int stat_get_counter_parse_cb(const struct nlmsghdr *nlh, void *data)
 611{
 612        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 613        struct nlattr *nla_table, *nla_entry;
 614        struct rd *rd = data;
 615        uint32_t lqpn, cntn;
 616        int err;
 617
 618        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 619
 620        if (!tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
 621                return MNL_CB_ERROR;
 622        cntn = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
 623
 624        nla_table = tb[RDMA_NLDEV_ATTR_RES_QP];
 625        if (!nla_table)
 626                return MNL_CB_ERROR;
 627
 628        mnl_attr_for_each_nested(nla_entry, nla_table) {
 629                struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
 630
 631                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
 632                if (err != MNL_CB_OK)
 633                        return -EINVAL;
 634
 635                if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN])
 636                        return -EINVAL;
 637
 638                lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 639                err = do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
 640                if (err)
 641                        return MNL_CB_ERROR;
 642        }
 643
 644        return MNL_CB_OK;
 645}
 646
 647static int stat_one_qp_unbind(struct rd *rd)
 648{
 649        int flags = NLM_F_REQUEST | NLM_F_ACK, ret;
 650        char buf[MNL_SOCKET_BUFFER_SIZE];
 651        int lqpn = 0, cntn = 0;
 652        unsigned int portid;
 653        uint32_t seq;
 654
 655        if (rd_no_arg(rd)) {
 656                stat_help(rd);
 657                return -EINVAL;
 658        }
 659
 660        ret = rd_build_filter(rd, stat_valid_filters);
 661        if (ret)
 662                return ret;
 663
 664        cntn = stat_get_arg(rd, "cntn");
 665        if (cntn < 0)
 666                return cntn;
 667
 668        if (rd_argc(rd)) {
 669                lqpn = stat_get_arg(rd, "lqpn");
 670                if (lqpn < 0)
 671                        return lqpn;
 672                return do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
 673        }
 674
 675        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
 676        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 677        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 678        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 679        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
 680        ret = rd_send_msg(rd);
 681        if (ret)
 682                return ret;
 683
 684
 685        /* Can't use rd_recv_msg() since the callback also calls it (recursively),
 686         * then rd_recv_msg() always return -1 here
 687         */
 688        portid = mnl_socket_get_portid(rd->nl);
 689        ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
 690        if (ret <= 0)
 691                return ret;
 692
 693        ret = mnl_cb_run(buf, ret, seq, portid, stat_get_counter_parse_cb, rd);
 694        mnl_socket_close(rd->nl);
 695        if (ret != MNL_CB_OK)
 696                return ret;
 697
 698        return 0;
 699}
 700
 701static int stat_qp_bind_link(struct rd *rd)
 702{
 703        return rd_exec_link(rd, stat_one_qp_bind, true);
 704}
 705
 706static int stat_qp_bind(struct rd *rd)
 707{
 708        const struct rd_cmd cmds[] = {
 709                { NULL,         stat_help },
 710                { "link",       stat_qp_bind_link },
 711                { "help",       stat_help },
 712                { 0 },
 713        };
 714
 715        return rd_exec_cmd(rd, cmds, "parameter");
 716}
 717
 718static int stat_qp_unbind_link(struct rd *rd)
 719{
 720        return rd_exec_link(rd, stat_one_qp_unbind, true);
 721}
 722
 723static int stat_qp_unbind(struct rd *rd)
 724{
 725        const struct rd_cmd cmds[] = {
 726                { NULL,         stat_help },
 727                { "link",       stat_qp_unbind_link },
 728                { "help",       stat_help },
 729                { 0 },
 730        };
 731
 732        return rd_exec_cmd(rd, cmds, "parameter");
 733}
 734
 735static int stat_qp(struct rd *rd)
 736{
 737        const struct rd_cmd cmds[] =  {
 738                { NULL,         stat_qp_show },
 739                { "show",       stat_qp_show },
 740                { "list",       stat_qp_show },
 741                { "mode",       stat_qp_get_mode },
 742                { "set",        stat_qp_set },
 743                { "bind",       stat_qp_bind },
 744                { "unbind",     stat_qp_unbind },
 745                { "help",       stat_help },
 746                { 0 }
 747        };
 748
 749        return rd_exec_cmd(rd, cmds, "parameter");
 750}
 751
 752static int do_stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data,
 753                                 bool supported)
 754{
 755        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 756        struct nlattr *nla_entry;
 757        const char *dev, *name;
 758        struct rd *rd = data;
 759        int enabled, err = 0;
 760        bool isfirst = true;
 761        uint32_t port;
 762
 763        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 764        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
 765            !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
 766            !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
 767                return MNL_CB_ERROR;
 768
 769        dev = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 770        port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
 771
 772        mnl_attr_for_each_nested(nla_entry,
 773                                 tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
 774                struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {};
 775
 776                err  = mnl_attr_parse_nested(nla_entry, rd_attr_cb, cnt);
 777                if ((err != MNL_CB_OK) ||
 778                    (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]))
 779                        return -EINVAL;
 780
 781                if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
 782                        continue;
 783
 784                enabled = mnl_attr_get_u8(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC]);
 785                name = mnl_attr_get_str(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
 786                if (supported || enabled) {
 787                        if (isfirst) {
 788                                open_json_object(NULL);
 789                                print_color_string(PRINT_ANY, COLOR_NONE,
 790                                                   "ifname", "link %s/", dev);
 791                                print_color_uint(PRINT_ANY, COLOR_NONE, "port",
 792                                                 "%u ", port);
 793                                if (supported)
 794                                        open_json_array(PRINT_ANY,
 795                                                "supported optional-counters");
 796                                else
 797                                        open_json_array(PRINT_ANY,
 798                                                        "optional-counters");
 799                                print_color_string(PRINT_FP, COLOR_NONE, NULL,
 800                                                   " ", NULL);
 801                                isfirst = false;
 802                        } else {
 803                                print_color_string(PRINT_FP, COLOR_NONE, NULL,
 804                                                   ",", NULL);
 805                        }
 806                        if (rd->pretty_output && !rd->json_output)
 807                                newline_indent(rd);
 808
 809                        print_color_string(PRINT_ANY, COLOR_NONE, NULL, "%s",
 810                                           name);
 811                }
 812        }
 813
 814        if (!isfirst) {
 815                close_json_array(PRINT_JSON, NULL);
 816                newline(rd);
 817        }
 818
 819        return 0;
 820}
 821
 822static int stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
 823{
 824        return do_stat_mode_parse_cb(nlh, data, false);
 825}
 826
 827static int stat_mode_parse_cb_supported(const struct nlmsghdr *nlh, void *data)
 828{
 829        return do_stat_mode_parse_cb(nlh, data, true);
 830}
 831
 832static int stat_one_link_get_status_req(struct rd *rd, uint32_t *seq)
 833{
 834        int flags = NLM_F_REQUEST | NLM_F_ACK;
 835
 836        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET_STATUS, seq,  flags);
 837        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 838        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 839
 840        return rd_send_msg(rd);
 841}
 842
 843static int stat_one_link_get_mode(struct rd *rd)
 844{
 845        uint32_t seq;
 846        int err;
 847
 848        if (!rd->port_idx)
 849                return 0;
 850
 851        err = stat_one_link_get_status_req(rd, &seq);
 852        if (err)
 853                return err;
 854
 855        return rd_recv_msg(rd, stat_mode_parse_cb, rd, seq);
 856}
 857
 858static int stat_one_link_get_mode_supported(struct rd *rd)
 859{
 860        uint32_t seq;
 861        int err;
 862
 863        if (!rd->port_idx)
 864                return 0;
 865
 866        err = stat_one_link_get_status_req(rd, &seq);
 867        if (err)
 868                return err;
 869
 870        return rd_recv_msg(rd, stat_mode_parse_cb_supported, rd, seq);
 871}
 872
 873static int stat_link_get_mode(struct rd *rd)
 874{
 875        return rd_exec_link(rd, stat_one_link_get_mode, false);
 876}
 877
 878static int stat_link_get_mode_supported(struct rd *rd)
 879{
 880        return rd_exec_link(rd, stat_one_link_get_mode_supported, false);
 881}
 882
 883static int stat_mode_supported(struct rd *rd)
 884{
 885        const struct rd_cmd cmds[] = {
 886                { NULL,         stat_link_get_mode_supported },
 887                { "link",       stat_link_get_mode_supported },
 888                { "help",       stat_help },
 889                { 0 },
 890        };
 891        return rd_exec_cmd(rd, cmds, "parameter");
 892}
 893
 894static int stat_mode(struct rd *rd)
 895{
 896        const struct rd_cmd cmds[] = {
 897                { NULL,         stat_link_get_mode },
 898                { "link",       stat_link_get_mode },
 899                { "show",       stat_link_get_mode },
 900                { "supported",  stat_mode_supported },
 901                { "help",       stat_help },
 902                { 0 },
 903        };
 904
 905        return rd_exec_cmd(rd, cmds, "parameter");
 906}
 907
 908static int stat_one_set_link_opcounters(const struct nlmsghdr *nlh, void *data)
 909{
 910        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 911        struct nlattr *nla_entry, *tb_set;
 912        int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
 913        char *opcnt, *opcnts;
 914        struct rd *rd = data;
 915        uint32_t seq;
 916        bool found;
 917
 918        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 919        if (!tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
 920                return MNL_CB_ERROR;
 921
 922        if (rd_no_arg(rd)) {
 923                stat_help(rd);
 924                return -EINVAL;
 925        }
 926
 927        ret = stat_get_arg_str(rd, "optional-counters", &opcnts, false);
 928        if (ret)
 929                return ret;
 930
 931        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
 932        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
 933                         rd->dev_idx);
 934        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
 935                         rd->port_idx);
 936
 937        tb_set = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
 938
 939        opcnt = strtok(opcnts, ",");
 940        while (opcnt) {
 941                found = false;
 942                mnl_attr_for_each_nested(nla_entry,
 943                                         tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
 944                        struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {}, *nm, *id;
 945
 946                        if (mnl_attr_parse_nested(nla_entry, rd_attr_cb,
 947                                                  cnt) != MNL_CB_OK)
 948                                return -EINVAL;
 949
 950                        nm = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME];
 951                        id = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX];
 952                        if (!nm || ! id)
 953                                return -EINVAL;
 954
 955                        if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
 956                                continue;
 957
 958                        if (strcmp(opcnt, mnl_attr_get_str(nm)) == 0) {
 959                                mnl_attr_put_u32(rd->nlh,
 960                                                 RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX,
 961                                                 mnl_attr_get_u32(id));
 962                                found = true;
 963                        }
 964                }
 965
 966                if (!found)
 967                        return -EINVAL;
 968
 969                opcnt = strtok(NULL, ",");
 970        }
 971        mnl_attr_nest_end(rd->nlh, tb_set);
 972
 973        return rd_sendrecv_msg(rd, seq);
 974}
 975
 976static int stat_one_set_link(struct rd *rd)
 977{
 978        uint32_t seq;
 979        int err;
 980
 981        if (!rd->port_idx)
 982                return 0;
 983
 984        err = stat_one_link_get_status_req(rd, &seq);
 985        if (err)
 986                return err;
 987
 988        return rd_recv_msg(rd, stat_one_set_link_opcounters, rd, seq);
 989}
 990
 991static int stat_set_link(struct rd *rd)
 992{
 993        return rd_exec_link(rd, stat_one_set_link, true);
 994}
 995
 996static int stat_set(struct rd *rd)
 997{
 998        const struct rd_cmd cmds[] = {
 999                { NULL,         stat_help },
1000                { "link",       stat_set_link },
1001                { "help",       stat_help },
1002                { 0 },
1003        };
1004        return rd_exec_cmd(rd, cmds, "parameter");
1005}
1006
1007static int stat_one_unset_link_opcounters(struct rd *rd)
1008{
1009        int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
1010        struct nlattr *tbl;
1011        uint32_t seq;
1012        char *opcnts;
1013
1014        if (rd_no_arg(rd)) {
1015                stat_help(rd);
1016                return -EINVAL;
1017        }
1018
1019        ret = stat_get_arg_str(rd, "optional-counters", &opcnts, true);
1020        if (ret)
1021                return ret;
1022
1023        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
1024        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
1025                         rd->dev_idx);
1026        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
1027                         rd->port_idx);
1028
1029        tbl = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
1030        mnl_attr_nest_end(rd->nlh, tbl);
1031
1032        return rd_sendrecv_msg(rd, seq);
1033}
1034
1035static int stat_one_unset_link(struct rd *rd)
1036{
1037        return stat_one_unset_link_opcounters(rd);
1038}
1039
1040static int stat_unset_link(struct rd *rd)
1041{
1042        return rd_exec_link(rd, stat_one_unset_link, true);
1043}
1044
1045static int stat_unset(struct rd *rd)
1046{
1047        const struct rd_cmd cmds[] = {
1048                { NULL,         stat_help },
1049                { "link",       stat_unset_link },
1050                { "help",       stat_help },
1051                { 0 },
1052        };
1053        return rd_exec_cmd(rd, cmds, "parameter");
1054}
1055
1056static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
1057{
1058        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
1059        struct rd *rd = data;
1060        const char *name;
1061        uint32_t port;
1062        int ret;
1063
1064        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
1065        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
1066            !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
1067            !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
1068                return MNL_CB_ERROR;
1069
1070        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
1071        port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
1072        open_json_object(NULL);
1073        print_color_string(PRINT_ANY, COLOR_NONE, "ifname", "link %s/", name);
1074        print_color_uint(PRINT_ANY, COLOR_NONE, "port", "%u ", port);
1075        ret = res_get_hwcounters(rd, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true);
1076
1077        newline(rd);
1078        return ret;
1079}
1080
1081static int stat_show_one_link(struct rd *rd)
1082{
1083        int flags = NLM_F_REQUEST | NLM_F_ACK;
1084        uint32_t seq;
1085        int ret;
1086
1087        if (!rd->port_idx)
1088                return 0;
1089
1090        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq,  flags);
1091        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
1092        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
1093        ret = rd_send_msg(rd);
1094        if (ret)
1095                return ret;
1096
1097        return rd_recv_msg(rd, stat_show_parse_cb, rd, seq);
1098}
1099
1100static int stat_show_link(struct rd *rd)
1101{
1102        return rd_exec_link(rd, stat_show_one_link, false);
1103}
1104
1105static int stat_show(struct rd *rd)
1106{
1107        const struct rd_cmd cmds[] = {
1108                { NULL,         stat_show_link },
1109                { "link",       stat_show_link },
1110                { "mr",         stat_mr },
1111                { "help",       stat_help },
1112                { 0 }
1113        };
1114
1115        return rd_exec_cmd(rd, cmds, "parameter");
1116}
1117
1118int cmd_stat(struct rd *rd)
1119{
1120        const struct rd_cmd cmds[] =  {
1121                { NULL,         stat_show },
1122                { "show",       stat_show },
1123                { "list",       stat_show },
1124                { "help",       stat_help },
1125                { "qp",         stat_qp },
1126                { "mr",         stat_mr },
1127                { "mode",       stat_mode },
1128                { "set",        stat_set },
1129                { "unset",      stat_unset },
1130                { 0 }
1131        };
1132
1133        return rd_exec_cmd(rd, cmds, "statistic command");
1134}
1135