[PATCH net-next v3 4/5] devlink: Add devlink-conf uAPI for NV memory management

Kory Maincent
2025-09-15T17:07:17Z

From: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>

Introduce a new devlink-conf uAPI to manage device configuration stored
in non-volatile memory. This provides a standardized interface for
devices that need to persist configuration changes across reboots.

Add two new devlink operations:
- conf_save: Save current configuration to non-volatile memory
- conf_reset: Reset configuration in non-volatile memory to factory
defaults

The uAPI is designed to be generic and can be used by any device driver
that manages persistent configuration storage.

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---

After two NAKs on sysfs and devlink-param design choices due to uAPI not
being appropriate, I moved to this new design approach.
Also, the choice of saving the configuration to NVM on every config change
seemed inappropriate as the save and the reset processes would be in two
different Kernel framework.
The new design intend to extend devlink uAPI with a devlink-conf interface.
This uAPI is designed to be generic. While currently only one driver
implements it, controllers that manage non-volatile memory to save
configuration are not uncommon.

Change in v3:
- New patch.
---
Documentation/netlink/specs/devlink.yaml | 23 +++++++++++++++++
Documentation/networking/devlink/devlink-conf.rst | 22 ++++++++++++++++
Documentation/networking/devlink/index.rst | 1 +
MAINTAINERS | 2 ++
include/net/devlink.h | 20 +++++++++++++++
include/uapi/linux/devlink.h | 4 +++
net/devlink/Makefile | 3 ++-
net/devlink/conf.c | 31 +++++++++++++++++++++++
net/devlink/netlink.c | 2 +-
net/devlink/netlink_gen.c | 20 ++++++++++++++-
net/devlink/netlink_gen.h | 3 ++-
11 files changed, 127 insertions(+), 4 deletions(-)

diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml
index 3db59c9658694..27db92457e886 100644
--- a/Documentation/netlink/specs/devlink.yaml
+++ b/Documentation/netlink/specs/devlink.yaml
@@ -857,6 +857,14 @@ attribute-sets:
name: health-reporter-burst-period
type: u64
doc: Time (in msec) for recoveries before starting the grace period.
+ -
+ name: conf-save
+ type: flag
+ doc: Save device configuration to non-volatile memory.
+ -
+ name: conf-reset
+ type: flag
+ doc: Reset device configuration located in non-volatile memory.
-
name: dl-dev-stats
subset-of: devlink
@@ -2325,3 +2333,18 @@ operations:
- bus-name
- dev-name
- port-index
+ -
+ name: conf-set
+ doc: Manage device configuration.
+ attribute-set: devlink
+ dont-validate: [strict]
+ flags: [admin-perm]
+ do:
+ pre: devlink-nl-pre-doit
+ post: devlink-nl-post-doit
+ request:
+ attributes:
+ - bus-name
+ - dev-name
+ - conf-save
+ - conf-reset
diff --git a/Documentation/networking/devlink/devlink-conf.rst b/Documentation/networking/devlink/devlink-conf.rst
new file mode 100644
index 0000000000000..34f866f2f9594
--- /dev/null
+++ b/Documentation/networking/devlink/devlink-conf.rst
@@ -0,0 +1,22 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+============
+Devlink Conf
+============
+
+The ``devlink-conf`` API allows saving the device configuration as
+permanent writing it to a non-volatile memory.
+
+Drivers are expected to implement ``devlink-conf`` functionality through
+``conf_save`` and ``conf_reset`` devlink operations.
+
+example usage
+-------------
+
+.. code:: shell
+
+ $ devlink dev conf help
+ $ devlink dev conf DEV [ save | reset ]
+
+ # Run conf command for saving configuration to non-volatile memory:
+ $ devlink dev conf i2c/1-003c save
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index 0c58e5c729d92..ba381ecadc3dc 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -56,6 +56,7 @@ general.
:maxdepth: 1

devlink-dpipe
+ devlink-conf
devlink-eswitch-attr
devlink-flash
devlink-health
diff --git a/MAINTAINERS b/MAINTAINERS
index b81595e9ea955..429303a9447f3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20299,7 +20299,9 @@ M: Kory Maincent <kory.maincent@bootlin.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/pse-pd/
+F: Documentation/networking/devlink/devlink-conf.rst
F: drivers/net/pse-pd/
+F: net/devlink/conf.c
F: net/ethtool/pse-pd.c

PSTORE FILESYSTEM
diff --git a/include/net/devlink.h b/include/net/devlink.h
index 5f44e702c25ca..04256d1973417 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -1551,6 +1551,26 @@ struct devlink_ops {
enum devlink_selftest_status
(*selftest_run)(struct devlink *devlink, unsigned int id,
struct netlink_ext_ack *extack);
+
+ /**
+ * conf_save - Save configuration to non-volatile memory
+ * @devlink: Devlink instance
+ * @extack: extack for reporting error messages
+ *
+ * Return: 0 on success, negative value otherwise.
+ */
+ int (*conf_save)(struct devlink *devlink,
+ struct netlink_ext_ack *extack);
+
+ /**
+ * conf_reset - Reset configuration located in non-volatile memory
+ * @devlink: Devlink instance
+ * @extack: extack for reporting error messages
+ *
+ * Return: 0 on success, negative value otherwise.
+ */
+ int (*conf_reset)(struct devlink *devlink,
+ struct netlink_ext_ack *extack);
};

void *devlink_priv(struct devlink *devlink);
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index bcad11a787a55..ce9192e772d93 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -141,6 +141,8 @@ enum devlink_command {

DEVLINK_CMD_NOTIFY_FILTER_SET,

+ DEVLINK_CMD_CONF_SET,
+
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
@@ -638,6 +640,8 @@ enum devlink_attr {

DEVLINK_ATTR_HEALTH_REPORTER_BURST_PERIOD, /* u64 */

+ DEVLINK_ATTR_CONF_SAVE, /* u8 */
+ DEVLINK_ATTR_CONF_RESET, /* u8 */
/* Add new attributes above here, update the spec in
* Documentation/netlink/specs/devlink.yaml and re-generate
* net/devlink/netlink_gen.c.
diff --git a/net/devlink/Makefile b/net/devlink/Makefile
index 000da622116a3..e4ff40fc65ef2 100644
--- a/net/devlink/Makefile
+++ b/net/devlink/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0

obj-y := core.o netlink.o netlink_gen.o dev.o port.o sb.o dpipe.o \
- resource.o param.o region.o health.o trap.o rate.o linecard.o
+ resource.o param.o region.o health.o trap.o rate.o linecard.o \
+ conf.o
diff --git a/net/devlink/conf.c b/net/devlink/conf.c
new file mode 100644
index 0000000000000..09cec798d5e48
--- /dev/null
+++ b/net/devlink/conf.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Bootlin, Kory Maincent <kory.maincent@bootlin.com>
+ */
+
+#include "devl_internal.h"
+
+int devlink_nl_conf_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct devlink *devlink = info->user_ptr[0];
+ struct nlattr **tb = info->attrs;
+ const struct devlink_ops *ops;
+
+ ops = devlink->ops;
+ if (!ops->conf_save || !ops->conf_reset)
+ return -EOPNOTSUPP;
+
+ if (tb[DEVLINK_ATTR_CONF_SAVE] && tb[DEVLINK_ATTR_CONF_RESET]) {
+ NL_SET_ERR_MSG_MOD(info->extack,
+ "Can't save and reset the configuration simultaneously");
+ return -EINVAL;
+ }
+
+ if (tb[DEVLINK_ATTR_CONF_SAVE])
+ return ops->conf_save(devlink, info->extack);
+
+ if (tb[DEVLINK_ATTR_CONF_RESET])
+ return ops->conf_reset(devlink, info->extack);
+
+ return 0;
+}
diff --git a/net/devlink/netlink.c b/net/devlink/netlink.c
index 593605c1b1ef4..e22f5bfaba931 100644
--- a/net/devlink/netlink.c
+++ b/net/devlink/netlink.c
@@ -367,7 +367,7 @@ struct genl_family devlink_nl_family __ro_after_init = {
.module = THIS_MODULE,
.split_ops = devlink_nl_ops,
.n_split_ops = ARRAY_SIZE(devlink_nl_ops),
- .resv_start_op = DEVLINK_CMD_SELFTESTS_RUN + 1,
+ .resv_start_op = DEVLINK_CMD_MAX + 1,
.mcgrps = devlink_nl_mcgrps,
.n_mcgrps = ARRAY_SIZE(devlink_nl_mcgrps),
.sock_priv_size = sizeof(struct devlink_nl_sock_priv),
diff --git a/net/devlink/netlink_gen.c b/net/devlink/netlink_gen.c
index 9fd00977d59e3..51a1f55c7063d 100644
--- a/net/devlink/netlink_gen.c
+++ b/net/devlink/netlink_gen.c
@@ -602,8 +602,16 @@ static const struct nla_policy devlink_notify_filter_set_nl_policy[DEVLINK_ATTR_
[DEVLINK_ATTR_PORT_INDEX] = { .type = NLA_U32, },
};

+/* DEVLINK_CMD_CONF_SET - do */
+static const struct nla_policy devlink_conf_set_nl_policy[DEVLINK_ATTR_CONF_RESET + 1] = {
+ [DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING, },
+ [DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING, },
+ [DEVLINK_ATTR_CONF_SAVE] = { .type = NLA_FLAG, },
+ [DEVLINK_ATTR_CONF_RESET] = { .type = NLA_FLAG, },
+};
+
/* Ops table for devlink */
-const struct genl_split_ops devlink_nl_ops[74] = {
+const struct genl_split_ops devlink_nl_ops[75] = {
{
.cmd = DEVLINK_CMD_GET,
.validate = GENL_DONT_VALIDATE_STRICT,
@@ -1282,4 +1290,14 @@ const struct genl_split_ops devlink_nl_ops[74] = {
.maxattr = DEVLINK_ATTR_PORT_INDEX,
.flags = GENL_CMD_CAP_DO,
},
+ {
+ .cmd = DEVLINK_CMD_CONF_SET,
+ .validate = GENL_DONT_VALIDATE_STRICT,
+ .pre_doit = devlink_nl_pre_doit,
+ .doit = devlink_nl_conf_set_doit,
+ .post_doit = devlink_nl_post_doit,
+ .policy = devlink_conf_set_nl_policy,
+ .maxattr = DEVLINK_ATTR_CONF_RESET,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
};
diff --git a/net/devlink/netlink_gen.h b/net/devlink/netlink_gen.h
index 09cc6f264ccfa..9e99528bd36c7 100644
--- a/net/devlink/netlink_gen.h
+++ b/net/devlink/netlink_gen.h
@@ -17,7 +17,7 @@ extern const struct nla_policy devlink_dl_rate_tc_bws_nl_policy[DEVLINK_RATE_TC_
extern const struct nla_policy devlink_dl_selftest_id_nl_policy[DEVLINK_ATTR_SELFTEST_ID_FLASH + 1];

/* Ops table for devlink */
-extern const struct genl_split_ops devlink_nl_ops[74];
+extern const struct genl_split_ops devlink_nl_ops[75];

int devlink_nl_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
struct genl_info *info);
@@ -145,5 +145,6 @@ int devlink_nl_selftests_get_dumpit(struct sk_buff *skb,
int devlink_nl_selftests_run_doit(struct sk_buff *skb, struct genl_info *info);
int devlink_nl_notify_filter_set_doit(struct sk_buff *skb,
struct genl_info *info);
+int devlink_nl_conf_set_doit(struct sk_buff *skb, struct genl_info *info);

#endif /* _LINUX_DEVLINK_GEN_H */

--
2.43.0