Author: adrian
Date: Tue Nov  8 21:49:33 2011
New Revision: 227361
URL: http://svn.freebsd.org/changeset/base/227361

Log:
  Refactor out the TX buffer management and completion code in
  preparation for TX aggregation.
  
  * Add in logic which calls ath_buf bf->bf_comp if it's set.
    This allows for AMPDU (and RIFS, and FF, if someone desires) code
    to handle completion - which includes freeing subframes, retransmitting
    subframes, etc.
  
  * Break out the buffer free, buffer busy/unbusy default completion handler
    code into separate functions. This allows bf_comp methods to free and
    unbusy each subframe ath_buf as required.
  
  * Break out the statistics update code into a separate function, just
    to clean up the TX completion path a little.
  
  Sponsored by: Hobnob, Inc.

Modified:
  head/sys/dev/ath/if_ath.c
  head/sys/dev/ath/if_ath_misc.h

Modified: head/sys/dev/ath/if_ath.c
==============================================================================
--- head/sys/dev/ath/if_ath.c   Tue Nov  8 21:25:36 2011        (r227360)
+++ head/sys/dev/ath/if_ath.c   Tue Nov  8 21:49:33 2011        (r227361)
@@ -185,7 +185,6 @@ static void ath_tx_cleanup(struct ath_so
 static void    ath_tx_proc_q0(void *, int);
 static void    ath_tx_proc_q0123(void *, int);
 static void    ath_tx_proc(void *, int);
-static void    ath_tx_draintxq(struct ath_softc *, struct ath_txq *);
 static int     ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
 static void    ath_draintxq(struct ath_softc *, ATH_RESET_TYPE reset_type);
 static void    ath_stoprecv(struct ath_softc *);
@@ -4279,6 +4278,121 @@ ath_tx_findrix(const struct ath_softc *s
        return (rix == 0xff ? 0 : rix);
 }
 
+static void
+ath_tx_update_stats(struct ath_softc *sc, struct ath_tx_status *ts,
+    struct ath_buf *bf)
+{
+       struct ieee80211_node *ni = bf->bf_node;
+       struct ifnet *ifp = sc->sc_ifp;
+       struct ieee80211com *ic = ifp->if_l2com;
+       int sr, lr, pri;
+
+       if (ts->ts_status == 0) {
+               u_int8_t txant = ts->ts_antenna;
+               sc->sc_stats.ast_ant_tx[txant]++;
+               sc->sc_ant_tx[txant]++;
+               if (ts->ts_finaltsi != 0)
+                       sc->sc_stats.ast_tx_altrate++;
+               pri = M_WME_GETAC(bf->bf_m);
+               if (pri >= WME_AC_VO)
+                       ic->ic_wme.wme_hipri_traffic++;
+               if ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0)
+                       ni->ni_inact = ni->ni_inact_reload;
+       } else {
+               if (ts->ts_status & HAL_TXERR_XRETRY)
+                       sc->sc_stats.ast_tx_xretries++;
+               if (ts->ts_status & HAL_TXERR_FIFO)
+                       sc->sc_stats.ast_tx_fifoerr++;
+               if (ts->ts_status & HAL_TXERR_FILT)
+                       sc->sc_stats.ast_tx_filtered++;
+               if (ts->ts_status & HAL_TXERR_XTXOP)
+                       sc->sc_stats.ast_tx_xtxop++;
+               if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED)
+                       sc->sc_stats.ast_tx_timerexpired++;
+
+               if (ts->ts_status & HAL_TX_DATA_UNDERRUN)
+                       sc->sc_stats.ast_tx_data_underrun++;
+               if (ts->ts_status & HAL_TX_DELIM_UNDERRUN)
+                       sc->sc_stats.ast_tx_delim_underrun++;
+
+               if (bf->bf_m->m_flags & M_FF)
+                       sc->sc_stats.ast_ff_txerr++;
+       }
+       /* XXX when is this valid? */
+       if (ts->ts_status & HAL_TX_DESC_CFG_ERR)
+               sc->sc_stats.ast_tx_desccfgerr++;
+
+       sr = ts->ts_shortretry;
+       lr = ts->ts_longretry;
+       sc->sc_stats.ast_tx_shortretry += sr;
+       sc->sc_stats.ast_tx_longretry += lr;
+
+}
+
+/*
+ * The default completion. If fail is 1, this means
+ * "please don't retry the frame, and just return -1 status
+ * to the net80211 stack.
+ */
+void
+ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf, int fail)
+{
+       struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
+       int st;
+
+       if (fail == 1)
+               st = -1;
+       else
+               st = ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0) ?
+                   ts->ts_status : HAL_TXERR_XRETRY;
+
+       if (bf->bf_state.bfs_dobaw)
+               device_printf(sc->sc_dev,
+                   "%s: dobaw should've been cleared!\n", __func__);
+       if (bf->bf_next != NULL)
+               device_printf(sc->sc_dev,
+                   "%s: bf_next not NULL!\n", __func__);
+
+       /*
+        * Do any tx complete callback.  Note this must
+        * be done before releasing the node reference.
+        * This will free the mbuf, release the net80211
+        * node and recycle the ath_buf.
+        */
+       ath_tx_freebuf(sc, bf, st);
+}
+
+/*
+ * Update the busy status of the last frame on the free list.
+ * When doing TDMA, the busy flag tracks whether the hardware
+ * currently points to this buffer or not, and thus gated DMA
+ * may restart by re-reading the last descriptor in this
+ * buffer.
+ *
+ * This should be called in the completion function once one
+ * of the buffers has been used.
+ */
+static void
+ath_tx_update_busy(struct ath_softc *sc)
+{
+       struct ath_buf *last;
+
+       /*
+        * Since the last frame may still be marked
+        * as ATH_BUF_BUSY, unmark it here before
+        * finishing the frame processing.
+        * Since we've completed a frame (aggregate
+        * or otherwise), the hardware has moved on
+        * and is no longer referencing the previous
+        * descriptor.
+        */
+       ATH_TXBUF_LOCK_ASSERT(sc);
+       last = TAILQ_LAST(&sc->sc_txbuf, ath_bufhead_s);
+       if (last != NULL)
+               last->bf_flags &= ~ATH_BUF_BUSY;
+}
+
+
 /*
  * Process completed xmit descriptors from the specified queue.
  */
@@ -4286,14 +4400,11 @@ static int
 ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq, int dosched)
 {
        struct ath_hal *ah = sc->sc_ah;
-       struct ifnet *ifp = sc->sc_ifp;
-       struct ieee80211com *ic = ifp->if_l2com;
-       struct ath_buf *bf, *last;
+       struct ath_buf *bf;
        struct ath_desc *ds;
        struct ath_tx_status *ts;
        struct ieee80211_node *ni;
-       struct ath_node *an;
-       int sr, lr, pri, nacked;
+       int nacked;
        HAL_STATUS status;
 
        DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %u head %p link %p\n",
@@ -4341,90 +4452,48 @@ ath_tx_processq(struct ath_softc *sc, st
                        txq->axq_link = NULL;
                if (bf->bf_state.bfs_aggr)
                        txq->axq_aggr_depth--;
-               ATH_TXQ_UNLOCK(txq);
 
                ni = bf->bf_node;
-               if (ni != NULL) {
-                       an = ATH_NODE(ni);
-                       if (ts->ts_status == 0) {
-                               u_int8_t txant = ts->ts_antenna;
-                               sc->sc_stats.ast_ant_tx[txant]++;
-                               sc->sc_ant_tx[txant]++;
-                               if (ts->ts_finaltsi != 0)
-                                       sc->sc_stats.ast_tx_altrate++;
-                               pri = M_WME_GETAC(bf->bf_m);
-                               if (pri >= WME_AC_VO)
-                                       ic->ic_wme.wme_hipri_traffic++;
-                               if ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0)
-                                       ni->ni_inact = ni->ni_inact_reload;
-                       } else {
-                               if (ts->ts_status & HAL_TXERR_XRETRY)
-                                       sc->sc_stats.ast_tx_xretries++;
-                               if (ts->ts_status & HAL_TXERR_FIFO)
-                                       sc->sc_stats.ast_tx_fifoerr++;
-                               if (ts->ts_status & HAL_TXERR_FILT)
-                                       sc->sc_stats.ast_tx_filtered++;
-                               if (ts->ts_status & HAL_TXERR_XTXOP)
-                                       sc->sc_stats.ast_tx_xtxop++;
-                               if (ts->ts_status & HAL_TXERR_TIMER_EXPIRED)
-                                       sc->sc_stats.ast_tx_timerexpired++;
+               /*
+                * If unicast frame was ack'd update RSSI,
+                * including the last rx time used to
+                * workaround phantom bmiss interrupts.
+                */
+               if (ni != NULL && ts->ts_status == 0 &&
+                   ((bf->bf_txflags & HAL_TXDESC_NOACK) == 0)) {
+                       nacked++;
+                       sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
+                       ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi,
+                               ts->ts_rssi);
+               }
+               ATH_TXQ_UNLOCK(txq);
 
-                               /* XXX HAL_TX_DATA_UNDERRUN */
-                               /* XXX HAL_TX_DELIM_UNDERRUN */
+               /* If unicast frame, update general statistics */
+               if (ni != NULL) {
+                       /* update statistics */
+                       ath_tx_update_stats(sc, ts, bf);
+               }
 
-                               if (bf->bf_m->m_flags & M_FF)
-                                       sc->sc_stats.ast_ff_txerr++;
-                       }
-                       /* XXX when is this valid? */
-                       if (ts->ts_status & HAL_TX_DESC_CFG_ERR)
-                               sc->sc_stats.ast_tx_desccfgerr++;
-
-                       sr = ts->ts_shortretry;
-                       lr = ts->ts_longretry;
-                       sc->sc_stats.ast_tx_shortretry += sr;
-                       sc->sc_stats.ast_tx_longretry += lr;
-                       /*
-                        * Hand the descriptor to the rate control algorithm.
-                        */
+               /*
+                * Call the completion handler.
+                * The completion handler is responsible for
+                * calling the rate control code.
+                *
+                * Frames with no completion handler get the
+                * rate control code called here.
+                */
+               if (bf->bf_comp == NULL) {
                        if ((ts->ts_status & HAL_TXERR_FILT) == 0 &&
                            (bf->bf_txflags & HAL_TXDESC_NOACK) == 0) {
                                /*
-                                * If frame was ack'd update statistics,
-                                * including the last rx time used to
-                                * workaround phantom bmiss interrupts.
+                                * XXX assume this isn't an aggregate
+                                * frame.
                                 */
-                               if (ts->ts_status == 0) {
-                                       nacked++;
-                                       sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
-                                       
ATH_RSSI_LPF(sc->sc_halstats.ns_avgtxrssi,
-                                               ts->ts_rssi);
-                               }
-                               ath_rate_tx_complete(sc, an, bf);
+                               ath_rate_tx_complete(sc, ATH_NODE(ni), bf);
                        }
-                       /*
-                        * Do any tx complete callback.  Note this must
-                        * be done before releasing the node reference.
-                        */
-                       if (bf->bf_m->m_flags & M_TXCB)
-                               ieee80211_process_callback(ni, bf->bf_m,
-                                   (bf->bf_txflags & HAL_TXDESC_NOACK) == 0 ?
-                                       ts->ts_status : HAL_TXERR_XRETRY);
-                       ieee80211_free_node(ni);
-               }
-               bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
-                   BUS_DMASYNC_POSTWRITE);
-               bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-
-               m_freem(bf->bf_m);
-               bf->bf_m = NULL;
-               bf->bf_node = NULL;
-
-               ATH_TXBUF_LOCK(sc);
-               last = TAILQ_LAST(&sc->sc_txbuf, ath_bufhead_s);
-               if (last != NULL)
-                       last->bf_flags &= ~ATH_BUF_BUSY;
-               TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
-               ATH_TXBUF_UNLOCK(sc);
+                       ath_tx_default_comp(sc, bf, 0);
+               } else
+                       bf->bf_comp(sc, bf, 0);
        }
 #ifdef IEEE80211_SUPPORT_SUPERG
        /*
@@ -4547,13 +4616,73 @@ ath_tx_proc(void *arg, int npending)
 }
 #undef TXQACTIVE
 
-static void
+/*
+ * Return a buffer to the pool and update the 'busy' flag on the
+ * previous 'tail' entry.
+ *
+ * This _must_ only be called when the buffer is involved in a completed
+ * TX. The logic is that if it was part of an active TX, the previous
+ * buffer on the list is now not involved in a halted TX DMA queue, waiting
+ * for restart (eg for TDMA.)
+ *
+ * The caller must free the mbuf and recycle the node reference.
+ */
+void
+ath_freebuf(struct ath_softc *sc, struct ath_buf *bf)
+{
+       bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+       bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_POSTWRITE);
+
+       KASSERT((bf->bf_node == NULL), ("%s: bf->bf_node != NULL\n", __func__));
+       KASSERT((bf->bf_m == NULL), ("%s: bf->bf_m != NULL\n", __func__));
+
+       ATH_TXBUF_LOCK(sc);
+       ath_tx_update_busy(sc);
+       TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+       ATH_TXBUF_UNLOCK(sc);
+}
+
+/*
+ * This is currently used by ath_tx_draintxq() and
+ * ath_tx_tid_free_pkts().
+ *
+ * It recycles a single ath_buf.
+ */
+void
+ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf, int status)
+{
+       struct ieee80211_node *ni = bf->bf_node;
+       struct mbuf *m0 = bf->bf_m;
+
+       bf->bf_node = NULL;
+       bf->bf_m = NULL;
+
+       /* Free the buffer, it's not needed any longer */
+       ath_freebuf(sc, bf);
+
+       if (ni != NULL) {
+               /*
+                * Do any callback and reclaim the node reference.
+                */
+               if (m0->m_flags & M_TXCB)
+                       ieee80211_process_callback(ni, m0, status);
+               ieee80211_free_node(ni);
+       }
+       m_freem(m0);
+
+       /*
+        * XXX the buffer used to be freed -after-, but the DMA map was
+        * freed where ath_freebuf() now is. I've no idea what this
+        * will do.
+        */
+}
+
+void
 ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
 {
 #ifdef ATH_DEBUG
        struct ath_hal *ah = sc->sc_ah;
 #endif
-       struct ieee80211_node *ni;
        struct ath_buf *bf;
        u_int ix;
 
@@ -4566,6 +4695,7 @@ ath_tx_draintxq(struct ath_softc *sc, st
        if (bf != NULL)
                bf->bf_flags &= ~ATH_BUF_BUSY;
        ATH_TXBUF_UNLOCK(sc);
+
        for (ix = 0;; ix++) {
                ATH_TXQ_LOCK(txq);
                bf = TAILQ_FIRST(&txq->axq_q);
@@ -4577,7 +4707,6 @@ ath_tx_draintxq(struct ath_softc *sc, st
                ATH_TXQ_REMOVE(txq, bf, bf_list);
                if (bf->bf_state.bfs_aggr)
                        txq->axq_aggr_depth--;
-               ATH_TXQ_UNLOCK(txq);
 #ifdef ATH_DEBUG
                if (sc->sc_debug & ATH_DEBUG_RESET) {
                        struct ieee80211com *ic = sc->sc_ifp->if_l2com;
@@ -4589,25 +4718,23 @@ ath_tx_draintxq(struct ath_softc *sc, st
                            bf->bf_m->m_len, 0, -1);
                }
 #endif /* ATH_DEBUG */
-               bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
-               ni = bf->bf_node;
-               bf->bf_node = NULL;
-               if (ni != NULL) {
-                       /*
-                        * Do any callback and reclaim the node reference.
-                        */
-                       if (bf->bf_m->m_flags & M_TXCB)
-                               ieee80211_process_callback(ni, bf->bf_m, -1);
-                       ieee80211_free_node(ni);
-               }
-               m_freem(bf->bf_m);
-               bf->bf_m = NULL;
+               /*
+                * Since we're now doing magic in the completion
+                * functions, we -must- call it for aggregation
+                * destinations or BAW tracking will get upset.
+                */
+               /*
+                * Clear ATH_BUF_BUSY; the completion handler
+                * will free the buffer.
+                */
+               ATH_TXQ_UNLOCK(txq);
                bf->bf_flags &= ~ATH_BUF_BUSY;
-
-               ATH_TXBUF_LOCK(sc);
-               TAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
-               ATH_TXBUF_UNLOCK(sc);
+               if (bf->bf_comp)
+                       bf->bf_comp(sc, bf, 1);
+               else
+                       ath_tx_default_comp(sc, bf, 1);
        }
+
 }
 
 static void

Modified: head/sys/dev/ath/if_ath_misc.h
==============================================================================
--- head/sys/dev/ath/if_ath_misc.h      Tue Nov  8 21:25:36 2011        
(r227360)
+++ head/sys/dev/ath/if_ath_misc.h      Tue Nov  8 21:49:33 2011        
(r227361)
@@ -54,7 +54,14 @@ extern struct ath_buf * ath_getbuf(struc
 extern struct ath_buf * _ath_getbuf_locked(struct ath_softc *sc);
 extern struct ath_buf * ath_buf_clone(struct ath_softc *sc,
            const struct ath_buf *bf);
+extern void ath_freebuf(struct ath_softc *sc, struct ath_buf *bf);
 
 extern int ath_reset(struct ifnet *, ATH_RESET_TYPE);
+extern void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq);
+extern void ath_tx_default_comp(struct ath_softc *sc, struct ath_buf *bf,
+           int fail);
+
+extern void ath_tx_freebuf(struct ath_softc *sc, struct ath_buf *bf,
+    int status);
 
 #endif
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to