This diff makes the RTS threshold dynamic in 11n mode. It flips the RTS threshold between DEFAULT (512 bytes) and MAX (the maximum size of a non-aggregated 802.11 frame).
The decision whether to use RTS is implemented as a heuristic described in the MiRA paper. MiRa enables RTS if there are retries with almost no loss, indicating competition with other APs and clients. It leaves RTS disabled if there is a lot of loss, indicating we are too far away from the AP or using a bad Tx rate which won't cover the distance. Because an athn(4) hostap needs a per-client threshold I am introducing ni_rtsthreshold in struct ieee80211_node. This could completely replace the ic_rtsthreshold in struct ieee80211com eventually. For now I am only switching 11n capable drivers over to ni_rtsthreshold. I am interested in test reports. When testing this please look for changes in both latency and throughput and let me know if you think it behaves better or worse with this diff. Index: dev/ic/ar5008.c =================================================================== RCS file: /cvs/src/sys/dev/ic/ar5008.c,v retrieving revision 1.42 diff -u -p -r1.42 ar5008.c --- dev/ic/ar5008.c 1 Feb 2017 12:45:56 -0000 1.42 +++ dev/ic/ar5008.c 4 Mar 2017 15:29:02 -0000 @@ -1513,7 +1513,7 @@ ar5008_tx(struct athn_softc *sc, struct htprot = (ic->ic_bss->ni_htop1 & IEEE80211_HTOP1_PROT_MASK); /* NB: Group frames are sent using CCK in 802.11b/g. */ - if (totlen > ic->ic_rtsthreshold) { + if (totlen > ni->ni_rtsthreshold) { ds->ds_ctl0 |= AR_TXC0_RTS_ENABLE; } else if (((ic->ic_flags & IEEE80211_F_USEPROT) && athn_rates[ridx[0]].phy == IEEE80211_T_OFDM) || Index: dev/ic/ath.c =================================================================== RCS file: /cvs/src/sys/dev/ic/ath.c,v retrieving revision 1.112 diff -u -p -r1.112 ath.c --- dev/ic/ath.c 22 Jan 2017 10:17:37 -0000 1.112 +++ dev/ic/ath.c 4 Mar 2017 15:29:23 -0000 @@ -2259,7 +2259,7 @@ ath_tx_start(struct ath_softc *sc, struc if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */ sc->sc_stats.ast_tx_noack++; - } else if (pktlen > ic->ic_rtsthreshold) { + } else if (pktlen > ni->ni_rtsthreshold) { flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ sc->sc_stats.ast_tx_rts++; } Index: dev/pci/if_iwm.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v retrieving revision 1.165 diff -u -p -r1.165 if_iwm.c --- dev/pci/if_iwm.c 20 Feb 2017 15:38:04 -0000 1.165 +++ dev/pci/if_iwm.c 4 Mar 2017 15:30:11 -0000 @@ -4038,7 +4038,7 @@ iwm_tx(struct iwm_softc *sc, struct mbuf if (type == IEEE80211_FC0_TYPE_DATA && !IEEE80211_IS_MULTICAST(wh->i_addr1) && - (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold || + (totlen + IEEE80211_CRC_LEN > ni->ni_rtsthreshold || (ic->ic_flags & IEEE80211_F_USEPROT))) flags |= IWM_TX_CMD_FLG_PROT_REQUIRE; Index: dev/pci/if_iwn.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iwn.c,v retrieving revision 1.184 diff -u -p -r1.184 if_iwn.c --- dev/pci/if_iwn.c 20 Feb 2017 15:38:04 -0000 1.184 +++ dev/pci/if_iwn.c 4 Mar 2017 15:30:02 -0000 @@ -2992,7 +2992,7 @@ iwn_tx(struct iwn_softc *sc, struct mbuf /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { /* NB: Group frames are sent using CCK in 802.11b/g/n (2GHz). */ - if (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { + if (totlen + IEEE80211_CRC_LEN > ni->ni_rtsthreshold) { flags |= IWN_TX_NEED_RTS; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && ridx >= IWN_RIDX_OFDM6) { Index: net80211/ieee80211_mira.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_mira.c,v retrieving revision 1.10 diff -u -p -r1.10 ieee80211_mira.c --- net80211/ieee80211_mira.c 28 Jan 2017 16:01:36 -0000 1.10 +++ net80211/ieee80211_mira.c 4 Mar 2017 16:55:00 -0000 @@ -84,6 +84,9 @@ void ieee80211_mira_probe_next_rate(stru uint32_t ieee80211_mira_valid_rates(struct ieee80211com *, struct ieee80211_node *); uint32_t ieee80211_mira_mcs_below(struct ieee80211_mira_node *, int); +void ieee80211_mira_set_rts_threshold(struct ieee80211_mira_node *, + struct ieee80211com *, struct ieee80211_node *); +void ieee80211_mira_reset_interference_stats(struct ieee80211_mira_node *); /* We use fixed point arithmetic with 64 bit integers. */ #define MIRA_FP_SHIFT 21 @@ -381,7 +384,7 @@ ieee80211_mira_toverhead(struct ieee8021 (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40)) rts = 1; else - rts = (mn->ampdu_size > ic->ic_rtsthreshold); + rts = (mn->ampdu_size > ni->ni_rtsthreshold); if (rts) { /* Assume RTS/CTS were sent at a basic rate. */ @@ -1024,6 +1027,82 @@ ieee80211_mira_mcs_below(struct ieee8021 return mcs_mask; } +/* Constants involved in detecting suspected frame collisions. */ +#define MIRA_COLLISION_MIN_FRAMES 11 /* XXX magic number */ +#define MIRA_COLLISION_LOSS_PERCENTAGE 10 +#define MIRA_COLLISION_DETECTED 3 + +/* + * Set the RTS threshold based on suspected interference from other STAs. + * Note that HT protection settings may enforce RTS regardless of threshold. + * + * See section 5.2 in MiRa paper. + * XXX The paper's algorithm assumes aggregated frames. We run the algorithm + * over the result of several individual frames instead since we do not yet + * support Tx aggregation. + */ +void +ieee80211_mira_set_rts_threshold(struct ieee80211_mira_node *mn, + struct ieee80211com *ic, struct ieee80211_node *ni) +{ + uint16_t rtsthreshold = IEEE80211_RTS_MAX; /* disable RTS by default */ + uint32_t loss; + /* Magic number from MiRA paper ("cost/benefit ratio"). */ + static const uint64_t k = MIRA_FP_1 + (MIRA_FP_1 / 2); /* 1.5 */ + + /* Update interference window stats. */ + mn->ifwnd_frames += mn->frames; + mn->ifwnd_retries += mn->retries; + mn->ifwnd_txfail += mn->txfail; + mn->ifwnd_size += mn->ampdu_size; + if (mn->ifwnd_frames < MIRA_COLLISION_MIN_FRAMES) + return; /* not enough frames yet */ + + /* Check whether the loss pattern indicates frame collisions. */ + loss = (mn->ifwnd_txfail * 100) / mn->ifwnd_frames; + if (mn->ifwnd_retries >= 1 && loss < MIRA_COLLISION_LOSS_PERCENTAGE) { + if (mn->ifwnd == 0) { + /* First frame collision confirmed. */ + mn->ifwnd = MIRA_COLLISION_DETECTED; + } else if (mn->ifwnd == MIRA_COLLISION_DETECTED) { + /* Successive frame collision confirmed. */ + int rate = ieee80211_mira_best_basic_rate(ni); + uint64_t txtime, rtsoverhead; + txtime = ieee80211_mira_ht_txtime(mn->ifwnd_size, + ni->ni_txmcs, IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)); + rtsoverhead = ieee80211_mira_legacy_txtime(MIRA_RTSLEN, + rate, ic); + rtsoverhead += ieee80211_mira_legacy_txtime(MIRA_CTSLEN, + rate, ic); + /* convert to fixed-point */ + txtime <<= MIRA_FP_SHIFT; + rtsoverhead <<= MIRA_FP_SHIFT; + /* Enable RTS if potential gains outweigh overhead. */ + if (txtime >= MIRA_FP_MUL(k, rtsoverhead)) + rtsthreshold = IEEE80211_RTS_DEFAULT; + } + } else if (mn->ifwnd > 0) + mn->ifwnd--; + +#ifdef MIRA_DEBUG + if (ni->ni_rtsthreshold != rtsthreshold) + DPRINTF(("%s RTS threshold %d (%d retries, %d%% loss)\n", + ether_sprintf(ni->ni_macaddr), rtsthreshold, + mn->ifwnd_retries, loss)); +#endif + ni->ni_rtsthreshold = rtsthreshold; + ieee80211_mira_reset_interference_stats(mn); +} + +void +ieee80211_mira_reset_interference_stats(struct ieee80211_mira_node *mn) +{ + mn->ifwnd_frames = 0; + mn->ifwnd_retries = 0; + mn->ifwnd_txfail = 0; + mn->ifwnd_size = 0; +} + void ieee80211_mira_choose(struct ieee80211_mira_node *mn, struct ieee80211com *ic, struct ieee80211_node *ni) @@ -1060,6 +1139,7 @@ ieee80211_mira_choose(struct ieee80211_m IEEE80211_MIRA_PROBE_TIMEOUT_MIN; mn->g[best].nprobes = 0; mn->g[best].nprobe_bytes = 0; + ieee80211_mira_reset_interference_stats(mn); } else ni->ni_txmcs = mn->best_mcs; ieee80211_mira_probe_done(mn); @@ -1068,6 +1148,7 @@ ieee80211_mira_choose(struct ieee80211_m splx(s); return; } else { + ieee80211_mira_set_rts_threshold(mn, ic, ni); ieee80211_mira_reset_driver_stats(mn); ieee80211_mira_schedule_probe_timers(mn, ni); } Index: net80211/ieee80211_mira.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_mira.h,v retrieving revision 1.3 diff -u -p -r1.3 ieee80211_mira.h --- net80211/ieee80211_mira.h 12 Jan 2017 18:06:57 -0000 1.3 +++ net80211/ieee80211_mira.h 4 Mar 2017 16:30:29 -0000 @@ -89,6 +89,13 @@ struct ieee80211_mira_node { /* Goodput statistics for each MCS. */ struct ieee80211_mira_goodput_stats g[IEEE80211_MIRA_NUM_RATES]; + + /* Interference observation window (see MiRa paper section 5.2). */ + int ifwnd; + uint32_t ifwnd_frames; + uint32_t ifwnd_retries; + uint32_t ifwnd_txfail; + uint32_t ifwnd_size; }; /* Initialize rate control state. */ Index: net80211/ieee80211_node.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v retrieving revision 1.115 diff -u -p -r1.115 ieee80211_node.c --- net80211/ieee80211_node.c 4 Mar 2017 12:44:27 -0000 1.115 +++ net80211/ieee80211_node.c 4 Mar 2017 16:54:10 -0000 @@ -184,6 +184,7 @@ ieee80211_node_lateattach(struct ifnet * if (ni == NULL) panic("unable to setup inital BSS node"); ni->ni_chan = IEEE80211_CHAN_ANYC; + ni->ni_rtsthreshold = IEEE80211_RTS_DEFAULT; ic->ic_bss = ieee80211_ref_node(ni); ic->ic_txpower = IEEE80211_TXPOWER_MAX; #ifndef IEEE80211_STA_ONLY Index: net80211/ieee80211_node.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v retrieving revision 1.67 diff -u -p -r1.67 ieee80211_node.h --- net80211/ieee80211_node.h 4 Mar 2017 12:44:27 -0000 1.67 +++ net80211/ieee80211_node.h 4 Mar 2017 16:54:10 -0000 @@ -277,6 +277,7 @@ struct ieee80211_node { int ni_inact; /* inactivity mark count */ int ni_txrate; /* index to ni_rates[] */ int ni_state; + uint16_t ni_rtsthreshold; u_int16_t ni_flags; /* special-purpose state */ #define IEEE80211_NODE_ERP 0x0001