All of lore.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: 27+ 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
2026-05-07 11:03     ` Jiri Pirko
2026-05-08 17:59       ` Mark Bloch
2026-05-08 18:07         ` Jiri Pirko
2026-05-09  0:52           ` Jakub Kicinski
2026-05-09  7:01             ` Jiri Pirko
2026-05-10 12:31               ` Mark Bloch
2026-05-11  8:07                 ` Jiri Pirko
2026-05-11 18:21                 ` Parav Pandit
2026-05-12  8:45                   ` Jiri Pirko
2026-05-12 13:48                     ` Parav Pandit
2026-05-12 14:07                       ` Jiri Pirko
2026-05-12 15:25                         ` Parav Pandit
2026-05-12 18:35                           ` Jiri Pirko
2026-05-13  5:53                             ` Mark Bloch
2026-05-13 11:11                               ` Jiri Pirko
2026-05-14 12:34                                 ` Mark Bloch
2026-05-10 16:37               ` Jakub Kicinski
2026-05-11  8:42                 ` Jiri Pirko
2026-05-11 23:41                   ` Jakub Kicinski
2026-05-12  8:42                     ` Jiri Pirko

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 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.