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