From afdc119410efe2a5e826c660446b1e4e1a72793d Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 20 Aug 2017 12:58:21 +0300 Subject: [PATCH 01/15] utils: Move BIT macro to common header BIT() macro was implemented and used by devlink for now, but following patches of rdmatool will reuse the same macro, so put it in common header file. Signed-off-by: Leon Romanovsky --- devlink/devlink.c | 2 +- include/utils.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index f9bc16c3..7602970b 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -25,6 +25,7 @@ #include "list.h" #include "mnlg.h" #include "json_writer.h" +#include "utils.h" #define ESWITCH_MODE_LEGACY "legacy" #define ESWITCH_MODE_SWITCHDEV "switchdev" @@ -160,7 +161,6 @@ static void ifname_map_free(struct ifname_map *ifname_map) free(ifname_map); } -#define BIT(nr) (1UL << (nr)) #define DL_OPT_HANDLE BIT(0) #define DL_OPT_HANDLEP BIT(1) #define DL_OPT_PORT_TYPE BIT(2) diff --git a/include/utils.h b/include/utils.h index 6080b962..7a3b3fd2 100644 --- a/include/utils.h +++ b/include/utils.h @@ -195,6 +195,8 @@ static inline void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies) int print_timestamp(FILE *fp); void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n); +#define BIT(nr) (1UL << (nr)) + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)])) From 74bd75c2b68d8da63465f51d0c60f55e29d609bd Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 20 Aug 2017 12:58:22 +0300 Subject: [PATCH 02/15] rdma: Add basic infrastructure for RDMA tool RDMA devices are cross-functional devices from one side, but very tailored for the specific markets from another. Such diversity caused to spread of RDMA related configuration across various tools, e.g. devlink, ip, ethtool, ib specific and vendor specific solutions. This patch adds ability to fill device and port information by reading RDMA netlink. Signed-off-by: Leon Romanovsky --- Makefile | 2 +- include/rdma/rdma_netlink.h | 307 ++++++++++++++++++++++++++++++++++++ rdma/.gitignore | 1 + rdma/Makefile | 22 +++ rdma/rdma.c | 116 ++++++++++++++ rdma/rdma.h | 71 +++++++++ rdma/utils.c | 217 +++++++++++++++++++++++++ 7 files changed, 735 insertions(+), 1 deletion(-) create mode 100644 include/rdma/rdma_netlink.h create mode 100644 rdma/.gitignore create mode 100644 rdma/Makefile create mode 100644 rdma/rdma.c create mode 100644 rdma/rdma.h create mode 100644 rdma/utils.c diff --git a/Makefile b/Makefile index 1f88f7f5..dbb4a4af 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ WFLAGS += -Wmissing-declarations -Wold-style-definition -Wformat=2 CFLAGS := $(WFLAGS) $(CCOPTS) -I../include $(DEFINES) $(CFLAGS) YACCFLAGS = -d -t -v -SUBDIRS=lib ip tc bridge misc netem genl tipc devlink man +SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma man LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a LDLIBS += $(LIBNETLINK) diff --git a/include/rdma/rdma_netlink.h b/include/rdma/rdma_netlink.h new file mode 100644 index 00000000..861440a8 --- /dev/null +++ b/include/rdma/rdma_netlink.h @@ -0,0 +1,307 @@ +#ifndef _UAPI_RDMA_NETLINK_H +#define _UAPI_RDMA_NETLINK_H + +#include + +enum { + RDMA_NL_RDMA_CM = 1, + RDMA_NL_IWCM, + RDMA_NL_RSVD, + RDMA_NL_LS, /* RDMA Local Services */ + RDMA_NL_NLDEV, /* RDMA device interface */ + RDMA_NL_NUM_CLIENTS +}; + +enum { + RDMA_NL_GROUP_CM = 1, + RDMA_NL_GROUP_IWPM, + 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) + +enum { + RDMA_NL_RDMA_CM_ID_STATS = 0, + RDMA_NL_RDMA_CM_NUM_OPS +}; + +enum { + RDMA_NL_RDMA_CM_ATTR_SRC_ADDR = 1, + RDMA_NL_RDMA_CM_ATTR_DST_ADDR, + RDMA_NL_RDMA_CM_NUM_ATTR, +}; + +/* 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_NUM_OPS +}; + +struct rdma_cm_id_stats { + __u32 qp_num; + __u32 bound_dev_if; + __u32 port_space; + __s32 pid; + __u8 cm_state; + __u8 node_type; + __u8 port_num; + __u8 qp_type; +}; + +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_MAPPED_LOC_ADDR, + IWPM_NLA_RMANAGE_MAPPING_ERR, + IWPM_NLA_RMANAGE_MAPPING_MAX +}; + +#define IWPM_NLA_MANAGE_MAPPING_MAX 3 +#define IWPM_NLA_QUERY_MAPPING_MAX 4 +#define IWPM_NLA_MAPINFO_SEND_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_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_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 +}; + +/* + * 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_NEW, + RDMA_NLDEV_CMD_DEL, + + RDMA_NLDEV_CMD_PORT_GET, /* can dump */ + RDMA_NLDEV_CMD_PORT_SET, + RDMA_NLDEV_CMD_PORT_NEW, + RDMA_NLDEV_CMD_PORT_DEL, + + RDMA_NLDEV_NUM_OPS +}; + +enum rdma_nldev_attr { + /* don't change the order or add anything between, this is ABI! */ + 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 + */ + 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_MAX +}; +#endif /* _UAPI_RDMA_NETLINK_H */ diff --git a/rdma/.gitignore b/rdma/.gitignore new file mode 100644 index 00000000..51fb172b --- /dev/null +++ b/rdma/.gitignore @@ -0,0 +1 @@ +rdma diff --git a/rdma/Makefile b/rdma/Makefile new file mode 100644 index 00000000..64da2142 --- /dev/null +++ b/rdma/Makefile @@ -0,0 +1,22 @@ +include ../Config + +ifeq ($(HAVE_MNL),y) + +RDMA_OBJ = rdma.o utils.o + +TARGETS=rdma +CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags) +LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) + +endif + +all: $(TARGETS) $(LIBS) + +rdma: $(RDMA_OBJ) $(LIBS) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(RDMA_OBJ) $(TARGETS) diff --git a/rdma/rdma.c b/rdma/rdma.c new file mode 100644 index 00000000..d850e396 --- /dev/null +++ b/rdma/rdma.c @@ -0,0 +1,116 @@ +/* + * rdma.c RDMA tool + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Leon Romanovsky + */ + +#include "rdma.h" +#include "SNAPSHOT.h" + +static void help(char *name) +{ + pr_out("Usage: %s [ OPTIONS ] OBJECT { COMMAND | help }\n" + "where OBJECT := { help }\n" + " OPTIONS := { -V[ersion] | -d[etails]}\n", name); +} + +static int cmd_help(struct rd *rd) +{ + help(rd->filename); + return 0; +} + +static int rd_cmd(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, cmd_help }, + { "help", cmd_help }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "object"); +} + +static int rd_init(struct rd *rd, int argc, char **argv, char *filename) +{ + uint32_t seq; + int ret; + + rd->filename = filename; + rd->argc = argc; + rd->argv = argv; + INIT_LIST_HEAD(&rd->dev_map_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_free(struct rd *rd) +{ + free(rd->buff); + rd_free_devmap(rd); +} + +int main(int argc, char **argv) +{ + static const struct option long_options[] = { + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { "details", no_argument, NULL, 'd' }, + { NULL, 0, NULL, 0 } + }; + bool show_details = false; + char *filename; + struct rd rd; + int opt; + int err; + + filename = basename(argv[0]); + + while ((opt = getopt_long(argc, argv, "Vhd", + long_options, NULL)) >= 0) { + switch (opt) { + case 'V': + printf("%s utility, iproute2-ss%s\n", + filename, SNAPSHOT); + return EXIT_SUCCESS; + case 'd': + show_details = true; + break; + case 'h': + help(filename); + return EXIT_SUCCESS; + default: + pr_err("Unknown option.\n"); + help(filename); + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + err = rd_init(&rd, argc, argv, filename); + if (err) + goto out; + + rd.show_details = show_details; + err = rd_cmd(&rd); +out: + /* Always cleanup */ + rd_free(&rd); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/rdma/rdma.h b/rdma/rdma.h new file mode 100644 index 00000000..2f655118 --- /dev/null +++ b/rdma/rdma.h @@ -0,0 +1,71 @@ +/* + * rdma.c RDMA tool + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Leon Romanovsky + */ +#ifndef _RDMA_TOOL_H_ +#define _RDMA_TOOL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" + +#define pr_err(args...) fprintf(stderr, ##args) +#define pr_out(args...) fprintf(stdout, ##args) + +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; + bool show_details; + struct list_head dev_map_list; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + char *buff; +}; + +struct rd_cmd { + const char *cmd; + int (*func)(struct rd *rd); +}; + +/* + * Parser interface + */ +bool rd_no_arg(struct rd *rd); +void rd_arg_inc(struct rd *rd); + +int rd_exec_cmd(struct rd *rd, const struct rd_cmd *c, const char *str); + +/* + * Device manipulation + */ +void rd_free_devmap(struct rd *rd); + +/* + * Netlink + */ +int rd_send_msg(struct rd *rd); +int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, uint32_t 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); +#endif /* _RDMA_TOOL_H_ */ diff --git a/rdma/utils.c b/rdma/utils.c new file mode 100644 index 00000000..9bd7418f --- /dev/null +++ b/rdma/utils.c @@ -0,0 +1,217 @@ +/* + * utils.c RDMA tool + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Leon Romanovsky + */ + +#include "rdma.h" + +static int rd_argc(struct rd *rd) +{ + return rd->argc; +} + +static char *rd_argv(struct rd *rd) +{ + if (!rd_argc(rd)) + return NULL; + return *rd->argv; +} + +static 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; +} + +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); + + return dev_map; +} + +static void dev_map_free(struct dev_map *dev_map) +{ + if (!dev_map) + return; + + free(dev_map->dev_name); + free(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); + dev_map_free(dev_map); + } +} + +static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = { + [RDMA_NLDEV_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_PORT_INDEX] = MNL_TYPE_U32, +}; + +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) < 0) + return MNL_CB_ERROR; + + 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_devmap(struct rd *rd) +{ + if (!rd) + return; + dev_map_cleanup(rd); +} + +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 = mnl_socket_open(NETLINK_RDMA); + if (!rd->nl) { + pr_err("Failed to open NETLINK_RDMA socket\n"); + return -ENODEV; + } + + ret = mnl_socket_bind(rd->nl, 0, MNL_SOCKET_AUTOPID); + if (ret < 0) { + pr_err("Failed to bind socket with err %d\n", ret); + goto err; + } + + 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) +{ + int ret; + unsigned int portid; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + portid = mnl_socket_get_portid(rd->nl); + do { + ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf)); + if (ret <= 0) + break; + + ret = mnl_cb_run(buf, ret, seq, portid, callback, data); + } while (ret > 0); + + mnl_socket_close(rd->nl); + return ret; +} From 40df8263a0f047d35f0b24fd35eba1b22f0107ab Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 20 Aug 2017 12:58:23 +0300 Subject: [PATCH 03/15] rdma: Add dev object Device (dev) object represents struct ib_device to the user space. Device properties: * Device capabilities * FW version to the device output * node_guid and sys_image_guid * node_type Signed-off-by: Leon Romanovsky --- rdma/Makefile | 2 +- rdma/dev.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++ rdma/rdma.c | 3 +- rdma/rdma.h | 17 ++++ rdma/utils.c | 54 +++++++++++- 5 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 rdma/dev.c diff --git a/rdma/Makefile b/rdma/Makefile index 64da2142..123d7ac5 100644 --- a/rdma/Makefile +++ b/rdma/Makefile @@ -2,7 +2,7 @@ include ../Config ifeq ($(HAVE_MNL),y) -RDMA_OBJ = rdma.o utils.o +RDMA_OBJ = rdma.o utils.o dev.o TARGETS=rdma CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags) diff --git a/rdma/dev.c b/rdma/dev.c new file mode 100644 index 00000000..f6b55bae --- /dev/null +++ b/rdma/dev.c @@ -0,0 +1,230 @@ +/* + * dev.c RDMA tool + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Leon Romanovsky + */ + +#include "rdma.h" + +static int dev_help(struct rd *rd) +{ + pr_out("Usage: %s dev show [DEV]\n", rd->filename); + return 0; +} + +static const char *dev_caps_to_str(uint32_t idx) +{ +#define RDMA_DEV_FLAGS(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) \ + x(SG_GAPS_REG, 32) \ + x(VIRTUAL_FUNCTION, 33) \ + x(RAW_SCATTER_FCS, 34) \ + x(RDMA_NETDEV_OPA_VNIC, 35) + + enum { RDMA_DEV_FLAGS(RDMA_BITMAP_ENUM) }; + + static const char * const + rdma_dev_names[] = { RDMA_DEV_FLAGS(RDMA_BITMAP_NAMES) }; + #undef RDMA_DEV_FLAGS + + if (idx < ARRAY_SIZE(rdma_dev_names) && rdma_dev_names[idx]) + return rdma_dev_names[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]); + + pr_out("\n caps: <"); + for (idx = 0; caps; idx++) { + if (caps & 0x1) { + pr_out("%s", dev_caps_to_str(idx)); + if (caps >> 0x1) + pr_out(", "); + } + caps >>= 0x1; + } + + pr_out(">"); +} + +static void dev_print_fw(struct nlattr **tb) +{ + if (!tb[RDMA_NLDEV_ATTR_FW_VERSION]) + return; + + pr_out("fw %s ", + mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_FW_VERSION])); +} + +static void dev_print_node_guid(struct nlattr **tb) +{ + uint64_t node_guid; + + if (!tb[RDMA_NLDEV_ATTR_NODE_GUID]) + return; + + node_guid = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_NODE_GUID]); + rd_print_u64("node_guid", node_guid); +} + +static void dev_print_sys_image_guid(struct nlattr **tb) +{ + uint64_t sys_image_guid; + + if (!tb[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID]) + return; + + sys_image_guid = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID]); + rd_print_u64("sys_image_guid", sys_image_guid); +} + +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_dp" }; + 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) +{ + 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]); + pr_out("node_type %s ", node_type_to_str(node_type)); +} + +static int dev_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + + 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; + + pr_out("%u: %s: ", + mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]), + mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME])); + dev_print_node_type(tb); + dev_print_fw(tb); + dev_print_node_guid(tb); + dev_print_sys_image_guid(tb); + if (rd->show_details) + dev_print_caps(tb); + + pr_out("\n"); + 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; + + return rd_recv_msg(rd, dev_parse_cb, rd, seq); +} + +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_show(struct rd *rd) +{ + struct dev_map *dev_map; + int ret = 0; + + if (rd_no_arg(rd)) { + list_for_each_entry(dev_map, &rd->dev_map_list, list) { + rd->dev_idx = dev_map->idx; + ret = dev_one_show(rd); + if (ret) + return ret; + } + + } else { + dev_map = dev_map_lookup(rd, false); + if (!dev_map) { + pr_err("Wrong device name\n"); + return -ENOENT; + } + rd_arg_inc(rd); + rd->dev_idx = dev_map->idx; + ret = dev_one_show(rd); + } + return ret; +} + +int cmd_dev(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, dev_show }, + { "show", dev_show }, + { "list", dev_show }, + { "help", dev_help }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "dev command"); +} diff --git a/rdma/rdma.c b/rdma/rdma.c index d850e396..9c2bdc8f 100644 --- a/rdma/rdma.c +++ b/rdma/rdma.c @@ -15,7 +15,7 @@ static void help(char *name) { pr_out("Usage: %s [ OPTIONS ] OBJECT { COMMAND | help }\n" - "where OBJECT := { help }\n" + "where OBJECT := { dev | help }\n" " OPTIONS := { -V[ersion] | -d[etails]}\n", name); } @@ -30,6 +30,7 @@ static int rd_cmd(struct rd *rd) const struct rd_cmd cmds[] = { { NULL, cmd_help }, { "help", cmd_help }, + { "dev", cmd_dev }, { 0 } }; diff --git a/rdma/rdma.h b/rdma/rdma.h index 2f655118..36b047d3 100644 --- a/rdma/rdma.h +++ b/rdma/rdma.h @@ -20,10 +20,14 @@ #include #include "list.h" +#include "utils.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, + struct dev_map { struct list_head list; char *dev_name; @@ -37,6 +41,8 @@ struct rd { char *filename; bool show_details; struct list_head dev_map_list; + uint32_t dev_idx; + uint32_t port_idx; struct mnl_socket *nl; struct nlmsghdr *nlh; char *buff; @@ -53,12 +59,23 @@ struct rd_cmd { bool rd_no_arg(struct rd *rd); void rd_arg_inc(struct rd *rd); +char *rd_argv(struct rd *rd); +uint32_t get_port_from_argv(struct rd *rd); + +void rd_print_u64(char *name, uint64_t val); +/* + * Commands interface + */ +int cmd_dev(struct rd *rd); +int cmd_link(struct rd *rd); int rd_exec_cmd(struct rd *rd, const struct rd_cmd *c, const char *str); /* * Device manipulation */ void rd_free_devmap(struct rd *rd); +struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index); +struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name); /* * Netlink diff --git a/rdma/utils.c b/rdma/utils.c index 9bd7418f..0e32eefe 100644 --- a/rdma/utils.c +++ b/rdma/utils.c @@ -16,7 +16,7 @@ static int rd_argc(struct rd *rd) return rd->argc; } -static char *rd_argv(struct rd *rd) +char *rd_argv(struct rd *rd) { if (!rd_argc(rd)) return NULL; @@ -50,6 +50,23 @@ bool rd_no_arg(struct rd *rd) return rd_argc(rd) == 0; } +uint32_t get_port_from_argv(struct rd *rd) +{ + char *slash; + + slash = strchr(rd_argv(rd), '/'); + /* if no port found, return 0 */ + return slash ? atoi(slash + 1) : 0; +} + +void rd_print_u64(char *name, uint64_t val) +{ + uint16_t vp[4]; + + memcpy(vp, &val, sizeof(uint64_t)); + pr_out("%s %04x:%04x:%04x:%04x ", name, vp[3], vp[2], vp[1], vp[0]); +} + static struct dev_map *dev_map_alloc(const char *dev_name) { struct dev_map *dev_map; @@ -83,8 +100,14 @@ static void dev_map_cleanup(struct rd *rd) } 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_DEV_NODE_TYPE] = MNL_TYPE_U8, }; int rd_attr_cb(const struct nlattr *attr, void *data) @@ -215,3 +238,32 @@ int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, unsigned int seq) mnl_socket_close(rd->nl); return ret; } + +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; + + dev_name = strdup(rd_argv(rd)); + 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; +} From da990ab40a92ed0df5c79fa6a8fd65d0f2d40aab Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 20 Aug 2017 12:58:24 +0300 Subject: [PATCH 04/15] rdma: Add link object Link (port) object represent struct ib_port to the user space. Link properties: * Port capabilities * IB subnet prefix * LID, SM_LID and LMC * Port state * Physical state Signed-off-by: Leon Romanovsky --- rdma/Makefile | 2 +- rdma/link.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++ rdma/rdma.c | 3 +- rdma/utils.c | 5 + 4 files changed, 285 insertions(+), 2 deletions(-) create mode 100644 rdma/link.c diff --git a/rdma/Makefile b/rdma/Makefile index 123d7ac5..1a9e4b1a 100644 --- a/rdma/Makefile +++ b/rdma/Makefile @@ -2,7 +2,7 @@ include ../Config ifeq ($(HAVE_MNL),y) -RDMA_OBJ = rdma.o utils.o dev.o +RDMA_OBJ = rdma.o utils.o dev.o link.o TARGETS=rdma CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags) diff --git a/rdma/link.c b/rdma/link.c new file mode 100644 index 00000000..b0e5bee0 --- /dev/null +++ b/rdma/link.c @@ -0,0 +1,277 @@ +/* + * link.c RDMA tool + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Leon Romanovsky + */ + +#include "rdma.h" + +static int link_help(struct rd *rd) +{ + pr_out("Usage: %s link show [DEV/PORT_INDEX]\n", rd->filename); + return 0; +} + +static const char *caps_to_str(uint32_t idx) +{ +#define RDMA_PORT_FLAGS(x) \ + 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_GUIG, 11) \ + x(PKEY_SW_EXT_PORT_TRAP, 12) \ + x(EXTENDED_SPEEDS, 14) \ + 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, 23) \ + x(IP_BASED_GIDS, 26) + + enum { RDMA_PORT_FLAGS(RDMA_BITMAP_ENUM) }; + + static const char * const + rdma_port_names[] = { RDMA_PORT_FLAGS(RDMA_BITMAP_NAMES) }; + #undef RDMA_PORT_FLAGS + + if (idx < ARRAY_SIZE(rdma_port_names) && rdma_port_names[idx]) + return rdma_port_names[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]); + + pr_out("\n caps: <"); + for (idx = 0; caps; idx++) { + if (caps & 0x1) { + pr_out("%s", caps_to_str(idx)); + if (caps >> 0x1) + pr_out(", "); + } + caps >>= 0x1; + } + + pr_out(">"); +} + +static void link_print_subnet_prefix(struct nlattr **tb) +{ + uint64_t subnet_prefix; + + if (!tb[RDMA_NLDEV_ATTR_SUBNET_PREFIX]) + return; + + subnet_prefix = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_SUBNET_PREFIX]); + rd_print_u64("subnet_prefix", subnet_prefix); +} + +static void link_print_lid(struct nlattr **tb) +{ + if (!tb[RDMA_NLDEV_ATTR_LID]) + return; + + pr_out("lid %u ", + mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_LID])); +} + +static void link_print_sm_lid(struct nlattr **tb) +{ + if (!tb[RDMA_NLDEV_ATTR_SM_LID]) + return; + + pr_out("sm_lid %u ", + mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_SM_LID])); +} + +static void link_print_lmc(struct nlattr **tb) +{ + if (!tb[RDMA_NLDEV_ATTR_LMC]) + return; + + pr_out("lmc %u ", mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_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]); + pr_out("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]); + pr_out("physical_state %s ", phys_state_to_str(phys_state)); +} + +static int link_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + + 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; + } + + pr_out("%u/%u: %s/%u: ", + mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]), + mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]), + mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]), + mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX])); + 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); + if (rd->show_details) + link_print_caps(tb); + + pr_out("\n"); + 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; + + return rd_recv_msg(rd, link_parse_cb, rd, seq); +} + +static int link_one_show(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, link_no_args}, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int link_show(struct rd *rd) +{ + struct dev_map *dev_map; + uint32_t port; + int ret; + + if (rd_no_arg(rd)) { + list_for_each_entry(dev_map, &rd->dev_map_list, list) { + rd->dev_idx = dev_map->idx; + for (port = 1; port < dev_map->num_ports + 1; port++) { + rd->port_idx = port; + ret = link_one_show(rd); + if (ret) + return ret; + } + } + + } else { + dev_map = dev_map_lookup(rd, true); + port = get_port_from_argv(rd); + if (!dev_map || port > dev_map->num_ports) { + pr_err("Wrong device name\n"); + return -ENOENT; + } + rd_arg_inc(rd); + rd->dev_idx = dev_map->idx; + rd->port_idx = port ? : 1; + for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) { + ret = link_one_show(rd); + if (ret) + return ret; + if (port) + /* + * We got request to show link for devname + * with port index. + */ + break; + } + } + return 0; +} + +int cmd_link(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, link_show }, + { "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 index 9c2bdc8f..74c09e8b 100644 --- a/rdma/rdma.c +++ b/rdma/rdma.c @@ -15,7 +15,7 @@ static void help(char *name) { pr_out("Usage: %s [ OPTIONS ] OBJECT { COMMAND | help }\n" - "where OBJECT := { dev | help }\n" + "where OBJECT := { dev | link | help }\n" " OPTIONS := { -V[ersion] | -d[etails]}\n", name); } @@ -31,6 +31,7 @@ static int rd_cmd(struct rd *rd) { NULL, cmd_help }, { "help", cmd_help }, { "dev", cmd_dev }, + { "link", cmd_link }, { 0 } }; diff --git a/rdma/utils.c b/rdma/utils.c index 0e32eefe..91d05271 100644 --- a/rdma/utils.c +++ b/rdma/utils.c @@ -107,6 +107,11 @@ static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [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, }; From ab6e2b7bdb949be6df1ac329bce22879f196fcea Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 20 Aug 2017 12:58:25 +0300 Subject: [PATCH 05/15] rdma: Add json and pretty outputs Signed-off-by: Leon Romanovsky --- rdma/rdma.c | 31 ++++++++++++++++++++++++++++--- rdma/rdma.h | 4 ++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/rdma/rdma.c b/rdma/rdma.c index 74c09e8b..f9f4f2a2 100644 --- a/rdma/rdma.c +++ b/rdma/rdma.c @@ -16,7 +16,7 @@ static void help(char *name) { pr_out("Usage: %s [ OPTIONS ] OBJECT { COMMAND | help }\n" "where OBJECT := { dev | link | help }\n" - " OPTIONS := { -V[ersion] | -d[etails]}\n", name); + " OPTIONS := { -V[ersion] | -d[etails] | -j[son] | -p[retty]}\n", name); } static int cmd_help(struct rd *rd) @@ -47,6 +47,16 @@ static int rd_init(struct rd *rd, int argc, char **argv, char *filename) rd->argc = argc; rd->argv = argv; INIT_LIST_HEAD(&rd->dev_map_list); + + if (rd->json_output) { + rd->jw = jsonw_new(stdout); + if (!rd->jw) { + pr_err("Failed to create JSON writer\n"); + return -ENOMEM; + } + jsonw_pretty(rd->jw, rd->pretty_output); + } + rd->buff = malloc(MNL_SOCKET_BUFFER_SIZE); if (!rd->buff) return -ENOMEM; @@ -62,6 +72,8 @@ static int rd_init(struct rd *rd, int argc, char **argv, char *filename) static void rd_free(struct rd *rd) { + if (rd->json_output) + jsonw_destroy(&rd->jw); free(rd->buff); rd_free_devmap(rd); } @@ -71,10 +83,14 @@ 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' }, + { "pretty", no_argument, NULL, 'p' }, { "details", no_argument, NULL, 'd' }, { NULL, 0, NULL, 0 } }; + bool pretty_output = false; bool show_details = false; + bool json_output = false; char *filename; struct rd rd; int opt; @@ -82,16 +98,22 @@ int main(int argc, char **argv) filename = basename(argv[0]); - while ((opt = getopt_long(argc, argv, "Vhd", + while ((opt = getopt_long(argc, argv, "Vhdpj", long_options, NULL)) >= 0) { switch (opt) { case 'V': printf("%s utility, iproute2-ss%s\n", filename, SNAPSHOT); return EXIT_SUCCESS; + case 'p': + pretty_output = true; + break; case 'd': show_details = true; break; + case 'j': + json_output = true; + break; case 'h': help(filename); return EXIT_SUCCESS; @@ -105,11 +127,14 @@ int main(int argc, char **argv) argc -= optind; argv += optind; + rd.show_details = show_details; + rd.json_output = json_output; + rd.pretty_output = pretty_output; + err = rd_init(&rd, argc, argv, filename); if (err) goto out; - rd.show_details = show_details; err = rd_cmd(&rd); out: /* Always cleanup */ diff --git a/rdma/rdma.h b/rdma/rdma.h index 36b047d3..4c564fef 100644 --- a/rdma/rdma.h +++ b/rdma/rdma.h @@ -21,6 +21,7 @@ #include "list.h" #include "utils.h" +#include "json_writer.h" #define pr_err(args...) fprintf(stderr, ##args) #define pr_out(args...) fprintf(stdout, ##args) @@ -46,6 +47,9 @@ struct rd { struct mnl_socket *nl; struct nlmsghdr *nlh; char *buff; + json_writer_t *jw; + bool json_output; + bool pretty_output; }; struct rd_cmd { From ef353e2e947092f2a2dbcf2ea75f0b4b5a670cda Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 20 Aug 2017 12:58:26 +0300 Subject: [PATCH 06/15] rdma: Implement json output for dev object The example output for machine with two devices root@mtr-leonro:~# rdma dev -j -p [{ "ifindex": 1, "ifname": "mlx5_0", "node_type": "ca", "fw": "2.8.9999", "node_guid": "5254:00c0:fe12:3457", "sys_image_guid": 5254:00c0:fe12:3457", "caps": [ "BAD_PKEY_CNTR", "BAD_QKEY_CNTR", "CHANGE_PHY_POR", "PORT_ACTIVE_EVENT", "SYS_IMAGE_GUID", "RC_RNR_NAK_GEN", "MEM_WINDOW", "UD_IP_CSUM", "UD_TSO", "XRC", "MEM_MGT_EXTENSIONS", "BLOCK_MULTICAST_LOOPBACK", "MEM_WINDOW_TYPE_2B", "RAW_IP_CSUM", "MANAGED_FLOW_STEERING", "RESIZE_MAX_WR" ] },{ "ifindex": 2, "ifname": mlx5_1, "node_type": "ca", "fw": "2.8.9999", "node_guid": "5254:00c0:fe12:3458", "sys_image_guid": "5254:00c0:fe12:3458", "caps": [ "BAD_PKEY_CNTR", "BAD_QKEY_CNTR", "CHANGE_PHY_POR", "PORT_ACTIVE_EVENT", "SYS_IMAGE_GUID", "RC_RNR_NAK_GEN", "MEM_WINDOW", "UD_IP_CSUM", "UD_TSO", "XRC", "MEM_MGT_EXTENSIONS", "BLOCK_MULTICAST_LOOPBACK", "MEM_WINDOW_TYPE_2B", "RAW_IP_CSUM", "MANAGED_FLOW_STEERING", "RESIZE_MAX_WR" ] } ] Signed-off-by: Leon Romanovsky --- rdma/dev.c | 112 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 29 deletions(-) diff --git a/rdma/dev.c b/rdma/dev.c index f6b55bae..9fadf3ac 100644 --- a/rdma/dev.c +++ b/rdma/dev.c @@ -66,7 +66,7 @@ static const char *dev_caps_to_str(uint32_t idx) return "UNKNOWN"; } -static void dev_print_caps(struct nlattr **tb) +static void dev_print_caps(struct rd *rd, struct nlattr **tb) { uint64_t caps; uint32_t idx; @@ -76,48 +76,78 @@ static void dev_print_caps(struct nlattr **tb) caps = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_CAP_FLAGS]); - pr_out("\n caps: <"); + if (rd->json_output) { + jsonw_name(rd->jw, "caps"); + jsonw_start_array(rd->jw); + } else { + pr_out("\n caps: <"); + } for (idx = 0; caps; idx++) { if (caps & 0x1) { - pr_out("%s", dev_caps_to_str(idx)); - if (caps >> 0x1) - pr_out(", "); + if (rd->json_output) { + jsonw_string(rd->jw, dev_caps_to_str(idx)); + } else { + pr_out("%s", dev_caps_to_str(idx)); + if (caps >> 0x1) + pr_out(", "); + } } caps >>= 0x1; } - pr_out(">"); + if (rd->json_output) + jsonw_end_array(rd->jw); + else + pr_out(">"); } -static void dev_print_fw(struct nlattr **tb) +static void dev_print_fw(struct rd *rd, struct nlattr **tb) { + const char *str; if (!tb[RDMA_NLDEV_ATTR_FW_VERSION]) return; - pr_out("fw %s ", - mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_FW_VERSION])); + str = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_FW_VERSION]); + if (rd->json_output) + jsonw_string_field(rd->jw, "fw", str); + else + pr_out("fw %s ", str); } -static void dev_print_node_guid(struct nlattr **tb) +static void dev_print_node_guid(struct rd *rd, 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]); - rd_print_u64("node_guid", node_guid); + memcpy(vp, &node_guid, sizeof(uint64_t)); + snprintf(str, 32, "%04x:%04x:%04x:%04x", vp[3], vp[2], vp[1], vp[0]); + if (rd->json_output) + jsonw_string_field(rd->jw, "node_guid", str); + else + pr_out("node_guid %s ", str); } -static void dev_print_sys_image_guid(struct nlattr **tb) +static void dev_print_sys_image_guid(struct rd *rd, 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]); - rd_print_u64("sys_image_guid", 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]); + if (rd->json_output) + jsonw_string_field(rd->jw, "sys_image_guid", str); + else + pr_out("sys_image_guid %s ", str); } static const char *node_type_to_str(uint8_t node_type) @@ -131,37 +161,51 @@ static const char *node_type_to_str(uint8_t node_type) return "unknown"; } -static void dev_print_node_type(struct nlattr **tb) +static void dev_print_node_type(struct rd *rd, 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]); - pr_out("node_type %s ", node_type_to_str(node_type)); + node_str = node_type_to_str(node_type); + if (rd->json_output) + jsonw_string_field(rd->jw, "node_type", node_str); + else + pr_out("node_type %s ", node_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; - pr_out("%u: %s: ", - mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]), - mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME])); - dev_print_node_type(tb); - dev_print_fw(tb); - dev_print_node_guid(tb); - dev_print_sys_image_guid(tb); - if (rd->show_details) - dev_print_caps(tb); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + if (rd->json_output) { + jsonw_uint_field(rd->jw, "ifindex", idx); + jsonw_string_field(rd->jw, "ifname", name); + } else { + pr_out("%u: %s: ", idx, name); + } - pr_out("\n"); + dev_print_node_type(rd, tb); + dev_print_fw(rd, tb); + dev_print_node_guid(rd, tb); + dev_print_sys_image_guid(rd, tb); + if (rd->show_details) + dev_print_caps(rd, tb); + + if (!rd->json_output) + pr_out("\n"); return MNL_CB_OK; } @@ -177,7 +221,12 @@ static int dev_no_args(struct rd *rd) if (ret) return ret; - return rd_recv_msg(rd, dev_parse_cb, rd, seq); + if (rd->json_output) + jsonw_start_object(rd->jw); + ret = rd_recv_msg(rd, dev_parse_cb, rd, seq); + if (rd->json_output) + jsonw_end_object(rd->jw); + return ret; } static int dev_one_show(struct rd *rd) @@ -195,24 +244,29 @@ static int dev_show(struct rd *rd) struct dev_map *dev_map; int ret = 0; + if (rd->json_output) + jsonw_start_array(rd->jw); if (rd_no_arg(rd)) { list_for_each_entry(dev_map, &rd->dev_map_list, list) { rd->dev_idx = dev_map->idx; ret = dev_one_show(rd); if (ret) - return ret; + goto out; } - } else { dev_map = dev_map_lookup(rd, false); if (!dev_map) { pr_err("Wrong device name\n"); - return -ENOENT; + ret = -ENOENT; + goto out; } rd_arg_inc(rd); rd->dev_idx = dev_map->idx; ret = dev_one_show(rd); } +out: + if (rd->json_output) + jsonw_end_array(rd->jw); return ret; } From 7fc75744c0677f70eff7586aaa40cc98ab27a7d9 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 20 Aug 2017 12:58:27 +0300 Subject: [PATCH 07/15] rdma: Add json output to link object An example for the JSON output for two devices system. root@mtr-leonro:~# rdma link -d -p -j [{ "ifindex": 1, "port": 1, "ifname": "mlx5_0/1", "subnet_prefix": "fe80:0000:0000:0000", "lid": 13399, "sm_lid": 49151, "lmc": 0, "state": "ACTIVE", "physical_state": "LINK_UP", "caps": ["AUTO_MIG" ] },{ "ifindex": 2, "port": 1, "ifname": "mlx5_1/1", "subnet_prefix": "fe80:0000:0000:0000", "lid": 13400, "sm_lid": 49151, "lmc": 0, "state": "ACTIVE", "physical_state": "LINK_UP", "caps": ["AUTO_MIG" ] } ] Signed-off-by: Leon Romanovsky --- rdma/link.c | 146 +++++++++++++++++++++++++++++++++++++-------------- rdma/rdma.h | 1 - rdma/utils.c | 8 --- 3 files changed, 106 insertions(+), 49 deletions(-) diff --git a/rdma/link.c b/rdma/link.c index b0e5bee0..eae96cd8 100644 --- a/rdma/link.c +++ b/rdma/link.c @@ -56,7 +56,7 @@ static const char *caps_to_str(uint32_t idx) return "UNKNOWN"; } -static void link_print_caps(struct nlattr **tb) +static void link_print_caps(struct rd *rd, struct nlattr **tb) { uint64_t caps; uint32_t idx; @@ -66,54 +66,89 @@ static void link_print_caps(struct nlattr **tb) caps = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_CAP_FLAGS]); - pr_out("\n caps: <"); + if (rd->json_output) { + jsonw_name(rd->jw, "caps"); + jsonw_start_array(rd->jw); + } else { + pr_out("\n caps: <"); + } for (idx = 0; caps; idx++) { if (caps & 0x1) { - pr_out("%s", caps_to_str(idx)); - if (caps >> 0x1) - pr_out(", "); + if (rd->json_output) { + jsonw_string(rd->jw, caps_to_str(idx)); + } else { + pr_out("%s", caps_to_str(idx)); + if (caps >> 0x1) + pr_out(", "); + } } caps >>= 0x1; } - pr_out(">"); + if (rd->json_output) + jsonw_end_array(rd->jw); + else + pr_out(">"); } -static void link_print_subnet_prefix(struct nlattr **tb) +static void link_print_subnet_prefix(struct rd *rd, 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]); - rd_print_u64("subnet_prefix", subnet_prefix); + memcpy(vp, &subnet_prefix, sizeof(uint64_t)); + snprintf(str, 32, "%04x:%04x:%04x:%04x", vp[3], vp[2], vp[1], vp[0]); + if (rd->json_output) + jsonw_string_field(rd->jw, "subnet_prefix", str); + else + pr_out("subnet_prefix %s ", str); } -static void link_print_lid(struct nlattr **tb) +static void link_print_lid(struct rd *rd, struct nlattr **tb) { + uint32_t lid; + if (!tb[RDMA_NLDEV_ATTR_LID]) return; - pr_out("lid %u ", - mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_LID])); + lid = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_LID]); + if (rd->json_output) + jsonw_uint_field(rd->jw, "lid", lid); + else + pr_out("lid %u ", lid); } -static void link_print_sm_lid(struct nlattr **tb) +static void link_print_sm_lid(struct rd *rd, struct nlattr **tb) { + uint32_t sm_lid; + if (!tb[RDMA_NLDEV_ATTR_SM_LID]) return; - pr_out("sm_lid %u ", - mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_SM_LID])); + sm_lid = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_SM_LID]); + if (rd->json_output) + jsonw_uint_field(rd->jw, "sm_lid", sm_lid); + else + pr_out("sm_lid %u ", sm_lid); } -static void link_print_lmc(struct nlattr **tb) +static void link_print_lmc(struct rd *rd, struct nlattr **tb) { + uint8_t lmc; + if (!tb[RDMA_NLDEV_ATTR_LMC]) return; - pr_out("lmc %u ", mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_LMC])); + lmc = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_LMC]); + if (rd->json_output) + jsonw_uint_field(rd->jw, "lmc", lmc); + else + pr_out("lmc %u ", lmc); } static const char *link_state_to_str(uint8_t link_state) @@ -127,7 +162,7 @@ static const char *link_state_to_str(uint8_t link_state) return "UNKNOWN"; } -static void link_print_state(struct nlattr **tb) +static void link_print_state(struct rd *rd, struct nlattr **tb) { uint8_t state; @@ -135,7 +170,10 @@ static void link_print_state(struct nlattr **tb) return; state = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_PORT_STATE]); - pr_out("state %s ", link_state_to_str(state)); + if (rd->json_output) + jsonw_string_field(rd->jw, "state", link_state_to_str(state)); + else + pr_out("state %s ", link_state_to_str(state)); } static const char *phys_state_to_str(uint8_t phys_state) @@ -152,7 +190,7 @@ static const char *phys_state_to_str(uint8_t phys_state) return "UNKNOWN"; }; -static void link_print_phys_state(struct nlattr **tb) +static void link_print_phys_state(struct rd *rd, struct nlattr **tb) { uint8_t phys_state; @@ -160,13 +198,19 @@ static void link_print_phys_state(struct nlattr **tb) return; phys_state = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_PORT_PHYS_STATE]); - pr_out("physical_state %s ", phys_state_to_str(phys_state)); + if (rd->json_output) + jsonw_string_field(rd->jw, "physical_state", + phys_state_to_str(phys_state)); + else + pr_out("physical_state %s ", phys_state_to_str(phys_state)); } 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; + char name[32]; mnl_attr_parse(nlh, 0, rd_attr_cb, tb); if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME]) @@ -177,21 +221,31 @@ static int link_parse_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_ERROR; } - pr_out("%u/%u: %s/%u: ", - mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]), - mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]), - mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]), - mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX])); - 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); - if (rd->show_details) - link_print_caps(tb); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); + snprintf(name, 32, "%s/%u", + mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]), port); - pr_out("\n"); + if (rd->json_output) { + jsonw_uint_field(rd->jw, "ifindex", idx); + jsonw_uint_field(rd->jw, "port", port); + jsonw_string_field(rd->jw, "ifname", name); + + } else { + pr_out("%u/%u: %s: ", idx, port, name); + } + + link_print_subnet_prefix(rd, tb); + link_print_lid(rd, tb); + link_print_sm_lid(rd, tb); + link_print_lmc(rd, tb); + link_print_state(rd, tb); + link_print_phys_state(rd, tb); + if (rd->show_details) + link_print_caps(rd, tb); + + if (!rd->json_output) + pr_out("\n"); return MNL_CB_OK; } @@ -208,7 +262,12 @@ static int link_no_args(struct rd *rd) if (ret) return ret; - return rd_recv_msg(rd, link_parse_cb, rd, seq); + if (rd->json_output) + jsonw_start_object(rd->jw); + ret = rd_recv_msg(rd, link_parse_cb, rd, seq); + if (rd->json_output) + jsonw_end_object(rd->jw); + return ret; } static int link_one_show(struct rd *rd) @@ -225,8 +284,10 @@ static int link_show(struct rd *rd) { struct dev_map *dev_map; uint32_t port; - int ret; + int ret = 0; + if (rd->json_output) + jsonw_start_array(rd->jw); if (rd_no_arg(rd)) { list_for_each_entry(dev_map, &rd->dev_map_list, list) { rd->dev_idx = dev_map->idx; @@ -234,7 +295,7 @@ static int link_show(struct rd *rd) rd->port_idx = port; ret = link_one_show(rd); if (ret) - return ret; + goto out; } } @@ -243,7 +304,8 @@ static int link_show(struct rd *rd) port = get_port_from_argv(rd); if (!dev_map || port > dev_map->num_ports) { pr_err("Wrong device name\n"); - return -ENOENT; + ret = -ENOENT; + goto out; } rd_arg_inc(rd); rd->dev_idx = dev_map->idx; @@ -251,7 +313,7 @@ static int link_show(struct rd *rd) for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) { ret = link_one_show(rd); if (ret) - return ret; + goto out; if (port) /* * We got request to show link for devname @@ -260,7 +322,11 @@ static int link_show(struct rd *rd) break; } } - return 0; + +out: + if (rd->json_output) + jsonw_end_array(rd->jw); + return ret; } int cmd_link(struct rd *rd) diff --git a/rdma/rdma.h b/rdma/rdma.h index 4c564fef..d551eb29 100644 --- a/rdma/rdma.h +++ b/rdma/rdma.h @@ -66,7 +66,6 @@ void rd_arg_inc(struct rd *rd); char *rd_argv(struct rd *rd); uint32_t get_port_from_argv(struct rd *rd); -void rd_print_u64(char *name, uint64_t val); /* * Commands interface */ diff --git a/rdma/utils.c b/rdma/utils.c index 91d05271..eb4377cf 100644 --- a/rdma/utils.c +++ b/rdma/utils.c @@ -59,14 +59,6 @@ uint32_t get_port_from_argv(struct rd *rd) return slash ? atoi(slash + 1) : 0; } -void rd_print_u64(char *name, uint64_t val) -{ - uint16_t vp[4]; - - memcpy(vp, &val, sizeof(uint64_t)); - pr_out("%s %04x:%04x:%04x:%04x ", name, vp[3], vp[2], vp[1], vp[0]); -} - static struct dev_map *dev_map_alloc(const char *dev_name) { struct dev_map *dev_map; From dbc76eb6cca43e4b354d11e09dcd84cbde2b9a9b Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Sun, 20 Aug 2017 12:58:28 +0300 Subject: [PATCH 08/15] rdma: Add initial manual for the tool Signed-off-by: Leon Romanovsky --- man/man8/rdma-dev.8 | 55 +++++++++++++++++++++++ man/man8/rdma-link.8 | 55 +++++++++++++++++++++++ man/man8/rdma.8 | 102 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 man/man8/rdma-dev.8 create mode 100644 man/man8/rdma-link.8 create mode 100644 man/man8/rdma.8 diff --git a/man/man8/rdma-dev.8 b/man/man8/rdma-dev.8 new file mode 100644 index 00000000..461681b6 --- /dev/null +++ b/man/man8/rdma-dev.8 @@ -0,0 +1,55 @@ +.TH RDMA\-DEV 8 "06 Jul 2017" "iproute2" "Linux" +.SH NAME +rdmak-dev \- RDMA device configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B rdma +.RI "[ " OPTIONS " ]" +.B dev +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-d\fR[\fIetails\fR] } + +.ti -8 +.B rdma dev show +.RI "[ " DEV " ]" + +.ti -8 +.B rdma dev help + +.SH "DESCRIPTION" +.SS rdma dev show - display rdma device attributes + +.PP +.I "DEV" +- specifies the RDMA device to show. +If this argument is omitted all devices are listed. + +.SH "EXAMPLES" +.PP +rdma dev +.RS 4 +Shows the state of all RDMA devices on the system. +.RE +.PP +rdma dev show mlx5_3 +.RS 4 +Shows the state of specified RDMA device. +.RE +.PP + +.SH SEE ALSO +.BR rdma (8), +.BR rdma-link (8), +.br + +.SH AUTHOR +Leon Romanovsky diff --git a/man/man8/rdma-link.8 b/man/man8/rdma-link.8 new file mode 100644 index 00000000..8ed049ef --- /dev/null +++ b/man/man8/rdma-link.8 @@ -0,0 +1,55 @@ +.TH RDMA\-LINK 8 "06 Jul 2017" "iproute2" "Linux" +.SH NAME +rdma-link \- rdma link configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B devlink +.RI "[ " OPTIONS " ]" +.B link +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-d\fR[\fIetails\fR] } + +.ti -8 +.B rdma link show +.RI "[ " DEV/PORT_INDEX " ]" + +.ti -8 +.B rdma link help + +.SH "DESCRIPTION" +.SS rdma link show - display rdma link attributes + +.PP +.I "DEV/PORT_INDEX" +- specifies the RDMa link to show. +If this argument is omitted all links are listed. + +.SH "EXAMPLES" +.PP +rdma link show +.RS 4 +Shows the state of all rdma links on the system. +.RE +.PP +rdma link show mlx5_2/1 +.RS 4 +Shows the state of specified rdma link. +.RE +.PP + +.SH SEE ALSO +.BR rdma (8), +.BR rdma-dev (8), +.br + +.SH AUTHOR +Leon Romanovsky diff --git a/man/man8/rdma.8 b/man/man8/rdma.8 new file mode 100644 index 00000000..798b33d3 --- /dev/null +++ b/man/man8/rdma.8 @@ -0,0 +1,102 @@ +.TH RDMA 8 "28 Mar 2017" "iproute2" "Linux" +.SH NAME +rdma \- RDMA tool +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B rdma +.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OBJECT " := { " +.BR dev " | " link " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-d\fR[\fIetails\fR] } +\fB\-j\fR[\fIson\fR] } +\fB\-p\fR[\fIretty\fR] } + +.SH OPTIONS + +.TP +.BR "\-V" , " -Version" +Print the version of the +.B rdma +tool and exit. + +.TP +.BR "\-d" , " --details" +Otuput detailed information. + +.TP +.BR "\-p" , " --pretty" +When combined with -j generate a pretty JSON output. + +.TP +.BR "\-j" , " --json" +Generate JSON output. + +.SS +.I OBJECT + +.TP +.B dev +- RDMA device. + +.TP +.B link +- RDMA port related. + +.PP +The names of all objects may be written in full or +abbreviated form, for example +.B stats +can be abbreviated as +.B stat +or just +.B s. + +.SS +.I COMMAND + +Specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to +.B show +(or +.B list +) objects, but some objects do not allow all of these operations +or have some additional commands. The +.B help +command is available for all objects. It prints +out a list of available commands and argument syntax conventions. +.sp +If no command is given, some default command is assumed. +Usually it is +.B list +or, if the objects of this class cannot be listed, +.BR "help" . + +.SH EXIT STATUS +Exit status is 0 if command was successful or a positive integer upon failure. + +.SH SEE ALSO +.BR rdma-dev (8), +.BR rdma-link (8), +.br + +.SH REPORTING BUGS +Report any bugs to the Linux RDMA mailing list +.B +where the development and maintenance is primarily done. +You do not have to be subscribed to the list to send a message there. + +.SH AUTHOR +Leon Romanovsky From 8579a398c5ab0d26bce0ed9b4b6b6e5d62fcc89d Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 17 Aug 2017 19:09:25 +0200 Subject: [PATCH 09/15] devlink: No need for this self-assignment dl_argv_handle_both() will either assign to handle_bit or error out in which case the variable is not used by the caller. Signed-off-by: Phil Sutter Acked-by: Jiri Pirko --- devlink/devlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index 7602970b..5b325ce6 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -779,7 +779,7 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, int err; if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) { - uint32_t handle_bit = handle_bit; + uint32_t handle_bit; err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name, &opts->port_index, &handle_bit); From 28692621442710f4a67fe33742f56efc582ee33a Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 17 Aug 2017 19:09:26 +0200 Subject: [PATCH 10/15] ipntable: No need to check and assign to parms_rta This variable is initialized at declaration and nowhere else does any assignment to it happen, so just drop the check. Signed-off-by: Phil Sutter --- ip/ipntable.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ip/ipntable.c b/ip/ipntable.c index 879626ee..1837909f 100644 --- a/ip/ipntable.c +++ b/ip/ipntable.c @@ -202,8 +202,6 @@ static int ipntable_modify(int cmd, int flags, int argc, char **argv) if (get_u32(&queue, *argv, 0)) invarg("\"queue\" value is invalid", *argv); - if (!parms_rta) - parms_rta = (struct rtattr *)&parms_buf; rta_addattr32(parms_rta, sizeof(parms_buf), NDTPA_QUEUE_LEN, queue); parms_change = 1; From 2a866256197f8b86e61fa1afc99b11d7056d5686 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 17 Aug 2017 19:09:27 +0200 Subject: [PATCH 11/15] iproute: Fix for missing 'Oifs:' display Covscan complained about dead code but after reading it, I assume the author's intention was to prefix the interface list with 'Oifs: '. Initializing first to 1 and setting it to 0 after above prefix was printed should fix it. Signed-off-by: Phil Sutter --- ip/iproute.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ip/iproute.c b/ip/iproute.c index cb695ad4..89caac12 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -624,7 +624,7 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) } if (tb[RTA_MULTIPATH]) { struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); - int first = 0; + int first = 1; len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); @@ -634,10 +634,12 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (nh->rtnh_len > len) break; if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { - if (first) + if (first) { fprintf(fp, "Oifs: "); - else + first = 0; + } else { fprintf(fp, " "); + } } else fprintf(fp, "%s\tnexthop ", _SL_); if (nh->rtnh_len > sizeof(*nh)) { From b3c5f84493d3399a546566475203207aa5b64d54 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 17 Aug 2017 19:09:28 +0200 Subject: [PATCH 12/15] lib/rt_names: Drop dead code in rtnl_rttable_n2a() Since 'id' is 32bit unsigned, it can never exceed RT_TABLE_MAX (which is defined to 0xFFFFFFFF). Therefore drop that never matching conditional. Signed-off-by: Phil Sutter --- lib/rt_names.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/rt_names.c b/lib/rt_names.c index 04c15ff5..e5efd78e 100644 --- a/lib/rt_names.c +++ b/lib/rt_names.c @@ -410,10 +410,6 @@ const char *rtnl_rttable_n2a(__u32 id, char *buf, int len) { struct rtnl_hash_entry *entry; - if (id > RT_TABLE_MAX) { - snprintf(buf, len, "%u", id); - return buf; - } if (!rtnl_rttable_init) rtnl_rttable_initialize(); entry = rtnl_rttable_hash[id & 255]; From 44448a90eab34713af019356926828720c67a268 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 17 Aug 2017 19:09:29 +0200 Subject: [PATCH 13/15] ss: Skip useless check in parse_hostcond() The passed 'addr' parameter is dereferenced by caller before and in parse_hostcond() multiple times before this check, so assume it is always true. Signed-off-by: Phil Sutter --- misc/ss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ss.c b/misc/ss.c index 10360e5a..419076b2 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -1671,7 +1671,7 @@ void *parse_hostcond(char *addr, bool is_port) } } } - if (!is_port && addr && *addr && *addr != '*') { + if (!is_port && *addr && *addr != '*') { if (get_prefix_1(&a.addr, addr, fam)) { if (get_dns_host(&a, addr, fam)) { fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", addr); From e469523e8e8d1d31c3b35251105e2a843216d687 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 17 Aug 2017 19:09:30 +0200 Subject: [PATCH 14/15] ss: Drop useless assignment After '*b = *a', 'b->next' already has the same value as 'a->next'. Signed-off-by: Phil Sutter --- misc/ss.c | 1 - 1 file changed, 1 deletion(-) diff --git a/misc/ss.c b/misc/ss.c index 419076b2..bd68ecdc 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -1440,7 +1440,6 @@ static int remember_he(struct aafilter *a, struct hostent *he) if ((b = malloc(sizeof(*b))) == NULL) return cnt; *b = *a; - b->next = a->next; a->next = b; } memcpy(b->addr.data, *ptr, len); From 73aa988868e7e068b4fc0daaca7cfdb3e07fe744 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 17 Aug 2017 19:09:31 +0200 Subject: [PATCH 15/15] tc/m_gact: Drop dead code The use of 'ok' variable in parse_gact() is ineffective: The second conditional increments it either if *argv is 'gact' or if parse_action_control() doesn't fail (in which case exit() is called). So this is effectively an unconditional increment and since no decrement happens anywhere, all remaining checks for 'ok != 0' can be dropped. Signed-off-by: Phil Sutter --- tc/m_gact.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tc/m_gact.c b/tc/m_gact.c index 1a258337..df143c9e 100644 --- a/tc/m_gact.c +++ b/tc/m_gact.c @@ -76,7 +76,6 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p, { int argc = *argc_p; char **argv = *argv_p; - int ok = 0; struct tc_gact p = { 0 }; #ifdef CONFIG_GACT_PROB int rd = 0; @@ -89,17 +88,14 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p, if (matches(*argv, "gact") == 0) { - ok++; argc--; argv++; - } else { - if (parse_action_control(&argc, &argv, &p.action, false) == -1) - usage(); - ok++; + } else if (parse_action_control(&argc, &argv, &p.action, false) == -1) { + usage(); /* does not return */ } #ifdef CONFIG_GACT_PROB - if (ok && argc > 0) { + if (argc > 0) { if (matches(*argv, "random") == 0) { rd = 1; NEXT_ARG(); @@ -142,15 +138,11 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p, } argc--; argv++; - ok++; } else if (matches(*argv, "help") == 0) { usage(); } } - if (!ok) - return -1; - tail = NLMSG_TAIL(n); addattr_l(n, MAX_MSG, tca_id, NULL, 0); addattr_l(n, MAX_MSG, TCA_GACT_PARMS, &p, sizeof(p));