1 From: Felix Fietkau <nbd@nbd.name>
2 Date: Sat, 28 Sep 2019 15:46:06 +0200
3 Subject: [PATCH] mac80211: minstrel_ht: replace rate stats ewma with a
6 Rate success probability usually fluctuates a lot under normal conditions.
7 With a simple EWMA, noise and fluctuation can be reduced by increasing the
8 window length, but that comes at the cost of introducing lag on sudden
11 This change replaces the EWMA implementation with a moving average that's
12 designed to significantly reduce lag while keeping a bigger window size
13 by being better at filtering out noise.
15 It is only slightly more expensive than the simple EWMA and still avoids
16 divisions in its calculation.
18 The algorithm is adapted from an implementation intended for a completely
19 different field (stock market trading), where the tradeoff of lag vs
20 noise filtering is equally important.
22 The algorithm works in the same way as the "smoothing filter" from
23 http://www.stockspotter.com/files/PredictiveIndicators.pdf adapted for
24 fixed-point math with some constants, using only addition, bit shifts
27 To better make use of the filtering and bigger window size, the update
28 interval is cut in half.
30 For testing, the algorithm can be reverted to the older one via debugfs
32 Signed-off-by: Felix Fietkau <nbd@nbd.name>
35 --- a/net/mac80211/rc80211_minstrel.c
36 +++ b/net/mac80211/rc80211_minstrel.c
37 @@ -157,14 +157,18 @@ minstrel_update_rates(struct minstrel_pr
38 * Recalculate statistics and counters of a given rate
41 -minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs)
42 +minstrel_calc_rate_stats(struct minstrel_priv *mp,
43 + struct minstrel_rate_stats *mrs)
45 unsigned int cur_prob;
47 if (unlikely(mrs->attempts > 0)) {
48 mrs->sample_skipped = 0;
49 cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
50 - if (unlikely(!mrs->att_hist)) {
52 + mrs->prob_ewma = minstrel_filter_avg_add(&mrs->avg,
54 + } else if (unlikely(!mrs->att_hist)) {
55 mrs->prob_ewma = cur_prob;
57 /*update exponential weighted moving avarage */
58 @@ -200,7 +204,7 @@ minstrel_update_stats(struct minstrel_pr
59 struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats;
61 /* Update statistics of success probability per rate */
62 - minstrel_calc_rate_stats(mrs);
63 + minstrel_calc_rate_stats(mp, mrs);
65 /* Sample less often below the 10% chance of success.
66 * Sample less often above the 95% chance of success. */
67 @@ -289,7 +293,8 @@ minstrel_tx_status(void *priv, struct ie
68 if (mi->sample_deferred > 0)
69 mi->sample_deferred--;
71 - if (time_after(jiffies, mi->last_stats_update + mp->update_interval))
72 + if (time_after(jiffies, mi->last_stats_update +
73 + mp->update_interval / (mp->new_avg ? 2 : 1)))
74 minstrel_update_stats(mp, mi);
77 --- a/net/mac80211/rc80211_minstrel.h
78 +++ b/net/mac80211/rc80211_minstrel.h
80 #define MAX_THR_RATES 4
83 + * Coefficients for moving average with noise filter (period=16),
86 + * a1 = exp(-pi * sqrt(2) / period)
87 + * coeff2 = 2 * a1 * cos(sqrt(2) * 2 * pi / period)
89 + * coeff1 = 1 - coeff2 - coeff3
91 +#define MINSTREL_AVG_COEFF1 (MINSTREL_FRAC(1, 1) - \
92 + MINSTREL_AVG_COEFF2 - \
93 + MINSTREL_AVG_COEFF3)
94 +#define MINSTREL_AVG_COEFF2 0x00001499
95 +#define MINSTREL_AVG_COEFF3 -0x0000092e
98 * Perform EWMA (Exponentially Weighted Moving Average) calculation
101 @@ -32,6 +47,41 @@ minstrel_ewma(int old, int new, int weig
105 +struct minstrel_avg_ctx {
109 +static inline int minstrel_filter_avg_add(struct minstrel_avg_ctx *ctx, s32 in)
111 + s32 out_1 = ctx->prev[0];
112 + s32 out_2 = ctx->prev[1];
123 + val = MINSTREL_AVG_COEFF1 * in;
124 + val += MINSTREL_AVG_COEFF2 * out_1;
125 + val += MINSTREL_AVG_COEFF3 * out_2;
126 + val >>= MINSTREL_SCALE;
128 + if (val > 1 << MINSTREL_SCALE)
129 + val = 1 << MINSTREL_SCALE;
134 + ctx->prev[1] = out_1;
135 + ctx->prev[0] = val;
140 struct minstrel_rate_stats {
141 /* current / last sampling period attempts/success counters */
142 u16 attempts, last_attempts;
143 @@ -40,6 +90,8 @@ struct minstrel_rate_stats {
144 /* total attempts/success counters */
145 u32 att_hist, succ_hist;
147 + struct minstrel_avg_ctx avg;
149 /* prob_ewma - exponential weighted moving average of prob */
152 @@ -95,6 +147,7 @@ struct minstrel_sta_info {
153 struct minstrel_priv {
154 struct ieee80211_hw *hw;
160 @@ -126,7 +179,8 @@ extern const struct rate_control_ops mac
161 void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
163 /* Recalculate success probabilities and counters for a given rate using EWMA */
164 -void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs);
165 +void minstrel_calc_rate_stats(struct minstrel_priv *mp,
166 + struct minstrel_rate_stats *mrs);
167 int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma);
170 --- a/net/mac80211/rc80211_minstrel_ht.c
171 +++ b/net/mac80211/rc80211_minstrel_ht.c
172 @@ -737,7 +737,7 @@ minstrel_ht_update_stats(struct minstrel
175 mrs->retry_updated = false;
176 - minstrel_calc_rate_stats(mrs);
177 + minstrel_calc_rate_stats(mp, mrs);
178 cur_prob = mrs->prob_ewma;
180 if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0)
181 @@ -773,6 +773,8 @@ minstrel_ht_update_stats(struct minstrel
183 /* try to sample all available rates during each interval */
184 mi->sample_count *= 8;
186 + mi->sample_count /= 2;
189 minstrel_ht_rate_sample_switch(mp, mi);
190 @@ -889,6 +891,7 @@ minstrel_ht_tx_status(void *priv, struct
191 struct ieee80211_tx_rate *ar = info->status.rates;
192 struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL;
193 struct minstrel_priv *mp = priv;
194 + u32 update_interval = mp->update_interval / 2;
195 bool last, update = false;
196 bool sample_status = false;
198 @@ -943,6 +946,10 @@ minstrel_ht_tx_status(void *priv, struct
200 switch (mi->sample_mode) {
201 case MINSTREL_SAMPLE_IDLE:
203 + (mp->hw->max_rates > 1 ||
204 + mi->total_packets_cur < SAMPLE_SWITCH_THR))
205 + update_interval /= 2;
208 case MINSTREL_SAMPLE_ACTIVE:
209 @@ -983,8 +990,7 @@ minstrel_ht_tx_status(void *priv, struct
213 - if (time_after(jiffies, mi->last_stats_update +
214 - mp->update_interval / 2)) {
215 + if (time_after(jiffies, mi->last_stats_update + update_interval)) {
217 minstrel_ht_update_stats(mp, mi, true);
219 @@ -1665,6 +1671,7 @@ minstrel_ht_alloc(struct ieee80211_hw *h
222 mp->update_interval = HZ / 10;
223 + mp->new_avg = true;
225 minstrel_ht_init_cck_rates(mp);
227 @@ -1682,6 +1689,8 @@ static void minstrel_ht_add_debugfs(stru
228 &mp->fixed_rate_idx);
229 debugfs_create_u32("sample_switch", S_IRUGO | S_IWUSR, debugfsdir,
231 + debugfs_create_bool("new_avg", S_IRUGO | S_IWUSR, debugfsdir,