From nobody Mon May 25 09:57:53 2026 Received: from mail-pf1-f172.google.com (mail-pf1-f172.google.com [209.85.210.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 230BA2D6409 for ; Mon, 25 May 2026 03:13:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779678810; cv=none; b=m+J3KeYX4PFLQ308fgBP/IRk6PfixsQhdHwZIelqOYyrK/uHbsd3iB814gr5rr2bMRqy9J6QYhWVs5ANwZqKDAKyPRbA4iLsAXpqLVCXjB7b6GR5sjwKq3H1LFb3RA6XwLuAqkZ3HktIpxNXiliMIVfjfoN2op5q6enHWeX0jaI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779678810; c=relaxed/simple; bh=KbCWJ56Ztr142YoKNAdVXlh/YvFieGfstI9Yb2Sqkrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=a4PyFu7QL3fvXW72nZSLWcOIEaEUpH73zRUKym2jhjDa4/7N4dPubndWQO7utoStPTdw1MWYL34p5ECsh4+HjLV7CNYWH6zmpo2yltWC9cIt1gnB6Gp9LzbylVxiOHx8C2xf7FJOfLIS/VKmYV9ud158+qIWi2cHFFA3e97tOzk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dziHZ3HQ; arc=none smtp.client-ip=209.85.210.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dziHZ3HQ" Received: by mail-pf1-f172.google.com with SMTP id d2e1a72fcca58-835386ff122so9330437b3a.3 for ; Sun, 24 May 2026 20:13:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779678808; x=1780283608; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=OwxZUNNLw1PFWW1oO7q8KXR63/wpofHqbMGlju32jag=; b=dziHZ3HQiJS2CYJQNWfaAAv0fRjST6sPpVLLsx6wgICuym214dd2PFYMo9kqJjKMSW fkuW8JmP3VrRw7fdiOMWFNw4HDKuXMDRIqIzdfBA8fiED8fWKGjZz6ZWRdHxCcOKw7JI t1LtNqgnCztFGeDcrmbG7X9TajFS/yCbPwCITD2xqLbGXqneD7e219eB6vrrZrTvSbdF jMQIntd14O/+MT1bE/GvYQmdA7kGjUZOv55Rr6NxLGwJ+3TY2S9i3t1tmBo9OPLCWaOz OhfQWrmL0K2aVFbFPZgpJR10cZgCN/Qmh6/uqEfUR0ygFTAzIQSjftWLqmhQ7M36nvpP sbKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779678808; x=1780283608; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=OwxZUNNLw1PFWW1oO7q8KXR63/wpofHqbMGlju32jag=; b=Z00I2PiF/O58M0F2GVUx5s6NRbwlwFNtUhGNFYYqoco2ROVu7c4S1/Ax4MqGhNC3Td kmcjJH7cxGn2vQBOhdnL2lEYDwofWSVRB24Ytj9RKGBI2bDwttick8eaaIiaYcrz/uwh 0LmXoAKlqq2vpQg8+88lNq/dV1DPG5saiopAtDd8tE+lTmoZbp/j9sGJ982C3CnNNfvI 8AtGvucNTjCTwsayXJifQctP1IBWyLx167AZnKQg6agP12J9gKTYc+Oses2K86VNAlfF u1xxgkcXXq40LAMz0v0I1V2HfUBc34D5U9chbOEL5F2J9FZRCCJTd+Ju6+fyXORnwGRT C72g== X-Gm-Message-State: AOJu0YzdZpc0q4Prk1iZpINsjAgNybPVi86FtQTMlS6q6NhqpXMcBbNA c5TXsfX9vlpJ3il67B6Zd3I4V5yjbMddTY7mF7nLjYlb5EiNUekFDIZCxrrmTd90 X-Gm-Gg: Acq92OHv8lHegTbI+JtXwG9dbwPLOeSBHTWqcqVoIFOKgVy7KaZKBeFQE/MTnep1LXP 1FTLO+oWHcUH0H5ir3Ajx2NijV7GDkmP2wr9gyPimiJvrbE1HiwUnUhOfozl5TUHjrYIVVOlfLc uWXne2nPSSYFzdTdvjvqxEjIEU+Yyu/GJgdu4UOhqmNw2cebVlnjt9XsOZKLWOlP8Hofqf+ey3q xvQzcCPWdZJ2YSc536JWNy4DIdoTgr+ug3K40pkmtos5ycW+0UZKs9je9aDbaSCEjMISxkMbIHK /i0l4uHzFHLKvUBdr955bEmDQAdE+tRhqgoSOUKk6gG42dCckx50wV5suea36DakxEGg4hNudov KQbrPlTL5MA76ZtgUgDPnUl42p86NsKgiD6t8I7RzF+V0Hw9aPamIySs0wiq2xZg8HyM51oiMCy exsxUSC4bY4ijIuVtPR+AFyM45qK9gWIxWKxEokSYhzGz9DoQtHlQCv/pNIT2g X-Received: by 2002:a05:6a00:188c:b0:835:6e77:1bbc with SMTP id d2e1a72fcca58-8415f61b465mr11516369b3a.36.1779678808333; Sun, 24 May 2026 20:13:28 -0700 (PDT) Received: from u.. (61-222-64-201.hinet-ip.hinet.net. [61.222.64.201]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84164ac9a42sm9134237b3a.8.2026.05.24.20.13.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 24 May 2026 20:13:27 -0700 (PDT) From: Tim JH Chen X-Google-Original-From: Tim JH Chen To: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org, tim.jh.chen@wnc.com.tw, Chih.Hung.Huang@wnc.com.tw Subject: [PATCH] net: wwan: t7xx: fix race between TX thread and system PM suspend Date: Mon, 25 May 2026 11:13:20 +0800 Message-ID: <20260525031320.519435-1-tim.jh.chen@wnc.com.tw> X-Mailer: git-send-email 2.43.0 In-Reply-To: <5315a3b0-4fc8-4d74-8e27-d7b84bd794c9@redhat.com> References: <5315a3b0-4fc8-4d74-8e27-d7b84bd794c9@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" v2: Address two concerns raised in AI-assisted code review of v1: 1. [High] t7xx_dpmaif_resume() was unconditionally restoring state to DPMAIF_STATE_PWRON regardless of the state before suspend. If the modem had already been moved to DPMAIF_STATE_PWROFF by t7xx_dpmaif_md_state_callback() (MD_STATE_EXCEPTION or MD_STATE_STOPPED) prior to system suspend, resume would incorrectly re-arm the TX kthread guard, allowing TX HW writes against a modem the MD state machine considers stopped or in exception. Fix: save dpmaif_ctrl->state into pre_suspend_state at the start of t7xx_dpmaif_suspend() and restore that saved value in t7xx_dpmaif_resume(), so a pre-suspend PWROFF is preserved across the suspend/resume cycle. 2. [Medium] The v1 second state check before pm_runtime_resume_and_get() only narrowed the TOCTOU window -- it did not close it. The state field was a plain enum read and written without any lock or READ_ONCE/WRITE_ONCE annotation. After the check passed on one CPU, the suspend path on another CPU could still set state=3DPWROFF and begin PM teardown before the kthread reached pm_runtime_resume_and_get(), reproducing the deadlock. Fix: introduce tx_pm_lock (struct mutex) held by the kthread across the [state check -> pm_runtime_resume_and_get -> pm_runtime_put] sequence. t7xx_dpmaif_suspend() acquires this lock before setting DPMAIF_STATE_PWROFF, which serialises with any in-progress kthread PM section and guarantees the kthread cannot enter pm_runtime_resume_and_get() after the state flag is set. READ_ONCE/WRITE_ONCE are added at every access point of the state flag that crosses the suspend/resume boundary to prevent compiler-visible tearing. The original v1 description of the root cause and tested fix still applies (deadlock between t7xx_dpmaif_tx_hw_push_thread calling pm_runtime_resume_and_get() and the system PM suspend path, triggered with ASPM L1 enabled after repeated suspend/resume cycles). Tested: no soft lockup over 500+ suspend/resume cycles with SIM registered and ASPM L1 enabled (previously triggered in < 300). Fixes: 05f7e89ab ("Linux 6.19") Signed-off-by: Tim JH Chen --- drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c | 6 ++++++ drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h | 3 +++ drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c | 16 +++++++++++++--- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c b/drivers/net/wwan/t7x= x/t7xx_hif_dpmaif.c index 7ff33c1d6ac7..e1f3eeb2c947 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c +++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c @@ -412,6 +412,11 @@ static int t7xx_dpmaif_suspend(struct t7xx_pci_dev *t7= xx_dev, void *param) struct dpmaif_ctrl *dpmaif_ctrl =3D param; =20 t7xx_dpmaif_tx_stop(dpmaif_ctrl); + dpmaif_ctrl->pre_suspend_state =3D READ_ONCE(dpmaif_ctrl->state); + mutex_lock(&dpmaif_ctrl->tx_pm_lock); + WRITE_ONCE(dpmaif_ctrl->state, DPMAIF_STATE_PWROFF); + mutex_unlock(&dpmaif_ctrl->tx_pm_lock); + wake_up(&dpmaif_ctrl->tx_wq); t7xx_dpmaif_hw_stop_all_txq(&dpmaif_ctrl->hw_info); t7xx_dpmaif_hw_stop_all_rxq(&dpmaif_ctrl->hw_info); t7xx_dpmaif_disable_irq(dpmaif_ctrl); @@ -451,6 +456,7 @@ static int t7xx_dpmaif_resume(struct t7xx_pci_dev *t7xx= _dev, void *param) if (!dpmaif_ctrl) return 0; =20 + WRITE_ONCE(dpmaif_ctrl->state, dpmaif_ctrl->pre_suspend_state); t7xx_dpmaif_start_txrx_qs(dpmaif_ctrl); t7xx_dpmaif_enable_irq(dpmaif_ctrl); t7xx_dpmaif_unmask_dlq_intr(dpmaif_ctrl); diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h b/drivers/net/wwan/t7x= x/t7xx_hif_dpmaif.h index 0ce4505e813d..670ed2cca761 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h +++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h @@ -20,6 +20,7 @@ =20 #include #include +#include #include #include #include @@ -172,6 +173,8 @@ struct dpmaif_ctrl { struct t7xx_pci_dev *t7xx_dev; struct md_pm_entity dpmaif_pm_entity; enum dpmaif_state state; + enum dpmaif_state pre_suspend_state; + struct mutex tx_pm_lock; bool dpmaif_sw_init_done; struct dpmaif_hw_info hw_info; struct dpmaif_tx_queue txq[DPMAIF_TXQ_NUM]; diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c b/drivers/net/wwan/= t7xx/t7xx_hif_dpmaif_tx.c index 236d632cf591..197c0ab3fd39 100644 --- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c +++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c @@ -449,10 +449,10 @@ static int t7xx_dpmaif_tx_hw_push_thread(void *arg) =20 while (!kthread_should_stop()) { if (t7xx_tx_lists_are_all_empty(dpmaif_ctrl) || - dpmaif_ctrl->state !=3D DPMAIF_STATE_PWRON) { + READ_ONCE(dpmaif_ctrl->state) !=3D DPMAIF_STATE_PWRON) { if (wait_event_interruptible(dpmaif_ctrl->tx_wq, (!t7xx_tx_lists_are_all_empty(dpmaif_ctrl) && - dpmaif_ctrl->state =3D=3D DPMAIF_STATE_PWRON) || + READ_ONCE(dpmaif_ctrl->state) =3D=3D DPMAIF_STATE_PWRON) || kthread_should_stop())) continue; =20 @@ -460,14 +460,23 @@ static int t7xx_dpmaif_tx_hw_push_thread(void *arg) break; } =20 + mutex_lock(&dpmaif_ctrl->tx_pm_lock); + if (READ_ONCE(dpmaif_ctrl->state) !=3D DPMAIF_STATE_PWRON) { + mutex_unlock(&dpmaif_ctrl->tx_pm_lock); + continue; + } + ret =3D pm_runtime_resume_and_get(dpmaif_ctrl->dev); - if (ret < 0 && ret !=3D -EACCES) + if (ret < 0 && ret !=3D -EACCES) { + mutex_unlock(&dpmaif_ctrl->tx_pm_lock); return ret; + } =20 t7xx_pci_disable_sleep(dpmaif_ctrl->t7xx_dev); t7xx_do_tx_hw_push(dpmaif_ctrl); t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev); pm_runtime_put_autosuspend(dpmaif_ctrl->dev); + mutex_unlock(&dpmaif_ctrl->tx_pm_lock); } =20 return 0; @@ -475,6 +484,7 @@ static int t7xx_dpmaif_tx_hw_push_thread(void *arg) =20 int t7xx_dpmaif_tx_thread_init(struct dpmaif_ctrl *dpmaif_ctrl) { + mutex_init(&dpmaif_ctrl->tx_pm_lock); init_waitqueue_head(&dpmaif_ctrl->tx_wq); dpmaif_ctrl->tx_thread =3D kthread_run(t7xx_dpmaif_tx_hw_push_thread, dpmaif_ctrl, "dpmaif_tx_hw_push"); --=20 2.43.0