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

Merge branch 'devlink-add-the-ability-to-update-device-flash'

Jakub Kicinski says:

====================
devlink: add the ability to update device flash

This series is the second step to allow trouble shooting and recovering
devices in bad state without the use of netdevs as handles.  We can
already query FW versions over devlink, now we add the ability to update
the FW.  This will allow drivers to implement some from of "limp-mode"
where the device can't really be used for networking and hence has no
netdev, but we can interrogate it over devlink and fix the broken FW.

Small but nice advantage of devlink is that it only holds the devlink
instance lock during flashing, unlike ethtool which holds rtnl_lock().
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8e31c474 5c5696f3
......@@ -330,6 +330,15 @@ nfp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
return err;
}
static int
nfp_devlink_flash_update(struct devlink *devlink, const char *path,
const char *component, struct netlink_ext_ack *extack)
{
if (component)
return -EOPNOTSUPP;
return nfp_flash_update_common(devlink_priv(devlink), path, extack);
}
const struct devlink_ops nfp_devlink_ops = {
.port_split = nfp_devlink_port_split,
.port_unsplit = nfp_devlink_port_unsplit,
......@@ -338,6 +347,7 @@ const struct devlink_ops nfp_devlink_ops = {
.eswitch_mode_get = nfp_devlink_eswitch_mode_get,
.eswitch_mode_set = nfp_devlink_eswitch_mode_set,
.info_get = nfp_devlink_info_get,
.flash_update = nfp_devlink_flash_update,
};
int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)
......
......@@ -300,6 +300,47 @@ static int nfp_pcie_sriov_configure(struct pci_dev *pdev, int num_vfs)
return nfp_pcie_sriov_enable(pdev, num_vfs);
}
int nfp_flash_update_common(struct nfp_pf *pf, const char *path,
struct netlink_ext_ack *extack)
{
struct device *dev = &pf->pdev->dev;
const struct firmware *fw;
struct nfp_nsp *nsp;
int err;
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
if (extack)
NL_SET_ERR_MSG_MOD(extack, "can't access NSP");
else
dev_err(dev, "Failed to access the NSP: %d\n", err);
return err;
}
err = request_firmware_direct(&fw, path, dev);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"unable to read flash file from disk");
goto exit_close_nsp;
}
dev_info(dev, "Please be patient while writing flash image: %s\n",
path);
err = nfp_nsp_write_flash(nsp, fw);
if (err < 0)
goto exit_release_fw;
dev_info(dev, "Finished writing flash image\n");
err = 0;
exit_release_fw:
release_firmware(fw);
exit_close_nsp:
nfp_nsp_close(nsp);
return err;
}
static const struct firmware *
nfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name)
{
......
......@@ -164,6 +164,8 @@ nfp_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
unsigned int min_size, struct nfp_cpp_area **area);
int nfp_mbox_cmd(struct nfp_pf *pf, u32 cmd, void *in_data, u64 in_length,
void *out_data, u64 out_length);
int nfp_flash_update_common(struct nfp_pf *pf, const char *path,
struct netlink_ext_ack *extack);
enum nfp_dump_diag {
NFP_DUMP_NSP_DIAG = 0,
......
......@@ -1237,11 +1237,8 @@ static int nfp_net_set_channels(struct net_device *netdev,
static int
nfp_net_flash_device(struct net_device *netdev, struct ethtool_flash *flash)
{
const struct firmware *fw;
struct nfp_app *app;
struct nfp_nsp *nsp;
struct device *dev;
int err;
int ret;
if (flash->region != ETHTOOL_FLASH_ALL_REGIONS)
return -EOPNOTSUPP;
......@@ -1250,39 +1247,13 @@ nfp_net_flash_device(struct net_device *netdev, struct ethtool_flash *flash)
if (!app)
return -EOPNOTSUPP;
dev = &app->pdev->dev;
nsp = nfp_nsp_open(app->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
dev_err(dev, "Failed to access the NSP: %d\n", err);
return err;
}
err = request_firmware_direct(&fw, flash->data, dev);
if (err)
goto exit_close_nsp;
dev_info(dev, "Please be patient while writing flash image: %s\n",
flash->data);
dev_hold(netdev);
rtnl_unlock();
err = nfp_nsp_write_flash(nsp, fw);
if (err < 0) {
dev_err(dev, "Flash write failed: %d\n", err);
goto exit_rtnl_lock;
}
dev_info(dev, "Finished writing flash image\n");
exit_rtnl_lock:
ret = nfp_flash_update_common(app->pf, flash->data, NULL);
rtnl_lock();
dev_put(netdev);
release_firmware(fw);
exit_close_nsp:
nfp_nsp_close(nsp);
return err;
return ret;
}
static const struct ethtool_ops nfp_net_ethtool_ops = {
......
......@@ -521,6 +521,9 @@ struct devlink_ops {
struct netlink_ext_ack *extack);
int (*info_get)(struct devlink *devlink, struct devlink_info_req *req,
struct netlink_ext_ack *extack);
int (*flash_update)(struct devlink *devlink, const char *file_name,
const char *component,
struct netlink_ext_ack *extack);
};
static inline void *devlink_priv(struct devlink *devlink)
......@@ -1192,11 +1195,18 @@ devlink_health_report(struct devlink_health_reporter *reporter,
#if IS_REACHABLE(CONFIG_NET_DEVLINK)
void devlink_compat_running_version(struct net_device *dev,
char *buf, size_t len);
int devlink_compat_flash_update(struct net_device *dev, const char *file_name);
#else
static inline void
devlink_compat_running_version(struct net_device *dev, char *buf, size_t len)
{
}
static inline int
devlink_compat_flash_update(struct net_device *dev, const char *file_name)
{
return -EOPNOTSUPP;
}
#endif
#endif /* _NET_DEVLINK_H_ */
......@@ -103,6 +103,8 @@ enum devlink_command {
DEVLINK_CMD_HEALTH_REPORTER_DUMP_GET,
DEVLINK_CMD_HEALTH_REPORTER_DUMP_CLEAR,
DEVLINK_CMD_FLASH_UPDATE,
/* add new commands above here */
__DEVLINK_CMD_MAX,
DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
......@@ -326,6 +328,10 @@ enum devlink_attr {
DEVLINK_ATTR_HEALTH_REPORTER_DUMP_TS, /* u64 */
DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD, /* u64 */
DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER, /* u8 */
DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME, /* string */
DEVLINK_ATTR_FLASH_UPDATE_COMPONENT, /* string */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
......
......@@ -2662,6 +2662,27 @@ static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
return devlink->ops->reload(devlink, info->extack);
}
static int devlink_nl_cmd_flash_update(struct sk_buff *skb,
struct genl_info *info)
{
struct devlink *devlink = info->user_ptr[0];
const char *file_name, *component;
struct nlattr *nla_component;
if (!devlink->ops->flash_update)
return -EOPNOTSUPP;
if (!info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME])
return -EINVAL;
file_name = nla_data(info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME]);
nla_component = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT];
component = nla_component ? nla_data(nla_component) : NULL;
return devlink->ops->flash_update(devlink, file_name, component,
info->extack);
}
static const struct devlink_param devlink_param_generic[] = {
{
.id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
......@@ -4883,6 +4904,8 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
[DEVLINK_ATTR_HEALTH_REPORTER_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_HEALTH_REPORTER_GRACEFUL_PERIOD] = { .type = NLA_U64 },
[DEVLINK_ATTR_HEALTH_REPORTER_AUTO_RECOVER] = { .type = NLA_U8 },
[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] = { .type = NLA_NUL_STRING },
[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] = { .type = NLA_NUL_STRING },
};
static const struct genl_ops devlink_nl_ops[] = {
......@@ -5171,6 +5194,13 @@ static const struct genl_ops devlink_nl_ops[] = {
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK |
DEVLINK_NL_FLAG_NO_LOCK,
},
{
.cmd = DEVLINK_CMD_FLASH_UPDATE,
.doit = devlink_nl_cmd_flash_update,
.policy = devlink_nl_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK,
},
};
static struct genl_family devlink_nl_family __ro_after_init = {
......@@ -6420,6 +6450,36 @@ void devlink_compat_running_version(struct net_device *dev,
mutex_unlock(&devlink_mutex);
}
int devlink_compat_flash_update(struct net_device *dev, const char *file_name)
{
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) {
int ret = -EOPNOTSUPP;
if (devlink_port->type != DEVLINK_PORT_TYPE_ETH ||
devlink_port->type_dev != dev)
continue;
mutex_unlock(&devlink_mutex);
if (devlink->ops->flash_update)
ret = devlink->ops->flash_update(devlink,
file_name,
NULL, NULL);
mutex_unlock(&devlink->lock);
return ret;
}
mutex_unlock(&devlink->lock);
}
mutex_unlock(&devlink_mutex);
return -EOPNOTSUPP;
}
static int __init devlink_module_init(void)
{
return genl_register_family(&devlink_nl_family);
......
......@@ -2038,11 +2038,17 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
if (copy_from_user(&efl, useraddr, sizeof(efl)))
return -EFAULT;
efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
if (!dev->ethtool_ops->flash_device)
return -EOPNOTSUPP;
if (!dev->ethtool_ops->flash_device) {
int ret;
efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
rtnl_unlock();
ret = devlink_compat_flash_update(dev, efl.data);
rtnl_lock();
return ret;
}
return dev->ethtool_ops->flash_device(dev, &efl);
}
......
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