On Tue, Feb 26, 2019 at 03:33:23PM +0100, Stefan Sperling wrote:
> On Tue, Feb 26, 2019 at 03:04:35PM +0100, Stefan Sperling wrote:
> > This diff makes the RTS threshold dynamic in 11n mode.
> > I am looking for tests with iwn(4), iwm(4), and athn(4) drivers.
> >
> > When there's a lot of competition for air time, RTS can do more harm than
> > good because we end up causing more RTS/CTS frames on the air than actual
> > data frames. So a fixed RTS threshold really doesn't make a lot of sense.
> >
> > This diff implements a heuristic for setting this threshold, based on
> > section
> > 5.2 of the MiRa paper. It should improve Tx throughput on busy channels
> > which
> > are especially common in the 2GHz band. In my testing it helps quite a bit.
> >
> > This diff won't change the situation in 11a/b/g modes, and it might
> > not make a difference if there are 11a/b/g networks in range.
>
> I realized the previous diff might take some time to raise throughput,
> so please try this one instead. The difference is just that the previous
> diff starts out with RTS threshold enabled, while this one starts out with
> RTS threshold disabled. This should make results look better for people
> doing quick tests rather than looking at long-term effects. :-)
>
> diff f727f040295e17987bfffe9c9952e45fd6ad7859 /usr/src
> blob - 7cf96bf074b3eef4d9fbb85c9253bac7e5550fd7
> file + sys/dev/ic/ar5008.c
> --- sys/dev/ic/ar5008.c
> +++ sys/dev/ic/ar5008.c
> @@ -1514,11 +1514,16 @@ ar5008_tx(struct athn_softc *sc, struct mbuf *m, struc
> if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
> (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
> IEEE80211_FC0_TYPE_DATA) {
> + int rtsthres = ic->ic_rtsthreshold;
> enum ieee80211_htprot htprot;
> -
> +
> + if (ni->ni_flags & IEEE80211_NODE_HT)
> + rtsthres = ieee80211_mira_get_rts_threshold(&an->mn,
> + ic, ni, totlen);
> 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 > rtsthres) {
> ds->ds_ctl0 |= AR_TXC0_RTS_ENABLE;
> } else if (((ic->ic_flags & IEEE80211_F_USEPROT) &&
> athn_rates[ridx[0]].phy == IEEE80211_T_OFDM) ||
> blob - 7d7f0697a2b609f4a6e0fab09ede9a480baa7bd3
> file + sys/dev/pci/if_iwm.c
> --- sys/dev/pci/if_iwm.c
> +++ sys/dev/pci/if_iwm.c
> @@ -4216,7 +4216,7 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie
> bus_dma_segment_t *seg;
> uint8_t tid, type;
> int i, totlen, err, pad;
> - int hdrlen2;
> + int hdrlen2, rtsthres = ic->ic_rtsthreshold;
>
> wh = mtod(m, struct ieee80211_frame *);
> hdrlen = ieee80211_get_hdrlen(wh);
> @@ -4292,9 +4292,13 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ie
> flags |= IWM_TX_CMD_FLG_ACK;
> }
>
> + if (ni->ni_flags & IEEE80211_NODE_HT)
> + rtsthres = ieee80211_mira_get_rts_threshold(&in->in_mn, ic, ni,
> + totlen + IEEE80211_CRC_LEN);
> +
> if (type == IEEE80211_FC0_TYPE_DATA &&
> !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
> - (totlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold ||
> + (totlen + IEEE80211_CRC_LEN > rtsthres ||
> (ic->ic_flags & IEEE80211_F_USEPROT)))
> flags |= IWM_TX_CMD_FLG_PROT_REQUIRE;
>
> blob - 1dab60807c0709735799436e7a4a8e2d8d9f1ff9
> file + sys/dev/pci/if_iwn.c
> --- sys/dev/pci/if_iwn.c
> +++ sys/dev/pci/if_iwn.c
> @@ -3069,8 +3069,13 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ie
>
> /* Check if frame must be protected using RTS/CTS or CTS-to-self. */
> if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
> + int rtsthres = ic->ic_rtsthreshold;
> + if (ni->ni_flags & IEEE80211_NODE_HT)
> + rtsthres = ieee80211_mira_get_rts_threshold(&wn->mn,
> + ic, ni, totlen + IEEE80211_CRC_LEN);
> +
> /* 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 > rtsthres) {
> flags |= IWN_TX_NEED_RTS;
> } else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
> ridx >= IWN_RIDX_OFDM6) {
> blob - 4edd26a5fde82a9d9ddfd31e0bd075c0f59d2143
> file + sys/net80211/ieee80211_mira.c
> --- sys/net80211/ieee80211_mira.c
> +++ sys/net80211/ieee80211_mira.c
> @@ -85,6 +85,9 @@ int ieee80211_mira_valid_tx_mcs(struct ieee80211com *,
> uint32_t ieee80211_mira_valid_rates(struct ieee80211com *,
> struct ieee80211_node *);
> uint32_t ieee80211_mira_mcs_below(struct ieee80211_mira_node *, int, int);
> +void ieee80211_mira_set_rts_threshold(struct ieee80211_mira_node *,
> + struct ieee80211com *, struct ieee80211_node *);
> +void ieee80211_mira_reset_collision_stats(struct ieee80211_mira_node *);
>
> /* We use fixed point arithmetic with 64 bit integers. */
> #define MIRA_FP_SHIFT 21
> @@ -309,7 +312,7 @@ ieee80211_mira_ack_rate(struct ieee80211_node *ni)
> {
> /*
> * Assume the ACK was sent at a mandatory ERP OFDM rate.
> - * In the worst case, the firmware has retried at non-HT rates,
> + * In the worst case, the driver has retried at non-HT rates,
> * so for MCS 0 assume we didn't actually send an OFDM frame
> * and ACKs arrived at a basic rate.
> */
> @@ -347,11 +350,12 @@ ieee80211_mira_toverhead(struct ieee80211_mira_node *m
> (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40))
> rts = 1;
> else
> - rts = (mn->ampdu_size > ic->ic_rtsthreshold);
> + rts = (mn->ampdu_size > ieee80211_mira_get_rts_threshold(mn,
> + ic, ni, mn->ampdu_size));
>
> if (rts) {
> /* Assume RTS/CTS were sent at a basic rate. */
> - rate = ieee80211_mira_best_basic_rate(ni);
> + rate = ieee80211_min_basic_rate(ic);
> overhead += ieee80211_mira_legacy_txtime(MIRA_RTSLEN, rate, ic);
> overhead += ieee80211_mira_legacy_txtime(MIRA_CTSLEN, rate, ic);
> }
> @@ -723,6 +727,7 @@ ieee80211_mira_probe_done(struct ieee80211_mira_node *
> {
> ieee80211_mira_cancel_timeouts(mn);
> ieee80211_mira_reset_driver_stats(mn);
> + ieee80211_mira_reset_collision_stats(mn);
> mn->probing = IEEE80211_MIRA_NOT_PROBING;
> mn->probed_rates = 0;
> mn->candidate_rates = 0;
> @@ -1021,7 +1026,104 @@ ieee80211_mira_mcs_below(struct ieee80211_mira_node *m
> return mcs_mask;
> }
>
> +/*
> + * Constants involved in detecting suspected frame collisions.
> + * See section 5.2 of MiRa paper
> + */
> +#define MIRA_COLLISION_LOSS_PERCENTAGE 10 /* from MiRA paper */
> +#define MIRA_COLLISION_DETECTED 3 /* from MiRA paper */
> +
> +/*
> + * XXX The paper's algorithm assumes aggregated frames. This is particularly
> + * important for the detection of consecutive frame collisions which indicate
> + * high competition for air time. Because we do not yet support Tx
> aggregation,
> + * we run the algorithm over the result of several frames instead.
> + * We also aggregate retries across all frames and act upon a percentage of
> + * retried frames, rather than acting on retries seen for one aggregated
> frame.
> + *
> + * The collision window size (number of frames sent) needs to be short to
> + * ensure our detection of consecutive collisions remains somewhat accurate.
> + * We really have no idea how much time passes between frames in the window!
> + * The good news is that users will only care about collision detection
> during
> + * a transmit burst anyway, and we have this case more or less covered.
> + */
> +#define MIRA_COLLISION_MIN_FRAMES 6 /* XXX magic number */
> +#define MIRA_COLLISION_RETRY_PERCENTAGE 60 /* XXX magic number */
> +
> +/* Set RTS threshold based on suspected collision from other STAs. */
> void
> +ieee80211_mira_set_rts_threshold(struct ieee80211_mira_node *mn,
> + struct ieee80211com *ic, struct ieee80211_node *ni)
> +{
> + uint16_t rtsthreshold = mn->rts_threshold;
> + uint32_t loss, retry;
> +
> + /* Update collision window stats. */
> + mn->ifwnd_frames += mn->frames;
> + mn->ifwnd_retries += mn->retries;
> + mn->ifwnd_txfail += mn->txfail;
> + 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;
> + retry = (mn->ifwnd_retries * 100) / mn->ifwnd_frames;
> + if (retry > MIRA_COLLISION_RETRY_PERCENTAGE &&
> + 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. Use RTS. */
> + rtsthreshold = IEEE80211_RTS_DEFAULT;
> + }
> + } else {
> + if (mn->ifwnd > 0)
> + mn->ifwnd--;
> + if (mn->ifwnd == 0)
> + rtsthreshold = IEEE80211_RTS_MAX;
> + }
> +
> + mn->rts_threshold = rtsthreshold;
> + ieee80211_mira_reset_collision_stats(mn);
> +}
> +
> +int
> +ieee80211_mira_get_rts_threshold(struct ieee80211_mira_node *mn,
> + struct ieee80211com *ic, struct ieee80211_node *ni, size_t framelen)
> +{
> + int rtsrate = ieee80211_min_basic_rate(ic);
> + uint64_t txtime, rtsoverhead;
> + /* Magic number from MiRA paper ("cost/benefit ratio"). */
> + static const uint64_t k = MIRA_FP_1 + (MIRA_FP_1 / 2); /* 1.5 */
> +
> + if (mn->probing || mn->rts_threshold >= IEEE80211_RTS_MAX)
> + return IEEE80211_RTS_MAX;
> +
> + /* Use RTS only if potential gains outweigh overhead. */
> + txtime = ieee80211_mira_ht_txtime(framelen, ni->ni_txmcs,
> + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan),
> + (ni->ni_flags & IEEE80211_NODE_HT_SGI20) ? 1 : 0);
> + rtsoverhead = ieee80211_mira_legacy_txtime(MIRA_RTSLEN, rtsrate, ic);
> + rtsoverhead += ieee80211_mira_legacy_txtime(MIRA_CTSLEN, rtsrate, ic);
> + /* convert to fixed-point */
> + txtime <<= MIRA_FP_SHIFT;
> + rtsoverhead <<= MIRA_FP_SHIFT;
> + if (txtime >= MIRA_FP_MUL(k, rtsoverhead))
> + return mn->rts_threshold;
> +
> + return IEEE80211_RTS_MAX;
> +}
> +
> +void
> +ieee80211_mira_reset_collision_stats(struct ieee80211_mira_node *mn)
> +{
> + mn->ifwnd_frames = 0;
> + mn->ifwnd_retries = 0;
> + mn->ifwnd_txfail = 0;
> +}
> +
> +void
> ieee80211_mira_choose(struct ieee80211_mira_node *mn, struct ieee80211com
> *ic,
> struct ieee80211_node *ni)
> {
> @@ -1065,6 +1167,7 @@ ieee80211_mira_choose(struct ieee80211_mira_node *mn,
> 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);
> }
> @@ -1125,7 +1228,9 @@ ieee80211_mira_node_init(struct ieee80211_mira_node *m
> {
> memset(mn, 0, sizeof(*mn));
> mn->agglen = 1;
> + mn->rts_threshold = IEEE80211_RTS_MAX;
> ieee80211_mira_reset_goodput_stats(mn);
> + ieee80211_mira_reset_collision_stats(mn);
>
> timeout_set(&mn->probe_to[IEEE80211_MIRA_PROBE_TO_UP],
> ieee80211_mira_probe_timeout_up, mn);
> blob - 35ea4193fe71cbdf953976ce26f460699b7f41c5
> file + sys/net80211/ieee80211_mira.h
> --- sys/net80211/ieee80211_mira.h
> +++ sys/net80211/ieee80211_mira.h
> @@ -86,6 +86,15 @@ struct ieee80211_mira_node {
>
> /* Goodput statistics for each MCS. */
> struct ieee80211_mira_goodput_stats g[IEEE80211_HT_RATESET_NUM_MCS];
> +
> + /* Interference observation window (see MiRa paper section 5.2). */
> + int ifwnd;
> + uint32_t ifwnd_frames;
> + uint32_t ifwnd_retries;
> + uint32_t ifwnd_txfail;
> +
> + /* Current RTS threshold for this node. */
> + int rts_threshold;
> };
>
> /* Initialize rate control state. */
> @@ -97,5 +106,9 @@ void ieee80211_mira_choose(struct
> ieee80211_mira_node
>
> /* Cancel timeouts scheduled by ieee80211_mira_choose(). */
> void ieee80211_mira_cancel_timeouts(struct ieee80211_mira_node *);
> +
> +/* Returns RTS threshold to be used for a frame about to be transmitted. */
> +int ieee80211_mira_get_rts_threshold(struct ieee80211_mira_node *,
> + struct ieee80211com *, struct ieee80211_node *, size_t);
>
> #endif /* _NET80211_IEEE80211_MIRA_H_ */
>
Device iwm0
iwm0 at pci2 dev 0 function 0 "Intel Dual Band Wireless-AC 8265" rev 0x78, msi
iwm0: hw rev 0x230, fw ver 22.361476.0, address b4:6b:fc:f3:e4:13
Network
iwm0: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
lladdr 8c:16:45:9b:c9:fe
index 1 priority 4 llprio 3
trunk: trunkdev trunk0
groups: wlan
media: IEEE802.11 autoselect (HT-MCS15 mode 11n)
status: active
ieee80211: join ZZZZZZZZZ chan 1 bssid XXXXXXXXXXXXXXXXX 83% wpakey
wpaprotos wpa2 wpaakms psk wpaciphers ccmp wpagroupcipher ccmp
Copy a file over NFS (3 times)
Before patch: 20.51 / 19.74 / 19.74 seconds
After patch: 14.48 / 15.03 / 14.49 seconds
It gets 25% faster!
Ping -c100 -i0.2 on local network
Before patch: 2.398/3.869/10.290/1.347 ms
After patch: 2.470/3.245/5.608/0.492 ms
3 differents file get from NFS (once because after it in cache, not
sure if test is relevant or reliable):
Before patch: file1 11.39 / file2 12.21 / file3 6.47 seconds
After patch: file1 12.44 / file2 12.50 / file3 7.26 seconds
This test show a small slowdown while other tests show a big
improvement.