If driver did not fill the fw_version field, try to call into
the new devlink get_info op and collect the versions that way.
We assume ethtool was always reporting running versions.

Signed-off-by: Jakub Kicinski <jakub.kicin...@netronome.com>
---
 include/net/devlink.h |  7 ++++++
 net/core/devlink.c    | 52 ++++++++++++++++++++++++++++++++++++++++++-
 net/core/ethtool.c    |  7 ++++++
 3 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/include/net/devlink.h b/include/net/devlink.h
index c678ed0cb099..b4750e93303a 100644
--- a/include/net/devlink.h
+++ b/include/net/devlink.h
@@ -640,6 +640,8 @@ int devlink_info_report_version(struct devlink_info_req 
*req,
                                enum devlink_version_type type,
                                const char *version_name,
                                const char *version_value);
+void devlink_compat_running_versions(struct net_device *dev,
+                                    char *buf, size_t len);
 
 #else
 
@@ -957,6 +959,11 @@ devlink_info_report_version(struct devlink_info_req *req,
 {
        return 0;
 }
+
+static inline void
+devlink_compat_running_versions(struct net_device *dev, char *buf, size_t len)
+{
+}
 #endif
 
 #endif /* _NET_DEVLINK_H_ */
diff --git a/net/core/devlink.c b/net/core/devlink.c
index e2027d3a5e40..5313e5918ee2 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -3715,12 +3715,18 @@ static int devlink_nl_cmd_region_read_dumpit(struct 
sk_buff *skb,
 }
 
 struct devlink_info_req {
+       bool compat;
        struct sk_buff *msg;
+       /* For compat call */
+       char *buf;
+       size_t len;
 };
 
 int devlink_info_report_driver_name(struct devlink_info_req *req,
                                    const char *name)
 {
+       if (req->compat)
+               return 0;
        return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRV_NAME, name);
 }
 EXPORT_SYMBOL_GPL(devlink_info_report_driver_name);
@@ -3728,6 +3734,8 @@ EXPORT_SYMBOL_GPL(devlink_info_report_driver_name);
 int devlink_info_report_serial_number(struct devlink_info_req *req,
                                      const char *sn)
 {
+       if (req->compat)
+               return 0;
        return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn);
 }
 EXPORT_SYMBOL_GPL(devlink_info_report_serial_number);
@@ -3743,7 +3751,15 @@ int devlink_info_report_version(struct devlink_info_req 
*req,
                [DEVLINK_VERSION_RUNNING] = DEVLINK_ATTR_INFO_VERSION_RUNNING,
        };
        struct nlattr *nest;
-       int err;
+       int len, err;
+
+       if (req->compat) {
+               if (type == DEVLINK_VERSION_RUNNING) {
+                       len = strlcpy(req->buf, version_value, req->len);
+                       req->len = max_t(size_t, 0, req->len - len);
+               }
+               return 0;
+       }
 
        if (type >= ARRAY_SIZE(type2attr) || !type2attr[type])
                return -EINVAL;
@@ -3789,6 +3805,7 @@ devlink_nl_info_fill(struct sk_buff *msg, struct devlink 
*devlink,
        if (devlink_nl_put_handle(msg, devlink))
                goto err_cancel_msg;
 
+       memset(&req, 0, sizeof(req));
        req.msg = msg;
        err = devlink->ops->info_get(devlink, &req, extack);
        if (err)
@@ -5263,6 +5280,39 @@ int devlink_region_snapshot_create(struct devlink_region 
*region, u64 data_len,
 }
 EXPORT_SYMBOL_GPL(devlink_region_snapshot_create);
 
+void devlink_compat_running_versions(struct net_device *dev,
+                                    char *buf, size_t len)
+{
+       struct devlink_port *devlink_port;
+       struct devlink_info_req req;
+       struct devlink *devlink;
+       bool found = false;
+
+       mutex_lock(&devlink_mutex);
+       list_for_each_entry(devlink, &devlink_list, list) {
+               mutex_lock(&devlink->lock);
+               list_for_each_entry(devlink_port, &devlink->port_list, list) {
+                       if (devlink_port->type == DEVLINK_PORT_TYPE_ETH ||
+                           devlink_port->type_dev == dev) {
+                               mutex_unlock(&devlink->lock);
+                               found = true;
+                               goto out;
+                       }
+               }
+               mutex_unlock(&devlink->lock);
+       }
+out:
+       if (found && devlink->ops->info_get) {
+               memset(&req, 0, sizeof(req));
+               req.compat = true;
+               req.buf = buf;
+               req.len = len;
+
+               devlink->ops->info_get(devlink, &req, NULL);
+       }
+       mutex_unlock(&devlink_mutex);
+}
+
 static int __init devlink_module_init(void)
 {
        return genl_register_family(&devlink_nl_family);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 158264f7cfaf..176b17d11f08 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -27,6 +27,7 @@
 #include <linux/rtnetlink.h>
 #include <linux/sched/signal.h>
 #include <linux/net.h>
+#include <net/devlink.h>
 #include <net/xdp_sock.h>
 
 /*
@@ -803,6 +804,12 @@ static noinline_for_stack int ethtool_get_drvinfo(struct 
net_device *dev,
        if (ops->get_eeprom_len)
                info.eedump_len = ops->get_eeprom_len(dev);
 
+       rtnl_unlock();
+       if (!info.fw_version[0])
+               devlink_compat_running_versions(dev, info.fw_version,
+                                               ARRAY_SIZE(info.fw_version));
+       rtnl_lock();
+
        if (copy_to_user(useraddr, &info, sizeof(info)))
                return -EFAULT;
        return 0;
-- 
2.19.2

Reply via email to