public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Mark Bloch <mbloch@nvidia.com>
To: Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>
Cc: Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Jiri Pirko <jiri@resnulli.us>, Simon Horman <horms@kernel.org>,
	"Saeed Mahameed" <saeedm@nvidia.com>,
	Leon Romanovsky <leon@kernel.org>,
	Tariq Toukan <tariqt@nvidia.com>, Mark Bloch <mbloch@nvidia.com>,
	Andrew Morton <akpm@linux-foundation.org>,
	"Borislav Petkov (AMD)" <bp@alien8.de>,
	"Randy Dunlap" <rdunlap@infradead.org>,
	Dave Hansen <dave.hansen@linux.intel.com>,
	Christian Brauner <brauner@kernel.org>,
	Petr Mladek <pmladek@suse.com>,
	"Peter Zijlstra (Intel)" <peterz@infradead.org>,
	Thomas Gleixner <tglx@kernel.org>,
	Pawan Gupta <pawan.kumar.gupta@linux.intel.com>,
	Dapeng Mi <dapeng1.mi@linux.intel.com>,
	Kees Cook <kees@kernel.org>, Marco Elver <elver@google.com>,
	Eric Biggers <ebiggers@kernel.org>,
	Li RongQing <lirongqing@baidu.com>,
	"Paul E. McKenney" <paulmck@kernel.org>,
	<linux-doc@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<netdev@vger.kernel.org>, <linux-rdma@vger.kernel.org>
Subject: [RFC net-next 1/4] devlink: Add infrastructure for boot-time defaults
Date: Wed, 6 May 2026 15:37:36 +0300	[thread overview]
Message-ID: <20260506123739.1959770-2-mbloch@nvidia.com> (raw)
In-Reply-To: <20260506123739.1959770-1-mbloch@nvidia.com>

Add generic devlink boot-default infrastructure driven by the
devlink= kernel command line parameter.

The parser stores defaults per devlink handle using the same
bus/device handle format exposed by devlink. Each handle keeps an
ordered list of parsed commands so that defaults can later be applied
in command-line order when the matching devlink instance is initialized.

This commit only adds the generic parsing, storage, duplicate handling
and devl_apply_defaults() API. Concrete default commands are added in
later commits.

Signed-off-by: Mark Bloch <mbloch@nvidia.com>
---
 include/net/devlink.h |   1 +
 net/devlink/core.c    | 441 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 442 insertions(+)

diff --git a/include/net/devlink.h b/include/net/devlink.h
index bcd31de1f890..058654d6800f 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -1622,6 +1622,7 @@ int devl_trylock(struct devlink *devlink);
 void devl_unlock(struct devlink *devlink);
 void devl_assert_locked(struct devlink *devlink);
 bool devl_lock_is_held(struct devlink *devlink);
+int devl_apply_defaults(struct devlink *devlink);
 DEFINE_GUARD(devl, struct devlink *, devl_lock(_T), devl_unlock(_T));
 
 struct ib_device;
diff --git a/net/devlink/core.c b/net/devlink/core.c
index eeb6a71f5f56..2421a1f8dbb7 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -4,6 +4,11 @@
  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
  */
 
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
 #include <net/genetlink.h>
 #define CREATE_TRACE_POINTS
 #include <trace/events/devlink.h>
@@ -16,6 +21,418 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report);
 
 DEFINE_XARRAY_FLAGS(devlinks, XA_FLAGS_ALLOC);
 
+static char *devlink_default;
+static LIST_HEAD(devlink_default_nodes);
+
+struct devlink_default_attr_item {
+	enum devlink_attr attr;
+	union {
+		enum devlink_eswitch_mode eswitch_mode;
+		struct {
+			char *name;
+			char *value;
+		} param;
+	} value;
+};
+
+struct devlink_default_cmd_item {
+	struct list_head list;
+	enum devlink_command cmd;
+	int (*run)(struct devlink *devlink,
+		   const struct devlink_default_attr_item *attr);
+	struct devlink_default_attr_item attr;
+};
+
+struct devlink_default_node {
+	struct list_head list;
+	char *bus_name;
+	char *dev_name;
+	struct list_head cmd_list;
+};
+
+struct devlink_default_cmd_spec {
+	const char *name;
+	enum devlink_command cmd;
+	int (*run)(struct devlink *devlink,
+		   const struct devlink_default_attr_item *attr);
+	int (*attr_parse)(char *str,
+			  struct devlink_default_attr_item *attr_item);
+};
+
+static int __init
+devlink_default_node_add(const char *bus_name, const char *dev_name,
+			 const char *cmd);
+
+static void __init
+devlink_default_attr_free(struct devlink_default_attr_item *attr)
+{
+	if (attr->attr != DEVLINK_ATTR_PARAM)
+		return;
+
+	kfree(attr->value.param.name);
+	kfree(attr->value.param.value);
+}
+
+static const struct devlink_default_cmd_spec *__init
+devlink_default_cmd_spec_find(const char *name)
+{
+	return NULL;
+}
+
+static int __init
+devlink_default_cmd_parse(char *str,
+			  struct devlink_default_cmd_item *cmd_item)
+{
+	const struct devlink_default_cmd_spec *spec;
+	struct devlink_default_attr_item attr_item = {};
+	char *cmd_name;
+	int err;
+
+	cmd_name = strsep(&str, ":");
+	if (!cmd_name || !*cmd_name || !str || !*str)
+		return -EINVAL;
+
+	spec = devlink_default_cmd_spec_find(cmd_name);
+	if (!spec)
+		return -EINVAL;
+
+	err = spec->attr_parse(str, &attr_item);
+	if (err) {
+		devlink_default_attr_free(&attr_item);
+		return err;
+	}
+	if (cmd_item) {
+		cmd_item->cmd = spec->cmd;
+		cmd_item->run = spec->run;
+		cmd_item->attr = attr_item;
+	} else {
+		devlink_default_attr_free(&attr_item);
+	}
+
+	return 0;
+}
+
+static int __init
+devlink_default_cmd_parse_copy(const char *str,
+			       struct devlink_default_cmd_item *cmd_item)
+{
+	char *cmd;
+	int err;
+
+	cmd = kstrdup(str, GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	err = devlink_default_cmd_parse(cmd, cmd_item);
+	kfree(cmd);
+	return err;
+}
+
+static int __init
+devlink_default_handle_parse(char *handle, char **bus_name, char **dev_name)
+{
+	char *slash;
+	char *p;
+
+	if (!handle || !*handle)
+		return -EINVAL;
+
+	for (p = handle; *p; p++) {
+		if (isspace(*p))
+			return -EINVAL;
+		if (*p == '[' || *p == ']')
+			return -EINVAL;
+	}
+
+	slash = strchr(handle, '/');
+	if (!slash || slash == handle || !slash[1])
+		return -EINVAL;
+	if (strchr(slash + 1, '/'))
+		return -EINVAL;
+
+	*slash = '\0';
+	if (strchr(handle, ':'))
+		return -EINVAL;
+
+	*bus_name = handle;
+	*dev_name = slash + 1;
+	return 0;
+}
+
+static int __init
+devlink_default_entry_parse(char *entry, bool store)
+{
+	char *handles_end;
+	char *handles;
+	char *handle;
+	char *cmd;
+	int err;
+
+	if (!entry || *entry != '[')
+		return -EINVAL;
+
+	handles = entry + 1;
+	handles_end = strchr(handles, ']');
+	if (!handles_end || handles_end[1] != ':' || !handles_end[2])
+		return -EINVAL;
+
+	*handles_end = '\0';
+	cmd = handles_end + 2;
+	if (!*handles)
+		return -EINVAL;
+
+	while ((handle = strsep(&handles, ",")) != NULL) {
+		char *bus_name;
+		char *dev_name;
+
+		err = devlink_default_handle_parse(handle, &bus_name,
+						   &dev_name);
+		if (err)
+			return err;
+
+		if (!store)
+			continue;
+
+		err = devlink_default_node_add(bus_name, dev_name, cmd);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static void __init
+devlink_default_cmd_item_free(struct devlink_default_cmd_item *cmd)
+{
+	devlink_default_attr_free(&cmd->attr);
+	kfree(cmd);
+}
+
+static void __init devlink_default_node_free(struct devlink_default_node *node)
+{
+	struct devlink_default_cmd_item *cmd;
+	struct devlink_default_cmd_item *cmd_tmp;
+
+	list_for_each_entry_safe(cmd, cmd_tmp, &node->cmd_list, list) {
+		list_del(&cmd->list);
+		devlink_default_cmd_item_free(cmd);
+	}
+
+	kfree(node->bus_name);
+	kfree(node->dev_name);
+	kfree(node);
+}
+
+static void __init devlink_default_nodes_clear(void)
+{
+	struct devlink_default_node *node;
+	struct devlink_default_node *node_tmp;
+
+	list_for_each_entry_safe(node, node_tmp, &devlink_default_nodes, list) {
+		list_del(&node->list);
+		devlink_default_node_free(node);
+	}
+}
+
+static struct devlink_default_node *__init
+devlink_default_node_find(const char *bus_name, const char *dev_name)
+{
+	struct devlink_default_node *node;
+
+	list_for_each_entry(node, &devlink_default_nodes, list) {
+		if (!strcmp(node->bus_name, bus_name) &&
+		    !strcmp(node->dev_name, dev_name))
+			return node;
+	}
+
+	return NULL;
+}
+
+static bool __init
+devlink_default_cmd_equal(const struct devlink_default_cmd_item *a,
+			  const struct devlink_default_cmd_item *b)
+{
+	if (a->cmd != b->cmd || a->attr.attr != b->attr.attr)
+		return false;
+
+	return true;
+}
+
+static bool __init
+devlink_default_cmd_exists(struct devlink_default_node *node,
+			   const struct devlink_default_cmd_item *cmd)
+{
+	struct devlink_default_cmd_item *cmd_item;
+
+	list_for_each_entry(cmd_item, &node->cmd_list, list) {
+		if (devlink_default_cmd_equal(cmd_item, cmd))
+			return true;
+	}
+
+	return false;
+}
+
+static int __init
+devlink_default_cmd_item_add(struct devlink_default_node *node,
+			     const char *cmd_str)
+{
+	struct devlink_default_cmd_item *cmd;
+	int err;
+
+	cmd = kzalloc_obj(*cmd);
+	if (!cmd)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&cmd->list);
+	err = devlink_default_cmd_parse_copy(cmd_str, cmd);
+	if (err) {
+		devlink_default_cmd_item_free(cmd);
+		return err;
+	}
+
+	if (devlink_default_cmd_exists(node, cmd)) {
+		devlink_default_cmd_item_free(cmd);
+		return -EEXIST;
+	}
+
+	list_add_tail(&cmd->list, &node->cmd_list);
+	return 0;
+}
+
+static int __init
+devlink_default_node_add(const char *bus_name, const char *dev_name,
+			 const char *cmd_str)
+{
+	struct devlink_default_node *node;
+	int err;
+
+	node = devlink_default_node_find(bus_name, dev_name);
+	if (node)
+		return devlink_default_cmd_item_add(node, cmd_str);
+
+	node = kzalloc_obj(*node);
+	if (!node)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&node->list);
+	INIT_LIST_HEAD(&node->cmd_list);
+	node->bus_name = kstrdup(bus_name, GFP_KERNEL);
+	node->dev_name = kstrdup(dev_name, GFP_KERNEL);
+	if (!node->bus_name || !node->dev_name) {
+		err = -ENOMEM;
+		goto err_free_node;
+	}
+
+	err = devlink_default_cmd_item_add(node, cmd_str);
+	if (err)
+		goto err_free_node;
+
+	list_add_tail(&node->list, &devlink_default_nodes);
+	return 0;
+
+err_free_node:
+	devlink_default_node_free(node);
+	return err;
+}
+
+static int __init devlink_default_parse(char *str, bool store)
+{
+	char *entry = str;
+	int err;
+
+	if (!str || !*str)
+		return -EINVAL;
+
+	while (entry) {
+		char *handles_end;
+		char *cmd_start;
+		char *entry_end;
+
+		if (*entry != '[') {
+			err = -EINVAL;
+			goto err_clear;
+		}
+
+		handles_end = strchr(entry + 1, ']');
+		if (!handles_end || handles_end[1] != ':') {
+			err = -EINVAL;
+			goto err_clear;
+		}
+
+		cmd_start = handles_end + 2;
+		entry_end = strchr(cmd_start, ',');
+		if (entry_end)
+			*entry_end = '\0';
+
+		err = devlink_default_entry_parse(entry, store);
+		if (err)
+			goto err_clear;
+		if (!entry_end)
+			return 0;
+
+		entry = entry_end + 1;
+		if (!*entry) {
+			err = -EINVAL;
+			goto err_clear;
+		}
+	}
+
+	return 0;
+
+err_clear:
+	if (store)
+		devlink_default_nodes_clear();
+	return err;
+}
+
+static int devlink_default_node_apply(struct devlink *devlink,
+				      const struct devlink_default_node *node)
+{
+	const struct devlink_default_cmd_item *cmd;
+	int err;
+
+	list_for_each_entry(cmd, &node->cmd_list, list) {
+		err = cmd->run(devlink, &cmd->attr);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/**
+ * devl_apply_defaults - Apply defaults matching the devlink instance
+ * @devlink: devlink
+ *
+ * The caller must hold the devlink instance lock.
+ */
+int devl_apply_defaults(struct devlink *devlink)
+{
+	const char *bus_name = devlink_bus_name(devlink);
+	const char *dev_name = devlink_dev_name(devlink);
+	struct devlink_default_node *node;
+
+	devl_assert_locked(devlink);
+
+	list_for_each_entry(node, &devlink_default_nodes, list) {
+		if (strcmp(node->bus_name, bus_name) ||
+		    strcmp(node->dev_name, dev_name))
+			continue;
+
+		return devlink_default_node_apply(devlink, node);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(devl_apply_defaults);
+
+static int __init devlink_default_setup(char *str)
+{
+	devlink_default = str;
+	return 1;
+}
+__setup("devlink=", devlink_default_setup);
+
 static struct devlink *devlinks_xa_get(unsigned long index)
 {
 	struct devlink *devlink;
@@ -578,6 +995,27 @@ static int __init devlink_init(void)
 {
 	int err;
 
+	if (devlink_default) {
+		char *def;
+
+		def = kstrdup(devlink_default, GFP_KERNEL);
+		if (!def) {
+			err = -ENOMEM;
+			goto out;
+		}
+		err = devlink_default_parse(def, true);
+		kfree(def);
+		if (err == -EEXIST) {
+			devlink_default = NULL;
+			pr_warn("devlink: duplicate defaults ignored\n");
+		} else if (err == -EINVAL) {
+			devlink_default = NULL;
+			pr_warn("devlink: invalid command line parameter ignored\n");
+		} else if (err) {
+			goto out;
+		}
+	}
+
 	err = register_pernet_subsys(&devlink_pernet_ops);
 	if (err)
 		goto out;
@@ -593,7 +1031,10 @@ static int __init devlink_init(void)
 out_unreg_pernet_subsys:
 	unregister_pernet_subsys(&devlink_pernet_ops);
 out:
+	if (err)
+		devlink_default_nodes_clear();
 	WARN_ON(err);
+
 	return err;
 }
 
-- 
2.34.1


  reply	other threads:[~2026-05-06 12:38 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-06 12:37 [RFC net-next 0/4] devlink: Add boot-time defaults Mark Bloch
2026-05-06 12:37 ` Mark Bloch [this message]
2026-05-06 12:37 ` [RFC net-next 2/4] devlink: Add eswitch mode boot default Mark Bloch
2026-05-06 12:37 ` [RFC net-next 3/4] devlink: Add runtime parameter boot defaults Mark Bloch
2026-05-06 12:37 ` [RFC net-next 4/4] net/mlx5: Apply devlink boot defaults during init Mark Bloch
2026-05-06 15:22 ` [RFC net-next 0/4] devlink: Add boot-time defaults Jiri Pirko
2026-05-06 17:35   ` Mark Bloch

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=20260506123739.1959770-2-mbloch@nvidia.com \
    --to=mbloch@nvidia.com \
    --cc=akpm@linux-foundation.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=bp@alien8.de \
    --cc=brauner@kernel.org \
    --cc=corbet@lwn.net \
    --cc=dapeng1.mi@linux.intel.com \
    --cc=dave.hansen@linux.intel.com \
    --cc=davem@davemloft.net \
    --cc=ebiggers@kernel.org \
    --cc=edumazet@google.com \
    --cc=elver@google.com \
    --cc=horms@kernel.org \
    --cc=jiri@resnulli.us \
    --cc=kees@kernel.org \
    --cc=kuba@kernel.org \
    --cc=leon@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=lirongqing@baidu.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=paulmck@kernel.org \
    --cc=pawan.kumar.gupta@linux.intel.com \
    --cc=peterz@infradead.org \
    --cc=pmladek@suse.com \
    --cc=rdunlap@infradead.org \
    --cc=saeedm@nvidia.com \
    --cc=skhan@linuxfoundation.org \
    --cc=tariqt@nvidia.com \
    --cc=tglx@kernel.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