Skip to content

Commit

Permalink
tg3: Fix soft lockup when tg3_reset_task() fails.
Browse files Browse the repository at this point in the history
If tg3_reset_task() fails, the device state is left in an inconsistent
state with IFF_RUNNING still set but NAPI state not enabled.  A
subsequent operation, such as ifdown or AER error can cause it to
soft lock up when it tries to disable NAPI state.

Fix it by bringing down the device to !IFF_RUNNING state when
tg3_reset_task() fails.  tg3_reset_task() running from workqueue
will now call tg3_close() when the reset fails.  We need to
modify tg3_reset_task_cancel() slightly to avoid tg3_close()
calling cancel_work_sync() to cancel tg3_reset_task().  Otherwise
cancel_work_sync() will wait forever for tg3_reset_task() to
finish.

Reported-by: David Christensen <[email protected]>
Reported-by: Baptiste Covolato <[email protected]>
Fixes: db21997 ("tg3: Schedule at most one tg3_reset_task run")
Signed-off-by: Michael Chan <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Michael Chan authored and davem330 committed Sep 3, 2020
1 parent af0ae99 commit 5566993
Showing 1 changed file with 13 additions and 4 deletions.
17 changes: 13 additions & 4 deletions drivers/net/ethernet/broadcom/tg3.c
Original file line number Diff line number Diff line change
Expand Up @@ -7221,8 +7221,8 @@ static inline void tg3_reset_task_schedule(struct tg3 *tp)

static inline void tg3_reset_task_cancel(struct tg3 *tp)
{
cancel_work_sync(&tp->reset_task);
tg3_flag_clear(tp, RESET_TASK_PENDING);
if (test_and_clear_bit(TG3_FLAG_RESET_TASK_PENDING, tp->tg3_flags))
cancel_work_sync(&tp->reset_task);
tg3_flag_clear(tp, TX_RECOVERY_PENDING);
}

Expand Down Expand Up @@ -11209,18 +11209,27 @@ static void tg3_reset_task(struct work_struct *work)

tg3_halt(tp, RESET_KIND_SHUTDOWN, 0);
err = tg3_init_hw(tp, true);
if (err)
if (err) {
tg3_full_unlock(tp);
tp->irq_sync = 0;
tg3_napi_enable(tp);
/* Clear this flag so that tg3_reset_task_cancel() will not
* call cancel_work_sync() and wait forever.
*/
tg3_flag_clear(tp, RESET_TASK_PENDING);
dev_close(tp->dev);
goto out;
}

tg3_netif_start(tp);

out:
tg3_full_unlock(tp);

if (!err)
tg3_phy_start(tp);

tg3_flag_clear(tp, RESET_TASK_PENDING);
out:
rtnl_unlock();
}

Expand Down

0 comments on commit 5566993

Please sign in to comment.