dynamic RTS threshold in 11n mode

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

dynamic RTS threshold in 11n mode

Stefan Sperling-5
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.

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_DEFAULT;
  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_ */

Reply | Threaded
Open this post in threaded view
|

Re: dynamic RTS threshold in 11n mode

Stefan Sperling-5
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_ */

Reply | Threaded
Open this post in threaded view
|

Re: dynamic RTS threshold in 11n mode

Solene Rapenne
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.

Reply | Threaded
Open this post in threaded view
|

Re: dynamic RTS threshold in 11n mode

Stefan Sperling-5
On Wed, Feb 27, 2019 at 12:06:32PM +0100, Solene Rapenne wrote:
> 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!

Nice :)

> 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

Ping times can vary widely and also depend on concurrent transmissions
made by other devices. But the trend looks good, and it is expected
that we're now causing less latency on the channel ourselves.

> 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.

It should be faster in general, but we'll still flip RTS on if the
MiRA heuristic believes it makes sense and that can lead to periods of
time during which performance drops again. We might still need to tweak
this heuristic in the future. There are a lot of details the paper does
not explain. We'll have to figure out what works best in practice.

Thanks for testing! The diff has been committed earlier today.