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/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
10
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.
|
||||
--------------------
|
||||
|
|
|
|||
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
|
||||
<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,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
|
||||
|
|
|
|||
4
ip/ip.c
4
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
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_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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 <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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue