All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch iproute2 00/11] devlink: add support for shared buffer configuration and control
@ 2016-04-15  7:51 Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 01/11] devlink: fix "devlink port" help message Jiri Pirko
                   ` (13 more replies)
  0 siblings, 14 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

Jiri Pirko (11):
  devlink: fix "devlink port" help message
  list: add list_for_each_entry_reverse macro
  list: add list_add_tail helper
  devlink: introduce pr_out_port_handle helper
  devlink: introduce helper to print out nice names (ifnames)
  devlink: split dl_argv_parse_put to parse and put parts
  devlink: introduce dump filtering function
  devlink: allow to parse both devlink and port handle in the same time
  devlink: implement shared buffer support
  devlink: implement shared buffer occupancy control
  devlink: add manpage for shared buffer

 devlink/devlink.c          | 1310 +++++++++++++++++++++++++++++++++++++++++---
 include/linux/devlink.h    |   63 +++
 include/list.h             |   16 +
 man/man8/devlink-dev.8     |    2 +
 man/man8/devlink-monitor.8 |    1 +
 man/man8/devlink-port.8    |    2 +
 man/man8/devlink-sb.8      |  313 +++++++++++
 man/man8/devlink.8         |    5 +
 8 files changed, 1636 insertions(+), 76 deletions(-)
 create mode 100644 man/man8/devlink-sb.8

-- 
2.5.5

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [patch iproute2 01/11] devlink: fix "devlink port" help message
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 02/11] list: add list_for_each_entry_reverse macro Jiri Pirko
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

"dl" -> "devlink"

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 devlink/devlink.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index c2da850..39f423a 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)
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 02/11] list: add list_for_each_entry_reverse macro
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 01/11] devlink: fix "devlink port" help message Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 03/11] list: add list_add_tail helper Jiri Pirko
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 include/list.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/include/list.h b/include/list.h
index cdebe4d..b549c3e 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;
 };
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 03/11] list: add list_add_tail helper
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 01/11] devlink: fix "devlink port" help message Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 02/11] list: add list_for_each_entry_reverse macro Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 04/11] devlink: introduce pr_out_port_handle helper Jiri Pirko
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 include/list.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/include/list.h b/include/list.h
index b549c3e..5b529dc 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;
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 04/11] devlink: introduce pr_out_port_handle helper
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (2 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 03/11] list: add list_add_tail helper Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 05/11] devlink: introduce helper to print out nice names (ifnames) Jiri Pirko
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 devlink/devlink.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index 39f423a..0904e07 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);
 
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 05/11] devlink: introduce helper to print out nice names (ifnames)
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (3 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 04/11] devlink: introduce pr_out_port_handle helper Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 06/11] devlink: split dl_argv_parse_put to parse and put parts Jiri Pirko
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

By default, ifnames will be printed out. User can turn that off using
"-n" option on the command line.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 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 0904e07..5e08666 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 7878d89..af96a29 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 e6ae686..d78837c 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 f608ccc..df00f4f 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
 
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 06/11] devlink: split dl_argv_parse_put to parse and put parts
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (4 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 05/11] devlink: introduce helper to print out nice names (ifnames) Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 07/11] devlink: introduce dump filtering function Jiri Pirko
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

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 <jiri@mellanox.com>
---
 devlink/devlink.c | 105 +++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 69 insertions(+), 36 deletions(-)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index 5e08666..0c2132f 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");
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 07/11] devlink: introduce dump filtering function
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (5 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 06/11] devlink: split dl_argv_parse_put to parse and put parts Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 08/11] devlink: allow to parse both devlink and port handle in the same time Jiri Pirko
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

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 <jiri@mellanox.com>
---
 devlink/devlink.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index 0c2132f..d436bbf 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");
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 08/11] devlink: allow to parse both devlink and port handle in the same time
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (6 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 07/11] devlink: introduce dump filtering function Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 09/11] devlink: implement shared buffer support Jiri Pirko
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

For filtering purposes, it makes sense for used to either specify
devlink handle of port handle.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 devlink/devlink.c | 109 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 90 insertions(+), 19 deletions(-)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index d436bbf..e2e0413 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 __dl_argv_handle_port(str, p_bus_name,
+					     p_dev_name, p_port_index);
+	} else if (slash_count == 0) {
+		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;
-		}
-		strslashrsplit(handlestr, p_bus_name, p_dev_name);
+		*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 = ifname_map_lookup(dl, str, p_bus_name, p_dev_name,
-					p_port_index);
-		if (err) {
-			pr_err("Netdevice \"%s\" not found\n", str);
+		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;
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 09/11] devlink: implement shared buffer support
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (7 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 08/11] devlink: allow to parse both devlink and port handle in the same time Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 10/11] devlink: implement shared buffer occupancy control Jiri Pirko
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

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 <jiri@mellanox.com>
---
 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 e2e0413..228807f 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 "<unknown type>";
+	}
+}
+
+static const char *threshold_type_name(uint8_t type)
+{
+	switch (type) {
+	case DEVLINK_SB_THRESHOLD_TYPE_STATIC: return "static";
+	case DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC: return "dynamic";
+	default: return "<unknown type>";
+	}
+}
+
+static void pr_out_sb_pool(struct nlattr **tb)
+{
+	pr_out_handle(tb);
+	pr_out(": sb %u pool %u type %s size %u thtype %s\n",
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
+	       pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])),
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE]),
+	       threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])));
+}
+
+static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
+	    !tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] ||
+	    !tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])
+		return MNL_CB_ERROR;
+	pr_out_sb_pool(tb);
+	return MNL_CB_OK;
+}
+
+static int cmd_sb_pool_show(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+	int err;
+
+	if (dl_argc(dl) == 0)
+		flags |= NLM_F_DUMP;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_GET, flags);
+
+	if (dl_argc(dl) > 0) {
+		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL,
+					DL_OPT_SB);
+		if (err)
+			return err;
+	}
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, NULL);
+}
+
+static int cmd_sb_pool_set(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_SET,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL |
+				DL_OPT_SB_SIZE | DL_OPT_SB_THTYPE, DL_OPT_SB);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
+static int cmd_sb_pool(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help")) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show") ||
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
+		dl_arg_inc(dl);
+		return cmd_sb_pool_show(dl);
+	} else if (dl_argv_match(dl, "set")) {
+		dl_arg_inc(dl);
+		return cmd_sb_pool_set(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb)
+{
+	pr_out_port_handle_nice(dl, tb);
+	pr_out(": sb %u pool %u threshold %u\n",
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
+}
+
+static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct dl *dl = data;
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
+	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
+		return MNL_CB_ERROR;
+	pr_out_sb_port_pool(dl, tb);
+	return MNL_CB_OK;
+}
+
+static int cmd_sb_port_pool_show(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+	int err;
+
+	if (dl_argc(dl) == 0)
+		flags |= NLM_F_DUMP;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
+
+	if (dl_argc(dl) > 0) {
+		err = dl_argv_parse_put(nlh, dl,
+					DL_OPT_HANDLEP | DL_OPT_SB_POOL,
+					DL_OPT_SB);
+		if (err)
+			return err;
+	}
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl);
+}
+
+static int cmd_sb_port_pool_set(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_SET,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_POOL |
+				DL_OPT_SB_TH, DL_OPT_SB);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
+static int cmd_sb_port_pool(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help")) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show") ||
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
+		dl_arg_inc(dl);
+		return cmd_sb_port_pool_show(dl);
+	} else if (dl_argv_match(dl, "set")) {
+		dl_arg_inc(dl);
+		return cmd_sb_port_pool_set(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static int cmd_sb_port(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "pool")) {
+		dl_arg_inc(dl);
+		return cmd_sb_port_pool(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb)
+{
+	pr_out_port_handle_nice(dl, tb);
+	pr_out(": sb %u tc %u type %s pool %u threshold %u\n",
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]),
+	       pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])),
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
+}
+
+static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct dl *dl = data;
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
+	    !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
+	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
+		return MNL_CB_ERROR;
+	pr_out_sb_tc_bind(dl, tb);
+	return MNL_CB_OK;
+}
+
+static int cmd_sb_tc_bind_show(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+	int err;
+
+	if (dl_argc(dl) == 0)
+		flags |= NLM_F_DUMP;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
+
+	if (dl_argc(dl) > 0) {
+		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
+					DL_OPT_SB_TYPE, DL_OPT_SB);
+		if (err)
+			return err;
+	}
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl);
+}
+
+static int cmd_sb_tc_bind_set(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_SET,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
+				DL_OPT_SB_TYPE | DL_OPT_SB_POOL | DL_OPT_SB_TH,
+				DL_OPT_SB);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
+static int cmd_sb_tc_bind(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help")) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show") ||
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
+		dl_arg_inc(dl);
+		return cmd_sb_tc_bind_show(dl);
+	} else if (dl_argv_match(dl, "set")) {
+		dl_arg_inc(dl);
+		return cmd_sb_tc_bind_set(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static int cmd_sb_tc(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "bind")) {
+		dl_arg_inc(dl);
+		return cmd_sb_tc_bind(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+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 c9fee57..9c1aa57 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 */
 
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 10/11] devlink: implement shared buffer occupancy control
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (8 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 09/11] devlink: implement shared buffer support Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-15  7:51 ` [patch iproute2 11/11] devlink: add manpage for shared buffer Jiri Pirko
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

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 <jiri@mellanox.com>
---
 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 228807f..ffefa86 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 9c1aa57..ba0073b 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 */
 
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [patch iproute2 11/11] devlink: add manpage for shared buffer
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (9 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 10/11] devlink: implement shared buffer occupancy control Jiri Pirko
@ 2016-04-15  7:51 ` Jiri Pirko
  2016-04-19 15:01 ` [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Stephen Hemminger
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-04-15  7:51 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

Manpage for devlink "sb" object.

Signed-off-by: Jiri Pirko <jiri@mellanox.com>
---
 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 af96a29..62bcead 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 98134c3..13fe641 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 d78837c..a639d01 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 0000000..ffb5553
--- /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 <jiri@mellanox.com>
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [patch iproute2 00/11] devlink: add support for shared buffer configuration and control
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (10 preceding siblings ...)
  2016-04-15  7:51 ` [patch iproute2 11/11] devlink: add manpage for shared buffer Jiri Pirko
@ 2016-04-19 15:01 ` Stephen Hemminger
  2016-05-13 21:44 ` Stephen Hemminger
  2016-05-14 13:21 ` [iproute2 net-next repost 1/2] devlink: implement shared buffer support Jiri Pirko
  13 siblings, 0 replies; 17+ messages in thread
From: Stephen Hemminger @ 2016-04-19 15:01 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

On Fri, 15 Apr 2016 09:51:42 +0200
Jiri Pirko <jiri@resnulli.us> wrote:

> From: Jiri Pirko <jiri@mellanox.com>
> 
> Jiri Pirko (11):
>   devlink: fix "devlink port" help message
>   list: add list_for_each_entry_reverse macro
>   list: add list_add_tail helper
>   devlink: introduce pr_out_port_handle helper
>   devlink: introduce helper to print out nice names (ifnames)
>   devlink: split dl_argv_parse_put to parse and put parts
>   devlink: introduce dump filtering function
>   devlink: allow to parse both devlink and port handle in the same time
>   devlink: implement shared buffer support
>   devlink: implement shared buffer occupancy control
>   devlink: add manpage for shared buffer
> 
>  devlink/devlink.c          | 1310 +++++++++++++++++++++++++++++++++++++++++---
>  include/linux/devlink.h    |   63 +++
>  include/list.h             |   16 +
>  man/man8/devlink-dev.8     |    2 +
>  man/man8/devlink-monitor.8 |    1 +
>  man/man8/devlink-port.8    |    2 +
>  man/man8/devlink-sb.8      |  313 +++++++++++
>  man/man8/devlink.8         |    5 +
>  8 files changed, 1636 insertions(+), 76 deletions(-)
>  create mode 100644 man/man8/devlink-sb.8
> 

Applied

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [patch iproute2 00/11] devlink: add support for shared buffer configuration and control
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (11 preceding siblings ...)
  2016-04-19 15:01 ` [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Stephen Hemminger
@ 2016-05-13 21:44 ` Stephen Hemminger
  2016-05-14 13:21 ` [iproute2 net-next repost 1/2] devlink: implement shared buffer support Jiri Pirko
  13 siblings, 0 replies; 17+ messages in thread
From: Stephen Hemminger @ 2016-05-13 21:44 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

On Fri, 15 Apr 2016 09:51:42 +0200
Jiri Pirko <jiri@resnulli.us> wrote:

> From: Jiri Pirko <jiri@mellanox.com>
> 
> Jiri Pirko (11):
>   devlink: fix "devlink port" help message
>   list: add list_for_each_entry_reverse macro
>   list: add list_add_tail helper
>   devlink: introduce pr_out_port_handle helper
>   devlink: introduce helper to print out nice names (ifnames)
>   devlink: split dl_argv_parse_put to parse and put parts
>   devlink: introduce dump filtering function
>   devlink: allow to parse both devlink and port handle in the same time
>   devlink: implement shared buffer support
>   devlink: implement shared buffer occupancy control
>   devlink: add manpage for shared buffer
> 
>  devlink/devlink.c          | 1310 +++++++++++++++++++++++++++++++++++++++++---
>  include/linux/devlink.h    |   63 +++
>  include/list.h             |   16 +
>  man/man8/devlink-dev.8     |    2 +
>  man/man8/devlink-monitor.8 |    1 +
>  man/man8/devlink-port.8    |    2 +
>  man/man8/devlink-sb.8      |  313 +++++++++++
>  man/man8/devlink.8         |    5 +
>  8 files changed, 1636 insertions(+), 76 deletions(-)
>  create mode 100644 man/man8/devlink-sb.8
> 

I had to revert all this. Because you are depending on headers in net-next.
Please resubmit against net-next tree of iproute.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [iproute2 net-next repost 1/2] devlink: implement shared buffer support
  2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
                   ` (12 preceding siblings ...)
  2016-05-13 21:44 ` Stephen Hemminger
@ 2016-05-14 13:21 ` Jiri Pirko
  2016-05-14 13:21   ` [iproute2 net-next repost 2/2] devlink: implement shared buffer occupancy control Jiri Pirko
  2016-05-16 18:23   ` [iproute2 net-next repost 1/2] devlink: implement shared buffer support Stephen Hemminger
  13 siblings, 2 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-05-14 13:21 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

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 <jiri@mellanox.com>
---
 devlink/devlink.c | 653 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 652 insertions(+), 1 deletion(-)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index 89a3083..ca3f586 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;
 }
@@ -307,6 +357,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;
@@ -346,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);
@@ -486,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);
@@ -513,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)
 {
@@ -562,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;
@@ -570,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;
@@ -581,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;
 }
 
@@ -603,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,
@@ -648,6 +889,39 @@ static void pr_out_port_handle(struct nlattr **tb)
 			     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);
@@ -850,6 +1124,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 "<unknown type>";
+	}
+}
+
+static const char *threshold_type_name(uint8_t type)
+{
+	switch (type) {
+	case DEVLINK_SB_THRESHOLD_TYPE_STATIC: return "static";
+	case DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC: return "dynamic";
+	default: return "<unknown type>";
+	}
+}
+
+static void pr_out_sb_pool(struct nlattr **tb)
+{
+	pr_out_handle(tb);
+	pr_out(": sb %u pool %u type %s size %u thtype %s\n",
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
+	       pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])),
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE]),
+	       threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])));
+}
+
+static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
+	    !tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] ||
+	    !tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])
+		return MNL_CB_ERROR;
+	pr_out_sb_pool(tb);
+	return MNL_CB_OK;
+}
+
+static int cmd_sb_pool_show(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+	int err;
+
+	if (dl_argc(dl) == 0)
+		flags |= NLM_F_DUMP;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_GET, flags);
+
+	if (dl_argc(dl) > 0) {
+		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL,
+					DL_OPT_SB);
+		if (err)
+			return err;
+	}
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, NULL);
+}
+
+static int cmd_sb_pool_set(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_SET,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL |
+				DL_OPT_SB_SIZE | DL_OPT_SB_THTYPE, DL_OPT_SB);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
+static int cmd_sb_pool(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help")) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show") ||
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
+		dl_arg_inc(dl);
+		return cmd_sb_pool_show(dl);
+	} else if (dl_argv_match(dl, "set")) {
+		dl_arg_inc(dl);
+		return cmd_sb_pool_set(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb)
+{
+	pr_out_port_handle_nice(dl, tb);
+	pr_out(": sb %u pool %u threshold %u\n",
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
+}
+
+static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct dl *dl = data;
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
+	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
+		return MNL_CB_ERROR;
+	pr_out_sb_port_pool(dl, tb);
+	return MNL_CB_OK;
+}
+
+static int cmd_sb_port_pool_show(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+	int err;
+
+	if (dl_argc(dl) == 0)
+		flags |= NLM_F_DUMP;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
+
+	if (dl_argc(dl) > 0) {
+		err = dl_argv_parse_put(nlh, dl,
+					DL_OPT_HANDLEP | DL_OPT_SB_POOL,
+					DL_OPT_SB);
+		if (err)
+			return err;
+	}
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl);
+}
+
+static int cmd_sb_port_pool_set(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_SET,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_POOL |
+				DL_OPT_SB_TH, DL_OPT_SB);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
+static int cmd_sb_port_pool(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help")) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show") ||
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
+		dl_arg_inc(dl);
+		return cmd_sb_port_pool_show(dl);
+	} else if (dl_argv_match(dl, "set")) {
+		dl_arg_inc(dl);
+		return cmd_sb_port_pool_set(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static int cmd_sb_port(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "pool")) {
+		dl_arg_inc(dl);
+		return cmd_sb_port_pool(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb)
+{
+	pr_out_port_handle_nice(dl, tb);
+	pr_out(": sb %u tc %u type %s pool %u threshold %u\n",
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]),
+	       pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])),
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
+}
+
+static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct dl *dl = data;
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
+	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
+	    !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
+	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
+		return MNL_CB_ERROR;
+	pr_out_sb_tc_bind(dl, tb);
+	return MNL_CB_OK;
+}
+
+static int cmd_sb_tc_bind_show(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+	int err;
+
+	if (dl_argc(dl) == 0)
+		flags |= NLM_F_DUMP;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
+
+	if (dl_argc(dl) > 0) {
+		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
+					DL_OPT_SB_TYPE, DL_OPT_SB);
+		if (err)
+			return err;
+	}
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl);
+}
+
+static int cmd_sb_tc_bind_set(struct dl *dl)
+{
+	struct nlmsghdr *nlh;
+	int err;
+
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_SET,
+			       NLM_F_REQUEST | NLM_F_ACK);
+
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
+				DL_OPT_SB_TYPE | DL_OPT_SB_POOL | DL_OPT_SB_TH,
+				DL_OPT_SB);
+	if (err)
+		return err;
+
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
+}
+
+static int cmd_sb_tc_bind(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help")) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show") ||
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
+		dl_arg_inc(dl);
+		return cmd_sb_tc_bind_show(dl);
+	} else if (dl_argv_match(dl, "set")) {
+		dl_arg_inc(dl);
+		return cmd_sb_tc_bind_set(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+static int cmd_sb_tc(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
+		cmd_sb_help();
+		return 0;
+	} else if (dl_argv_match(dl, "bind")) {
+		dl_arg_inc(dl);
+		return cmd_sb_tc_bind(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
+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) {
@@ -985,7 +1633,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");
 }
 
@@ -1000,6 +1648,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);
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [iproute2 net-next repost 2/2] devlink: implement shared buffer occupancy control
  2016-05-14 13:21 ` [iproute2 net-next repost 1/2] devlink: implement shared buffer support Jiri Pirko
@ 2016-05-14 13:21   ` Jiri Pirko
  2016-05-16 18:23   ` [iproute2 net-next repost 1/2] devlink: implement shared buffer support Stephen Hemminger
  1 sibling, 0 replies; 17+ messages in thread
From: Jiri Pirko @ 2016-05-14 13:21 UTC (permalink / raw)
  To: netdev
  Cc: stephen, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

From: Jiri Pirko <jiri@mellanox.com>

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 <jiri@mellanox.com>
---
 devlink/devlink.c | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 378 insertions(+)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index ca3f586..ffefa86 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;
 }
@@ -858,6 +870,42 @@ 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];
+	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)
 {
@@ -1139,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)
@@ -1475,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")) {
@@ -1493,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;
-- 
2.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [iproute2 net-next repost 1/2] devlink: implement shared buffer support
  2016-05-14 13:21 ` [iproute2 net-next repost 1/2] devlink: implement shared buffer support Jiri Pirko
  2016-05-14 13:21   ` [iproute2 net-next repost 2/2] devlink: implement shared buffer occupancy control Jiri Pirko
@ 2016-05-16 18:23   ` Stephen Hemminger
  1 sibling, 0 replies; 17+ messages in thread
From: Stephen Hemminger @ 2016-05-16 18:23 UTC (permalink / raw)
  To: Jiri Pirko
  Cc: netdev, davem, idosch, eladr, yotamg, ogerlitz, roopa, nikolay,
	jhs, john.fastabend, rami.rosen, gospo, sfeldma

On Sat, 14 May 2016 15:21:01 +0200
Jiri Pirko <jiri@resnulli.us> wrote:

> From: Jiri Pirko <jiri@mellanox.com>
> 
> 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 <jiri@mellanox.com>
> ---
>  devlink/devlink.c | 653 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 652 insertions(+), 1 deletion(-)

Both applied to net-next

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2016-05-16 18:23 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-15  7:51 [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 01/11] devlink: fix "devlink port" help message Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 02/11] list: add list_for_each_entry_reverse macro Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 03/11] list: add list_add_tail helper Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 04/11] devlink: introduce pr_out_port_handle helper Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 05/11] devlink: introduce helper to print out nice names (ifnames) Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 06/11] devlink: split dl_argv_parse_put to parse and put parts Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 07/11] devlink: introduce dump filtering function Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 08/11] devlink: allow to parse both devlink and port handle in the same time Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 09/11] devlink: implement shared buffer support Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 10/11] devlink: implement shared buffer occupancy control Jiri Pirko
2016-04-15  7:51 ` [patch iproute2 11/11] devlink: add manpage for shared buffer Jiri Pirko
2016-04-19 15:01 ` [patch iproute2 00/11] devlink: add support for shared buffer configuration and control Stephen Hemminger
2016-05-13 21:44 ` Stephen Hemminger
2016-05-14 13:21 ` [iproute2 net-next repost 1/2] devlink: implement shared buffer support Jiri Pirko
2016-05-14 13:21   ` [iproute2 net-next repost 2/2] devlink: implement shared buffer occupancy control Jiri Pirko
2016-05-16 18:23   ` [iproute2 net-next repost 1/2] devlink: implement shared buffer support Stephen Hemminger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.