Linux内核Adaptive-RED实现

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

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页