Subject: Re: [PATCH iproute2 V2 1/4] rdma: Add basic infrastructure for RDMA tool

From: Yuval Shaia <[email-masked]>
Date: Mon, 3 Jul 2017 17:33:56 +0300

On Mon, Jul 03, 2017 at 05:06:55PM +0300, Leon Romanovsky wrote:
> From: Leon Romanovsky <[email-masked]>
> 
> 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 <[email-masked]>
> ---
>  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 <[email-masked]>
> + */
> +
> +#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 <[email-masked]>
> + */
> +#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 <[email-masked]>
> + */
> +
> +#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 [email-masked]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html