summaryrefslogtreecommitdiffstats
path: root/rdma
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:14:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:14:35 +0000
commit9b8a97db9ec4b795e29e72289005fbc58484ebeb (patch)
treee24ca2d68215e57b4759fe5c032629821eabb250 /rdma
parentInitial commit. (diff)
downloadiproute2-9b8a97db9ec4b795e29e72289005fbc58484ebeb.tar.xz
iproute2-9b8a97db9ec4b795e29e72289005fbc58484ebeb.zip
Adding upstream version 6.8.0.upstream/6.8.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'rdma')
-rw-r--r--rdma/.gitignore1
-rw-r--r--rdma/Makefile22
-rw-r--r--rdma/dev.c381
-rw-r--r--rdma/include/uapi/rdma/ib_user_sa.h77
-rw-r--r--rdma/include/uapi/rdma/ib_user_verbs.h1364
-rw-r--r--rdma/include/uapi/rdma/rdma_netlink.h599
-rw-r--r--rdma/include/uapi/rdma/rdma_user_cm.h341
-rw-r--r--rdma/link.c382
-rw-r--r--rdma/rdma.c177
-rw-r--r--rdma/rdma.h144
-rw-r--r--rdma/res-cmid.c250
-rw-r--r--rdma/res-cq.c181
-rw-r--r--rdma/res-ctx.c103
-rw-r--r--rdma/res-mr.c146
-rw-r--r--rdma/res-pd.c130
-rw-r--r--rdma/res-qp.c233
-rw-r--r--rdma/res-srq.c300
-rw-r--r--rdma/res.c258
-rw-r--r--rdma/res.h196
-rw-r--r--rdma/stat-mr.c86
-rw-r--r--rdma/stat.c1131
-rw-r--r--rdma/stat.h24
-rw-r--r--rdma/sys.c182
-rw-r--r--rdma/utils.c958
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;
+}