Commit 60658059 authored by Stephen Hemminger's avatar Stephen Hemminger

Merge branch 'master' into net-next

parents 346410bd 4bf138d6
...@@ -27,6 +27,12 @@ ...@@ -27,6 +27,12 @@
#define pr_err(args...) fprintf(stderr, ##args) #define pr_err(args...) fprintf(stderr, ##args)
#define pr_out(args...) fprintf(stdout, ##args) #define pr_out(args...) fprintf(stdout, ##args)
#define pr_out_sp(num, args...) \
do { \
int ret = fprintf(stdout, ##args); \
if (ret < num) \
fprintf(stdout, "%*s", num - ret, ""); \
} while (0)
static int _mnlg_socket_recv_run(struct mnlg_socket *nlg, static int _mnlg_socket_recv_run(struct mnlg_socket *nlg,
mnl_cb_t data_cb, void *data) mnl_cb_t data_cb, void *data)
...@@ -109,11 +115,42 @@ static void ifname_map_free(struct ifname_map *ifname_map) ...@@ -109,11 +115,42 @@ static void ifname_map_free(struct ifname_map *ifname_map)
free(ifname_map); free(ifname_map);
} }
#define BIT(nr) (1UL << (nr))
#define DL_OPT_HANDLE BIT(0)
#define DL_OPT_HANDLEP BIT(1)
#define DL_OPT_PORT_TYPE BIT(2)
#define DL_OPT_PORT_COUNT BIT(3)
#define DL_OPT_SB BIT(4)
#define DL_OPT_SB_POOL BIT(5)
#define DL_OPT_SB_SIZE BIT(6)
#define DL_OPT_SB_TYPE BIT(7)
#define DL_OPT_SB_THTYPE BIT(8)
#define DL_OPT_SB_TH BIT(9)
#define DL_OPT_SB_TC BIT(10)
struct dl_opts {
uint32_t present; /* flags of present items */
char *bus_name;
char *dev_name;
uint32_t port_index;
enum devlink_port_type port_type;
uint32_t port_count;
uint32_t sb_index;
uint16_t sb_pool_index;
uint32_t sb_pool_size;
enum devlink_sb_pool_type sb_pool_type;
enum devlink_sb_threshold_type sb_pool_thtype;
uint32_t sb_threshold;
uint16_t sb_tc_index;
};
struct dl { struct dl {
struct mnlg_socket *nlg; struct mnlg_socket *nlg;
struct list_head ifname_map_list; struct list_head ifname_map_list;
int argc; int argc;
char **argv; char **argv;
bool no_nice_names;
struct dl_opts opts;
}; };
static int dl_argc(struct dl *dl) static int dl_argc(struct dl *dl)
...@@ -208,6 +245,48 @@ static int attr_cb(const struct nlattr *attr, void *data) ...@@ -208,6 +245,48 @@ static int attr_cb(const struct nlattr *attr, void *data)
if (type == DEVLINK_ATTR_PORT_IBDEV_NAME && if (type == DEVLINK_ATTR_PORT_IBDEV_NAME &&
mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
return MNL_CB_ERROR; return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_INDEX &&
mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_SIZE &&
mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_INGRESS_POOL_COUNT &&
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_EGRESS_POOL_COUNT &&
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_INGRESS_TC_COUNT &&
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_EGRESS_TC_COUNT &&
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_POOL_INDEX &&
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_POOL_TYPE &&
mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_POOL_SIZE &&
mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE &&
mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_THRESHOLD &&
mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_TC_INDEX &&
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_OCC_CUR &&
mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
if (type == DEVLINK_ATTR_SB_OCC_MAX &&
mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
return MNL_CB_ERROR;
tb[type] = attr; tb[type] = attr;
return MNL_CB_OK; return MNL_CB_OK;
} }
...@@ -290,6 +369,23 @@ static int ifname_map_lookup(struct dl *dl, const char *ifname, ...@@ -290,6 +369,23 @@ static int ifname_map_lookup(struct dl *dl, const char *ifname,
return -ENOENT; return -ENOENT;
} }
static int ifname_map_rev_lookup(struct dl *dl, const char *bus_name,
const char *dev_name, uint32_t port_index,
char **p_ifname)
{
struct ifname_map *ifname_map;
list_for_each_entry(ifname_map, &dl->ifname_map_list, list) {
if (strcmp(bus_name, ifname_map->bus_name) == 0 &&
strcmp(dev_name, ifname_map->dev_name) == 0 &&
port_index == ifname_map->port_index) {
*p_ifname = ifname_map->ifname;
return 0;
}
}
return -ENOENT;
}
static unsigned int strslashcount(char *str) static unsigned int strslashcount(char *str)
{ {
unsigned int count = 0; unsigned int count = 0;
...@@ -329,11 +425,29 @@ static int strtouint32_t(const char *str, uint32_t *p_val) ...@@ -329,11 +425,29 @@ static int strtouint32_t(const char *str, uint32_t *p_val)
return 0; return 0;
} }
static int dl_argv_put_handle(struct nlmsghdr *nlh, struct dl *dl) static int strtouint16_t(const char *str, uint16_t *p_val)
{
char *endptr;
unsigned long int val;
val = strtoul(str, &endptr, 10);
if (endptr == str || *endptr != '\0')
return -EINVAL;
if (val > USHRT_MAX)
return -ERANGE;
*p_val = val;
return 0;
}
static int __dl_argv_handle(char *str, char **p_bus_name, char **p_dev_name)
{
strslashrsplit(str, p_bus_name, p_dev_name);
return 0;
}
static int dl_argv_handle(struct dl *dl, char **p_bus_name, char **p_dev_name)
{ {
char *str = dl_argv_next(dl); char *str = dl_argv_next(dl);
char *bus_name = bus_name;
char *dev_name = dev_name;
if (!str) { if (!str) {
pr_err("Devlink identification (\"bus_name/dev_name\") expected\n"); pr_err("Devlink identification (\"bus_name/dev_name\") expected\n");
...@@ -344,21 +458,48 @@ static int dl_argv_put_handle(struct nlmsghdr *nlh, struct dl *dl) ...@@ -344,21 +458,48 @@ static int dl_argv_put_handle(struct nlmsghdr *nlh, struct dl *dl)
pr_err("Expected \"bus_name/dev_name\".\n"); pr_err("Expected \"bus_name/dev_name\".\n");
return -EINVAL; return -EINVAL;
} }
return __dl_argv_handle(str, p_bus_name, p_dev_name);
}
static int __dl_argv_handle_port(char *str,
char **p_bus_name, char **p_dev_name,
uint32_t *p_port_index)
{
char *handlestr = handlestr;
char *portstr = portstr;
int err;
strslashrsplit(str, &handlestr, &portstr);
err = strtouint32_t(portstr, p_port_index);
if (err) {
pr_err("Port index \"%s\" is not a number or not within range\n",
portstr);
return err;
}
strslashrsplit(handlestr, p_bus_name, p_dev_name);
return 0;
}
strslashrsplit(str, &bus_name, &dev_name); static int __dl_argv_handle_port_ifname(struct dl *dl, char *str,
mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, bus_name); char **p_bus_name, char **p_dev_name,
mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, dev_name); uint32_t *p_port_index)
{
int err;
err = ifname_map_lookup(dl, str, p_bus_name, p_dev_name,
p_port_index);
if (err) {
pr_err("Netdevice \"%s\" not found\n", str);
return err;
}
return 0; return 0;
} }
static int dl_argv_put_handle_port(struct nlmsghdr *nlh, struct dl *dl) static int dl_argv_handle_port(struct dl *dl, char **p_bus_name,
char **p_dev_name, uint32_t *p_port_index)
{ {
char *str = dl_argv_next(dl); char *str = dl_argv_next(dl);
unsigned int slash_count; unsigned int slash_count;
char *bus_name = bus_name;
char *dev_name = dev_name;
uint32_t port_index = port_index;
int err;
if (!str) { if (!str) {
pr_err("Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\") expected.\n"); pr_err("Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\") expected.\n");
...@@ -370,30 +511,53 @@ static int dl_argv_put_handle_port(struct nlmsghdr *nlh, struct dl *dl) ...@@ -370,30 +511,53 @@ static int dl_argv_put_handle_port(struct nlmsghdr *nlh, struct dl *dl)
pr_err("Expected \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n"); pr_err("Expected \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n");
return -EINVAL; return -EINVAL;
} }
if (slash_count == 2) { if (slash_count == 2) {
char *handlestr = handlestr; return __dl_argv_handle_port(str, p_bus_name,
char *portstr = portstr; p_dev_name, p_port_index);
} else if (slash_count == 0) {
err = strslashrsplit(str, &handlestr, &portstr); return __dl_argv_handle_port_ifname(dl, str, p_bus_name,
err = strtouint32_t(portstr, &port_index); p_dev_name, p_port_index);
if (err) { }
pr_err("Port index \"%s\" is not a number or not within range\n", return 0;
portstr); }
static int dl_argv_handle_both(struct dl *dl, char **p_bus_name,
char **p_dev_name, uint32_t *p_port_index,
uint32_t *p_handle_bit)
{
char *str = dl_argv_next(dl);
unsigned int slash_count;
int err;
if (!str) {
pr_err("One of following identifications expected:\n"
"Devlink identification (\"bus_name/dev_name\")\n"
"Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\")\n");
return -EINVAL;
}
slash_count = strslashcount(str);
if (slash_count == 1) {
err = __dl_argv_handle(str, p_bus_name, p_dev_name);
if (err)
return err; return err;
} *p_handle_bit = DL_OPT_HANDLE;
strslashrsplit(handlestr, &bus_name, &dev_name); } else if (slash_count == 2) {
err = __dl_argv_handle_port(str, p_bus_name,
p_dev_name, p_port_index);
if (err)
return err;
*p_handle_bit = DL_OPT_HANDLEP;
} else if (slash_count == 0) { } else if (slash_count == 0) {
err = ifname_map_lookup(dl, str, &bus_name, &dev_name, err = __dl_argv_handle_port_ifname(dl, str, p_bus_name,
&port_index); p_dev_name, p_port_index);
if (err) { if (err)
pr_err("Netdevice \"%s\" not found\n", str);
return err; return err;
} *p_handle_bit = DL_OPT_HANDLEP;
} else {
pr_err("Wrong port identification string format.\n");
pr_err("Expected \"bus_name/dev_name\" or \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n");
return -EINVAL;
} }
mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, bus_name);
mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, dev_name);
mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_INDEX, port_index);
return 0; return 0;
} }
...@@ -415,6 +579,24 @@ static int dl_argv_uint32_t(struct dl *dl, uint32_t *p_val) ...@@ -415,6 +579,24 @@ static int dl_argv_uint32_t(struct dl *dl, uint32_t *p_val)
return 0; return 0;
} }
static int dl_argv_uint16_t(struct dl *dl, uint16_t *p_val)
{
char *str = dl_argv_next(dl);
int err;
if (!str) {
pr_err("Unsigned number argument expected\n");
return -EINVAL;
}
err = strtouint16_t(str, p_val);
if (err) {
pr_err("\"%s\" is not a number or not within range\n", str);
return err;
}
return 0;
}
static int dl_argv_str(struct dl *dl, const char **p_str) static int dl_argv_str(struct dl *dl, const char **p_str)
{ {
const char *str = dl_argv_next(dl); const char *str = dl_argv_next(dl);
...@@ -442,62 +624,155 @@ static int port_type_get(const char *typestr, enum devlink_port_type *p_type) ...@@ -442,62 +624,155 @@ static int port_type_get(const char *typestr, enum devlink_port_type *p_type)
return 0; return 0;
} }
#define BIT(nr) (1UL << (nr)) static int pool_type_get(const char *typestr, enum devlink_sb_pool_type *p_type)
#define DL_OPT_HANDLE BIT(0) {
#define DL_OPT_HANDLEP BIT(1) if (strcmp(typestr, "ingress") == 0) {
#define DL_OPT_PORT_TYPE BIT(2) *p_type = DEVLINK_SB_POOL_TYPE_INGRESS;
#define DL_OPT_PORT_COUNT BIT(3) } else if (strcmp(typestr, "egress") == 0) {
*p_type = DEVLINK_SB_POOL_TYPE_EGRESS;
} else {
pr_err("Unknown pool type \"%s\"\n", typestr);
return -EINVAL;
}
return 0;
}
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, static int threshold_type_get(const char *typestr,
uint32_t o_required, uint32_t o_optional) enum devlink_sb_threshold_type *p_type)
{
if (strcmp(typestr, "static") == 0) {
*p_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC;
} else if (strcmp(typestr, "dynamic") == 0) {
*p_type = DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC;
} else {
pr_err("Unknown threshold type \"%s\"\n", typestr);
return -EINVAL;
}
return 0;
}
static int dl_argv_parse(struct dl *dl, uint32_t o_required,
uint32_t o_optional)
{ {
struct dl_opts *opts = &dl->opts;
uint32_t o_all = o_required | o_optional; uint32_t o_all = o_required | o_optional;
uint32_t o_found = 0; uint32_t o_found = 0;
int err; int err;
if (o_required & DL_OPT_HANDLE) { if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) {
err = dl_argv_put_handle(nlh, dl); uint32_t handle_bit = handle_bit;
err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name,
&opts->port_index, &handle_bit);
if (err) if (err)
return err; return err;
o_found |= handle_bit;
} else if (o_required & DL_OPT_HANDLE) {
err = dl_argv_handle(dl, &opts->bus_name, &opts->dev_name);
if (err)
return err;
o_found |= DL_OPT_HANDLE;
} else if (o_required & DL_OPT_HANDLEP) { } else if (o_required & DL_OPT_HANDLEP) {
err = dl_argv_put_handle_port(nlh, dl); err = dl_argv_handle_port(dl, &opts->bus_name, &opts->dev_name,
&opts->port_index);
if (err) if (err)
return err; return err;
o_found |= DL_OPT_HANDLEP;
} }
while (dl_argc(dl)) { while (dl_argc(dl)) {
if (dl_argv_match(dl, "type") && if (dl_argv_match(dl, "type") &&
(o_all & DL_OPT_PORT_TYPE)) { (o_all & DL_OPT_PORT_TYPE)) {
enum devlink_port_type port_type;
const char *typestr; const char *typestr;
dl_arg_inc(dl); dl_arg_inc(dl);
err = dl_argv_str(dl, &typestr); err = dl_argv_str(dl, &typestr);
if (err) if (err)
return err; return err;
err = port_type_get(typestr, &port_type); err = port_type_get(typestr, &opts->port_type);
if (err) if (err)
return err; return err;
mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_TYPE,
port_type);
o_found |= DL_OPT_PORT_TYPE; o_found |= DL_OPT_PORT_TYPE;
} else if (dl_argv_match(dl, "count") && } else if (dl_argv_match(dl, "count") &&
(o_all & DL_OPT_PORT_COUNT)) { (o_all & DL_OPT_PORT_COUNT)) {
uint32_t count;
dl_arg_inc(dl); dl_arg_inc(dl);
err = dl_argv_uint32_t(dl, &count); err = dl_argv_uint32_t(dl, &opts->port_count);
if (err) if (err)
return err; return err;
mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_SPLIT_COUNT,
count);
o_found |= DL_OPT_PORT_COUNT; o_found |= DL_OPT_PORT_COUNT;
} else if (dl_argv_match(dl, "sb") &&
(o_all & DL_OPT_SB)) {
dl_arg_inc(dl);
err = dl_argv_uint32_t(dl, &opts->sb_index);
if (err)
return err;
o_found |= DL_OPT_SB;
} else if (dl_argv_match(dl, "pool") &&
(o_all & DL_OPT_SB_POOL)) {
dl_arg_inc(dl);
err = dl_argv_uint16_t(dl, &opts->sb_pool_index);
if (err)
return err;
o_found |= DL_OPT_SB_POOL;
} else if (dl_argv_match(dl, "size") &&
(o_all & DL_OPT_SB_SIZE)) {
dl_arg_inc(dl);
err = dl_argv_uint32_t(dl, &opts->sb_pool_size);
if (err)
return err;
o_found |= DL_OPT_SB_SIZE;
} else if (dl_argv_match(dl, "type") &&
(o_all & DL_OPT_SB_TYPE)) {
const char *typestr;
dl_arg_inc(dl);
err = dl_argv_str(dl, &typestr);
if (err)
return err;
err = pool_type_get(typestr, &opts->sb_pool_type);
if (err)
return err;
o_found |= DL_OPT_SB_TYPE;
} else if (dl_argv_match(dl, "thtype") &&
(o_all & DL_OPT_SB_THTYPE)) {
const char *typestr;
dl_arg_inc(dl);
err = dl_argv_str(dl, &typestr);
if (err)
return err;
err = threshold_type_get(typestr,
&opts->sb_pool_thtype);
if (err)
return err;
o_found |= DL_OPT_SB_THTYPE;
} else if (dl_argv_match(dl, "th") &&
(o_all & DL_OPT_SB_TH)) {
dl_arg_inc(dl);
err = dl_argv_uint32_t(dl, &opts->sb_threshold);
if (err)
return err;
o_found |= DL_OPT_SB_TH;
} else if (dl_argv_match(dl, "tc") &&
(o_all & DL_OPT_SB_TC)) {
dl_arg_inc(dl);
err = dl_argv_uint16_t(dl, &opts->sb_tc_index);
if (err)
return err;
o_found |= DL_OPT_SB_TC;
} else { } else {
pr_err("Unknown option \"%s\"\n", dl_argv(dl)); pr_err("Unknown option \"%s\"\n", dl_argv(dl));
return -EINVAL; return -EINVAL;
} }
} }
opts->present = o_found;
if ((o_optional & DL_OPT_SB) && !(o_found & DL_OPT_SB)) {
opts->sb_index = 0;
opts->present |= DL_OPT_SB;
}
if ((o_required & DL_OPT_PORT_TYPE) && !(o_found & DL_OPT_PORT_TYPE)) { if ((o_required & DL_OPT_PORT_TYPE) && !(o_found & DL_OPT_PORT_TYPE)) {
pr_err("Port type option expected.\n"); pr_err("Port type option expected.\n");
return -EINVAL; return -EINVAL;
...@@ -509,20 +784,192 @@ static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, ...@@ -509,20 +784,192 @@ static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
return -EINVAL; return -EINVAL;
} }
if ((o_required & DL_OPT_SB_POOL) && !(o_found & DL_OPT_SB_POOL)) {
pr_err("Pool index option expected.\n");
return -EINVAL;
}
if ((o_required & DL_OPT_SB_SIZE) && !(o_found & DL_OPT_SB_SIZE)) {
pr_err("Pool size option expected.\n");
return -EINVAL;
}
if ((o_required & DL_OPT_SB_TYPE) && !(o_found & DL_OPT_SB_TYPE)) {
pr_err("Pool type option expected.\n");
return -EINVAL;
}
if ((o_required & DL_OPT_SB_THTYPE) && !(o_found & DL_OPT_SB_THTYPE)) {
pr_err("Pool threshold type option expected.\n");
return -EINVAL;
}
if ((o_required & DL_OPT_SB_TH) && !(o_found & DL_OPT_SB_TH)) {
pr_err("Threshold option expected.\n");
return -EINVAL;
}
if ((o_required & DL_OPT_SB_TC) && !(o_found & DL_OPT_SB_TC)) {
pr_err("TC index option expected.\n");
return -EINVAL;
}
return 0;
}
static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
{
struct dl_opts *opts = &dl->opts;
if (opts->present & DL_OPT_HANDLE) {
mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name);
mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name);
} else if (opts->present & DL_OPT_HANDLEP) {
mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, opts->bus_name);
mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, opts->dev_name);
mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_INDEX,
opts->port_index);
}
if (opts->present & DL_OPT_PORT_TYPE)
mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_TYPE,
opts->port_type);
if (opts->present & DL_OPT_PORT_COUNT)
mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_SPLIT_COUNT,
opts->port_count);
if (opts->present & DL_OPT_SB)
mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_INDEX,
opts->sb_index);
if (opts->present & DL_OPT_SB_POOL)
mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_POOL_INDEX,
opts->sb_pool_index);
if (opts->present & DL_OPT_SB_SIZE)
mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_POOL_SIZE,
opts->sb_pool_size);
if (opts->present & DL_OPT_SB_TYPE)
mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_TYPE,
opts->sb_pool_type);
if (opts->present & DL_OPT_SB_THTYPE)
mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
opts->sb_pool_thtype);
if (opts->present & DL_OPT_SB_TH)
mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_THRESHOLD,
opts->sb_threshold);
if (opts->present & DL_OPT_SB_TC)
mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_TC_INDEX,
opts->sb_tc_index);
}
static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
uint32_t o_required, uint32_t o_optional)
{
int err;
err = dl_argv_parse(dl, o_required, o_optional);
if (err)
return err;
dl_opts_put(nlh, dl);
return 0; return 0;
} }
static bool dl_dump_filter(struct dl *dl, struct nlattr **tb)
{
struct dl_opts *opts = &dl->opts;
struct nlattr *attr_bus_name = tb[DEVLINK_ATTR_BUS_NAME];
struct nlattr *attr_dev_name = tb[DEVLINK_ATTR_DEV_NAME];
struct nlattr *attr_port_index = tb[DEVLINK_ATTR_PORT_INDEX];
struct nlattr *attr_sb_index = tb[DEVLINK_ATTR_SB_INDEX];
if (opts->present & DL_OPT_HANDLE &&
attr_bus_name && attr_dev_name) {
const char *bus_name = mnl_attr_get_str(attr_bus_name);
const char *dev_name = mnl_attr_get_str(attr_dev_name);
if (strcmp(bus_name, opts->bus_name) != 0 ||
strcmp(dev_name, opts->dev_name) != 0)
return false;
}
if (opts->present & DL_OPT_HANDLEP &&
attr_bus_name && attr_dev_name && attr_port_index) {
const char *bus_name = mnl_attr_get_str(attr_bus_name);
const char *dev_name = mnl_attr_get_str(attr_dev_name);
uint32_t port_index = mnl_attr_get_u32(attr_port_index);
if (strcmp(bus_name, opts->bus_name) != 0 ||
strcmp(dev_name, opts->dev_name) != 0 ||
port_index != opts->port_index)
return false;
}
if (opts->present & DL_OPT_SB && attr_sb_index) {
uint32_t sb_index = mnl_attr_get_u32(attr_sb_index);
if (sb_index != opts->sb_index)
return false;
}
return true;
}
static void cmd_dev_help(void) static void cmd_dev_help(void)
{ {
pr_out("Usage: devlink dev show [ DEV ]\n"); pr_out("Usage: devlink dev show [ DEV ]\n");
} }
static void __pr_out_handle(const char *bus_name, const char *dev_name)
{
pr_out("%s/%s", bus_name, dev_name);
}
static void pr_out_handle(struct nlattr **tb) static void pr_out_handle(struct nlattr **tb)
{ {
pr_out("%s/%s", mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]), __pr_out_handle(mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]),
mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME])); mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]));
} }
static void __pr_out_port_handle(const char *bus_name, const char *dev_name,
uint32_t port_index)
{
__pr_out_handle(bus_name, dev_name);
pr_out("/%d", port_index);
}
static void pr_out_port_handle(struct nlattr **tb)
{
__pr_out_port_handle(mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]),
mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]),
mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]));
}
static void __pr_out_port_handle_nice(struct dl *dl, const char *bus_name,
const char *dev_name, uint32_t port_index)
{
char *ifname;
int err;
if (dl->no_nice_names)
goto no_nice_names;
err = ifname_map_rev_lookup(dl, bus_name, dev_name,
port_index, &ifname);
if (err)
goto no_nice_names;
pr_out("%s", ifname);
return;
no_nice_names:
__pr_out_port_handle(bus_name, dev_name, port_index);
}
static void pr_out_port_handle_nice(struct dl *dl, struct nlattr **tb)
{
const char *bus_name;
const char *dev_name;
uint32_t port_index;
bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]);
dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]);
port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
__pr_out_port_handle_nice(dl, bus_name, dev_name, port_index);
}
static void pr_out_dev(struct nlattr **tb) static void pr_out_dev(struct nlattr **tb)
{ {
pr_out_handle(tb); pr_out_handle(tb);
...@@ -578,9 +1025,9 @@ static int cmd_dev(struct dl *dl) ...@@ -578,9 +1025,9 @@ static int cmd_dev(struct dl *dl)
static void cmd_port_help(void) static void cmd_port_help(void)
{ {
pr_out("Usage: devlink port show [ DEV/PORT_INDEX ]\n"); pr_out("Usage: devlink port show [ DEV/PORT_INDEX ]\n");
pr_out(" dl port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n"); pr_out(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n");
pr_out(" dl port split DEV/PORT_INDEX count COUNT\n"); pr_out(" devlink port split DEV/PORT_INDEX count COUNT\n");
pr_out(" dl port unsplit DEV/PORT_INDEX\n"); pr_out(" devlink port unsplit DEV/PORT_INDEX\n");
} }
static const char *port_type_name(uint32_t type) static const char *port_type_name(uint32_t type)
...@@ -599,8 +1046,8 @@ static void pr_out_port(struct nlattr **tb) ...@@ -599,8 +1046,8 @@ static void pr_out_port(struct nlattr **tb)
struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE]; struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE];
struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE]; struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE];
pr_out_handle(tb); pr_out_port_handle(tb);
pr_out("/%d:", mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX])); pr_out(":");
if (pt_attr) { if (pt_attr) {
uint16_t port_type = mnl_attr_get_u16(pt_attr); uint16_t port_type = mnl_attr_get_u16(pt_attr);
...@@ -725,19 +1172,723 @@ static int cmd_port(struct dl *dl) ...@@ -725,19 +1172,723 @@ static int cmd_port(struct dl *dl)
return -ENOENT; return -ENOENT;
} }
static const char *cmd_name(uint8_t cmd) static void cmd_sb_help(void)
{ {
switch (cmd) { pr_out("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n");
case DEVLINK_CMD_UNSPEC: return "unspec"; pr_out(" devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n");
case DEVLINK_CMD_GET: return "get"; pr_out(" devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n");
case DEVLINK_CMD_SET: return "set"; pr_out(" size POOL_SIZE thtype { static | dynamic }\n");
case DEVLINK_CMD_NEW: return "new"; pr_out(" devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n");
case DEVLINK_CMD_DEL: return "del"; pr_out(" pool POOL_INDEX ]\n");
case DEVLINK_CMD_PORT_GET: return "get"; pr_out(" devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n");
case DEVLINK_CMD_PORT_SET: return "set"; pr_out(" pool POOL_INDEX th THRESHOLD\n");
case DEVLINK_CMD_PORT_NEW: return "net"; pr_out(" devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n");
case DEVLINK_CMD_PORT_DEL: return "del"; pr_out(" type { ingress | egress } ]\n");
default: return "<unknown cmd>"; pr_out(" devlink sb tc bind set DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n");
pr_out(" type { ingress | egress } pool POOL_INDEX\n");
pr_out(" th THRESHOLD\n");
pr_out(" devlink sb occupancy show { DEV | DEV/PORT_INDEX } [ sb SB_INDEX ]\n");
pr_out(" devlink sb occupancy snapshot DEV [ sb SB_INDEX ]\n");
pr_out(" devlink sb occupancy clearmax DEV [ sb SB_INDEX ]\n");
}
static void pr_out_sb(struct nlattr **tb)
{
pr_out_handle(tb);
pr_out(": sb %u size %u ing_pools %u eg_pools %u ing_tcs %u eg_tcs %u\n",
mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE]),
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT]),
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT]),
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT]),
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT]));
}
static int cmd_sb_show_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
!tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_SIZE] ||
!tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] ||
!tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] ||
!tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT] ||
!tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])
return MNL_CB_ERROR;
pr_out_sb(tb);
return MNL_CB_OK;
}
static int cmd_sb_show(struct dl *dl)
{
struct nlmsghdr *nlh;
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
int err;
if (dl_argc(dl) == 0)
flags |= NLM_F_DUMP;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_GET, flags);
if (dl_argc(dl) > 0) {
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
if (err)
return err;
}
return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_show_cb, NULL);
}
static const char *pool_type_name(uint8_t type)
{
switch (type) {
case DEVLINK_SB_POOL_TYPE_INGRESS: return "ingress";
case DEVLINK_SB_POOL_TYPE_EGRESS: return "egress";
default: return "<unknown type>";
}
}
static const char *threshold_type_name(uint8_t type)
{
switch (type) {
case DEVLINK_SB_THRESHOLD_TYPE_STATIC: return "static";
case DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC: return "dynamic";
default: return "<unknown type>";
}
}
static void pr_out_sb_pool(struct nlattr **tb)
{
pr_out_handle(tb);
pr_out(": sb %u pool %u type %s size %u thtype %s\n",
mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])),
mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE]),
threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])));
}
static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
!tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
!tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] ||
!tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])
return MNL_CB_ERROR;
pr_out_sb_pool(tb);
return MNL_CB_OK;
}
static int cmd_sb_pool_show(struct dl *dl)
{
struct nlmsghdr *nlh;
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
int err;
if (dl_argc(dl) == 0)
flags |= NLM_F_DUMP;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_GET, flags);
if (dl_argc(dl) > 0) {
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL,
DL_OPT_SB);
if (err)
return err;
}
return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, NULL);
}
static int cmd_sb_pool_set(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_SET,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL |
DL_OPT_SB_SIZE | DL_OPT_SB_THTYPE, DL_OPT_SB);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
}
static int cmd_sb_pool(struct dl *dl)
{
if (dl_argv_match(dl, "help")) {
cmd_sb_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_sb_pool_show(dl);
} else if (dl_argv_match(dl, "set")) {
dl_arg_inc(dl);
return cmd_sb_pool_set(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb)
{
pr_out_port_handle_nice(dl, tb);
pr_out(": sb %u pool %u threshold %u\n",
mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
}
static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data)
{
struct dl *dl = data;
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
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_SB_INDEX] ||
!tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
return MNL_CB_ERROR;
pr_out_sb_port_pool(dl, tb);
return MNL_CB_OK;
}
static int cmd_sb_port_pool_show(struct dl *dl)
{
struct nlmsghdr *nlh;
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
int err;
if (dl_argc(dl) == 0)
flags |= NLM_F_DUMP;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
if (dl_argc(dl) > 0) {
err = dl_argv_parse_put(nlh, dl,
DL_OPT_HANDLEP | DL_OPT_SB_POOL,
DL_OPT_SB);
if (err)
return err;
}
return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl);
}
static int cmd_sb_port_pool_set(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_SET,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_POOL |
DL_OPT_SB_TH, DL_OPT_SB);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
}
static int cmd_sb_port_pool(struct dl *dl)
{
if (dl_argv_match(dl, "help")) {
cmd_sb_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_sb_port_pool_show(dl);
} else if (dl_argv_match(dl, "set")) {
dl_arg_inc(dl);
return cmd_sb_port_pool_set(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
static int cmd_sb_port(struct dl *dl)
{
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
cmd_sb_help();
return 0;
} else if (dl_argv_match(dl, "pool")) {
dl_arg_inc(dl);
return cmd_sb_port_pool(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb)
{
pr_out_port_handle_nice(dl, tb);
pr_out(": sb %u tc %u type %s pool %u threshold %u\n",
mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]),
pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])),
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
}
static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data)
{
struct dl *dl = data;
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
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_SB_INDEX] ||
!tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
!tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
return MNL_CB_ERROR;
pr_out_sb_tc_bind(dl, tb);
return MNL_CB_OK;
}
static int cmd_sb_tc_bind_show(struct dl *dl)
{
struct nlmsghdr *nlh;
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
int err;
if (dl_argc(dl) == 0)
flags |= NLM_F_DUMP;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
if (dl_argc(dl) > 0) {
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
DL_OPT_SB_TYPE, DL_OPT_SB);
if (err)
return err;
}
return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl);
}
static int cmd_sb_tc_bind_set(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_SET,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
DL_OPT_SB_TYPE | DL_OPT_SB_POOL | DL_OPT_SB_TH,
DL_OPT_SB);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
}
static int cmd_sb_tc_bind(struct dl *dl)
{
if (dl_argv_match(dl, "help")) {
cmd_sb_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_sb_tc_bind_show(dl);
} else if (dl_argv_match(dl, "set")) {
dl_arg_inc(dl);
return cmd_sb_tc_bind_set(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
static int cmd_sb_tc(struct dl *dl)
{
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
cmd_sb_help();
return 0;
} else if (dl_argv_match(dl, "bind")) {
dl_arg_inc(dl);
return cmd_sb_tc_bind(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
struct occ_item {
struct list_head list;
uint32_t index;
uint32_t cur;
uint32_t max;
uint32_t bound_pool_index;
};
struct occ_port {
struct list_head list;
char *bus_name;
char *dev_name;
uint32_t port_index;
uint32_t sb_index;
struct list_head pool_list;
struct list_head ing_tc_list;
struct list_head eg_tc_list;
};
struct occ_show {
struct dl *dl;
int err;
struct list_head port_list;
};
static struct occ_item *occ_item_alloc(void)
{
return calloc(1, sizeof(struct occ_item));
}
static void occ_item_free(struct occ_item *occ_item)
{
free(occ_item);
}
static struct occ_port *occ_port_alloc(uint32_t port_index)
{
struct occ_port *occ_port;
occ_port = calloc(1, sizeof(*occ_port));
if (!occ_port)
return NULL;
occ_port->port_index = port_index;
INIT_LIST_HEAD(&occ_port->pool_list);
INIT_LIST_HEAD(&occ_port->ing_tc_list);
INIT_LIST_HEAD(&occ_port->eg_tc_list);
return occ_port;
}
static void occ_port_free(struct occ_port *occ_port)
{
struct occ_item *occ_item, *tmp;
list_for_each_entry_safe(occ_item, tmp, &occ_port->pool_list, list)
occ_item_free(occ_item);
list_for_each_entry_safe(occ_item, tmp, &occ_port->ing_tc_list, list)
occ_item_free(occ_item);
list_for_each_entry_safe(occ_item, tmp, &occ_port->eg_tc_list, list)
occ_item_free(occ_item);
}
static struct occ_show *occ_show_alloc(struct dl *dl)
{
struct occ_show *occ_show;
occ_show = calloc(1, sizeof(*occ_show));
if (!occ_show)
return NULL;
occ_show->dl = dl;
INIT_LIST_HEAD(&occ_show->port_list);
return occ_show;
}
static void occ_show_free(struct occ_show *occ_show)
{
struct occ_port *occ_port, *tmp;
list_for_each_entry_safe(occ_port, tmp, &occ_show->port_list, list)
occ_port_free(occ_port);
}
static struct occ_port *occ_port_get(struct occ_show *occ_show,
struct nlattr **tb)
{
struct occ_port *occ_port;
uint32_t port_index;
port_index = mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX]);
list_for_each_entry_reverse(occ_port, &occ_show->port_list, list) {
if (occ_port->port_index == port_index)
return occ_port;
}
occ_port = occ_port_alloc(port_index);
if (!occ_port)
return NULL;
list_add_tail(&occ_port->list, &occ_show->port_list);
return occ_port;
}
static void pr_out_occ_show_item_list(const char *label, struct list_head *list,
bool bound_pool)
{
struct occ_item *occ_item;
int i = 1;
pr_out_sp(7, " %s:", label);
list_for_each_entry(occ_item, list, list) {
if ((i - 1) % 4 == 0 && i != 1)
pr_out_sp(7, " ");
if (bound_pool)
pr_out_sp(7, "%2u(%u):", occ_item->index,
occ_item->bound_pool_index);
else
pr_out_sp(7, "%2u:", occ_item->index);
pr_out_sp(15, "%7u/%u", occ_item->cur, occ_item->max);
if (i++ % 4 == 0)
pr_out("\n");
}
if ((i - 1) % 4 != 0)
pr_out("\n");
}
static void pr_out_occ_show_port(struct occ_port *occ_port)
{
pr_out_occ_show_item_list("pool", &occ_port->pool_list, false);
pr_out_occ_show_item_list("itc", &occ_port->ing_tc_list, true);
pr_out_occ_show_item_list("etc", &occ_port->eg_tc_list, true);
}
static void pr_out_occ_show(struct occ_show *occ_show)
{
struct dl *dl = occ_show->dl;
struct dl_opts *opts = &dl->opts;
struct occ_port *occ_port;
list_for_each_entry(occ_port, &occ_show->port_list, list) {
__pr_out_port_handle_nice(dl, opts->bus_name, opts->dev_name,
occ_port->port_index);
pr_out(":\n");
pr_out_occ_show_port(occ_port);
}
}
static void cmd_sb_occ_port_pool_process(struct occ_show *occ_show,
struct nlattr **tb)
{
struct occ_port *occ_port;
struct occ_item *occ_item;
if (occ_show->err || !dl_dump_filter(occ_show->dl, tb))
return;
occ_port = occ_port_get(occ_show, tb);
if (!occ_port) {
occ_show->err = -ENOMEM;
return;
}
occ_item = occ_item_alloc();
if (!occ_item) {
occ_show->err = -ENOMEM;
return;
}
occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]);
occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]);
occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]);
list_add_tail(&occ_item->list, &occ_port->pool_list);
}
static int cmd_sb_occ_port_pool_process_cb(const struct nlmsghdr *nlh, void *data)
{
struct occ_show *occ_show = data;
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
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_SB_INDEX] ||
!tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
!tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX])
return MNL_CB_ERROR;
cmd_sb_occ_port_pool_process(occ_show, tb);
return MNL_CB_OK;
}
static void cmd_sb_occ_tc_pool_process(struct occ_show *occ_show,
struct nlattr **tb)
{
struct occ_port *occ_port;
struct occ_item *occ_item;
uint8_t pool_type;
if (occ_show->err || !dl_dump_filter(occ_show->dl, tb))
return;
occ_port = occ_port_get(occ_show, tb);
if (!occ_port) {
occ_show->err = -ENOMEM;
return;
}
occ_item = occ_item_alloc();
if (!occ_item) {
occ_show->err = -ENOMEM;
return;
}
occ_item->index = mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]);
occ_item->cur = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_CUR]);
occ_item->max = mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_OCC_MAX]);
occ_item->bound_pool_index =
mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]);
pool_type = mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE]);
if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
list_add_tail(&occ_item->list, &occ_port->ing_tc_list);
else if (pool_type == DEVLINK_SB_POOL_TYPE_EGRESS)
list_add_tail(&occ_item->list, &occ_port->eg_tc_list);
else
occ_item_free(occ_item);
}
static int cmd_sb_occ_tc_pool_process_cb(const struct nlmsghdr *nlh, void *data)
{
struct occ_show *occ_show = data;
struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
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_SB_INDEX] ||
!tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
!tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
!tb[DEVLINK_ATTR_SB_OCC_CUR] || !tb[DEVLINK_ATTR_SB_OCC_MAX])
return MNL_CB_ERROR;
cmd_sb_occ_tc_pool_process(occ_show, tb);
return MNL_CB_OK;
}
static int cmd_sb_occ_show(struct dl *dl)
{
struct nlmsghdr *nlh;
struct occ_show *occ_show;
uint16_t flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
int err;
err = dl_argv_parse(dl, DL_OPT_HANDLE | DL_OPT_HANDLEP, DL_OPT_SB);
if (err)
return err;
occ_show = occ_show_alloc(dl);
if (!occ_show)
return -ENOMEM;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
err = _mnlg_socket_sndrcv(dl->nlg, nlh,
cmd_sb_occ_port_pool_process_cb, occ_show);
if (err)
goto out;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
err = _mnlg_socket_sndrcv(dl->nlg, nlh,
cmd_sb_occ_tc_pool_process_cb, occ_show);
if (err)
goto out;
pr_out_occ_show(occ_show);
out:
occ_show_free(occ_show);
return err;
}
static int cmd_sb_occ_snapshot(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_SNAPSHOT,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
}
static int cmd_sb_occ_clearmax(struct dl *dl)
{
struct nlmsghdr *nlh;
int err;
nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_OCC_MAX_CLEAR,
NLM_F_REQUEST | NLM_F_ACK);
err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
if (err)
return err;
return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
}
static int cmd_sb_occ(struct dl *dl)
{
if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
cmd_sb_help();
return 0;
} else if (dl_argv_match(dl, "show") ||
dl_argv_match(dl, "list")) {
dl_arg_inc(dl);
return cmd_sb_occ_show(dl);
} else if (dl_argv_match(dl, "snapshot")) {
dl_arg_inc(dl);
return cmd_sb_occ_snapshot(dl);
} else if (dl_argv_match(dl, "clearmax")) {
dl_arg_inc(dl);
return cmd_sb_occ_clearmax(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
static int cmd_sb(struct dl *dl)
{
if (dl_argv_match(dl, "help")) {
cmd_sb_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_sb_show(dl);
} else if (dl_argv_match(dl, "pool")) {
dl_arg_inc(dl);
return cmd_sb_pool(dl);
} else if (dl_argv_match(dl, "port")) {
dl_arg_inc(dl);
return cmd_sb_port(dl);
} else if (dl_argv_match(dl, "tc")) {
dl_arg_inc(dl);
return cmd_sb_tc(dl);
} else if (dl_argv_match(dl, "occupancy")) {
dl_arg_inc(dl);
return cmd_sb_occ(dl);
}
pr_err("Command \"%s\" not found\n", dl_argv(dl));
return -ENOENT;
}
static const char *cmd_name(uint8_t cmd)
{
switch (cmd) {
case DEVLINK_CMD_UNSPEC: return "unspec";
case DEVLINK_CMD_GET: return "get";
case DEVLINK_CMD_SET: return "set";
case DEVLINK_CMD_NEW: return "new";
case DEVLINK_CMD_DEL: return "del";
case DEVLINK_CMD_PORT_GET: return "get";
case DEVLINK_CMD_PORT_SET: return "set";
case DEVLINK_CMD_PORT_NEW: return "net";
case DEVLINK_CMD_PORT_DEL: return "del";
default: return "<unknown cmd>";
} }
} }
...@@ -860,8 +2011,8 @@ static int cmd_mon(struct dl *dl) ...@@ -860,8 +2011,8 @@ static int cmd_mon(struct dl *dl)
static void help(void) static void help(void)
{ {
pr_out("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" pr_out("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
"where OBJECT := { dev | port | monitor }\n" "where OBJECT := { dev | port | sb | monitor }\n"
" OPTIONS := { -V[ersion] }\n"); " OPTIONS := { -V[ersion] | -n[no-nice-names] }\n");
} }
static int dl_cmd(struct dl *dl) static int dl_cmd(struct dl *dl)
...@@ -875,6 +2026,9 @@ static int dl_cmd(struct dl *dl) ...@@ -875,6 +2026,9 @@ static int dl_cmd(struct dl *dl)
} else if (dl_argv_match(dl, "port")) { } else if (dl_argv_match(dl, "port")) {
dl_arg_inc(dl); dl_arg_inc(dl);
return cmd_port(dl); return cmd_port(dl);
} else if (dl_argv_match(dl, "sb")) {
dl_arg_inc(dl);
return cmd_sb(dl);
} else if (dl_argv_match(dl, "monitor")) { } else if (dl_argv_match(dl, "monitor")) {
dl_arg_inc(dl); dl_arg_inc(dl);
return cmd_mon(dl); return cmd_mon(dl);
...@@ -933,6 +2087,7 @@ int main(int argc, char **argv) ...@@ -933,6 +2087,7 @@ int main(int argc, char **argv)
{ {
static const struct option long_options[] = { static const struct option long_options[] = {
{ "Version", no_argument, NULL, 'V' }, { "Version", no_argument, NULL, 'V' },
{ "no-nice-names", no_argument, NULL, 'n' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
struct dl *dl; struct dl *dl;
...@@ -940,13 +2095,22 @@ int main(int argc, char **argv) ...@@ -940,13 +2095,22 @@ int main(int argc, char **argv)
int err; int err;
int ret; int ret;
while ((opt = getopt_long(argc, argv, "V", dl = dl_alloc();
if (!dl) {
pr_err("Failed to allocate memory for devlink\n");
return EXIT_FAILURE;
}
while ((opt = getopt_long(argc, argv, "Vn",
long_options, NULL)) >= 0) { long_options, NULL)) >= 0) {
switch (opt) { switch (opt) {
case 'V': case 'V':
printf("devlink utility, iproute2-ss%s\n", SNAPSHOT); printf("devlink utility, iproute2-ss%s\n", SNAPSHOT);
return EXIT_SUCCESS; return EXIT_SUCCESS;
case 'n':
dl->no_nice_names = true;
break;
default: default:
pr_err("Unknown option.\n"); pr_err("Unknown option.\n");
help(); help();
...@@ -957,12 +2121,6 @@ int main(int argc, char **argv) ...@@ -957,12 +2121,6 @@ int main(int argc, char **argv)
argc -= optind; argc -= optind;
argv += optind; argv += optind;
dl = dl_alloc();
if (!dl) {
pr_err("Failed to allocate memory for devlink\n");
return EXIT_FAILURE;
}
err = dl_init(dl, argc, argv); err = dl_init(dl, argc, argv);
if (err) { if (err) {
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
......
...@@ -33,6 +33,30 @@ enum devlink_command { ...@@ -33,6 +33,30 @@ enum devlink_command {
DEVLINK_CMD_PORT_SPLIT, DEVLINK_CMD_PORT_SPLIT,
DEVLINK_CMD_PORT_UNSPLIT, DEVLINK_CMD_PORT_UNSPLIT,
DEVLINK_CMD_SB_GET, /* can dump */
DEVLINK_CMD_SB_SET,
DEVLINK_CMD_SB_NEW,
DEVLINK_CMD_SB_DEL,
DEVLINK_CMD_SB_POOL_GET, /* can dump */
DEVLINK_CMD_SB_POOL_SET,
DEVLINK_CMD_SB_POOL_NEW,
DEVLINK_CMD_SB_POOL_DEL,
DEVLINK_CMD_SB_PORT_POOL_GET, /* can dump */
DEVLINK_CMD_SB_PORT_POOL_SET,
DEVLINK_CMD_SB_PORT_POOL_NEW,
DEVLINK_CMD_SB_PORT_POOL_DEL,
DEVLINK_CMD_SB_TC_POOL_BIND_GET, /* can dump */
DEVLINK_CMD_SB_TC_POOL_BIND_SET,
DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
DEVLINK_CMD_SB_TC_POOL_BIND_DEL,
/* Shared buffer occupancy monitoring commands */
DEVLINK_CMD_SB_OCC_SNAPSHOT,
DEVLINK_CMD_SB_OCC_MAX_CLEAR,
/* add new commands above here */ /* add new commands above here */
__DEVLINK_CMD_MAX, __DEVLINK_CMD_MAX,
...@@ -46,6 +70,31 @@ enum devlink_port_type { ...@@ -46,6 +70,31 @@ enum devlink_port_type {
DEVLINK_PORT_TYPE_IB, DEVLINK_PORT_TYPE_IB,
}; };
enum devlink_sb_pool_type {
DEVLINK_SB_POOL_TYPE_INGRESS,
DEVLINK_SB_POOL_TYPE_EGRESS,
};
/* static threshold - limiting the maximum number of bytes.
* dynamic threshold - limiting the maximum number of bytes
* based on the currently available free space in the shared buffer pool.
* In this mode, the maximum quota is calculated based
* on the following formula:
* max_quota = alpha / (1 + alpha) * Free_Buffer
* While Free_Buffer is the amount of none-occupied buffer associated to
* the relevant pool.
* The value range which can be passed is 0-20 and serves
* for computation of alpha by following formula:
* alpha = 2 ^ (passed_value - 10)
*/
enum devlink_sb_threshold_type {
DEVLINK_SB_THRESHOLD_TYPE_STATIC,
DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC,
};
#define DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX 20
enum devlink_attr { enum devlink_attr {
/* don't change the order or add anything between, this is ABI! */ /* don't change the order or add anything between, this is ABI! */
DEVLINK_ATTR_UNSPEC, DEVLINK_ATTR_UNSPEC,
...@@ -62,6 +111,20 @@ enum devlink_attr { ...@@ -62,6 +111,20 @@ enum devlink_attr {
DEVLINK_ATTR_PORT_IBDEV_NAME, /* string */ DEVLINK_ATTR_PORT_IBDEV_NAME, /* string */
DEVLINK_ATTR_PORT_SPLIT_COUNT, /* u32 */ DEVLINK_ATTR_PORT_SPLIT_COUNT, /* u32 */
DEVLINK_ATTR_PORT_SPLIT_GROUP, /* u32 */ DEVLINK_ATTR_PORT_SPLIT_GROUP, /* u32 */
DEVLINK_ATTR_SB_INDEX, /* u32 */
DEVLINK_ATTR_SB_SIZE, /* u32 */
DEVLINK_ATTR_SB_INGRESS_POOL_COUNT, /* u16 */
DEVLINK_ATTR_SB_EGRESS_POOL_COUNT, /* u16 */
DEVLINK_ATTR_SB_INGRESS_TC_COUNT, /* u16 */
DEVLINK_ATTR_SB_EGRESS_TC_COUNT, /* u16 */
DEVLINK_ATTR_SB_POOL_INDEX, /* u16 */
DEVLINK_ATTR_SB_POOL_TYPE, /* u8 */
DEVLINK_ATTR_SB_POOL_SIZE, /* u32 */
DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE, /* u8 */
DEVLINK_ATTR_SB_THRESHOLD, /* u32 */
DEVLINK_ATTR_SB_TC_INDEX, /* u16 */
DEVLINK_ATTR_SB_OCC_CUR, /* u32 */
DEVLINK_ATTR_SB_OCC_MAX, /* u32 */
/* add new attributes above here, update the policy in devlink.c */ /* add new attributes above here, update the policy in devlink.c */
......
...@@ -33,6 +33,11 @@ static inline void list_add(struct list_head *new, struct list_head *head) ...@@ -33,6 +33,11 @@ static inline void list_add(struct list_head *new, struct list_head *head)
__list_add(new, head, head->next); __list_add(new, head, head->next);
} }
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_del(struct list_head *prev, struct list_head *next) static inline void __list_del(struct list_head *prev, struct list_head *next)
{ {
next->prev = prev; next->prev = prev;
...@@ -50,9 +55,15 @@ static inline void list_del(struct list_head *entry) ...@@ -50,9 +55,15 @@ static inline void list_del(struct list_head *entry)
#define list_first_entry(ptr, type, member) \ #define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member) list_entry((ptr)->next, type, member)
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
#define list_next_entry(pos, member) \ #define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member) list_entry((pos)->member.next, typeof(*(pos)), member)
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, typeof(*(pos)), member)
#define list_for_each_entry(pos, head, member) \ #define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \ for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \ &pos->member != (head); \
...@@ -64,6 +75,11 @@ static inline void list_del(struct list_head *entry) ...@@ -64,6 +75,11 @@ static inline void list_del(struct list_head *entry)
&pos->member != (head); \ &pos->member != (head); \
pos = n, n = list_next_entry(n, member)) pos = n, n = list_next_entry(n, member))
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_prev_entry(pos, member))
struct hlist_head { struct hlist_head {
struct hlist_node *first; struct hlist_node *first;
}; };
......
...@@ -430,6 +430,8 @@ static int do_show_or_flush(int argc, char **argv, int flush) ...@@ -430,6 +430,8 @@ static int do_show_or_flush(int argc, char **argv, int flush)
addattr32(&req.n, sizeof(req), NDA_IFINDEX, filter.index); addattr32(&req.n, sizeof(req), NDA_IFINDEX, filter.index);
} }
req.ndm.ndm_family = filter.family;
if (flush) { if (flush) {
int round = 0; int round = 0;
char flushb[4096-512]; char flushb[4096-512];
...@@ -440,7 +442,7 @@ static int do_show_or_flush(int argc, char **argv, int flush) ...@@ -440,7 +442,7 @@ static int do_show_or_flush(int argc, char **argv, int flush)
filter.state &= ~NUD_FAILED; filter.state &= ~NUD_FAILED;
while (round < MAX_ROUNDS) { while (round < MAX_ROUNDS) {
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETNEIGH) < 0) { if (rtnl_dump_request_n(&rth, &req.n) < 0) {
perror("Cannot send dump request"); perror("Cannot send dump request");
exit(1); exit(1);
} }
...@@ -472,8 +474,6 @@ static int do_show_or_flush(int argc, char **argv, int flush) ...@@ -472,8 +474,6 @@ static int do_show_or_flush(int argc, char **argv, int flush)
return 1; return 1;
} }
req.ndm.ndm_family = filter.family;
if (rtnl_dump_request_n(&rth, &req.n) < 0) { if (rtnl_dump_request_n(&rth, &req.n) < 0) {
perror("Cannot send dump request"); perror("Cannot send dump request");
exit(1); exit(1);
......
...@@ -16,6 +16,7 @@ devlink-dev \- devlink device configuration ...@@ -16,6 +16,7 @@ devlink-dev \- devlink device configuration
.ti -8 .ti -8
.IR OPTIONS " := { " .IR OPTIONS " := { "
\fB\-V\fR[\fIersion\fR] | \fB\-V\fR[\fIersion\fR] |
\fB\-n\fR[\fIno-nice-names\fR] }
.ti -8 .ti -8
.B devlink dev show .B devlink dev show
...@@ -51,6 +52,7 @@ Shows the state of specified devlink device. ...@@ -51,6 +52,7 @@ Shows the state of specified devlink device.
.SH SEE ALSO .SH SEE ALSO
.BR devlink (8), .BR devlink (8),
.BR devlink-port (8), .BR devlink-port (8),
.BR devlink-sb (8),
.BR devlink-monitor (8), .BR devlink-monitor (8),
.br .br
......
...@@ -29,6 +29,7 @@ opens Devlink Netlink socket, listens on it and dumps state changes. ...@@ -29,6 +29,7 @@ opens Devlink Netlink socket, listens on it and dumps state changes.
.SH SEE ALSO .SH SEE ALSO
.BR devlink (8), .BR devlink (8),
.BR devlink-dev (8), .BR devlink-dev (8),
.BR devlink-sb (8),
.BR devlink-port (8), .BR devlink-port (8),
.br .br
......
...@@ -16,6 +16,7 @@ devlink-port \- devlink port configuration ...@@ -16,6 +16,7 @@ devlink-port \- devlink port configuration
.ti -8 .ti -8
.IR OPTIONS " := { " .IR OPTIONS " := { "
\fB\-V\fR[\fIersion\fR] | \fB\-V\fR[\fIersion\fR] |
\fB\-n\fR[\fIno-nice-names\fR] }
.ti -8 .ti -8
.BR "devlink port set " .BR "devlink port set "
...@@ -119,6 +120,7 @@ Unplit the specified previously split devlink port. ...@@ -119,6 +120,7 @@ Unplit the specified previously split devlink port.
.SH SEE ALSO .SH SEE ALSO
.BR devlink (8), .BR devlink (8),
.BR devlink-dev (8), .BR devlink-dev (8),
.BR devlink-sb (8),
.BR devlink-monitor (8), .BR devlink-monitor (8),
.br .br
......
.TH DEVLINK\-SB 8 "14 Apr 2016" "iproute2" "Linux"
.SH NAME
devlink-sb \- devlink shared buffer configuration
.SH SYNOPSIS
.sp
.ad l
.in +8
.ti -8
.B devlink
.RI "[ " OPTIONS " ]"
.B sb
.RI " { " COMMAND " | "
.BR help " }"
.sp
.ti -8
.IR OPTIONS " := { "
\fB\-V\fR[\fIersion\fR] |
\fB\-n\fR[\fIno-nice-names\fR] }
.ti -8
.BR "devlink sb show "
.RI "[ " DEV " [ "
.B sb
.IR SB_INDEX " ] ]"
.ti -8
.BR "devlink sb pool show "
.RI "[ " DEV " [ "
.B sb
.IR SB_INDEX " ] "
.br
.B pool
.IR POOL_INDEX " ]"
.ti -8
.BI "devlink sb pool set " DEV "
.RB "[ " sb
.IR SB_INDEX " ] "
.br
.BI pool " POOL_INDEX "
.br
.BI size " POOL_SIZE "
.br
.BR thtype " { " static " | " dynamic " }"
.ti -8
.BR "devlink sb port pool show "
.RI "[ " DEV/PORT_INDEX " [ "
.B sb
.IR SB_INDEX " ] "
.br
.B pool
.IR POOL_INDEX " ]"
.ti -8
.BI "devlink sb port pool set " DEV/PORT_INDEX "
.RB "[ " sb
.IR SB_INDEX " ] "
.br
.BI pool " POOL_INDEX "
.br
.BI th " THRESHOLD "
.ti -8
.BR "devlink sb tc bind show "
.RI "[ " DEV/PORT_INDEX " [ "
.B sb
.IR SB_INDEX " ] "
.br
.BI tc " TC_INDEX "
.br
.B type
.RB "{ " ingress " | " egress " } ]"
.ti -8
.BI "devlink sb tc bind set " DEV/PORT_INDEX "
.RB "[ " sb
.IR SB_INDEX " ] "
.br
.BI tc " TC_INDEX "
.br
.BR type " { " ingress " | " egress " }"
.br
.BI pool " POOL_INDEX "
.br
.BI th " THRESHOLD "
.ti -8
.BR "devlink sb occupancy show "
.RI "{ " DEV " | " DEV/PORT_INDEX " } [ "
.B sb
.IR SB_INDEX " ] "
.ti -8
.BR "devlink sb occupancy snapshot "
.IR DEV " [ "
.B sb
.IR SB_INDEX " ]"
.ti -8
.BR "devlink sb occupancy clearmax "
.IR DEV " [ "
.B sb
.IR SB_INDEX " ]"
.ti -8
.B devlink sb help
.SH "DESCRIPTION"
.SS devlink sb show - display available shared buffers and their attributes
.PP
.I "DEV"
- specifies the devlink device to show shared buffers.
If this argument is omitted all shared buffers of all devices are listed.
.PP
.I "SB_INDEX"
- specifies the shared buffer.
If this argument is omitted shared buffer with index 0 is selected.
Behaviour of this argument it the same for every command.
.SS devlink sb pool show - display available pools and their attributes
.PP
.I "DEV"
- specifies the devlink device to show pools.
If this argument is omitted all pools of all devices are listed.
.SS devlink sb pool set - set attributes of pool
.PP
.I "DEV"
- specifies the devlink device to set pool.
.TP
.BI size " POOL_SIZE"
size of the pool in Bytes.
.TP
.BR thtype " { " static " | " dynamic " } "
pool threshold type.
.I static
- Threshold values for the pool will be passed in Bytes.
.I dynamic
- Threshold values ("to_alpha") for the pool will be used to compute alpha parameter according to formula:
.br
.in +16
alpha = 2 ^ (to_alpha - 10)
.in -16
.in +10
The range of the passed value is between 0 to 20. The computed alpha is used to determine the maximum usage of the flow:
.in -10
.br
.in +16
max_usage = alpha / (1 + alpha) * Free_Buffer
.in -16
.SS devlink sb port pool show - display port-pool combinations and threshold for each
.I "DEV/PORT_INDEX"
- specifies the devlink port.
.TP
.BI pool " POOL_INDEX"
pool index.
.SS devlink sb port pool set - set port-pool threshold
.I "DEV/PORT_INDEX"
- specifies the devlink port.
.TP
.BI pool " POOL_INDEX"
pool index.
.TP
.BI th " THRESHOLD"
threshold value. Type of the value is either Bytes or "to_alpha", depends on
.B thtype
set for the pool.
.SS devlink sb tc bind show - display port-TC to pool bindings and threshold for each
.I "DEV/PORT_INDEX"
- specifies the devlink port.
.TP
.BI tc " TC_INDEX"
index of either ingress or egress TC, usually in range 0 to 8 (depends on device).
.TP
.BR type " { " ingress " | " egress " } "
TC type.
.SS devlink sb tc bind set - set port-TC to pool binding with specified threshold
.I "DEV/PORT_INDEX"
- specifies the devlink port.
.TP
.BI tc " TC_INDEX"
index of either ingress or egress TC, usually in range 0 to 8 (depends on device).
.TP
.BR type " { " ingress " | " egress " } "
TC type.
.TP
.BI pool " POOL_INDEX"
index of pool to bind this to.
.TP
.BI th " THRESHOLD"
threshold value. Type of the value is either Bytes or "to_alpha", depends on
.B thtype
set for the pool.
.SS devlink sb occupancy show - display shared buffer occupancy values for device or port
.PP
This command is used to browse shared buffer occupancy values. Values are showed for every port-pool combination as well as for all port-TC combinations (with pool this port-TC is bound to). Format of value is:
.br
.in +16
current_value/max_value
.in -16
Note that before showing values, one has to issue
.b occupancy snapshot
command first.
.PP
.I "DEV"
- specifies the devlink device to show occupancy values for.
.I "DEV/PORT_INDEX"
- specifies the devlink port to show occupancy values for.
.SS devlink sb occupancy snapshot - take occupancy snapshot of shared buffer for device
This command is used to take a snapshot of shared buffer occupancy values. After that, the values can be showed using
.B occupancy show
command.
.PP
.I "DEV"
- specifies the devlink device to take occupancy snapshot on.
.SS devlink sb occupancy clearmax - clear occupancy watermarks of shared buffer for device
This command is used to reset maximal occupancy values reached for whole device. Note that before browsing reset values, one has to issue
.B occupancy snapshot
command.
.PP
.I "DEV"
- specifies the devlink device to clear occupancy watermarks on.
.SH "EXAMPLES"
.PP
devlink sb show
.RS 4
List available share buffers.
.RE
.PP
devlink sb pool show
.RS 4
List available pools and their config.
.RE
.PP
devlink sb port pool show pci/0000:03:00.0/1 pool 0
.RS 4
Show port-pool setup for specified port and pool.
.RE
.PP
sudo devlink sb port pool set pci/0000:03:00.0/1 pool 0 th 15
.RS 4
Change threshold for port specified port and pool.
.RE
.PP
devlink sb tc bind show pci/0000:03:00.0/1 tc 0 type ingress
.RS 4
Show pool binding and threshold for specified port and TC.
.RE
.PP
sudo devlink sb tc bind set pci/0000:03:00.0/1 tc 0 type ingress pool 0 th 9
.RS 4
Set pool binding and threshold for specified port and TC.
.RE
.PP
sudo devlink sb occupancy snapshot pci/0000:03:00.0
.RS 4
Make a snapshot of occupancy of shared buffer for specified devlink device.
.RE
.PP
devlink sb occupancy show pci/0000:03:00.0/1
.RS 4
Show occupancy for specified port from the snapshot.
.RE
.PP
sudo devlink sb occupancy clearmax pci/0000:03:00.0
.RS 4
Clear watermarks for shared buffer of specified devlink device.
.SH SEE ALSO
.BR devlink (8),
.BR devlink-dev (8),
.BR devlink-port (8),
.BR devlink-monitor (8),
.br
.SH AUTHOR
Jiri Pirko <jiri@mellanox.com>
...@@ -19,6 +19,7 @@ devlink \- Devlink tool ...@@ -19,6 +19,7 @@ devlink \- Devlink tool
.ti -8 .ti -8
.IR OPTIONS " := { " .IR OPTIONS " := { "
\fB\-V\fR[\fIersion\fR] | \fB\-V\fR[\fIersion\fR] |
\fB\-n\fR[\fIno-nice-names\fR] }
.SH OPTIONS .SH OPTIONS
...@@ -28,6 +29,10 @@ Print the version of the ...@@ -28,6 +29,10 @@ Print the version of the
.B devlink .B devlink
utility and exit. utility and exit.
.TP
.BR "\-n" , " -no-nice-names"
Turn off printing out nice names, for example netdevice ifnames instead of devlink port identification.
.SS .SS
.I OBJECT .I OBJECT
......
...@@ -267,7 +267,7 @@ static void filter_default_dbs(struct filter *f) ...@@ -267,7 +267,7 @@ static void filter_default_dbs(struct filter *f)
static void filter_states_set(struct filter *f, int states) static void filter_states_set(struct filter *f, int states)
{ {
if (states) if (states)
f->states = (f->states | states) & states; f->states = states;
} }
static void filter_merge_defaults(struct filter *f) static void filter_merge_defaults(struct filter *f)
...@@ -1556,9 +1556,10 @@ void *parse_hostcond(char *addr, bool is_port) ...@@ -1556,9 +1556,10 @@ void *parse_hostcond(char *addr, bool is_port)
out: out:
if (fam != AF_UNSPEC) { if (fam != AF_UNSPEC) {
int states = f->states;
f->families = 0; f->families = 0;
filter_af_set(f, fam); filter_af_set(f, fam);
filter_states_set(f, 0); filter_states_set(f, states);
} }
res = malloc(sizeof(*res)); res = malloc(sizeof(*res));
...@@ -2018,7 +2019,8 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, ...@@ -2018,7 +2019,8 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r,
s.segs_out = info->tcpi_segs_out; s.segs_out = info->tcpi_segs_out;
s.segs_in = info->tcpi_segs_in; s.segs_in = info->tcpi_segs_in;
s.not_sent = info->tcpi_notsent_bytes; s.not_sent = info->tcpi_notsent_bytes;
s.min_rtt = (double) info->tcpi_min_rtt / 1000; if (info->tcpi_min_rtt && info->tcpi_min_rtt != ~0U)
s.min_rtt = (double) info->tcpi_min_rtt / 1000;
tcp_stats_print(&s); tcp_stats_print(&s);
free(s.dctcp); free(s.dctcp);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment