Commit d69ea414 authored by Jacob Keller's avatar Jacob Keller Committed by David S. Miller

ice: implement device flash update via devlink

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: default avatarJacob Keller <jacob.e.keller@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2ab560a7
...@@ -295,6 +295,7 @@ config ICE ...@@ -295,6 +295,7 @@ config ICE
default n default n
depends on PCI_MSI depends on PCI_MSI
select NET_DEVLINK select NET_DEVLINK
select PLDMFW
help help
This driver supports Intel(R) Ethernet Connection E800 Series of This driver supports Intel(R) Ethernet Connection E800 Series of
devices. For more information on how to identify your adapter, go devices. For more information on how to identify your adapter, go
......
...@@ -23,6 +23,7 @@ ice-y := ice_main.o \ ...@@ -23,6 +23,7 @@ ice-y := ice_main.o \
ice_flex_pipe.o \ ice_flex_pipe.o \
ice_flow.o \ ice_flow.o \
ice_devlink.o \ ice_devlink.o \
ice_fw_update.o \
ice_ethtool.o ice_ethtool.o
ice-$(CONFIG_PCI_IOV) += ice_virtchnl_pf.o ice_sriov.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 ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/aer.h> #include <linux/aer.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
...@@ -412,6 +413,12 @@ struct ice_pf { ...@@ -412,6 +413,12 @@ struct ice_pf {
struct mutex sw_mutex; /* lock for protecting VSI alloc flow */ struct mutex sw_mutex; /* lock for protecting VSI alloc flow */
struct mutex tc_mutex; /* lock to protect TC changes */ struct mutex tc_mutex; /* lock to protect TC changes */
u32 msg_enable; 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; u32 hw_csum_rx_error;
u16 oicr_idx; /* Other interrupt cause MSIX vector index */ u16 oicr_idx; /* Other interrupt cause MSIX vector index */
u16 num_avail_sw_msix; /* remaining MSIX SW vectors left unclaimed */ u16 num_avail_sw_msix; /* remaining MSIX SW vectors left unclaimed */
...@@ -593,6 +600,8 @@ void ice_fdir_release_flows(struct ice_hw *hw); ...@@ -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_flows(struct ice_hw *hw);
void ice_fdir_replay_fltrs(struct ice_pf *pf); void ice_fdir_replay_fltrs(struct ice_pf *pf);
int ice_fdir_create_dflt_rules(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_open(struct net_device *netdev);
int ice_stop(struct net_device *netdev); int ice_stop(struct net_device *netdev);
void ice_service_task_schedule(struct ice_pf *pf); void ice_service_task_schedule(struct ice_pf *pf);
......
...@@ -2223,7 +2223,7 @@ ice_aq_list_caps(struct ice_hw *hw, void *buf, u16 buf_size, u32 *cap_count, ...@@ -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 * Read the device capabilities and extract them into the dev_caps structure
* for later use. * 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) ice_discover_dev_caps(struct ice_hw *hw, struct ice_hw_dev_caps *dev_caps)
{ {
enum ice_status status; enum ice_status status;
......
...@@ -88,6 +88,8 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode, ...@@ -88,6 +88,8 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
enum ice_status enum ice_status
ice_aq_list_caps(struct ice_hw *hw, void *buf, u16 buf_size, u32 *cap_count, 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_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 void
ice_update_phy_type(u64 *phy_type_low, u64 *phy_type_high, ice_update_phy_type(u64 *phy_type_low, u64 *phy_type_high,
u16 link_speeds_bitmap); u16 link_speeds_bitmap);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "ice.h" #include "ice.h"
#include "ice_lib.h" #include "ice_lib.h"
#include "ice_devlink.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) static int ice_info_get_dsn(struct ice_pf *pf, char *buf, size_t len)
{ {
...@@ -229,8 +230,61 @@ static int ice_devlink_info_get(struct devlink *devlink, ...@@ -229,8 +230,61 @@ static int ice_devlink_info_get(struct devlink *devlink,
return 0; 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 = { static const struct devlink_ops ice_devlink_ops = {
.info_get = ice_devlink_info_get, .info_get = ice_devlink_info_get,
.flash_update = ice_devlink_flash_update,
}; };
static void ice_devlink_free(void *devlink_ptr) static void ice_devlink_free(void *devlink_ptr)
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2018-2019, Intel Corporation. */
#ifndef _ICE_FW_UPDATE_H_
#define _ICE_FW_UPDATE_H_
int ice_flash_pldm_image(struct ice_pf *pf, const struct firmware *fw,
struct netlink_ext_ack *extack);
int ice_check_for_pending_update(struct ice_pf *pf, const char *component,
struct netlink_ext_ack *extack);
#endif
...@@ -914,6 +914,151 @@ ice_handle_link_event(struct ice_pf *pf, struct ice_rq_event_info *event) ...@@ -914,6 +914,151 @@ ice_handle_link_event(struct ice_pf *pf, struct ice_rq_event_info *event)
return status; return status;
} }
enum ice_aq_task_state {
ICE_AQ_TASK_WAITING = 0,
ICE_AQ_TASK_COMPLETE,
ICE_AQ_TASK_CANCELED,
};
struct ice_aq_task {
struct hlist_node entry;
u16 opcode;
struct ice_rq_event_info *event;
enum ice_aq_task_state state;
};
/**
* ice_wait_for_aq_event - Wait for an AdminQ event from firmware
* @pf: pointer to the PF private structure
* @opcode: the opcode to wait for
* @timeout: how long to wait, in jiffies
* @event: storage for the event info
*
* Waits for a specific AdminQ completion event on the ARQ for a given PF. The
* current thread will be put to sleep until the specified event occurs or
* until the given timeout is reached.
*
* To obtain only the descriptor contents, pass an event without an allocated
* msg_buf. If the complete data buffer is desired, allocate the
* event->msg_buf with enough space ahead of time.
*
* Returns: zero on success, or a negative error code on failure.
*/
int ice_aq_wait_for_event(struct ice_pf *pf, u16 opcode, unsigned long timeout,
struct ice_rq_event_info *event)
{
struct ice_aq_task *task;
long ret;
int err;
task = kzalloc(sizeof(*task), GFP_KERNEL);
if (!task)
return -ENOMEM;
INIT_HLIST_NODE(&task->entry);
task->opcode = opcode;
task->event = event;
task->state = ICE_AQ_TASK_WAITING;
spin_lock_bh(&pf->aq_wait_lock);
hlist_add_head(&task->entry, &pf->aq_wait_list);
spin_unlock_bh(&pf->aq_wait_lock);
ret = wait_event_interruptible_timeout(pf->aq_wait_queue, task->state,
timeout);
switch (task->state) {
case ICE_AQ_TASK_WAITING:
err = ret < 0 ? ret : -ETIMEDOUT;
break;
case ICE_AQ_TASK_CANCELED:
err = ret < 0 ? ret : -ECANCELED;
break;
case ICE_AQ_TASK_COMPLETE:
err = ret < 0 ? ret : 0;
break;
default:
WARN(1, "Unexpected AdminQ wait task state %u", task->state);
err = -EINVAL;
break;
}
spin_lock_bh(&pf->aq_wait_lock);
hlist_del(&task->entry);
spin_unlock_bh(&pf->aq_wait_lock);
kfree(task);
return err;
}
/**
* ice_aq_check_events - Check if any thread is waiting for an AdminQ event
* @pf: pointer to the PF private structure
* @opcode: the opcode of the event
* @event: the event to check
*
* Loops over the current list of pending threads waiting for an AdminQ event.
* For each matching task, copy the contents of the event into the task
* structure and wake up the thread.
*
* If multiple threads wait for the same opcode, they will all be woken up.
*
* Note that event->msg_buf will only be duplicated if the event has a buffer
* with enough space already allocated. Otherwise, only the descriptor and
* message length will be copied.
*
* Returns: true if an event was found, false otherwise
*/
static void ice_aq_check_events(struct ice_pf *pf, u16 opcode,
struct ice_rq_event_info *event)
{
struct ice_aq_task *task;
bool found = false;
spin_lock_bh(&pf->aq_wait_lock);
hlist_for_each_entry(task, &pf->aq_wait_list, entry) {
if (task->state || task->opcode != opcode)
continue;
memcpy(&task->event->desc, &event->desc, sizeof(event->desc));
task->event->msg_len = event->msg_len;
/* Only copy the data buffer if a destination was set */
if (task->event->msg_buf &&
task->event->buf_len > event->buf_len) {
memcpy(task->event->msg_buf, event->msg_buf,
event->buf_len);
task->event->buf_len = event->buf_len;
}
task->state = ICE_AQ_TASK_COMPLETE;
found = true;
}
spin_unlock_bh(&pf->aq_wait_lock);
if (found)
wake_up(&pf->aq_wait_queue);
}
/**
* ice_aq_cancel_waiting_tasks - Immediately cancel all waiting tasks
* @pf: the PF private structure
*
* Set all waiting tasks to ICE_AQ_TASK_CANCELED, and wake up their threads.
* This will then cause ice_aq_wait_for_event to exit with -ECANCELED.
*/
static void ice_aq_cancel_waiting_tasks(struct ice_pf *pf)
{
struct ice_aq_task *task;
spin_lock_bh(&pf->aq_wait_lock);
hlist_for_each_entry(task, &pf->aq_wait_list, entry)
task->state = ICE_AQ_TASK_CANCELED;
spin_unlock_bh(&pf->aq_wait_lock);
wake_up(&pf->aq_wait_queue);
}
/** /**
* __ice_clean_ctrlq - helper function to clean controlq rings * __ice_clean_ctrlq - helper function to clean controlq rings
* @pf: ptr to struct ice_pf * @pf: ptr to struct ice_pf
...@@ -1010,6 +1155,9 @@ static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type) ...@@ -1010,6 +1155,9 @@ static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type)
opcode = le16_to_cpu(event.desc.opcode); opcode = le16_to_cpu(event.desc.opcode);
/* Notify any thread that might be waiting for this event */
ice_aq_check_events(pf, opcode, &event);
switch (opcode) { switch (opcode) {
case ice_aqc_opc_get_link_status: case ice_aqc_opc_get_link_status:
if (ice_handle_link_event(pf, &event)) if (ice_handle_link_event(pf, &event))
...@@ -3089,6 +3237,10 @@ static int ice_init_pf(struct ice_pf *pf) ...@@ -3089,6 +3237,10 @@ static int ice_init_pf(struct ice_pf *pf)
mutex_init(&pf->sw_mutex); mutex_init(&pf->sw_mutex);
mutex_init(&pf->tc_mutex); mutex_init(&pf->tc_mutex);
INIT_HLIST_HEAD(&pf->aq_wait_list);
spin_lock_init(&pf->aq_wait_lock);
init_waitqueue_head(&pf->aq_wait_queue);
/* setup service timer and periodic service task */ /* setup service timer and periodic service task */
timer_setup(&pf->serv_tmr, ice_service_timer, 0); timer_setup(&pf->serv_tmr, ice_service_timer, 0);
pf->serv_tmr_period = HZ; pf->serv_tmr_period = HZ;
...@@ -4017,6 +4169,8 @@ static void ice_remove(struct pci_dev *pdev) ...@@ -4017,6 +4169,8 @@ static void ice_remove(struct pci_dev *pdev)
set_bit(__ICE_DOWN, pf->state); set_bit(__ICE_DOWN, pf->state);
ice_service_task_stop(pf); ice_service_task_stop(pf);
ice_aq_cancel_waiting_tasks(pf);
mutex_destroy(&(&pf->hw)->fdir_fltr_lock); mutex_destroy(&(&pf->hw)->fdir_fltr_lock);
if (!ice_is_safe_mode(pf)) if (!ice_is_safe_mode(pf))
ice_remove_arfs(pf); ice_remove_arfs(pf);
......
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