diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2016-01-07 23:22:32 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2016-01-07 23:22:32 +0000 |
commit | 79d762a14ce9b90da53a9280ef6ba7134283c153 (patch) | |
tree | 23e7d056ebe0f80d0a1eb74536aaad43737079bd /sys/net80211 | |
parent | 05ec0ca18c2dd74878fdeb9cc1025f9a0cef5aae (diff) |
Make the A-MPDU reordering buffer more resilient against APs which drop
some subframes or let the sequence number jump up by more than 1 (hard
to tell which it is). We decrease the BA inactivity timeout for quicker
recovery from stalled BA sessions, and add a new timeout which keeps track
of gaps in the subframe sequence number space due to dropped frames.
Gaps expire after 500 msec, the same value FreeBSD uses for their
implementation of this workaround. And Linux uses this trick, too.
This should fix network stalls some people have been seeing in 11n mode.
ok deraadt@
Diffstat (limited to 'sys/net80211')
-rw-r--r-- | sys/net80211/ieee80211_input.c | 74 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 8 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 13 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 3 |
4 files changed, 75 insertions, 23 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 219808866a0..17b57390438 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_input.c,v 1.150 2016/01/05 18:41:16 stsp Exp $ */ +/* $OpenBSD: ieee80211_input.c,v 1.151 2016/01/07 23:22:31 stsp Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe @@ -60,8 +60,11 @@ struct mbuf *ieee80211_defrag(struct ieee80211com *, struct mbuf *, int); void ieee80211_defrag_timeout(void *); -void ieee80211_input_ba(struct ifnet *, struct mbuf *, +void ieee80211_input_ba(struct ieee80211com *, struct mbuf *, struct ieee80211_node *, int, struct ieee80211_rxinfo *); +void ieee80211_input_ba_flush(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_rx_ba *); +void ieee80211_input_ba_gap_timeout(void *arg); void ieee80211_ba_move_window(struct ieee80211com *, struct ieee80211_node *, u_int8_t, u_int16_t); struct mbuf *ieee80211_align_mbuf(struct mbuf *); @@ -301,7 +304,7 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni, (qos & IEEE80211_QOS_ACK_POLICY_MASK) == IEEE80211_QOS_ACK_POLICY_NORMAL)) { /* go through A-MPDU reordering */ - ieee80211_input_ba(ifp, m, ni, tid, rxi); + ieee80211_input_ba(ic, m, ni, tid, rxi); return; /* don't free m! */ } } @@ -688,9 +691,10 @@ ieee80211_defrag_timeout(void *arg) * agreement (see 9.10.7.6). */ void -ieee80211_input_ba(struct ifnet *ifp, struct mbuf *m, +ieee80211_input_ba(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni, int tid, struct ieee80211_rxinfo *rxi) { + struct ifnet *ifp = &ic->ic_if; struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; struct ieee80211_frame *wh; int idx, count; @@ -740,6 +744,22 @@ ieee80211_input_ba(struct ifnet *ifp, struct mbuf *m, rxi->rxi_flags |= IEEE80211_RXI_AMPDU_DONE; ba->ba_buf[idx].rxi = *rxi; + if (ba->ba_buf[ba->ba_head].m == NULL) + timeout_add_msec(&ba->ba_gap_to, IEEE80211_BA_GAP_TIMEOUT); + else if (timeout_pending(&ba->ba_gap_to)) + timeout_del(&ba->ba_gap_to); + + ieee80211_input_ba_flush(ic, ni, ba); +} + +/* Flush a consecutive sequence of frames from the reorder buffer. */ +void +ieee80211_input_ba_flush(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_rx_ba *ba) + +{ + struct ifnet *ifp = &ic->ic_if; + /* pass reordered MPDUs up to the next MAC process */ while (ba->ba_buf[ba->ba_head].m != NULL) { ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, ni, @@ -753,6 +773,38 @@ ieee80211_input_ba(struct ifnet *ifp, struct mbuf *m, ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff; } +/* + * Forcibly move the BA window forward to remove a leading gap which has + * been causing frames to linger in the reordering buffer for too long. + * A leading gap will occur if a particular A-MPDU subframe never arrives + * or if a bug in the sender causes sequence numbers to jump forward by > 1. + */ +void +ieee80211_input_ba_gap_timeout(void *arg) +{ + struct ieee80211_rx_ba *ba = arg; + struct ieee80211_node *ni = ba->ba_ni; + struct ieee80211com *ic = ni->ni_ic; + int s, skipped; + + s = splnet(); + + skipped = 0; + while (skipped < ba->ba_winsize && ba->ba_buf[ba->ba_head].m == NULL) { + /* move window forward */ + ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ; + ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff; + skipped++; + } + if (skipped > 0) + ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff; + + ieee80211_input_ba_flush(ic, ni, ba); + + splx(s); +} + + /* * Change the value of WinStartB (move window forward) upon reception of a * BlockAckReq frame or an ADDBA Request (PBAC). @@ -782,17 +834,7 @@ ieee80211_ba_move_window(struct ieee80211com *ic, struct ieee80211_node *ni, /* move window forward */ ba->ba_winstart = ssn; - /* pass reordered MPDUs up to the next MAC process */ - while (ba->ba_buf[ba->ba_head].m != NULL) { - ieee80211_input(ifp, ba->ba_buf[ba->ba_head].m, ni, - &ba->ba_buf[ba->ba_head].rxi); - ba->ba_buf[ba->ba_head].m = NULL; - - ba->ba_head = (ba->ba_head + 1) % IEEE80211_BA_MAX_WINSZ; - /* move window forward */ - ba->ba_winstart = (ba->ba_winstart + 1) & 0xfff; - } - ba->ba_winend = (ba->ba_winstart + ba->ba_winsize - 1) & 0xfff; + ieee80211_input_ba_flush(ic, ni, ba); } void @@ -2440,6 +2482,7 @@ ieee80211_recv_addba_req(struct ieee80211com *ic, struct mbuf *m, ba->ba_timeout_val = IEEE80211_BA_MAX_TIMEOUT; ba->ba_ni = ni; timeout_set(&ba->ba_to, ieee80211_rx_ba_timeout, ba); + timeout_set(&ba->ba_gap_to, ieee80211_input_ba_gap_timeout, ba); ba->ba_winsize = bufsz; if (ba->ba_winsize == 0 || ba->ba_winsize > IEEE80211_BA_MAX_WINSZ) ba->ba_winsize = IEEE80211_BA_MAX_WINSZ; @@ -2589,6 +2632,7 @@ ieee80211_recv_delba(struct ieee80211com *ic, struct mbuf *m, ba->ba_state = IEEE80211_BA_INIT; /* stop Block Ack inactivity timer */ timeout_del(&ba->ba_to); + timeout_del(&ba->ba_gap_to); if (ba->ba_buf != NULL) { /* free all MSDUs stored in reordering buffer */ diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 7d057a16167..f39b8c6f38a 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.c,v 1.96 2016/01/05 18:41:16 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.c,v 1.97 2016/01/07 23:22:31 stsp Exp $ */ /* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */ /*- @@ -1062,16 +1062,18 @@ ieee80211_ba_del(struct ieee80211_node *ni) for (tid = 0; tid < nitems(ni->ni_rx_ba); tid++) { struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; - if (ba->ba_state == IEEE80211_BA_AGREED) { + if (ba->ba_state != IEEE80211_BA_INIT) { if (timeout_pending(&ba->ba_to)) timeout_del(&ba->ba_to); + if (timeout_pending(&ba->ba_gap_to)) + timeout_del(&ba->ba_gap_to); ba->ba_state = IEEE80211_BA_INIT; } } for (tid = 0; tid < nitems(ni->ni_tx_ba); tid++) { struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; - if (ba->ba_state == IEEE80211_BA_AGREED) { + if (ba->ba_state != IEEE80211_BA_INIT) { if (timeout_pending(&ba->ba_to)) timeout_del(&ba->ba_to); ba->ba_state = IEEE80211_BA_INIT; diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index 4198b1c27ba..74e1718f59f 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.h,v 1.51 2016/01/05 18:41:16 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.h,v 1.52 2016/01/07 23:22:31 stsp Exp $ */ /* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */ /*- @@ -112,18 +112,21 @@ struct ieee80211_tx_ba { struct ieee80211_node *ba_ni; /* backpointer for callbacks */ struct timeout ba_to; int ba_timeout_val; -#define IEEE80211_BA_MIN_TIMEOUT (10 * 1000 * 1000) /* 10 sec */ -#define IEEE80211_BA_MAX_TIMEOUT (60 * 1000 * 1000) /* 60 sec */ +#define IEEE80211_BA_MIN_TIMEOUT (1000 * 1000) /* 1 sec */ +#define IEEE80211_BA_MAX_TIMEOUT (5000 * 1000) /* 5 sec */ int ba_state; #define IEEE80211_BA_INIT 0 #define IEEE80211_BA_REQUESTED 1 #define IEEE80211_BA_AGREED 2 + /* These values are IEEE802.11 frame sequence numbers (0x0-0xfff) */ u_int16_t ba_winstart; u_int16_t ba_winend; + + /* Number of A-MPDU subframes in reorder buffer. */ u_int16_t ba_winsize; -#define IEEE80211_BA_MAX_WINSZ 128 /* maximum we will accept */ +#define IEEE80211_BA_MAX_WINSZ 64 /* corresponds to maximum ADDBA BUFSZ */ u_int8_t ba_token; }; @@ -141,6 +144,8 @@ struct ieee80211_rx_ba { u_int16_t ba_winend; u_int16_t ba_winsize; u_int16_t ba_head; + struct timeout ba_gap_to; +#define IEEE80211_BA_GAP_TIMEOUT 500 /* msec */ }; /* diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 5c6105598e9..edd13c38007 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_proto.c,v 1.58 2016/01/05 18:41:16 stsp Exp $ */ +/* $OpenBSD: ieee80211_proto.c,v 1.59 2016/01/07 23:22:31 stsp Exp $ */ /* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */ /*- @@ -687,6 +687,7 @@ ieee80211_delba_request(struct ieee80211com *ic, struct ieee80211_node *ni, ba->ba_state = IEEE80211_BA_INIT; /* stop Block Ack inactivity timer */ timeout_del(&ba->ba_to); + timeout_del(&ba->ba_gap_to); if (ba->ba_buf != NULL) { /* free all MSDUs stored in reordering buffer */ |