On Mon, Jul 03, 2017 at 05:06:55PM +0300, Leon Romanovsky wrote: > From: Leon Romanovsky <leonro@xxxxxxxxxxxx> > > 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 <leonro@xxxxxxxxxxxx> > --- > Makefile | 2 +- > rdma/.gitignore | 1 + > rdma/Makefile | 22 ++++++ > rdma/rdma.c | 116 ++++++++++++++++++++++++++++ > rdma/rdma.h | 71 +++++++++++++++++ > rdma/utils.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 6 files changed, 443 insertions(+), 1 deletion(-) > 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 18de7dcb..c255063b 100644 > --- a/Makefile > +++ b/Makefile > @@ -52,7 +52,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/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..29273839 > --- /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 <leonro@xxxxxxxxxxxx> > + */ > + > +#include <limits.h> > +#include <rdma/rdma_netlink.h> > + > +#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 rdma *rd) > +{ > + help(rd->filename); > + return 0; Can we change it to void? > +} > + > +static int rd_cmd(struct rdma *rd) > +{ > + const struct rdma_cmd cmds[] = { > + { NULL, cmd_help }, > + { "help", cmd_help }, > + { 0 } > + }; > + > + return rdma_exec_cmd(rd, cmds, "object"); > +} > + > +static int rd_init(struct rdma *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; > + > + rdma_prepare_msg(rd, RDMA_NLDEV_CMD_GET, &seq, (NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP)); > + if ((ret = rdma_send_msg(rd))) > + return ret; Maybe it is only my perspective but as i see it - if some init function fails at one of the steps it needs to rollback to starting point before returning an error. A caller expect that if init fails then no reason to call the free. Caller here calls free when init fails so we are fine but just razing a point here. > + > + return rdma_recv_msg(rd, rd_dev_init_cb, rd, seq); > +} > + > +static void rd_free(struct rdma *rd) > +{ > + free(rd->buff); > + rdma_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 rdma 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..3c070836 > --- /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 <leonro@xxxxxxxxxxxx> > + */ > +#ifndef _RDMA_TOOL_H_ > +#define _RDMA_TOOL_H_ > + > +#include <stdlib.h> > +#include <string.h> > +#include <errno.h> > +#include <getopt.h> > +#include <libmnl/libmnl.h> > + > +#include <rdma/rdma_netlink.h> > +#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 rdma { > + 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 rdma_cmd { > + const char *cmd; > + int (*func)(struct rdma *rd); > +}; > + > +/* > + * Parser interface > + */ > +bool rd_no_arg(struct rdma *rd); > +bool rd_argv_match(struct rdma *rd, const char *pattern); > +void rd_arg_inc(struct rdma *rd); > + > +int rdma_exec_cmd(struct rdma *rd, const struct rdma_cmd *c, const char *str); > + > +/* > + * Device manipulation > + */ > +void rdma_free_devmap(struct rdma *rd); > + > +/* > + * Netlink > + */ > +int rdma_send_msg(struct rdma *rd); > +int rdma_recv_msg(struct rdma *rd, mnl_cb_t callback, void *data, uint32_t seq); > +int rdma_prepare_msg(struct rdma *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..cabec6e7 > --- /dev/null > +++ b/rdma/utils.c > @@ -0,0 +1,232 @@ > +/* > + * 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 <leonro@xxxxxxxxxxxx> > + */ > + > +#include <sys/types.h> > +#include <dirent.h> > +#include <time.h> > + > +#include "rdma.h" > +#include <rdma/rdma_netlink.h> > + > +/* > + * This macro will be moved to generic layer, > + * after code will be accepted. > + * it is placed here to avoid rebases with upstream code. > + */ > +#define MAX(a, b) ((a) > (b) ? (a) : (b)) Can we use /usr/include/sys/param.h? > + > +static int rd_argc(struct rdma *rd) > +{ > + return rd->argc; > +} > + > +static char *rd_argv(struct rdma *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)); > +} > + > +bool rd_argv_match(struct rdma *rd, const char *pattern) > +{ > + if (!rd_argc(rd)) > + return false; > + return strcmpx(rd_argv(rd), pattern) == 0; rd_argv might return NULL. > +} > + > +void rd_arg_inc(struct rdma *rd) > +{ > + if (!rd_argc(rd)) > + return; > + rd->argc--; > + rd->argv++; > +} > + > +bool rd_no_arg(struct rdma *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 rdma *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 rdma *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 rdma_free_devmap(struct rdma *rd) > +{ > + if(!rd) > + return; > + dev_map_cleanup(rd); > + return; Redundant > +} > + > +int rdma_exec_cmd(struct rdma *rd, const struct rdma_cmd *cmds, const char *str) > +{ > + const struct rdma_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; > +} > + > +int rdma_prepare_msg(struct rdma *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; > + > + return 0; Can we change it to void? > +} > + > +int rdma_send_msg(struct rdma *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); Suggesting to break the above line. > + return ret; > +} > + > +int rdma_recv_msg(struct rdma *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; > +} > -- > 2.13.2 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-rdma" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-rdma" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html