Commit ddb6e99e authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller
Browse files

ethtool: add compat for devlink info



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: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
Acked-by: default avatarJiri Pirko <jiri@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7c908f46
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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_ */
+63 −0
Original line number Diff line number Diff line
@@ -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);
+7 −0
Original line number Diff line number Diff line
@@ -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;