Index: sys/net80211/ieee80211_freebsd.h =================================================================== --- sys/net80211/ieee80211_freebsd.h +++ sys/net80211/ieee80211_freebsd.h @@ -143,6 +143,31 @@ #define IEEE80211_NODE_LOCK_ASSERT(_nt) \ mtx_assert(IEEE80211_NODE_LOCK_OBJ(_nt), MA_OWNED) +/* + * Node TX queue locking. + */ +typedef struct { + char name[16]; /* e.g. "node_txq_lock" */ + struct mtx mtx; +} ieee80211_node_txq_lock_t; +#define IEEE80211_NODE_TXQ_LOCK_INIT(_ni, _name_ext) do { \ + ieee80211_node_txq_lock_t *ntxql = &(_ni)->ni_txq_lock; \ + snprintf(ntxql->name, sizeof(ntxql->name), "node_txq_lock"); \ + mtx_init(&ntxql->mtx, ntxql->name, NULL, MTX_DEF); \ +} while (0) +#define IEEE80211_NODE_TXQ_LOCK_OBJ(_ni) (&(_ni)->ni_txq_lock.mtx) +#define IEEE80211_NODE_TXQ_LOCK_DESTROY(_ni) \ + mtx_destroy(IEEE80211_NODE_TXQ_LOCK_OBJ(_ni)) +#define IEEE80211_NODE_TXQ_LOCK(_ni) \ + mtx_lock(IEEE80211_NODE_TXQ_LOCK_OBJ(_ni)) +#define IEEE80211_NODE_TXQ_IS_LOCKED(_ni) \ + mtx_owned(IEEE80211_NODE_TXQ_LOCK_OBJ(_ni)) +#define IEEE80211_NODE_TXQ_UNLOCK(_ni) \ + mtx_unlock(IEEE80211_NODE_TXQ_LOCK_OBJ(_ni)) +#define IEEE80211_NODE_TXQ_LOCK_ASSERT(_ni) \ + mtx_assert(IEEE80211_NODE_TXQ_LOCK_OBJ(_ni), MA_OWNED) + + /* * Power-save queue definitions. */ Index: sys/net80211/ieee80211_node.h =================================================================== --- sys/net80211/ieee80211_node.h +++ sys/net80211/ieee80211_node.h @@ -260,6 +260,13 @@ /* U-APSD */ uint8_t ni_uapsd; /* U-APSD per-node flags matching WMM STA QoS Info field */ + /* TX path. */ + /* Eventually we might want to migrate this into net80211 entirely. */ + struct mbufq ni_txq; /* HW driver managed TX queue. */ + ieee80211_node_txq_lock_t ni_txq_lock; /* protects ni_txq, need to hold ni reference per packet. */ + + void *drv_data; /* driver specific */ + uint64_t ni_spare[3]; }; MALLOC_DECLARE(M_80211_NODE); Index: sys/net80211/ieee80211_node.c =================================================================== --- sys/net80211/ieee80211_node.c +++ sys/net80211/ieee80211_node.c @@ -1272,6 +1272,19 @@ ieee80211_node_delucastkey(ni); } +static void +ieee80211_node_txq_free(struct ieee80211_node *ni) +{ + + /* + * Given we need to hold a reference for each queued packet + * we cannot get here with any packets queued. + */ + KASSERT(mbufq_len(&ni->ni_txq) == 0, ("%s: ni %p has txq len %d != 0\n", + __func__, ni, mbufq_len(&ni->ni_txq))); + IEEE80211_NODE_TXQ_LOCK_DESTROY(ni); +} + static void node_free(struct ieee80211_node *ni) { @@ -1281,6 +1294,7 @@ ic->ic_node_cleanup(ni); ieee80211_ies_cleanup(&ni->ni_ies); ieee80211_psq_cleanup(&ni->ni_psq); + ieee80211_node_txq_free(ni); IEEE80211_FREE(ni, M_80211_NODE); } @@ -1414,6 +1428,8 @@ if (vap->iv_opmode == IEEE80211_M_MBSS) ieee80211_mesh_node_init(vap, ni); #endif + IEEE80211_NODE_TXQ_LOCK_INIT(ni, "unused"); + mbufq_init(&ni->ni_txq, IFQ_MAXLEN); IEEE80211_NODE_LOCK(nt); ieee80211_add_node_nt(nt, ni); ni->ni_vap = vap;