ARED在RED的基础上实现了最大报文标记概率max_P的自动调整,以便将平均队列长度控制在目标区间内。
1 ARED初始化
在RED初始化函数red_init中,设置ARED定时器adapt_timer用于定期执行ARED算法,定时处理函数为red_adaptative_timer。
static int red_init(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack)
{
struct red_sched_data *q = qdisc_priv(sch);
q->qdisc = &noop_qdisc;
q->sch = sch;
timer_setup(&q->adapt_timer, red_adaptative_timer, 0);
return red_change(sch, opt, extack);
}
通过TC命令设置RED队列时,如果指定了adaptive选项,将会向内核下发TC_RED_ADAPTATIVE标志,内核将启动adapt_timer定时器,时长为HZ/2,即500毫秒。
static int red_change(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
struct tc_red_qopt *ctl;
...
ctl = nla_data(tb[TCA_RED_PARMS]);
if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog))
return -EINVAL;
...
red_set_parms(&q->parms,
ctl->qth_min, ctl->qth_max, ctl->Wlog,
ctl->Plog, ctl->Scell_log,
nla_data(tb[TCA_RED_STAB]),
max_P);
red_set_vars(&q->vars);
del_timer(&q->adapt_timer);
if (ctl->flags & TC_RED_ADAPTATIVE)
mod_timer(&q->adapt_timer, jiffies + HZ/2);
函数red_set_params设置了ARED的参数,如下所示,ARED的最低目标avg(target_min)设置为: min_th + 0.4*(min_th - max_th),最高目标avg(target_max)设置为: min_th + 0.6*(min_th - max_th),即ARED尝试将平均队列长度avg控制在min_th和max_th的40%到60%的区间内。
注意,这里target_min和target_max都是原值,不同于p->qth_min和p->qth_max,后者使用Wlog进行了左移位。
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;
p->qth_min = qth_min << Wlog;
p->qth_max = qth_max << Wlog;
if (delta <= 0)
delta = 1;
p->qth_delta = 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;
2 Adaptive-RED处理
如下为ARED的定时处理函数,其主要是对函数red_adaptative_algo的封装。
static inline void red_adaptative_timer(struct timer_list *t)
{
struct red_sched_data *q = from_timer(q, t, adapt_timer);
struct Qdisc *sch = q->sch;
spinlock_t *root_lock = qdisc_lock(qdisc_root_sleeping(sch));
spin_lock(root_lock);
red_adaptative_algo(&q->parms, &q->vars);
mod_timer(&q->adapt_timer, jiffies + HZ/2);
spin_unlock(root_lock);
以下为ARED算法的实现,详情可参见: Adaptive-RED队列。首先,如果队列当前处于空闲状态,不能直接使用qavg,需要重新计算一下平均队列长度qavg。其次,由于qavg为经过Wlog左移位之后的值,这里进行还原。
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;
如果当前qavg大于目标最大值target_max,表明qavg过大,需要max_P增加Alpha。但是,当max_P已经大于MAX_P_MAX时,不再增加max_P。
if (qavg > p->target_max && p->max_P <= MAX_P_MAX)
p->max_P += MAX_P_ALPHA(p->max_P); /* maxp = maxp + alpha */
否则,如果qavg小于target_min,表明qavg过小,需要将max_P减小,乘以系数Beta。但是,当max_P已经小于MAX_P_MIN时,不再减小max_P的值。在max_P调整完毕之后,更新max_P_reciprocal的值。
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);
}
根据ARED算法定义,maxp的最大值为0.5,而maxp的最小值为0.01,但是由于内核中p->max_P变量的值等于:probability * pow(2, 32),增大了2^32 倍,所以,宏MAX_P_MAX和MAX_P_MIN的定义也进行了2^32 倍的放大。
#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)
另外,按照ARED算法,beta的值为0.9,在以上算法实现函数red_adaptative_algo中,通过(p->max_P/10)*9实现。alpha的值定义为:min(0.01, max_p / 4)两者之间的最小值,其由宏定义MAX_P_ALPHA实现:
#define MAX_P_ALPHA(val) min(MAX_P_MIN, val / 4)
内核版本 5.0