diff mbox series

[RFC,net-next,2/6] devlink: add version reporting API

Message ID 20190115005009.16025-3-jakub.kicinski@netronome.com
State RFC
Delegated to: David Miller
Headers show
Series devlink: add device (driver) information API | expand

Commit Message

Jakub Kicinski Jan. 15, 2019, 12:50 a.m. UTC
ethtool -i has a few fixed-size fields which can be used to report
firmware version and expansion ROM version.  Unfortunately, modern
hardware has more firmware components.  There is usually some
datapath microcode, management controller, PXE drivers, and a
CPLD load.  Running ethtool -i on modern controllers reveals the
fact that vendors cram multiple values into firmware version field.

Here are some examples from systems I could lay my hands on quickly:

tg3:  "FFV20.2.17 bc 5720-v1.39"
i40e: "6.01 0x800034a4 1.1747.0"
nfp:  "0.0.3.5 0.25 sriov-2.1.16 nic"

Add a new devlink API to allow retrieving multiple versions, and
provide user-readable name for those versions.

While at it break down the versions into three categories:
 - fixed - this is the board/fixed component version, usually vendors
           report information like the board version in the PCI VPD,
           but it will benefit from naming and common API as well;
 - running - this is the running firmware version;
 - stored - this is firmware in the flash, after firmware update
            this value will reflect the flashed version, while the
            running version may only be updated after reboot.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
---
 include/net/devlink.h        | 13 +++++++
 include/uapi/linux/devlink.h |  6 ++++
 net/core/devlink.c           | 67 +++++++++++++++++++++++++++++++++++-
 3 files changed, 85 insertions(+), 1 deletion(-)

Comments

Jiri Pirko Jan. 15, 2019, 10:12 a.m. UTC | #1
Tue, Jan 15, 2019 at 01:50:04AM CET, jakub.kicinski@netronome.com wrote:

[...]

>@@ -3701,6 +3732,40 @@ static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg,
> 	return msg->len;
> }
> 
>+int devlink_versions_report(struct sk_buff *skb, enum devlink_attr attr,
>+			    const char *version_name, const char *version_value)

Hmm, so this is a helper to construct the msg. I think it would be good
to have this somehow separated from the netlink. Meaning a separate enum
just for fixed/running/stored and perhaps a "context structure" which
you can use just as a pointer with layout invisible for the driver
(containing skb). Passing skb itself is a bit confusing and gives the
driver opportunity to put in some weird crap. Better to disallow it by
the api design.


>+{
>+	struct nlattr *nest;
>+	int err;
>+
>+	if (attr < DEVLINK_ATTR_INFO_VERSIONS_FIXED ||
>+	    attr > DEVLINK_ATTR_INFO_VERSIONS_STORED)
>+		return -EINVAL;
>+
>+	nest = nla_nest_start(skb, attr);
>+	if (!nest)
>+		return -EMSGSIZE;
>+
>+	err = nla_put_string(skb, DEVLINK_ATTR_INFO_VERSIONS_NAME,
>+			     version_name);
>+	if (err)
>+		goto nla_put_failure;
>+
>+	err = nla_put_string(skb, DEVLINK_ATTR_INFO_VERSIONS_VALUE,
>+			     version_value);
>+	if (err)
>+		goto nla_put_failure;
>+
>+	nla_nest_end(skb, nest);
>+
>+	return 0;
>+
>+nla_put_failure:
>+	nla_nest_cancel(skb, nest);
>+	return err;
>+}
>+EXPORT_SYMBOL_GPL(devlink_versions_report);
>+
> static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
> 	[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
> 	[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },
>-- 
>2.19.2
>
diff mbox series

Patch

diff --git a/include/net/devlink.h b/include/net/devlink.h
index 4358c111ce83..51f08edb1138 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -477,6 +477,8 @@  struct devlink_ops {
 				      struct netlink_ext_ack *extack);
 	int (*serial_get)(struct devlink *devlink, u8 *buf, size_t buf_len,
 			  size_t *len, struct netlink_ext_ack *extack);
+	int (*versions_get)(struct devlink *devlink, struct sk_buff *skb,
+			    struct netlink_ext_ack *extack);
 };
 
 static inline void *devlink_priv(struct devlink *devlink)
@@ -586,6 +588,10 @@  int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len,
 				   u8 *data, u32 snapshot_id,
 				   devlink_snapshot_data_dest_t *data_destructor);
 
+int devlink_versions_report(struct sk_buff *skb, enum devlink_attr attr,
+			    const char *version_name,
+			    const char *version_value);
+
 #else
 
 static inline struct devlink *devlink_alloc(const struct devlink_ops *ops,
@@ -846,6 +852,13 @@  devlink_region_snapshot_create(struct devlink_region *region, u64 data_len,
 	return 0;
 }
 
+static inline int
+devlink_versions_report(struct sk_buff *skb, enum devlink_attr attr,
+			const char *version_name, const char *version_value)
+{
+	return 0;
+}
+
 #endif
 
 #endif /* _NET_DEVLINK_H_ */
diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h
index 760c9c360330..eb97a52246ef 100644
--- a/include/uapi/linux/devlink.h
+++ b/include/uapi/linux/devlink.h
@@ -288,6 +288,12 @@  enum devlink_attr {
 	DEVLINK_ATTR_REGION_CHUNK_LEN,          /* u64 */
 
 	DEVLINK_ATTR_INFO_SERIAL_NUMBER,	/* binary */
+	DEVLINK_ATTR_INFO_VERSIONS,		/* nested */
+	DEVLINK_ATTR_INFO_VERSIONS_FIXED,	/* nested */
+	DEVLINK_ATTR_INFO_VERSIONS_RUNNING,	/* nested */
+	DEVLINK_ATTR_INFO_VERSIONS_STORED,	/* nested */
+	DEVLINK_ATTR_INFO_VERSIONS_NAME,	/* string */
+	DEVLINK_ATTR_INFO_VERSIONS_VALUE,	/* string */
 
 	/* add new attributes above here, update the policy in devlink.c */
 
diff --git a/net/core/devlink.c b/net/core/devlink.c
index 55b7b006df28..f35d917da7f2 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -3615,6 +3615,32 @@  static int devlink_nl_info_sn_fill(struct sk_buff *msg, struct devlink *devlink,
 	return nla_put(msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, len, sn);
 }
 
+static int devlink_nl_info_versions_fill(struct sk_buff *msg,
+					 struct devlink *devlink,
+					 struct netlink_ext_ack *extack)
+{
+	struct nlattr *attr;
+	int err;
+
+	if (!devlink->ops->versions_get)
+		return 0;
+
+	attr = nla_nest_start(msg, DEVLINK_ATTR_INFO_VERSIONS);
+	if (!attr)
+		return -EMSGSIZE;
+
+	err = devlink->ops->versions_get(devlink, msg, extack);
+	if (err)
+		goto err_cancel;
+
+	nla_nest_end(msg, attr);
+	return 0;
+
+err_cancel:
+	nla_nest_cancel(msg, attr);
+	return err;
+}
+
 static int
 devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
 		     enum devlink_command cmd, u32 portid,
@@ -3635,6 +3661,10 @@  devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
 	if (err)
 		goto err_cancel_msg;
 
+	err = devlink_nl_info_versions_fill(msg, devlink, extack);
+	if (err)
+		goto err_cancel_msg;
+
 	genlmsg_end(msg, hdr);
 	return 0;
 
@@ -3650,7 +3680,8 @@  static int devlink_nl_cmd_info_get_doit(struct sk_buff *skb,
 	struct sk_buff *msg;
 	int err;
 
-	if (!devlink->ops || !devlink->ops->serial_get)
+	if (!devlink->ops ||
+	    (!devlink->ops->serial_get && !devlink->ops->versions_get))
 		return -EOPNOTSUPP;
 
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -3701,6 +3732,40 @@  static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg,
 	return msg->len;
 }
 
+int devlink_versions_report(struct sk_buff *skb, enum devlink_attr attr,
+			    const char *version_name, const char *version_value)
+{
+	struct nlattr *nest;
+	int err;
+
+	if (attr < DEVLINK_ATTR_INFO_VERSIONS_FIXED ||
+	    attr > DEVLINK_ATTR_INFO_VERSIONS_STORED)
+		return -EINVAL;
+
+	nest = nla_nest_start(skb, attr);
+	if (!nest)
+		return -EMSGSIZE;
+
+	err = nla_put_string(skb, DEVLINK_ATTR_INFO_VERSIONS_NAME,
+			     version_name);
+	if (err)
+		goto nla_put_failure;
+
+	err = nla_put_string(skb, DEVLINK_ATTR_INFO_VERSIONS_VALUE,
+			     version_value);
+	if (err)
+		goto nla_put_failure;
+
+	nla_nest_end(skb, nest);
+
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(skb, nest);
+	return err;
+}
+EXPORT_SYMBOL_GPL(devlink_versions_report);
+
 static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
 	[DEVLINK_ATTR_BUS_NAME] = { .type = NLA_NUL_STRING },
 	[DEVLINK_ATTR_DEV_NAME] = { .type = NLA_NUL_STRING },