diff options
Diffstat (limited to 'include/net/red.h')
| -rw-r--r-- | include/net/red.h | 204 | 
1 files changed, 144 insertions, 60 deletions
diff --git a/include/net/red.h b/include/net/red.h index 995108e54d9..76e0b5f922c 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -2,9 +2,11 @@  #define __NET_SCHED_RED_H  #include <linux/types.h> +#include <linux/bug.h>  #include <net/pkt_sched.h>  #include <net/inet_ecn.h>  #include <net/dsfield.h> +#include <linux/reciprocal_div.h>  /*	Random Early Detection (RED) algorithm.  	======================================= @@ -87,6 +89,29 @@  	etc.   */ +/* + * Adaptative RED : An Algorithm for Increasing the Robustness of RED's AQM + * (Sally FLoyd, Ramakrishna Gummadi, and Scott Shenker) August 2001 + * + * Every 500 ms: + *  if (avg > target and max_p <= 0.5) + *   increase max_p : max_p += alpha; + *  else if (avg < target and max_p >= 0.01) + *   decrease max_p : max_p *= beta; + * + * target :[qth_min + 0.4*(qth_min - qth_max), + *          qth_min + 0.6*(qth_min - qth_max)]. + * alpha : min(0.01, max_p / 4) + * beta : 0.9 + * max_P is a Q0.32 fixed point number (with 32 bits mantissa) + * max_P between 0.01 and 0.5 (1% - 50%) [ Its no longer a negative power of two ] + */ +#define RED_ONE_PERCENT ((u32)DIV_ROUND_CLOSEST(1ULL<<32, 100)) + +#define MAX_P_MIN (1 * RED_ONE_PERCENT) +#define MAX_P_MAX (50 * RED_ONE_PERCENT) +#define MAX_P_ALPHA(val) min(MAX_P_MIN, val / 4) +  #define RED_STAB_SIZE	256  #define RED_STAB_MASK	(RED_STAB_SIZE - 1) @@ -97,87 +122,118 @@ struct red_stats {  	u32		forced_mark;	/* Forced marks, qavg > max_thresh */  	u32		pdrop;          /* Drops due to queue limits */  	u32		other;          /* Drops due to drop() calls */ -	u32		backlog;  };  struct red_parms {  	/* Parameters */ -	u32		qth_min;	/* Min avg length threshold: A scaled */ -	u32		qth_max;	/* Max avg length threshold: A scaled */ +	u32		qth_min;	/* Min avg length threshold: Wlog scaled */ +	u32		qth_max;	/* Max avg length threshold: Wlog scaled */  	u32		Scell_max; -	u32		Rmask;		/* Cached random mask, see red_rmask */ +	u32		max_P;		/* probability, [0 .. 1.0] 32 scaled */ +	/* reciprocal_value(max_P / qth_delta) */ +	struct reciprocal_value	max_P_reciprocal; +	u32		qth_delta;	/* max_th - min_th */ +	u32		target_min;	/* min_th + 0.4*(max_th - min_th) */ +	u32		target_max;	/* min_th + 0.6*(max_th - min_th) */  	u8		Scell_log;  	u8		Wlog;		/* log(W)		*/  	u8		Plog;		/* random number bits	*/  	u8		Stab[RED_STAB_SIZE]; +}; +struct red_vars {  	/* Variables */  	int		qcount;		/* Number of packets since last random  					   number generation */  	u32		qR;		/* Cached random number */ -	unsigned long	qavg;		/* Average queue length: A scaled */ -	psched_time_t	qidlestart;	/* Start of current idle period */ +	unsigned long	qavg;		/* Average queue length: Wlog scaled */ +	ktime_t		qidlestart;	/* Start of current idle period */  }; -static inline u32 red_rmask(u8 Plog) +static inline u32 red_maxp(u8 Plog)  { -	return Plog < 32 ? ((1 << Plog) - 1) : ~0UL; +	return Plog < 32 ? (~0U >> Plog) : ~0U;  } -static inline void red_set_parms(struct red_parms *p, -				 u32 qth_min, u32 qth_max, u8 Wlog, u8 Plog, -				 u8 Scell_log, u8 *stab) +static inline void red_set_vars(struct red_vars *v)  {  	/* Reset average queue length, the value is strictly bound  	 * to the parameters below, reseting hurts a bit but leaving  	 * it might result in an unreasonable qavg for a while. --TGR  	 */ -	p->qavg		= 0; +	v->qavg		= 0; + +	v->qcount	= -1; +} + +static inline void red_set_parms(struct red_parms *p, +				 u32 qth_min, u32 qth_max, u8 Wlog, u8 Plog, +				 u8 Scell_log, u8 *stab, u32 max_P) +{ +	int delta = qth_max - qth_min; +	u32 max_p_delta; -	p->qcount	= -1;  	p->qth_min	= qth_min << Wlog;  	p->qth_max	= qth_max << Wlog;  	p->Wlog		= Wlog;  	p->Plog		= Plog; -	p->Rmask	= red_rmask(Plog); +	if (delta < 0) +		delta = 1; +	p->qth_delta	= delta; +	if (!max_P) { +		max_P = red_maxp(Plog); +		max_P *= delta; /* max_P = (qth_max - qth_min)/2^Plog */ +	} +	p->max_P = max_P; +	max_p_delta = max_P / delta; +	max_p_delta = max(max_p_delta, 1U); +	p->max_P_reciprocal  = reciprocal_value(max_p_delta); + +	/* RED Adaptative target : +	 * [min_th + 0.4*(min_th - max_th), +	 *  min_th + 0.6*(min_th - max_th)]. +	 */ +	delta /= 5; +	p->target_min = qth_min + 2*delta; +	p->target_max = qth_min + 3*delta; +  	p->Scell_log	= Scell_log;  	p->Scell_max	= (255 << Scell_log); -	memcpy(p->Stab, stab, sizeof(p->Stab)); +	if (stab) +		memcpy(p->Stab, stab, sizeof(p->Stab));  } -static inline int red_is_idling(struct red_parms *p) +static inline int red_is_idling(const struct red_vars *v)  { -	return p->qidlestart != PSCHED_PASTPERFECT; +	return v->qidlestart.tv64 != 0;  } -static inline void red_start_of_idle_period(struct red_parms *p) +static inline void red_start_of_idle_period(struct red_vars *v)  { -	p->qidlestart = psched_get_time(); +	v->qidlestart = ktime_get();  } -static inline void red_end_of_idle_period(struct red_parms *p) +static inline void red_end_of_idle_period(struct red_vars *v)  { -	p->qidlestart = PSCHED_PASTPERFECT; +	v->qidlestart.tv64 = 0;  } -static inline void red_restart(struct red_parms *p) +static inline void red_restart(struct red_vars *v)  { -	red_end_of_idle_period(p); -	p->qavg = 0; -	p->qcount = -1; +	red_end_of_idle_period(v); +	v->qavg = 0; +	v->qcount = -1;  } -static inline unsigned long red_calc_qavg_from_idle_time(struct red_parms *p) +static inline unsigned long red_calc_qavg_from_idle_time(const struct red_parms *p, +							 const struct red_vars *v)  { -	psched_time_t now; -	long us_idle; +	s64 delta = ktime_us_delta(ktime_get(), v->qidlestart); +	long us_idle = min_t(s64, delta, p->Scell_max);  	int  shift; -	now = psched_get_time(); -	us_idle = psched_tdiff_bounded(now, p->qidlestart, p->Scell_max); -  	/*  	 * The problem: ideally, average length queue recalcultion should  	 * be done over constant clock intervals. This is too expensive, so @@ -190,7 +246,7 @@ static inline unsigned long red_calc_qavg_from_idle_time(struct red_parms *p)  	 *  	 * dummy packets as a burst after idle time, i.e.  	 * -	 * 	p->qavg *= (1-W)^m +	 * 	v->qavg *= (1-W)^m  	 *  	 * This is an apparently overcomplicated solution (f.e. we have to  	 * precompute a table to make this calculation in reasonable time) @@ -201,7 +257,7 @@ static inline unsigned long red_calc_qavg_from_idle_time(struct red_parms *p)  	shift = p->Stab[(us_idle >> p->Scell_log) & RED_STAB_MASK];  	if (shift) -		return p->qavg >> shift; +		return v->qavg >> shift;  	else {  		/* Approximate initial part of exponent with linear function:  		 * @@ -210,20 +266,21 @@ static inline unsigned long red_calc_qavg_from_idle_time(struct red_parms *p)  		 * Seems, it is the best solution to  		 * problem of too coarse exponent tabulation.  		 */ -		us_idle = (p->qavg * (u64)us_idle) >> p->Scell_log; +		us_idle = (v->qavg * (u64)us_idle) >> p->Scell_log; -		if (us_idle < (p->qavg >> 1)) -			return p->qavg - us_idle; +		if (us_idle < (v->qavg >> 1)) +			return v->qavg - us_idle;  		else -			return p->qavg >> 1; +			return v->qavg >> 1;  	}  } -static inline unsigned long red_calc_qavg_no_idle_time(struct red_parms *p, +static inline unsigned long red_calc_qavg_no_idle_time(const struct red_parms *p, +						       const struct red_vars *v,  						       unsigned int backlog)  {  	/* -	 * NOTE: p->qavg is fixed point number with point at Wlog. +	 * NOTE: v->qavg is fixed point number with point at Wlog.  	 * The formula below is equvalent to floating point  	 * version:  	 * @@ -231,42 +288,46 @@ static inline unsigned long red_calc_qavg_no_idle_time(struct red_parms *p,  	 *  	 * --ANK (980924)  	 */ -	return p->qavg + (backlog - (p->qavg >> p->Wlog)); +	return v->qavg + (backlog - (v->qavg >> p->Wlog));  } -static inline unsigned long red_calc_qavg(struct red_parms *p, +static inline unsigned long red_calc_qavg(const struct red_parms *p, +					  const struct red_vars *v,  					  unsigned int backlog)  { -	if (!red_is_idling(p)) -		return red_calc_qavg_no_idle_time(p, backlog); +	if (!red_is_idling(v)) +		return red_calc_qavg_no_idle_time(p, v, backlog);  	else -		return red_calc_qavg_from_idle_time(p); +		return red_calc_qavg_from_idle_time(p, v);  } -static inline u32 red_random(struct red_parms *p) + +static inline u32 red_random(const struct red_parms *p)  { -	return net_random() & p->Rmask; +	return reciprocal_divide(prandom_u32(), p->max_P_reciprocal);  } -static inline int red_mark_probability(struct red_parms *p, unsigned long qavg) +static inline int red_mark_probability(const struct red_parms *p, +				       const struct red_vars *v, +				       unsigned long qavg)  {  	/* The formula used below causes questions. -	   OK. qR is random number in the interval 0..Rmask +	   OK. qR is random number in the interval +		(0..1/max_P)*(qth_max-qth_min)  	   i.e. 0..(2^Plog). If we used floating point  	   arithmetics, it would be: (2^Plog)*rnd_num,  	   where rnd_num is less 1.  	   Taking into account, that qavg have fixed -	   point at Wlog, and Plog is related to max_P by -	   max_P = (qth_max-qth_min)/2^Plog; two lines +	   point at Wlog, two lines  	   below have the following floating point equivalent:  	   max_P*(qavg - qth_min)/(qth_max-qth_min) < rnd/qcount  	   Any questions? --ANK (980924)  	 */ -	return !(((qavg - p->qth_min) >> p->Wlog) * p->qcount < p->qR); +	return !(((qavg - p->qth_min) >> p->Wlog) * v->qcount < v->qR);  }  enum { @@ -275,7 +336,7 @@ enum {  	RED_ABOVE_MAX_TRESH,  }; -static inline int red_cmp_thresh(struct red_parms *p, unsigned long qavg) +static inline int red_cmp_thresh(const struct red_parms *p, unsigned long qavg)  {  	if (qavg < p->qth_min)  		return RED_BELOW_MIN_THRESH; @@ -291,27 +352,29 @@ enum {  	RED_HARD_MARK,  }; -static inline int red_action(struct red_parms *p, unsigned long qavg) +static inline int red_action(const struct red_parms *p, +			     struct red_vars *v, +			     unsigned long qavg)  {  	switch (red_cmp_thresh(p, qavg)) {  		case RED_BELOW_MIN_THRESH: -			p->qcount = -1; +			v->qcount = -1;  			return RED_DONT_MARK;  		case RED_BETWEEN_TRESH: -			if (++p->qcount) { -				if (red_mark_probability(p, qavg)) { -					p->qcount = 0; -					p->qR = red_random(p); +			if (++v->qcount) { +				if (red_mark_probability(p, v, qavg)) { +					v->qcount = 0; +					v->qR = red_random(p);  					return RED_PROB_MARK;  				}  			} else -				p->qR = red_random(p); +				v->qR = red_random(p);  			return RED_DONT_MARK;  		case RED_ABOVE_MAX_TRESH: -			p->qcount = -1; +			v->qcount = -1;  			return RED_HARD_MARK;  	} @@ -319,4 +382,25 @@ static inline int red_action(struct red_parms *p, unsigned long qavg)  	return RED_DONT_MARK;  } +static inline void red_adaptative_algo(struct red_parms *p, struct red_vars *v) +{ +	unsigned long qavg; +	u32 max_p_delta; + +	qavg = v->qavg; +	if (red_is_idling(v)) +		qavg = red_calc_qavg_from_idle_time(p, v); + +	/* v->qavg is fixed point number with point at Wlog */ +	qavg >>= p->Wlog; + +	if (qavg > p->target_max && p->max_P <= MAX_P_MAX) +		p->max_P += MAX_P_ALPHA(p->max_P); /* maxp = maxp + alpha */ +	else if (qavg < p->target_min && p->max_P >= MAX_P_MIN) +		p->max_P = (p->max_P/10)*9; /* maxp = maxp * Beta */ + +	max_p_delta = DIV_ROUND_CLOSEST(p->max_P, p->qth_delta); +	max_p_delta = max(max_p_delta, 1U); +	p->max_P_reciprocal = reciprocal_value(max_p_delta); +}  #endif  | 
