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.

MFC after:	3 days
PR:		276083
Reviewed by:	cc
Differential Revision: https://reviews.freebsd.org/D43968
This commit is contained in:
Bjoern A. Zeeb 2024-02-12 16:03:13 +00:00
parent 46a968ecfb
commit 759a996d61
2 changed files with 154 additions and 11 deletions

View file

@ -144,6 +144,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);
@ -4271,6 +4272,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.
@ -4296,11 +4303,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);
@ -4795,6 +4833,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,
@ -5018,20 +5116,34 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
}
#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();

View file

@ -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)