diff mbox series

[net-next,v1,1/3] devlink: introduce framework for selftests

Message ID 20220628164241.44360-2-vikas.gupta@broadcom.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series add framework for selftests in devlink | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 384 this patch: 384
netdev/cc_maintainers warning 3 maintainers not CCed: pabeni@redhat.com linux-doc@vger.kernel.org corbet@lwn.net
netdev/build_clang fail Errors and warnings before: 35 this patch: 42
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 531 this patch: 531
netdev/checkpatch warning WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
netdev/kdoc success Errors and warnings before: 14 this patch: 14
netdev/source_inline success Was 0 now: 0

Commit Message

Vikas Gupta June 28, 2022, 4:42 p.m. UTC
Add a framework for running selftests.
Framework exposes devlink commands and test suite(s) to the user
to execute and query the supported tests by the driver.

To execute tests, users can provide a test mask for executing
group tests or standalone tests. API devlink_selftest_result_put() helps
drivers to populate the test results along with their names.

To query supported tests by device, API devlink_selftest_name_put()
helps a driver to populate test names.

Signed-off-by: Vikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: Michael Chan <michael.chan@broadcom.com>
Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>
---
 .../networking/devlink/devlink-selftests.rst  |  39 +++++
 include/net/devlink.h                         |  40 +++++
 include/uapi/linux/devlink.h                  |  24 +++
 net/core/devlink.c                            | 147 ++++++++++++++++++
 4 files changed, 250 insertions(+)
 create mode 100644 Documentation/networking/devlink/devlink-selftests.rst

Comments

Jakub Kicinski June 29, 2022, 5:05 a.m. UTC | #1
On Tue, 28 Jun 2022 22:12:39 +0530 Vikas Gupta wrote:
> Add a framework for running selftests.
> Framework exposes devlink commands and test suite(s) to the user
> to execute and query the supported tests by the driver.
> 
> To execute tests, users can provide a test mask for executing
> group tests or standalone tests. API devlink_selftest_result_put() helps
> drivers to populate the test results along with their names.
> 
> To query supported tests by device, API devlink_selftest_name_put()
> helps a driver to populate test names.
> 
> Signed-off-by: Vikas Gupta <vikas.gupta@broadcom.com>
> Reviewed-by: Michael Chan <michael.chan@broadcom.com>
> Reviewed-by: Andy Gospodarek <andrew.gospodarek@broadcom.com>
> ---
>  .../networking/devlink/devlink-selftests.rst  |  39 +++++
>  include/net/devlink.h                         |  40 +++++
>  include/uapi/linux/devlink.h                  |  24 +++
>  net/core/devlink.c                            | 147 ++++++++++++++++++
>  4 files changed, 250 insertions(+)
>  create mode 100644 Documentation/networking/devlink/devlink-selftests.rst
> 
> diff --git a/Documentation/networking/devlink/devlink-selftests.rst b/Documentation/networking/devlink/devlink-selftests.rst
> new file mode 100644
> index 000000000000..df7c8fcac9bf
> --- /dev/null
> +++ b/Documentation/networking/devlink/devlink-selftests.rst
> @@ -0,0 +1,39 @@
> +.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +
> +.. _devlink_selftests:

Why the label?

> +=================
> +Devlink Selftests
> +=================
> +
> +The ``devlink-selftests`` API allows executing selftests on the device.
> +
> +Tests Mask
> +==========
> +The ``devlink-selftests`` command should be run with a mask indicating
> +the tests to be executed.
> +
> +Tests Description
> +=================
> +The following is a list of tests that drivers may execute.
> +
> +.. list-table:: List of tests
> +   :widths: 5 90
> +
> +   * - Name
> +     - Description
> +   * - ``DEVLINK_SELFTEST_FLASH``
> +     - Runs a flash test on the device which helps to log the health of the flash.
> +       see Documentation/networking/devlink/devlink-health.rst

Quick grep of "flash" in devlink-health.rst shows nothing.
New sentence should be capitalized.

> +example usage
> +-------------
> +
> +.. code:: shell
> +
> +    # Query selftests supported on the device
> +    $ devlink dev selftests show DEV
> +    # Executes selftests on the device
> +    $ devlink dev selftests run DEV test {flash | all}
> +
> +

Spurious nl.

> diff --git a/include/net/devlink.h b/include/net/devlink.h
> index 2a2a2a0c93f7..493dab368340 100644
> --- a/include/net/devlink.h
> +++ b/include/net/devlink.h
> @@ -1215,6 +1215,19 @@ enum {
>  	DEVLINK_F_RELOAD = 1UL << 0,
>  };
>  
> +/**
> + * struct devlink_selftest_exec_info - Devlink selftest execution info.
> + * @name: Test name.
> + * @result: Test result.

Don't create kdoc if you have nothing to say in it.

> + */
> +struct devlink_selftest_exec_info {
> +	const char *name;
> +	bool result;

"result" doesn't indicate the polarity, better call it passed or failed.
int may be better to indicate pass/fail/skip?

> +};
> +
> +#define DEVLINK_SELFTEST_FLASH_TEST_NAME \
> +	"flash test"

no need to break this line

>  struct devlink_ops {
>  	/**
>  	 * @supported_flash_update_params:
> @@ -1509,6 +1522,28 @@ struct devlink_ops {
>  				    struct devlink_rate *parent,
>  				    void *priv_child, void *priv_parent,
>  				    struct netlink_ext_ack *extack);
> +	/**
> +	 * selftests_show() - Shows selftests supported by device
> +	 * @devlink: Devlink instance
> +	 * @skb: message payload
> +	 * @extack: extack for reporting error messages
> +	 *
> +	 * Return: 0 on success, negative value otherwise.
> +	 */
> +

spurious nl

> +	int (*selftests_show)(struct devlink *devlink, struct sk_buff *skb,
> +			      struct netlink_ext_ack *extack);

Why do we need to pass skb into this? The bits seem predefined so why
not return the mask and let the core do the formatting? 

> +	/**
> +	 * selftests_run() - Runs selftests
> +	 * @devlink: Devlink instance
> +	 * @skb: message payload
> +	 * @tests_mask: tests to be run
> +	 * @extack: extack for reporting error messages
> +	 *
> +	 * Return: 0 on success, negative value otherwise.
> +	 */
> +	int (*selftests_run)(struct devlink *devlink, struct sk_buff *skb,
> +			     u32 tests_mask, struct netlink_ext_ack *extack);

Similarly it may be more convenient for the drivers to pass in an array
in which to record results rather than having to deal with outputting
to an ask.

>  };
>  
>  void *devlink_priv(struct devlink *devlink);
> @@ -1774,6 +1809,11 @@ void
>  devlink_trap_policers_unregister(struct devlink *devlink,
>  				 const struct devlink_trap_policer *policers,
>  				 size_t policers_count);
> +int
> +devlink_selftest_result_put(struct sk_buff *skb,
> +			    struct devlink_selftest_exec_info *test);
> +int
> +devlink_selftest_name_put(struct sk_buff *skb, const char *name);
>  
>  #if IS_ENABLED(CONFIG_NET_DEVLINK)
>  
> diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
> index b3d40a5d72ff..58e2ef4010f0 100644
> --- a/include/uapi/linux/devlink.h
> +++ b/include/uapi/linux/devlink.h
> @@ -136,6 +136,9 @@ enum devlink_command {
>  	DEVLINK_CMD_LINECARD_NEW,
>  	DEVLINK_CMD_LINECARD_DEL,
>  
> +	DEVLINK_CMD_SELFTESTS_SHOW,
> +	DEVLINK_CMD_SELFTESTS_RUN,
> +
>  	/* add new commands above here */
>  	__DEVLINK_CMD_MAX,
>  	DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
> @@ -276,6 +279,21 @@ enum {
>  #define DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS \
>  	(_BITUL(__DEVLINK_FLASH_OVERWRITE_MAX_BIT) - 1)
>  
> +/* Commonly used test cases. Drivers might interpret test bit
> + * in their own way and it may map single to multiple tests.
> + */
> +enum {
> +	DEVLINK_SELFTEST_FLASH_BIT,
> +
> +	__DEVLINK_SELFTEST_MAX_BIT,
> +	DEVLINK_SELFTEST_MAX_BIT = __DEVLINK_SELFTEST_MAX_BIT - 1
> +};
> +
> +#define DEVLINK_SELFTEST_FLASH _BITUL(DEVLINK_SELFTEST_FLASH_BIT)
> +
> +#define DEVLINK_SUPPORTED_SELFTESTS \
> +	(_BITUL(__DEVLINK_SELFTEST_MAX_BIT) - 1)
> +
>  /**
>   * enum devlink_trap_action - Packet trap action.
>   * @DEVLINK_TRAP_ACTION_DROP: Packet is dropped by the device and a copy is not
> @@ -576,6 +594,12 @@ enum devlink_attr {
>  	DEVLINK_ATTR_LINECARD_TYPE,		/* string */
>  	DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES,	/* nested */
>  
> +	DEVLINK_ATTR_SELFTESTS_MASK,		/* bitfield32 */
> +	DEVLINK_ATTR_TEST_NAMES,		/* nested */
> +	DEVLINK_ATTR_TEST_NAME,			/* string */
> +	DEVLINK_ATTR_TEST_RESULTS,		/* nested */
> +	DEVLINK_ATTR_TEST_RESULT,		/* nested */

So many nests, netlink is much lighter on nesting that say JSON.
All you need is one nest type to wrap name and result, and just
output that multiple times to the message.

> +	DEVLINK_ATTR_TEST_RESULT_VAL,		/* u8 */
>  	/* add new attributes above here, update the policy in devlink.c */
>  
>  	__DEVLINK_ATTR_MAX,
> diff --git a/net/core/devlink.c b/net/core/devlink.c
> index db61f3a341cb..3c4c27a7dd40 100644
> --- a/net/core/devlink.c
> +++ b/net/core/devlink.c
> @@ -4794,6 +4794,139 @@ static int devlink_nl_cmd_flash_update(struct sk_buff *skb,
>  	return ret;
>  }
>  
> +int devlink_selftest_name_put(struct sk_buff *skb, const char *name)
> +{
> +	if (nla_put_string(skb, DEVLINK_ATTR_TEST_NAME, name))
> +		return -EMSGSIZE;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(devlink_selftest_name_put);
> +
> +static int devlink_nl_cmd_selftests_show(struct sk_buff *skb,
> +					 struct genl_info *info)
> +{
> +	struct devlink *devlink = info->user_ptr[0];
> +	struct nlattr  *names_attr;
> +	struct sk_buff *msg;
> +	void *hdr;
> +	int err;
> +
> +	if (!devlink->ops->selftests_show)
> +		return -EOPNOTSUPP;
> +
> +	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> +	if (!msg)
> +		return -ENOMEM;
> +
> +	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
> +			  &devlink_nl_family, 0, DEVLINK_CMD_SELFTESTS_SHOW);
> +	if (!hdr)

Leak.

> +		return -EMSGSIZE;
> +
> +	if (devlink_nl_put_handle(msg, devlink))
> +		goto genlmsg_cancel;

err not initialized

> +	names_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_TEST_NAMES);
> +	if (!names_attr)
> +		goto genlmsg_cancel;
> +
> +	err =  devlink->ops->selftests_show(devlink, msg, info->extack);

double space

> +	if (err)
> +		goto names_nest_cancel;
> +
> +	nla_nest_end(msg, names_attr);
> +	genlmsg_end(msg, hdr);
> +
> +	return genlmsg_reply(msg, info);
> +
> +names_nest_cancel:
> +	nla_nest_cancel(msg, names_attr);
> +genlmsg_cancel:
> +	genlmsg_cancel(msg, hdr);
> +	nlmsg_free(msg);
> +	return err;
> +}

>  static const struct devlink_param devlink_param_generic[] = {
>  	{
>  		.id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
> @@ -9000,6 +9133,8 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
>  	[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING },
>  	[DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 },
>  	[DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING },
> +	[DEVLINK_ATTR_SELFTESTS_MASK] =
> +		NLA_POLICY_BITFIELD32(DEVLINK_SUPPORTED_SELFTESTS),

You don't need a bitfield, use a u32 (u64?) with a mask policy.
diff mbox series

Patch

diff --git a/Documentation/networking/devlink/devlink-selftests.rst b/Documentation/networking/devlink/devlink-selftests.rst
new file mode 100644
index 000000000000..df7c8fcac9bf
--- /dev/null
+++ b/Documentation/networking/devlink/devlink-selftests.rst
@@ -0,0 +1,39 @@ 
+.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+.. _devlink_selftests:
+
+=================
+Devlink Selftests
+=================
+
+The ``devlink-selftests`` API allows executing selftests on the device.
+
+Tests Mask
+==========
+The ``devlink-selftests`` command should be run with a mask indicating
+the tests to be executed.
+
+Tests Description
+=================
+The following is a list of tests that drivers may execute.
+
+.. list-table:: List of tests
+   :widths: 5 90
+
+   * - Name
+     - Description
+   * - ``DEVLINK_SELFTEST_FLASH``
+     - Runs a flash test on the device which helps to log the health of the flash.
+       see Documentation/networking/devlink/devlink-health.rst
+
+example usage
+-------------
+
+.. code:: shell
+
+    # Query selftests supported on the device
+    $ devlink dev selftests show DEV
+    # Executes selftests on the device
+    $ devlink dev selftests run DEV test {flash | all}
+
+
diff --git a/include/net/devlink.h b/include/net/devlink.h
index 2a2a2a0c93f7..493dab368340 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -1215,6 +1215,19 @@  enum {
 	DEVLINK_F_RELOAD = 1UL << 0,
 };
 
+/**
+ * struct devlink_selftest_exec_info - Devlink selftest execution info.
+ * @name: Test name.
+ * @result: Test result.
+ */
+struct devlink_selftest_exec_info {
+	const char *name;
+	bool result;
+};
+
+#define DEVLINK_SELFTEST_FLASH_TEST_NAME \
+	"flash test"
+
 struct devlink_ops {
 	/**
 	 * @supported_flash_update_params:
@@ -1509,6 +1522,28 @@  struct devlink_ops {
 				    struct devlink_rate *parent,
 				    void *priv_child, void *priv_parent,
 				    struct netlink_ext_ack *extack);
+	/**
+	 * selftests_show() - Shows selftests supported by device
+	 * @devlink: Devlink instance
+	 * @skb: message payload
+	 * @extack: extack for reporting error messages
+	 *
+	 * Return: 0 on success, negative value otherwise.
+	 */
+
+	int (*selftests_show)(struct devlink *devlink, struct sk_buff *skb,
+			      struct netlink_ext_ack *extack);
+	/**
+	 * selftests_run() - Runs selftests
+	 * @devlink: Devlink instance
+	 * @skb: message payload
+	 * @tests_mask: tests to be run
+	 * @extack: extack for reporting error messages
+	 *
+	 * Return: 0 on success, negative value otherwise.
+	 */
+	int (*selftests_run)(struct devlink *devlink, struct sk_buff *skb,
+			     u32 tests_mask, struct netlink_ext_ack *extack);
 };
 
 void *devlink_priv(struct devlink *devlink);
@@ -1774,6 +1809,11 @@  void
 devlink_trap_policers_unregister(struct devlink *devlink,
 				 const struct devlink_trap_policer *policers,
 				 size_t policers_count);
+int
+devlink_selftest_result_put(struct sk_buff *skb,
+			    struct devlink_selftest_exec_info *test);
+int
+devlink_selftest_name_put(struct sk_buff *skb, const char *name);
 
 #if IS_ENABLED(CONFIG_NET_DEVLINK)
 
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index b3d40a5d72ff..58e2ef4010f0 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -136,6 +136,9 @@  enum devlink_command {
 	DEVLINK_CMD_LINECARD_NEW,
 	DEVLINK_CMD_LINECARD_DEL,
 
+	DEVLINK_CMD_SELFTESTS_SHOW,
+	DEVLINK_CMD_SELFTESTS_RUN,
+
 	/* add new commands above here */
 	__DEVLINK_CMD_MAX,
 	DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
@@ -276,6 +279,21 @@  enum {
 #define DEVLINK_SUPPORTED_FLASH_OVERWRITE_SECTIONS \
 	(_BITUL(__DEVLINK_FLASH_OVERWRITE_MAX_BIT) - 1)
 
+/* Commonly used test cases. Drivers might interpret test bit
+ * in their own way and it may map single to multiple tests.
+ */
+enum {
+	DEVLINK_SELFTEST_FLASH_BIT,
+
+	__DEVLINK_SELFTEST_MAX_BIT,
+	DEVLINK_SELFTEST_MAX_BIT = __DEVLINK_SELFTEST_MAX_BIT - 1
+};
+
+#define DEVLINK_SELFTEST_FLASH _BITUL(DEVLINK_SELFTEST_FLASH_BIT)
+
+#define DEVLINK_SUPPORTED_SELFTESTS \
+	(_BITUL(__DEVLINK_SELFTEST_MAX_BIT) - 1)
+
 /**
  * enum devlink_trap_action - Packet trap action.
  * @DEVLINK_TRAP_ACTION_DROP: Packet is dropped by the device and a copy is not
@@ -576,6 +594,12 @@  enum devlink_attr {
 	DEVLINK_ATTR_LINECARD_TYPE,		/* string */
 	DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES,	/* nested */
 
+	DEVLINK_ATTR_SELFTESTS_MASK,		/* bitfield32 */
+	DEVLINK_ATTR_TEST_NAMES,		/* nested */
+	DEVLINK_ATTR_TEST_NAME,			/* string */
+	DEVLINK_ATTR_TEST_RESULTS,		/* nested */
+	DEVLINK_ATTR_TEST_RESULT,		/* nested */
+	DEVLINK_ATTR_TEST_RESULT_VAL,		/* u8 */
 	/* add new attributes above here, update the policy in devlink.c */
 
 	__DEVLINK_ATTR_MAX,
diff --git a/net/core/devlink.c b/net/core/devlink.c
index db61f3a341cb..3c4c27a7dd40 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -4794,6 +4794,139 @@  static int devlink_nl_cmd_flash_update(struct sk_buff *skb,
 	return ret;
 }
 
+int devlink_selftest_name_put(struct sk_buff *skb, const char *name)
+{
+	if (nla_put_string(skb, DEVLINK_ATTR_TEST_NAME, name))
+		return -EMSGSIZE;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(devlink_selftest_name_put);
+
+static int devlink_nl_cmd_selftests_show(struct sk_buff *skb,
+					 struct genl_info *info)
+{
+	struct devlink *devlink = info->user_ptr[0];
+	struct nlattr  *names_attr;
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+
+	if (!devlink->ops->selftests_show)
+		return -EOPNOTSUPP;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
+			  &devlink_nl_family, 0, DEVLINK_CMD_SELFTESTS_SHOW);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	if (devlink_nl_put_handle(msg, devlink))
+		goto genlmsg_cancel;
+
+	names_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_TEST_NAMES);
+	if (!names_attr)
+		goto genlmsg_cancel;
+
+	err =  devlink->ops->selftests_show(devlink, msg, info->extack);
+	if (err)
+		goto names_nest_cancel;
+
+	nla_nest_end(msg, names_attr);
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+
+names_nest_cancel:
+	nla_nest_cancel(msg, names_attr);
+genlmsg_cancel:
+	genlmsg_cancel(msg, hdr);
+	nlmsg_free(msg);
+	return err;
+}
+
+int devlink_selftest_result_put(struct sk_buff *skb,
+				struct devlink_selftest_exec_info *test)
+{
+	struct nlattr *result_attr;
+
+	result_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_TEST_RESULT);
+	if (!result_attr)
+		return -EMSGSIZE;
+
+	if (nla_put_string(skb, DEVLINK_ATTR_TEST_NAME, test->name) ||
+	    nla_put_u8(skb, DEVLINK_ATTR_TEST_RESULT_VAL, test->result))
+		goto nla_put_failure;
+
+	nla_nest_end(skb, result_attr);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, result_attr);
+	return -EMSGSIZE;
+}
+EXPORT_SYMBOL_GPL(devlink_selftest_result_put);
+
+static int devlink_nl_cmd_selftests_run(struct sk_buff *skb,
+					struct genl_info *info)
+{
+	struct devlink *devlink = info->user_ptr[0];
+	struct nlattr  *nla_selftests_mask;
+	struct nlattr  *results_attr;
+	struct nla_bitfield32 tests;
+	struct sk_buff *msg;
+	u32 tests_mask;
+	void *hdr;
+	int err;
+
+	if (!devlink->ops->selftests_run)
+		return -EOPNOTSUPP;
+
+	if (!info->attrs[DEVLINK_ATTR_SELFTESTS_MASK])
+		return -EINVAL;
+
+	nla_selftests_mask = info->attrs[DEVLINK_ATTR_SELFTESTS_MASK];
+
+	tests = nla_get_bitfield32(nla_selftests_mask);
+	tests_mask = tests.value & tests.selector;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
+			  &devlink_nl_family, 0, DEVLINK_CMD_SELFTESTS_RUN);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	if (devlink_nl_put_handle(msg, devlink))
+		goto genlmsg_cancel;
+
+	results_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_TEST_RESULTS);
+	if (!results_attr)
+		goto genlmsg_cancel;
+
+	err =  devlink->ops->selftests_run(devlink, msg, tests_mask,
+					   info->extack);
+	if (err)
+		goto results_nest_cancel;
+
+	nla_nest_end(msg, results_attr);
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_reply(msg, info);
+
+results_nest_cancel:
+	nla_nest_cancel(msg, results_attr);
+genlmsg_cancel:
+	genlmsg_cancel(msg, hdr);
+	nlmsg_free(msg);
+	return err;
+}
+
 static const struct devlink_param devlink_param_generic[] = {
 	{
 		.id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
@@ -9000,6 +9133,8 @@  static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
 	[DEVLINK_ATTR_RATE_PARENT_NODE_NAME] = { .type = NLA_NUL_STRING },
 	[DEVLINK_ATTR_LINECARD_INDEX] = { .type = NLA_U32 },
 	[DEVLINK_ATTR_LINECARD_TYPE] = { .type = NLA_NUL_STRING },
+	[DEVLINK_ATTR_SELFTESTS_MASK] =
+		NLA_POLICY_BITFIELD32(DEVLINK_SUPPORTED_SELFTESTS),
 };
 
 static const struct genl_small_ops devlink_nl_ops[] = {
@@ -9361,6 +9496,18 @@  static const struct genl_small_ops devlink_nl_ops[] = {
 		.doit = devlink_nl_cmd_trap_policer_set_doit,
 		.flags = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd = DEVLINK_CMD_SELFTESTS_SHOW,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = devlink_nl_cmd_selftests_show,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = DEVLINK_CMD_SELFTESTS_RUN,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = devlink_nl_cmd_selftests_run,
+		.flags = GENL_ADMIN_PERM,
+	},
 };
 
 static struct genl_family devlink_nl_family __ro_after_init = {