ethtool: add compat for devlink info
authorJakub Kicinski <jakub.kicinski@netronome.com>
Thu, 31 Jan 2019 18:50:47 +0000 (10:50 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 1 Feb 2019 23:30:31 +0000 (15:30 -0800)
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.

v4:
 - use IS_REACHABLE() to avoid problems with DEVLINK=m (kbuildbot).
v3 (Jiri):
 - do a dump and then parse it instead of special handling;
 - concatenate all versions (well, all that fit :)).

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Acked-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/devlink.h
net/core/devlink.c
net/core/ethtool.c

index 6b417f1..1c85239 100644 (file)
@@ -972,4 +972,14 @@ devlink_info_version_running_put(struct devlink_info_req *req,
 }
 #endif
 
+#if IS_REACHABLE(CONFIG_NET_DEVLINK)
+void devlink_compat_running_version(struct net_device *dev,
+                                   char *buf, size_t len);
+#else
+static inline void
+devlink_compat_running_version(struct net_device *dev, char *buf, size_t len)
+{
+}
+#endif
+
 #endif /* _NET_DEVLINK_H_ */
index e31b6d6..eb839d7 100644 (file)
@@ -5278,6 +5278,69 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(devlink_region_snapshot_create);
 
+static void __devlink_compat_running_version(struct devlink *devlink,
+                                            char *buf, size_t len)
+{
+       const struct nlattr *nlattr;
+       struct devlink_info_req req;
+       struct sk_buff *msg;
+       int rem, err;
+
+       if (!devlink->ops->info_get)
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       req.msg = msg;
+       err = devlink->ops->info_get(devlink, &req, NULL);
+       if (err)
+               goto free_msg;
+
+       nla_for_each_attr(nlattr, (void *)msg->data, msg->len, rem) {
+               const struct nlattr *kv;
+               int rem_kv;
+
+               if (nla_type(nlattr) != DEVLINK_ATTR_INFO_VERSION_RUNNING)
+                       continue;
+
+               nla_for_each_nested(kv, nlattr, rem_kv) {
+                       if (nla_type(kv) != DEVLINK_ATTR_INFO_VERSION_VALUE)
+                               continue;
+
+                       strlcat(buf, nla_data(kv), len);
+                       strlcat(buf, " ", len);
+               }
+       }
+free_msg:
+       nlmsg_free(msg);
+}
+
+void devlink_compat_running_version(struct net_device *dev,
+                                   char *buf, size_t len)
+{
+       struct devlink_port *devlink_port;
+       struct devlink *devlink;
+
+       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) {
+                               __devlink_compat_running_version(devlink,
+                                                                buf, len);
+                               mutex_unlock(&devlink->lock);
+                               goto out;
+                       }
+               }
+               mutex_unlock(&devlink->lock);
+       }
+out:
+       mutex_unlock(&devlink_mutex);
+}
+
 static int __init devlink_module_init(void)
 {
        return genl_register_family(&devlink_nl_family);
index 3fe6e9d..45c0a6e 100644 (file)
@@ -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_version(dev, info.fw_version,
+                                              sizeof(info.fw_version));
+       rtnl_lock();
+
        if (copy_to_user(useraddr, &info, sizeof(info)))
                return -EFAULT;
        return 0;