i40e: Fix VF hang when reset is triggered on another VF
authorSylwester Dziedziuch <sylwesterx.dziedziuch@intel.com>
Mon, 24 Oct 2022 10:05:25 +0000 (03:05 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 25 Oct 2022 23:18:31 +0000 (16:18 -0700)
When a reset was triggered on one VF with i40e_reset_vf
global PF state __I40E_VF_DISABLE was set on a PF until
the reset finished. If immediately after triggering reset
on one VF there is a request to reset on another
it will cause a hang on VF side because VF will be notified
of incoming reset but the reset will never happen because
of this global state, we will get such error message:

[  +4.890195] iavf 0000:86:02.1: Never saw reset

and VF will hang waiting for the reset to be triggered.

Fix this by introducing new VF state I40E_VF_STATE_RESETTING
that will be set on a VF if it is currently resetting instead of
the global __I40E_VF_DISABLE PF state.

Fixes: 3ba9bcb4b68f ("i40e: add locking around VF reset")
Signed-off-by: Sylwester Dziedziuch <sylwesterx.dziedziuch@intel.com>
Signed-off-by: Mateusz Palczewski <mateusz.palczewski@intel.com>
Tested-by: Konrad Jankowski <konrad0.jankowski@intel.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Link: https://lore.kernel.org/r/20221024100526.1874914-2-jacob.e.keller@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h

index 7e9f6a6..72ddcef 100644 (file)
@@ -1536,10 +1536,12 @@ bool i40e_reset_vf(struct i40e_vf *vf, bool flr)
        if (test_bit(__I40E_VF_RESETS_DISABLED, pf->state))
                return true;
 
-       /* If the VFs have been disabled, this means something else is
-        * resetting the VF, so we shouldn't continue.
-        */
-       if (test_and_set_bit(__I40E_VF_DISABLE, pf->state))
+       /* Bail out if VFs are disabled. */
+       if (test_bit(__I40E_VF_DISABLE, pf->state))
+               return true;
+
+       /* If VF is being reset already we don't need to continue. */
+       if (test_and_set_bit(I40E_VF_STATE_RESETTING, &vf->vf_states))
                return true;
 
        i40e_trigger_vf_reset(vf, flr);
@@ -1576,7 +1578,7 @@ bool i40e_reset_vf(struct i40e_vf *vf, bool flr)
        i40e_cleanup_reset_vf(vf);
 
        i40e_flush(hw);
-       clear_bit(__I40E_VF_DISABLE, pf->state);
+       clear_bit(I40E_VF_STATE_RESETTING, &vf->vf_states);
 
        return true;
 }
@@ -1609,8 +1611,12 @@ bool i40e_reset_all_vfs(struct i40e_pf *pf, bool flr)
                return false;
 
        /* Begin reset on all VFs at once */
-       for (v = 0; v < pf->num_alloc_vfs; v++)
-               i40e_trigger_vf_reset(&pf->vf[v], flr);
+       for (v = 0; v < pf->num_alloc_vfs; v++) {
+               vf = &pf->vf[v];
+               /* If VF is being reset no need to trigger reset again */
+               if (!test_bit(I40E_VF_STATE_RESETTING, &vf->vf_states))
+                       i40e_trigger_vf_reset(&pf->vf[v], flr);
+       }
 
        /* HW requires some time to make sure it can flush the FIFO for a VF
         * when it resets it. Poll the VPGEN_VFRSTAT register for each VF in
@@ -1626,9 +1632,11 @@ bool i40e_reset_all_vfs(struct i40e_pf *pf, bool flr)
                 */
                while (v < pf->num_alloc_vfs) {
                        vf = &pf->vf[v];
-                       reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id));
-                       if (!(reg & I40E_VPGEN_VFRSTAT_VFRD_MASK))
-                               break;
+                       if (!test_bit(I40E_VF_STATE_RESETTING, &vf->vf_states)) {
+                               reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id));
+                               if (!(reg & I40E_VPGEN_VFRSTAT_VFRD_MASK))
+                                       break;
+                       }
 
                        /* If the current VF has finished resetting, move on
                         * to the next VF in sequence.
@@ -1656,6 +1664,10 @@ bool i40e_reset_all_vfs(struct i40e_pf *pf, bool flr)
                if (pf->vf[v].lan_vsi_idx == 0)
                        continue;
 
+               /* If VF is reset in another thread just continue */
+               if (test_bit(I40E_VF_STATE_RESETTING, &vf->vf_states))
+                       continue;
+
                i40e_vsi_stop_rings_no_wait(pf->vsi[pf->vf[v].lan_vsi_idx]);
        }
 
@@ -1667,6 +1679,10 @@ bool i40e_reset_all_vfs(struct i40e_pf *pf, bool flr)
                if (pf->vf[v].lan_vsi_idx == 0)
                        continue;
 
+               /* If VF is reset in another thread just continue */
+               if (test_bit(I40E_VF_STATE_RESETTING, &vf->vf_states))
+                       continue;
+
                i40e_vsi_wait_queues_disabled(pf->vsi[pf->vf[v].lan_vsi_idx]);
        }
 
@@ -1676,8 +1692,13 @@ bool i40e_reset_all_vfs(struct i40e_pf *pf, bool flr)
        mdelay(50);
 
        /* Finish the reset on each VF */
-       for (v = 0; v < pf->num_alloc_vfs; v++)
+       for (v = 0; v < pf->num_alloc_vfs; v++) {
+               /* If VF is reset in another thread just continue */
+               if (test_bit(I40E_VF_STATE_RESETTING, &vf->vf_states))
+                       continue;
+
                i40e_cleanup_reset_vf(&pf->vf[v]);
+       }
 
        i40e_flush(hw);
        clear_bit(__I40E_VF_DISABLE, pf->state);
index a554d0a..358bbdb 100644 (file)
@@ -39,6 +39,7 @@ enum i40e_vf_states {
        I40E_VF_STATE_MC_PROMISC,
        I40E_VF_STATE_UC_PROMISC,
        I40E_VF_STATE_PRE_ENABLE,
+       I40E_VF_STATE_RESETTING
 };
 
 /* VF capabilities */