diff options
author | adrian <adrian@FreeBSD.org> | 2012-05-22 19:37:12 +0000 |
---|---|---|
committer | adrian <adrian@FreeBSD.org> | 2012-05-22 19:37:12 +0000 |
commit | e4c6de87cb3ac748004684340772c60210685b98 (patch) | |
tree | d5acc01899f69f0662933358b42c144f6f6041b5 | |
parent | 9243e0fcfd77d81bd05001344bad067e0d406e75 (diff) | |
download | FreeBSD-src-e4c6de87cb3ac748004684340772c60210685b98.zip FreeBSD-src-e4c6de87cb3ac748004684340772c60210685b98.tar.gz |
Fix some corner cases in the ieee80211_send_bar() handling.
* If the first call succeeded but failed to transmit, a timer would
reschedule it via bar_timeout(). Unfortunately bar_timeout() didn't
check the return value from the ieee80211_send_bar() reattempt and
if that failed (eg the driver ic_raw_xmit() failed), it would never
re-arm the timer.
* If BARPEND is cleared (which ieee80211_send_bar() will do if it can't
TX), then re-arming the timer isn't enough - once bar_timeout() occurs,
it'll see BARPEND is 0 and not run through the rest of the routine.
So when rearming the timer, also set that flag.
* If the TX wasn't occuring, bar_tx_complete() wouldn't be called and the
driver callback wouldn't be called either. So the driver had no idea
that the BAR TX attempt had failed. In the ath(4) case, TX would stay
paused.
(There's no callback to indicate that BAR TX had failed or not;
only a "BAR TX was attempted". That's a separate, later problem.)
So call the driver callback (ic_bar_response()) before the ADDBA session
is torn down, so it has a chance of being notified that things didn't
quite go to plan.
I've verified that yes, this does suspend traffic for ath(4), retry BAR
TX even if the driver is failing ic_raw_xmit(), and then eventually giving
up and sending a DELBA. I'll address the "out of ath_buf" issue in ath(4)
in a subsequent commit - this commit just fixes the edge case where any
driver is (way) out of internal buffers/descriptors and fails frame TX.
PR: kern/168170
Reviewed by: bschmidt
MFC after: 1 month
-rw-r--r-- | sys/net80211/ieee80211_ht.c | 28 |
1 files changed, 27 insertions, 1 deletions
diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index 9134b8d..6134467 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -2166,6 +2166,9 @@ ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, } } +/* XXX */ +static void bar_start_timer(struct ieee80211_tx_ampdu *tap); + static void bar_timeout(void *arg) { @@ -2184,11 +2187,34 @@ bar_timeout(void *arg) return; /* XXX ? */ if (tap->txa_attempts >= ieee80211_bar_maxtries) { + struct ieee80211com *ic = ni->ni_ic; + ni->ni_vap->iv_stats.is_ampdu_bar_tx_fail++; + /* + * If (at least) the last BAR TX timeout was due to + * an ieee80211_send_bar() failures, then we need + * to make sure we notify the driver that a BAR + * TX did occur and fail. This gives the driver + * a chance to undo any queue pause that may + * have occured. + */ + ic->ic_bar_response(ni, tap, 1); ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT); } else { ni->ni_vap->iv_stats.is_ampdu_bar_tx_retry++; - ieee80211_send_bar(ni, tap, tap->txa_seqpending); + if (ieee80211_send_bar(ni, tap, tap->txa_seqpending) != 0) { + /* + * If ieee80211_send_bar() fails here, the + * timer may have stopped and/or the pending + * flag may be clear. Because of this, + * fake the BARPEND and reset the timer. + * A retransmission attempt will then occur + * during the next timeout. + */ + /* XXX locking */ + tap->txa_flags |= IEEE80211_AGGR_BARPEND; + bar_start_timer(tap); + } } } |