Commit 8ed728b3 authored by Thinh Tran's avatar Thinh Tran Committed by Baogen Shang
Browse files

net/tg3: fix race condition in tg3_reset_task()

stable inclusion
from stable-v5.10.209
commit 1059aa41c5a84abfab4cc7371d6b5ff2b30b6c2d
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I9J6AL
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-5.10.y&id=1059aa41c5a84abfab4cc7371d6b5ff2b30b6c2d



-------------------------

[ Upstream commit 16b55b1f2269962fb6b5154b8bf43f37c9a96637 ]

When an EEH error is encountered by a PCI adapter, the EEH driver
modifies the PCI channel's state as shown below:

   enum {
      /* I/O channel is in normal state */
      pci_channel_io_normal = (__force pci_channel_state_t) 1,

      /* I/O to channel is blocked */
      pci_channel_io_frozen = (__force pci_channel_state_t) 2,

      /* PCI card is dead */
      pci_channel_io_perm_failure = (__force pci_channel_state_t) 3,
   };

If the same EEH error then causes the tg3 driver's transmit timeout
logic to execute, the tg3_tx_timeout() function schedules a reset
task via tg3_reset_task_schedule(), which may cause a race condition
between the tg3 and EEH driver as both attempt to recover the HW via
a reset action.

EEH driver gets error event
--> eeh_set_channel_state()
    and set device to one of
    error state above           scheduler: tg3_reset_task() get
                                returned error from tg3_init_hw()
                             --> dev_close() shuts down the interface
tg3_io_slot_reset() and
tg3_io_resume() fail to
reset/resume the device

To resolve this issue, we avoid the race condition by checking the PCI
channel state in the tg3_reset_task() function and skip the tg3 driver
initiated reset when the PCI channel is not in the normal state.  (The
driver has no access to tg3 device registers at this point and cannot
even complete the reset task successfully without external assistance.)
We'll leave the reset procedure to be managed by the EEH driver which
calls the tg3_io_error_detected(), tg3_io_slot_reset() and
tg3_io_resume() functions as appropriate.

Adding the same checking in tg3_dump_state() to avoid dumping all
device registers when the PCI channel is not in the normal state.

Signed-off-by: default avatarThinh Tran <thinhtr@linux.vnet.ibm.com>
Tested-by: default avatarVenkata Sai Duggi <venkata.sai.duggi@ibm.com>
Reviewed-by: default avatarDavid Christensen <drc@linux.vnet.ibm.com>
Reviewed-by: default avatarMichael Chan <michael.chan@broadcom.com>
Link: https://lore.kernel.org/r/20231201001911.656-1-thinhtr@linux.vnet.ibm.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarBaogen Shang <baogen.shang@windriver.com>
parent 34c4be6c
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -6454,6 +6454,14 @@ static void tg3_dump_state(struct tg3 *tp)
	int i;
	u32 *regs;

	/* If it is a PCI error, all registers will be 0xffff,
	 * we don't dump them out, just report the error and return
	 */
	if (tp->pdev->error_state != pci_channel_io_normal) {
		netdev_err(tp->dev, "PCI channel ERROR!\n");
		return;
	}

	regs = kzalloc(TG3_REG_BLK_SIZE, GFP_ATOMIC);
	if (!regs)
		return;
@@ -11195,7 +11203,8 @@ static void tg3_reset_task(struct work_struct *work)
	rtnl_lock();
	tg3_full_lock(tp, 0);

	if (tp->pcierr_recovery || !netif_running(tp->dev)) {
	if (tp->pcierr_recovery || !netif_running(tp->dev) ||
	    tp->pdev->error_state != pci_channel_io_normal) {
		tg3_flag_clear(tp, RESET_TASK_PENDING);
		tg3_full_unlock(tp);
		rtnl_unlock();