mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-04 06:54:27 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			236 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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);
 |