ath_rx_tasklet improvements...

*  Only hold the RX queue lock while removing and while re-inserting a buffer -
   allowing pre-emption to work properly while the tasklet is reaping and 
   recycling rx buffers.

*  All skb and bus handling is managed in the ath_rxbuf_init function now.

*  Do not stop processing when ath_rxbuf_init fails, because that means low
   free skb but does NOT mean the queue is fully processed.  Since we also keep
   our skbuff except during a very brief period when we free one and reallocate
   one - unless pre-emption or SMP steals it, chances are very good we can keep
   processing the queue even under low memory conditions -- and avoid dreaded
   rx overruns.

*  Add missing locks around code blocks that iterate over rx buffer list in
   flush/drain/init/cleanup/etc helper functions.



git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@3519 0192ed92-7a03-0410-a25b-9323aeb14dbd
This commit is contained in:
mtaylor 2008-04-10 06:19:53 +00:00
parent fcb3fdbe90
commit 9b8af6f68a
2 changed files with 80 additions and 80 deletions

View File

@ -6510,40 +6510,48 @@ ath_rx_tasklet(TQUEUE_ARG data)
struct ath_hal *ah = sc ? sc->sc_ah : NULL;
struct ath_desc *ds;
struct ath_rx_status *rs;
struct sk_buff *skb = NULL;
struct ieee80211_node *ni;
unsigned int len, phyerr, mic_fail = 0;
int is_mcast = 0;
int type = -1; /* undefined */
int init_ret = 0;
int bf_processed = 0;
int skb_accepted = 0;
int errors = 0;
DPRINTF(sc, ATH_DEBUG_RX_PROC, "invoked\n");
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s started...\n", __func__);
do {
bf = STAILQ_FIRST(&sc->sc_rxbuf);
if (bf == NULL) { /* XXX ??? can this happen */
EPRINTF(sc, "Dropping; no recieve buffers available.\n");
break;
}
/*
* Descriptors are now processed at in the first-level
* interrupt handler to support U-APSD trigger search.
* This must also be done even when U-APSD is not active to support
* other error handling that requires immediate attention.
* We check bf_status to find out if the bf's descriptors have
* been processed by the HAL.
/*
* Get next rx buffer pending processing by rx tasklet...
*
* Descriptors are now processed at in the first-level interrupt
* handler to support U-APSD trigger search. This must also be done
* even when U-APSD is not active to support other error handling
* that requires immediate attention. We check bf_status to find
* out if the bf's descriptors have been processed by the interrupt
* handler and are ready for this tasklet to consume them. We
* also never process/remove the self-linked entry at the end
*/
if (!bf || !(bf->bf_status & ATH_BUFSTATUS_RXDESC_DONE))
ATH_RXBUF_LOCK_IRQ(sc);
bf = STAILQ_FIRST(&sc->sc_rxbuf);
if (bf && (bf->bf_status & ATH_BUFSTATUS_RXDESC_DONE) &&
(bf->bf_desc->ds_link != bf->bf_daddr))
STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
else {
if(bf && (bf->bf_status & ATH_BUFSTATUS_RXDESC_DONE))
EPRINTF(sc, "Warning: %s detected a non-empty skb that is self-linked. "
"This may be a driver bug.\n",
__func__);
bf = NULL;
}
ATH_RXBUF_UNLOCK_IRQ(sc);
if (!bf)
break;
ds = bf->bf_desc;
if (ds->ds_link == bf->bf_daddr) {
/* NB: never process the self-linked entry at the end */
break;
}
skb = bf->bf_skb;
if (skb == NULL) {
EPRINTF(sc, "Dropping; buffer contains NULL skbuff.\n");
continue;
}
bf_processed++;
ds = bf->bf_desc;
is_mcast = (((const struct ieee80211_frame*)bf->bf_skb->data)->i_addr1[0] & 0x01) ||
(((const struct ieee80211_frame*)bf->bf_skb->data)->i_addr3[0] & 0x01);
#ifdef AR_DEBUG
if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
@ -6551,14 +6559,10 @@ ath_rx_tasklet(TQUEUE_ARG data)
#endif
rs = &bf->bf_dsstatus.ds_rxstat;
if (rs->rs_rssi < 0)
rs->rs_rssi = 0;
len = rs->rs_datalen;
/* DMA sync. dies spectacularly if len == 0 */
if (len == 0)
goto rx_next;
if (rs->rs_more) {
/*
* Frame spans multiple descriptors; this
@ -6573,11 +6577,13 @@ ath_rx_tasklet(TQUEUE_ARG data)
*/
if (ic->ic_opmode != IEEE80211_M_MONITOR) {
sc->sc_stats.ast_rx_toobig++;
errors++;
goto rx_next;
}
#endif
/* fall thru for monitor mode handling... */
} else if (rs->rs_status != 0) {
errors++;
if (rs->rs_status & HAL_RXERR_CRC)
sc->sc_stats.ast_rx_crcerr++;
if (rs->rs_status & HAL_RXERR_FIFO)
@ -6614,6 +6620,7 @@ ath_rx_tasklet(TQUEUE_ARG data)
goto rx_next;
}
rx_accept:
skb_accepted++;
/*
* Sync and unmap the frame. At this point we're
* committed to passing the sk_buff somewhere so
@ -6624,51 +6631,41 @@ rx_accept:
bus_dma_sync_single(sc->sc_bdev,
bf->bf_skbaddr, len, BUS_DMA_FROMDEVICE);
bus_unmap_single(sc->sc_bdev, bf->bf_skbaddr,
sc->sc_rxbufsize, BUS_DMA_FROMDEVICE);
bf->bf_skbaddr = 0;
bf->bf_skb = NULL;
sc->sc_stats.ast_ant_rx[rs->rs_antenna]++;
sc->sc_devstats.rx_packets++;
sc->sc_devstats.rx_bytes += len;
skb_put(skb, len);
skb->protocol = __constant_htons(ETH_P_CONTROL);
skb_put(bf->bf_skb, len);
bf->bf_skb->protocol = __constant_htons(ETH_P_CONTROL);
ath_capture(dev, bf, skb, bf->bf_tsf, 0 /* RX */);
ath_capture(dev, bf, bf->bf_skb, bf->bf_tsf, 0 /* RX */);
/* Finished monitor mode handling, now reject error frames
* before passing to other VAPs. Ignore MIC failures here, as
* we need to recheck them. */
if (rs->rs_status & ~(HAL_RXERR_MIC | HAL_RXERR_DECRYPT)) {
ieee80211_dev_kfree_skb(&skb);
if (rs->rs_status & ~(HAL_RXERR_MIC | HAL_RXERR_DECRYPT))
goto rx_next;
}
/* remove the CRC */
skb_trim(skb, skb->len - IEEE80211_CRC_LEN);
skb_trim(bf->bf_skb, bf->bf_skb->len - IEEE80211_CRC_LEN);
if (mic_fail) {
/* Ignore control frames which are reported with MIC
* error. */
if ((((struct ieee80211_frame *)skb->data)->i_fc[0] &
if ((((struct ieee80211_frame *)bf->bf_skb->data)->i_fc[0] &
IEEE80211_FC0_TYPE_MASK) ==
IEEE80211_FC0_TYPE_CTL)
goto drop_micfail;
ni = ieee80211_find_rxnode(ic, (const struct
ieee80211_frame_min *)skb->data);
ieee80211_frame_min *)bf->bf_skb->data);
if (ni) {
if (ni->ni_table)
ieee80211_check_mic(ni, skb);
ieee80211_check_mic(ni, bf->bf_skb);
ieee80211_unref_node(&ni);
}
drop_micfail:
ieee80211_dev_kfree_skb(&skb);
skb = NULL;
mic_fail = 0;
goto rx_next;
}
@ -6679,23 +6676,23 @@ drop_micfail:
DPRINTF(sc, ATH_DEBUG_RECV, "Dropping short packet; length %d.\n",
len);
sc->sc_stats.ast_rx_tooshort++;
ieee80211_dev_kfree_skb(&skb);
errors++;
goto rx_next;
}
/* Normal receive. */
if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV))
ieee80211_dump_pkt(ic, skb->data, skb->len,
ieee80211_dump_pkt(ic, bf->bf_skb->data, bf->bf_skb->len,
sc->sc_hwmap[rs->rs_rate].ieeerate,
rs->rs_rssi);
{
struct ieee80211_frame * wh =
(struct ieee80211_frame *) skb->data;
(struct ieee80211_frame *) bf->bf_skb->data;
/* only print beacons */
if ((skb->len >= sizeof(struct ieee80211_frame)) &&
if ((bf->bf_skb->len >= sizeof(struct ieee80211_frame)) &&
((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK)
== IEEE80211_FC0_TYPE_MGT) &&
((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
@ -6714,7 +6711,7 @@ drop_micfail:
* are on-channel. */
if (!sc->sc_scanning && !(ic->ic_flags & IEEE80211_F_SCAN))
ATH_RSSI_LPF(sc->sc_halstats.ns_avgrssi, rs->rs_rssi);
KASSERT((atomic_read(&skb->users) == 1),
KASSERT((atomic_read(&bf->bf_skb->users) == 1),
("BAD starting skb reference count!"));
/*
* Locate the node for sender, track state, and then
@ -6727,7 +6724,7 @@ drop_micfail:
/* Fast path: node is present in the key map;
* grab a reference for processing the frame. */
ni = ieee80211_ref_node(ni);
type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
type = ieee80211_input(ni->ni_vap, ni, bf->bf_skb, rs->rs_rssi, bf->bf_tsf);
ieee80211_unref_node(&ni);
} else {
/*
@ -6735,10 +6732,10 @@ drop_micfail:
* add the node to the mapping table if possible.
*/
ni = ieee80211_find_rxnode(ic,
(const struct ieee80211_frame_min *)skb->data);
(const struct ieee80211_frame_min *)bf->bf_skb->data);
if (ni != NULL) {
ieee80211_keyix_t keyix;
type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
type = ieee80211_input(ni->ni_vap, ni, bf->bf_skb, rs->rs_rssi, bf->bf_tsf);
/*
* If the station has a key cache slot assigned
* update the key->node mapping table.
@ -6751,11 +6748,11 @@ drop_micfail:
} else {
struct ieee80211vap * vap;
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
type = ieee80211_input(vap, NULL, skb, rs->rs_rssi, bf->bf_tsf);
type = ieee80211_input(vap, NULL, bf->bf_skb, rs->rs_rssi, bf->bf_tsf);
}
}
}
KASSERT((atomic_read(&skb->users) == 1),
KASSERT((atomic_read(&bf->bf_skb->users) == 1),
("ieee80211_input changed skb reference count!"));
if (sc->sc_diversity) {
@ -6786,22 +6783,27 @@ drop_micfail:
rx_next:
KASSERT(bf != NULL, ("null bf"));
KASSERT(bf->bf_skb != NULL, ("null bf->bf_skb"));
if (0 != (init_ret = ath_rxbuf_init(sc, bf))) {
EPRINTF(sc, "Failed to reinitialize rxbuf: %d. Lost an RX buffer!\n", init_ret);
break;
}
/* Return the rx buffer to the queue... */
ATH_RXBUF_LOCK_IRQ(sc);
STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
ATH_RXBUF_RESET(bf);
STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
ATH_RXBUF_UNLOCK_IRQ(sc);
} while (ath_rxbuf_init(sc, bf) == 0);
/* RX signal state monitoring.
* XXX: GIANT HACK
* With 0.9.30.13 ANI control appears to be broken. ANI is designed
* only for client (STA/AHDEMO) only mode. This function updates
* the data used for ANI, so we will only call it for client only
* mode.
* This may will not affect ANI problems in client only mode. */
} while (1);
if (sc->sc_useintmit)
ath_hal_rxmonitor(ah, &sc->sc_halstats, &sc->sc_curchan);
if (!bf_processed)
DPRINTF(sc, ATH_DEBUG_RX_PROC,
"Warning: %s got scheduled when no recieve "
"buffers were ready. Were they cleared?\n",
__func__);
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: cycle completed. "
" %d rx buf processed. %d were errors. %d skb accepted.\n",
__func__, bf_processed, errors, skb_accepted);
#undef PA2DESC
}
@ -8653,6 +8655,7 @@ ath_stoprecv(struct ath_softc *sc)
DPRINTF(sc, ATH_DEBUG_ANY, "receive queue buffer 0x%x, link %p\n",
ath_hal_getrxbuf(ah), sc->sc_rxlink);
ATH_RXBUF_LOCK_IRQ(sc);
STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
struct ath_desc *ds = bf->bf_desc;
struct ath_rx_status *rs = &bf->bf_dsstatus.ds_rxstat;
@ -8661,6 +8664,7 @@ ath_stoprecv(struct ath_softc *sc)
if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL))
ath_printrxbuf(bf, status == HAL_OK);
}
ATH_RXBUF_UNLOCK_IRQ(sc);
}
#endif
sc->sc_rxlink = NULL; /* just in case */
@ -8692,12 +8696,15 @@ ath_startrecv(struct ath_softc *sc)
dev->mtu, sc->sc_cachelsz, sc->sc_rxbufsize);
sc->sc_rxlink = NULL;
ATH_RXBUF_LOCK_IRQ(sc);
STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
int error = ath_rxbuf_init(sc, bf);
ATH_RXBUF_RESET(bf);
if (error < 0)
if (error < 0) {
ATH_RXBUF_UNLOCK_IRQ_EARLY(sc);
return error;
}
}
ATH_RXBUF_UNLOCK_IRQ(sc);
sc->sc_rxbufcur = NULL;
@ -8717,8 +8724,10 @@ static void
ath_flushrecv(struct ath_softc *sc)
{
struct ath_buf *bf;
ATH_RXBUF_LOCK_IRQ(sc);
STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list)
cleanup_ath_buf(sc, bf, BUS_DMA_FROMDEVICE);
ATH_RXBUF_UNLOCK_IRQ(sc);
}
/*

View File

@ -459,15 +459,6 @@ struct ath_buf {
const char* bf_taken_at_func;
};
/*
* reset the rx buffer.
* any new fields added to the athbuf and require
* reset need to be added to this macro.
* currently bf_status is the only one that
* requires reset.
*/
#define ATH_RXBUF_RESET(bf) bf->bf_status=0
/* XXX: only managed for rx at the moment */
#define ATH_BUFSTATUS_RXDESC_DONE 0x00000001 /* rx descriptor processing complete, desc processed by hal */
#define ATH_BUFSTATUS_RADAR_DONE 0x00000002 /* marker to indicate a PHYERR for radar pulse