Index: sys/net80211/ieee80211_hwmp.c =================================================================== --- sys/net80211/ieee80211_hwmp.c (revision 247090) +++ sys/net80211/ieee80211_hwmp.c (working copy) @@ -592,6 +592,7 @@ struct ieee80211_bpf_params params; struct mbuf *m; uint8_t *frm; + int ret; if (IEEE80211_IS_MULTICAST(da)) { ni = ieee80211_ref_node(vap->iv_bss); @@ -654,6 +655,7 @@ vap->iv_stats.is_tx_nobuf++; return ENOMEM; } + IEEE80211_VAP_TX_LOCK(vap); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, IEEE80211_NONQOS_TID, vap->iv_myaddr, da, vap->iv_myaddr); @@ -669,7 +671,9 @@ else params.ibp_try0 = ni->ni_txparms->maxretry; params.ibp_power = ni->ni_txpower; - return ic->ic_raw_xmit(ni, m, ¶ms); + ret = ic->ic_raw_xmit(ni, m, ¶ms); + IEEE80211_VAP_TX_UNLOCK(vap); + return (ret); } #define ADDSHORT(frm, v) do { \ Index: sys/net80211/ieee80211_ht.c =================================================================== --- sys/net80211/ieee80211_ht.c (revision 247090) +++ sys/net80211/ieee80211_ht.c (working copy) @@ -2120,6 +2120,13 @@ dialogtoken = (tokens+1) % 63; /* XXX */ tid = tap->txa_tid; + + /* + * XXX TODO: this should really occur with the VAP TX lock held, + * or there's no real guarantee that we're starting at the + * right place in the sequence space (ie, a parallel TX may + * be in progress and allocate some more sequence numbers. + */ tap->txa_start = ni->ni_txseqs[tid]; args[0] = dialogtoken; @@ -2392,7 +2399,9 @@ * ic_raw_xmit will free the node reference * regardless of queue/TX success or failure. */ + IEEE80211_VAP_TX_LOCK(vap); ret = ic->ic_raw_xmit(ni, m, NULL); + IEEE80211_VAP_TX_UNLOCK(vap); if (ret != 0) { IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N, ni, "send BAR: failed: (ret = %d)\n", Index: sys/net80211/ieee80211_mesh.c =================================================================== --- sys/net80211/ieee80211_mesh.c (revision 247090) +++ sys/net80211/ieee80211_mesh.c (working copy) @@ -1046,6 +1046,8 @@ struct ether_header *eh; int error; + IEEE80211_VAP_TX_UNLOCK_ASSERT(vap); + eh = mtod(m, struct ether_header *); ni = ieee80211_mesh_find_txnode(vap, rt_gate->rt_dest); if (ni == NULL) { @@ -1132,6 +1134,7 @@ } } #endif /* IEEE80211_SUPPORT_SUPERG */ + IEEE80211_VAP_TX_LOCK(vap); if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { /* * Encapsulate the packet in prep for transmission. @@ -1139,11 +1142,13 @@ m = ieee80211_encap(vap, ni, m); if (m == NULL) { /* NB: stat+msg handled in ieee80211_encap */ + IEEE80211_VAP_TX_UNLOCK(vap); ieee80211_free_node(ni); return; } } error = parent->if_transmit(parent, m); + IEEE80211_VAP_TX_UNLOCK(vap); if (error != 0) { m_freem(m); ieee80211_free_node(ni); @@ -1174,6 +1179,8 @@ KASSERT( rt_dest->rt_flags == IEEE80211_MESHRT_FLAGS_DISCOVER, ("Route is not marked with IEEE80211_MESHRT_FLAGS_DISCOVER")); + IEEE80211_VAP_TX_UNLOCK_ASSERT(vap); + /* XXX: send to more than one valid mash gate */ MESH_RT_LOCK(ms); @@ -2743,6 +2750,7 @@ struct ieee80211com *ic = ni->ni_ic; struct ieee80211_bpf_params params; struct ieee80211_frame *wh; + int ret; KASSERT(ni != NULL, ("null node")); @@ -2762,6 +2770,7 @@ } wh = mtod(m, struct ieee80211_frame *); + IEEE80211_VAP_TX_LOCK(vap); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_ACTION, IEEE80211_NONQOS_TID, sa, da, sa); @@ -2778,7 +2787,9 @@ IEEE80211_NODE_STAT(ni, tx_mgmt); - return ic->ic_raw_xmit(ni, m, ¶ms); + ret = ic->ic_raw_xmit(ni, m, ¶ms); + IEEE80211_VAP_TX_UNLOCK(vap); + return (ret); } #define ADDSHORT(frm, v) do { \ Index: sys/net80211/ieee80211_wds.c =================================================================== --- sys/net80211/ieee80211_wds.c (revision 247090) +++ sys/net80211/ieee80211_wds.c (working copy) @@ -254,6 +254,10 @@ if (ifp == m->m_pkthdr.rcvif) continue; /* + * Make sure we don't have the VAP TX lock held here. + */ + IEEE80211_VAP_TX_UNLOCK_ASSERT(vap); + /* * Duplicate the frame and send it. */ mcopy = m_copypacket(m, M_NOWAIT); @@ -287,16 +291,23 @@ /* * Encapsulate the packet in prep for transmission. */ + IEEE80211_VAP_TX_LOCK(vap); mcopy = ieee80211_encap(vap, ni, mcopy); if (mcopy == NULL) { /* NB: stat+msg handled in ieee80211_encap */ ieee80211_free_node(ni); + IEEE80211_VAP_TX_UNLOCK(vap); continue; } mcopy->m_flags |= M_MCAST; mcopy->m_pkthdr.rcvif = (void *) ni; + /* + * Serialise the encapsulation and transmit + * with the VAP TX lock. + */ err = parent->if_transmit(parent, mcopy); + IEEE80211_VAP_TX_UNLOCK(vap); if (err) { /* NB: IFQ_HANDOFF reclaims mbuf */ ifp->if_oerrors++; Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c (revision 247090) +++ sys/net80211/ieee80211_output.c (working copy) @@ -110,6 +110,252 @@ #endif /* + * Send the given mbuf through the given vap. + * + * This consumes the mbuf regardless of whether the transmit + * was successful or not. + * + * This does none of the initial checks that ieee80211_start() + * does (eg CAC timeout, interface wakeup) - the caller must + * do this first. + */ +static int +ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) +{ +#define IS_DWDS(vap) \ + (vap->iv_opmode == IEEE80211_M_WDS && \ + (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *parent = ic->ic_ifp; + struct ifnet *ifp = vap->iv_ifp; + struct ieee80211_node *ni; + struct ether_header *eh; + int error; + + /* + * Sanitize mbuf flags for net80211 use. We cannot + * clear M_PWR_SAV or M_MORE_DATA because these may + * be set for frames that are re-submitted from the + * power save queue. + * + * NB: This must be done before ieee80211_classify as + * it marks EAPOL in frames with M_EAPOL. + */ + m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); + /* + * Cancel any background scan. + */ + if (ic->ic_flags & IEEE80211_F_SCAN) + ieee80211_cancel_anyscan(vap); + /* + * Find the node for the destination so we can do + * things like power save and fast frames aggregation. + * + * NB: past this point various code assumes the first + * mbuf has the 802.3 header present (and contiguous). + */ + ni = NULL; + if (m->m_len < sizeof(struct ether_header) && + (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "discard frame, %s\n", "m_pullup failed"); + vap->iv_stats.is_tx_nobuf++; /* XXX */ + ifp->if_oerrors++; + return (ENOBUFS); + } + eh = mtod(m, struct ether_header *); + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + if (IS_DWDS(vap)) { + /* + * Only unicast frames from the above go out + * DWDS vaps; multicast frames are handled by + * dispatching the frame as it comes through + * the AP vap (see below). + */ + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, + eh->ether_dhost, "mcast", "%s", "on DWDS"); + vap->iv_stats.is_dwds_mcast++; + m_freem(m); + /* XXX better status? */ + return (ENOBUFS); + } + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { + /* + * Spam DWDS vap's w/ multicast traffic. + */ + /* XXX only if dwds in use? */ + ieee80211_dwds_mcast(vap, m); + } + } +#ifdef IEEE80211_SUPPORT_MESH + if (vap->iv_opmode != IEEE80211_M_MBSS) { +#endif + ni = ieee80211_find_txnode(vap, eh->ether_dhost); + if (ni == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + ifp->if_oerrors++; + m_freem(m); + /* XXX better status? */ + return (ENOBUFS); + } + if (ni->ni_associd == 0 && + (ni->ni_flags & IEEE80211_NODE_ASSOCID)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "sta not associated (type 0x%04x)", + htons(eh->ether_type)); + vap->iv_stats.is_tx_notassoc++; + ifp->if_oerrors++; + m_freem(m); + ieee80211_free_node(ni); + /* XXX better status? */ + return (ENOBUFS); + } +#ifdef IEEE80211_SUPPORT_MESH + } else { + if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { + /* + * Proxy station only if configured. + */ + if (!ieee80211_mesh_isproxyena(vap)) { + IEEE80211_DISCARD_MAC(vap, + IEEE80211_MSG_OUTPUT | + IEEE80211_MSG_MESH, + eh->ether_dhost, NULL, + "%s", "proxy not enabled"); + vap->iv_stats.is_mesh_notproxy++; + ifp->if_oerrors++; + m_freem(m); + /* XXX better status? */ + return (ENOBUFS); + } + IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, + "forward frame from DS SA(%6D), DA(%6D)\n", + eh->ether_shost, ":", + eh->ether_dhost, ":"); + ieee80211_mesh_proxy_check(vap, eh->ether_shost); + } + ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); + if (ni == NULL) { + /* + * NB: ieee80211_mesh_discover holds/disposes + * frame (e.g. queueing on path discovery). + */ + ifp->if_oerrors++; + /* XXX better status? */ + return (ENOBUFS); + } + } +#endif + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + (m->m_flags & M_PWR_SAV) == 0) { + /* + * Station in power save mode; pass the frame + * to the 802.11 layer and continue. We'll get + * the frame back when the time is right. + * XXX lose WDS vap linkage? + */ + (void) ieee80211_pwrsave(ni, m); + ieee80211_free_node(ni); + /* XXX better status? */ + return (ENOBUFS); + } + /* calculate priority so drivers can find the tx queue */ + if (ieee80211_classify(ni, m)) { + IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, + eh->ether_dhost, NULL, + "%s", "classification failure"); + vap->iv_stats.is_tx_classify++; + ifp->if_oerrors++; + m_freem(m); + ieee80211_free_node(ni); + /* XXX better status? */ + return (ENOBUFS); + } + /* + * Stash the node pointer. Note that we do this after + * any call to ieee80211_dwds_mcast because that code + * uses any existing value for rcvif to identify the + * interface it (might have been) received on. + */ + m->m_pkthdr.rcvif = (void *)ni; + + BPF_MTAP(ifp, m); /* 802.3 tx */ + + /* + * Check if A-MPDU tx aggregation is setup or if we + * should try to enable it. The sta must be associated + * with HT and A-MPDU enabled for use. When the policy + * routine decides we should enable A-MPDU we issue an + * ADDBA request and wait for a reply. The frame being + * encapsulated will go out w/o using A-MPDU, or possibly + * it might be collected by the driver and held/retransmit. + * The default ic_ampdu_enable routine handles staggering + * ADDBA requests in case the receiver NAK's us or we are + * otherwise unable to establish a BA stream. + */ + if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && + (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) && + (m->m_flags & M_EAPOL) == 0) { + int tid = WME_AC_TO_TID(M_WME_GETAC(m)); + struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; + + ieee80211_txampdu_count_packet(tap); + if (IEEE80211_AMPDU_RUNNING(tap)) { + /* + * Operational, mark frame for aggregation. + * + * XXX do tx aggregation here + */ + m->m_flags |= M_AMPDU_MPDU; + } else if (!IEEE80211_AMPDU_REQUESTED(tap) && + ic->ic_ampdu_enable(ni, tap)) { + /* + * Not negotiated yet, request service. + */ + ieee80211_ampdu_request(ni, tap); + /* XXX hold frame for reply? */ + } + } +#ifdef IEEE80211_SUPPORT_SUPERG + else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { + m = ieee80211_ff_check(ni, m); + if (m == NULL) { + /* NB: any ni ref held on stageq */ + /* XXX better status? */ + return (ENOBUFS); + } + } +#endif /* IEEE80211_SUPPORT_SUPERG */ + IEEE80211_VAP_TX_LOCK(vap); + if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { + /* + * Encapsulate the packet in prep for transmission. + */ + m = ieee80211_encap(vap, ni, m); + if (m == NULL) { + /* NB: stat+msg handled in ieee80211_encap */ + IEEE80211_VAP_TX_UNLOCK(vap); + ieee80211_free_node(ni); + /* XXX better status? */ + return (ENOBUFS); + } + } + error = parent->if_transmit(parent, m); + IEEE80211_VAP_TX_UNLOCK(vap); + if (error != 0) { + /* NB: IFQ_HANDOFF reclaims mbuf */ + ieee80211_free_node(ni); + } else { + ifp->if_opackets++; + } + ic->ic_lastdata = ticks; + + return (0); +#undef IS_DWDS +} + +/* * Start method for vap's. All packets from the stack come * through here. We handle common processing of the packets * before dispatching them to the underlying device. @@ -117,16 +363,10 @@ void ieee80211_start(struct ifnet *ifp) { -#define IS_DWDS(vap) \ - (vap->iv_opmode == IEEE80211_M_WDS && \ - (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) == 0) struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; struct ifnet *parent = ic->ic_ifp; - struct ieee80211_node *ni; struct mbuf *m; - struct ether_header *eh; - int error; /* NB: parent must be up and running */ if (!IFNET_IS_UP_RUNNING(parent)) { @@ -165,218 +405,14 @@ } IEEE80211_UNLOCK(ic); } + for (;;) { IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; - /* - * Sanitize mbuf flags for net80211 use. We cannot - * clear M_PWR_SAV or M_MORE_DATA because these may - * be set for frames that are re-submitted from the - * power save queue. - * - * NB: This must be done before ieee80211_classify as - * it marks EAPOL in frames with M_EAPOL. - */ - m->m_flags &= ~(M_80211_TX - M_PWR_SAV - M_MORE_DATA); - /* - * Cancel any background scan. - */ - if (ic->ic_flags & IEEE80211_F_SCAN) - ieee80211_cancel_anyscan(vap); - /* - * Find the node for the destination so we can do - * things like power save and fast frames aggregation. - * - * NB: past this point various code assumes the first - * mbuf has the 802.3 header present (and contiguous). - */ - ni = NULL; - if (m->m_len < sizeof(struct ether_header) && - (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, - "discard frame, %s\n", "m_pullup failed"); - vap->iv_stats.is_tx_nobuf++; /* XXX */ - ifp->if_oerrors++; - continue; - } - eh = mtod(m, struct ether_header *); - if (ETHER_IS_MULTICAST(eh->ether_dhost)) { - if (IS_DWDS(vap)) { - /* - * Only unicast frames from the above go out - * DWDS vaps; multicast frames are handled by - * dispatching the frame as it comes through - * the AP vap (see below). - */ - IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_WDS, - eh->ether_dhost, "mcast", "%s", "on DWDS"); - vap->iv_stats.is_dwds_mcast++; - m_freem(m); - continue; - } - if (vap->iv_opmode == IEEE80211_M_HOSTAP) { - /* - * Spam DWDS vap's w/ multicast traffic. - */ - /* XXX only if dwds in use? */ - ieee80211_dwds_mcast(vap, m); - } - } -#ifdef IEEE80211_SUPPORT_MESH - if (vap->iv_opmode != IEEE80211_M_MBSS) { -#endif - ni = ieee80211_find_txnode(vap, eh->ether_dhost); - if (ni == NULL) { - /* NB: ieee80211_find_txnode does stat+msg */ - ifp->if_oerrors++; - m_freem(m); - continue; - } - if (ni->ni_associd == 0 && - (ni->ni_flags & IEEE80211_NODE_ASSOCID)) { - IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, - eh->ether_dhost, NULL, - "sta not associated (type 0x%04x)", - htons(eh->ether_type)); - vap->iv_stats.is_tx_notassoc++; - ifp->if_oerrors++; - m_freem(m); - ieee80211_free_node(ni); - continue; - } -#ifdef IEEE80211_SUPPORT_MESH - } else { - if (!IEEE80211_ADDR_EQ(eh->ether_shost, vap->iv_myaddr)) { - /* - * Proxy station only if configured. - */ - if (!ieee80211_mesh_isproxyena(vap)) { - IEEE80211_DISCARD_MAC(vap, - IEEE80211_MSG_OUTPUT | - IEEE80211_MSG_MESH, - eh->ether_dhost, NULL, - "%s", "proxy not enabled"); - vap->iv_stats.is_mesh_notproxy++; - ifp->if_oerrors++; - m_freem(m); - continue; - } - IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, - "forward frame from DS SA(%6D), DA(%6D)\n", - eh->ether_shost, ":", - eh->ether_dhost, ":"); - ieee80211_mesh_proxy_check(vap, eh->ether_shost); - } - ni = ieee80211_mesh_discover(vap, eh->ether_dhost, m); - if (ni == NULL) { - /* - * NB: ieee80211_mesh_discover holds/disposes - * frame (e.g. queueing on path discovery). - */ - ifp->if_oerrors++; - continue; - } - } -#endif - if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && - (m->m_flags & M_PWR_SAV) == 0) { - /* - * Station in power save mode; pass the frame - * to the 802.11 layer and continue. We'll get - * the frame back when the time is right. - * XXX lose WDS vap linkage? - */ - (void) ieee80211_pwrsave(ni, m); - ieee80211_free_node(ni); - continue; - } - /* calculate priority so drivers can find the tx queue */ - if (ieee80211_classify(ni, m)) { - IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_OUTPUT, - eh->ether_dhost, NULL, - "%s", "classification failure"); - vap->iv_stats.is_tx_classify++; - ifp->if_oerrors++; - m_freem(m); - ieee80211_free_node(ni); - continue; - } - /* - * Stash the node pointer. Note that we do this after - * any call to ieee80211_dwds_mcast because that code - * uses any existing value for rcvif to identify the - * interface it (might have been) received on. - */ - m->m_pkthdr.rcvif = (void *)ni; - - BPF_MTAP(ifp, m); /* 802.3 tx */ - - /* - * Check if A-MPDU tx aggregation is setup or if we - * should try to enable it. The sta must be associated - * with HT and A-MPDU enabled for use. When the policy - * routine decides we should enable A-MPDU we issue an - * ADDBA request and wait for a reply. The frame being - * encapsulated will go out w/o using A-MPDU, or possibly - * it might be collected by the driver and held/retransmit. - * The default ic_ampdu_enable routine handles staggering - * ADDBA requests in case the receiver NAK's us or we are - * otherwise unable to establish a BA stream. - */ - if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) && - (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX) && - (m->m_flags & M_EAPOL) == 0) { - int tid = WME_AC_TO_TID(M_WME_GETAC(m)); - struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid]; - - ieee80211_txampdu_count_packet(tap); - if (IEEE80211_AMPDU_RUNNING(tap)) { - /* - * Operational, mark frame for aggregation. - * - * XXX do tx aggregation here - */ - m->m_flags |= M_AMPDU_MPDU; - } else if (!IEEE80211_AMPDU_REQUESTED(tap) && - ic->ic_ampdu_enable(ni, tap)) { - /* - * Not negotiated yet, request service. - */ - ieee80211_ampdu_request(ni, tap); - /* XXX hold frame for reply? */ - } - } -#ifdef IEEE80211_SUPPORT_SUPERG - else if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_NODE_FF)) { - m = ieee80211_ff_check(ni, m); - if (m == NULL) { - /* NB: any ni ref held on stageq */ - continue; - } - } -#endif /* IEEE80211_SUPPORT_SUPERG */ - if (__predict_true((vap->iv_caps & IEEE80211_C_8023ENCAP) == 0)) { - /* - * Encapsulate the packet in prep for transmission. - */ - m = ieee80211_encap(vap, ni, m); - if (m == NULL) { - /* NB: stat+msg handled in ieee80211_encap */ - ieee80211_free_node(ni); - continue; - } - } - error = parent->if_transmit(parent, m); - if (error != 0) { - /* NB: IFQ_HANDOFF reclaims mbuf */ - ieee80211_free_node(ni); - } else { - ifp->if_opackets++; - } - ic->ic_lastdata = ticks; + (void) ieee80211_start_pkt(vap, m); + /* mbuf is consumed here */ } -#undef IS_DWDS } /* @@ -393,6 +429,7 @@ struct ieee80211vap *vap; struct ieee80211_frame *wh; int error; + int ret; IFQ_LOCK(&ifp->if_snd); if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { @@ -490,14 +527,24 @@ IEEE80211_NODE_STAT_ADD(ni, tx_bytes, m->m_pkthdr.len); /* + * Serialise the order of raw frames with the rest of + * the normal stack, so things such as sequence and + * crypto state assignment are done in the same order + * as are passed to the driver (either by if_transmit() + * or ic_raw_xmit()). + */ + IEEE80211_VAP_TX_LOCK(vap); + /* * NB: DLT_IEEE802_11_RADIO identifies the parameters are * present by setting the sa_len field of the sockaddr (yes, * this is a hack). * NB: we assume sa_data is suitably aligned to cast. */ - return vap->iv_ic->ic_raw_xmit(ni, m, + ret = vap->iv_ic->ic_raw_xmit(ni, m, (const struct ieee80211_bpf_params *)(dst->sa_len ? dst->sa_data : NULL)); + IEEE80211_VAP_TX_UNLOCK(vap); + return (ret); bad: if (m != NULL) m_freem(m); @@ -528,6 +575,8 @@ struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *); ieee80211_seq seqno; + IEEE80211_VAP_TX_LOCK_ASSERT(vap); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { switch (vap->iv_opmode) { @@ -621,6 +670,7 @@ struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame *wh; + int ret; KASSERT(ni != NULL, ("null node")); @@ -643,6 +693,7 @@ } wh = mtod(m, struct ieee80211_frame *); + IEEE80211_VAP_TX_LOCK(vap); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | type, IEEE80211_NONQOS_TID, vap->iv_myaddr, ni->ni_macaddr, ni->ni_bssid); @@ -670,7 +721,10 @@ #endif IEEE80211_NODE_STAT(ni, tx_mgmt); - return ic->ic_raw_xmit(ni, m, params); + ret = ic->ic_raw_xmit(ni, m, params); + IEEE80211_VAP_TX_UNLOCK(vap); + + return (ret); } /* @@ -694,6 +748,7 @@ struct ieee80211_frame *wh; int hdrlen; uint8_t *frm; + int ret; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT | IEEE80211_MSG_DOTH, @@ -730,6 +785,7 @@ } wh = mtod(m, struct ieee80211_frame *); /* NB: a little lie */ + IEEE80211_VAP_TX_LOCK(vap); if (ni->ni_flags & IEEE80211_NODE_QOS) { const int tid = WME_AC_TO_TID(WME_AC_BE); uint8_t *qos; @@ -771,7 +827,9 @@ ieee80211_chan2ieee(ic, ic->ic_curchan), wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis"); - return ic->ic_raw_xmit(ni, m, NULL); + ret = ic->ic_raw_xmit(ni, m, NULL); + IEEE80211_VAP_TX_UNLOCK(vap); + return (ret); } /* @@ -1035,6 +1093,8 @@ int meshhdrsize, meshae; uint8_t *qos; + IEEE80211_VAP_TX_LOCK_ASSERT(vap); + /* * Copy existing Ethernet header to a safe place. The * rest of the code assumes it's ok to strip it when @@ -1806,6 +1866,7 @@ const struct ieee80211_rateset *rs; struct mbuf *m; uint8_t *frm; + int ret; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, @@ -1879,6 +1940,7 @@ } wh = mtod(m, struct ieee80211_frame *); + IEEE80211_VAP_TX_LOCK(vap); ieee80211_send_setup(ni, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, IEEE80211_NONQOS_TID, sa, da, bssid); @@ -1905,7 +1967,9 @@ } else params.ibp_try0 = tp->maxretry; params.ibp_power = ni->ni_txpower; - return ic->ic_raw_xmit(ni, m, ¶ms); + ret = ic->ic_raw_xmit(ni, m, ¶ms); + IEEE80211_VAP_TX_UNLOCK(vap); + return (ret); } /* @@ -2474,6 +2538,7 @@ struct ieee80211com *ic = vap->iv_ic; struct ieee80211_frame *wh; struct mbuf *m; + int ret; if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, bss, @@ -2503,6 +2568,7 @@ KASSERT(m != NULL, ("no room for header")); wh = mtod(m, struct ieee80211_frame *); + IEEE80211_VAP_TX_LOCK(vap); ieee80211_send_setup(bss, m, IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP, IEEE80211_NONQOS_TID, vap->iv_myaddr, da, bss->ni_bssid); @@ -2517,7 +2583,9 @@ legacy ? " " : ""); IEEE80211_NODE_STAT(bss, tx_mgmt); - return ic->ic_raw_xmit(bss, m, NULL); + ret = ic->ic_raw_xmit(bss, m, NULL); + IEEE80211_VAP_TX_UNLOCK(vap); + return (ret); } /* @@ -2898,6 +2966,19 @@ struct ieee80211_frame *wh; ieee80211_seq seqno; + IEEE80211_VAP_TX_UNLOCK_ASSERT(vap); + + /* + * Grab the beacon sequence number here, outside of the ic lock. + * + * This does mean that we will end up with gaps in the sequence + * number space during a CSA, but that's not really a concern + * at this point as we're about to change channels. + */ + IEEE80211_VAP_TX_LOCK(vap); + seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; + IEEE80211_VAP_TX_UNLOCK(vap); + IEEE80211_LOCK(ic); /* * Handle 11h channel change when we've reached the count. @@ -2929,7 +3010,7 @@ } wh = mtod(m, struct ieee80211_frame *); - seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; + /* XXX seqno previously allocated */ *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); Index: sys/net80211/ieee80211_superg.c =================================================================== --- sys/net80211/ieee80211_superg.c (revision 247090) +++ sys/net80211/ieee80211_superg.c (working copy) @@ -382,6 +382,12 @@ struct mbuf *m; int pad; + /* + * This is called from ieee80211_encap() - so the TX lock + * is required. + */ + IEEE80211_VAP_TX_LOCK_ASSERT(vap); + m2 = m1->m_nextpkt; if (m2 == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, @@ -503,21 +509,27 @@ struct ieee80211vap *vap = ni->ni_vap; int error; + IEEE80211_VAP_TX_UNLOCK_ASSERT(vap); + /* encap and xmit */ + IEEE80211_VAP_TX_LOCK(vap); m = ieee80211_encap(vap, ni, m); if (m != NULL) { struct ifnet *ifp = vap->iv_ifp; struct ifnet *parent = ni->ni_ic->ic_ifp; error = parent->if_transmit(parent, m); + IEEE80211_VAP_TX_UNLOCK(vap); if (error != 0) { /* NB: IFQ_HANDOFF reclaims mbuf */ ieee80211_free_node(ni); } else { ifp->if_opackets++; } - } else + } else { + IEEE80211_VAP_TX_UNLOCK(vap); ieee80211_free_node(ni); + } } /* @@ -679,6 +691,8 @@ struct mbuf *mstaged; uint32_t txtime, limit; + IEEE80211_VAP_TX_UNLOCK_ASSERT(vap); + /* * Check if the supplied frame can be aggregated. * Index: sys/net80211/ieee80211_freebsd.h =================================================================== --- sys/net80211/ieee80211_freebsd.h (revision 247090) +++ sys/net80211/ieee80211_freebsd.h (working copy) @@ -160,6 +160,32 @@ #define IEEE80211_SCAN_TABLE_UNLOCK(_st) mtx_unlock(&(_st)->st_lock) /* + * VAP transmit state lock definition. + */ +typedef struct { + char name[16]; /* e.g. "wlan0_tx_lock" */ + struct mtx mtx; +} ieee80211_vap_tx_lock_t; +#define IEEE80211_VAP_TX_LOCK_INIT(_vap, _name) do { \ + ieee80211_vap_tx_lock_t *txl = &(_vap)->iv_tx_lock; \ + snprintf(txl->name, sizeof(txl->name), "%s_tx_lock", _name); \ + mtx_init(&txl->mtx, txl->name, NULL, MTX_DEF); \ +} while (0) +#define IEEE80211_VAP_TX_LOCK_OBJ(_txl) (&(_txl)->iv_tx_lock.mtx) +#define IEEE80211_VAP_TX_LOCK_DESTROY(_txl) \ + mtx_destroy(IEEE80211_VAP_TX_LOCK_OBJ(_txl)) +#define IEEE80211_VAP_TX_LOCK(_txl) \ + mtx_lock(IEEE80211_VAP_TX_LOCK_OBJ(_txl)) +#define IEEE80211_VAP_TX_IS_LOCKED(_txl) \ + mtx_owned(IEEE80211_VAP_TX_LOCK_OBJ(_txl)) +#define IEEE80211_VAP_TX_UNLOCK(_txl) \ + mtx_unlock(IEEE80211_VAP_TX_LOCK_OBJ(_txl)) +#define IEEE80211_VAP_TX_LOCK_ASSERT(_txl) \ + mtx_assert(IEEE80211_VAP_TX_LOCK_OBJ(_txl), MA_OWNED) +#define IEEE80211_VAP_TX_UNLOCK_ASSERT(_txl) \ + mtx_assert(IEEE80211_VAP_TX_LOCK_OBJ(_txl), MA_NOTOWNED) + +/* * Node reference counting definitions. * * ieee80211_node_initref initialize the reference count to 1 Index: sys/net80211/ieee80211_power.c =================================================================== --- sys/net80211/ieee80211_power.c (revision 247090) +++ sys/net80211/ieee80211_power.c (working copy) @@ -418,10 +418,13 @@ struct ifnet *parent, *ifp; struct mbuf *parent_q = NULL, *ifp_q = NULL; struct mbuf *m; + int ret; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "flush ps queue, %u packets queued", psq->psq_len); + IEEE80211_VAP_TX_UNLOCK_ASSERT(vap); + IEEE80211_PSQ_LOCK(psq); qhead = &psq->psq_head[0]; /* 802.11 frames */ if (qhead->head != NULL) { @@ -463,7 +466,10 @@ * For encaped frames, we need to free the node * reference upon failure. */ - if (parent->if_transmit(parent, m) != 0) + IEEE80211_VAP_TX_LOCK(vap); + ret = parent->if_transmit(parent, m); + IEEE80211_VAP_TX_UNLOCK(vap); + if (ret != 0) ieee80211_free_node(ni); } } Index: sys/net80211/ieee80211_var.h =================================================================== --- sys/net80211/ieee80211_var.h (revision 247090) +++ sys/net80211/ieee80211_var.h (working copy) @@ -342,6 +342,8 @@ struct sysctl_ctx_list *iv_sysctl; /* dynamic sysctl context */ struct sysctl_oid *iv_oid; /* net.wlan.X sysctl oid */ + ieee80211_vap_tx_lock_t iv_tx_lock; /* per-VAP TX serialisation */ + TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */ struct ieee80211com *iv_ic; /* back ptr to common state */ uint32_t iv_debug; /* debug msg flags */ @@ -497,6 +499,7 @@ /* 802.3 output method for raw frame xmit */ int (*iv_output)(struct ifnet *, struct mbuf *, struct sockaddr *, struct route *); + uint64_t iv_spare[6]; }; MALLOC_DECLARE(M_80211_VAP); Index: sys/net80211/ieee80211_hostap.c =================================================================== --- sys/net80211/ieee80211_hostap.c (revision 247090) +++ sys/net80211/ieee80211_hostap.c (working copy) @@ -354,6 +354,8 @@ struct ether_header *eh = mtod(m, struct ether_header *); struct ifnet *ifp = vap->iv_ifp; + IEEE80211_VAP_TX_UNLOCK_ASSERT(vap); + /* clear driver/net80211 flags before passing up */ m->m_flags &= ~(M_80211_RX | M_MCAST | M_BCAST); @@ -412,6 +414,7 @@ if (mcopy != NULL) { int len, err; len = mcopy->m_pkthdr.len; + /* VAP transmit */ err = ifp->if_transmit(ifp, mcopy); if (err) { /* NB: IFQ_HANDOFF reclaims mcopy */ @@ -437,6 +440,7 @@ m->m_pkthdr.ether_vtag = ni->ni_vlan; m->m_flags |= M_VLANTAG; } + /* VAP transmit */ ifp->if_input(ifp, m); } } Index: sys/net80211/ieee80211.c =================================================================== --- sys/net80211/ieee80211.c (revision 247090) +++ sys/net80211/ieee80211.c (working copy) @@ -511,6 +511,8 @@ IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); + IEEE80211_VAP_TX_LOCK_INIT(vap, if_name(vap->iv_ifp)); + ieee80211_sysctl_vattach(vap); ieee80211_crypto_vattach(vap); ieee80211_node_vattach(vap); @@ -657,6 +659,8 @@ ieee80211_node_vdetach(vap); ieee80211_sysctl_vdetach(vap); + IEEE80211_VAP_TX_LOCK_DESTROY(vap); + if_free(ifp); CURVNET_RESTORE();