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: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-02-17 15:27:39 -08:00
commit eaec2efbe4
8 changed files with 142 additions and 36 deletions

View File

@ -330,6 +330,15 @@ err_close_nsp:
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)

View File

@ -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)
{

View File

@ -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,

View File

@ -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 = {

View File

@ -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_ */

View File

@ -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,

View File

@ -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 @@ out:
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);

View File

@ -2038,12 +2038,18 @@ static noinline_for_stack int ethtool_flash_device(struct net_device *dev,
if (copy_from_user(&efl, useraddr, sizeof(efl)))
return -EFAULT;
if (!dev->ethtool_ops->flash_device)
return -EOPNOTSUPP;
efl.data[ETHTOOL_FLASH_MAX_FILENAME - 1] = 0;
if (!dev->ethtool_ops->flash_device) {
int ret;
rtnl_unlock();
ret = devlink_compat_flash_update(dev, efl.data);
rtnl_lock();
return ret;
}
return dev->ethtool_ops->flash_device(dev, &efl);
}