-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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: David S. Miller <davem@davemloft.net>
- Loading branch information
Showing
10 changed files
with
339 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.