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("where  OBJECT: = { qp }\n");
  24        pr_out("       CRITERIA : = { type }\n");
  25        pr_out("       COUNTER_SCOPE: = { link | dev }\n");
  26        pr_out("       FILTER_NAME: = { cntn | lqpn | pid }\n");
  27        pr_out("Examples:\n");
  28        pr_out("       %s statistic qp show\n", rd->filename);
  29        pr_out("       %s statistic qp show link mlx5_2/1\n", rd->filename);
  30        pr_out("       %s statistic qp mode\n", rd->filename);
  31        pr_out("       %s statistic qp mode link mlx5_0\n", rd->filename);
  32        pr_out("       %s statistic qp set link mlx5_2/1 auto type on\n", rd->filename);
  33        pr_out("       %s statistic qp set link mlx5_2/1 auto off\n", rd->filename);
  34        pr_out("       %s statistic qp bind link mlx5_2/1 lqpn 178\n", rd->filename);
  35        pr_out("       %s statistic qp bind link mlx5_2/1 lqpn 178 cntn 4\n", rd->filename);
  36        pr_out("       %s statistic qp unbind link mlx5_2/1 cntn 4\n", rd->filename);
  37        pr_out("       %s statistic qp unbind link mlx5_2/1 cntn 4 lqpn 178\n", rd->filename);
  38        pr_out("       %s statistic show\n", rd->filename);
  39        pr_out("       %s statistic show link mlx5_2/1\n", rd->filename);
  40
  41        return 0;
  42}
  43
  44struct counter_param {
  45        char *name;
  46        uint32_t attr;
  47};
  48
  49static struct counter_param auto_params[] = {
  50        { "type", RDMA_COUNTER_MASK_QP_TYPE, },
  51        { "pid", RDMA_COUNTER_MASK_PID, },
  52        { NULL },
  53};
  54
  55static int prepare_auto_mode_str(struct nlattr **tb, uint32_t mask,
  56                                 char *output, int len)
  57{
  58        char s[] = "qp auto";
  59        int i, outlen = strlen(s);
  60        bool first = true;
  61
  62        memset(output, 0, len);
  63        snprintf(output, len, "%s", s);
  64
  65        if (mask) {
  66                for (i = 0; auto_params[i].name != NULL; i++) {
  67                        if (mask & auto_params[i].attr) {
  68                                outlen += strlen(auto_params[i].name) + 1;
  69                                if (outlen >= len)
  70                                        return -EINVAL;
  71                                if (first) {
  72                                        strcat(output, " ");
  73                                        first = false;
  74                                } else
  75                                        strcat(output, ",");
  76
  77                                strcat(output, auto_params[i].name);
  78                        }
  79                }
  80
  81                if (outlen + strlen(" on") >= len)
  82                        return -EINVAL;
  83                strcat(output, " on");
  84        } else {
  85                if (outlen + strlen(" off") >= len)
  86                        return -EINVAL;
  87                strcat(output, " off");
  88        }
  89
  90        return 0;
  91}
  92
  93static int qp_link_get_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
  94{
  95        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
  96        uint32_t mode = 0, mask = 0;
  97        char output[128] = {};
  98        struct rd *rd = data;
  99        uint32_t idx, port;
 100        const char *name;
 101
 102        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 103        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME])
 104                return MNL_CB_ERROR;
 105
 106        if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
 107                pr_err("This tool doesn't support switches yet\n");
 108                return MNL_CB_ERROR;
 109        }
 110
 111        idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
 112        port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
 113        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 114        if (tb[RDMA_NLDEV_ATTR_STAT_MODE])
 115                mode = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_MODE]);
 116
 117        if (mode == RDMA_COUNTER_MODE_AUTO) {
 118                if (!tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK])
 119                        return MNL_CB_ERROR;
 120                mask = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]);
 121                prepare_auto_mode_str(tb, mask, output, sizeof(output));
 122        } else {
 123                snprintf(output, sizeof(output), "qp auto off");
 124        }
 125
 126        open_json_object(NULL);
 127        print_link(rd, idx, name, port, tb);
 128        print_color_string(PRINT_ANY, COLOR_NONE, "mode", "mode %s ", output);
 129        newline(rd);
 130        return MNL_CB_OK;
 131}
 132
 133static int stat_one_qp_link_get_mode(struct rd *rd)
 134{
 135        uint32_t seq;
 136        int ret;
 137
 138        if (!rd->port_idx)
 139                return 0;
 140
 141        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET,
 142                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 143
 144        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 145        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 146        /* Make RDMA_NLDEV_ATTR_STAT_MODE valid so that kernel knows
 147         * return only mode instead of all counters
 148         */
 149        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 150                         RDMA_COUNTER_MODE_MANUAL);
 151        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 152        ret = rd_send_msg(rd);
 153        if (ret)
 154                return ret;
 155
 156        ret = rd_recv_msg(rd, qp_link_get_mode_parse_cb, rd, seq);
 157        return ret;
 158}
 159
 160static int stat_qp_link_get_mode(struct rd *rd)
 161{
 162        return rd_exec_link(rd, stat_one_qp_link_get_mode, false);
 163}
 164
 165static int stat_qp_get_mode(struct rd *rd)
 166{
 167        const struct rd_cmd cmds[] = {
 168                { NULL,         stat_qp_link_get_mode },
 169                { "link",       stat_qp_link_get_mode },
 170                { "help",       stat_help },
 171                { 0 }
 172        };
 173
 174        return rd_exec_cmd(rd, cmds, "parameter");
 175}
 176
 177int res_get_hwcounters(struct rd *rd, struct nlattr *hwc_table, bool print)
 178{
 179        struct nlattr *nla_entry;
 180        const char *nm;
 181        uint64_t v;
 182        int err;
 183
 184        mnl_attr_for_each_nested(nla_entry, hwc_table) {
 185                struct nlattr *hw_line[RDMA_NLDEV_ATTR_MAX] = {};
 186
 187                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, hw_line);
 188                if (err != MNL_CB_OK)
 189                        return -EINVAL;
 190
 191                if (!hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] ||
 192                    !hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]) {
 193                        return -EINVAL;
 194                }
 195
 196                if (!print)
 197                        continue;
 198
 199                nm = mnl_attr_get_str(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
 200                v = mnl_attr_get_u64(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]);
 201                if (rd->pretty_output && !rd->json_output)
 202                        newline_indent(rd);
 203                res_print_uint(rd, nm, v, hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
 204        }
 205
 206        return MNL_CB_OK;
 207}
 208
 209static int res_counter_line(struct rd *rd, const char *name, int index,
 210                       struct nlattr **nla_line)
 211{
 212        uint32_t cntn, port = 0, pid = 0, qpn, qp_type = 0;
 213        struct nlattr *hwc_table, *qp_table;
 214        struct nlattr *nla_entry;
 215        const char *comm = NULL;
 216        bool isfirst;
 217        int err;
 218
 219        if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX])
 220                port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]);
 221
 222        hwc_table = nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS];
 223        qp_table = nla_line[RDMA_NLDEV_ATTR_RES_QP];
 224        if (!hwc_table || !qp_table ||
 225            !nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
 226                return MNL_CB_ERROR;
 227
 228        cntn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
 229        if (rd_is_filtered_attr(rd, "cntn", cntn,
 230                                nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]))
 231                return MNL_CB_OK;
 232
 233        if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
 234                qp_type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]);
 235
 236        if (rd_is_string_filtered_attr(rd, "qp-type", qp_types_to_str(qp_type),
 237                                       nla_line[RDMA_NLDEV_ATTR_RES_TYPE]))
 238                return MNL_CB_OK;
 239
 240        if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
 241                pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 242                comm = get_task_name(pid);
 243        }
 244        if (rd_is_filtered_attr(rd, "pid", pid,
 245                                nla_line[RDMA_NLDEV_ATTR_RES_PID]))
 246                return MNL_CB_OK;
 247
 248        if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
 249                comm = (char *)mnl_attr_get_str(
 250                        nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 251
 252        mnl_attr_for_each_nested(nla_entry, qp_table) {
 253                struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
 254
 255                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
 256                if (err != MNL_CB_OK)
 257                        return -EINVAL;
 258
 259                if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
 260                        return -EINVAL;
 261
 262                qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 263                if (rd_is_filtered_attr(rd, "lqpn", qpn,
 264                                        qp_line[RDMA_NLDEV_ATTR_RES_LQPN]))
 265                        return MNL_CB_OK;
 266        }
 267
 268        err = res_get_hwcounters(rd, hwc_table, false);
 269        if (err != MNL_CB_OK)
 270                return err;
 271        open_json_object(NULL);
 272        print_link(rd, index, name, port, nla_line);
 273        print_color_uint(PRINT_ANY, COLOR_NONE, "cntn", "cntn %u ", cntn);
 274        if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
 275                print_qp_type(rd, qp_type);
 276        res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 277        print_comm(rd, comm, nla_line);
 278        res_get_hwcounters(rd, hwc_table, true);
 279        isfirst = true;
 280        open_json_array(PRINT_JSON, "lqpn");
 281        print_color_string(PRINT_FP, COLOR_NONE, NULL, "\n    LQPN: <", NULL);
 282        mnl_attr_for_each_nested(nla_entry, qp_table) {
 283                struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
 284                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
 285                if (err != MNL_CB_OK)
 286                        return -EINVAL;
 287
 288                if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
 289                        return -EINVAL;
 290
 291                qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 292                if (!isfirst)
 293                        print_color_string(PRINT_FP, COLOR_NONE, NULL, ",",
 294                                           NULL);
 295                print_color_uint(PRINT_ANY, COLOR_NONE, NULL, "%d", qpn);
 296                isfirst = false;
 297        }
 298        close_json_array(PRINT_ANY, ">");
 299        newline(rd);
 300        return MNL_CB_OK;
 301}
 302
 303static int stat_qp_show_parse_cb(const struct nlmsghdr *nlh, void *data)
 304{
 305        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 306        struct nlattr *nla_table, *nla_entry;
 307        struct rd *rd = data;
 308        const char *name;
 309        uint32_t idx;
 310        int ret;
 311
 312        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 313        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
 314            !tb[RDMA_NLDEV_ATTR_STAT_COUNTER])
 315                return MNL_CB_ERROR;
 316
 317        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 318        idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
 319        nla_table = tb[RDMA_NLDEV_ATTR_STAT_COUNTER];
 320
 321        mnl_attr_for_each_nested(nla_entry, nla_table) {
 322                struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
 323
 324                ret = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
 325                if (ret != MNL_CB_OK)
 326                        break;
 327
 328                ret = res_counter_line(rd, name, idx, nla_line);
 329                if (ret != MNL_CB_OK)
 330                        break;
 331        }
 332
 333        return ret;
 334}
 335
 336static const struct filters stat_valid_filters[MAX_NUMBER_OF_FILTERS] = {
 337        { .name = "cntn", .is_number = true },
 338        { .name = "lqpn", .is_number = true },
 339        { .name = "pid", .is_number = true },
 340        { .name = "qp-type", .is_number = false },
 341};
 342
 343static int stat_qp_show_one_link(struct rd *rd)
 344{
 345        int flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
 346        uint32_t seq;
 347        int ret;
 348
 349        if (!rd->port_idx)
 350                return 0;
 351
 352        ret = rd_build_filter(rd, stat_valid_filters);
 353        if (ret)
 354                return ret;
 355
 356        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
 357        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 358        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 359        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 360        ret = rd_send_msg(rd);
 361        if (ret)
 362                return ret;
 363
 364        ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq);
 365        return ret;
 366}
 367
 368static int stat_qp_show_link(struct rd *rd)
 369{
 370        return rd_exec_link(rd, stat_qp_show_one_link, false);
 371}
 372
 373static int stat_qp_show(struct rd *rd)
 374{
 375        const struct rd_cmd cmds[] = {
 376                { NULL,         stat_qp_show_link },
 377                { "link",       stat_qp_show_link },
 378                { "help",       stat_help },
 379                { 0 }
 380        };
 381
 382        return rd_exec_cmd(rd, cmds, "parameter");
 383}
 384
 385static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask)
 386{
 387        uint32_t seq;
 388
 389        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
 390                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 391
 392        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 393        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 394        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 395        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 396                         RDMA_COUNTER_MODE_AUTO);
 397        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask);
 398
 399        return rd_sendrecv_msg(rd, seq);
 400}
 401
 402static int stat_get_auto_mode_mask(struct rd *rd)
 403{
 404        char *modes = rd_argv(rd), *mode, *saved_ptr;
 405        const char *delim = ",";
 406        int mask = 0, found, i;
 407
 408        if (!modes)
 409                return mask;
 410
 411        mode = strtok_r(modes, delim, &saved_ptr);
 412        do {
 413                if (!mode)
 414                        break;
 415
 416                found = false;
 417                for (i = 0;  auto_params[i].name != NULL; i++) {
 418                        if (!strcmp(mode, auto_params[i].name)) {
 419                                mask |= auto_params[i].attr;
 420                                found = true;
 421                                break;
 422                        }
 423                }
 424
 425                if (!found) {
 426                        pr_err("Unknown auto mode '%s'.\n", mode);
 427                        mask = 0;
 428                        break;
 429                }
 430
 431                mode = strtok_r(NULL, delim, &saved_ptr);
 432        } while(1);
 433
 434        if (mask)
 435                rd_arg_inc(rd);
 436
 437        return mask;
 438}
 439
 440static int stat_one_qp_set_link_auto(struct rd *rd)
 441{
 442        int auto_mask = 0;
 443
 444        if (!rd_argc(rd))
 445                return -EINVAL;
 446
 447        if (!strcmpx(rd_argv(rd), "off")) {
 448                rd_arg_inc(rd);
 449                return stat_qp_set_link_auto_sendmsg(rd, 0);
 450        }
 451
 452        auto_mask = stat_get_auto_mode_mask(rd);
 453        if (!auto_mask || !rd_argc(rd))
 454                return -EINVAL;
 455
 456        if (!strcmpx(rd_argv(rd), "on")) {
 457                rd_arg_inc(rd);
 458                return stat_qp_set_link_auto_sendmsg(rd, auto_mask);
 459        } else {
 460                pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
 461                return -EINVAL;
 462        }
 463}
 464
 465static int stat_one_qp_set_link(struct rd *rd)
 466{
 467        const struct rd_cmd cmds[] = {
 468                { NULL,         stat_one_qp_link_get_mode },
 469                { "auto",       stat_one_qp_set_link_auto },
 470                { 0 }
 471        };
 472
 473        if (!rd->port_idx)
 474                return 0;
 475
 476        return rd_exec_cmd(rd, cmds, "parameter");
 477}
 478
 479static int stat_qp_set_link(struct rd *rd)
 480{
 481        return rd_exec_link(rd, stat_one_qp_set_link, false);
 482}
 483
 484static int stat_qp_set(struct rd *rd)
 485{
 486        const struct rd_cmd cmds[] = {
 487                { NULL,         stat_help },
 488                { "link",       stat_qp_set_link },
 489                { "help",       stat_help },
 490                { 0 }
 491        };
 492
 493        return rd_exec_cmd(rd, cmds, "parameter");
 494}
 495
 496static int stat_get_arg(struct rd *rd, const char *arg)
 497{
 498        int value = 0;
 499        char *endp;
 500
 501        if (strcmpx(rd_argv(rd), arg) != 0)
 502                return -EINVAL;
 503
 504        rd_arg_inc(rd);
 505
 506        if (rd_is_multiarg(rd)) {
 507                pr_err("The parameter %s shouldn't include range\n", arg);
 508                return -EINVAL;
 509        }
 510
 511        value = strtol(rd_argv(rd), &endp, 10);
 512        rd_arg_inc(rd);
 513
 514        return value;
 515}
 516
 517static int stat_one_qp_bind(struct rd *rd)
 518{
 519        int lqpn = 0, cntn = 0, ret;
 520        uint32_t seq;
 521
 522        if (rd_no_arg(rd)) {
 523                stat_help(rd);
 524                return -EINVAL;
 525        }
 526
 527        ret = rd_build_filter(rd, stat_valid_filters);
 528        if (ret)
 529                return ret;
 530
 531        lqpn = stat_get_arg(rd, "lqpn");
 532        if (lqpn < 0)
 533                return lqpn;
 534
 535        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
 536                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 537
 538        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 539                         RDMA_COUNTER_MODE_MANUAL);
 540
 541        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 542        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 543        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 544        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
 545
 546        if (rd_argc(rd)) {
 547                cntn = stat_get_arg(rd, "cntn");
 548                if (cntn < 0)
 549                        return cntn;
 550
 551                mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID,
 552                                 cntn);
 553        }
 554
 555        return rd_sendrecv_msg(rd, seq);
 556}
 557
 558static int do_stat_qp_unbind_lqpn(struct rd *rd, uint32_t cntn, uint32_t lqpn)
 559{
 560        uint32_t seq;
 561
 562        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_DEL,
 563                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 564
 565        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 566                         RDMA_COUNTER_MODE_MANUAL);
 567        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 568        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 569        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 570        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
 571        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
 572
 573        return rd_sendrecv_msg(rd, seq);
 574}
 575
 576static int stat_get_counter_parse_cb(const struct nlmsghdr *nlh, void *data)
 577{
 578        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 579        struct nlattr *nla_table, *nla_entry;
 580        struct rd *rd = data;
 581        uint32_t lqpn, cntn;
 582        int err;
 583
 584        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 585
 586        if (!tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
 587                return MNL_CB_ERROR;
 588        cntn = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
 589
 590        nla_table = tb[RDMA_NLDEV_ATTR_RES_QP];
 591        if (!nla_table)
 592                return MNL_CB_ERROR;
 593
 594        mnl_attr_for_each_nested(nla_entry, nla_table) {
 595                struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
 596
 597                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
 598                if (err != MNL_CB_OK)
 599                        return -EINVAL;
 600
 601                if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN])
 602                        return -EINVAL;
 603
 604                lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 605                err = do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
 606                if (err)
 607                        return MNL_CB_ERROR;
 608        }
 609
 610        return MNL_CB_OK;
 611}
 612
 613static int stat_one_qp_unbind(struct rd *rd)
 614{
 615        int flags = NLM_F_REQUEST | NLM_F_ACK, ret;
 616        char buf[MNL_SOCKET_BUFFER_SIZE];
 617        int lqpn = 0, cntn = 0;
 618        unsigned int portid;
 619        uint32_t seq;
 620
 621        if (rd_no_arg(rd)) {
 622                stat_help(rd);
 623                return -EINVAL;
 624        }
 625
 626        ret = rd_build_filter(rd, stat_valid_filters);
 627        if (ret)
 628                return ret;
 629
 630        cntn = stat_get_arg(rd, "cntn");
 631        if (cntn < 0)
 632                return cntn;
 633
 634        if (rd_argc(rd)) {
 635                lqpn = stat_get_arg(rd, "lqpn");
 636                if (lqpn < 0)
 637                        return lqpn;
 638                return do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
 639        }
 640
 641        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
 642        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 643        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 644        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 645        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
 646        ret = rd_send_msg(rd);
 647        if (ret)
 648                return ret;
 649
 650
 651        /* Can't use rd_recv_msg() since the callback also calls it (recursively),
 652         * then rd_recv_msg() always return -1 here
 653         */
 654        portid = mnl_socket_get_portid(rd->nl);
 655        ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
 656        if (ret <= 0)
 657                return ret;
 658
 659        ret = mnl_cb_run(buf, ret, seq, portid, stat_get_counter_parse_cb, rd);
 660        mnl_socket_close(rd->nl);
 661        if (ret != MNL_CB_OK)
 662                return ret;
 663
 664        return 0;
 665}
 666
 667static int stat_qp_bind_link(struct rd *rd)
 668{
 669        return rd_exec_link(rd, stat_one_qp_bind, true);
 670}
 671
 672static int stat_qp_bind(struct rd *rd)
 673{
 674        const struct rd_cmd cmds[] = {
 675                { NULL,         stat_help },
 676                { "link",       stat_qp_bind_link },
 677                { "help",       stat_help },
 678                { 0 },
 679        };
 680
 681        return rd_exec_cmd(rd, cmds, "parameter");
 682}
 683
 684static int stat_qp_unbind_link(struct rd *rd)
 685{
 686        return rd_exec_link(rd, stat_one_qp_unbind, true);
 687}
 688
 689static int stat_qp_unbind(struct rd *rd)
 690{
 691        const struct rd_cmd cmds[] = {
 692                { NULL,         stat_help },
 693                { "link",       stat_qp_unbind_link },
 694                { "help",       stat_help },
 695                { 0 },
 696        };
 697
 698        return rd_exec_cmd(rd, cmds, "parameter");
 699}
 700
 701static int stat_qp(struct rd *rd)
 702{
 703        const struct rd_cmd cmds[] =  {
 704                { NULL,         stat_qp_show },
 705                { "show",       stat_qp_show },
 706                { "list",       stat_qp_show },
 707                { "mode",       stat_qp_get_mode },
 708                { "set",        stat_qp_set },
 709                { "bind",       stat_qp_bind },
 710                { "unbind",     stat_qp_unbind },
 711                { "help",       stat_help },
 712                { 0 }
 713        };
 714
 715        return rd_exec_cmd(rd, cmds, "parameter");
 716}
 717
 718static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
 719{
 720        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 721        struct rd *rd = data;
 722        const char *name;
 723        uint32_t port;
 724        int ret;
 725
 726        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 727        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
 728            !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
 729            !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
 730                return MNL_CB_ERROR;
 731
 732        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 733        port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
 734        open_json_object(NULL);
 735        print_color_string(PRINT_ANY, COLOR_NONE, "ifname", "link %s/", name);
 736        print_color_uint(PRINT_ANY, COLOR_NONE, "port", "%u ", port);
 737        ret = res_get_hwcounters(rd, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true);
 738
 739        newline(rd);
 740        return ret;
 741}
 742
 743static int stat_show_one_link(struct rd *rd)
 744{
 745        int flags = NLM_F_REQUEST | NLM_F_ACK;
 746        uint32_t seq;
 747        int ret;
 748
 749        if (!rd->port_idx)
 750                return 0;
 751
 752        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq,  flags);
 753        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 754        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 755        ret = rd_send_msg(rd);
 756        if (ret)
 757                return ret;
 758
 759        return rd_recv_msg(rd, stat_show_parse_cb, rd, seq);
 760}
 761
 762static int stat_show_link(struct rd *rd)
 763{
 764        return rd_exec_link(rd, stat_show_one_link, false);
 765}
 766
 767static int stat_show(struct rd *rd)
 768{
 769        const struct rd_cmd cmds[] = {
 770                { NULL,         stat_show_link },
 771                { "link",       stat_show_link },
 772                { "mr",         stat_mr },
 773                { "help",       stat_help },
 774                { 0 }
 775        };
 776
 777        return rd_exec_cmd(rd, cmds, "parameter");
 778}
 779
 780int cmd_stat(struct rd *rd)
 781{
 782        const struct rd_cmd cmds[] =  {
 783                { NULL,         stat_show },
 784                { "show",       stat_show },
 785                { "list",       stat_show },
 786                { "help",       stat_help },
 787                { "qp",         stat_qp },
 788                { "mr",         stat_mr },
 789                { 0 }
 790        };
 791
 792        return rd_exec_cmd(rd, cmds, "statistic command");
 793}
 794