* [Intel-wired-lan] [net-next PATCH v3 1/2] ice: move ice_devlink_flash_update and merge with ice_flash_pldm_image
@ 2021-10-15 23:13 Jacob Keller
2021-10-15 23:13 ` [Intel-wired-lan] [net-next PATCH v3 2/2] ice: support immediate firmware activation via devlink reload Jacob Keller
0 siblings, 1 reply; 3+ messages in thread
From: Jacob Keller @ 2021-10-15 23:13 UTC (permalink / raw)
To: intel-wired-lan
The ice_devlink_flash_update function performs a few upfront checks and
then calls ice_flash_pldm_image.
Most if these checks make more sense in the context of code within
ice_flash_pldm_image. Merge ice_devlink_flash_update and
ice_flash_pldm_image into one function, placing it in ice_fw_update.c
Since this is still the entry point for devlink, call the function
ice_devlink_flash_update instead of ice_flash_pldm_image. This leaves a
single function which handles the devlink parameters and then initiates
a PLDM update.
With this change, the ice_devlink_flash_update function in
ice_fw_update.c becomes the main entry point for flash update. It
elimintes some unnecessary boiler plate code between the two previous
functions. The ultimate motivation for this is that it eases supporting
a dry run with the PLDM library in a future change.
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
Changes since v2
* don't hold NVM resource while issuing the discover device capabilities
firmware command
drivers/net/ethernet/intel/ice/ice_devlink.c | 50 -----------------
.../net/ethernet/intel/ice/ice_fw_update.c | 54 ++++++++++++-------
.../net/ethernet/intel/ice/ice_fw_update.h | 7 ++-
3 files changed, 39 insertions(+), 72 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c
index 34ceb8e964fe..c64286aad7be 100644
--- a/drivers/net/ethernet/intel/ice/ice_devlink.c
+++ b/drivers/net/ethernet/intel/ice/ice_devlink.c
@@ -370,56 +370,6 @@ static int ice_devlink_info_get(struct devlink *devlink,
return err;
}
-/**
- * ice_devlink_flash_update - Update firmware stored in flash on the device
- * @devlink: pointer to devlink associated with device to update
- * @params: flash update parameters
- * @extack: netlink extended ACK structure
- *
- * Perform a device flash update. The bulk of the update logic is contained
- * within the ice_flash_pldm_image function.
- *
- * Returns: zero on success, or an error code on failure.
- */
-static int
-ice_devlink_flash_update(struct devlink *devlink,
- struct devlink_flash_update_params *params,
- struct netlink_ext_ack *extack)
-{
- struct ice_pf *pf = devlink_priv(devlink);
- struct ice_hw *hw = &pf->hw;
- u8 preservation;
- int err;
-
- if (!params->overwrite_mask) {
- /* preserve all settings and identifiers */
- preservation = ICE_AQC_NVM_PRESERVE_ALL;
- } else if (params->overwrite_mask == DEVLINK_FLASH_OVERWRITE_SETTINGS) {
- /* overwrite settings, but preserve the vital device identifiers */
- preservation = ICE_AQC_NVM_PRESERVE_SELECTED;
- } else if (params->overwrite_mask == (DEVLINK_FLASH_OVERWRITE_SETTINGS |
- DEVLINK_FLASH_OVERWRITE_IDENTIFIERS)) {
- /* overwrite both settings and identifiers, preserve nothing */
- preservation = ICE_AQC_NVM_NO_PRESERVATION;
- } else {
- NL_SET_ERR_MSG_MOD(extack, "Requested overwrite mask is not supported");
- return -EOPNOTSUPP;
- }
-
- if (!hw->dev_caps.common_cap.nvm_unified_update) {
- NL_SET_ERR_MSG_MOD(extack, "Current firmware does not support unified update");
- return -EOPNOTSUPP;
- }
-
- err = ice_cancel_pending_update(pf, NULL, extack);
- if (err)
- return err;
-
- devlink_flash_update_status_notify(devlink, "Preparing to flash", NULL, 0, 0);
-
- return ice_flash_pldm_image(pf, params->fw, preservation, extack);
-}
-
static const struct devlink_ops ice_devlink_ops = {
.supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
.eswitch_mode_get = ice_eswitch_mode_get,
diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.c b/drivers/net/ethernet/intel/ice/ice_fw_update.c
index 489766e6ca6b..1f8e0e5d5660 100644
--- a/drivers/net/ethernet/intel/ice/ice_fw_update.c
+++ b/drivers/net/ethernet/intel/ice/ice_fw_update.c
@@ -652,8 +652,9 @@ static const struct pldmfw_ops ice_fwu_ops = {
*
* Returns: zero on success, or a negative error code on failure.
*/
-int ice_cancel_pending_update(struct ice_pf *pf, const char *component,
- struct netlink_ext_ack *extack)
+static int
+ice_cancel_pending_update(struct ice_pf *pf, const char *component,
+ struct netlink_ext_ack *extack)
{
struct devlink *devlink = priv_to_devlink(pf);
struct device *dev = ice_pf_to_dev(pf);
@@ -737,10 +738,9 @@ int ice_cancel_pending_update(struct ice_pf *pf, const char *component,
}
/**
- * ice_flash_pldm_image - Write a PLDM-formatted firmware image to the device
- * @pf: private device driver structure
- * @fw: firmware object pointing to the relevant firmware file
- * @preservation: preservation level to request from firmware
+ * ice_devlink_flash_update - Write a firmware image to the device
+ * @devlink: pointer to devlink associated with the device to update
+ * @params: devlink flash update parameters
* @extack: netlink extended ACK structure
*
* Parse the data for a given firmware file, verifying that it is a valid PLDM
@@ -753,23 +753,35 @@ int ice_cancel_pending_update(struct ice_pf *pf, const char *component,
*
* Returns: zero on success or a negative error code on failure.
*/
-int ice_flash_pldm_image(struct ice_pf *pf, const struct firmware *fw,
- u8 preservation, struct netlink_ext_ack *extack)
+int ice_devlink_flash_update(struct devlink *devlink,
+ struct devlink_flash_update_params *params,
+ struct netlink_ext_ack *extack)
{
+ struct ice_pf *pf = devlink_priv(devlink);
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
struct ice_fwu_priv priv;
+ u8 preservation;
int err;
- switch (preservation) {
- case ICE_AQC_NVM_PRESERVE_ALL:
- case ICE_AQC_NVM_PRESERVE_SELECTED:
- case ICE_AQC_NVM_NO_PRESERVATION:
- case ICE_AQC_NVM_FACTORY_DEFAULT:
- break;
- default:
- WARN(1, "Unexpected preservation level request %u", preservation);
- return -EINVAL;
+ if (!params->overwrite_mask) {
+ /* preserve all settings and identifiers */
+ preservation = ICE_AQC_NVM_PRESERVE_ALL;
+ } else if (params->overwrite_mask == DEVLINK_FLASH_OVERWRITE_SETTINGS) {
+ /* overwrite settings, but preserve the vital device identifiers */
+ preservation = ICE_AQC_NVM_PRESERVE_SELECTED;
+ } else if (params->overwrite_mask == (DEVLINK_FLASH_OVERWRITE_SETTINGS |
+ DEVLINK_FLASH_OVERWRITE_IDENTIFIERS)) {
+ /* overwrite both settings and identifiers, preserve nothing */
+ preservation = ICE_AQC_NVM_NO_PRESERVATION;
+ } else {
+ NL_SET_ERR_MSG_MOD(extack, "Requested overwrite mask is not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (!hw->dev_caps.common_cap.nvm_unified_update) {
+ NL_SET_ERR_MSG_MOD(extack, "Current firmware does not support unified update");
+ return -EOPNOTSUPP;
}
memset(&priv, 0, sizeof(priv));
@@ -780,6 +792,12 @@ int ice_flash_pldm_image(struct ice_pf *pf, const struct firmware *fw,
priv.pf = pf;
priv.activate_flags = preservation;
+ devlink_flash_update_status_notify(devlink, "Preparing to flash", NULL, 0, 0);
+
+ err = ice_cancel_pending_update(pf, NULL, extack);
+ if (err)
+ return err;
+
err = ice_acquire_nvm(hw, ICE_RES_WRITE);
if (err) {
dev_err(dev, "Failed to acquire device flash lock, err %d aq_err %s\n",
@@ -788,7 +806,7 @@ int ice_flash_pldm_image(struct ice_pf *pf, const struct firmware *fw,
return err;
}
- err = pldmfw_flash_image(&priv.context, fw);
+ err = pldmfw_flash_image(&priv.context, params->fw);
if (err == -ENOENT) {
dev_err(dev, "Firmware image has no record matching this device\n");
NL_SET_ERR_MSG_MOD(extack, "Firmware image has no record matching this device");
diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.h b/drivers/net/ethernet/intel/ice/ice_fw_update.h
index 1f84ef18bfd1..be6d222124f2 100644
--- a/drivers/net/ethernet/intel/ice/ice_fw_update.h
+++ b/drivers/net/ethernet/intel/ice/ice_fw_update.h
@@ -4,9 +4,8 @@
#ifndef _ICE_FW_UPDATE_H_
#define _ICE_FW_UPDATE_H_
-int ice_flash_pldm_image(struct ice_pf *pf, const struct firmware *fw,
- u8 preservation, struct netlink_ext_ack *extack);
-int ice_cancel_pending_update(struct ice_pf *pf, const char *component,
- struct netlink_ext_ack *extack);
+int ice_devlink_flash_update(struct devlink *devlink,
+ struct devlink_flash_update_params *params,
+ struct netlink_ext_ack *extack);
#endif
--
2.31.1.331.gb0c09ab8796f
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [Intel-wired-lan] [net-next PATCH v3 2/2] ice: support immediate firmware activation via devlink reload
2021-10-15 23:13 [Intel-wired-lan] [net-next PATCH v3 1/2] ice: move ice_devlink_flash_update and merge with ice_flash_pldm_image Jacob Keller
@ 2021-10-15 23:13 ` Jacob Keller
2021-10-19 19:37 ` Keller, Jacob E
0 siblings, 1 reply; 3+ messages in thread
From: Jacob Keller @ 2021-10-15 23:13 UTC (permalink / raw)
To: intel-wired-lan
In order to complete a device flash update for the ice device, the
system administrator currently must perform a system reboot. This is not
ideal, as rebooting the entire system can cause unwanted downtime.
In many cases, it is possible to update the device firmware immediately
using what is known as an EMP reset. This can be used to complete
a device firmware update without the undesired effects caused by a full
platform reboot.
We can implement support for this in userspace using the devlink reload
command. Implement this command for the ice driver so that a devlink
reload with the FW_ACTIVATE reload action. This enables the
administrator to request a firmware activation after performing a flash
update.
Unfortunately, it is not always possible to complete an update with
a simple EMP reset. In some cases, the update cannot be completed
without performing either a full power on or a cold PCIe reset. In these
cases a system reboot is the only known way to complete the update.
When actually writing the flash device, newer versions of firmware can
inform the driver about whether an EMP reset is sufficient. It does this
using two mechanisms, which are detected via firmware capability bits.
The first is known as PCIe reset avoidance, and the second is known as
indication of reset restriction.
If the firmware supports these capabilities, it will be able to indicate
to the driver when an EMP reset is sufficient to complete an update.
The minimum required reset level is provided as part of the response to
the final NVM module block write. This reset level indicates what reset
is required in order to properly initialize the firmware given the
contents that were updated in the NVM.
The availability of EMP reset is indicated as part of the response to
asking firmware to switch flash backs for the next load.
Track these responses in the private structure when performing a flash
update. After finalizing the update, add a status notification to inform
the user of the required reset needed to switch the running firmware.
Also track whether or not the EMP reset is available for use during
reload. If firmware reported that it is available, an EMP reset can be
requested using the appropriate firmware command.
Add support for devlink reload, and allow the user to request a reload
to the new firmware using the FW_ACTIVIATE flag. If an EMP reset is
available from firmware, we will attempt to load the newly activated
firmware.
In some cases, such as older firmware versions which do not check or
report whether EMP reset is suitable, the driver may not be able to
determine when an EMP reset is available. In this case, whether an EMP
reset is sufficient or not. In this case, we will always attempt the EMP
reset.
Finally, this change does *not* implement basic driver-only reload
support. I did look into trying to do this. However, it requires
significant refactor of how the ice driver probes and loads everything.
The ice driver probe and allocation flows were not designed with such
a reload in mind.
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
---
Changes since v2
* ensure DEVLINK_F_RELOAD gets set
* rebase to avoid conflicts
drivers/net/ethernet/intel/ice/ice.h | 1 +
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 7 +
drivers/net/ethernet/intel/ice/ice_common.c | 12 ++
drivers/net/ethernet/intel/ice/ice_devlink.c | 91 ++++++++++
.../net/ethernet/intel/ice/ice_fw_update.c | 166 +++++++++++++++---
.../net/ethernet/intel/ice/ice_fw_update.h | 2 +
drivers/net/ethernet/intel/ice/ice_type.h | 4 +
7 files changed, 261 insertions(+), 22 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 2fc2208be9de..6c0bf0f41a4c 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -557,6 +557,7 @@ struct ice_pf {
spinlock_t aq_wait_lock;
struct hlist_head aq_wait_list;
wait_queue_head_t aq_wait_queue;
+ bool fw_empr_disabled;
wait_queue_head_t reset_wait_queue;
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index a8909c01bd59..d79578f01d18 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -117,6 +117,8 @@ struct ice_aqc_list_caps_elem {
#define ICE_AQC_CAPS_NET_VER 0x004C
#define ICE_AQC_CAPS_PENDING_NET_VER 0x004D
#define ICE_AQC_CAPS_RDMA 0x0051
+#define ICE_AQC_CAPS_PCIE_RESET_AVOIDANCE 0x0076
+#define ICE_AQC_CAPS_POST_UPDATE_RESET_RESTRICT 0x0077
#define ICE_AQC_CAPS_NVM_MGMT 0x0080
u8 major_ver;
@@ -1424,6 +1426,11 @@ struct ice_aqc_nvm {
#define ICE_AQC_NVM_REVERT_LAST_ACTIV BIT(6) /* Write Activate only */
#define ICE_AQC_NVM_ACTIV_SEL_MASK ICE_M(0x7, 3)
#define ICE_AQC_NVM_FLASH_ONLY BIT(7)
+#define ICE_AQC_NVM_RESET_LVL_M ICE_M(0x3, 0) /* Write reply only */
+#define ICE_AQC_NVM_POR_FLAG 0
+#define ICE_AQC_NVM_PERST_FLAG 1
+#define ICE_AQC_NVM_EMPR_FLAG 2
+#define ICE_AQC_NVM_EMPR_ENA BIT(0) /* Write Activate reply only */
__le16 module_typeid;
__le16 length;
#define ICE_AQC_NVM_ERASE_LEN 0xFFFF
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index 3ab115c3bdf9..53c2fdc909ce 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -2067,6 +2067,18 @@ ice_parse_common_caps(struct ice_hw *hw, struct ice_hw_common_caps *caps,
ice_debug(hw, ICE_DBG_INIT, "%s: max_mtu = %d\n",
prefix, caps->max_mtu);
break;
+ case ICE_AQC_CAPS_PCIE_RESET_AVOIDANCE:
+ caps->pcie_reset_avoidance = (number > 0);
+ ice_debug(hw, ICE_DBG_INIT,
+ "%s: pcie_reset_avoidance = %d\n", prefix,
+ caps->pcie_reset_avoidance);
+ break;
+ case ICE_AQC_CAPS_POST_UPDATE_RESET_RESTRICT:
+ caps->reset_restrict_support = (number == 1);
+ ice_debug(hw, ICE_DBG_INIT,
+ "%s: reset_restrict_support = %d\n", prefix,
+ caps->reset_restrict_support);
+ break;
default:
/* Not one of the recognized common capabilities */
found = false;
diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c b/drivers/net/ethernet/intel/ice/ice_devlink.c
index c64286aad7be..80e4a747b220 100644
--- a/drivers/net/ethernet/intel/ice/ice_devlink.c
+++ b/drivers/net/ethernet/intel/ice/ice_devlink.c
@@ -370,8 +370,98 @@ static int ice_devlink_info_get(struct devlink *devlink,
return err;
}
+/**
+ * ice_devlink_reload_down - Start reload
+ * @devlink: pointer to the devlink instance to reload
+ * @netns_change: if true, the network namespace is changing
+ * @action: the action to perform. Must be DEVLINK_RELOAD_ACTION_FW_ACTIVATE
+ * @limit: limits on what reload should do, such as not resetting
+ * @extack: netlink extended ACK structure
+ *
+ * Command issued by devlink core to perform a reload. This driver only
+ * supports firmware activation.
+ */
+static int
+ice_devlink_reload_down(struct devlink *devlink, bool netns_change,
+ enum devlink_reload_action action,
+ enum devlink_reload_limit limit,
+ struct netlink_ext_ack *extack)
+{
+ struct ice_pf *pf = devlink_priv(devlink);
+ struct device *dev = ice_pf_to_dev(pf);
+ struct ice_hw *hw = &pf->hw;
+ u8 pending;
+ int err;
+
+ err = ice_get_pending_updates(pf, &pending, extack);
+ if (err)
+ return err;
+
+ if (!pending) {
+ NL_SET_ERR_MSG_MOD(extack, "No pending firmware update");
+ return -ECANCELED;
+ }
+
+ if (pf->fw_empr_disabled) {
+ NL_SET_ERR_MSG_MOD(extack, "EMP reset is not available. To activate firmware, a reboot or power cycle is needed\n");
+ return -ECANCELED;
+ }
+
+ dev_dbg(dev, "Issuing device EMP reset to activate firmware\n");
+
+ err = ice_aq_nvm_update_empr(hw);
+ if (err) {
+ dev_err(dev, "Failed to trigger EMP device reset to reload firmware, err %d aq_err %s\n",
+ err, ice_aq_str(hw->adminq.sq_last_status));
+ NL_SET_ERR_MSG_MOD(extack, "Failed to trigger EMP device reset to reload firmware");
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_devlink_reload_up - Finish reload
+ * @devlink: pointer to the devlink instance reloading
+ * @action: the action requested
+ * @limit: limits imposed by userspace, such as not resetting
+ * @actions_performed: on return, indicate what actions actually performed
+ * @extack: netlink extended ACK structure
+ *
+ * Complete a reload, such as waiting for the driver to come back up. The ice
+ * driver only supports firmware activation, which requires a device reset.
+ */
+static int
+ice_devlink_reload_up(struct devlink *devlink,
+ enum devlink_reload_action action,
+ enum devlink_reload_limit limit,
+ u32 *actions_performed,
+ struct netlink_ext_ack *extack)
+{
+ struct ice_pf *pf = devlink_priv(devlink);
+ int err;
+
+ *actions_performed = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE);
+
+ err = ice_wait_for_reset(pf, 20 * HZ);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack, "Device still resetting");
+ return err;
+ }
+
+ /* After a device reset is complete, we no longer know whether the EMP
+ * reset is restricted.
+ */
+ pf->fw_empr_disabled = false;
+
+ return 0;
+}
+
static const struct devlink_ops ice_devlink_ops = {
.supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
+ .reload_actions = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
+ .reload_down = ice_devlink_reload_down,
+ .reload_up = ice_devlink_reload_up,
.eswitch_mode_get = ice_eswitch_mode_get,
.eswitch_mode_set = ice_eswitch_mode_set,
.info_get = ice_devlink_info_get,
@@ -516,6 +606,7 @@ void ice_devlink_register(struct ice_pf *pf)
struct device *dev = ice_pf_to_dev(pf);
int err;
+ devlink_set_features(devlink, DEVLINK_F_RELOAD);
devlink_register(devlink);
err = devlink_params_register(devlink, ice_devlink_params,
diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.c b/drivers/net/ethernet/intel/ice/ice_fw_update.c
index 1f8e0e5d5660..e947fd6d8daa 100644
--- a/drivers/net/ethernet/intel/ice/ice_fw_update.c
+++ b/drivers/net/ethernet/intel/ice/ice_fw_update.c
@@ -16,6 +16,12 @@ struct ice_fwu_priv {
/* Track which NVM banks to activate at the end of the update */
u8 activate_flags;
+
+ /* Track the firmware response of reset level required */
+ u8 reset_level;
+
+ /* Track if EMP reset is available */
+ u8 empr_available;
};
/**
@@ -257,6 +263,7 @@ ice_send_component_table(struct pldmfw *context, struct pldmfw_component *compon
* @block_size: size of the block to write, up to 4k
* @block: pointer to block of data to write
* @last_cmd: whether this is the last command
+ * @reset_level: storage for reset level required
* @extack: netlink extended ACK structure
*
* Write a block of data to a flash module, and await for the completion
@@ -269,7 +276,7 @@ ice_send_component_table(struct pldmfw *context, struct pldmfw_component *compon
static int
ice_write_one_nvm_block(struct ice_pf *pf, u16 module, u32 offset,
u16 block_size, u8 *block, bool last_cmd,
- struct netlink_ext_ack *extack)
+ u8 *reset_level, struct netlink_ext_ack *extack)
{
u16 completion_module, completion_retval;
struct device *dev = ice_pf_to_dev(pf);
@@ -335,6 +342,24 @@ ice_write_one_nvm_block(struct ice_pf *pf, u16 module, u32 offset,
return -EIO;
}
+ /* For the last command to write the NVM bank, newer versions of
+ * firmware indicate the required level of reset to complete
+ * activation of firmware. If the firmware supports this, cache the
+ * response for indicating to the user later. Otherwise, assume that
+ * a full power cycle is required.
+ */
+ if (reset_level && last_cmd && module == ICE_SR_1ST_NVM_BANK_PTR) {
+ if (hw->dev_caps.common_cap.pcie_reset_avoidance) {
+ *reset_level = (event.desc.params.nvm.cmd_flags &
+ ICE_AQC_NVM_RESET_LVL_M);
+ dev_dbg(dev, "Firmware reported required reset level as %u\n",
+ *reset_level);
+ } else {
+ *reset_level = ICE_AQC_NVM_POR_FLAG;
+ dev_dbg(dev, "Firmware doesn't support indicating required reset level. Assuming a power cycle is required\n");
+ }
+ }
+
return 0;
}
@@ -345,6 +370,7 @@ ice_write_one_nvm_block(struct ice_pf *pf, u16 module, u32 offset,
* @component: the name of the component being updated
* @image: buffer of image data to write to the NVM
* @length: length of the buffer
+ * @reset_level: storage for reset level required
* @extack: netlink extended ACK structure
*
* Loop over the data for a given NVM module and program it in 4 Kb
@@ -357,7 +383,7 @@ ice_write_one_nvm_block(struct ice_pf *pf, u16 module, u32 offset,
*/
static int
ice_write_nvm_module(struct ice_pf *pf, u16 module, const char *component,
- const u8 *image, u32 length,
+ const u8 *image, u32 length, u8 *reset_level,
struct netlink_ext_ack *extack)
{
struct device *dev = ice_pf_to_dev(pf);
@@ -391,7 +417,8 @@ ice_write_nvm_module(struct ice_pf *pf, u16 module, const char *component,
memcpy(block, image + offset, block_size);
err = ice_write_one_nvm_block(pf, module, offset, block_size,
- block, last_cmd, extack);
+ block, last_cmd, reset_level,
+ extack);
if (err)
break;
@@ -507,6 +534,7 @@ ice_erase_nvm_module(struct ice_pf *pf, u16 module, const char *component,
* ice_switch_flash_banks - Tell firmware to switch NVM banks
* @pf: Pointer to the PF data structure
* @activate_flags: flags used for the activation command
+ * @empr_available: on return, indicates of EMP reset is available
* @extack: netlink extended ACK structure
*
* Notify firmware to activate the newly written flash banks, and wait for the
@@ -514,8 +542,9 @@ ice_erase_nvm_module(struct ice_pf *pf, u16 module, const char *component,
*
* Returns: zero on success or an error code on failure.
*/
-static int ice_switch_flash_banks(struct ice_pf *pf, u8 activate_flags,
- struct netlink_ext_ack *extack)
+static int
+ice_switch_flash_banks(struct ice_pf *pf, u8 activate_flags,
+ u8 *empr_available, struct netlink_ext_ack *extack)
{
struct device *dev = ice_pf_to_dev(pf);
struct ice_rq_event_info event;
@@ -550,6 +579,23 @@ static int ice_switch_flash_banks(struct ice_pf *pf, u8 activate_flags,
return -EIO;
}
+ /* Newer versions of firmware have support to indicate whether an EMP
+ * reset to reload firmware is available. For older firmware, EMP
+ * reset is always available.
+ */
+ if (empr_available) {
+ if (hw->dev_caps.common_cap.reset_restrict_support) {
+ *empr_available = (event.desc.params.nvm.cmd_flags &
+ ICE_AQC_NVM_EMPR_ENA);
+ dev_dbg(dev, "Firmware indicated that EMP reset is %s\n",
+ *empr_available ?
+ "available" : "not available");
+ } else {
+ *empr_available = true;
+ dev_dbg(dev, "Firmware does not support restricting EMP reset availability\n");
+ }
+ }
+
return 0;
}
@@ -573,6 +619,7 @@ ice_flash_component(struct pldmfw *context, struct pldmfw_component *component)
struct netlink_ext_ack *extack = priv->extack;
struct ice_pf *pf = priv->pf;
const char *name;
+ u8 *reset_level;
u16 module;
u8 flag;
int err;
@@ -581,16 +628,19 @@ ice_flash_component(struct pldmfw *context, struct pldmfw_component *component)
case NVM_COMP_ID_OROM:
module = ICE_SR_1ST_OROM_BANK_PTR;
flag = ICE_AQC_NVM_ACTIV_SEL_OROM;
+ reset_level = NULL;
name = "fw.undi";
break;
case NVM_COMP_ID_NVM:
module = ICE_SR_1ST_NVM_BANK_PTR;
flag = ICE_AQC_NVM_ACTIV_SEL_NVM;
+ reset_level = &priv->reset_level;
name = "fw.mgmt";
break;
case NVM_COMP_ID_NETLIST:
module = ICE_SR_NETLIST_BANK_PTR;
flag = ICE_AQC_NVM_ACTIV_SEL_NETLIST;
+ reset_level = NULL;
name = "fw.netlist";
break;
default:
@@ -610,7 +660,8 @@ ice_flash_component(struct pldmfw *context, struct pldmfw_component *component)
return err;
return ice_write_nvm_module(pf, module, name, component->component_data,
- component->component_size, extack);
+ component->component_size, reset_level,
+ extack);
}
/**
@@ -628,9 +679,48 @@ static int ice_finalize_update(struct pldmfw *context)
struct ice_fwu_priv *priv = container_of(context, struct ice_fwu_priv, context);
struct netlink_ext_ack *extack = priv->extack;
struct ice_pf *pf = priv->pf;
+ struct devlink *devlink;
+ int err;
/* Finally, notify firmware to activate the written NVM banks */
- return ice_switch_flash_banks(pf, priv->activate_flags, extack);
+ err = ice_switch_flash_banks(pf, priv->activate_flags,
+ &priv->empr_available, extack);
+ if (err)
+ return err;
+
+ devlink = priv_to_devlink(pf);
+
+ /* If the required reset is EMPR, but EMPR is disabled, report that
+ * a reboot is required instead.
+ */
+ if (priv->reset_level == ICE_AQC_NVM_EMPR_FLAG &&
+ !priv->empr_available) {
+ dev_dbg(ice_pf_to_dev(pf), "Firmware indicated EMP reset as sufficient, but EMP reset is disabled\n");
+ priv->reset_level = ICE_AQC_NVM_PERST_FLAG;
+ }
+
+ switch (priv->reset_level) {
+ case ICE_AQC_NVM_EMPR_FLAG:
+ devlink_flash_update_status_notify(devlink,
+ "Activate new firmware by devlink reload",
+ NULL, 0, 0);
+ break;
+ case ICE_AQC_NVM_PERST_FLAG:
+ devlink_flash_update_status_notify(devlink,
+ "Activate new firmware by rebooting the system",
+ NULL, 0, 0);
+ break;
+ case ICE_AQC_NVM_POR_FLAG:
+ default:
+ devlink_flash_update_status_notify(devlink,
+ "Activate new firmware by power cycling the system",
+ NULL, 0, 0);
+ break;
+ }
+
+ pf->fw_empr_disabled = !priv->empr_available;
+
+ return 0;
}
static const struct pldmfw_ops ice_fwu_ops = {
@@ -642,25 +732,22 @@ static const struct pldmfw_ops ice_fwu_ops = {
};
/**
- * ice_cancel_pending_update - Cancel any pending update for a component
+ * ice_get_pending_updates - Check if the component has a pending update
* @pf: the PF driver structure
- * @component: if not NULL, the name of the component being updated
- * @extack: Netlink extended ACK structure
+ * @pending: on return, bitmap of updates pending
+ * @extack: Netlink extended ACK
*
- * Cancel any pending update for the specified component. If component is
- * NULL, all device updates will be canceled.
+ * Check if the device has any pending updates on any flash components.
*
- * Returns: zero on success, or a negative error code on failure.
+ * Returns: zero on success, or a negative error code on failure. Updates
+ * pending with the bitmap of pending updates.
*/
-static int
-ice_cancel_pending_update(struct ice_pf *pf, const char *component,
- struct netlink_ext_ack *extack)
+int ice_get_pending_updates(struct ice_pf *pf, u8 *pending,
+ struct netlink_ext_ack *extack)
{
- struct devlink *devlink = priv_to_devlink(pf);
struct device *dev = ice_pf_to_dev(pf);
struct ice_hw_dev_caps *dev_caps;
struct ice_hw *hw = &pf->hw;
- u8 pending = 0;
int err;
dev_caps = kzalloc(sizeof(*dev_caps), GFP_KERNEL);
@@ -679,23 +766,53 @@ ice_cancel_pending_update(struct ice_pf *pf, const char *component,
return err;
}
+ *pending = 0;
+
if (dev_caps->common_cap.nvm_update_pending_nvm) {
dev_info(dev, "The fw.mgmt flash component has a pending update\n");
- pending |= ICE_AQC_NVM_ACTIV_SEL_NVM;
+ *pending |= ICE_AQC_NVM_ACTIV_SEL_NVM;
}
if (dev_caps->common_cap.nvm_update_pending_orom) {
dev_info(dev, "The fw.undi flash component has a pending update\n");
- pending |= ICE_AQC_NVM_ACTIV_SEL_OROM;
+ *pending |= ICE_AQC_NVM_ACTIV_SEL_OROM;
}
if (dev_caps->common_cap.nvm_update_pending_netlist) {
dev_info(dev, "The fw.netlist flash component has a pending update\n");
- pending |= ICE_AQC_NVM_ACTIV_SEL_NETLIST;
+ *pending |= ICE_AQC_NVM_ACTIV_SEL_NETLIST;
}
kfree(dev_caps);
+ return 0;
+}
+
+/**
+ * ice_cancel_pending_update - Cancel any pending update for a component
+ * @pf: the PF driver structure
+ * @component: if not NULL, the name of the component being updated
+ * @extack: Netlink extended ACK structure
+ *
+ * Cancel any pending update for the specified component. If component is
+ * NULL, all device updates will be canceled.
+ *
+ * Returns: zero on success, or a negative error code on failure.
+ */
+static int
+ice_cancel_pending_update(struct ice_pf *pf, const char *component,
+ struct netlink_ext_ack *extack)
+{
+ struct devlink *devlink = priv_to_devlink(pf);
+ struct device *dev = ice_pf_to_dev(pf);
+ struct ice_hw *hw = &pf->hw;
+ u8 pending;
+ int err;
+
+ err = ice_get_pending_updates(pf, &pending, extack);
+ if (err)
+ return err;
+
/* If the flash_update request is for a specific component, ignore all
* of the other components.
*/
@@ -730,10 +847,15 @@ ice_cancel_pending_update(struct ice_pf *pf, const char *component,
}
pending |= ICE_AQC_NVM_REVERT_LAST_ACTIV;
- err = ice_switch_flash_banks(pf, pending, extack);
+ err = ice_switch_flash_banks(pf, pending, NULL, extack);
ice_release_nvm(hw);
+ /* Since we've canceled the pending update, we no longer know if EMP
+ * reset is restricted.
+ */
+ pf->fw_empr_disabled = false;
+
return err;
}
diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.h b/drivers/net/ethernet/intel/ice/ice_fw_update.h
index be6d222124f2..750574885716 100644
--- a/drivers/net/ethernet/intel/ice/ice_fw_update.h
+++ b/drivers/net/ethernet/intel/ice/ice_fw_update.h
@@ -7,5 +7,7 @@
int ice_devlink_flash_update(struct devlink *devlink,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack);
+int ice_get_pending_updates(struct ice_pf *pf, u8 *pending,
+ struct netlink_ext_ack *extack);
#endif
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index b8644adfba78..669702b16ef5 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -278,6 +278,10 @@ struct ice_hw_common_caps {
#define ICE_NVM_PENDING_NETLIST BIT(2)
bool nvm_unified_update;
#define ICE_NVM_MGMT_UNIFIED_UPD_SUPPORT BIT(3)
+ /* PCIe reset avoidance */
+ bool pcie_reset_avoidance; /* false: not supported, true: supported */
+ /* Post update reset restriction */
+ bool reset_restrict_support; /* false: not supported, true: supported */
};
/* IEEE 1588 TIME_SYNC specific info */
--
2.31.1.331.gb0c09ab8796f
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [Intel-wired-lan] [net-next PATCH v3 2/2] ice: support immediate firmware activation via devlink reload
2021-10-15 23:13 ` [Intel-wired-lan] [net-next PATCH v3 2/2] ice: support immediate firmware activation via devlink reload Jacob Keller
@ 2021-10-19 19:37 ` Keller, Jacob E
0 siblings, 0 replies; 3+ messages in thread
From: Keller, Jacob E @ 2021-10-19 19:37 UTC (permalink / raw)
To: intel-wired-lan
> -----Original Message-----
> From: Keller, Jacob E <jacob.e.keller@intel.com>
> Sent: Friday, October 15, 2021 4:13 PM
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Intel Wired LAN <intel-
> wired-lan at lists.osuosl.org>
> Cc: Keller, Jacob E <jacob.e.keller@intel.com>
> Subject: [net-next PATCH v3 2/2] ice: support immediate firmware activation via
> devlink reload
>
> In order to complete a device flash update for the ice device, the
> system administrator currently must perform a system reboot. This is not
> ideal, as rebooting the entire system can cause unwanted downtime.
>
> In many cases, it is possible to update the device firmware immediately
> using what is known as an EMP reset. This can be used to complete
> a device firmware update without the undesired effects caused by a full
> platform reboot.
>
> We can implement support for this in userspace using the devlink reload
> command. Implement this command for the ice driver so that a devlink
> reload with the FW_ACTIVATE reload action. This enables the
> administrator to request a firmware activation after performing a flash
> update.
>
> Unfortunately, it is not always possible to complete an update with
> a simple EMP reset. In some cases, the update cannot be completed
> without performing either a full power on or a cold PCIe reset. In these
> cases a system reboot is the only known way to complete the update.
>
> When actually writing the flash device, newer versions of firmware can
> inform the driver about whether an EMP reset is sufficient. It does this
> using two mechanisms, which are detected via firmware capability bits.
>
> The first is known as PCIe reset avoidance, and the second is known as
> indication of reset restriction.
>
> If the firmware supports these capabilities, it will be able to indicate
> to the driver when an EMP reset is sufficient to complete an update.
>
> The minimum required reset level is provided as part of the response to
> the final NVM module block write. This reset level indicates what reset
> is required in order to properly initialize the firmware given the
> contents that were updated in the NVM.
>
> The availability of EMP reset is indicated as part of the response to
> asking firmware to switch flash backs for the next load.
>
> Track these responses in the private structure when performing a flash
> update. After finalizing the update, add a status notification to inform
> the user of the required reset needed to switch the running firmware.
>
> Also track whether or not the EMP reset is available for use during
> reload. If firmware reported that it is available, an EMP reset can be
> requested using the appropriate firmware command.
>
> Add support for devlink reload, and allow the user to request a reload
> to the new firmware using the FW_ACTIVIATE flag. If an EMP reset is
> available from firmware, we will attempt to load the newly activated
> firmware.
>
> In some cases, such as older firmware versions which do not check or
> report whether EMP reset is suitable, the driver may not be able to
> determine when an EMP reset is available. In this case, whether an EMP
> reset is sufficient or not. In this case, we will always attempt the EMP
> reset.
>
> Finally, this change does *not* implement basic driver-only reload
> support. I did look into trying to do this. However, it requires
> significant refactor of how the ice driver probes and loads everything.
> The ice driver probe and allocation flows were not designed with such
> a reload in mind.
>
> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> ---
>
Ugh. There's another issue here. Turns out we're checking the wrong location for a bit offset, and so we're always reporting the wrong value for a reset level.
Need to send a v4 that fixes this.
Thanks,
Jake
> Changes since v2
> * ensure DEVLINK_F_RELOAD gets set
> * rebase to avoid conflicts
>
> drivers/net/ethernet/intel/ice/ice.h | 1 +
> .../net/ethernet/intel/ice/ice_adminq_cmd.h | 7 +
> drivers/net/ethernet/intel/ice/ice_common.c | 12 ++
> drivers/net/ethernet/intel/ice/ice_devlink.c | 91 ++++++++++
> .../net/ethernet/intel/ice/ice_fw_update.c | 166 +++++++++++++++---
> .../net/ethernet/intel/ice/ice_fw_update.h | 2 +
> drivers/net/ethernet/intel/ice/ice_type.h | 4 +
> 7 files changed, 261 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice.h
> b/drivers/net/ethernet/intel/ice/ice.h
> index 2fc2208be9de..6c0bf0f41a4c 100644
> --- a/drivers/net/ethernet/intel/ice/ice.h
> +++ b/drivers/net/ethernet/intel/ice/ice.h
> @@ -557,6 +557,7 @@ struct ice_pf {
> spinlock_t aq_wait_lock;
> struct hlist_head aq_wait_list;
> wait_queue_head_t aq_wait_queue;
> + bool fw_empr_disabled;
>
> wait_queue_head_t reset_wait_queue;
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
> b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
> index a8909c01bd59..d79578f01d18 100644
> --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
> +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
> @@ -117,6 +117,8 @@ struct ice_aqc_list_caps_elem {
> #define ICE_AQC_CAPS_NET_VER 0x004C
> #define ICE_AQC_CAPS_PENDING_NET_VER 0x004D
> #define ICE_AQC_CAPS_RDMA 0x0051
> +#define ICE_AQC_CAPS_PCIE_RESET_AVOIDANCE 0x0076
> +#define ICE_AQC_CAPS_POST_UPDATE_RESET_RESTRICT 0x0077
> #define ICE_AQC_CAPS_NVM_MGMT 0x0080
>
> u8 major_ver;
> @@ -1424,6 +1426,11 @@ struct ice_aqc_nvm {
> #define ICE_AQC_NVM_REVERT_LAST_ACTIV BIT(6) /* Write Activate only */
> #define ICE_AQC_NVM_ACTIV_SEL_MASK ICE_M(0x7, 3)
> #define ICE_AQC_NVM_FLASH_ONLY BIT(7)
> +#define ICE_AQC_NVM_RESET_LVL_M ICE_M(0x3, 0) /* Write
> reply only */
> +#define ICE_AQC_NVM_POR_FLAG 0
> +#define ICE_AQC_NVM_PERST_FLAG 1
> +#define ICE_AQC_NVM_EMPR_FLAG 2
> +#define ICE_AQC_NVM_EMPR_ENA BIT(0) /* Write Activate reply only
> */
> __le16 module_typeid;
> __le16 length;
> #define ICE_AQC_NVM_ERASE_LEN 0xFFFF
> diff --git a/drivers/net/ethernet/intel/ice/ice_common.c
> b/drivers/net/ethernet/intel/ice/ice_common.c
> index 3ab115c3bdf9..53c2fdc909ce 100644
> --- a/drivers/net/ethernet/intel/ice/ice_common.c
> +++ b/drivers/net/ethernet/intel/ice/ice_common.c
> @@ -2067,6 +2067,18 @@ ice_parse_common_caps(struct ice_hw *hw, struct
> ice_hw_common_caps *caps,
> ice_debug(hw, ICE_DBG_INIT, "%s: max_mtu = %d\n",
> prefix, caps->max_mtu);
> break;
> + case ICE_AQC_CAPS_PCIE_RESET_AVOIDANCE:
> + caps->pcie_reset_avoidance = (number > 0);
> + ice_debug(hw, ICE_DBG_INIT,
> + "%s: pcie_reset_avoidance = %d\n", prefix,
> + caps->pcie_reset_avoidance);
> + break;
> + case ICE_AQC_CAPS_POST_UPDATE_RESET_RESTRICT:
> + caps->reset_restrict_support = (number == 1);
> + ice_debug(hw, ICE_DBG_INIT,
> + "%s: reset_restrict_support = %d\n", prefix,
> + caps->reset_restrict_support);
> + break;
> default:
> /* Not one of the recognized common capabilities */
> found = false;
> diff --git a/drivers/net/ethernet/intel/ice/ice_devlink.c
> b/drivers/net/ethernet/intel/ice/ice_devlink.c
> index c64286aad7be..80e4a747b220 100644
> --- a/drivers/net/ethernet/intel/ice/ice_devlink.c
> +++ b/drivers/net/ethernet/intel/ice/ice_devlink.c
> @@ -370,8 +370,98 @@ static int ice_devlink_info_get(struct devlink *devlink,
> return err;
> }
>
> +/**
> + * ice_devlink_reload_down - Start reload
> + * @devlink: pointer to the devlink instance to reload
> + * @netns_change: if true, the network namespace is changing
> + * @action: the action to perform. Must be
> DEVLINK_RELOAD_ACTION_FW_ACTIVATE
> + * @limit: limits on what reload should do, such as not resetting
> + * @extack: netlink extended ACK structure
> + *
> + * Command issued by devlink core to perform a reload. This driver only
> + * supports firmware activation.
> + */
> +static int
> +ice_devlink_reload_down(struct devlink *devlink, bool netns_change,
> + enum devlink_reload_action action,
> + enum devlink_reload_limit limit,
> + struct netlink_ext_ack *extack)
> +{
> + struct ice_pf *pf = devlink_priv(devlink);
> + struct device *dev = ice_pf_to_dev(pf);
> + struct ice_hw *hw = &pf->hw;
> + u8 pending;
> + int err;
> +
> + err = ice_get_pending_updates(pf, &pending, extack);
> + if (err)
> + return err;
> +
> + if (!pending) {
> + NL_SET_ERR_MSG_MOD(extack, "No pending firmware update");
> + return -ECANCELED;
> + }
> +
> + if (pf->fw_empr_disabled) {
> + NL_SET_ERR_MSG_MOD(extack, "EMP reset is not available. To
> activate firmware, a reboot or power cycle is needed\n");
> + return -ECANCELED;
> + }
> +
> + dev_dbg(dev, "Issuing device EMP reset to activate firmware\n");
> +
> + err = ice_aq_nvm_update_empr(hw);
> + if (err) {
> + dev_err(dev, "Failed to trigger EMP device reset to reload
> firmware, err %d aq_err %s\n",
> + err, ice_aq_str(hw->adminq.sq_last_status));
> + NL_SET_ERR_MSG_MOD(extack, "Failed to trigger EMP device
> reset to reload firmware");
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * ice_devlink_reload_up - Finish reload
> + * @devlink: pointer to the devlink instance reloading
> + * @action: the action requested
> + * @limit: limits imposed by userspace, such as not resetting
> + * @actions_performed: on return, indicate what actions actually performed
> + * @extack: netlink extended ACK structure
> + *
> + * Complete a reload, such as waiting for the driver to come back up. The ice
> + * driver only supports firmware activation, which requires a device reset.
> + */
> +static int
> +ice_devlink_reload_up(struct devlink *devlink,
> + enum devlink_reload_action action,
> + enum devlink_reload_limit limit,
> + u32 *actions_performed,
> + struct netlink_ext_ack *extack)
> +{
> + struct ice_pf *pf = devlink_priv(devlink);
> + int err;
> +
> + *actions_performed = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE);
> +
> + err = ice_wait_for_reset(pf, 20 * HZ);
> + if (err) {
> + NL_SET_ERR_MSG_MOD(extack, "Device still resetting");
> + return err;
> + }
> +
> + /* After a device reset is complete, we no longer know whether the EMP
> + * reset is restricted.
> + */
> + pf->fw_empr_disabled = false;
> +
> + return 0;
> +}
> +
> static const struct devlink_ops ice_devlink_ops = {
> .supported_flash_update_params =
> DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
> + .reload_actions = BIT(DEVLINK_RELOAD_ACTION_FW_ACTIVATE),
> + .reload_down = ice_devlink_reload_down,
> + .reload_up = ice_devlink_reload_up,
> .eswitch_mode_get = ice_eswitch_mode_get,
> .eswitch_mode_set = ice_eswitch_mode_set,
> .info_get = ice_devlink_info_get,
> @@ -516,6 +606,7 @@ void ice_devlink_register(struct ice_pf *pf)
> struct device *dev = ice_pf_to_dev(pf);
> int err;
>
> + devlink_set_features(devlink, DEVLINK_F_RELOAD);
> devlink_register(devlink);
>
> err = devlink_params_register(devlink, ice_devlink_params,
> diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.c
> b/drivers/net/ethernet/intel/ice/ice_fw_update.c
> index 1f8e0e5d5660..e947fd6d8daa 100644
> --- a/drivers/net/ethernet/intel/ice/ice_fw_update.c
> +++ b/drivers/net/ethernet/intel/ice/ice_fw_update.c
> @@ -16,6 +16,12 @@ struct ice_fwu_priv {
>
> /* Track which NVM banks to activate at the end of the update */
> u8 activate_flags;
> +
> + /* Track the firmware response of reset level required */
> + u8 reset_level;
> +
> + /* Track if EMP reset is available */
> + u8 empr_available;
> };
>
> /**
> @@ -257,6 +263,7 @@ ice_send_component_table(struct pldmfw *context,
> struct pldmfw_component *compon
> * @block_size: size of the block to write, up to 4k
> * @block: pointer to block of data to write
> * @last_cmd: whether this is the last command
> + * @reset_level: storage for reset level required
> * @extack: netlink extended ACK structure
> *
> * Write a block of data to a flash module, and await for the completion
> @@ -269,7 +276,7 @@ ice_send_component_table(struct pldmfw *context,
> struct pldmfw_component *compon
> static int
> ice_write_one_nvm_block(struct ice_pf *pf, u16 module, u32 offset,
> u16 block_size, u8 *block, bool last_cmd,
> - struct netlink_ext_ack *extack)
> + u8 *reset_level, struct netlink_ext_ack *extack)
> {
> u16 completion_module, completion_retval;
> struct device *dev = ice_pf_to_dev(pf);
> @@ -335,6 +342,24 @@ ice_write_one_nvm_block(struct ice_pf *pf, u16
> module, u32 offset,
> return -EIO;
> }
>
> + /* For the last command to write the NVM bank, newer versions of
> + * firmware indicate the required level of reset to complete
> + * activation of firmware. If the firmware supports this, cache the
> + * response for indicating to the user later. Otherwise, assume that
> + * a full power cycle is required.
> + */
> + if (reset_level && last_cmd && module == ICE_SR_1ST_NVM_BANK_PTR)
> {
> + if (hw->dev_caps.common_cap.pcie_reset_avoidance) {
> + *reset_level = (event.desc.params.nvm.cmd_flags &
> + ICE_AQC_NVM_RESET_LVL_M);
> + dev_dbg(dev, "Firmware reported required reset level as
> %u\n",
> + *reset_level);
> + } else {
> + *reset_level = ICE_AQC_NVM_POR_FLAG;
> + dev_dbg(dev, "Firmware doesn't support indicating
> required reset level. Assuming a power cycle is required\n");
> + }
> + }
> +
> return 0;
> }
>
> @@ -345,6 +370,7 @@ ice_write_one_nvm_block(struct ice_pf *pf, u16 module,
> u32 offset,
> * @component: the name of the component being updated
> * @image: buffer of image data to write to the NVM
> * @length: length of the buffer
> + * @reset_level: storage for reset level required
> * @extack: netlink extended ACK structure
> *
> * Loop over the data for a given NVM module and program it in 4 Kb
> @@ -357,7 +383,7 @@ ice_write_one_nvm_block(struct ice_pf *pf, u16 module,
> u32 offset,
> */
> static int
> ice_write_nvm_module(struct ice_pf *pf, u16 module, const char *component,
> - const u8 *image, u32 length,
> + const u8 *image, u32 length, u8 *reset_level,
> struct netlink_ext_ack *extack)
> {
> struct device *dev = ice_pf_to_dev(pf);
> @@ -391,7 +417,8 @@ ice_write_nvm_module(struct ice_pf *pf, u16 module,
> const char *component,
> memcpy(block, image + offset, block_size);
>
> err = ice_write_one_nvm_block(pf, module, offset, block_size,
> - block, last_cmd, extack);
> + block, last_cmd, reset_level,
> + extack);
> if (err)
> break;
>
> @@ -507,6 +534,7 @@ ice_erase_nvm_module(struct ice_pf *pf, u16 module,
> const char *component,
> * ice_switch_flash_banks - Tell firmware to switch NVM banks
> * @pf: Pointer to the PF data structure
> * @activate_flags: flags used for the activation command
> + * @empr_available: on return, indicates of EMP reset is available
> * @extack: netlink extended ACK structure
> *
> * Notify firmware to activate the newly written flash banks, and wait for the
> @@ -514,8 +542,9 @@ ice_erase_nvm_module(struct ice_pf *pf, u16 module,
> const char *component,
> *
> * Returns: zero on success or an error code on failure.
> */
> -static int ice_switch_flash_banks(struct ice_pf *pf, u8 activate_flags,
> - struct netlink_ext_ack *extack)
> +static int
> +ice_switch_flash_banks(struct ice_pf *pf, u8 activate_flags,
> + u8 *empr_available, struct netlink_ext_ack *extack)
> {
> struct device *dev = ice_pf_to_dev(pf);
> struct ice_rq_event_info event;
> @@ -550,6 +579,23 @@ static int ice_switch_flash_banks(struct ice_pf *pf, u8
> activate_flags,
> return -EIO;
> }
>
> + /* Newer versions of firmware have support to indicate whether an EMP
> + * reset to reload firmware is available. For older firmware, EMP
> + * reset is always available.
> + */
> + if (empr_available) {
> + if (hw->dev_caps.common_cap.reset_restrict_support) {
> + *empr_available = (event.desc.params.nvm.cmd_flags &
> + ICE_AQC_NVM_EMPR_ENA);
> + dev_dbg(dev, "Firmware indicated that EMP reset is
> %s\n",
> + *empr_available ?
> + "available" : "not available");
> + } else {
> + *empr_available = true;
> + dev_dbg(dev, "Firmware does not support restricting
> EMP reset availability\n");
> + }
> + }
> +
> return 0;
> }
>
> @@ -573,6 +619,7 @@ ice_flash_component(struct pldmfw *context, struct
> pldmfw_component *component)
> struct netlink_ext_ack *extack = priv->extack;
> struct ice_pf *pf = priv->pf;
> const char *name;
> + u8 *reset_level;
> u16 module;
> u8 flag;
> int err;
> @@ -581,16 +628,19 @@ ice_flash_component(struct pldmfw *context, struct
> pldmfw_component *component)
> case NVM_COMP_ID_OROM:
> module = ICE_SR_1ST_OROM_BANK_PTR;
> flag = ICE_AQC_NVM_ACTIV_SEL_OROM;
> + reset_level = NULL;
> name = "fw.undi";
> break;
> case NVM_COMP_ID_NVM:
> module = ICE_SR_1ST_NVM_BANK_PTR;
> flag = ICE_AQC_NVM_ACTIV_SEL_NVM;
> + reset_level = &priv->reset_level;
> name = "fw.mgmt";
> break;
> case NVM_COMP_ID_NETLIST:
> module = ICE_SR_NETLIST_BANK_PTR;
> flag = ICE_AQC_NVM_ACTIV_SEL_NETLIST;
> + reset_level = NULL;
> name = "fw.netlist";
> break;
> default:
> @@ -610,7 +660,8 @@ ice_flash_component(struct pldmfw *context, struct
> pldmfw_component *component)
> return err;
>
> return ice_write_nvm_module(pf, module, name, component-
> >component_data,
> - component->component_size, extack);
> + component->component_size, reset_level,
> + extack);
> }
>
> /**
> @@ -628,9 +679,48 @@ static int ice_finalize_update(struct pldmfw *context)
> struct ice_fwu_priv *priv = container_of(context, struct ice_fwu_priv,
> context);
> struct netlink_ext_ack *extack = priv->extack;
> struct ice_pf *pf = priv->pf;
> + struct devlink *devlink;
> + int err;
>
> /* Finally, notify firmware to activate the written NVM banks */
> - return ice_switch_flash_banks(pf, priv->activate_flags, extack);
> + err = ice_switch_flash_banks(pf, priv->activate_flags,
> + &priv->empr_available, extack);
> + if (err)
> + return err;
> +
> + devlink = priv_to_devlink(pf);
> +
> + /* If the required reset is EMPR, but EMPR is disabled, report that
> + * a reboot is required instead.
> + */
> + if (priv->reset_level == ICE_AQC_NVM_EMPR_FLAG &&
> + !priv->empr_available) {
> + dev_dbg(ice_pf_to_dev(pf), "Firmware indicated EMP reset as
> sufficient, but EMP reset is disabled\n");
> + priv->reset_level = ICE_AQC_NVM_PERST_FLAG;
> + }
> +
> + switch (priv->reset_level) {
> + case ICE_AQC_NVM_EMPR_FLAG:
> + devlink_flash_update_status_notify(devlink,
> + "Activate new firmware by
> devlink reload",
> + NULL, 0, 0);
> + break;
> + case ICE_AQC_NVM_PERST_FLAG:
> + devlink_flash_update_status_notify(devlink,
> + "Activate new firmware by
> rebooting the system",
> + NULL, 0, 0);
> + break;
> + case ICE_AQC_NVM_POR_FLAG:
> + default:
> + devlink_flash_update_status_notify(devlink,
> + "Activate new firmware by
> power cycling the system",
> + NULL, 0, 0);
> + break;
> + }
> +
> + pf->fw_empr_disabled = !priv->empr_available;
> +
> + return 0;
> }
>
> static const struct pldmfw_ops ice_fwu_ops = {
> @@ -642,25 +732,22 @@ static const struct pldmfw_ops ice_fwu_ops = {
> };
>
> /**
> - * ice_cancel_pending_update - Cancel any pending update for a component
> + * ice_get_pending_updates - Check if the component has a pending update
> * @pf: the PF driver structure
> - * @component: if not NULL, the name of the component being updated
> - * @extack: Netlink extended ACK structure
> + * @pending: on return, bitmap of updates pending
> + * @extack: Netlink extended ACK
> *
> - * Cancel any pending update for the specified component. If component is
> - * NULL, all device updates will be canceled.
> + * Check if the device has any pending updates on any flash components.
> *
> - * Returns: zero on success, or a negative error code on failure.
> + * Returns: zero on success, or a negative error code on failure. Updates
> + * pending with the bitmap of pending updates.
> */
> -static int
> -ice_cancel_pending_update(struct ice_pf *pf, const char *component,
> - struct netlink_ext_ack *extack)
> +int ice_get_pending_updates(struct ice_pf *pf, u8 *pending,
> + struct netlink_ext_ack *extack)
> {
> - struct devlink *devlink = priv_to_devlink(pf);
> struct device *dev = ice_pf_to_dev(pf);
> struct ice_hw_dev_caps *dev_caps;
> struct ice_hw *hw = &pf->hw;
> - u8 pending = 0;
> int err;
>
> dev_caps = kzalloc(sizeof(*dev_caps), GFP_KERNEL);
> @@ -679,23 +766,53 @@ ice_cancel_pending_update(struct ice_pf *pf, const
> char *component,
> return err;
> }
>
> + *pending = 0;
> +
> if (dev_caps->common_cap.nvm_update_pending_nvm) {
> dev_info(dev, "The fw.mgmt flash component has a pending
> update\n");
> - pending |= ICE_AQC_NVM_ACTIV_SEL_NVM;
> + *pending |= ICE_AQC_NVM_ACTIV_SEL_NVM;
> }
>
> if (dev_caps->common_cap.nvm_update_pending_orom) {
> dev_info(dev, "The fw.undi flash component has a pending
> update\n");
> - pending |= ICE_AQC_NVM_ACTIV_SEL_OROM;
> + *pending |= ICE_AQC_NVM_ACTIV_SEL_OROM;
> }
>
> if (dev_caps->common_cap.nvm_update_pending_netlist) {
> dev_info(dev, "The fw.netlist flash component has a pending
> update\n");
> - pending |= ICE_AQC_NVM_ACTIV_SEL_NETLIST;
> + *pending |= ICE_AQC_NVM_ACTIV_SEL_NETLIST;
> }
>
> kfree(dev_caps);
>
> + return 0;
> +}
> +
> +/**
> + * ice_cancel_pending_update - Cancel any pending update for a component
> + * @pf: the PF driver structure
> + * @component: if not NULL, the name of the component being updated
> + * @extack: Netlink extended ACK structure
> + *
> + * Cancel any pending update for the specified component. If component is
> + * NULL, all device updates will be canceled.
> + *
> + * Returns: zero on success, or a negative error code on failure.
> + */
> +static int
> +ice_cancel_pending_update(struct ice_pf *pf, const char *component,
> + struct netlink_ext_ack *extack)
> +{
> + struct devlink *devlink = priv_to_devlink(pf);
> + struct device *dev = ice_pf_to_dev(pf);
> + struct ice_hw *hw = &pf->hw;
> + u8 pending;
> + int err;
> +
> + err = ice_get_pending_updates(pf, &pending, extack);
> + if (err)
> + return err;
> +
> /* If the flash_update request is for a specific component, ignore all
> * of the other components.
> */
> @@ -730,10 +847,15 @@ ice_cancel_pending_update(struct ice_pf *pf, const
> char *component,
> }
>
> pending |= ICE_AQC_NVM_REVERT_LAST_ACTIV;
> - err = ice_switch_flash_banks(pf, pending, extack);
> + err = ice_switch_flash_banks(pf, pending, NULL, extack);
>
> ice_release_nvm(hw);
>
> + /* Since we've canceled the pending update, we no longer know if EMP
> + * reset is restricted.
> + */
> + pf->fw_empr_disabled = false;
> +
> return err;
> }
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.h
> b/drivers/net/ethernet/intel/ice/ice_fw_update.h
> index be6d222124f2..750574885716 100644
> --- a/drivers/net/ethernet/intel/ice/ice_fw_update.h
> +++ b/drivers/net/ethernet/intel/ice/ice_fw_update.h
> @@ -7,5 +7,7 @@
> int ice_devlink_flash_update(struct devlink *devlink,
> struct devlink_flash_update_params *params,
> struct netlink_ext_ack *extack);
> +int ice_get_pending_updates(struct ice_pf *pf, u8 *pending,
> + struct netlink_ext_ack *extack);
>
> #endif
> diff --git a/drivers/net/ethernet/intel/ice/ice_type.h
> b/drivers/net/ethernet/intel/ice/ice_type.h
> index b8644adfba78..669702b16ef5 100644
> --- a/drivers/net/ethernet/intel/ice/ice_type.h
> +++ b/drivers/net/ethernet/intel/ice/ice_type.h
> @@ -278,6 +278,10 @@ struct ice_hw_common_caps {
> #define ICE_NVM_PENDING_NETLIST BIT(2)
> bool nvm_unified_update;
> #define ICE_NVM_MGMT_UNIFIED_UPD_SUPPORT BIT(3)
> + /* PCIe reset avoidance */
> + bool pcie_reset_avoidance; /* false: not supported, true: supported */
> + /* Post update reset restriction */
> + bool reset_restrict_support; /* false: not supported, true: supported */
> };
>
> /* IEEE 1588 TIME_SYNC specific info */
> --
> 2.31.1.331.gb0c09ab8796f
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2021-10-19 19:37 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-10-15 23:13 [Intel-wired-lan] [net-next PATCH v3 1/2] ice: move ice_devlink_flash_update and merge with ice_flash_pldm_image Jacob Keller
2021-10-15 23:13 ` [Intel-wired-lan] [net-next PATCH v3 2/2] ice: support immediate firmware activation via devlink reload Jacob Keller
2021-10-19 19:37 ` Keller, Jacob E
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox