ath9k: use split rx buffers to get rid of order-1 skb allocations
authorLeann Ogasawara <leann.ogasawara@canonical.com>
Tue, 23 Aug 2011 18:26:17 +0000 (11:26 -0700)
committerHerton Ronaldo Krzesinski <herton.krzesinski@canonical.com>
Mon, 29 Aug 2011 19:23:11 +0000 (16:23 -0300)
BugLink: http://bugs.launchpad.net/bugs/728835

With this change, less CPU time is spent trying to look for consecutive
pages for rx skbs. This also reduces the socket memory required for IP/UDP
reassembly.
Only two buffers per frame are supported. Frames spanning more buffers
will be dropped, but the buffer size is enough to handle the required
AMSDU size.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
(cherry picked from commit 0d95521ea74735826cb2e28bebf6a07392c75bfa)

Signed-off-by: Leann Ogasawara <leann.ogasawara@canonical.com>
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>

drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/recv.c

index 1a7fa6e..33e4431 100644 (file)
@@ -301,6 +301,8 @@ struct ath_rx {
        struct ath_descdma rxdma;
        struct ath_buf *rx_bufptr;
        struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
+
+       struct sk_buff *frag;
 };
 
 int ath_startrecv(struct ath_softc *sc);
index 0848e09..97dbc8f 100644 (file)
@@ -1323,6 +1323,11 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        } else
                sc->rx.rxlink = NULL;
 
+       if (sc->rx.frag) {
+               dev_kfree_skb_any(sc->rx.frag);
+               sc->rx.frag = NULL;
+       }
+
        /* disable HAL and put h/w to sleep */
        ath9k_hw_disable(ah);
        ath9k_hw_configpcipowersave(ah, 1, 1);
index 1e0c1e3..1b074f2 100644 (file)
@@ -230,11 +230,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
        int error = 0, i;
        u32 size;
 
-
-       common->rx_bufsize = roundup(IEEE80211_MAX_MPDU_LEN +
-                                    ah->caps.rx_status_len,
-                                    min(common->cachelsz, (u16)64));
-
        ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
                                    ah->caps.rx_status_len);
 
@@ -321,12 +316,12 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
        sc->sc_flags &= ~SC_OP_RXFLUSH;
        spin_lock_init(&sc->rx.rxbuflock);
 
+       common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
+                            sc->sc_ah->caps.rx_status_len;
+
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
                return ath_rx_edma_init(sc, nbufs);
        } else {
-               common->rx_bufsize = roundup(IEEE80211_MAX_MPDU_LEN,
-                               min(common->cachelsz, (u16)64));
-
                ath_dbg(common, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n",
                        common->cachelsz, common->rx_bufsize);
 
@@ -860,15 +855,9 @@ static bool ath9k_rx_accept(struct ath_common *common,
        if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len))
                return false;
 
-       /*
-        * rs_more indicates chained descriptors which can be used
-        * to link buffers together for a sort of scatter-gather
-        * operation.
-        * reject the frame, we don't support scatter-gather yet and
-        * the frame is probably corrupt anyway
-        */
+       /* Only use error bits from the last fragment */
        if (rx_stats->rs_more)
-               return false;
+               return true;
 
        /*
         * The rx_stats->rs_status will not be set until the end of the
@@ -1020,6 +1009,10 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common,
        if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error))
                return -EINVAL;
 
+       /* Only use status info from the last fragment */
+       if (rx_stats->rs_more)
+               return 0;
+
        ath9k_process_rssi(common, hw, hdr, rx_stats);
 
        if (ath9k_process_rate(common, hw, rx_stats, rx_status))
@@ -1621,7 +1614,7 @@ div_comb_done:
 int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 {
        struct ath_buf *bf;
-       struct sk_buff *skb = NULL, *requeue_skb;
+       struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb;
        struct ieee80211_rx_status *rxs;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
@@ -1672,8 +1665,17 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                if (!skb)
                        continue;
 
-               hdr = (struct ieee80211_hdr *) (skb->data + rx_status_len);
-               rxs =  IEEE80211_SKB_RXCB(skb);
+               /*
+                * Take frame header from the first fragment and RX status from
+                * the last one.
+                */
+               if (sc->rx.frag)
+                       hdr_skb = sc->rx.frag;
+               else
+                       hdr_skb = skb;
+
+               hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len);
+               rxs = IEEE80211_SKB_RXCB(hdr_skb);
 
                hw = ath_get_virt_hw(sc, hdr);
 
@@ -1684,12 +1686,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                 * chain it back at the queue without processing it.
                 */
                if (flush)
-                       goto requeue;
+                       goto requeue_drop_frag;
 
                retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs,
                                                 rxs, &decrypt_error);
                if (retval)
-                       goto requeue;
+                       goto requeue_drop_frag;
 
                rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
                if (rs.rs_tstamp > tsf_lower &&
@@ -1709,7 +1711,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                 * skb and put it at the tail of the sc->rx.rxbuf list for
                 * processing. */
                if (!requeue_skb)
-                       goto requeue;
+                       goto requeue_drop_frag;
 
                /* Unmap the frame */
                dma_unmap_single(sc->dev, bf->bf_buf_addr,
@@ -1720,8 +1722,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                if (ah->caps.rx_status_len)
                        skb_pull(skb, ah->caps.rx_status_len);
 
-               ath9k_rx_skb_postprocess(common, skb, &rs,
-                                        rxs, decrypt_error);
+               if (!rs.rs_more)
+                       ath9k_rx_skb_postprocess(common, hdr_skb, &rs,
+                                                rxs, decrypt_error);
 
                /* We will now give hardware our shiny new allocated skb */
                bf->bf_mpdu = requeue_skb;
@@ -1738,6 +1741,38 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
                        break;
                }
 
+               if (rs.rs_more) {
+                       /*
+                        * rs_more indicates chained descriptors which can be
+                        * used to link buffers together for a sort of
+                        * scatter-gather operation.
+                        */
+                       if (sc->rx.frag) {
+                               /* too many fragments - cannot handle frame */
+                               dev_kfree_skb_any(sc->rx.frag);
+                               dev_kfree_skb_any(skb);
+                               skb = NULL;
+                       }
+                       sc->rx.frag = skb;
+                       goto requeue;
+               }
+
+               if (sc->rx.frag) {
+                       int space = skb->len - skb_tailroom(hdr_skb);
+
+                       sc->rx.frag = NULL;
+
+                       if (pskb_expand_head(hdr_skb, 0, space, GFP_ATOMIC) < 0) {
+                               dev_kfree_skb(skb);
+                               goto requeue_drop_frag;
+                       }
+
+                       skb_copy_from_linear_data(skb, skb_put(hdr_skb, skb->len),
+                                                 skb->len);
+                       dev_kfree_skb_any(skb);
+                       skb = hdr_skb;
+               }
+
                /*
                 * change the default rx antenna if rx diversity chooses the
                 * other antenna 3 times in a row.
@@ -1763,6 +1798,11 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 
                ath_rx_send_to_mac80211(hw, sc, skb);
 
+requeue_drop_frag:
+               if (sc->rx.frag) {
+                       dev_kfree_skb_any(sc->rx.frag);
+                       sc->rx.frag = NULL;
+               }
 requeue:
                if (edma) {
                        list_add_tail(&bf->list, &sc->rx.rxbuf);