2021-01-22 23:17:31 +00:00
|
|
|
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;
|
|
|
|
-
|
2021-01-26 18:25:10 +00:00
|
|
|
if (mi->supported[MINSTREL_CCK_GROUP])
|
|
|
|
group = MINSTREL_CCK_GROUP;
|
|
|
|
else if (mi->supported[MINSTREL_OFDM_GROUP])
|
|
|
|
@@ -884,8 +1080,6 @@ minstrel_ht_update_stats(struct minstrel
|
2021-01-22 23:17:31 +00:00
|
|
|
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);
|
2021-01-26 18:25:10 +00:00
|
|
|
@@ -952,9 +1146,7 @@ minstrel_ht_update_stats(struct minstrel
|
2021-01-22 23:17:31 +00:00
|
|
|
|
|
|
|
/* 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);
|
2021-01-26 18:25:10 +00:00
|
|
|
@@ -971,6 +1163,7 @@ minstrel_ht_update_stats(struct minstrel
|
2021-01-22 23:17:31 +00:00
|
|
|
|
|
|
|
/* Reset update timer */
|
|
|
|
mi->last_stats_update = jiffies;
|
|
|
|
+ mi->sample_time = jiffies;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2021-01-26 18:25:10 +00:00
|
|
|
@@ -1001,28 +1194,6 @@ minstrel_ht_txstat_valid(struct minstrel
|
2021-01-22 23:17:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2021-01-26 18:25:10 +00:00
|
|
|
@@ -1107,14 +1278,6 @@ minstrel_ht_tx_status(void *priv, struct
|
2021-01-22 23:17:31 +00:00
|
|
|
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);
|
|
|
|
|
2021-01-26 18:25:10 +00:00
|
|
|
@@ -1386,97 +1549,20 @@ minstrel_ht_update_rates(struct minstrel
|
2021-01-22 23:17:31 +00:00
|
|
|
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
|
2021-01-26 18:25:10 +00:00
|
|
|
@@ -1488,7 +1574,7 @@ minstrel_ht_get_rate(void *priv, struct
|
2021-01-22 23:17:31 +00:00
|
|
|
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)))
|
2021-01-26 18:25:10 +00:00
|
|
|
@@ -1504,11 +1590,19 @@ minstrel_ht_get_rate(void *priv, struct
|
2021-01-22 23:17:31 +00:00
|
|
|
/* 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)];
|
2021-01-26 18:25:10 +00:00
|
|
|
@@ -1629,16 +1723,6 @@ minstrel_ht_update_caps(void *priv, stru
|
2021-01-22 23:17:31 +00:00
|
|
|
|
|
|
|
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 */
|