Blame SOURCES/0194-devlink-implement-shared-buffer-support.patch

049c96
From ae919a21446e668c06b5abb440b412487e60ece6 Mon Sep 17 00:00:00 2001
049c96
From: Phil Sutter <psutter@redhat.com>
049c96
Date: Sat, 9 Jul 2016 11:33:14 +0200
049c96
Subject: [PATCH] devlink: implement shared buffer support
049c96
049c96
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1342515
049c96
Upstream Status: iproute2.git commit b56700bf8add4
049c96
049c96
commit b56700bf8add4ebb2fe451c85f50602b58a886a2
049c96
Author: Jiri Pirko <jiri@mellanox.com>
049c96
Date:   Fri Apr 15 09:51:51 2016 +0200
049c96
049c96
    devlink: implement shared buffer support
049c96
049c96
    Implement kernel devlink shared buffer interface. Introduce new object
049c96
    "sb" and allow to browse the shared buffer parameters and also change
049c96
    configuration.
049c96
049c96
    Signed-off-by: Jiri Pirko <jiri@mellanox.com>
049c96
---
049c96
 devlink/devlink.c       | 603 +++++++++++++++++++++++++++++++++++++++++++++++-
049c96
 include/linux/devlink.h |  57 +++++
049c96
 2 files changed, 659 insertions(+), 1 deletion(-)
049c96
049c96
diff --git a/devlink/devlink.c b/devlink/devlink.c
049c96
index e2e0413..228807f 100644
049c96
--- a/devlink/devlink.c
049c96
+++ b/devlink/devlink.c
049c96
@@ -114,6 +114,13 @@ static void ifname_map_free(struct ifname_map *ifname_map)
049c96
 #define DL_OPT_HANDLEP		BIT(1)
049c96
 #define DL_OPT_PORT_TYPE	BIT(2)
049c96
 #define DL_OPT_PORT_COUNT	BIT(3)
049c96
+#define DL_OPT_SB		BIT(4)
049c96
+#define DL_OPT_SB_POOL		BIT(5)
049c96
+#define DL_OPT_SB_SIZE		BIT(6)
049c96
+#define DL_OPT_SB_TYPE		BIT(7)
049c96
+#define DL_OPT_SB_THTYPE	BIT(8)
049c96
+#define DL_OPT_SB_TH		BIT(9)
049c96
+#define DL_OPT_SB_TC		BIT(10)
049c96
 
049c96
 struct dl_opts {
049c96
 	uint32_t present; /* flags of present items */
049c96
@@ -122,6 +129,13 @@ struct dl_opts {
049c96
 	uint32_t port_index;
049c96
 	enum devlink_port_type port_type;
049c96
 	uint32_t port_count;
049c96
+	uint32_t sb_index;
049c96
+	uint16_t sb_pool_index;
049c96
+	uint32_t sb_pool_size;
049c96
+	enum devlink_sb_pool_type sb_pool_type;
049c96
+	enum devlink_sb_threshold_type sb_pool_thtype;
049c96
+	uint32_t sb_threshold;
049c96
+	uint16_t sb_tc_index;
049c96
 };
049c96
 
049c96
 struct dl {
049c96
@@ -225,6 +239,42 @@ static int attr_cb(const struct nlattr *attr, void *data)
049c96
 	if (type == DEVLINK_ATTR_PORT_IBDEV_NAME &&
049c96
 	    mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0)
049c96
 		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_INDEX &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_SIZE &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_INGRESS_POOL_COUNT &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_EGRESS_POOL_COUNT &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_INGRESS_TC_COUNT &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_EGRESS_TC_COUNT &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_POOL_INDEX &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_POOL_TYPE &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_POOL_SIZE &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_THRESHOLD &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
+	if (type == DEVLINK_ATTR_SB_TC_INDEX &&
049c96
+	    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
049c96
+		return MNL_CB_ERROR;
049c96
 	tb[type] = attr;
049c96
 	return MNL_CB_OK;
049c96
 }
049c96
@@ -363,6 +413,20 @@ static int strtouint32_t(const char *str, uint32_t *p_val)
049c96
 	return 0;
049c96
 }
049c96
 
049c96
+static int strtouint16_t(const char *str, uint16_t *p_val)
049c96
+{
049c96
+	char *endptr;
049c96
+	unsigned long int val;
049c96
+
049c96
+	val = strtoul(str, &endptr, 10);
049c96
+	if (endptr == str || *endptr != '\0')
049c96
+		return -EINVAL;
049c96
+	if (val > USHRT_MAX)
049c96
+		return -ERANGE;
049c96
+	*p_val = val;
049c96
+	return 0;
049c96
+}
049c96
+
049c96
 static int __dl_argv_handle(char *str, char **p_bus_name, char **p_dev_name)
049c96
 {
049c96
 	strslashrsplit(str, p_bus_name, p_dev_name);
049c96
@@ -503,6 +567,24 @@ static int dl_argv_uint32_t(struct dl *dl, uint32_t *p_val)
049c96
 	return 0;
049c96
 }
049c96
 
049c96
+static int dl_argv_uint16_t(struct dl *dl, uint16_t *p_val)
049c96
+{
049c96
+	char *str = dl_argv_next(dl);
049c96
+	int err;
049c96
+
049c96
+	if (!str) {
049c96
+		pr_err("Unsigned number argument expected\n");
049c96
+		return -EINVAL;
049c96
+	}
049c96
+
049c96
+	err = strtouint16_t(str, p_val);
049c96
+	if (err) {
049c96
+		pr_err("\"%s\" is not a number or not within range\n", str);
049c96
+		return err;
049c96
+	}
049c96
+	return 0;
049c96
+}
049c96
+
049c96
 static int dl_argv_str(struct dl *dl, const char **p_str)
049c96
 {
049c96
 	const char *str = dl_argv_next(dl);
049c96
@@ -530,6 +612,33 @@ static int port_type_get(const char *typestr, enum devlink_port_type *p_type)
049c96
 	return 0;
049c96
 }
049c96
 
049c96
+static int pool_type_get(const char *typestr, enum devlink_sb_pool_type *p_type)
049c96
+{
049c96
+	if (strcmp(typestr, "ingress") == 0) {
049c96
+		*p_type = DEVLINK_SB_POOL_TYPE_INGRESS;
049c96
+	} else if (strcmp(typestr, "egress") == 0) {
049c96
+		*p_type = DEVLINK_SB_POOL_TYPE_EGRESS;
049c96
+	} else {
049c96
+		pr_err("Unknown pool type \"%s\"\n", typestr);
049c96
+		return -EINVAL;
049c96
+	}
049c96
+	return 0;
049c96
+}
049c96
+
049c96
+static int threshold_type_get(const char *typestr,
049c96
+			      enum devlink_sb_threshold_type *p_type)
049c96
+{
049c96
+	if (strcmp(typestr, "static") == 0) {
049c96
+		*p_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC;
049c96
+	} else if (strcmp(typestr, "dynamic") == 0) {
049c96
+		*p_type = DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC;
049c96
+	} else {
049c96
+		pr_err("Unknown threshold type \"%s\"\n", typestr);
049c96
+		return -EINVAL;
049c96
+	}
049c96
+	return 0;
049c96
+}
049c96
+
049c96
 static int dl_argv_parse(struct dl *dl, uint32_t o_required,
049c96
 			 uint32_t o_optional)
049c96
 {
049c96
@@ -579,6 +688,66 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
049c96
 			if (err)
049c96
 				return err;
049c96
 			o_found |= DL_OPT_PORT_COUNT;
049c96
+		} else if (dl_argv_match(dl, "sb") &&
049c96
+			   (o_all & DL_OPT_SB)) {
049c96
+			dl_arg_inc(dl);
049c96
+			err = dl_argv_uint32_t(dl, &opts->sb_index);
049c96
+			if (err)
049c96
+				return err;
049c96
+			o_found |= DL_OPT_SB;
049c96
+		} else if (dl_argv_match(dl, "pool") &&
049c96
+			   (o_all & DL_OPT_SB_POOL)) {
049c96
+			dl_arg_inc(dl);
049c96
+			err = dl_argv_uint16_t(dl, &opts->sb_pool_index);
049c96
+			if (err)
049c96
+				return err;
049c96
+			o_found |= DL_OPT_SB_POOL;
049c96
+		} else if (dl_argv_match(dl, "size") &&
049c96
+			   (o_all & DL_OPT_SB_SIZE)) {
049c96
+			dl_arg_inc(dl);
049c96
+			err = dl_argv_uint32_t(dl, &opts->sb_pool_size);
049c96
+			if (err)
049c96
+				return err;
049c96
+			o_found |= DL_OPT_SB_SIZE;
049c96
+		} else if (dl_argv_match(dl, "type") &&
049c96
+			   (o_all & DL_OPT_SB_TYPE)) {
049c96
+			const char *typestr;
049c96
+
049c96
+			dl_arg_inc(dl);
049c96
+			err = dl_argv_str(dl, &typestr);
049c96
+			if (err)
049c96
+				return err;
049c96
+			err = pool_type_get(typestr, &opts->sb_pool_type);
049c96
+			if (err)
049c96
+				return err;
049c96
+			o_found |= DL_OPT_SB_TYPE;
049c96
+		} else if (dl_argv_match(dl, "thtype") &&
049c96
+			   (o_all & DL_OPT_SB_THTYPE)) {
049c96
+			const char *typestr;
049c96
+
049c96
+			dl_arg_inc(dl);
049c96
+			err = dl_argv_str(dl, &typestr);
049c96
+			if (err)
049c96
+				return err;
049c96
+			err = threshold_type_get(typestr,
049c96
+						 &opts->sb_pool_thtype);
049c96
+			if (err)
049c96
+				return err;
049c96
+			o_found |= DL_OPT_SB_THTYPE;
049c96
+		} else if (dl_argv_match(dl, "th") &&
049c96
+			   (o_all & DL_OPT_SB_TH)) {
049c96
+			dl_arg_inc(dl);
049c96
+			err = dl_argv_uint32_t(dl, &opts->sb_threshold);
049c96
+			if (err)
049c96
+				return err;
049c96
+			o_found |= DL_OPT_SB_TH;
049c96
+		} else if (dl_argv_match(dl, "tc") &&
049c96
+			   (o_all & DL_OPT_SB_TC)) {
049c96
+			dl_arg_inc(dl);
049c96
+			err = dl_argv_uint16_t(dl, &opts->sb_tc_index);
049c96
+			if (err)
049c96
+				return err;
049c96
+			o_found |= DL_OPT_SB_TC;
049c96
 		} else {
049c96
 			pr_err("Unknown option \"%s\"\n", dl_argv(dl));
049c96
 			return -EINVAL;
049c96
@@ -587,6 +756,11 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
049c96
 
049c96
 	opts->present = o_found;
049c96
 
049c96
+	if ((o_optional & DL_OPT_SB) && !(o_found & DL_OPT_SB)) {
049c96
+		opts->sb_index = 0;
049c96
+		opts->present |= DL_OPT_SB;
049c96
+	}
049c96
+
049c96
 	if ((o_required & DL_OPT_PORT_TYPE) && !(o_found & DL_OPT_PORT_TYPE)) {
049c96
 		pr_err("Port type option expected.\n");
049c96
 		return -EINVAL;
049c96
@@ -598,6 +772,35 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required,
049c96
 		return -EINVAL;
049c96
 	}
049c96
 
049c96
+	if ((o_required & DL_OPT_SB_POOL) && !(o_found & DL_OPT_SB_POOL)) {
049c96
+		pr_err("Pool index option expected.\n");
049c96
+		return -EINVAL;
049c96
+	}
049c96
+
049c96
+	if ((o_required & DL_OPT_SB_SIZE) && !(o_found & DL_OPT_SB_SIZE)) {
049c96
+		pr_err("Pool size option expected.\n");
049c96
+		return -EINVAL;
049c96
+	}
049c96
+
049c96
+	if ((o_required & DL_OPT_SB_TYPE) && !(o_found & DL_OPT_SB_TYPE)) {
049c96
+		pr_err("Pool type option expected.\n");
049c96
+		return -EINVAL;
049c96
+	}
049c96
+
049c96
+	if ((o_required & DL_OPT_SB_THTYPE) && !(o_found & DL_OPT_SB_THTYPE)) {
049c96
+		pr_err("Pool threshold type option expected.\n");
049c96
+		return -EINVAL;
049c96
+	}
049c96
+
049c96
+	if ((o_required & DL_OPT_SB_TH) && !(o_found & DL_OPT_SB_TH)) {
049c96
+		pr_err("Threshold option expected.\n");
049c96
+		return -EINVAL;
049c96
+	}
049c96
+
049c96
+	if ((o_required & DL_OPT_SB_TC) && !(o_found & DL_OPT_SB_TC)) {
049c96
+		pr_err("TC index option expected.\n");
049c96
+		return -EINVAL;
049c96
+	}
049c96
 	return 0;
049c96
 }
049c96
 
049c96
@@ -620,6 +823,27 @@ static void dl_opts_put(struct nlmsghdr *nlh, struct dl *dl)
049c96
 	if (opts->present & DL_OPT_PORT_COUNT)
049c96
 		mnl_attr_put_u32(nlh, DEVLINK_ATTR_PORT_SPLIT_COUNT,
049c96
 				 opts->port_count);
049c96
+	if (opts->present & DL_OPT_SB)
049c96
+		mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_INDEX,
049c96
+				 opts->sb_index);
049c96
+	if (opts->present & DL_OPT_SB_POOL)
049c96
+		mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_POOL_INDEX,
049c96
+				 opts->sb_pool_index);
049c96
+	if (opts->present & DL_OPT_SB_SIZE)
049c96
+		mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_POOL_SIZE,
049c96
+				 opts->sb_pool_size);
049c96
+	if (opts->present & DL_OPT_SB_TYPE)
049c96
+		mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_TYPE,
049c96
+				opts->sb_pool_type);
049c96
+	if (opts->present & DL_OPT_SB_THTYPE)
049c96
+		mnl_attr_put_u8(nlh, DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,
049c96
+				opts->sb_pool_thtype);
049c96
+	if (opts->present & DL_OPT_SB_TH)
049c96
+		mnl_attr_put_u32(nlh, DEVLINK_ATTR_SB_THRESHOLD,
049c96
+				 opts->sb_threshold);
049c96
+	if (opts->present & DL_OPT_SB_TC)
049c96
+		mnl_attr_put_u16(nlh, DEVLINK_ATTR_SB_TC_INDEX,
049c96
+				 opts->sb_tc_index);
049c96
 }
049c96
 
049c96
 static int dl_argv_parse_put(struct nlmsghdr *nlh, struct dl *dl,
049c96
@@ -929,6 +1153,380 @@ static int cmd_port(struct dl *dl)
049c96
 	return -ENOENT;
049c96
 }
049c96
 
049c96
+static void cmd_sb_help(void)
049c96
+{
049c96
+	pr_out("Usage: devlink sb show [ DEV [ sb SB_INDEX ] ]\n");
049c96
+	pr_out("       devlink sb pool show [ DEV [ sb SB_INDEX ] pool POOL_INDEX ]\n");
049c96
+	pr_out("       devlink sb pool set DEV [ sb SB_INDEX ] pool POOL_INDEX\n");
049c96
+	pr_out("                           size POOL_SIZE thtype { static | dynamic }\n");
049c96
+	pr_out("       devlink sb port pool show [ DEV/PORT_INDEX [ sb SB_INDEX ]\n");
049c96
+	pr_out("                                   pool POOL_INDEX ]\n");
049c96
+	pr_out("       devlink sb port pool set DEV/PORT_INDEX [ sb SB_INDEX ]\n");
049c96
+	pr_out("                                pool POOL_INDEX th THRESHOLD\n");
049c96
+	pr_out("       devlink sb tc bind show [ DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n");
049c96
+	pr_out("                                 type { ingress | egress } ]\n");
049c96
+	pr_out("       devlink sb tc bind set DEV/PORT_INDEX [ sb SB_INDEX ] tc TC_INDEX\n");
049c96
+	pr_out("                              type { ingress | egress } pool POOL_INDEX\n");
049c96
+	pr_out("                              th THRESHOLD\n");
049c96
+}
049c96
+
049c96
+static void pr_out_sb(struct nlattr **tb)
049c96
+{
049c96
+	pr_out_handle(tb);
049c96
+	pr_out(": sb %u size %u ing_pools %u eg_pools %u ing_tcs %u eg_tcs %u\n",
049c96
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
049c96
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_SIZE]),
049c96
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT]),
049c96
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT]),
049c96
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT]),
049c96
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT]));
049c96
+}
049c96
+
049c96
+static int cmd_sb_show_cb(const struct nlmsghdr *nlh, void *data)
049c96
+{
049c96
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
049c96
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
049c96
+
049c96
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
049c96
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_SIZE] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_INGRESS_POOL_COUNT] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_EGRESS_POOL_COUNT] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_INGRESS_TC_COUNT] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_EGRESS_TC_COUNT])
049c96
+		return MNL_CB_ERROR;
049c96
+	pr_out_sb(tb);
049c96
+	return MNL_CB_OK;
049c96
+}
049c96
+
049c96
+static int cmd_sb_show(struct dl *dl)
049c96
+{
049c96
+	struct nlmsghdr *nlh;
049c96
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
049c96
+	int err;
049c96
+
049c96
+	if (dl_argc(dl) == 0)
049c96
+		flags |= NLM_F_DUMP;
049c96
+
049c96
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_GET, flags);
049c96
+
049c96
+	if (dl_argc(dl) > 0) {
049c96
+		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE, DL_OPT_SB);
049c96
+		if (err)
049c96
+			return err;
049c96
+	}
049c96
+
049c96
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_show_cb, NULL);
049c96
+}
049c96
+
049c96
+static const char *pool_type_name(uint8_t type)
049c96
+{
049c96
+	switch (type) {
049c96
+	case DEVLINK_SB_POOL_TYPE_INGRESS: return "ingress";
049c96
+	case DEVLINK_SB_POOL_TYPE_EGRESS: return "egress";
049c96
+	default: return "<unknown type>";
049c96
+	}
049c96
+}
049c96
+
049c96
+static const char *threshold_type_name(uint8_t type)
049c96
+{
049c96
+	switch (type) {
049c96
+	case DEVLINK_SB_THRESHOLD_TYPE_STATIC: return "static";
049c96
+	case DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC: return "dynamic";
049c96
+	default: return "<unknown type>";
049c96
+	}
049c96
+}
049c96
+
049c96
+static void pr_out_sb_pool(struct nlattr **tb)
049c96
+{
049c96
+	pr_out_handle(tb);
049c96
+	pr_out(": sb %u pool %u type %s size %u thtype %s\n",
049c96
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
049c96
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
049c96
+	       pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])),
049c96
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_POOL_SIZE]),
049c96
+	       threshold_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])));
049c96
+}
049c96
+
049c96
+static int cmd_sb_pool_show_cb(const struct nlmsghdr *nlh, void *data)
049c96
+{
049c96
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
049c96
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
049c96
+
049c96
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
049c96
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_INDEX] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_POOL_TYPE] || !tb[DEVLINK_ATTR_SB_POOL_SIZE] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE])
049c96
+		return MNL_CB_ERROR;
049c96
+	pr_out_sb_pool(tb);
049c96
+	return MNL_CB_OK;
049c96
+}
049c96
+
049c96
+static int cmd_sb_pool_show(struct dl *dl)
049c96
+{
049c96
+	struct nlmsghdr *nlh;
049c96
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
049c96
+	int err;
049c96
+
049c96
+	if (dl_argc(dl) == 0)
049c96
+		flags |= NLM_F_DUMP;
049c96
+
049c96
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_GET, flags);
049c96
+
049c96
+	if (dl_argc(dl) > 0) {
049c96
+		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL,
049c96
+					DL_OPT_SB);
049c96
+		if (err)
049c96
+			return err;
049c96
+	}
049c96
+
049c96
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_pool_show_cb, NULL);
049c96
+}
049c96
+
049c96
+static int cmd_sb_pool_set(struct dl *dl)
049c96
+{
049c96
+	struct nlmsghdr *nlh;
049c96
+	int err;
049c96
+
049c96
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_POOL_SET,
049c96
+			       NLM_F_REQUEST | NLM_F_ACK);
049c96
+
049c96
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLE | DL_OPT_SB_POOL |
049c96
+				DL_OPT_SB_SIZE | DL_OPT_SB_THTYPE, DL_OPT_SB);
049c96
+	if (err)
049c96
+		return err;
049c96
+
049c96
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
049c96
+}
049c96
+
049c96
+static int cmd_sb_pool(struct dl *dl)
049c96
+{
049c96
+	if (dl_argv_match(dl, "help")) {
049c96
+		cmd_sb_help();
049c96
+		return 0;
049c96
+	} else if (dl_argv_match(dl, "show") ||
049c96
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_pool_show(dl);
049c96
+	} else if (dl_argv_match(dl, "set")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_pool_set(dl);
049c96
+	}
049c96
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
049c96
+	return -ENOENT;
049c96
+}
049c96
+
049c96
+static void pr_out_sb_port_pool(struct dl *dl, struct nlattr **tb)
049c96
+{
049c96
+	pr_out_port_handle_nice(dl, tb);
049c96
+	pr_out(": sb %u pool %u threshold %u\n",
049c96
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
049c96
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
049c96
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
049c96
+}
049c96
+
049c96
+static int cmd_sb_port_pool_show_cb(const struct nlmsghdr *nlh, void *data)
049c96
+{
049c96
+	struct dl *dl = data;
049c96
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
049c96
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
049c96
+
049c96
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
049c96
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
049c96
+	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
049c96
+		return MNL_CB_ERROR;
049c96
+	pr_out_sb_port_pool(dl, tb);
049c96
+	return MNL_CB_OK;
049c96
+}
049c96
+
049c96
+static int cmd_sb_port_pool_show(struct dl *dl)
049c96
+{
049c96
+	struct nlmsghdr *nlh;
049c96
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
049c96
+	int err;
049c96
+
049c96
+	if (dl_argc(dl) == 0)
049c96
+		flags |= NLM_F_DUMP;
049c96
+
049c96
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_GET, flags);
049c96
+
049c96
+	if (dl_argc(dl) > 0) {
049c96
+		err = dl_argv_parse_put(nlh, dl,
049c96
+					DL_OPT_HANDLEP | DL_OPT_SB_POOL,
049c96
+					DL_OPT_SB);
049c96
+		if (err)
049c96
+			return err;
049c96
+	}
049c96
+
049c96
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_port_pool_show_cb, dl);
049c96
+}
049c96
+
049c96
+static int cmd_sb_port_pool_set(struct dl *dl)
049c96
+{
049c96
+	struct nlmsghdr *nlh;
049c96
+	int err;
049c96
+
049c96
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_PORT_POOL_SET,
049c96
+			       NLM_F_REQUEST | NLM_F_ACK);
049c96
+
049c96
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_POOL |
049c96
+				DL_OPT_SB_TH, DL_OPT_SB);
049c96
+	if (err)
049c96
+		return err;
049c96
+
049c96
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
049c96
+}
049c96
+
049c96
+static int cmd_sb_port_pool(struct dl *dl)
049c96
+{
049c96
+	if (dl_argv_match(dl, "help")) {
049c96
+		cmd_sb_help();
049c96
+		return 0;
049c96
+	} else if (dl_argv_match(dl, "show") ||
049c96
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_port_pool_show(dl);
049c96
+	} else if (dl_argv_match(dl, "set")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_port_pool_set(dl);
049c96
+	}
049c96
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
049c96
+	return -ENOENT;
049c96
+}
049c96
+
049c96
+static int cmd_sb_port(struct dl *dl)
049c96
+{
049c96
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
049c96
+		cmd_sb_help();
049c96
+		return 0;
049c96
+	} else if (dl_argv_match(dl, "pool")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_port_pool(dl);
049c96
+	}
049c96
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
049c96
+	return -ENOENT;
049c96
+}
049c96
+
049c96
+static void pr_out_sb_tc_bind(struct dl *dl, struct nlattr **tb)
049c96
+{
049c96
+	pr_out_port_handle_nice(dl, tb);
049c96
+	pr_out(": sb %u tc %u type %s pool %u threshold %u\n",
049c96
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_INDEX]),
049c96
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_TC_INDEX]),
049c96
+	       pool_type_name(mnl_attr_get_u8(tb[DEVLINK_ATTR_SB_POOL_TYPE])),
049c96
+	       mnl_attr_get_u16(tb[DEVLINK_ATTR_SB_POOL_INDEX]),
049c96
+	       mnl_attr_get_u32(tb[DEVLINK_ATTR_SB_THRESHOLD]));
049c96
+}
049c96
+
049c96
+static int cmd_sb_tc_bind_show_cb(const struct nlmsghdr *nlh, void *data)
049c96
+{
049c96
+	struct dl *dl = data;
049c96
+	struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {};
049c96
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
049c96
+
049c96
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
049c96
+	if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] ||
049c96
+	    !tb[DEVLINK_ATTR_PORT_INDEX] || !tb[DEVLINK_ATTR_SB_INDEX] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_TC_INDEX] || !tb[DEVLINK_ATTR_SB_POOL_TYPE] ||
049c96
+	    !tb[DEVLINK_ATTR_SB_POOL_INDEX] || !tb[DEVLINK_ATTR_SB_THRESHOLD])
049c96
+		return MNL_CB_ERROR;
049c96
+	pr_out_sb_tc_bind(dl, tb);
049c96
+	return MNL_CB_OK;
049c96
+}
049c96
+
049c96
+static int cmd_sb_tc_bind_show(struct dl *dl)
049c96
+{
049c96
+	struct nlmsghdr *nlh;
049c96
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
049c96
+	int err;
049c96
+
049c96
+	if (dl_argc(dl) == 0)
049c96
+		flags |= NLM_F_DUMP;
049c96
+
049c96
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_GET, flags);
049c96
+
049c96
+	if (dl_argc(dl) > 0) {
049c96
+		err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
049c96
+					DL_OPT_SB_TYPE, DL_OPT_SB);
049c96
+		if (err)
049c96
+			return err;
049c96
+	}
049c96
+
049c96
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, cmd_sb_tc_bind_show_cb, dl);
049c96
+}
049c96
+
049c96
+static int cmd_sb_tc_bind_set(struct dl *dl)
049c96
+{
049c96
+	struct nlmsghdr *nlh;
049c96
+	int err;
049c96
+
049c96
+	nlh = mnlg_msg_prepare(dl->nlg, DEVLINK_CMD_SB_TC_POOL_BIND_SET,
049c96
+			       NLM_F_REQUEST | NLM_F_ACK);
049c96
+
049c96
+	err = dl_argv_parse_put(nlh, dl, DL_OPT_HANDLEP | DL_OPT_SB_TC |
049c96
+				DL_OPT_SB_TYPE | DL_OPT_SB_POOL | DL_OPT_SB_TH,
049c96
+				DL_OPT_SB);
049c96
+	if (err)
049c96
+		return err;
049c96
+
049c96
+	return _mnlg_socket_sndrcv(dl->nlg, nlh, NULL, NULL);
049c96
+}
049c96
+
049c96
+static int cmd_sb_tc_bind(struct dl *dl)
049c96
+{
049c96
+	if (dl_argv_match(dl, "help")) {
049c96
+		cmd_sb_help();
049c96
+		return 0;
049c96
+	} else if (dl_argv_match(dl, "show") ||
049c96
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_tc_bind_show(dl);
049c96
+	} else if (dl_argv_match(dl, "set")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_tc_bind_set(dl);
049c96
+	}
049c96
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
049c96
+	return -ENOENT;
049c96
+}
049c96
+
049c96
+static int cmd_sb_tc(struct dl *dl)
049c96
+{
049c96
+	if (dl_argv_match(dl, "help") || dl_no_arg(dl)) {
049c96
+		cmd_sb_help();
049c96
+		return 0;
049c96
+	} else if (dl_argv_match(dl, "bind")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_tc_bind(dl);
049c96
+	}
049c96
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
049c96
+	return -ENOENT;
049c96
+}
049c96
+
049c96
+static int cmd_sb(struct dl *dl)
049c96
+{
049c96
+	if (dl_argv_match(dl, "help")) {
049c96
+		cmd_sb_help();
049c96
+		return 0;
049c96
+	} else if (dl_argv_match(dl, "show") ||
049c96
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_show(dl);
049c96
+	} else if (dl_argv_match(dl, "pool")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_pool(dl);
049c96
+	} else if (dl_argv_match(dl, "port")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_port(dl);
049c96
+	} else if (dl_argv_match(dl, "tc")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb_tc(dl);
049c96
+	}
049c96
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
049c96
+	return -ENOENT;
049c96
+}
049c96
+
049c96
 static const char *cmd_name(uint8_t cmd)
049c96
 {
049c96
 	switch (cmd) {
049c96
@@ -1064,7 +1662,7 @@ static int cmd_mon(struct dl *dl)
049c96
 static void help(void)
049c96
 {
049c96
 	pr_out("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
049c96
-	       "where  OBJECT := { dev | port | monitor }\n"
049c96
+	       "where  OBJECT := { dev | port | sb | monitor }\n"
049c96
 	       "       OPTIONS := { -V[ersion] | -n[no-nice-names] }\n");
049c96
 }
049c96
 
049c96
@@ -1079,6 +1677,9 @@ static int dl_cmd(struct dl *dl)
049c96
 	} else if (dl_argv_match(dl, "port")) {
049c96
 		dl_arg_inc(dl);
049c96
 		return cmd_port(dl);
049c96
+	} else if (dl_argv_match(dl, "sb")) {
049c96
+		dl_arg_inc(dl);
049c96
+		return cmd_sb(dl);
049c96
 	} else if (dl_argv_match(dl, "monitor")) {
049c96
 		dl_arg_inc(dl);
049c96
 		return cmd_mon(dl);
049c96
diff --git a/include/linux/devlink.h b/include/linux/devlink.h
049c96
index a96e1a0..d40699f 100644
049c96
--- a/include/linux/devlink.h
049c96
+++ b/include/linux/devlink.h
049c96
@@ -33,6 +33,26 @@ enum devlink_command {
049c96
 	DEVLINK_CMD_PORT_SPLIT,
049c96
 	DEVLINK_CMD_PORT_UNSPLIT,
049c96
 
049c96
+	DEVLINK_CMD_SB_GET,		/* can dump */
049c96
+	DEVLINK_CMD_SB_SET,
049c96
+	DEVLINK_CMD_SB_NEW,
049c96
+	DEVLINK_CMD_SB_DEL,
049c96
+
049c96
+	DEVLINK_CMD_SB_POOL_GET,	/* can dump */
049c96
+	DEVLINK_CMD_SB_POOL_SET,
049c96
+	DEVLINK_CMD_SB_POOL_NEW,
049c96
+	DEVLINK_CMD_SB_POOL_DEL,
049c96
+
049c96
+	DEVLINK_CMD_SB_PORT_POOL_GET,	/* can dump */
049c96
+	DEVLINK_CMD_SB_PORT_POOL_SET,
049c96
+	DEVLINK_CMD_SB_PORT_POOL_NEW,
049c96
+	DEVLINK_CMD_SB_PORT_POOL_DEL,
049c96
+
049c96
+	DEVLINK_CMD_SB_TC_POOL_BIND_GET,	/* can dump */
049c96
+	DEVLINK_CMD_SB_TC_POOL_BIND_SET,
049c96
+	DEVLINK_CMD_SB_TC_POOL_BIND_NEW,
049c96
+	DEVLINK_CMD_SB_TC_POOL_BIND_DEL,
049c96
+
049c96
 	/* add new commands above here */
049c96
 
049c96
 	__DEVLINK_CMD_MAX,
049c96
@@ -46,6 +66,31 @@ enum devlink_port_type {
049c96
 	DEVLINK_PORT_TYPE_IB,
049c96
 };
049c96
 
049c96
+enum devlink_sb_pool_type {
049c96
+	DEVLINK_SB_POOL_TYPE_INGRESS,
049c96
+	DEVLINK_SB_POOL_TYPE_EGRESS,
049c96
+};
049c96
+
049c96
+/* static threshold - limiting the maximum number of bytes.
049c96
+ * dynamic threshold - limiting the maximum number of bytes
049c96
+ *   based on the currently available free space in the shared buffer pool.
049c96
+ *   In this mode, the maximum quota is calculated based
049c96
+ *   on the following formula:
049c96
+ *     max_quota = alpha / (1 + alpha) * Free_Buffer
049c96
+ *   While Free_Buffer is the amount of none-occupied buffer associated to
049c96
+ *   the relevant pool.
049c96
+ *   The value range which can be passed is 0-20 and serves
049c96
+ *   for computation of alpha by following formula:
049c96
+ *     alpha = 2 ^ (passed_value - 10)
049c96
+ */
049c96
+
049c96
+enum devlink_sb_threshold_type {
049c96
+	DEVLINK_SB_THRESHOLD_TYPE_STATIC,
049c96
+	DEVLINK_SB_THRESHOLD_TYPE_DYNAMIC,
049c96
+};
049c96
+
049c96
+#define DEVLINK_SB_THRESHOLD_TO_ALPHA_MAX 20
049c96
+
049c96
 enum devlink_attr {
049c96
 	/* don't change the order or add anything between, this is ABI! */
049c96
 	DEVLINK_ATTR_UNSPEC,
049c96
@@ -62,6 +107,18 @@ enum devlink_attr {
049c96
 	DEVLINK_ATTR_PORT_IBDEV_NAME,		/* string */
049c96
 	DEVLINK_ATTR_PORT_SPLIT_COUNT,		/* u32 */
049c96
 	DEVLINK_ATTR_PORT_SPLIT_GROUP,		/* u32 */
049c96
+	DEVLINK_ATTR_SB_INDEX,			/* u32 */
049c96
+	DEVLINK_ATTR_SB_SIZE,			/* u32 */
049c96
+	DEVLINK_ATTR_SB_INGRESS_POOL_COUNT,	/* u16 */
049c96
+	DEVLINK_ATTR_SB_EGRESS_POOL_COUNT,	/* u16 */
049c96
+	DEVLINK_ATTR_SB_INGRESS_TC_COUNT,	/* u16 */
049c96
+	DEVLINK_ATTR_SB_EGRESS_TC_COUNT,	/* u16 */
049c96
+	DEVLINK_ATTR_SB_POOL_INDEX,		/* u16 */
049c96
+	DEVLINK_ATTR_SB_POOL_TYPE,		/* u8 */
049c96
+	DEVLINK_ATTR_SB_POOL_SIZE,		/* u32 */
049c96
+	DEVLINK_ATTR_SB_POOL_THRESHOLD_TYPE,	/* u8 */
049c96
+	DEVLINK_ATTR_SB_THRESHOLD,		/* u32 */
049c96
+	DEVLINK_ATTR_SB_TC_INDEX,		/* u16 */
049c96
 
049c96
 	/* add new attributes above here, update the policy in devlink.c */
049c96
 
049c96
-- 
049c96
1.8.3.1
049c96