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        value = strtol(rd_argv(rd), &endp, 10);
 506        rd_arg_inc(rd);
 507
 508        return value;
 509}
 510
 511static int stat_one_qp_bind(struct rd *rd)
 512{
 513        int lqpn = 0, cntn = 0, ret;
 514        uint32_t seq;
 515
 516        if (rd_no_arg(rd)) {
 517                stat_help(rd);
 518                return -EINVAL;
 519        }
 520
 521        ret = rd_build_filter(rd, stat_valid_filters);
 522        if (ret)
 523                return ret;
 524
 525        lqpn = stat_get_arg(rd, "lqpn");
 526
 527        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
 528                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 529
 530        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 531                         RDMA_COUNTER_MODE_MANUAL);
 532
 533        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 534        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 535        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 536        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
 537
 538        if (rd_argc(rd)) {
 539                cntn = stat_get_arg(rd, "cntn");
 540                mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID,
 541                                 cntn);
 542        }
 543
 544        return rd_sendrecv_msg(rd, seq);
 545}
 546
 547static int do_stat_qp_unbind_lqpn(struct rd *rd, uint32_t cntn, uint32_t lqpn)
 548{
 549        uint32_t seq;
 550
 551        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_DEL,
 552                       &seq, (NLM_F_REQUEST | NLM_F_ACK));
 553
 554        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
 555                         RDMA_COUNTER_MODE_MANUAL);
 556        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 557        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 558        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 559        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
 560        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
 561
 562        return rd_sendrecv_msg(rd, seq);
 563}
 564
 565static int stat_get_counter_parse_cb(const struct nlmsghdr *nlh, void *data)
 566{
 567        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 568        struct nlattr *nla_table, *nla_entry;
 569        struct rd *rd = data;
 570        uint32_t lqpn, cntn;
 571        int err;
 572
 573        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 574
 575        if (!tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
 576                return MNL_CB_ERROR;
 577        cntn = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
 578
 579        nla_table = tb[RDMA_NLDEV_ATTR_RES_QP];
 580        if (!nla_table)
 581                return MNL_CB_ERROR;
 582
 583        mnl_attr_for_each_nested(nla_entry, nla_table) {
 584                struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
 585
 586                err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
 587                if (err != MNL_CB_OK)
 588                        return -EINVAL;
 589
 590                if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN])
 591                        return -EINVAL;
 592
 593                lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 594                err = do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
 595                if (err)
 596                        return MNL_CB_ERROR;
 597        }
 598
 599        return MNL_CB_OK;
 600}
 601
 602static int stat_one_qp_unbind(struct rd *rd)
 603{
 604        int flags = NLM_F_REQUEST | NLM_F_ACK, ret;
 605        char buf[MNL_SOCKET_BUFFER_SIZE];
 606        int lqpn = 0, cntn = 0;
 607        unsigned int portid;
 608        uint32_t seq;
 609
 610        ret = rd_build_filter(rd, stat_valid_filters);
 611        if (ret)
 612                return ret;
 613
 614        cntn = stat_get_arg(rd, "cntn");
 615        if (rd_argc(rd)) {
 616                lqpn = stat_get_arg(rd, "lqpn");
 617                return do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
 618        }
 619
 620        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
 621        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 622        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 623        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
 624        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
 625        ret = rd_send_msg(rd);
 626        if (ret)
 627                return ret;
 628
 629
 630        /* Can't use rd_recv_msg() since the callback also calls it (recursively),
 631         * then rd_recv_msg() always return -1 here
 632         */
 633        portid = mnl_socket_get_portid(rd->nl);
 634        ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
 635        if (ret <= 0)
 636                return ret;
 637
 638        ret = mnl_cb_run(buf, ret, seq, portid, stat_get_counter_parse_cb, rd);
 639        mnl_socket_close(rd->nl);
 640        if (ret != MNL_CB_OK)
 641                return ret;
 642
 643        return 0;
 644}
 645
 646static int stat_qp_bind_link(struct rd *rd)
 647{
 648        return rd_exec_link(rd, stat_one_qp_bind, true);
 649}
 650
 651static int stat_qp_bind(struct rd *rd)
 652{
 653        const struct rd_cmd cmds[] = {
 654                { NULL,         stat_help },
 655                { "link",       stat_qp_bind_link },
 656                { "help",       stat_help },
 657                { 0 },
 658        };
 659
 660        return rd_exec_cmd(rd, cmds, "parameter");
 661}
 662
 663static int stat_qp_unbind_link(struct rd *rd)
 664{
 665        return rd_exec_link(rd, stat_one_qp_unbind, true);
 666}
 667
 668static int stat_qp_unbind(struct rd *rd)
 669{
 670        const struct rd_cmd cmds[] = {
 671                { NULL,         stat_help },
 672                { "link",       stat_qp_unbind_link },
 673                { "help",       stat_help },
 674                { 0 },
 675        };
 676
 677        return rd_exec_cmd(rd, cmds, "parameter");
 678}
 679
 680static int stat_qp(struct rd *rd)
 681{
 682        const struct rd_cmd cmds[] =  {
 683                { NULL,         stat_qp_show },
 684                { "show",       stat_qp_show },
 685                { "list",       stat_qp_show },
 686                { "mode",       stat_qp_get_mode },
 687                { "set",        stat_qp_set },
 688                { "bind",       stat_qp_bind },
 689                { "unbind",     stat_qp_unbind },
 690                { "help",       stat_help },
 691                { 0 }
 692        };
 693
 694        return rd_exec_cmd(rd, cmds, "parameter");
 695}
 696
 697static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
 698{
 699        struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
 700        struct rd *rd = data;
 701        const char *name;
 702        uint32_t port;
 703        int ret;
 704
 705        mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
 706        if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
 707            !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
 708            !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
 709                return MNL_CB_ERROR;
 710
 711        name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
 712        port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
 713        open_json_object(NULL);
 714        print_color_string(PRINT_ANY, COLOR_NONE, "ifname", "link %s/", name);
 715        print_color_uint(PRINT_ANY, COLOR_NONE, "port", "%u ", port);
 716        ret = res_get_hwcounters(rd, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true);
 717
 718        newline(rd);
 719        return ret;
 720}
 721
 722static int stat_show_one_link(struct rd *rd)
 723{
 724        int flags = NLM_F_REQUEST | NLM_F_ACK;
 725        uint32_t seq;
 726        int ret;
 727
 728        if (!rd->port_idx)
 729                return 0;
 730
 731        rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq,  flags);
 732        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
 733        mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
 734        ret = rd_send_msg(rd);
 735        if (ret)
 736                return ret;
 737
 738        return rd_recv_msg(rd, stat_show_parse_cb, rd, seq);
 739}
 740
 741static int stat_show_link(struct rd *rd)
 742{
 743        return rd_exec_link(rd, stat_show_one_link, false);
 744}
 745
 746static int stat_show(struct rd *rd)
 747{
 748        const struct rd_cmd cmds[] = {
 749                { NULL,         stat_show_link },
 750                { "link",       stat_show_link },
 751                { "mr",         stat_mr },
 752                { "help",       stat_help },
 753                { 0 }
 754        };
 755
 756        return rd_exec_cmd(rd, cmds, "parameter");
 757}
 758
 759int cmd_stat(struct rd *rd)
 760{
 761        const struct rd_cmd cmds[] =  {
 762                { NULL,         stat_show },
 763                { "show",       stat_show },
 764                { "list",       stat_show },
 765                { "help",       stat_help },
 766                { "qp",         stat_qp },
 767                { "mr",         stat_mr },
 768                { 0 }
 769        };
 770
 771        return rd_exec_cmd(rd, cmds, "statistic command");
 772}
 773