diff --git a/devlink/devlink.c b/devlink/devlink.c index c2da8507..ffefa86d 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -27,6 +27,12 @@ #define pr_err(args...) fprintf(stderr, ##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, mnl_cb_t data_cb, void *data) @@ -109,11 +115,42 @@ static void ifname_map_free(struct ifname_map *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 mnlg_socket *nlg; struct list_head ifname_map_list; int argc; char **argv; + bool no_nice_names; + struct dl_opts opts; }; static int dl_argc(struct dl *dl) @@ -208,6 +245,48 @@ static int attr_cb(const struct nlattr *attr, void *data) if (type == DEVLINK_ATTR_PORT_IBDEV_NAME && mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) 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; return MNL_CB_OK; } @@ -290,6 +369,23 @@ static int ifname_map_lookup(struct dl *dl, const char *ifname, 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) { unsigned int count = 0; @@ -329,11 +425,29 @@ static int strtouint32_t(const char *str, uint32_t *p_val) 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 *bus_name = bus_name; - char *dev_name = dev_name; if (!str) { 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) pr_err("Expected \"bus_name/dev_name\".\n"); return -EINVAL; } + return __dl_argv_handle(str, p_bus_name, p_dev_name); +} - strslashrsplit(str, &bus_name, &dev_name); - mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, bus_name); - mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, 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; } -static int dl_argv_put_handle_port(struct nlmsghdr *nlh, struct dl *dl) +static int __dl_argv_handle_port_ifname(struct dl *dl, char *str, + char **p_bus_name, char **p_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; +} + +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); unsigned int slash_count; - char *bus_name = bus_name; - char *dev_name = dev_name; - uint32_t port_index = port_index; - int err; if (!str) { 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) pr_err("Expected \"bus_name/dev_name/port_index\" or \"netdev_ifname\".\n"); return -EINVAL; } - if (slash_count == 2) { - char *handlestr = handlestr; - char *portstr = portstr; - - err = strslashrsplit(str, &handlestr, &portstr); - err = strtouint32_t(portstr, &port_index); - if (err) { - pr_err("Port index \"%s\" is not a number or not within range\n", - portstr); - return err; - } - strslashrsplit(handlestr, &bus_name, &dev_name); + return __dl_argv_handle_port(str, p_bus_name, + p_dev_name, p_port_index); } else if (slash_count == 0) { - err = ifname_map_lookup(dl, str, &bus_name, &dev_name, - &port_index); - if (err) { - pr_err("Netdevice \"%s\" not found\n", str); - return err; - } + return __dl_argv_handle_port_ifname(dl, str, p_bus_name, + p_dev_name, p_port_index); + } + return 0; +} + +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; + *p_handle_bit = DL_OPT_HANDLE; + } 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) { + err = __dl_argv_handle_port_ifname(dl, str, p_bus_name, + p_dev_name, p_port_index); + if (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; } @@ -415,6 +579,24 @@ static int dl_argv_uint32_t(struct dl *dl, uint32_t *p_val) 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) { 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) return 0; } -#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) - -static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, - uint32_t o_required, uint32_t o_optional) +static int pool_type_get(const char *typestr, enum devlink_sb_pool_type *p_type) { + if (strcmp(typestr, "ingress") == 0) { + *p_type = DEVLINK_SB_POOL_TYPE_INGRESS; + } 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 threshold_type_get(const char *typestr, + 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_found = 0; int err; - if (o_required & DL_OPT_HANDLE) { - err = dl_argv_put_handle(nlh, dl); + if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) { + 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) 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) { - 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) return err; + o_found |= DL_OPT_HANDLEP; } while (dl_argc(dl)) { if (dl_argv_match(dl, "type") && (o_all & DL_OPT_PORT_TYPE)) { - enum devlink_port_type port_type; const char *typestr; dl_arg_inc(dl); err = dl_argv_str(dl, &typestr); if (err) return err; - err = port_type_get(typestr, &port_type); + err = port_type_get(typestr, &opts->port_type); if (err) return err; - mnl_attr_put_u16(nlh, DEVLINK_ATTR_PORT_TYPE, - port_type); o_found |= DL_OPT_PORT_TYPE; } else if (dl_argv_match(dl, "count") && (o_all & DL_OPT_PORT_COUNT)) { - uint32_t count; - dl_arg_inc(dl); - err = dl_argv_uint32_t(dl, &count); + err = dl_argv_uint32_t(dl, &opts->port_count); if (err) return err; - mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_SPLIT_COUNT, - 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 { pr_err("Unknown option \"%s\"\n", dl_argv(dl)); 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)) { pr_err("Port type option expected.\n"); return -EINVAL; @@ -509,20 +784,192 @@ static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, 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; +} + +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) { 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) { - 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])); } +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) { pr_out_handle(tb); @@ -578,9 +1025,9 @@ static int cmd_dev(struct dl *dl) static void cmd_port_help(void) { 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(" dl port split DEV/PORT_INDEX count COUNT\n"); - pr_out(" dl port unsplit DEV/PORT_INDEX\n"); + pr_out(" devlink port set DEV/PORT_INDEX [ type { eth | ib | auto} ]\n"); + pr_out(" devlink port split DEV/PORT_INDEX count COUNT\n"); + pr_out(" devlink port unsplit DEV/PORT_INDEX\n"); } static const char *port_type_name(uint32_t type) @@ -599,8 +1046,8 @@ static void pr_out_port(struct nlattr **tb) struct nlattr *pt_attr = tb[DEVLINK_ATTR_PORT_TYPE]; struct nlattr *dpt_attr = tb[DEVLINK_ATTR_PORT_DESIRED_TYPE]; - pr_out_handle(tb); - pr_out("/%d:", mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX])); + pr_out_port_handle(tb); + pr_out(":"); if (pt_attr) { uint16_t port_type = mnl_attr_get_u16(pt_attr); @@ -725,6 +1172,710 @@ static int cmd_port(struct dl *dl) return -ENOENT; } +static void cmd_sb_help(void) +{ + pr_out("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n"); + pr_out(" devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n"); + pr_out(" devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n"); + pr_out(" size POOL_SIZE thtype { static | dynamic }\n"); + pr_out(" devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n"); + pr_out(" pool POOL_INDEX ]\n"); + pr_out(" devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n"); + pr_out(" pool POOL_INDEX th THRESHOLD\n"); + pr_out(" devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n"); + pr_out(" type { ingress | egress } ]\n"); + 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 ""; + } +} + +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 ""; + } +} + +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) { @@ -860,8 +2011,8 @@ static int cmd_mon(struct dl *dl) static void help(void) { pr_out("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" - "where OBJECT := { dev | port | monitor }\n" - " OPTIONS := { -V[ersion] }\n"); + "where OBJECT := { dev | port | sb | monitor }\n" + " OPTIONS := { -V[ersion] | -n[no-nice-names] }\n"); } 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")) { dl_arg_inc(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")) { dl_arg_inc(dl); return cmd_mon(dl); @@ -933,6 +2087,7 @@ int main(int argc, char **argv) { static const struct option long_options[] = { { "Version", no_argument, NULL, 'V' }, + { "no-nice-names", no_argument, NULL, 'n' }, { NULL, 0, NULL, 0 } }; struct dl *dl; @@ -940,13 +2095,22 @@ int main(int argc, char **argv) int err; 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) { switch (opt) { case 'V': printf("devlink utility, iproute2-ss%s\n", SNAPSHOT); return EXIT_SUCCESS; + case 'n': + dl->no_nice_names = true; + break; default: pr_err("Unknown option.\n"); help(); @@ -957,12 +2121,6 @@ int main(int argc, char **argv) argc -= 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); if (err) { ret = EXIT_FAILURE; diff --git a/include/linux/devlink.h b/include/linux/devlink.h index c9fee578..ba0073b2 100644 --- a/include/linux/devlink.h +++ b/include/linux/devlink.h @@ -33,6 +33,30 @@ enum devlink_command { DEVLINK_CMD_PORT_SPLIT, 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 */ __DEVLINK_CMD_MAX, @@ -46,6 +70,31 @@ enum devlink_port_type { 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 { /* don't change the order or add anything between, this is ABI! */ DEVLINK_ATTR_UNSPEC, @@ -62,6 +111,20 @@ enum devlink_attr { DEVLINK_ATTR_PORT_IBDEV_NAME, /* string */ DEVLINK_ATTR_PORT_SPLIT_COUNT, /* 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 */ diff --git a/include/list.h b/include/list.h index cdebe4de..5b529dc6 100644 --- a/include/list.h +++ b/include/list.h @@ -33,6 +33,11 @@ static inline void list_add(struct list_head *new, struct list_head *head) __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) { next->prev = prev; @@ -50,9 +55,15 @@ static inline void list_del(struct list_head *entry) #define list_first_entry(ptr, 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) \ 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) \ for (pos = list_first_entry(head, typeof(*pos), member); \ &pos->member != (head); \ @@ -64,6 +75,11 @@ static inline void list_del(struct list_head *entry) &pos->member != (head); \ 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_node *first; }; diff --git a/ip/ipneigh.c b/ip/ipneigh.c index c49fb4e7..4ddb747e 100644 --- a/ip/ipneigh.c +++ b/ip/ipneigh.c @@ -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); } + req.ndm.ndm_family = filter.family; + if (flush) { int round = 0; char flushb[4096-512]; @@ -440,7 +442,7 @@ static int do_show_or_flush(int argc, char **argv, int flush) filter.state &= ~NUD_FAILED; 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"); exit(1); } @@ -472,8 +474,6 @@ static int do_show_or_flush(int argc, char **argv, int flush) return 1; } - req.ndm.ndm_family = filter.family; - if (rtnl_dump_request_n(&rth, &req.n) < 0) { perror("Cannot send dump request"); exit(1); diff --git a/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8 index 7878d89b..62bcead3 100644 --- a/man/man8/devlink-dev.8 +++ b/man/man8/devlink-dev.8 @@ -16,6 +16,7 @@ devlink-dev \- devlink device configuration .ti -8 .IR OPTIONS " := { " \fB\-V\fR[\fIersion\fR] | +\fB\-n\fR[\fIno-nice-names\fR] } .ti -8 .B devlink dev show @@ -51,6 +52,7 @@ Shows the state of specified devlink device. .SH SEE ALSO .BR devlink (8), .BR devlink-port (8), +.BR devlink-sb (8), .BR devlink-monitor (8), .br diff --git a/man/man8/devlink-monitor.8 b/man/man8/devlink-monitor.8 index 98134c37..13fe641d 100644 --- a/man/man8/devlink-monitor.8 +++ b/man/man8/devlink-monitor.8 @@ -29,6 +29,7 @@ opens Devlink Netlink socket, listens on it and dumps state changes. .SH SEE ALSO .BR devlink (8), .BR devlink-dev (8), +.BR devlink-sb (8), .BR devlink-port (8), .br diff --git a/man/man8/devlink-port.8 b/man/man8/devlink-port.8 index e6ae6869..a639d01f 100644 --- a/man/man8/devlink-port.8 +++ b/man/man8/devlink-port.8 @@ -16,6 +16,7 @@ devlink-port \- devlink port configuration .ti -8 .IR OPTIONS " := { " \fB\-V\fR[\fIersion\fR] | +\fB\-n\fR[\fIno-nice-names\fR] } .ti -8 .BR "devlink port set " @@ -119,6 +120,7 @@ Unplit the specified previously split devlink port. .SH SEE ALSO .BR devlink (8), .BR devlink-dev (8), +.BR devlink-sb (8), .BR devlink-monitor (8), .br diff --git a/man/man8/devlink-sb.8 b/man/man8/devlink-sb.8 new file mode 100644 index 00000000..ffb5553e --- /dev/null +++ b/man/man8/devlink-sb.8 @@ -0,0 +1,313 @@ +.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 diff --git a/man/man8/devlink.8 b/man/man8/devlink.8 index f608ccca..df00f4fa 100644 --- a/man/man8/devlink.8 +++ b/man/man8/devlink.8 @@ -19,6 +19,7 @@ devlink \- Devlink tool .ti -8 .IR OPTIONS " := { " \fB\-V\fR[\fIersion\fR] | +\fB\-n\fR[\fIno-nice-names\fR] } .SH OPTIONS @@ -28,6 +29,10 @@ Print the version of the .B devlink 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 .I OBJECT diff --git a/misc/ss.c b/misc/ss.c index 38cf3312..deefc967 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -267,7 +267,7 @@ static void filter_default_dbs(struct filter *f) static void filter_states_set(struct filter *f, int states) { if (states) - f->states = (f->states | states) & states; + f->states = states; } static void filter_merge_defaults(struct filter *f) @@ -1556,9 +1556,10 @@ void *parse_hostcond(char *addr, bool is_port) out: if (fam != AF_UNSPEC) { + int states = f->states; f->families = 0; filter_af_set(f, fam); - filter_states_set(f, 0); + filter_states_set(f, states); } res = malloc(sizeof(*res)); @@ -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_in = info->tcpi_segs_in; 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); free(s.dctcp); }