| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
| /* |
| * rdma.c RDMA tool |
| * Authors: Mark Zhang <markz@mellanox.com> |
| */ |
| |
| #include "rdma.h" |
| #include "res.h" |
| #include <inttypes.h> |
| |
| static int stat_help(struct rd *rd) |
| { |
| pr_out("Usage: %s [ OPTIONS ] statistic { COMMAND | help }\n", rd->filename); |
| pr_out(" %s statistic OBJECT show\n", rd->filename); |
| pr_out(" %s statistic OBJECT show link [ DEV/PORT_INDEX ] [ FILTER-NAME FILTER-VALUE ]\n", rd->filename); |
| pr_out(" %s statistic OBJECT mode\n", rd->filename); |
| pr_out(" %s statistic OBJECT set COUNTER_SCOPE [DEV/PORT_INDEX] auto {CRITERIA | off}\n", rd->filename); |
| pr_out(" %s statistic OBJECT bind COUNTER_SCOPE [DEV/PORT_INDEX] [OBJECT-ID] [COUNTER-ID]\n", rd->filename); |
| pr_out(" %s statistic OBJECT unbind COUNTER_SCOPE [DEV/PORT_INDEX] [COUNTER-ID]\n", rd->filename); |
| pr_out(" %s statistic show\n", rd->filename); |
| pr_out(" %s statistic show link [ DEV/PORT_INDEX ]\n", rd->filename); |
| pr_out("where OBJECT: = { qp }\n"); |
| pr_out(" CRITERIA : = { type }\n"); |
| pr_out(" COUNTER_SCOPE: = { link | dev }\n"); |
| pr_out("Examples:\n"); |
| pr_out(" %s statistic qp show\n", rd->filename); |
| pr_out(" %s statistic qp show link mlx5_2/1\n", rd->filename); |
| pr_out(" %s statistic qp mode\n", rd->filename); |
| pr_out(" %s statistic qp mode link mlx5_0\n", rd->filename); |
| pr_out(" %s statistic qp set link mlx5_2/1 auto type on\n", rd->filename); |
| pr_out(" %s statistic qp set link mlx5_2/1 auto off\n", rd->filename); |
| pr_out(" %s statistic qp bind link mlx5_2/1 lqpn 178\n", rd->filename); |
| pr_out(" %s statistic qp bind link mlx5_2/1 lqpn 178 cntn 4\n", rd->filename); |
| pr_out(" %s statistic qp unbind link mlx5_2/1 cntn 4\n", rd->filename); |
| pr_out(" %s statistic qp unbind link mlx5_2/1 cntn 4 lqpn 178\n", rd->filename); |
| pr_out(" %s statistic show\n", rd->filename); |
| pr_out(" %s statistic show link mlx5_2/1\n", rd->filename); |
| |
| return 0; |
| } |
| |
| struct counter_param { |
| char *name; |
| uint32_t attr; |
| }; |
| |
| static struct counter_param auto_params[] = { |
| { "type", RDMA_COUNTER_MASK_QP_TYPE, }, |
| { NULL }, |
| }; |
| |
| static int prepare_auto_mode_str(struct nlattr **tb, uint32_t mask, |
| char *output, int len) |
| { |
| char s[] = "qp auto"; |
| int i, outlen = strlen(s); |
| |
| memset(output, 0, len); |
| snprintf(output, len, "%s", s); |
| |
| if (mask) { |
| for (i = 0; auto_params[i].name != NULL; i++) { |
| if (mask & auto_params[i].attr) { |
| outlen += strlen(auto_params[i].name) + 1; |
| if (outlen >= len) |
| return -EINVAL; |
| strcat(output, " "); |
| strcat(output, auto_params[i].name); |
| } |
| } |
| |
| if (outlen + strlen(" on") >= len) |
| return -EINVAL; |
| strcat(output, " on"); |
| } else { |
| if (outlen + strlen(" off") >= len) |
| return -EINVAL; |
| strcat(output, " off"); |
| } |
| |
| return 0; |
| } |
| |
| static int qp_link_get_mode_parse_cb(const struct nlmsghdr *nlh, void *data) |
| { |
| struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; |
| uint32_t mode = 0, mask = 0; |
| char output[128] = {}; |
| struct rd *rd = data; |
| uint32_t idx, port; |
| const char *name; |
| |
| mnl_attr_parse(nlh, 0, rd_attr_cb, tb); |
| if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME]) |
| return MNL_CB_ERROR; |
| |
| if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { |
| pr_err("This tool doesn't support switches yet\n"); |
| return MNL_CB_ERROR; |
| } |
| |
| idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); |
| port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); |
| name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); |
| if (tb[RDMA_NLDEV_ATTR_STAT_MODE]) |
| mode = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_MODE]); |
| |
| if (mode == RDMA_COUNTER_MODE_AUTO) { |
| if (!tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]) |
| return MNL_CB_ERROR; |
| mask = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]); |
| prepare_auto_mode_str(tb, mask, output, sizeof(output)); |
| } else { |
| snprintf(output, sizeof(output), "qp auto off"); |
| } |
| |
| if (rd->json_output) { |
| jsonw_uint_field(rd->jw, "ifindex", idx); |
| jsonw_uint_field(rd->jw, "port", port); |
| jsonw_string_field(rd->jw, "mode", output); |
| } else { |
| pr_out("%u/%u: %s/%u: %s\n", idx, port, name, port, output); |
| } |
| |
| return MNL_CB_OK; |
| } |
| |
| static int stat_one_qp_link_get_mode(struct rd *rd) |
| { |
| uint32_t seq; |
| int ret; |
| |
| if (!rd->port_idx) |
| return 0; |
| |
| rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, |
| &seq, (NLM_F_REQUEST | NLM_F_ACK)); |
| |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); |
| /* Make RDMA_NLDEV_ATTR_STAT_MODE valid so that kernel knows |
| * return only mode instead of all counters |
| */ |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE, |
| RDMA_COUNTER_MODE_MANUAL); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); |
| ret = rd_send_msg(rd); |
| if (ret) |
| return ret; |
| |
| if (rd->json_output) |
| jsonw_start_object(rd->jw); |
| ret = rd_recv_msg(rd, qp_link_get_mode_parse_cb, rd, seq); |
| if (rd->json_output) |
| jsonw_end_object(rd->jw); |
| |
| return ret; |
| } |
| |
| static int stat_qp_link_get_mode(struct rd *rd) |
| { |
| return rd_exec_link(rd, stat_one_qp_link_get_mode, false); |
| } |
| |
| static int stat_qp_get_mode(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_qp_link_get_mode }, |
| { "link", stat_qp_link_get_mode }, |
| { "help", stat_help }, |
| { 0 } |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int res_get_hwcounters(struct rd *rd, struct nlattr *hwc_table, bool print) |
| { |
| struct nlattr *nla_entry; |
| const char *nm; |
| uint64_t v; |
| int err; |
| |
| mnl_attr_for_each_nested(nla_entry, hwc_table) { |
| struct nlattr *hw_line[RDMA_NLDEV_ATTR_MAX] = {}; |
| |
| err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, hw_line); |
| if (err != MNL_CB_OK) |
| return -EINVAL; |
| |
| if (!hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] || |
| !hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]) { |
| return -EINVAL; |
| } |
| |
| if (!print) |
| continue; |
| |
| nm = mnl_attr_get_str(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]); |
| v = mnl_attr_get_u64(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]); |
| if (rd->pretty_output && !rd->json_output) |
| newline_indent(rd); |
| res_print_uint(rd, nm, v, hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]); |
| } |
| |
| return MNL_CB_OK; |
| } |
| |
| static int res_counter_line(struct rd *rd, const char *name, int index, |
| struct nlattr **nla_line) |
| { |
| uint32_t cntn, port = 0, pid = 0, qpn; |
| struct nlattr *hwc_table, *qp_table; |
| struct nlattr *nla_entry; |
| const char *comm = NULL; |
| bool isfirst; |
| int err; |
| |
| if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) |
| port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]); |
| |
| hwc_table = nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]; |
| qp_table = nla_line[RDMA_NLDEV_ATTR_RES_QP]; |
| if (!hwc_table || !qp_table || |
| !nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]) |
| return MNL_CB_ERROR; |
| |
| cntn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]); |
| if (rd_is_filtered_attr(rd, "cntn", cntn, |
| nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])) |
| return MNL_CB_OK; |
| |
| if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { |
| pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); |
| comm = get_task_name(pid); |
| } |
| if (rd_is_filtered_attr(rd, "pid", pid, |
| nla_line[RDMA_NLDEV_ATTR_RES_PID])) |
| return MNL_CB_OK; |
| |
| if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) |
| comm = (char *)mnl_attr_get_str( |
| nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); |
| |
| mnl_attr_for_each_nested(nla_entry, qp_table) { |
| struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {}; |
| |
| err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line); |
| if (err != MNL_CB_OK) |
| return -EINVAL; |
| |
| if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN]) |
| return -EINVAL; |
| |
| qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]); |
| if (rd_is_filtered_attr(rd, "lqpn", qpn, |
| qp_line[RDMA_NLDEV_ATTR_RES_LQPN])) |
| return MNL_CB_OK; |
| } |
| |
| err = res_get_hwcounters(rd, hwc_table, false); |
| if (err != MNL_CB_OK) |
| return err; |
| |
| if (rd->json_output) { |
| jsonw_string_field(rd->jw, "ifname", name); |
| if (port) |
| jsonw_uint_field(rd->jw, "port", port); |
| jsonw_uint_field(rd->jw, "cntn", cntn); |
| } else { |
| if (port) |
| pr_out("link %s/%u cntn %u ", name, port, cntn); |
| else |
| pr_out("dev %s cntn %u ", name, cntn); |
| } |
| |
| res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); |
| print_comm(rd, comm, nla_line); |
| |
| res_get_hwcounters(rd, hwc_table, true); |
| |
| isfirst = true; |
| mnl_attr_for_each_nested(nla_entry, qp_table) { |
| struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {}; |
| |
| if (isfirst && !rd->json_output) |
| pr_out("\n LQPN: <"); |
| |
| err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line); |
| if (err != MNL_CB_OK) |
| return -EINVAL; |
| |
| if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN]) |
| return -EINVAL; |
| |
| qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]); |
| if (rd->json_output) { |
| jsonw_uint_field(rd->jw, "lqpn", qpn); |
| } else { |
| if (isfirst) |
| pr_out("%d", qpn); |
| else |
| pr_out(", %d", qpn); |
| } |
| isfirst = false; |
| } |
| |
| if (!rd->json_output) |
| pr_out(">\n"); |
| return MNL_CB_OK; |
| } |
| |
| static int stat_qp_show_parse_cb(const struct nlmsghdr *nlh, void *data) |
| { |
| struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; |
| struct nlattr *nla_table, *nla_entry; |
| struct rd *rd = data; |
| const char *name; |
| uint32_t idx; |
| int ret; |
| |
| mnl_attr_parse(nlh, 0, rd_attr_cb, tb); |
| if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] || |
| !tb[RDMA_NLDEV_ATTR_STAT_COUNTER]) |
| return MNL_CB_ERROR; |
| |
| name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); |
| idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); |
| nla_table = tb[RDMA_NLDEV_ATTR_STAT_COUNTER]; |
| |
| mnl_attr_for_each_nested(nla_entry, nla_table) { |
| struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; |
| |
| ret = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); |
| if (ret != MNL_CB_OK) |
| break; |
| |
| ret = res_counter_line(rd, name, idx, nla_line); |
| if (ret != MNL_CB_OK) |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static const struct filters stat_valid_filters[MAX_NUMBER_OF_FILTERS] = { |
| { .name = "cntn", .is_number = true }, |
| { .name = "lqpn", .is_number = true }, |
| { .name = "pid", .is_number = true }, |
| }; |
| |
| static int stat_qp_show_one_link(struct rd *rd) |
| { |
| int flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP; |
| uint32_t seq; |
| int ret; |
| |
| if (!rd->port_idx) |
| return 0; |
| |
| ret = rd_build_filter(rd, stat_valid_filters); |
| if (ret) |
| return ret; |
| |
| rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); |
| ret = rd_send_msg(rd); |
| if (ret) |
| return ret; |
| |
| if (rd->json_output) |
| jsonw_start_object(rd->jw); |
| ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq); |
| if (rd->json_output) |
| jsonw_end_object(rd->jw); |
| |
| return ret; |
| } |
| |
| static int stat_qp_show_link(struct rd *rd) |
| { |
| return rd_exec_link(rd, stat_qp_show_one_link, false); |
| } |
| |
| static int stat_qp_show(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_qp_show_link }, |
| { "link", stat_qp_show_link }, |
| { "help", stat_help }, |
| { 0 } |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask) |
| { |
| uint32_t seq; |
| |
| rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, |
| &seq, (NLM_F_REQUEST | NLM_F_ACK)); |
| |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE, |
| RDMA_COUNTER_MODE_AUTO); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask); |
| |
| return rd_sendrecv_msg(rd, seq); |
| } |
| |
| static int stat_one_qp_set_link_auto_off(struct rd *rd) |
| { |
| return stat_qp_set_link_auto_sendmsg(rd, 0); |
| } |
| |
| static int stat_one_qp_set_auto_type_on(struct rd *rd) |
| { |
| return stat_qp_set_link_auto_sendmsg(rd, RDMA_COUNTER_MASK_QP_TYPE); |
| } |
| |
| static int stat_one_qp_set_link_auto_type(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_help }, |
| { "on", stat_one_qp_set_auto_type_on }, |
| { 0 } |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int stat_one_qp_set_link_auto(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_one_qp_link_get_mode }, |
| { "off", stat_one_qp_set_link_auto_off }, |
| { "type", stat_one_qp_set_link_auto_type }, |
| { 0 } |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int stat_one_qp_set_link(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_one_qp_link_get_mode }, |
| { "auto", stat_one_qp_set_link_auto }, |
| { 0 } |
| }; |
| |
| if (!rd->port_idx) |
| return 0; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int stat_qp_set_link(struct rd *rd) |
| { |
| return rd_exec_link(rd, stat_one_qp_set_link, false); |
| } |
| |
| static int stat_qp_set(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_help }, |
| { "link", stat_qp_set_link }, |
| { "help", stat_help }, |
| { 0 } |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int stat_get_arg(struct rd *rd, const char *arg) |
| { |
| int value = 0; |
| char *endp; |
| |
| if (strcmpx(rd_argv(rd), arg) != 0) |
| return -EINVAL; |
| |
| rd_arg_inc(rd); |
| value = strtol(rd_argv(rd), &endp, 10); |
| rd_arg_inc(rd); |
| |
| return value; |
| } |
| |
| static int stat_one_qp_bind(struct rd *rd) |
| { |
| int lqpn = 0, cntn = 0, ret; |
| uint32_t seq; |
| |
| if (rd_no_arg(rd)) { |
| stat_help(rd); |
| return -EINVAL; |
| } |
| |
| ret = rd_build_filter(rd, stat_valid_filters); |
| if (ret) |
| return ret; |
| |
| lqpn = stat_get_arg(rd, "lqpn"); |
| |
| rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, |
| &seq, (NLM_F_REQUEST | NLM_F_ACK)); |
| |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE, |
| RDMA_COUNTER_MODE_MANUAL); |
| |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn); |
| |
| if (rd_argc(rd)) { |
| cntn = stat_get_arg(rd, "cntn"); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, |
| cntn); |
| } |
| |
| return rd_sendrecv_msg(rd, seq); |
| } |
| |
| static int do_stat_qp_unbind_lqpn(struct rd *rd, uint32_t cntn, uint32_t lqpn) |
| { |
| uint32_t seq; |
| |
| rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_DEL, |
| &seq, (NLM_F_REQUEST | NLM_F_ACK)); |
| |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE, |
| RDMA_COUNTER_MODE_MANUAL); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn); |
| |
| return rd_sendrecv_msg(rd, seq); |
| } |
| |
| static int stat_get_counter_parse_cb(const struct nlmsghdr *nlh, void *data) |
| { |
| struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; |
| struct nlattr *nla_table, *nla_entry; |
| struct rd *rd = data; |
| uint32_t lqpn, cntn; |
| int err; |
| |
| mnl_attr_parse(nlh, 0, rd_attr_cb, tb); |
| |
| if (!tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]) |
| return MNL_CB_ERROR; |
| cntn = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]); |
| |
| nla_table = tb[RDMA_NLDEV_ATTR_RES_QP]; |
| if (!nla_table) |
| return MNL_CB_ERROR; |
| |
| mnl_attr_for_each_nested(nla_entry, nla_table) { |
| struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; |
| |
| err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); |
| if (err != MNL_CB_OK) |
| return -EINVAL; |
| |
| if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN]) |
| return -EINVAL; |
| |
| lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]); |
| err = do_stat_qp_unbind_lqpn(rd, cntn, lqpn); |
| if (err) |
| return MNL_CB_ERROR; |
| } |
| |
| return MNL_CB_OK; |
| } |
| |
| static int stat_one_qp_unbind(struct rd *rd) |
| { |
| int flags = NLM_F_REQUEST | NLM_F_ACK, ret; |
| char buf[MNL_SOCKET_BUFFER_SIZE]; |
| int lqpn = 0, cntn = 0; |
| unsigned int portid; |
| uint32_t seq; |
| |
| ret = rd_build_filter(rd, stat_valid_filters); |
| if (ret) |
| return ret; |
| |
| cntn = stat_get_arg(rd, "cntn"); |
| if (rd_argc(rd)) { |
| lqpn = stat_get_arg(rd, "lqpn"); |
| return do_stat_qp_unbind_lqpn(rd, cntn, lqpn); |
| } |
| |
| rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn); |
| ret = rd_send_msg(rd); |
| if (ret) |
| return ret; |
| |
| |
| /* Can't use rd_recv_msg() since the callback also calls it (recursively), |
| * then rd_recv_msg() always return -1 here |
| */ |
| portid = mnl_socket_get_portid(rd->nl); |
| ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf)); |
| if (ret <= 0) |
| return ret; |
| |
| ret = mnl_cb_run(buf, ret, seq, portid, stat_get_counter_parse_cb, rd); |
| mnl_socket_close(rd->nl); |
| if (ret != MNL_CB_OK) |
| return ret; |
| |
| return 0; |
| } |
| |
| static int stat_qp_bind_link(struct rd *rd) |
| { |
| return rd_exec_link(rd, stat_one_qp_bind, true); |
| } |
| |
| static int stat_qp_bind(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_help }, |
| { "link", stat_qp_bind_link }, |
| { "help", stat_help }, |
| { 0 }, |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int stat_qp_unbind_link(struct rd *rd) |
| { |
| return rd_exec_link(rd, stat_one_qp_unbind, true); |
| } |
| |
| static int stat_qp_unbind(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_help }, |
| { "link", stat_qp_unbind_link }, |
| { "help", stat_help }, |
| { 0 }, |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int stat_qp(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_qp_show }, |
| { "show", stat_qp_show }, |
| { "list", stat_qp_show }, |
| { "mode", stat_qp_get_mode }, |
| { "set", stat_qp_set }, |
| { "bind", stat_qp_bind }, |
| { "unbind", stat_qp_unbind }, |
| { "help", stat_help }, |
| { 0 } |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data) |
| { |
| struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; |
| struct rd *rd = data; |
| const char *name; |
| uint32_t port; |
| int ret; |
| |
| mnl_attr_parse(nlh, 0, rd_attr_cb, tb); |
| if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] || |
| !tb[RDMA_NLDEV_ATTR_PORT_INDEX] || |
| !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) |
| return MNL_CB_ERROR; |
| |
| name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); |
| port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); |
| if (rd->json_output) { |
| jsonw_string_field(rd->jw, "ifname", name); |
| jsonw_uint_field(rd->jw, "port", port); |
| } else { |
| pr_out("link %s/%u ", name, port); |
| } |
| |
| ret = res_get_hwcounters(rd, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true); |
| |
| if (!rd->json_output) |
| pr_out("\n"); |
| return ret; |
| } |
| |
| static int stat_show_one_link(struct rd *rd) |
| { |
| int flags = NLM_F_REQUEST | NLM_F_ACK; |
| uint32_t seq; |
| int ret; |
| |
| if (!rd->port_idx) |
| return 0; |
| |
| rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); |
| mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); |
| ret = rd_send_msg(rd); |
| if (ret) |
| return ret; |
| |
| return rd_recv_msg(rd, stat_show_parse_cb, rd, seq); |
| } |
| |
| static int stat_show_link(struct rd *rd) |
| { |
| return rd_exec_link(rd, stat_show_one_link, false); |
| } |
| |
| static int stat_show(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_show_link }, |
| { "link", stat_show_link }, |
| { "help", stat_help }, |
| { 0 } |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "parameter"); |
| } |
| |
| int cmd_stat(struct rd *rd) |
| { |
| const struct rd_cmd cmds[] = { |
| { NULL, stat_show }, |
| { "show", stat_show }, |
| { "list", stat_show }, |
| { "help", stat_help }, |
| { "qp", stat_qp }, |
| { 0 } |
| }; |
| |
| return rd_exec_cmd(rd, cmds, "statistic command"); |
| } |