Sync BAR frame handling with out of tree work:
o correct BAR frame construction for AMPDU o retransmit BAR frames until ACK'd or timeout (use tunables to control behaviour, default is very aggressive) o defer seq# update until BAR frame is ACK'd o add BAR response handling callback for driver to interpose and push new state to device or push pending aggregates While here also: o add backpointer to node in the per-tid tx aggregation data structure o move ampdu tx state setup/teardown work to separate functions
This commit is contained in:
parent
dfcd1f4d0e
commit
cc71a422ea
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=184280
@ -86,6 +86,8 @@ int ieee80211_recv_bar_ena = 1;
|
||||
int ieee80211_addba_timeout = -1; /* timeout waiting for ADDBA response */
|
||||
int ieee80211_addba_backoff = -1; /* backoff after max ADDBA requests */
|
||||
int ieee80211_addba_maxtries = 3; /* max ADDBA requests before backoff */
|
||||
int ieee80211_bar_timeout = -1; /* timeout waiting for BAR response */
|
||||
int ieee80211_bar_maxtries = 50; /* max BAR requests before DELBA */
|
||||
|
||||
/*
|
||||
* Setup HT parameters that depends on the clock frequency.
|
||||
@ -98,6 +100,7 @@ ieee80211_ht_setup(void)
|
||||
#endif
|
||||
ieee80211_addba_timeout = msecs_to_ticks(250);
|
||||
ieee80211_addba_backoff = msecs_to_ticks(10*1000);
|
||||
ieee80211_bar_timeout = msecs_to_ticks(250);
|
||||
}
|
||||
SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_setup, NULL);
|
||||
|
||||
@ -113,6 +116,10 @@ static void ieee80211_addba_stop(struct ieee80211_node *ni,
|
||||
struct ieee80211_tx_ampdu *tap);
|
||||
static void ieee80211_aggr_recv_action(struct ieee80211_node *ni,
|
||||
const uint8_t *frm, const uint8_t *efrm);
|
||||
static void ieee80211_bar_response(struct ieee80211_node *ni,
|
||||
struct ieee80211_tx_ampdu *tap, int status);
|
||||
static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap);
|
||||
static void bar_stop_timer(struct ieee80211_tx_ampdu *tap);
|
||||
|
||||
void
|
||||
ieee80211_ht_attach(struct ieee80211com *ic)
|
||||
@ -124,6 +131,7 @@ ieee80211_ht_attach(struct ieee80211com *ic)
|
||||
ic->ic_addba_request = ieee80211_addba_request;
|
||||
ic->ic_addba_response = ieee80211_addba_response;
|
||||
ic->ic_addba_stop = ieee80211_addba_stop;
|
||||
ic->ic_bar_response = ieee80211_bar_response;
|
||||
|
||||
ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
|
||||
ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
|
||||
@ -534,7 +542,6 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
|
||||
*/
|
||||
return PROCESS;
|
||||
}
|
||||
|
||||
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
|
||||
tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0];
|
||||
else
|
||||
@ -803,6 +810,7 @@ ieee80211_ht_node_init(struct ieee80211_node *ni)
|
||||
for (ac = 0; ac < WME_NUM_AC; ac++) {
|
||||
tap = &ni->ni_tx_ampdu[ac];
|
||||
tap->txa_ac = ac;
|
||||
tap->txa_ni = ni;
|
||||
/* NB: further initialization deferred */
|
||||
}
|
||||
ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
|
||||
@ -815,7 +823,6 @@ ieee80211_ht_node_init(struct ieee80211_node *ni)
|
||||
void
|
||||
ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
|
||||
{
|
||||
struct ieee80211com *ic = ni->ni_ic;
|
||||
int i;
|
||||
|
||||
KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node"));
|
||||
@ -823,18 +830,8 @@ ieee80211_ht_node_cleanup(struct ieee80211_node *ni)
|
||||
/* XXX optimize this */
|
||||
for (i = 0; i < WME_NUM_AC; i++) {
|
||||
struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i];
|
||||
if (tap->txa_flags & IEEE80211_AGGR_SETUP) {
|
||||
/*
|
||||
* Stop BA stream if setup so driver has a chance
|
||||
* to reclaim any resources it might have allocated.
|
||||
*/
|
||||
ic->ic_addba_stop(ni, &ni->ni_tx_ampdu[i]);
|
||||
tap->txa_lastsample = 0;
|
||||
tap->txa_avgpps = 0;
|
||||
/* NB: clearing NAK means we may re-send ADDBA */
|
||||
tap->txa_flags &=
|
||||
~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
|
||||
}
|
||||
if (tap->txa_flags & IEEE80211_AGGR_SETUP)
|
||||
ampdu_tx_stop(tap);
|
||||
}
|
||||
for (i = 0; i < WME_NUM_TID; i++)
|
||||
ampdu_rx_stop(&ni->ni_rx_ampdu[i]);
|
||||
@ -1421,6 +1418,38 @@ ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ampdu_tx_setup(struct ieee80211_tx_ampdu *tap)
|
||||
{
|
||||
callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
|
||||
tap->txa_flags |= IEEE80211_AGGR_SETUP;
|
||||
}
|
||||
|
||||
static void
|
||||
ampdu_tx_stop(struct ieee80211_tx_ampdu *tap)
|
||||
{
|
||||
struct ieee80211_node *ni = tap->txa_ni;
|
||||
struct ieee80211com *ic = ni->ni_ic;
|
||||
|
||||
KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP,
|
||||
("txa_flags 0x%x ac %d", tap->txa_flags, tap->txa_ac));
|
||||
|
||||
/*
|
||||
* Stop BA stream if setup so driver has a chance
|
||||
* to reclaim any resources it might have allocated.
|
||||
*/
|
||||
ic->ic_addba_stop(ni, tap);
|
||||
/*
|
||||
* Stop any pending BAR transmit.
|
||||
*/
|
||||
bar_stop_timer(tap);
|
||||
|
||||
tap->txa_lastsample = 0;
|
||||
tap->txa_avgpps = 0;
|
||||
/* NB: clearing NAK means we may re-send ADDBA */
|
||||
tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
|
||||
}
|
||||
|
||||
static void
|
||||
addba_timeout(void *arg)
|
||||
{
|
||||
@ -1483,7 +1512,7 @@ ieee80211_addba_response(struct ieee80211_node *ni,
|
||||
struct ieee80211_tx_ampdu *tap,
|
||||
int status, int baparamset, int batimeout)
|
||||
{
|
||||
int bufsiz;
|
||||
int bufsiz, tid;
|
||||
|
||||
/* XXX locking */
|
||||
addba_stop_timeout(tap);
|
||||
@ -1492,7 +1521,10 @@ ieee80211_addba_response(struct ieee80211_node *ni,
|
||||
/* XXX override our request? */
|
||||
tap->txa_wnd = (bufsiz == 0) ?
|
||||
IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX);
|
||||
/* XXX AC/TID */
|
||||
tid = MS(baparamset, IEEE80211_BAPS_TID);
|
||||
tap->txa_flags |= IEEE80211_AGGR_RUNNING;
|
||||
tap->txa_attempts = 0;
|
||||
} else {
|
||||
/* mark tid so we don't try again */
|
||||
tap->txa_flags |= IEEE80211_AGGR_NAK;
|
||||
@ -1648,7 +1680,6 @@ ieee80211_aggr_recv_action(struct ieee80211_node *ni,
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
IEEE80211_NOTE(vap,
|
||||
IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni,
|
||||
"recv ADDBA response: dialogtoken %u code %d "
|
||||
@ -1817,8 +1848,7 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
|
||||
/* XXX locking */
|
||||
if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
|
||||
/* do deferred setup of state */
|
||||
callout_init(&tap->txa_timer, CALLOUT_MPSAFE);
|
||||
tap->txa_flags |= IEEE80211_AGGR_SETUP;
|
||||
ampdu_tx_setup(tap);
|
||||
}
|
||||
/* XXX hack for not doing proper locking */
|
||||
tap->txa_flags &= ~IEEE80211_AGGR_NAK;
|
||||
@ -1827,7 +1857,6 @@ ieee80211_ampdu_request(struct ieee80211_node *ni,
|
||||
tid = WME_AC_TO_TID(tap->txa_ac);
|
||||
tap->txa_start = ni->ni_txseqs[tid];
|
||||
|
||||
tid = WME_AC_TO_TID(tap->txa_ac);
|
||||
args[0] = dialogtoken;
|
||||
args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE
|
||||
| SM(tid, IEEE80211_BAPS_TID)
|
||||
@ -1869,6 +1898,7 @@ ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
|
||||
uint16_t args[4];
|
||||
|
||||
/* XXX locking */
|
||||
tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
|
||||
if (IEEE80211_AMPDU_RUNNING(tap)) {
|
||||
IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N,
|
||||
ni, "%s: stop BA stream for AC %d (reason %d)",
|
||||
@ -1889,72 +1919,171 @@ ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bar_timeout(void *arg)
|
||||
{
|
||||
struct ieee80211_tx_ampdu *tap = arg;
|
||||
struct ieee80211_node *ni = tap->txa_ni;
|
||||
|
||||
KASSERT((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0,
|
||||
("bar/addba collision, flags 0x%x", tap->txa_flags));
|
||||
|
||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
|
||||
ni, "%s: tid %u flags 0x%x attempts %d", __func__,
|
||||
tap->txa_ac, tap->txa_flags, tap->txa_attempts);
|
||||
|
||||
/* guard against race with bar_tx_complete */
|
||||
if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0)
|
||||
return;
|
||||
/* XXX ? */
|
||||
if (tap->txa_attempts >= ieee80211_bar_maxtries)
|
||||
ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT);
|
||||
else
|
||||
ieee80211_send_bar(ni, tap, tap->txa_seqpending);
|
||||
}
|
||||
|
||||
static void
|
||||
bar_start_timer(struct ieee80211_tx_ampdu *tap)
|
||||
{
|
||||
callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap);
|
||||
}
|
||||
|
||||
static void
|
||||
bar_stop_timer(struct ieee80211_tx_ampdu *tap)
|
||||
{
|
||||
callout_stop(&tap->txa_timer);
|
||||
}
|
||||
|
||||
static void
|
||||
bar_tx_complete(struct ieee80211_node *ni, void *arg, int status)
|
||||
{
|
||||
struct ieee80211_tx_ampdu *tap = arg;
|
||||
|
||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
|
||||
ni, "%s: tid %u flags 0x%x pending %d status %d",
|
||||
__func__, tap->txa_ac, tap->txa_flags,
|
||||
callout_pending(&tap->txa_timer), status);
|
||||
|
||||
/* XXX locking */
|
||||
if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) &&
|
||||
callout_pending(&tap->txa_timer)) {
|
||||
struct ieee80211com *ic = ni->ni_ic;
|
||||
|
||||
if (status) /* ACK'd */
|
||||
bar_stop_timer(tap);
|
||||
ic->ic_bar_response(ni, tap, status);
|
||||
/* NB: just let timer expire so we pace requests */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_bar_response(struct ieee80211_node *ni,
|
||||
struct ieee80211_tx_ampdu *tap, int status)
|
||||
{
|
||||
|
||||
if (status != 0) { /* got ACK */
|
||||
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N,
|
||||
ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u",
|
||||
tap->txa_start,
|
||||
IEEE80211_SEQ_ADD(tap->txa_start, tap->txa_wnd-1),
|
||||
tap->txa_qframes, tap->txa_seqpending,
|
||||
WME_AC_TO_TID(tap->txa_ac));
|
||||
|
||||
/* NB: timer already stopped in bar_tx_complete */
|
||||
tap->txa_start = tap->txa_seqpending;
|
||||
tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Transmit a BAR frame to the specified node. The
|
||||
* BAR contents are drawn from the supplied aggregation
|
||||
* state associated with the node.
|
||||
*
|
||||
* NB: we only handle immediate ACK w/ compressed bitmap.
|
||||
*/
|
||||
int
|
||||
ieee80211_send_bar(struct ieee80211_node *ni,
|
||||
const struct ieee80211_tx_ampdu *tap)
|
||||
struct ieee80211_tx_ampdu *tap, ieee80211_seq seq)
|
||||
{
|
||||
#define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0)
|
||||
#define ADDSHORT(frm, v) do { \
|
||||
frm[0] = (v) & 0xff; \
|
||||
frm[1] = (v) >> 8; \
|
||||
frm += 2; \
|
||||
} while (0)
|
||||
struct ieee80211vap *vap = ni->ni_vap;
|
||||
struct ieee80211com *ic = ni->ni_ic;
|
||||
struct ieee80211_frame_min *wh;
|
||||
struct ieee80211_frame_bar *bar;
|
||||
struct mbuf *m;
|
||||
uint8_t *frm;
|
||||
uint16_t barctl, barseqctl;
|
||||
uint8_t *frm;
|
||||
int tid, ret;
|
||||
|
||||
if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) {
|
||||
/* no ADDBA response, should not happen */
|
||||
/* XXX stat+msg */
|
||||
return EINVAL;
|
||||
}
|
||||
/* XXX locking */
|
||||
bar_stop_timer(tap);
|
||||
|
||||
ieee80211_ref_node(ni);
|
||||
|
||||
m = ieee80211_getmgtframe(&frm,
|
||||
ic->ic_headroom + sizeof(struct ieee80211_frame_min),
|
||||
sizeof(struct ieee80211_ba_request)
|
||||
);
|
||||
m = ieee80211_getmgtframe(&frm, ic->ic_headroom, sizeof(*bar));
|
||||
if (m == NULL)
|
||||
senderr(ENOMEM, is_tx_nobuf);
|
||||
|
||||
wh = mtod(m, struct ieee80211_frame_min *);
|
||||
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 |
|
||||
if (!ieee80211_add_callback(m, bar_tx_complete, tap)) {
|
||||
m_freem(m);
|
||||
senderr(ENOMEM, is_tx_nobuf); /* XXX */
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
bar = mtod(m, struct ieee80211_frame_bar *);
|
||||
bar->i_fc[0] = IEEE80211_FC0_VERSION_0 |
|
||||
IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
|
||||
wh->i_fc[1] = 0;
|
||||
IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
|
||||
IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr);
|
||||
bar->i_fc[1] = 0;
|
||||
IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr);
|
||||
IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr);
|
||||
|
||||
tid = WME_AC_TO_TID(tap->txa_ac);
|
||||
barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
|
||||
IEEE80211_BAPS_POLICY_IMMEDIATE :
|
||||
IEEE80211_BAPS_POLICY_DELAYED)
|
||||
| SM(tid, IEEE80211_BAPS_TID)
|
||||
| SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ)
|
||||
0 : IEEE80211_BAR_NOACK)
|
||||
| IEEE80211_BAR_COMP
|
||||
| SM(tid, IEEE80211_BAR_TID)
|
||||
;
|
||||
barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START)
|
||||
| SM(0, IEEE80211_BASEQ_FRAG)
|
||||
;
|
||||
ADDSHORT(frm, barctl);
|
||||
ADDSHORT(frm, barseqctl);
|
||||
m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
|
||||
barseqctl = SM(seq, IEEE80211_BAR_SEQ_START);
|
||||
/* NB: known to have proper alignment */
|
||||
bar->i_ctl = htole16(barctl);
|
||||
bar->i_seq = htole16(barseqctl);
|
||||
m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_bar);
|
||||
|
||||
M_WME_SETAC(m, WME_AC_VO);
|
||||
|
||||
IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */
|
||||
|
||||
IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
|
||||
ni, "send bar frame (tid %u start %u) on channel %u",
|
||||
tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
|
||||
/* XXX locking */
|
||||
/* init/bump attempts counter */
|
||||
if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0)
|
||||
tap->txa_attempts = 1;
|
||||
else
|
||||
tap->txa_attempts++;
|
||||
tap->txa_seqpending = seq;
|
||||
tap->txa_flags |= IEEE80211_AGGR_BARPEND;
|
||||
|
||||
return ic->ic_raw_xmit(ni, m, NULL);
|
||||
IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N,
|
||||
ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)",
|
||||
tid, barctl, seq, tap->txa_attempts);
|
||||
|
||||
ret = ic->ic_raw_xmit(ni, m, NULL);
|
||||
if (ret != 0) {
|
||||
/* xmit failed, clear state flag */
|
||||
tap->txa_flags &= ~IEEE80211_AGGR_BARPEND;
|
||||
goto bad;
|
||||
}
|
||||
/* XXX hack against tx complete happening before timer is started */
|
||||
if (tap->txa_flags & IEEE80211_AGGR_BARPEND)
|
||||
bar_start_timer(tap);
|
||||
return 0;
|
||||
bad:
|
||||
ieee80211_free_node(ni);
|
||||
return ret;
|
||||
#undef ADDSHORT
|
||||
#undef senderr
|
||||
}
|
||||
|
||||
|
@ -38,12 +38,14 @@
|
||||
typedef uint16_t ieee80211_seq;
|
||||
|
||||
struct ieee80211_tx_ampdu {
|
||||
struct ieee80211_node *txa_ni; /* back pointer */
|
||||
u_short txa_flags;
|
||||
#define IEEE80211_AGGR_IMMEDIATE 0x0001 /* BA policy */
|
||||
#define IEEE80211_AGGR_XCHGPEND 0x0002 /* ADDBA response pending */
|
||||
#define IEEE80211_AGGR_RUNNING 0x0004 /* ADDBA response received */
|
||||
#define IEEE80211_AGGR_SETUP 0x0008 /* deferred state setup */
|
||||
#define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */
|
||||
#define IEEE80211_AGGR_BARPEND 0x0020 /* BAR response pending */
|
||||
uint8_t txa_ac;
|
||||
uint8_t txa_token; /* dialog token */
|
||||
int txa_lastsample; /* ticks @ last traffic sample */
|
||||
@ -54,8 +56,8 @@ struct ieee80211_tx_ampdu {
|
||||
ieee80211_seq txa_start; /* BA window left edge */
|
||||
ieee80211_seq txa_seqpending; /* new txa_start pending BAR response */
|
||||
uint16_t txa_wnd; /* BA window size */
|
||||
uint8_t txa_attempts; /* # ADDBA requests w/o a response */
|
||||
int txa_nextrequest;/* soonest to make next ADDBA request */
|
||||
uint8_t txa_attempts; /* # ADDBA/BAR requests w/o a response*/
|
||||
int txa_nextrequest;/* soonest to make next request */
|
||||
struct callout txa_timer;
|
||||
void *txa_private; /* driver-private storage */
|
||||
};
|
||||
@ -187,8 +189,8 @@ int ieee80211_ampdu_request(struct ieee80211_node *,
|
||||
struct ieee80211_tx_ampdu *);
|
||||
void ieee80211_ampdu_stop(struct ieee80211_node *,
|
||||
struct ieee80211_tx_ampdu *, int);
|
||||
int ieee80211_send_bar(struct ieee80211_node *,
|
||||
const struct ieee80211_tx_ampdu *);
|
||||
int ieee80211_send_bar(struct ieee80211_node *, struct ieee80211_tx_ampdu *,
|
||||
ieee80211_seq);
|
||||
int ieee80211_send_action(struct ieee80211_node *,
|
||||
int, int, uint16_t [4]);
|
||||
uint8_t *ieee80211_add_htcap(uint8_t *, struct ieee80211_node *);
|
||||
|
@ -273,6 +273,9 @@ struct ieee80211com {
|
||||
int status, int baparamset, int batimeout);
|
||||
void (*ic_addba_stop)(struct ieee80211_node *,
|
||||
struct ieee80211_tx_ampdu *);
|
||||
/* BAR response received */
|
||||
void (*ic_bar_response)(struct ieee80211_node *,
|
||||
struct ieee80211_tx_ampdu *, int status);
|
||||
};
|
||||
|
||||
struct ieee80211_aclator;
|
||||
|
Loading…
Reference in New Issue
Block a user