From 1c346dccf8a16e32f855427dcb2bb7f29cbf06f1 Mon Sep 17 00:00:00 2001 From: Jeff Harris Date: Thu, 14 Apr 2016 14:15:03 -0400 Subject: [PATCH 01/15] ip: neigh: Fix leftover attributes message during flush Use the same rtnl_dump_request_n call as the show. The rtnl_wilddump_request assumes the type uses an ifinfomsg which is not the case for the neighbor table. Signed-off-by: Jeff Harris Acked-by: David Ahern --- ip/ipneigh.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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); From 9d320e1e928630d8b2b83d5e4ef4e10a117352bc Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 13 Apr 2016 22:07:04 +0200 Subject: [PATCH 02/15] ss: Drop silly assignment An expression of the form '(a | b) & b' will evaluate to the value of b for any value of a or b. Signed-off-by: Phil Sutter --- misc/ss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ss.c b/misc/ss.c index 38cf3312..d6090018 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) From e56a959e550f424023ebf3ebb8437f214944a245 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 13 Apr 2016 22:07:05 +0200 Subject: [PATCH 03/15] ss: Fix accidental state filter override Passing a filter expression and selecting an address family using the '-f' flag would overwrite the state filter by accident. Therefore calling e.g. 'ss -nl -f inet '(sport = :22)' would not only print listening sockets (as requested by '-l' flag) but connected ones, as well. Fix this by reusing the formerly ineffective call to filter_states_set() to restore the state filter as it was before the call to filter_af_set(). Signed-off-by: Phil Sutter --- misc/ss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/ss.c b/misc/ss.c index d6090018..544def3f 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -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)); From d9ba887e9d37b2c5ff012f2abd0776cf93208fba Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 13 Apr 2016 15:18:38 -0700 Subject: [PATCH 04/15] ss: take care of unknown min_rtt Kernel sets info->tcpi_min_rtt to ~0U when no RTT sample was ever taken for the session, thus min_rtt is unknown. Signed-off-by: Eric Dumazet --- misc/ss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/ss.c b/misc/ss.c index 544def3f..deefc967 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -2019,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); } From ec7513faa959811e3a68aed0836d949dd924afca Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:43 +0200 Subject: [PATCH 05/15] devlink: fix "devlink port" help message "dl" -> "devlink" Signed-off-by: Jiri Pirko --- devlink/devlink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index c2da8507..39f423aa 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -578,9 +578,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) From f1239ca1f96c76fbc0742ca0d0c7e87b9b15d437 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:44 +0200 Subject: [PATCH 06/15] list: add list_for_each_entry_reverse macro Signed-off-by: Jiri Pirko --- include/list.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/list.h b/include/list.h index cdebe4de..b549c3ec 100644 --- a/include/list.h +++ b/include/list.h @@ -50,9 +50,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 +70,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; }; From ebaf76b55ea509bcfb8845e4c40a9ecd3e8377f5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:45 +0200 Subject: [PATCH 07/15] list: add list_add_tail helper Signed-off-by: Jiri Pirko --- include/list.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/list.h b/include/list.h index b549c3ec..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; From 68cab0ba763f9d80740d171e1882ad4f262ca6e1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:46 +0200 Subject: [PATCH 08/15] devlink: introduce pr_out_port_handle helper Signed-off-by: Jiri Pirko --- devlink/devlink.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index 39f423aa..0904e07f 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -523,6 +523,12 @@ static void pr_out_handle(struct nlattr **tb) mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME])); } +static void pr_out_port_handle(struct nlattr **tb) +{ + pr_out_handle(tb); + pr_out("/%d", mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX])); +} + static void pr_out_dev(struct nlattr **tb) { pr_out_handle(tb); @@ -599,8 +605,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); From 43f35be4ebb63bf5dea9cc0570ba8d15458d2457 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:47 +0200 Subject: [PATCH 09/15] devlink: introduce helper to print out nice names (ifnames) By default, ifnames will be printed out. User can turn that off using "-n" option on the command line. Signed-off-by: Jiri Pirko --- devlink/devlink.c | 90 ++++++++++++++++++++++++++++++++++++----- man/man8/devlink-dev.8 | 1 + man/man8/devlink-port.8 | 1 + man/man8/devlink.8 | 5 +++ 4 files changed, 86 insertions(+), 11 deletions(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index 0904e07f..5e08666d 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -114,6 +114,7 @@ struct dl { struct list_head ifname_map_list; int argc; char **argv; + bool no_nice_names; }; static int dl_argc(struct dl *dl) @@ -290,6 +291,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; @@ -517,16 +535,62 @@ 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_handle(tb); - pr_out("/%d", mnl_attr_get_u32(tb[DEVLINK_ATTR_PORT_INDEX])); + __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) @@ -867,7 +931,7 @@ static void help(void) { pr_out("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n" "where OBJECT := { dev | port | monitor }\n" - " OPTIONS := { -V[ersion] }\n"); + " OPTIONS := { -V[ersion] | -n[no-nice-names] }\n"); } static int dl_cmd(struct dl *dl) @@ -939,6 +1003,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; @@ -946,13 +1011,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(); @@ -963,12 +1037,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/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8 index 7878d89b..af96a298 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 diff --git a/man/man8/devlink-port.8 b/man/man8/devlink-port.8 index e6ae6869..d78837c7 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 " 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 From 6563a6eb539ba5c3f9fa262bc302190abb7e5f39 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:48 +0200 Subject: [PATCH 10/15] devlink: split dl_argv_parse_put to parse and put parts It is handy to have parsed cmdline data stored so they can be used for dumps filtering. So split original dl_argv_parse_put into parse and put parts. Signed-off-by: Jiri Pirko --- devlink/devlink.c | 105 ++++++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index 5e08666d..0c2132f0 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -109,12 +109,28 @@ 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) + +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; +}; + 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) @@ -347,11 +363,9 @@ 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 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"); @@ -363,19 +377,15 @@ static int dl_argv_put_handle(struct nlmsghdr *nlh, struct dl *dl) return -EINVAL; } - 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); + strslashrsplit(str, 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(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) { @@ -394,24 +404,21 @@ static int dl_argv_put_handle_port(struct nlmsghdr *nlh, struct dl *dl) char *portstr = portstr; err = strslashrsplit(str, &handlestr, &portstr); - err = strtouint32_t(portstr, &port_index); + 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, &bus_name, &dev_name); + strslashrsplit(handlestr, p_bus_name, p_dev_name); } else if (slash_count == 0) { - err = ifname_map_lookup(dl, str, &bus_name, &dev_name, - &port_index); + 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; } } - 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; } @@ -460,55 +467,46 @@ 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 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); + 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 { pr_err("Unknown option \"%s\"\n", dl_argv(dl)); @@ -516,6 +514,8 @@ static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, } } + opts->present = o_found; + if ((o_required & DL_OPT_PORT_TYPE) && !(o_found & DL_OPT_PORT_TYPE)) { pr_err("Port type option expected.\n"); return -EINVAL; @@ -530,6 +530,39 @@ static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl, 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); +} + +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 void cmd_dev_help(void) { pr_out("Usage: devlink dev show [ DEV ]\n"); From 707a91c5494962e3f5b358fc866ebb79dc28c3e3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:49 +0200 Subject: [PATCH 11/15] devlink: introduce dump filtering function This function is to be used from dump callbacks to decide if the output currect output should be filtered off or not. Filtering is based on previously parsed and stored command line options. Signed-off-by: Jiri Pirko --- devlink/devlink.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/devlink/devlink.c b/devlink/devlink.c index 0c2132f0..d436bbf3 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -563,6 +563,36 @@ static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *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]; + + 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; + } + return true; +} + static void cmd_dev_help(void) { pr_out("Usage: devlink dev show [ DEV ]\n"); From 2f85a9c535874e721cfc8b8743325afc93a8b1fa Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:50 +0200 Subject: [PATCH 12/15] devlink: allow to parse both devlink and port handle in the same time For filtering purposes, it makes sense for used to either specify devlink handle of port handle. Signed-off-by: Jiri Pirko --- devlink/devlink.c | 111 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 20 deletions(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index d436bbf3..e2e04136 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -363,6 +363,12 @@ static int strtouint32_t(const char *str, uint32_t *p_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); @@ -376,8 +382,40 @@ static int dl_argv_handle(struct dl *dl, char **p_bus_name, char **p_dev_name) pr_err("Expected \"bus_name/dev_name\".\n"); return -EINVAL; } + return __dl_argv_handle(str, p_bus_name, p_dev_name); +} - strslashrsplit(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; +} + +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; } @@ -386,7 +424,6 @@ static int dl_argv_handle_port(struct dl *dl, char **p_bus_name, { char *str = dl_argv_next(dl); unsigned int slash_count; - int err; if (!str) { pr_err("Port identification (\"bus_name/dev_name/port_index\" or \"netdev ifname\") expected.\n"); @@ -398,26 +435,52 @@ static int dl_argv_handle_port(struct dl *dl, char **p_bus_name, 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, 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 __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, p_bus_name, p_dev_name, - p_port_index); - if (err) { - pr_err("Netdevice \"%s\" not found\n", str); + 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; } return 0; } @@ -475,7 +538,15 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, uint32_t o_found = 0; int err; - if (o_required & DL_OPT_HANDLE) { + 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; From b56700bf8add4ebb2fe451c85f50602b58a886a2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:51 +0200 Subject: [PATCH 13/15] devlink: implement shared buffer support Implement kernel devlink shared buffer interface. Introduce new object "sb" and allow to browse the shared buffer parameters and also change configuration. Signed-off-by: Jiri Pirko --- devlink/devlink.c | 603 +++++++++++++++++++++++++++++++++++++++- include/linux/devlink.h | 57 ++++ 2 files changed, 659 insertions(+), 1 deletion(-) diff --git a/devlink/devlink.c b/devlink/devlink.c index e2e04136..228807f8 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -114,6 +114,13 @@ static void ifname_map_free(struct ifname_map *ifname_map) #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 */ @@ -122,6 +129,13 @@ struct dl_opts { 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 { @@ -225,6 +239,42 @@ 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; tb[type] = attr; return MNL_CB_OK; } @@ -363,6 +413,20 @@ static int strtouint32_t(const char *str, uint32_t *p_val) return 0; } +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); @@ -503,6 +567,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); @@ -530,6 +612,33 @@ static int port_type_get(const char *typestr, enum devlink_port_type *p_type) return 0; } +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) { @@ -579,6 +688,66 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, if (err) return err; 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; @@ -587,6 +756,11 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, 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; @@ -598,6 +772,35 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, 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; } @@ -620,6 +823,27 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl) 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, @@ -929,6 +1153,380 @@ 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"); +} + +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; +} + +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); + } + pr_err("Command \"%s\" not found\n", dl_argv(dl)); + return -ENOENT; +} + static const char *cmd_name(uint8_t cmd) { switch (cmd) { @@ -1064,7 +1662,7 @@ 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" + "where OBJECT := { dev | port | sb | monitor }\n" " OPTIONS := { -V[ersion] | -n[no-nice-names] }\n"); } @@ -1079,6 +1677,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); diff --git a/include/linux/devlink.h b/include/linux/devlink.h index c9fee578..9c1aa578 100644 --- a/include/linux/devlink.h +++ b/include/linux/devlink.h @@ -33,6 +33,26 @@ 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, + /* add new commands above here */ __DEVLINK_CMD_MAX, @@ -46,6 +66,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 +107,18 @@ 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 */ /* add new attributes above here, update the policy in devlink.c */ From a60ebcb6f34f4c43cba092f52b1150d7fb1deec5 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:52 +0200 Subject: [PATCH 14/15] devlink: implement shared buffer occupancy control Use kernel shared buffer occupancy control commands to make snapshot and clear occupancy watermarks. Also, allow to show occupancy values in a nice way. Signed-off-by: Jiri Pirko --- devlink/devlink.c | 349 ++++++++++++++++++++++++++++++++++++++++ include/linux/devlink.h | 6 + 2 files changed, 355 insertions(+) diff --git a/devlink/devlink.c b/devlink/devlink.c index 228807f8..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) @@ -275,6 +281,12 @@ static int attr_cb(const struct nlattr *attr, void *data) 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; } @@ -864,6 +876,7 @@ static bool dl_dump_filter(struct dl *dl, struct nlattr **tb) 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) { @@ -885,6 +898,12 @@ static bool dl_dump_filter(struct dl *dl, struct nlattr **tb) 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; } @@ -1168,6 +1187,9 @@ static void cmd_sb_help(void) 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) @@ -1504,6 +1526,330 @@ static int cmd_sb_tc(struct dl *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")) { @@ -1522,6 +1868,9 @@ static int cmd_sb(struct dl *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; diff --git a/include/linux/devlink.h b/include/linux/devlink.h index 9c1aa578..ba0073b2 100644 --- a/include/linux/devlink.h +++ b/include/linux/devlink.h @@ -53,6 +53,10 @@ enum devlink_command { 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, @@ -119,6 +123,8 @@ enum devlink_attr { 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 */ From 4bf138d6d2747b198fc0a78f5fe4e1c9287e9e90 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 15 Apr 2016 09:51:53 +0200 Subject: [PATCH 15/15] devlink: add manpage for shared buffer Manpage for devlink "sb" object. Signed-off-by: Jiri Pirko --- man/man8/devlink-dev.8 | 1 + man/man8/devlink-monitor.8 | 1 + man/man8/devlink-port.8 | 1 + man/man8/devlink-sb.8 | 313 +++++++++++++++++++++++++++++++++++++ 4 files changed, 316 insertions(+) create mode 100644 man/man8/devlink-sb.8 diff --git a/man/man8/devlink-dev.8 b/man/man8/devlink-dev.8 index af96a298..62bcead3 100644 --- a/man/man8/devlink-dev.8 +++ b/man/man8/devlink-dev.8 @@ -52,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 d78837c7..a639d01f 100644 --- a/man/man8/devlink-port.8 +++ b/man/man8/devlink-port.8 @@ -120,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