diff options
Diffstat (limited to 'package/kernel/mac80211/patches/subsys/349-mac80211-minstrel_ht-significantly-redesign-the-rate.patch')
-rw-r--r-- | package/kernel/mac80211/patches/subsys/349-mac80211-minstrel_ht-significantly-redesign-the-rate.patch | 767 |
1 files changed, 767 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/subsys/349-mac80211-minstrel_ht-significantly-redesign-the-rate.patch b/package/kernel/mac80211/patches/subsys/349-mac80211-minstrel_ht-significantly-redesign-the-rate.patch new file mode 100644 index 0000000000..6626804eb8 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/349-mac80211-minstrel_ht-significantly-redesign-the-rate.patch @@ -0,0 +1,767 @@ +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 22 Jan 2021 23:57:50 +0100 +Subject: [PATCH] mac80211: minstrel_ht: significantly redesign the rate + probing strategy + +The biggest flaw in current minstrel_ht is the fact that it needs way too +many probing packets to be able to quickly find the best rate. +Depending on the wifi hardware and operating mode, this can significantly +reduce throughput when not operating at the highest available data rate. + +In order to be able to significantly reduce the amount of rate sampling, +we need a much smarter selection of probing rates. + +The new approach introduced by this patch maintains a limited set of +available rates to be tested during a statistics window. + +They are split into distinct categories: +- MINSTREL_SAMPLE_TYPE_INC - incremental rate upgrade: + Pick the next rate group and find the first rate that is faster than + the current max. throughput rate +- MINSTREL_SAMPLE_TYPE_JUMP - random testing of higher rates: + Pick a random rate from the next group that is faster than the current + max throughput rate. This allows faster adaptation when the link changes + significantly +- MINSTREL_SAMPLE_TYPE_SLOW - test a rate between max_prob, max_tp2 and + max_tp in order to reduce the gap between them + +In order to prioritize sampling, every 6 attempts are split into 3x INC, +2x JUMP, 1x SLOW. + +Available rates are checked and refilled on every stats window update. + +With this approach, we finally get a very small delta in throughput when +comparing setting the optimal data rate as a fixed rate vs normal rate +control operation. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -266,6 +266,14 @@ const struct mcs_group minstrel_mcs_grou + const s16 minstrel_cck_bitrates[4] = { 10, 20, 55, 110 }; + const s16 minstrel_ofdm_bitrates[8] = { 60, 90, 120, 180, 240, 360, 480, 540 }; + static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; ++static const u8 minstrel_sample_seq[] = { ++ MINSTREL_SAMPLE_TYPE_INC, ++ MINSTREL_SAMPLE_TYPE_JUMP, ++ MINSTREL_SAMPLE_TYPE_INC, ++ MINSTREL_SAMPLE_TYPE_JUMP, ++ MINSTREL_SAMPLE_TYPE_INC, ++ MINSTREL_SAMPLE_TYPE_SLOW, ++}; + + static void + minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); +@@ -620,77 +628,31 @@ minstrel_ht_prob_rate_reduce_streams(str + } + } + +-static bool +-minstrel_ht_probe_group(struct minstrel_ht_sta *mi, const struct mcs_group *tp_group, +- int tp_idx, const struct mcs_group *group) +-{ +- if (group->bw < tp_group->bw) +- return false; +- +- if (group->streams == tp_group->streams) +- return true; +- +- if (tp_idx < 4 && group->streams == tp_group->streams - 1) +- return true; +- +- return group->streams == tp_group->streams + 1; +-} +- +-static void +-minstrel_ht_find_probe_rates(struct minstrel_ht_sta *mi, u16 *rates, int *n_rates, +- bool faster_rate) ++static u16 ++__minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi, ++ enum minstrel_sample_type type) + { +- const struct mcs_group *group, *tp_group; +- int i, g, max_dur; +- int tp_idx; +- +- tp_group = &minstrel_mcs_groups[MI_RATE_GROUP(mi->max_tp_rate[0])]; +- tp_idx = MI_RATE_IDX(mi->max_tp_rate[0]); +- +- max_dur = minstrel_get_duration(mi->max_tp_rate[0]); +- if (faster_rate) +- max_dur -= max_dur / 16; +- +- for (g = 0; g < MINSTREL_GROUPS_NB; g++) { +- u16 supported = mi->supported[g]; +- +- if (!supported) +- continue; ++ u16 *rates = mi->sample[type].sample_rates; ++ u16 cur; ++ int i; + +- group = &minstrel_mcs_groups[g]; +- if (!minstrel_ht_probe_group(mi, tp_group, tp_idx, group)) ++ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { ++ if (!rates[i]) + continue; + +- for (i = 0; supported; supported >>= 1, i++) { +- int idx; +- +- if (!(supported & 1)) +- continue; +- +- if ((group->duration[i] << group->shift) > max_dur) +- continue; +- +- idx = MI_RATE(g, i); +- if (idx == mi->max_tp_rate[0]) +- continue; +- +- rates[(*n_rates)++] = idx; +- break; +- } ++ cur = rates[i]; ++ rates[i] = 0; ++ return cur; + } ++ ++ return 0; + } + + static void + minstrel_ht_rate_sample_switch(struct minstrel_priv *mp, + struct minstrel_ht_sta *mi) + { +- struct minstrel_rate_stats *mrs; +- u16 rates[MINSTREL_GROUPS_NB]; +- int n_rates = 0; +- int probe_rate = 0; +- bool faster_rate; +- int i; +- u8 random; ++ u16 rate; + + /* + * Use rate switching instead of probing packets for devices with +@@ -699,43 +661,11 @@ minstrel_ht_rate_sample_switch(struct mi + if (mp->hw->max_rates > 1) + return; + +- /* +- * If the current EWMA prob is >75%, look for a rate that's 6.25% +- * faster than the max tp rate. +- * If that fails, look again for a rate that is at least as fast +- */ +- mrs = minstrel_get_ratestats(mi, mi->max_tp_rate[0]); +- faster_rate = mrs->prob_avg > MINSTREL_FRAC(75, 100); +- minstrel_ht_find_probe_rates(mi, rates, &n_rates, faster_rate); +- if (!n_rates && faster_rate) +- minstrel_ht_find_probe_rates(mi, rates, &n_rates, false); +- +- /* If no suitable rate was found, try to pick the next one in the group */ +- if (!n_rates) { +- int g_idx = MI_RATE_GROUP(mi->max_tp_rate[0]); +- u16 supported = mi->supported[g_idx]; +- +- supported >>= MI_RATE_IDX(mi->max_tp_rate[0]); +- for (i = 0; supported; supported >>= 1, i++) { +- if (!(supported & 1)) +- continue; +- +- probe_rate = mi->max_tp_rate[0] + i; +- goto out; +- } +- ++ rate = __minstrel_ht_get_sample_rate(mi, MINSTREL_SAMPLE_TYPE_INC); ++ if (!rate) + return; +- } + +- i = 0; +- if (n_rates > 1) { +- random = prandom_u32(); +- i = random % n_rates; +- } +- probe_rate = rates[i]; +- +-out: +- mi->sample_rate = probe_rate; ++ mi->sample_rate = rate; + mi->sample_mode = MINSTREL_SAMPLE_ACTIVE; + } + +@@ -804,6 +734,274 @@ minstrel_ht_calc_rate_stats(struct minst + mrs->attempts = 0; + } + ++static bool ++minstrel_ht_find_sample_rate(struct minstrel_ht_sta *mi, int type, int idx) ++{ ++ int i; ++ ++ for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { ++ u16 cur = mi->sample[type].sample_rates[i]; ++ ++ if (cur == idx) ++ return true; ++ ++ if (!cur) ++ break; ++ } ++ ++ return false; ++} ++ ++static int ++minstrel_ht_move_sample_rates(struct minstrel_ht_sta *mi, int type, ++ u32 fast_rate_dur, u32 slow_rate_dur) ++{ ++ u16 *rates = mi->sample[type].sample_rates; ++ int i, j; ++ ++ for (i = 0, j = 0; i < MINSTREL_SAMPLE_RATES; i++) { ++ u32 duration; ++ bool valid = false; ++ u16 cur; ++ ++ cur = rates[i]; ++ if (!cur) ++ continue; ++ ++ duration = minstrel_get_duration(cur); ++ switch (type) { ++ case MINSTREL_SAMPLE_TYPE_SLOW: ++ valid = duration > fast_rate_dur && ++ duration < slow_rate_dur; ++ break; ++ case MINSTREL_SAMPLE_TYPE_INC: ++ case MINSTREL_SAMPLE_TYPE_JUMP: ++ valid = duration < fast_rate_dur; ++ break; ++ default: ++ valid = false; ++ break; ++ } ++ ++ if (!valid) { ++ rates[i] = 0; ++ continue; ++ } ++ ++ if (i == j) ++ continue; ++ ++ rates[j++] = cur; ++ rates[i] = 0; ++ } ++ ++ return j; ++} ++ ++static int ++minstrel_ht_group_min_rate_offset(struct minstrel_ht_sta *mi, int group, ++ u32 max_duration) ++{ ++ u16 supported = mi->supported[group]; ++ int i; ++ ++ for (i = 0; i < MCS_GROUP_RATES && supported; i++, supported >>= 1) { ++ if (!(supported & BIT(0))) ++ continue; ++ ++ if (minstrel_get_duration(MI_RATE(group, i)) >= max_duration) ++ continue; ++ ++ return i; ++ } ++ ++ return -1; ++} ++ ++/* ++ * Incremental update rates: ++ * Flip through groups and pick the first group rate that is faster than the ++ * highest currently selected rate ++ */ ++static u16 ++minstrel_ht_next_inc_rate(struct minstrel_ht_sta *mi, u32 fast_rate_dur) ++{ ++ struct minstrel_mcs_group_data *mg; ++ u8 type = MINSTREL_SAMPLE_TYPE_INC; ++ int i, index = 0; ++ u8 group; ++ ++ group = mi->sample[type].sample_group; ++ for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { ++ group = (group + 1) % ARRAY_SIZE(minstrel_mcs_groups); ++ mg = &mi->groups[group]; ++ ++ index = minstrel_ht_group_min_rate_offset(mi, group, ++ fast_rate_dur); ++ if (index < 0) ++ continue; ++ ++ index = MI_RATE(group, index & 0xf); ++ if (!minstrel_ht_find_sample_rate(mi, type, index)) ++ goto out; ++ } ++ index = 0; ++ ++out: ++ mi->sample[type].sample_group = group; ++ ++ return index; ++} ++ ++static int ++minstrel_ht_next_group_sample_rate(struct minstrel_ht_sta *mi, int group, ++ u16 supported, int offset) ++{ ++ struct minstrel_mcs_group_data *mg = &mi->groups[group]; ++ u16 idx; ++ int i; ++ ++ for (i = 0; i < MCS_GROUP_RATES; i++) { ++ idx = sample_table[mg->column][mg->index]; ++ if (++mg->index >= MCS_GROUP_RATES) { ++ mg->index = 0; ++ if (++mg->column >= ARRAY_SIZE(sample_table)) ++ mg->column = 0; ++ } ++ ++ if (idx < offset) ++ continue; ++ ++ if (!(supported & BIT(idx))) ++ continue; ++ ++ return MI_RATE(group, idx); ++ } ++ ++ return -1; ++} ++ ++/* ++ * Jump rates: ++ * Sample random rates, use those that are faster than the highest ++ * currently selected rate. Rates between the fastest and the slowest ++ * get sorted into the slow sample bucket, but only if it has room ++ */ ++static u16 ++minstrel_ht_next_jump_rate(struct minstrel_ht_sta *mi, u32 fast_rate_dur, ++ u32 slow_rate_dur, int *slow_rate_ofs) ++{ ++ struct minstrel_mcs_group_data *mg; ++ struct minstrel_rate_stats *mrs; ++ u32 max_duration = slow_rate_dur; ++ int i, index, offset; ++ u16 *slow_rates; ++ u16 supported; ++ u32 duration; ++ u8 group; ++ ++ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) ++ max_duration = fast_rate_dur; ++ ++ slow_rates = mi->sample[MINSTREL_SAMPLE_TYPE_SLOW].sample_rates; ++ group = mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_group; ++ for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { ++ u8 type; ++ ++ group = (group + 1) % ARRAY_SIZE(minstrel_mcs_groups); ++ mg = &mi->groups[group]; ++ ++ supported = mi->supported[group]; ++ if (!supported) ++ continue; ++ ++ offset = minstrel_ht_group_min_rate_offset(mi, group, ++ max_duration); ++ if (offset < 0) ++ continue; ++ ++ index = minstrel_ht_next_group_sample_rate(mi, group, supported, ++ offset); ++ if (index < 0) ++ continue; ++ ++ duration = minstrel_get_duration(index); ++ if (duration < fast_rate_dur) ++ type = MINSTREL_SAMPLE_TYPE_JUMP; ++ else ++ type = MINSTREL_SAMPLE_TYPE_SLOW; ++ ++ if (minstrel_ht_find_sample_rate(mi, type, index)) ++ continue; ++ ++ if (type == MINSTREL_SAMPLE_TYPE_JUMP) ++ goto found; ++ ++ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) ++ continue; ++ ++ if (duration >= slow_rate_dur) ++ continue; ++ ++ /* skip slow rates with high success probability */ ++ mrs = minstrel_get_ratestats(mi, index); ++ if (mrs->prob_avg > MINSTREL_FRAC(95, 100)) ++ continue; ++ ++ slow_rates[(*slow_rate_ofs)++] = index; ++ if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) ++ max_duration = fast_rate_dur; ++ } ++ index = 0; ++ ++found: ++ mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_group = group; ++ ++ return index; ++} ++ ++static void ++minstrel_ht_refill_sample_rates(struct minstrel_ht_sta *mi) ++{ ++ u32 prob_dur = minstrel_get_duration(mi->max_prob_rate); ++ u32 tp_dur = minstrel_get_duration(mi->max_tp_rate[0]); ++ u32 tp2_dur = minstrel_get_duration(mi->max_tp_rate[1]); ++ u32 fast_rate_dur = min(min(tp_dur, tp2_dur), prob_dur); ++ u32 slow_rate_dur = max(max(tp_dur, tp2_dur), prob_dur); ++ u16 *rates; ++ int i, j; ++ ++ rates = mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates; ++ i = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_INC, ++ fast_rate_dur, slow_rate_dur); ++ while (i < MINSTREL_SAMPLE_RATES) { ++ rates[i] = minstrel_ht_next_inc_rate(mi, tp_dur); ++ if (!rates[i]) ++ break; ++ ++ i++; ++ } ++ ++ rates = mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_rates; ++ i = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_JUMP, ++ fast_rate_dur, slow_rate_dur); ++ j = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_SLOW, ++ fast_rate_dur, slow_rate_dur); ++ while (i < MINSTREL_SAMPLE_RATES) { ++ rates[i] = minstrel_ht_next_jump_rate(mi, fast_rate_dur, ++ slow_rate_dur, &j); ++ if (!rates[i]) ++ break; ++ ++ i++; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(mi->sample); i++) ++ memcpy(mi->sample[i].cur_sample_rates, mi->sample[i].sample_rates, ++ sizeof(mi->sample[i].cur_sample_rates)); ++} ++ ++ + /* + * Update rate statistics and select new primary rates + * +@@ -848,8 +1046,6 @@ minstrel_ht_update_stats(struct minstrel + mi->ampdu_packets = 0; + } + +- mi->sample_count = 0; +- + memset(tmp_mcs_tp_rate, 0, sizeof(tmp_mcs_tp_rate)); + memset(tmp_legacy_tp_rate, 0, sizeof(tmp_legacy_tp_rate)); + +@@ -885,8 +1081,6 @@ minstrel_ht_update_stats(struct minstrel + if (!mi->supported[group]) + continue; + +- mi->sample_count++; +- + /* (re)Initialize group rate indexes */ + for(j = 0; j < MAX_THR_RATES; j++) + tmp_group_tp_rate[j] = MI_RATE(group, 0); +@@ -953,9 +1147,7 @@ minstrel_ht_update_stats(struct minstrel + + /* Try to increase robustness of max_prob_rate*/ + minstrel_ht_prob_rate_reduce_streams(mi); +- +- /* try to sample half of all available rates during each interval */ +- mi->sample_count *= 4; ++ minstrel_ht_refill_sample_rates(mi); + + if (sample) + minstrel_ht_rate_sample_switch(mp, mi); +@@ -972,6 +1164,7 @@ minstrel_ht_update_stats(struct minstrel + + /* Reset update timer */ + mi->last_stats_update = jiffies; ++ mi->sample_time = jiffies; + } + + static bool +@@ -1002,28 +1195,6 @@ minstrel_ht_txstat_valid(struct minstrel + } + + static void +-minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi) +-{ +- struct minstrel_mcs_group_data *mg; +- +- for (;;) { +- mi->sample_group++; +- mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); +- mg = &mi->groups[mi->sample_group]; +- +- if (!mi->supported[mi->sample_group]) +- continue; +- +- if (++mg->index >= MCS_GROUP_RATES) { +- mg->index = 0; +- if (++mg->column >= ARRAY_SIZE(sample_table)) +- mg->column = 0; +- } +- break; +- } +-} +- +-static void + minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary) + { + int group, orig_group; +@@ -1108,14 +1279,6 @@ minstrel_ht_tx_status(void *priv, struct + mi->ampdu_packets++; + mi->ampdu_len += info->status.ampdu_len; + +- if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) { +- int avg_ampdu_len = minstrel_ht_avg_ampdu_len(mi); +- +- mi->sample_wait = 16 + 2 * avg_ampdu_len; +- mi->sample_tries = 1; +- mi->sample_count--; +- } +- + if (mi->sample_mode != MINSTREL_SAMPLE_IDLE) + rate_sample = minstrel_get_ratestats(mi, mi->sample_rate); + +@@ -1387,97 +1550,20 @@ minstrel_ht_update_rates(struct minstrel + rate_control_set_rates(mp->hw, mi->sta, rates); + } + +-static int +-minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++static u16 ++minstrel_ht_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) + { +- struct minstrel_rate_stats *mrs; +- struct minstrel_mcs_group_data *mg; +- unsigned int sample_dur, sample_group, cur_max_tp_streams; +- int tp_rate1, tp_rate2; +- int sample_idx = 0; +- +- if (mp->hw->max_rates == 1 && mp->sample_switch && +- (mi->total_packets_cur >= SAMPLE_SWITCH_THR || +- mp->sample_switch == 1)) +- return -1; +- +- if (mi->sample_wait > 0) { +- mi->sample_wait--; +- return -1; +- } +- +- if (!mi->sample_tries) +- return -1; +- +- sample_group = mi->sample_group; +- mg = &mi->groups[sample_group]; +- sample_idx = sample_table[mg->column][mg->index]; +- minstrel_set_next_sample_idx(mi); +- +- if (!(mi->supported[sample_group] & BIT(sample_idx))) +- return -1; +- +- mrs = &mg->rates[sample_idx]; +- sample_idx += MI_RATE(sample_group, 0); +- +- tp_rate1 = mi->max_tp_rate[0]; ++ u8 seq; + +- /* Set tp_rate2 to the second highest max_tp_rate */ +- if (minstrel_get_duration(mi->max_tp_rate[0]) > +- minstrel_get_duration(mi->max_tp_rate[1])) { +- tp_rate2 = mi->max_tp_rate[0]; ++ if (mp->hw->max_rates > 1) { ++ seq = mi->sample_seq; ++ mi->sample_seq = (seq + 1) % ARRAY_SIZE(minstrel_sample_seq); ++ seq = minstrel_sample_seq[seq]; + } else { +- tp_rate2 = mi->max_tp_rate[1]; ++ seq = MINSTREL_SAMPLE_TYPE_INC; + } + +- /* +- * Sampling might add some overhead (RTS, no aggregation) +- * to the frame. Hence, don't use sampling for the highest currently +- * used highest throughput or probability rate. +- */ +- if (sample_idx == mi->max_tp_rate[0] || sample_idx == mi->max_prob_rate) +- return -1; +- +- /* +- * Do not sample if the probability is already higher than 95%, +- * or if the rate is 3 times slower than the current max probability +- * rate, to avoid wasting airtime. +- */ +- sample_dur = minstrel_get_duration(sample_idx); +- if (mrs->prob_avg > MINSTREL_FRAC(95, 100) || +- minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur) +- return -1; +- +- +- /* +- * For devices with no configurable multi-rate retry, skip sampling +- * below the per-group max throughput rate, and only use one sampling +- * attempt per rate +- */ +- if (mp->hw->max_rates == 1 && +- (minstrel_get_duration(mg->max_group_tp_rate[0]) < sample_dur || +- mrs->attempts)) +- return -1; +- +- /* Skip already sampled slow rates */ +- if (sample_dur >= minstrel_get_duration(tp_rate1) && mrs->attempts) +- return -1; +- +- /* +- * Make sure that lower rates get sampled only occasionally, +- * if the link is working perfectly. +- */ +- +- cur_max_tp_streams = minstrel_mcs_groups[MI_RATE_GROUP(tp_rate1)].streams; +- if (sample_dur >= minstrel_get_duration(tp_rate2) && +- (cur_max_tp_streams - 1 < +- minstrel_mcs_groups[sample_group].streams || +- sample_dur >= minstrel_get_duration(mi->max_prob_rate))) +- return -1; +- +- mi->sample_tries--; +- +- return sample_idx; ++ return __minstrel_ht_get_sample_rate(mi, seq); + } + + static void +@@ -1489,7 +1575,7 @@ minstrel_ht_get_rate(void *priv, struct + struct ieee80211_tx_rate *rate = &info->status.rates[0]; + struct minstrel_ht_sta *mi = priv_sta; + struct minstrel_priv *mp = priv; +- int sample_idx; ++ u16 sample_idx; + + if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && + !minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_prob_rate))) +@@ -1505,11 +1591,19 @@ minstrel_ht_get_rate(void *priv, struct + /* Don't use EAPOL frames for sampling on non-mrr hw */ + if (mp->hw->max_rates == 1 && + (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) +- sample_idx = -1; +- else +- sample_idx = minstrel_get_sample_rate(mp, mi); ++ return; + +- if (sample_idx < 0) ++ if (mp->hw->max_rates == 1 && mp->sample_switch && ++ (mi->total_packets_cur >= SAMPLE_SWITCH_THR || ++ mp->sample_switch == 1)) ++ return; ++ ++ if (time_is_before_jiffies(mi->sample_time)) ++ return; ++ ++ mi->sample_time = jiffies + MINSTREL_SAMPLE_INTERVAL; ++ sample_idx = minstrel_ht_get_sample_rate(mp, mi); ++ if (!sample_idx) + return; + + sample_group = &minstrel_mcs_groups[MI_RATE_GROUP(sample_idx)]; +@@ -1630,16 +1724,6 @@ minstrel_ht_update_caps(void *priv, stru + + mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); + +- /* When using MRR, sample more on the first attempt, without delay */ +- if (mp->has_mrr) { +- mi->sample_count = 16; +- mi->sample_wait = 0; +- } else { +- mi->sample_count = 8; +- mi->sample_wait = 8; +- } +- mi->sample_tries = 4; +- + if (!use_vht) { + stbc = (ht_cap & IEEE80211_HT_CAP_RX_STBC) >> + IEEE80211_HT_CAP_RX_STBC_SHIFT; +--- a/net/mac80211/rc80211_minstrel_ht.h ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -69,6 +69,8 @@ + #define MI_RATE_IDX(_rate) FIELD_GET(MI_RATE_IDX_MASK, _rate) + #define MI_RATE_GROUP(_rate) FIELD_GET(MI_RATE_GROUP_MASK, _rate) + ++#define MINSTREL_SAMPLE_RATES 5 /* rates per sample type */ ++#define MINSTREL_SAMPLE_INTERVAL (HZ / 50) + + struct minstrel_priv { + struct ieee80211_hw *hw; +@@ -126,6 +128,13 @@ struct minstrel_rate_stats { + bool retry_updated; + }; + ++enum minstrel_sample_type { ++ MINSTREL_SAMPLE_TYPE_INC, ++ MINSTREL_SAMPLE_TYPE_JUMP, ++ MINSTREL_SAMPLE_TYPE_SLOW, ++ __MINSTREL_SAMPLE_TYPE_MAX ++}; ++ + struct minstrel_mcs_group_data { + u8 index; + u8 column; +@@ -144,6 +153,12 @@ enum minstrel_sample_mode { + MINSTREL_SAMPLE_PENDING, + }; + ++struct minstrel_sample_category { ++ u8 sample_group; ++ u16 sample_rates[MINSTREL_SAMPLE_RATES]; ++ u16 cur_sample_rates[MINSTREL_SAMPLE_RATES]; ++}; ++ + struct minstrel_ht_sta { + struct ieee80211_sta *sta; + +@@ -175,16 +190,14 @@ struct minstrel_ht_sta { + /* tx flags to add for frames for this sta */ + u32 tx_flags; + +- u8 sample_wait; +- u8 sample_tries; +- u8 sample_count; ++ unsigned long sample_time; ++ struct minstrel_sample_category sample[__MINSTREL_SAMPLE_TYPE_MAX]; ++ ++ u8 sample_seq; + + enum minstrel_sample_mode sample_mode; + u16 sample_rate; + +- /* current MCS group to be sampled */ +- u8 sample_group; +- + u8 band; + + /* Bitfield of supported MCS rates of all groups */ |