[PATCH] wifi: iwlegacy: serialize watchdog updates with device teardown

From: Runyu Xiao

Date: Sun Oct 04 2026 - 07:45:44 EST


The writable wd_timeout debugfs file changes il->cfg->wd_timeout, but the
iwl3945 configuration is shared and const. The handler also rearms the
watchdog without taking il->mutex. The down paths hold this mutex, delete
the timer, and then free the TX queues, so an unlocked debugfs write can
rearm the timer after deletion. The callback can then access the queues
after they have been freed.

Store wd_timeout in per-device state. Serialize the debugfs update with
the down paths and only arm the watchdog while TX queues exist. Use
READ_ONCE() and WRITE_ONCE() for accesses that do not hold il->mutex.

Fixes: 1dc80798a8ca ("iwlegacy: constify local structures")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Runyu Xiao <runyu.xiao@xxxxxxxxxx>
---
drivers/net/wireless/intel/iwlegacy/3945-mac.c | 1 +
drivers/net/wireless/intel/iwlegacy/4965-mac.c | 1 +
drivers/net/wireless/intel/iwlegacy/common.c | 6 +++---
drivers/net/wireless/intel/iwlegacy/common.h | 1 +
drivers/net/wireless/intel/iwlegacy/debug.c | 8 ++++++--
5 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
index cbaf250626..ee40cf534d 100644
--- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
@@ -3563,6 +3563,7 @@ il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

D_INFO("*** LOAD DRIVER ***\n");
il->cfg = cfg;
+ il->wd_timeout = cfg->wd_timeout;
il->ops = &il3945_ops;
#ifdef CONFIG_IWLEGACY_DEBUGFS
il->debugfs_ops = &il3945_debugfs_ops;
diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
index 18bb556826..9a42a74cf8 100644
--- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c
@@ -6500,6 +6500,7 @@ il4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

D_INFO("*** LOAD DRIVER ***\n");
il->cfg = cfg;
+ il->wd_timeout = cfg->wd_timeout;
il->ops = &il4965_ops;
#ifdef CONFIG_IWLEGACY_DEBUGFS
il->debugfs_ops = &il4965_debugfs_ops;
diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c
index 0bb807ff8e..c2adb1ef63 100644
--- a/drivers/net/wireless/intel/iwlegacy/common.c
+++ b/drivers/net/wireless/intel/iwlegacy/common.c
@@ -4774,7 +4774,7 @@ il_check_stuck_queue(struct il_priv *il, int cnt)

timeout =
txq->time_stamp +
- msecs_to_jiffies(il->cfg->wd_timeout);
+ msecs_to_jiffies(READ_ONCE(il->wd_timeout));

if (time_after(now, timeout)) {
IL_ERR("Queue %d stuck for %u ms.\n", q->id,
@@ -4806,7 +4806,7 @@ il_bg_watchdog(struct timer_list *t)
if (test_bit(S_EXIT_PENDING, &il->status))
return;

- timeout = il->cfg->wd_timeout;
+ timeout = READ_ONCE(il->wd_timeout);
if (timeout == 0)
return;

@@ -4831,7 +4831,7 @@ EXPORT_SYMBOL(il_bg_watchdog);
void
il_setup_watchdog(struct il_priv *il)
{
- unsigned int timeout = il->cfg->wd_timeout;
+ unsigned int timeout = READ_ONCE(il->wd_timeout);

if (timeout)
mod_timer(&il->watchdog,
diff --git a/drivers/net/wireless/intel/iwlegacy/common.h b/drivers/net/wireless/intel/iwlegacy/common.h
index 21f1c7702a..9c1c252dcc 100644
--- a/drivers/net/wireless/intel/iwlegacy/common.h
+++ b/drivers/net/wireless/intel/iwlegacy/common.h
@@ -1129,6 +1129,7 @@ struct il_priv {
struct ieee80211_rate *ieee_rates;

struct il_cfg *cfg;
+ unsigned int wd_timeout;
const struct il_ops *ops;
#ifdef CONFIG_IWLEGACY_DEBUGFS
const struct il_debugfs_ops *debugfs_ops;
diff --git a/drivers/net/wireless/intel/iwlegacy/debug.c b/drivers/net/wireless/intel/iwlegacy/debug.c
index 8a9f79ff1c..13dfc263a2 100644
--- a/drivers/net/wireless/intel/iwlegacy/debug.c
+++ b/drivers/net/wireless/intel/iwlegacy/debug.c
@@ -1284,8 +1284,12 @@ il_dbgfs_wd_timeout_write(struct file *file, const char __user *user_buf,
if (timeout < 0 || timeout > IL_MAX_WD_TIMEOUT)
timeout = IL_DEF_WD_TIMEOUT;

- il->cfg->wd_timeout = timeout;
- il_setup_watchdog(il);
+ mutex_lock(&il->mutex);
+ WRITE_ONCE(il->wd_timeout, timeout);
+ if (il->txq)
+ il_setup_watchdog(il);
+ mutex_unlock(&il->mutex);
+
return count;
}

--
2.34.1