Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next
This commit is contained in:
commit
52c5f3f043
2
Makefile
2
Makefile
|
|
@ -55,7 +55,7 @@ WFLAGS += -Wmissing-declarations -Wold-style-definition -Wformat=2
|
|||
CFLAGS := $(WFLAGS) $(CCOPTS) -I../include -I../include/uapi $(DEFINES) $(CFLAGS)
|
||||
YACCFLAGS = -d -t -v
|
||||
|
||||
SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma dcb man
|
||||
SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma dcb man vdpa
|
||||
|
||||
LIBNETLINK=../lib/libutil.a ../lib/libnetlink.a
|
||||
LDLIBS += $(LIBNETLINK)
|
||||
|
|
|
|||
|
|
@ -319,6 +319,57 @@ _devlink_port_split()
|
|||
esac
|
||||
}
|
||||
|
||||
# Completion for devlink port param set
|
||||
_devlink_port_param_set()
|
||||
{
|
||||
case $cword in
|
||||
7)
|
||||
COMPREPLY=( $( compgen -W "value" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
8)
|
||||
# String argument
|
||||
return
|
||||
;;
|
||||
9)
|
||||
COMPREPLY=( $( compgen -W "cmode" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
10)
|
||||
COMPREPLY=( $( compgen -W "runtime driverinit permanent" -- \
|
||||
"$cur" ) )
|
||||
return
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Completion for devlink port param
|
||||
_devlink_port_param()
|
||||
{
|
||||
case "$cword" in
|
||||
3)
|
||||
COMPREPLY=( $( compgen -W "show set" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
4)
|
||||
_devlink_direct_complete "port"
|
||||
return
|
||||
;;
|
||||
5)
|
||||
COMPREPLY=( $( compgen -W "name" -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
6)
|
||||
_devlink_direct_complete "param_name"
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "${words[3]}" == "set" ]]; then
|
||||
_devlink_port_param_set
|
||||
fi
|
||||
}
|
||||
|
||||
# Completion for devlink port
|
||||
_devlink_port()
|
||||
{
|
||||
|
|
@ -331,6 +382,10 @@ _devlink_port()
|
|||
_devlink_port_split
|
||||
return
|
||||
;;
|
||||
param)
|
||||
_devlink_port_param
|
||||
return
|
||||
;;
|
||||
show|unsplit)
|
||||
if [[ $cword -eq 3 ]]; then
|
||||
_devlink_direct_complete "port"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,13 @@ TARGETS :=
|
|||
|
||||
ifeq ($(HAVE_MNL),y)
|
||||
|
||||
DCBOBJ = dcb.o dcb_buffer.o dcb_ets.o dcb_maxrate.o dcb_pfc.o
|
||||
DCBOBJ = dcb.o \
|
||||
dcb_app.o \
|
||||
dcb_buffer.o \
|
||||
dcb_dcbx.o \
|
||||
dcb_ets.o \
|
||||
dcb_maxrate.o \
|
||||
dcb_pfc.o
|
||||
TARGETS += dcb
|
||||
LDLIBS += -lm
|
||||
|
||||
|
|
|
|||
195
dcb/dcb.c
195
dcb/dcb.c
|
|
@ -59,25 +59,19 @@ static void dcb_free(struct dcb *dcb)
|
|||
struct dcb_get_attribute {
|
||||
struct dcb *dcb;
|
||||
int attr;
|
||||
void *data;
|
||||
size_t data_len;
|
||||
void *payload;
|
||||
__u16 payload_len;
|
||||
};
|
||||
|
||||
static int dcb_get_attribute_attr_ieee_cb(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct dcb_get_attribute *ga = data;
|
||||
uint16_t len;
|
||||
|
||||
if (mnl_attr_get_type(attr) != ga->attr)
|
||||
return MNL_CB_OK;
|
||||
|
||||
len = mnl_attr_get_payload_len(attr);
|
||||
if (len != ga->data_len) {
|
||||
fprintf(stderr, "Wrong len %d, expected %zd\n", len, ga->data_len);
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
|
||||
memcpy(ga->data, mnl_attr_get_payload(attr), ga->data_len);
|
||||
ga->payload = mnl_attr_get_payload(attr);
|
||||
ga->payload_len = mnl_attr_get_payload_len(attr);
|
||||
return MNL_CB_STOP;
|
||||
}
|
||||
|
||||
|
|
@ -94,12 +88,27 @@ static int dcb_get_attribute_cb(const struct nlmsghdr *nlh, void *data)
|
|||
return mnl_attr_parse(nlh, sizeof(struct dcbmsg), dcb_get_attribute_attr_cb, data);
|
||||
}
|
||||
|
||||
static int dcb_get_attribute_bare_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
/* Bare attributes (e.g. DCB_ATTR_DCBX) are not wrapped inside an IEEE
|
||||
* container, so this does not have to go through unpacking in
|
||||
* dcb_get_attribute_attr_cb().
|
||||
*/
|
||||
return mnl_attr_parse(nlh, sizeof(struct dcbmsg),
|
||||
dcb_get_attribute_attr_ieee_cb, data);
|
||||
}
|
||||
|
||||
struct dcb_set_attribute_response {
|
||||
int response_attr;
|
||||
};
|
||||
|
||||
static int dcb_set_attribute_attr_cb(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct dcb_set_attribute_response *resp = data;
|
||||
uint16_t len;
|
||||
uint8_t err;
|
||||
|
||||
if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE)
|
||||
if (mnl_attr_get_type(attr) != resp->response_attr)
|
||||
return MNL_CB_OK;
|
||||
|
||||
len = mnl_attr_get_payload_len(attr);
|
||||
|
|
@ -150,41 +159,90 @@ static struct nlmsghdr *dcb_prepare(struct dcb *dcb, const char *dev,
|
|||
return nlh;
|
||||
}
|
||||
|
||||
int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr, void *data, size_t data_len)
|
||||
static int __dcb_get_attribute(struct dcb *dcb, int command,
|
||||
const char *dev, int attr,
|
||||
void **payload_p, __u16 *payload_len_p,
|
||||
int (*get_attribute_cb)(const struct nlmsghdr *nlh,
|
||||
void *data))
|
||||
{
|
||||
struct dcb_get_attribute ga;
|
||||
struct nlmsghdr *nlh;
|
||||
int ret;
|
||||
|
||||
nlh = dcb_prepare(dcb, dev, RTM_GETDCB, DCB_CMD_IEEE_GET);
|
||||
nlh = dcb_prepare(dcb, dev, RTM_GETDCB, command);
|
||||
|
||||
ga = (struct dcb_get_attribute) {
|
||||
.dcb = dcb,
|
||||
.attr = attr,
|
||||
.data = data,
|
||||
.data_len = data_len,
|
||||
.payload = NULL,
|
||||
};
|
||||
ret = dcb_talk(dcb, nlh, dcb_get_attribute_cb, &ga);
|
||||
ret = dcb_talk(dcb, nlh, get_attribute_cb, &ga);
|
||||
if (ret) {
|
||||
perror("Attribute read");
|
||||
return ret;
|
||||
}
|
||||
if (ga.payload == NULL) {
|
||||
perror("Attribute not found");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
*payload_p = ga.payload;
|
||||
*payload_len_p = ga.payload_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *data, size_t data_len)
|
||||
int dcb_get_attribute_va(struct dcb *dcb, const char *dev, int attr,
|
||||
void **payload_p, __u16 *payload_len_p)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct nlattr *nest;
|
||||
return __dcb_get_attribute(dcb, DCB_CMD_IEEE_GET, dev, attr,
|
||||
payload_p, payload_len_p,
|
||||
dcb_get_attribute_cb);
|
||||
}
|
||||
|
||||
int dcb_get_attribute_bare(struct dcb *dcb, int cmd, const char *dev, int attr,
|
||||
void **payload_p, __u16 *payload_len_p)
|
||||
{
|
||||
return __dcb_get_attribute(dcb, cmd, dev, attr,
|
||||
payload_p, payload_len_p,
|
||||
dcb_get_attribute_bare_cb);
|
||||
}
|
||||
|
||||
int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr, void *data, size_t data_len)
|
||||
{
|
||||
__u16 payload_len;
|
||||
void *payload;
|
||||
int ret;
|
||||
|
||||
nlh = dcb_prepare(dcb, dev, RTM_SETDCB, DCB_CMD_IEEE_SET);
|
||||
ret = dcb_get_attribute_va(dcb, dev, attr, &payload, &payload_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE);
|
||||
mnl_attr_put(nlh, attr, data_len, data);
|
||||
mnl_attr_nest_end(nlh, nest);
|
||||
if (payload_len != data_len) {
|
||||
fprintf(stderr, "Wrong len %d, expected %zd\n", payload_len, data_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, NULL);
|
||||
memcpy(data, payload, data_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __dcb_set_attribute(struct dcb *dcb, int command, const char *dev,
|
||||
int (*cb)(struct dcb *, struct nlmsghdr *, void *),
|
||||
void *data, int response_attr)
|
||||
{
|
||||
struct dcb_set_attribute_response resp = {
|
||||
.response_attr = response_attr,
|
||||
};
|
||||
struct nlmsghdr *nlh;
|
||||
int ret;
|
||||
|
||||
nlh = dcb_prepare(dcb, dev, RTM_SETDCB, command);
|
||||
|
||||
ret = cb(dcb, nlh, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, &resp);
|
||||
if (ret) {
|
||||
perror("Attribute write");
|
||||
return ret;
|
||||
|
|
@ -192,6 +250,80 @@ int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *da
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct dcb_set_attribute_ieee_cb {
|
||||
int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
static int dcb_set_attribute_ieee_cb(struct dcb *dcb, struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct dcb_set_attribute_ieee_cb *ieee_data = data;
|
||||
struct nlattr *nest;
|
||||
int ret;
|
||||
|
||||
nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE);
|
||||
ret = ieee_data->cb(dcb, nlh, ieee_data->data);
|
||||
if (ret)
|
||||
return ret;
|
||||
mnl_attr_nest_end(nlh, nest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dcb_set_attribute_va(struct dcb *dcb, int command, const char *dev,
|
||||
int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data),
|
||||
void *data)
|
||||
{
|
||||
struct dcb_set_attribute_ieee_cb ieee_data = {
|
||||
.cb = cb,
|
||||
.data = data,
|
||||
};
|
||||
|
||||
return __dcb_set_attribute(dcb, command, dev,
|
||||
&dcb_set_attribute_ieee_cb, &ieee_data,
|
||||
DCB_ATTR_IEEE);
|
||||
}
|
||||
|
||||
struct dcb_set_attribute {
|
||||
int attr;
|
||||
const void *data;
|
||||
size_t data_len;
|
||||
};
|
||||
|
||||
static int dcb_set_attribute_put(struct dcb *dcb, struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct dcb_set_attribute *dsa = data;
|
||||
|
||||
mnl_attr_put(nlh, dsa->attr, dsa->data_len, dsa->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr, const void *data, size_t data_len)
|
||||
{
|
||||
struct dcb_set_attribute dsa = {
|
||||
.attr = attr,
|
||||
.data = data,
|
||||
.data_len = data_len,
|
||||
};
|
||||
|
||||
return dcb_set_attribute_va(dcb, DCB_CMD_IEEE_SET, dev,
|
||||
&dcb_set_attribute_put, &dsa);
|
||||
}
|
||||
|
||||
int dcb_set_attribute_bare(struct dcb *dcb, int command, const char *dev,
|
||||
int attr, const void *data, size_t data_len,
|
||||
int response_attr)
|
||||
{
|
||||
struct dcb_set_attribute dsa = {
|
||||
.attr = attr,
|
||||
.data = data,
|
||||
.data_len = data_len,
|
||||
};
|
||||
|
||||
return __dcb_set_attribute(dcb, command, dev,
|
||||
&dcb_set_attribute_put, &dsa, response_attr);
|
||||
}
|
||||
|
||||
void dcb_print_array_u8(const __u8 *array, size_t size)
|
||||
{
|
||||
SPRINT_BUF(b);
|
||||
|
|
@ -333,9 +465,10 @@ static void dcb_help(void)
|
|||
fprintf(stderr,
|
||||
"Usage: dcb [ OPTIONS ] OBJECT { COMMAND | help }\n"
|
||||
" dcb [ -f | --force ] { -b | --batch } filename [ -n | --netns ] netnsname\n"
|
||||
"where OBJECT := { buffer | ets | maxrate | pfc }\n"
|
||||
"where OBJECT := { app | buffer | dcbx | ets | maxrate | pfc }\n"
|
||||
" OPTIONS := [ -V | --Version | -i | --iec | -j | --json\n"
|
||||
" | -p | --pretty | -s | --statistics | -v | --verbose]\n");
|
||||
" | -N | --Numeric | -p | --pretty\n"
|
||||
" | -s | --statistics | -v | --verbose]\n");
|
||||
}
|
||||
|
||||
static int dcb_cmd(struct dcb *dcb, int argc, char **argv)
|
||||
|
|
@ -343,8 +476,12 @@ static int dcb_cmd(struct dcb *dcb, int argc, char **argv)
|
|||
if (!argc || matches(*argv, "help") == 0) {
|
||||
dcb_help();
|
||||
return 0;
|
||||
} else if (matches(*argv, "app") == 0) {
|
||||
return dcb_cmd_app(dcb, argc - 1, argv + 1);
|
||||
} else if (matches(*argv, "buffer") == 0) {
|
||||
return dcb_cmd_buffer(dcb, argc - 1, argv + 1);
|
||||
} else if (matches(*argv, "dcbx") == 0) {
|
||||
return dcb_cmd_dcbx(dcb, argc - 1, argv + 1);
|
||||
} else if (matches(*argv, "ets") == 0) {
|
||||
return dcb_cmd_ets(dcb, argc - 1, argv + 1);
|
||||
} else if (matches(*argv, "maxrate") == 0) {
|
||||
|
|
@ -377,6 +514,7 @@ int main(int argc, char **argv)
|
|||
{ "batch", required_argument, NULL, 'b' },
|
||||
{ "iec", no_argument, NULL, 'i' },
|
||||
{ "json", no_argument, NULL, 'j' },
|
||||
{ "Numeric", no_argument, NULL, 'N' },
|
||||
{ "pretty", no_argument, NULL, 'p' },
|
||||
{ "statistics", no_argument, NULL, 's' },
|
||||
{ "netns", required_argument, NULL, 'n' },
|
||||
|
|
@ -396,7 +534,7 @@ int main(int argc, char **argv)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "b:fhijn:psvV",
|
||||
while ((opt = getopt_long(argc, argv, "b:fhijn:psvNV",
|
||||
long_options, NULL)) >= 0) {
|
||||
|
||||
switch (opt) {
|
||||
|
|
@ -413,6 +551,9 @@ int main(int argc, char **argv)
|
|||
case 'j':
|
||||
dcb->json_output = true;
|
||||
break;
|
||||
case 'N':
|
||||
dcb->numeric = true;
|
||||
break;
|
||||
case 'p':
|
||||
pretty = true;
|
||||
break;
|
||||
|
|
|
|||
20
dcb/dcb.h
20
dcb/dcb.h
|
|
@ -2,6 +2,7 @@
|
|||
#ifndef __DCB_H__
|
||||
#define __DCB_H__ 1
|
||||
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
|
@ -13,6 +14,7 @@ struct dcb {
|
|||
bool json_output;
|
||||
bool stats;
|
||||
bool use_iec;
|
||||
bool numeric;
|
||||
};
|
||||
|
||||
int dcb_parse_mapping(const char *what_key, __u32 key, __u32 max_key,
|
||||
|
|
@ -32,6 +34,16 @@ int dcb_get_attribute(struct dcb *dcb, const char *dev, int attr,
|
|||
void *data, size_t data_len);
|
||||
int dcb_set_attribute(struct dcb *dcb, const char *dev, int attr,
|
||||
const void *data, size_t data_len);
|
||||
int dcb_get_attribute_va(struct dcb *dcb, const char *dev, int attr,
|
||||
void **payload_p, __u16 *payload_len_p);
|
||||
int dcb_set_attribute_va(struct dcb *dcb, int command, const char *dev,
|
||||
int (*cb)(struct dcb *dcb, struct nlmsghdr *nlh, void *data),
|
||||
void *data);
|
||||
int dcb_get_attribute_bare(struct dcb *dcb, int cmd, const char *dev, int attr,
|
||||
void **payload_p, __u16 *payload_len_p);
|
||||
int dcb_set_attribute_bare(struct dcb *dcb, int command, const char *dev,
|
||||
int attr, const void *data, size_t data_len,
|
||||
int response_attr);
|
||||
|
||||
void dcb_print_named_array(const char *json_name, const char *fp_name,
|
||||
const __u8 *array, size_t size,
|
||||
|
|
@ -42,10 +54,18 @@ void dcb_print_array_on_off(const __u8 *array, size_t size);
|
|||
void dcb_print_array_kw(const __u8 *array, size_t array_size,
|
||||
const char *const kw[], size_t kw_size);
|
||||
|
||||
/* dcb_app.c */
|
||||
|
||||
int dcb_cmd_app(struct dcb *dcb, int argc, char **argv);
|
||||
|
||||
/* dcb_buffer.c */
|
||||
|
||||
int dcb_cmd_buffer(struct dcb *dcb, int argc, char **argv);
|
||||
|
||||
/* dcb_dcbx.c */
|
||||
|
||||
int dcb_cmd_dcbx(struct dcb *dcb, int argc, char **argv);
|
||||
|
||||
/* dcb_ets.c */
|
||||
|
||||
int dcb_cmd_ets(struct dcb *dcb, int argc, char **argv);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,796 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <linux/dcbnl.h>
|
||||
|
||||
#include "dcb.h"
|
||||
#include "utils.h"
|
||||
#include "rt_names.h"
|
||||
|
||||
static void dcb_app_help_add(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: dcb app { add | del | replace } dev STRING\n"
|
||||
" [ default-prio PRIO ]\n"
|
||||
" [ ethtype-prio ET:PRIO ]\n"
|
||||
" [ stream-port-prio PORT:PRIO ]\n"
|
||||
" [ dgram-port-prio PORT:PRIO ]\n"
|
||||
" [ port-prio PORT:PRIO ]\n"
|
||||
" [ dscp-prio INTEGER:PRIO ]\n"
|
||||
"\n"
|
||||
" where PRIO := { 0 .. 7 }\n"
|
||||
" ET := { 0x600 .. 0xffff }\n"
|
||||
" PORT := { 1 .. 65535 }\n"
|
||||
" DSCP := { 0 .. 63 }\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void dcb_app_help_show_flush(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: dcb app { show | flush } dev STRING\n"
|
||||
" [ default-prio ]\n"
|
||||
" [ ethtype-prio ]\n"
|
||||
" [ stream-port-prio ]\n"
|
||||
" [ dgram-port-prio ]\n"
|
||||
" [ port-prio ]\n"
|
||||
" [ dscp-prio ]\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void dcb_app_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: dcb app help\n"
|
||||
"\n"
|
||||
);
|
||||
dcb_app_help_show_flush();
|
||||
dcb_app_help_add();
|
||||
}
|
||||
|
||||
struct dcb_app_table {
|
||||
struct dcb_app *apps;
|
||||
size_t n_apps;
|
||||
};
|
||||
|
||||
static void dcb_app_table_fini(struct dcb_app_table *tab)
|
||||
{
|
||||
free(tab->apps);
|
||||
}
|
||||
|
||||
static int dcb_app_table_push(struct dcb_app_table *tab, struct dcb_app *app)
|
||||
{
|
||||
struct dcb_app *apps = reallocarray(tab->apps, tab->n_apps + 1,
|
||||
sizeof(*tab->apps));
|
||||
|
||||
if (apps == NULL) {
|
||||
perror("Cannot allocate APP table");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tab->apps = apps;
|
||||
tab->apps[tab->n_apps++] = *app;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dcb_app_table_remove_existing(struct dcb_app_table *a,
|
||||
const struct dcb_app_table *b)
|
||||
{
|
||||
size_t ia, ja;
|
||||
size_t ib;
|
||||
|
||||
for (ia = 0, ja = 0; ia < a->n_apps; ia++) {
|
||||
struct dcb_app *aa = &a->apps[ia];
|
||||
bool found = false;
|
||||
|
||||
for (ib = 0; ib < b->n_apps; ib++) {
|
||||
const struct dcb_app *ab = &b->apps[ib];
|
||||
|
||||
if (aa->selector == ab->selector &&
|
||||
aa->protocol == ab->protocol &&
|
||||
aa->priority == ab->priority) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
a->apps[ja++] = *aa;
|
||||
}
|
||||
|
||||
a->n_apps = ja;
|
||||
}
|
||||
|
||||
static void dcb_app_table_remove_replaced(struct dcb_app_table *a,
|
||||
const struct dcb_app_table *b)
|
||||
{
|
||||
size_t ia, ja;
|
||||
size_t ib;
|
||||
|
||||
for (ia = 0, ja = 0; ia < a->n_apps; ia++) {
|
||||
struct dcb_app *aa = &a->apps[ia];
|
||||
bool present = false;
|
||||
bool found = false;
|
||||
|
||||
for (ib = 0; ib < b->n_apps; ib++) {
|
||||
const struct dcb_app *ab = &b->apps[ib];
|
||||
|
||||
if (aa->selector == ab->selector &&
|
||||
aa->protocol == ab->protocol)
|
||||
present = true;
|
||||
else
|
||||
continue;
|
||||
|
||||
if (aa->priority == ab->priority) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Entries that remain in A will be removed, so keep in the
|
||||
* table only APP entries whose sel/pid is mentioned in B,
|
||||
* but that do not have the full sel/pid/prio match.
|
||||
*/
|
||||
if (present && !found)
|
||||
a->apps[ja++] = *aa;
|
||||
}
|
||||
|
||||
a->n_apps = ja;
|
||||
}
|
||||
|
||||
static int dcb_app_table_copy(struct dcb_app_table *a,
|
||||
const struct dcb_app_table *b)
|
||||
{
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < b->n_apps; i++) {
|
||||
ret = dcb_app_table_push(a, &b->apps[i]);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcb_app_cmp(const struct dcb_app *a, const struct dcb_app *b)
|
||||
{
|
||||
if (a->protocol < b->protocol)
|
||||
return -1;
|
||||
if (a->protocol > b->protocol)
|
||||
return 1;
|
||||
return a->priority - b->priority;
|
||||
}
|
||||
|
||||
static int dcb_app_cmp_cb(const void *a, const void *b)
|
||||
{
|
||||
return dcb_app_cmp(a, b);
|
||||
}
|
||||
|
||||
static void dcb_app_table_sort(struct dcb_app_table *tab)
|
||||
{
|
||||
qsort(tab->apps, tab->n_apps, sizeof(*tab->apps), dcb_app_cmp_cb);
|
||||
}
|
||||
|
||||
struct dcb_app_parse_mapping {
|
||||
__u8 selector;
|
||||
struct dcb_app_table *tab;
|
||||
int err;
|
||||
};
|
||||
|
||||
static void dcb_app_parse_mapping_cb(__u32 key, __u64 value, void *data)
|
||||
{
|
||||
struct dcb_app_parse_mapping *pm = data;
|
||||
struct dcb_app app = {
|
||||
.selector = pm->selector,
|
||||
.priority = value,
|
||||
.protocol = key,
|
||||
};
|
||||
|
||||
if (pm->err)
|
||||
return;
|
||||
|
||||
pm->err = dcb_app_table_push(pm->tab, &app);
|
||||
}
|
||||
|
||||
static int dcb_app_parse_mapping_ethtype_prio(__u32 key, char *value, void *data)
|
||||
{
|
||||
__u8 prio;
|
||||
|
||||
if (key < 0x600) {
|
||||
fprintf(stderr, "Protocol IDs < 0x600 are reserved for EtherType\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (get_u8(&prio, value, 0))
|
||||
return -EINVAL;
|
||||
|
||||
return dcb_parse_mapping("ETHTYPE", key, 0xffff,
|
||||
"PRIO", prio, IEEE_8021QAZ_MAX_TCS - 1,
|
||||
dcb_app_parse_mapping_cb, data);
|
||||
}
|
||||
|
||||
static int dcb_app_parse_dscp(__u32 *key, const char *arg)
|
||||
{
|
||||
if (parse_mapping_num_all(key, arg) == 0)
|
||||
return 0;
|
||||
|
||||
if (rtnl_dsfield_a2n(key, arg) != 0)
|
||||
return -1;
|
||||
|
||||
if (*key & 0x03) {
|
||||
fprintf(stderr, "The values `%s' uses non-DSCP bits.\n", arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Unshift the value to convert it from dsfield to DSCP. */
|
||||
*key >>= 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcb_app_parse_mapping_dscp_prio(__u32 key, char *value, void *data)
|
||||
{
|
||||
__u8 prio;
|
||||
|
||||
if (get_u8(&prio, value, 0))
|
||||
return -EINVAL;
|
||||
|
||||
return dcb_parse_mapping("DSCP", key, 63,
|
||||
"PRIO", prio, IEEE_8021QAZ_MAX_TCS - 1,
|
||||
dcb_app_parse_mapping_cb, data);
|
||||
}
|
||||
|
||||
static int dcb_app_parse_mapping_port_prio(__u32 key, char *value, void *data)
|
||||
{
|
||||
__u8 prio;
|
||||
|
||||
if (key == 0) {
|
||||
fprintf(stderr, "Port ID of 0 is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (get_u8(&prio, value, 0))
|
||||
return -EINVAL;
|
||||
|
||||
return dcb_parse_mapping("PORT", key, 0xffff,
|
||||
"PRIO", prio, IEEE_8021QAZ_MAX_TCS - 1,
|
||||
dcb_app_parse_mapping_cb, data);
|
||||
}
|
||||
|
||||
static int dcb_app_parse_default_prio(int *argcp, char ***argvp, struct dcb_app_table *tab)
|
||||
{
|
||||
int argc = *argcp;
|
||||
char **argv = *argvp;
|
||||
int ret = 0;
|
||||
|
||||
while (argc > 0) {
|
||||
struct dcb_app app;
|
||||
__u8 prio;
|
||||
|
||||
if (get_u8(&prio, *argv, 0)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
app = (struct dcb_app){
|
||||
.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE,
|
||||
.protocol = 0,
|
||||
.priority = prio,
|
||||
};
|
||||
ret = dcb_app_table_push(tab, &app);
|
||||
if (ret != 0)
|
||||
break;
|
||||
|
||||
argc--, argv++;
|
||||
}
|
||||
|
||||
*argcp = argc;
|
||||
*argvp = argv;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool dcb_app_is_ethtype(const struct dcb_app *app)
|
||||
{
|
||||
return app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
|
||||
app->protocol != 0;
|
||||
}
|
||||
|
||||
static bool dcb_app_is_default(const struct dcb_app *app)
|
||||
{
|
||||
return app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE &&
|
||||
app->protocol == 0;
|
||||
}
|
||||
|
||||
static bool dcb_app_is_dscp(const struct dcb_app *app)
|
||||
{
|
||||
return app->selector == IEEE_8021QAZ_APP_SEL_DSCP;
|
||||
}
|
||||
|
||||
static bool dcb_app_is_stream_port(const struct dcb_app *app)
|
||||
{
|
||||
return app->selector == IEEE_8021QAZ_APP_SEL_STREAM;
|
||||
}
|
||||
|
||||
static bool dcb_app_is_dgram_port(const struct dcb_app *app)
|
||||
{
|
||||
return app->selector == IEEE_8021QAZ_APP_SEL_DGRAM;
|
||||
}
|
||||
|
||||
static bool dcb_app_is_port(const struct dcb_app *app)
|
||||
{
|
||||
return app->selector == IEEE_8021QAZ_APP_SEL_ANY;
|
||||
}
|
||||
|
||||
static int dcb_app_print_key_dec(__u16 protocol)
|
||||
{
|
||||
return print_uint(PRINT_ANY, NULL, "%d:", protocol);
|
||||
}
|
||||
|
||||
static int dcb_app_print_key_hex(__u16 protocol)
|
||||
{
|
||||
return print_uint(PRINT_ANY, NULL, "%x:", protocol);
|
||||
}
|
||||
|
||||
static int dcb_app_print_key_dscp(__u16 protocol)
|
||||
{
|
||||
const char *name = rtnl_dsfield_get_name(protocol << 2);
|
||||
|
||||
|
||||
if (!is_json_context() && name != NULL)
|
||||
return print_string(PRINT_FP, NULL, "%s:", name);
|
||||
return print_uint(PRINT_ANY, NULL, "%d:", protocol);
|
||||
}
|
||||
|
||||
static void dcb_app_print_filtered(const struct dcb_app_table *tab,
|
||||
bool (*filter)(const struct dcb_app *),
|
||||
int (*print_key)(__u16 protocol),
|
||||
const char *json_name,
|
||||
const char *fp_name)
|
||||
{
|
||||
bool first = true;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < tab->n_apps; i++) {
|
||||
struct dcb_app *app = &tab->apps[i];
|
||||
|
||||
if (!filter(app))
|
||||
continue;
|
||||
if (first) {
|
||||
open_json_array(PRINT_JSON, json_name);
|
||||
print_string(PRINT_FP, NULL, "%s ", fp_name);
|
||||
first = false;
|
||||
}
|
||||
|
||||
open_json_array(PRINT_JSON, NULL);
|
||||
print_key(app->protocol);
|
||||
print_uint(PRINT_ANY, NULL, "%d ", app->priority);
|
||||
close_json_array(PRINT_JSON, NULL);
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
close_json_array(PRINT_JSON, json_name);
|
||||
print_nl();
|
||||
}
|
||||
}
|
||||
|
||||
static void dcb_app_print_ethtype_prio(const struct dcb_app_table *tab)
|
||||
{
|
||||
dcb_app_print_filtered(tab, dcb_app_is_ethtype, dcb_app_print_key_hex,
|
||||
"ethtype_prio", "ethtype-prio");
|
||||
}
|
||||
|
||||
static void dcb_app_print_dscp_prio(const struct dcb *dcb,
|
||||
const struct dcb_app_table *tab)
|
||||
{
|
||||
dcb_app_print_filtered(tab, dcb_app_is_dscp,
|
||||
dcb->numeric ? dcb_app_print_key_dec
|
||||
: dcb_app_print_key_dscp,
|
||||
"dscp_prio", "dscp-prio");
|
||||
}
|
||||
|
||||
static void dcb_app_print_stream_port_prio(const struct dcb_app_table *tab)
|
||||
{
|
||||
dcb_app_print_filtered(tab, dcb_app_is_stream_port, dcb_app_print_key_dec,
|
||||
"stream_port_prio", "stream-port-prio");
|
||||
}
|
||||
|
||||
static void dcb_app_print_dgram_port_prio(const struct dcb_app_table *tab)
|
||||
{
|
||||
dcb_app_print_filtered(tab, dcb_app_is_dgram_port, dcb_app_print_key_dec,
|
||||
"dgram_port_prio", "dgram-port-prio");
|
||||
}
|
||||
|
||||
static void dcb_app_print_port_prio(const struct dcb_app_table *tab)
|
||||
{
|
||||
dcb_app_print_filtered(tab, dcb_app_is_port, dcb_app_print_key_dec,
|
||||
"port_prio", "port-prio");
|
||||
}
|
||||
|
||||
static void dcb_app_print_default_prio(const struct dcb_app_table *tab)
|
||||
{
|
||||
bool first = true;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < tab->n_apps; i++) {
|
||||
if (!dcb_app_is_default(&tab->apps[i]))
|
||||
continue;
|
||||
if (first) {
|
||||
open_json_array(PRINT_JSON, "default_prio");
|
||||
print_string(PRINT_FP, NULL, "default-prio ", NULL);
|
||||
first = false;
|
||||
}
|
||||
print_uint(PRINT_ANY, NULL, "%d ", tab->apps[i].priority);
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
close_json_array(PRINT_JSON, "default_prio");
|
||||
print_nl();
|
||||
}
|
||||
}
|
||||
|
||||
static void dcb_app_print(const struct dcb *dcb, const struct dcb_app_table *tab)
|
||||
{
|
||||
dcb_app_print_ethtype_prio(tab);
|
||||
dcb_app_print_default_prio(tab);
|
||||
dcb_app_print_dscp_prio(dcb, tab);
|
||||
dcb_app_print_stream_port_prio(tab);
|
||||
dcb_app_print_dgram_port_prio(tab);
|
||||
dcb_app_print_port_prio(tab);
|
||||
}
|
||||
|
||||
static int dcb_app_get_table_attr_cb(const struct nlattr *attr, void *data)
|
||||
{
|
||||
struct dcb_app_table *tab = data;
|
||||
struct dcb_app *app;
|
||||
int ret;
|
||||
|
||||
if (mnl_attr_get_type(attr) != DCB_ATTR_IEEE_APP) {
|
||||
fprintf(stderr, "Unknown attribute in DCB_ATTR_IEEE_APP_TABLE: %d\n",
|
||||
mnl_attr_get_type(attr));
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
if (mnl_attr_get_payload_len(attr) < sizeof(struct dcb_app)) {
|
||||
fprintf(stderr, "DCB_ATTR_IEEE_APP payload expected to have size %zd, not %d\n",
|
||||
sizeof(struct dcb_app), mnl_attr_get_payload_len(attr));
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
app = mnl_attr_get_payload(attr);
|
||||
ret = dcb_app_table_push(tab, app);
|
||||
if (ret != 0)
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int dcb_app_get(struct dcb *dcb, const char *dev, struct dcb_app_table *tab)
|
||||
{
|
||||
uint16_t payload_len;
|
||||
void *payload;
|
||||
int ret;
|
||||
|
||||
ret = dcb_get_attribute_va(dcb, dev, DCB_ATTR_IEEE_APP_TABLE, &payload, &payload_len);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = mnl_attr_parse_payload(payload, payload_len, dcb_app_get_table_attr_cb, tab);
|
||||
if (ret != MNL_CB_OK)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dcb_app_add_del {
|
||||
const struct dcb_app_table *tab;
|
||||
bool (*filter)(const struct dcb_app *app);
|
||||
};
|
||||
|
||||
static int dcb_app_add_del_cb(struct dcb *dcb, struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct dcb_app_add_del *add_del = data;
|
||||
struct nlattr *nest;
|
||||
size_t i;
|
||||
|
||||
nest = mnl_attr_nest_start(nlh, DCB_ATTR_IEEE_APP_TABLE);
|
||||
|
||||
for (i = 0; i < add_del->tab->n_apps; i++) {
|
||||
const struct dcb_app *app = &add_del->tab->apps[i];
|
||||
|
||||
if (add_del->filter == NULL || add_del->filter(app))
|
||||
mnl_attr_put(nlh, DCB_ATTR_IEEE_APP, sizeof(*app), app);
|
||||
}
|
||||
|
||||
mnl_attr_nest_end(nlh, nest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcb_app_add_del(struct dcb *dcb, const char *dev, int command,
|
||||
const struct dcb_app_table *tab,
|
||||
bool (*filter)(const struct dcb_app *))
|
||||
{
|
||||
struct dcb_app_add_del add_del = {
|
||||
.tab = tab,
|
||||
.filter = filter,
|
||||
};
|
||||
|
||||
if (tab->n_apps == 0)
|
||||
return 0;
|
||||
|
||||
return dcb_set_attribute_va(dcb, command, dev, dcb_app_add_del_cb, &add_del);
|
||||
}
|
||||
|
||||
static int dcb_cmd_app_parse_add_del(struct dcb *dcb, const char *dev,
|
||||
int argc, char **argv, struct dcb_app_table *tab)
|
||||
{
|
||||
struct dcb_app_parse_mapping pm = {
|
||||
.tab = tab,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!argc) {
|
||||
dcb_app_help_add();
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
if (matches(*argv, "help") == 0) {
|
||||
dcb_app_help_add();
|
||||
return 0;
|
||||
} else if (matches(*argv, "ethtype-prio") == 0) {
|
||||
NEXT_ARG();
|
||||
pm.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE;
|
||||
ret = parse_mapping(&argc, &argv, false,
|
||||
&dcb_app_parse_mapping_ethtype_prio,
|
||||
&pm);
|
||||
} else if (matches(*argv, "default-prio") == 0) {
|
||||
NEXT_ARG();
|
||||
ret = dcb_app_parse_default_prio(&argc, &argv, pm.tab);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Invalid default priority %s\n", *argv);
|
||||
return ret;
|
||||
}
|
||||
} else if (matches(*argv, "dscp-prio") == 0) {
|
||||
NEXT_ARG();
|
||||
pm.selector = IEEE_8021QAZ_APP_SEL_DSCP;
|
||||
ret = parse_mapping_gen(&argc, &argv,
|
||||
&dcb_app_parse_dscp,
|
||||
&dcb_app_parse_mapping_dscp_prio,
|
||||
&pm);
|
||||
} else if (matches(*argv, "stream-port-prio") == 0) {
|
||||
NEXT_ARG();
|
||||
pm.selector = IEEE_8021QAZ_APP_SEL_STREAM;
|
||||
ret = parse_mapping(&argc, &argv, false,
|
||||
&dcb_app_parse_mapping_port_prio,
|
||||
&pm);
|
||||
} else if (matches(*argv, "dgram-port-prio") == 0) {
|
||||
NEXT_ARG();
|
||||
pm.selector = IEEE_8021QAZ_APP_SEL_DGRAM;
|
||||
ret = parse_mapping(&argc, &argv, false,
|
||||
&dcb_app_parse_mapping_port_prio,
|
||||
&pm);
|
||||
} else if (matches(*argv, "port-prio") == 0) {
|
||||
NEXT_ARG();
|
||||
pm.selector = IEEE_8021QAZ_APP_SEL_ANY;
|
||||
ret = parse_mapping(&argc, &argv, false,
|
||||
&dcb_app_parse_mapping_port_prio,
|
||||
&pm);
|
||||
} else {
|
||||
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||
dcb_app_help_add();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Invalid mapping %s\n", *argv);
|
||||
return ret;
|
||||
}
|
||||
if (pm.err)
|
||||
return pm.err;
|
||||
} while (argc > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcb_cmd_app_add(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||
{
|
||||
struct dcb_app_table tab = {};
|
||||
int ret;
|
||||
|
||||
ret = dcb_cmd_app_parse_add_del(dcb, dev, argc, argv, &tab);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = dcb_app_add_del(dcb, dev, DCB_CMD_IEEE_SET, &tab, NULL);
|
||||
dcb_app_table_fini(&tab);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dcb_cmd_app_del(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||
{
|
||||
struct dcb_app_table tab = {};
|
||||
int ret;
|
||||
|
||||
ret = dcb_cmd_app_parse_add_del(dcb, dev, argc, argv, &tab);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = dcb_app_add_del(dcb, dev, DCB_CMD_IEEE_DEL, &tab, NULL);
|
||||
dcb_app_table_fini(&tab);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dcb_cmd_app_show(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||
{
|
||||
struct dcb_app_table tab = {};
|
||||
int ret;
|
||||
|
||||
ret = dcb_app_get(dcb, dev, &tab);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
dcb_app_table_sort(&tab);
|
||||
|
||||
open_json_object(NULL);
|
||||
|
||||
if (!argc) {
|
||||
dcb_app_print(dcb, &tab);
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
if (matches(*argv, "help") == 0) {
|
||||
dcb_app_help_show_flush();
|
||||
goto out;
|
||||
} else if (matches(*argv, "ethtype-prio") == 0) {
|
||||
dcb_app_print_ethtype_prio(&tab);
|
||||
} else if (matches(*argv, "dscp-prio") == 0) {
|
||||
dcb_app_print_dscp_prio(dcb, &tab);
|
||||
} else if (matches(*argv, "stream-port-prio") == 0) {
|
||||
dcb_app_print_stream_port_prio(&tab);
|
||||
} else if (matches(*argv, "dgram-port-prio") == 0) {
|
||||
dcb_app_print_dgram_port_prio(&tab);
|
||||
} else if (matches(*argv, "port-prio") == 0) {
|
||||
dcb_app_print_port_prio(&tab);
|
||||
} else {
|
||||
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||
dcb_app_help_show_flush();
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
NEXT_ARG_FWD();
|
||||
} while (argc > 0);
|
||||
|
||||
out:
|
||||
close_json_object();
|
||||
dcb_app_table_fini(&tab);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcb_cmd_app_flush(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||
{
|
||||
struct dcb_app_table tab = {};
|
||||
int ret;
|
||||
|
||||
ret = dcb_app_get(dcb, dev, &tab);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (!argc) {
|
||||
ret = dcb_app_add_del(dcb, dev, DCB_CMD_IEEE_DEL, &tab, NULL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
if (matches(*argv, "help") == 0) {
|
||||
dcb_app_help_show_flush();
|
||||
goto out;
|
||||
} else if (matches(*argv, "ethtype-prio") == 0) {
|
||||
ret = dcb_app_add_del(dcb, dev, DCB_CMD_IEEE_DEL, &tab,
|
||||
&dcb_app_is_ethtype);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
} else if (matches(*argv, "default-prio") == 0) {
|
||||
ret = dcb_app_add_del(dcb, dev, DCB_CMD_IEEE_DEL, &tab,
|
||||
&dcb_app_is_default);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
} else if (matches(*argv, "dscp-prio") == 0) {
|
||||
ret = dcb_app_add_del(dcb, dev, DCB_CMD_IEEE_DEL, &tab,
|
||||
&dcb_app_is_dscp);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
} else {
|
||||
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||
dcb_app_help_show_flush();
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
NEXT_ARG_FWD();
|
||||
} while (argc > 0);
|
||||
|
||||
out:
|
||||
dcb_app_table_fini(&tab);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dcb_cmd_app_replace(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||
{
|
||||
struct dcb_app_table orig = {};
|
||||
struct dcb_app_table tab = {};
|
||||
struct dcb_app_table new = {};
|
||||
int ret;
|
||||
|
||||
ret = dcb_app_get(dcb, dev, &orig);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = dcb_cmd_app_parse_add_del(dcb, dev, argc, argv, &tab);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
/* Attempts to add an existing entry would be rejected, so drop
|
||||
* these entries from tab.
|
||||
*/
|
||||
ret = dcb_app_table_copy(&new, &tab);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
dcb_app_table_remove_existing(&new, &orig);
|
||||
|
||||
ret = dcb_app_add_del(dcb, dev, DCB_CMD_IEEE_SET, &new, NULL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Could not add new APP entries\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Remove the obsolete entries. */
|
||||
dcb_app_table_remove_replaced(&orig, &tab);
|
||||
ret = dcb_app_add_del(dcb, dev, DCB_CMD_IEEE_DEL, &orig, NULL);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Could not remove replaced APP entries\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
dcb_app_table_fini(&new);
|
||||
dcb_app_table_fini(&tab);
|
||||
dcb_app_table_fini(&orig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dcb_cmd_app(struct dcb *dcb, int argc, char **argv)
|
||||
{
|
||||
if (!argc || matches(*argv, "help") == 0) {
|
||||
dcb_app_help();
|
||||
return 0;
|
||||
} else if (matches(*argv, "show") == 0) {
|
||||
NEXT_ARG_FWD();
|
||||
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||
dcb_cmd_app_show, dcb_app_help_show_flush);
|
||||
} else if (matches(*argv, "flush") == 0) {
|
||||
NEXT_ARG_FWD();
|
||||
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||
dcb_cmd_app_flush, dcb_app_help_show_flush);
|
||||
} else if (matches(*argv, "add") == 0) {
|
||||
NEXT_ARG_FWD();
|
||||
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||
dcb_cmd_app_add, dcb_app_help_add);
|
||||
} else if (matches(*argv, "del") == 0) {
|
||||
NEXT_ARG_FWD();
|
||||
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||
dcb_cmd_app_del, dcb_app_help_add);
|
||||
} else if (matches(*argv, "replace") == 0) {
|
||||
NEXT_ARG_FWD();
|
||||
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||
dcb_cmd_app_replace, dcb_app_help_add);
|
||||
} else {
|
||||
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||
dcb_app_help();
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <linux/dcbnl.h>
|
||||
|
||||
#include "dcb.h"
|
||||
#include "utils.h"
|
||||
|
||||
static void dcb_dcbx_help_set(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: dcb dcbx set dev STRING\n"
|
||||
" [ host | lld-managed ]\n"
|
||||
" [ cee | ieee ] [ static ]\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void dcb_dcbx_help_show(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: dcb dcbx show dev STRING\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static void dcb_dcbx_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: dcb dcbx help\n"
|
||||
"\n"
|
||||
);
|
||||
dcb_dcbx_help_show();
|
||||
dcb_dcbx_help_set();
|
||||
}
|
||||
|
||||
struct dcb_dcbx_flag {
|
||||
__u8 value;
|
||||
const char *key_fp;
|
||||
const char *key_json;
|
||||
};
|
||||
|
||||
static struct dcb_dcbx_flag dcb_dcbx_flags[] = {
|
||||
{DCB_CAP_DCBX_HOST, "host"},
|
||||
{DCB_CAP_DCBX_LLD_MANAGED, "lld-managed", "lld_managed"},
|
||||
{DCB_CAP_DCBX_VER_CEE, "cee"},
|
||||
{DCB_CAP_DCBX_VER_IEEE, "ieee"},
|
||||
{DCB_CAP_DCBX_STATIC, "static"},
|
||||
};
|
||||
|
||||
static void dcb_dcbx_print(__u8 dcbx)
|
||||
{
|
||||
int bit;
|
||||
int i;
|
||||
|
||||
while ((bit = ffs(dcbx))) {
|
||||
bool found = false;
|
||||
|
||||
bit--;
|
||||
for (i = 0; i < ARRAY_SIZE(dcb_dcbx_flags); i++) {
|
||||
struct dcb_dcbx_flag *flag = &dcb_dcbx_flags[i];
|
||||
|
||||
if (flag->value == 1 << bit) {
|
||||
print_bool(PRINT_JSON, flag->key_json ?: flag->key_fp,
|
||||
NULL, true);
|
||||
print_string(PRINT_FP, NULL, "%s ", flag->key_fp);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
fprintf(stderr, "Unknown DCBX bit %#x.\n", 1 << bit);
|
||||
|
||||
dcbx &= ~(1 << bit);
|
||||
}
|
||||
|
||||
print_nl();
|
||||
}
|
||||
|
||||
static int dcb_dcbx_get(struct dcb *dcb, const char *dev, __u8 *dcbx)
|
||||
{
|
||||
__u16 payload_len;
|
||||
void *payload;
|
||||
int err;
|
||||
|
||||
err = dcb_get_attribute_bare(dcb, DCB_CMD_IEEE_GET, dev, DCB_ATTR_DCBX,
|
||||
&payload, &payload_len);
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
if (payload_len != 1) {
|
||||
fprintf(stderr, "DCB_ATTR_DCBX payload has size %d, expected 1.\n",
|
||||
payload_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
*dcbx = *(__u8 *) payload;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcb_dcbx_set(struct dcb *dcb, const char *dev, __u8 dcbx)
|
||||
{
|
||||
return dcb_set_attribute_bare(dcb, DCB_CMD_SDCBX, dev, DCB_ATTR_DCBX,
|
||||
&dcbx, 1, DCB_ATTR_DCBX);
|
||||
}
|
||||
|
||||
static int dcb_cmd_dcbx_set(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||
{
|
||||
__u8 dcbx = 0;
|
||||
__u8 i;
|
||||
|
||||
if (!argc) {
|
||||
dcb_dcbx_help_set();
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
if (matches(*argv, "help") == 0) {
|
||||
dcb_dcbx_help_set();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dcb_dcbx_flags); i++) {
|
||||
struct dcb_dcbx_flag *flag = &dcb_dcbx_flags[i];
|
||||
|
||||
if (matches(*argv, flag->key_fp) == 0) {
|
||||
dcbx |= flag->value;
|
||||
NEXT_ARG_FWD();
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||
dcb_dcbx_help_set();
|
||||
return -EINVAL;
|
||||
|
||||
next:
|
||||
;
|
||||
} while (argc > 0);
|
||||
|
||||
return dcb_dcbx_set(dcb, dev, dcbx);
|
||||
}
|
||||
|
||||
static int dcb_cmd_dcbx_show(struct dcb *dcb, const char *dev, int argc, char **argv)
|
||||
{
|
||||
__u8 dcbx;
|
||||
int ret;
|
||||
|
||||
ret = dcb_dcbx_get(dcb, dev, &dcbx);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
while (argc > 0) {
|
||||
if (matches(*argv, "help") == 0) {
|
||||
dcb_dcbx_help_show();
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||
dcb_dcbx_help_show();
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
NEXT_ARG_FWD();
|
||||
}
|
||||
|
||||
open_json_object(NULL);
|
||||
dcb_dcbx_print(dcbx);
|
||||
close_json_object();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dcb_cmd_dcbx(struct dcb *dcb, int argc, char **argv)
|
||||
{
|
||||
if (!argc || matches(*argv, "help") == 0) {
|
||||
dcb_dcbx_help();
|
||||
return 0;
|
||||
} else if (matches(*argv, "show") == 0) {
|
||||
NEXT_ARG_FWD();
|
||||
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||
dcb_cmd_dcbx_show, dcb_dcbx_help_show);
|
||||
} else if (matches(*argv, "set") == 0) {
|
||||
NEXT_ARG_FWD();
|
||||
return dcb_cmd_parse_dev(dcb, argc, argv,
|
||||
dcb_cmd_dcbx_set, dcb_dcbx_help_set);
|
||||
} else {
|
||||
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||
dcb_dcbx_help();
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
|
@ -306,6 +306,10 @@ static void ifname_map_free(struct ifname_map *ifname_map)
|
|||
#define DL_OPT_FLASH_OVERWRITE BIT(39)
|
||||
#define DL_OPT_RELOAD_ACTION BIT(40)
|
||||
#define DL_OPT_RELOAD_LIMIT BIT(41)
|
||||
#define DL_OPT_PORT_FLAVOUR BIT(42)
|
||||
#define DL_OPT_PORT_PFNUMBER BIT(43)
|
||||
#define DL_OPT_PORT_SFNUMBER BIT(44)
|
||||
#define DL_OPT_PORT_FUNCTION_STATE BIT(45)
|
||||
|
||||
struct dl_opts {
|
||||
uint64_t present; /* flags of present items */
|
||||
|
|
@ -356,6 +360,10 @@ struct dl_opts {
|
|||
uint32_t overwrite_mask;
|
||||
enum devlink_reload_action reload_action;
|
||||
enum devlink_reload_limit reload_limit;
|
||||
uint32_t port_sfnumber;
|
||||
uint16_t port_flavour;
|
||||
uint16_t port_pfnumber;
|
||||
uint8_t port_fn_state;
|
||||
};
|
||||
|
||||
struct dl {
|
||||
|
|
@ -741,6 +749,7 @@ static int attr_stats_cb(const struct nlattr *attr, void *data)
|
|||
static const enum mnl_attr_data_type
|
||||
devlink_function_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {
|
||||
[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR ] = MNL_TYPE_BINARY,
|
||||
[DEVLINK_PORT_FN_ATTR_STATE] = MNL_TYPE_U8,
|
||||
};
|
||||
|
||||
static int function_attr_cb(const struct nlattr *attr, void *data)
|
||||
|
|
@ -1383,6 +1392,51 @@ static int reload_limit_get(struct dl *dl, const char *limitstr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct str_num_map port_flavour_map[] = {
|
||||
{ .str = "physical", .num = DEVLINK_PORT_FLAVOUR_PHYSICAL },
|
||||
{ .str = "cpu", .num = DEVLINK_PORT_FLAVOUR_CPU },
|
||||
{ .str = "dsa", .num = DEVLINK_PORT_FLAVOUR_DSA },
|
||||
{ .str = "pcipf", .num = DEVLINK_PORT_FLAVOUR_PCI_PF },
|
||||
{ .str = "pcivf", .num = DEVLINK_PORT_FLAVOUR_PCI_VF },
|
||||
{ .str = "pcisf", .num = DEVLINK_PORT_FLAVOUR_PCI_SF },
|
||||
{ .str = "virtual", .num = DEVLINK_PORT_FLAVOUR_VIRTUAL},
|
||||
{ .str = NULL, },
|
||||
};
|
||||
|
||||
static struct str_num_map port_fn_state_map[] = {
|
||||
{ .str = "inactive", .num = DEVLINK_PORT_FN_STATE_INACTIVE},
|
||||
{ .str = "active", .num = DEVLINK_PORT_FN_STATE_ACTIVE },
|
||||
{ .str = NULL, }
|
||||
};
|
||||
|
||||
static struct str_num_map port_fn_opstate_map[] = {
|
||||
{ .str = "attached", .num = DEVLINK_PORT_FN_OPSTATE_ATTACHED},
|
||||
{ .str = "detached", .num = DEVLINK_PORT_FN_OPSTATE_DETACHED},
|
||||
{ .str = NULL, }
|
||||
};
|
||||
|
||||
static int port_flavour_parse(const char *flavour, uint16_t *value)
|
||||
{
|
||||
int num;
|
||||
|
||||
num = str_map_lookup_str(port_flavour_map, flavour);
|
||||
if (num < 0)
|
||||
return num;
|
||||
*value = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int port_fn_state_parse(const char *statestr, uint8_t *state)
|
||||
{
|
||||
int num;
|
||||
|
||||
num = str_map_lookup_str(port_fn_state_map, statestr);
|
||||
if (num < 0)
|
||||
return num;
|
||||
*state = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dl_args_metadata {
|
||||
uint64_t o_flag;
|
||||
char err_msg[DL_ARGS_REQUIRED_MAX_ERR_LEN];
|
||||
|
|
@ -1414,6 +1468,8 @@ static const struct dl_args_metadata dl_args_required[] = {
|
|||
{DL_OPT_TRAP_NAME, "Trap's name is expected."},
|
||||
{DL_OPT_TRAP_GROUP_NAME, "Trap group's name is expected."},
|
||||
{DL_OPT_PORT_FUNCTION_HW_ADDR, "Port function's hardware address is expected."},
|
||||
{DL_OPT_PORT_FLAVOUR, "Port flavour is expected."},
|
||||
{DL_OPT_PORT_PFNUMBER, "Port PCI PF number is expected."},
|
||||
};
|
||||
|
||||
static int dl_args_finding_required_validate(uint64_t o_required,
|
||||
|
|
@ -1832,7 +1888,42 @@ static int dl_argv_parse(struct dl *dl, uint64_t o_required,
|
|||
if (err)
|
||||
return err;
|
||||
o_found |= DL_OPT_PORT_FUNCTION_HW_ADDR;
|
||||
} else if (dl_argv_match(dl, "state") &&
|
||||
(o_all & DL_OPT_PORT_FUNCTION_STATE)) {
|
||||
const char *statestr;
|
||||
|
||||
dl_arg_inc(dl);
|
||||
err = dl_argv_str(dl, &statestr);
|
||||
if (err)
|
||||
return err;
|
||||
err = port_fn_state_parse(statestr, &opts->port_fn_state);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
o_found |= DL_OPT_PORT_FUNCTION_STATE;
|
||||
} else if (dl_argv_match(dl, "flavour") && (o_all & DL_OPT_PORT_FLAVOUR)) {
|
||||
const char *flavourstr;
|
||||
|
||||
dl_arg_inc(dl);
|
||||
err = dl_argv_str(dl, &flavourstr);
|
||||
if (err)
|
||||
return err;
|
||||
err = port_flavour_parse(flavourstr, &opts->port_flavour);
|
||||
if (err)
|
||||
return err;
|
||||
o_found |= DL_OPT_PORT_FLAVOUR;
|
||||
} else if (dl_argv_match(dl, "pfnum") && (o_all & DL_OPT_PORT_PFNUMBER)) {
|
||||
dl_arg_inc(dl);
|
||||
err = dl_argv_uint16_t(dl, &opts->port_pfnumber);
|
||||
if (err)
|
||||
return err;
|
||||
o_found |= DL_OPT_PORT_PFNUMBER;
|
||||
} else if (dl_argv_match(dl, "sfnum") && (o_all & DL_OPT_PORT_SFNUMBER)) {
|
||||
dl_arg_inc(dl);
|
||||
err = dl_argv_uint32_t(dl, &opts->port_sfnumber);
|
||||
if (err)
|
||||
return err;
|
||||
o_found |= DL_OPT_PORT_SFNUMBER;
|
||||
} else {
|
||||
pr_err("Unknown option \"%s\"\n", dl_argv(dl));
|
||||
return -EINVAL;
|
||||
|
|
@ -1855,9 +1946,14 @@ dl_function_attr_put(struct nlmsghdr *nlh, const struct dl_opts *opts)
|
|||
struct nlattr *nest;
|
||||
|
||||
nest = mnl_attr_nest_start(nlh, DEVLINK_ATTR_PORT_FUNCTION);
|
||||
mnl_attr_put(nlh, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR,
|
||||
opts->port_function_hw_addr_len,
|
||||
opts->port_function_hw_addr);
|
||||
|
||||
if (opts->present & DL_OPT_PORT_FUNCTION_HW_ADDR)
|
||||
mnl_attr_put(nlh, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR,
|
||||
opts->port_function_hw_addr_len,
|
||||
opts->port_function_hw_addr);
|
||||
if (opts->present & DL_OPT_PORT_FUNCTION_STATE)
|
||||
mnl_attr_put_u8(nlh, DEVLINK_PORT_FN_ATTR_STATE,
|
||||
opts->port_fn_state);
|
||||
mnl_attr_nest_end(nlh, nest);
|
||||
}
|
||||
|
||||
|
|
@ -2013,8 +2109,14 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
|
|||
if (opts->present & DL_OPT_TRAP_POLICER_BURST)
|
||||
mnl_attr_put_u64(nlh, DEVLINK_ATTR_TRAP_POLICER_BURST,
|
||||
opts->trap_policer_burst);
|
||||
if (opts->present & DL_OPT_PORT_FUNCTION_HW_ADDR)
|
||||
if (opts->present & (DL_OPT_PORT_FUNCTION_HW_ADDR | DL_OPT_PORT_FUNCTION_STATE))
|
||||
dl_function_attr_put(nlh, opts);
|
||||
if (opts->present & DL_OPT_PORT_FLAVOUR)
|
||||
mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_FLAVOUR, opts->port_flavour);
|
||||
if (opts->present & DL_OPT_PORT_PFNUMBER)
|
||||
mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_PCI_PF_NUMBER, opts->port_pfnumber);
|
||||
if (opts->present & DL_OPT_PORT_SFNUMBER)
|
||||
mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_PCI_SF_NUMBER, opts->port_sfnumber);
|
||||
}
|
||||
|
||||
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
|
||||
|
|
@ -2706,7 +2808,8 @@ static void pr_out_param_value(struct dl *dl, const char *nla_name,
|
|||
}
|
||||
}
|
||||
|
||||
static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array)
|
||||
static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array,
|
||||
bool is_port_param)
|
||||
{
|
||||
struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
|
||||
struct nlattr *param_value_attr;
|
||||
|
|
@ -2723,9 +2826,15 @@ static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array)
|
|||
return;
|
||||
|
||||
if (array)
|
||||
pr_out_handle_start_arr(dl, tb);
|
||||
if (is_port_param)
|
||||
pr_out_port_handle_start_arr(dl, tb, false);
|
||||
else
|
||||
pr_out_handle_start_arr(dl, tb);
|
||||
else
|
||||
__pr_out_handle_start(dl, tb, true, false);
|
||||
if (is_port_param)
|
||||
pr_out_port_handle_start(dl, tb, false);
|
||||
else
|
||||
__pr_out_handle_start(dl, tb, true, false);
|
||||
|
||||
nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
|
||||
|
||||
|
|
@ -2745,7 +2854,10 @@ static void pr_out_param(struct dl *dl, struct nlattr **tb, bool array)
|
|||
pr_out_entry_end(dl);
|
||||
}
|
||||
pr_out_array_end(dl);
|
||||
pr_out_handle_end(dl);
|
||||
if (is_port_param)
|
||||
pr_out_port_handle_end(dl);
|
||||
else
|
||||
pr_out_handle_end(dl);
|
||||
}
|
||||
|
||||
static int cmd_dev_param_show_cb(const struct nlmsghdr *nlh, void *data)
|
||||
|
|
@ -2758,7 +2870,7 @@ static int cmd_dev_param_show_cb(const struct nlmsghdr *nlh, void *data)
|
|||
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
|
||||
!tb[DEVLINK_ATTR_PARAM])
|
||||
return MNL_CB_ERROR;
|
||||
pr_out_param(dl, tb, true);
|
||||
pr_out_param(dl, tb, true, false);
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
|
|
@ -2956,6 +3068,21 @@ err_param_value_parse:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int cmd_port_param_show_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
||||
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
|
||||
struct dl *dl = data;
|
||||
|
||||
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
|
||||
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
|
||||
!tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_PARAM])
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
pr_out_param(dl, tb, true, true);
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int cmd_dev_param_show(struct dl *dl)
|
||||
{
|
||||
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
|
|
@ -3700,8 +3827,12 @@ static void cmd_port_help(void)
|
|||
pr_err(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n");
|
||||
pr_err(" devlink port split DEV/PORT_INDEX count COUNT\n");
|
||||
pr_err(" devlink port unsplit DEV/PORT_INDEX\n");
|
||||
pr_err(" devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ]\n");
|
||||
pr_err(" devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ] [ state STATE ]\n");
|
||||
pr_err(" devlink port param set DEV/PORT_INDEX name PARAMETER value VALUE cmode { permanent | driverinit | runtime }\n");
|
||||
pr_err(" devlink port param show [DEV/PORT_INDEX name PARAMETER]\n");
|
||||
pr_err(" devlink port health show [ DEV/PORT_INDEX reporter REPORTER_NAME ]\n");
|
||||
pr_err(" devlink port add DEV/PORT_INDEX flavour FLAVOUR pfnum PFNUM [ sfnum SFNUM ]\n");
|
||||
pr_err(" devlink port del DEV/PORT_INDEX\n");
|
||||
}
|
||||
|
||||
static const char *port_type_name(uint32_t type)
|
||||
|
|
@ -3717,25 +3848,13 @@ static const char *port_type_name(uint32_t type)
|
|||
|
||||
static const char *port_flavour_name(uint16_t flavour)
|
||||
{
|
||||
switch (flavour) {
|
||||
case DEVLINK_PORT_FLAVOUR_PHYSICAL:
|
||||
return "physical";
|
||||
case DEVLINK_PORT_FLAVOUR_CPU:
|
||||
return "cpu";
|
||||
case DEVLINK_PORT_FLAVOUR_DSA:
|
||||
return "dsa";
|
||||
case DEVLINK_PORT_FLAVOUR_PCI_PF:
|
||||
return "pcipf";
|
||||
case DEVLINK_PORT_FLAVOUR_PCI_VF:
|
||||
return "pcivf";
|
||||
case DEVLINK_PORT_FLAVOUR_VIRTUAL:
|
||||
return "virtual";
|
||||
default:
|
||||
return "<unknown flavour>";
|
||||
}
|
||||
const char *str;
|
||||
|
||||
str = str_map_lookup_u16(port_flavour_map, flavour);
|
||||
return str ? str : "<unknown flavour>";
|
||||
}
|
||||
|
||||
static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
|
||||
static void pr_out_port_pfvfsf_num(struct dl *dl, struct nlattr **tb)
|
||||
{
|
||||
uint16_t fn_num;
|
||||
|
||||
|
|
@ -3750,6 +3869,10 @@ static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
|
|||
fn_num = mnl_attr_get_u16(tb[DEVLINK_ATTR_PORT_PCI_VF_NUMBER]);
|
||||
print_uint(PRINT_ANY, "vfnum", " vfnum %u", fn_num);
|
||||
}
|
||||
if (tb[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]) {
|
||||
fn_num = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]);
|
||||
print_uint(PRINT_ANY, "sfnum", " sfnum %u", fn_num);
|
||||
}
|
||||
if (tb[DEVLINK_ATTR_PORT_EXTERNAL]) {
|
||||
uint8_t external;
|
||||
|
||||
|
|
@ -3758,6 +3881,22 @@ static void pr_out_port_pfvf_num(struct dl *dl, struct nlattr **tb)
|
|||
}
|
||||
}
|
||||
|
||||
static const char *port_fn_state(uint8_t state)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
str = str_map_lookup_u8(port_fn_state_map, state);
|
||||
return str ? str : "<unknown state>";
|
||||
}
|
||||
|
||||
static const char *port_fn_opstate(uint8_t state)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
str = str_map_lookup_u8(port_fn_opstate_map, state);
|
||||
return str ? str : "<unknown state>";
|
||||
}
|
||||
|
||||
static void pr_out_port_function(struct dl *dl, struct nlattr **tb_port)
|
||||
{
|
||||
struct nlattr *tb[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {};
|
||||
|
|
@ -3774,16 +3913,33 @@ static void pr_out_port_function(struct dl *dl, struct nlattr **tb_port)
|
|||
if (err != MNL_CB_OK)
|
||||
return;
|
||||
|
||||
if (!tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR])
|
||||
return;
|
||||
|
||||
len = mnl_attr_get_payload_len(tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
|
||||
data = mnl_attr_get_payload(tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
|
||||
|
||||
pr_out_object_start(dl, "function");
|
||||
check_indent_newline(dl);
|
||||
print_string(PRINT_ANY, "hw_addr", "hw_addr %s",
|
||||
ll_addr_n2a(data, len, 0, hw_addr, sizeof(hw_addr)));
|
||||
|
||||
if (tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]) {
|
||||
len = mnl_attr_get_payload_len(tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
|
||||
data = mnl_attr_get_payload(tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR]);
|
||||
|
||||
print_string(PRINT_ANY, "hw_addr", "hw_addr %s",
|
||||
ll_addr_n2a(data, len, 0, hw_addr, sizeof(hw_addr)));
|
||||
}
|
||||
if (tb[DEVLINK_PORT_FN_ATTR_STATE]) {
|
||||
uint8_t state;
|
||||
|
||||
state = mnl_attr_get_u8(tb[DEVLINK_PORT_FN_ATTR_STATE]);
|
||||
|
||||
print_string(PRINT_ANY, "state", " state %s",
|
||||
port_fn_state(state));
|
||||
}
|
||||
if (tb[DEVLINK_PORT_FN_ATTR_OPSTATE]) {
|
||||
uint8_t state;
|
||||
|
||||
state = mnl_attr_get_u8(tb[DEVLINK_PORT_FN_ATTR_OPSTATE]);
|
||||
|
||||
print_string(PRINT_ANY, "opstate", " opstate %s",
|
||||
port_fn_opstate(state));
|
||||
}
|
||||
|
||||
if (!dl->json_output)
|
||||
__pr_out_indent_dec();
|
||||
pr_out_object_end(dl);
|
||||
|
|
@ -3827,7 +3983,8 @@ static void pr_out_port(struct dl *dl, struct nlattr **tb)
|
|||
switch (port_flavour) {
|
||||
case DEVLINK_PORT_FLAVOUR_PCI_PF:
|
||||
case DEVLINK_PORT_FLAVOUR_PCI_VF:
|
||||
pr_out_port_pfvf_num(dl, tb);
|
||||
case DEVLINK_PORT_FLAVOUR_PCI_SF:
|
||||
pr_out_port_pfvfsf_num(dl, tb);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -3935,9 +4092,34 @@ static int cmd_port_unsplit(struct dl *dl)
|
|||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
||||
}
|
||||
|
||||
static int cmd_port_param_show(struct dl *dl)
|
||||
{
|
||||
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
if (dl_argc(dl) == 0)
|
||||
flags |= NLM_F_DUMP;
|
||||
|
||||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_PARAM_GET, flags);
|
||||
|
||||
if (dl_argc(dl) > 0) {
|
||||
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP |
|
||||
DL_OPT_PARAM_NAME, 0);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
pr_out_section_start(dl, "param");
|
||||
err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_param_show_cb, dl);
|
||||
pr_out_section_end(dl);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void cmd_port_function_help(void)
|
||||
{
|
||||
pr_err("Usage: devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ]\n");
|
||||
pr_err("Usage: devlink port function set DEV/PORT_INDEX [ hw_addr ADDR ] [ state STATE ]\n");
|
||||
}
|
||||
|
||||
static int cmd_port_function_set(struct dl *dl)
|
||||
|
|
@ -3945,15 +4127,219 @@ static int cmd_port_function_set(struct dl *dl)
|
|||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
if (dl_no_arg(dl)) {
|
||||
cmd_port_function_help();
|
||||
return 0;
|
||||
}
|
||||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_SET, NLM_F_REQUEST | NLM_F_ACK);
|
||||
|
||||
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_PORT_FUNCTION_HW_ADDR, 0);
|
||||
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP,
|
||||
DL_OPT_PORT_FUNCTION_HW_ADDR | DL_OPT_PORT_FUNCTION_STATE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
||||
}
|
||||
|
||||
static int cmd_port_param_set_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
||||
struct nlattr *nla_param[DEVLINK_ATTR_MAX + 1] = {};
|
||||
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
|
||||
struct nlattr *param_value_attr;
|
||||
enum devlink_param_cmode cmode;
|
||||
struct param_ctx *ctx = data;
|
||||
struct dl *dl = ctx->dl;
|
||||
int nla_type;
|
||||
int err;
|
||||
|
||||
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
|
||||
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
|
||||
!tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_PARAM])
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
err = mnl_attr_parse_nested(tb[DEVLINK_ATTR_PARAM], attr_cb, nla_param);
|
||||
if (err != MNL_CB_OK)
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
if (!nla_param[DEVLINK_ATTR_PARAM_TYPE] ||
|
||||
!nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST])
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
nla_type = mnl_attr_get_u8(nla_param[DEVLINK_ATTR_PARAM_TYPE]);
|
||||
mnl_attr_for_each_nested(param_value_attr,
|
||||
nla_param[DEVLINK_ATTR_PARAM_VALUES_LIST]) {
|
||||
struct nlattr *nla_value[DEVLINK_ATTR_MAX + 1] = {};
|
||||
struct nlattr *val_attr;
|
||||
|
||||
err = mnl_attr_parse_nested(param_value_attr,
|
||||
attr_cb, nla_value);
|
||||
if (err != MNL_CB_OK)
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
|
||||
(nla_type != MNL_TYPE_FLAG &&
|
||||
!nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
cmode = mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]);
|
||||
if (cmode == dl->opts.cmode) {
|
||||
val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
|
||||
switch (nla_type) {
|
||||
case MNL_TYPE_U8:
|
||||
ctx->value.vu8 = mnl_attr_get_u8(val_attr);
|
||||
break;
|
||||
case MNL_TYPE_U16:
|
||||
ctx->value.vu16 = mnl_attr_get_u16(val_attr);
|
||||
break;
|
||||
case MNL_TYPE_U32:
|
||||
ctx->value.vu32 = mnl_attr_get_u32(val_attr);
|
||||
break;
|
||||
case MNL_TYPE_STRING:
|
||||
ctx->value.vstr = mnl_attr_get_str(val_attr);
|
||||
break;
|
||||
case MNL_TYPE_FLAG:
|
||||
ctx->value.vbool = val_attr ? true : false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->nla_type = nla_type;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int cmd_port_param_set(struct dl *dl)
|
||||
{
|
||||
struct param_ctx ctx = {};
|
||||
struct nlmsghdr *nlh;
|
||||
bool conv_exists;
|
||||
uint32_t val_u32 = 0;
|
||||
uint16_t val_u16;
|
||||
uint8_t val_u8;
|
||||
bool val_bool;
|
||||
int err;
|
||||
|
||||
err = dl_argv_parse(dl, DL_OPT_HANDLEP |
|
||||
DL_OPT_PARAM_NAME |
|
||||
DL_OPT_PARAM_VALUE |
|
||||
DL_OPT_PARAM_CMODE, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Get value type */
|
||||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_PARAM_GET,
|
||||
NLM_F_REQUEST | NLM_F_ACK);
|
||||
dl_opts_put(nlh, dl);
|
||||
|
||||
ctx.dl = dl;
|
||||
err = _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_param_set_cb, &ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_PARAM_SET,
|
||||
NLM_F_REQUEST | NLM_F_ACK);
|
||||
dl_opts_put(nlh, dl);
|
||||
|
||||
conv_exists = param_val_conv_exists(param_val_conv, PARAM_VAL_CONV_LEN,
|
||||
dl->opts.param_name);
|
||||
|
||||
mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_TYPE, ctx.nla_type);
|
||||
switch (ctx.nla_type) {
|
||||
case MNL_TYPE_U8:
|
||||
if (conv_exists) {
|
||||
err = param_val_conv_uint_get(param_val_conv,
|
||||
PARAM_VAL_CONV_LEN,
|
||||
dl->opts.param_name,
|
||||
dl->opts.param_value,
|
||||
&val_u32);
|
||||
val_u8 = val_u32;
|
||||
} else {
|
||||
err = strtouint8_t(dl->opts.param_value, &val_u8);
|
||||
}
|
||||
if (err)
|
||||
goto err_param_value_parse;
|
||||
if (val_u8 == ctx.value.vu8)
|
||||
return 0;
|
||||
mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u8);
|
||||
break;
|
||||
case MNL_TYPE_U16:
|
||||
if (conv_exists) {
|
||||
err = param_val_conv_uint_get(param_val_conv,
|
||||
PARAM_VAL_CONV_LEN,
|
||||
dl->opts.param_name,
|
||||
dl->opts.param_value,
|
||||
&val_u32);
|
||||
val_u16 = val_u32;
|
||||
} else {
|
||||
err = strtouint16_t(dl->opts.param_value, &val_u16);
|
||||
}
|
||||
if (err)
|
||||
goto err_param_value_parse;
|
||||
if (val_u16 == ctx.value.vu16)
|
||||
return 0;
|
||||
mnl_attr_put_u16(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u16);
|
||||
break;
|
||||
case MNL_TYPE_U32:
|
||||
if (conv_exists)
|
||||
err = param_val_conv_uint_get(param_val_conv,
|
||||
PARAM_VAL_CONV_LEN,
|
||||
dl->opts.param_name,
|
||||
dl->opts.param_value,
|
||||
&val_u32);
|
||||
else
|
||||
err = strtouint32_t(dl->opts.param_value, &val_u32);
|
||||
if (err)
|
||||
goto err_param_value_parse;
|
||||
if (val_u32 == ctx.value.vu32)
|
||||
return 0;
|
||||
mnl_attr_put_u32(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u32);
|
||||
break;
|
||||
case MNL_TYPE_FLAG:
|
||||
err = strtobool(dl->opts.param_value, &val_bool);
|
||||
if (err)
|
||||
goto err_param_value_parse;
|
||||
if (val_bool == ctx.value.vbool)
|
||||
return 0;
|
||||
if (val_bool)
|
||||
mnl_attr_put(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
|
||||
0, NULL);
|
||||
break;
|
||||
case MNL_TYPE_STRING:
|
||||
mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
|
||||
dl->opts.param_value);
|
||||
if (!strcmp(dl->opts.param_value, ctx.value.vstr))
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
printf("Value type not supported\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
||||
|
||||
err_param_value_parse:
|
||||
pr_err("Value \"%s\" is not a number or not within range\n",
|
||||
dl->opts.param_value);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_port_param(struct dl *dl)
|
||||
{
|
||||
if (dl_argv_match(dl, "help")) {
|
||||
cmd_port_help();
|
||||
return 0;
|
||||
} else if (dl_argv_match(dl, "show") ||
|
||||
dl_argv_match(dl, "list") || dl_no_arg(dl)) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_param_show(dl);
|
||||
} else if (dl_argv_match(dl, "set")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_param_set(dl);
|
||||
}
|
||||
pr_err("Command \"%s\" not found\n", dl_argv(dl));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int cmd_port_function(struct dl *dl)
|
||||
{
|
||||
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
|
||||
|
|
@ -3970,6 +4356,58 @@ static int cmd_port_function(struct dl *dl)
|
|||
static int cmd_health(struct dl *dl);
|
||||
static int __cmd_health_show(struct dl *dl, bool show_device, bool show_port);
|
||||
|
||||
static void cmd_port_add_help(void)
|
||||
{
|
||||
pr_err(" devlink port add { DEV | DEV/PORT_INDEX } flavour FLAVOUR pfnum PFNUM [ sfnum SFNUM ]\n");
|
||||
}
|
||||
|
||||
static int cmd_port_add(struct dl *dl)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
|
||||
cmd_port_add_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_NEW,
|
||||
NLM_F_REQUEST | NLM_F_ACK);
|
||||
|
||||
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_HANDLEP |
|
||||
DL_OPT_PORT_FLAVOUR | DL_OPT_PORT_PFNUMBER,
|
||||
DL_OPT_PORT_SFNUMBER);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_port_show_cb, dl);
|
||||
}
|
||||
|
||||
static void cmd_port_del_help(void)
|
||||
{
|
||||
pr_err(" devlink port del DEV/PORT_INDEX\n");
|
||||
}
|
||||
|
||||
static int cmd_port_del(struct dl *dl)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
|
||||
cmd_port_del_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_PORT_DEL,
|
||||
NLM_F_REQUEST | NLM_F_ACK);
|
||||
|
||||
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
|
||||
}
|
||||
|
||||
static int cmd_port(struct dl *dl)
|
||||
{
|
||||
if (dl_argv_match(dl, "help")) {
|
||||
|
|
@ -3988,6 +4426,9 @@ static int cmd_port(struct dl *dl)
|
|||
} else if (dl_argv_match(dl, "unsplit")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_unsplit(dl);
|
||||
} else if (dl_argv_match(dl, "param")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_param(dl);
|
||||
} else if (dl_argv_match(dl, "function")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_function(dl);
|
||||
|
|
@ -4000,7 +4441,14 @@ static int cmd_port(struct dl *dl)
|
|||
} else {
|
||||
return cmd_health(dl);
|
||||
}
|
||||
} else if (dl_argv_match(dl, "add")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_add(dl);
|
||||
} else if (dl_argv_match(dl, "del")) {
|
||||
dl_arg_inc(dl);
|
||||
return cmd_port_del(dl);
|
||||
}
|
||||
|
||||
pr_err("Command \"%s\" not found\n", dl_argv(dl));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
@ -4802,6 +5250,10 @@ static const char *cmd_name(uint8_t cmd)
|
|||
case DEVLINK_CMD_REGION_SET: return "set";
|
||||
case DEVLINK_CMD_REGION_NEW: return "new";
|
||||
case DEVLINK_CMD_REGION_DEL: return "del";
|
||||
case DEVLINK_CMD_PORT_PARAM_GET: return "get";
|
||||
case DEVLINK_CMD_PORT_PARAM_SET: return "set";
|
||||
case DEVLINK_CMD_PORT_PARAM_NEW: return "new";
|
||||
case DEVLINK_CMD_PORT_PARAM_DEL: return "del";
|
||||
case DEVLINK_CMD_FLASH_UPDATE: return "begin";
|
||||
case DEVLINK_CMD_FLASH_UPDATE_END: return "end";
|
||||
case DEVLINK_CMD_FLASH_UPDATE_STATUS: return "status";
|
||||
|
|
@ -4840,6 +5292,10 @@ static const char *cmd_obj(uint8_t cmd)
|
|||
case DEVLINK_CMD_PARAM_SET:
|
||||
case DEVLINK_CMD_PARAM_NEW:
|
||||
case DEVLINK_CMD_PARAM_DEL:
|
||||
case DEVLINK_CMD_PORT_PARAM_GET:
|
||||
case DEVLINK_CMD_PORT_PARAM_SET:
|
||||
case DEVLINK_CMD_PORT_PARAM_NEW:
|
||||
case DEVLINK_CMD_PORT_PARAM_DEL:
|
||||
return "param";
|
||||
case DEVLINK_CMD_REGION_GET:
|
||||
case DEVLINK_CMD_REGION_SET:
|
||||
|
|
@ -4982,7 +5438,7 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data)
|
|||
!tb[DEVLINK_ATTR_PARAM])
|
||||
return MNL_CB_ERROR;
|
||||
pr_out_mon_header(genl->cmd);
|
||||
pr_out_param(dl, tb, false);
|
||||
pr_out_param(dl, tb, false, false);
|
||||
pr_out_mon_footer();
|
||||
break;
|
||||
case DEVLINK_CMD_REGION_GET: /* fall through */
|
||||
|
|
|
|||
|
|
@ -2,6 +2,22 @@
|
|||
#ifndef __MNL_UTILS_H__
|
||||
#define __MNL_UTILS_H__ 1
|
||||
|
||||
struct mnlu_gen_socket {
|
||||
struct mnl_socket *nl;
|
||||
char *buf;
|
||||
uint32_t family;
|
||||
unsigned int seq;
|
||||
uint8_t version;
|
||||
};
|
||||
|
||||
int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
|
||||
uint8_t version);
|
||||
void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg);
|
||||
struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
|
||||
uint8_t cmd, uint16_t flags);
|
||||
int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh,
|
||||
mnl_cb_t data_cb, void *data);
|
||||
|
||||
struct mnl_socket *mnlu_socket_open(int bus);
|
||||
struct nlmsghdr *mnlu_msg_prepare(void *buf, uint32_t nlmsg_type, uint16_t flags,
|
||||
void *extra_header, size_t extra_header_size);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const char *rtnl_rtscope_n2a(int id, char *buf, int len);
|
|||
const char *rtnl_rttable_n2a(__u32 id, char *buf, int len);
|
||||
const char *rtnl_rtrealm_n2a(int id, char *buf, int len);
|
||||
const char *rtnl_dsfield_n2a(int id, char *buf, int len);
|
||||
const char *rtnl_dsfield_get_name(int id);
|
||||
const char *rtnl_group_n2a(int id, char *buf, int len);
|
||||
|
||||
int rtnl_rtprot_a2n(__u32 *id, const char *arg);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@
|
|||
|
||||
/* ld/ldx fields */
|
||||
#define BPF_DW 0x18 /* double word (64-bit) */
|
||||
#define BPF_XADD 0xc0 /* exclusive add */
|
||||
#define BPF_ATOMIC 0xc0 /* atomic memory ops - op type in immediate */
|
||||
#define BPF_XADD 0xc0 /* exclusive add - legacy name */
|
||||
|
||||
/* alu/jmp fields */
|
||||
#define BPF_MOV 0xb0 /* mov reg to reg */
|
||||
|
|
@ -43,6 +44,11 @@
|
|||
#define BPF_CALL 0x80 /* function call */
|
||||
#define BPF_EXIT 0x90 /* function return */
|
||||
|
||||
/* atomic op type fields (stored in immediate) */
|
||||
#define BPF_FETCH 0x01 /* not an opcode on its own, used to build others */
|
||||
#define BPF_XCHG (0xe0 | BPF_FETCH) /* atomic exchange */
|
||||
#define BPF_CMPXCHG (0xf0 | BPF_FETCH) /* atomic compare-and-write */
|
||||
|
||||
/* Register numbers */
|
||||
enum {
|
||||
BPF_REG_0 = 0,
|
||||
|
|
@ -2448,7 +2454,7 @@ union bpf_attr {
|
|||
* running simultaneously.
|
||||
*
|
||||
* A user should care about the synchronization by himself.
|
||||
* For example, by using the **BPF_STX_XADD** instruction to alter
|
||||
* For example, by using the **BPF_ATOMIC** instructions to alter
|
||||
* the shared data.
|
||||
* Return
|
||||
* A pointer to the local storage area.
|
||||
|
|
@ -2993,10 +2999,10 @@ union bpf_attr {
|
|||
* string length is larger than *size*, just *size*-1 bytes are
|
||||
* copied and the last byte is set to NUL.
|
||||
*
|
||||
* On success, the length of the copied string is returned. This
|
||||
* makes this helper useful in tracing programs for reading
|
||||
* strings, and more importantly to get its length at runtime. See
|
||||
* the following snippet:
|
||||
* On success, returns the number of bytes that were written,
|
||||
* including the terminal NUL. This makes this helper useful in
|
||||
* tracing programs for reading strings, and more importantly to
|
||||
* get its length at runtime. See the following snippet:
|
||||
*
|
||||
* ::
|
||||
*
|
||||
|
|
@ -3024,7 +3030,7 @@ union bpf_attr {
|
|||
* **->mm->env_start**: using this helper and the return value,
|
||||
* one can quickly iterate at the right offset of the memory area.
|
||||
* Return
|
||||
* On success, the strictly positive length of the string,
|
||||
* On success, the strictly positive length of the output string,
|
||||
* including the trailing NUL character. On error, a negative
|
||||
* value.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -200,6 +200,10 @@ enum devlink_port_flavour {
|
|||
DEVLINK_PORT_FLAVOUR_UNUSED, /* Port which exists in the switch, but
|
||||
* is not used in any way.
|
||||
*/
|
||||
DEVLINK_PORT_FLAVOUR_PCI_SF, /* Represents eswitch port
|
||||
* for the PCI SF. It is an internal
|
||||
* port that faces the PCI SF.
|
||||
*/
|
||||
};
|
||||
|
||||
enum devlink_param_cmode {
|
||||
|
|
@ -529,6 +533,7 @@ enum devlink_attr {
|
|||
DEVLINK_ATTR_RELOAD_ACTION_INFO, /* nested */
|
||||
DEVLINK_ATTR_RELOAD_ACTION_STATS, /* nested */
|
||||
|
||||
DEVLINK_ATTR_PORT_PCI_SF_NUMBER, /* u32 */
|
||||
/* add new attributes above here, update the policy in devlink.c */
|
||||
|
||||
__DEVLINK_ATTR_MAX,
|
||||
|
|
@ -578,9 +583,29 @@ enum devlink_resource_unit {
|
|||
enum devlink_port_function_attr {
|
||||
DEVLINK_PORT_FUNCTION_ATTR_UNSPEC,
|
||||
DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, /* binary */
|
||||
DEVLINK_PORT_FN_ATTR_STATE, /* u8 */
|
||||
DEVLINK_PORT_FN_ATTR_OPSTATE, /* u8 */
|
||||
|
||||
__DEVLINK_PORT_FUNCTION_ATTR_MAX,
|
||||
DEVLINK_PORT_FUNCTION_ATTR_MAX = __DEVLINK_PORT_FUNCTION_ATTR_MAX - 1
|
||||
};
|
||||
|
||||
enum devlink_port_fn_state {
|
||||
DEVLINK_PORT_FN_STATE_INACTIVE,
|
||||
DEVLINK_PORT_FN_STATE_ACTIVE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum devlink_port_fn_opstate - indicates operational state of the function
|
||||
* @DEVLINK_PORT_FN_OPSTATE_ATTACHED: Driver is attached to the function.
|
||||
* For graceful tear down of the function, after inactivation of the
|
||||
* function, user should wait for operational state to turn DETACHED.
|
||||
* @DEVLINK_PORT_FN_OPSTATE_DETACHED: Driver is detached from the function.
|
||||
* It is safe to delete the port.
|
||||
*/
|
||||
enum devlink_port_fn_opstate {
|
||||
DEVLINK_PORT_FN_OPSTATE_DETACHED,
|
||||
DEVLINK_PORT_FN_OPSTATE_ATTACHED,
|
||||
};
|
||||
|
||||
#endif /* _LINUX_DEVLINK_H_ */
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@
|
|||
#define BOND_XMIT_POLICY_LAYER23 2 /* layer 2+3 (IP ^ MAC) */
|
||||
#define BOND_XMIT_POLICY_ENCAP23 3 /* encapsulated layer 2+3 */
|
||||
#define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */
|
||||
#define BOND_XMIT_POLICY_VLAN_SRCMAC 5 /* vlan + source MAC */
|
||||
|
||||
/* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */
|
||||
#define LACP_STATE_LACP_ACTIVITY 0x1
|
||||
|
|
|
|||
|
|
@ -523,6 +523,8 @@ enum {
|
|||
IFLA_BRPORT_BACKUP_PORT,
|
||||
IFLA_BRPORT_MRP_RING_OPEN,
|
||||
IFLA_BRPORT_MRP_IN_OPEN,
|
||||
IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
|
||||
IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
|
||||
__IFLA_BRPORT_MAX
|
||||
};
|
||||
#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ enum {
|
|||
/* netlink interface */
|
||||
#define MPTCP_PM_NAME "mptcp_pm"
|
||||
#define MPTCP_PM_CMD_GRP_NAME "mptcp_pm_cmds"
|
||||
#define MPTCP_PM_EV_GRP_NAME "mptcp_pm_events"
|
||||
#define MPTCP_PM_VER 0x1
|
||||
|
||||
/*
|
||||
|
|
@ -82,6 +83,7 @@ enum {
|
|||
MPTCP_PM_CMD_FLUSH_ADDRS,
|
||||
MPTCP_PM_CMD_SET_LIMITS,
|
||||
MPTCP_PM_CMD_GET_LIMITS,
|
||||
MPTCP_PM_CMD_SET_FLAGS,
|
||||
|
||||
__MPTCP_PM_CMD_AFTER_LAST
|
||||
};
|
||||
|
|
@ -103,4 +105,77 @@ struct mptcp_info {
|
|||
__u64 mptcpi_rcv_nxt;
|
||||
};
|
||||
|
||||
/*
|
||||
* MPTCP_EVENT_CREATED: token, family, saddr4 | saddr6, daddr4 | daddr6,
|
||||
* sport, dport
|
||||
* A new MPTCP connection has been created. It is the good time to allocate
|
||||
* memory and send ADD_ADDR if needed. Depending on the traffic-patterns
|
||||
* it can take a long time until the MPTCP_EVENT_ESTABLISHED is sent.
|
||||
*
|
||||
* MPTCP_EVENT_ESTABLISHED: token, family, saddr4 | saddr6, daddr4 | daddr6,
|
||||
* sport, dport
|
||||
* A MPTCP connection is established (can start new subflows).
|
||||
*
|
||||
* MPTCP_EVENT_CLOSED: token
|
||||
* A MPTCP connection has stopped.
|
||||
*
|
||||
* MPTCP_EVENT_ANNOUNCED: token, rem_id, family, daddr4 | daddr6 [, dport]
|
||||
* A new address has been announced by the peer.
|
||||
*
|
||||
* MPTCP_EVENT_REMOVED: token, rem_id
|
||||
* An address has been lost by the peer.
|
||||
*
|
||||
* MPTCP_EVENT_SUB_ESTABLISHED: token, family, saddr4 | saddr6,
|
||||
* daddr4 | daddr6, sport, dport, backup,
|
||||
* if_idx [, error]
|
||||
* A new subflow has been established. 'error' should not be set.
|
||||
*
|
||||
* MPTCP_EVENT_SUB_CLOSED: token, family, saddr4 | saddr6, daddr4 | daddr6,
|
||||
* sport, dport, backup, if_idx [, error]
|
||||
* A subflow has been closed. An error (copy of sk_err) could be set if an
|
||||
* error has been detected for this subflow.
|
||||
*
|
||||
* MPTCP_EVENT_SUB_PRIORITY: token, family, saddr4 | saddr6, daddr4 | daddr6,
|
||||
* sport, dport, backup, if_idx [, error]
|
||||
* The priority of a subflow has changed. 'error' should not be set.
|
||||
*/
|
||||
enum mptcp_event_type {
|
||||
MPTCP_EVENT_UNSPEC = 0,
|
||||
MPTCP_EVENT_CREATED = 1,
|
||||
MPTCP_EVENT_ESTABLISHED = 2,
|
||||
MPTCP_EVENT_CLOSED = 3,
|
||||
|
||||
MPTCP_EVENT_ANNOUNCED = 6,
|
||||
MPTCP_EVENT_REMOVED = 7,
|
||||
|
||||
MPTCP_EVENT_SUB_ESTABLISHED = 10,
|
||||
MPTCP_EVENT_SUB_CLOSED = 11,
|
||||
|
||||
MPTCP_EVENT_SUB_PRIORITY = 13,
|
||||
};
|
||||
|
||||
enum mptcp_event_attr {
|
||||
MPTCP_ATTR_UNSPEC = 0,
|
||||
|
||||
MPTCP_ATTR_TOKEN, /* u32 */
|
||||
MPTCP_ATTR_FAMILY, /* u16 */
|
||||
MPTCP_ATTR_LOC_ID, /* u8 */
|
||||
MPTCP_ATTR_REM_ID, /* u8 */
|
||||
MPTCP_ATTR_SADDR4, /* be32 */
|
||||
MPTCP_ATTR_SADDR6, /* struct in6_addr */
|
||||
MPTCP_ATTR_DADDR4, /* be32 */
|
||||
MPTCP_ATTR_DADDR6, /* struct in6_addr */
|
||||
MPTCP_ATTR_SPORT, /* be16 */
|
||||
MPTCP_ATTR_DPORT, /* be16 */
|
||||
MPTCP_ATTR_BACKUP, /* u8 */
|
||||
MPTCP_ATTR_ERROR, /* u8 */
|
||||
MPTCP_ATTR_FLAGS, /* u16 */
|
||||
MPTCP_ATTR_TIMEOUT, /* u32 */
|
||||
MPTCP_ATTR_IF_IDX, /* s32 */
|
||||
|
||||
__MPTCP_ATTR_AFTER_LAST
|
||||
};
|
||||
|
||||
#define MPTCP_ATTR_MAX (__MPTCP_ATTR_AFTER_LAST - 1)
|
||||
|
||||
#endif /* _MPTCP_H */
|
||||
|
|
|
|||
|
|
@ -591,6 +591,8 @@ enum {
|
|||
TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED = 1 << 1, /* Part of an existing connection. */
|
||||
TCA_FLOWER_KEY_CT_FLAGS_RELATED = 1 << 2, /* Related to an established connection. */
|
||||
TCA_FLOWER_KEY_CT_FLAGS_TRACKED = 1 << 3, /* Conntrack has occurred. */
|
||||
TCA_FLOWER_KEY_CT_FLAGS_INVALID = 1 << 4, /* Conntrack is invalid. */
|
||||
TCA_FLOWER_KEY_CT_FLAGS_REPLY = 1 << 5, /* Packet is in the reply direction. */
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
|||
|
|
@ -434,6 +434,7 @@ enum {
|
|||
TCA_HTB_RATE64,
|
||||
TCA_HTB_CEIL64,
|
||||
TCA_HTB_PAD,
|
||||
TCA_HTB_OFFLOAD,
|
||||
__TCA_HTB_MAX,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -319,6 +319,11 @@ enum rt_scope_t {
|
|||
#define RTM_F_FIB_MATCH 0x2000 /* return full fib lookup match */
|
||||
#define RTM_F_OFFLOAD 0x4000 /* route is offloaded */
|
||||
#define RTM_F_TRAP 0x8000 /* route is trapping packets */
|
||||
#define RTM_F_OFFLOAD_FAILED 0x20000000 /* route offload failed, this value
|
||||
* is chosen to avoid conflicts with
|
||||
* other flags defined in
|
||||
* include/uapi/linux/ipv6_route.h
|
||||
*/
|
||||
|
||||
/* Reserved table identifiers */
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ struct tcphdr {
|
|||
fin:1;
|
||||
#else
|
||||
#error "Adjust your <asm/byteorder.h> defines"
|
||||
#endif
|
||||
#endif
|
||||
__be16 window;
|
||||
__sum16 check;
|
||||
__be16 urg_ptr;
|
||||
|
|
@ -62,14 +62,14 @@ struct tcphdr {
|
|||
* (union is compatible to any of its members)
|
||||
* This means this part of the code is -fstrict-aliasing safe now.
|
||||
*/
|
||||
union tcp_word_hdr {
|
||||
union tcp_word_hdr {
|
||||
struct tcphdr hdr;
|
||||
__be32 words[5];
|
||||
};
|
||||
__be32 words[5];
|
||||
};
|
||||
|
||||
#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3])
|
||||
#define tcp_flag_word(tp) (((union tcp_word_hdr *)(tp))->words[3])
|
||||
|
||||
enum {
|
||||
enum {
|
||||
TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000),
|
||||
TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000),
|
||||
TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000),
|
||||
|
|
@ -80,7 +80,7 @@ enum {
|
|||
TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000),
|
||||
TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000),
|
||||
TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000)
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* TCP general constants
|
||||
|
|
@ -103,8 +103,8 @@ enum {
|
|||
#define TCP_QUICKACK 12 /* Block/reenable quick acks */
|
||||
#define TCP_CONGESTION 13 /* Congestion control algorithm */
|
||||
#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */
|
||||
#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/
|
||||
#define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */
|
||||
#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/
|
||||
#define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */
|
||||
#define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */
|
||||
#define TCP_REPAIR 19 /* TCP sock is under repair right now */
|
||||
#define TCP_REPAIR_QUEUE 20
|
||||
|
|
@ -314,6 +314,7 @@ enum {
|
|||
TCP_NLA_TIMEOUT_REHASH, /* Timeout-triggered rehash attempts */
|
||||
TCP_NLA_BYTES_NOTSENT, /* Bytes in write queue not yet sent */
|
||||
TCP_NLA_EDT, /* Earliest departure time (CLOCK_MONOTONIC) */
|
||||
TCP_NLA_TTL, /* TTL or hop limit of a packet received */
|
||||
};
|
||||
|
||||
/* for TCP_MD5SIG socket option */
|
||||
|
|
@ -353,5 +354,9 @@ struct tcp_zerocopy_receive {
|
|||
__u64 copybuf_address; /* in: copybuf address (small reads) */
|
||||
__s32 copybuf_len; /* in/out: copybuf bytes avail/used or error */
|
||||
__u32 flags; /* in: flags */
|
||||
__u64 msg_control; /* ancillary data */
|
||||
__u64 msg_controllen;
|
||||
__u32 msg_flags;
|
||||
__u32 reserved; /* set to 0 for now */
|
||||
};
|
||||
#endif /* _LINUX_TCP_H */
|
||||
|
|
|
|||
|
|
@ -331,8 +331,40 @@ int parse_one_of(const char *msg, const char *realval, const char * const *list,
|
|||
size_t len, int *p_err);
|
||||
bool parse_on_off(const char *msg, const char *realval, int *p_err);
|
||||
|
||||
int parse_mapping_num_all(__u32 *keyp, const char *key);
|
||||
int parse_mapping_gen(int *argcp, char ***argvp,
|
||||
int (*key_cb)(__u32 *keyp, const char *key),
|
||||
int (*mapping_cb)(__u32 key, char *value, void *data),
|
||||
void *mapping_cb_data);
|
||||
int parse_mapping(int *argcp, char ***argvp, bool allow_all,
|
||||
int (*mapping_cb)(__u32 key, char *value, void *data),
|
||||
void *mapping_cb_data);
|
||||
|
||||
struct str_num_map {
|
||||
const char *str;
|
||||
unsigned int num;
|
||||
};
|
||||
|
||||
int str_map_lookup_str(const struct str_num_map *map, const char *needle);
|
||||
const char *str_map_lookup_uint(const struct str_num_map *map,
|
||||
unsigned int val);
|
||||
const char *str_map_lookup_u16(const struct str_num_map *map, uint16_t val);
|
||||
const char *str_map_lookup_u8(const struct str_num_map *map, uint8_t val);
|
||||
|
||||
unsigned int get_str_char_count(const char *str, int match);
|
||||
int str_split_by_char(char *str, char **before, char **after, int match);
|
||||
|
||||
#define INDENT_STR_MAXLEN 32
|
||||
|
||||
struct indent_mem {
|
||||
int indent_level;
|
||||
char indent_str[INDENT_STR_MAXLEN + 1];
|
||||
};
|
||||
|
||||
struct indent_mem *alloc_indent_mem(void);
|
||||
void free_indent_mem(struct indent_mem *mem);
|
||||
void inc_indent(struct indent_mem *mem);
|
||||
void dec_indent(struct indent_mem *mem);
|
||||
void print_indent(struct indent_mem *mem);
|
||||
|
||||
#endif /* __UTILS_H__ */
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ static const char *xmit_hash_policy_tbl[] = {
|
|||
"layer2+3",
|
||||
"encap2+3",
|
||||
"encap3+4",
|
||||
"vlan+srcmac",
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -148,7 +149,7 @@ static void print_explain(FILE *f)
|
|||
"ARP_ALL_TARGETS := any|all\n"
|
||||
"PRIMARY_RESELECT := always|better|failure\n"
|
||||
"FAIL_OVER_MAC := none|active|follow\n"
|
||||
"XMIT_HASH_POLICY := layer2|layer2+3|layer3+4|encap2+3|encap3+4\n"
|
||||
"XMIT_HASH_POLICY := layer2|layer2+3|layer3+4|encap2+3|encap3+4|vlan+srcmac\n"
|
||||
"LACP_RATE := slow|fast\n"
|
||||
"AD_SELECT := stable|bandwidth|count\n"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ static void print_usage(FILE *f)
|
|||
"\t[ fd { on | off } ]\n"
|
||||
"\t[ fd-non-iso { on | off } ]\n"
|
||||
"\t[ presume-ack { on | off } ]\n"
|
||||
"\t[ cc-len8-dlc { on | off } ]\n"
|
||||
"\n"
|
||||
"\t[ restart-ms TIME-MS ]\n"
|
||||
"\t[ restart ]\n"
|
||||
|
|
@ -103,6 +104,7 @@ static void print_ctrlmode(FILE *f, __u32 cm)
|
|||
_PF(CAN_CTRLMODE_FD, "FD");
|
||||
_PF(CAN_CTRLMODE_FD_NON_ISO, "FD-NON-ISO");
|
||||
_PF(CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK");
|
||||
_PF(CAN_CTRLMODE_CC_LEN8_DLC, "CC-LEN8-DLC");
|
||||
#undef _PF
|
||||
if (cm)
|
||||
print_hex(PRINT_ANY, NULL, "%x", cm);
|
||||
|
|
@ -211,6 +213,10 @@ static int can_parse_opt(struct link_util *lu, int argc, char **argv,
|
|||
NEXT_ARG();
|
||||
set_ctrlmode("presume-ack", *argv, &cm,
|
||||
CAN_CTRLMODE_PRESUME_ACK);
|
||||
} else if (matches(*argv, "cc-len8-dlc") == 0) {
|
||||
NEXT_ARG();
|
||||
set_ctrlmode("cc-len8-dlc", *argv, &cm,
|
||||
CAN_CTRLMODE_CC_LEN8_DLC);
|
||||
} else if (matches(*argv, "restart") == 0) {
|
||||
__u32 val = 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ static void usage(void)
|
|||
fprintf(stderr,
|
||||
"Usage: ip monitor [ all | LISTofOBJECTS ] [ FILE ] [ label ] [all-nsid] [dev DEVICE]\n"
|
||||
"LISTofOBJECTS := link | address | route | mroute | prefix |\n"
|
||||
" neigh | netconf | rule | nsid\n"
|
||||
" neigh | netconf | rule | nsid | nexthop\n"
|
||||
"FILE := file FILENAME\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,12 +38,12 @@ static void usage(void)
|
|||
fprintf(stderr,
|
||||
"Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
|
||||
" ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
|
||||
" ip nexthop { get| del } id ID\n"
|
||||
" ip nexthop { get | del } id ID\n"
|
||||
"SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
|
||||
" [ groups ] [ fdb ]\n"
|
||||
"NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
|
||||
" [ encap ENCAPTYPE ENCAPHDR ] | group GROUP ] }\n"
|
||||
"GROUP := [ id[,weight]>/<id[,weight]>/... ]\n"
|
||||
" [ encap ENCAPTYPE ENCAPHDR ] | group GROUP [ fdb ] }\n"
|
||||
"GROUP := [ <id[,weight]>/<id[,weight]>/... ]\n"
|
||||
"ENCAPTYPE := [ mpls ]\n"
|
||||
"ENCAPHDR := [ MPLSLABEL ]\n");
|
||||
exit(-1);
|
||||
|
|
|
|||
|
|
@ -374,6 +374,8 @@ void print_rt_flags(FILE *fp, unsigned int flags)
|
|||
print_string(PRINT_ANY, NULL, "%s ", "rt_offload");
|
||||
if (flags & RTM_F_TRAP)
|
||||
print_string(PRINT_ANY, NULL, "%s ", "rt_trap");
|
||||
if (flags & RTM_F_OFFLOAD_FAILED)
|
||||
print_string(PRINT_ANY, NULL, "%s ", "rt_offload_failed");
|
||||
|
||||
close_json_array(PRINT_JSON, NULL);
|
||||
}
|
||||
|
|
|
|||
121
lib/mnl_utils.c
121
lib/mnl_utils.c
|
|
@ -7,6 +7,7 @@
|
|||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <linux/genetlink.h>
|
||||
|
||||
#include "libnetlink.h"
|
||||
#include "mnl_utils.h"
|
||||
|
|
@ -108,3 +109,123 @@ int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, siz
|
|||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
|
||||
{
|
||||
int type = mnl_attr_get_type(attr);
|
||||
const struct nlattr **tb = data;
|
||||
|
||||
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
if (type == CTRL_ATTR_FAMILY_ID &&
|
||||
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
|
||||
return MNL_CB_ERROR;
|
||||
tb[type] = attr;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
||||
struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
|
||||
uint32_t *p_id = data;
|
||||
|
||||
mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb);
|
||||
if (!tb[CTRL_ATTR_FAMILY_ID])
|
||||
return MNL_CB_ERROR;
|
||||
*p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int family_get(struct mnlu_gen_socket *nlg, const char *family_name)
|
||||
{
|
||||
struct genlmsghdr hdr = {};
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
hdr.cmd = CTRL_CMD_GETFAMILY;
|
||||
hdr.version = 0x1;
|
||||
|
||||
nlh = mnlu_msg_prepare(nlg->buf, GENL_ID_CTRL,
|
||||
NLM_F_REQUEST | NLM_F_ACK,
|
||||
&hdr, sizeof(hdr));
|
||||
|
||||
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
|
||||
|
||||
err = mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = mnlu_socket_recv_run(nlg->nl, nlh->nlmsg_seq, nlg->buf,
|
||||
MNL_SOCKET_BUFFER_SIZE,
|
||||
get_family_id_cb, &nlg->family);
|
||||
return err;
|
||||
}
|
||||
|
||||
int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
|
||||
uint8_t version)
|
||||
{
|
||||
int err;
|
||||
|
||||
nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
|
||||
if (!nlg->buf)
|
||||
goto err_buf_alloc;
|
||||
|
||||
nlg->nl = mnlu_socket_open(NETLINK_GENERIC);
|
||||
if (!nlg->nl)
|
||||
goto err_socket_open;
|
||||
|
||||
err = family_get(nlg, family_name);
|
||||
if (err)
|
||||
goto err_socket;
|
||||
|
||||
return 0;
|
||||
|
||||
err_socket:
|
||||
mnl_socket_close(nlg->nl);
|
||||
err_socket_open:
|
||||
free(nlg->buf);
|
||||
err_buf_alloc:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg)
|
||||
{
|
||||
mnl_socket_close(nlg->nl);
|
||||
free(nlg->buf);
|
||||
}
|
||||
|
||||
struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
|
||||
uint8_t cmd, uint16_t flags)
|
||||
{
|
||||
struct genlmsghdr hdr = {};
|
||||
struct nlmsghdr *nlh;
|
||||
|
||||
hdr.cmd = cmd;
|
||||
hdr.version = nlg->version;
|
||||
nlh = mnlu_msg_prepare(nlg->buf, nlg->family, flags, &hdr, sizeof(hdr));
|
||||
nlg->seq = nlh->nlmsg_seq;
|
||||
return nlh;
|
||||
}
|
||||
|
||||
int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh,
|
||||
mnl_cb_t data_cb, void *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
|
||||
if (err < 0) {
|
||||
perror("Failed to send data");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = mnlu_socket_recv_run(nlg->nl, nlh->nlmsg_seq, nlg->buf,
|
||||
MNL_SOCKET_BUFFER_SIZE,
|
||||
data_cb, data);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "kernel answers: %s\n", strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -479,18 +479,30 @@ static void rtnl_rtdsfield_initialize(void)
|
|||
|
||||
const char *rtnl_dsfield_n2a(int id, char *buf, int len)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
if (id < 0 || id >= 256) {
|
||||
snprintf(buf, len, "%d", id);
|
||||
return buf;
|
||||
}
|
||||
if (!numeric) {
|
||||
name = rtnl_dsfield_get_name(id);
|
||||
if (name != NULL)
|
||||
return name;
|
||||
}
|
||||
snprintf(buf, len, "0x%02x", id);
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *rtnl_dsfield_get_name(int id)
|
||||
{
|
||||
if (id < 0 || id >= 256)
|
||||
return NULL;
|
||||
if (!rtnl_rtdsfield_tab[id]) {
|
||||
if (!rtnl_rtdsfield_init)
|
||||
rtnl_rtdsfield_initialize();
|
||||
}
|
||||
if (!numeric && rtnl_rtdsfield_tab[id])
|
||||
return rtnl_rtdsfield_tab[id];
|
||||
snprintf(buf, len, "0x%02x", id);
|
||||
return buf;
|
||||
return rtnl_rtdsfield_tab[id];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
157
lib/utils.c
157
lib/utils.c
|
|
@ -1764,9 +1764,10 @@ bool parse_on_off(const char *msg, const char *realval, int *p_err)
|
|||
return parse_one_of(msg, realval, values_on_off, ARRAY_SIZE(values_on_off), p_err);
|
||||
}
|
||||
|
||||
int parse_mapping(int *argcp, char ***argvp, bool allow_all,
|
||||
int (*mapping_cb)(__u32 key, char *value, void *data),
|
||||
void *mapping_cb_data)
|
||||
int parse_mapping_gen(int *argcp, char ***argvp,
|
||||
int (*key_cb)(__u32 *keyp, const char *key),
|
||||
int (*mapping_cb)(__u32 key, char *value, void *data),
|
||||
void *mapping_cb_data)
|
||||
{
|
||||
int argc = *argcp;
|
||||
char **argv = *argvp;
|
||||
|
|
@ -1780,9 +1781,7 @@ int parse_mapping(int *argcp, char ***argvp, bool allow_all,
|
|||
break;
|
||||
*colon = '\0';
|
||||
|
||||
if (allow_all && matches(*argv, "all") == 0) {
|
||||
key = (__u32) -1;
|
||||
} else if (get_u32(&key, *argv, 0)) {
|
||||
if (key_cb(&key, *argv)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1798,3 +1797,149 @@ int parse_mapping(int *argcp, char ***argvp, bool allow_all,
|
|||
*argvp = argv;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_mapping_num(__u32 *keyp, const char *key)
|
||||
{
|
||||
return get_u32(keyp, key, 0);
|
||||
}
|
||||
|
||||
int parse_mapping_num_all(__u32 *keyp, const char *key)
|
||||
{
|
||||
if (matches(key, "all") == 0) {
|
||||
*keyp = (__u32) -1;
|
||||
return 0;
|
||||
}
|
||||
return parse_mapping_num(keyp, key);
|
||||
}
|
||||
|
||||
int parse_mapping(int *argcp, char ***argvp, bool allow_all,
|
||||
int (*mapping_cb)(__u32 key, char *value, void *data),
|
||||
void *mapping_cb_data)
|
||||
{
|
||||
if (allow_all)
|
||||
return parse_mapping_gen(argcp, argvp, parse_mapping_num_all,
|
||||
mapping_cb, mapping_cb_data);
|
||||
else
|
||||
return parse_mapping_gen(argcp, argvp, parse_mapping_num,
|
||||
mapping_cb, mapping_cb_data);
|
||||
}
|
||||
|
||||
int str_map_lookup_str(const struct str_num_map *map, const char *needle)
|
||||
{
|
||||
if (!needle)
|
||||
return -EINVAL;
|
||||
|
||||
/* Process array which is NULL terminated by the string. */
|
||||
while (map && map->str) {
|
||||
if (strcmp(map->str, needle) == 0)
|
||||
return map->num;
|
||||
|
||||
map++;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const char *str_map_lookup_uint(const struct str_num_map *map, unsigned int val)
|
||||
{
|
||||
unsigned int num = val;
|
||||
|
||||
while (map && map->str) {
|
||||
if (num == map->num)
|
||||
return map->str;
|
||||
|
||||
map++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *str_map_lookup_u16(const struct str_num_map *map, uint16_t val)
|
||||
{
|
||||
unsigned int num = val;
|
||||
|
||||
while (map && map->str) {
|
||||
if (num == map->num)
|
||||
return map->str;
|
||||
|
||||
map++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *str_map_lookup_u8(const struct str_num_map *map, uint8_t val)
|
||||
{
|
||||
unsigned int num = val;
|
||||
|
||||
while (map && map->str) {
|
||||
if (num == map->num)
|
||||
return map->str;
|
||||
|
||||
map++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int get_str_char_count(const char *str, int match)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
const char *pos = str;
|
||||
|
||||
while ((pos = strchr(pos, match))) {
|
||||
count++;
|
||||
pos++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int str_split_by_char(char *str, char **before, char **after, int match)
|
||||
{
|
||||
char *slash;
|
||||
|
||||
slash = strrchr(str, match);
|
||||
if (!slash)
|
||||
return -EINVAL;
|
||||
*slash = '\0';
|
||||
*before = str;
|
||||
*after = slash + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct indent_mem *alloc_indent_mem(void)
|
||||
{
|
||||
struct indent_mem *mem = malloc(sizeof(*mem));
|
||||
|
||||
if (!mem)
|
||||
return NULL;
|
||||
strcpy(mem->indent_str, "");
|
||||
mem->indent_level = 0;
|
||||
return mem;
|
||||
}
|
||||
|
||||
void free_indent_mem(struct indent_mem *mem)
|
||||
{
|
||||
free(mem);
|
||||
}
|
||||
|
||||
#define INDENT_STR_STEP 2
|
||||
|
||||
void inc_indent(struct indent_mem *mem)
|
||||
{
|
||||
if (mem->indent_level + INDENT_STR_STEP > INDENT_STR_MAXLEN)
|
||||
return;
|
||||
mem->indent_level += INDENT_STR_STEP;
|
||||
memset(mem->indent_str, ' ', sizeof(mem->indent_str));
|
||||
mem->indent_str[mem->indent_level] = '\0';
|
||||
}
|
||||
|
||||
void dec_indent(struct indent_mem *mem)
|
||||
{
|
||||
if (mem->indent_level - INDENT_STR_STEP < 0)
|
||||
return;
|
||||
mem->indent_level -= INDENT_STR_STEP;
|
||||
mem->indent_str[mem->indent_level] = '\0';
|
||||
}
|
||||
|
||||
void print_indent(struct indent_mem *mem)
|
||||
{
|
||||
if (mem->indent_level)
|
||||
printf("%s", mem->indent_str);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
.TH DCB-ETS 8 "6 December 2020" "iproute2" "Linux"
|
||||
.SH NAME
|
||||
dcb-app \- show / manipulate application priority table of
|
||||
the DCB (Data Center Bridging) subsystem
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
.ad l
|
||||
.in +8
|
||||
|
||||
.ti -8
|
||||
.B dcb
|
||||
.RI "[ " OPTIONS " ] "
|
||||
.B app
|
||||
.RI "{ " COMMAND " | " help " }"
|
||||
.sp
|
||||
|
||||
.ti -8
|
||||
.B dcb app " { " show " | " flush " } " dev
|
||||
.RI DEV
|
||||
.RB "[ " default-prio " ]"
|
||||
.RB "[ " ethtype-prio " ]"
|
||||
.RB "[ " stream-port-prio " ]"
|
||||
.RB "[ " dgram-port-prio " ]"
|
||||
.RB "[ " port-prio " ]"
|
||||
.RB "[ " dscp-prio " ]"
|
||||
|
||||
.ti -8
|
||||
.B dcb ets " { " add " | " del " | " replace " } " dev
|
||||
.RI DEV
|
||||
.RB "[ " default-prio " " \fIPRIO-LIST\fB " ]"
|
||||
.RB "[ " ethtype-prio " " \fIET-MAP\fB " ]"
|
||||
.RB "[ " stream-port-prio " " \fIPORT-MAP\fB " ]"
|
||||
.RB "[ " dgram-port-prio " " \fIPORT-MAP\fB " ]"
|
||||
.RB "[ " port-prio " " \fIPORT-MAP\fB " ]"
|
||||
.RB "[ " dscp-prio " " \fIDSCP-MAP\fB " ]"
|
||||
|
||||
.ti -8
|
||||
.IR PRIO-LIST " := [ " PRIO-LIST " ] " PRIO
|
||||
|
||||
.ti -8
|
||||
.IR ET-MAP " := [ " ET-MAP " ] " ET-MAPPING
|
||||
|
||||
.ti -8
|
||||
.IR ET-MAPPING " := " ET\fB:\fIPRIO\fR
|
||||
|
||||
.ti -8
|
||||
.IR PORT-MAP " := [ " PORT-MAP " ] " PORT-MAPPING
|
||||
|
||||
.ti -8
|
||||
.IR PORT-MAPPING " := " PORT\fB:\fIPRIO\fR
|
||||
|
||||
.ti -8
|
||||
.IR DSCP-MAP " := [ " DSCP-MAP " ] " DSCP-MAPPING
|
||||
|
||||
.ti -8
|
||||
.IR DSCP-MAPPING " := { " DSCP " | " \fBall " }" \fB:\fIPRIO\fR
|
||||
|
||||
.ti -8
|
||||
.IR ET " := { " \fB0x600\fR " .. " \fB0xffff\fR " }"
|
||||
|
||||
.ti -8
|
||||
.IR PORT " := { " \fB1\fR " .. " \fB65535\fR " }"
|
||||
|
||||
.ti -8
|
||||
.IR DSCP " := { " \fB0\fR " .. " \fB63\fR " }"
|
||||
|
||||
.ti -8
|
||||
.IR PRIO " := { " \fB0\fR " .. " \fB7\fR " }"
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
.B dcb app
|
||||
is used to configure APP table, or application priority table in the DCB (Data
|
||||
Center Bridging) subsystem. The APP table is used to assign priority to traffic
|
||||
based on value in one of several headers: EtherType, L4 destination port, or
|
||||
DSCP. It also allows configuration of port-default priority that is chosen if no
|
||||
other prioritization rule applies.
|
||||
|
||||
DCB APP entries are 3-tuples of selector, protocol ID, and priority. Selector is
|
||||
an enumeration that picks one of the prioritization namespaces. Currently it
|
||||
mostly corresponds to configurable parameters described below. Protocol ID is a
|
||||
value in the selector namespace. E.g. for EtherType selector, protocol IDs are
|
||||
the individual EtherTypes, for DSCP they are individual code points. The
|
||||
priority is the priority that should be assigned to traffic that matches the
|
||||
selector and protocol ID.
|
||||
|
||||
The APP table is a set of DCB APP entries. The only requirement is that
|
||||
duplicate entries are not added. Notably, it is valid to have conflicting
|
||||
priority assignment for the same selector and protocol ID. For example, the set
|
||||
of two APP entries (DSCP, 10, 1) and (DSCP, 10, 2), where packets with DSCP of
|
||||
10 should get priority of both 1 and 2, form a well-defined APP table. The
|
||||
.B dcb app
|
||||
tool allows low-level management of the app table by adding and deleting
|
||||
individual APP 3-tuples through
|
||||
.B add
|
||||
and
|
||||
.B del
|
||||
commands. On the other other hand, the command
|
||||
.B replace
|
||||
does what one would typically want in this situation--first adds the new
|
||||
configuration, and then removes the obsolete one, so that only one
|
||||
prioritization is in effect for a given selector and protocol ID.
|
||||
|
||||
.SH COMMANDS
|
||||
|
||||
.TP
|
||||
.B show
|
||||
Display all entries with a given selector. When no selector is given, shows all
|
||||
APP table entries categorized per selector.
|
||||
|
||||
.TP
|
||||
.B flush
|
||||
Remove all entries with a given selector. When no selector is given, removes all
|
||||
APP table entries.
|
||||
|
||||
.TP
|
||||
.B add
|
||||
.TQ
|
||||
.B del
|
||||
Add and, respectively, remove individual APP 3-tuples to and from the DCB APP
|
||||
table.
|
||||
|
||||
.TP
|
||||
.B replace
|
||||
Take the list of entries mentioned as parameter, and add those that are not
|
||||
present in the APP table yet. Then remove those entries, whose selector and
|
||||
protocol ID have been mentioned as parameter, but not with the exact same
|
||||
priority. This has the effect of, for the given selector and protocol ID,
|
||||
causing that the table only contains the priority (or priorities) given as
|
||||
parameter.
|
||||
|
||||
.SH PARAMETERS
|
||||
|
||||
The following table shows parameters in a way that they would be used with
|
||||
\fBadd\fR, \fBdel\fR and \fBreplace\fR commands. For \fBshow\fR and \fBflush\fR,
|
||||
the parameter name is to be used as a simple keyword without further arguments.
|
||||
|
||||
.TP
|
||||
.B default-prio \fIPRIO-LIST
|
||||
The priority to be used for traffic the priority of which is otherwise
|
||||
unspecified. The argument is a list of individual priorities. Note that
|
||||
.B default-prio
|
||||
rules are configured as triplets (\fBEtherType\fR, \fB0\fR, \fIPRIO\fR).
|
||||
.B dcb app
|
||||
translates these rules to the symbolic name
|
||||
.B default-prio
|
||||
and back.
|
||||
|
||||
.TP
|
||||
.B ethtype-prio \fIET-MAP
|
||||
\fIET-MAP\fR uses the array parameter syntax, see
|
||||
.BR dcb (8)
|
||||
for details. Keys are EtherType values. Values are priorities to be assigned to
|
||||
traffic with the matching EtherType.
|
||||
|
||||
.TP
|
||||
.B stream-port-prio \fIPORT-MAP
|
||||
.TQ
|
||||
.B dgram-port-prio \fIPORT-MAP
|
||||
.TQ
|
||||
.B port-prio \fIPORT-MAP
|
||||
\fIPORT-MAP\fR uses the array parameter syntax, see
|
||||
.BR dcb (8)
|
||||
for details. Keys are L4 destination port numbers that match on, respectively,
|
||||
TCP and SCTP traffic, UDP and DCCP traffic, and either of those. Values are
|
||||
priorities that should be assigned to matching traffic.
|
||||
|
||||
.TP
|
||||
.B dscp-prio \fIDSCP-MAP
|
||||
\fIDSCP-MAP\fR uses the array parameter syntax, see
|
||||
.BR dcb (8)
|
||||
for details. Keys are DSCP points, values are priorities assigned to
|
||||
traffic with matching DSCP. DSCP points can be written either direcly as
|
||||
numeric values, or using symbolic names specified in
|
||||
.B /etc/iproute2/rt_dsfield
|
||||
(however note that that file specifies full 8-bit dsfield values, whereas
|
||||
.B dcb app
|
||||
will only use the higher six bits).
|
||||
.B dcb app show
|
||||
will similarly format DSCP values as symbolic names if possible. The
|
||||
command line option
|
||||
.B -N
|
||||
turns the show translation off.
|
||||
|
||||
.SH EXAMPLE & USAGE
|
||||
|
||||
Prioritize traffic with DSCP 0 to priority 0, 24 to 3 and 48 to 6:
|
||||
|
||||
.P
|
||||
# dcb app add dev eth0 dscp-prio 0:0 24:3 48:6
|
||||
|
||||
Add another rule to configure DSCP 24 to priority 2 and show the result:
|
||||
|
||||
.P
|
||||
# dcb app add dev eth0 dscp-prio 24:2
|
||||
.br
|
||||
# dcb app show dev eth0 dscp-prio
|
||||
.br
|
||||
dscp-prio 0:0 CS3:2 CS3:3 CS6:6
|
||||
.br
|
||||
# dcb -N app show dev eth0 dscp-prio
|
||||
.br
|
||||
dscp-prio 0:0 24:2 24:3 48:6
|
||||
|
||||
Reconfigure the table so that the only rule for DSCP 24 is for assignment of
|
||||
priority 4:
|
||||
|
||||
.P
|
||||
# dcb app replace dev eth0 dscp-prio 24:4
|
||||
.br
|
||||
# dcb app show dev eth0 dscp-prio
|
||||
.br
|
||||
dscp-prio 0:0 24:4 48:6
|
||||
|
||||
Flush all DSCP rules:
|
||||
|
||||
.P
|
||||
# dcb app flush dev eth0 dscp-prio
|
||||
.br
|
||||
# dcb app show dev eth0 dscp-prio
|
||||
.br
|
||||
(nothing)
|
||||
|
||||
.SH EXIT STATUS
|
||||
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR dcb (8)
|
||||
|
||||
.SH REPORTING BUGS
|
||||
Report any bugs to the Network Developers mailing list
|
||||
.B <netdev@vger.kernel.org>
|
||||
where the development and maintenance is primarily done.
|
||||
You do not have to be subscribed to the list to send a message there.
|
||||
|
||||
.SH AUTHOR
|
||||
Petr Machata <me@pmachata.org>
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
.TH DCB-DCBX 8 "13 December 2020" "iproute2" "Linux"
|
||||
.SH NAME
|
||||
dcb-dcbx \- show / manipulate port DCBX (Data Center Bridging eXchange)
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
.ad l
|
||||
.in +8
|
||||
|
||||
.ti -8
|
||||
.B dcb
|
||||
.RI "[ " OPTIONS " ] "
|
||||
.B dcbx
|
||||
.RI "{ " COMMAND " | " help " }"
|
||||
.sp
|
||||
|
||||
.ti -8
|
||||
.B dcb dcbx show dev
|
||||
.RI DEV
|
||||
|
||||
.ti -8
|
||||
.B dcb dcbx set dev
|
||||
.RI DEV
|
||||
.RB "[ " host " ]"
|
||||
.RB "[ " lld-managed " ]"
|
||||
.RB "[ " cee " ]"
|
||||
.RB "[ " ieee " ]"
|
||||
.RB "[ " static " ]"
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
Data Center Bridging eXchange (DCBX) is a protocol used by DCB devices to
|
||||
exchange configuration information with directly connected peers. The Linux DCBX
|
||||
object is a 1-byte bitfield of flags that configure whether DCBX is implemented
|
||||
in the device or in the host, and which version of the protocol should be used.
|
||||
.B dcb dcbx
|
||||
is used to access the per-port Linux DCBX object.
|
||||
|
||||
There are two principal modes of operation: in
|
||||
.B host
|
||||
mode, DCBX protocol is implemented by the host LLDP agent, and the DCB
|
||||
interfaces are used to propagate the negotiate parameters to capable devices. In
|
||||
.B lld-managed
|
||||
mode, the configuration is handled by the device, and DCB interfaces are used
|
||||
for inspection of negotiated parameters, and can also be used to set initial
|
||||
parameters.
|
||||
|
||||
.SH PARAMETERS
|
||||
|
||||
When used with
|
||||
.B dcb dcbx set,
|
||||
the following keywords enable the corresponding configuration. The keywords that
|
||||
are not mentioned on the command line are considered disabled. When used with
|
||||
.B show,
|
||||
each enabled feature is shown by its corresponding keyword.
|
||||
|
||||
.TP
|
||||
.B host
|
||||
.TQ
|
||||
.B lld-managed
|
||||
The device is in the host mode of operation and, respectively, the lld-managed
|
||||
mode of operation, as described above. In principle these two keywords are
|
||||
mutually exclusive, but
|
||||
.B dcb dcbx
|
||||
allows setting both and lets the driver handle it as appropriate.
|
||||
|
||||
.TP
|
||||
.B cee
|
||||
.TQ
|
||||
.B ieee
|
||||
The device supports CEE (Converged Enhanced Ethernet) and, respecively, IEEE
|
||||
version of the DCB specification. Typically only one of these will be set, but
|
||||
.B dcb dcbx
|
||||
does not mandate this.
|
||||
|
||||
.TP
|
||||
.B static
|
||||
indicates the engine supports static configuration. No actual negotiation is
|
||||
performed, negotiated parameters are always the initial configuration.
|
||||
|
||||
.SH EXAMPLE & USAGE
|
||||
|
||||
Put the DCB engine into the "host" mode of operation, and use IEEE-standardized
|
||||
DCB interfaces:
|
||||
|
||||
.P
|
||||
# dcb dcbx set dev eth0 host ieee
|
||||
|
||||
Show what was set:
|
||||
|
||||
.P
|
||||
# dcb dcbx show dev eth0
|
||||
.br
|
||||
host ieee
|
||||
|
||||
.SH EXIT STATUS
|
||||
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR dcb (8)
|
||||
|
||||
.SH REPORTING BUGS
|
||||
Report any bugs to the Network Developers mailing list
|
||||
.B <netdev@vger.kernel.org>
|
||||
where the development and maintenance is primarily done.
|
||||
You do not have to be subscribed to the list to send a message there.
|
||||
|
||||
.SH AUTHOR
|
||||
Petr Machata <me@pmachata.org>
|
||||
|
|
@ -9,7 +9,7 @@ dcb \- show / manipulate DCB (Data Center Bridging) settings
|
|||
.ti -8
|
||||
.B dcb
|
||||
.RI "[ " OPTIONS " ] "
|
||||
.RB "{ " buffer " | " ets " | " maxrate " | " pfc " }"
|
||||
.RB "{ " app " | " buffer " | " ets " | " maxrate " | " pfc " }"
|
||||
.RI "{ " COMMAND " | " help " }"
|
||||
.sp
|
||||
|
||||
|
|
@ -59,6 +59,11 @@ the 1000-based ones (K, M, B).
|
|||
.BR "\-j" , " --json"
|
||||
Generate JSON output.
|
||||
|
||||
.TP
|
||||
.BR "\-N" , " --Numeric"
|
||||
If the subtool in question translates numbers to symbolic names in some way,
|
||||
suppress this translation.
|
||||
|
||||
.TP
|
||||
.BR "\-p" , " --pretty"
|
||||
When combined with -j generate a pretty JSON output.
|
||||
|
|
@ -70,6 +75,10 @@ part of the "show" output.
|
|||
|
||||
.SH OBJECTS
|
||||
|
||||
.TP
|
||||
.B app
|
||||
- Configuration of application priority table
|
||||
|
||||
.TP
|
||||
.B buffer
|
||||
- Configuration of port buffers
|
||||
|
|
@ -130,6 +139,7 @@ other values:
|
|||
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR dcb-app (8),
|
||||
.BR dcb-buffer (8),
|
||||
.BR dcb-ets (8),
|
||||
.BR dcb-maxrate (8),
|
||||
|
|
|
|||
|
|
@ -43,6 +43,50 @@ devlink-port \- devlink port configuration
|
|||
.B devlink port health
|
||||
.RI "{ " show " | " recover " | " diagnose " | " dump " | " set " }"
|
||||
|
||||
.ti -8
|
||||
.BI "devlink port add"
|
||||
.RB "["
|
||||
.IR "DEV | DEV/PORT_INDEX"
|
||||
.RB "] "
|
||||
.RB "[ " flavour
|
||||
.IR FLAVOUR " ]"
|
||||
.RB "[ " pcipf
|
||||
.IR PFNUMBER " ]"
|
||||
.RB "{ " pcisf
|
||||
.IR SFNUMBER " }"
|
||||
.br
|
||||
|
||||
.ti -8
|
||||
.B devlink port del
|
||||
.IR DEV/PORT_INDEX
|
||||
|
||||
.ti -8
|
||||
.BR "devlink port function set "
|
||||
.IR DEV/PORT_INDEX
|
||||
.RI "{ "
|
||||
.BR "hw_addr "
|
||||
.RI "ADDR }"
|
||||
.RI "{ "
|
||||
.BR "state"
|
||||
.RI "STATE }"
|
||||
|
||||
.ti -8
|
||||
.B devlink dev param set
|
||||
.I DEV/PORT_INDEX
|
||||
.B name
|
||||
.I PARAMETER
|
||||
.B value
|
||||
.I VALUE
|
||||
.BR cmode " { " runtime " | " driverinit " | " permanent " } "
|
||||
|
||||
.ti -8
|
||||
.B devlink dev param show
|
||||
[
|
||||
.I DEV/PORT_INDEX
|
||||
.B name
|
||||
.I PARAMETER
|
||||
]
|
||||
|
||||
.ti -8
|
||||
.B devlink port help
|
||||
|
||||
|
|
@ -99,6 +143,103 @@ If this argument is omitted all ports are listed.
|
|||
Is an alias for
|
||||
.BR devlink-health (8).
|
||||
|
||||
.ti -8
|
||||
.SS devlink port add - add a devlink port
|
||||
.PP
|
||||
.B "DEV"
|
||||
- specifies the devlink device to operate on. or
|
||||
|
||||
.PP
|
||||
.B "DEV/PORT_INDEX"
|
||||
- specifies the devlink port index to use for the requested new port.
|
||||
This is optional. When ommited, driver allocates unique port index.
|
||||
|
||||
.TP
|
||||
.BR flavour " { " pcipf " | " pcisf " } "
|
||||
set port flavour
|
||||
|
||||
.I pcipf
|
||||
- PCI PF port
|
||||
|
||||
.I pcisf
|
||||
- PCI SF port
|
||||
|
||||
.TP
|
||||
.BR pfnum " { " pfnumber " } "
|
||||
Specifies PCI pfnumber to use on which a SF device to create
|
||||
|
||||
.TP
|
||||
.BR sfnum " { " sfnumber " } "
|
||||
Specifies sfnumber to assign to the device of the SF.
|
||||
This field is optional for those devices which supports auto assignment of the
|
||||
SF number.
|
||||
|
||||
.ti -8
|
||||
.SS devlink port function set - Set the port function attribute(s).
|
||||
|
||||
.PP
|
||||
.B "DEV/PORT_INDEX"
|
||||
- specifies the devlink port to operate on.
|
||||
|
||||
.TP
|
||||
.BR hw_addr " ADDR"
|
||||
- hardware address of the function to set. This is a Ethernet MAC address when
|
||||
port type is Ethernet.
|
||||
|
||||
.TP
|
||||
.BR state " { " active " | " inactive " } "
|
||||
- new state of the function to change to.
|
||||
|
||||
.I active
|
||||
- Once configuration of the function is done, activate the function.
|
||||
|
||||
.I inactive
|
||||
- To inactivate the function and its device(s), set to inactive.
|
||||
|
||||
.ti -8
|
||||
.SS devlink port del - delete a devlink port
|
||||
.PP
|
||||
.B "DEV/PORT_INDEX"
|
||||
- specifies the devlink port to delete.
|
||||
|
||||
.ti -8
|
||||
.SS devlink port param set - set new value to devlink port configuration parameter
|
||||
.PP
|
||||
.B "DEV/PORT_INDEX"
|
||||
- specifies the devlink port to operate on.
|
||||
|
||||
.TP
|
||||
.BI name " PARAMETER"
|
||||
Specify parameter name to set.
|
||||
|
||||
.TP
|
||||
.BI value " VALUE"
|
||||
New value to set.
|
||||
|
||||
.TP
|
||||
.BR cmode " { " runtime " | " driverinit " | " permanent " } "
|
||||
Configuration mode in which the new value is set.
|
||||
|
||||
.I runtime
|
||||
- Set new value while driver is running. This configuration mode doesn't require any reset to apply the new value.
|
||||
|
||||
.I driverinit
|
||||
- Set new value which will be applied during driver initialization. This configuration mode requires restart driver by devlink reload command to apply the new value.
|
||||
|
||||
.I permanent
|
||||
- New value is written to device's non-volatile memory. This configuration mode requires hard reset to apply the new value.
|
||||
|
||||
.SS devlink port param show - display devlink port supported configuration parameters attributes
|
||||
|
||||
.PP
|
||||
.B "DEV/PORT_INDEX"
|
||||
- specifies the devlink port to operate on.
|
||||
|
||||
.B name
|
||||
.I PARAMETER
|
||||
Specify parameter name to show.
|
||||
If this argument, as well as port index, are omitted - all parameters supported by devlink device ports are listed.
|
||||
|
||||
.SH "EXAMPLES"
|
||||
.PP
|
||||
devlink port show
|
||||
|
|
@ -135,6 +276,57 @@ devlink port health show pci/0000:01:00.0/1 reporter tx
|
|||
.RS 4
|
||||
Shows status and configuration of tx reporter registered on pci/0000:01:00.0/1 devlink port.
|
||||
.RE
|
||||
.PP
|
||||
devlink port add pci/0000:06:00.0 flavour pcisf pfnum 0 sfnum 88
|
||||
.RS 4
|
||||
Add a devlink port of flavour PCI SF on PCI PF having number 0 with SF number 88.
|
||||
To make use of the function an example sequence is to add a port, configure the
|
||||
function attribute and activate the function. Once function usage is completed,
|
||||
inactivate the function and finally delete the port. When there is desire to
|
||||
reuse the port without deletion, it can be reconfigured and activated again when
|
||||
function is in inactive state and function's operational state is detached.
|
||||
.RE
|
||||
.PP
|
||||
devlink port del pci/0000:06:00.0/1
|
||||
.RS 4
|
||||
Delete previously created devlink port. It is recommended to first deactivate
|
||||
the function if the function supports state management.
|
||||
.RE
|
||||
.PP
|
||||
devlink port function set pci/0000:01:00.0/1 hw_addr 00:00:00:11:22:33
|
||||
.RS 4
|
||||
Configure hardware address of the PCI function represented by devlink port.
|
||||
If the port supports change in function state, hardware address must be configured
|
||||
before activating the function.
|
||||
.RE
|
||||
.PP
|
||||
devlink port function set pci/0000:01:00.0/1 state active
|
||||
.RS 4
|
||||
Activate the function. This will initiate the function enumeration and driver loading.
|
||||
.RE
|
||||
.PP
|
||||
devlink port function set pci/0000:01:00.0/1 state inactive
|
||||
.RS 4
|
||||
Deactivate the function. This will initiate the function teardown which results
|
||||
in driver unload and device removal.
|
||||
.RE
|
||||
.PP
|
||||
devlink port function set pci/0000:01:00.0/1 hw_addr 00:00:00:11:22:33 state active
|
||||
.RS 4
|
||||
Configure hardware address and also active the function. When a function is
|
||||
activated together with other configuration in a single command, all the
|
||||
configuration is applied first before changing the state to active.
|
||||
.RE
|
||||
.PP
|
||||
devlink dev param show
|
||||
.RS 4
|
||||
Shows (dumps) all the port parameters across all the devices registered in the devlink.
|
||||
.RE
|
||||
.PP
|
||||
devlink dev param set pci/0000:01:00.0/1 name internal_error_reset value true cmode runtime
|
||||
.RS 4
|
||||
Sets the parameter internal_error_reset of specified devlink port (#1) to true.
|
||||
.RE
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR devlink (8),
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ command is the first in the command line and then the object list follows:
|
|||
is the list of object types that we want to monitor.
|
||||
It may contain
|
||||
.BR link ", " address ", " route ", " mroute ", " prefix ", "
|
||||
.BR neigh ", " netconf ", " rule " and " nsid "."
|
||||
.BR neigh ", " netconf ", " rule ", " nsid " and " nexthop "."
|
||||
If no
|
||||
.B file
|
||||
argument is given,
|
||||
|
|
|
|||
|
|
@ -387,6 +387,10 @@ new - New connection.
|
|||
.TP
|
||||
est - Established connection.
|
||||
.TP
|
||||
rpl - The packet is in the reply direction, meaning that it is in the opposite direction from the packet that initiated the connection.
|
||||
.TP
|
||||
inv - The state is invalid. The packet couldn't be associated to a connection.
|
||||
.TP
|
||||
Example: +trk+est
|
||||
.RE
|
||||
.TP
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ major:
|
|||
minor-id
|
||||
.B ] [ r2q
|
||||
divisor
|
||||
.B ]
|
||||
.B ] [ offload ]
|
||||
|
||||
.B tc class ... dev
|
||||
dev
|
||||
|
|
@ -104,6 +104,9 @@ Divisor used to calculate
|
|||
values for classes. Classes divide
|
||||
.B rate
|
||||
by this number. Default value is 10.
|
||||
.TP
|
||||
offload
|
||||
Offload the HTB algorithm to hardware (requires driver and device support).
|
||||
|
||||
.SH CLASSES
|
||||
Classes have a host of parameters to configure their operation.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
.TH DEVLINK\-DEV 8 "5 Jan 2021" "iproute2" "Linux"
|
||||
.SH NAME
|
||||
vdpa-dev \- vdpa device configuration
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
.ad l
|
||||
.in +8
|
||||
.ti -8
|
||||
.B vdpa
|
||||
.B dev
|
||||
.RI "[ " OPTIONS " ] "
|
||||
.RI " { " COMMAND | " "
|
||||
.BR help " }"
|
||||
.sp
|
||||
|
||||
.ti -8
|
||||
.IR OPTIONS " := { "
|
||||
\fB\-V\fR[\fIersion\fR]
|
||||
}
|
||||
|
||||
.ti -8
|
||||
.B vdpa dev show
|
||||
.RI "[ " DEV " ]"
|
||||
|
||||
.ti -8
|
||||
.B vdpa dev help
|
||||
|
||||
.ti -8
|
||||
.B vdpa dev add
|
||||
.B name
|
||||
.I NAME
|
||||
.B mgmtdev
|
||||
.I MGMTDEV
|
||||
|
||||
.ti -8
|
||||
.B vdpa dev del
|
||||
.I DEV
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
.SS vdpa dev show - display vdpa device attributes
|
||||
|
||||
.PP
|
||||
.I "DEV"
|
||||
- specifies the vdpa device to show.
|
||||
If this argument is omitted all devices are listed.
|
||||
|
||||
.in +4
|
||||
Format is:
|
||||
.in +2
|
||||
VDPA_DEVICE_NAME
|
||||
|
||||
.SS vdpa dev add - add a new vdpa device.
|
||||
|
||||
.TP
|
||||
.BI name " NAME"
|
||||
Name of the new vdpa device to add.
|
||||
|
||||
.TP
|
||||
.BI mgmtdev " MGMTDEV"
|
||||
Name of the management device to use for device addition.
|
||||
|
||||
.SS vdpa dev del - Delete the vdpa device.
|
||||
|
||||
.PP
|
||||
.I "DEV"
|
||||
- specifies the vdpa device to delete.
|
||||
|
||||
.SH "EXAMPLES"
|
||||
.PP
|
||||
vdpa dev show
|
||||
.RS 4
|
||||
Shows the all vdpa devices on the system.
|
||||
.RE
|
||||
.PP
|
||||
vdpa dev show foo
|
||||
.RS 4
|
||||
Shows the specified vdpa device.
|
||||
.RE
|
||||
.PP
|
||||
vdpa dev add name foo mgmtdev vdpa_sim_net
|
||||
.RS 4
|
||||
Add the vdpa device named foo on the management device vdpa_sim_net.
|
||||
.RE
|
||||
.PP
|
||||
vdpa dev del foo
|
||||
.RS 4
|
||||
Delete the vdpa device named foo which was previously created.
|
||||
.RE
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR vdpa (8),
|
||||
.BR vdpa-mgmtdev (8),
|
||||
.br
|
||||
|
||||
.SH AUTHOR
|
||||
Parav Pandit <parav@nvidia.com>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
.TH DEVLINK\-DEV 8 "5 Jan 2021" "iproute2" "Linux"
|
||||
.SH NAME
|
||||
vdpa-dev \- vdpa management device view
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
.ad l
|
||||
.in +8
|
||||
.ti -8
|
||||
.B vdpa
|
||||
.B mgmtdev
|
||||
.RI " { " COMMAND | " "
|
||||
.BR help " }"
|
||||
.sp
|
||||
|
||||
.ti -8
|
||||
.IR OPTIONS " := { "
|
||||
\fB\-V\fR[\fIersion\fR]
|
||||
}
|
||||
|
||||
.ti -8
|
||||
.B vdpa mgmtdev show
|
||||
.RI "[ " MGMTDEV " ]"
|
||||
|
||||
.ti -8
|
||||
.B vdpa mgmtdev help
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
.SS vdpa mgmtdev show - display vdpa management device attributes
|
||||
|
||||
.PP
|
||||
.I "MGMTDEV"
|
||||
- specifies the vdpa management device to show.
|
||||
If this argument is omitted all management devices are listed.
|
||||
|
||||
.SH "EXAMPLES"
|
||||
.PP
|
||||
vdpa mgmtdev show
|
||||
.RS 4
|
||||
Shows all the vdpa management devices on the system.
|
||||
.RE
|
||||
.PP
|
||||
vdpa mgmtdev show bar
|
||||
.RS 4
|
||||
Shows the specified vdpa management device.
|
||||
.RE
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR vdpa (8),
|
||||
.BR vdpa-dev (8),
|
||||
.br
|
||||
|
||||
.SH AUTHOR
|
||||
Parav Pandit <parav@nvidia.com>
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
.TH VDPA 8 "5 Jan 2021" "iproute2" "Linux"
|
||||
.SH NAME
|
||||
vdpa \- vdpa management tool
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
.ad l
|
||||
.in +8
|
||||
.ti -8
|
||||
.B vdpa
|
||||
.RI "[ " OPTIONS " ] { " dev | mgmtdev " } { " COMMAND " | "
|
||||
.BR help " }"
|
||||
.sp
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
.TP
|
||||
.BR "\-V" , " --Version"
|
||||
Print the version of the
|
||||
.B vdpa
|
||||
utility and exit.
|
||||
|
||||
.TP
|
||||
.BR "\-j" , " --json"
|
||||
Generate JSON output.
|
||||
|
||||
.TP
|
||||
.BR "\-p" , " --pretty"
|
||||
When combined with -j generate a pretty JSON output.
|
||||
|
||||
.SS
|
||||
.I OBJECT
|
||||
|
||||
.TP
|
||||
.B dev
|
||||
- vdpa device.
|
||||
|
||||
.TP
|
||||
.B mgmtdev
|
||||
- vdpa management device.
|
||||
|
||||
.SS
|
||||
.I COMMAND
|
||||
|
||||
Specifies the action to perform on the object.
|
||||
The set of possible actions depends on the object type.
|
||||
It is possible to
|
||||
.B show
|
||||
(or
|
||||
.B list
|
||||
) objects. The
|
||||
.B help
|
||||
command is available for all objects. It prints
|
||||
out a list of available commands and argument syntax conventions.
|
||||
.sp
|
||||
If no command is given, some default command is assumed.
|
||||
Usually it is
|
||||
.B show
|
||||
or, if the objects of this class cannot be listed,
|
||||
.BR "help" .
|
||||
|
||||
.SH EXIT STATUS
|
||||
Exit status is 0 if command was successful or a positive integer upon failure.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR vdpa-dev (8),
|
||||
.BR vdpa-mgmtdev (8),
|
||||
.br
|
||||
|
||||
.SH REPORTING BUGS
|
||||
Report any bugs to the Network Developers mailing list
|
||||
.B <netdev@vger.kernel.org>
|
||||
where the development and maintenance is primarily done.
|
||||
You do not have to be subscribed to the list to send a message there.
|
||||
|
||||
.SH AUTHOR
|
||||
Parav Pandit <parav@nvidia.com>
|
||||
96
misc/ss.c
96
misc/ss.c
|
|
@ -2111,6 +2111,18 @@ static void vsock_set_inet_prefix(inet_prefix *a, __u32 cid)
|
|||
memcpy(a->data, &cid, sizeof(cid));
|
||||
}
|
||||
|
||||
static char* find_port(char *addr, bool is_port)
|
||||
{
|
||||
char *port = NULL;
|
||||
if (is_port)
|
||||
port = addr;
|
||||
else
|
||||
port = strchr(addr, ':');
|
||||
if (port && *port == ':')
|
||||
*port++ = '\0';
|
||||
return port;
|
||||
}
|
||||
|
||||
void *parse_hostcond(char *addr, bool is_port)
|
||||
{
|
||||
char *port = NULL;
|
||||
|
|
@ -2119,35 +2131,49 @@ void *parse_hostcond(char *addr, bool is_port)
|
|||
int fam = preferred_family;
|
||||
struct filter *f = ¤t_filter;
|
||||
|
||||
if (fam == AF_UNIX || strncmp(addr, "unix:", 5) == 0) {
|
||||
if (strncmp(addr, "unix:", 5) == 0) {
|
||||
fam = AF_UNIX;
|
||||
addr += 5;
|
||||
} else if (strncmp(addr, "link:", 5) == 0) {
|
||||
fam = AF_PACKET;
|
||||
addr += 5;
|
||||
} else if (strncmp(addr, "netlink:", 8) == 0) {
|
||||
fam = AF_NETLINK;
|
||||
addr += 8;
|
||||
} else if (strncmp(addr, "vsock:", 6) == 0) {
|
||||
fam = AF_VSOCK;
|
||||
addr += 6;
|
||||
} else if (strncmp(addr, "inet:", 5) == 0) {
|
||||
fam = AF_INET;
|
||||
addr += 5;
|
||||
} else if (strncmp(addr, "inet6:", 6) == 0) {
|
||||
fam = AF_INET6;
|
||||
addr += 6;
|
||||
}
|
||||
|
||||
if (fam == AF_UNIX) {
|
||||
char *p;
|
||||
|
||||
a.addr.family = AF_UNIX;
|
||||
if (strncmp(addr, "unix:", 5) == 0)
|
||||
addr += 5;
|
||||
p = strdup(addr);
|
||||
a.addr.bitlen = 8*strlen(p);
|
||||
memcpy(a.addr.data, &p, sizeof(p));
|
||||
fam = AF_UNIX;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fam == AF_PACKET || strncmp(addr, "link:", 5) == 0) {
|
||||
if (fam == AF_PACKET) {
|
||||
a.addr.family = AF_PACKET;
|
||||
a.addr.bitlen = 0;
|
||||
if (strncmp(addr, "link:", 5) == 0)
|
||||
addr += 5;
|
||||
port = strchr(addr, ':');
|
||||
port = find_port(addr, is_port);
|
||||
if (port) {
|
||||
*port = 0;
|
||||
if (port[1] && strcmp(port+1, "*")) {
|
||||
if (get_integer(&a.port, port+1, 0)) {
|
||||
if ((a.port = xll_name_to_index(port+1)) <= 0)
|
||||
if (*port && strcmp(port, "*")) {
|
||||
if (get_integer(&a.port, port, 0)) {
|
||||
if ((a.port = xll_name_to_index(port)) <= 0)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addr[0] && strcmp(addr, "*")) {
|
||||
if (!is_port && addr[0] && strcmp(addr, "*")) {
|
||||
unsigned short tmp;
|
||||
|
||||
a.addr.bitlen = 32;
|
||||
|
|
@ -2155,77 +2181,51 @@ void *parse_hostcond(char *addr, bool is_port)
|
|||
return NULL;
|
||||
a.addr.data[0] = ntohs(tmp);
|
||||
}
|
||||
fam = AF_PACKET;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fam == AF_NETLINK || strncmp(addr, "netlink:", 8) == 0) {
|
||||
if (fam == AF_NETLINK) {
|
||||
a.addr.family = AF_NETLINK;
|
||||
a.addr.bitlen = 0;
|
||||
if (strncmp(addr, "netlink:", 8) == 0)
|
||||
addr += 8;
|
||||
port = strchr(addr, ':');
|
||||
port = find_port(addr, is_port);
|
||||
if (port) {
|
||||
*port = 0;
|
||||
if (port[1] && strcmp(port+1, "*")) {
|
||||
if (get_integer(&a.port, port+1, 0)) {
|
||||
if (strcmp(port+1, "kernel") == 0)
|
||||
if (*port && strcmp(port, "*")) {
|
||||
if (get_integer(&a.port, port, 0)) {
|
||||
if (strcmp(port, "kernel") == 0)
|
||||
a.port = 0;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addr[0] && strcmp(addr, "*")) {
|
||||
if (!is_port && addr[0] && strcmp(addr, "*")) {
|
||||
a.addr.bitlen = 32;
|
||||
if (nl_proto_a2n(&a.addr.data[0], addr) == -1)
|
||||
return NULL;
|
||||
}
|
||||
fam = AF_NETLINK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fam == AF_VSOCK || strncmp(addr, "vsock:", 6) == 0) {
|
||||
if (fam == AF_VSOCK) {
|
||||
__u32 cid = ~(__u32)0;
|
||||
|
||||
a.addr.family = AF_VSOCK;
|
||||
if (strncmp(addr, "vsock:", 6) == 0)
|
||||
addr += 6;
|
||||
|
||||
if (is_port)
|
||||
port = addr;
|
||||
else {
|
||||
port = strchr(addr, ':');
|
||||
if (port) {
|
||||
*port = '\0';
|
||||
port++;
|
||||
}
|
||||
}
|
||||
port = find_port(addr, is_port);
|
||||
|
||||
if (port && strcmp(port, "*") &&
|
||||
get_u32((__u32 *)&a.port, port, 0))
|
||||
return NULL;
|
||||
|
||||
if (addr[0] && strcmp(addr, "*")) {
|
||||
if (!is_port && addr[0] && strcmp(addr, "*")) {
|
||||
a.addr.bitlen = 32;
|
||||
if (get_u32(&cid, addr, 0))
|
||||
return NULL;
|
||||
}
|
||||
vsock_set_inet_prefix(&a.addr, cid);
|
||||
fam = AF_VSOCK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fam == AF_INET || !strncmp(addr, "inet:", 5)) {
|
||||
fam = AF_INET;
|
||||
if (!strncmp(addr, "inet:", 5))
|
||||
addr += 5;
|
||||
} else if (fam == AF_INET6 || !strncmp(addr, "inet6:", 6)) {
|
||||
fam = AF_INET6;
|
||||
if (!strncmp(addr, "inet6:", 6))
|
||||
addr += 6;
|
||||
}
|
||||
|
||||
/* URL-like literal [] */
|
||||
if (addr[0] == '[') {
|
||||
addr++;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "list.h"
|
||||
#include "utils.h"
|
||||
#include "mnl_utils.h"
|
||||
#include "json_print.h"
|
||||
|
||||
#define pr_err(args...) fprintf(stderr, ##args)
|
||||
|
|
|
|||
24
rdma/utils.c
24
rdma/utils.c
|
|
@ -673,18 +673,12 @@ int rd_send_msg(struct rd *rd)
|
|||
{
|
||||
int ret;
|
||||
|
||||
rd->nl = mnl_socket_open(NETLINK_RDMA);
|
||||
rd->nl = mnlu_socket_open(NETLINK_RDMA);
|
||||
if (!rd->nl) {
|
||||
pr_err("Failed to open NETLINK_RDMA socket\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = mnl_socket_bind(rd->nl, 0, MNL_SOCKET_AUTOPID);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to bind socket with err %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = mnl_socket_sendto(rd->nl, rd->nlh, rd->nlh->nlmsg_len);
|
||||
if (ret < 0) {
|
||||
pr_err("Failed to send to socket with err %d\n", ret);
|
||||
|
|
@ -699,23 +693,13 @@ err:
|
|||
|
||||
int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, unsigned int seq)
|
||||
{
|
||||
int ret;
|
||||
unsigned int portid;
|
||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
int ret;
|
||||
|
||||
portid = mnl_socket_get_portid(rd->nl);
|
||||
do {
|
||||
ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf));
|
||||
if (ret <= 0)
|
||||
break;
|
||||
|
||||
ret = mnl_cb_run(buf, ret, seq, portid, callback, data);
|
||||
} while (ret > 0);
|
||||
|
||||
ret = mnlu_socket_recv_run(rd->nl, seq, buf, MNL_SOCKET_BUFFER_SIZE,
|
||||
callback, data);
|
||||
if (ret < 0 && !rd->suppress_errors)
|
||||
perror("error");
|
||||
|
||||
mnl_socket_close(rd->nl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -345,6 +345,8 @@ static struct flower_ct_states {
|
|||
{ "trk", TCA_FLOWER_KEY_CT_FLAGS_TRACKED },
|
||||
{ "new", TCA_FLOWER_KEY_CT_FLAGS_NEW },
|
||||
{ "est", TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED },
|
||||
{ "inv", TCA_FLOWER_KEY_CT_FLAGS_INVALID },
|
||||
{ "rpl", TCA_FLOWER_KEY_CT_FLAGS_REPLY },
|
||||
};
|
||||
|
||||
static int flower_parse_ct_state(char *str, struct nlmsghdr *n)
|
||||
|
|
|
|||
10
tc/q_htb.c
10
tc/q_htb.c
|
|
@ -30,11 +30,12 @@
|
|||
static void explain(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: ... qdisc add ... htb [default N] [r2q N]\n"
|
||||
" [direct_qlen P]\n"
|
||||
" [direct_qlen P] [offload]\n"
|
||||
" default minor id of class to which unclassified packets are sent {0}\n"
|
||||
" r2q DRR quantums are computed as rate in Bps/r2q {10}\n"
|
||||
" debug string of 16 numbers each 0-3 {0}\n\n"
|
||||
" direct_qlen Limit of the direct queue {in packets}\n"
|
||||
" offload enable hardware offload\n"
|
||||
"... class add ... htb rate R1 [burst B1] [mpu B] [overhead O]\n"
|
||||
" [prio P] [slot S] [pslot PS]\n"
|
||||
" [ceil R2] [cburst B2] [mtu MTU] [quantum Q]\n"
|
||||
|
|
@ -68,6 +69,7 @@ static int htb_parse_opt(struct qdisc_util *qu, int argc,
|
|||
};
|
||||
struct rtattr *tail;
|
||||
unsigned int i; char *p;
|
||||
bool offload = false;
|
||||
|
||||
while (argc > 0) {
|
||||
if (matches(*argv, "r2q") == 0) {
|
||||
|
|
@ -91,6 +93,8 @@ static int htb_parse_opt(struct qdisc_util *qu, int argc,
|
|||
if (get_u32(&direct_qlen, *argv, 10)) {
|
||||
explain1("direct_qlen"); return -1;
|
||||
}
|
||||
} else if (matches(*argv, "offload") == 0) {
|
||||
offload = true;
|
||||
} else {
|
||||
fprintf(stderr, "What is \"%s\"?\n", *argv);
|
||||
explain();
|
||||
|
|
@ -103,6 +107,8 @@ static int htb_parse_opt(struct qdisc_util *qu, int argc,
|
|||
if (direct_qlen != ~0U)
|
||||
addattr_l(n, 2024, TCA_HTB_DIRECT_QLEN,
|
||||
&direct_qlen, sizeof(direct_qlen));
|
||||
if (offload)
|
||||
addattr(n, 2024, TCA_HTB_OFFLOAD);
|
||||
addattr_nest_end(n, tail);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -344,6 +350,8 @@ static int htb_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
|
|||
print_uint(PRINT_ANY, "direct_qlen", " direct_qlen %u",
|
||||
direct_qlen);
|
||||
}
|
||||
if (tb[TCA_HTB_OFFLOAD])
|
||||
print_null(PRINT_ANY, "offload", " offload", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
include ../config.mk
|
||||
|
||||
TARGETS :=
|
||||
|
||||
ifeq ($(HAVE_MNL),y)
|
||||
|
||||
CFLAGS += -I./include/uapi/
|
||||
VDPAOBJ = vdpa.o
|
||||
TARGETS += vdpa
|
||||
|
||||
endif
|
||||
|
||||
all: $(TARGETS) $(LIBS)
|
||||
|
||||
vdpa: $(VDPAOBJ)
|
||||
$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
|
||||
|
||||
install: all
|
||||
for i in $(TARGETS); \
|
||||
do install -m 0755 $$i $(DESTDIR)$(SBINDIR); \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -f $(VDPAOBJ) $(TARGETS)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* vdpa device management interface
|
||||
* Copyright (c) 2020 Mellanox Technologies Ltd. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_VDPA_H_
|
||||
#define _LINUX_VDPA_H_
|
||||
|
||||
#define VDPA_GENL_NAME "vdpa"
|
||||
#define VDPA_GENL_VERSION 0x1
|
||||
|
||||
enum vdpa_command {
|
||||
VDPA_CMD_UNSPEC,
|
||||
VDPA_CMD_MGMTDEV_NEW,
|
||||
VDPA_CMD_MGMTDEV_GET, /* can dump */
|
||||
VDPA_CMD_DEV_NEW,
|
||||
VDPA_CMD_DEV_DEL,
|
||||
VDPA_CMD_DEV_GET, /* can dump */
|
||||
};
|
||||
|
||||
enum vdpa_attr {
|
||||
VDPA_ATTR_UNSPEC,
|
||||
|
||||
/* bus name (optional) + dev name together make the parent device handle */
|
||||
VDPA_ATTR_MGMTDEV_BUS_NAME, /* string */
|
||||
VDPA_ATTR_MGMTDEV_DEV_NAME, /* string */
|
||||
VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES, /* u64 */
|
||||
|
||||
VDPA_ATTR_DEV_NAME, /* string */
|
||||
VDPA_ATTR_DEV_ID, /* u32 */
|
||||
VDPA_ATTR_DEV_VENDOR_ID, /* u32 */
|
||||
VDPA_ATTR_DEV_MAX_VQS, /* u32 */
|
||||
VDPA_ATTR_DEV_MAX_VQ_SIZE, /* u16 */
|
||||
|
||||
/* new attributes must be added above here */
|
||||
VDPA_ATTR_MAX,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef _LINUX_VIRTIO_IDS_H
|
||||
#define _LINUX_VIRTIO_IDS_H
|
||||
/*
|
||||
* Virtio IDs
|
||||
*
|
||||
* This header is BSD licensed so anyone can use the definitions to implement
|
||||
* compatible drivers/servers.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of IBM nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE. */
|
||||
|
||||
#define VIRTIO_ID_NET 1 /* virtio net */
|
||||
#define VIRTIO_ID_BLOCK 2 /* virtio block */
|
||||
#define VIRTIO_ID_CONSOLE 3 /* virtio console */
|
||||
#define VIRTIO_ID_RNG 4 /* virtio rng */
|
||||
#define VIRTIO_ID_BALLOON 5 /* virtio balloon */
|
||||
#define VIRTIO_ID_IOMEM 6 /* virtio ioMemory */
|
||||
#define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */
|
||||
#define VIRTIO_ID_SCSI 8 /* virtio scsi */
|
||||
#define VIRTIO_ID_9P 9 /* 9p virtio console */
|
||||
#define VIRTIO_ID_MAC80211_WLAN 10 /* virtio WLAN MAC */
|
||||
#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
|
||||
#define VIRTIO_ID_CAIF 12 /* Virtio caif */
|
||||
#define VIRTIO_ID_MEMORY_BALLOON 13 /* virtio memory balloon */
|
||||
#define VIRTIO_ID_GPU 16 /* virtio GPU */
|
||||
#define VIRTIO_ID_CLOCK 17 /* virtio clock/timer */
|
||||
#define VIRTIO_ID_INPUT 18 /* virtio input */
|
||||
#define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
|
||||
#define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
|
||||
#define VIRTIO_ID_SIGNAL_DIST 21 /* virtio signal distribution device */
|
||||
#define VIRTIO_ID_PSTORE 22 /* virtio pstore device */
|
||||
#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
|
||||
#define VIRTIO_ID_MEM 24 /* virtio mem */
|
||||
#define VIRTIO_ID_FS 26 /* virtio filesystem */
|
||||
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
|
||||
#define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */
|
||||
|
||||
#endif /* _LINUX_VIRTIO_IDS_H */
|
||||
|
|
@ -0,0 +1,675 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/vdpa.h>
|
||||
#include <linux/virtio_ids.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
#include "mnl_utils.h"
|
||||
|
||||
#include "version.h"
|
||||
#include "json_print.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define VDPA_OPT_MGMTDEV_HANDLE BIT(0)
|
||||
#define VDPA_OPT_VDEV_MGMTDEV_HANDLE BIT(1)
|
||||
#define VDPA_OPT_VDEV_NAME BIT(2)
|
||||
#define VDPA_OPT_VDEV_HANDLE BIT(3)
|
||||
|
||||
struct vdpa_opts {
|
||||
uint64_t present; /* flags of present items */
|
||||
char *mdev_bus_name;
|
||||
char *mdev_name;
|
||||
const char *vdev_name;
|
||||
unsigned int device_id;
|
||||
};
|
||||
|
||||
struct vdpa {
|
||||
struct mnlu_gen_socket nlg;
|
||||
struct vdpa_opts opts;
|
||||
bool json_output;
|
||||
struct indent_mem *indent;
|
||||
};
|
||||
|
||||
static void pr_out_section_start(struct vdpa *vdpa, const char *name)
|
||||
{
|
||||
open_json_object(NULL);
|
||||
open_json_object(name);
|
||||
}
|
||||
|
||||
static void pr_out_section_end(struct vdpa *vdpa)
|
||||
{
|
||||
close_json_object();
|
||||
close_json_object();
|
||||
}
|
||||
|
||||
static void pr_out_array_start(struct vdpa *vdpa, const char *name)
|
||||
{
|
||||
if (!vdpa->json_output) {
|
||||
print_nl();
|
||||
inc_indent(vdpa->indent);
|
||||
print_indent(vdpa->indent);
|
||||
}
|
||||
open_json_array(PRINT_ANY, name);
|
||||
}
|
||||
|
||||
static void pr_out_array_end(struct vdpa *vdpa)
|
||||
{
|
||||
close_json_array(PRINT_JSON, NULL);
|
||||
if (!vdpa->json_output)
|
||||
dec_indent(vdpa->indent);
|
||||
}
|
||||
|
||||
static const enum mnl_attr_data_type vdpa_policy[VDPA_ATTR_MAX + 1] = {
|
||||
[VDPA_ATTR_MGMTDEV_BUS_NAME] = MNL_TYPE_NUL_STRING,
|
||||
[VDPA_ATTR_MGMTDEV_DEV_NAME] = MNL_TYPE_NUL_STRING,
|
||||
[VDPA_ATTR_DEV_NAME] = MNL_TYPE_STRING,
|
||||
[VDPA_ATTR_DEV_ID] = MNL_TYPE_U32,
|
||||
[VDPA_ATTR_DEV_VENDOR_ID] = MNL_TYPE_U32,
|
||||
[VDPA_ATTR_DEV_MAX_VQS] = MNL_TYPE_U32,
|
||||
[VDPA_ATTR_DEV_MAX_VQ_SIZE] = MNL_TYPE_U16,
|
||||
};
|
||||
|
||||
static int attr_cb(const struct nlattr *attr, void *data)
|
||||
{
|
||||
const struct nlattr **tb = data;
|
||||
int type;
|
||||
|
||||
if (mnl_attr_type_valid(attr, VDPA_ATTR_MAX) < 0)
|
||||
return MNL_CB_OK;
|
||||
|
||||
type = mnl_attr_get_type(attr);
|
||||
if (mnl_attr_validate(attr, vdpa_policy[type]) < 0)
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
tb[type] = attr;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int vdpa_argv_handle(struct vdpa *vdpa, int argc, char **argv,
|
||||
char **p_mdev_bus_name,
|
||||
char **p_mdev_name)
|
||||
{
|
||||
unsigned int slashcount;
|
||||
char *str;
|
||||
|
||||
if (argc <= 0 || *argv == NULL) {
|
||||
fprintf(stderr,
|
||||
"vdpa identification (\"mgmtdev_bus_name/mgmtdev_name\") expected\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
str = *argv;
|
||||
slashcount = get_str_char_count(str, '/');
|
||||
if (slashcount > 1) {
|
||||
fprintf(stderr,
|
||||
"Wrong vdpa mgmtdev identification string format\n");
|
||||
fprintf(stderr, "Expected \"mgmtdev_bus_name/mgmtdev_name\"\n");
|
||||
fprintf(stderr, "Expected \"mgmtdev_name\"\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (slashcount) {
|
||||
case 0:
|
||||
*p_mdev_bus_name = NULL;
|
||||
*p_mdev_name = str;
|
||||
return 0;
|
||||
case 1:
|
||||
str_split_by_char(str, p_mdev_bus_name, p_mdev_name, '/');
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int vdpa_argv_str(struct vdpa *vdpa, int argc, char **argv,
|
||||
const char **p_str)
|
||||
{
|
||||
if (argc <= 0 || *argv == NULL) {
|
||||
fprintf(stderr, "String parameter expected\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
*p_str = *argv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vdpa_args_metadata {
|
||||
uint64_t o_flag;
|
||||
const char *err_msg;
|
||||
};
|
||||
|
||||
static const struct vdpa_args_metadata vdpa_args_required[] = {
|
||||
{VDPA_OPT_VDEV_MGMTDEV_HANDLE, "management device handle not set."},
|
||||
{VDPA_OPT_VDEV_NAME, "device name is not set."},
|
||||
{VDPA_OPT_VDEV_HANDLE, "device name is not set."},
|
||||
};
|
||||
|
||||
static int vdpa_args_finding_required_validate(uint64_t o_required,
|
||||
uint64_t o_found)
|
||||
{
|
||||
uint64_t o_flag;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vdpa_args_required); i++) {
|
||||
o_flag = vdpa_args_required[i].o_flag;
|
||||
if ((o_required & o_flag) && !(o_found & o_flag)) {
|
||||
fprintf(stderr, "%s\n", vdpa_args_required[i].err_msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (o_required & ~o_found) {
|
||||
fprintf(stderr,
|
||||
"BUG: unknown argument required but not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vdpa_opts_put(struct nlmsghdr *nlh, struct vdpa *vdpa)
|
||||
{
|
||||
struct vdpa_opts *opts = &vdpa->opts;
|
||||
|
||||
if ((opts->present & VDPA_OPT_MGMTDEV_HANDLE) ||
|
||||
(opts->present & VDPA_OPT_VDEV_MGMTDEV_HANDLE)) {
|
||||
if (opts->mdev_bus_name)
|
||||
mnl_attr_put_strz(nlh, VDPA_ATTR_MGMTDEV_BUS_NAME,
|
||||
opts->mdev_bus_name);
|
||||
mnl_attr_put_strz(nlh, VDPA_ATTR_MGMTDEV_DEV_NAME,
|
||||
opts->mdev_name);
|
||||
}
|
||||
if ((opts->present & VDPA_OPT_VDEV_NAME) ||
|
||||
(opts->present & VDPA_OPT_VDEV_HANDLE))
|
||||
mnl_attr_put_strz(nlh, VDPA_ATTR_DEV_NAME, opts->vdev_name);
|
||||
}
|
||||
|
||||
static int vdpa_argv_parse(struct vdpa *vdpa, int argc, char **argv,
|
||||
uint64_t o_required)
|
||||
{
|
||||
struct vdpa_opts *opts = &vdpa->opts;
|
||||
uint64_t o_all = o_required;
|
||||
uint64_t o_found = 0;
|
||||
int err;
|
||||
|
||||
if (o_required & VDPA_OPT_MGMTDEV_HANDLE) {
|
||||
err = vdpa_argv_handle(vdpa, argc, argv, &opts->mdev_bus_name,
|
||||
&opts->mdev_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
NEXT_ARG_FWD();
|
||||
o_found |= VDPA_OPT_MGMTDEV_HANDLE;
|
||||
} else if (o_required & VDPA_OPT_VDEV_HANDLE) {
|
||||
err = vdpa_argv_str(vdpa, argc, argv, &opts->vdev_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
NEXT_ARG_FWD();
|
||||
o_found |= VDPA_OPT_VDEV_HANDLE;
|
||||
}
|
||||
|
||||
while (NEXT_ARG_OK()) {
|
||||
if ((matches(*argv, "name") == 0) &&
|
||||
(o_all & VDPA_OPT_VDEV_NAME)) {
|
||||
const char *namestr;
|
||||
|
||||
NEXT_ARG_FWD();
|
||||
err = vdpa_argv_str(vdpa, argc, argv, &namestr);
|
||||
if (err)
|
||||
return err;
|
||||
opts->vdev_name = namestr;
|
||||
NEXT_ARG_FWD();
|
||||
o_found |= VDPA_OPT_VDEV_NAME;
|
||||
} else if ((matches(*argv, "mgmtdev") == 0) &&
|
||||
(o_all & VDPA_OPT_VDEV_MGMTDEV_HANDLE)) {
|
||||
NEXT_ARG_FWD();
|
||||
err = vdpa_argv_handle(vdpa, argc, argv,
|
||||
&opts->mdev_bus_name,
|
||||
&opts->mdev_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
NEXT_ARG_FWD();
|
||||
o_found |= VDPA_OPT_VDEV_MGMTDEV_HANDLE;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option \"%s\"\n", *argv);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
opts->present = o_found;
|
||||
|
||||
return vdpa_args_finding_required_validate(o_required, o_found);
|
||||
}
|
||||
|
||||
static int vdpa_argv_parse_put(struct nlmsghdr *nlh, struct vdpa *vdpa,
|
||||
int argc, char **argv,
|
||||
uint64_t o_required)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = vdpa_argv_parse(vdpa, argc, argv, o_required);
|
||||
if (err)
|
||||
return err;
|
||||
vdpa_opts_put(nlh, vdpa);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cmd_mgmtdev_help(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: vdpa mgmtdev show [ DEV ]\n");
|
||||
}
|
||||
|
||||
static void pr_out_handle_start(struct vdpa *vdpa, struct nlattr **tb)
|
||||
{
|
||||
const char *mdev_bus_name = NULL;
|
||||
const char *mdev_name;
|
||||
SPRINT_BUF(buf);
|
||||
|
||||
mdev_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_DEV_NAME]);
|
||||
if (tb[VDPA_ATTR_MGMTDEV_BUS_NAME]) {
|
||||
mdev_bus_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_BUS_NAME]);
|
||||
sprintf(buf, "%s/%s", mdev_bus_name, mdev_name);
|
||||
} else {
|
||||
sprintf(buf, "%s", mdev_name);
|
||||
}
|
||||
|
||||
if (vdpa->json_output)
|
||||
open_json_object(buf);
|
||||
else
|
||||
printf("%s: ", buf);
|
||||
}
|
||||
|
||||
static void pr_out_handle_end(struct vdpa *vdpa)
|
||||
{
|
||||
if (vdpa->json_output)
|
||||
close_json_object();
|
||||
else
|
||||
print_nl();
|
||||
}
|
||||
|
||||
static void __pr_out_vdev_handle_start(struct vdpa *vdpa, const char *vdev_name)
|
||||
{
|
||||
SPRINT_BUF(buf);
|
||||
|
||||
sprintf(buf, "%s", vdev_name);
|
||||
if (vdpa->json_output)
|
||||
open_json_object(buf);
|
||||
else
|
||||
printf("%s: ", buf);
|
||||
}
|
||||
|
||||
static void pr_out_vdev_handle_start(struct vdpa *vdpa, struct nlattr **tb)
|
||||
{
|
||||
const char *vdev_name;
|
||||
|
||||
vdev_name = mnl_attr_get_str(tb[VDPA_ATTR_DEV_NAME]);
|
||||
__pr_out_vdev_handle_start(vdpa, vdev_name);
|
||||
}
|
||||
|
||||
static void pr_out_vdev_handle_end(struct vdpa *vdpa)
|
||||
{
|
||||
if (vdpa->json_output)
|
||||
close_json_object();
|
||||
else
|
||||
print_nl();
|
||||
}
|
||||
|
||||
static struct str_num_map class_map[] = {
|
||||
{ .str = "net", .num = VIRTIO_ID_NET },
|
||||
{ .str = "block", .num = VIRTIO_ID_BLOCK },
|
||||
{ .str = NULL, },
|
||||
};
|
||||
|
||||
static const char *parse_class(int num)
|
||||
{
|
||||
const char *class;
|
||||
|
||||
class = str_map_lookup_uint(class_map, num);
|
||||
return class ? class : "< unknown class >";
|
||||
}
|
||||
|
||||
static void pr_out_mgmtdev_show(struct vdpa *vdpa, const struct nlmsghdr *nlh,
|
||||
struct nlattr **tb)
|
||||
{
|
||||
const char *class;
|
||||
unsigned int i;
|
||||
|
||||
pr_out_handle_start(vdpa, tb);
|
||||
|
||||
if (tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]) {
|
||||
uint64_t classes = mnl_attr_get_u64(tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]);
|
||||
|
||||
pr_out_array_start(vdpa, "supported_classes");
|
||||
|
||||
for (i = 1; i < 64; i++) {
|
||||
if ((classes & (1ULL << i)) == 0)
|
||||
continue;
|
||||
|
||||
class = parse_class(i);
|
||||
print_string(PRINT_ANY, NULL, " %s", class);
|
||||
}
|
||||
pr_out_array_end(vdpa);
|
||||
}
|
||||
|
||||
pr_out_handle_end(vdpa);
|
||||
}
|
||||
|
||||
static int cmd_mgmtdev_show_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
||||
struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
|
||||
struct vdpa *vdpa = data;
|
||||
|
||||
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
|
||||
|
||||
if (!tb[VDPA_ATTR_MGMTDEV_DEV_NAME])
|
||||
return MNL_CB_ERROR;
|
||||
|
||||
pr_out_mgmtdev_show(vdpa, nlh, tb);
|
||||
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int cmd_mgmtdev_show(struct vdpa *vdpa, int argc, char **argv)
|
||||
{
|
||||
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
if (argc == 0)
|
||||
flags |= NLM_F_DUMP;
|
||||
|
||||
nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_MGMTDEV_GET,
|
||||
flags);
|
||||
if (argc > 0) {
|
||||
err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
|
||||
VDPA_OPT_MGMTDEV_HANDLE);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
pr_out_section_start(vdpa, "mgmtdev");
|
||||
err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_mgmtdev_show_cb, vdpa);
|
||||
pr_out_section_end(vdpa);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_mgmtdev(struct vdpa *vdpa, int argc, char **argv)
|
||||
{
|
||||
if (!argc || matches(*argv, "help") == 0) {
|
||||
cmd_mgmtdev_help();
|
||||
return 0;
|
||||
} else if (matches(*argv, "show") == 0 ||
|
||||
matches(*argv, "list") == 0) {
|
||||
return cmd_mgmtdev_show(vdpa, argc - 1, argv + 1);
|
||||
}
|
||||
fprintf(stderr, "Command \"%s\" not found\n", *argv);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void cmd_dev_help(void)
|
||||
{
|
||||
fprintf(stderr, "Usage: vdpa dev show [ DEV ]\n");
|
||||
fprintf(stderr, " vdpa dev add name NAME mgmtdev MANAGEMENTDEV\n");
|
||||
fprintf(stderr, " vdpa dev del DEV\n");
|
||||
}
|
||||
|
||||
static const char *device_type_name(uint32_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case 0x1: return "network";
|
||||
case 0x2: return "block";
|
||||
default: return "<unknown type>";
|
||||
}
|
||||
}
|
||||
|
||||
static void pr_out_dev(struct vdpa *vdpa, struct nlattr **tb)
|
||||
{
|
||||
const char *mdev_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_DEV_NAME]);
|
||||
uint32_t device_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
|
||||
const char *mdev_bus_name = NULL;
|
||||
char mgmtdev_buf[128];
|
||||
|
||||
if (tb[VDPA_ATTR_MGMTDEV_BUS_NAME])
|
||||
mdev_bus_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_BUS_NAME]);
|
||||
|
||||
if (mdev_bus_name)
|
||||
sprintf(mgmtdev_buf, "%s/%s", mdev_bus_name, mdev_name);
|
||||
else
|
||||
sprintf(mgmtdev_buf, "%s", mdev_name);
|
||||
pr_out_vdev_handle_start(vdpa, tb);
|
||||
print_string(PRINT_ANY, "type", "type %s", device_type_name(device_id));
|
||||
print_string(PRINT_ANY, "mgmtdev", " mgmtdev %s", mgmtdev_buf);
|
||||
|
||||
if (tb[VDPA_ATTR_DEV_VENDOR_ID])
|
||||
print_uint(PRINT_ANY, "vendor_id", " vendor_id %u",
|
||||
mnl_attr_get_u32(tb[VDPA_ATTR_DEV_VENDOR_ID]));
|
||||
if (tb[VDPA_ATTR_DEV_MAX_VQS])
|
||||
print_uint(PRINT_ANY, "max_vqs", " max_vqs %u",
|
||||
mnl_attr_get_u32(tb[VDPA_ATTR_DEV_MAX_VQS]));
|
||||
if (tb[VDPA_ATTR_DEV_MAX_VQ_SIZE])
|
||||
print_uint(PRINT_ANY, "max_vq_size", " max_vq_size %u",
|
||||
mnl_attr_get_u16(tb[VDPA_ATTR_DEV_MAX_VQ_SIZE]));
|
||||
pr_out_vdev_handle_end(vdpa);
|
||||
}
|
||||
|
||||
static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
||||
struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
|
||||
struct vdpa *vdpa = data;
|
||||
|
||||
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
|
||||
if (!tb[VDPA_ATTR_MGMTDEV_DEV_NAME] ||
|
||||
!tb[VDPA_ATTR_DEV_NAME] || !tb[VDPA_ATTR_DEV_ID])
|
||||
return MNL_CB_ERROR;
|
||||
pr_out_dev(vdpa, tb);
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int cmd_dev_show(struct vdpa *vdpa, int argc, char **argv)
|
||||
{
|
||||
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
if (argc <= 0)
|
||||
flags |= NLM_F_DUMP;
|
||||
|
||||
nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_GET, flags);
|
||||
if (argc > 0) {
|
||||
err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
|
||||
VDPA_OPT_VDEV_HANDLE);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
pr_out_section_start(vdpa, "dev");
|
||||
err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_dev_show_cb, vdpa);
|
||||
pr_out_section_end(vdpa);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_dev_add(struct vdpa *vdpa, int argc, char **argv)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_NEW,
|
||||
NLM_F_REQUEST | NLM_F_ACK);
|
||||
err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
|
||||
VDPA_OPT_VDEV_MGMTDEV_HANDLE | VDPA_OPT_VDEV_NAME);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
|
||||
}
|
||||
|
||||
static int cmd_dev_del(struct vdpa *vdpa, int argc, char **argv)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_DEL,
|
||||
NLM_F_REQUEST | NLM_F_ACK);
|
||||
err = vdpa_argv_parse_put(nlh, vdpa, argc, argv, VDPA_OPT_VDEV_HANDLE);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
|
||||
}
|
||||
|
||||
static int cmd_dev(struct vdpa *vdpa, int argc, char **argv)
|
||||
{
|
||||
if (!argc)
|
||||
return cmd_dev_show(vdpa, argc - 1, argv + 1);
|
||||
|
||||
if (matches(*argv, "help") == 0) {
|
||||
cmd_dev_help();
|
||||
return 0;
|
||||
} else if (matches(*argv, "show") == 0 ||
|
||||
matches(*argv, "list") == 0) {
|
||||
return cmd_dev_show(vdpa, argc - 1, argv + 1);
|
||||
} else if (matches(*argv, "add") == 0) {
|
||||
return cmd_dev_add(vdpa, argc - 1, argv + 1);
|
||||
} else if (matches(*argv, "del") == 0) {
|
||||
return cmd_dev_del(vdpa, argc - 1, argv + 1);
|
||||
}
|
||||
fprintf(stderr, "Command \"%s\" not found\n", *argv);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static void help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: vdpa [ OPTIONS ] OBJECT { COMMAND | help }\n"
|
||||
"where OBJECT := { mgmtdev | dev }\n"
|
||||
" OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] }\n");
|
||||
}
|
||||
|
||||
static int vdpa_cmd(struct vdpa *vdpa, int argc, char **argv)
|
||||
{
|
||||
if (!argc || matches(*argv, "help") == 0) {
|
||||
help();
|
||||
return 0;
|
||||
} else if (matches(*argv, "mgmtdev") == 0) {
|
||||
return cmd_mgmtdev(vdpa, argc - 1, argv + 1);
|
||||
} else if (matches(*argv, "dev") == 0) {
|
||||
return cmd_dev(vdpa, argc - 1, argv + 1);
|
||||
}
|
||||
fprintf(stderr, "Object \"%s\" not found\n", *argv);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int vdpa_init(struct vdpa *vdpa)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = mnlu_gen_socket_open(&vdpa->nlg, VDPA_GENL_NAME,
|
||||
VDPA_GENL_VERSION);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to connect to vdpa Netlink\n");
|
||||
return -errno;
|
||||
}
|
||||
new_json_obj_plain(vdpa->json_output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vdpa_fini(struct vdpa *vdpa)
|
||||
{
|
||||
delete_json_obj_plain();
|
||||
mnlu_gen_socket_close(&vdpa->nlg);
|
||||
}
|
||||
|
||||
static struct vdpa *vdpa_alloc(void)
|
||||
{
|
||||
struct vdpa *vdpa = calloc(1, sizeof(struct vdpa));
|
||||
|
||||
if (!vdpa)
|
||||
return NULL;
|
||||
|
||||
vdpa->indent = alloc_indent_mem();
|
||||
if (!vdpa->indent)
|
||||
goto indent_err;
|
||||
|
||||
return vdpa;
|
||||
|
||||
indent_err:
|
||||
free(vdpa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void vdpa_free(struct vdpa *vdpa)
|
||||
{
|
||||
free_indent_mem(vdpa->indent);
|
||||
free(vdpa);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static const struct option long_options[] = {
|
||||
{ "Version", no_argument, NULL, 'V' },
|
||||
{ "json", no_argument, NULL, 'j' },
|
||||
{ "pretty", no_argument, NULL, 'p' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
struct vdpa *vdpa;
|
||||
int opt;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
vdpa = vdpa_alloc();
|
||||
if (!vdpa) {
|
||||
fprintf(stderr, "Failed to allocate memory for vdpa\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "Vjpsh", long_options, NULL)) >= 0) {
|
||||
switch (opt) {
|
||||
case 'V':
|
||||
printf("vdpa utility, iproute2-%s\n", version);
|
||||
ret = EXIT_SUCCESS;
|
||||
goto vdpa_free;
|
||||
case 'j':
|
||||
vdpa->json_output = true;
|
||||
break;
|
||||
case 'p':
|
||||
pretty = true;
|
||||
break;
|
||||
case 'h':
|
||||
help();
|
||||
ret = EXIT_SUCCESS;
|
||||
goto vdpa_free;
|
||||
default:
|
||||
fprintf(stderr, "Unknown option.\n");
|
||||
help();
|
||||
ret = EXIT_FAILURE;
|
||||
goto vdpa_free;
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
err = vdpa_init(vdpa);
|
||||
if (err) {
|
||||
ret = EXIT_FAILURE;
|
||||
goto vdpa_free;
|
||||
}
|
||||
|
||||
err = vdpa_cmd(vdpa, argc, argv);
|
||||
if (err) {
|
||||
ret = EXIT_FAILURE;
|
||||
goto vdpa_fini;
|
||||
}
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
vdpa_fini:
|
||||
vdpa_fini(vdpa);
|
||||
vdpa_free:
|
||||
vdpa_free(vdpa);
|
||||
return ret;
|
||||
}
|
||||
Loading…
Reference in New Issue