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

Merge branch 'ionic-add-devlink-dev-flash-support'

Shannon Nelson says:

====================
ionic: add devlink dev flash support

Add support for using devlink's dev flash facility to update the
firmware on an ionic device, and add a new timeout parameter to the
devlink flash netlink message.

For long-running flash commands, we add a timeout element to the dev
flash notify message in order for a userland utility to display a timeout
deadline to the user.  This allows the userland utility to display a
count down to the user when a firmware update action is otherwise going
to go for ahile without any updates.  An example use is added to the
netdevsim module.

The ionic driver uses this timeout element in its new flash function.
The driver uses a simple model of pushing the firmware file to the NIC,
asking the NIC to unpack and install the file into the device, and then
selecting it for the next boot.  If any of these steps fail, the whole
transaction is failed.  A couple of the steps can take a long time,
so we use the timeout status message rather than faking it with bogus
done/total messages.

The driver doesn't currently support doing these steps individually.
In the future we want to be able to list the FW that is installed and
selectable but we don't yet have the API to fully support that.

v5: pulled the cmd field back out of the new params struct
    changed netdevsim example message to "Flash select"

v4: Added a new devlink status notify message for showing timeout
    information, and modified the ionic fw update to use it for its long
    running firmware commands.

v3: Changed long dev_cmd timeout on status check calls to a loop around
    calls with a normal timeout, which allows for more intermediate log
    messaging when in a long wait, and for letting other threads run
    dev_cmds if waiting.

v2: Changed "Activate" to "Select" in status messages.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0e4be9e5 30b5191a
......@@ -5,4 +5,4 @@ obj-$(CONFIG_IONIC) := ionic.o
ionic-y := ionic_main.o ionic_bus_pci.o ionic_devlink.o ionic_dev.o \
ionic_debugfs.o ionic_lif.o ionic_rx_filter.o ionic_ethtool.o \
ionic_txrx.o ionic_stats.o
ionic_txrx.o ionic_stats.o ionic_fw.o
......@@ -9,6 +9,19 @@
#include "ionic_lif.h"
#include "ionic_devlink.h"
static int ionic_dl_flash_update(struct devlink *dl,
const char *fwname,
const char *component,
struct netlink_ext_ack *extack)
{
struct ionic *ionic = devlink_priv(dl);
if (component)
return -EOPNOTSUPP;
return ionic_firmware_update(ionic->lif, fwname, extack);
}
static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
struct netlink_ext_ack *extack)
{
......@@ -48,6 +61,7 @@ static int ionic_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
static const struct devlink_ops ionic_dl_ops = {
.info_get = ionic_dl_info_get,
.flash_update = ionic_dl_flash_update,
};
struct ionic *ionic_devlink_alloc(struct device *dev)
......
......@@ -6,6 +6,9 @@
#include <net/devlink.h>
int ionic_firmware_update(struct ionic_lif *lif, const char *fw_name,
struct netlink_ext_ack *extack);
struct ionic *ionic_devlink_alloc(struct device *dev);
void ionic_devlink_free(struct ionic *ionic);
int ionic_devlink_register(struct ionic *ionic);
......
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2020 Pensando Systems, Inc */
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/firmware.h>
#include "ionic.h"
#include "ionic_dev.h"
#include "ionic_lif.h"
#include "ionic_devlink.h"
/* The worst case wait for the install activity is about 25 minutes when
* installing a new CPLD, which is very seldom. Normal is about 30-35
* seconds. Since the driver can't tell if a CPLD update will happen we
* set the timeout for the ugly case.
*/
#define IONIC_FW_INSTALL_TIMEOUT (25 * 60)
#define IONIC_FW_SELECT_TIMEOUT 30
/* Number of periodic log updates during fw file download */
#define IONIC_FW_INTERVAL_FRACTION 32
static void ionic_dev_cmd_firmware_download(struct ionic_dev *idev, u64 addr,
u32 offset, u32 length)
{
union ionic_dev_cmd cmd = {
.fw_download.opcode = IONIC_CMD_FW_DOWNLOAD,
.fw_download.offset = offset,
.fw_download.addr = addr,
.fw_download.length = length
};
ionic_dev_cmd_go(idev, &cmd);
}
static void ionic_dev_cmd_firmware_install(struct ionic_dev *idev)
{
union ionic_dev_cmd cmd = {
.fw_control.opcode = IONIC_CMD_FW_CONTROL,
.fw_control.oper = IONIC_FW_INSTALL_ASYNC
};
ionic_dev_cmd_go(idev, &cmd);
}
static void ionic_dev_cmd_firmware_activate(struct ionic_dev *idev, u8 slot)
{
union ionic_dev_cmd cmd = {
.fw_control.opcode = IONIC_CMD_FW_CONTROL,
.fw_control.oper = IONIC_FW_ACTIVATE_ASYNC,
.fw_control.slot = slot
};
ionic_dev_cmd_go(idev, &cmd);
}
static int ionic_fw_status_long_wait(struct ionic *ionic,
const char *label,
unsigned long timeout,
u8 fw_cmd,
struct netlink_ext_ack *extack)
{
union ionic_dev_cmd cmd = {
.fw_control.opcode = IONIC_CMD_FW_CONTROL,
.fw_control.oper = fw_cmd,
};
unsigned long start_time;
unsigned long end_time;
int err;
start_time = jiffies;
end_time = start_time + (timeout * HZ);
do {
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_go(&ionic->idev, &cmd);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
msleep(20);
} while (time_before(jiffies, end_time) && (err == -EAGAIN || err == -ETIMEDOUT));
if (err == -EAGAIN || err == -ETIMEDOUT) {
NL_SET_ERR_MSG_MOD(extack, "Firmware wait timed out");
dev_err(ionic->dev, "DEV_CMD firmware wait %s timed out\n", label);
} else if (err) {
NL_SET_ERR_MSG_MOD(extack, "Firmware wait failed");
}
return err;
}
int ionic_firmware_update(struct ionic_lif *lif, const char *fw_name,
struct netlink_ext_ack *extack)
{
struct ionic_dev *idev = &lif->ionic->idev;
struct net_device *netdev = lif->netdev;
struct ionic *ionic = lif->ionic;
union ionic_dev_cmd_comp comp;
u32 buf_sz, copy_sz, offset;
const struct firmware *fw;
struct devlink *dl;
int next_interval;
int err = 0;
u8 fw_slot;
netdev_info(netdev, "Installing firmware %s\n", fw_name);
dl = priv_to_devlink(ionic);
devlink_flash_update_begin_notify(dl);
devlink_flash_update_status_notify(dl, "Preparing to flash", NULL, 0, 0);
err = request_firmware(&fw, fw_name, ionic->dev);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Unable to find firmware file");
goto err_out;
}
buf_sz = sizeof(idev->dev_cmd_regs->data);
netdev_dbg(netdev,
"downloading firmware - size %d part_sz %d nparts %lu\n",
(int)fw->size, buf_sz, DIV_ROUND_UP(fw->size, buf_sz));
offset = 0;
next_interval = 0;
while (offset < fw->size) {
if (offset >= next_interval) {
devlink_flash_update_status_notify(dl, "Downloading", NULL,
offset, fw->size);
next_interval = offset + (fw->size / IONIC_FW_INTERVAL_FRACTION);
}
copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);
mutex_lock(&ionic->dev_cmd_lock);
memcpy_toio(&idev->dev_cmd_regs->data, fw->data + offset, copy_sz);
ionic_dev_cmd_firmware_download(idev,
offsetof(union ionic_dev_cmd_regs, data),
offset, copy_sz);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
if (err) {
netdev_err(netdev,
"download failed offset 0x%x addr 0x%lx len 0x%x\n",
offset, offsetof(union ionic_dev_cmd_regs, data),
copy_sz);
NL_SET_ERR_MSG_MOD(extack, "Segment download failed");
goto err_out;
}
offset += copy_sz;
}
devlink_flash_update_status_notify(dl, "Downloading", NULL,
fw->size, fw->size);
devlink_flash_update_timeout_notify(dl, "Installing", NULL,
IONIC_FW_INSTALL_TIMEOUT);
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_firmware_install(idev);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
ionic_dev_cmd_comp(idev, (union ionic_dev_cmd_comp *)&comp);
fw_slot = comp.fw_control.slot;
mutex_unlock(&ionic->dev_cmd_lock);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware install");
goto err_out;
}
err = ionic_fw_status_long_wait(ionic, "Installing",
IONIC_FW_INSTALL_TIMEOUT,
IONIC_FW_INSTALL_STATUS,
extack);
if (err)
goto err_out;
devlink_flash_update_timeout_notify(dl, "Selecting", NULL,
IONIC_FW_SELECT_TIMEOUT);
mutex_lock(&ionic->dev_cmd_lock);
ionic_dev_cmd_firmware_activate(idev, fw_slot);
err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
mutex_unlock(&ionic->dev_cmd_lock);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware select");
goto err_out;
}
err = ionic_fw_status_long_wait(ionic, "Selecting",
IONIC_FW_SELECT_TIMEOUT,
IONIC_FW_ACTIVATE_STATUS,
extack);
if (err)
goto err_out;
netdev_info(netdev, "Firmware update completed\n");
err_out:
if (err)
devlink_flash_update_status_notify(dl, "Flash failed", NULL, 0, 0);
else
devlink_flash_update_status_notify(dl, "Flash done", NULL, 0, 0);
release_firmware(fw);
devlink_flash_update_end_notify(dl);
return err;
}
......@@ -63,8 +63,10 @@ enum ionic_cmd_opcode {
IONIC_CMD_QOS_RESET = 245,
/* Firmware commands */
IONIC_CMD_FW_DOWNLOAD = 254,
IONIC_CMD_FW_CONTROL = 255,
IONIC_CMD_FW_DOWNLOAD = 252,
IONIC_CMD_FW_CONTROL = 253,
IONIC_CMD_FW_DOWNLOAD_V1 = 254,
IONIC_CMD_FW_CONTROL_V1 = 255,
};
/**
......@@ -2069,14 +2071,23 @@ typedef struct ionic_admin_comp ionic_fw_download_comp;
/**
* enum ionic_fw_control_oper - FW control operations
* @IONIC_FW_RESET: Reset firmware
* @IONIC_FW_INSTALL: Install firmware
* @IONIC_FW_ACTIVATE: Activate firmware
* @IONIC_FW_RESET: Reset firmware
* @IONIC_FW_INSTALL: Install firmware
* @IONIC_FW_ACTIVATE: Activate firmware
* @IONIC_FW_INSTALL_ASYNC: Install firmware asynchronously
* @IONIC_FW_INSTALL_STATUS: Firmware installation status
* @IONIC_FW_ACTIVATE_ASYNC: Activate firmware asynchronously
* @IONIC_FW_ACTIVATE_STATUS: Firmware activate status
*/
enum ionic_fw_control_oper {
IONIC_FW_RESET = 0,
IONIC_FW_INSTALL = 1,
IONIC_FW_ACTIVATE = 2,
IONIC_FW_RESET = 0,
IONIC_FW_INSTALL = 1,
IONIC_FW_ACTIVATE = 2,
IONIC_FW_INSTALL_ASYNC = 3,
IONIC_FW_INSTALL_STATUS = 4,
IONIC_FW_ACTIVATE_ASYNC = 5,
IONIC_FW_ACTIVATE_STATUS = 6,
IONIC_FW_UPDATE_CLEANUP = 7,
};
/**
......@@ -2689,6 +2700,9 @@ union ionic_dev_cmd {
struct ionic_q_identify_cmd q_identify;
struct ionic_q_init_cmd q_init;
struct ionic_q_control_cmd q_control;
struct ionic_fw_download_cmd fw_download;
struct ionic_fw_control_cmd fw_control;
};
union ionic_dev_cmd_comp {
......@@ -2722,6 +2736,9 @@ union ionic_dev_cmd_comp {
struct ionic_q_identify_comp q_identify;
struct ionic_q_init_comp q_init;
ionic_fw_download_comp fw_download;
struct ionic_fw_control_comp fw_control;
};
/**
......
......@@ -170,6 +170,10 @@ static const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode)
return "IONIC_CMD_FW_DOWNLOAD";
case IONIC_CMD_FW_CONTROL:
return "IONIC_CMD_FW_CONTROL";
case IONIC_CMD_FW_DOWNLOAD_V1:
return "IONIC_CMD_FW_DOWNLOAD_V1";
case IONIC_CMD_FW_CONTROL_V1:
return "IONIC_CMD_FW_CONTROL_V1";
case IONIC_CMD_VF_GETATTR:
return "IONIC_CMD_VF_GETATTR";
case IONIC_CMD_VF_SETATTR:
......@@ -331,17 +335,22 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
*/
max_wait = jiffies + (max_seconds * HZ);
try_again:
opcode = idev->dev_cmd_regs->cmd.cmd.opcode;
start_time = jiffies;
do {
done = ionic_dev_cmd_done(idev);
if (done)
break;
msleep(5);
hb = ionic_heartbeat_check(ionic);
usleep_range(100, 200);
/* Don't check the heartbeat on FW_CONTROL commands as they are
* notorious for interrupting the firmware's heartbeat update.
*/
if (opcode != IONIC_CMD_FW_CONTROL)
hb = ionic_heartbeat_check(ionic);
} while (!done && !hb && time_before(jiffies, max_wait));
duration = jiffies - start_time;
opcode = idev->dev_cmd_regs->cmd.cmd.opcode;
dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n",
ionic_opcode_to_str(opcode), opcode,
done, duration / HZ, duration);
......@@ -365,8 +374,9 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
err = ionic_dev_cmd_status(&ionic->idev);
if (err) {
if (err == IONIC_RC_EAGAIN && !time_after(jiffies, max_wait)) {
dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) retrying...\n",
if (err == IONIC_RC_EAGAIN &&
time_before(jiffies, (max_wait - HZ))) {
dev_dbg(ionic->dev, "DEV_CMD %s (%d), %s (%d) retrying...\n",
ionic_opcode_to_str(opcode), opcode,
ionic_error_to_str(err), err);
......@@ -376,9 +386,10 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds)
goto try_again;
}
dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n",
ionic_opcode_to_str(opcode), opcode,
ionic_error_to_str(err), err);
if (!(opcode == IONIC_CMD_FW_CONTROL && err == IONIC_RC_EAGAIN))
dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n",
ionic_opcode_to_str(opcode), opcode,
ionic_error_to_str(err), err);
return ionic_error_to_errno(err);
}
......
......@@ -768,6 +768,8 @@ static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name,
component,
NSIM_DEV_FLASH_SIZE,
NSIM_DEV_FLASH_SIZE);
devlink_flash_update_timeout_notify(devlink, "Flash select",
component, 81);
devlink_flash_update_status_notify(devlink, "Flashing done",
component, 0, 0);
devlink_flash_update_end_notify(devlink);
......
......@@ -391,6 +391,25 @@ struct devlink_param_gset_ctx {
enum devlink_param_cmode cmode;
};
/**
* struct devlink_flash_notify - devlink dev flash notify data
* @status_msg: current status string
* @component: firmware component being updated
* @done: amount of work completed of total amount
* @total: amount of work expected to be done
* @timeout: expected max timeout in seconds
*
* These are values to be given to userland to be displayed in order
* to show current activity in a firmware update process.
*/
struct devlink_flash_notify {
const char *status_msg;
const char *component;
unsigned long done;
unsigned long total;
unsigned long timeout;
};
/**
* struct devlink_param - devlink configuration parameter data
* @name: name of the parameter
......@@ -1403,6 +1422,10 @@ void devlink_flash_update_status_notify(struct devlink *devlink,
const char *component,
unsigned long done,
unsigned long total);
void devlink_flash_update_timeout_notify(struct devlink *devlink,
const char *status_msg,
const char *component,
unsigned long timeout);
int devlink_traps_register(struct devlink *devlink,
const struct devlink_trap *traps,
......
......@@ -462,6 +462,9 @@ enum devlink_attr {
DEVLINK_ATTR_PORT_EXTERNAL, /* u8 */
DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, /* u32 */
DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT, /* u64 */
/* add new attributes above here, update the policy in devlink.c */
__DEVLINK_ATTR_MAX,
......
......@@ -3022,9 +3022,7 @@ static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
static int devlink_nl_flash_update_fill(struct sk_buff *msg,
struct devlink *devlink,
enum devlink_command cmd,
const char *status_msg,
const char *component,
unsigned long done, unsigned long total)
struct devlink_flash_notify *params)
{
void *hdr;
......@@ -3038,19 +3036,22 @@ static int devlink_nl_flash_update_fill(struct sk_buff *msg,
if (cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS)
goto out;
if (status_msg &&
if (params->status_msg &&
nla_put_string(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG,
status_msg))
params->status_msg))
goto nla_put_failure;
if (component &&
if (params->component &&
nla_put_string(msg, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT,
component))
params->component))
goto nla_put_failure;
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE,
done, DEVLINK_ATTR_PAD))
params->done, DEVLINK_ATTR_PAD))
goto nla_put_failure;
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL,
total, DEVLINK_ATTR_PAD))
params->total, DEVLINK_ATTR_PAD))
goto nla_put_failure;
if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT,
params->timeout, DEVLINK_ATTR_PAD))
goto nla_put_failure;
out:
......@@ -3064,10 +3065,7 @@ static int devlink_nl_flash_update_fill(struct sk_buff *msg,
static void __devlink_flash_update_notify(struct devlink *devlink,
enum devlink_command cmd,
const char *status_msg,
const char *component,
unsigned long done,
unsigned long total)
struct devlink_flash_notify *params)
{
struct sk_buff *msg;
int err;
......@@ -3080,8 +3078,7 @@ static void __devlink_flash_update_notify(struct devlink *devlink,
if (!msg)
return;
err = devlink_nl_flash_update_fill(msg, devlink, cmd, status_msg,
component, done, total);
err = devlink_nl_flash_update_fill(msg, devlink, cmd, params);
if (err)
goto out_free_msg;
......@@ -3095,17 +3092,21 @@ static void __devlink_flash_update_notify(struct devlink *devlink,
void devlink_flash_update_begin_notify(struct devlink *devlink)
{
struct devlink_flash_notify params = { 0 };
__devlink_flash_update_notify(devlink,
DEVLINK_CMD_FLASH_UPDATE,
NULL, NULL, 0, 0);
&params);
}
EXPORT_SYMBOL_GPL(devlink_flash_update_begin_notify);
void devlink_flash_update_end_notify(struct devlink *devlink)
{
struct devlink_flash_notify params = { 0 };
__devlink_flash_update_notify(devlink,
DEVLINK_CMD_FLASH_UPDATE_END,
NULL, NULL, 0, 0);
&params);
}
EXPORT_SYMBOL_GPL(devlink_flash_update_end_notify);
......@@ -3115,12 +3116,36 @@ void devlink_flash_update_status_notify(struct devlink *devlink,
unsigned long done,
unsigned long total)
{
struct devlink_flash_notify params = {
.status_msg = status_msg,
.component = component,
.done = done,
.total = total,
};
__devlink_flash_update_notify(devlink,
DEVLINK_CMD_FLASH_UPDATE_STATUS,
status_msg, component, done, total);
&params);
}
EXPORT_SYMBOL_GPL(devlink_flash_update_status_notify);
void devlink_flash_update_timeout_notify(struct devlink *devlink,
const char *status_msg,
const char *component,
unsigned long timeout)
{
struct devlink_flash_notify params = {
.status_msg = status_msg,
.component = component,
.timeout = timeout,
};
__devlink_flash_update_notify(devlink,
DEVLINK_CMD_FLASH_UPDATE_STATUS,
&params);
}
EXPORT_SYMBOL_GPL(devlink_flash_update_timeout_notify);
static int devlink_nl_cmd_flash_update(struct sk_buff *skb,
struct genl_info *info)
{
......
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