Skip to content

Commit

Permalink
ice: implement device flash update via devlink
Browse files Browse the repository at this point in the history
Use the newly added pldmfw library to implement device flash update for
the Intel ice networking device driver. This support uses the devlink
flash update interface.

The main parts of the flash include the Option ROM, the netlist module,
and the main NVM data. The PLDM firmware file contains modules for each
of these components.

Using the pldmfw library, the provided firmware file will be scanned for
the three major components, "fw.undi" for the Option ROM, "fw.mgmt" for
the main NVM module containing the primary device firmware, and
"fw.netlist" containing the netlist module.

The flash is separated into two banks, the active bank containing the
running firmware, and the inactive bank which we use for update. Each
module is updated in a staged process. First, the inactive bank is
erased, preparing the device for update. Second, the contents of the
component are copied to the inactive portion of the flash. After all
components are updated, the driver signals the device to switch the
active bank during the next EMP reset (which would usually occur during
the next reboot).

Although the firmware AdminQ interface does report an immediate status
for each command, the NVM erase and NVM write commands receive status
asynchronously. The driver must not continue writing until previous
erase and write commands have finished. The real status of the NVM
commands is returned over the receive AdminQ. Implement a simple
interface that uses a wait queue so that the main update thread can
sleep until the completion status is reported by firmware. For erasing
the inactive banks, this can take quite a while in practice.

To help visualize the process to the devlink application and other
applications based on the devlink netlink interface, status is reported
via the devlink_flash_update_status_notify. While we do report status
after each 4k block when writing, there is no real status we can report
during erasing. We simply must wait for the complete module erasure to
finish.

With this implementation, basic flash update for the ice hardware is
supported.

Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
jacob-keller authored and davem330 committed Jul 29, 2020
1 parent 2ab560a commit d69ea41
Show file tree
Hide file tree
Showing 9 changed files with 1,007 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/net/ethernet/intel/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ config ICE
default n
depends on PCI_MSI
select NET_DEVLINK
select PLDMFW
help
This driver supports Intel(R) Ethernet Connection E800 Series of
devices. For more information on how to identify your adapter, go
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/intel/ice/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ice-y := ice_main.o \
ice_flex_pipe.o \
ice_flow.o \
ice_devlink.o \
ice_fw_update.o \
ice_ethtool.o
ice-$(CONFIG_PCI_IOV) += ice_virtchnl_pf.o ice_sriov.o
ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/ethernet/intel/ice/ice.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/aer.h>
#include <linux/interrupt.h>
#include <linux/ethtool.h>
Expand Down Expand Up @@ -412,6 +413,12 @@ struct ice_pf {
struct mutex sw_mutex; /* lock for protecting VSI alloc flow */
struct mutex tc_mutex; /* lock to protect TC changes */
u32 msg_enable;

/* spinlock to protect the AdminQ wait list */
spinlock_t aq_wait_lock;
struct hlist_head aq_wait_list;
wait_queue_head_t aq_wait_queue;

u32 hw_csum_rx_error;
u16 oicr_idx; /* Other interrupt cause MSIX vector index */
u16 num_avail_sw_msix; /* remaining MSIX SW vectors left unclaimed */
Expand Down Expand Up @@ -593,6 +600,8 @@ void ice_fdir_release_flows(struct ice_hw *hw);
void ice_fdir_replay_flows(struct ice_hw *hw);
void ice_fdir_replay_fltrs(struct ice_pf *pf);
int ice_fdir_create_dflt_rules(struct ice_pf *pf);
int ice_aq_wait_for_event(struct ice_pf *pf, u16 opcode, unsigned long timeout,
struct ice_rq_event_info *event);
int ice_open(struct net_device *netdev);
int ice_stop(struct net_device *netdev);
void ice_service_task_schedule(struct ice_pf *pf);
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/ethernet/intel/ice/ice_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -2223,7 +2223,7 @@ ice_aq_list_caps(struct ice_hw *hw, void *buf, u16 buf_size, u32 *cap_count,
* Read the device capabilities and extract them into the dev_caps structure
* for later use.
*/
static enum ice_status
enum ice_status
ice_discover_dev_caps(struct ice_hw *hw, struct ice_hw_dev_caps *dev_caps)
{
enum ice_status status;
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/ethernet/intel/ice/ice_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
enum ice_status
ice_aq_list_caps(struct ice_hw *hw, void *buf, u16 buf_size, u32 *cap_count,
enum ice_adminq_opc opc, struct ice_sq_cd *cd);
enum ice_status
ice_discover_dev_caps(struct ice_hw *hw, struct ice_hw_dev_caps *dev_caps);
void
ice_update_phy_type(u64 *phy_type_low, u64 *phy_type_high,
u16 link_speeds_bitmap);
Expand Down
54 changes: 54 additions & 0 deletions drivers/net/ethernet/intel/ice/ice_devlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "ice.h"
#include "ice_lib.h"
#include "ice_devlink.h"
#include "ice_fw_update.h"

static int ice_info_get_dsn(struct ice_pf *pf, char *buf, size_t len)
{
Expand Down Expand Up @@ -229,8 +230,61 @@ static int ice_devlink_info_get(struct devlink *devlink,
return 0;
}

/**
* ice_devlink_flash_update - Update firmware stored in flash on the device
* @devlink: pointer to devlink associated with device to update
* @path: the path of the firmware file to use via request_firmware
* @component: name of the component to update, or NULL
* @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, const char *path,
const char *component, struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
struct device *dev = &pf->pdev->dev;
struct ice_hw *hw = &pf->hw;
const struct firmware *fw;
int err;

/* individual component update is not yet supported */
if (component)
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_check_for_pending_update(pf, component, extack);
if (err)
return err;

err = request_firmware(&fw, path, dev);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Unable to read file from disk");
return err;
}

devlink_flash_update_begin_notify(devlink);
devlink_flash_update_status_notify(devlink, "Preparing to flash",
component, 0, 0);
err = ice_flash_pldm_image(pf, fw, extack);
devlink_flash_update_end_notify(devlink);

release_firmware(fw);

return err;
}

static const struct devlink_ops ice_devlink_ops = {
.info_get = ice_devlink_info_get,
.flash_update = ice_devlink_flash_update,
};

static void ice_devlink_free(void *devlink_ptr)
Expand Down

0 comments on commit d69ea41

Please sign in to comment.