Merge branch 'master' into next

Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
David Ahern 2019-08-07 11:59:19 -07:00
commit 74ddde9b5f
48 changed files with 1421 additions and 522 deletions

View File

@ -25,7 +25,6 @@
#include <linux/devlink.h>
#include <libmnl/libmnl.h>
#include <netinet/ether.h>
#include <sys/queue.h>
#include "SNAPSHOT.h"
#include "list.h"
@ -1779,29 +1778,26 @@ static void pr_out_uint64_value(struct dl *dl, uint64_t value)
pr_out(" %"PRIu64, value);
}
static bool is_binary_eol(int i)
{
return !(i%16);
}
static void pr_out_binary_value(struct dl *dl, uint8_t *data, uint32_t len)
{
int i = 1;
if (dl->json_output)
jsonw_start_array(dl->jw);
else
pr_out("\n");
int i = 0;
while (i < len) {
if (dl->json_output) {
if (dl->json_output)
jsonw_printf(dl->jw, "%d", data[i]);
} else {
pr_out(" %02x", data[i]);
if (!(i % 16))
pr_out("\n");
}
else
pr_out("%02x ", data[i]);
i++;
if (!dl->json_output && is_binary_eol(i))
__pr_out_newline();
}
if (dl->json_output)
jsonw_end_array(dl->jw);
else if ((i - 1) % 16)
pr_out("\n");
if (!dl->json_output && !is_binary_eol(i))
__pr_out_newline();
}
static void pr_out_str_value(struct dl *dl, const char *value)
@ -5982,35 +5978,36 @@ static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data)
return MNL_CB_OK;
}
struct nest_qentry {
struct nest_entry {
int attr_type;
TAILQ_ENTRY(nest_qentry) nest_entries;
struct list_head list;
};
struct fmsg_cb_data {
struct dl *dl;
uint8_t value_type;
TAILQ_HEAD(, nest_qentry) qhead;
struct list_head entry_list;
};
static int cmd_fmsg_nest_queue(struct fmsg_cb_data *fmsg_data,
uint8_t *attr_value, bool insert)
{
struct nest_qentry *entry = NULL;
struct nest_entry *entry;
if (insert) {
entry = malloc(sizeof(struct nest_qentry));
entry = malloc(sizeof(struct nest_entry));
if (!entry)
return -ENOMEM;
entry->attr_type = *attr_value;
TAILQ_INSERT_HEAD(&fmsg_data->qhead, entry, nest_entries);
list_add(&entry->list, &fmsg_data->entry_list);
} else {
if (TAILQ_EMPTY(&fmsg_data->qhead))
if (list_empty(&fmsg_data->entry_list))
return MNL_CB_ERROR;
entry = TAILQ_FIRST(&fmsg_data->qhead);
entry = list_first_entry(&fmsg_data->entry_list,
struct nest_entry, list);
*attr_value = entry->attr_type;
TAILQ_REMOVE(&fmsg_data->qhead, entry, nest_entries);
list_del(&entry->list);
free(entry);
}
return MNL_CB_OK;
@ -6105,13 +6102,13 @@ static int cmd_fmsg_object_cb(const struct nlmsghdr *nlh, void *data)
return MNL_CB_OK;
}
static int cmd_health_object_common(struct dl *dl, uint8_t cmd)
static int cmd_health_object_common(struct dl *dl, uint8_t cmd, uint16_t flags)
{
struct fmsg_cb_data data;
struct nlmsghdr *nlh;
int err;
nlh = mnlg_msg_prepare(dl->nlg, cmd, NLM_F_REQUEST | NLM_F_ACK);
nlh = mnlg_msg_prepare(dl->nlg, cmd, flags | NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl,
DL_OPT_HANDLE | DL_OPT_HEALTH_REPORTER_NAME, 0);
@ -6119,19 +6116,23 @@ static int cmd_health_object_common(struct dl *dl, uint8_t cmd)
return err;
data.dl = dl;
TAILQ_INIT(&data.qhead);
INIT_LIST_HEAD(&data.entry_list);
err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_fmsg_object_cb, &data);
return err;
}
static int cmd_health_dump_show(struct dl *dl)
{
return cmd_health_object_common(dl, DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET);
return cmd_health_object_common(dl,
DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
NLM_F_DUMP);
}
static int cmd_health_diagnose(struct dl *dl)
{
return cmd_health_object_common(dl, DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE);
return cmd_health_object_common(dl,
DEVLINK_CMD_HEALTH_REPORTER_DIAGNOSE,
0);
}
static int cmd_health_recover(struct dl *dl)

View File

@ -57,20 +57,21 @@ void print_nl(void);
{ \
print_color_##type_name(t, COLOR_NONE, key, fmt, value); \
}
_PRINT_FUNC(int, int);
_PRINT_FUNC(s64, int64_t);
_PRINT_FUNC(bool, bool);
_PRINT_FUNC(null, const char*);
_PRINT_FUNC(string, const char*);
_PRINT_FUNC(uint, unsigned int);
_PRINT_FUNC(u64, uint64_t);
_PRINT_FUNC(hhu, unsigned char);
_PRINT_FUNC(hu, unsigned short);
_PRINT_FUNC(hex, unsigned int);
_PRINT_FUNC(0xhex, unsigned long long);
_PRINT_FUNC(luint, unsigned long);
_PRINT_FUNC(lluint, unsigned long long);
_PRINT_FUNC(float, double);
_PRINT_FUNC(int, int)
_PRINT_FUNC(s64, int64_t)
_PRINT_FUNC(bool, bool)
_PRINT_FUNC(null, const char*)
_PRINT_FUNC(string, const char*)
_PRINT_FUNC(uint, unsigned int)
_PRINT_FUNC(u64, uint64_t)
_PRINT_FUNC(hhu, unsigned char)
_PRINT_FUNC(hu, unsigned short)
_PRINT_FUNC(hex, unsigned int)
_PRINT_FUNC(0xhex, unsigned long long)
_PRINT_FUNC(luint, unsigned long)
_PRINT_FUNC(lluint, unsigned long long)
_PRINT_FUNC(float, double)
#undef _PRINT_FUNC
#endif /* _JSON_PRINT_H_ */

View File

@ -806,7 +806,7 @@ union bpf_attr {
* based on a user-provided identifier for all traffic coming from
* the tasks belonging to the related cgroup. See also the related
* kernel documentation, available from the Linux sources in file
* *Documentation/cgroup-v1/net_cls.txt*.
* *Documentation/admin-guide/cgroup-v1/net_cls.rst*.
*
* The Linux kernel has two versions for cgroups: there are
* cgroups v1 and cgroups v2. Both are available to users, who can
@ -3248,7 +3248,7 @@ struct bpf_sock_addr {
__u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
* Stored in network byte order.
*/
__u32 user_ip6[4]; /* Allows 1,2,4-byte read and 4,8-byte write.
__u32 user_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write.
* Stored in network byte order.
*/
__u32 user_port; /* Allows 4-byte read and write.
@ -3260,7 +3260,7 @@ struct bpf_sock_addr {
__u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write.
* Stored in network byte order.
*/
__u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read and 4,8-byte write.
__u32 msg_src_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write.
* Stored in network byte order.
*/
__bpf_md_ptr(struct bpf_sock *, sk);

View File

@ -91,5 +91,7 @@
#define UDF_SUPER_MAGIC 0x15013346
#define BALLOON_KVM_MAGIC 0x13661366
#define ZSMALLOC_MAGIC 0x58295829
#define DMA_BUF_MAGIC 0x444d4142 /* "DMAB" */
#define Z3FOLD_MAGIC 0x33
#endif /* __LINUX_MAGIC_H__ */

View File

@ -1174,7 +1174,7 @@ enum {
TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME, /* s64 */
TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION, /* s64 */
TCA_TAPRIO_ATTR_FLAGS, /* u32 */
TCA_TAPRIO_ATTR_TXTIME_DELAY, /* s32 */
TCA_TAPRIO_ATTR_TXTIME_DELAY, /* u32 */
__TCA_TAPRIO_ATTR_MAX,
};

View File

@ -198,7 +198,7 @@ int nodev(const char *dev);
int check_ifname(const char *);
int get_ifname(char *, const char *);
const char *get_ifname_rta(int ifindex, const struct rtattr *rta);
int matches(const char *arg, const char *pattern);
bool matches(const char *prefix, const char *string);
int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
int inet_addr_match_rta(const inet_prefix *m, const struct rtattr *rta);

View File

@ -281,8 +281,6 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p)
p->link = ll_name_to_index(medium);
if (!p->link)
return nodev(medium);
else
strlcpy(p->name, medium, sizeof(p->name));
}
return 0;
}
@ -371,6 +369,9 @@ static int do_add(int cmd, int argc, char **argv)
if (parse_args(argc, argv, cmd, &p) < 0)
return -1;
if (!*p.name)
fprintf(stderr, "Tunnel interface name not specified\n");
if (p.proto == IPPROTO_GRE)
basedev = "ip6gre0";
else if (p.i_flags & VTI_ISVTI)

View File

@ -545,7 +545,7 @@ static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
print_int(PRINT_ANY,
"clock",
"\n clock %d",
"\n clock %d ",
clock->freq);
}

View File

@ -75,7 +75,7 @@ static void jsonw_puts(json_writer_t *self, const char *str)
fputs("\\b", self->out);
break;
case '\\':
fputs("\\n", self->out);
fputs("\\\\", self->out);
break;
case '"':
fputs("\\\"", self->out);

View File

@ -374,7 +374,7 @@ int get_u8(__u8 *val, const char *arg, int base)
int get_s64(__s64 *val, const char *arg, int base)
{
long res;
long long res;
char *ptr;
errno = 0;
@ -871,13 +871,18 @@ const char *get_ifname_rta(int ifindex, const struct rtattr *rta)
return name;
}
int matches(const char *cmd, const char *pattern)
/* Returns false if 'prefix' is a not empty prefix of 'string'.
*/
bool matches(const char *prefix, const char *string)
{
int len = strlen(cmd);
if (!*prefix)
return true;
while (*string && *prefix == *string) {
prefix++;
string++;
}
if (len > strlen(pattern))
return -1;
return memcmp(pattern, cmd, len);
return !!*prefix;
}
int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits)

View File

@ -1815,6 +1815,11 @@ can move the system to an unpredictable state. The solution
is to avoid changing several parameters with one
.B ip link set
call.
The modifier
.B change
is equivalent to
.BR "set" .
.TP
.BI dev " DEVICE "

View File

@ -34,11 +34,17 @@ rdma-dev \- RDMA device configuration
.BR netns
.BR NSNAME
.ti -8
.B rdma dev set
.RI "[ " DEV " ]"
.BR adaptive-moderation
.BR [on/off]
.ti -8
.B rdma dev help
.SH "DESCRIPTION"
.SS rdma dev set - rename RDMA device or set network namespace
.SS rdma dev set - rename RDMA device or set network namespace or set RDMA device adaptive-moderation
.SS rdma dev show - display RDMA device attributes
@ -70,6 +76,14 @@ Changes the network namespace of RDMA device to foo where foo is
previously created using iproute2 ip command.
.RE
.PP
rdma dev set mlx5_3 adaptive-moderation [on/off]
.RS 4
Sets the state of adaptive interrupt moderation for the RDMA device.
.RE
.RS 4
This is a global setting for the RDMA device but the value is printed for each CQ individually because the state is constant from CQ allocation.
.RE
.PP
.SH SEE ALSO
.BR ip (8),
@ -77,6 +91,7 @@ previously created using iproute2 ip command.
.BR rdma-link (8),
.BR rdma-resource (8),
.BR rdma-system (8),
.BR rdma-statistic (8),
.br
.SH AUTHOR

View File

@ -97,6 +97,7 @@ Removes RXE link rxe_eth0
.BR rdma (8),
.BR rdma-dev (8),
.BR rdma-resource (8),
.BR rdma-statistic (8),
.br
.SH AUTHOR

View File

@ -103,6 +103,7 @@ Show CQs belonging to pid 30489
.BR rdma (8),
.BR rdma-dev (8),
.BR rdma-link (8),
.BR rdma-statistic (8),
.br
.SH AUTHOR

167
man/man8/rdma-statistic.8 Normal file
View File

@ -0,0 +1,167 @@
.TH RDMA\-STATISTIC 8 "17 Mar 2019" "iproute2" "Linux"
.SH NAME
rdma-statistic \- RDMA statistic counter configuration
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.B rdma
.RI "[ " OPTIONS " ]"
.B statistic
.RI " { " COMMAND " | "
.BR help " }"
.sp
.ti -8
.B rdma statistic
.RI "[ " OBJECT " ]"
.B show
.ti -8
.B rdma statistic
.RI "[ " OBJECT " ]"
.B show link
.RI "[ " DEV/PORT_INDX " ]"
.ti -8
.B rdma statistic
.IR OBJECT
.B mode
.ti -8
.B rdma statistic
.IR OBJECT
.B set
.IR COUNTER_SCOPE
.RI "[ " DEV/PORT_INDEX "]"
.B auto
.RI "{ " CRITERIA " | "
.BR off " }"
.ti -8
.B rdma statistic
.IR OBJECT
.B bind
.IR COUNTER_SCOPE
.RI "[ " DEV/PORT_INDEX "]"
.RI "[ " OBJECT-ID " ]"
.RI "[ " COUNTER-ID " ]"
.ti -8
.B rdma statistic
.IR OBJECT
.B unbind
.IR COUNTER_SCOPE
.RI "[ " DEV/PORT_INDEX "]"
.RI "[ " COUNTER-ID " ]"
.RI "[ " OBJECT-ID " ]"
.ti -8
.IR COUNTER_SCOPE " := "
.RB "{ " link " | " dev " }"
.ti -8
.IR OBJECT " := "
.RB "{ " qp " }"
.ti -8
.IR CRITERIA " := "
.RB "{ " type " }"
.SH "DESCRIPTION"
.SS rdma statistic [object] show - Queries the specified RDMA device for RDMA and driver-specific statistics. Show the default hw counters if object is not specified
.PP
.I "DEV"
- specifies counters on this RDMA device to show.
.I "PORT_INDEX"
- specifies counters on this RDMA port to show.
.SS rdma statistic <object> set - configure counter statistic auto-mode for a specific device/port
In auto mode all objects belong to one category are bind automatically to a single counter set.
.SS rdma statistic <object> bind - manually bind an object (e.g., a qp) with a counter
When bound the statistics of this object are available in this counter.
.SS rdma statistic <object> unbind - manually unbind an object (e.g., a qp) from the counter previously bound
When unbound the statistics of this object are no longer available in this counter; And if object id is not specified then all objects on this counter will be unbound.
.I "COUNTER-ID"
- specifies the id of the counter to be bound.
If this argument is omitted then a new counter will be allocated.
.SH "EXAMPLES"
.PP
rdma statistic show
.RS 4
Shows the state of the default counter of all RDMA devices on the system.
.RE
.PP
rdma statistic show link mlx5_2/1
.RS 4
Shows the state of the default counter of specified RDMA port
.RE
.PP
rdma statistic qp show
.RS 4
Shows the state of all qp counters of all RDMA devices on the system.
.RE
.PP
rdma statistic qp show link mlx5_2/1
.RS 4
Shows the state of all qp counters of specified RDMA port.
.RE
.PP
rdma statistic qp show link mlx5_2 pid 30489
.RS 4
Shows the state of all qp counters of specified RDMA port and belonging to pid 30489
.RE
.PP
rdma statistic qp mode
.RS 4
List current counter mode on all devices
.RE
.PP
rdma statistic qp mode link mlx5_2/1
.RS 4
List current counter mode of device mlx5_2 port 1
.RE
.PP
rdma statistic qp set link mlx5_2/1 auto type on
.RS 4
On device mlx5_2 port 1, for each new QP bind it with a counter automatically. Per counter for QPs with same qp type in each process. Currently only "type" is supported.
.RE
.PP
rdma statistic qp set link mlx5_2/1 auto off
.RS 4
Turn-off auto mode on device mlx5_2 port 1. The allocated counters can be manually accessed.
.RE
.PP
rdma statistic qp bind link mlx5_2/1 lqpn 178
.RS 4
On device mlx5_2 port 1, allocate a counter and bind the specified qp on it
.RE
.PP
rdma statistic qp unbind link mlx5_2/1 cntn 4 lqpn 178
.RS 4
On device mlx5_2 port 1, bind the specified qp on the specified counter
.RE
.PP
rdma statistic qp unbind link mlx5_2/1 cntn 4
.RS 4
On device mlx5_2 port 1, unbind all QPs on the specified counter. After that this counter will be released automatically by the kernel.
.RE
.PP
.SH SEE ALSO
.BR rdma (8),
.BR rdma-dev (8),
.BR rdma-link (8),
.BR rdma-resource (8),
.br
.SH AUTHOR
Mark Zhang <markz@mellanox.com>

View File

@ -19,7 +19,7 @@ rdma \- RDMA tool
.ti -8
.IR OBJECT " := { "
.BR dev " | " link " | " system " }"
.BR dev " | " link " | " system " | " statistic " }"
.sp
.ti -8
@ -74,6 +74,10 @@ Generate JSON output.
.B sys
- RDMA subsystem related.
.TP
.B statistic
- RDMA counter statistic related.
.PP
The names of all objects may be written in full or
abbreviated form, for example
@ -112,6 +116,7 @@ Exit status is 0 if command was successful or a positive integer upon failure.
.BR rdma-link (8),
.BR rdma-resource (8),
.BR rdma-system (8),
.BR rdma-statistic (8),
.br
.SH REPORTING BUGS

View File

@ -2414,7 +2414,7 @@ static void sctp_stats_print(struct sctp_info *s)
if (s->sctpi_s_pd_point)
out(" pdpoint:%d", s->sctpi_s_pd_point);
if (s->sctpi_s_nodelay)
out(" nodealy:%d", s->sctpi_s_nodelay);
out(" nodelay:%d", s->sctpi_s_nodelay);
if (s->sctpi_s_disable_fragments)
out(" nofrag:%d", s->sctpi_s_disable_fragments);
if (s->sctpi_s_v4mapped)
@ -2937,7 +2937,7 @@ static void sctp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
len = RTA_PAYLOAD(tb[INET_DIAG_LOCALS]);
sa = RTA_DATA(tb[INET_DIAG_LOCALS]);
out("locals:%s", format_host_sa(sa));
out(" locals:%s", format_host_sa(sa));
for (sa++, len -= sizeof(*sa); len > 0; sa++, len -= sizeof(*sa))
out(",%s", format_host_sa(sa));

View File

@ -7,7 +7,7 @@ ifeq ($(HAVE_MNL),y)
CFLAGS += -I./include/uapi/
RDMA_OBJ = rdma.o utils.o dev.o link.o res.o res-pd.o res-mr.o res-cq.o \
res-cmid.o res-qp.o sys.o
res-cmid.o res-qp.o sys.o stat.o
TARGETS += rdma
endif

View File

@ -12,6 +12,7 @@ static int dev_help(struct rd *rd)
pr_out("Usage: %s dev show [DEV]\n", rd->filename);
pr_out(" %s dev set [DEV] name DEVNAME\n", rd->filename);
pr_out(" %s dev set [DEV] netns NSNAME\n", rd->filename);
pr_out(" %s dev set [DEV] adaptive-moderation [on|off]\n", rd->filename);
return 0;
}
@ -167,6 +168,21 @@ static void dev_print_sys_image_guid(struct rd *rd, struct nlattr **tb)
pr_out("sys_image_guid %s ", str);
}
static void dev_print_dim_setting(struct rd *rd, struct nlattr **tb)
{
uint8_t dim_setting;
if (!tb[RDMA_NLDEV_ATTR_DEV_DIM])
return;
dim_setting = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_DEV_DIM]);
if (dim_setting > 1)
return;
print_on_off(rd, "adaptive-moderation", dim_setting);
}
static const char *node_type_to_str(uint8_t node_type)
{
static const char * const node_type_str[] = { "unknown", "ca",
@ -219,8 +235,10 @@ static int dev_parse_cb(const struct nlmsghdr *nlh, void *data)
dev_print_fw(rd, tb);
dev_print_node_guid(rd, tb);
dev_print_sys_image_guid(rd, tb);
if (rd->show_details)
if (rd->show_details) {
dev_print_dim_setting(rd, tb);
dev_print_caps(rd, tb);
}
if (!rd->json_output)
pr_out("\n");
@ -308,12 +326,47 @@ done:
return ret;
}
static int dev_set_dim_sendmsg(struct rd *rd, uint8_t dim_setting)
{
uint32_t seq;
rd_prepare_msg(rd, RDMA_NLDEV_CMD_SET, &seq,
(NLM_F_REQUEST | NLM_F_ACK));
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u8(rd->nlh, RDMA_NLDEV_ATTR_DEV_DIM, dim_setting);
return rd_sendrecv_msg(rd, seq);
}
static int dev_set_dim_off(struct rd *rd)
{
return dev_set_dim_sendmsg(rd, 0);
}
static int dev_set_dim_on(struct rd *rd)
{
return dev_set_dim_sendmsg(rd, 1);
}
static int dev_set_dim(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, dev_help},
{ "on", dev_set_dim_on},
{ "off", dev_set_dim_off},
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int dev_one_set(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, dev_help},
{ "name", dev_set_name},
{ "netns", dev_set_netns},
{ "adaptive-moderation", dev_set_dim},
{ 0 }
};

View File

@ -147,6 +147,18 @@ enum {
IWPM_NLA_HELLO_MAX
};
/* For RDMA_NLDEV_ATTR_DEV_NODE_TYPE */
enum {
/* IB values map to NodeInfo:NodeType. */
RDMA_NODE_IB_CA = 1,
RDMA_NODE_IB_SWITCH,
RDMA_NODE_IB_ROUTER,
RDMA_NODE_RNIC,
RDMA_NODE_USNIC,
RDMA_NODE_USNIC_UDP,
RDMA_NODE_UNSPECIFIED,
};
/*
* Local service operations:
* RESOLVE - The client requests the local service to resolve a path.
@ -267,11 +279,15 @@ enum rdma_nldev_command {
RDMA_NLDEV_CMD_RES_PD_GET, /* can dump */
RDMA_NLDEV_NUM_OPS
};
RDMA_NLDEV_CMD_GET_CHARDEV,
enum {
RDMA_NLDEV_ATTR_ENTRY_STRLEN = 16,
RDMA_NLDEV_CMD_STAT_SET,
RDMA_NLDEV_CMD_STAT_GET, /* can dump */
RDMA_NLDEV_CMD_STAT_DEL,
RDMA_NLDEV_NUM_OPS
};
enum rdma_nldev_print_type {
@ -478,10 +494,72 @@ enum rdma_nldev_attr {
* File descriptor handle of the net namespace object
*/
RDMA_NLDEV_NET_NS_FD, /* u32 */
/*
* Information about a chardev.
* CHARDEV_TYPE is the name of the chardev ABI (ie uverbs, umad, etc)
* CHARDEV_ABI signals the ABI revision (historical)
* CHARDEV_NAME is the kernel name for the /dev/ file (no directory)
* CHARDEV is the 64 bit dev_t for the inode
*/
RDMA_NLDEV_ATTR_CHARDEV_TYPE, /* string */
RDMA_NLDEV_ATTR_CHARDEV_NAME, /* string */
RDMA_NLDEV_ATTR_CHARDEV_ABI, /* u64 */
RDMA_NLDEV_ATTR_CHARDEV, /* u64 */
RDMA_NLDEV_ATTR_UVERBS_DRIVER_ID, /* u64 */
/*
* Counter-specific attributes.
*/
RDMA_NLDEV_ATTR_STAT_MODE, /* u32 */
RDMA_NLDEV_ATTR_STAT_RES, /* u32 */
RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, /* u32 */
RDMA_NLDEV_ATTR_STAT_COUNTER, /* nested table */
RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY, /* nested table */
RDMA_NLDEV_ATTR_STAT_COUNTER_ID, /* u32 */
RDMA_NLDEV_ATTR_STAT_HWCOUNTERS, /* nested table */
RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY, /* nested table */
RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME, /* string */
RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE, /* u64 */
/*
* CQ adaptive moderatio (DIM)
*/
RDMA_NLDEV_ATTR_DEV_DIM, /* u8 */
/*
* Always the end
*/
RDMA_NLDEV_ATTR_MAX
};
/*
* Supported counter bind modes. All modes are mutual-exclusive.
*/
enum rdma_nl_counter_mode {
RDMA_COUNTER_MODE_NONE,
/*
* A qp is bound with a counter automatically during initialization
* based on the auto mode (e.g., qp type, ...)
*/
RDMA_COUNTER_MODE_AUTO,
/*
* Which qp are bound with which counter is explicitly specified
* by the user
*/
RDMA_COUNTER_MODE_MANUAL,
/*
* Always the end
*/
RDMA_COUNTER_MODE_MAX,
};
/*
* Supported criteria in counter auto mode.
* Currently only "qp type" is supported
*/
enum rdma_nl_counter_mask {
RDMA_COUNTER_MASK_QP_TYPE = 1,
};
#endif /* _RDMA_NETLINK_H */

View File

@ -11,7 +11,7 @@ static void help(char *name)
{
pr_out("Usage: %s [ OPTIONS ] OBJECT { COMMAND | help }\n"
" %s [ -f[orce] ] -b[atch] filename\n"
"where OBJECT := { dev | link | resource | system | help }\n"
"where OBJECT := { dev | link | resource | system | statistic | help }\n"
" OPTIONS := { -V[ersion] | -d[etails] | -j[son] | -p[retty]}\n", name, name);
}
@ -30,6 +30,7 @@ static int rd_cmd(struct rd *rd, int argc, char **argv)
{ "link", cmd_link },
{ "resource", cmd_res },
{ "system", cmd_sys },
{ "statistic", cmd_stat },
{ 0 }
};

View File

@ -94,6 +94,7 @@ int cmd_dev(struct rd *rd);
int cmd_link(struct rd *rd);
int cmd_res(struct rd *rd);
int cmd_sys(struct rd *rd);
int cmd_stat(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_require_dev(struct rd *rd, int (*cb)(struct rd *rd));
@ -135,6 +136,7 @@ int rd_attr_check(const struct nlattr *attr, int *typep);
void print_driver_table(struct rd *rd, struct nlattr *tb);
void newline(struct rd *rd);
void newline_indent(struct rd *rd);
void print_on_off(struct rd *rd, const char *key_str, bool on);
#define MAX_LINE_LENGTH 80
#endif /* _RDMA_TOOL_H_ */

View File

@ -30,6 +30,20 @@ static void print_poll_ctx(struct rd *rd, uint8_t poll_ctx, struct nlattr *attr)
pr_out("poll-ctx %s ", poll_ctx_to_str(poll_ctx));
}
static void print_cq_dim_setting(struct rd *rd, struct nlattr *attr)
{
uint8_t dim_setting;
if (!attr)
return;
dim_setting = mnl_attr_get_u8(attr);
if (dim_setting > 1)
return;
print_on_off(rd, "adaptive-moderation", dim_setting);
}
static int res_cq_line(struct rd *rd, const char *name, int idx,
struct nlattr **nla_line)
{
@ -97,6 +111,7 @@ static int res_cq_line(struct rd *rd, const char *name, int idx,
res_print_uint(rd, "users", users,
nla_line[RDMA_NLDEV_ATTR_RES_USECNT]);
print_poll_ctx(rd, poll_ctx, nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX]);
print_cq_dim_setting(rd, nla_line[RDMA_NLDEV_ATTR_DEV_DIM]);
res_print_uint(rd, "ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]);
res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
print_comm(rd, comm, nla_line);

759
rdma/stat.c Normal file
View File

@ -0,0 +1,759 @@
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* rdma.c RDMA tool
* Authors: Mark Zhang <markz@mellanox.com>
*/
#include "rdma.h"
#include "res.h"
#include <inttypes.h>
static int stat_help(struct rd *rd)
{
pr_out("Usage: %s [ OPTIONS ] statistic { COMMAND | help }\n", rd->filename);
pr_out(" %s statistic OBJECT show\n", rd->filename);
pr_out(" %s statistic OBJECT show link [ DEV/PORT_INDEX ] [ FILTER-NAME FILTER-VALUE ]\n", rd->filename);
pr_out(" %s statistic OBJECT mode\n", rd->filename);
pr_out(" %s statistic OBJECT set COUNTER_SCOPE [DEV/PORT_INDEX] auto {CRITERIA | off}\n", rd->filename);
pr_out(" %s statistic OBJECT bind COUNTER_SCOPE [DEV/PORT_INDEX] [OBJECT-ID] [COUNTER-ID]\n", rd->filename);
pr_out(" %s statistic OBJECT unbind COUNTER_SCOPE [DEV/PORT_INDEX] [COUNTER-ID]\n", rd->filename);
pr_out(" %s statistic show\n", rd->filename);
pr_out(" %s statistic show link [ DEV/PORT_INDEX ]\n", rd->filename);
pr_out("where OBJECT: = { qp }\n");
pr_out(" CRITERIA : = { type }\n");
pr_out(" COUNTER_SCOPE: = { link | dev }\n");
pr_out("Examples:\n");
pr_out(" %s statistic qp show\n", rd->filename);
pr_out(" %s statistic qp show link mlx5_2/1\n", rd->filename);
pr_out(" %s statistic qp mode\n", rd->filename);
pr_out(" %s statistic qp mode link mlx5_0\n", rd->filename);
pr_out(" %s statistic qp set link mlx5_2/1 auto type on\n", rd->filename);
pr_out(" %s statistic qp set link mlx5_2/1 auto off\n", rd->filename);
pr_out(" %s statistic qp bind link mlx5_2/1 lqpn 178\n", rd->filename);
pr_out(" %s statistic qp bind link mlx5_2/1 lqpn 178 cntn 4\n", rd->filename);
pr_out(" %s statistic qp unbind link mlx5_2/1 cntn 4\n", rd->filename);
pr_out(" %s statistic qp unbind link mlx5_2/1 cntn 4 lqpn 178\n", rd->filename);
pr_out(" %s statistic show\n", rd->filename);
pr_out(" %s statistic show link mlx5_2/1\n", rd->filename);
return 0;
}
struct counter_param {
char *name;
uint32_t attr;
};
static struct counter_param auto_params[] = {
{ "type", RDMA_COUNTER_MASK_QP_TYPE, },
{ NULL },
};
static int prepare_auto_mode_str(struct nlattr **tb, uint32_t mask,
char *output, int len)
{
char s[] = "qp auto";
int i, outlen = strlen(s);
memset(output, 0, len);
snprintf(output, len, "%s", s);
if (mask) {
for (i = 0; auto_params[i].name != NULL; i++) {
if (mask & auto_params[i].attr) {
outlen += strlen(auto_params[i].name) + 1;
if (outlen >= len)
return -EINVAL;
strcat(output, " ");
strcat(output, auto_params[i].name);
}
}
if (outlen + strlen(" on") >= len)
return -EINVAL;
strcat(output, " on");
} else {
if (outlen + strlen(" off") >= len)
return -EINVAL;
strcat(output, " off");
}
return 0;
}
static int qp_link_get_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
uint32_t mode = 0, mask = 0;
char output[128] = {};
struct rd *rd = data;
uint32_t idx, port;
const char *name;
mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME])
return MNL_CB_ERROR;
if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) {
pr_err("This tool doesn't support switches yet\n");
return MNL_CB_ERROR;
}
idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]);
port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
if (tb[RDMA_NLDEV_ATTR_STAT_MODE])
mode = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_MODE]);
if (mode == RDMA_COUNTER_MODE_AUTO) {
if (!tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK])
return MNL_CB_ERROR;
mask = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]);
prepare_auto_mode_str(tb, mask, output, sizeof(output));
} else {
snprintf(output, sizeof(output), "qp auto off");
}
if (rd->json_output) {
jsonw_uint_field(rd->jw, "ifindex", idx);
jsonw_uint_field(rd->jw, "port", port);
jsonw_string_field(rd->jw, "mode", output);
} else {
pr_out("%u/%u: %s/%u: %s\n", idx, port, name, port, output);
}
return MNL_CB_OK;
}
static int stat_one_qp_link_get_mode(struct rd *rd)
{
uint32_t seq;
int ret;
if (!rd->port_idx)
return 0;
rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET,
&seq, (NLM_F_REQUEST | NLM_F_ACK));
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
/* Make RDMA_NLDEV_ATTR_STAT_MODE valid so that kernel knows
* return only mode instead of all counters
*/
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
RDMA_COUNTER_MODE_MANUAL);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
ret = rd_send_msg(rd);
if (ret)
return ret;
if (rd->json_output)
jsonw_start_object(rd->jw);
ret = rd_recv_msg(rd, qp_link_get_mode_parse_cb, rd, seq);
if (rd->json_output)
jsonw_end_object(rd->jw);
return ret;
}
static int stat_qp_link_get_mode(struct rd *rd)
{
return rd_exec_link(rd, stat_one_qp_link_get_mode, false);
}
static int stat_qp_get_mode(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_qp_link_get_mode },
{ "link", stat_qp_link_get_mode },
{ "help", stat_help },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int res_get_hwcounters(struct rd *rd, struct nlattr *hwc_table, bool print)
{
struct nlattr *nla_entry;
const char *nm;
uint64_t v;
int err;
mnl_attr_for_each_nested(nla_entry, hwc_table) {
struct nlattr *hw_line[RDMA_NLDEV_ATTR_MAX] = {};
err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, hw_line);
if (err != MNL_CB_OK)
return -EINVAL;
if (!hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] ||
!hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]) {
return -EINVAL;
}
if (!print)
continue;
nm = mnl_attr_get_str(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
v = mnl_attr_get_u64(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]);
if (rd->pretty_output && !rd->json_output)
newline_indent(rd);
res_print_uint(rd, nm, v, hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
}
return MNL_CB_OK;
}
static int res_counter_line(struct rd *rd, const char *name, int index,
struct nlattr **nla_line)
{
uint32_t cntn, port = 0, pid = 0, qpn;
struct nlattr *hwc_table, *qp_table;
struct nlattr *nla_entry;
const char *comm = NULL;
bool isfirst;
int err;
if (nla_line[RDMA_NLDEV_ATTR_PORT_INDEX])
port = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_PORT_INDEX]);
hwc_table = nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS];
qp_table = nla_line[RDMA_NLDEV_ATTR_RES_QP];
if (!hwc_table || !qp_table ||
!nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
return MNL_CB_ERROR;
cntn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
if (rd_is_filtered_attr(rd, "cntn", cntn,
nla_line[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]))
return MNL_CB_OK;
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_is_filtered_attr(rd, "pid", pid,
nla_line[RDMA_NLDEV_ATTR_RES_PID]))
return MNL_CB_OK;
if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
comm = (char *)mnl_attr_get_str(
nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
mnl_attr_for_each_nested(nla_entry, qp_table) {
struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
if (err != MNL_CB_OK)
return -EINVAL;
if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
return -EINVAL;
qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
if (rd_is_filtered_attr(rd, "lqpn", qpn,
qp_line[RDMA_NLDEV_ATTR_RES_LQPN]))
return MNL_CB_OK;
}
err = res_get_hwcounters(rd, hwc_table, false);
if (err != MNL_CB_OK)
return err;
if (rd->json_output) {
jsonw_string_field(rd->jw, "ifname", name);
if (port)
jsonw_uint_field(rd->jw, "port", port);
jsonw_uint_field(rd->jw, "cntn", cntn);
} else {
if (port)
pr_out("link %s/%u cntn %u ", name, port, cntn);
else
pr_out("dev %s cntn %u ", name, cntn);
}
res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
print_comm(rd, comm, nla_line);
res_get_hwcounters(rd, hwc_table, true);
isfirst = true;
mnl_attr_for_each_nested(nla_entry, qp_table) {
struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
if (isfirst && !rd->json_output)
pr_out("\n LQPN: <");
err = mnl_attr_parse_nested(nla_entry, rd_attr_cb, qp_line);
if (err != MNL_CB_OK)
return -EINVAL;
if (!qp_line[RDMA_NLDEV_ATTR_RES_LQPN])
return -EINVAL;
qpn = mnl_attr_get_u32(qp_line[RDMA_NLDEV_ATTR_RES_LQPN]);
if (rd->json_output) {
jsonw_uint_field(rd->jw, "lqpn", qpn);
} else {
if (isfirst)
pr_out("%d", qpn);
else
pr_out(", %d", qpn);
}
isfirst = false;
}
if (!rd->json_output)
pr_out(">\n");
return MNL_CB_OK;
}
static int stat_qp_show_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;
int ret;
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_STAT_COUNTER])
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_STAT_COUNTER];
mnl_attr_for_each_nested(nla_entry, nla_table) {
struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
ret = mnl_attr_parse_nested(nla_entry, rd_attr_cb, nla_line);
if (ret != MNL_CB_OK)
break;
ret = res_counter_line(rd, name, idx, nla_line);
if (ret != MNL_CB_OK)
break;
}
return ret;
}
static const struct filters stat_valid_filters[MAX_NUMBER_OF_FILTERS] = {
{ .name = "cntn", .is_number = true },
{ .name = "lqpn", .is_number = true },
{ .name = "pid", .is_number = true },
};
static int stat_qp_show_one_link(struct rd *rd)
{
int flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
uint32_t seq;
int ret;
if (!rd->port_idx)
return 0;
ret = rd_build_filter(rd, stat_valid_filters);
if (ret)
return ret;
rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
ret = rd_send_msg(rd);
if (ret)
return ret;
if (rd->json_output)
jsonw_start_object(rd->jw);
ret = rd_recv_msg(rd, stat_qp_show_parse_cb, rd, seq);
if (rd->json_output)
jsonw_end_object(rd->jw);
return ret;
}
static int stat_qp_show_link(struct rd *rd)
{
return rd_exec_link(rd, stat_qp_show_one_link, false);
}
static int stat_qp_show(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_qp_show_link },
{ "link", stat_qp_show_link },
{ "help", stat_help },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask)
{
uint32_t seq;
rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
&seq, (NLM_F_REQUEST | NLM_F_ACK));
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
RDMA_COUNTER_MODE_AUTO);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask);
return rd_sendrecv_msg(rd, seq);
}
static int stat_one_qp_set_link_auto_off(struct rd *rd)
{
return stat_qp_set_link_auto_sendmsg(rd, 0);
}
static int stat_one_qp_set_auto_type_on(struct rd *rd)
{
return stat_qp_set_link_auto_sendmsg(rd, RDMA_COUNTER_MASK_QP_TYPE);
}
static int stat_one_qp_set_link_auto_type(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_help },
{ "on", stat_one_qp_set_auto_type_on },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int stat_one_qp_set_link_auto(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_one_qp_link_get_mode },
{ "off", stat_one_qp_set_link_auto_off },
{ "type", stat_one_qp_set_link_auto_type },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int stat_one_qp_set_link(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_one_qp_link_get_mode },
{ "auto", stat_one_qp_set_link_auto },
{ 0 }
};
if (!rd->port_idx)
return 0;
return rd_exec_cmd(rd, cmds, "parameter");
}
static int stat_qp_set_link(struct rd *rd)
{
return rd_exec_link(rd, stat_one_qp_set_link, false);
}
static int stat_qp_set(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_help },
{ "link", stat_qp_set_link },
{ "help", stat_help },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int stat_get_arg(struct rd *rd, const char *arg)
{
int value = 0;
char *endp;
if (strcmpx(rd_argv(rd), arg) != 0)
return -EINVAL;
rd_arg_inc(rd);
value = strtol(rd_argv(rd), &endp, 10);
rd_arg_inc(rd);
return value;
}
static int stat_one_qp_bind(struct rd *rd)
{
int lqpn = 0, cntn = 0, ret;
uint32_t seq;
if (rd_no_arg(rd)) {
stat_help(rd);
return -EINVAL;
}
ret = rd_build_filter(rd, stat_valid_filters);
if (ret)
return ret;
lqpn = stat_get_arg(rd, "lqpn");
rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
&seq, (NLM_F_REQUEST | NLM_F_ACK));
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
RDMA_COUNTER_MODE_MANUAL);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
if (rd_argc(rd)) {
cntn = stat_get_arg(rd, "cntn");
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID,
cntn);
}
return rd_sendrecv_msg(rd, seq);
}
static int do_stat_qp_unbind_lqpn(struct rd *rd, uint32_t cntn, uint32_t lqpn)
{
uint32_t seq;
rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_DEL,
&seq, (NLM_F_REQUEST | NLM_F_ACK));
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
RDMA_COUNTER_MODE_MANUAL);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_RES_LQPN, lqpn);
return rd_sendrecv_msg(rd, seq);
}
static int stat_get_counter_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;
uint32_t lqpn, cntn;
int err;
mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
if (!tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID])
return MNL_CB_ERROR;
cntn = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_COUNTER_ID]);
nla_table = tb[RDMA_NLDEV_ATTR_RES_QP];
if (!nla_table)
return MNL_CB_ERROR;
mnl_attr_for_each_nested(nla_entry, nla_table) {
struct nlattr *nla_line[RDMA_NLDEV_ATTR_MAX] = {};
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_LQPN])
return -EINVAL;
lqpn = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
err = do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
if (err)
return MNL_CB_ERROR;
}
return MNL_CB_OK;
}
static int stat_one_qp_unbind(struct rd *rd)
{
int flags = NLM_F_REQUEST | NLM_F_ACK, ret;
char buf[MNL_SOCKET_BUFFER_SIZE];
int lqpn = 0, cntn = 0;
unsigned int portid;
uint32_t seq;
ret = rd_build_filter(rd, stat_valid_filters);
if (ret)
return ret;
cntn = stat_get_arg(rd, "cntn");
if (rd_argc(rd)) {
lqpn = stat_get_arg(rd, "lqpn");
return do_stat_qp_unbind_lqpn(rd, cntn, lqpn);
}
rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_RES, RDMA_NLDEV_ATTR_RES_QP);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_COUNTER_ID, cntn);
ret = rd_send_msg(rd);
if (ret)
return ret;
/* Can't use rd_recv_msg() since the callback also calls it (recursively),
* then rd_recv_msg() always return -1 here
*/
portid = mnl_socket_get_portid(rd->nl);
ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
if (ret <= 0)
return ret;
ret = mnl_cb_run(buf, ret, seq, portid, stat_get_counter_parse_cb, rd);
mnl_socket_close(rd->nl);
if (ret != MNL_CB_OK)
return ret;
return 0;
}
static int stat_qp_bind_link(struct rd *rd)
{
return rd_exec_link(rd, stat_one_qp_bind, true);
}
static int stat_qp_bind(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_help },
{ "link", stat_qp_bind_link },
{ "help", stat_help },
{ 0 },
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int stat_qp_unbind_link(struct rd *rd)
{
return rd_exec_link(rd, stat_one_qp_unbind, true);
}
static int stat_qp_unbind(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_help },
{ "link", stat_qp_unbind_link },
{ "help", stat_help },
{ 0 },
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int stat_qp(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_qp_show },
{ "show", stat_qp_show },
{ "list", stat_qp_show },
{ "mode", stat_qp_get_mode },
{ "set", stat_qp_set },
{ "bind", stat_qp_bind },
{ "unbind", stat_qp_unbind },
{ "help", stat_help },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
struct rd *rd = data;
const char *name;
uint32_t port;
int ret;
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_PORT_INDEX] ||
!tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
return MNL_CB_ERROR;
name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
if (rd->json_output) {
jsonw_string_field(rd->jw, "ifname", name);
jsonw_uint_field(rd->jw, "port", port);
} else {
pr_out("link %s/%u ", name, port);
}
ret = res_get_hwcounters(rd, tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS], true);
if (!rd->json_output)
pr_out("\n");
return ret;
}
static int stat_show_one_link(struct rd *rd)
{
int flags = NLM_F_REQUEST | NLM_F_ACK;
uint32_t seq;
int ret;
if (!rd->port_idx)
return 0;
rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET, &seq, flags);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
ret = rd_send_msg(rd);
if (ret)
return ret;
return rd_recv_msg(rd, stat_show_parse_cb, rd, seq);
}
static int stat_show_link(struct rd *rd)
{
return rd_exec_link(rd, stat_show_one_link, false);
}
static int stat_show(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_show_link },
{ "link", stat_show_link },
{ "help", stat_help },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
int cmd_stat(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, stat_show },
{ "show", stat_show },
{ "list", stat_show },
{ "help", stat_help },
{ "qp", stat_qp },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "statistic command");
}

View File

@ -56,7 +56,7 @@ bool rd_no_arg(struct rd *rd)
* mlx5_1/1 | 1 | false
* mlx5_1/- | 0 | false
*
* In strict mode, /- will return error.
* In strict port mode, a non-0 port must be provided
*/
static int get_port_from_argv(struct rd *rd, uint32_t *port,
bool *is_dump_all, bool strict_port)
@ -64,7 +64,7 @@ static int get_port_from_argv(struct rd *rd, uint32_t *port,
char *slash;
*port = 0;
*is_dump_all = true;
*is_dump_all = strict_port ? false : true;
slash = strchr(rd_argv(rd), '/');
/* if no port found, return 0 */
@ -83,6 +83,9 @@ static int get_port_from_argv(struct rd *rd, uint32_t *port,
if (!*port && strlen(slash))
return -EINVAL;
}
if (strict_port && (*port == 0))
return -EINVAL;
return 0;
}
@ -436,6 +439,17 @@ static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = {
[RDMA_NLDEV_ATTR_DRIVER_S64] = MNL_TYPE_U64,
[RDMA_NLDEV_ATTR_DRIVER_U64] = MNL_TYPE_U64,
[RDMA_NLDEV_SYS_ATTR_NETNS_MODE] = MNL_TYPE_U8,
[RDMA_NLDEV_ATTR_STAT_COUNTER] = MNL_TYPE_NESTED,
[RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY] = MNL_TYPE_NESTED,
[RDMA_NLDEV_ATTR_STAT_COUNTER_ID] = MNL_TYPE_U32,
[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS] = MNL_TYPE_NESTED,
[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY] = MNL_TYPE_NESTED,
[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] = MNL_TYPE_NUL_STRING,
[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE] = MNL_TYPE_U64,
[RDMA_NLDEV_ATTR_STAT_MODE] = MNL_TYPE_U32,
[RDMA_NLDEV_ATTR_STAT_RES] = MNL_TYPE_U32,
[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK] = MNL_TYPE_U32,
[RDMA_NLDEV_ATTR_DEV_DIM] = MNL_TYPE_U8,
};
int rd_attr_check(const struct nlattr *attr, int *typep)
@ -776,6 +790,11 @@ static int print_driver_string(struct rd *rd, const char *key_str,
}
}
void print_on_off(struct rd *rd, const char *key_str, bool on)
{
print_driver_string(rd, key_str, (on) ? "on":"off");
}
static int print_driver_s32(struct rd *rd, const char *key_str, int32_t val,
enum rdma_nldev_print_type print_type)
{

View File

@ -557,61 +557,40 @@ bad_val:
return ret;
}
struct tc_action_req {
struct nlmsghdr n;
struct tcamsg t;
char buf[MAX_MSG];
};
static int tc_action_modify(int cmd, unsigned int flags,
int *argc_p, char ***argv_p,
void *buf, size_t buflen)
int *argc_p, char ***argv_p)
{
struct tc_action_req *req, action_req;
char **argv = *argv_p;
struct rtattr *tail;
int argc = *argc_p;
struct iovec iov;
char **argv = *argv_p;
int ret = 0;
if (buf) {
req = buf;
if (buflen < sizeof (struct tc_action_req)) {
fprintf(stderr, "buffer is too small: %zu\n", buflen);
return -1;
}
} else {
memset(&action_req, 0, sizeof (struct tc_action_req));
req = &action_req;
}
req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
req->n.nlmsg_flags = NLM_F_REQUEST | flags;
req->n.nlmsg_type = cmd;
req->t.tca_family = AF_UNSPEC;
tail = NLMSG_TAIL(&req->n);
struct {
struct nlmsghdr n;
struct tcamsg t;
char buf[MAX_MSG];
} req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
.n.nlmsg_flags = NLM_F_REQUEST | flags,
.n.nlmsg_type = cmd,
.t.tca_family = AF_UNSPEC,
};
struct rtattr *tail = NLMSG_TAIL(&req.n);
argc -= 1;
argv += 1;
if (parse_action(&argc, &argv, TCA_ACT_TAB, &req->n)) {
if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
fprintf(stderr, "Illegal \"action\"\n");
return -1;
}
tail->rta_len = (void *) NLMSG_TAIL(&req->n) - (void *) tail;
tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
*argc_p = argc;
*argv_p = argv;
if (buf)
return 0;
iov.iov_base = &req->n;
iov.iov_len = req->n.nlmsg_len;
if (rtnl_talk_iov(&rth, &iov, 1, NULL) < 0) {
if (rtnl_talk(&rth, &req.n, NULL) < 0) {
fprintf(stderr, "We have an error talking to the kernel\n");
ret = -1;
}
*argc_p = argc;
*argv_p = argv;
return ret;
}
@ -712,7 +691,7 @@ bad_val:
return ret;
}
int do_action(int argc, char **argv, void *buf, size_t buflen)
int do_action(int argc, char **argv)
{
int ret = 0;
@ -722,12 +701,12 @@ int do_action(int argc, char **argv, void *buf, size_t buflen)
if (matches(*argv, "add") == 0) {
ret = tc_action_modify(RTM_NEWACTION,
NLM_F_EXCL | NLM_F_CREATE,
&argc, &argv, buf, buflen);
&argc, &argv);
} else if (matches(*argv, "change") == 0 ||
matches(*argv, "replace") == 0) {
ret = tc_action_modify(RTM_NEWACTION,
NLM_F_CREATE | NLM_F_REPLACE,
&argc, &argv, buf, buflen);
&argc, &argv);
} else if (matches(*argv, "delete") == 0) {
argc -= 1;
argv += 1;

View File

@ -167,7 +167,7 @@ static int bpf_print_opt(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_ACT_BPF_MAX, arg);
if (!tb[TCA_ACT_BPF_PARMS]) {
fprintf(f, "[NULL bpf parameters]");
fprintf(stderr, "Missing bpf parameters\n");
return -1;
}

View File

@ -115,7 +115,7 @@ static int print_connmark(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_CONNMARK_MAX, arg);
if (tb[TCA_CONNMARK_PARMS] == NULL) {
print_string(PRINT_FP, NULL, "%s", "[NULL connmark parameters]");
fprintf(stderr, "Missing connmark parameters\n");
return -1;
}

View File

@ -172,7 +172,7 @@ print_csum(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_CSUM_MAX, arg);
if (tb[TCA_CSUM_PARMS] == NULL) {
fprintf(f, "[NULL csum parameters]");
fprintf(stderr, "Missing csum parameters\n");
return -1;
}
sel = RTA_DATA(tb[TCA_CSUM_PARMS]);

View File

@ -177,7 +177,7 @@ print_gact(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_GACT_MAX, arg);
if (tb[TCA_GACT_PARMS] == NULL) {
print_string(PRINT_FP, NULL, "%s", "[NULL gact parameters]");
fprintf(stderr, "Missing gact parameters\n");
return -1;
}
p = RTA_DATA(tb[TCA_GACT_PARMS]);

View File

@ -218,7 +218,7 @@ skip_encode:
static int print_ife(struct action_util *au, FILE *f, struct rtattr *arg)
{
struct tc_ife *p = NULL;
struct tc_ife *p;
struct rtattr *tb[TCA_IFE_MAX + 1];
__u16 ife_type = 0;
__u32 mmark = 0;
@ -233,7 +233,7 @@ static int print_ife(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_IFE_MAX, arg);
if (tb[TCA_IFE_PARMS] == NULL) {
print_string(PRINT_FP, NULL, "%s", "[NULL ife parameters]");
fprintf(stderr, "Missing ife parameters\n");
return -1;
}
p = RTA_DATA(tb[TCA_IFE_PARMS]);

View File

@ -429,6 +429,8 @@ print_ipt(struct action_util *au, FILE * f, struct rtattr *arg)
{
struct rtattr *tb[TCA_IPT_MAX + 1];
struct ipt_entry_target *t = NULL;
struct xtables_target *m;
__u32 hook;
if (arg == NULL)
return -1;
@ -440,70 +442,68 @@ print_ipt(struct action_util *au, FILE * f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
if (tb[TCA_IPT_TABLE] == NULL) {
fprintf(f, "[NULL ipt table name ] assuming mangle ");
fprintf(stderr, "Missing ipt table name, assuming mangle\n");
} else {
fprintf(f, "tablename: %s ",
rta_getattr_str(tb[TCA_IPT_TABLE]));
}
if (tb[TCA_IPT_HOOK] == NULL) {
fprintf(f, "[NULL ipt hook name ]\n ");
fprintf(stderr, "Missing ipt hook name\n ");
return -1;
} else {
__u32 hook;
hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
fprintf(f, " hook: %s\n", ipthooks[hook]);
}
hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
fprintf(f, " hook: %s\n", ipthooks[hook]);
if (tb[TCA_IPT_TARG] == NULL) {
fprintf(f, "\t[NULL ipt target parameters ]\n");
fprintf(stderr, "Missing ipt target parameters\n");
return -1;
} else {
struct xtables_target *m = NULL;
}
t = RTA_DATA(tb[TCA_IPT_TARG]);
m = get_target_name(t->u.user.name);
if (m != NULL) {
if (build_st(m, t) < 0) {
fprintf(stderr, " %s error\n", m->name);
return -1;
}
opts =
merge_options(opts, m->extra_opts,
&m->option_offset);
} else {
fprintf(stderr, " failed to find target %s\n\n",
t->u.user.name);
t = RTA_DATA(tb[TCA_IPT_TARG]);
m = get_target_name(t->u.user.name);
if (m != NULL) {
if (build_st(m, t) < 0) {
fprintf(stderr, " %s error\n", m->name);
return -1;
}
fprintf(f, "\ttarget ");
m->print(NULL, m->t, 0);
if (tb[TCA_IPT_INDEX] == NULL) {
fprintf(f, " [NULL ipt target index ]\n");
} else {
__u32 index;
index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
fprintf(f, "\n\tindex %u", index);
}
if (tb[TCA_IPT_CNT]) {
struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);
fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
}
if (show_stats) {
if (tb[TCA_IPT_TM]) {
struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
print_tm(f, tm);
}
}
fprintf(f, "\n");
opts =
merge_options(opts, m->extra_opts,
&m->option_offset);
} else {
fprintf(stderr, " failed to find target %s\n\n",
t->u.user.name);
return -1;
}
fprintf(f, "\ttarget ");
m->print(NULL, m->t, 0);
if (tb[TCA_IPT_INDEX] == NULL) {
fprintf(stderr, "Missing ipt target index\n");
} else {
__u32 index;
index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
fprintf(f, "\n\tindex %u", index);
}
if (tb[TCA_IPT_CNT]) {
struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);
fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
}
if (show_stats) {
if (tb[TCA_IPT_TM]) {
struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
print_tm(f, tm);
}
}
fprintf(f, "\n");
free_opts(opts);
return 0;

View File

@ -287,7 +287,7 @@ print_mirred(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_MIRRED_MAX, arg);
if (tb[TCA_MIRRED_PARMS] == NULL) {
print_string(PRINT_FP, NULL, "%s", "[NULL mirred parameters]");
fprintf(stderr, "Missing mirred parameters\n");
return -1;
}
p = RTA_DATA(tb[TCA_MIRRED_PARMS]);

View File

@ -152,7 +152,7 @@ print_nat(struct action_util *au, FILE * f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_NAT_MAX, arg);
if (tb[TCA_NAT_PARMS] == NULL) {
print_string(PRINT_FP, NULL, "%s", "[NULL nat parameters]");
fprintf(stderr, "Missing nat parameters\n");
return -1;
}
sel = RTA_DATA(tb[TCA_NAT_PARMS]);

View File

@ -742,7 +742,7 @@ static int print_pedit(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_PEDIT_MAX, arg);
if (!tb[TCA_PEDIT_PARMS] && !tb[TCA_PEDIT_PARMS_EX]) {
fprintf(f, "[NULL pedit parameters]");
fprintf(stderr, "Missing pedit parameters\n");
return -1;
}

View File

@ -150,7 +150,7 @@ static int print_sample(struct action_util *au, FILE *f, struct rtattr *arg)
if (!tb[TCA_SAMPLE_PARMS] || !tb[TCA_SAMPLE_RATE] ||
!tb[TCA_SAMPLE_PSAMPLE_GROUP]) {
print_string(PRINT_FP, NULL, "%s", "[NULL sample parameters]");
fprintf(stderr, "Missing sample parameters\n");
return -1;
}
p = RTA_DATA(tb[TCA_SAMPLE_PARMS]);

View File

@ -171,13 +171,13 @@ static int print_simple(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_DEF_MAX, arg);
if (tb[TCA_DEF_PARMS] == NULL) {
fprintf(f, "[NULL simple parameters]");
fprintf(stderr, "Missing simple parameters\n");
return -1;
}
sel = RTA_DATA(tb[TCA_DEF_PARMS]);
if (tb[TCA_DEF_DATA] == NULL) {
fprintf(f, "[missing simple string]");
fprintf(stderr, "Missing simple string\n");
return -1;
}

View File

@ -196,7 +196,7 @@ static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg)
SPRINT_BUF(b1);
__u32 priority;
__u16 ptype;
struct tc_skbedit *p = NULL;
struct tc_skbedit *p;
if (arg == NULL)
return -1;
@ -204,7 +204,7 @@ static int print_skbedit(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_SKBEDIT_MAX, arg);
if (tb[TCA_SKBEDIT_PARMS] == NULL) {
print_string(PRINT_FP, NULL, "%s", "[NULL skbedit parameters]");
fprintf(stderr, "Missing skbedit parameters\n");
return -1;
}
p = RTA_DATA(tb[TCA_SKBEDIT_PARMS]);

View File

@ -161,7 +161,7 @@ static int parse_skbmod(struct action_util *a, int *argc_p, char ***argv_p,
static int print_skbmod(struct action_util *au, FILE *f, struct rtattr *arg)
{
struct tc_skbmod *p = NULL;
struct tc_skbmod *p;
struct rtattr *tb[TCA_SKBMOD_MAX + 1];
__u16 skbmod_etype = 0;
int has_optional = 0;
@ -174,7 +174,7 @@ static int print_skbmod(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_SKBMOD_MAX, arg);
if (tb[TCA_SKBMOD_PARMS] == NULL) {
fprintf(f, "[NULL skbmod parameters]");
fprintf(stderr, "Missing skbmod parameters\n");
return -1;
}

View File

@ -493,8 +493,7 @@ static int print_tunnel_key(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_TUNNEL_KEY_MAX, arg);
if (!tb[TCA_TUNNEL_KEY_PARMS]) {
print_string(PRINT_FP, NULL, "%s",
"[NULL tunnel_key parameters]");
fprintf(stderr, "Missing tunnel_key parameters\n");
return -1;
}
parm = RTA_DATA(tb[TCA_TUNNEL_KEY_PARMS]);

View File

@ -188,7 +188,7 @@ static int print_vlan(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_VLAN_MAX, arg);
if (!tb[TCA_VLAN_PARMS]) {
print_string(PRINT_FP, NULL, "%s", "[NULL vlan parameters]");
fprintf(stderr, "Missing vlanparameters\n");
return -1;
}
parm = RTA_DATA(tb[TCA_VLAN_PARMS]);

View File

@ -317,6 +317,7 @@ print_ipt(struct action_util *au, FILE *f, struct rtattr *arg)
struct xtables_target *m;
struct rtattr *tb[TCA_IPT_MAX + 1];
struct xt_entry_target *t = NULL;
__u32 hook;
if (arg == NULL)
return -1;
@ -330,27 +331,25 @@ print_ipt(struct action_util *au, FILE *f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
if (tb[TCA_IPT_TABLE] == NULL) {
fprintf(f, "[NULL ipt table name ] assuming mangle ");
fprintf(stderr, "Missing ipt table name, assuming mangle\n");
} else {
fprintf(f, "tablename: %s ",
rta_getattr_str(tb[TCA_IPT_TABLE]));
}
if (tb[TCA_IPT_HOOK] == NULL) {
fprintf(f, "[NULL ipt hook name ]\n ");
fprintf(stderr, "Missing ipt hook name\n ");
return -1;
} else {
__u32 hook;
hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
fprintf(f, " hook: %s\n", ipthooks[hook]);
}
if (tb[TCA_IPT_TARG] == NULL) {
fprintf(f, "\t[NULL ipt target parameters ]\n");
fprintf(stderr, "Missing ipt target parameters\n");
return -1;
}
hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
fprintf(f, " hook: %s\n", ipthooks[hook]);
t = RTA_DATA(tb[TCA_IPT_TARG]);
m = xtables_find_target(t->u.user.name, XTF_TRY_LOAD);
if (!m) {

View File

@ -354,6 +354,8 @@ print_ipt(struct action_util *au, FILE * f, struct rtattr *arg)
{
struct rtattr *tb[TCA_IPT_MAX + 1];
struct xt_entry_target *t = NULL;
struct xtables_target *m;
__u32 hook;
if (arg == NULL)
return -1;
@ -363,70 +365,66 @@ print_ipt(struct action_util *au, FILE * f, struct rtattr *arg)
parse_rtattr_nested(tb, TCA_IPT_MAX, arg);
if (tb[TCA_IPT_TABLE] == NULL) {
fprintf(f, "[NULL ipt table name ] assuming mangle ");
fprintf(stderr, "Missing ipt table name, assuming mangle\n");
} else {
fprintf(f, "tablename: %s ",
rta_getattr_str(tb[TCA_IPT_TABLE]));
}
if (tb[TCA_IPT_HOOK] == NULL) {
fprintf(f, "[NULL ipt hook name ]\n ");
fprintf(stderr, "Missing ipt hook name\n");
return -1;
} else {
__u32 hook;
hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
fprintf(f, " hook: %s\n", ipthooks[hook]);
}
if (tb[TCA_IPT_TARG] == NULL) {
fprintf(f, "\t[NULL ipt target parameters ]\n");
fprintf(stderr, "Missing ipt target parameters\n");
return -1;
} else {
struct xtables_target *m = NULL;
}
t = RTA_DATA(tb[TCA_IPT_TARG]);
m = find_target(t->u.user.name, TRY_LOAD);
if (m != NULL) {
if (build_st(m, t) < 0) {
fprintf(stderr, " %s error\n", m->name);
return -1;
}
hook = rta_getattr_u32(tb[TCA_IPT_HOOK]);
fprintf(f, " hook: %s\n", ipthooks[hook]);
opts =
merge_options(opts, m->extra_opts,
&m->option_offset);
} else {
fprintf(stderr, " failed to find target %s\n\n",
t->u.user.name);
t = RTA_DATA(tb[TCA_IPT_TARG]);
m = find_target(t->u.user.name, TRY_LOAD);
if (m != NULL) {
if (build_st(m, t) < 0) {
fprintf(stderr, " %s error\n", m->name);
return -1;
}
fprintf(f, "\ttarget ");
m->print(NULL, m->t, 0);
if (tb[TCA_IPT_INDEX] == NULL) {
fprintf(f, " [NULL ipt target index ]\n");
} else {
__u32 index;
index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
fprintf(f, "\n\tindex %u", index);
}
if (tb[TCA_IPT_CNT]) {
struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);
fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
}
if (show_stats) {
if (tb[TCA_IPT_TM]) {
struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
print_tm(f, tm);
}
}
fprintf(f, "\n");
opts =
merge_options(opts, m->extra_opts,
&m->option_offset);
} else {
fprintf(stderr, " failed to find target %s\n\n",
t->u.user.name);
return -1;
}
fprintf(f, "\ttarget ");
m->print(NULL, m->t, 0);
if (tb[TCA_IPT_INDEX] == NULL) {
fprintf(f, " [NULL ipt target index ]\n");
} else {
__u32 index;
index = rta_getattr_u32(tb[TCA_IPT_INDEX]);
fprintf(f, "\n\tindex %u", index);
}
if (tb[TCA_IPT_CNT]) {
struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);
fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt);
}
if (show_stats) {
if (tb[TCA_IPT_TM]) {
struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]);
print_tm(f, tm);
}
}
fprintf(f, "\n");
free_opts(opts);
return 0;

202
tc/tc.c
View File

@ -205,18 +205,18 @@ static void usage(void)
" -nm | -nam[es] | { -cf | -conf } path }\n");
}
static int do_cmd(int argc, char **argv, void *buf, size_t buflen)
static int do_cmd(int argc, char **argv)
{
if (matches(*argv, "qdisc") == 0)
return do_qdisc(argc-1, argv+1);
if (matches(*argv, "class") == 0)
return do_class(argc-1, argv+1);
if (matches(*argv, "filter") == 0)
return do_filter(argc-1, argv+1, buf, buflen);
return do_filter(argc-1, argv+1);
if (matches(*argv, "chain") == 0)
return do_chain(argc-1, argv+1, buf, buflen);
return do_chain(argc-1, argv+1);
if (matches(*argv, "actions") == 0)
return do_action(argc-1, argv+1, buf, buflen);
return do_action(argc-1, argv+1);
if (matches(*argv, "monitor") == 0)
return do_tcmonitor(argc-1, argv+1);
if (matches(*argv, "exec") == 0)
@ -231,111 +231,11 @@ static int do_cmd(int argc, char **argv, void *buf, size_t buflen)
return -1;
}
#define TC_MAX_SUBC 10
static bool batchsize_enabled(int argc, char *argv[])
{
struct {
char *c;
char *subc[TC_MAX_SUBC];
} table[] = {
{ "filter", { "add", "delete", "change", "replace", NULL} },
{ "actions", { "add", "change", "replace", NULL} },
{ NULL },
}, *iter;
char *s;
int i;
if (argc < 2)
return false;
for (iter = table; iter->c; iter++) {
if (matches(argv[0], iter->c))
continue;
for (i = 0; i < TC_MAX_SUBC; i++) {
s = iter->subc[i];
if (s && matches(argv[1], s) == 0)
return true;
}
}
return false;
}
struct batch_buf {
struct batch_buf *next;
char buf[16420]; /* sizeof (struct nlmsghdr) +
max(sizeof (struct tcmsg) +
sizeof (struct tcamsg)) +
MAX_MSG */
};
static struct batch_buf *get_batch_buf(struct batch_buf **pool,
struct batch_buf **head,
struct batch_buf **tail)
{
struct batch_buf *buf;
if (*pool == NULL)
buf = calloc(1, sizeof(struct batch_buf));
else {
buf = *pool;
*pool = (*pool)->next;
memset(buf, 0, sizeof(struct batch_buf));
}
if (*head == NULL)
*head = *tail = buf;
else {
(*tail)->next = buf;
(*tail) = buf;
}
return buf;
}
static void put_batch_bufs(struct batch_buf **pool,
struct batch_buf **head,
struct batch_buf **tail)
{
if (*head == NULL || *tail == NULL)
return;
if (*pool == NULL)
*pool = *head;
else {
(*tail)->next = *pool;
*pool = *head;
}
*head = NULL;
*tail = NULL;
}
static void free_batch_bufs(struct batch_buf **pool)
{
struct batch_buf *buf;
for (buf = *pool; buf != NULL; buf = *pool) {
*pool = buf->next;
free(buf);
}
*pool = NULL;
}
static int batch(const char *name)
{
struct batch_buf *head = NULL, *tail = NULL, *buf_pool = NULL;
char *largv[100], *largv_next[100];
char *line, *line_next = NULL;
bool bs_enabled = false;
bool lastline = false;
int largc, largc_next;
bool bs_enabled_saved;
bool bs_enabled_next;
int batchsize = 0;
char *line = NULL;
size_t len = 0;
int ret = 0;
int err;
bool send;
batch_mode = 1;
if (name && strcmp(name, "-") != 0) {
@ -355,96 +255,26 @@ static int batch(const char *name)
}
cmdlineno = 0;
if (getcmdline(&line, &len, stdin) == -1)
goto Exit;
largc = makeargs(line, largv, 100);
bs_enabled = batchsize_enabled(largc, largv);
do {
if (getcmdline(&line_next, &len, stdin) == -1)
lastline = true;
while (getcmdline(&line, &len, stdin) != -1) {
char *largv[100];
int largc;
largc_next = makeargs(line_next, largv_next, 100);
bs_enabled_next = batchsize_enabled(largc_next, largv_next);
if (bs_enabled) {
struct batch_buf *buf;
buf = get_batch_buf(&buf_pool, &head, &tail);
if (!buf) {
fprintf(stderr,
"failed to allocate batch_buf\n");
return -1;
}
++batchsize;
}
/*
* In batch mode, if we haven't accumulated enough commands
* and this is not the last command and this command & next
* command both support the batchsize feature, don't send the
* message immediately.
*/
if (!lastline && bs_enabled && bs_enabled_next
&& batchsize != MSG_IOV_MAX)
send = false;
else
send = true;
line = line_next;
line_next = NULL;
len = 0;
bs_enabled_saved = bs_enabled;
bs_enabled = bs_enabled_next;
if (largc == 0) {
largc = largc_next;
memcpy(largv, largv_next, largc * sizeof(char *));
largc = makeargs(line, largv, 100);
if (largc == 0)
continue; /* blank line */
}
err = do_cmd(largc, largv, tail == NULL ? NULL : tail->buf,
tail == NULL ? 0 : sizeof(tail->buf));
fflush(stdout);
if (err != 0) {
fprintf(stderr, "Command failed %s:%d\n", name,
cmdlineno - 1);
if (do_cmd(largc, largv)) {
fprintf(stderr, "Command failed %s:%d\n",
name, cmdlineno);
ret = 1;
if (!force)
break;
}
largc = largc_next;
memcpy(largv, largv_next, largc * sizeof(char *));
fflush(stdout);
}
if (send && bs_enabled_saved) {
struct iovec *iov, *iovs;
struct batch_buf *buf;
struct nlmsghdr *n;
iov = iovs = malloc(batchsize * sizeof(struct iovec));
for (buf = head; buf != NULL; buf = buf->next, ++iov) {
n = (struct nlmsghdr *)&buf->buf;
iov->iov_base = n;
iov->iov_len = n->nlmsg_len;
}
err = rtnl_talk_iov(&rth, iovs, batchsize, NULL);
put_batch_bufs(&buf_pool, &head, &tail);
free(iovs);
if (err < 0) {
fprintf(stderr, "Command failed %s:%d\n", name,
cmdlineno - (batchsize + err) - 1);
ret = 1;
if (!force)
break;
}
batchsize = 0;
}
} while (!lastline);
free_batch_bufs(&buf_pool);
Exit:
free(line);
rtnl_close(&rth);
return ret;
}
@ -538,7 +368,7 @@ int main(int argc, char **argv)
goto Exit;
}
ret = do_cmd(argc-1, argv+1, NULL, 0);
ret = do_cmd(argc-1, argv+1);
Exit:
rtnl_close(&rth);

View File

@ -1,15 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */
#define TCA_BUF_MAX (64*1024)
#define MSG_IOV_MAX 128
extern struct rtnl_handle rth;
int do_qdisc(int argc, char **argv);
int do_class(int argc, char **argv);
int do_filter(int argc, char **argv, void *buf, size_t buflen);
int do_chain(int argc, char **argv, void *buf, size_t buflen);
int do_action(int argc, char **argv, void *buf, size_t buflen);
int do_filter(int argc, char **argv);
int do_chain(int argc, char **argv);
int do_action(int argc, char **argv);
int do_tcmonitor(int argc, char **argv);
int do_exec(int argc, char **argv);

View File

@ -58,42 +58,30 @@ struct tc_filter_req {
char buf[MAX_MSG];
};
static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv,
void *buf, size_t buflen)
static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
{
struct tc_filter_req *req, filter_req;
struct {
struct nlmsghdr n;
struct tcmsg t;
char buf[MAX_MSG];
} req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
.n.nlmsg_flags = NLM_F_REQUEST | flags,
.n.nlmsg_type = cmd,
.t.tcm_family = AF_UNSPEC,
};
struct filter_util *q = NULL;
struct tc_estimator est = {};
char k[FILTER_NAMESZ] = {};
int chain_index_set = 0;
char d[IFNAMSIZ] = {};
int protocol_set = 0;
__u32 block_index = 0;
char *fhandle = NULL;
__u32 protocol = 0;
__u32 chain_index;
struct iovec iov;
__u32 prio = 0;
int ret;
__u32 protocol = 0;
int protocol_set = 0;
__u32 chain_index;
int chain_index_set = 0;
char *fhandle = NULL;
char d[IFNAMSIZ] = {};
char k[FILTER_NAMESZ] = {};
struct tc_estimator est = {};
if (buf) {
req = buf;
if (buflen < sizeof (struct tc_filter_req)) {
fprintf(stderr, "buffer is too small: %zu\n", buflen);
return -1;
}
} else {
memset(&filter_req, 0, sizeof (struct tc_filter_req));
req = &filter_req;
}
req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
req->n.nlmsg_flags = NLM_F_REQUEST | flags;
req->n.nlmsg_type = cmd;
req->t.tcm_family = AF_UNSPEC;
if ((cmd == RTM_NEWTFILTER || cmd == RTM_NEWCHAIN) &&
flags & NLM_F_CREATE)
if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE)
protocol = htons(ETH_P_ALL);
while (argc > 0) {
@ -101,53 +89,39 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv,
NEXT_ARG();
if (d[0])
duparg("dev", *argv);
if (block_index) {
fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exlusive\n");
return -1;
}
strncpy(d, *argv, sizeof(d)-1);
} else if (matches(*argv, "block") == 0) {
NEXT_ARG();
if (block_index)
duparg("block", *argv);
if (d[0]) {
fprintf(stderr, "Error: \"dev\" and \"block\" are mutually exlusive\n");
return -1;
}
if (get_u32(&block_index, *argv, 0) || !block_index)
invarg("invalid block index value", *argv);
} else if (strcmp(*argv, "root") == 0) {
if (req->t.tcm_parent) {
if (req.t.tcm_parent) {
fprintf(stderr,
"Error: \"root\" is duplicate parent ID\n");
return -1;
}
req->t.tcm_parent = TC_H_ROOT;
req.t.tcm_parent = TC_H_ROOT;
} else if (strcmp(*argv, "ingress") == 0) {
if (req->t.tcm_parent) {
if (req.t.tcm_parent) {
fprintf(stderr,
"Error: \"ingress\" is duplicate parent ID\n");
return -1;
}
req->t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
TC_H_MIN_INGRESS);
} else if (strcmp(*argv, "egress") == 0) {
if (req->t.tcm_parent) {
if (req.t.tcm_parent) {
fprintf(stderr,
"Error: \"egress\" is duplicate parent ID\n");
return -1;
}
req->t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
TC_H_MIN_EGRESS);
} else if (strcmp(*argv, "parent") == 0) {
__u32 handle;
NEXT_ARG();
if (req->t.tcm_parent)
if (req.t.tcm_parent)
duparg("parent", *argv);
if (get_tc_classid(&handle, *argv))
invarg("Invalid parent ID", *argv);
req->t.tcm_parent = handle;
req.t.tcm_parent = handle;
} else if (strcmp(*argv, "handle") == 0) {
NEXT_ARG();
if (fhandle)
@ -194,27 +168,26 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv,
argc--; argv++;
}
req->t.tcm_info = TC_H_MAKE(prio<<16, protocol);
req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
if (chain_index_set)
addattr32(&req->n, sizeof(*req), TCA_CHAIN, chain_index);
addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
if (k[0])
addattr_l(&req->n, sizeof(*req), TCA_KIND, k, strlen(k)+1);
addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
if (d[0]) {
ll_init_map(&rth);
req->t.tcm_ifindex = ll_name_to_index(d);
if (!req->t.tcm_ifindex)
return -nodev(d);
} else if (block_index) {
req->t.tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
req->t.tcm_block_index = block_index;
req.t.tcm_ifindex = ll_name_to_index(d);
if (req.t.tcm_ifindex == 0) {
fprintf(stderr, "Cannot find device \"%s\"\n", d);
return 1;
}
}
if (q) {
if (q->parse_fopt(q, fhandle, argc, argv, &req->n))
if (q->parse_fopt(q, fhandle, argc, argv, &req.n))
return 1;
} else {
if (fhandle) {
@ -233,16 +206,10 @@ static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv,
}
if (est.ewma_log)
addattr_l(&req->n, sizeof(*req), TCA_RATE, &est, sizeof(est));
addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
if (buf)
return 0;
iov.iov_base = &req->n;
iov.iov_len = req->n.nlmsg_len;
ret = rtnl_talk_iov(&rth, &iov, 1, NULL);
if (ret < 0) {
fprintf(stderr, "We have an error talking to the kernel, %d\n", ret);
if (rtnl_talk(&rth, &req.n, NULL) < 0) {
fprintf(stderr, "We have an error talking to the kernel\n");
return 2;
}
@ -375,8 +342,7 @@ int print_filter(struct nlmsghdr *n, void *arg)
if (q)
q->print_fopt(q, fp, tb[TCA_OPTIONS], t->tcm_handle);
else
print_string(PRINT_FP, NULL,
"[cannot parse parameters]", NULL);
fprintf(stderr, "cannot parse option parameters\n");
close_json_object();
}
}
@ -752,22 +718,20 @@ static int tc_filter_list(int cmd, int argc, char **argv)
return 0;
}
int do_filter(int argc, char **argv, void *buf, size_t buflen)
int do_filter(int argc, char **argv)
{
if (argc < 1)
return tc_filter_list(RTM_GETTFILTER, 0, NULL);
if (matches(*argv, "add") == 0)
return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE,
argc-1, argv+1, buf, buflen);
argc-1, argv+1);
if (matches(*argv, "change") == 0)
return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1,
buf, buflen);
return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1);
if (matches(*argv, "replace") == 0)
return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1,
argv+1, buf, buflen);
argv+1);
if (matches(*argv, "delete") == 0)
return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1,
buf, buflen);
return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1);
if (matches(*argv, "get") == 0)
return tc_filter_get(RTM_GETTFILTER, 0, argc-1, argv+1);
if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
@ -782,16 +746,16 @@ int do_filter(int argc, char **argv, void *buf, size_t buflen)
return -1;
}
int do_chain(int argc, char **argv, void *buf, size_t buflen)
int do_chain(int argc, char **argv)
{
if (argc < 1)
return tc_filter_list(RTM_GETCHAIN, 0, NULL);
if (matches(*argv, "add") == 0) {
return tc_filter_modify(RTM_NEWCHAIN, NLM_F_EXCL | NLM_F_CREATE,
argc - 1, argv + 1, buf, buflen);
argc - 1, argv + 1);
} else if (matches(*argv, "delete") == 0) {
return tc_filter_modify(RTM_DELCHAIN, 0,
argc - 1, argv + 1, buf, buflen);
argc - 1, argv + 1);
} else if (matches(*argv, "get") == 0) {
return tc_filter_get(RTM_GETCHAIN, 0,
argc - 1, argv + 1);

View File

@ -313,8 +313,7 @@ int print_qdisc(struct nlmsghdr *n, void *arg)
if (q)
q->print_qopt(q, fp, tb[TCA_OPTIONS]);
else
print_string(PRINT_FP, NULL,
"[cannot parse qdisc parameters]", NULL);
fprintf(stderr, "Cannot parse qdisc parameters\n");
}
close_json_object();

View File

@ -198,7 +198,7 @@ int parse_percent(double *val, const char *str)
char *p;
*val = strtod(str, &p) / 100.;
if (*val == HUGE_VALF || *val == HUGE_VALL)
if (*val > 1.0 || *val < 0.0)
return 1;
if (*p && strcmp(p, "%"))
return -1;
@ -226,16 +226,16 @@ static int parse_percent_rate(char *rate, size_t len,
if (ret != 1)
goto malf;
if (parse_percent(&perc, str_perc))
ret = parse_percent(&perc, str_perc);
if (ret == 1) {
fprintf(stderr, "Invalid rate specified; should be between [0,100]%% but is %s\n", str);
goto err;
} else if (ret == -1) {
goto malf;
}
free(str_perc);
if (perc > 1.0 || perc < 0.0) {
fprintf(stderr, "Invalid rate specified; should be between [0,100]%% but is %s\n", str);
return -1;
}
rate_bit = perc * dev_mbit * 1000 * 1000;
ret = snprintf(rate, len, "%lf", rate_bit);
@ -247,8 +247,9 @@ static int parse_percent_rate(char *rate, size_t len,
return 0;
malf:
free(str_perc);
fprintf(stderr, "Specified rate value could not be read or is malformed\n");
err:
free(str_perc);
return -1;
}