Commit 0d95521e authored by Felix Fietkau's avatar Felix Fietkau Committed by John W. Linville

ath9k: use split rx buffers to get rid of order-1 skb allocations

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: default avatarJouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 4d906740
...@@ -310,6 +310,8 @@ struct ath_rx { ...@@ -310,6 +310,8 @@ struct ath_rx {
struct ath_descdma rxdma; struct ath_descdma rxdma;
struct ath_buf *rx_bufptr; struct ath_buf *rx_bufptr;
struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
struct sk_buff *frag;
}; };
int ath_startrecv(struct ath_softc *sc); int ath_startrecv(struct ath_softc *sc);
......
...@@ -1294,6 +1294,11 @@ static void ath9k_stop(struct ieee80211_hw *hw) ...@@ -1294,6 +1294,11 @@ static void ath9k_stop(struct ieee80211_hw *hw)
} else } else
sc->rx.rxlink = NULL; 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 */ /* disable HAL and put h/w to sleep */
ath9k_hw_disable(ah); ath9k_hw_disable(ah);
ath9k_hw_configpcipowersave(ah, 1, 1); ath9k_hw_configpcipowersave(ah, 1, 1);
......
...@@ -209,11 +209,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) ...@@ -209,11 +209,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
int error = 0, i; int error = 0, i;
u32 size; 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 - ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
ah->caps.rx_status_len); ah->caps.rx_status_len);
...@@ -300,12 +295,12 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) ...@@ -300,12 +295,12 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
sc->sc_flags &= ~SC_OP_RXFLUSH; sc->sc_flags &= ~SC_OP_RXFLUSH;
spin_lock_init(&sc->rx.rxbuflock); 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) { if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
return ath_rx_edma_init(sc, nbufs); return ath_rx_edma_init(sc, nbufs);
} else { } 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", ath_dbg(common, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n",
common->cachelsz, common->rx_bufsize); common->cachelsz, common->rx_bufsize);
...@@ -815,15 +810,9 @@ static bool ath9k_rx_accept(struct ath_common *common, ...@@ -815,15 +810,9 @@ static bool ath9k_rx_accept(struct ath_common *common,
if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len)) if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len))
return false; return false;
/* /* Only use error bits from the last fragment */
* 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
*/
if (rx_stats->rs_more) if (rx_stats->rs_more)
return false; return true;
/* /*
* The rx_stats->rs_status will not be set until the end of the * The rx_stats->rs_status will not be set until the end of the
...@@ -981,6 +970,10 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common, ...@@ -981,6 +970,10 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common,
if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error))
return -EINVAL; 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); ath9k_process_rssi(common, hw, hdr, rx_stats);
if (ath9k_process_rate(common, hw, rx_stats, rx_status)) if (ath9k_process_rate(common, hw, rx_stats, rx_status))
...@@ -1582,7 +1575,7 @@ static void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) ...@@ -1582,7 +1575,7 @@ static void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
{ {
struct ath_buf *bf; 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 ieee80211_rx_status *rxs;
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
...@@ -1633,8 +1626,17 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ...@@ -1633,8 +1626,17 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
if (!skb) if (!skb)
continue; 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);
ath_debug_stat_rx(sc, &rs); ath_debug_stat_rx(sc, &rs);
...@@ -1643,12 +1645,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ...@@ -1643,12 +1645,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
* chain it back at the queue without processing it. * chain it back at the queue without processing it.
*/ */
if (flush) if (flush)
goto requeue; goto requeue_drop_frag;
retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs, retval = ath9k_rx_skb_preprocess(common, hw, hdr, &rs,
rxs, &decrypt_error); rxs, &decrypt_error);
if (retval) if (retval)
goto requeue; goto requeue_drop_frag;
rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp; rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
if (rs.rs_tstamp > tsf_lower && if (rs.rs_tstamp > tsf_lower &&
...@@ -1668,7 +1670,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ...@@ -1668,7 +1670,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 * skb and put it at the tail of the sc->rx.rxbuf list for
* processing. */ * processing. */
if (!requeue_skb) if (!requeue_skb)
goto requeue; goto requeue_drop_frag;
/* Unmap the frame */ /* Unmap the frame */
dma_unmap_single(sc->dev, bf->bf_buf_addr, dma_unmap_single(sc->dev, bf->bf_buf_addr,
...@@ -1679,8 +1681,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ...@@ -1679,8 +1681,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
if (ah->caps.rx_status_len) if (ah->caps.rx_status_len)
skb_pull(skb, ah->caps.rx_status_len); skb_pull(skb, ah->caps.rx_status_len);
ath9k_rx_skb_postprocess(common, skb, &rs, if (!rs.rs_more)
rxs, decrypt_error); ath9k_rx_skb_postprocess(common, hdr_skb, &rs,
rxs, decrypt_error);
/* We will now give hardware our shiny new allocated skb */ /* We will now give hardware our shiny new allocated skb */
bf->bf_mpdu = requeue_skb; bf->bf_mpdu = requeue_skb;
...@@ -1697,6 +1700,38 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ...@@ -1697,6 +1700,38 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
break; 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 * change the default rx antenna if rx diversity chooses the
* other antenna 3 times in a row. * other antenna 3 times in a row.
...@@ -1722,6 +1757,11 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) ...@@ -1722,6 +1757,11 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
ieee80211_rx(hw, skb); ieee80211_rx(hw, skb);
requeue_drop_frag:
if (sc->rx.frag) {
dev_kfree_skb_any(sc->rx.frag);
sc->rx.frag = NULL;
}
requeue: requeue:
if (edma) { if (edma) {
list_add_tail(&bf->list, &sc->rx.rxbuf); list_add_tail(&bf->list, &sc->rx.rxbuf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment