aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-replace-rate-stats-ewma-with-a-.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-replace-rate-stats-ewma-with-a-.patch')
-rw-r--r--package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-replace-rate-stats-ewma-with-a-.patch235
1 files changed, 235 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-replace-rate-stats-ewma-with-a-.patch b/package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-replace-rate-stats-ewma-with-a-.patch
new file mode 100644
index 0000000000..bce9e75378
--- /dev/null
+++ b/package/kernel/mac80211/patches/subsys/361-mac80211-minstrel_ht-replace-rate-stats-ewma-with-a-.patch
@@ -0,0 +1,235 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 28 Sep 2019 15:46:06 +0200
+Subject: [PATCH] mac80211: minstrel_ht: replace rate stats ewma with a
+ better moving average
+
+Rate success probability usually fluctuates a lot under normal conditions.
+With a simple EWMA, noise and fluctuation can be reduced by increasing the
+window length, but that comes at the cost of introducing lag on sudden
+changes.
+
+This change replaces the EWMA implementation with a moving average that's
+designed to significantly reduce lag while keeping a bigger window size
+by being better at filtering out noise.
+
+It is only slightly more expensive than the simple EWMA and still avoids
+divisions in its calculation.
+
+The algorithm is adapted from an implementation intended for a completely
+different field (stock market trading), where the tradeoff of lag vs
+noise filtering is equally important.
+
+The algorithm works in the same way as the "smoothing filter" from
+http://www.stockspotter.com/files/PredictiveIndicators.pdf adapted for
+fixed-point math with some constants, using only addition, bit shifts
+and multiplication
+
+To better make use of the filtering and bigger window size, the update
+interval is cut in half.
+
+For testing, the algorithm can be reverted to the older one via debugfs
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/rc80211_minstrel.c
++++ b/net/mac80211/rc80211_minstrel.c
+@@ -157,14 +157,18 @@ minstrel_update_rates(struct minstrel_pr
+ * Recalculate statistics and counters of a given rate
+ */
+ void
+-minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs)
++minstrel_calc_rate_stats(struct minstrel_priv *mp,
++ struct minstrel_rate_stats *mrs)
+ {
+ unsigned int cur_prob;
+
+ if (unlikely(mrs->attempts > 0)) {
+ mrs->sample_skipped = 0;
+ cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
+- if (unlikely(!mrs->att_hist)) {
++ if (mp->new_avg) {
++ mrs->prob_ewma = minstrel_filter_avg_add(&mrs->avg,
++ cur_prob);
++ } else if (unlikely(!mrs->att_hist)) {
+ mrs->prob_ewma = cur_prob;
+ } else {
+ /*update exponential weighted moving avarage */
+@@ -200,7 +204,7 @@ minstrel_update_stats(struct minstrel_pr
+ struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats;
+
+ /* Update statistics of success probability per rate */
+- minstrel_calc_rate_stats(mrs);
++ minstrel_calc_rate_stats(mp, mrs);
+
+ /* Sample less often below the 10% chance of success.
+ * Sample less often above the 95% chance of success. */
+@@ -289,7 +293,8 @@ minstrel_tx_status(void *priv, struct ie
+ if (mi->sample_deferred > 0)
+ mi->sample_deferred--;
+
+- if (time_after(jiffies, mi->last_stats_update + mp->update_interval))
++ if (time_after(jiffies, mi->last_stats_update +
++ mp->update_interval / (mp->new_avg ? 2 : 1)))
+ minstrel_update_stats(mp, mi);
+ }
+
+--- a/net/mac80211/rc80211_minstrel.h
++++ b/net/mac80211/rc80211_minstrel.h
+@@ -19,6 +19,21 @@
+ #define MAX_THR_RATES 4
+
+ /*
++ * Coefficients for moving average with noise filter (period=16),
++ * scaled by 10 bits
++ *
++ * a1 = exp(-pi * sqrt(2) / period)
++ * coeff2 = 2 * a1 * cos(sqrt(2) * 2 * pi / period)
++ * coeff3 = -sqr(a1)
++ * coeff1 = 1 - coeff2 - coeff3
++ */
++#define MINSTREL_AVG_COEFF1 (MINSTREL_FRAC(1, 1) - \
++ MINSTREL_AVG_COEFF2 - \
++ MINSTREL_AVG_COEFF3)
++#define MINSTREL_AVG_COEFF2 0x00001499
++#define MINSTREL_AVG_COEFF3 -0x0000092e
++
++/*
+ * Perform EWMA (Exponentially Weighted Moving Average) calculation
+ */
+ static inline int
+@@ -32,6 +47,41 @@ minstrel_ewma(int old, int new, int weig
+ return old + incr;
+ }
+
++struct minstrel_avg_ctx {
++ s32 prev[2];
++};
++
++static inline int minstrel_filter_avg_add(struct minstrel_avg_ctx *ctx, s32 in)
++{
++ s32 out_1 = ctx->prev[0];
++ s32 out_2 = ctx->prev[1];
++ s32 val;
++
++ if (!in)
++ in += 1;
++
++ if (!out_1) {
++ val = out_1 = in;
++ goto out;
++ }
++
++ val = MINSTREL_AVG_COEFF1 * in;
++ val += MINSTREL_AVG_COEFF2 * out_1;
++ val += MINSTREL_AVG_COEFF3 * out_2;
++ val >>= MINSTREL_SCALE;
++
++ if (val > 1 << MINSTREL_SCALE)
++ val = 1 << MINSTREL_SCALE;
++ if (val < 0)
++ val = 1;
++
++out:
++ ctx->prev[1] = out_1;
++ ctx->prev[0] = val;
++
++ return val;
++}
++
+ struct minstrel_rate_stats {
+ /* current / last sampling period attempts/success counters */
+ u16 attempts, last_attempts;
+@@ -40,6 +90,8 @@ struct minstrel_rate_stats {
+ /* total attempts/success counters */
+ u32 att_hist, succ_hist;
+
++ struct minstrel_avg_ctx avg;
++
+ /* prob_ewma - exponential weighted moving average of prob */
+ u16 prob_ewma;
+
+@@ -95,6 +147,7 @@ struct minstrel_sta_info {
+ struct minstrel_priv {
+ struct ieee80211_hw *hw;
+ bool has_mrr;
++ bool new_avg;
+ u32 sample_switch;
+ unsigned int cw_min;
+ unsigned int cw_max;
+@@ -126,7 +179,8 @@ extern const struct rate_control_ops mac
+ void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
+
+ /* Recalculate success probabilities and counters for a given rate using EWMA */
+-void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs);
++void minstrel_calc_rate_stats(struct minstrel_priv *mp,
++ struct minstrel_rate_stats *mrs);
+ int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma);
+
+ /* debugfs */
+--- a/net/mac80211/rc80211_minstrel_ht.c
++++ b/net/mac80211/rc80211_minstrel_ht.c
+@@ -737,7 +737,7 @@ minstrel_ht_update_stats(struct minstrel
+
+ mrs = &mg->rates[i];
+ mrs->retry_updated = false;
+- minstrel_calc_rate_stats(mrs);
++ minstrel_calc_rate_stats(mp, mrs);
+ cur_prob = mrs->prob_ewma;
+
+ if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0)
+@@ -773,6 +773,8 @@ minstrel_ht_update_stats(struct minstrel
+
+ /* try to sample all available rates during each interval */
+ mi->sample_count *= 8;
++ if (mp->new_avg)
++ mi->sample_count /= 2;
+
+ if (sample)
+ minstrel_ht_rate_sample_switch(mp, mi);
+@@ -889,6 +891,7 @@ minstrel_ht_tx_status(void *priv, struct
+ struct ieee80211_tx_rate *ar = info->status.rates;
+ struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL;
+ struct minstrel_priv *mp = priv;
++ u32 update_interval = mp->update_interval / 2;
+ bool last, update = false;
+ bool sample_status = false;
+ int i;
+@@ -943,6 +946,10 @@ minstrel_ht_tx_status(void *priv, struct
+
+ switch (mi->sample_mode) {
+ case MINSTREL_SAMPLE_IDLE:
++ if (mp->new_avg &&
++ (mp->hw->max_rates > 1 ||
++ mi->total_packets_cur < SAMPLE_SWITCH_THR))
++ update_interval /= 2;
+ break;
+
+ case MINSTREL_SAMPLE_ACTIVE:
+@@ -983,8 +990,7 @@ minstrel_ht_tx_status(void *priv, struct
+ }
+ }
+
+- if (time_after(jiffies, mi->last_stats_update +
+- mp->update_interval / 2)) {
++ if (time_after(jiffies, mi->last_stats_update + update_interval)) {
+ update = true;
+ minstrel_ht_update_stats(mp, mi, true);
+ }
+@@ -1665,6 +1671,7 @@ minstrel_ht_alloc(struct ieee80211_hw *h
+
+ mp->hw = hw;
+ mp->update_interval = HZ / 10;
++ mp->new_avg = true;
+
+ #ifdef CPTCFG_MAC80211_DEBUGFS
+ mp->fixed_rate_idx = (u32) -1;
+@@ -1672,6 +1679,8 @@ minstrel_ht_alloc(struct ieee80211_hw *h
+ &mp->fixed_rate_idx);
+ debugfs_create_u32("sample_switch", S_IRUGO | S_IWUSR, debugfsdir,
+ &mp->sample_switch);
++ debugfs_create_bool("new_avg", S_IRUGO | S_IWUSR, debugfsdir,
++ &mp->new_avg);
+ #endif
+
+ minstrel_ht_init_cck_rates(mp);