diff options
Diffstat (limited to 'rdma')
-rw-r--r-- | rdma/.gitignore | 1 | ||||
-rw-r--r-- | rdma/Makefile | 22 | ||||
-rw-r--r-- | rdma/dev.c | 381 | ||||
-rw-r--r-- | rdma/include/uapi/rdma/ib_user_sa.h | 77 | ||||
-rw-r--r-- | rdma/include/uapi/rdma/ib_user_verbs.h | 1364 | ||||
-rw-r--r-- | rdma/include/uapi/rdma/rdma_netlink.h | 599 | ||||
-rw-r--r-- | rdma/include/uapi/rdma/rdma_user_cm.h | 341 | ||||
-rw-r--r-- | rdma/link.c | 382 | ||||
-rw-r--r-- | rdma/rdma.c | 177 | ||||
-rw-r--r-- | rdma/rdma.h | 144 | ||||
-rw-r--r-- | rdma/res-cmid.c | 250 | ||||
-rw-r--r-- | rdma/res-cq.c | 181 | ||||
-rw-r--r-- | rdma/res-ctx.c | 103 | ||||
-rw-r--r-- | rdma/res-mr.c | 146 | ||||
-rw-r--r-- | rdma/res-pd.c | 130 | ||||
-rw-r--r-- | rdma/res-qp.c | 233 | ||||
-rw-r--r-- | rdma/res-srq.c | 300 | ||||
-rw-r--r-- | rdma/res.c | 258 | ||||
-rw-r--r-- | rdma/res.h | 196 | ||||
-rw-r--r-- | rdma/stat-mr.c | 86 | ||||
-rw-r--r-- | rdma/stat.c | 1131 | ||||
-rw-r--r-- | rdma/stat.h | 24 | ||||
-rw-r--r-- | rdma/sys.c | 182 | ||||
-rw-r--r-- | rdma/utils.c | 958 |
24 files changed, 7666 insertions, 0 deletions
diff --git a/rdma/.gitignore b/rdma/.gitignore new file mode 100644 index 0000000..51fb172 --- /dev/null +++ b/rdma/.gitignore @@ -0,0 +1 @@ +rdma diff --git a/rdma/Makefile b/rdma/Makefile new file mode 100644 index 0000000..37d904a --- /dev/null +++ b/rdma/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +include ../config.mk + +CFLAGS += -I./include/uapi/ + +RDMA_OBJ = rdma.o utils.o dev.o link.o res.o res-pd.o res-mr.o res-cq.o \ + res-cmid.o res-qp.o sys.o stat.o stat-mr.o res-ctx.o res-srq.o + +TARGETS += rdma + +all: $(TARGETS) $(LIBS) + +rdma: $(RDMA_OBJ) $(LIBS) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + for i in $(TARGETS); \ + do install -m 0755 $$i $(DESTDIR)$(SBINDIR); \ + done + +clean: + rm -f $(RDMA_OBJ) $(TARGETS) diff --git a/rdma/dev.c b/rdma/dev.c new file mode 100644 index 0000000..f495b71 --- /dev/null +++ b/rdma/dev.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * dev.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include <fcntl.h> +#include "rdma.h" + +static int dev_help(struct rd *rd) +{ + pr_out("Usage: %s dev show [DEV]\n", rd->filename); + pr_out(" %s dev set [DEV] name DEVNAME\n", rd->filename); + pr_out(" %s dev set [DEV] netns NSNAME\n", rd->filename); + pr_out(" %s dev set [DEV] adaptive-moderation [on|off]\n", rd->filename); + return 0; +} + +static const char *dev_caps_to_str(uint32_t idx) +{ +#define RDMA_DEV_FLAGS_LOW(x) \ + x(RESIZE_MAX_WR, 0) \ + x(BAD_PKEY_CNTR, 1) \ + x(BAD_QKEY_CNTR, 2) \ + x(RAW_MULTI, 3) \ + x(AUTO_PATH_MIG, 4) \ + x(CHANGE_PHY_PORT, 5) \ + x(UD_AV_PORT_ENFORCE_PORT_ENFORCE, 6) \ + x(CURR_QP_STATE_MOD, 7) \ + x(SHUTDOWN_PORT, 8) \ + x(INIT_TYPE, 9) \ + x(PORT_ACTIVE_EVENT, 10) \ + x(SYS_IMAGE_GUID, 11) \ + x(RC_RNR_NAK_GEN, 12) \ + x(SRQ_RESIZE, 13) \ + x(N_NOTIFY_CQ, 14) \ + x(LOCAL_DMA_LKEY, 15) \ + x(MEM_WINDOW, 17) \ + x(UD_IP_CSUM, 18) \ + x(UD_TSO, 19) \ + x(XRC, 20) \ + x(MEM_MGT_EXTENSIONS, 21) \ + x(BLOCK_MULTICAST_LOOPBACK, 22) \ + x(MEM_WINDOW_TYPE_2A, 23) \ + x(MEM_WINDOW_TYPE_2B, 24) \ + x(RC_IP_CSUM, 25) \ + x(RAW_IP_CSUM, 26) \ + x(CROSS_CHANNEL, 27) \ + x(MANAGED_FLOW_STEERING, 29) \ + x(SIGNATURE_HANDOVER, 30) \ + x(ON_DEMAND_PAGING, 31) + +#define RDMA_DEV_FLAGS_HIGH(x) \ + x(SG_GAPS_REG, 0) \ + x(VIRTUAL_FUNCTION, 1) \ + x(RAW_SCATTER_FCS, 2) \ + x(RDMA_NETDEV_OPA_VNIC, 3) \ + x(PCI_WRITE_END_PADDING, 4) + + /* + * Separation below is needed to allow compilation of rdmatool + * on 32bits systems. On such systems, C-enum is limited to be + * int and can't hold more than 32 bits. + */ + enum { RDMA_DEV_FLAGS_LOW(RDMA_BITMAP_ENUM) }; + enum { RDMA_DEV_FLAGS_HIGH(RDMA_BITMAP_ENUM) }; + + static const char * const + rdma_dev_names_low[] = { RDMA_DEV_FLAGS_LOW(RDMA_BITMAP_NAMES) }; + static const char * const + rdma_dev_names_high[] = { RDMA_DEV_FLAGS_HIGH(RDMA_BITMAP_NAMES) }; + uint32_t high_idx; + #undef RDMA_DEV_FLAGS_LOW + #undef RDMA_DEV_FLAGS_HIGH + + if (idx < ARRAY_SIZE(rdma_dev_names_low) && rdma_dev_names_low[idx]) + return rdma_dev_names_low[idx]; + + high_idx = idx - ARRAY_SIZE(rdma_dev_names_low); + if (high_idx < ARRAY_SIZE(rdma_dev_names_high) && + rdma_dev_names_high[high_idx]) + return rdma_dev_names_high[high_idx]; + + return "UNKNOWN"; +} + +static void dev_print_caps(struct nlattr **tb) +{ + uint64_t caps; + uint32_t idx; + + if (!tb[RDMA_NLDEV_ATTR_CAP_FLAGS]) + return; + + caps = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_CAP_FLAGS]); + + print_string(PRINT_FP, NULL, "%s caps: <", _SL_); + open_json_array(PRINT_JSON, "caps"); + for (idx = 0; caps; idx++) { + if (caps & 0x1) + print_string(PRINT_ANY, NULL, + caps >> 0x1 ? "%s, " : "%s", + dev_caps_to_str(idx)); + caps >>= 0x1; + } + close_json_array(PRINT_ANY, ">"); +} + +static void dev_print_fw(struct nlattr **tb) +{ + const char *str; + + if (!tb[RDMA_NLDEV_ATTR_FW_VERSION]) + return; + + str = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_FW_VERSION]); + print_string(PRINT_ANY, "fw", "fw %s ", str); +} + +static void dev_print_node_guid(struct nlattr **tb) +{ + uint64_t node_guid; + uint16_t vp[4]; + char str[32]; + + if (!tb[RDMA_NLDEV_ATTR_NODE_GUID]) + return; + + node_guid = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_NODE_GUID]); + memcpy(vp, &node_guid, sizeof(uint64_t)); + snprintf(str, 32, "%04x:%04x:%04x:%04x", vp[3], vp[2], vp[1], vp[0]); + print_string(PRINT_ANY, "node_guid", "node_guid %s ", + str); +} + +static void dev_print_sys_image_guid(struct nlattr **tb) +{ + uint64_t sys_image_guid; + uint16_t vp[4]; + char str[32]; + + if (!tb[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID]) + return; + + sys_image_guid = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID]); + memcpy(vp, &sys_image_guid, sizeof(uint64_t)); + snprintf(str, 32, "%04x:%04x:%04x:%04x", vp[3], vp[2], vp[1], vp[0]); + print_string(PRINT_ANY, "sys_image_guid", "sys_image_guid %s ", str); +} + +static void dev_print_dim_setting(struct nlattr **tb) +{ + uint8_t dim_setting; + + if (!tb[RDMA_NLDEV_ATTR_DEV_DIM]) + return; + + dim_setting = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_DEV_DIM]); + if (dim_setting > 1) + return; + + print_on_off(PRINT_ANY, "adaptive-moderation", "adaptive-moderation %s ", dim_setting); + +} + +static const char *node_type_to_str(uint8_t node_type) +{ + static const char * const node_type_str[] = { "unknown", "ca", + "switch", "router", + "rnic", "usnic", + "usnic_udp", + "unspecified" }; + if (node_type < ARRAY_SIZE(node_type_str)) + return node_type_str[node_type]; + return "unknown"; +} + +static void dev_print_node_type(struct nlattr **tb) +{ + const char *node_str; + uint8_t node_type; + + if (!tb[RDMA_NLDEV_ATTR_DEV_NODE_TYPE]) + return; + + node_type = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_DEV_NODE_TYPE]); + node_str = node_type_to_str(node_type); + print_string(PRINT_ANY, "node_type", "node_type %s ", node_str); +} + +static void dev_print_dev_proto(struct nlattr **tb) +{ + const char *str; + + if (!tb[RDMA_NLDEV_ATTR_DEV_PROTOCOL]) + return; + + str = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_PROTOCOL]); + print_string(PRINT_ANY, "protocol", "protocol %s ", str); +} + +static int dev_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + uint32_t idx; + + 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; + + open_json_object(NULL); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + print_uint(PRINT_ANY, "ifindex", "%u: ", idx); + print_string(PRINT_ANY, "ifname", "%s: ", name); + + dev_print_node_type(tb); + dev_print_dev_proto(tb); + dev_print_fw(tb); + dev_print_node_guid(tb); + dev_print_sys_image_guid(tb); + if (rd->show_details) { + dev_print_dim_setting(tb); + dev_print_caps(tb); + } + + close_json_object(); + newline(); + + return MNL_CB_OK; +} + +static int dev_no_args(struct rd *rd) +{ + uint32_t seq; + int ret; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_GET, + &seq, (NLM_F_REQUEST | NLM_F_ACK)); + mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); + ret = rd_send_msg(rd); + if (ret) + return ret; + + ret = rd_recv_msg(rd, dev_parse_cb, rd, seq); + return ret; +} + +static int dev_one_show(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, dev_no_args}, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int dev_set_name(struct rd *rd) +{ + uint32_t seq; + + if (rd_no_arg(rd)) { + pr_err("Please provide device new name.\n"); + return -EINVAL; + } + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_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_strz(rd->nlh, RDMA_NLDEV_ATTR_DEV_NAME, rd_argv(rd)); + + return rd_sendrecv_msg(rd, seq); +} + +static int dev_set_netns(struct rd *rd) +{ + char *netns_path; + uint32_t seq; + int netns; + int ret; + + if (rd_no_arg(rd)) { + pr_err("Please provide device name.\n"); + return -EINVAL; + } + + if (asprintf(&netns_path, "%s/%s", NETNS_RUN_DIR, rd_argv(rd)) < 0) + return -ENOMEM; + + netns = open(netns_path, O_RDONLY | O_CLOEXEC); + if (netns < 0) { + fprintf(stderr, "Cannot open network namespace \"%s\": %s\n", + rd_argv(rd), strerror(errno)); + ret = -EINVAL; + goto done; + } + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_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_NET_NS_FD, netns); + ret = rd_sendrecv_msg(rd, seq); + close(netns); +done: + free(netns_path); + return ret; +} + +static int dev_set_dim_sendmsg(struct rd *rd, uint8_t dim_setting) +{ + uint32_t seq; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_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_u8(rd->nlh, RDMA_NLDEV_ATTR_DEV_DIM, dim_setting); + + return rd_sendrecv_msg(rd, seq); +} + +static int dev_set_dim_off(struct rd *rd) +{ + return dev_set_dim_sendmsg(rd, 0); +} + +static int dev_set_dim_on(struct rd *rd) +{ + return dev_set_dim_sendmsg(rd, 1); +} + +static int dev_set_dim(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, dev_help}, + { "on", dev_set_dim_on}, + { "off", dev_set_dim_off}, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int dev_one_set(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, dev_help}, + { "name", dev_set_name}, + { "netns", dev_set_netns}, + { "adaptive-moderation", dev_set_dim}, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int dev_show(struct rd *rd) +{ + return rd_exec_dev(rd, dev_one_show); +} + +static int dev_set(struct rd *rd) +{ + return rd_exec_require_dev(rd, dev_one_set); +} + +int cmd_dev(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, dev_show }, + { "show", dev_show }, + { "list", dev_show }, + { "set", dev_set }, + { "help", dev_help }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "dev command"); +} diff --git a/rdma/include/uapi/rdma/ib_user_sa.h b/rdma/include/uapi/rdma/ib_user_sa.h new file mode 100644 index 0000000..435155d --- /dev/null +++ b/rdma/include/uapi/rdma/ib_user_sa.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR Linux-OpenIB) */ +/* + * Copyright (c) 2005 Intel Corporation. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IB_USER_SA_H +#define IB_USER_SA_H + +#include <linux/types.h> + +enum { + IB_PATH_GMP = 1, + IB_PATH_PRIMARY = (1<<1), + IB_PATH_ALTERNATE = (1<<2), + IB_PATH_OUTBOUND = (1<<3), + IB_PATH_INBOUND = (1<<4), + IB_PATH_INBOUND_REVERSE = (1<<5), + IB_PATH_BIDIRECTIONAL = IB_PATH_OUTBOUND | IB_PATH_INBOUND_REVERSE +}; + +struct ib_path_rec_data { + __u32 flags; + __u32 reserved; + __u32 path_rec[16]; +}; + +struct ib_user_path_rec { + __u8 dgid[16]; + __u8 sgid[16]; + __be16 dlid; + __be16 slid; + __u32 raw_traffic; + __be32 flow_label; + __u32 reversible; + __u32 mtu; + __be16 pkey; + __u8 hop_limit; + __u8 traffic_class; + __u8 numb_path; + __u8 sl; + __u8 mtu_selector; + __u8 rate_selector; + __u8 rate; + __u8 packet_life_time_selector; + __u8 packet_life_time; + __u8 preference; +}; + +#endif /* IB_USER_SA_H */ diff --git a/rdma/include/uapi/rdma/ib_user_verbs.h b/rdma/include/uapi/rdma/ib_user_verbs.h new file mode 100644 index 0000000..e16650f --- /dev/null +++ b/rdma/include/uapi/rdma/ib_user_verbs.h @@ -0,0 +1,1364 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR Linux-OpenIB) */ +/* + * Copyright (c) 2005 Topspin Communications. All rights reserved. + * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved. + * Copyright (c) 2005 PathScale, Inc. All rights reserved. + * Copyright (c) 2006 Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IB_USER_VERBS_H +#define IB_USER_VERBS_H + +#include <linux/types.h> + +/* + * Increment this value if any changes that break userspace ABI + * compatibility are made. + */ +#define IB_USER_VERBS_ABI_VERSION 6 +#define IB_USER_VERBS_CMD_THRESHOLD 50 + +enum ib_uverbs_write_cmds { + IB_USER_VERBS_CMD_GET_CONTEXT, + IB_USER_VERBS_CMD_QUERY_DEVICE, + IB_USER_VERBS_CMD_QUERY_PORT, + IB_USER_VERBS_CMD_ALLOC_PD, + IB_USER_VERBS_CMD_DEALLOC_PD, + IB_USER_VERBS_CMD_CREATE_AH, + IB_USER_VERBS_CMD_MODIFY_AH, + IB_USER_VERBS_CMD_QUERY_AH, + IB_USER_VERBS_CMD_DESTROY_AH, + IB_USER_VERBS_CMD_REG_MR, + IB_USER_VERBS_CMD_REG_SMR, + IB_USER_VERBS_CMD_REREG_MR, + IB_USER_VERBS_CMD_QUERY_MR, + IB_USER_VERBS_CMD_DEREG_MR, + IB_USER_VERBS_CMD_ALLOC_MW, + IB_USER_VERBS_CMD_BIND_MW, + IB_USER_VERBS_CMD_DEALLOC_MW, + IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL, + IB_USER_VERBS_CMD_CREATE_CQ, + IB_USER_VERBS_CMD_RESIZE_CQ, + IB_USER_VERBS_CMD_DESTROY_CQ, + IB_USER_VERBS_CMD_POLL_CQ, + IB_USER_VERBS_CMD_PEEK_CQ, + IB_USER_VERBS_CMD_REQ_NOTIFY_CQ, + IB_USER_VERBS_CMD_CREATE_QP, + IB_USER_VERBS_CMD_QUERY_QP, + IB_USER_VERBS_CMD_MODIFY_QP, + IB_USER_VERBS_CMD_DESTROY_QP, + IB_USER_VERBS_CMD_POST_SEND, + IB_USER_VERBS_CMD_POST_RECV, + IB_USER_VERBS_CMD_ATTACH_MCAST, + IB_USER_VERBS_CMD_DETACH_MCAST, + IB_USER_VERBS_CMD_CREATE_SRQ, + IB_USER_VERBS_CMD_MODIFY_SRQ, + IB_USER_VERBS_CMD_QUERY_SRQ, + IB_USER_VERBS_CMD_DESTROY_SRQ, + IB_USER_VERBS_CMD_POST_SRQ_RECV, + IB_USER_VERBS_CMD_OPEN_XRCD, + IB_USER_VERBS_CMD_CLOSE_XRCD, + IB_USER_VERBS_CMD_CREATE_XSRQ, + IB_USER_VERBS_CMD_OPEN_QP, +}; + +enum { + IB_USER_VERBS_EX_CMD_QUERY_DEVICE = IB_USER_VERBS_CMD_QUERY_DEVICE, + IB_USER_VERBS_EX_CMD_CREATE_CQ = IB_USER_VERBS_CMD_CREATE_CQ, + IB_USER_VERBS_EX_CMD_CREATE_QP = IB_USER_VERBS_CMD_CREATE_QP, + IB_USER_VERBS_EX_CMD_MODIFY_QP = IB_USER_VERBS_CMD_MODIFY_QP, + IB_USER_VERBS_EX_CMD_CREATE_FLOW = IB_USER_VERBS_CMD_THRESHOLD, + IB_USER_VERBS_EX_CMD_DESTROY_FLOW, + IB_USER_VERBS_EX_CMD_CREATE_WQ, + IB_USER_VERBS_EX_CMD_MODIFY_WQ, + IB_USER_VERBS_EX_CMD_DESTROY_WQ, + IB_USER_VERBS_EX_CMD_CREATE_RWQ_IND_TBL, + IB_USER_VERBS_EX_CMD_DESTROY_RWQ_IND_TBL, + IB_USER_VERBS_EX_CMD_MODIFY_CQ +}; + +/* see IBA A19.4.1.1 Placement Types */ +enum ib_placement_type { + IB_FLUSH_GLOBAL = 1U << 0, + IB_FLUSH_PERSISTENT = 1U << 1, +}; + +/* see IBA A19.4.1.2 Selectivity Level */ +enum ib_selectivity_level { + IB_FLUSH_RANGE = 0, + IB_FLUSH_MR, +}; + +/* + * Make sure that all structs defined in this file remain laid out so + * that they pack the same way on 32-bit and 64-bit architectures (to + * avoid incompatibility between 32-bit userspace and 64-bit kernels). + * Specifically: + * - Do not use pointer types -- pass pointers in __u64 instead. + * - Make sure that any structure larger than 4 bytes is padded to a + * multiple of 8 bytes. Otherwise the structure size will be + * different between 32-bit and 64-bit architectures. + */ + +struct ib_uverbs_async_event_desc { + __aligned_u64 element; + __u32 event_type; /* enum ib_event_type */ + __u32 reserved; +}; + +struct ib_uverbs_comp_event_desc { + __aligned_u64 cq_handle; +}; + +struct ib_uverbs_cq_moderation_caps { + __u16 max_cq_moderation_count; + __u16 max_cq_moderation_period; + __u32 reserved; +}; + +/* + * All commands from userspace should start with a __u32 command field + * followed by __u16 in_words and out_words fields (which give the + * length of the command block and response buffer if any in 32-bit + * words). The kernel driver will read these fields first and read + * the rest of the command struct based on these value. + */ + +#define IB_USER_VERBS_CMD_COMMAND_MASK 0xff +#define IB_USER_VERBS_CMD_FLAG_EXTENDED 0x80000000u + +struct ib_uverbs_cmd_hdr { + __u32 command; + __u16 in_words; + __u16 out_words; +}; + +struct ib_uverbs_ex_cmd_hdr { + __aligned_u64 response; + __u16 provider_in_words; + __u16 provider_out_words; + __u32 cmd_hdr_reserved; +}; + +struct ib_uverbs_get_context { + __aligned_u64 response; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_get_context_resp { + __u32 async_fd; + __u32 num_comp_vectors; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_query_device { + __aligned_u64 response; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_query_device_resp { + __aligned_u64 fw_ver; + __be64 node_guid; + __be64 sys_image_guid; + __aligned_u64 max_mr_size; + __aligned_u64 page_size_cap; + __u32 vendor_id; + __u32 vendor_part_id; + __u32 hw_ver; + __u32 max_qp; + __u32 max_qp_wr; + __u32 device_cap_flags; + __u32 max_sge; + __u32 max_sge_rd; + __u32 max_cq; + __u32 max_cqe; + __u32 max_mr; + __u32 max_pd; + __u32 max_qp_rd_atom; + __u32 max_ee_rd_atom; + __u32 max_res_rd_atom; + __u32 max_qp_init_rd_atom; + __u32 max_ee_init_rd_atom; + __u32 atomic_cap; + __u32 max_ee; + __u32 max_rdd; + __u32 max_mw; + __u32 max_raw_ipv6_qp; + __u32 max_raw_ethy_qp; + __u32 max_mcast_grp; + __u32 max_mcast_qp_attach; + __u32 max_total_mcast_qp_attach; + __u32 max_ah; + __u32 max_fmr; + __u32 max_map_per_fmr; + __u32 max_srq; + __u32 max_srq_wr; + __u32 max_srq_sge; + __u16 max_pkeys; + __u8 local_ca_ack_delay; + __u8 phys_port_cnt; + __u8 reserved[4]; +}; + +struct ib_uverbs_ex_query_device { + __u32 comp_mask; + __u32 reserved; +}; + +struct ib_uverbs_odp_caps { + __aligned_u64 general_caps; + struct { + __u32 rc_odp_caps; + __u32 uc_odp_caps; + __u32 ud_odp_caps; + } per_transport_caps; + __u32 reserved; +}; + +struct ib_uverbs_rss_caps { + /* Corresponding bit will be set if qp type from + * 'enum ib_qp_type' is supported, e.g. + * supported_qpts |= 1 << IB_QPT_UD + */ + __u32 supported_qpts; + __u32 max_rwq_indirection_tables; + __u32 max_rwq_indirection_table_size; + __u32 reserved; +}; + +struct ib_uverbs_tm_caps { + /* Max size of rendezvous request message */ + __u32 max_rndv_hdr_size; + /* Max number of entries in tag matching list */ + __u32 max_num_tags; + /* TM flags */ + __u32 flags; + /* Max number of outstanding list operations */ + __u32 max_ops; + /* Max number of SGE in tag matching entry */ + __u32 max_sge; + __u32 reserved; +}; + +struct ib_uverbs_ex_query_device_resp { + struct ib_uverbs_query_device_resp base; + __u32 comp_mask; + __u32 response_length; + struct ib_uverbs_odp_caps odp_caps; + __aligned_u64 timestamp_mask; + __aligned_u64 hca_core_clock; /* in KHZ */ + __aligned_u64 device_cap_flags_ex; + struct ib_uverbs_rss_caps rss_caps; + __u32 max_wq_type_rq; + __u32 raw_packet_caps; + struct ib_uverbs_tm_caps tm_caps; + struct ib_uverbs_cq_moderation_caps cq_moderation_caps; + __aligned_u64 max_dm_size; + __u32 xrc_odp_caps; + __u32 reserved; +}; + +struct ib_uverbs_query_port { + __aligned_u64 response; + __u8 port_num; + __u8 reserved[7]; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_query_port_resp { + __u32 port_cap_flags; /* see ib_uverbs_query_port_cap_flags */ + __u32 max_msg_sz; + __u32 bad_pkey_cntr; + __u32 qkey_viol_cntr; + __u32 gid_tbl_len; + __u16 pkey_tbl_len; + __u16 lid; + __u16 sm_lid; + __u8 state; + __u8 max_mtu; + __u8 active_mtu; + __u8 lmc; + __u8 max_vl_num; + __u8 sm_sl; + __u8 subnet_timeout; + __u8 init_type_reply; + __u8 active_width; + __u8 active_speed; + __u8 phys_state; + __u8 link_layer; + __u8 flags; /* see ib_uverbs_query_port_flags */ + __u8 reserved; +}; + +struct ib_uverbs_alloc_pd { + __aligned_u64 response; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_alloc_pd_resp { + __u32 pd_handle; + __u32 driver_data[]; +}; + +struct ib_uverbs_dealloc_pd { + __u32 pd_handle; +}; + +struct ib_uverbs_open_xrcd { + __aligned_u64 response; + __u32 fd; + __u32 oflags; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_open_xrcd_resp { + __u32 xrcd_handle; + __u32 driver_data[]; +}; + +struct ib_uverbs_close_xrcd { + __u32 xrcd_handle; +}; + +struct ib_uverbs_reg_mr { + __aligned_u64 response; + __aligned_u64 start; + __aligned_u64 length; + __aligned_u64 hca_va; + __u32 pd_handle; + __u32 access_flags; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_reg_mr_resp { + __u32 mr_handle; + __u32 lkey; + __u32 rkey; + __u32 driver_data[]; +}; + +struct ib_uverbs_rereg_mr { + __aligned_u64 response; + __u32 mr_handle; + __u32 flags; + __aligned_u64 start; + __aligned_u64 length; + __aligned_u64 hca_va; + __u32 pd_handle; + __u32 access_flags; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_rereg_mr_resp { + __u32 lkey; + __u32 rkey; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_dereg_mr { + __u32 mr_handle; +}; + +struct ib_uverbs_alloc_mw { + __aligned_u64 response; + __u32 pd_handle; + __u8 mw_type; + __u8 reserved[3]; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_alloc_mw_resp { + __u32 mw_handle; + __u32 rkey; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_dealloc_mw { + __u32 mw_handle; +}; + +struct ib_uverbs_create_comp_channel { + __aligned_u64 response; +}; + +struct ib_uverbs_create_comp_channel_resp { + __u32 fd; +}; + +struct ib_uverbs_create_cq { + __aligned_u64 response; + __aligned_u64 user_handle; + __u32 cqe; + __u32 comp_vector; + __s32 comp_channel; + __u32 reserved; + __aligned_u64 driver_data[]; +}; + +enum ib_uverbs_ex_create_cq_flags { + IB_UVERBS_CQ_FLAGS_TIMESTAMP_COMPLETION = 1 << 0, + IB_UVERBS_CQ_FLAGS_IGNORE_OVERRUN = 1 << 1, +}; + +struct ib_uverbs_ex_create_cq { + __aligned_u64 user_handle; + __u32 cqe; + __u32 comp_vector; + __s32 comp_channel; + __u32 comp_mask; + __u32 flags; /* bitmask of ib_uverbs_ex_create_cq_flags */ + __u32 reserved; +}; + +struct ib_uverbs_create_cq_resp { + __u32 cq_handle; + __u32 cqe; + __aligned_u64 driver_data[0]; +}; + +struct ib_uverbs_ex_create_cq_resp { + struct ib_uverbs_create_cq_resp base; + __u32 comp_mask; + __u32 response_length; +}; + +struct ib_uverbs_resize_cq { + __aligned_u64 response; + __u32 cq_handle; + __u32 cqe; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_resize_cq_resp { + __u32 cqe; + __u32 reserved; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_poll_cq { + __aligned_u64 response; + __u32 cq_handle; + __u32 ne; +}; + +enum ib_uverbs_wc_opcode { + IB_UVERBS_WC_SEND = 0, + IB_UVERBS_WC_RDMA_WRITE = 1, + IB_UVERBS_WC_RDMA_READ = 2, + IB_UVERBS_WC_COMP_SWAP = 3, + IB_UVERBS_WC_FETCH_ADD = 4, + IB_UVERBS_WC_BIND_MW = 5, + IB_UVERBS_WC_LOCAL_INV = 6, + IB_UVERBS_WC_TSO = 7, + IB_UVERBS_WC_FLUSH = 8, + IB_UVERBS_WC_ATOMIC_WRITE = 9, +}; + +struct ib_uverbs_wc { + __aligned_u64 wr_id; + __u32 status; + __u32 opcode; + __u32 vendor_err; + __u32 byte_len; + union { + __be32 imm_data; + __u32 invalidate_rkey; + } ex; + __u32 qp_num; + __u32 src_qp; + __u32 wc_flags; + __u16 pkey_index; + __u16 slid; + __u8 sl; + __u8 dlid_path_bits; + __u8 port_num; + __u8 reserved; +}; + +struct ib_uverbs_poll_cq_resp { + __u32 count; + __u32 reserved; + struct ib_uverbs_wc wc[]; +}; + +struct ib_uverbs_req_notify_cq { + __u32 cq_handle; + __u32 solicited_only; +}; + +struct ib_uverbs_destroy_cq { + __aligned_u64 response; + __u32 cq_handle; + __u32 reserved; +}; + +struct ib_uverbs_destroy_cq_resp { + __u32 comp_events_reported; + __u32 async_events_reported; +}; + +struct ib_uverbs_global_route { + __u8 dgid[16]; + __u32 flow_label; + __u8 sgid_index; + __u8 hop_limit; + __u8 traffic_class; + __u8 reserved; +}; + +struct ib_uverbs_ah_attr { + struct ib_uverbs_global_route grh; + __u16 dlid; + __u8 sl; + __u8 src_path_bits; + __u8 static_rate; + __u8 is_global; + __u8 port_num; + __u8 reserved; +}; + +struct ib_uverbs_qp_attr { + __u32 qp_attr_mask; + __u32 qp_state; + __u32 cur_qp_state; + __u32 path_mtu; + __u32 path_mig_state; + __u32 qkey; + __u32 rq_psn; + __u32 sq_psn; + __u32 dest_qp_num; + __u32 qp_access_flags; + + struct ib_uverbs_ah_attr ah_attr; + struct ib_uverbs_ah_attr alt_ah_attr; + + /* ib_qp_cap */ + __u32 max_send_wr; + __u32 max_recv_wr; + __u32 max_send_sge; + __u32 max_recv_sge; + __u32 max_inline_data; + + __u16 pkey_index; + __u16 alt_pkey_index; + __u8 en_sqd_async_notify; + __u8 sq_draining; + __u8 max_rd_atomic; + __u8 max_dest_rd_atomic; + __u8 min_rnr_timer; + __u8 port_num; + __u8 timeout; + __u8 retry_cnt; + __u8 rnr_retry; + __u8 alt_port_num; + __u8 alt_timeout; + __u8 reserved[5]; +}; + +struct ib_uverbs_create_qp { + __aligned_u64 response; + __aligned_u64 user_handle; + __u32 pd_handle; + __u32 send_cq_handle; + __u32 recv_cq_handle; + __u32 srq_handle; + __u32 max_send_wr; + __u32 max_recv_wr; + __u32 max_send_sge; + __u32 max_recv_sge; + __u32 max_inline_data; + __u8 sq_sig_all; + __u8 qp_type; + __u8 is_srq; + __u8 reserved; + __aligned_u64 driver_data[]; +}; + +enum ib_uverbs_create_qp_mask { + IB_UVERBS_CREATE_QP_MASK_IND_TABLE = 1UL << 0, +}; + +enum { + IB_UVERBS_CREATE_QP_SUP_COMP_MASK = IB_UVERBS_CREATE_QP_MASK_IND_TABLE, +}; + +struct ib_uverbs_ex_create_qp { + __aligned_u64 user_handle; + __u32 pd_handle; + __u32 send_cq_handle; + __u32 recv_cq_handle; + __u32 srq_handle; + __u32 max_send_wr; + __u32 max_recv_wr; + __u32 max_send_sge; + __u32 max_recv_sge; + __u32 max_inline_data; + __u8 sq_sig_all; + __u8 qp_type; + __u8 is_srq; + __u8 reserved; + __u32 comp_mask; + __u32 create_flags; + __u32 rwq_ind_tbl_handle; + __u32 source_qpn; +}; + +struct ib_uverbs_open_qp { + __aligned_u64 response; + __aligned_u64 user_handle; + __u32 pd_handle; + __u32 qpn; + __u8 qp_type; + __u8 reserved[7]; + __aligned_u64 driver_data[]; +}; + +/* also used for open response */ +struct ib_uverbs_create_qp_resp { + __u32 qp_handle; + __u32 qpn; + __u32 max_send_wr; + __u32 max_recv_wr; + __u32 max_send_sge; + __u32 max_recv_sge; + __u32 max_inline_data; + __u32 reserved; + __u32 driver_data[0]; +}; + +struct ib_uverbs_ex_create_qp_resp { + struct ib_uverbs_create_qp_resp base; + __u32 comp_mask; + __u32 response_length; +}; + +/* + * This struct needs to remain a multiple of 8 bytes to keep the + * alignment of the modify QP parameters. + */ +struct ib_uverbs_qp_dest { + __u8 dgid[16]; + __u32 flow_label; + __u16 dlid; + __u16 reserved; + __u8 sgid_index; + __u8 hop_limit; + __u8 traffic_class; + __u8 sl; + __u8 src_path_bits; + __u8 static_rate; + __u8 is_global; + __u8 port_num; +}; + +struct ib_uverbs_query_qp { + __aligned_u64 response; + __u32 qp_handle; + __u32 attr_mask; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_query_qp_resp { + struct ib_uverbs_qp_dest dest; + struct ib_uverbs_qp_dest alt_dest; + __u32 max_send_wr; + __u32 max_recv_wr; + __u32 max_send_sge; + __u32 max_recv_sge; + __u32 max_inline_data; + __u32 qkey; + __u32 rq_psn; + __u32 sq_psn; + __u32 dest_qp_num; + __u32 qp_access_flags; + __u16 pkey_index; + __u16 alt_pkey_index; + __u8 qp_state; + __u8 cur_qp_state; + __u8 path_mtu; + __u8 path_mig_state; + __u8 sq_draining; + __u8 max_rd_atomic; + __u8 max_dest_rd_atomic; + __u8 min_rnr_timer; + __u8 port_num; + __u8 timeout; + __u8 retry_cnt; + __u8 rnr_retry; + __u8 alt_port_num; + __u8 alt_timeout; + __u8 sq_sig_all; + __u8 reserved[5]; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_modify_qp { + struct ib_uverbs_qp_dest dest; + struct ib_uverbs_qp_dest alt_dest; + __u32 qp_handle; + __u32 attr_mask; + __u32 qkey; + __u32 rq_psn; + __u32 sq_psn; + __u32 dest_qp_num; + __u32 qp_access_flags; + __u16 pkey_index; + __u16 alt_pkey_index; + __u8 qp_state; + __u8 cur_qp_state; + __u8 path_mtu; + __u8 path_mig_state; + __u8 en_sqd_async_notify; + __u8 max_rd_atomic; + __u8 max_dest_rd_atomic; + __u8 min_rnr_timer; + __u8 port_num; + __u8 timeout; + __u8 retry_cnt; + __u8 rnr_retry; + __u8 alt_port_num; + __u8 alt_timeout; + __u8 reserved[2]; + __aligned_u64 driver_data[0]; +}; + +struct ib_uverbs_ex_modify_qp { + struct ib_uverbs_modify_qp base; + __u32 rate_limit; + __u32 reserved; +}; + +struct ib_uverbs_ex_modify_qp_resp { + __u32 comp_mask; + __u32 response_length; +}; + +struct ib_uverbs_destroy_qp { + __aligned_u64 response; + __u32 qp_handle; + __u32 reserved; +}; + +struct ib_uverbs_destroy_qp_resp { + __u32 events_reported; +}; + +/* + * The ib_uverbs_sge structure isn't used anywhere, since we assume + * the ib_sge structure is packed the same way on 32-bit and 64-bit + * architectures in both kernel and user space. It's just here to + * document the ABI. + */ +struct ib_uverbs_sge { + __aligned_u64 addr; + __u32 length; + __u32 lkey; +}; + +enum ib_uverbs_wr_opcode { + IB_UVERBS_WR_RDMA_WRITE = 0, + IB_UVERBS_WR_RDMA_WRITE_WITH_IMM = 1, + IB_UVERBS_WR_SEND = 2, + IB_UVERBS_WR_SEND_WITH_IMM = 3, + IB_UVERBS_WR_RDMA_READ = 4, + IB_UVERBS_WR_ATOMIC_CMP_AND_SWP = 5, + IB_UVERBS_WR_ATOMIC_FETCH_AND_ADD = 6, + IB_UVERBS_WR_LOCAL_INV = 7, + IB_UVERBS_WR_BIND_MW = 8, + IB_UVERBS_WR_SEND_WITH_INV = 9, + IB_UVERBS_WR_TSO = 10, + IB_UVERBS_WR_RDMA_READ_WITH_INV = 11, + IB_UVERBS_WR_MASKED_ATOMIC_CMP_AND_SWP = 12, + IB_UVERBS_WR_MASKED_ATOMIC_FETCH_AND_ADD = 13, + IB_UVERBS_WR_FLUSH = 14, + IB_UVERBS_WR_ATOMIC_WRITE = 15, + /* Review enum ib_wr_opcode before modifying this */ +}; + +struct ib_uverbs_send_wr { + __aligned_u64 wr_id; + __u32 num_sge; + __u32 opcode; /* see enum ib_uverbs_wr_opcode */ + __u32 send_flags; + union { + __be32 imm_data; + __u32 invalidate_rkey; + } ex; + union { + struct { + __aligned_u64 remote_addr; + __u32 rkey; + __u32 reserved; + } rdma; + struct { + __aligned_u64 remote_addr; + __aligned_u64 compare_add; + __aligned_u64 swap; + __u32 rkey; + __u32 reserved; + } atomic; + struct { + __u32 ah; + __u32 remote_qpn; + __u32 remote_qkey; + __u32 reserved; + } ud; + } wr; +}; + +struct ib_uverbs_post_send { + __aligned_u64 response; + __u32 qp_handle; + __u32 wr_count; + __u32 sge_count; + __u32 wqe_size; + struct ib_uverbs_send_wr send_wr[]; +}; + +struct ib_uverbs_post_send_resp { + __u32 bad_wr; +}; + +struct ib_uverbs_recv_wr { + __aligned_u64 wr_id; + __u32 num_sge; + __u32 reserved; +}; + +struct ib_uverbs_post_recv { + __aligned_u64 response; + __u32 qp_handle; + __u32 wr_count; + __u32 sge_count; + __u32 wqe_size; + struct ib_uverbs_recv_wr recv_wr[]; +}; + +struct ib_uverbs_post_recv_resp { + __u32 bad_wr; +}; + +struct ib_uverbs_post_srq_recv { + __aligned_u64 response; + __u32 srq_handle; + __u32 wr_count; + __u32 sge_count; + __u32 wqe_size; + struct ib_uverbs_recv_wr recv[]; +}; + +struct ib_uverbs_post_srq_recv_resp { + __u32 bad_wr; +}; + +struct ib_uverbs_create_ah { + __aligned_u64 response; + __aligned_u64 user_handle; + __u32 pd_handle; + __u32 reserved; + struct ib_uverbs_ah_attr attr; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_create_ah_resp { + __u32 ah_handle; + __u32 driver_data[]; +}; + +struct ib_uverbs_destroy_ah { + __u32 ah_handle; +}; + +struct ib_uverbs_attach_mcast { + __u8 gid[16]; + __u32 qp_handle; + __u16 mlid; + __u16 reserved; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_detach_mcast { + __u8 gid[16]; + __u32 qp_handle; + __u16 mlid; + __u16 reserved; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_flow_spec_hdr { + __u32 type; + __u16 size; + __u16 reserved; + /* followed by flow_spec */ + __aligned_u64 flow_spec_data[0]; +}; + +struct ib_uverbs_flow_eth_filter { + __u8 dst_mac[6]; + __u8 src_mac[6]; + __be16 ether_type; + __be16 vlan_tag; +}; + +struct ib_uverbs_flow_spec_eth { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + struct ib_uverbs_flow_eth_filter val; + struct ib_uverbs_flow_eth_filter mask; +}; + +struct ib_uverbs_flow_ipv4_filter { + __be32 src_ip; + __be32 dst_ip; + __u8 proto; + __u8 tos; + __u8 ttl; + __u8 flags; +}; + +struct ib_uverbs_flow_spec_ipv4 { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + struct ib_uverbs_flow_ipv4_filter val; + struct ib_uverbs_flow_ipv4_filter mask; +}; + +struct ib_uverbs_flow_tcp_udp_filter { + __be16 dst_port; + __be16 src_port; +}; + +struct ib_uverbs_flow_spec_tcp_udp { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + struct ib_uverbs_flow_tcp_udp_filter val; + struct ib_uverbs_flow_tcp_udp_filter mask; +}; + +struct ib_uverbs_flow_ipv6_filter { + __u8 src_ip[16]; + __u8 dst_ip[16]; + __be32 flow_label; + __u8 next_hdr; + __u8 traffic_class; + __u8 hop_limit; + __u8 reserved; +}; + +struct ib_uverbs_flow_spec_ipv6 { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + struct ib_uverbs_flow_ipv6_filter val; + struct ib_uverbs_flow_ipv6_filter mask; +}; + +struct ib_uverbs_flow_spec_action_tag { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + __u32 tag_id; + __u32 reserved1; +}; + +struct ib_uverbs_flow_spec_action_drop { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; +}; + +struct ib_uverbs_flow_spec_action_handle { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + __u32 handle; + __u32 reserved1; +}; + +struct ib_uverbs_flow_spec_action_count { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + __u32 handle; + __u32 reserved1; +}; + +struct ib_uverbs_flow_tunnel_filter { + __be32 tunnel_id; +}; + +struct ib_uverbs_flow_spec_tunnel { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + struct ib_uverbs_flow_tunnel_filter val; + struct ib_uverbs_flow_tunnel_filter mask; +}; + +struct ib_uverbs_flow_spec_esp_filter { + __u32 spi; + __u32 seq; +}; + +struct ib_uverbs_flow_spec_esp { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + struct ib_uverbs_flow_spec_esp_filter val; + struct ib_uverbs_flow_spec_esp_filter mask; +}; + +struct ib_uverbs_flow_gre_filter { + /* c_ks_res0_ver field is bits 0-15 in offset 0 of a standard GRE header: + * bit 0 - C - checksum bit. + * bit 1 - reserved. set to 0. + * bit 2 - key bit. + * bit 3 - sequence number bit. + * bits 4:12 - reserved. set to 0. + * bits 13:15 - GRE version. + */ + __be16 c_ks_res0_ver; + __be16 protocol; + __be32 key; +}; + +struct ib_uverbs_flow_spec_gre { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + struct ib_uverbs_flow_gre_filter val; + struct ib_uverbs_flow_gre_filter mask; +}; + +struct ib_uverbs_flow_mpls_filter { + /* The field includes the entire MPLS label: + * bits 0:19 - label field. + * bits 20:22 - traffic class field. + * bits 23 - bottom of stack bit. + * bits 24:31 - ttl field. + */ + __be32 label; +}; + +struct ib_uverbs_flow_spec_mpls { + union { + struct ib_uverbs_flow_spec_hdr hdr; + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + }; + struct ib_uverbs_flow_mpls_filter val; + struct ib_uverbs_flow_mpls_filter mask; +}; + +struct ib_uverbs_flow_attr { + __u32 type; + __u16 size; + __u16 priority; + __u8 num_of_specs; + __u8 reserved[2]; + __u8 port; + __u32 flags; + /* Following are the optional layers according to user request + * struct ib_flow_spec_xxx + * struct ib_flow_spec_yyy + */ + struct ib_uverbs_flow_spec_hdr flow_specs[]; +}; + +struct ib_uverbs_create_flow { + __u32 comp_mask; + __u32 qp_handle; + struct ib_uverbs_flow_attr flow_attr; +}; + +struct ib_uverbs_create_flow_resp { + __u32 comp_mask; + __u32 flow_handle; +}; + +struct ib_uverbs_destroy_flow { + __u32 comp_mask; + __u32 flow_handle; +}; + +struct ib_uverbs_create_srq { + __aligned_u64 response; + __aligned_u64 user_handle; + __u32 pd_handle; + __u32 max_wr; + __u32 max_sge; + __u32 srq_limit; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_create_xsrq { + __aligned_u64 response; + __aligned_u64 user_handle; + __u32 srq_type; + __u32 pd_handle; + __u32 max_wr; + __u32 max_sge; + __u32 srq_limit; + __u32 max_num_tags; + __u32 xrcd_handle; + __u32 cq_handle; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_create_srq_resp { + __u32 srq_handle; + __u32 max_wr; + __u32 max_sge; + __u32 srqn; + __u32 driver_data[]; +}; + +struct ib_uverbs_modify_srq { + __u32 srq_handle; + __u32 attr_mask; + __u32 max_wr; + __u32 srq_limit; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_query_srq { + __aligned_u64 response; + __u32 srq_handle; + __u32 reserved; + __aligned_u64 driver_data[]; +}; + +struct ib_uverbs_query_srq_resp { + __u32 max_wr; + __u32 max_sge; + __u32 srq_limit; + __u32 reserved; +}; + +struct ib_uverbs_destroy_srq { + __aligned_u64 response; + __u32 srq_handle; + __u32 reserved; +}; + +struct ib_uverbs_destroy_srq_resp { + __u32 events_reported; +}; + +struct ib_uverbs_ex_create_wq { + __u32 comp_mask; + __u32 wq_type; + __aligned_u64 user_handle; + __u32 pd_handle; + __u32 cq_handle; + __u32 max_wr; + __u32 max_sge; + __u32 create_flags; /* Use enum ib_wq_flags */ + __u32 reserved; +}; + +struct ib_uverbs_ex_create_wq_resp { + __u32 comp_mask; + __u32 response_length; + __u32 wq_handle; + __u32 max_wr; + __u32 max_sge; + __u32 wqn; +}; + +struct ib_uverbs_ex_destroy_wq { + __u32 comp_mask; + __u32 wq_handle; +}; + +struct ib_uverbs_ex_destroy_wq_resp { + __u32 comp_mask; + __u32 response_length; + __u32 events_reported; + __u32 reserved; +}; + +struct ib_uverbs_ex_modify_wq { + __u32 attr_mask; + __u32 wq_handle; + __u32 wq_state; + __u32 curr_wq_state; + __u32 flags; /* Use enum ib_wq_flags */ + __u32 flags_mask; /* Use enum ib_wq_flags */ +}; + +/* Prevent memory allocation rather than max expected size */ +#define IB_USER_VERBS_MAX_LOG_IND_TBL_SIZE 0x0d +struct ib_uverbs_ex_create_rwq_ind_table { + __u32 comp_mask; + __u32 log_ind_tbl_size; + /* Following are the wq handles according to log_ind_tbl_size + * wq_handle1 + * wq_handle2 + */ + __u32 wq_handles[]; +}; + +struct ib_uverbs_ex_create_rwq_ind_table_resp { + __u32 comp_mask; + __u32 response_length; + __u32 ind_tbl_handle; + __u32 ind_tbl_num; +}; + +struct ib_uverbs_ex_destroy_rwq_ind_table { + __u32 comp_mask; + __u32 ind_tbl_handle; +}; + +struct ib_uverbs_cq_moderation { + __u16 cq_count; + __u16 cq_period; +}; + +struct ib_uverbs_ex_modify_cq { + __u32 cq_handle; + __u32 attr_mask; + struct ib_uverbs_cq_moderation attr; + __u32 reserved; +}; + +#define IB_DEVICE_NAME_MAX 64 + +/* + * bits 9, 15, 16, 19, 22, 27, 30, 31, 32, 33, 35 and 37 may be set by old + * kernels and should not be used. + */ +enum ib_uverbs_device_cap_flags { + IB_UVERBS_DEVICE_RESIZE_MAX_WR = 1 << 0, + IB_UVERBS_DEVICE_BAD_PKEY_CNTR = 1 << 1, + IB_UVERBS_DEVICE_BAD_QKEY_CNTR = 1 << 2, + IB_UVERBS_DEVICE_RAW_MULTI = 1 << 3, + IB_UVERBS_DEVICE_AUTO_PATH_MIG = 1 << 4, + IB_UVERBS_DEVICE_CHANGE_PHY_PORT = 1 << 5, + IB_UVERBS_DEVICE_UD_AV_PORT_ENFORCE = 1 << 6, + IB_UVERBS_DEVICE_CURR_QP_STATE_MOD = 1 << 7, + IB_UVERBS_DEVICE_SHUTDOWN_PORT = 1 << 8, + /* IB_UVERBS_DEVICE_INIT_TYPE = 1 << 9, (not in use) */ + IB_UVERBS_DEVICE_PORT_ACTIVE_EVENT = 1 << 10, + IB_UVERBS_DEVICE_SYS_IMAGE_GUID = 1 << 11, + IB_UVERBS_DEVICE_RC_RNR_NAK_GEN = 1 << 12, + IB_UVERBS_DEVICE_SRQ_RESIZE = 1 << 13, + IB_UVERBS_DEVICE_N_NOTIFY_CQ = 1 << 14, + IB_UVERBS_DEVICE_MEM_WINDOW = 1 << 17, + IB_UVERBS_DEVICE_UD_IP_CSUM = 1 << 18, + IB_UVERBS_DEVICE_XRC = 1 << 20, + IB_UVERBS_DEVICE_MEM_MGT_EXTENSIONS = 1 << 21, + IB_UVERBS_DEVICE_MEM_WINDOW_TYPE_2A = 1 << 23, + IB_UVERBS_DEVICE_MEM_WINDOW_TYPE_2B = 1 << 24, + IB_UVERBS_DEVICE_RC_IP_CSUM = 1 << 25, + /* Deprecated. Please use IB_UVERBS_RAW_PACKET_CAP_IP_CSUM. */ + IB_UVERBS_DEVICE_RAW_IP_CSUM = 1 << 26, + IB_UVERBS_DEVICE_MANAGED_FLOW_STEERING = 1 << 29, + /* Deprecated. Please use IB_UVERBS_RAW_PACKET_CAP_SCATTER_FCS. */ + IB_UVERBS_DEVICE_RAW_SCATTER_FCS = 1ULL << 34, + IB_UVERBS_DEVICE_PCI_WRITE_END_PADDING = 1ULL << 36, + /* Flush placement types */ + IB_UVERBS_DEVICE_FLUSH_GLOBAL = 1ULL << 38, + IB_UVERBS_DEVICE_FLUSH_PERSISTENT = 1ULL << 39, + /* Atomic write attributes */ + IB_UVERBS_DEVICE_ATOMIC_WRITE = 1ULL << 40, +}; + +enum ib_uverbs_raw_packet_caps { + IB_UVERBS_RAW_PACKET_CAP_CVLAN_STRIPPING = 1 << 0, + IB_UVERBS_RAW_PACKET_CAP_SCATTER_FCS = 1 << 1, + IB_UVERBS_RAW_PACKET_CAP_IP_CSUM = 1 << 2, + IB_UVERBS_RAW_PACKET_CAP_DELAY_DROP = 1 << 3, +}; + +#endif /* IB_USER_VERBS_H */ diff --git a/rdma/include/uapi/rdma/rdma_netlink.h b/rdma/include/uapi/rdma/rdma_netlink.h new file mode 100644 index 0000000..e8861b5 --- /dev/null +++ b/rdma/include/uapi/rdma/rdma_netlink.h @@ -0,0 +1,599 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _RDMA_NETLINK_H +#define _RDMA_NETLINK_H + +#include <linux/types.h> + +enum { + RDMA_NL_IWCM = 2, + RDMA_NL_RSVD, + RDMA_NL_LS, /* RDMA Local Services */ + RDMA_NL_NLDEV, /* RDMA device interface */ + RDMA_NL_NUM_CLIENTS +}; + +enum { + RDMA_NL_GROUP_IWPM = 2, + RDMA_NL_GROUP_LS, + RDMA_NL_NUM_GROUPS +}; + +#define RDMA_NL_GET_CLIENT(type) ((type & (((1 << 6) - 1) << 10)) >> 10) +#define RDMA_NL_GET_OP(type) (type & ((1 << 10) - 1)) +#define RDMA_NL_GET_TYPE(client, op) ((client << 10) + op) + +/* The minimum version that the iwpm kernel supports */ +#define IWPM_UABI_VERSION_MIN 3 + +/* The latest version that the iwpm kernel supports */ +#define IWPM_UABI_VERSION 4 + +/* iwarp port mapper message flags */ +enum { + + /* Do not map the port for this IWPM request */ + IWPM_FLAGS_NO_PORT_MAP = (1 << 0), +}; + +/* iwarp port mapper op-codes */ +enum { + RDMA_NL_IWPM_REG_PID = 0, + RDMA_NL_IWPM_ADD_MAPPING, + RDMA_NL_IWPM_QUERY_MAPPING, + RDMA_NL_IWPM_REMOVE_MAPPING, + RDMA_NL_IWPM_REMOTE_INFO, + RDMA_NL_IWPM_HANDLE_ERR, + RDMA_NL_IWPM_MAPINFO, + RDMA_NL_IWPM_MAPINFO_NUM, + RDMA_NL_IWPM_HELLO, + RDMA_NL_IWPM_NUM_OPS +}; + +enum { + IWPM_NLA_REG_PID_UNSPEC = 0, + IWPM_NLA_REG_PID_SEQ, + IWPM_NLA_REG_IF_NAME, + IWPM_NLA_REG_IBDEV_NAME, + IWPM_NLA_REG_ULIB_NAME, + IWPM_NLA_REG_PID_MAX +}; + +enum { + IWPM_NLA_RREG_PID_UNSPEC = 0, + IWPM_NLA_RREG_PID_SEQ, + IWPM_NLA_RREG_IBDEV_NAME, + IWPM_NLA_RREG_ULIB_NAME, + IWPM_NLA_RREG_ULIB_VER, + IWPM_NLA_RREG_PID_ERR, + IWPM_NLA_RREG_PID_MAX + +}; + +enum { + IWPM_NLA_MANAGE_MAPPING_UNSPEC = 0, + IWPM_NLA_MANAGE_MAPPING_SEQ, + IWPM_NLA_MANAGE_ADDR, + IWPM_NLA_MANAGE_FLAGS, + IWPM_NLA_MANAGE_MAPPING_MAX +}; + +enum { + IWPM_NLA_RMANAGE_MAPPING_UNSPEC = 0, + IWPM_NLA_RMANAGE_MAPPING_SEQ, + IWPM_NLA_RMANAGE_ADDR, + IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR, + /* The following maintains bisectability of rdma-core */ + IWPM_NLA_MANAGE_MAPPED_LOC_ADDR = IWPM_NLA_RMANAGE_MAPPED_LOC_ADDR, + IWPM_NLA_RMANAGE_MAPPING_ERR, + IWPM_NLA_RMANAGE_MAPPING_MAX +}; + +#define IWPM_NLA_MAPINFO_SEND_MAX 3 +#define IWPM_NLA_REMOVE_MAPPING_MAX 3 + +enum { + IWPM_NLA_QUERY_MAPPING_UNSPEC = 0, + IWPM_NLA_QUERY_MAPPING_SEQ, + IWPM_NLA_QUERY_LOCAL_ADDR, + IWPM_NLA_QUERY_REMOTE_ADDR, + IWPM_NLA_QUERY_FLAGS, + IWPM_NLA_QUERY_MAPPING_MAX, +}; + +enum { + IWPM_NLA_RQUERY_MAPPING_UNSPEC = 0, + IWPM_NLA_RQUERY_MAPPING_SEQ, + IWPM_NLA_RQUERY_LOCAL_ADDR, + IWPM_NLA_RQUERY_REMOTE_ADDR, + IWPM_NLA_RQUERY_MAPPED_LOC_ADDR, + IWPM_NLA_RQUERY_MAPPED_REM_ADDR, + IWPM_NLA_RQUERY_MAPPING_ERR, + IWPM_NLA_RQUERY_MAPPING_MAX +}; + +enum { + IWPM_NLA_MAPINFO_REQ_UNSPEC = 0, + IWPM_NLA_MAPINFO_ULIB_NAME, + IWPM_NLA_MAPINFO_ULIB_VER, + IWPM_NLA_MAPINFO_REQ_MAX +}; + +enum { + IWPM_NLA_MAPINFO_UNSPEC = 0, + IWPM_NLA_MAPINFO_LOCAL_ADDR, + IWPM_NLA_MAPINFO_MAPPED_ADDR, + IWPM_NLA_MAPINFO_FLAGS, + IWPM_NLA_MAPINFO_MAX +}; + +enum { + IWPM_NLA_MAPINFO_NUM_UNSPEC = 0, + IWPM_NLA_MAPINFO_SEQ, + IWPM_NLA_MAPINFO_SEND_NUM, + IWPM_NLA_MAPINFO_ACK_NUM, + IWPM_NLA_MAPINFO_NUM_MAX +}; + +enum { + IWPM_NLA_ERR_UNSPEC = 0, + IWPM_NLA_ERR_SEQ, + IWPM_NLA_ERR_CODE, + IWPM_NLA_ERR_MAX +}; + +enum { + IWPM_NLA_HELLO_UNSPEC = 0, + IWPM_NLA_HELLO_ABI_VERSION, + IWPM_NLA_HELLO_MAX +}; + +/* For RDMA_NLDEV_ATTR_DEV_NODE_TYPE */ +enum { + /* IB values map to NodeInfo:NodeType. */ + RDMA_NODE_IB_CA = 1, + RDMA_NODE_IB_SWITCH, + RDMA_NODE_IB_ROUTER, + RDMA_NODE_RNIC, + RDMA_NODE_USNIC, + RDMA_NODE_USNIC_UDP, + RDMA_NODE_UNSPECIFIED, +}; + +/* + * Local service operations: + * RESOLVE - The client requests the local service to resolve a path. + * SET_TIMEOUT - The local service requests the client to set the timeout. + * IP_RESOLVE - The client requests the local service to resolve an IP to GID. + */ +enum { + RDMA_NL_LS_OP_RESOLVE = 0, + RDMA_NL_LS_OP_SET_TIMEOUT, + RDMA_NL_LS_OP_IP_RESOLVE, + RDMA_NL_LS_NUM_OPS +}; + +/* Local service netlink message flags */ +#define RDMA_NL_LS_F_ERR 0x0100 /* Failed response */ + +/* + * Local service resolve operation family header. + * The layout for the resolve operation: + * nlmsg header + * family header + * attributes + */ + +/* + * Local service path use: + * Specify how the path(s) will be used. + * ALL - For connected CM operation (6 pathrecords) + * UNIDIRECTIONAL - For unidirectional UD (1 pathrecord) + * GMP - For miscellaneous GMP like operation (at least 1 reversible + * pathrecord) + */ +enum { + LS_RESOLVE_PATH_USE_ALL = 0, + LS_RESOLVE_PATH_USE_UNIDIRECTIONAL, + LS_RESOLVE_PATH_USE_GMP, + LS_RESOLVE_PATH_USE_MAX +}; + +#define LS_DEVICE_NAME_MAX 64 + +struct rdma_ls_resolve_header { + __u8 device_name[LS_DEVICE_NAME_MAX]; + __u8 port_num; + __u8 path_use; +}; + +struct rdma_ls_ip_resolve_header { + __u32 ifindex; +}; + +/* Local service attribute type */ +#define RDMA_NLA_F_MANDATORY (1 << 13) +#define RDMA_NLA_TYPE_MASK (~(NLA_F_NESTED | NLA_F_NET_BYTEORDER | \ + RDMA_NLA_F_MANDATORY)) + +/* + * Local service attributes: + * Attr Name Size Byte order + * ----------------------------------------------------- + * PATH_RECORD struct ib_path_rec_data + * TIMEOUT u32 cpu + * SERVICE_ID u64 cpu + * DGID u8[16] BE + * SGID u8[16] BE + * TCLASS u8 + * PKEY u16 cpu + * QOS_CLASS u16 cpu + * IPV4 u32 BE + * IPV6 u8[16] BE + */ +enum { + LS_NLA_TYPE_UNSPEC = 0, + LS_NLA_TYPE_PATH_RECORD, + LS_NLA_TYPE_TIMEOUT, + LS_NLA_TYPE_SERVICE_ID, + LS_NLA_TYPE_DGID, + LS_NLA_TYPE_SGID, + LS_NLA_TYPE_TCLASS, + LS_NLA_TYPE_PKEY, + LS_NLA_TYPE_QOS_CLASS, + LS_NLA_TYPE_IPV4, + LS_NLA_TYPE_IPV6, + LS_NLA_TYPE_MAX +}; + +/* Local service DGID/SGID attribute: big endian */ +struct rdma_nla_ls_gid { + __u8 gid[16]; +}; + +enum rdma_nldev_command { + RDMA_NLDEV_CMD_UNSPEC, + + RDMA_NLDEV_CMD_GET, /* can dump */ + RDMA_NLDEV_CMD_SET, + + RDMA_NLDEV_CMD_NEWLINK, + + RDMA_NLDEV_CMD_DELLINK, + + RDMA_NLDEV_CMD_PORT_GET, /* can dump */ + + RDMA_NLDEV_CMD_SYS_GET, + RDMA_NLDEV_CMD_SYS_SET, + + /* 8 is free to use */ + + RDMA_NLDEV_CMD_RES_GET = 9, /* can dump */ + + RDMA_NLDEV_CMD_RES_QP_GET, /* can dump */ + + RDMA_NLDEV_CMD_RES_CM_ID_GET, /* can dump */ + + RDMA_NLDEV_CMD_RES_CQ_GET, /* can dump */ + + RDMA_NLDEV_CMD_RES_MR_GET, /* can dump */ + + RDMA_NLDEV_CMD_RES_PD_GET, /* can dump */ + + RDMA_NLDEV_CMD_GET_CHARDEV, + + RDMA_NLDEV_CMD_STAT_SET, + + RDMA_NLDEV_CMD_STAT_GET, /* can dump */ + + RDMA_NLDEV_CMD_STAT_DEL, + + RDMA_NLDEV_CMD_RES_QP_GET_RAW, + + RDMA_NLDEV_CMD_RES_CQ_GET_RAW, + + RDMA_NLDEV_CMD_RES_MR_GET_RAW, + + RDMA_NLDEV_CMD_RES_CTX_GET, /* can dump */ + + RDMA_NLDEV_CMD_RES_SRQ_GET, /* can dump */ + + RDMA_NLDEV_CMD_STAT_GET_STATUS, + + RDMA_NLDEV_CMD_RES_SRQ_GET_RAW, + + RDMA_NLDEV_NUM_OPS +}; + +enum rdma_nldev_print_type { + RDMA_NLDEV_PRINT_TYPE_UNSPEC, + RDMA_NLDEV_PRINT_TYPE_HEX, +}; + +enum rdma_nldev_attr { + /* don't change the order or add anything between, this is ABI! */ + RDMA_NLDEV_ATTR_UNSPEC, + + /* Pad attribute for 64b alignment */ + RDMA_NLDEV_ATTR_PAD = RDMA_NLDEV_ATTR_UNSPEC, + + /* Identifier for ib_device */ + RDMA_NLDEV_ATTR_DEV_INDEX, /* u32 */ + + RDMA_NLDEV_ATTR_DEV_NAME, /* string */ + /* + * Device index together with port index are identifiers + * for port/link properties. + * + * For RDMA_NLDEV_CMD_GET commamnd, port index will return number + * of available ports in ib_device, while for port specific operations, + * it will be real port index as it appears in sysfs. Port index follows + * sysfs notation and starts from 1 for the first port. + */ + RDMA_NLDEV_ATTR_PORT_INDEX, /* u32 */ + + /* + * Device and port capabilities + * + * When used for port info, first 32-bits are CapabilityMask followed by + * 16-bit CapabilityMask2. + */ + RDMA_NLDEV_ATTR_CAP_FLAGS, /* u64 */ + + /* + * FW version + */ + RDMA_NLDEV_ATTR_FW_VERSION, /* string */ + + /* + * Node GUID (in host byte order) associated with the RDMA device. + */ + RDMA_NLDEV_ATTR_NODE_GUID, /* u64 */ + + /* + * System image GUID (in host byte order) associated with + * this RDMA device and other devices which are part of a + * single system. + */ + RDMA_NLDEV_ATTR_SYS_IMAGE_GUID, /* u64 */ + + /* + * Subnet prefix (in host byte order) + */ + RDMA_NLDEV_ATTR_SUBNET_PREFIX, /* u64 */ + + /* + * Local Identifier (LID), + * According to IB specification, It is 16-bit address assigned + * by the Subnet Manager. Extended to be 32-bit for OmniPath users. + */ + RDMA_NLDEV_ATTR_LID, /* u32 */ + RDMA_NLDEV_ATTR_SM_LID, /* u32 */ + + /* + * LID mask control (LMC) + */ + RDMA_NLDEV_ATTR_LMC, /* u8 */ + + RDMA_NLDEV_ATTR_PORT_STATE, /* u8 */ + RDMA_NLDEV_ATTR_PORT_PHYS_STATE, /* u8 */ + + RDMA_NLDEV_ATTR_DEV_NODE_TYPE, /* u8 */ + + RDMA_NLDEV_ATTR_RES_SUMMARY, /* nested table */ + RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME, /* string */ + RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR, /* u64 */ + + RDMA_NLDEV_ATTR_RES_QP, /* nested table */ + RDMA_NLDEV_ATTR_RES_QP_ENTRY, /* nested table */ + /* + * Local QPN + */ + RDMA_NLDEV_ATTR_RES_LQPN, /* u32 */ + /* + * Remote QPN, + * Applicable for RC and UC only IBTA 11.2.5.3 QUERY QUEUE PAIR + */ + RDMA_NLDEV_ATTR_RES_RQPN, /* u32 */ + /* + * Receive Queue PSN, + * Applicable for RC and UC only 11.2.5.3 QUERY QUEUE PAIR + */ + RDMA_NLDEV_ATTR_RES_RQ_PSN, /* u32 */ + /* + * Send Queue PSN + */ + RDMA_NLDEV_ATTR_RES_SQ_PSN, /* u32 */ + RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE, /* u8 */ + /* + * QP types as visible to RDMA/core, the reserved QPT + * are not exported through this interface. + */ + RDMA_NLDEV_ATTR_RES_TYPE, /* u8 */ + RDMA_NLDEV_ATTR_RES_STATE, /* u8 */ + /* + * Process ID which created object, + * in case of kernel origin, PID won't exist. + */ + RDMA_NLDEV_ATTR_RES_PID, /* u32 */ + /* + * The name of process created following resource. + * It will exist only for kernel objects. + * For user created objects, the user is supposed + * to read /proc/PID/comm file. + */ + RDMA_NLDEV_ATTR_RES_KERN_NAME, /* string */ + + RDMA_NLDEV_ATTR_RES_CM_ID, /* nested table */ + RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY, /* nested table */ + /* + * rdma_cm_id port space. + */ + RDMA_NLDEV_ATTR_RES_PS, /* u32 */ + /* + * Source and destination socket addresses + */ + RDMA_NLDEV_ATTR_RES_SRC_ADDR, /* __kernel_sockaddr_storage */ + RDMA_NLDEV_ATTR_RES_DST_ADDR, /* __kernel_sockaddr_storage */ + + RDMA_NLDEV_ATTR_RES_CQ, /* nested table */ + RDMA_NLDEV_ATTR_RES_CQ_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_RES_CQE, /* u32 */ + RDMA_NLDEV_ATTR_RES_USECNT, /* u64 */ + RDMA_NLDEV_ATTR_RES_POLL_CTX, /* u8 */ + + RDMA_NLDEV_ATTR_RES_MR, /* nested table */ + RDMA_NLDEV_ATTR_RES_MR_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_RES_RKEY, /* u32 */ + RDMA_NLDEV_ATTR_RES_LKEY, /* u32 */ + RDMA_NLDEV_ATTR_RES_IOVA, /* u64 */ + RDMA_NLDEV_ATTR_RES_MRLEN, /* u64 */ + + RDMA_NLDEV_ATTR_RES_PD, /* nested table */ + RDMA_NLDEV_ATTR_RES_PD_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY, /* u32 */ + RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY, /* u32 */ + /* + * Provides logical name and index of netdevice which is + * connected to physical port. This information is relevant + * for RoCE and iWARP. + * + * The netdevices which are associated with containers are + * supposed to be exported together with GID table once it + * will be exposed through the netlink. Because the + * associated netdevices are properties of GIDs. + */ + RDMA_NLDEV_ATTR_NDEV_INDEX, /* u32 */ + RDMA_NLDEV_ATTR_NDEV_NAME, /* string */ + /* + * driver-specific attributes. + */ + RDMA_NLDEV_ATTR_DRIVER, /* nested table */ + RDMA_NLDEV_ATTR_DRIVER_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_DRIVER_STRING, /* string */ + /* + * u8 values from enum rdma_nldev_print_type + */ + RDMA_NLDEV_ATTR_DRIVER_PRINT_TYPE, /* u8 */ + RDMA_NLDEV_ATTR_DRIVER_S32, /* s32 */ + RDMA_NLDEV_ATTR_DRIVER_U32, /* u32 */ + RDMA_NLDEV_ATTR_DRIVER_S64, /* s64 */ + RDMA_NLDEV_ATTR_DRIVER_U64, /* u64 */ + + /* + * Indexes to get/set secific entry, + * for QP use RDMA_NLDEV_ATTR_RES_LQPN + */ + RDMA_NLDEV_ATTR_RES_PDN, /* u32 */ + RDMA_NLDEV_ATTR_RES_CQN, /* u32 */ + RDMA_NLDEV_ATTR_RES_MRN, /* u32 */ + RDMA_NLDEV_ATTR_RES_CM_IDN, /* u32 */ + RDMA_NLDEV_ATTR_RES_CTXN, /* u32 */ + /* + * Identifies the rdma driver. eg: "rxe" or "siw" + */ + RDMA_NLDEV_ATTR_LINK_TYPE, /* string */ + + /* + * net namespace mode for rdma subsystem: + * either shared or exclusive among multiple net namespaces. + */ + RDMA_NLDEV_SYS_ATTR_NETNS_MODE, /* u8 */ + /* + * Device protocol, e.g. ib, iw, usnic, roce and opa + */ + RDMA_NLDEV_ATTR_DEV_PROTOCOL, /* string */ + + /* + * File descriptor handle of the net namespace object + */ + RDMA_NLDEV_NET_NS_FD, /* u32 */ + /* + * Information about a chardev. + * CHARDEV_TYPE is the name of the chardev ABI (ie uverbs, umad, etc) + * CHARDEV_ABI signals the ABI revision (historical) + * CHARDEV_NAME is the kernel name for the /dev/ file (no directory) + * CHARDEV is the 64 bit dev_t for the inode + */ + RDMA_NLDEV_ATTR_CHARDEV_TYPE, /* string */ + RDMA_NLDEV_ATTR_CHARDEV_NAME, /* string */ + RDMA_NLDEV_ATTR_CHARDEV_ABI, /* u64 */ + RDMA_NLDEV_ATTR_CHARDEV, /* u64 */ + RDMA_NLDEV_ATTR_UVERBS_DRIVER_ID, /* u64 */ + /* + * Counter-specific attributes. + */ + RDMA_NLDEV_ATTR_STAT_MODE, /* u32 */ + RDMA_NLDEV_ATTR_STAT_RES, /* u32 */ + RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, /* u32 */ + RDMA_NLDEV_ATTR_STAT_COUNTER, /* nested table */ + RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_STAT_COUNTER_ID, /* u32 */ + RDMA_NLDEV_ATTR_STAT_HWCOUNTERS, /* nested table */ + RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME, /* string */ + RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE, /* u64 */ + + /* + * CQ adaptive moderatio (DIM) + */ + RDMA_NLDEV_ATTR_DEV_DIM, /* u8 */ + + RDMA_NLDEV_ATTR_RES_RAW, /* binary */ + + RDMA_NLDEV_ATTR_RES_CTX, /* nested table */ + RDMA_NLDEV_ATTR_RES_CTX_ENTRY, /* nested table */ + + RDMA_NLDEV_ATTR_RES_SRQ, /* nested table */ + RDMA_NLDEV_ATTR_RES_SRQ_ENTRY, /* nested table */ + RDMA_NLDEV_ATTR_RES_SRQN, /* u32 */ + + RDMA_NLDEV_ATTR_MIN_RANGE, /* u32 */ + RDMA_NLDEV_ATTR_MAX_RANGE, /* u32 */ + + RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK, /* u8 */ + + RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX, /* u32 */ + RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC, /* u8 */ + + RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE, /* u8 */ + + /* + * Always the end + */ + RDMA_NLDEV_ATTR_MAX +}; + +/* + * Supported counter bind modes. All modes are mutual-exclusive. + */ +enum rdma_nl_counter_mode { + RDMA_COUNTER_MODE_NONE, + + /* + * A qp is bound with a counter automatically during initialization + * based on the auto mode (e.g., qp type, ...) + */ + RDMA_COUNTER_MODE_AUTO, + + /* + * Which qp are bound with which counter is explicitly specified + * by the user + */ + RDMA_COUNTER_MODE_MANUAL, + + /* + * Always the end + */ + RDMA_COUNTER_MODE_MAX, +}; + +/* + * Supported criteria in counter auto mode. + * Currently only "qp type" is supported + */ +enum rdma_nl_counter_mask { + RDMA_COUNTER_MASK_QP_TYPE = 1, + RDMA_COUNTER_MASK_PID = 1 << 1, +}; +#endif /* _RDMA_NETLINK_H */ diff --git a/rdma/include/uapi/rdma/rdma_user_cm.h b/rdma/include/uapi/rdma/rdma_user_cm.h new file mode 100644 index 0000000..7cea035 --- /dev/null +++ b/rdma/include/uapi/rdma/rdma_user_cm.h @@ -0,0 +1,341 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR Linux-OpenIB) */ +/* + * Copyright (c) 2005-2006 Intel Corporation. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RDMA_USER_CM_H +#define RDMA_USER_CM_H + +#include <linux/types.h> +#include <linux/socket.h> +#include <linux/in6.h> +#include <rdma/ib_user_verbs.h> +#include <rdma/ib_user_sa.h> + +#define RDMA_USER_CM_ABI_VERSION 4 + +#define RDMA_MAX_PRIVATE_DATA 256 + +enum { + RDMA_USER_CM_CMD_CREATE_ID, + RDMA_USER_CM_CMD_DESTROY_ID, + RDMA_USER_CM_CMD_BIND_IP, + RDMA_USER_CM_CMD_RESOLVE_IP, + RDMA_USER_CM_CMD_RESOLVE_ROUTE, + RDMA_USER_CM_CMD_QUERY_ROUTE, + RDMA_USER_CM_CMD_CONNECT, + RDMA_USER_CM_CMD_LISTEN, + RDMA_USER_CM_CMD_ACCEPT, + RDMA_USER_CM_CMD_REJECT, + RDMA_USER_CM_CMD_DISCONNECT, + RDMA_USER_CM_CMD_INIT_QP_ATTR, + RDMA_USER_CM_CMD_GET_EVENT, + RDMA_USER_CM_CMD_GET_OPTION, + RDMA_USER_CM_CMD_SET_OPTION, + RDMA_USER_CM_CMD_NOTIFY, + RDMA_USER_CM_CMD_JOIN_IP_MCAST, + RDMA_USER_CM_CMD_LEAVE_MCAST, + RDMA_USER_CM_CMD_MIGRATE_ID, + RDMA_USER_CM_CMD_QUERY, + RDMA_USER_CM_CMD_BIND, + RDMA_USER_CM_CMD_RESOLVE_ADDR, + RDMA_USER_CM_CMD_JOIN_MCAST +}; + +/* See IBTA Annex A11, servies ID bytes 4 & 5 */ +enum rdma_ucm_port_space { + RDMA_PS_IPOIB = 0x0002, + RDMA_PS_IB = 0x013F, + RDMA_PS_TCP = 0x0106, + RDMA_PS_UDP = 0x0111, +}; + +/* + * command ABI structures. + */ +struct rdma_ucm_cmd_hdr { + __u32 cmd; + __u16 in; + __u16 out; +}; + +struct rdma_ucm_create_id { + __aligned_u64 uid; + __aligned_u64 response; + __u16 ps; /* use enum rdma_ucm_port_space */ + __u8 qp_type; + __u8 reserved[5]; +}; + +struct rdma_ucm_create_id_resp { + __u32 id; +}; + +struct rdma_ucm_destroy_id { + __aligned_u64 response; + __u32 id; + __u32 reserved; +}; + +struct rdma_ucm_destroy_id_resp { + __u32 events_reported; +}; + +struct rdma_ucm_bind_ip { + __aligned_u64 response; + struct sockaddr_in6 addr; + __u32 id; +}; + +struct rdma_ucm_bind { + __u32 id; + __u16 addr_size; + __u16 reserved; + struct __kernel_sockaddr_storage addr; +}; + +struct rdma_ucm_resolve_ip { + struct sockaddr_in6 src_addr; + struct sockaddr_in6 dst_addr; + __u32 id; + __u32 timeout_ms; +}; + +struct rdma_ucm_resolve_addr { + __u32 id; + __u32 timeout_ms; + __u16 src_size; + __u16 dst_size; + __u32 reserved; + struct __kernel_sockaddr_storage src_addr; + struct __kernel_sockaddr_storage dst_addr; +}; + +struct rdma_ucm_resolve_route { + __u32 id; + __u32 timeout_ms; +}; + +enum { + RDMA_USER_CM_QUERY_ADDR, + RDMA_USER_CM_QUERY_PATH, + RDMA_USER_CM_QUERY_GID +}; + +struct rdma_ucm_query { + __aligned_u64 response; + __u32 id; + __u32 option; +}; + +struct rdma_ucm_query_route_resp { + __aligned_u64 node_guid; + struct ib_user_path_rec ib_route[2]; + struct sockaddr_in6 src_addr; + struct sockaddr_in6 dst_addr; + __u32 num_paths; + __u8 port_num; + __u8 reserved[3]; + __u32 ibdev_index; + __u32 reserved1; +}; + +struct rdma_ucm_query_addr_resp { + __aligned_u64 node_guid; + __u8 port_num; + __u8 reserved; + __u16 pkey; + __u16 src_size; + __u16 dst_size; + struct __kernel_sockaddr_storage src_addr; + struct __kernel_sockaddr_storage dst_addr; + __u32 ibdev_index; + __u32 reserved1; +}; + +struct rdma_ucm_query_path_resp { + __u32 num_paths; + __u32 reserved; + struct ib_path_rec_data path_data[]; +}; + +struct rdma_ucm_conn_param { + __u32 qp_num; + __u32 qkey; + __u8 private_data[RDMA_MAX_PRIVATE_DATA]; + __u8 private_data_len; + __u8 srq; + __u8 responder_resources; + __u8 initiator_depth; + __u8 flow_control; + __u8 retry_count; + __u8 rnr_retry_count; + __u8 valid; +}; + +struct rdma_ucm_ud_param { + __u32 qp_num; + __u32 qkey; + struct ib_uverbs_ah_attr ah_attr; + __u8 private_data[RDMA_MAX_PRIVATE_DATA]; + __u8 private_data_len; + __u8 reserved[7]; +}; + +struct rdma_ucm_ece { + __u32 vendor_id; + __u32 attr_mod; +}; + +struct rdma_ucm_connect { + struct rdma_ucm_conn_param conn_param; + __u32 id; + __u32 reserved; + struct rdma_ucm_ece ece; +}; + +struct rdma_ucm_listen { + __u32 id; + __u32 backlog; +}; + +struct rdma_ucm_accept { + __aligned_u64 uid; + struct rdma_ucm_conn_param conn_param; + __u32 id; + __u32 reserved; + struct rdma_ucm_ece ece; +}; + +struct rdma_ucm_reject { + __u32 id; + __u8 private_data_len; + __u8 reason; + __u8 reserved[2]; + __u8 private_data[RDMA_MAX_PRIVATE_DATA]; +}; + +struct rdma_ucm_disconnect { + __u32 id; +}; + +struct rdma_ucm_init_qp_attr { + __aligned_u64 response; + __u32 id; + __u32 qp_state; +}; + +struct rdma_ucm_notify { + __u32 id; + __u32 event; +}; + +struct rdma_ucm_join_ip_mcast { + __aligned_u64 response; /* rdma_ucm_create_id_resp */ + __aligned_u64 uid; + struct sockaddr_in6 addr; + __u32 id; +}; + +/* Multicast join flags */ +enum { + RDMA_MC_JOIN_FLAG_FULLMEMBER, + RDMA_MC_JOIN_FLAG_SENDONLY_FULLMEMBER, + RDMA_MC_JOIN_FLAG_RESERVED, +}; + +struct rdma_ucm_join_mcast { + __aligned_u64 response; /* rdma_ucma_create_id_resp */ + __aligned_u64 uid; + __u32 id; + __u16 addr_size; + __u16 join_flags; + struct __kernel_sockaddr_storage addr; +}; + +struct rdma_ucm_get_event { + __aligned_u64 response; +}; + +struct rdma_ucm_event_resp { + __aligned_u64 uid; + __u32 id; + __u32 event; + __u32 status; + /* + * NOTE: This union is not aligned to 8 bytes so none of the union + * members may contain a u64 or anything with higher alignment than 4. + */ + union { + struct rdma_ucm_conn_param conn; + struct rdma_ucm_ud_param ud; + } param; + __u32 reserved; + struct rdma_ucm_ece ece; +}; + +/* Option levels */ +enum { + RDMA_OPTION_ID = 0, + RDMA_OPTION_IB = 1 +}; + +/* Option details */ +enum { + RDMA_OPTION_ID_TOS = 0, + RDMA_OPTION_ID_REUSEADDR = 1, + RDMA_OPTION_ID_AFONLY = 2, + RDMA_OPTION_ID_ACK_TIMEOUT = 3 +}; + +enum { + RDMA_OPTION_IB_PATH = 1 +}; + +struct rdma_ucm_set_option { + __aligned_u64 optval; + __u32 id; + __u32 level; + __u32 optname; + __u32 optlen; +}; + +struct rdma_ucm_migrate_id { + __aligned_u64 response; + __u32 id; + __u32 fd; +}; + +struct rdma_ucm_migrate_resp { + __u32 events_reported; +}; + +#endif /* RDMA_USER_CM_H */ diff --git a/rdma/link.c b/rdma/link.c new file mode 100644 index 0000000..8531817 --- /dev/null +++ b/rdma/link.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * link.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "rdma.h" + +static int link_help(struct rd *rd) +{ + pr_out("Usage: %s link show [DEV/PORT_INDEX]\n", rd->filename); + pr_out("Usage: %s link add NAME type TYPE netdev NETDEV\n", + rd->filename); + pr_out("Usage: %s link delete NAME\n", rd->filename); + return 0; +} + +static const char *caps_to_str(uint32_t idx) +{ +#define RDMA_PORT_FLAGS_LOW(x) \ + x(RESERVED, 0) \ + x(SM, 1) \ + x(NOTICE, 2) \ + x(TRAP, 3) \ + x(OPT_IPD, 4) \ + x(AUTO_MIGR, 5) \ + x(SL_MAP, 6) \ + x(MKEY_NVRAM, 7) \ + x(PKEY_NVRAM, 8) \ + x(LED_INFO, 9) \ + x(SM_DISABLED, 10) \ + x(SYS_IMAGE_GUID, 11) \ + x(PKEY_SW_EXT_PORT_TRAP, 12) \ + x(CABLE_INFO, 13) \ + x(EXTENDED_SPEEDS, 14) \ + x(CAP_MASK2, 15) \ + x(CM, 16) \ + x(SNMP_TUNNEL, 17) \ + x(REINIT, 18) \ + x(DEVICE_MGMT, 19) \ + x(VENDOR_CLASS, 20) \ + x(DR_NOTICE, 21) \ + x(CAP_MASK_NOTICE, 22) \ + x(BOOT_MGMT, 23) \ + x(LINK_LATENCY, 24) \ + x(CLIENT_REG, 25) \ + x(OTHER_LOCAL_CHANGES, 26) \ + x(LINK_SPPED_WIDTH, 27) \ + x(VENDOR_SPECIFIC_MADS, 28) \ + x(MULT_PKER_TRAP, 29) \ + x(MULT_FDB, 30) \ + x(HIERARCHY_INFO, 31) + +#define RDMA_PORT_FLAGS_HIGH(x) \ + x(SET_NODE_DESC, 0) \ + x(EXT_INFO, 1) \ + x(VIRT, 2) \ + x(SWITCH_POR_STATE_TABLE, 3) \ + x(LINK_WIDTH_2X, 4) \ + x(LINK_SPEED_HDR, 5) + + /* + * Separation below is needed to allow compilation of rdmatool + * on 32bits systems. On such systems, C-enum is limited to be + * int and can't hold more than 32 bits. + */ + enum { RDMA_PORT_FLAGS_LOW(RDMA_BITMAP_ENUM) }; + enum { RDMA_PORT_FLAGS_HIGH(RDMA_BITMAP_ENUM) }; + + static const char * const + rdma_port_names_low[] = { RDMA_PORT_FLAGS_LOW(RDMA_BITMAP_NAMES) }; + static const char * const + rdma_port_names_high[] = { RDMA_PORT_FLAGS_HIGH(RDMA_BITMAP_NAMES) }; + uint32_t high_idx; + #undef RDMA_PORT_FLAGS_LOW + #undef RDMA_PORT_FLAGS_HIGH + + if (idx < ARRAY_SIZE(rdma_port_names_low) && rdma_port_names_low[idx]) + return rdma_port_names_low[idx]; + + high_idx = idx - ARRAY_SIZE(rdma_port_names_low); + if (high_idx < ARRAY_SIZE(rdma_port_names_high) && + rdma_port_names_high[high_idx]) + return rdma_port_names_high[high_idx]; + + return "UNKNOWN"; +} + +static void link_print_caps(struct nlattr **tb) +{ + uint64_t caps; + uint32_t idx; + + if (!tb[RDMA_NLDEV_ATTR_CAP_FLAGS]) + return; + + caps = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_CAP_FLAGS]); + + print_string(PRINT_FP, NULL, "%s caps: <", _SL_); + open_json_array(PRINT_JSON, "caps"); + for (idx = 0; caps; idx++) { + if (caps & 0x1) + print_string(PRINT_ANY, NULL, + caps >> 0x1 ? "%s, " : "%s", + caps_to_str(idx)); + caps >>= 0x1; + } + close_json_array(PRINT_ANY, ">"); +} + +static void link_print_subnet_prefix(struct nlattr **tb) +{ + uint64_t subnet_prefix; + uint16_t vp[4]; + char str[32]; + + if (!tb[RDMA_NLDEV_ATTR_SUBNET_PREFIX]) + return; + + subnet_prefix = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_SUBNET_PREFIX]); + memcpy(vp, &subnet_prefix, sizeof(uint64_t)); + snprintf(str, 32, "%04x:%04x:%04x:%04x", vp[3], vp[2], vp[1], vp[0]); + print_string(PRINT_ANY, "subnet_prefix", "subnet_prefix %s ", str); +} + +static void link_print_lid(struct nlattr **tb) +{ + uint32_t lid; + + if (!tb[RDMA_NLDEV_ATTR_LID]) + return; + + lid = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_LID]); + print_uint(PRINT_ANY, "lid", "lid %u ", lid); +} + +static void link_print_sm_lid(struct nlattr **tb) +{ + uint32_t sm_lid; + + if (!tb[RDMA_NLDEV_ATTR_SM_LID]) + return; + + sm_lid = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_SM_LID]); + print_uint(PRINT_ANY, "sm_lid", "sm_lid %u ", sm_lid); +} + +static void link_print_lmc(struct nlattr **tb) +{ + uint8_t lmc; + + if (!tb[RDMA_NLDEV_ATTR_LMC]) + return; + + lmc = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_LMC]); + print_uint(PRINT_ANY, "lmc", "lmc %u ", lmc); +} + +static const char *link_state_to_str(uint8_t link_state) +{ + static const char * const link_state_str[] = { "NOP", "DOWN", + "INIT", "ARMED", + "ACTIVE", + "ACTIVE_DEFER" }; + if (link_state < ARRAY_SIZE(link_state_str)) + return link_state_str[link_state]; + return "UNKNOWN"; +} + +static void link_print_state(struct nlattr **tb) +{ + uint8_t state; + + if (!tb[RDMA_NLDEV_ATTR_PORT_STATE]) + return; + + state = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_PORT_STATE]); + print_string(PRINT_ANY, "state", "state %s ", link_state_to_str(state)); +} + +static const char *phys_state_to_str(uint8_t phys_state) +{ + static const char * const phys_state_str[] = { "NOP", "SLEEP", + "POLLING", "DISABLED", + "ARMED", "LINK_UP", + "LINK_ERROR_RECOVER", + "PHY_TEST", "UNKNOWN", + "OPA_OFFLINE", + "UNKNOWN", "OPA_TEST" }; + if (phys_state < ARRAY_SIZE(phys_state_str)) + return phys_state_str[phys_state]; + return "UNKNOWN"; +}; + +static void link_print_phys_state(struct nlattr **tb) +{ + uint8_t phys_state; + + if (!tb[RDMA_NLDEV_ATTR_PORT_PHYS_STATE]) + return; + + phys_state = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_PORT_PHYS_STATE]); + print_string(PRINT_ANY, "physical_state", "physical_state %s ", + phys_state_to_str(phys_state)); +} + +static void link_print_netdev(const struct rd * rd, struct nlattr **tb) +{ + const char *netdev_name; + uint32_t idx; + + if (!tb[RDMA_NLDEV_ATTR_NDEV_NAME] || !tb[RDMA_NLDEV_ATTR_NDEV_INDEX]) + return; + + netdev_name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_NDEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_NDEV_INDEX]); + print_string(PRINT_ANY, "netdev", "netdev %s ", netdev_name); + print_uint(PRINT_ANY, "netdev_index", rd->show_details ? "netdev_index %u " : "", idx); +} + +static int link_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + uint32_t port, idx; + 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]); + + open_json_object(NULL); + print_uint(PRINT_JSON, "ifindex", NULL, idx); + print_string(PRINT_ANY, "ifname", "link %s/", name); + print_uint(PRINT_ANY, "port", "%u ", port); + link_print_subnet_prefix(tb); + link_print_lid(tb); + link_print_sm_lid(tb); + link_print_lmc(tb); + link_print_state(tb); + link_print_phys_state(tb); + link_print_netdev(rd, tb); + if (rd->show_details) + link_print_caps(tb); + + close_json_object(); + newline(); + + return MNL_CB_OK; +} + +static int link_no_args(struct rd *rd) +{ + uint32_t seq; + int ret; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_PORT_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); + ret = rd_send_msg(rd); + if (ret) + return ret; + + ret = rd_recv_msg(rd, link_parse_cb, rd, seq); + return ret; +} + +static int link_one_show(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, link_no_args}, + { 0 } + }; + + if (!rd->port_idx) + return 0; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int link_show(struct rd *rd) +{ + return rd_exec_link(rd, link_one_show, true); +} + +static int link_add_netdev(struct rd *rd) +{ + char *link_netdev; + uint32_t seq; + + if (rd_no_arg(rd)) { + pr_err("Please provide a net device name.\n"); + return -EINVAL; + } + + link_netdev = rd_argv(rd); + rd_prepare_msg(rd, RDMA_NLDEV_CMD_NEWLINK, &seq, + (NLM_F_REQUEST | NLM_F_ACK)); + mnl_attr_put_strz(rd->nlh, RDMA_NLDEV_ATTR_DEV_NAME, rd->link_name); + mnl_attr_put_strz(rd->nlh, RDMA_NLDEV_ATTR_LINK_TYPE, rd->link_type); + mnl_attr_put_strz(rd->nlh, RDMA_NLDEV_ATTR_NDEV_NAME, link_netdev); + return rd_sendrecv_msg(rd, seq); +} + +static int link_add_type(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, link_help}, + { "netdev", link_add_netdev}, + { 0 } + }; + + if (rd_no_arg(rd)) { + pr_err("Please provide a link type name.\n"); + return -EINVAL; + } + rd->link_type = rd_argv(rd); + rd_arg_inc(rd); + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int link_add(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, link_help}, + { "type", link_add_type}, + { 0 } + }; + + if (rd_no_arg(rd)) { + pr_err("Please provide a link name to add.\n"); + return -EINVAL; + } + rd->link_name = rd_argv(rd); + rd_arg_inc(rd); + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int _link_del(struct rd *rd) +{ + uint32_t seq; + + if (!rd_no_arg(rd)) { + pr_err("Unknown parameter %s\n", rd_argv(rd)); + return -EINVAL; + } + rd_prepare_msg(rd, RDMA_NLDEV_CMD_DELLINK, &seq, + (NLM_F_REQUEST | NLM_F_ACK)); + mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); + return rd_sendrecv_msg(rd, seq); +} + +static int link_del(struct rd *rd) +{ + return rd_exec_require_dev(rd, _link_del); +} + +int cmd_link(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, link_show }, + { "add", link_add }, + { "delete", link_del }, + { "show", link_show }, + { "list", link_show }, + { "help", link_help }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "link command"); +} diff --git a/rdma/rdma.c b/rdma/rdma.c new file mode 100644 index 0000000..131c6b2 --- /dev/null +++ b/rdma/rdma.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * rdma.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "rdma.h" +#include "version.h" +#include "color.h" + +/* Global utils flags */ +int json; + +static void help(char *name) +{ + pr_out("Usage: %s [ OPTIONS ] OBJECT { COMMAND | help }\n" + " %s [ -f[orce] ] -b[atch] filename\n" + "where OBJECT := { dev | link | resource | system | statistic | help }\n" + " OPTIONS := { -V[ersion] | -d[etails] | -j[son] | -p[retty] | -r[aw]}\n", name, name); +} + +static int cmd_help(struct rd *rd) +{ + help(rd->filename); + return 0; +} + +static int rd_cmd(struct rd *rd, int argc, char **argv) +{ + const struct rd_cmd cmds[] = { + { NULL, cmd_help }, + { "help", cmd_help }, + { "dev", cmd_dev }, + { "link", cmd_link }, + { "resource", cmd_res }, + { "system", cmd_sys }, + { "statistic", cmd_stat }, + { 0 } + }; + + rd->argc = argc; + rd->argv = argv; + + return rd_exec_cmd(rd, cmds, "object"); +} + +static int rd_batch_cmd(int argc, char *argv[], void *data) +{ + struct rd *rd = data; + + return rd_cmd(rd, argc, argv); +} + +static int rd_batch(struct rd *rd, const char *name, bool force) +{ + return do_batch(name, force, rd_batch_cmd, rd); +} + +static int rd_init(struct rd *rd, char *filename) +{ + uint32_t seq; + int ret; + + rd->filename = filename; + INIT_LIST_HEAD(&rd->dev_map_list); + INIT_LIST_HEAD(&rd->filter_list); + + rd->buff = malloc(MNL_SOCKET_BUFFER_SIZE); + if (!rd->buff) + return -ENOMEM; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_GET, + &seq, (NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP)); + ret = rd_send_msg(rd); + if (ret) + return ret; + + return rd_recv_msg(rd, rd_dev_init_cb, rd, seq); +} + +static void rd_cleanup(struct rd *rd) +{ + rd_free(rd); +} + +int main(int argc, char **argv) +{ + static const struct option long_options[] = { + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { "json", no_argument, NULL, 'j' }, + { "oneline", no_argument, NULL, 'o' }, + { "pretty", no_argument, NULL, 'p' }, + { "details", no_argument, NULL, 'd' }, + { "raw", no_argument, NULL, 'r' }, + { "force", no_argument, NULL, 'f' }, + { "batch", required_argument, NULL, 'b' }, + { NULL, 0, NULL, 0 } + }; + bool show_driver_details = false; + const char *batch_file = NULL; + bool show_details = false; + bool show_raw = false; + bool force = false; + bool oneline = false; + struct rd rd = {}; + char *filename; + int opt; + int err; + filename = basename(argv[0]); + + while ((opt = getopt_long(argc, argv, ":Vhdropjfb:", + long_options, NULL)) >= 0) { + switch (opt) { + case 'V': + printf("%s utility, iproute2-%s\n", + filename, version); + return EXIT_SUCCESS; + case 'p': + ++pretty; + break; + case 'd': + if (show_details) + show_driver_details = true; + else + show_details = true; + break; + case 'r': + show_raw = true; + break; + case 'o': + oneline = true; + break; + case 'j': + ++json; + break; + case 'f': + force = true; + break; + case 'b': + batch_file = optarg; + break; + case 'h': + help(filename); + return EXIT_SUCCESS; + case ':': + pr_err("-%c option requires an argument\n", optopt); + return EXIT_FAILURE; + default: + pr_err("Unknown option.\n"); + help(filename); + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + _SL_ = oneline ? "\\" : "\n"; + + rd.show_details = show_details; + rd.show_driver_details = show_driver_details; + rd.show_raw = show_raw; + + err = rd_init(&rd, filename); + if (err) + goto out; + + if (batch_file) + err = rd_batch(&rd, batch_file, force); + else + err = rd_cmd(&rd, argc, argv); +out: + /* Always cleanup */ + rd_cleanup(&rd); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/rdma/rdma.h b/rdma/rdma.h new file mode 100644 index 0000000..df1852d --- /dev/null +++ b/rdma/rdma.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* + * rdma.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ +#ifndef _RDMA_TOOL_H_ +#define _RDMA_TOOL_H_ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> +#include <netinet/in.h> +#include <libmnl/libmnl.h> +#include <rdma/rdma_netlink.h> +#include <rdma/rdma_user_cm.h> +#include <time.h> +#include <net/if_arp.h> + +#include "list.h" +#include "utils.h" +#include "mnl_utils.h" +#include "json_print.h" + +#define pr_err(args...) fprintf(stderr, ##args) +#define pr_out(args...) fprintf(stdout, ##args) + +#define RDMA_BITMAP_ENUM(name, bit_no) RDMA_BITMAP_##name = BIT(bit_no), +#define RDMA_BITMAP_NAMES(name, bit_no) [bit_no] = #name, + +#define MAX_NUMBER_OF_FILTERS 64 +struct filters { + const char *name; + uint8_t is_number:1; + uint8_t is_doit:1; +}; + +struct filter_entry { + struct list_head list; + char *key; + char *value; + /* + * This field means that we can try to issue .doit callback + * on value above. This value can be converted to integer + * with simple atoi(). Otherwise "is_doit" will be false. + */ + uint8_t is_doit:1; +}; + +struct dev_map { + struct list_head list; + char *dev_name; + uint32_t num_ports; + uint32_t idx; +}; + +struct rd { + int argc; + char **argv; + char *filename; + uint8_t show_details:1; + uint8_t show_driver_details:1; + uint8_t show_raw:1; + uint8_t suppress_errors:1; + struct list_head dev_map_list; + uint32_t dev_idx; + uint32_t port_idx; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + char *buff; + json_writer_t *jw; + struct list_head filter_list; + char *link_name; + char *link_type; +}; + +struct rd_cmd { + const char *cmd; + int (*func)(struct rd *rd); +}; + +/* + * Parser interface + */ +bool rd_no_arg(struct rd *rd); +bool rd_is_multiarg(struct rd *rd); +void rd_arg_inc(struct rd *rd); + +char *rd_argv(struct rd *rd); + +/* + * Commands interface + */ +int cmd_dev(struct rd *rd); +int cmd_link(struct rd *rd); +int cmd_res(struct rd *rd); +int cmd_sys(struct rd *rd); +int cmd_stat(struct rd *rd); +int rd_exec_cmd(struct rd *rd, const struct rd_cmd *c, const char *str); +int rd_exec_dev(struct rd *rd, int (*cb)(struct rd *rd)); +int rd_exec_require_dev(struct rd *rd, int (*cb)(struct rd *rd)); +int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd), bool strict_port); +void rd_free(struct rd *rd); +int rd_set_arg_to_devname(struct rd *rd); +int rd_argc(struct rd *rd); + +int strcmpx(const char *str1, const char *str2); + +/* + * Device manipulation + */ +struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index); + +/* + * Filter manipulation + */ +bool rd_doit_index(struct rd *rd, uint32_t *idx); +int rd_build_filter(struct rd *rd, const struct filters valid_filters[]); +bool rd_is_filtered_attr(struct rd *rd, const char *key, uint32_t val, + struct nlattr *attr); +bool rd_is_string_filtered_attr(struct rd *rd, const char *key, const char *val, + struct nlattr *attr); +/* + * Netlink + */ +int rd_send_msg(struct rd *rd); +int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, uint32_t seq); +int rd_sendrecv_msg(struct rd *rd, unsigned int seq); +void rd_prepare_msg(struct rd *rd, uint32_t cmd, uint32_t *seq, uint16_t flags); +int rd_dev_init_cb(const struct nlmsghdr *nlh, void *data); +int rd_attr_cb(const struct nlattr *attr, void *data); + +/* + * Print helpers + */ +void print_driver_table(struct rd *rd, struct nlattr *tb); +void print_raw_data(struct rd *rd, struct nlattr **nla_line); +void newline_indent(void); +void newline(void); + +#define MAX_LINE_LENGTH 80 + + +#endif /* _RDMA_TOOL_H_ */ diff --git a/rdma/res-cmid.c b/rdma/res-cmid.c new file mode 100644 index 0000000..17a89cc --- /dev/null +++ b/rdma/res-cmid.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * res-cmid.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "res.h" +#include <inttypes.h> + +static const char *cm_id_state_to_str(uint8_t idx) +{ + static const char *const cm_id_states_str[] = { + "IDLE", "ADDR_QUERY", "ADDR_RESOLVED", + "ROUTE_QUERY", "ROUTE_RESOLVED", "CONNECT", + "DISCONNECT", "ADDR_BOUND", "LISTEN", + "DEVICE_REMOVAL", "DESTROYING" + }; + + if (idx < ARRAY_SIZE(cm_id_states_str)) + return cm_id_states_str[idx]; + return "UNKNOWN"; +} + +static const char *cm_id_ps_to_str(uint32_t ps) +{ + switch (ps) { + case RDMA_PS_IPOIB: + return "IPoIB"; + case RDMA_PS_IB: + return "IPoIB"; + case RDMA_PS_TCP: + return "TCP"; + case RDMA_PS_UDP: + return "UDP"; + default: + return "---"; + } +} + +static void print_cm_id_state(uint8_t state) +{ + print_string(PRINT_ANY, "state", "state %s ", cm_id_state_to_str(state)); +} + +static void print_ps(uint32_t ps) +{ + print_string(PRINT_ANY, "ps", "ps %s ", cm_id_ps_to_str(ps)); +} + +static void print_ipaddr(const char *key, char *addrstr, uint16_t port) +{ + int name_size = INET6_ADDRSTRLEN + strlen(":65535"); + char json_name[name_size]; + + snprintf(json_name, name_size, "%s:%u", addrstr, port); + print_string(PRINT_ANY, key, key, json_name); + print_string(PRINT_FP, NULL, " %s:", addrstr); + print_uint(PRINT_FP, NULL, "%u ", port); +} + +static int ss_ntop(struct nlattr *nla_line, char *addr_str, uint16_t *port) +{ + struct __kernel_sockaddr_storage *addr; + + addr = (struct __kernel_sockaddr_storage *)mnl_attr_get_payload( + nla_line); + switch (addr->ss_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + + if (!inet_ntop(AF_INET, (const void *)&sin->sin_addr, addr_str, + INET6_ADDRSTRLEN)) + return -EINVAL; + *port = ntohs(sin->sin_port); + break; + } + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; + + if (!inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr, + addr_str, INET6_ADDRSTRLEN)) + return -EINVAL; + *port = ntohs(sin6->sin6_port); + break; + } + default: + return -EINVAL; + } + return 0; +} +static int res_cm_id_line(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + char src_addr_str[INET6_ADDRSTRLEN]; + char dst_addr_str[INET6_ADDRSTRLEN]; + uint16_t src_port, dst_port; + uint32_t port = 0, pid = 0; + uint8_t type = 0, state; + uint32_t lqpn = 0, ps; + uint32_t cm_idn = 0; + char *comm = NULL; + SPRINT_BUF(b); + + if (!nla_line[RDMA_NLDEV_ATTR_RES_STATE] || + !nla_line[RDMA_NLDEV_ATTR_RES_PS]) + return MNL_CB_ERROR; + + if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) + port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]); + + if (port && port != rd->port_idx) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_LQPN]) + lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]); + + if (rd_is_filtered_attr(rd, "lqpn", lqpn, + nla_line[RDMA_NLDEV_ATTR_RES_LQPN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE]) + type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]); + if (rd_is_string_filtered_attr(rd, "qp-type", qp_types_to_str(type), + nla_line[RDMA_NLDEV_ATTR_RES_TYPE])) + goto out; + + ps = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PS]); + if (rd_is_string_filtered_attr(rd, "ps", cm_id_ps_to_str(ps), + nla_line[RDMA_NLDEV_ATTR_RES_PS])) + goto out; + + state = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_STATE]); + if (rd_is_string_filtered_attr(rd, "state", cm_id_state_to_str(state), + nla_line[RDMA_NLDEV_ATTR_RES_STATE])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR]) + if (ss_ntop(nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR], + src_addr_str, &src_port)) + goto out; + if (rd_is_string_filtered_attr(rd, "src-addr", src_addr_str, + nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR])) + goto out; + if (rd_is_filtered_attr(rd, "src-port", src_port, + nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_DST_ADDR]) + if (ss_ntop(nla_line[RDMA_NLDEV_ATTR_RES_DST_ADDR], + dst_addr_str, &dst_port)) + goto out; + if (rd_is_string_filtered_attr(rd, "dst-addr", dst_addr_str, + nla_line[RDMA_NLDEV_ATTR_RES_DST_ADDR])) + goto out; + if (rd_is_filtered_attr(rd, "dst-port", dst_port, + nla_line[RDMA_NLDEV_ATTR_RES_DST_ADDR])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + if (!get_task_name(pid, b, sizeof(b))) + comm = b; + } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str( + nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + } + + if (rd_is_filtered_attr(rd, "pid", pid, + nla_line[RDMA_NLDEV_ATTR_RES_PID])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_CM_IDN]) + cm_idn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_CM_IDN]); + if (rd_is_filtered_attr(rd, "cm-idn", cm_idn, + nla_line[RDMA_NLDEV_ATTR_RES_CM_IDN])) + goto out; + + open_json_object(NULL); + print_link(idx, name, port, nla_line); + res_print_u32("cm-idn", cm_idn, nla_line[RDMA_NLDEV_ATTR_RES_CM_IDN]); + res_print_u32("lqpn", lqpn, nla_line[RDMA_NLDEV_ATTR_RES_LQPN]); + if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE]) + print_qp_type(type); + print_cm_id_state(state); + print_ps(ps); + res_print_u32("pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); + print_comm(comm, nla_line); + + if (nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR]) + print_ipaddr("src-addr", src_addr_str, src_port); + if (nla_line[RDMA_NLDEV_ATTR_RES_DST_ADDR]) + print_ipaddr("dst-addr", dst_addr_str, dst_port); + + print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]); + close_json_object(); + newline(); + +out: + return MNL_CB_OK; +} + +int res_cm_id_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + int idx; + + 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; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + + return res_cm_id_line(rd, name, idx, tb); +} + +int res_cm_id_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; + int ret = MNL_CB_OK; + const char *name; + int idx; + + 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_RES_CM_ID]) + 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_RES_CM_ID]; + + 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_cm_id_line(rd, name, idx, nla_line); + if (ret != MNL_CB_OK) + break; + } + return ret; +} diff --git a/rdma/res-cq.c b/rdma/res-cq.c new file mode 100644 index 0000000..0cab3fe --- /dev/null +++ b/rdma/res-cq.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * res-cq.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "res.h" +#include <inttypes.h> + +static const char *poll_ctx_to_str(uint8_t idx) +{ + static const char * const cm_id_states_str[] = { + "DIRECT", "SOFTIRQ", "WORKQUEUE", "UNBOUND_WORKQUEUE"}; + + if (idx < ARRAY_SIZE(cm_id_states_str)) + return cm_id_states_str[idx]; + return "UNKNOWN"; +} + +static void print_poll_ctx(uint8_t poll_ctx, struct nlattr *attr) +{ + if (!attr) + return; + print_string(PRINT_ANY, "poll-ctx", "poll-ctx %s ", poll_ctx_to_str(poll_ctx)); +} + +static void print_cq_dim_setting(struct nlattr *attr) +{ + uint8_t dim_setting; + + if (!attr) + return; + + dim_setting = mnl_attr_get_u8(attr); + if (dim_setting > 1) + return; + + print_on_off(PRINT_ANY, "adaptive-moderation", "adaptive-moderation %s ", dim_setting); +} + +static int res_cq_line_raw(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_RAW]) + return MNL_CB_ERROR; + + open_json_object(NULL); + print_dev(idx, name); + print_raw_data(rd, nla_line); + close_json_object(); + newline(); + + return MNL_CB_OK; +} + +static int res_cq_line(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + char *comm = NULL; + uint32_t pid = 0; + uint8_t poll_ctx = 0; + uint32_t ctxn = 0; + uint32_t cqn = 0; + uint64_t users; + uint32_t cqe; + SPRINT_BUF(b); + + if (!nla_line[RDMA_NLDEV_ATTR_RES_CQE] || + !nla_line[RDMA_NLDEV_ATTR_RES_USECNT]) + return MNL_CB_ERROR; + + cqe = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_CQE]); + + users = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_USECNT]); + if (rd_is_filtered_attr(rd, "users", users, + nla_line[RDMA_NLDEV_ATTR_RES_USECNT])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX]) + poll_ctx = + mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX]); + if (rd_is_string_filtered_attr(rd, "poll-ctx", + poll_ctx_to_str(poll_ctx), + nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + if (!get_task_name(pid, b, sizeof(b))) + comm = b; + } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str( + nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + } + + if (rd_is_filtered_attr(rd, "pid", pid, + nla_line[RDMA_NLDEV_ATTR_RES_PID])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_CQN]) + cqn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_CQN]); + if (rd_is_filtered_attr(rd, "cqn", cqn, + nla_line[RDMA_NLDEV_ATTR_RES_CQN])) + goto out; + if (nla_line[RDMA_NLDEV_ATTR_RES_CTXN]) + ctxn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_CTXN]); + if (rd_is_filtered_attr(rd, "ctxn", ctxn, + nla_line[RDMA_NLDEV_ATTR_RES_CTXN])) + goto out; + + open_json_object(NULL); + print_dev(idx, name); + res_print_u32("cqn", cqn, nla_line[RDMA_NLDEV_ATTR_RES_CQN]); + res_print_u32("cqe", cqe, nla_line[RDMA_NLDEV_ATTR_RES_CQE]); + res_print_u64("users", users, nla_line[RDMA_NLDEV_ATTR_RES_USECNT]); + print_poll_ctx(poll_ctx, nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX]); + print_cq_dim_setting(nla_line[RDMA_NLDEV_ATTR_DEV_DIM]); + res_print_u32("ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]); + res_print_u32("pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); + print_comm(comm, nla_line); + + print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]); + close_json_object(); + newline(); + +out: + return MNL_CB_OK; +} + +int res_cq_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + uint32_t idx; + + 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; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + + return (rd->show_raw) ? res_cq_line_raw(rd, name, idx, tb) : + res_cq_line(rd, name, idx, tb); +} + +int res_cq_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; + int ret = MNL_CB_OK; + const char *name; + uint32_t idx; + + 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_RES_CQ]) + 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_RES_CQ]; + + 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 = (rd->show_raw) ? res_cq_line_raw(rd, name, idx, nla_line) : + res_cq_line(rd, name, idx, nla_line); + + if (ret != MNL_CB_OK) + break; + } + return ret; +} diff --git a/rdma/res-ctx.c b/rdma/res-ctx.c new file mode 100644 index 0000000..235c837 --- /dev/null +++ b/rdma/res-ctx.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * res-ctx.c RDMA tool + * Authors: Neta Ostrovsky <netao@nvidia.com> + */ + +#include "res.h" +#include <inttypes.h> + +static int res_ctx_line(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + char *comm = NULL; + uint32_t ctxn = 0; + uint32_t pid = 0; + SPRINT_BUF(b); + + if (!nla_line[RDMA_NLDEV_ATTR_RES_CTXN]) + return MNL_CB_ERROR; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + if (!get_task_name(pid, b, sizeof(b))) + comm = b; + } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str( + nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + } + + if (rd_is_filtered_attr(rd, "pid", pid, + nla_line[RDMA_NLDEV_ATTR_RES_PID])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_CTXN]) + ctxn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_CTXN]); + + if (rd_is_filtered_attr(rd, "ctxn", ctxn, + nla_line[RDMA_NLDEV_ATTR_RES_CTXN])) + goto out; + + open_json_object(NULL); + print_dev(idx, name); + res_print_u32("ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]); + res_print_u32("pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); + print_comm(comm, nla_line); + + print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]); + close_json_object(); + newline(); + +out: + return MNL_CB_OK; +} + +int res_ctx_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + uint32_t idx; + + 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; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + + return res_ctx_line(rd, name, idx, tb); +} + +int res_ctx_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; + int ret = MNL_CB_OK; + const char *name; + uint32_t idx; + + 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_RES_CTX]) + 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_RES_CTX]; + + 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_ctx_line(rd, name, idx, nla_line); + if (ret != MNL_CB_OK) + break; + } + return ret; +} diff --git a/rdma/res-mr.c b/rdma/res-mr.c new file mode 100644 index 0000000..f6c2534 --- /dev/null +++ b/rdma/res-mr.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * res-mr.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "res.h" +#include <inttypes.h> + +static int res_mr_line_raw(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_RAW]) + return MNL_CB_ERROR; + + open_json_object(NULL); + print_dev(idx, name); + print_raw_data(rd, nla_line); + close_json_object(); + newline(); + + return MNL_CB_OK; +} + +static int res_mr_line(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + uint32_t rkey = 0, lkey = 0; + uint64_t iova = 0, mrlen; + char *comm = NULL; + uint32_t pdn = 0; + uint32_t mrn = 0; + uint32_t pid = 0; + SPRINT_BUF(b); + + if (!nla_line[RDMA_NLDEV_ATTR_RES_MRLEN]) + return MNL_CB_ERROR; + + if (nla_line[RDMA_NLDEV_ATTR_RES_RKEY]) + rkey = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_RKEY]); + if (nla_line[RDMA_NLDEV_ATTR_RES_LKEY]) + lkey = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LKEY]); + if (nla_line[RDMA_NLDEV_ATTR_RES_IOVA]) + iova = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_IOVA]); + + mrlen = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_MRLEN]); + if (rd_is_filtered_attr(rd, "mrlen", mrlen, + nla_line[RDMA_NLDEV_ATTR_RES_MRLEN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + if (!get_task_name(pid, b, sizeof(b))) + comm = b; + } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str( + nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + } + + if (rd_is_filtered_attr(rd, "pid", pid, + nla_line[RDMA_NLDEV_ATTR_RES_PID])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_MRN]) + mrn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_MRN]); + if (rd_is_filtered_attr(rd, "mrn", mrn, + nla_line[RDMA_NLDEV_ATTR_RES_MRN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PDN]) + pdn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PDN]); + if (rd_is_filtered_attr(rd, "pdn", pdn, + nla_line[RDMA_NLDEV_ATTR_RES_PDN])) + goto out; + + open_json_object(NULL); + print_dev(idx, name); + res_print_u32("mrn", mrn, nla_line[RDMA_NLDEV_ATTR_RES_MRN]); + print_key("rkey", rkey, nla_line[RDMA_NLDEV_ATTR_RES_RKEY]); + print_key("lkey", lkey, nla_line[RDMA_NLDEV_ATTR_RES_LKEY]); + print_key("iova", iova, nla_line[RDMA_NLDEV_ATTR_RES_IOVA]); + res_print_u64("mrlen", mrlen, nla_line[RDMA_NLDEV_ATTR_RES_MRLEN]); + res_print_u32("pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]); + res_print_u32("pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); + print_comm(comm, nla_line); + + print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]); + print_raw_data(rd, nla_line); + close_json_object(); + newline(); + +out: + return MNL_CB_OK; +} + +int res_mr_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + uint32_t idx; + + 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; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + + return (rd->show_raw) ? res_mr_line_raw(rd, name, idx, tb) : + res_mr_line(rd, name, idx, tb); +} + +int res_mr_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; + int ret = MNL_CB_OK; + const char *name; + uint32_t idx; + + 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_RES_MR]) + 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_RES_MR]; + + 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 = (rd->show_raw) ? res_mr_line_raw(rd, name, idx, nla_line) : + res_mr_line(rd, name, idx, nla_line); + if (ret != MNL_CB_OK) + break; + } + return ret; +} diff --git a/rdma/res-pd.c b/rdma/res-pd.c new file mode 100644 index 0000000..8b9f7aa --- /dev/null +++ b/rdma/res-pd.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * res-pd.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "res.h" +#include <inttypes.h> + +static int res_pd_line(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + uint32_t local_dma_lkey = 0, unsafe_global_rkey = 0; + char *comm = NULL; + uint32_t ctxn = 0; + uint32_t pid = 0; + uint32_t pdn = 0; + uint64_t users; + SPRINT_BUF(b); + + if (!nla_line[RDMA_NLDEV_ATTR_RES_USECNT]) + return MNL_CB_ERROR; + + if (nla_line[RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY]) + local_dma_lkey = mnl_attr_get_u32( + nla_line[RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY]); + + users = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_USECNT]); + if (rd_is_filtered_attr(rd, "users", users, + nla_line[RDMA_NLDEV_ATTR_RES_USECNT])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY]) + unsafe_global_rkey = mnl_attr_get_u32( + nla_line[RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY]); + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + if (!get_task_name(pid, b, sizeof(b))) + comm = b; + } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str( + nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + } + + if (rd_is_filtered_attr(rd, "pid", pid, + nla_line[RDMA_NLDEV_ATTR_RES_PID])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_CTXN]) + ctxn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_CTXN]); + + if (rd_is_filtered_attr(rd, "ctxn", ctxn, + nla_line[RDMA_NLDEV_ATTR_RES_CTXN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PDN]) + pdn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PDN]); + if (rd_is_filtered_attr(rd, "pdn", pdn, + nla_line[RDMA_NLDEV_ATTR_RES_PDN])) + goto out; + + open_json_object(NULL); + print_dev(idx, name); + res_print_u32("pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]); + print_key("local_dma_lkey", local_dma_lkey, + nla_line[RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY]); + res_print_u64("users", users, nla_line[RDMA_NLDEV_ATTR_RES_USECNT]); + print_key("unsafe_global_rkey", unsafe_global_rkey, + nla_line[RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY]); + res_print_u32("ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]); + res_print_u32("pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); + print_comm(comm, nla_line); + + print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]); + close_json_object(); + newline(); +out: + return MNL_CB_OK; +} + +int res_pd_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + uint32_t idx; + + 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; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + + return res_pd_line(rd, name, idx, tb); +} + +int res_pd_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; + int ret = MNL_CB_OK; + const char *name; + uint32_t idx; + + 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_RES_PD]) + 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_RES_PD]; + + 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_pd_line(rd, name, idx, nla_line); + if (ret != MNL_CB_OK) + break; + } + return ret; +} diff --git a/rdma/res-qp.c b/rdma/res-qp.c new file mode 100644 index 0000000..65ff54a --- /dev/null +++ b/rdma/res-qp.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * res-qp.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "res.h" +#include <inttypes.h> + +static const char *path_mig_to_str(uint8_t idx) +{ + static const char *const path_mig_str[] = { "MIGRATED", "REARM", + "ARMED" }; + + if (idx < ARRAY_SIZE(path_mig_str)) + return path_mig_str[idx]; + return "UNKNOWN"; +} + +static const char *qp_states_to_str(uint8_t idx) +{ + static const char *const qp_states_str[] = { "RESET", "INIT", "RTR", + "RTS", "SQD", "SQE", + "ERR" }; + + if (idx < ARRAY_SIZE(qp_states_str)) + return qp_states_str[idx]; + return "UNKNOWN"; +} + +static void print_rqpn(uint32_t val, struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_RQPN]) + return; + print_uint(PRINT_ANY, "rqpn", "rqpn %d ", val); +} + +static void print_type(uint32_t val) +{ + print_string(PRINT_ANY, "type", "type %s ", qp_types_to_str(val)); +} + +static void print_state(uint32_t val) +{ + print_string(PRINT_ANY, "state", "state %s ", qp_states_to_str(val)); +} + +static void print_rqpsn(uint32_t val, struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]) + return; + + print_uint(PRINT_ANY, "rq-psn", "rq-psn %d ", val); +} + +static void print_pathmig(uint32_t val, struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]) + return; + + print_string(PRINT_ANY, "path-mig-state", "path-mig-state %s ", path_mig_to_str(val)); +} + +static int res_qp_line_raw(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_RAW]) + return MNL_CB_ERROR; + + open_json_object(NULL); + print_link(idx, name, rd->port_idx, nla_line); + print_raw_data(rd, nla_line); + close_json_object(); + newline(); + + return MNL_CB_OK; +} + +static int res_qp_line(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + uint32_t lqpn, rqpn = 0, rq_psn = 0, sq_psn; + uint8_t type, state, path_mig_state = 0; + uint32_t port = 0, pid = 0; + uint32_t pdn = 0; + char *comm = NULL; + SPRINT_BUF(b); + + if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN] || + !nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN] || + !nla_line[RDMA_NLDEV_ATTR_RES_TYPE] || + !nla_line[RDMA_NLDEV_ATTR_RES_STATE]) + return MNL_CB_ERROR; + + if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) + port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]); + + if (port != rd->port_idx) + goto out; + + lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]); + if (rd_is_filtered_attr(rd, "lqpn", lqpn, + nla_line[RDMA_NLDEV_ATTR_RES_LQPN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PDN]) + pdn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PDN]); + if (rd_is_filtered_attr(rd, "pdn", pdn, + nla_line[RDMA_NLDEV_ATTR_RES_PDN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_RQPN]) + rqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_RQPN]); + if (rd_is_filtered_attr(rd, "rqpn", rqpn, + nla_line[RDMA_NLDEV_ATTR_RES_RQPN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]) + rq_psn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]); + if (rd_is_filtered_attr(rd, "rq-psn", rq_psn, + nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN])) + goto out; + + sq_psn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN]); + if (rd_is_filtered_attr(rd, "sq-psn", sq_psn, + nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]) + path_mig_state = mnl_attr_get_u8( + nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]); + if (rd_is_string_filtered_attr( + rd, "path-mig-state", path_mig_to_str(path_mig_state), + nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE])) + goto out; + + type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]); + if (rd_is_string_filtered_attr(rd, "type", qp_types_to_str(type), + nla_line[RDMA_NLDEV_ATTR_RES_TYPE])) + goto out; + + state = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_STATE]); + if (rd_is_string_filtered_attr(rd, "state", qp_states_to_str(state), + nla_line[RDMA_NLDEV_ATTR_RES_STATE])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + if (!get_task_name(pid, b, sizeof(b))) + comm = b; + } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str( + nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + } + + if (rd_is_filtered_attr(rd, "pid", pid, + nla_line[RDMA_NLDEV_ATTR_RES_PID])) + goto out; + + open_json_object(NULL); + print_link(idx, name, port, nla_line); + res_print_u32("lqpn", lqpn, nla_line[RDMA_NLDEV_ATTR_RES_LQPN]); + print_rqpn(rqpn, nla_line); + + print_type(type); + print_state(state); + + print_rqpsn(rq_psn, nla_line); + res_print_u32("sq-psn", sq_psn, nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN]); + + print_pathmig(path_mig_state, nla_line); + res_print_u32("pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]); + res_print_u32("pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); + print_comm(comm, nla_line); + + print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]); + close_json_object(); + newline(); +out: + return MNL_CB_OK; +} + +int res_qp_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + uint32_t idx; + + 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; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + + return (rd->show_raw) ? res_qp_line_raw(rd, name, idx, tb) : + res_qp_line(rd, name, idx, tb); +} + +int res_qp_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; + int ret = MNL_CB_OK; + const char *name; + uint32_t idx; + + 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_RES_QP]) + 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_RES_QP]; + + 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 = (rd->show_raw) ? res_qp_line_raw(rd, name, idx, nla_line) : + res_qp_line(rd, name, idx, nla_line); + if (ret != MNL_CB_OK) + break; + } + return ret; +} diff --git a/rdma/res-srq.c b/rdma/res-srq.c new file mode 100644 index 0000000..8ab2538 --- /dev/null +++ b/rdma/res-srq.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * res-srq.c RDMA tool + * Authors: Neta Ostrovsky <netao@nvidia.com> + */ + +#include "res.h" +#include <inttypes.h> + +#define MAX_QP_STR_LEN 256 + +static const char *srq_types_to_str(uint8_t idx) +{ + static const char *const srq_types_str[] = { "BASIC", + "XRC", + "TM" }; + + if (idx < ARRAY_SIZE(srq_types_str)) + return srq_types_str[idx]; + return "UNKNOWN"; +} + +static void print_type(uint32_t val) +{ + print_string(PRINT_ANY, "type", "type %s ", srq_types_to_str(val)); +} + +static void print_qps(char *qp_str) +{ + char *qpn; + + if (!strlen(qp_str)) + return; + + open_json_array(PRINT_ANY, "lqpn"); + print_string(PRINT_FP, NULL, " ", NULL); + qpn = strtok(qp_str, ","); + while (qpn) { + print_string(PRINT_ANY, NULL, "%s", qpn); + qpn = strtok(NULL, ","); + if (qpn) + print_string(PRINT_FP, NULL, ",", NULL); + } + print_string(PRINT_FP, NULL, " ", NULL); + close_json_array(PRINT_JSON, NULL); +} + +static int filter_srq_range_qps(struct rd *rd, struct nlattr **qp_line, + uint32_t min_range, uint32_t max_range, + char **delimiter, char *qp_str) +{ + uint32_t qpn = 0, tmp_min_range = 0, tmp_max_range = 0; + char tmp[16] = {}; + + for (qpn = min_range; qpn <= max_range; qpn++) { + if (rd_is_filtered_attr(rd, "lqpn", qpn, + qp_line[RDMA_NLDEV_ATTR_MIN_RANGE])) { + /* The QPs range contains a LQPN that is filtered */ + if (!tmp_min_range) + /* There are no QPs previous to + * the filtered one + */ + continue; + if (!tmp_max_range) + snprintf(tmp, sizeof(tmp), "%s%d", *delimiter, + tmp_min_range); + else + snprintf(tmp, sizeof(tmp), "%s%d-%d", + *delimiter, tmp_min_range, + tmp_max_range); + + strncat(qp_str, tmp, + MAX_QP_STR_LEN - strlen(qp_str) - 1); + + memset(tmp, 0, strlen(tmp)); + *delimiter = ","; + tmp_min_range = 0; + tmp_max_range = 0; + continue; + } + if (!tmp_min_range) + tmp_min_range = qpn; + else + tmp_max_range = qpn; + } + + if (!tmp_min_range) + return 0; + if (!tmp_max_range) + snprintf(tmp, sizeof(tmp), "%s%d", *delimiter, tmp_min_range); + else + snprintf(tmp, sizeof(tmp), "%s%d-%d", *delimiter, + tmp_min_range, tmp_max_range); + + strncat(qp_str, tmp, MAX_QP_STR_LEN - strlen(qp_str) - 1); + *delimiter = ","; + return 0; +} + +static int get_srq_qps(struct rd *rd, struct nlattr *qp_table, char *qp_str) +{ + uint32_t qpn = 0, min_range = 0, max_range = 0; + struct nlattr *nla_entry; + struct filter_entry *fe; + char *delimiter = ""; + char tmp[16] = {}; + + if (!qp_table) + return MNL_CB_ERROR; + + /* If there are no QPs associated with the SRQ, return */ + if (!(mnl_attr_get_payload_len(qp_table))) { + list_for_each_entry(fe, &rd->filter_list, list) { + if (!strcmpx(fe->key, "lqpn")) + /* We found the key - + * user requested to filter by LQPN + */ + return -EINVAL; + } + return MNL_CB_OK; + } + + mnl_attr_for_each_nested(nla_entry, qp_table) { + struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {}; + + if (mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line) != + MNL_CB_OK) + goto out; + + if (qp_line[RDMA_NLDEV_ATTR_RES_LQPN]) { + 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])) + continue; + snprintf(tmp, sizeof(tmp), "%s%d", delimiter, qpn); + strncat(qp_str, tmp, + MAX_QP_STR_LEN - strlen(qp_str) - 1); + delimiter = ","; + } else if (qp_line[RDMA_NLDEV_ATTR_MIN_RANGE] && + qp_line[RDMA_NLDEV_ATTR_MAX_RANGE]) { + min_range = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_MIN_RANGE]); + max_range = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_MAX_RANGE]); + + if (filter_srq_range_qps(rd, qp_line, min_range, + max_range, &delimiter, + qp_str)) + goto out; + } else { + goto out; + } + } + + if (!strlen(qp_str)) + /* Check if there are no QPs to display after filter */ + goto out; + + return MNL_CB_OK; + +out: + memset(qp_str, 0, strlen(qp_str)); + return -EINVAL; +} + +static int res_srq_line_raw(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_RAW]) + return MNL_CB_ERROR; + + open_json_object(NULL); + print_dev(idx, name); + print_raw_data(rd, nla_line); + close_json_object(); + newline(); + + return MNL_CB_OK; +} + +static int res_srq_line(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + uint32_t srqn = 0, pid = 0, pdn = 0, cqn = 0; + char qp_str[MAX_QP_STR_LEN] = {}; + char *comm = NULL; + uint8_t type = 0; + SPRINT_BUF(b); + + if (!nla_line[RDMA_NLDEV_ATTR_RES_SRQN]) + return MNL_CB_ERROR; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + if (!get_task_name(pid, b, sizeof(b))) + comm = b; + } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str( + nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + } + + if (rd_is_filtered_attr(rd, "pid", pid, + nla_line[RDMA_NLDEV_ATTR_RES_PID])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_SRQN]) + srqn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_SRQN]); + if (rd_is_filtered_attr(rd, "srqn", srqn, + nla_line[RDMA_NLDEV_ATTR_RES_SRQN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE]) + type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]); + if (rd_is_string_filtered_attr(rd, "type", srq_types_to_str(type), + nla_line[RDMA_NLDEV_ATTR_RES_TYPE])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PDN]) + pdn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PDN]); + if (rd_is_filtered_attr(rd, "pdn", pdn, + nla_line[RDMA_NLDEV_ATTR_RES_PDN])) + goto out; + + if (nla_line[RDMA_NLDEV_ATTR_RES_CQN]) + cqn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_CQN]); + if (rd_is_filtered_attr(rd, "cqn", cqn, + nla_line[RDMA_NLDEV_ATTR_RES_CQN])) + goto out; + + if (get_srq_qps(rd, nla_line[RDMA_NLDEV_ATTR_RES_QP], qp_str) != + MNL_CB_OK) + goto out; + + open_json_object(NULL); + print_dev(idx, name); + res_print_u32("srqn", srqn, nla_line[RDMA_NLDEV_ATTR_RES_SRQN]); + print_type(type); + print_qps(qp_str); + res_print_u32("pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]); + res_print_u32("cqn", cqn, nla_line[RDMA_NLDEV_ATTR_RES_CQN]); + res_print_u32("pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); + print_comm(comm, nla_line); + + print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]); + close_json_object(); + newline(); + +out: + return MNL_CB_OK; +} + +int res_srq_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + uint32_t idx; + + 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; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + + return (rd->show_raw) ? res_srq_line_raw(rd, name, idx, tb) : + res_srq_line(rd, name, idx, tb); +} + +int res_srq_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; + int ret = MNL_CB_OK; + const char *name; + uint32_t idx; + + 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_RES_SRQ]) + 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_RES_SRQ]; + + 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 = (rd->show_raw) ? res_srq_line_raw(rd, name, idx, nla_line) : + res_srq_line(rd, name, idx, nla_line); + if (ret != MNL_CB_OK) + break; + } + return ret; +} diff --git a/rdma/res.c b/rdma/res.c new file mode 100644 index 0000000..3e02413 --- /dev/null +++ b/rdma/res.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * res.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "res.h" +#include <inttypes.h> + +static int res_help(struct rd *rd) +{ + pr_out("Usage: %s resource\n", rd->filename); + pr_out(" resource show [DEV]\n"); + pr_out(" resource show [qp|cm_id|pd|mr|cq|ctx|srq]\n"); + pr_out(" resource show qp link [DEV/PORT]\n"); + pr_out(" resource show qp link [DEV/PORT] [FILTER-NAME FILTER-VALUE]\n"); + pr_out(" resource show cm_id link [DEV/PORT]\n"); + pr_out(" resource show cm_id link [DEV/PORT] [FILTER-NAME FILTER-VALUE]\n"); + pr_out(" resource show cq link [DEV/PORT]\n"); + pr_out(" resource show cq link [DEV/PORT] [FILTER-NAME FILTER-VALUE]\n"); + pr_out(" resource show pd dev [DEV]\n"); + pr_out(" resource show pd dev [DEV] [FILTER-NAME FILTER-VALUE]\n"); + pr_out(" resource show mr dev [DEV]\n"); + pr_out(" resource show mr dev [DEV] [FILTER-NAME FILTER-VALUE]\n"); + pr_out(" resource show ctx dev [DEV]\n"); + pr_out(" resource show ctx dev [DEV] [FILTER-NAME FILTER-VALUE]\n"); + pr_out(" resource show srq dev [DEV]\n"); + pr_out(" resource show srq dev [DEV] [FILTER-NAME FILTER-VALUE]\n"); + return 0; +} + +static int res_print_summary(struct nlattr **tb) +{ + struct nlattr *nla_table = tb[RDMA_NLDEV_ATTR_RES_SUMMARY]; + struct nlattr *nla_entry; + const char *name; + uint64_t curr; + int err; + + 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_SUMMARY_ENTRY_NAME] || + !nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]) { + return -EINVAL; + } + + name = mnl_attr_get_str(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME]); + curr = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]); + res_print_u64(name, curr, nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]); + } + return 0; +} + +static int res_no_args_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_OK; +} + +static int res_no_args_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + const char *name; + uint32_t idx; + + 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_RES_SUMMARY]) + return MNL_CB_ERROR; + + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + open_json_object(NULL); + print_uint(PRINT_ANY, "ifindex", "%u: ", idx); + print_string(PRINT_ANY, "ifname", "%s: ", name); + res_print_summary(tb); + close_json_object(); + newline(); + + return MNL_CB_OK; +} + +int _res_send_idx_msg(struct rd *rd, uint32_t command, mnl_cb_t callback, + uint32_t idx, uint32_t id) +{ + uint32_t flags = NLM_F_REQUEST | NLM_F_ACK; + uint32_t seq; + int ret; + + rd_prepare_msg(rd, command, &seq, flags); + mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); + if (rd->port_idx) + mnl_attr_put_u32(rd->nlh, + RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); + + mnl_attr_put_u32(rd->nlh, id, idx); + + if (command == RDMA_NLDEV_CMD_STAT_GET) + mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, + RDMA_NLDEV_ATTR_RES_MR); + + ret = rd_send_msg(rd); + if (ret) + return ret; + ret = rd_recv_msg(rd, callback, rd, seq); + return ret; +} + +int _res_send_msg(struct rd *rd, uint32_t command, mnl_cb_t callback) +{ + uint32_t flags = NLM_F_REQUEST | NLM_F_ACK; + uint32_t seq; + int ret; + + if (command != RDMA_NLDEV_CMD_RES_GET) + flags |= NLM_F_DUMP; + + rd_prepare_msg(rd, command, &seq, flags); + mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); + if (rd->port_idx) + mnl_attr_put_u32(rd->nlh, + RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); + + if (command == RDMA_NLDEV_CMD_STAT_GET) + mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, + RDMA_NLDEV_ATTR_RES_MR); + + ret = rd_send_msg(rd); + if (ret) + return ret; + + ret = rd_recv_msg(rd, callback, rd, seq); + return ret; +} + +const char *qp_types_to_str(uint8_t idx) +{ + static const char * const qp_types_str[] = { "SMI", "GSI", "RC", + "UC", "UD", "RAW_IPV6", + "RAW_ETHERTYPE", + "UNKNOWN", "RAW_PACKET", + "XRC_INI", "XRC_TGT", + }; + + if (idx < ARRAY_SIZE(qp_types_str)) + return qp_types_str[idx]; + + return (idx == 0xFF) ? "DRIVER" : "UNKNOWN"; +} + +void print_comm(const char *str, struct nlattr **nla_line) +{ + char tmp[18]; + + if (!str) + return; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID] || is_json_context()) + snprintf(tmp, sizeof(tmp), "%s", str); + else + snprintf(tmp, sizeof(tmp), "[%s]", str); + print_string(PRINT_ANY, "comm", "comm %s ", tmp); +} + +void print_dev(uint32_t idx, const char *name) +{ + print_int(PRINT_ANY, "ifindex", NULL, idx); + print_string(PRINT_ANY, "ifname", "dev %s ", name); +} + +void print_link(uint32_t idx, const char *name, uint32_t port, + struct nlattr **nla_line) +{ + char tmp[64] = {}; + + print_uint(PRINT_JSON, "ifindex", NULL, idx); + print_string(PRINT_ANY, "ifname", NULL, name); + if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) { + print_uint(PRINT_ANY, "port", NULL, port); + snprintf(tmp, sizeof(tmp), "%s/%d", name, port); + } else { + snprintf(tmp, sizeof(tmp), "%s/-", name); + } + + print_string(PRINT_FP, NULL, "link %s ", tmp); +} + +void print_qp_type(uint32_t val) +{ + print_string(PRINT_ANY, "qp-type", "qp-type %s ", qp_types_to_str(val)); +} + +void print_key(const char *name, uint64_t val, struct nlattr *nlattr) +{ + if (!nlattr) + return; + print_string(PRINT_FP, NULL, name, NULL); + print_hex(PRINT_ANY, name, " 0x%" PRIx64 " ", val); +} + +void res_print_u32(const char *name, uint32_t val, struct nlattr *nlattr) +{ + if (!nlattr) + return; + print_uint(PRINT_ANY, name, name, val); + print_uint(PRINT_FP, NULL, " %" PRIu32 " ", val); +} + +void res_print_u64(const char *name, uint64_t val, struct nlattr *nlattr) +{ + if (!nlattr) + return; + print_u64(PRINT_ANY, name, name, val); + print_u64(PRINT_FP, NULL, " %" PRIu64 " ", val); +} + +RES_FUNC(res_no_args, RDMA_NLDEV_CMD_RES_GET, NULL, true, 0); + +static int res_show(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, res_no_args }, + { "qp", res_qp }, + { "cm_id", res_cm_id }, + { "cq", res_cq }, + { "mr", res_mr }, + { "pd", res_pd }, + { "ctx", res_ctx }, + { "srq", res_srq }, + { 0 } + }; + + /* + * Special case to support "rdma res show DEV_NAME" + */ + if (rd_argc(rd) == 1 && dev_map_lookup(rd, false)) + return rd_exec_dev(rd, _res_no_args); + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +int cmd_res(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, res_show }, + { "show", res_show }, + { "list", res_show }, + { "help", res_help }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "resource command"); +} diff --git a/rdma/res.h b/rdma/res.h new file mode 100644 index 0000000..fd09ce7 --- /dev/null +++ b/rdma/res.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* + * res.h RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ +#ifndef _RDMA_TOOL_RES_H_ +#define _RDMA_TOOL_RES_H_ + +#include "rdma.h" + +int _res_send_msg(struct rd *rd, uint32_t command, mnl_cb_t callback); +int _res_send_idx_msg(struct rd *rd, uint32_t command, mnl_cb_t callback, + uint32_t idx, uint32_t id); + +int res_pd_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_pd_idx_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_mr_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_mr_idx_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_cq_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_cq_idx_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_cm_id_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_cm_id_idx_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_qp_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_qp_idx_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_ctx_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_ctx_idx_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_srq_parse_cb(const struct nlmsghdr *nlh, void *data); +int res_srq_idx_parse_cb(const struct nlmsghdr *nlh, void *data); + +static inline uint32_t res_get_command(uint32_t command, struct rd *rd) +{ + if (!rd->show_raw) + return command; + + switch (command) { + case RDMA_NLDEV_CMD_RES_QP_GET: + return RDMA_NLDEV_CMD_RES_QP_GET_RAW; + case RDMA_NLDEV_CMD_RES_CQ_GET: + return RDMA_NLDEV_CMD_RES_CQ_GET_RAW; + case RDMA_NLDEV_CMD_RES_MR_GET: + return RDMA_NLDEV_CMD_RES_MR_GET_RAW; + case RDMA_NLDEV_CMD_RES_SRQ_GET: + return RDMA_NLDEV_CMD_RES_SRQ_GET_RAW; + default: + return command; + } +} + +#define RES_FUNC(name, command, valid_filters, strict_port, id) \ + static inline int _##name(struct rd *rd) \ + { \ + uint32_t idx, _command; \ + int ret; \ + _command = res_get_command(command, rd); \ + if (id) { \ + ret = rd_doit_index(rd, &idx); \ + if (ret) { \ + rd->suppress_errors = true; \ + ret = _res_send_idx_msg(rd, _command, \ + name##_idx_parse_cb, \ + idx, id); \ + if (!ret || rd->show_raw) \ + return ret; \ + /* Fallback for old systems without .doit callbacks. \ + * Kernel that supports raw, for sure supports doit. \ + */ \ + } \ + } \ + return _res_send_msg(rd, _command, name##_parse_cb); \ + } \ + static inline int name(struct rd *rd) \ + { \ + int ret = rd_build_filter(rd, valid_filters); \ + if (ret) \ + return ret; \ + if ((uintptr_t)valid_filters != (uintptr_t)NULL) { \ + ret = rd_set_arg_to_devname(rd); \ + if (ret) \ + return ret; \ + } \ + if (strict_port) \ + return rd_exec_dev(rd, _##name); \ + else \ + return rd_exec_link(rd, _##name, strict_port); \ + } + +static const +struct filters pd_valid_filters[MAX_NUMBER_OF_FILTERS] = { + { .name = "dev", .is_number = false }, + { .name = "users", .is_number = true }, + { .name = "pid", .is_number = true }, + { .name = "ctxn", .is_number = true }, + { .name = "pdn", .is_number = true, .is_doit = true }, + { .name = "ctxn", .is_number = true } +}; + +RES_FUNC(res_pd, RDMA_NLDEV_CMD_RES_PD_GET, pd_valid_filters, true, + RDMA_NLDEV_ATTR_RES_PDN); + +static const +struct filters mr_valid_filters[MAX_NUMBER_OF_FILTERS] = { + { .name = "dev", .is_number = false }, + { .name = "rkey", .is_number = true }, + { .name = "lkey", .is_number = true }, + { .name = "mrlen", .is_number = true }, + { .name = "pid", .is_number = true }, + { .name = "mrn", .is_number = true, .is_doit = true }, + { .name = "pdn", .is_number = true } +}; + +RES_FUNC(res_mr, RDMA_NLDEV_CMD_RES_MR_GET, mr_valid_filters, true, + RDMA_NLDEV_ATTR_RES_MRN); + +static const +struct filters cq_valid_filters[MAX_NUMBER_OF_FILTERS] = { + { .name = "dev", .is_number = false }, + { .name = "users", .is_number = true }, + { .name = "poll-ctx", .is_number = false }, + { .name = "pid", .is_number = true }, + { .name = "cqn", .is_number = true, .is_doit = true }, + { .name = "ctxn", .is_number = true } +}; + +RES_FUNC(res_cq, RDMA_NLDEV_CMD_RES_CQ_GET, cq_valid_filters, true, + RDMA_NLDEV_ATTR_RES_CQN); + +static const +struct filters cm_id_valid_filters[MAX_NUMBER_OF_FILTERS] = { + { .name = "link", .is_number = false }, + { .name = "lqpn", .is_number = true }, + { .name = "qp-type", .is_number = false }, + { .name = "state", .is_number = false }, + { .name = "ps", .is_number = false }, + { .name = "dev-type", .is_number = false }, + { .name = "transport-type", .is_number = false }, + { .name = "pid", .is_number = true }, + { .name = "src-addr", .is_number = false }, + { .name = "src-port", .is_number = true }, + { .name = "dst-addr", .is_number = false }, + { .name = "dst-port", .is_number = true }, + { .name = "cm-idn", .is_number = true, .is_doit = true } +}; + +RES_FUNC(res_cm_id, RDMA_NLDEV_CMD_RES_CM_ID_GET, cm_id_valid_filters, false, + RDMA_NLDEV_ATTR_RES_CM_IDN); + +static const struct +filters qp_valid_filters[MAX_NUMBER_OF_FILTERS] = { + { .name = "link", .is_number = false }, + { .name = "lqpn", .is_number = true, .is_doit = true }, + { .name = "rqpn", .is_number = true }, + { .name = "pid", .is_number = true }, + { .name = "sq-psn", .is_number = true }, + { .name = "rq-psn", .is_number = true }, + { .name = "type", .is_number = false }, + { .name = "path-mig-state", .is_number = false }, + { .name = "state", .is_number = false }, + { .name = "pdn", .is_number = true }, +}; + +RES_FUNC(res_qp, RDMA_NLDEV_CMD_RES_QP_GET, qp_valid_filters, false, + RDMA_NLDEV_ATTR_RES_LQPN); + +static const +struct filters ctx_valid_filters[MAX_NUMBER_OF_FILTERS] = { + { .name = "dev", .is_number = false }, + { .name = "pid", .is_number = true }, + { .name = "ctxn", .is_number = true, .is_doit = true }, +}; + +RES_FUNC(res_ctx, RDMA_NLDEV_CMD_RES_CTX_GET, ctx_valid_filters, true, + RDMA_NLDEV_ATTR_RES_CTXN); + +static const +struct filters srq_valid_filters[MAX_NUMBER_OF_FILTERS] = { + { .name = "dev", .is_number = false }, + { .name = "pid", .is_number = true }, + { .name = "srqn", .is_number = true, .is_doit = true }, + { .name = "type", .is_number = false }, + { .name = "pdn", .is_number = true }, + { .name = "cqn", .is_number = true }, + { .name = "lqpn", .is_number = true }, +}; + +RES_FUNC(res_srq, RDMA_NLDEV_CMD_RES_SRQ_GET, srq_valid_filters, true, + RDMA_NLDEV_ATTR_RES_SRQN); + +void print_dev(uint32_t idx, const char *name); +void print_link(uint32_t idx, const char *name, uint32_t port, struct nlattr **nla_line); +void print_key(const char *name, uint64_t val, struct nlattr *nlattr); +void res_print_u32(const char *name, uint32_t val, struct nlattr *nlattr); +void res_print_u64(const char *name, uint64_t val, struct nlattr *nlattr); +void print_comm(const char *str, struct nlattr **nla_line); +const char *qp_types_to_str(uint8_t idx); +void print_qp_type(uint32_t val); +#endif /* _RDMA_TOOL_RES_H_ */ diff --git a/rdma/stat-mr.c b/rdma/stat-mr.c new file mode 100644 index 0000000..8f9eb17 --- /dev/null +++ b/rdma/stat-mr.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * stat-mr.c RDMA tool + * Authors: Erez Alfasi <ereza@mellanox.com> + */ + +#include "res.h" +#include "stat.h" +#include <inttypes.h> + +static int stat_mr_line(struct rd *rd, const char *name, int idx, + struct nlattr **nla_line) +{ + uint32_t mrn = 0; + int ret; + + if (nla_line[RDMA_NLDEV_ATTR_RES_MRN]) + mrn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_MRN]); + if (rd_is_filtered_attr(rd, "mrn", mrn, + nla_line[RDMA_NLDEV_ATTR_RES_MRN])) + goto out; + + open_json_object(NULL); + print_dev(idx, name); + res_print_u32("mrn", mrn, nla_line[RDMA_NLDEV_ATTR_RES_MRN]); + + if (nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) { + ret = res_get_hwcounters(nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true); + if (ret != MNL_CB_OK) + return ret; + } + + close_json_object(); + print_nl(); +out: + return MNL_CB_OK; +} + +int stat_mr_idx_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + const char *name; + uint32_t idx; + + 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; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + + return stat_mr_line(rd, name, idx, tb); +} + +int stat_mr_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; + int ret = MNL_CB_OK; + const char *name; + uint32_t idx; + + 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_RES_MR]) + 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_RES_MR]; + + 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 = stat_mr_line(rd, name, idx, nla_line); + if (ret != MNL_CB_OK) + break; + } + return ret; +} diff --git a/rdma/stat.c b/rdma/stat.c new file mode 100644 index 0000000..bf78f7c --- /dev/null +++ b/rdma/stat.c @@ -0,0 +1,1131 @@ +// 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 "stat.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(" %s statistic mode [ supported ]\n", rd->filename); + pr_out(" %s statistic mode [ supported ] link [ DEV/PORT_INDEX ]\n", rd->filename); + pr_out(" %s statistic set link [ DEV/PORT_INDEX ] optional-counters [ OPTIONAL-COUNTERS ]\n", rd->filename); + pr_out(" %s statistic unset link [ DEV/PORT_INDEX ] optional-counters\n", rd->filename); + pr_out("where OBJECT: = { qp }\n"); + pr_out(" CRITERIA : = { type }\n"); + pr_out(" COUNTER_SCOPE: = { link | dev }\n"); + pr_out(" FILTER_NAME: = { cntn | lqpn | pid }\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); + pr_out(" %s statistic mode\n", rd->filename); + pr_out(" %s statistic mode link mlx5_2/1\n", rd->filename); + pr_out(" %s statistic mode supported\n", rd->filename); + pr_out(" %s statistic mode supported link mlx5_2/1\n", rd->filename); + pr_out(" %s statistic set link mlx5_2/1 optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts\n", rd->filename); + pr_out(" %s statistic unset link mlx5_2/1 optional-counters\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, }, + { "pid", RDMA_COUNTER_MASK_PID, }, + { NULL }, +}; + +static int prepare_auto_mode_str(uint32_t mask, char *output, int len) +{ + char s[] = "qp auto"; + int i, outlen = strlen(s); + bool first = true; + + 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; + if (first) { + strcat(output, " "); + first = false; + } else + 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] = {}; + 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(mask, output, sizeof(output)); + } else { + snprintf(output, sizeof(output), "qp auto off"); + } + + open_json_object(NULL); + print_link(idx, name, port, tb); + print_string(PRINT_ANY, "mode", "mode %s ", output); + close_json_object(); + newline(); + + 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; + + ret = rd_recv_msg(rd, qp_link_get_mode_parse_cb, rd, seq); + 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"); +} + +int res_get_hwcounters(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]); + newline_indent(); + res_print_u64(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, qp_type = 0; + struct nlattr *hwc_table, *qp_table; + struct nlattr *nla_entry; + const char *comm = NULL; + SPRINT_BUF(b); + 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_TYPE]) + qp_type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]); + + if (rd_is_string_filtered_attr(rd, "qp-type", qp_types_to_str(qp_type), + nla_line[RDMA_NLDEV_ATTR_RES_TYPE])) + return MNL_CB_OK; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + if (!get_task_name(pid, b, sizeof(b))) + comm = b; + } else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) { + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str( + nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + } + + if (rd_is_filtered_attr(rd, "pid", pid, + nla_line[RDMA_NLDEV_ATTR_RES_PID])) + return MNL_CB_OK; + + 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(hwc_table, false); + if (err != MNL_CB_OK) + return err; + open_json_object(NULL); + print_link(index, name, port, nla_line); + print_uint(PRINT_ANY, "cntn", "cntn %u ", cntn); + if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE]) + print_qp_type(qp_type); + res_print_u64("pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]); + print_comm(comm, nla_line); + res_get_hwcounters(hwc_table, true); + isfirst = true; + open_json_array(PRINT_JSON, "lqpn"); + print_string(PRINT_FP, NULL, "%s LQPN: <", _SL_); + 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 (!isfirst) + print_string(PRINT_FP, NULL, ",", NULL); + print_uint(PRINT_ANY, NULL, "%d", qpn); + isfirst = false; + } + close_json_array(PRINT_ANY, ">"); + close_json_object(); + newline(); + + 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_CB_OK; + + 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 }, + { .name = "qp-type", .is_number = false }, +}; + +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; + + ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq); + 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_get_auto_mode_mask(struct rd *rd) +{ + char *modes = rd_argv(rd), *mode, *saved_ptr; + const char *delim = ","; + int mask = 0, found, i; + + if (!modes) + return mask; + + mode = strtok_r(modes, delim, &saved_ptr); + do { + if (!mode) + break; + + found = false; + for (i = 0; auto_params[i].name != NULL; i++) { + if (!strcmp(mode, auto_params[i].name)) { + mask |= auto_params[i].attr; + found = true; + break; + } + } + + if (!found) { + pr_err("Unknown auto mode '%s'.\n", mode); + mask = 0; + break; + } + + mode = strtok_r(NULL, delim, &saved_ptr); + } while (1); + + if (mask) + rd_arg_inc(rd); + + return mask; +} + +static int stat_one_qp_set_link_auto(struct rd *rd) +{ + int auto_mask = 0; + + if (!rd_argc(rd)) + return -EINVAL; + + if (!strcmpx(rd_argv(rd), "off")) { + rd_arg_inc(rd); + return stat_qp_set_link_auto_sendmsg(rd, 0); + } + + auto_mask = stat_get_auto_mode_mask(rd); + if (!auto_mask || !rd_argc(rd)) + return -EINVAL; + + if (!strcmpx(rd_argv(rd), "on")) { + rd_arg_inc(rd); + return stat_qp_set_link_auto_sendmsg(rd, auto_mask); + } else { + pr_err("Unknown parameter '%s'.\n", rd_argv(rd)); + return -EINVAL; + } +} + +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_str(struct rd *rd, const char *arg, char **value, bool allow_empty) +{ + int len = 0; + + if (strcmpx(rd_argv(rd), arg) != 0) { + pr_err("Unknown parameter '%s'.\n", rd_argv(rd)); + return -EINVAL; + } + + rd_arg_inc(rd); + if (!rd_no_arg(rd)) { + *value = strdup(rd_argv(rd)); + len = strlen(*value); + rd_arg_inc(rd); + } + + if ((allow_empty && len) || (!allow_empty && !len)) { + stat_help(rd); + return -EINVAL; + } + + return 0; +} + +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); + + if (rd_is_multiarg(rd)) { + pr_err("The parameter %s shouldn't include range\n", arg); + return -EINVAL; + } + + 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"); + if (lqpn < 0) + return 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"); + if (cntn < 0) + return 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; + + if (rd_no_arg(rd)) { + stat_help(rd); + return -EINVAL; + } + + ret = rd_build_filter(rd, stat_valid_filters); + if (ret) + return ret; + + cntn = stat_get_arg(rd, "cntn"); + if (cntn < 0) + return cntn; + + if (rd_argc(rd)) { + lqpn = stat_get_arg(rd, "lqpn"); + if (lqpn < 0) + return 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 do_stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data, + bool supported) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct nlattr *nla_entry; + const char *dev, *name; + int enabled, err = 0; + bool isfirst = true; + uint32_t port; + + 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; + + dev = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); + + mnl_attr_for_each_nested(nla_entry, + tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) { + struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {}; + + err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, cnt); + if ((err != MNL_CB_OK) || + (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME])) + return -EINVAL; + + if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC]) + continue; + + enabled = mnl_attr_get_u8(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC]); + name = mnl_attr_get_str(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]); + if (supported || enabled) { + if (isfirst) { + open_json_object(NULL); + print_string(PRINT_ANY, "ifname", "link %s/", dev); + print_uint(PRINT_ANY, "port", "%u ", port); + if (supported) + open_json_array(PRINT_ANY, + "supported optional-counters"); + else + open_json_array(PRINT_ANY, + "optional-counters"); + print_string(PRINT_FP, NULL, " ", NULL); + isfirst = false; + } else { + print_string(PRINT_FP, NULL, ",", NULL); + } + newline_indent(); + + print_string(PRINT_ANY, NULL, "%s", name); + } + } + + if (!isfirst) { + close_json_array(PRINT_JSON, NULL); + close_json_object(); + newline(); + } + + return 0; +} + +static int stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + return do_stat_mode_parse_cb(nlh, data, false); +} + +static int stat_mode_parse_cb_supported(const struct nlmsghdr *nlh, void *data) +{ + return do_stat_mode_parse_cb(nlh, data, true); +} + +static int stat_one_link_get_status_req(struct rd *rd, uint32_t *seq) +{ + int flags = NLM_F_REQUEST | NLM_F_ACK; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET_STATUS, 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); + + return rd_send_msg(rd); +} + +static int stat_one_link_get_mode(struct rd *rd) +{ + uint32_t seq; + int err; + + if (!rd->port_idx) + return 0; + + err = stat_one_link_get_status_req(rd, &seq); + if (err) + return err; + + return rd_recv_msg(rd, stat_mode_parse_cb, rd, seq); +} + +static int stat_one_link_get_mode_supported(struct rd *rd) +{ + uint32_t seq; + int err; + + if (!rd->port_idx) + return 0; + + err = stat_one_link_get_status_req(rd, &seq); + if (err) + return err; + + return rd_recv_msg(rd, stat_mode_parse_cb_supported, rd, seq); +} + +static int stat_link_get_mode(struct rd *rd) +{ + return rd_exec_link(rd, stat_one_link_get_mode, false); +} + +static int stat_link_get_mode_supported(struct rd *rd) +{ + return rd_exec_link(rd, stat_one_link_get_mode_supported, false); +} + +static int stat_mode_supported(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, stat_link_get_mode_supported }, + { "link", stat_link_get_mode_supported }, + { "help", stat_help }, + { 0 }, + }; + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int stat_mode(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, stat_link_get_mode }, + { "link", stat_link_get_mode }, + { "show", stat_link_get_mode }, + { "supported", stat_mode_supported }, + { "help", stat_help }, + { 0 }, + }; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int stat_one_set_link_opcounters(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct nlattr *nla_entry, *tb_set; + int ret, flags = NLM_F_REQUEST | NLM_F_ACK; + char *opcnt, *opcnts; + struct rd *rd = data; + uint32_t seq; + bool found; + + mnl_attr_parse(nlh, 0, rd_attr_cb, tb); + if (!tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) + return MNL_CB_ERROR; + + if (rd_no_arg(rd)) { + stat_help(rd); + return -EINVAL; + } + + ret = stat_get_arg_str(rd, "optional-counters", &opcnts, false); + if (ret) + return ret; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &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); + + tb_set = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS); + + opcnt = strtok(opcnts, ","); + while (opcnt) { + found = false; + mnl_attr_for_each_nested(nla_entry, + tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) { + struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {}, *nm, *id; + + if (mnl_attr_parse_nested(nla_entry, rd_attr_cb, + cnt) != MNL_CB_OK) + return -EINVAL; + + nm = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]; + id = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX]; + if (!nm || ! id) + return -EINVAL; + + if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC]) + continue; + + if (strcmp(opcnt, mnl_attr_get_str(nm)) == 0) { + mnl_attr_put_u32(rd->nlh, + RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX, + mnl_attr_get_u32(id)); + found = true; + } + } + + if (!found) + return -EINVAL; + + opcnt = strtok(NULL, ","); + } + mnl_attr_nest_end(rd->nlh, tb_set); + + return rd_sendrecv_msg(rd, seq); +} + +static int stat_one_set_link(struct rd *rd) +{ + uint32_t seq; + int err; + + if (!rd->port_idx) + return 0; + + err = stat_one_link_get_status_req(rd, &seq); + if (err) + return err; + + return rd_recv_msg(rd, stat_one_set_link_opcounters, rd, seq); +} + +static int stat_set_link(struct rd *rd) +{ + return rd_exec_link(rd, stat_one_set_link, true); +} + +static int stat_set(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, stat_help }, + { "link", stat_set_link }, + { "help", stat_help }, + { 0 }, + }; + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int stat_one_unset_link_opcounters(struct rd *rd) +{ + int ret, flags = NLM_F_REQUEST | NLM_F_ACK; + struct nlattr *tbl; + uint32_t seq; + char *opcnts; + + if (rd_no_arg(rd)) { + stat_help(rd); + return -EINVAL; + } + + ret = stat_get_arg_str(rd, "optional-counters", &opcnts, true); + if (ret) + return ret; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &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); + + tbl = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS); + mnl_attr_nest_end(rd->nlh, tbl); + + return rd_sendrecv_msg(rd, seq); +} + +static int stat_one_unset_link(struct rd *rd) +{ + return stat_one_unset_link_opcounters(rd); +} + +static int stat_unset_link(struct rd *rd) +{ + return rd_exec_link(rd, stat_one_unset_link, true); +} + +static int stat_unset(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, stat_help }, + { "link", stat_unset_link }, + { "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] = {}; + 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]); + open_json_object(NULL); + print_string(PRINT_ANY, "ifname", "link %s/", name); + print_uint(PRINT_ANY, "port", "%u ", port); + ret = res_get_hwcounters(tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true); + + close_json_object(); + newline(); + 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 }, + { "mr", stat_mr }, + { "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 }, + { "mr", stat_mr }, + { "mode", stat_mode }, + { "set", stat_set }, + { "unset", stat_unset }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "statistic command"); +} diff --git a/rdma/stat.h b/rdma/stat.h new file mode 100644 index 0000000..5adcf4f --- /dev/null +++ b/rdma/stat.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ +/* + * stat.h RDMA tool + * Authors: Mark Zhang <markz@mellanox.com> + * Erez Alfasi <ereza@mellanox.com> + */ +#ifndef _RDMA_TOOL_STAT_H_ +#define _RDMA_TOOL_STAT_H_ + +#include "rdma.h" + +int res_get_hwcounters(struct nlattr *hwc_table, bool print); +int stat_mr_parse_cb(const struct nlmsghdr *nlh, void *data); +int stat_mr_idx_parse_cb(const struct nlmsghdr *nlh, void *data); + +static const +struct filters stat_mr_valid_filters[MAX_NUMBER_OF_FILTERS] = { + { .name = "mrn", .is_number = true, .is_doit = true }, +}; + +RES_FUNC(stat_mr, RDMA_NLDEV_CMD_STAT_GET, stat_mr_valid_filters, true, + RDMA_NLDEV_ATTR_RES_MRN); + +#endif /* _RDMA_TOOL_STAT_H_ */ diff --git a/rdma/sys.c b/rdma/sys.c new file mode 100644 index 0000000..7dbe440 --- /dev/null +++ b/rdma/sys.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * sys.c RDMA tool + */ + +#include "rdma.h" + +static int sys_help(struct rd *rd) +{ + pr_out("Usage: %s system show [ netns ]\n", rd->filename); + pr_out(" %s system set netns { shared | exclusive }\n", rd->filename); + return 0; +} + +static const char *netns_modes_str[] = { + "exclusive", + "shared", +}; + +static int sys_show_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + bool cof = false; + + mnl_attr_parse(nlh, 0, rd_attr_cb, tb); + + if (tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE]) { + const char *mode_str; + uint8_t netns_mode; + + netns_mode = + mnl_attr_get_u8(tb[RDMA_NLDEV_SYS_ATTR_NETNS_MODE]); + + if (netns_mode < ARRAY_SIZE(netns_modes_str)) + mode_str = netns_modes_str[netns_mode]; + else + mode_str = "unknown"; + + print_string(PRINT_ANY, "netns", "netns %s ", mode_str); + } + + if (tb[RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE]) { + uint8_t mode; + + mode = mnl_attr_get_u8(tb[RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE]); + + print_on_off(PRINT_ANY, "privileged-qkey", "privileged-qkey %s ", mode); + + } + + if (tb[RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK]) + cof = mnl_attr_get_u8(tb[RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK]); + + print_on_off(PRINT_ANY, "copy-on-fork", "copy-on-fork %s", cof); + print_nl(); + + return MNL_CB_OK; +} + +static int sys_show_no_args(struct rd *rd) +{ + uint32_t seq; + int ret; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_SYS_GET, + &seq, (NLM_F_REQUEST | NLM_F_ACK)); + ret = rd_send_msg(rd); + if (ret) + return ret; + + return rd_recv_msg(rd, sys_show_parse_cb, rd, seq); +} + +static int sys_show(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, sys_show_no_args}, + { "netns", sys_show_no_args}, + { "privileged-qkey", sys_show_no_args}, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int sys_set_netns_cmd(struct rd *rd, bool enable) +{ + uint32_t seq; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_SYS_SET, + &seq, (NLM_F_REQUEST | NLM_F_ACK)); + mnl_attr_put_u8(rd->nlh, RDMA_NLDEV_SYS_ATTR_NETNS_MODE, enable); + + return rd_sendrecv_msg(rd, seq); +} + +static int sys_set_privileged_qkey_cmd(struct rd *rd, bool enable) +{ + uint32_t seq; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_SYS_SET, + &seq, (NLM_F_REQUEST | NLM_F_ACK)); + mnl_attr_put_u8(rd->nlh, RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE, enable); + + return rd_sendrecv_msg(rd, seq); +} + +static bool sys_valid_netns_cmd(const char *cmd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(netns_modes_str); i++) { + if (!strcmp(cmd, netns_modes_str[i])) + return true; + } + return false; +} + +static int sys_set_netns_args(struct rd *rd) +{ + bool cmd; + + if (rd_no_arg(rd) || !sys_valid_netns_cmd(rd_argv(rd))) { + pr_err("valid options are: { shared | exclusive }\n"); + return -EINVAL; + } + + cmd = (strcmp(rd_argv(rd), "shared") == 0) ? true : false; + + return sys_set_netns_cmd(rd, cmd); +} + +static int sys_set_privileged_qkey_args(struct rd *rd) +{ + bool cmd; + int ret; + + if (rd_no_arg(rd)) { + pr_err("valid options are: { on | off }\n"); + return -EINVAL; + } + + cmd = parse_on_off("privileged-qkey", rd_argv(rd), &ret); + if (ret) + return -EINVAL; + + return sys_set_privileged_qkey_cmd(rd, cmd); +} + +static int sys_set_help(struct rd *rd) +{ + pr_out("Usage: %s system set [PARAM] value\n", rd->filename); + pr_out(" system set netns { shared | exclusive }\n"); + pr_out(" system set privileged-qkey { on | off }\n"); + return 0; +} + +static int sys_set(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, sys_set_help }, + { "help", sys_set_help }, + { "netns", sys_set_netns_args}, + { "privileged-qkey", sys_set_privileged_qkey_args}, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +int cmd_sys(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, sys_show }, + { "show", sys_show }, + { "set", sys_set }, + { "help", sys_help }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "system command"); +} diff --git a/rdma/utils.c b/rdma/utils.c new file mode 100644 index 0000000..27595a3 --- /dev/null +++ b/rdma/utils.c @@ -0,0 +1,958 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * utils.c RDMA tool + * Authors: Leon Romanovsky <leonro@mellanox.com> + */ + +#include "rdma.h" +#include <ctype.h> +#include <inttypes.h> + +int rd_argc(struct rd *rd) +{ + return rd->argc; +} + +char *rd_argv(struct rd *rd) +{ + if (!rd_argc(rd)) + return NULL; + return *rd->argv; +} + +int strcmpx(const char *str1, const char *str2) +{ + if (strlen(str1) > strlen(str2)) + return -1; + return strncmp(str1, str2, strlen(str1)); +} + +static bool rd_argv_match(struct rd *rd, const char *pattern) +{ + if (!rd_argc(rd)) + return false; + return strcmpx(rd_argv(rd), pattern) == 0; +} + +void rd_arg_inc(struct rd *rd) +{ + if (!rd_argc(rd)) + return; + rd->argc--; + rd->argv++; +} + +bool rd_no_arg(struct rd *rd) +{ + return rd_argc(rd) == 0; +} + +bool rd_is_multiarg(struct rd *rd) +{ + if (!rd_argc(rd)) + return false; + return strpbrk(rd_argv(rd), ",-") != NULL; +} + +/* + * Possible input:output + * dev/port | first port | is_dump_all + * mlx5_1 | 0 | true + * mlx5_1/ | 0 | true + * mlx5_1/0 | 0 | false + * mlx5_1/1 | 1 | false + * mlx5_1/- | 0 | false + * + * In strict port mode, a non-0 port must be provided + */ +static int get_port_from_argv(struct rd *rd, uint32_t *port, + bool *is_dump_all, bool strict_port) +{ + char *slash; + + *port = 0; + *is_dump_all = strict_port ? false : true; + + slash = strchr(rd_argv(rd), '/'); + /* if no port found, return 0 */ + if (slash == NULL) { + if (strict_port) + return -EINVAL; + else + return 0; + } + + if (slash++) { + if (*slash == '-') { + if (strict_port) + return -EINVAL; + *is_dump_all = false; + return 0; + } + + if (isdigit(*slash)) { + *is_dump_all = false; + *port = atoi(slash); + } + if (!*port && strlen(slash)) + return -EINVAL; + } + if (strict_port && (*port == 0)) + return -EINVAL; + + return 0; +} + +static struct dev_map *dev_map_alloc(const char *dev_name) +{ + struct dev_map *dev_map; + + dev_map = calloc(1, sizeof(*dev_map)); + if (!dev_map) + return NULL; + dev_map->dev_name = strdup(dev_name); + if (!dev_map->dev_name) { + free(dev_map); + return NULL; + } + + return dev_map; +} + +static void dev_map_cleanup(struct rd *rd) +{ + struct dev_map *dev_map, *tmp; + + list_for_each_entry_safe(dev_map, tmp, + &rd->dev_map_list, list) { + list_del(&dev_map->list); + free(dev_map->dev_name); + free(dev_map); + } +} + +static int add_filter(struct rd *rd, char *key, char *value, + const struct filters valid_filters[]) +{ + char cset[] = "1234567890,-"; + struct filter_entry *fe; + bool key_found = false; + int idx = 0; + char *endp; + int ret; + + fe = calloc(1, sizeof(*fe)); + if (!fe) + return -ENOMEM; + + while (idx < MAX_NUMBER_OF_FILTERS && valid_filters[idx].name) { + if (!strcmpx(key, valid_filters[idx].name)) { + key_found = true; + break; + } + idx++; + } + if (!key_found) { + pr_err("Unsupported filter option: %s\n", key); + ret = -EINVAL; + goto err; + } + + /* + * Check the filter validity, not optimal, but works + * + * Actually, there are three types of filters + * numeric - for example PID or QPN + * string - for example states + * link - user requested to filter on specific link + * e.g. mlx5_1/1, mlx5_1/-, mlx5_1 ... + */ + if (valid_filters[idx].is_number && + strspn(value, cset) != strlen(value)) { + pr_err("%s filter accepts \"%s\" characters only\n", key, cset); + ret = -EINVAL; + goto err; + } + + fe->key = strdup(key); + fe->value = strdup(value); + if (!fe->key || !fe->value) { + ret = -ENOMEM; + goto err_alloc; + } + + errno = 0; + strtol(fe->value, &endp, 10); + if (valid_filters[idx].is_doit && !errno && *endp == '\0') + fe->is_doit = true; + + for (idx = 0; idx < strlen(fe->value); idx++) + fe->value[idx] = tolower(fe->value[idx]); + + list_add_tail(&fe->list, &rd->filter_list); + return 0; + +err_alloc: + free(fe->value); + free(fe->key); +err: + free(fe); + return ret; +} + +bool rd_doit_index(struct rd *rd, uint32_t *idx) +{ + struct filter_entry *fe; + + list_for_each_entry(fe, &rd->filter_list, list) { + if (fe->is_doit) { + *idx = atoi(fe->value); + return true; + } + } + + return false; +} + +int rd_build_filter(struct rd *rd, const struct filters valid_filters[]) +{ + int ret = 0; + int idx = 0; + + if (!valid_filters || !rd_argc(rd)) + goto out; + + if (rd_argc(rd) == 1) { + pr_err("No filter data was supplied to filter option %s\n", rd_argv(rd)); + ret = -EINVAL; + goto out; + } + + if (rd_argc(rd) % 2) { + pr_err("There is filter option without data\n"); + ret = -EINVAL; + goto out; + } + + while (idx != rd_argc(rd)) { + /* + * We can do micro-optimization and skip "dev" + * and "link" filters, but it is not worth of it. + */ + ret = add_filter(rd, *(rd->argv + idx), + *(rd->argv + idx + 1), valid_filters); + if (ret) + goto out; + idx += 2; + } + +out: + return ret; +} + +static bool rd_check_is_key_exist(struct rd *rd, const char *key) +{ + struct filter_entry *fe; + + list_for_each_entry(fe, &rd->filter_list, list) { + if (!strcmpx(fe->key, key)) + return true; + } + + return false; +} + +/* + * Check if string entry is filtered: + * * key doesn't exist -> user didn't request -> not filtered + */ +static bool rd_check_is_string_filtered(struct rd *rd, const char *key, + const char *val) +{ + bool key_is_filtered = false; + struct filter_entry *fe; + char *p = NULL; + char *str; + + list_for_each_entry(fe, &rd->filter_list, list) { + if (!strcmpx(fe->key, key)) { + /* We found the key */ + p = strdup(fe->value); + key_is_filtered = true; + if (!p) { + /* + * Something extremely wrong if we fail + * to allocate small amount of bytes. + */ + pr_err("Found key, but failed to allocate memory to store value\n"); + return key_is_filtered; + } + + /* + * Need to check if value in range + * It can come in the following formats + * and their permutations: + * str + * str1,str2 + */ + str = strtok(p, ","); + while (str) { + if (strlen(str) == strlen(val) && + !strcasecmp(str, val)) { + key_is_filtered = false; + goto out; + } + str = strtok(NULL, ","); + } + goto out; + } + } + +out: + free(p); + return key_is_filtered; +} + +/* + * Check if key is filtered: + * key doesn't exist -> user didn't request -> not filtered + */ +static bool rd_check_is_filtered(struct rd *rd, const char *key, uint32_t val) +{ + bool key_is_filtered = false; + struct filter_entry *fe; + + list_for_each_entry(fe, &rd->filter_list, list) { + uint32_t left_val = 0, fe_value = 0; + bool range_check = false; + char *p = fe->value; + + if (!strcmpx(fe->key, key)) { + /* We found the key */ + key_is_filtered = true; + /* + * Need to check if value in range + * It can come in the following formats + * (and their permutations): + * numb + * numb1,numb2 + * ,numb1,numb2 + * numb1-numb2 + * numb1,numb2-numb3,numb4-numb5 + */ + while (*p) { + if (isdigit(*p)) { + fe_value = strtol(p, &p, 10); + if (fe_value == val || + (range_check && left_val < val && + val < fe_value)) { + key_is_filtered = false; + goto out; + } + range_check = false; + } else { + if (*p == '-') { + left_val = fe_value; + range_check = true; + } + p++; + } + } + goto out; + } + } + +out: + return key_is_filtered; +} + +bool rd_is_filtered_attr(struct rd *rd, const char *key, uint32_t val, + struct nlattr *attr) +{ + if (!attr) + return rd_check_is_key_exist(rd, key); + + return rd_check_is_filtered(rd, key, val); +} + +bool rd_is_string_filtered_attr(struct rd *rd, const char *key, const char *val, + struct nlattr *attr) +{ + if (!attr) + rd_check_is_key_exist(rd, key); + + return rd_check_is_string_filtered(rd, key, val); +} + +static void filters_cleanup(struct rd *rd) +{ + struct filter_entry *fe, *tmp; + + list_for_each_entry_safe(fe, tmp, + &rd->filter_list, list) { + list_del(&fe->list); + free(fe->key); + free(fe->value); + free(fe); + } +} + +static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = { + [RDMA_NLDEV_ATTR_DEV_INDEX] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_PORT_INDEX] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_CAP_FLAGS] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_FW_VERSION] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_NODE_GUID] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_SYS_IMAGE_GUID] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_LID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_SM_LID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_LMC] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_PORT_STATE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_PORT_PHYS_STATE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_DEV_NODE_TYPE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_SUMMARY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_RES_QP] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_QP_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_LQPN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_RQPN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_RQ_PSN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_SQ_PSN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_TYPE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_STATE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_PID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_KERN_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_RES_CM_ID] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_PS] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_SRC_ADDR] = MNL_TYPE_UNSPEC, + [RDMA_NLDEV_ATTR_RES_DST_ADDR] = MNL_TYPE_UNSPEC, + [RDMA_NLDEV_ATTR_RES_CQ] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_CQ_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_CQE] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_USECNT] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_RES_POLL_CTX] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_MR] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_MR_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_RKEY] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_LKEY] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_IOVA] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_RES_MRLEN] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_RES_CTX] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_CTX_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_CTXN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_SRQ] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_SRQ_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_SRQN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_MIN_RANGE] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_MAX_RANGE] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_NDEV_INDEX] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_NDEV_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_DRIVER] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_DRIVER_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_DRIVER_STRING] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_DRIVER_PRINT_TYPE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_DRIVER_S32] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_DRIVER_U32] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_DRIVER_S64] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_DRIVER_U64] = MNL_TYPE_U64, + [RDMA_NLDEV_SYS_ATTR_NETNS_MODE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_STAT_COUNTER] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_STAT_COUNTER_ID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_STAT_HWCOUNTERS] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_STAT_MODE] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_STAT_RES] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_DEV_DIM] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_RAW] = MNL_TYPE_BINARY, + [RDMA_NLDEV_SYS_ATTR_PRIVILEGED_QKEY_MODE] = MNL_TYPE_U8, +}; + +static int rd_attr_check(const struct nlattr *attr, int *typep) +{ + int type; + + if (mnl_attr_type_valid(attr, RDMA_NLDEV_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + type = mnl_attr_get_type(attr); + + if (mnl_attr_validate(attr, nldev_policy[type]) < 0) + return MNL_CB_ERROR; + + *typep = nldev_policy[type]; + return MNL_CB_OK; +} + +int rd_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type; + + if (mnl_attr_type_valid(attr, RDMA_NLDEV_ATTR_MAX - 1) < 0) + /* We received unknown attribute */ + return MNL_CB_OK; + + type = mnl_attr_get_type(attr); + + if (mnl_attr_validate(attr, nldev_policy[type]) < 0) + return MNL_CB_ERROR; + + tb[type] = attr; + return MNL_CB_OK; +} + +int rd_dev_init_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct dev_map *dev_map; + struct rd *rd = data; + const char *dev_name; + + mnl_attr_parse(nlh, 0, rd_attr_cb, tb); + if (!tb[RDMA_NLDEV_ATTR_DEV_NAME] || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) + 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; + } + + dev_name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + + dev_map = dev_map_alloc(dev_name); + if (!dev_map) + /* The main function will cleanup the allocations */ + return MNL_CB_ERROR; + list_add_tail(&dev_map->list, &rd->dev_map_list); + + dev_map->num_ports = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); + dev_map->idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + return MNL_CB_OK; +} + +void rd_free(struct rd *rd) +{ + if (!rd) + return; + free(rd->buff); + dev_map_cleanup(rd); + filters_cleanup(rd); +} + +int rd_set_arg_to_devname(struct rd *rd) +{ + int ret = 0; + + while (!rd_no_arg(rd)) { + if (rd_argv_match(rd, "dev") || rd_argv_match(rd, "link")) { + rd_arg_inc(rd); + if (rd_no_arg(rd)) { + pr_err("No device name was supplied\n"); + ret = -EINVAL; + } + goto out; + } + rd_arg_inc(rd); + } +out: + return ret; +} + +int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd), bool strict_port) +{ + struct dev_map *dev_map; + uint32_t port; + int ret = 0; + + new_json_obj(json); + if (rd_no_arg(rd)) { + list_for_each_entry(dev_map, &rd->dev_map_list, list) { + rd->dev_idx = dev_map->idx; + port = (strict_port) ? 1 : 0; + for (; port < dev_map->num_ports + 1; port++) { + rd->port_idx = port; + ret = cb(rd); + if (ret) + goto out; + } + } + + } else { + bool is_dump_all; + + dev_map = dev_map_lookup(rd, true); + ret = get_port_from_argv(rd, &port, &is_dump_all, strict_port); + if (!dev_map || port > dev_map->num_ports || (!port && ret)) { + pr_err("Wrong device name\n"); + ret = -ENOENT; + goto out; + } + rd_arg_inc(rd); + rd->dev_idx = dev_map->idx; + rd->port_idx = port; + for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) { + ret = cb(rd); + if (ret) + goto out; + if (!is_dump_all) + /* + * We got request to show link for devname + * with port index. + */ + break; + } + } + +out: + delete_json_obj(); + return ret; +} + +int rd_exec_dev(struct rd *rd, int (*cb)(struct rd *rd)) +{ + struct dev_map *dev_map; + int ret = 0; + + new_json_obj(json); + if (rd_no_arg(rd)) { + list_for_each_entry(dev_map, &rd->dev_map_list, list) { + rd->dev_idx = dev_map->idx; + ret = cb(rd); + if (ret) + goto out; + } + } else { + dev_map = dev_map_lookup(rd, false); + if (!dev_map) { + pr_err("Wrong device name - %s\n", rd_argv(rd)); + ret = -ENOENT; + goto out; + } + rd_arg_inc(rd); + rd->dev_idx = dev_map->idx; + ret = cb(rd); + } +out: + delete_json_obj(); + + return ret; +} + +int rd_exec_require_dev(struct rd *rd, int (*cb)(struct rd *rd)) +{ + if (rd_no_arg(rd)) { + pr_err("Please provide device name.\n"); + return -EINVAL; + } + + return rd_exec_dev(rd, cb); +} + +int rd_exec_cmd(struct rd *rd, const struct rd_cmd *cmds, const char *str) +{ + const struct rd_cmd *c; + + /* First argument in objs table is default variant */ + if (rd_no_arg(rd)) + return cmds->func(rd); + + for (c = cmds + 1; c->cmd; ++c) { + if (rd_argv_match(rd, c->cmd)) { + /* Move to next argument */ + rd_arg_inc(rd); + return c->func(rd); + } + } + + pr_err("Unknown %s '%s'.\n", str, rd_argv(rd)); + return 0; +} + +void rd_prepare_msg(struct rd *rd, uint32_t cmd, uint32_t *seq, uint16_t flags) +{ + *seq = time(NULL); + + rd->nlh = mnl_nlmsg_put_header(rd->buff); + rd->nlh->nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, cmd); + rd->nlh->nlmsg_seq = *seq; + rd->nlh->nlmsg_flags = flags; +} + +int rd_send_msg(struct rd *rd) +{ + int ret; + + rd->nl = mnlu_socket_open(NETLINK_RDMA); + if (!rd->nl) { + pr_err("Failed to open NETLINK_RDMA socket\n"); + return -ENODEV; + } + + ret = mnl_socket_sendto(rd->nl, rd->nlh, rd->nlh->nlmsg_len); + if (ret < 0) { + pr_err("Failed to send to socket with err %d\n", ret); + goto err; + } + return 0; + +err: + mnl_socket_close(rd->nl); + return ret; +} + +int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, unsigned int seq) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + ret = mnlu_socket_recv_run(rd->nl, seq, buf, MNL_SOCKET_BUFFER_SIZE, + callback, data); + if (ret < 0 && !rd->suppress_errors) + perror("error"); + return ret; +} + +static int null_cb(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_OK; +} + +int rd_sendrecv_msg(struct rd *rd, unsigned int seq) +{ + int ret; + + ret = rd_send_msg(rd); + if (!ret) + ret = rd_recv_msg(rd, null_cb, rd, seq); + return ret; +} + +static struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name) +{ + struct dev_map *dev_map; + + list_for_each_entry(dev_map, &rd->dev_map_list, list) + if (strcmp(dev_name, dev_map->dev_name) == 0) + return dev_map; + + return NULL; +} + +struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index) +{ + struct dev_map *dev_map; + char *dev_name; + char *slash; + + if (rd_no_arg(rd)) + return NULL; + + dev_name = strdup(rd_argv(rd)); + if (!dev_name) + return NULL; + + if (allow_port_index) { + slash = strrchr(dev_name, '/'); + if (slash) + *slash = '\0'; + } + + dev_map = _dev_map_lookup(rd, dev_name); + free(dev_name); + return dev_map; +} + +#define nla_type(attr) ((attr)->nla_type & NLA_TYPE_MASK) + +/* End of device object always print a newline */ +void newline(void) +{ + putchar('\n'); + fflush(stdout); +} + +/* End of partial multi-line segment of a device object */ +void newline_indent(void) +{ + if (!is_json_context()) + printf("%s ", _SL_); +} + +static int print_driver_string(const char *key_str, const char *val_str) +{ + print_string(PRINT_ANY, key_str, key_str, val_str); + print_string(PRINT_FP, NULL, " %s ", val_str); + return 0; +} + +static int print_driver_s32(const char *key_str, int32_t val, + enum rdma_nldev_print_type print_type) +{ + if (!is_json_context()) { + switch (print_type) { + case RDMA_NLDEV_PRINT_TYPE_UNSPEC: + return pr_out("%s %d ", key_str, val); + case RDMA_NLDEV_PRINT_TYPE_HEX: + return pr_out("%s 0x%x ", key_str, val); + default: + return -EINVAL; + } + } + print_int(PRINT_JSON, key_str, NULL, val); + return 0; +} + +static int print_driver_u32(const char *key_str, uint32_t val, + enum rdma_nldev_print_type print_type) +{ + if (!is_json_context()) { + switch (print_type) { + case RDMA_NLDEV_PRINT_TYPE_UNSPEC: + return pr_out("%s %u ", key_str, val); + case RDMA_NLDEV_PRINT_TYPE_HEX: + return pr_out("%s 0x%x ", key_str, val); + default: + return -EINVAL; + } + } + print_int(PRINT_JSON, key_str, NULL, val); + return 0; +} + +static int print_driver_s64(const char *key_str, int64_t val, + enum rdma_nldev_print_type print_type) +{ + if (!is_json_context()) { + switch (print_type) { + case RDMA_NLDEV_PRINT_TYPE_UNSPEC: + return pr_out("%s %" PRId64 " ", key_str, val); + case RDMA_NLDEV_PRINT_TYPE_HEX: + return pr_out("%s 0x%" PRIx64 " ", key_str, val); + default: + return -EINVAL; + } + } + print_int(PRINT_JSON, key_str, NULL, val); + return 0; +} + +static int print_driver_u64(const char *key_str, uint64_t val, + enum rdma_nldev_print_type print_type) +{ + if (!is_json_context()) { + switch (print_type) { + case RDMA_NLDEV_PRINT_TYPE_UNSPEC: + return pr_out("%s %" PRIu64 " ", key_str, val); + case RDMA_NLDEV_PRINT_TYPE_HEX: + return pr_out("%s 0x%" PRIx64 " ", key_str, val); + default: + return -EINVAL; + } + } + print_int(PRINT_JSON, key_str, NULL, val); + return 0; +} + +static int print_driver_entry(struct nlattr *key_attr, struct nlattr *val_attr, + enum rdma_nldev_print_type print_type) +{ + int attr_type = nla_type(val_attr); + int ret = -EINVAL; + char *key_str; + + if (asprintf(&key_str, "drv_%s", mnl_attr_get_str(key_attr)) == -1) + return -ENOMEM; + + switch (attr_type) { + case RDMA_NLDEV_ATTR_DRIVER_STRING: + ret = print_driver_string(key_str, mnl_attr_get_str(val_attr)); + break; + case RDMA_NLDEV_ATTR_DRIVER_S32: + ret = print_driver_s32(key_str, mnl_attr_get_u32(val_attr), print_type); + break; + case RDMA_NLDEV_ATTR_DRIVER_U32: + ret = print_driver_u32(key_str, mnl_attr_get_u32(val_attr), print_type); + break; + case RDMA_NLDEV_ATTR_DRIVER_S64: + ret = print_driver_s64(key_str, mnl_attr_get_u64(val_attr), print_type); + break; + case RDMA_NLDEV_ATTR_DRIVER_U64: + ret = print_driver_u64(key_str, mnl_attr_get_u64(val_attr), print_type); + break; + } + free(key_str); + return ret; +} + +void print_raw_data(struct rd *rd, struct nlattr **nla_line) +{ + uint8_t *data; + uint32_t len; + int i = 0; + + if (!rd->show_raw) + return; + + len = mnl_attr_get_payload_len(nla_line[RDMA_NLDEV_ATTR_RES_RAW]); + data = mnl_attr_get_payload(nla_line[RDMA_NLDEV_ATTR_RES_RAW]); + open_json_array(PRINT_JSON, "data"); + while (i < len) { + print_uint(PRINT_ANY, NULL, "%d", data[i]); + i++; + } + close_json_array(PRINT_ANY, ">"); +} + +void print_driver_table(struct rd *rd, struct nlattr *tb) +{ + int print_type = RDMA_NLDEV_PRINT_TYPE_UNSPEC; + struct nlattr *tb_entry, *key = NULL, *val; + int type, cc = 0; + int ret; + + if (!rd->show_driver_details || !tb) + return; + + newline_indent(); + + /* + * Driver attrs are tuples of {key, [print-type], value}. + * The key must be a string. If print-type is present, it + * defines an alternate printf format type vs the native format + * for the attribute. And the value can be any available + * driver type. + */ + mnl_attr_for_each_nested(tb_entry, tb) { + + if (cc > MAX_LINE_LENGTH) { + newline_indent(); + cc = 0; + } + if (rd_attr_check(tb_entry, &type) != MNL_CB_OK) + return; + if (!key) { + if (type != MNL_TYPE_NUL_STRING) + return; + key = tb_entry; + } else if (type == MNL_TYPE_U8) { + print_type = mnl_attr_get_u8(tb_entry); + } else { + val = tb_entry; + ret = print_driver_entry(key, val, print_type); + if (ret < 0) + return; + cc += ret; + print_type = RDMA_NLDEV_PRINT_TYPE_UNSPEC; + key = NULL; + } + } + return; +} |