Commit d6b0a01f authored by David S. Miller's avatar David S. Miller

Merge branch 'devlink-add-device-driver-information-API'

Jakub Kicinski says:

====================
devlink: add device (driver) information API

fw_version field in ethtool -i does not suit modern needs with 31
characters being quite limiting on more complex systems.  There is
also no distinction between the running and flashed versions of
the firmware.

Since the driver information pertains to the entire device, rather
than a particular netdev, it seems wise to move it do devlink, at
the same time fixing the aforementioned issues.

The new API allows exposing the device serial number and versions
of the components of the card - both hardware, firmware (running
and flashed).  Driver authors can choose descriptive identifiers
for the version fields.  A few version identifiers which seemed
relevant for most devices have been added to the global devlink
header.

Example:
$ devlink dev info pci/0000:05:00.0
pci/0000:05:00.0:
  driver nfp
  serial_number 16240145
  versions:
    fixed:
      board.id AMDA0099-0001
      board.rev 07
      board.vendor SMA
      board.model carbon
    running:
      fw.mgmt: 010156.010156.010156
      fw.cpld: 0x44
      fw.app: sriov-2.1.16
    stored:
      fw.mgmt: 010158.010158.010158
      fw.cpld: 0x44
      fw.app: sriov-2.1.20

Last patch also includes a compat code for ethtool.  If driver
reports no fw_version via the traditional ethtool API, ethtool
can call into devlink and try to cram as many versions as possible
into the 31 characters.

v4:
 - use IS_REACHABLE instead of IS_ENABLED in last patch.

v3 (Jiri):
 - rename various functions and attributes;
 - break out the version helpers per-type;
 - make the compat code parse a dump instead of special casing
   in each helper;
 - move generic version defines to a separate patch.

v2:
 - rebase.

this non-RFC, v3 some would say:
 - add three more versions in the NFP patches;
 - add last patch (ethool compat) - Andrew & Michal.

RFCv2:
 - use one driver op;
 - allow longer serial number;
 - wrap the skb into an opaque request struct;
 - add some common identifier into the devlink header.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 26281e2c ddb6e99e
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
=====================
Devlink info versions
=====================
board.id
========
Unique identifier of the board design.
board.rev
=========
Board design revision.
fw.mgmt
=======
Control unit firmware version. This firmware is responsible for house
keeping tasks, PHY control etc. but not the packet-by-packet data path
operation.
fw.app
======
Data path microcode controlling high-speed packet processing.
fw.undi
=======
UNDI software, may include the UEFI driver, firmware or both.
fw.ncsi
=======
Version of the software responsible for supporting/handling the
Network Controller Sideband Interface.
......@@ -24,6 +24,7 @@ Contents:
device_drivers/intel/i40e
device_drivers/intel/iavf
device_drivers/intel/ice
devlink-info-versions
kapi
z8530book
msg_zerocopy
......
......@@ -4,6 +4,7 @@
#include <linux/rtnetlink.h>
#include <net/devlink.h>
#include "nfpcore/nfp.h"
#include "nfpcore/nfp_nsp.h"
#include "nfp_app.h"
#include "nfp_main.h"
......@@ -171,6 +172,149 @@ static int nfp_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
return ret;
}
static const struct nfp_devlink_versions_simple {
const char *key;
const char *hwinfo;
} nfp_devlink_versions_hwinfo[] = {
{ DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, "assembly.partno", },
{ DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, "assembly.revision", },
{ "board.vendor", /* fab */ "assembly.vendor", },
{ "board.model", /* code name */ "assembly.model", },
};
static int
nfp_devlink_versions_get_hwinfo(struct nfp_pf *pf, struct devlink_info_req *req)
{
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(nfp_devlink_versions_hwinfo); i++) {
const struct nfp_devlink_versions_simple *info;
const char *val;
info = &nfp_devlink_versions_hwinfo[i];
val = nfp_hwinfo_lookup(pf->hwinfo, info->hwinfo);
if (!val)
continue;
err = devlink_info_version_fixed_put(req, info->key, val);
if (err)
return err;
}
return 0;
}
static const struct nfp_devlink_versions {
enum nfp_nsp_versions id;
const char *key;
} nfp_devlink_versions_nsp[] = {
{ NFP_VERSIONS_BUNDLE, "fw.bundle_id", },
{ NFP_VERSIONS_BSP, DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, },
{ NFP_VERSIONS_CPLD, "fw.cpld", },
{ NFP_VERSIONS_APP, DEVLINK_INFO_VERSION_GENERIC_FW_APP, },
{ NFP_VERSIONS_UNDI, DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, },
{ NFP_VERSIONS_NCSI, DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, },
{ NFP_VERSIONS_CFGR, "chip.init", },
};
static int
nfp_devlink_versions_get_nsp(struct devlink_info_req *req, bool flash,
const u8 *buf, unsigned int size)
{
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(nfp_devlink_versions_nsp); i++) {
const struct nfp_devlink_versions *info;
const char *version;
info = &nfp_devlink_versions_nsp[i];
version = nfp_nsp_versions_get(info->id, flash, buf, size);
if (IS_ERR(version)) {
if (PTR_ERR(version) == -ENOENT)
continue;
else
return PTR_ERR(version);
}
if (flash)
err = devlink_info_version_stored_put(req, info->key,
version);
else
err = devlink_info_version_running_put(req, info->key,
version);
if (err)
return err;
}
return 0;
}
static int
nfp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
struct nfp_pf *pf = devlink_priv(devlink);
struct nfp_nsp *nsp;
char *buf = NULL;
const char *sn;
int err;
err = devlink_info_driver_name_put(req, "nfp");
if (err)
return err;
sn = nfp_hwinfo_lookup(pf->hwinfo, "assembly.serial");
if (sn) {
err = devlink_info_serial_number_put(req, sn);
if (err)
return err;
}
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
NL_SET_ERR_MSG_MOD(extack, "can't access NSP");
return PTR_ERR(nsp);
}
if (nfp_nsp_has_versions(nsp)) {
buf = kzalloc(NFP_NSP_VERSION_BUFSZ, GFP_KERNEL);
if (!buf) {
err = -ENOMEM;
goto err_close_nsp;
}
err = nfp_nsp_versions(nsp, buf, NFP_NSP_VERSION_BUFSZ);
if (err)
goto err_free_buf;
err = nfp_devlink_versions_get_nsp(req, false,
buf, NFP_NSP_VERSION_BUFSZ);
if (err)
goto err_free_buf;
err = nfp_devlink_versions_get_nsp(req, true,
buf, NFP_NSP_VERSION_BUFSZ);
if (err)
goto err_free_buf;
kfree(buf);
}
nfp_nsp_close(nsp);
return nfp_devlink_versions_get_hwinfo(pf, req);
err_free_buf:
kfree(buf);
err_close_nsp:
nfp_nsp_close(nsp);
return err;
}
const struct devlink_ops nfp_devlink_ops = {
.port_split = nfp_devlink_port_split,
.port_unsplit = nfp_devlink_port_unsplit,
......@@ -178,6 +322,7 @@ const struct devlink_ops nfp_devlink_ops = {
.sb_pool_set = nfp_devlink_sb_pool_set,
.eswitch_mode_get = nfp_devlink_eswitch_mode_get,
.eswitch_mode_set = nfp_devlink_eswitch_mode_set,
.info_get = nfp_devlink_info_get,
};
int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
......
......@@ -7,6 +7,7 @@
* Jason McMullan <jason.mcmullan@netronome.com>
*/
#include <asm/unaligned.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/firmware.h>
......@@ -62,6 +63,16 @@
#define NFP_HWINFO_LOOKUP_SIZE GENMASK(11, 0)
#define NFP_VERSIONS_SIZE GENMASK(11, 0)
#define NFP_VERSIONS_CNT_OFF 0
#define NFP_VERSIONS_BSP_OFF 2
#define NFP_VERSIONS_CPLD_OFF 6
#define NFP_VERSIONS_APP_OFF 10
#define NFP_VERSIONS_BUNDLE_OFF 14
#define NFP_VERSIONS_UNDI_OFF 18
#define NFP_VERSIONS_NCSI_OFF 22
#define NFP_VERSIONS_CFGR_OFF 26
enum nfp_nsp_cmd {
SPCODE_NOOP = 0, /* No operation */
SPCODE_SOFT_RESET = 1, /* Soft reset the NFP */
......@@ -77,6 +88,7 @@ enum nfp_nsp_cmd {
SPCODE_NSP_IDENTIFY = 13, /* Read NSP version */
SPCODE_FW_STORED = 16, /* If no FW loaded, load flash app FW */
SPCODE_HWINFO_LOOKUP = 17, /* Lookup HWinfo with overwrites etc. */
SPCODE_VERSIONS = 21, /* Report FW versions */
};
static const struct {
......@@ -711,3 +723,52 @@ int nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size)
return 0;
}
int nfp_nsp_versions(struct nfp_nsp *state, void *buf, unsigned int size)
{
struct nfp_nsp_command_buf_arg versions = {
{
.code = SPCODE_VERSIONS,
.option = min_t(u32, size, NFP_VERSIONS_SIZE),
},
.out_buf = buf,
.out_size = min_t(u32, size, NFP_VERSIONS_SIZE),
};
return nfp_nsp_command_buf(state, &versions);
}
const char *nfp_nsp_versions_get(enum nfp_nsp_versions id, bool flash,
const u8 *buf, unsigned int size)
{
static const u32 id2off[] = {
[NFP_VERSIONS_BSP] = NFP_VERSIONS_BSP_OFF,
[NFP_VERSIONS_CPLD] = NFP_VERSIONS_CPLD_OFF,
[NFP_VERSIONS_APP] = NFP_VERSIONS_APP_OFF,
[NFP_VERSIONS_BUNDLE] = NFP_VERSIONS_BUNDLE_OFF,
[NFP_VERSIONS_UNDI] = NFP_VERSIONS_UNDI_OFF,
[NFP_VERSIONS_NCSI] = NFP_VERSIONS_NCSI_OFF,
[NFP_VERSIONS_CFGR] = NFP_VERSIONS_CFGR_OFF,
};
unsigned int field, buf_field_cnt, buf_off;
if (id >= ARRAY_SIZE(id2off) || !id2off[id])
return ERR_PTR(-EINVAL);
field = id * 2 + flash;
buf_field_cnt = get_unaligned_le16(buf);
if (buf_field_cnt <= field)
return ERR_PTR(-ENOENT);
buf_off = get_unaligned_le16(buf + id2off[id] + flash * 2);
if (!buf_off)
return ERR_PTR(-ENOENT);
if (buf_off >= size)
return ERR_PTR(-EINVAL);
if (strnlen(&buf[buf_off], size - buf_off) == size - buf_off)
return ERR_PTR(-EINVAL);
return (const char *)&buf[buf_off];
}
......@@ -38,6 +38,11 @@ static inline bool nfp_nsp_has_hwinfo_lookup(struct nfp_nsp *state)
return nfp_nsp_get_abi_ver_minor(state) > 24;
}
static inline bool nfp_nsp_has_versions(struct nfp_nsp *state)
{
return nfp_nsp_get_abi_ver_minor(state) > 27;
}
enum nfp_eth_interface {
NFP_INTERFACE_NONE = 0,
NFP_INTERFACE_SFP = 1,
......@@ -208,4 +213,19 @@ enum nfp_nsp_sensor_id {
int nfp_hwmon_read_sensor(struct nfp_cpp *cpp, enum nfp_nsp_sensor_id id,
long *val);
#define NFP_NSP_VERSION_BUFSZ 1024 /* reasonable size, not in the ABI */
enum nfp_nsp_versions {
NFP_VERSIONS_BSP,
NFP_VERSIONS_CPLD,
NFP_VERSIONS_APP,
NFP_VERSIONS_BUNDLE,
NFP_VERSIONS_UNDI,
NFP_VERSIONS_NCSI,
NFP_VERSIONS_CFGR,
};
int nfp_nsp_versions(struct nfp_nsp *state, void *buf, unsigned int size);
const char *nfp_nsp_versions_get(enum nfp_nsp_versions id, bool flash,
const u8 *buf, unsigned int size);
#endif
......@@ -428,7 +428,22 @@ enum devlink_param_wol_types {
.validate = _validate, \
}
/* Part number, identifier of board design */
#define DEVLINK_INFO_VERSION_GENERIC_BOARD_ID "board.id"
/* Revision of board design */
#define DEVLINK_INFO_VERSION_GENERIC_BOARD_REV "board.rev"
/* Control processor FW version */
#define DEVLINK_INFO_VERSION_GENERIC_FW_MGMT "fw.mgmt"
/* Data path microcode controlling high-speed packet processing */
#define DEVLINK_INFO_VERSION_GENERIC_FW_APP "fw.app"
/* UNDI software version */
#define DEVLINK_INFO_VERSION_GENERIC_FW_UNDI "fw.undi"
/* NCSI support/handler version */
#define DEVLINK_INFO_VERSION_GENERIC_FW_NCSI "fw.ncsi"
struct devlink_region;
struct devlink_info_req;
typedef void devlink_snapshot_data_dest_t(const void *data);
......@@ -484,6 +499,8 @@ struct devlink_ops {
int (*eswitch_encap_mode_get)(struct devlink *devlink, u8 *p_encap_mode);
int (*eswitch_encap_mode_set)(struct devlink *devlink, u8 encap_mode,
struct netlink_ext_ack *extack);
int (*info_get)(struct devlink *devlink, struct devlink_info_req *req,
struct netlink_ext_ack *extack);
};
static inline void *devlink_priv(struct devlink *devlink)
......@@ -607,6 +624,19 @@ u32 devlink_region_shapshot_id_get(struct devlink *devlink);
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_info_serial_number_put(struct devlink_info_req *req,
const char *sn);
int devlink_info_driver_name_put(struct devlink_info_req *req,
const char *name);
int devlink_info_version_fixed_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value);
int devlink_info_version_stored_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value);
int devlink_info_version_running_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value);
#else
......@@ -905,6 +935,51 @@ devlink_region_snapshot_create(struct devlink_region *region, u64 data_len,
return 0;
}
static inline int
devlink_info_driver_name_put(struct devlink_info_req *req, const char *name)
{
return 0;
}
static inline int
devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
{
return 0;
}
static inline int
devlink_info_version_fixed_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value)
{
return 0;
}
static inline int
devlink_info_version_stored_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value)
{
return 0;
}
static inline int
devlink_info_version_running_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value)
{
return 0;
}
#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_ */
......@@ -94,6 +94,8 @@ enum devlink_command {
DEVLINK_CMD_PORT_PARAM_NEW,
DEVLINK_CMD_PORT_PARAM_DEL,
DEVLINK_CMD_INFO_GET, /* can dump */
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
......@@ -290,6 +292,14 @@ enum devlink_attr {
DEVLINK_ATTR_REGION_CHUNK_ADDR, /* u64 */
DEVLINK_ATTR_REGION_CHUNK_LEN, /* u64 */
DEVLINK_ATTR_INFO_DRIVER_NAME, /* string */
DEVLINK_ATTR_INFO_SERIAL_NUMBER, /* string */
DEVLINK_ATTR_INFO_VERSION_FIXED, /* nested */
DEVLINK_ATTR_INFO_VERSION_RUNNING, /* nested */
DEVLINK_ATTR_INFO_VERSION_STORED, /* nested */
DEVLINK_ATTR_INFO_VERSION_NAME, /* string */
DEVLINK_ATTR_INFO_VERSION_VALUE, /* string */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
......
......@@ -3714,6 +3714,167 @@ static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
return 0;
}
struct devlink_info_req {
struct sk_buff *msg;
};
int devlink_info_driver_name_put(struct devlink_info_req *req, const char *name)
{
return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME, name);
}
EXPORT_SYMBOL_GPL(devlink_info_driver_name_put);
int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
{
return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn);
}
EXPORT_SYMBOL_GPL(devlink_info_serial_number_put);
static int devlink_info_version_put(struct devlink_info_req *req, int attr,
const char *version_name,
const char *version_value)
{
struct nlattr *nest;
int err;
nest = nla_nest_start(req->msg, attr);
if (!nest)
return -EMSGSIZE;
err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_NAME,
version_name);
if (err)
goto nla_put_failure;
err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_VALUE,
version_value);
if (err)
goto nla_put_failure;
nla_nest_end(req->msg, nest);
return 0;
nla_put_failure:
nla_nest_cancel(req->msg, nest);
return err;
}
int devlink_info_version_fixed_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value)
{
return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_FIXED,
version_name, version_value);
}
EXPORT_SYMBOL_GPL(devlink_info_version_fixed_put);
int devlink_info_version_stored_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value)
{
return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
version_name, version_value);
}
EXPORT_SYMBOL_GPL(devlink_info_version_stored_put);
int devlink_info_version_running_put(struct devlink_info_req *req,
const char *version_name,
const char *version_value)
{
return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
version_name, version_value);
}
EXPORT_SYMBOL_GPL(devlink_info_version_running_put);
static int
devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
enum devlink_command cmd, u32 portid,
u32 seq, int flags, struct netlink_ext_ack *extack)
{
struct devlink_info_req req;
void *hdr;
int err;
hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
if (!hdr)
return -EMSGSIZE;
err = -EMSGSIZE;
if (devlink_nl_put_handle(msg, devlink))
goto err_cancel_msg;
req.msg = msg;
err = devlink->ops->info_get(devlink, &req, extack);
if (err)
goto err_cancel_msg;
genlmsg_end(msg, hdr);
return 0;
err_cancel_msg:
genlmsg_cancel(msg, hdr);
return err;
}
static int devlink_nl_cmd_info_get_doit(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
struct sk_buff *msg;
int err;
if (!devlink->ops || !devlink->ops->info_get)
return -EOPNOTSUPP;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
info->snd_portid, info->snd_seq, 0,
info->extack);
if (err) {
nlmsg_free(msg);
return err;
}
return genlmsg_reply(msg, info);
}
static int devlink_nl_cmd_info_get_dumpit(struct sk_buff *msg,
struct netlink_callback *cb)
{
struct devlink *devlink;
int start = cb->args[0];
int idx = 0;
int err;
mutex_lock(&devlink_mutex);
list_for_each_entry(devlink, &devlink_list, list) {
if (!net_eq(devlink_net(devlink), sock_net(msg->sk)))
continue;
if (idx < start) {
idx++;
continue;
}
mutex_lock(&devlink->lock);
err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
cb->extack);
mutex_unlock(&devlink->lock);
if (err)
break;
idx++;
}
mutex_unlock(&devlink_mutex);
cb->args[0] = idx;
return msg->len;
}
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 },
......@@ -3974,6 +4135,14 @@ static const struct genl_ops devlink_nl_ops[] = {
.flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
},
{
.cmd = DEVLINK_CMD_INFO_GET,
.doit = devlink_nl_cmd_info_get_doit,
.dumpit = devlink_nl_cmd_info_get_dumpit,
.policy = devlink_nl_policy,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
/* can be retrieved by unprivileged users */
},
};
static struct genl_family devlink_nl_family __ro_after_init = {
......@@ -5109,6 +5278,69 @@ int devlink_region_snapshot_create(struct devlink_region *region, u64 data_len,
}
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);
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment