Netdev List
 help / color / mirror / Atom feed
From: Ratheesh Kannoth <rkannoth@marvell.com>
To: <stephen@networkplumber.org>, <dsahern@kernel.org>,
	<kuba@kernel.org>, <linux-kernel@vger.kernel.org>,
	<netdev@vger.kernel.org>
Cc: <rkannoth@marvell.com>, <andrew+netdev@lunn.ch>,
	<edumazet@google.com>, <pabeni@redhat.com>, <jiri@resnulli.us>
Subject: [PATCH iproute2-next v4 2/2] devlink: support u64-array values in devlink param show/set
Date: Thu, 2 Jul 2026 08:43:59 +0530	[thread overview]
Message-ID: <20260702031359.2392868-3-rkannoth@marvell.com> (raw)
In-Reply-To: <20260702031359.2392868-1-rkannoth@marvell.com>

Add support for DEVLINK_VAR_ATTR_TYPE_U64_ARRAY parameters that carry
multiple DEVLINK_ATTR_PARAM_VALUE_DATA attributes. Parse and display
u64 array values in param show, and accept space- or comma-separated
u64 values in devlink and port param set commands.

  - Show search order

  devlink dev param show pci/0002:01:00.0 name npc_srch_order
  pci/0002:01:00.0:
    name npc_srch_order type driver-specific
      values:
        cmode runtime value  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

   - Set search order

   devlink dev param set pci/0002:01:00.0 name npc_srch_order value 31,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,\
                22,23,24,25,26,27,28,29,30  cmode runtime

Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
 devlink/devlink.c | 233 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 209 insertions(+), 24 deletions(-)

diff --git a/devlink/devlink.c b/devlink/devlink.c
index 803ea5d7..5d092d92 100644
--- a/devlink/devlink.c
+++ b/devlink/devlink.c
@@ -3516,12 +3516,143 @@ static const struct param_val_conv param_val_conv[] = {
 
 #define PARAM_VAL_CONV_LEN ARRAY_SIZE(param_val_conv)
 
+struct devlink_param_u64_array {
+	uint64_t size;
+	uint64_t *val;
+};
+
+static void param_value_u64_array_free(struct devlink_param_u64_array *arr)
+{
+	free(arr->val);
+	arr->val = NULL;
+	arr->size = 0;
+}
+
+static int param_value_nested_u64_attr_cb(const struct nlattr *attr, void *data)
+{
+	struct devlink_param_u64_array *arr = data;
+	unsigned int len;
+	uint64_t val;
+	uint64_t *new_val;
+
+	if (mnl_attr_get_type(attr) != DEVLINK_ATTR_PARAM_VALUE_DATA)
+		return MNL_CB_OK;
+
+	len = mnl_attr_get_payload_len(attr);
+	if (len == sizeof(uint32_t))
+		val = mnl_attr_get_u32(attr);
+	else if (len == sizeof(uint64_t))
+		val = mnl_attr_get_u64(attr);
+	else
+		return MNL_CB_ERROR;
+
+	new_val = realloc(arr->val, (arr->size + 1) * sizeof(uint64_t));
+	if (!new_val)
+		return MNL_CB_ERROR;
+	arr->val = new_val;
+
+	arr->val[arr->size] = val;
+	arr->size++;
+
+	return MNL_CB_OK;
+}
+
+static int param_value_u64_array_fill(struct nlattr *nl,
+				      struct devlink_param_u64_array *arr)
+{
+	int err;
+
+	param_value_u64_array_free(arr);
+	err = mnl_attr_parse_nested(nl, param_value_nested_u64_attr_cb, arr);
+	if (err != MNL_CB_OK) {
+		param_value_u64_array_free(arr);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static bool param_value_u64_array_equal(const struct devlink_param_u64_array *a,
+					const struct devlink_param_u64_array *b)
+{
+	uint64_t i;
+
+	if (a->size != b->size)
+		return false;
+
+	for (i = 0; i < a->size; i++) {
+		if (a->val[i] != b->val[i])
+			return false;
+	}
+
+	return true;
+}
+
+static int param_value_u64_array_put_from_str(struct nlmsghdr *nlh,
+					      const char *param_value,
+					      const struct devlink_param_u64_array *cur)
+{
+	struct devlink_param_u64_array new_arr = {};
+	char *copy, *token, *saveptr = NULL;
+	uint64_t val, *new_val;
+	char delim[] = " ,";
+	int err, i;
+
+	copy = strdup(param_value);
+	if (!copy)
+		return -ENOMEM;
+
+	token = strtok_r(copy, delim, &saveptr);
+	while (token) {
+		err = get_u64((__u64 *)&val, token, 10);
+		if (err) {
+			free(copy);
+			param_value_u64_array_free(&new_arr);
+			pr_err("Value \"%s\" is not a number or not within range\n",
+			       token);
+			return err;
+		}
+
+		new_val = realloc(new_arr.val, (new_arr.size + 1) * sizeof(uint64_t));
+		if (!new_val) {
+			free(copy);
+			param_value_u64_array_free(&new_arr);
+			return -ENOMEM;
+		}
+		new_arr.val = new_val;
+
+		new_arr.val[new_arr.size] = val;
+		new_arr.size++;
+		token = strtok_r(NULL, delim, &saveptr);
+	}
+	free(copy);
+
+	if (!new_arr.size) {
+		param_value_u64_array_free(&new_arr);
+		pr_err("Value must contain at least one element\n");
+		return -EINVAL;
+	}
+
+	/* Check current and new values. If both are equal, bail out */
+	if (cur && param_value_u64_array_equal(&new_arr, cur)) {
+		param_value_u64_array_free(&new_arr);
+		return 1;
+	}
+
+	for (i = 0; i < new_arr.size; i++)
+		mnl_attr_put_u64(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, new_arr.val[i]);
+
+	param_value_u64_array_free(&new_arr);
+	return 0;
+}
+
 static int pr_out_param_value_print(const char *nla_name, int nla_type,
 				     struct nlattr *val_attr, bool conv_exists,
-				     const char *label, bool flag_as_u8)
+				     const char *label, bool flag_as_u8, struct nlattr *nl)
 {
-	const char *vstr;
+	struct devlink_param_u64_array u64_arr = { };
 	int err;
+	const char *vstr;
 
 	print_string(PRINT_FP, NULL, " %s ", label);
 
@@ -3582,6 +3713,16 @@ static int pr_out_param_value_print(const char *nla_name, int nla_type,
 		else
 			print_bool(PRINT_ANY, label, "%s", val_attr);
 		break;
+	case DEVLINK_VAR_ATTR_TYPE_U64_ARRAY:
+		err = param_value_u64_array_fill(flag_as_u8 ? val_attr : nl, &u64_arr);
+		if (err)
+			return err;
+
+		for (uint64_t i = 0; i < u64_arr.size; i++)
+			print_u64(PRINT_ANY, label, "%llu ", u64_arr.val[i]);
+
+		param_value_u64_array_free(&u64_arr);
+		break;
 	}
 
 	return 0;
@@ -3601,6 +3742,7 @@ static void pr_out_param_value(struct dl *dl, const char *nla_name,
 
 	if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
 	    (nla_type != MNL_TYPE_FLAG &&
+	     nla_type != DEVLINK_VAR_ATTR_TYPE_U64_ARRAY &&
 	     !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
 		return;
 
@@ -3614,14 +3756,14 @@ static void pr_out_param_value(struct dl *dl, const char *nla_name,
 					    nla_name);
 
 	err = pr_out_param_value_print(nla_name, nla_type, val_attr,
-				       conv_exists, "value", false);
+				       conv_exists, "value", false, nl);
 	if (err)
 		return;
 
 	val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DEFAULT];
 	if (val_attr) {
 		err = pr_out_param_value_print(nla_name, nla_type, val_attr,
-					       conv_exists, "default", true);
+					       conv_exists, "default", true, nl);
 		if (err)
 			return;
 	}
@@ -3704,6 +3846,7 @@ struct param_ctx {
 		uint64_t vu64;
 		const char *vstr;
 		bool vbool;
+		struct devlink_param_u64_array u64arr;
 	} value;
 };
 
@@ -3745,6 +3888,7 @@ static int cmd_dev_param_set_cb(const struct nlmsghdr *nlh, void *data)
 
 		if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
 		    (nla_type != MNL_TYPE_FLAG &&
+		     nla_type != DEVLINK_VAR_ATTR_TYPE_U64_ARRAY &&
 		     !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
 			return MNL_CB_ERROR;
 
@@ -3771,6 +3915,12 @@ static int cmd_dev_param_set_cb(const struct nlmsghdr *nlh, void *data)
 			case DEVLINK_VAR_ATTR_TYPE_FLAG:
 				ctx->value.vbool = val_attr ? true : false;
 				break;
+			case DEVLINK_VAR_ATTR_TYPE_U64_ARRAY:
+				err = param_value_u64_array_fill(param_value_attr,
+								 &ctx->value.u64arr);
+				if (err)
+					return MNL_CB_ERROR;
+				break;
 			}
 			break;
 		}
@@ -3818,10 +3968,11 @@ static int cmd_dev_param_set(struct dl *dl)
 	ctx.dl = dl;
 	err = mnlu_gen_socket_sndrcv(&dl->nlg, nlh, cmd_dev_param_set_cb, &ctx);
 	if (err)
-		return err;
+		goto out;
 	if (!ctx.cmode_found) {
 		pr_err("Configuration mode not supported\n");
-		return -ENOTSUP;
+		err = -ENOTSUP;
+		goto out;
 	}
 
 	if (dl->opts.present & DL_OPT_PARAM_SET_DEFAULT) {
@@ -3829,7 +3980,8 @@ static int cmd_dev_param_set(struct dl *dl)
 				       NLM_F_REQUEST | NLM_F_ACK);
 		dl_opts_put(nlh, dl);
 		mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_TYPE, ctx.nla_type);
-		return mnlu_gen_socket_sndrcv(&dl->nlg, nlh, NULL, NULL);
+		err = mnlu_gen_socket_sndrcv(&dl->nlg, nlh, NULL, NULL);
+		goto out;
 	}
 
 	nlh = mnlu_gen_socket_cmd_prepare(&dl->nlg, DEVLINK_CMD_PARAM_SET,
@@ -3855,7 +4007,7 @@ static int cmd_dev_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_u8 == ctx.value.vu8)
-			return 0;
+			goto out;
 		mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u8);
 		break;
 	case DEVLINK_VAR_ATTR_TYPE_U16:
@@ -3872,7 +4024,7 @@ static int cmd_dev_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_u16 == ctx.value.vu16)
-			return 0;
+			goto out;
 		mnl_attr_put_u16(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u16);
 		break;
 	case DEVLINK_VAR_ATTR_TYPE_U32:
@@ -3889,7 +4041,7 @@ static int cmd_dev_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_u32 == ctx.value.vu32)
-			return 0;
+			goto out;
 		mnl_attr_put_u32(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u32);
 		break;
 	case DEVLINK_VAR_ATTR_TYPE_U64:
@@ -3904,7 +4056,7 @@ static int cmd_dev_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_u64 == ctx.value.vu64)
-			return 0;
+			goto out;
 		mnl_attr_put_u64(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u64);
 		break;
 	case DEVLINK_VAR_ATTR_TYPE_FLAG:
@@ -3912,7 +4064,7 @@ static int cmd_dev_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_bool == ctx.value.vbool)
-			return 0;
+			goto out;
 		if (val_bool)
 			mnl_attr_put(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
 				     0, NULL);
@@ -3921,17 +4073,30 @@ static int cmd_dev_param_set(struct dl *dl)
 		mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
 				  dl->opts.param_value);
 		if (!strcmp(dl->opts.param_value, ctx.value.vstr))
-			return 0;
+			goto out;
+		break;
+	case DEVLINK_VAR_ATTR_TYPE_U64_ARRAY:
+		err = param_value_u64_array_put_from_str(nlh, dl->opts.param_value,
+							 &ctx.value.u64arr);
+		if (err == 1)
+			goto out;
+		if (err)
+			goto out;
 		break;
 	default:
 		printf("Value type not supported\n");
-		return -ENOTSUP;
+		err = -ENOTSUP;
+		goto out;
 	}
-	return mnlu_gen_socket_sndrcv(&dl->nlg, nlh, NULL, NULL);
+	err = mnlu_gen_socket_sndrcv(&dl->nlg, nlh, NULL, NULL);
+	goto out;
 
 err_param_value_parse:
 	pr_err("Value \"%s\" is not a number or not within range\n",
 	       dl->opts.param_value);
+
+out:
+	param_value_u64_array_free(&ctx.value.u64arr);
 	return err;
 }
 
@@ -5369,6 +5534,7 @@ static int cmd_port_param_set_cb(const struct nlmsghdr *nlh, void *data)
 
 		if (!nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE] ||
 		    (nla_type != MNL_TYPE_FLAG &&
+		     nla_type != DEVLINK_VAR_ATTR_TYPE_U64_ARRAY &&
 		     !nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA]))
 			return MNL_CB_ERROR;
 
@@ -5391,6 +5557,12 @@ static int cmd_port_param_set_cb(const struct nlmsghdr *nlh, void *data)
 			case MNL_TYPE_FLAG:
 				ctx->value.vbool = val_attr ? true : false;
 				break;
+			case DEVLINK_VAR_ATTR_TYPE_U64_ARRAY:
+				err = param_value_u64_array_fill(param_value_attr,
+								 &ctx->value.u64arr);
+				if (err)
+					return MNL_CB_ERROR;
+				break;
 			}
 			break;
 		}
@@ -5426,7 +5598,7 @@ static int cmd_port_param_set(struct dl *dl)
 	ctx.dl = dl;
 	err = mnlu_gen_socket_sndrcv(&dl->nlg, nlh, cmd_port_param_set_cb, &ctx);
 	if (err)
-		return err;
+		goto out;
 
 	nlh = mnlu_gen_socket_cmd_prepare(&dl->nlg, DEVLINK_CMD_PORT_PARAM_SET,
 					  NLM_F_REQUEST | NLM_F_ACK);
@@ -5451,7 +5623,7 @@ static int cmd_port_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_u8 == ctx.value.vu8)
-			return 0;
+			goto out;
 		mnl_attr_put_u8(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u8);
 		break;
 	case MNL_TYPE_U16:
@@ -5468,7 +5640,7 @@ static int cmd_port_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_u16 == ctx.value.vu16)
-			return 0;
+			goto out;
 		mnl_attr_put_u16(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u16);
 		break;
 	case MNL_TYPE_U32:
@@ -5485,7 +5657,7 @@ static int cmd_port_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_u32 == ctx.value.vu32)
-			return 0;
+			goto out;
 		mnl_attr_put_u32(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u32);
 		break;
 	case MNL_TYPE_U64:
@@ -5500,7 +5672,7 @@ static int cmd_port_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_u64 == ctx.value.vu64)
-			return 0;
+			goto out;
 		mnl_attr_put_u64(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA, val_u64);
 		break;
 	case MNL_TYPE_FLAG:
@@ -5508,7 +5680,7 @@ static int cmd_port_param_set(struct dl *dl)
 		if (err)
 			goto err_param_value_parse;
 		if (val_bool == ctx.value.vbool)
-			return 0;
+			goto out;
 		if (val_bool)
 			mnl_attr_put(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
 				     0, NULL);
@@ -5517,17 +5689,30 @@ static int cmd_port_param_set(struct dl *dl)
 		mnl_attr_put_strz(nlh, DEVLINK_ATTR_PARAM_VALUE_DATA,
 				  dl->opts.param_value);
 		if (!strcmp(dl->opts.param_value, ctx.value.vstr))
-			return 0;
+			goto out;
+		break;
+	case DEVLINK_VAR_ATTR_TYPE_U64_ARRAY:
+		err = param_value_u64_array_put_from_str(nlh, dl->opts.param_value,
+							 &ctx.value.u64arr);
+		if (err == 1)
+			goto out;
+		if (err)
+			goto out;
 		break;
 	default:
 		printf("Value type not supported\n");
-		return -ENOTSUP;
+		err = -ENOTSUP;
+		goto out;
 	}
-	return mnlu_gen_socket_sndrcv(&dl->nlg, nlh, NULL, NULL);
+	err = mnlu_gen_socket_sndrcv(&dl->nlg, nlh, NULL, NULL);
+	goto out;
 
 err_param_value_parse:
 	pr_err("Value \"%s\" is not a number or not within range\n",
 	       dl->opts.param_value);
+
+out:
+	param_value_u64_array_free(&ctx.value.u64arr);
 	return err;
 }
 
-- 
2.43.0


      parent reply	other threads:[~2026-07-02  3:14 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-02  3:13 [PATCH iproute2-next v4 0/2] devlink: support u64-array devlink parameters Ratheesh Kannoth
2026-07-02  3:13 ` [PATCH iproute2-next v4 1/2] devlink: use DEVLINK_VAR_ATTR_TYPE_* in param show/set Ratheesh Kannoth
2026-07-02  3:13 ` Ratheesh Kannoth [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260702031359.2392868-3-rkannoth@marvell.com \
    --to=rkannoth@marvell.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=jiri@resnulli.us \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=stephen@networkplumber.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox