Merge branch 'iproute2-master' into iproute2-next
Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
commit
472e59b0eb
|
|
@ -39,13 +39,3 @@ testsuite/results
|
||||||
testsuite/iproute2/iproute2-this
|
testsuite/iproute2/iproute2-this
|
||||||
testsuite/tools/generate_nlmsg
|
testsuite/tools/generate_nlmsg
|
||||||
testsuite/tests/ip/link/dev_wo_vf_rate.nl
|
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
10
README
|
|
@ -1,13 +1,13 @@
|
||||||
This is a set of utilities for Linux networking.
|
This is a set of utilities for Linux networking.
|
||||||
|
|
||||||
Information:
|
|
||||||
http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2
|
|
||||||
|
|
||||||
Download:
|
Download:
|
||||||
http://www.kernel.org/pub/linux/utils/net/iproute2/
|
http://www.kernel.org/pub/linux/utils/net/iproute2/
|
||||||
|
|
||||||
Repository:
|
Stable version repository:
|
||||||
git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
|
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.
|
How to compile this.
|
||||||
--------------------
|
--------------------
|
||||||
|
|
|
||||||
17
README.devel
17
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
|
Please submit both to the Linux networking mailing list
|
||||||
<netdev@vger.kernel.org>
|
<netdev@vger.kernel.org>
|
||||||
|
|
||||||
The current source is in the git repository:
|
The current source for the stable version is in the git repository:
|
||||||
git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
|
git://git.kernel.org/pub/scm/network/iproute2/iproute2.git
|
||||||
|
|
||||||
The master branch contains the source corresponding to the current
|
The development git repository is available at the following address:
|
||||||
code in the mainline Linux kernel (ie follows Linus). The net-next
|
git://git.kernel.org/pub/scm/network/iproute2/iproute2-next.git
|
||||||
branch is a temporary branch that tracks the code intended for the
|
|
||||||
next release; it corresponds with networking development branch in
|
|
||||||
the kernel.
|
|
||||||
|
|
||||||
|
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,18 +17,6 @@ check_prog()
|
||||||
command -v $1 >/dev/null 2>&1 && (echo "$3:=y" >> $CONFIG; echo "yes") || (echo "no"; return 1)
|
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()
|
check_toolchain()
|
||||||
{
|
{
|
||||||
: ${PKG_CONFIG:=pkg-config}
|
: ${PKG_CONFIG:=pkg-config}
|
||||||
|
|
@ -422,11 +410,6 @@ check_berkeley_db
|
||||||
echo -n "need for strlcpy: "
|
echo -n "need for strlcpy: "
|
||||||
check_strlcpy
|
check_strlcpy
|
||||||
|
|
||||||
echo
|
|
||||||
echo -n "docs:"
|
|
||||||
check_docs
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo >> $CONFIG
|
echo >> $CONFIG
|
||||||
echo "%.o: %.c" >> $CONFIG
|
echo "%.o: %.c" >> $CONFIG
|
||||||
echo ' $(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<' >> $CONFIG
|
echo ' $(QUIET_CC)$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<' >> $CONFIG
|
||||||
|
|
|
||||||
4
ip/ip.c
4
ip/ip.c
|
|
@ -240,10 +240,6 @@ int main(int argc, char **argv)
|
||||||
} else if (matches(opt, "-tshort") == 0) {
|
} else if (matches(opt, "-tshort") == 0) {
|
||||||
++timestamp;
|
++timestamp;
|
||||||
++timestamp_short;
|
++timestamp_short;
|
||||||
#if 0
|
|
||||||
} else if (matches(opt, "-numeric") == 0) {
|
|
||||||
rtnl_names_numeric++;
|
|
||||||
#endif
|
|
||||||
} else if (matches(opt, "-Version") == 0) {
|
} else if (matches(opt, "-Version") == 0) {
|
||||||
printf("ip utility, iproute2-ss%s\n", SNAPSHOT);
|
printf("ip utility, iproute2-ss%s\n", SNAPSHOT);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
|
||||||
|
|
@ -718,7 +718,10 @@ static int netns_set(int argc, char **argv)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
name = argv[0];
|
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]);
|
invarg("Invalid \"netnsid\" value\n", argv[1]);
|
||||||
|
|
||||||
snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
|
snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
|
||||||
|
|
|
||||||
|
|
@ -137,6 +137,7 @@ $ ip netns del net0
|
||||||
.sp
|
.sp
|
||||||
This command assigns a id to a peer network namespace. This id is valid
|
This command assigns a id to a peer network namespace. This id is valid
|
||||||
only in the current network namespace.
|
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
|
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
|
assigned when the kernel needs it, it will be automatically assigned by
|
||||||
the kernel.
|
the kernel.
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -5,7 +5,7 @@ TARGETS :=
|
||||||
|
|
||||||
ifeq ($(HAVE_MNL),y)
|
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
|
TARGETS += rdma
|
||||||
endif
|
endif
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@ static int link_one_show(struct rd *rd)
|
||||||
|
|
||||||
static int link_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)
|
int cmd_link(struct rd *rd)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
static void help(char *name)
|
static void help(char *name)
|
||||||
{
|
{
|
||||||
pr_out("Usage: %s [ OPTIONS ] OBJECT { COMMAND | help }\n"
|
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);
|
" 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 },
|
{ "help", cmd_help },
|
||||||
{ "dev", cmd_dev },
|
{ "dev", cmd_dev },
|
||||||
{ "link", cmd_link },
|
{ "link", cmd_link },
|
||||||
|
{ "resource", cmd_res },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -47,6 +48,7 @@ static int rd_init(struct rd *rd, int argc, char **argv, char *filename)
|
||||||
rd->argc = argc;
|
rd->argc = argc;
|
||||||
rd->argv = argv;
|
rd->argv = argv;
|
||||||
INIT_LIST_HEAD(&rd->dev_map_list);
|
INIT_LIST_HEAD(&rd->dev_map_list);
|
||||||
|
INIT_LIST_HEAD(&rd->filter_list);
|
||||||
|
|
||||||
if (rd->json_output) {
|
if (rd->json_output) {
|
||||||
rd->jw = jsonw_new(stdout);
|
rd->jw = jsonw_new(stdout);
|
||||||
|
|
|
||||||
28
rdma/rdma.h
28
rdma/rdma.h
|
|
@ -29,6 +29,18 @@
|
||||||
#define RDMA_BITMAP_ENUM(name, bit_no) RDMA_BITMAP_##name = BIT(bit_no),
|
#define RDMA_BITMAP_ENUM(name, bit_no) RDMA_BITMAP_##name = BIT(bit_no),
|
||||||
#define RDMA_BITMAP_NAMES(name, bit_no) [bit_no] = #name,
|
#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 dev_map {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
char *dev_name;
|
char *dev_name;
|
||||||
|
|
@ -50,6 +62,7 @@ struct rd {
|
||||||
json_writer_t *jw;
|
json_writer_t *jw;
|
||||||
bool json_output;
|
bool json_output;
|
||||||
bool pretty_output;
|
bool pretty_output;
|
||||||
|
struct list_head filter_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rd_cmd {
|
struct rd_cmd {
|
||||||
|
|
@ -64,23 +77,34 @@ bool rd_no_arg(struct rd *rd);
|
||||||
void rd_arg_inc(struct rd *rd);
|
void rd_arg_inc(struct rd *rd);
|
||||||
|
|
||||||
char *rd_argv(struct rd *rd);
|
char *rd_argv(struct rd *rd);
|
||||||
uint32_t get_port_from_argv(struct rd *rd);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Commands interface
|
* Commands interface
|
||||||
*/
|
*/
|
||||||
int cmd_dev(struct rd *rd);
|
int cmd_dev(struct rd *rd);
|
||||||
int cmd_link(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_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_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);
|
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
|
* Device manipulation
|
||||||
*/
|
*/
|
||||||
struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index);
|
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
|
* Netlink
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
321
rdma/utils.c
321
rdma/utils.c
|
|
@ -10,8 +10,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rdma.h"
|
#include "rdma.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
static int rd_argc(struct rd *rd)
|
int rd_argc(struct rd *rd)
|
||||||
{
|
{
|
||||||
return rd->argc;
|
return rd->argc;
|
||||||
}
|
}
|
||||||
|
|
@ -23,7 +24,7 @@ char *rd_argv(struct rd *rd)
|
||||||
return *rd->argv;
|
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))
|
if (strlen(str1) > strlen(str2))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -50,13 +51,43 @@ bool rd_no_arg(struct rd *rd)
|
||||||
return rd_argc(rd) == 0;
|
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;
|
char *slash;
|
||||||
|
|
||||||
|
*port = 0;
|
||||||
|
*is_dump_all = true;
|
||||||
|
|
||||||
slash = strchr(rd_argv(rd), '/');
|
slash = strchr(rd_argv(rd), '/');
|
||||||
/* if no port found, return 0 */
|
/* 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)
|
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)
|
if (!dev_map)
|
||||||
return NULL;
|
return NULL;
|
||||||
dev_map->dev_name = strdup(dev_name);
|
dev_map->dev_name = strdup(dev_name);
|
||||||
|
if (!dev_map->dev_name) {
|
||||||
|
free(dev_map);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return dev_map;
|
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] = {
|
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_INDEX] = MNL_TYPE_U32,
|
||||||
[RDMA_NLDEV_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING,
|
[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_STATE] = MNL_TYPE_U8,
|
||||||
[RDMA_NLDEV_ATTR_PORT_PHYS_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_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)
|
int rd_attr_cb(const struct nlattr *attr, void *data)
|
||||||
|
|
@ -150,9 +428,29 @@ void rd_free(struct rd *rd)
|
||||||
return;
|
return;
|
||||||
free(rd->buff);
|
free(rd->buff);
|
||||||
dev_map_cleanup(rd);
|
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;
|
struct dev_map *dev_map;
|
||||||
uint32_t port;
|
uint32_t port;
|
||||||
|
|
@ -163,7 +461,8 @@ int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd))
|
||||||
if (rd_no_arg(rd)) {
|
if (rd_no_arg(rd)) {
|
||||||
list_for_each_entry(dev_map, &rd->dev_map_list, list) {
|
list_for_each_entry(dev_map, &rd->dev_map_list, list) {
|
||||||
rd->dev_idx = dev_map->idx;
|
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;
|
rd->port_idx = port;
|
||||||
ret = cb(rd);
|
ret = cb(rd);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
@ -172,21 +471,23 @@ int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd))
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
bool is_dump_all;
|
||||||
|
|
||||||
dev_map = dev_map_lookup(rd, true);
|
dev_map = dev_map_lookup(rd, true);
|
||||||
port = get_port_from_argv(rd);
|
ret = get_port_from_argv(rd, &port, &is_dump_all, strict_port);
|
||||||
if (!dev_map || port > dev_map->num_ports) {
|
if (!dev_map || port > dev_map->num_ports || (!port && ret)) {
|
||||||
pr_err("Wrong device name\n");
|
pr_err("Wrong device name\n");
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
rd_arg_inc(rd);
|
rd_arg_inc(rd);
|
||||||
rd->dev_idx = dev_map->idx;
|
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++) {
|
for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) {
|
||||||
ret = cb(rd);
|
ret = cb(rd);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
if (port)
|
if (!is_dump_all)
|
||||||
/*
|
/*
|
||||||
* We got request to show link for devname
|
* We got request to show link for devname
|
||||||
* with port index.
|
* with port index.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue