summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ath/if_ath_tx.c105
1 files changed, 71 insertions, 34 deletions
diff --git a/sys/dev/ath/if_ath_tx.c b/sys/dev/ath/if_ath_tx.c
index 9abac87..cae07b5 100644
--- a/sys/dev/ath/if_ath_tx.c
+++ b/sys/dev/ath/if_ath_tx.c
@@ -1171,6 +1171,15 @@ ath_tx_normal_setup(struct ath_softc *sc, struct ieee80211_node *ni,
struct ath_node *an;
u_int pri;
+ /*
+ * To ensure that both sequence numbers and the CCMP PN handling
+ * is "correct", make sure that the relevant TID queue is locked.
+ * Otherwise the CCMP PN and seqno may appear out of order, causing
+ * re-ordered frames to have out of order CCMP PN's, resulting
+ * in many, many frame drops.
+ */
+ ATH_TXQ_LOCK_ASSERT(txq);
+
wh = mtod(m0, struct ieee80211_frame *);
iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
@@ -1506,11 +1515,18 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
/* XXX should just bzero the bf_state? */
bf->bf_state.bfs_dobaw = 0;
+ /*
+ * Acquire the TXQ lock early, so both the encap and seqno
+ * are allocated together.
+ */
+ ATH_TXQ_LOCK(txq);
+
/* A-MPDU TX? Manually set sequence number */
- /* Don't do it whilst pending; the net80211 layer still assigns them */
- /* XXX do we need locking here? */
+ /*
+ * Don't do it whilst pending; the net80211 layer still
+ * assigns them.
+ */
if (is_ampdu_tx) {
- ATH_TXQ_LOCK(txq);
/*
* Always call; this function will
* handle making sure that null data frames
@@ -1526,7 +1542,6 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
subtype != IEEE80211_FC0_SUBTYPE_QOS_NULL) {
bf->bf_state.bfs_dobaw = 1;
}
- ATH_TXQ_UNLOCK(txq);
}
/*
@@ -1545,7 +1560,7 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
r = ath_tx_normal_setup(sc, ni, bf, m0, txq);
if (r != 0)
- return r;
+ goto done;
/* At this point m0 could have changed! */
m0 = bf->bf_m;
@@ -1570,16 +1585,12 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
if (txq == &avp->av_mcastq) {
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: bf=%p: mcastq: TX'ing\n", __func__, bf);
- ATH_TXQ_LOCK(txq);
ath_tx_xmit_normal(sc, txq, bf);
- ATH_TXQ_UNLOCK(txq);
} else if (type == IEEE80211_FC0_TYPE_CTL &&
subtype == IEEE80211_FC0_SUBTYPE_BAR) {
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: BAR: TX'ing direct\n", __func__);
- ATH_TXQ_LOCK(txq);
ath_tx_xmit_normal(sc, txq, bf);
- ATH_TXQ_UNLOCK(txq);
} else {
/* add to software queue */
DPRINTF(sc, ATH_DEBUG_SW_TX,
@@ -1591,10 +1602,10 @@ ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni,
* For now, since there's no software queue,
* direct-dispatch to the hardware.
*/
- ATH_TXQ_LOCK(txq);
ath_tx_xmit_normal(sc, txq, bf);
- ATH_TXQ_UNLOCK(txq);
#endif
+done:
+ ATH_TXQ_UNLOCK(txq);
return 0;
}
@@ -1630,10 +1641,29 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
/* XXX honor IEEE80211_BPF_DATAPAD */
pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN;
-
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: ismcast=%d\n",
__func__, ismcast);
+ pri = params->ibp_pri & 3;
+ /* Override pri if the frame isn't a QoS one */
+ if (! IEEE80211_QOS_HAS_SEQ(wh))
+ pri = ath_tx_getac(sc, m0);
+
+ /* XXX If it's an ADDBA, override the correct queue */
+ do_override = ath_tx_action_frame_override_queue(sc, ni, m0, &o_tid);
+
+ /* Map ADDBA to the correct priority */
+ if (do_override) {
+#if 0
+ device_printf(sc->sc_dev,
+ "%s: overriding tid %d pri %d -> %d\n",
+ __func__, o_tid, pri, TID_TO_WME_AC(o_tid));
+#endif
+ pri = TID_TO_WME_AC(o_tid);
+ }
+
+ ATH_TXQ_LOCK(sc->sc_ac2q[pri]);
+
/* Handle encryption twiddling if needed */
if (! ath_tx_tag_crypto(sc, ni,
m0, params->ibp_flags & IEEE80211_BPF_CRYPTO, 0,
@@ -1688,11 +1718,6 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA))
bf->bf_state.bfs_ctsrate0 = params->ibp_ctsrate;
- pri = params->ibp_pri & 3;
- /* Override pri if the frame isn't a QoS one */
- if (! IEEE80211_QOS_HAS_SEQ(wh))
- pri = ath_tx_getac(sc, m0);
-
/*
* NB: we mark all packets as type PSPOLL so the h/w won't
* set the sequence number, duration, etc.
@@ -1774,19 +1799,6 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
/* NB: no buffered multicast in power save support */
- /* XXX If it's an ADDBA, override the correct queue */
- do_override = ath_tx_action_frame_override_queue(sc, ni, m0, &o_tid);
-
- /* Map ADDBA to the correct priority */
- if (do_override) {
-#if 0
- device_printf(sc->sc_dev,
- "%s: overriding tid %d pri %d -> %d\n",
- __func__, o_tid, pri, TID_TO_WME_AC(o_tid));
-#endif
- pri = TID_TO_WME_AC(o_tid);
- }
-
/*
* If we're overiding the ADDBA destination, dump directly
* into the hardware queue, right after any pending
@@ -1796,13 +1808,12 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni,
__func__, do_override);
if (do_override) {
- ATH_TXQ_LOCK(sc->sc_ac2q[pri]);
ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
- ATH_TXQ_UNLOCK(sc->sc_ac2q[pri]);
} else {
/* Queue to software queue */
ath_tx_swq(sc, ni, sc->sc_ac2q[pri], bf);
}
+ ATH_TXQ_UNLOCK(sc->sc_ac2q[pri]);
return 0;
}
@@ -2032,6 +2043,15 @@ ath_tx_addto_baw(struct ath_softc *sc, struct ath_node *an,
if (bf->bf_state.bfs_isretried)
return;
+ if (! bf->bf_state.bfs_dobaw) {
+ device_printf(sc->sc_dev,
+ "%s: dobaw=0, seqno=%d, window %d:%d\n",
+ __func__,
+ SEQNO(bf->bf_state.bfs_seqno),
+ tap->txa_start,
+ tap->txa_wnd);
+ }
+
tap = ath_tx_get_tx_tid(an, tid->tid);
if (bf->bf_state.bfs_addedbaw)
@@ -2043,6 +2063,20 @@ ath_tx_addto_baw(struct ath_softc *sc, struct ath_node *an,
tid->baw_tail);
/*
+ * Verify that the given sequence number is not outside of the
+ * BAW. Complain loudly if that's the case.
+ */
+ if (! BAW_WITHIN(tap->txa_start, tap->txa_wnd,
+ SEQNO(bf->bf_state.bfs_seqno))) {
+ device_printf(sc->sc_dev,
+ "%s: bf=%p: outside of BAW?? tid=%d, seqno %d; window %d:%d; "
+ "baw head=%d tail=%d\n",
+ __func__, bf, tid->tid, SEQNO(bf->bf_state.bfs_seqno),
+ tap->txa_start, tap->txa_wnd, tid->baw_head,
+ tid->baw_tail);
+ }
+
+ /*
* ni->ni_txseqs[] is the currently allocated seqno.
* the txa state contains the current baw start.
*/
@@ -2265,6 +2299,8 @@ ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni,
if (! IEEE80211_QOS_HAS_SEQ(wh))
return -1;
+ ATH_TID_LOCK_ASSERT(sc, &(ATH_NODE(ni)->an_tid[tid]));
+
/*
* Is it a QOS NULL Data frame? Give it a sequence number from
* the default TID (IEEE80211_NONQOS_TID.)
@@ -2276,6 +2312,7 @@ ath_tx_tid_seqno_assign(struct ath_softc *sc, struct ieee80211_node *ni,
*/
subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
if (subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) {
+ /* XXX no locking for this TID? This is a bit of a problem. */
seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID];
INCR(ni->ni_txseqs[IEEE80211_NONQOS_TID], IEEE80211_SEQ_RANGE);
} else {
@@ -2369,6 +2406,8 @@ ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_txq *txq,
int pri, tid;
struct mbuf *m0 = bf->bf_m;
+ ATH_TXQ_LOCK_ASSERT(txq);
+
/* Fetch the TID - non-QoS frames get assigned to TID 16 */
wh = mtod(m0, struct ieee80211_frame *);
pri = ath_tx_getac(sc, m0);
@@ -2391,7 +2430,6 @@ ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_txq *txq,
* If the TID is paused or the traffic it outside BAW, software
* queue it.
*/
- ATH_TXQ_LOCK(txq);
if (atid->paused) {
/* TID is paused, queue */
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: paused\n", __func__);
@@ -2439,7 +2477,6 @@ ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_txq *txq,
ATH_TXQ_INSERT_TAIL(atid, bf, bf_list);
ath_tx_tid_sched(sc, atid);
}
- ATH_TXQ_UNLOCK(txq);
}
/*
OpenPOWER on IntegriCloud