svn commit: r249738 - user/adrian/net80211_tx/sys/dev/ath
Adrian Chadd
adrian at FreeBSD.org
Sun Apr 21 19:33:56 UTC 2013
Author: adrian
Date: Sun Apr 21 19:33:55 2013
New Revision: 249738
URL: http://svnweb.freebsd.org/changeset/base/249738
Log:
Bring over my initial power save queue work.
Since this is a little more complicated than I thought, I'll need some
subversion history for this.
So far:
* Do node pause/unpause when the stack enters/leaves powersave for
the given station;
* Remove the use of ath_node lock outside of the rate control code -
now the rest of the ath_node stat is protected by the tx lock;
* When a node reassociates, a bunch of frames are in the software
queue and hardware queue. We need to tidy that up - so for now,
just go through the normal cleanup path. But that may not work
in the long run.
* .. and whilst doing this, reset some of the state (eg BAR TX
state, filtered frames, etc.)
* .. this still doesn't entirely close the queue stalls; but they
may be BAR TX related rather than this particular work.
* Add support for leaking frames out whilst the node is in sleep
state. That way I can do interesting things for PS-POLL.
* Tie into the PS-POLL hook from net80211; leak out a software queued
frame if we have any; else just mark the node as ready to leak
a frame and leave it up to the stack.
I'm still looking into whether there's any possible races with
ps-poll handling - notably, if this happens:
* STA sends PS-POLL
* Stack generates a null frame response;
* .. but in another thread, the stack gets handed a frame;
* .. and that thread wins the transmit race and it gets queued
out first.
Now hopefully the TX path locking in net80211 stops this from occuring.
But still..
Modified:
user/adrian/net80211_tx/sys/dev/ath/if_ath.c
user/adrian/net80211_tx/sys/dev/ath/if_ath_tx.c
user/adrian/net80211_tx/sys/dev/ath/if_ath_tx.h
user/adrian/net80211_tx/sys/dev/ath/if_ath_tx_ht.c
user/adrian/net80211_tx/sys/dev/ath/if_ath_tx_ht.h
user/adrian/net80211_tx/sys/dev/ath/if_athvar.h
Modified: user/adrian/net80211_tx/sys/dev/ath/if_ath.c
==============================================================================
--- user/adrian/net80211_tx/sys/dev/ath/if_ath.c Sun Apr 21 19:24:37 2013 (r249737)
+++ user/adrian/net80211_tx/sys/dev/ath/if_ath.c Sun Apr 21 19:33:55 2013 (r249738)
@@ -125,7 +125,7 @@ __FBSDID("$FreeBSD$");
/*
* Only enable this if you're working on PS-POLL support.
*/
-#undef ATH_SW_PSQ
+#define ATH_SW_PSQ
/*
* ATH_BCBUF determines the number of vap's that can transmit
@@ -212,6 +212,7 @@ static void ath_announce(struct ath_soft
static void ath_dfs_tasklet(void *, int);
static void ath_node_powersave(struct ieee80211_node *, int);
static int ath_node_set_tim(struct ieee80211_node *, int);
+static void ath_node_recv_pspoll(struct ieee80211_node *, struct mbuf *);
#ifdef IEEE80211_SUPPORT_TDMA
#include <dev/ath/if_ath_tdma.h>
@@ -1241,6 +1242,9 @@ ath_vap_create(struct ieee80211com *ic,
avp->av_set_tim = vap->iv_set_tim;
vap->iv_set_tim = ath_node_set_tim;
+ avp->av_recv_pspoll = vap->iv_recv_pspoll;
+ vap->iv_recv_pspoll = ath_node_recv_pspoll;
+
/* Set default parameters */
/*
@@ -3378,7 +3382,7 @@ ath_node_alloc(struct ieee80211vap *vap,
/* XXX setup ath_tid */
ath_tx_tid_init(sc, an);
- DPRINTF(sc, ATH_DEBUG_NODE, "%s: an %p\n", __func__, an);
+ DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__, mac, ":", an);
return &an->an_node;
}
@@ -3388,6 +3392,9 @@ ath_node_cleanup(struct ieee80211_node *
struct ieee80211com *ic = ni->ni_ic;
struct ath_softc *sc = ic->ic_ifp->if_softc;
+ DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__,
+ ni->ni_macaddr, ":", ATH_NODE(ni));
+
/* Cleanup ath_tid, free unused bufs, unlink bufs in TXQ */
ath_tx_node_flush(sc, ATH_NODE(ni));
ath_rate_node_cleanup(sc, ATH_NODE(ni));
@@ -3400,7 +3407,8 @@ ath_node_free(struct ieee80211_node *ni)
struct ieee80211com *ic = ni->ni_ic;
struct ath_softc *sc = ic->ic_ifp->if_softc;
- DPRINTF(sc, ATH_DEBUG_NODE, "%s: ni %p\n", __func__, ni);
+ DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: an %p\n", __func__,
+ ni->ni_macaddr, ":", ATH_NODE(ni));
mtx_destroy(&ATH_NODE(ni)->an_mtx);
sc->sc_node_free(ni);
}
@@ -3773,8 +3781,11 @@ ath_tx_default_comp(struct ath_softc *sc
* XXX TODO: during drain, ensure that the callback is
* being called so we get a chance to update the TIM.
*/
- if (bf->bf_node)
+ if (bf->bf_node) {
+ ATH_TX_LOCK(sc);
ath_tx_update_tim(sc, bf->bf_node, 0);
+ ATH_TX_UNLOCK(sc);
+ }
/*
* Do any tx complete callback. Note this must
@@ -5220,6 +5231,31 @@ ath_newassoc(struct ieee80211_node *ni,
(vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey &&
ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
ath_setup_stationkey(ni);
+
+ /*
+ * If we're reassociating, make sure that any paused queues
+ * get unpaused.
+ *
+ * Now, we may hvae frames in the hardware queue for this node.
+ * So if we are reassociating and there are frames in the queue,
+ * we need to go through the cleanup path to ensure that they're
+ * marked as non-aggregate.
+ */
+ if (! isnew) {
+ device_printf(sc->sc_dev,
+ "%s: %6D: reassoc; is_powersave=%d\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ an->an_is_powersave);
+
+ /* XXX for now, we can't hold the lock across assoc */
+ ath_tx_node_reassoc(sc, an);
+
+ /* XXX for now, we can't hold the lock across wakeup */
+ if (an->an_is_powersave)
+ ath_tx_node_wakeup(sc, an);
+ }
}
static int
@@ -5732,11 +5768,13 @@ ath_node_powersave(struct ieee80211_node
struct ath_softc *sc = ic->ic_ifp->if_softc;
struct ath_vap *avp = ATH_VAP(ni->ni_vap);
- ATH_NODE_UNLOCK_ASSERT(an);
/* XXX and no TXQ locks should be held here */
- DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: ni=%p, enable=%d\n",
- __func__, ni, enable);
+ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE, "%s: %6D: enable=%d\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ !! enable);
/* Suspend or resume software queue handling */
if (enable)
@@ -5799,12 +5837,7 @@ ath_node_set_tim(struct ieee80211_node *
struct ath_vap *avp = ATH_VAP(ni->ni_vap);
int changed = 0;
- ATH_NODE_UNLOCK_ASSERT(an);
-
- /*
- * For now, just track and then update the TIM.
- */
- ATH_NODE_LOCK(an);
+ ATH_TX_LOCK(sc);
an->an_stack_psq = enable;
/*
@@ -5815,7 +5848,7 @@ ath_node_set_tim(struct ieee80211_node *
* and AP/IBSS node power save.
*/
if (avp->av_set_tim == NULL) {
- ATH_NODE_UNLOCK(an);
+ ATH_TX_UNLOCK(sc);
return (0);
}
@@ -5835,33 +5868,45 @@ ath_node_set_tim(struct ieee80211_node *
*/
if (enable && an->an_tim_set == 1) {
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
- "%s: an=%p, enable=%d, tim_set=1, ignoring\n",
- __func__, an, enable);
- ATH_NODE_UNLOCK(an);
+ "%s: %6D: enable=%d, tim_set=1, ignoring\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ enable);
+ ATH_TX_UNLOCK(sc);
} else if (enable) {
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
- "%s: an=%p, enable=%d, enabling TIM\n",
- __func__, an, enable);
+ "%s: %6D: enable=%d, enabling TIM\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ enable);
an->an_tim_set = 1;
- ATH_NODE_UNLOCK(an);
+ ATH_TX_UNLOCK(sc);
changed = avp->av_set_tim(ni, enable);
} else if (atomic_load_acq_int(&an->an_swq_depth) == 0) {
/* disable */
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
- "%s: an=%p, enable=%d, an_swq_depth == 0, disabling\n",
- __func__, an, enable);
+ "%s: %6D: enable=%d, an_swq_depth == 0, disabling\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ enable);
an->an_tim_set = 0;
- ATH_NODE_UNLOCK(an);
+ ATH_TX_UNLOCK(sc);
changed = avp->av_set_tim(ni, enable);
} else if (! an->an_is_powersave) {
/*
* disable regardless; the node isn't in powersave now
*/
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
- "%s: an=%p, enable=%d, an_pwrsave=0, disabling\n",
- __func__, an, enable);
+ "%s: %6D: enable=%d, an_pwrsave=0, disabling\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ enable);
an->an_tim_set = 0;
- ATH_NODE_UNLOCK(an);
+ ATH_TX_UNLOCK(sc);
changed = avp->av_set_tim(ni, enable);
} else {
/*
@@ -5869,10 +5914,13 @@ ath_node_set_tim(struct ieee80211_node *
* software queue isn't empty, so don't clear the TIM bit
* for now.
*/
- ATH_NODE_UNLOCK(an);
+ ATH_TX_UNLOCK(sc);
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
- "%s: enable=%d, an_swq_depth > 0, ignoring\n",
- __func__, enable);
+ "%s: %6D: enable=%d, an_swq_depth > 0, ignoring\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ enable);
changed = 0;
}
@@ -5934,7 +5982,7 @@ ath_tx_update_tim(struct ath_softc *sc,
if (avp->av_set_tim == NULL)
return;
- ATH_NODE_UNLOCK_ASSERT(an);
+ ATH_TX_LOCK_ASSERT(sc);
if (enable) {
/*
@@ -5944,18 +5992,16 @@ ath_tx_update_tim(struct ath_softc *sc,
if (atomic_load_acq_int(&an->an_swq_depth) == 0)
return;
- ATH_NODE_LOCK(an);
if (an->an_is_powersave &&
an->an_tim_set == 0 &&
atomic_load_acq_int(&an->an_swq_depth) != 0) {
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
- "%s: an=%p, swq_depth>0, tim_set=0, set!\n",
- __func__, an);
+ "%s: %6D: swq_depth>0, tim_set=0, set!\n",
+ __func__,
+ ni->ni_macaddr,
+ ":");
an->an_tim_set = 1;
- ATH_NODE_UNLOCK(an);
(void) avp->av_set_tim(ni, 1);
- } else {
- ATH_NODE_UNLOCK(an);
}
} else {
/*
@@ -5964,20 +6010,18 @@ ath_tx_update_tim(struct ath_softc *sc,
if (atomic_load_acq_int(&an->an_swq_depth) != 0)
return;
- ATH_NODE_LOCK(an);
if (an->an_is_powersave &&
an->an_stack_psq == 0 &&
an->an_tim_set == 1 &&
atomic_load_acq_int(&an->an_swq_depth) == 0) {
DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
- "%s: an=%p, swq_depth=0, tim_set=1, psq_set=0,"
+ "%s: %6D: swq_depth=0, tim_set=1, psq_set=0,"
" clear!\n",
- __func__, an);
+ __func__,
+ ni->ni_macaddr,
+ ":");
an->an_tim_set = 0;
- ATH_NODE_UNLOCK(an);
(void) avp->av_set_tim(ni, 0);
- } else {
- ATH_NODE_UNLOCK(an);
}
}
#else
@@ -5985,6 +6029,150 @@ ath_tx_update_tim(struct ath_softc *sc,
#endif /* ATH_SW_PSQ */
}
+/*
+ * Received a ps-poll frame from net80211.
+ *
+ * Here we get a chance to serve out a software-queued frame ourselves
+ * before we punt it to net80211 to transmit us one itself - either
+ * because there's traffic in the net80211 psq, or a NULL frame to
+ * indicate there's nothing else.
+ */
+static void
+ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m)
+{
+ struct ath_node *an;
+ struct ath_vap *avp;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ath_softc *sc = ic->ic_ifp->if_softc;
+ int tid;
+
+ /* Just paranoia */
+ if (ni == NULL)
+ return;
+
+ /*
+ * Unassociated (temporary node) station.
+ */
+ if (ni->ni_associd == 0)
+ return;
+
+ /*
+ * We do have an active node, so let's begin looking into it.
+ */
+ an = ATH_NODE(ni);
+ avp = ATH_VAP(ni->ni_vap);
+
+ /*
+ * For now, we just call the original ps-poll method.
+ * Once we're ready to flip this on:
+ *
+ * + Set leak to 1, as no matter what we're going to have
+ * to send a frame;
+ * + Check the software queue and if there's something in it,
+ * schedule the highest TID thas has traffic from this node.
+ * Then make sure we schedule the software scheduler to
+ * run so it picks up said frame.
+ *
+ * That way whatever happens, we'll at least send _a_ frame
+ * to the given node.
+ *
+ * Again, yes, it's crappy QoS if the node has multiple
+ * TIDs worth of traffic - but let's get it working first
+ * before we optimise it.
+ *
+ * Also yes, there's definitely latency here - we're not
+ * direct dispatching to the hardware in this path (and
+ * we're likely being called from the packet receive path,
+ * so going back into TX may be a little hairy!) but again
+ * I'd like to get this working first before optimising
+ * turn-around time.
+ */
+
+ ATH_NODE_LOCK(an);
+
+ /*
+ * Legacy - we're called and the node isn't asleep.
+ * Immediately punt.
+ */
+ if (! an->an_is_powersave) {
+ device_printf(sc->sc_dev,
+ "%s: %6D: not in powersave?\n",
+ __func__,
+ ni->ni_macaddr,
+ ":");
+ ATH_NODE_UNLOCK(an);
+ avp->av_recv_pspoll(ni, m);
+ return;
+ }
+
+ /*
+ * We're in powersave.
+ *
+ * Leak a frame.
+ */
+ an->an_leak_count = 1;
+
+ /*
+ * Now, if there's no frames in the node, just punt to
+ * recv_pspoll.
+ *
+ * Don't bother checking if the TIM bit is set, we really
+ * only care if there are any frames here!
+ */
+ if (atomic_load_acq_int(&an->an_swq_depth) == 0) {
+ ATH_NODE_UNLOCK(an);
+ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+ "%s: %6D: SWQ empty; punting to net80211\n",
+ __func__,
+ ni->ni_macaddr,
+ ":");
+ avp->av_recv_pspoll(ni, m);
+ return;
+ }
+
+ ATH_NODE_UNLOCK(an);
+
+ /*
+ * Ok, let's schedule the highest TID that has traffic
+ * and then schedule something.
+ */
+ ATH_TX_LOCK(sc);
+ for (tid = IEEE80211_TID_SIZE - 1; tid >= 0; tid--) {
+ struct ath_tid *atid = &an->an_tid[tid];
+ /*
+ * No frames? Skip.
+ */
+ if (atid->axq_depth == 0)
+ continue;
+ ath_tx_tid_sched(sc, atid);
+ /*
+ * XXX we could do a direct call to the TXQ
+ * scheduler code here to optimise latency
+ * at the expense of a REALLY deep callstack.
+ */
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_txqtask);
+ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+ "%s: %6D: leaking frame to TID %d\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ tid);
+ ATH_TX_UNLOCK(sc);
+ return;
+ }
+
+ ATH_TX_UNLOCK(sc);
+
+ /*
+ * XXX nothing in the TIDs at this point? Eek.
+ */
+ device_printf(sc->sc_dev, "%s: %6D: TIDs empty, but ath_node showed traffic?!\n",
+ __func__,
+ ni->ni_macaddr,
+ ":");
+ avp->av_recv_pspoll(ni, m);
+}
+
MODULE_VERSION(if_ath, 1);
MODULE_DEPEND(if_ath, wlan, 1, 1, 1); /* 802.11 media layer */
#if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ)
Modified: user/adrian/net80211_tx/sys/dev/ath/if_ath_tx.c
==============================================================================
--- user/adrian/net80211_tx/sys/dev/ath/if_ath_tx.c Sun Apr 21 19:24:37 2013 (r249737)
+++ user/adrian/net80211_tx/sys/dev/ath/if_ath_tx.c Sun Apr 21 19:33:55 2013 (r249738)
@@ -1401,6 +1401,69 @@ ath_tx_update_clrdmask(struct ath_softc
}
/*
+ * Return whether this frame should be software queued or
+ * direct dispatched.
+ *
+ * When doing powersave, BAR frames should be queued but other management
+ * frames should be directly sent.
+ *
+ * When not doing powersave, stick BAR frames into the hardware queue
+ * so it goes out even though the queue is paused.
+ *
+ * For now, management frames are also software queued by default.
+ */
+static int
+ath_tx_should_swq_frame(struct ath_softc *sc, struct ath_node *an,
+ struct mbuf *m0, int *queue_to_head)
+{
+ struct ieee80211_node *ni = &an->an_node;
+ struct ieee80211_frame *wh;
+ uint8_t type, subtype;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
+ (*queue_to_head) = 0;
+
+ /* If it's not in powersave - direct-dispatch BAR */
+ if ((ATH_NODE(ni)->an_is_powersave == 0)
+ && type == IEEE80211_FC0_TYPE_CTL &&
+ subtype == IEEE80211_FC0_SUBTYPE_BAR) {
+ DPRINTF(sc, ATH_DEBUG_SW_TX,
+ "%s: BAR: TX'ing direct\n", __func__);
+ return (0);
+ } else if ((ATH_NODE(ni)->an_is_powersave == 1)
+ && type == IEEE80211_FC0_TYPE_CTL &&
+ subtype == IEEE80211_FC0_SUBTYPE_BAR) {
+ /* BAR TX whilst asleep; queue */
+ DPRINTF(sc, ATH_DEBUG_SW_TX,
+ "%s: swq: TX'ing\n", __func__);
+ (*queue_to_head) = 1;
+ return (1);
+ } else if ((ATH_NODE(ni)->an_is_powersave == 1)
+ && (type == IEEE80211_FC0_TYPE_MGT ||
+ type == IEEE80211_FC0_TYPE_CTL)) {
+ /*
+ * Other control/mgmt frame; bypass software queuing
+ * for now!
+ */
+ device_printf(sc->sc_dev,
+ "%s: %6D: Node is asleep; sending mgmt "
+ "(type=%d, subtype=%d)\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ type,
+ subtype);
+ return (0);
+ } else {
+ return (1);
+ }
+}
+
+
+/*
* Transmit the given frame to the hardware.
*
* The frame must already be setup; rate control must already have
@@ -1410,6 +1473,10 @@ ath_tx_update_clrdmask(struct ath_softc
* it for this long when not doing software aggregation), later on
* break this function into "setup_normal" and "xmit_normal". The
* lock only needs to be held for the ath_tx_handoff call.
+ *
+ * XXX we don't update the leak count here - if we're doing
+ * direct frame dispatch, we need to be able to do it without
+ * decrementing the leak count (eg multicast queue frames.)
*/
static void
ath_tx_xmit_normal(struct ath_softc *sc, struct ath_txq *txq,
@@ -1786,6 +1853,7 @@ ath_tx_start(struct ath_softc *sc, struc
int is_ampdu, is_ampdu_tx, is_ampdu_pending;
ieee80211_seq seqno;
uint8_t type, subtype;
+ int queue_to_head;
ATH_TX_LOCK_ASSERT(sc);
@@ -1927,22 +1995,26 @@ ath_tx_start(struct ath_softc *sc, struc
* either been TXed successfully or max retries has been
* reached.)
*/
+ /*
+ * Until things are better debugged - if this node is asleep
+ * and we're sending it a non-BAR frame, direct dispatch it.
+ * Why? Because we need to figure out what's actually being
+ * sent - eg, during reassociation/reauthentication after
+ * the node (last) disappeared whilst asleep, the driver should
+ * have unpaused/unsleep'ed the node. So until that is
+ * sorted out, use this workaround.
+ */
if (txq == &avp->av_mcastq) {
DPRINTF(sc, ATH_DEBUG_SW_TX,
"%s: bf=%p: mcastq: TX'ing\n", __func__, bf);
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_xmit_normal(sc, txq, bf);
- } 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__);
+ } else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0,
+ &queue_to_head)) {
+ ath_tx_swq(sc, ni, txq, queue_to_head, bf);
+ } else {
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
ath_tx_xmit_normal(sc, txq, bf);
- } else {
- /* add to software queue */
- DPRINTF(sc, ATH_DEBUG_SW_TX,
- "%s: bf=%p: swq: TX'ing\n", __func__, bf);
- ath_tx_swq(sc, ni, txq, bf);
}
#else
/*
@@ -1950,6 +2022,12 @@ ath_tx_start(struct ath_softc *sc, struc
* direct-dispatch to the hardware.
*/
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+ /*
+ * Update the current leak count if
+ * we're leaking frames; and set the
+ * MORE flag as appropriate.
+ */
+ ath_tx_leak_count_update(sc, tid, bf);
ath_tx_xmit_normal(sc, txq, bf);
#endif
done:
@@ -1976,6 +2054,8 @@ ath_tx_raw_start(struct ath_softc *sc, s
u_int pri;
int o_tid = -1;
int do_override;
+ uint8_t type, subtype;
+ int queue_to_head;
ATH_TX_LOCK_ASSERT(sc);
@@ -1989,6 +2069,9 @@ ath_tx_raw_start(struct ath_softc *sc, s
/* XXX honor IEEE80211_BPF_DATAPAD */
pktlen = m0->m_pkthdr.len - (hdrlen & 3) + IEEE80211_CRC_LEN;
+ type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+
ATH_KTR(sc, ATH_KTR_TX, 2,
"ath_tx_raw_start: ni=%p, bf=%p, raw", ni, bf);
@@ -2165,16 +2248,35 @@ ath_tx_raw_start(struct ath_softc *sc, s
__func__, do_override);
#if 1
+ /*
+ * Put addba frames in the right place in the right TID/HWQ.
+ */
if (do_override) {
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+ /*
+ * XXX if it's addba frames, should we be leaking
+ * them out via the frame leak method?
+ * XXX for now let's not risk it; but we may wish
+ * to investigate this later.
+ */
ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
- } else {
+ } else if (ath_tx_should_swq_frame(sc, ATH_NODE(ni), m0,
+ &queue_to_head)) {
/* Queue to software queue */
- ath_tx_swq(sc, ni, sc->sc_ac2q[pri], bf);
+ ath_tx_swq(sc, ni, sc->sc_ac2q[pri], queue_to_head, bf);
+ } else {
+ bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+ ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
}
#else
/* Direct-dispatch to the hardware */
bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
+ /*
+ * Update the current leak count if
+ * we're leaking frames; and set the
+ * MORE flag as appropriate.
+ */
+ ath_tx_leak_count_update(sc, tid, bf);
ath_tx_xmit_normal(sc, sc->sc_ac2q[pri], bf);
#endif
return 0;
@@ -2606,6 +2708,60 @@ ath_tx_update_baw(struct ath_softc *sc,
__func__, tap->txa_start, tap->txa_wnd, tid->baw_head);
}
+static void
+ath_tx_leak_count_update(struct ath_softc *sc, struct ath_tid *tid,
+ struct ath_buf *bf)
+{
+ struct ieee80211_frame *wh;
+
+ ATH_TX_LOCK_ASSERT(sc);
+
+ if (tid->an->an_leak_count > 0) {
+ wh = mtod(bf->bf_m, struct ieee80211_frame *);
+
+ /*
+ * Update MORE based on the software/net80211 queue states.
+ */
+ if ((tid->an->an_stack_psq > 0)
+ || (atomic_load_acq_int(&tid->an->an_swq_depth) > 0))
+ wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+ else
+ wh->i_fc[1] &= ~IEEE80211_FC1_MORE_DATA;
+
+ DPRINTF(sc, ATH_DEBUG_NODE_PWRSAVE,
+ "%s: %6D: leak count = %d, psq=%d, swq=%d, MORE=%d\n",
+ __func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
+ tid->an->an_leak_count,
+ tid->an->an_stack_psq,
+ atomic_load_acq_int(&tid->an->an_swq_depth),
+ !! (wh->i_fc[1] & IEEE80211_FC1_MORE_DATA));
+
+ /*
+ * Re-sync the underlying buffer.
+ */
+ bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+ BUS_DMASYNC_PREWRITE);
+
+ tid->an->an_leak_count --;
+ }
+}
+
+static int
+ath_tx_tid_can_tx_or_sched(struct ath_softc *sc, struct ath_tid *tid)
+{
+
+ ATH_TX_LOCK_ASSERT(sc);
+
+ if (tid->an->an_leak_count > 0) {
+ return (1);
+ }
+ if (tid->paused)
+ return (0);
+ return (1);
+}
+
/*
* Mark the current node/TID as ready to TX.
*
@@ -2614,14 +2770,19 @@ ath_tx_update_baw(struct ath_softc *sc,
*
* The TXQ lock must be held.
*/
-static void
+void
ath_tx_tid_sched(struct ath_softc *sc, struct ath_tid *tid)
{
struct ath_txq *txq = sc->sc_ac2q[tid->ac];
ATH_TX_LOCK_ASSERT(sc);
- if (tid->paused)
+ /*
+ * If we are leaking out a frame to this destination
+ * for PS-POLL, ensure that we allow scheduling to
+ * occur.
+ */
+ if (! ath_tx_tid_can_tx_or_sched(sc, tid))
return; /* paused, can't schedule yet */
if (tid->sched)
@@ -2629,6 +2790,30 @@ ath_tx_tid_sched(struct ath_softc *sc, s
tid->sched = 1;
+#if 0
+ /*
+ * If this is a sleeping node we're leaking to, given
+ * it a higher priority. This is so bad for QoS it hurts.
+ */
+ if (tid->an->an_leak_count) {
+ TAILQ_INSERT_HEAD(&txq->axq_tidq, tid, axq_qelem);
+ } else {
+ TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem);
+ }
+#endif
+
+ /*
+ * We can't do the above - it'll confuse the TXQ software
+ * scheduler which will keep checking the _head_ TID
+ * in the list to see if it has traffic. If we queue
+ * a TID to the head of the list and it doesn't transmit,
+ * we'll check it again.
+ *
+ * So, get the rest of this leaking frames support working
+ * and reliable first and _then_ optimise it so they're
+ * pushed out in front of any other pending software
+ * queued nodes.
+ */
TAILQ_INSERT_TAIL(&txq->axq_tidq, tid, axq_qelem);
}
@@ -2725,7 +2910,7 @@ ath_tx_xmit_aggr(struct ath_softc *sc, s
tap = ath_tx_get_tx_tid(an, tid->tid);
/* paused? queue */
- if (tid->paused) {
+ if (! ath_tx_tid_can_tx_or_sched(sc, tid)) {
ATH_TID_INSERT_HEAD(tid, bf, bf_list);
/* XXX don't sched - we're paused! */
return;
@@ -2785,6 +2970,13 @@ ath_tx_xmit_aggr(struct ath_softc *sc, s
/* Set completion handler, multi-frame aggregate or not */
bf->bf_comp = ath_tx_aggr_comp;
+ /*
+ * Update the current leak count if
+ * we're leaking frames; and set the
+ * MORE flag as appropriate.
+ */
+ ath_tx_leak_count_update(sc, tid, bf);
+
/* Hand off to hardware */
ath_tx_handoff(sc, txq, bf);
}
@@ -2796,8 +2988,8 @@ ath_tx_xmit_aggr(struct ath_softc *sc, s
* relevant software queue.
*/
void
-ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_txq *txq,
- struct ath_buf *bf)
+ath_tx_swq(struct ath_softc *sc, struct ieee80211_node *ni,
+ struct ath_txq *txq, int queue_to_head, struct ath_buf *bf)
{
struct ath_node *an = ATH_NODE(ni);
struct ieee80211_frame *wh;
@@ -2827,11 +3019,21 @@ ath_tx_swq(struct ath_softc *sc, struct
* If the hardware queue is busy, queue it.
* If the TID is paused or the traffic it outside BAW, software
* queue it.
+ *
+ * If the node is in power-save and we're leaking a frame,
+ * leak a single frame.
*/
- if (atid->paused) {
+ if (! ath_tx_tid_can_tx_or_sched(sc, atid)) {
/* TID is paused, queue */
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: paused\n", __func__);
- ATH_TID_INSERT_TAIL(atid, bf, bf_list);
+ /*
+ * If the caller requested that it be sent at a high
+ * priority, queue it at the head of the list.
+ */
+ if (queue_to_head)
+ ATH_TID_INSERT_HEAD(atid, bf, bf_list);
+ else
+ ATH_TID_INSERT_TAIL(atid, bf, bf_list);
} else if (ath_tx_ampdu_pending(sc, an, tid)) {
/* AMPDU pending; queue */
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: pending\n", __func__);
@@ -2881,6 +3083,17 @@ ath_tx_swq(struct ath_softc *sc, struct
DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: xmit_normal\n", __func__);
/* See if clrdmask needs to be set */
ath_tx_update_clrdmask(sc, atid, bf);
+
+ /*
+ * Update the current leak count if
+ * we're leaking frames; and set the
+ * MORE flag as appropriate.
+ */
+ ath_tx_leak_count_update(sc, atid, bf);
+
+ /*
+ * Dispatch the frame.
+ */
ath_tx_xmit_normal(sc, txq, bf);
} else {
/* Busy; queue */
@@ -3254,13 +3467,19 @@ ath_tx_tid_bar_unsuspend(struct ath_soft
ATH_TX_LOCK_ASSERT(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
- "%s: tid=%p, called\n",
+ "%s: %6D: tid=%p, called\n",
__func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
tid);
if (tid->bar_tx == 0 || tid->bar_wait == 0) {
- device_printf(sc->sc_dev, "%s: bar_tx=%d, bar_wait=%d: ?\n",
- __func__, tid->bar_tx, tid->bar_wait);
+ device_printf(sc->sc_dev,
+ "%s: %6D: bar_tx=%d, bar_wait=%d: ?\n",
+ __func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
+ tid->bar_tx, tid->bar_wait);
}
tid->bar_tx = tid->bar_wait = 0;
@@ -3281,8 +3500,12 @@ ath_tx_tid_bar_tx_ready(struct ath_softc
if (tid->bar_wait == 0 || tid->hwq_depth > 0)
return (0);
- DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, "%s: tid=%p (%d), bar ready\n",
- __func__, tid, tid->tid);
+ DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
+ "%s: %6D: tid=%p (%d), bar ready\n",
+ __func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
+ tid, tid->tid);
return (1);
}
@@ -3307,8 +3530,10 @@ ath_tx_tid_bar_tx(struct ath_softc *sc,
ATH_TX_LOCK_ASSERT(sc);
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
- "%s: tid=%p, called\n",
+ "%s: %6D: tid=%p, called\n",
__func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
tid);
tap = ath_tx_get_tx_tid(tid->an, tid->tid);
@@ -3318,8 +3543,10 @@ ath_tx_tid_bar_tx(struct ath_softc *sc,
*/
if (tid->bar_wait == 0 || tid->bar_tx == 1) {
device_printf(sc->sc_dev,
- "%s: tid=%p, bar_tx=%d, bar_wait=%d: ?\n",
+ "%s: %6D: tid=%p, bar_tx=%d, bar_wait=%d: ?\n",
__func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
tid,
tid->bar_tx,
tid->bar_wait);
@@ -3329,8 +3556,10 @@ ath_tx_tid_bar_tx(struct ath_softc *sc,
/* Don't do anything if we still have pending frames */
if (tid->hwq_depth > 0) {
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
- "%s: tid=%p, hwq_depth=%d, waiting\n",
+ "%s: %6D: tid=%p, hwq_depth=%d, waiting\n",
__func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
tid,
tid->hwq_depth);
return;
@@ -3352,8 +3581,10 @@ ath_tx_tid_bar_tx(struct ath_softc *sc,
* XXX verify this is _actually_ the valid value to begin at!
*/
DPRINTF(sc, ATH_DEBUG_SW_TX_BAR,
- "%s: tid=%p, new BAW left edge=%d\n",
+ "%s: %6D: tid=%p, new BAW left edge=%d\n",
__func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
tid,
tap->txa_start);
@@ -3369,8 +3600,12 @@ ath_tx_tid_bar_tx(struct ath_softc *sc,
/* Failure? For now, warn loudly and continue */
ATH_TX_LOCK(sc);
- device_printf(sc->sc_dev, "%s: tid=%p, failed to TX BAR, continue!\n",
- __func__, tid);
+ device_printf(sc->sc_dev,
+ "%s: %6D: tid=%p, failed to TX BAR, continue!\n",
+ __func__,
+ tid->an->an_node.ni_macaddr,
+ ":",
+ tid);
ath_tx_tid_bar_unsuspend(sc, tid);
}
@@ -3457,10 +3692,12 @@ ath_tx_tid_drain_print(struct ath_softc
tid->baw_tail, tap == NULL ? -1 : tap->txa_start,
ni->ni_txseqs[tid->tid]);
+#if 0
/* XXX Dump the frame, see what it is? */
ieee80211_dump_pkt(ni->ni_ic,
mtod(bf->bf_m, const uint8_t *),
bf->bf_m->m_len, 0, -1);
+#endif
}
/*
@@ -3501,7 +3738,7 @@ ath_tx_tid_drain(struct ath_softc *sc, s
if (t == 0) {
ath_tx_tid_drain_print(sc, an, "norm", tid, bf);
- t = 1;
+// t = 1;
}
ATH_TID_REMOVE(tid, bf, bf_list);
@@ -3517,7 +3754,7 @@ ath_tx_tid_drain(struct ath_softc *sc, s
if (t == 0) {
ath_tx_tid_drain_print(sc, an, "filt", tid, bf);
- t = 1;
+// t = 1;
}
ATH_TID_FILT_REMOVE(tid, bf, bf_list);
@@ -3548,10 +3785,15 @@ ath_tx_tid_drain(struct ath_softc *sc, s
/* But don't do it for non-QoS TIDs */
if (tap) {
-#if 0
+#if 1
DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL,
- "%s: node %p: TID %d: sliding BAW left edge to %d\n",
- __func__, an, tid->tid, tap->txa_start);
+ "%s: %6D: node %p: TID %d: sliding BAW left edge to %d\n",
+ __func__,
+ ni->ni_macaddr,
+ ":",
+ an,
+ tid->tid,
+ tap->txa_start);
#endif
ni->ni_txseqs[tid->tid] = tap->txa_start;
tid->baw_tail = tid->baw_head;
@@ -3559,6 +3801,43 @@ ath_tx_tid_drain(struct ath_softc *sc, s
}
/*
+ * Reset the TID state. This must be only called once the node has
+ * had its frames flushed from this TID, to ensure that no other
+ * pause / unpause logic can kick in.
+ */
+static void
+ath_tx_tid_reset(struct ath_softc *sc, struct ath_tid *tid)
+{
+
+#if 0
+ tid->bar_wait = tid->bar_tx = tid->isfiltered = 0;
+ tid->paused = tid->sched = tid->addba_tx_pending = 0;
+ tid->incomp = tid->cleanup_inprogress = 0;
+#endif
+
+ /*
+ * Clear BAR, filtered frames, scheduled and ADDBA pending.
+ * The TID may be going through cleanup from the last association
+ * where things in the BAW are still in the hardware queue.
+ */
+ tid->bar_wait = 0;
+ tid->bar_tx = 0;
+ tid->isfiltered = 0;
+ tid->sched = 0;
+ tid->addba_tx_pending = 0;
+
+ /*
+ * XXX TODO: it may just be enough to walk the HWQs and mark
+ * frames for that node as non-aggregate; or mark the ath_node
+ * with something that indicates that aggregation is no longer
+ * occuring. Then we can just toss the BAW complaints and
+ * do a complete hard reset of state here - no pause, no
+ * complete counter, etc.
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-user
mailing list