git: 2a12368e7ced - stable/13 - LinuxKPI: 802.11: implement a deferred RX path
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 14 Jun 2024 18:43:01 UTC
The branch stable/13 has been updated by bz: URL: https://cgit.FreeBSD.org/src/commit/?id=2a12368e7ced1c103d5be14e545f3e0f25edf947 commit 2a12368e7ced1c103d5be14e545f3e0f25edf947 Author: Bjoern A. Zeeb <bz@FreeBSD.org> AuthorDate: 2024-02-12 16:03:13 +0000 Commit: Bjoern A. Zeeb <bz@FreeBSD.org> CommitDate: 2024-06-14 14:55:15 +0000 LinuxKPI: 802.11: implement a deferred RX path Some calls, e.g., action frames cause us to call through all the way down to firmware from the RX path without any deferral in net80211. For LinuxKPI and iwlwifi this goes (with omissions) like this: lkpi_napi_task -> linuxkpi_ieee80211_rx -> ieee80211_input_mimo -> sta_input -> ht_recv_action_ba_addba_request -> lkpi_ic_ampdu_rx_start -> iwl_mvm_mac_ampdu_action -> iwl_trans_txq_send_hcmd. At that point we are waiting for an interrupt from the firmware but given the lkpi_napi_task has not finished (and may have more to dispatch based on budget and what was received) we will not see the new interrupt/fw response. With no answer from the firmware, the software timeout in the driver kills the command and the firmware and issues a complete restart. Implement the deferred RX path in LinuxKPI for the moment. At a later point we should carefully shift this into net80211. This fixes the hangs for (*ic_ampdu_rx_start)() calls with iwlwifi. PR: 276083 Reviewed by: cc Differential Revision: https://reviews.freebsd.org/D43968 (cherry picked from commit 759a996d610d9354aac5c48a6bdc9cedcba2f48b) --- sys/compat/linuxkpi/common/src/linux_80211.c | 134 ++++++++++++++++++++++++--- sys/compat/linuxkpi/common/src/linux_80211.h | 31 +++++++ 2 files changed, 154 insertions(+), 11 deletions(-) diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c index df72495e68b6..ef4e22542b0f 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.c +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -145,6 +145,7 @@ const struct cfg80211_ops linuxkpi_mac80211cfgops = { static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *, struct ieee80211_node *); 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 *); #ifdef LKPI_80211_WME static int lkpi_wme_update(struct lkpi_hw *, struct ieee80211vap *, bool); @@ -4272,6 +4273,12 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops) TAILQ_INIT(&lhw->scheduled_txqs[ac]); } + /* Deferred RX path. */ + LKPI_80211_LHW_RXQ_LOCK_INIT(lhw); + TASK_INIT(&lhw->rxq_task, 0, lkpi_80211_lhw_rxq_task, lhw); + mbufq_init(&lhw->rxq, IFQ_MAXLEN); + lhw->rxq_stopped = false; + /* * XXX-BZ TODO make sure there is a "_null" function to all ops * not initialized. @@ -4297,11 +4304,42 @@ void linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw) { struct lkpi_hw *lhw; + struct mbuf *m; lhw = HW_TO_LHW(hw); free(lhw->ic, M_LKPI80211); lhw->ic = NULL; + /* + * Drain the deferred RX path. + */ + LKPI_80211_LHW_RXQ_LOCK(lhw); + lhw->rxq_stopped = true; + LKPI_80211_LHW_RXQ_UNLOCK(lhw); + + /* Drain taskq, won't be restarted due to rxq_stopped being set. */ + while (taskqueue_cancel(taskqueue_thread, &lhw->rxq_task, NULL) != 0) + taskqueue_drain(taskqueue_thread, &lhw->rxq_task); + + /* Flush mbufq (make sure to release ni refs!). */ + m = mbufq_dequeue(&lhw->rxq); + while (m != NULL) { + struct m_tag *mtag; + + mtag = m_tag_locate(m, MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, NULL); + if (mtag != NULL) { + struct lkpi_80211_tag_rxni *rxni; + + rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1); + ieee80211_free_node(rxni->ni); + } + m_freem(m); + m = mbufq_dequeue(&lhw->rxq); + } + KASSERT(mbufq_empty(&lhw->rxq), ("%s: lhw %p has rxq len %d != 0\n", + __func__, lhw, mbufq_len(&lhw->rxq))); + LKPI_80211_LHW_RXQ_LOCK_DESTROY(lhw); + /* Cleanup more of lhw here or in wiphy_free()? */ LKPI_80211_LHW_TXQ_LOCK_DESTROY(lhw); LKPI_80211_LHW_SCAN_LOCK_DESTROY(lhw); @@ -4796,6 +4834,66 @@ linuxkpi_ieee80211_scan_completed(struct ieee80211_hw *hw, return; } +static void +lkpi_80211_lhw_rxq_rx_one(struct lkpi_hw *lhw, struct mbuf *m) +{ + struct ieee80211_node *ni; + struct m_tag *mtag; + int ok; + + ni = NULL; + mtag = m_tag_locate(m, MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, NULL); + if (mtag != NULL) { + struct lkpi_80211_tag_rxni *rxni; + + rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1); + ni = rxni->ni; + } + + if (ni != NULL) { + ok = ieee80211_input_mimo(ni, m); + ieee80211_free_node(ni); /* Release the reference. */ + if (ok < 0) + m_freem(m); + } else { + ok = ieee80211_input_mimo_all(lhw->ic, m); + /* mbuf got consumed. */ + } + +#ifdef LINUXKPI_DEBUG_80211 + if (linuxkpi_debug_80211 & D80211_TRACE_RX) + printf("TRACE %s: handled frame type %#0x\n", __func__, ok); +#endif +} + +static void +lkpi_80211_lhw_rxq_task(void *ctx, int pending) +{ + struct lkpi_hw *lhw; + struct mbufq mq; + struct mbuf *m; + + lhw = ctx; + +#ifdef LINUXKPI_DEBUG_80211 + if (linuxkpi_debug_80211 & D80211_TRACE_RX) + printf("%s:%d lhw %p pending %d mbuf_qlen %d\n", + __func__, __LINE__, lhw, pending, mbufq_len(&lhw->rxq)); +#endif + + mbufq_init(&mq, IFQ_MAXLEN); + + LKPI_80211_LHW_RXQ_LOCK(lhw); + mbufq_concat(&mq, &lhw->rxq); + LKPI_80211_LHW_RXQ_UNLOCK(lhw); + + m = mbufq_dequeue(&mq); + while (m != NULL) { + lkpi_80211_lhw_rxq_rx_one(lhw, m); + m = mbufq_dequeue(&mq); + } +} + /* For %list see comment towards the end of the function. */ void linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, @@ -5020,20 +5118,34 @@ skip_device_ts: } #endif + /* + * Attach meta-information to the mbuf for the deferred RX path. + * Currently this is best-effort. Should we need to be hard, + * drop the frame and goto err; + */ if (ni != NULL) { - ok = ieee80211_input_mimo(ni, m); - ieee80211_free_node(ni); - if (ok < 0) - m_freem(m); - } else { - ok = ieee80211_input_mimo_all(ic, m); - /* mbuf got consumed. */ + struct m_tag *mtag; + struct lkpi_80211_tag_rxni *rxni; + + mtag = m_tag_alloc(MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, + sizeof(*rxni), IEEE80211_M_NOWAIT); + if (mtag != NULL) { + rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1); + rxni->ni = ni; /* We hold a reference. */ + m_tag_prepend(m, mtag); + } } -#ifdef LINUXKPI_DEBUG_80211 - if (linuxkpi_debug_80211 & D80211_TRACE_RX) - printf("TRACE %s: handled frame type %#0x\n", __func__, ok); -#endif + LKPI_80211_LHW_RXQ_LOCK(lhw); + if (lhw->rxq_stopped) { + LKPI_80211_LHW_RXQ_UNLOCK(lhw); + m_freem(m); + goto err; + } + + mbufq_enqueue(&lhw->rxq, m); + taskqueue_enqueue(taskqueue_thread, &lhw->rxq_task); + LKPI_80211_LHW_RXQ_UNLOCK(lhw); IMPROVE(); diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h index d25614de56dc..b0156a5ade3f 100644 --- a/sys/compat/linuxkpi/common/src/linux_80211.h +++ b/sys/compat/linuxkpi/common/src/linux_80211.h @@ -76,6 +76,18 @@ if (linuxkpi_debug_80211 & D80211_TRACE_MODE_HT) \ printf("%s:%d: XXX LKPI80211 IMPROVE_HT\n", __func__, __LINE__) +#define MTAG_ABI_LKPI80211 1707696513 /* LinuxKPI 802.11 KBI */ + +/* + * Deferred RX path. + * We need to pass *ni along (and possibly more in the future so + * we use a struct right from the start. + */ +#define LKPI80211_TAG_RXNI 0 /* deferred RX path */ +struct lkpi_80211_tag_rxni { + struct ieee80211_node *ni; /* MUST hold a reference to it. */ +}; + struct lkpi_radiotap_tx_hdr { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; @@ -192,6 +204,11 @@ struct lkpi_hw { /* name it mac80211_sc? */ uint32_t txq_generation[IEEE80211_NUM_ACS]; TAILQ_HEAD(, lkpi_txq) scheduled_txqs[IEEE80211_NUM_ACS]; + /* Deferred RX path. */ + struct task rxq_task; + struct mbufq rxq; + struct mtx rxq_mtx; + /* Scan functions we overload to handle depending on scan mode. */ void (*ic_scan_curchan)(struct ieee80211_scan_state *, unsigned long); @@ -240,6 +257,7 @@ struct lkpi_hw { /* name it mac80211_sc? */ bool update_mc; bool update_wme; + bool rxq_stopped; /* Must be last! */ struct ieee80211_hw hw __aligned(CACHE_LINE_SIZE); @@ -304,6 +322,19 @@ struct lkpi_wiphy { #define LKPI_80211_LHW_TXQ_UNLOCK_ASSERT(_lhw) \ mtx_assert(&(_lhw)->txq_mtx, MA_NOTOWNED) +#define LKPI_80211_LHW_RXQ_LOCK_INIT(_lhw) \ + mtx_init(&(_lhw)->rxq_mtx, "lhw-rxq", NULL, MTX_DEF | MTX_RECURSE); +#define LKPI_80211_LHW_RXQ_LOCK_DESTROY(_lhw) \ + mtx_destroy(&(_lhw)->rxq_mtx); +#define LKPI_80211_LHW_RXQ_LOCK(_lhw) \ + mtx_lock(&(_lhw)->rxq_mtx) +#define LKPI_80211_LHW_RXQ_UNLOCK(_lhw) \ + mtx_unlock(&(_lhw)->rxq_mtx) +#define LKPI_80211_LHW_RXQ_LOCK_ASSERT(_lhw) \ + mtx_assert(&(_lhw)->rxq_mtx, MA_OWNED) +#define LKPI_80211_LHW_RXQ_UNLOCK_ASSERT(_lhw) \ + mtx_assert(&(_lhw)->rxq_mtx, MA_NOTOWNED) + #define LKPI_80211_LHW_LVIF_LOCK(_lhw) sx_xlock(&(_lhw)->lvif_sx) #define LKPI_80211_LHW_LVIF_UNLOCK(_lhw) sx_xunlock(&(_lhw)->lvif_sx)