diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c --- a/sys/compat/linuxkpi/common/src/linux_80211.c +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -145,6 +145,7 @@ static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *, struct ieee80211_node *); #endif +static void lkpi_80211_txq_tx_one(struct lkpi_sta *, struct mbuf *); static void lkpi_80211_txq_task(void *, int); static void lkpi_80211_lhw_rxq_task(void *, int); static void lkpi_ieee80211_free_skb_mbuf(void *); @@ -1061,6 +1062,51 @@ } } +/* + * On the way down from RUN -> ASSOC -> AUTH we may send a DISASSOC or DEAUTH + * packet. The problem is that the state machine functions tend to hold the + * LHW lock which will prevent lkpi_80211_txq_tx_one() from sending the packet. + * We call this after dropping the ic lock and before acquiring the LHW lock. + * we make sure no further packets are queued and if they are queued the task + * will finish or be cancelled. At the end if a packet is left we manually + * send it. scan_to_auth() would re-enable sending if the lsta would be + * re-used. + */ +static void +lkpi_80211_flush_tx(struct lkpi_hw *lhw, struct lkpi_sta *lsta) +{ + struct mbufq mq; + struct mbuf *m; + int len; + + LKPI_80211_LHW_UNLOCK_ASSERT(lhw); + + /* Do not accept any new packets until scan_to_auth or lsta_free(). */ + LKPI_80211_LSTA_TXQ_LOCK(lsta); + lsta->txq_ready = false; + LKPI_80211_LSTA_TXQ_UNLOCK(lsta); + + while (taskqueue_cancel(taskqueue_thread, &lsta->txq_task, NULL) != 0) + taskqueue_drain(taskqueue_thread, &lsta->txq_task); + + LKPI_80211_LSTA_TXQ_LOCK(lsta); + len = mbufq_len(&lsta->txq); + if (len <= 0) { + LKPI_80211_LSTA_TXQ_UNLOCK(lsta); + return; + } + + mbufq_init(&mq, IFQ_MAXLEN); + mbufq_concat(&mq, &lsta->txq); + LKPI_80211_LSTA_TXQ_UNLOCK(lsta); + + m = mbufq_dequeue(&mq); + while (m != NULL) { + lkpi_80211_txq_tx_one(lsta, m); + m = mbufq_dequeue(&mq); + } +} + /* -------------------------------------------------------------------------- */ static int @@ -1274,6 +1320,14 @@ __func__, ni, ni->ni_drv_data)); lsta = ni->ni_drv_data; + /* + * Make sure in case the sta did not change and we re-add it, + * that we can tx again. + */ + LKPI_80211_LSTA_TXQ_LOCK(lsta); + lsta->txq_ready = true; + LKPI_80211_LSTA_TXQ_UNLOCK(lsta); + LKPI_80211_LVIF_LOCK(lvif); /* Insert the [l]sta into the list of known stations. */ TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry); @@ -1426,7 +1480,7 @@ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true); /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, true, true); + lkpi_wake_tx_queues(hw, sta, false, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); @@ -1584,7 +1638,7 @@ } /* Wake tx queue to get packet out. */ - lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), true, true); + lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, true); /* * .. we end up in "assoc_to_run" @@ -1728,7 +1782,7 @@ LKPI_80211_LHW_UNLOCK(lhw); IEEE80211_LOCK(vap->iv_ic); - /* Call iv_newstate first so we get potential DISASSOC packet out. */ + /* Call iv_newstate first so we get potential DEAUTH packet out. */ error = lvif->iv_newstate(vap, nstate, arg); if (error != 0) { ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) " @@ -1737,12 +1791,16 @@ } IEEE80211_UNLOCK(vap->iv_ic); + + /* Ensure the packets get out. */ + lkpi_80211_flush_tx(lhw, lsta); + LKPI_80211_LHW_LOCK(lhw); lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, true, true); + lkpi_wake_tx_queues(hw, sta, false, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); @@ -2120,12 +2178,16 @@ } IEEE80211_UNLOCK(vap->iv_ic); + + /* Ensure the packets get out. */ + lkpi_80211_flush_tx(lhw, lsta); + LKPI_80211_LHW_LOCK(lhw); lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, true, true); + lkpi_wake_tx_queues(hw, sta, false, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); @@ -2254,12 +2316,16 @@ } IEEE80211_UNLOCK(vap->iv_ic); + + /* Ensure the packets get out. */ + lkpi_80211_flush_tx(lhw, lsta); + LKPI_80211_LHW_LOCK(lhw); lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* Wake tx queues to get packet(s) out. */ - lkpi_wake_tx_queues(hw, sta, true, true); + lkpi_wake_tx_queues(hw, sta, false, true); /* flush, no drop */ lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), false); @@ -3595,7 +3661,7 @@ lsta = ni->ni_drv_data; LKPI_80211_LSTA_TXQ_LOCK(lsta); - if (!lsta->txq_ready) { + if (!lsta->added_to_drv || !lsta->txq_ready) { LKPI_80211_LSTA_TXQ_UNLOCK(lsta); /* * Free the mbuf (do NOT release ni ref for the m_pkthdr.rcvif! @@ -3821,6 +3887,7 @@ struct lkpi_sta *lsta; struct mbufq mq; struct mbuf *m; + bool shall_tx; lsta = ctx; @@ -3836,9 +3903,19 @@ LKPI_80211_LSTA_TXQ_LOCK(lsta); /* * Do not re-check lsta->txq_ready here; we may have a pending - * disassoc frame still. + * disassoc/deauth frame still. On the contrary if txq_ready is + * false we do not have a valid sta anymore in the firmware so no + * point to try to TX. + * We also use txq_ready as a semaphore and will drain the txq manually + * if needed on our way towards SCAN/INIT in the state machine. + */ + shall_tx = lsta->added_to_drv && lsta->txq_ready; + if (__predict_true(shall_tx)) + mbufq_concat(&mq, &lsta->txq); + /* + * else a state change will push the packets out manually or + * lkpi_lsta_free() will drain the lsta->txq and free the mbufs. */ - mbufq_concat(&mq, &lsta->txq); LKPI_80211_LSTA_TXQ_UNLOCK(lsta); m = mbufq_dequeue(&mq);