git: 8fc0789a4dda - stable/14 - LinuxKPI: 802.11: lock down the "txq_scheduled" tailq
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 16 Jan 2026 19:41:30 UTC
The branch stable/14 has been updated by bz:
URL: https://cgit.FreeBSD.org/src/commit/?id=8fc0789a4dda595311bec566719ff3c2e2744ee2
commit 8fc0789a4dda595311bec566719ff3c2e2744ee2
Author: Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2025-12-10 20:29:23 +0000
Commit: Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2026-01-16 19:37:55 +0000
LinuxKPI: 802.11: lock down the "txq_scheduled" tailq
For consistency rename the "scheduled_txqs" tailq to
"txq_scheduled" and add a lock per txq ("txq_scheduled_lock[]").
We use the "_bh" locking as this called from the device driver.
This fixes panics due to concurrent access to the tailq, especially
in between "first" and "remove" on the out-direction and between
"insert" and "elem_init" on the in-direction.
This was easily reproducible just running iperf3 at basic rates for
a few seconds to minutes with multiple chipsets, not only rtw89.
Sponsored by: The FreeBSD Foundation
PR: 290636
Reported by: arved, and others before
(cherry picked from commit 567a097c8ab60d9fcd68a87c3c5ad605fe8715cc)
(cherry picked from commit 2e1156f75836a0e5a4026e13d72b54f272e705cb)
---
sys/compat/linuxkpi/common/src/linux_80211.c | 51 ++++++++++++++++++++++------
sys/compat/linuxkpi/common/src/linux_80211.h | 3 +-
2 files changed, 42 insertions(+), 12 deletions(-)
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 96d9890beeb3..a92f3f75912d 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -6019,8 +6019,9 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
sx_init_flags(&lhw->lvif_sx, "lhw-lvif", SX_RECURSE | SX_DUPOK);
TAILQ_INIT(&lhw->lvif_head);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ spin_lock_init(&lhw->txq_scheduled_lock[ac]);
lhw->txq_generation[ac] = 1;
- TAILQ_INIT(&lhw->scheduled_txqs[ac]);
+ TAILQ_INIT(&lhw->txq_scheduled[ac]);
}
/* Chanctx_conf */
@@ -6054,6 +6055,7 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
{
struct lkpi_hw *lhw;
struct mbuf *m;
+ int ac;
lhw = HW_TO_LHW(hw);
free(lhw->ic, M_LKPI80211);
@@ -6114,6 +6116,9 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
}
}
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ spin_lock_destroy(&lhw->txq_scheduled_lock[ac]);
+
/* Cleanup more of lhw here or in wiphy_free()? */
spin_lock_destroy(&lhw->txq_lock);
LKPI_80211_LHW_TXQ_LOCK_DESTROY(lhw);
@@ -8121,7 +8126,12 @@ linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
spin_unlock_irqrestore(&lhw->txq_lock, flags);
}
+/* -------------------------------------------------------------------------- */
+
/* This is just hardware queues. */
+/*
+ * Being called from the driver thus use _bh() locking.
+ */
void
linuxkpi_ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint8_t ac)
{
@@ -8129,10 +8139,16 @@ linuxkpi_ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint8_t ac)
lhw = HW_TO_LHW(hw);
- IMPROVE_TXQ("Are there reasons why we wouldn't schedule?");
- IMPROVE_TXQ("LOCKING");
+ if (ac >= IEEE80211_NUM_ACS) {
+ ic_printf(lhw->ic, "%s: ac %u out of bounds.\n", __func__, ac);
+ return;
+ }
+
+ spin_lock_bh(&lhw->txq_scheduled_lock[ac]);
+ IMPROVE("check AIRTIME_FAIRNESS");
if (++lhw->txq_generation[ac] == 0)
lhw->txq_generation[ac]++;
+ spin_unlock_bh(&lhw->txq_scheduled_lock[ac]);
}
struct ieee80211_txq *
@@ -8145,24 +8161,33 @@ linuxkpi_ieee80211_next_txq(struct ieee80211_hw *hw, uint8_t ac)
lhw = HW_TO_LHW(hw);
txq = NULL;
- IMPROVE_TXQ("LOCKING");
+ if (ac >= IEEE80211_NUM_ACS) {
+ ic_printf(lhw->ic, "%s: ac %u out of bounds.\n", __func__, ac);
+ return (NULL);
+ }
+
+ spin_lock_bh(&lhw->txq_scheduled_lock[ac]);
/* Check that we are scheduled. */
if (lhw->txq_generation[ac] == 0)
goto out;
- ltxq = TAILQ_FIRST(&lhw->scheduled_txqs[ac]);
+ ltxq = TAILQ_FIRST(&lhw->txq_scheduled[ac]);
if (ltxq == NULL)
goto out;
if (ltxq->txq_generation == lhw->txq_generation[ac])
goto out;
+ IMPROVE("check AIRTIME_FAIRNESS");
+
+ TAILQ_REMOVE(&lhw->txq_scheduled[ac], ltxq, txq_entry);
ltxq->txq_generation = lhw->txq_generation[ac];
- TAILQ_REMOVE(&lhw->scheduled_txqs[ac], ltxq, txq_entry);
txq = <xq->txq;
TAILQ_ELEM_INIT(ltxq, txq_entry);
out:
+ spin_unlock_bh(&lhw->txq_scheduled_lock[ac]);
+
return (txq);
}
@@ -8175,8 +8200,6 @@ void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *hw,
ltxq = TXQ_TO_LTXQ(txq);
- IMPROVE_TXQ("LOCKING");
-
/* Only schedule if work to do or asked to anyway. */
LKPI_80211_LTXQ_LOCK(ltxq);
ltxq_empty = skb_queue_empty(<xq->skbq);
@@ -8184,20 +8207,26 @@ void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *hw,
if (!withoutpkts && ltxq_empty)
goto out;
+ lhw = HW_TO_LHW(hw);
+ spin_lock_bh(&lhw->txq_scheduled_lock[txq->ac]);
/*
* Make sure we do not double-schedule. We do this by checking tqe_prev,
* the previous entry in our tailq. tqe_prev is always valid if this entry
* is queued, tqe_next may be NULL if this is the only element in the list.
*/
if (ltxq->txq_entry.tqe_prev != NULL)
- goto out;
+ goto unlock;
+
+ TAILQ_INSERT_TAIL(&lhw->txq_scheduled[txq->ac], ltxq, txq_entry);
+unlock:
+ spin_unlock_bh(&lhw->txq_scheduled_lock[txq->ac]);
- lhw = HW_TO_LHW(hw);
- TAILQ_INSERT_TAIL(&lhw->scheduled_txqs[txq->ac], ltxq, txq_entry);
out:
return;
}
+/* -------------------------------------------------------------------------- */
+
void
linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index eb7bae811695..ea5d1d72e78c 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -226,7 +226,8 @@ struct lkpi_hw { /* name it mac80211_sc? */
struct mtx txq_mtx;
uint32_t txq_generation[IEEE80211_NUM_ACS];
- TAILQ_HEAD(, lkpi_txq) scheduled_txqs[IEEE80211_NUM_ACS];
+ spinlock_t txq_scheduled_lock[IEEE80211_NUM_ACS];
+ TAILQ_HEAD(, lkpi_txq) txq_scheduled[IEEE80211_NUM_ACS];
spinlock_t txq_lock;
/* Deferred RX path. */