Skip to content

Commit

Permalink
Merge branch 'ionic-add-devlink-dev-flash-support'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Sep 18, 2020
2 parents 0e4be9e + 30b5191 commit cb589a5
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 35 deletions.
2 changes: 1 addition & 1 deletion drivers/net/ethernet/pensando/ionic/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 14 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_devlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_devlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
206 changes: 206 additions & 0 deletions drivers/net/ethernet/pensando/ionic/ionic_fw.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// 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;
}
33 changes: 25 additions & 8 deletions drivers/net/ethernet/pensando/ionic/ionic_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

/**
Expand Down Expand Up @@ -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,
};

/**
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
};

/**
Expand Down
27 changes: 19 additions & 8 deletions drivers/net/ethernet/pensando/ionic/ionic_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/netdevsim/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit cb589a5

Please sign in to comment.