diff --git a/.gitignore b/.gitignore index f8c3dfca..e5234a3d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,13 +39,3 @@ testsuite/results testsuite/iproute2/iproute2-this testsuite/tools/generate_nlmsg testsuite/tests/ip/link/dev_wo_vf_rate.nl - -# doc files generated at runtime -doc/*.aux -doc/*.log -doc/*.toc -doc/*.ps -doc/*.dvi -doc/*.html -doc/*.pdf -doc/*.out diff --git a/README b/README index 386fbaf6..1b7f4427 100644 --- a/README +++ b/README @@ -1,13 +1,13 @@ This is a set of utilities for Linux networking. -Information: - http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2 - Download: http://www.kernel.org/pub/linux/utils/net/iproute2/ -Repository: - git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git +Stable version repository: + git://git.kernel.org/pub/scm/network/iproute2/iproute2.git + +Development repository: + git://git.kernel.org/pub/scm/network/iproute2/iproute2-next.git How to compile this. -------------------- diff --git a/README.devel b/README.devel index 9b7d11e3..60c95468 100644 --- a/README.devel +++ b/README.devel @@ -4,12 +4,15 @@ development. Most new features require a kernel and a utility component. Please submit both to the Linux networking mailing list -The current source is in the git repository: - git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git +The current source for the stable version is in the git repository: + git://git.kernel.org/pub/scm/network/iproute2/iproute2.git -The master branch contains the source corresponding to the current -code in the mainline Linux kernel (ie follows Linus). The net-next -branch is a temporary branch that tracks the code intended for the -next release; it corresponds with networking development branch in -the kernel. +The development git repository is available at the following address: + git://git.kernel.org/pub/scm/network/iproute2/iproute2-next.git +The stable repository contains the source corresponding to the +current code in the Linux networking tree (net), which in turn is +aligned on the mainline Linux kernel (ie follows Linus). +The iproute2-next repository tracks the code intended for the next +release; it corresponds with networking development tree (net-next) +in the kernel. diff --git a/configure b/configure index d6832464..f7c2d7a7 100755 --- a/configure +++ b/configure @@ -17,18 +17,6 @@ check_prog() command -v $1 >/dev/null 2>&1 && (echo "$3:=y" >> $CONFIG; echo "yes") || (echo "no"; return 1) } -check_docs() -{ - if check_prog latex " latex: " HAVE_LATEX; then - check_prog pdflatex " pdflatex: " HAVE_PDFLATEX || echo " WARNING: no PDF docs can be built from LaTeX files" - check_prog sgml2latex " sgml2latex: " HAVE_SGML2LATEX || echo " WARNING: no LaTeX files can be build from SGML files" - else - echo " WARNING: no docs can be built from LaTeX files" - fi - - check_prog sgml2html " sgml2html: " HAVE_SGML2HTML || echo " WARNING: no HTML docs can be built from SGML" -} - check_toolchain() { : ${PKG_CONFIG:=pkg-config} @@ -422,11 +410,6 @@ check_berkeley_db echo -n "need for strlcpy: " check_strlcpy -echo -echo -n "docs:" -check_docs -echo - echo >> $CONFIG echo "%.o: %.c" >> $CONFIG echo ' $(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<' >> $CONFIG diff --git a/ip/ip.c b/ip/ip.c index f6248846..362790a0 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -240,10 +240,6 @@ int main(int argc, char **argv) } else if (matches(opt, "-tshort") == 0) { ++timestamp; ++timestamp_short; -#if 0 - } else if (matches(opt, "-numeric") == 0) { - rtnl_names_numeric++; -#endif } else if (matches(opt, "-Version") == 0) { printf("ip utility, iproute2-ss%s\n", SNAPSHOT); exit(0); diff --git a/ip/ipnetns.c b/ip/ipnetns.c index 059a4220..631794b8 100644 --- a/ip/ipnetns.c +++ b/ip/ipnetns.c @@ -718,7 +718,10 @@ static int netns_set(int argc, char **argv) return -1; } name = argv[0]; - if (get_unsigned(&nsid, argv[1], 0)) + /* If a negative nsid is specified the kernel will select the nsid. */ + if (strcmp(argv[1], "auto") == 0) + nsid = -1; + else if (get_unsigned(&nsid, argv[1], 0)) invarg("Invalid \"netnsid\" value\n", argv[1]); snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name); diff --git a/man/man8/ip-netns.8 b/man/man8/ip-netns.8 index c5310e24..d539f18b 100644 --- a/man/man8/ip-netns.8 +++ b/man/man8/ip-netns.8 @@ -137,6 +137,7 @@ $ ip netns del net0 .sp This command assigns a id to a peer network namespace. This id is valid only in the current network namespace. +If the keyword "auto" is specified an available nsid will be chosen. This id will be used by the kernel in some netlink messages. If no id is assigned when the kernel needs it, it will be automatically assigned by the kernel. diff --git a/man/man8/rdma-resource.8 b/man/man8/rdma-resource.8 new file mode 100644 index 00000000..ff5d25d7 --- /dev/null +++ b/man/man8/rdma-resource.8 @@ -0,0 +1,86 @@ +.TH RDMA\-RESOURCE 8 "26 Dec 2017" "iproute2" "Linux" +.SH NAME +rdma-resource \- rdma resource configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B rdma +.RI "[ " OPTIONS " ]" +.B resource +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-j\fR[\fIson\fR] | +\fB\-d\fR[\fIetails\fR] } + +.ti -8 +.B rdma resource show +.RI "[ " DEV/PORT_INDEX " ]" + +.ti -8 +.B rdma resource help + +.SH "DESCRIPTION" +.SS rdma resource show - display rdma resource tracking information + +.PP +.I "DEV/PORT_INDEX" +- specifies the RDMA link to show. +If this argument is omitted all links are listed. + +.SH "EXAMPLES" +.PP +rdma resource show +.RS 4 +Shows summary for all devices on the system. +.RE +.PP +rdma resource show mlx5_2 +.RS 4 +Shows the state of specified rdma device. +.RE +.PP +rdma res show qp link mlx5_4 +.RS 4 +Get all QPs for the specific device. +.RE +.PP +rdma res show qp link mlx5_4/1 +.RS 4 +Get QPs of specific port. +.RE +.PP +rdma res show qp link mlx5_4/0 +.RS 4 +Provide illegal port number (0 is illegal). +.RE +.PP +rdma res show qp link mlx5_4/- +.RS 4 +Get QPs which have not assigned port yet. +.RE +.PP +rdma res show qp link mlx5_4/- -d +.RS 4 +Detailed view. +.RE +.PP +rdma res show qp link mlx5_4/1 lqpn 0-6 +.RS 4 +Limit to specific Local QPNs. +.RE +.PP + +.SH SEE ALSO +.BR rdma (8), +.BR rdma-dev (8), +.BR rdma-link (8), +.br + +.SH AUTHOR +Leon Romanovsky diff --git a/rdma/Makefile b/rdma/Makefile index 454f25f8..360f09b2 100644 --- a/rdma/Makefile +++ b/rdma/Makefile @@ -5,7 +5,7 @@ TARGETS := ifeq ($(HAVE_MNL),y) -RDMA_OBJ = rdma.o utils.o dev.o link.o +RDMA_OBJ = rdma.o utils.o dev.o link.o res.o TARGETS += rdma endif diff --git a/rdma/link.c b/rdma/link.c index 676cb21d..66bcd50e 100644 --- a/rdma/link.c +++ b/rdma/link.c @@ -285,7 +285,7 @@ static int link_one_show(struct rd *rd) static int link_show(struct rd *rd) { - return rd_exec_link(rd, link_one_show); + return rd_exec_link(rd, link_one_show, true); } int cmd_link(struct rd *rd) diff --git a/rdma/rdma.c b/rdma/rdma.c index 0e8fd688..19608f41 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 | link | help }\n" + "where OBJECT := { dev | link | resource | help }\n" " OPTIONS := { -V[ersion] | -d[etails] | -j[son] | -p[retty]}\n", name); } @@ -32,6 +32,7 @@ static int rd_cmd(struct rd *rd) { "help", cmd_help }, { "dev", cmd_dev }, { "link", cmd_link }, + { "resource", cmd_res }, { 0 } }; @@ -47,6 +48,7 @@ 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); + INIT_LIST_HEAD(&rd->filter_list); if (rd->json_output) { rd->jw = jsonw_new(stdout); diff --git a/rdma/rdma.h b/rdma/rdma.h index 8d53d3a0..5809f706 100644 --- a/rdma/rdma.h +++ b/rdma/rdma.h @@ -29,6 +29,18 @@ #define RDMA_BITMAP_ENUM(name, bit_no) RDMA_BITMAP_##name = BIT(bit_no), #define RDMA_BITMAP_NAMES(name, bit_no) [bit_no] = #name, +#define MAX_NUMBER_OF_FILTERS 64 +struct filters { + char name[32]; + bool is_number; +}; + +struct filter_entry { + struct list_head list; + char *key; + char *value; +}; + struct dev_map { struct list_head list; char *dev_name; @@ -50,6 +62,7 @@ struct rd { json_writer_t *jw; bool json_output; bool pretty_output; + struct list_head filter_list; }; struct rd_cmd { @@ -64,23 +77,34 @@ 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); /* * Commands interface */ int cmd_dev(struct rd *rd); int cmd_link(struct rd *rd); +int cmd_res(struct rd *rd); int rd_exec_cmd(struct rd *rd, const struct rd_cmd *c, const char *str); int rd_exec_dev(struct rd *rd, int (*cb)(struct rd *rd)); -int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd)); +int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd), bool strict_port); void rd_free(struct rd *rd); +int rd_set_arg_to_devname(struct rd *rd); +int rd_argc(struct rd *rd); + +int strcmpx(const char *str1, const char *str2); /* * Device manipulation */ struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index); +/* + * Filter manipulation + */ +int rd_build_filter(struct rd *rd, const struct filters valid_filters[]); +bool rd_check_is_filtered(struct rd *rd, const char *key, uint32_t val); +bool rd_check_is_string_filtered(struct rd *rd, const char *key, const char *val); +bool rd_check_is_key_exist(struct rd *rd, const char *key); /* * Netlink */ diff --git a/rdma/res.c b/rdma/res.c new file mode 100644 index 00000000..2a63e712 --- /dev/null +++ b/rdma/res.c @@ -0,0 +1,486 @@ +/* + * res.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 + +static int res_help(struct rd *rd) +{ + pr_out("Usage: %s resource\n", rd->filename); + pr_out(" resource show [DEV]\n"); + pr_out(" resource show [qp]\n"); + pr_out(" resource show qp link [DEV/PORT]\n"); + pr_out(" resource show qp link [DEV/PORT] [FILTER-NAME FILTER-VALUE]\n"); + return 0; +} + +static int res_print_summary(struct rd *rd, struct nlattr **tb) +{ + struct nlattr *nla_table = tb[RDMA_NLDEV_ATTR_RES_SUMMARY]; + struct nlattr *nla_entry; + const char *name; + uint64_t curr; + int err; + + mnl_attr_for_each_nested(nla_entry, nla_table) { + struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; + char json_name[32]; + + err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); + if (err != MNL_CB_OK) + return -EINVAL; + + if (!nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME] || + !nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]) { + return -EINVAL; + } + + name = mnl_attr_get_str(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME]); + curr = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]); + if (rd->json_output) { + snprintf(json_name, 32, "%s", name); + jsonw_lluint_field(rd->jw, json_name, curr); + } else { + pr_out("%s %"PRId64 " ", name, curr); + } + } + return 0; +} + +static int res_no_args_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] || + !tb[RDMA_NLDEV_ATTR_RES_SUMMARY]) + return MNL_CB_ERROR; + + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + 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); + } + + res_print_summary(rd, tb); + + if (!rd->json_output) + pr_out("\n"); + return MNL_CB_OK; +} + +static int _res_send_msg(struct rd *rd, uint32_t command, mnl_cb_t callback) +{ + uint32_t flags = NLM_F_REQUEST | NLM_F_ACK; + uint32_t seq; + int ret; + + if (command != RDMA_NLDEV_CMD_RES_GET) + flags |= NLM_F_DUMP; + + rd_prepare_msg(rd, command, &seq, flags); + mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); + if (rd->port_idx) + mnl_attr_put_u32(rd->nlh, + RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx); + + ret = rd_send_msg(rd); + if (ret) + return ret; + + if (rd->json_output) + jsonw_start_object(rd->jw); + ret = rd_recv_msg(rd, callback, rd, seq); + if (rd->json_output) + jsonw_end_object(rd->jw); + return ret; +} + +#define RES_FUNC(name, command, valid_filters, strict_port) \ + static int _##name(struct rd *rd)\ + { \ + return _res_send_msg(rd, command, name##_parse_cb); \ + } \ + static int name(struct rd *rd) \ + {\ + int ret = rd_build_filter(rd, valid_filters); \ + if (ret) \ + return ret; \ + if ((uintptr_t)valid_filters != (uintptr_t)NULL) { \ + ret = rd_set_arg_to_devname(rd); \ + if (ret) \ + return ret;\ + } \ + if (strict_port) \ + return rd_exec_dev(rd, _##name); \ + else \ + return rd_exec_link(rd, _##name, strict_port); \ + } + +static const char *path_mig_to_str(uint8_t idx) +{ + static const char * const path_mig_str[] = { "MIGRATED", + "REARM", "ARMED" }; + + if (idx < ARRAY_SIZE(path_mig_str)) + return path_mig_str[idx]; + return "UNKNOWN"; +} + +static const char *qp_states_to_str(uint8_t idx) +{ + static const char * const qp_states_str[] = { "RESET", "INIT", + "RTR", "RTS", "SQD", + "SQE", "ERR" }; + + if (idx < ARRAY_SIZE(qp_states_str)) + return qp_states_str[idx]; + return "UNKNOWN"; +} + +static const char *qp_types_to_str(uint8_t idx) +{ + static const char * const qp_types_str[] = { "SMI", "GSI", "RC", + "UC", "UD", "RAW_IPV6", + "RAW_ETHERTYPE", + "UNKNOWN", "RAW_PACKET", + "XRC_INI", "XRC_TGT" }; + + if (idx < ARRAY_SIZE(qp_types_str)) + return qp_types_str[idx]; + return "UNKNOWN"; +} + +static void print_lqpn(struct rd *rd, uint32_t val) +{ + if (rd->json_output) + jsonw_uint_field(rd->jw, "lqpn", val); + else + pr_out("lqpn %u ", val); +} + +static void print_rqpn(struct rd *rd, uint32_t val, struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_RQPN]) + return; + + if (rd->json_output) + jsonw_uint_field(rd->jw, "rqpn", val); + else + pr_out("rqpn %u ", val); +} + +static void print_type(struct rd *rd, uint32_t val) +{ + if (rd->json_output) + jsonw_string_field(rd->jw, "type", + qp_types_to_str(val)); + else + pr_out("type %s ", qp_types_to_str(val)); +} + +static void print_state(struct rd *rd, uint32_t val) +{ + if (rd->json_output) + jsonw_string_field(rd->jw, "state", + qp_states_to_str(val)); + else + pr_out("state %s ", qp_states_to_str(val)); +} + +static void print_rqpsn(struct rd *rd, uint32_t val, struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]) + return; + + if (rd->json_output) + jsonw_uint_field(rd->jw, "rq-psn", val); + else + pr_out("rq-psn %u ", val); +} + +static void print_sqpsn(struct rd *rd, uint32_t val) +{ + if (rd->json_output) + jsonw_uint_field(rd->jw, "sq-psn", val); + else + pr_out("sq-psn %u ", val); +} + +static void print_pathmig(struct rd *rd, uint32_t val, + struct nlattr **nla_line) +{ + if (!nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]) + return; + + if (rd->json_output) + jsonw_string_field(rd->jw, + "path-mig-state", + path_mig_to_str(val)); + else + pr_out("path-mig-state %s ", path_mig_to_str(val)); +} + +static void print_pid(struct rd *rd, uint32_t val) +{ + if (rd->json_output) + jsonw_uint_field(rd->jw, "pid", val); + else + pr_out("pid %u ", val); +} + +static void print_comm(struct rd *rd, const char *str, + struct nlattr **nla_line) +{ + char tmp[18]; + + if (rd->json_output) { + /* Don't beatify output in JSON format */ + jsonw_string_field(rd->jw, "comm", str); + return; + } + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) + snprintf(tmp, sizeof(tmp), "%s", str); + else + snprintf(tmp, sizeof(tmp), "[%s]", str); + + pr_out("comm %s ", tmp); +} + +static void print_link(struct rd *rd, uint32_t idx, const char *name, + uint32_t port, struct nlattr **nla_line) +{ + if (rd->json_output) { + jsonw_uint_field(rd->jw, "ifindex", idx); + + if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) + jsonw_uint_field(rd->jw, "port", port); + + jsonw_string_field(rd->jw, "ifname", name); + } else { + if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) + pr_out("link %s/%u ", name, port); + else + pr_out("link %s/- ", name); + } +} + +static char *get_task_name(uint32_t pid) +{ + char *comm; + FILE *f; + + if (asprintf(&comm, "/proc/%d/comm", pid) < 0) + return NULL; + + f = fopen(comm, "r"); + free(comm); + if (!f) + return NULL; + + if (fscanf(f, "%ms\n", &comm) != 1) + comm = NULL; + + fclose(f); + + return comm; +} + +static int res_qp_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct nlattr *nla_table, *nla_entry; + struct rd *rd = data; + const char *name; + uint32_t idx; + + mnl_attr_parse(nlh, 0, rd_attr_cb, tb); + if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || + !tb[RDMA_NLDEV_ATTR_DEV_NAME] || + !tb[RDMA_NLDEV_ATTR_RES_QP]) + return MNL_CB_ERROR; + + name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + nla_table = tb[RDMA_NLDEV_ATTR_RES_QP]; + + mnl_attr_for_each_nested(nla_entry, nla_table) { + struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {}; + uint32_t lqpn, rqpn = 0, rq_psn = 0, sq_psn; + uint8_t type, state, path_mig_state = 0; + uint32_t port = 0, pid = 0; + char *comm = NULL; + int err; + + err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line); + if (err != MNL_CB_OK) + return MNL_CB_ERROR; + + if (!nla_line[RDMA_NLDEV_ATTR_RES_LQPN] || + !nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN] || + !nla_line[RDMA_NLDEV_ATTR_RES_TYPE] || + !nla_line[RDMA_NLDEV_ATTR_RES_STATE] || + (!nla_line[RDMA_NLDEV_ATTR_RES_PID] && + !nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])) { + return MNL_CB_ERROR; + } + + if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]) + port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]); + + if (port != rd->port_idx) + continue; + + lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]); + if (rd_check_is_filtered(rd, "lqpn", lqpn)) + continue; + + if (nla_line[RDMA_NLDEV_ATTR_RES_RQPN]) { + rqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_RQPN]); + if (rd_check_is_filtered(rd, "rqpn", rqpn)) + continue; + } else { + if (rd_check_is_key_exist(rd, "rqpn")) + continue; + } + + if (nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]) { + rq_psn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_RQ_PSN]); + if (rd_check_is_filtered(rd, "rq-psn", rq_psn)) + continue; + } else { + if (rd_check_is_key_exist(rd, "rq-psn")) + continue; + } + + sq_psn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN]); + if (rd_check_is_filtered(rd, "sq-psn", sq_psn)) + continue; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]) { + path_mig_state = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE]); + if (rd_check_is_string_filtered(rd, "path-mig-state", path_mig_to_str(path_mig_state))) + continue; + } else { + if (rd_check_is_key_exist(rd, "path-mig-state")) + continue; + } + + type = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_TYPE]); + if (rd_check_is_string_filtered(rd, "type", qp_types_to_str(type))) + continue; + + state = mnl_attr_get_u8(nla_line[RDMA_NLDEV_ATTR_RES_STATE]); + if (rd_check_is_string_filtered(rd, "state", qp_states_to_str(state))) + continue; + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) { + pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]); + comm = get_task_name(pid); + } + + if (rd_check_is_filtered(rd, "pid", pid)) + continue; + + if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) + /* discard const from mnl_attr_get_str */ + comm = (char *)mnl_attr_get_str(nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]); + + if (rd->json_output) + jsonw_start_array(rd->jw); + + print_link(rd, idx, name, port, nla_line); + + print_lqpn(rd, lqpn); + print_rqpn(rd, rqpn, nla_line); + + print_type(rd, type); + print_state(rd, state); + + print_rqpsn(rd, rq_psn, nla_line); + print_sqpsn(rd, sq_psn); + + print_pathmig(rd, path_mig_state, nla_line); + print_pid(rd, pid); + print_comm(rd, comm, nla_line); + + if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) + free(comm); + + if (rd->json_output) + jsonw_end_array(rd->jw); + else + pr_out("\n"); + } + return MNL_CB_OK; +} + +RES_FUNC(res_no_args, RDMA_NLDEV_CMD_RES_GET, NULL, true); + +static const struct +filters qp_valid_filters[MAX_NUMBER_OF_FILTERS] = {{ .name = "link", + .is_number = false }, + { .name = "lqpn", + .is_number = true }, + { .name = "rqpn", + .is_number = true }, + { .name = "pid", + .is_number = true }, + { .name = "sq-psn", + .is_number = true }, + { .name = "rq-psn", + .is_number = true }, + { .name = "type", + .is_number = false }, + { .name = "path-mig-state", + .is_number = false }, + { .name = "state", + .is_number = false } }; + +RES_FUNC(res_qp, RDMA_NLDEV_CMD_RES_QP_GET, qp_valid_filters, false); + +static int res_show(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, res_no_args }, + { "qp", res_qp }, + { 0 } + }; + + /* + * Special case to support "rdma res show DEV_NAME" + */ + if (rd_argc(rd) == 1 && dev_map_lookup(rd, false)) + return rd_exec_dev(rd, _res_no_args); + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +int cmd_res(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, res_show }, + { "show", res_show }, + { "list", res_show }, + { "help", res_help }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "resource command"); +} diff --git a/rdma/utils.c b/rdma/utils.c index 7b2001e2..f9460162 100644 --- a/rdma/utils.c +++ b/rdma/utils.c @@ -10,8 +10,9 @@ */ #include "rdma.h" +#include -static int rd_argc(struct rd *rd) +int rd_argc(struct rd *rd) { return rd->argc; } @@ -23,7 +24,7 @@ char *rd_argv(struct rd *rd) return *rd->argv; } -static int strcmpx(const char *str1, const char *str2) +int strcmpx(const char *str1, const char *str2) { if (strlen(str1) > strlen(str2)) return -1; @@ -50,13 +51,43 @@ bool rd_no_arg(struct rd *rd) return rd_argc(rd) == 0; } -uint32_t get_port_from_argv(struct rd *rd) +/* + * Possible input:output + * dev/port | first port | is_dump_all + * mlx5_1 | 0 | true + * mlx5_1/ | 0 | true + * mlx5_1/0 | 0 | false + * mlx5_1/1 | 1 | false + * mlx5_1/- | 0 | false + * + * In strict mode, /- will return error. + */ +static int get_port_from_argv(struct rd *rd, uint32_t *port, + bool *is_dump_all, bool strict_port) { char *slash; + *port = 0; + *is_dump_all = true; + slash = strchr(rd_argv(rd), '/'); /* if no port found, return 0 */ - return slash ? atoi(slash + 1) : 0; + if (slash++) { + if (*slash == '-') { + if (strict_port) + return -EINVAL; + *is_dump_all = false; + return 0; + } + + if (isdigit(*slash)) { + *is_dump_all = false; + *port = atoi(slash); + } + if (!*port && strlen(slash)) + return -EINVAL; + } + return 0; } static struct dev_map *dev_map_alloc(const char *dev_name) @@ -67,6 +98,10 @@ static struct dev_map *dev_map_alloc(const char *dev_name) if (!dev_map) return NULL; dev_map->dev_name = strdup(dev_name); + if (!dev_map->dev_name) { + free(dev_map); + return NULL; + } return dev_map; } @@ -83,6 +118,234 @@ static void dev_map_cleanup(struct rd *rd) } } +static int add_filter(struct rd *rd, char *key, char *value, + const struct filters valid_filters[]) +{ + char cset[] = "1234567890,-"; + struct filter_entry *fe; + bool key_found = false; + int idx = 0; + int ret; + + fe = calloc(1, sizeof(*fe)); + if (!fe) + return -ENOMEM; + + while (idx < MAX_NUMBER_OF_FILTERS && valid_filters[idx].name) { + if (!strcmpx(key, valid_filters[idx].name)) { + key_found = true; + break; + } + idx++; + } + if (!key_found) { + pr_err("Unsupported filter option: %s\n", key); + ret = -EINVAL; + goto err; + } + + /* + * Check the filter validity, not optimal, but works + * + * Actually, there are three types of filters + * numeric - for example PID or QPN + * string - for example states + * link - user requested to filter on specific link + * e.g. mlx5_1/1, mlx5_1/-, mlx5_1 ... + */ + if (valid_filters[idx].is_number && + strspn(value, cset) != strlen(value)) { + pr_err("%s filter accepts \"%s\" characters only\n", key, cset); + ret = -EINVAL; + goto err; + } + + fe->key = strdup(key); + fe->value = strdup(value); + if (!fe->key || !fe->value) { + ret = -ENOMEM; + goto err_alloc; + } + + for (idx = 0; idx < strlen(fe->value); idx++) + fe->value[idx] = tolower(fe->value[idx]); + + list_add_tail(&fe->list, &rd->filter_list); + return 0; + +err_alloc: + free(fe->value); + free(fe->key); +err: + free(fe); + return ret; +} + +int rd_build_filter(struct rd *rd, const struct filters valid_filters[]) +{ + int ret = 0; + int idx = 0; + + if (!valid_filters || !rd_argc(rd)) + goto out; + + if (rd_argc(rd) == 1) { + pr_err("No filter data was supplied to filter option %s\n", rd_argv(rd)); + ret = -EINVAL; + goto out; + } + + if (rd_argc(rd) % 2) { + pr_err("There is filter option without data\n"); + ret = -EINVAL; + goto out; + } + + while (idx != rd_argc(rd)) { + /* + * We can do micro-optimization and skip "dev" + * and "link" filters, but it is not worth of it. + */ + ret = add_filter(rd, *(rd->argv + idx), + *(rd->argv + idx + 1), valid_filters); + if (ret) + goto out; + idx += 2; + } + +out: + return ret; +} + +bool rd_check_is_key_exist(struct rd *rd, const char *key) +{ + struct filter_entry *fe; + + list_for_each_entry(fe, &rd->filter_list, list) { + if (!strcmpx(fe->key, key)) + return true; + } + + return false; +} + +/* + * Check if string entry is filtered: + * * key doesn't exist -> user didn't request -> not filtered + */ +bool rd_check_is_string_filtered(struct rd *rd, + const char *key, const char *val) +{ + bool key_is_filtered = false; + struct filter_entry *fe; + char *p = NULL; + char *str; + + list_for_each_entry(fe, &rd->filter_list, list) { + if (!strcmpx(fe->key, key)) { + /* We found the key */ + p = strdup(fe->value); + key_is_filtered = true; + if (!p) { + /* + * Something extremely wrong if we fail + * to allocate small amount of bytes. + */ + pr_err("Found key, but failed to allocate memory to store value\n"); + return key_is_filtered; + } + + /* + * Need to check if value in range + * It can come in the following formats + * and their permutations: + * str + * str1,str2 + */ + str = strtok(p, ","); + while (str) { + if (strlen(str) == strlen(val) && + !strcasecmp(str, val)) { + key_is_filtered = false; + goto out; + } + str = strtok(NULL, ","); + } + goto out; + } + } + +out: + free(p); + return key_is_filtered; +} + +/* + * Check if key is filtered: + * key doesn't exist -> user didn't request -> not filtered + */ +bool rd_check_is_filtered(struct rd *rd, const char *key, uint32_t val) +{ + bool key_is_filtered = false; + struct filter_entry *fe; + + list_for_each_entry(fe, &rd->filter_list, list) { + uint32_t left_val = 0, fe_value = 0; + bool range_check = false; + char *p = fe->value; + + if (!strcmpx(fe->key, key)) { + /* We found the key */ + key_is_filtered = true; + /* + * Need to check if value in range + * It can come in the following formats + * (and their permutations): + * numb + * numb1,numb2 + * ,numb1,numb2 + * numb1-numb2 + * numb1,numb2-numb3,numb4-numb5 + */ + while (*p) { + if (isdigit(*p)) { + fe_value = strtol(p, &p, 10); + if (fe_value == val || + (range_check && left_val < val && + val < fe_value)) { + key_is_filtered = false; + goto out; + } + range_check = false; + } else { + if (*p == '-') { + left_val = fe_value; + range_check = true; + } + p++; + } + } + goto out; + } + } + +out: + return key_is_filtered; +} + +static void filters_cleanup(struct rd *rd) +{ + struct filter_entry *fe, *tmp; + + list_for_each_entry_safe(fe, tmp, + &rd->filter_list, list) { + list_del(&fe->list); + free(fe->key); + free(fe->value); + free(fe); + } +} + static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_DEV_INDEX] = MNL_TYPE_U32, [RDMA_NLDEV_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING, @@ -97,6 +360,21 @@ static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_PORT_STATE] = MNL_TYPE_U8, [RDMA_NLDEV_ATTR_PORT_PHYS_STATE] = MNL_TYPE_U8, [RDMA_NLDEV_ATTR_DEV_NODE_TYPE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_SUMMARY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_RES_QP] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_QP_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_LQPN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_RQPN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_RQ_PSN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_SQ_PSN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_TYPE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_STATE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_PID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_KERN_NAME] = MNL_TYPE_NUL_STRING, }; int rd_attr_cb(const struct nlattr *attr, void *data) @@ -150,9 +428,29 @@ void rd_free(struct rd *rd) return; free(rd->buff); dev_map_cleanup(rd); + filters_cleanup(rd); } -int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd)) +int rd_set_arg_to_devname(struct rd *rd) +{ + int ret = 0; + + while (!rd_no_arg(rd)) { + if (rd_argv_match(rd, "dev") || rd_argv_match(rd, "link")) { + rd_arg_inc(rd); + if (rd_no_arg(rd)) { + pr_err("No device name was supplied\n"); + ret = -EINVAL; + } + goto out; + } + rd_arg_inc(rd); + } +out: + return ret; +} + +int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd), bool strict_port) { struct dev_map *dev_map; uint32_t port; @@ -163,7 +461,8 @@ int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd)) 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++) { + port = (strict_port) ? 1 : 0; + for (; port < dev_map->num_ports + 1; port++) { rd->port_idx = port; ret = cb(rd); if (ret) @@ -172,21 +471,23 @@ int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd)) } } else { + bool is_dump_all; + dev_map = dev_map_lookup(rd, true); - port = get_port_from_argv(rd); - if (!dev_map || port > dev_map->num_ports) { + ret = get_port_from_argv(rd, &port, &is_dump_all, strict_port); + if (!dev_map || port > dev_map->num_ports || (!port && ret)) { pr_err("Wrong device name\n"); ret = -ENOENT; goto out; } rd_arg_inc(rd); rd->dev_idx = dev_map->idx; - rd->port_idx = port ? : 1; + rd->port_idx = port; for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) { ret = cb(rd); if (ret) goto out; - if (port) + if (!is_dump_all) /* * We got request to show link for devname * with port index.