Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 Feb 2013 07:10:09 -0800
From:      Adrian Chadd <adrian@freebsd.org>
To:        Monthadar Al Jaberi <monthadar@gmail.com>
Cc:        PseudoCylon <moonlightakkiy@yahoo.ca>, freebsd-wireless@freebsd.org
Subject:   Re: [RFC] serialising net80211 TX
Message-ID:  <CAJ-Vmon=SdO66j99dEPT1uSqhmJoNPYVexy-PT0__XTX3My%2BTQ@mail.gmail.com>
In-Reply-To: <CAJ-VmomHCz2nEd-GEh2JKoYbfAdsG0xYbAt=j-BqW6D5jHxMeQ@mail.gmail.com>
References:  <CAFZ_MYJjV=5FtEmWkO7rRBtAuvn2R0Ec=O0ojhPxBfcBuLRUJQ@mail.gmail.com> <CAJ-VmonAXBxuD51y-j5PEt4uGHO_EX15C3inj9wTmR%2BJnb21LA@mail.gmail.com> <CAFZ_MYLswF_3OvEg=uc5GXUAi=EipXmqj-cAWjRC9xi93V-R1Q@mail.gmail.com> <CAJ-Vmon%2BuSKwkEkeiUsC=Gh%2Bk=uVpZdXM5kTKtP_cmfBD0nwjg@mail.gmail.com> <CAJ-VmomMnZ7EM=bgS9NpM_pYDaLQxFg5k2vd7vrdEa4oYx3XNw@mail.gmail.com> <CAJ-Vmo=g5F06dY0p9mOYxY_%2Bh1bLNdQ7%2BukQToU5eb4E%2BNuBuA@mail.gmail.com> <CA%2BsBSoJZ0uQm_M_OZ-eOSCVFtvu7DeboE28JNbLrY15BTcRRhA@mail.gmail.com> <CAJ-Vmo=sJ3Qh8VU%2B99bNr-a3wm5E6e9c=O_qy3w-6ERkVGj5MA@mail.gmail.com> <CAJ-VmomHCz2nEd-GEh2JKoYbfAdsG0xYbAt=j-BqW6D5jHxMeQ@mail.gmail.com>

index | next in thread | previous in thread | raw e-mail

[-- Attachment #1 --]
Here's my current patch. I've tested it lightly in AP mode.



Adrian

[-- Attachment #2 --]
Index: sys/net80211/ieee80211_hwmp.c
===================================================================
--- sys/net80211/ieee80211_hwmp.c	(revision 246818)
+++ 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);
@@ -669,7 +670,10 @@
 	else
 		params.ibp_try0 = ni->ni_txparms->maxretry;
 	params.ibp_power = ni->ni_txpower;
-	return ic->ic_raw_xmit(ni, m, &params);
+	IEEE80211_VAP_TX_LOCK(vap);
+	ret = ic->ic_raw_xmit(ni, m, &params);
+	IEEE80211_VAP_TX_UNLOCK(vap);
+	return (ret);
 }
 
 #define ADDSHORT(frm, v) do {		\
Index: sys/net80211/ieee80211_ht.c
===================================================================
--- sys/net80211/ieee80211_ht.c	(revision 246818)
+++ sys/net80211/ieee80211_ht.c	(working copy)
@@ -2392,7 +2392,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 246861)
+++ 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"));
 
@@ -2778,7 +2786,10 @@
 
 	IEEE80211_NODE_STAT(ni, tx_mgmt);
 
-	return ic->ic_raw_xmit(ni, m, &params);
+	IEEE80211_VAP_TX_LOCK(vap);
+	ret = ic->ic_raw_xmit(ni, m, &params);
+	IEEE80211_VAP_TX_UNLOCK(vap);
+	return (ret);
 }
 
 #define	ADDSHORT(frm, v) do {			\
Index: sys/net80211/ieee80211_wds.c
===================================================================
--- sys/net80211/ieee80211_wds.c	(revision 246818)
+++ 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 246818)
+++ 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);
@@ -621,6 +668,7 @@
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211_frame *wh;
+	int ret;
 
 	KASSERT(ni != NULL, ("null node"));
 
@@ -670,7 +718,11 @@
 #endif
 	IEEE80211_NODE_STAT(ni, tx_mgmt);
 
-	return ic->ic_raw_xmit(ni, m, params);
+	IEEE80211_VAP_TX_LOCK(vap);
+	ret = ic->ic_raw_xmit(ni, m, params);
+	IEEE80211_VAP_TX_UNLOCK(vap);
+
+	return (ret);
 }
 
 /*
@@ -694,6 +746,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,
@@ -771,7 +824,10 @@
 	    ieee80211_chan2ieee(ic, ic->ic_curchan),
 	    wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
 
-	return ic->ic_raw_xmit(ni, m, NULL);
+	IEEE80211_VAP_TX_LOCK(vap);
+	ret = ic->ic_raw_xmit(ni, m, NULL);
+	IEEE80211_VAP_TX_UNLOCK(vap);
+	return (ret);
 }
 
 /* 
@@ -1035,6 +1091,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 +1864,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,
@@ -1905,7 +1964,10 @@
 	} else
 		params.ibp_try0 = tp->maxretry;
 	params.ibp_power = ni->ni_txpower;
-	return ic->ic_raw_xmit(ni, m, &params);
+	IEEE80211_VAP_TX_LOCK(vap);
+	ret = ic->ic_raw_xmit(ni, m, &params);
+	IEEE80211_VAP_TX_UNLOCK(vap);
+	return (ret);
 }
 
 /*
@@ -2474,6 +2536,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,
@@ -2517,7 +2580,10 @@
 	    legacy ? " <legacy>" : "");
 	IEEE80211_NODE_STAT(bss, tx_mgmt);
 
-	return ic->ic_raw_xmit(bss, m, NULL);
+	IEEE80211_VAP_TX_LOCK(vap);
+	ret = ic->ic_raw_xmit(bss, m, NULL);
+	IEEE80211_VAP_TX_UNLOCK(vap);
+	return (ret);
 }
 
 /*
Index: sys/net80211/ieee80211_superg.c
===================================================================
--- sys/net80211/ieee80211_superg.c	(revision 246818)
+++ 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 246818)
+++ 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 246818)
+++ 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 246818)
+++ 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 246818)
+++ 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 246818)
+++ 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();
help

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAJ-Vmon=SdO66j99dEPT1uSqhmJoNPYVexy-PT0__XTX3My%2BTQ>