ice: Refactor VSI allocation, deletion and rebuild flow

This patch refactors aspects of the VSI allocation, deletion and rebuild
flow. Some of the more noteworthy changes are described below.

1) On reset, all switch filters applied in the hardware are lost. In
   the rebuild flow, only MAC and broadcast filters are being restored.
   Instead, use a new function ice_replay_all_fltr to restore all the
   filters that were previously added. To do this, remove calls to
   ice_remove_vsi_fltr to prevent cleaning out the internal bookkeeping
   structures that ice_replay_all_fltr uses to replay filters.

2) Introduce a new state bit __ICE_PREPARED_FOR_RESET to distinguish the
   PF that requested the reset (and consequently prepared for it) from
   the rest of the PFs. These other PFs will prepare for reset only
   when they receive an interrupt from the firmware.

3) Use new functions ice_add_vsi and ice_free_vsi to create and destroy
   VSIs respectively. These functions accept a handle to uniquely
   identify a VSI. This same handle is required to rebuild the VSI post
   reset. To prevent confusion, the existing ice_vsi_add was renamed to
   ice_vsi_init.

4) Enhance ice_vsi_setup for the upcoming SR-IOV changes and expose a
   new wrapper function ice_pf_vsi_setup to create PF VSIs. Rework the
   error handling path in ice_setup_pf_sw.

5) Introduce a new function ice_vsi_release_all to release all PF VSIs.

Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
This commit is contained in:
Anirudh Venkataramanan 2018-08-09 06:29:50 -07:00 committed by Jeff Kirsher
parent 80d144c9ac
commit 0f9d5027a7
7 changed files with 582 additions and 173 deletions

View File

@ -62,6 +62,7 @@ extern const char ice_drv_ver[];
#define ICE_RES_VALID_BIT 0x8000
#define ICE_RES_MISC_VEC_ID (ICE_RES_VALID_BIT - 1)
#define ICE_INVAL_Q_INDEX 0xffff
#define ICE_INVAL_VFID 256
#define ICE_VSIQF_HKEY_ARRAY_SIZE ((VSIQF_HKEY_MAX_INDEX + 1) * 4)
@ -122,6 +123,7 @@ struct ice_sw {
enum ice_state {
__ICE_DOWN,
__ICE_NEEDS_RESTART,
__ICE_PREPARED_FOR_RESET, /* set by driver when prepared */
__ICE_RESET_RECOVERY_PENDING, /* set by driver when reset starts */
__ICE_PFR_REQ, /* set by driver and peers */
__ICE_CORER_REQ, /* set by driver and peers */

View File

@ -1253,6 +1253,7 @@ struct ice_aq_desc {
struct ice_aqc_add_txqs add_txqs;
struct ice_aqc_dis_txqs dis_txqs;
struct ice_aqc_add_get_update_free_vsi vsi_cmd;
struct ice_aqc_add_update_free_vsi_resp add_update_free_vsi_res;
struct ice_aqc_alloc_free_res_cmd sw_res_ctrl;
struct ice_aqc_set_event_mask set_event_mask;
struct ice_aqc_get_link_status get_link_status;

View File

@ -711,6 +711,8 @@ enum ice_status ice_reset(struct ice_hw *hw, enum ice_reset_req req)
ice_debug(hw, ICE_DBG_INIT, "GlobalR requested\n");
val = GLGEN_RTRIG_GLOBR_M;
break;
default:
return ICE_ERR_PARAM;
}
val |= rd32(hw, GLGEN_RTRIG);

View File

@ -32,6 +32,7 @@ static const struct net_device_ops ice_netdev_ops;
static void ice_pf_dis_all_vsi(struct ice_pf *pf);
static void ice_rebuild(struct ice_pf *pf);
static int ice_vsi_release(struct ice_vsi *vsi);
static void ice_vsi_release_all(struct ice_pf *pf);
static void ice_update_vsi_stats(struct ice_vsi *vsi);
static void ice_update_pf_stats(struct ice_pf *pf);
@ -456,23 +457,13 @@ static void
ice_prepare_for_reset(struct ice_pf *pf)
{
struct ice_hw *hw = &pf->hw;
u32 v;
ice_for_each_vsi(pf, v)
if (pf->vsi[v])
ice_remove_vsi_fltr(hw, pf->vsi[v]->vsi_num);
dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n");
/* disable the VSIs and their queues that are not already DOWN */
/* pf_dis_all_vsi modifies netdev structures -rtnl_lock needed */
ice_pf_dis_all_vsi(pf);
ice_for_each_vsi(pf, v)
if (pf->vsi[v])
pf->vsi[v]->vsi_num = 0;
ice_shutdown_all_ctrlq(hw);
set_bit(__ICE_PREPARED_FOR_RESET, pf->state);
}
/**
@ -490,26 +481,32 @@ static void ice_do_reset(struct ice_pf *pf, enum ice_reset_req reset_type)
WARN_ON(in_interrupt());
/* PFR is a bit of a special case because it doesn't result in an OICR
* interrupt. So for PFR, we prepare for reset, issue the reset and
* rebuild sequentially.
* interrupt. Set pending bit here which otherwise gets set in the
* OICR handler.
*/
if (reset_type == ICE_RESET_PFR) {
if (reset_type == ICE_RESET_PFR)
set_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
ice_prepare_for_reset(pf);
}
ice_prepare_for_reset(pf);
/* trigger the reset */
if (ice_reset(hw, reset_type)) {
dev_err(dev, "reset %d failed\n", reset_type);
set_bit(__ICE_RESET_FAILED, pf->state);
clear_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
clear_bit(__ICE_PREPARED_FOR_RESET, pf->state);
return;
}
/* PFR is a bit of a special case because it doesn't result in an OICR
* interrupt. So for PFR, rebuild after the reset and clear the reset-
* associated state bits.
*/
if (reset_type == ICE_RESET_PFR) {
pf->pfr_count++;
ice_rebuild(pf);
clear_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
clear_bit(__ICE_PREPARED_FOR_RESET, pf->state);
}
}
@ -519,20 +516,23 @@ static void ice_do_reset(struct ice_pf *pf, enum ice_reset_req reset_type)
*/
static void ice_reset_subtask(struct ice_pf *pf)
{
enum ice_reset_req reset_type;
rtnl_lock();
enum ice_reset_req reset_type = ICE_RESET_INVAL;
/* When a CORER/GLOBR/EMPR is about to happen, the hardware triggers an
* OICR interrupt. The OICR handler (ice_misc_intr) determines what
* type of reset happened and sets __ICE_RESET_RECOVERY_PENDING bit in
* pf->state. So if reset/recovery is pending (as indicated by this bit)
* we do a rebuild and return.
* OICR interrupt. The OICR handler (ice_misc_intr) determines what type
* of reset is pending and sets bits in pf->state indicating the reset
* type and __ICE_RESET_RECOVERY_PENDING. So, if the latter bit is set
* prepare for pending reset if not already (for PF software-initiated
* global resets the software should already be prepared for it as
* indicated by __ICE_PREPARED_FOR_RESET; for global resets initiated
* by firmware or software on other PFs, that bit is not set so prepare
* for the reset now), poll for reset done, rebuild and return.
*/
if (ice_is_reset_recovery_pending(pf->state)) {
clear_bit(__ICE_GLOBR_RECV, pf->state);
clear_bit(__ICE_CORER_RECV, pf->state);
ice_prepare_for_reset(pf);
if (!test_bit(__ICE_PREPARED_FOR_RESET, pf->state))
ice_prepare_for_reset(pf);
/* make sure we are ready to rebuild */
if (ice_check_reset(&pf->hw)) {
@ -541,29 +541,32 @@ static void ice_reset_subtask(struct ice_pf *pf)
/* done with reset. start rebuild */
pf->hw.reset_ongoing = false;
ice_rebuild(pf);
/* clear bit to resume normal operations, but
* ICE_NEEDS_RESTART bit is set incase rebuild failed
*/
clear_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
clear_bit(__ICE_PREPARED_FOR_RESET, pf->state);
}
clear_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
goto unlock;
return;
}
/* No pending resets to finish processing. Check for new resets */
if (test_and_clear_bit(__ICE_PFR_REQ, pf->state))
reset_type = ICE_RESET_PFR;
if (test_and_clear_bit(__ICE_CORER_REQ, pf->state))
reset_type = ICE_RESET_CORER;
if (test_and_clear_bit(__ICE_GLOBR_REQ, pf->state))
reset_type = ICE_RESET_GLOBR;
else if (test_and_clear_bit(__ICE_CORER_REQ, pf->state))
reset_type = ICE_RESET_CORER;
else if (test_and_clear_bit(__ICE_PFR_REQ, pf->state))
reset_type = ICE_RESET_PFR;
else
goto unlock;
/* If no valid reset type requested just return */
if (reset_type == ICE_RESET_INVAL)
return;
/* reset if not already down or resetting */
/* reset if not already down or busy */
if (!test_bit(__ICE_DOWN, pf->state) &&
!test_bit(__ICE_CFG_BUSY, pf->state)) {
ice_do_reset(pf, reset_type);
}
unlock:
rtnl_unlock();
}
/**
@ -970,7 +973,8 @@ static void ice_clean_adminq_subtask(struct ice_pf *pf)
static void ice_service_task_schedule(struct ice_pf *pf)
{
if (!test_bit(__ICE_DOWN, pf->state) &&
!test_and_set_bit(__ICE_SERVICE_SCHED, pf->state))
!test_and_set_bit(__ICE_SERVICE_SCHED, pf->state) &&
!test_bit(__ICE_NEEDS_RESTART, pf->state))
queue_work(ice_wq, &pf->serv_task);
}
@ -1013,9 +1017,10 @@ static void ice_service_task(struct work_struct *work)
/* process reset requests first */
ice_reset_subtask(pf);
/* bail if a reset/recovery cycle is pending */
/* bail if a reset/recovery cycle is pending or rebuild failed */
if (ice_is_reset_recovery_pending(pf->state) ||
test_bit(__ICE_SUSPENDED, pf->state)) {
test_bit(__ICE_SUSPENDED, pf->state) ||
test_bit(__ICE_NEEDS_RESTART, pf->state)) {
ice_service_task_complete(pf);
return;
}
@ -1160,7 +1165,7 @@ static void ice_vsi_delete(struct ice_vsi *vsi)
memcpy(&ctxt.info, &vsi->info, sizeof(struct ice_aqc_vsi_props));
status = ice_aq_free_vsi(&pf->hw, &ctxt, false, NULL);
status = ice_free_vsi(&pf->hw, vsi->idx, &ctxt, false, NULL);
if (status)
dev_err(&pf->pdev->dev, "Failed to delete VSI %i in FW\n",
vsi->vsi_num);
@ -1423,13 +1428,13 @@ static void ice_set_rss_vsi_ctx(struct ice_vsi_ctx *ctxt, struct ice_vsi *vsi)
}
/**
* ice_vsi_add - Create a new VSI or fetch preallocated VSI
* ice_vsi_init - Create and initialize a VSI
* @vsi: the VSI being configured
*
* This initializes a VSI context depending on the VSI type to be added and
* passes it down to the add_vsi aq command to create a new VSI.
*/
static int ice_vsi_add(struct ice_vsi *vsi)
static int ice_vsi_init(struct ice_vsi *vsi)
{
struct ice_vsi_ctx ctxt = { 0 };
struct ice_pf *pf = vsi->back;
@ -1456,13 +1461,17 @@ static int ice_vsi_add(struct ice_vsi *vsi)
ctxt.info.sw_id = vsi->port_info->sw_id;
ice_vsi_setup_q_map(vsi, &ctxt);
ret = ice_aq_add_vsi(hw, &ctxt, NULL);
ret = ice_add_vsi(hw, vsi->idx, &ctxt, NULL);
if (ret) {
dev_err(&vsi->back->pdev->dev,
"Add VSI AQ call failed, err %d\n", ret);
dev_err(&pf->pdev->dev,
"Add VSI failed, err %d\n", ret);
return -EIO;
}
/* keep context for update VSI operations */
vsi->info = ctxt.info;
/* record VSI number returned */
vsi->vsi_num = ctxt.vsi_num;
return ret;
@ -2652,14 +2661,12 @@ ice_vsi_cfg_rss_exit:
}
/**
* ice_vsi_reinit_setup - return resource and reallocate resource for a VSI
* @vsi: pointer to the ice_vsi
*
* This reallocates the VSIs queue resources
* ice_vsi_rebuild - Rebuild VSI after reset
* @vsi: vsi to be rebuild
*
* Returns 0 on success and negative value on failure
*/
static int ice_vsi_reinit_setup(struct ice_vsi *vsi)
static int ice_vsi_rebuild(struct ice_vsi *vsi)
{
u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 };
int ret, i;
@ -2675,7 +2682,7 @@ static int ice_vsi_reinit_setup(struct ice_vsi *vsi)
ice_vsi_set_num_qs(vsi);
/* Initialize VSI struct elements and create VSI in FW */
ret = ice_vsi_add(vsi);
ret = ice_vsi_init(vsi);
if (ret < 0)
goto err_vsi;
@ -2685,19 +2692,7 @@ static int ice_vsi_reinit_setup(struct ice_vsi *vsi)
switch (vsi->type) {
case ICE_VSI_PF:
if (!vsi->netdev) {
ret = ice_cfg_netdev(vsi);
if (ret)
goto err_rings;
ret = register_netdev(vsi->netdev);
if (ret)
goto err_rings;
netif_carrier_off(vsi->netdev);
netif_tx_stop_all_queues(vsi->netdev);
}
/* fall through */
ret = ice_vsi_alloc_q_vectors(vsi);
if (ret)
goto err_rings;
@ -2749,21 +2744,23 @@ err_vsi:
/**
* ice_vsi_setup - Set up a VSI by a given type
* @pf: board private structure
* @type: VSI type
* @pi: pointer to the port_info instance
* @type: VSI type
* @vf_id: defines VF id to which this VSI connects. This field is meant to be
* used only for ICE_VSI_VF VSI type. For other VSI types, should
* fill-in ICE_INVAL_VFID as input.
*
* This allocates the sw VSI structure and its queue resources.
*
* Returns pointer to the successfully allocated and configure VSI sw struct on
* success, otherwise returns NULL on failure.
* Returns pointer to the successfully allocated and configured VSI sw struct on
* success, NULL on failure.
*/
static struct ice_vsi *
ice_vsi_setup(struct ice_pf *pf, enum ice_vsi_type type,
struct ice_port_info *pi)
ice_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi,
enum ice_vsi_type type, u16 __always_unused vf_id)
{
u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 };
struct device *dev = &pf->pdev->dev;
struct ice_vsi_ctx ctxt = { 0 };
struct ice_vsi *vsi;
int ret, i;
@ -2786,12 +2783,10 @@ ice_vsi_setup(struct ice_pf *pf, enum ice_vsi_type type,
ice_vsi_set_rss_params(vsi);
/* create the VSI */
ret = ice_vsi_add(vsi);
ret = ice_vsi_init(vsi);
if (ret)
goto err_vsi;
ctxt.vsi_num = vsi->vsi_num;
switch (vsi->type) {
case ICE_VSI_PF:
ret = ice_cfg_netdev(vsi);
@ -2860,10 +2855,7 @@ err_register_netdev:
vsi->netdev = NULL;
}
err_cfg_netdev:
ret = ice_aq_free_vsi(&pf->hw, &ctxt, false, NULL);
if (ret)
dev_err(&vsi->back->pdev->dev,
"Free VSI AQ call failed, err %d\n", ret);
ice_vsi_delete(vsi);
err_vsi:
ice_vsi_put_qs(vsi);
err_get_qs:
@ -2874,6 +2866,20 @@ err_get_qs:
return NULL;
}
/**
* ice_pf_vsi_setup - Set up a PF VSI
* @pf: board private structure
* @pi: pointer to the port_info instance
*
* Returns pointer to the successfully allocated VSI sw struct on success,
* otherwise returns NULL on failure.
*/
static struct ice_vsi *
ice_pf_vsi_setup(struct ice_pf *pf, struct ice_port_info *pi)
{
return ice_vsi_setup(pf, pi, ICE_VSI_PF, ICE_INVAL_VFID);
}
/**
* ice_vsi_add_vlan - Add vsi membership for given vlan
* @vsi: the vsi being configured
@ -3021,50 +3027,48 @@ static int ice_setup_pf_sw(struct ice_pf *pf)
struct ice_vsi *vsi;
int status = 0;
if (!ice_is_reset_recovery_pending(pf->state)) {
vsi = ice_vsi_setup(pf, ICE_VSI_PF, pf->hw.port_info);
if (!vsi) {
status = -ENOMEM;
goto error_exit;
}
} else {
vsi = pf->vsi[0];
status = ice_vsi_reinit_setup(vsi);
if (status < 0)
return -EIO;
if (ice_is_reset_recovery_pending(pf->state))
return -EBUSY;
vsi = ice_pf_vsi_setup(pf, pf->hw.port_info);
if (!vsi) {
status = -ENOMEM;
goto unroll_vsi_setup;
}
/* tmp_add_list contains a list of MAC addresses for which MAC
* filters need to be programmed. Add the VSI's unicast MAC to
* this list
/* To add a MAC filter, first add the MAC to a list and then
* pass the list to ice_add_mac.
*/
/* Add a unicast MAC filter so the VSI can get its packets */
status = ice_add_mac_to_list(vsi, &tmp_add_list,
vsi->port_info->mac.perm_addr);
if (status)
goto error_exit;
goto unroll_vsi_setup;
/* VSI needs to receive broadcast traffic, so add the broadcast
* MAC address to the list.
* MAC address to the list as well.
*/
eth_broadcast_addr(broadcast);
status = ice_add_mac_to_list(vsi, &tmp_add_list, broadcast);
if (status)
goto error_exit;
goto free_mac_list;
/* program MAC filters for entries in tmp_add_list */
status = ice_add_mac(&pf->hw, &tmp_add_list);
if (status) {
dev_err(&pf->pdev->dev, "Could not add MAC filters\n");
status = -ENOMEM;
goto error_exit;
goto free_mac_list;
}
ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
return status;
error_exit:
free_mac_list:
ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
unroll_vsi_setup:
if (vsi) {
ice_vsi_free_q_vectors(vsi);
if (vsi->netdev && vsi->netdev->reg_state == NETREG_REGISTERED)
@ -3453,24 +3457,13 @@ err_exit_unroll:
static void ice_remove(struct pci_dev *pdev)
{
struct ice_pf *pf = pci_get_drvdata(pdev);
int i = 0;
int err;
if (!pf)
return;
set_bit(__ICE_DOWN, pf->state);
for (i = 0; i < pf->num_alloc_vsi; i++) {
if (!pf->vsi[i])
continue;
err = ice_vsi_release(pf->vsi[i]);
if (err)
dev_dbg(&pf->pdev->dev, "Failed to release VSI index %d (err %d)\n",
i, err);
}
ice_vsi_release_all(pf);
ice_free_irq_msix_misc(pf);
ice_clear_interrupt_scheme(pf);
ice_deinit_pf(pf);
@ -3517,7 +3510,7 @@ static int __init ice_module_init(void)
pr_info("%s - version %s\n", ice_driver_string, ice_drv_ver);
pr_info("%s\n", ice_copyright);
ice_wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, KBUILD_MODNAME);
ice_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
if (!ice_wq) {
pr_err("Failed to create workqueue\n");
return -ENOMEM;
@ -5104,8 +5097,14 @@ static int ice_vsi_release(struct ice_vsi *vsi)
if (!vsi->back)
return -ENODEV;
pf = vsi->back;
if (vsi->netdev) {
/* do not unregister and free netdevs while driver is in the reset
* recovery pending state. Since reset/rebuild happens through PF
* service task workqueue, its not a good idea to unregister netdev
* that is associated to the PF that is running the work queue items
* currently. This is done to avoid check_flush_dependency() warning
* on this wq
*/
if (vsi->netdev && !ice_is_reset_recovery_pending(pf->state)) {
unregister_netdev(vsi->netdev);
free_netdev(vsi->netdev);
vsi->netdev = NULL;
@ -5131,11 +5130,39 @@ static int ice_vsi_release(struct ice_vsi *vsi)
pf->q_left_tx += vsi->alloc_txq;
pf->q_left_rx += vsi->alloc_rxq;
ice_vsi_clear(vsi);
/* retain SW VSI data structure since it is needed to unregister and
* free VSI netdev when PF is not in reset recovery pending state,\
* for ex: during rmmod.
*/
if (!ice_is_reset_recovery_pending(pf->state))
ice_vsi_clear(vsi);
return 0;
}
/**
* ice_vsi_release_all - Delete all VSIs
* @pf: PF from which all VSIs are being removed
*/
static void ice_vsi_release_all(struct ice_pf *pf)
{
int err, i;
if (!pf->vsi)
return;
for (i = 0; i < pf->num_alloc_vsi; i++) {
if (!pf->vsi[i])
continue;
err = ice_vsi_release(pf->vsi[i]);
if (err)
dev_dbg(&pf->pdev->dev,
"Failed to release pf->vsi[%d], err %d, vsi_num = %d\n",
i, err, pf->vsi[i]->vsi_num);
}
}
/**
* ice_dis_vsi - pause a VSI
* @vsi: the VSI being paused
@ -5148,27 +5175,31 @@ static void ice_dis_vsi(struct ice_vsi *vsi)
set_bit(__ICE_NEEDS_RESTART, vsi->state);
if (vsi->netdev && netif_running(vsi->netdev) &&
vsi->type == ICE_VSI_PF)
vsi->type == ICE_VSI_PF) {
rtnl_lock();
vsi->netdev->netdev_ops->ndo_stop(vsi->netdev);
ice_vsi_close(vsi);
rtnl_unlock();
} else {
ice_vsi_close(vsi);
}
}
/**
* ice_ena_vsi - resume a VSI
* @vsi: the VSI being resume
*/
static void ice_ena_vsi(struct ice_vsi *vsi)
static int ice_ena_vsi(struct ice_vsi *vsi)
{
if (!test_and_clear_bit(__ICE_NEEDS_RESTART, vsi->state))
return;
int err = 0;
if (vsi->netdev && netif_running(vsi->netdev))
vsi->netdev->netdev_ops->ndo_open(vsi->netdev);
else if (ice_vsi_open(vsi))
/* this clears the DOWN bit */
dev_dbg(&vsi->back->pdev->dev, "Failed open VSI 0x%04X on switch 0x%04X\n",
vsi->vsi_num, vsi->vsw->sw_id);
if (test_and_clear_bit(__ICE_NEEDS_RESTART, vsi->state))
if (vsi->netdev && netif_running(vsi->netdev)) {
rtnl_lock();
err = vsi->netdev->netdev_ops->ndo_open(vsi->netdev);
rtnl_unlock();
}
return err;
}
/**
@ -5188,13 +5219,47 @@ static void ice_pf_dis_all_vsi(struct ice_pf *pf)
* ice_pf_ena_all_vsi - Resume all VSIs on a PF
* @pf: the PF
*/
static void ice_pf_ena_all_vsi(struct ice_pf *pf)
static int ice_pf_ena_all_vsi(struct ice_pf *pf)
{
int v;
ice_for_each_vsi(pf, v)
if (pf->vsi[v])
ice_ena_vsi(pf->vsi[v]);
if (ice_ena_vsi(pf->vsi[v]))
return -EIO;
return 0;
}
/**
* ice_vsi_rebuild_all - rebuild all VSIs in pf
* @pf: the PF
*/
static int ice_vsi_rebuild_all(struct ice_pf *pf)
{
int i;
/* loop through pf->vsi array and reinit the VSI if found */
for (i = 0; i < pf->num_alloc_vsi; i++) {
int err;
if (!pf->vsi[i])
continue;
err = ice_vsi_rebuild(pf->vsi[i]);
if (err) {
dev_err(&pf->pdev->dev,
"VSI at index %d rebuild failed\n",
pf->vsi[i]->idx);
return err;
}
dev_info(&pf->pdev->dev,
"VSI at index %d rebuilt. vsi_num = 0x%x\n",
pf->vsi[i]->idx, pf->vsi[i]->vsi_num);
}
return 0;
}
/**
@ -5216,13 +5281,13 @@ static void ice_rebuild(struct ice_pf *pf)
ret = ice_init_all_ctrlq(hw);
if (ret) {
dev_err(dev, "control queues init failed %d\n", ret);
goto fail_reset;
goto err_init_ctrlq;
}
ret = ice_clear_pf_cfg(hw);
if (ret) {
dev_err(dev, "clear PF configuration failed %d\n", ret);
goto fail_reset;
goto err_init_ctrlq;
}
ice_clear_pxe_mode(hw);
@ -5230,14 +5295,24 @@ static void ice_rebuild(struct ice_pf *pf)
ret = ice_get_caps(hw);
if (ret) {
dev_err(dev, "ice_get_caps failed %d\n", ret);
goto fail_reset;
goto err_init_ctrlq;
}
/* basic nic switch setup */
err = ice_setup_pf_sw(pf);
err = ice_sched_init_port(hw->port_info);
if (err)
goto err_sched_init_port;
err = ice_vsi_rebuild_all(pf);
if (err) {
dev_err(dev, "ice_setup_pf_sw failed\n");
goto fail_reset;
dev_err(dev, "ice_vsi_rebuild_all failed\n");
goto err_vsi_rebuild;
}
ret = ice_replay_all_fltr(&pf->hw);
if (ret) {
dev_err(&pf->pdev->dev,
"error replaying switch filter rules\n");
goto err_vsi_rebuild;
}
/* start misc vector */
@ -5245,20 +5320,35 @@ static void ice_rebuild(struct ice_pf *pf)
err = ice_req_irq_msix_misc(pf);
if (err) {
dev_err(dev, "misc vector setup failed: %d\n", err);
goto fail_reset;
goto err_vsi_rebuild;
}
}
/* restart the VSIs that were rebuilt and running before the reset */
ice_pf_ena_all_vsi(pf);
err = ice_pf_ena_all_vsi(pf);
if (err) {
dev_err(&pf->pdev->dev, "error enabling VSIs\n");
/* no need to disable VSIs in tear down path in ice_rebuild()
* since its already taken care in ice_vsi_open()
*/
goto err_vsi_rebuild;
}
/* if we get here, reset flow is successful */
clear_bit(__ICE_RESET_FAILED, pf->state);
return;
fail_reset:
err_vsi_rebuild:
ice_vsi_release_all(pf);
err_sched_init_port:
ice_sched_cleanup_all(hw);
err_init_ctrlq:
ice_shutdown_all_ctrlq(hw);
set_bit(__ICE_RESET_FAILED, pf->state);
clear_recovery:
set_bit(__ICE_RESET_RECOVERY_PENDING, pf->state);
/* set this bit in PF state to control service task scheduling */
set_bit(__ICE_NEEDS_RESTART, pf->state);
dev_err(dev, "Rebuild failed, unload and reload driver\n");
}
/**
@ -5431,6 +5521,11 @@ static int ice_open(struct net_device *netdev)
struct ice_vsi *vsi = np->vsi;
int err;
if (test_bit(__ICE_NEEDS_RESTART, vsi->back->state)) {
netdev_err(netdev, "driver needs to be unloaded and reloaded\n");
return -EIO;
}
netif_carrier_off(netdev);
err = ice_vsi_open(vsi);

View File

@ -169,17 +169,17 @@ ice_aq_get_sw_cfg(struct ice_hw *hw, struct ice_aqc_get_sw_cfg_resp *buf,
*
* Add a VSI context to the hardware (0x0210)
*/
enum ice_status
static enum ice_status
ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
struct ice_sq_cd *cd)
{
struct ice_aqc_add_update_free_vsi_resp *res;
struct ice_aqc_add_get_update_free_vsi *cmd;
enum ice_status status;
struct ice_aq_desc desc;
enum ice_status status;
cmd = &desc.params.vsi_cmd;
res = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
res = &desc.params.add_update_free_vsi_res;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_add_vsi);
@ -203,6 +203,42 @@ ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
return status;
}
/**
* ice_aq_free_vsi
* @hw: pointer to the hw struct
* @vsi_ctx: pointer to a VSI context struct
* @keep_vsi_alloc: keep VSI allocation as part of this PF's resources
* @cd: pointer to command details structure or NULL
*
* Free VSI context info from hardware (0x0213)
*/
static enum ice_status
ice_aq_free_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
bool keep_vsi_alloc, struct ice_sq_cd *cd)
{
struct ice_aqc_add_update_free_vsi_resp *resp;
struct ice_aqc_add_get_update_free_vsi *cmd;
struct ice_aq_desc desc;
enum ice_status status;
cmd = &desc.params.vsi_cmd;
resp = &desc.params.add_update_free_vsi_res;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_free_vsi);
cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID);
if (keep_vsi_alloc)
cmd->cmd_flags = cpu_to_le16(ICE_AQ_VSI_KEEP_ALLOC);
status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
if (!status) {
vsi_ctx->vsis_allocd = le16_to_cpu(resp->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
}
return status;
}
/**
* ice_aq_update_vsi
* @hw: pointer to the hw struct
@ -221,7 +257,7 @@ ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
enum ice_status status;
cmd = &desc.params.vsi_cmd;
resp = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
resp = &desc.params.add_update_free_vsi_res;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_update_vsi);
@ -241,38 +277,202 @@ ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
}
/**
* ice_aq_free_vsi
* ice_update_fltr_vsi_map - update given filter VSI map
* @list_head: list for which filters needs to be updated
* @list_lock: filter lock which needs to be updated
* @old_vsi_num: old VSI HW id
* @new_vsi_num: new VSI HW id
*
* update the VSI map for a given filter list
*/
static void
ice_update_fltr_vsi_map(struct list_head *list_head,
struct mutex *list_lock, u16 old_vsi_num,
u16 new_vsi_num)
{
struct ice_fltr_mgmt_list_entry *itr;
mutex_lock(list_lock);
if (list_empty(list_head))
goto exit_update_map;
list_for_each_entry(itr, list_head, list_entry) {
if (itr->vsi_list_info &&
test_bit(old_vsi_num, itr->vsi_list_info->vsi_map)) {
clear_bit(old_vsi_num, itr->vsi_list_info->vsi_map);
set_bit(new_vsi_num, itr->vsi_list_info->vsi_map);
} else if (itr->fltr_info.fltr_act == ICE_FWD_TO_VSI &&
itr->fltr_info.fwd_id.vsi_id == old_vsi_num) {
itr->fltr_info.fwd_id.vsi_id = new_vsi_num;
itr->fltr_info.src = new_vsi_num;
}
}
exit_update_map:
mutex_unlock(list_lock);
}
/**
* ice_update_all_fltr_vsi_map - update all filters VSI map
* @hw: pointer to the hardware structure
* @old_vsi_num: old VSI HW id
* @new_vsi_num: new VSI HW id
*
* update all filters VSI map
*/
static void
ice_update_all_fltr_vsi_map(struct ice_hw *hw, u16 old_vsi_num, u16 new_vsi_num)
{
struct ice_switch_info *sw = hw->switch_info;
u8 i;
for (i = 0; i < ICE_SW_LKUP_LAST; i++) {
struct list_head *head = &sw->recp_list[i].filt_rules;
struct mutex *lock; /* Lock to protect filter rule list */
lock = &sw->recp_list[i].filt_rule_lock;
ice_update_fltr_vsi_map(head, lock, old_vsi_num,
new_vsi_num);
}
}
/**
* ice_is_vsi_valid - check whether the VSI is valid or not
* @hw: pointer to the hw struct
* @vsi_handle: VSI handle
*
* check whether the VSI is valid or not
*/
static bool ice_is_vsi_valid(struct ice_hw *hw, u16 vsi_handle)
{
return vsi_handle < ICE_MAX_VSI && hw->vsi_ctx[vsi_handle];
}
/**
* ice_get_hw_vsi_num - return the hw VSI number
* @hw: pointer to the hw struct
* @vsi_handle: VSI handle
*
* return the hw VSI number
* Caution: call this function only if VSI is valid (ice_is_vsi_valid)
*/
static u16 ice_get_hw_vsi_num(struct ice_hw *hw, u16 vsi_handle)
{
return hw->vsi_ctx[vsi_handle]->vsi_num;
}
/**
* ice_get_vsi_ctx - return the VSI context entry for a given VSI handle
* @hw: pointer to the hw struct
* @vsi_handle: VSI handle
*
* return the VSI context entry for a given VSI handle
*/
static struct ice_vsi_ctx *ice_get_vsi_ctx(struct ice_hw *hw, u16 vsi_handle)
{
return (vsi_handle >= ICE_MAX_VSI) ? NULL : hw->vsi_ctx[vsi_handle];
}
/**
* ice_save_vsi_ctx - save the VSI context for a given VSI handle
* @hw: pointer to the hw struct
* @vsi_handle: VSI handle
* @vsi: VSI context pointer
*
* save the VSI context entry for a given VSI handle
*/
static void ice_save_vsi_ctx(struct ice_hw *hw, u16 vsi_handle,
struct ice_vsi_ctx *vsi)
{
hw->vsi_ctx[vsi_handle] = vsi;
}
/**
* ice_clear_vsi_ctx - clear the VSI context entry
* @hw: pointer to the hw struct
* @vsi_handle: VSI handle
*
* clear the VSI context entry
*/
static void ice_clear_vsi_ctx(struct ice_hw *hw, u16 vsi_handle)
{
struct ice_vsi_ctx *vsi;
vsi = ice_get_vsi_ctx(hw, vsi_handle);
if (vsi) {
devm_kfree(ice_hw_to_dev(hw), vsi);
hw->vsi_ctx[vsi_handle] = NULL;
}
}
/**
* ice_add_vsi - add VSI context to the hardware and VSI handle list
* @hw: pointer to the hw struct
* @vsi_handle: unique VSI handle provided by drivers
* @vsi_ctx: pointer to a VSI context struct
* @cd: pointer to command details structure or NULL
*
* Add a VSI context to the hardware also add it into the VSI handle list.
* If this function gets called after reset for existing VSIs then update
* with the new HW VSI number in the corresponding VSI handle list entry.
*/
enum ice_status
ice_add_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx,
struct ice_sq_cd *cd)
{
struct ice_vsi_ctx *tmp_vsi_ctx;
enum ice_status status;
if (vsi_handle >= ICE_MAX_VSI)
return ICE_ERR_PARAM;
status = ice_aq_add_vsi(hw, vsi_ctx, cd);
if (status)
return status;
tmp_vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
if (!tmp_vsi_ctx) {
/* Create a new vsi context */
tmp_vsi_ctx = devm_kzalloc(ice_hw_to_dev(hw),
sizeof(*tmp_vsi_ctx), GFP_KERNEL);
if (!tmp_vsi_ctx) {
ice_aq_free_vsi(hw, vsi_ctx, false, cd);
return ICE_ERR_NO_MEMORY;
}
*tmp_vsi_ctx = *vsi_ctx;
ice_save_vsi_ctx(hw, vsi_handle, tmp_vsi_ctx);
} else {
/* update with new HW VSI num */
if (tmp_vsi_ctx->vsi_num != vsi_ctx->vsi_num) {
/* update all filter lists with new HW VSI num */
ice_update_all_fltr_vsi_map(hw, tmp_vsi_ctx->vsi_num,
vsi_ctx->vsi_num);
tmp_vsi_ctx->vsi_num = vsi_ctx->vsi_num;
}
}
return status;
}
/**
* ice_free_vsi- free VSI context from hardware and VSI handle list
* @hw: pointer to the hw struct
* @vsi_handle: unique VSI handle
* @vsi_ctx: pointer to a VSI context struct
* @keep_vsi_alloc: keep VSI allocation as part of this PF's resources
* @cd: pointer to command details structure or NULL
*
* Get VSI context info from hardware (0x0213)
* Free VSI context info from hardware as well as from VSI handle list
*/
enum ice_status
ice_aq_free_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
bool keep_vsi_alloc, struct ice_sq_cd *cd)
ice_free_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx,
bool keep_vsi_alloc, struct ice_sq_cd *cd)
{
struct ice_aqc_add_update_free_vsi_resp *resp;
struct ice_aqc_add_get_update_free_vsi *cmd;
struct ice_aq_desc desc;
enum ice_status status;
cmd = &desc.params.vsi_cmd;
resp = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_free_vsi);
cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID);
if (keep_vsi_alloc)
cmd->cmd_flags = cpu_to_le16(ICE_AQ_VSI_KEEP_ALLOC);
status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
if (!status) {
vsi_ctx->vsis_allocd = le16_to_cpu(resp->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
}
if (!ice_is_vsi_valid(hw, vsi_handle))
return ICE_ERR_PARAM;
vsi_ctx->vsi_num = ice_get_hw_vsi_num(hw, vsi_handle);
status = ice_aq_free_vsi(hw, vsi_ctx, keep_vsi_alloc, cd);
if (!status)
ice_clear_vsi_ctx(hw, vsi_handle);
return status;
}
@ -1516,6 +1716,25 @@ ice_add_vlan(struct ice_hw *hw, struct list_head *v_list)
return 0;
}
/**
* ice_rem_sw_rule_info
* @hw: pointer to the hardware structure
* @rule_head: pointer to the switch list structure that we want to delete
*/
static void
ice_rem_sw_rule_info(struct ice_hw *hw, struct list_head *rule_head)
{
if (!list_empty(rule_head)) {
struct ice_fltr_mgmt_list_entry *entry;
struct ice_fltr_mgmt_list_entry *tmp;
list_for_each_entry_safe(entry, tmp, rule_head, list_entry) {
list_del(&entry->list_entry);
devm_kfree(ice_hw_to_dev(hw), entry);
}
}
}
/**
* ice_cfg_dflt_vsi - change state of VSI to set/clear default
* @hw: pointer to the hardware structure
@ -1822,3 +2041,89 @@ void ice_remove_vsi_fltr(struct ice_hw *hw, u16 vsi_id)
ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_ETHERTYPE_MAC);
ice_remove_vsi_lkup_fltr(hw, vsi_id, ICE_SW_LKUP_PROMISC_VLAN);
}
/**
* ice_replay_fltr - Replay all the filters stored by a specific list head
* @hw: pointer to the hardware structure
* @list_head: list for which filters needs to be replayed
* @recp_id: Recipe id for which rules need to be replayed
*/
static enum ice_status
ice_replay_fltr(struct ice_hw *hw, u8 recp_id, struct list_head *list_head)
{
struct ice_fltr_mgmt_list_entry *itr;
struct list_head l_head;
enum ice_status status = 0;
if (list_empty(list_head))
return status;
/* Move entries from the given list_head to a temporary l_head so that
* they can be replayed. Otherwise when trying to re-add the same
* filter, the function will return already exists
*/
list_replace_init(list_head, &l_head);
/* Mark the given list_head empty by reinitializing it so filters
* could be added again by *handler
*/
list_for_each_entry(itr, &l_head, list_entry) {
struct ice_fltr_list_entry f_entry;
f_entry.fltr_info = itr->fltr_info;
if (itr->vsi_count < 2 && recp_id != ICE_SW_LKUP_VLAN) {
status = ice_add_rule_internal(hw, recp_id, &f_entry);
if (status)
goto end;
continue;
}
/* Add a filter per vsi separately */
while (1) {
u16 vsi;
vsi = find_first_bit(itr->vsi_list_info->vsi_map,
ICE_MAX_VSI);
if (vsi == ICE_MAX_VSI)
break;
clear_bit(vsi, itr->vsi_list_info->vsi_map);
f_entry.fltr_info.fwd_id.vsi_id = vsi;
f_entry.fltr_info.fltr_act = ICE_FWD_TO_VSI;
if (recp_id == ICE_SW_LKUP_VLAN)
status = ice_add_vlan_internal(hw, &f_entry);
else
status = ice_add_rule_internal(hw, recp_id,
&f_entry);
if (status)
goto end;
}
}
end:
/* Clear the filter management list */
ice_rem_sw_rule_info(hw, &l_head);
return status;
}
/**
* ice_replay_all_fltr - replay all filters stored in bookkeeping lists
* @hw: pointer to the hardware structure
*
* NOTE: This function does not clean up partially added filters on error.
* It is up to caller of the function to issue a reset or fail early.
*/
enum ice_status ice_replay_all_fltr(struct ice_hw *hw)
{
struct ice_switch_info *sw = hw->switch_info;
enum ice_status status = 0;
u8 i;
for (i = 0; i < ICE_SW_LKUP_LAST; i++) {
struct list_head *head = &sw->recp_list[i].filt_rules;
status = ice_replay_fltr(hw, i, head);
if (status)
return status;
}
return status;
}

View File

@ -158,15 +158,14 @@ struct ice_fltr_mgmt_list_entry {
/* VSI related commands */
enum ice_status
ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
struct ice_sq_cd *cd);
enum ice_status
ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
struct ice_sq_cd *cd);
enum ice_status
ice_aq_free_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
bool keep_vsi_alloc, struct ice_sq_cd *cd);
ice_add_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx,
struct ice_sq_cd *cd);
enum ice_status
ice_free_vsi(struct ice_hw *hw, u16 vsi_handle, struct ice_vsi_ctx *vsi_ctx,
bool keep_vsi_alloc, struct ice_sq_cd *cd);
enum ice_status ice_get_initial_sw_cfg(struct ice_hw *hw);
/* Switch/bridge related commands */
@ -177,6 +176,9 @@ enum ice_status ice_add_vlan(struct ice_hw *hw, struct list_head *m_list);
enum ice_status ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list);
enum ice_status
ice_cfg_dflt_vsi(struct ice_hw *hw, u16 vsi_id, bool set, u8 direction);
enum ice_status ice_replay_all_fltr(struct ice_hw *hw);
enum ice_status ice_init_def_sw_recp(struct ice_hw *hw);
#endif /* _ICE_SWITCH_H_ */

View File

@ -149,9 +149,10 @@ struct ice_mac_info {
/* Various RESET request, These are not tied with HW reset types */
enum ice_reset_req {
ICE_RESET_PFR = 0,
ICE_RESET_CORER = 1,
ICE_RESET_GLOBR = 2,
ICE_RESET_INVAL = 0,
ICE_RESET_PFR = 1,
ICE_RESET_CORER = 2,
ICE_RESET_GLOBR = 3,
};
/* Bus parameters */
@ -283,6 +284,7 @@ struct ice_hw {
u8 sw_entry_point_layer;
u16 max_children[ICE_AQC_TOPO_MAX_LEVEL_NUM];
struct ice_vsi_ctx *vsi_ctx[ICE_MAX_VSI];
u8 evb_veb; /* true for VEB, false for VEPA */
u8 reset_ongoing; /* true if hw is in reset, false otherwise */
struct ice_bus_info bus;