Merge branch 'iproute2-master' into iproute2-next

Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
David Ahern 2018-02-22 14:43:33 -08:00
commit 472e59b0eb
14 changed files with 934 additions and 59 deletions

10
.gitignore vendored
View File

@ -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

10
README
View File

@ -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.
--------------------

View File

@ -4,12 +4,15 @@ development. Most new features require a kernel and a utility component.
Please submit both to the Linux networking mailing list
<netdev@vger.kernel.org>
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.

17
configure vendored
View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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.

86
man/man8/rdma-resource.8 Normal file
View File

@ -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 <leonro@mellanox.com>

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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
*/

486
rdma/res.c Normal file
View File

@ -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 <leonro@mellanox.com>
*/
#include "rdma.h"
#include <inttypes.h>
static int res_help(struct rd *rd)
{
pr_out("Usage: %s resource\n", rd->filename);
pr_out(" resource show [DEV]\n");
pr_out(" resource show [qp]\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");
}

View File

@ -10,8 +10,9 @@
*/
#include "rdma.h"
#include <ctype.h>
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.