The branch main has been updated by bz:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=567a097c8ab60d9fcd68a87c3c5ad605fe8715cc

commit 567a097c8ab60d9fcd68a87c3c5ad605fe8715cc
Author:     Bjoern A. Zeeb <[email protected]>
AuthorDate: 2025-12-10 20:29:23 +0000
Commit:     Bjoern A. Zeeb <[email protected]>
CommitDate: 2025-12-11 00:46:15 +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
    MFC after:      3 days
---
 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 8098d02da1a9..1d510577f00c 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -6472,8 +6472,9 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct 
ieee80211_ops *ops)
        TAILQ_INIT(&lhw->lvif_head);
        __hw_addr_init(&lhw->mc_list);
        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 */
@@ -6507,6 +6508,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);
@@ -6571,6 +6573,9 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
        lkpi_cleanup_mcast_list_locked(lhw);
        LKPI_80211_LHW_MC_UNLOCK(lhw);
 
+       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);
@@ -8664,7 +8669,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)
 {
@@ -8672,10 +8682,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 *
@@ -8688,24 +8704,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 = &ltxq->txq;
        TAILQ_ELEM_INIT(ltxq, txq_entry);
 
 out:
+       spin_unlock_bh(&lhw->txq_scheduled_lock[ac]);
+
        return (txq);
 }
 
@@ -8718,8 +8743,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(&ltxq->skbq);
@@ -8727,20 +8750,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 0dfcd7646c34..fcbef46fc6de 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -252,7 +252,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. */

Reply via email to