On 26. 07. 23 22:43, Aleksandr Loktionov wrote:
Fix livelocks by acquiring rtnl_lock() before any reset related operations.
Add rtnl_lock()/rtnl_unlock() at top/bottom of i40e_reset_subtask()
Add check for __I40E_RESET_INTR_RECEIVED bit.
Add re-entry guard by fast return in case reset is already in progress.
Fixes: 373149fc99a0 ("i40e: Decrease the scope of rtnl lock")
Signed-off-by: Aleksandr Loktionov <aleksandr.loktio...@intel.com>
---
v3->v4 fix | position in the code
v2->v3 fix typo in the commit message
v1->v2 * apply on
https://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue.git
---
---
drivers/net/ethernet/intel/i40e/i40e_main.c | 39 +++++++++++----------
1 file changed, 20 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c
b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 7c30abd..164f7c6 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -10011,42 +10011,44 @@ static void i40e_reset_subtask(struct i40e_pf *pf)
{
u32 reset_flags = 0;
- if (test_bit(__I40E_REINIT_REQUESTED, pf->state)) {
+ if (test_and_clear_bit(__I40E_REINIT_REQUESTED, pf->state))
reset_flags |= BIT(__I40E_REINIT_REQUESTED);
- clear_bit(__I40E_REINIT_REQUESTED, pf->state);
- }
- if (test_bit(__I40E_PF_RESET_REQUESTED, pf->state)) {
+ if (test_and_clear_bit(__I40E_PF_RESET_REQUESTED, pf->state))
reset_flags |= BIT(__I40E_PF_RESET_REQUESTED);
- clear_bit(__I40E_PF_RESET_REQUESTED, pf->state);
- }
- if (test_bit(__I40E_CORE_RESET_REQUESTED, pf->state)) {
+ if (test_and_clear_bit(__I40E_CORE_RESET_REQUESTED, pf->state))
reset_flags |= BIT(__I40E_CORE_RESET_REQUESTED);
- clear_bit(__I40E_CORE_RESET_REQUESTED, pf->state);
- }
- if (test_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state)) {
+ if (test_and_clear_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state))
reset_flags |= BIT(__I40E_GLOBAL_RESET_REQUESTED);
- clear_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state);
- }
- if (test_bit(__I40E_DOWN_REQUESTED, pf->state)) {
+ if (test_and_clear_bit(__I40E_DOWN_REQUESTED, pf->state))
reset_flags |= BIT(__I40E_DOWN_REQUESTED);
- clear_bit(__I40E_DOWN_REQUESTED, pf->state);
- }
+ if (test_and_clear_bit(__I40E_RESET_INTR_RECEIVED, pf->state))
+ reset_flags |= BIT(__I40E_RESET_INTR_RECEIVED);
+
+ if (!(reset_flags & (BIT(__I40E_PF_RESET_REQUESTED) |
+ BIT(__I40E_CORE_RESET_REQUESTED) |
+ BIT(__I40E_GLOBAL_RESET_REQUESTED) |
+ BIT(__I40E_RESET_INTR_RECEIVED))))
+ return;
+
+ rtnl_lock();
/* If there's a recovery already waiting, it takes
* precedence before starting a new reset sequence.
*/
- if (test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) {
+ if (reset_flags & BIT(__I40E_RESET_INTR_RECEIVED)) {
i40e_prep_for_reset(pf);
i40e_reset(pf);
- i40e_rebuild(pf, false, false);
+ i40e_rebuild(pf, false, true);
}
/* If we're already down or resetting, just bail */
if (reset_flags &&
!test_bit(__I40E_DOWN, pf->state) &&
!test_bit(__I40E_CONFIG_BUSY, pf->state)) {
- i40e_do_reset(pf, reset_flags, false);
+ i40e_do_reset(pf, reset_flags, true);
}
+
+ rtnl_unlock();
}
/**
@@ -10694,7 +10696,6 @@ static void i40e_prep_for_reset(struct i40e_pf *pf)
int ret = 0;
u32 v;
- clear_bit(__I40E_RESET_INTR_RECEIVED, pf->state);
if (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, pf->state))
return;
if (i40e_check_asq_alive(&pf->hw))
Tony, why this is marked in IWL patchwork as ChangesRequested? There
were no comments and the patch disappeared from dev-queue. Are you
planning to process it?
Thanks,
Ivan