`

Linux内核中流量控制(8)

阅读更多
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
5.8 GRED(Generic Random Early Detection queue)

GRED算法是GRED的通用化,还是以RED算法为基础,但不再是根据一个RED流控节点计算丢包情况,而
是可以定义多个虚拟RED流控节点,然后根据skb数据包中的tc_index参数将数据分配到不同的节点,
每个节点都按RED算法进行流控。之所以叫虚拟队列,因为实际的队列数据结构还是只有一个队列,
但每个skb包的入队出队是由不同的RED流控结构控制的。
GRED和RED的关系就类似PRIO和pfifo_fast的关系,但队列只是一个。

5.8.1 GRED操作结构定义

// GRED算法参数
struct gred_sched_data
{
// 流量限制值
 u32  limit;  /* HARD maximal queue length */
 u32       DP;  /* the drop pramaters */
// 该虚拟队列看到的字节数和包数
 u32  bytesin; /* bytes seen on virtualQ so far*/
 u32  packetsin; /* packets seen on virtualQ so far*/
// 队列中正在等待的数据长度
 u32  backlog; /* bytes on the virtualQ */
// 该虚拟队列优先级
 u8  prio;  /* the prio of this vq */
// RED算法参数和统计结构
 struct red_parms parms;
 struct red_stats stats;
};

// WRED模式或RIO模式
enum {
// WRED模式是处理有相同的PRIO的不同虚拟队列的情况
 GRED_WGRED_MODE = 1,
// RIO应该就是PRIO吧, 队列优先级有效
 GRED_RIO_MODE,
};

// GRED私有数据结构
struct gred_sched
{
// 最大MAX_DPs(16)个GRED参数项, 每个相当于一个RED虚拟队列
 struct gred_sched_data *tab[MAX_DPs];
 unsigned long flags;    // 包括WRED和RIO标志
 u32  red_flags;  // RED算法标志, 包括ECN和HARDDROP
 u32   DPs;     // DP的数量, 有效的tab的数量, 小于16
 u32   def;     // 缺省DP
 struct red_parms wred_set; // RED算法总体参数
};
 
// GRED流控操作结构
static struct Qdisc_ops gred_qdisc_ops = {
 .id  = "gred",
 .priv_size = sizeof(struct gred_sched),
 .enqueue = gred_enqueue,
 .dequeue = gred_dequeue,
 .requeue = gred_requeue,
 .drop  = gred_drop,
 .init  = gred_init,
 .reset  = gred_reset,
 .destroy = gred_destroy,
 .change  = gred_change,
 .dump  = gred_dump,
 .owner  = THIS_MODULE,
};

GRED没有定义类别操作结构

5.8.1 GRED一些操作函数

// 检查是否设置WRED模式, 检测WRED位是否设置
static inline int gred_wred_mode(struct gred_sched *table)
{
 return test_bit(GRED_WRED_MODE, &table->flags);
}

// 打开WRED模式
static inline void gred_enable_wred_mode(struct gred_sched *table)
{
 __set_bit(GRED_WRED_MODE, &table->flags);
}

// 关闭WRED模式
static inline void gred_disable_wred_mode(struct gred_sched *table)
{
 __clear_bit(GRED_WRED_MODE, &table->flags);
}

// 检测是否设置RIO模式, 检测RIO位是否设置
static inline int gred_rio_mode(struct gred_sched *table)
{
 return test_bit(GRED_RIO_MODE, &table->flags);
}

// 打开RIO模式
static inline void gred_enable_rio_mode(struct gred_sched *table)
{
 __set_bit(GRED_RIO_MODE, &table->flags);
}

// 关闭RIO模式
static inline void gred_disable_rio_mode(struct gred_sched *table)
{
 __clear_bit(GRED_RIO_MODE, &table->flags);
}

// WRED模式检查
static inline int gred_wred_mode_check(struct Qdisc *sch)
{
// GRED私有数据
 struct gred_sched *table = qdisc_priv(sch);
 int i;
// 两层循环比较不同的表项的prio值是否相同
 /* Really ugly O(n^2) but shouldn't be necessary too frequent. */
 for (i = 0; i < table->DPs; i++) {
  struct gred_sched_data *q = table->tab[i];
  int n;
  if (q == NULL)
   continue;
  for (n = 0; n < table->DPs; n++)
   if (table->tab[n] && table->tab[n] != q &&
       table->tab[n]->prio == q->prio)
// 有prio相同的不同表项返回1
    return 1;
 }
// prio值都不同返回0
 return 0;
}

// GRED等待队列值
static inline unsigned int gred_backlog(struct gred_sched *table,
     struct gred_sched_data *q,
     struct Qdisc *sch)
{
// WRED模式下使用qdisc的统计值
 if (gred_wred_mode(table))
  return sch->qstats.backlog;
 else
// 否则返回GRED的backlog
  return q->backlog;
}

// 将skb包的tc_index转换为DP索引值
static inline u16 tc_index_to_dp(struct sk_buff *skb)
{
// 直接和GRED_VQ_MASK(MAX_DPs - 1)相与, 不知道为什么不用%, 这样就不必限制
// MAX_DPs是2的整数幂
 return skb->tc_index & GRED_VQ_MASK;
}

// 加载RED参数, 从gred_sched到gred_sched_data
static inline void gred_load_wred_set(struct gred_sched *table,
          struct gred_sched_data *q)
{
// 平均队列值
 q->parms.qavg = table->wred_set.qavg;
// 休眠起始时间
 q->parms.qidlestart = table->wred_set.qidlestart;
}

// 恢复RED参数, 从gred_sched_data到gred_sched
static inline void gred_store_wred_set(struct gred_sched *table,
           struct gred_sched_data *q)
{
 table->wred_set.qavg = q->parms.qavg;
}

// 检查RED算法是否使用ECN, 检查TC_RED_ECN位
static inline int gred_use_ecn(struct gred_sched *t)
{
 return t->red_flags & TC_RED_ECN;
}

// 检查RED算法是否使用HARDDROP, 检查TC_RED_HARDDROP位
static inline int gred_use_harddrop(struct gred_sched *t)
{
 return t->red_flags & TC_RED_HARDDROP;
}

5.8.3 初始化

static int gred_init(struct Qdisc *sch, struct rtattr *opt)
{
 struct rtattr *tb[TCA_GRED_MAX];
// 输入参数检查并解析
 if (opt == NULL || rtattr_parse_nested(tb, TCA_GRED_MAX, opt))
  return -EINVAL;
// 不能有TCA_GRED_PARAMS和TCA_GRED_STAB类型数据
 if (tb[TCA_GRED_PARMS-1] || tb[TCA_GRED_STAB-1])
  return -EINVAL;
// 参数修改, 针对TCA_GRED_DPS类型数据
 return gred_change_table_def(sch, tb[TCA_GRED_DPS-1]);
}

static inline int gred_change_table_def(struct Qdisc *sch, struct rtattr *dps)
{
// GRED私有数据,
 struct gred_sched *table = qdisc_priv(sch);
// 这里是sopt, 针对配置的
 struct tc_gred_sopt *sopt;
 int i;
// 数据合法性检查
 if (dps == NULL || RTA_PAYLOAD(dps) < sizeof(*sopt))
  return -EINVAL;

// TC输入的GRED设置(setup)相关选项参数
 sopt = RTA_DATA(dps);

// DPs参数检查, DPs范围为(0, MAX_DPs), 不过为什么要32位数呢, 8位就够了
 if (sopt->DPs > MAX_DPs || sopt->DPs == 0 || sopt->def_DP >= sopt->DPs)
  return -EINVAL;
 sch_tree_lock(sch);
// GRED基本参数设置
 table->DPs = sopt->DPs;
 table->def = sopt->def_DP;
 table->red_flags = sopt->flags;
 /*
  * Every entry point to GRED is synchronized with the above code
  * and the DP is checked against DPs, i.e. shadowed VQs can no
  * longer be found so we can unlock right here.
  */
 sch_tree_unlock(sch);
 if (sopt->grio) {
// 打开RIO模式, 关闭WRED模式
  gred_enable_rio_mode(table);
  gred_disable_wred_mode(table);
// 不同的tab表项的prio参数相同时也打开WRED模式, 这时两个标志都被设置了
  if (gred_wred_mode_check(sch))
   gred_enable_wred_mode(table);
 } else {
// 关闭RIO和WRED模式
  gred_disable_rio_mode(table);
  gred_disable_wred_mode(table);
 }
// 释放多余的GRED结构表项
 for (i = table->DPs; i < MAX_DPs; i++) {
  if (table->tab[i]) {
   printk(KERN_WARNING "GRED: Warning: Destroying "
          "shadowed VQ 0x%x\n", i);
   gred_destroy_vq(table->tab[i]);
   table->tab[i] = NULL;
    }
 }
// 但注意的是没有对 0~DPs-1 表项进行初始化
 return 0;
}

// 释放虚拟队列项
static inline void gred_destroy_vq(struct gred_sched_data *q)
{
// 直接释放空间
 kfree(q);
}

5.8.4 参数修改

// 一次调用只修改一个DP表项
static int gred_change(struct Qdisc *sch, struct rtattr *opt)
{
// GRED私有数据
 struct gred_sched *table = qdisc_priv(sch);
// 注意这里的选项是qopt, 不再是sopt, 是针对队列的
 struct tc_gred_qopt *ctl;
 struct rtattr *tb[TCA_GRED_MAX];
// PRIO初始化为缺省值
 int err = -EINVAL, prio = GRED_DEF_PRIO;
 u8 *stab;
// 参数检查
 if (opt == NULL || rtattr_parse_nested(tb, TCA_GRED_MAX, opt))
  return -EINVAL;
// 没有TCA_GRED_STAB和TCA_GRED_PARMS类型数据是就只是设置sopt
 if (tb[TCA_GRED_PARMS-1] == NULL && tb[TCA_GRED_STAB-1] == NULL)
  return gred_change_table_def(sch, opt);
// 检查参数合法性
 if (tb[TCA_GRED_PARMS-1] == NULL ||
     RTA_PAYLOAD(tb[TCA_GRED_PARMS-1]) < sizeof(*ctl) ||
     tb[TCA_GRED_STAB-1] == NULL ||
     RTA_PAYLOAD(tb[TCA_GRED_STAB-1]) < 256)
  return -EINVAL;
// 控制数据
 ctl = RTA_DATA(tb[TCA_GRED_PARMS-1]);
// STAB数据
 stab = RTA_DATA(tb[TCA_GRED_STAB-1]);
// 参数中的DP数不能超过当前有效DP数
 if (ctl->DP >= table->DPs)
  goto errout;
// 如果是RIO模式, 更新prio参数
 if (gred_rio_mode(table)) {
// 如果没提供prio参数
  if (ctl->prio == 0) {
// 先使用缺省缺省prio
   int def_prio = GRED_DEF_PRIO;
// 如果有缺省GRED表项, 使用其prio
   if (table->tab[table->def])
    def_prio = table->tab[table->def]->prio;
   printk(KERN_DEBUG "GRED: DP %u does not have a prio "
          "setting default to %d\n", ctl->DP, def_prio);
   prio = def_prio;
  } else
// 提供了prio的话就用此prio参数
   prio = ctl->prio;
 }
 sch_tree_lock(sch);
// 更新DP位置表项的RED参数
 err = gred_change_vq(sch, ctl->DP, ctl, prio, stab);
 if (err < 0)
  goto errout_locked;
// 如果是RIO模式, 关闭WRED模式
 if (gred_rio_mode(table)) {
  gred_disable_wred_mode(table);
// 不同的tab表项的prio参数相同时再打开WRED模式
  if (gred_wred_mode_check(sch))
   gred_enable_wred_mode(table);
 }
 err = 0;
errout_locked:
 sch_tree_unlock(sch);
errout:
 return err;
}

// 虚拟队列修改, 修改DP表项参数
static inline int gred_change_vq(struct Qdisc *sch, int dp,
     struct tc_gred_qopt *ctl, int prio, u8 *stab)
{
// GRED私有数据, DP表
 struct gred_sched *table = qdisc_priv(sch);
 struct gred_sched_data *q;
// 如果dp位置表项为空,先分配空间
 if (table->tab[dp] == NULL) {
  table->tab[dp] = kzalloc(sizeof(*q), GFP_KERNEL);
  if (table->tab[dp] == NULL)
   return -ENOMEM;
 }
// 参数赋值
 q = table->tab[dp];
 q->DP = dp;
 q->prio = prio;
 q->limit = ctl->limit;
// 如果当前队列为空, 结束休眠
 if (q->backlog == 0)
  red_end_of_idle_period(&q->parms);
// 设置RED算法的基本参数
 red_set_parms(&q->parms,
        ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Plog,
        ctl->Scell_log, stab);
 return 0;
}
 
5.8.5 入队

static int gred_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
// 用来指向tab表项, 即虚拟队列
 struct gred_sched_data *q=NULL;
// GRED私有数据
 struct gred_sched *t= qdisc_priv(sch);
 unsigned long qavg = 0;
// 根据skb的tc_index参数获取dp
 u16 dp = tc_index_to_dp(skb);
// 如果dp超过有效表项数或该表项指针为空
 if (dp >= t->DPs  || (q = t->tab[dp]) == NULL) {
// 使用缺省dp流控节点
  dp = t->def;
// 如果该缺省dp处的表项为空, 即没有缺省RED流控节点
  if ((q = t->tab[dp]) == NULL) {
   /* Pass through packets not assigned to a DP
    * if no default DP has been configured. This
    * allows for DP flows to be left untouched.
    */
// 如果队列没满
   if (skb_queue_len(&sch->q) < sch->dev->tx_queue_len)
// 直接添加到流控节点队列末尾,不用进行RED流控
    return qdisc_enqueue_tail(skb, sch);
   else
// 否则丢弃
    goto drop;
  }
  /* fix tc_index? --could be controvesial but needed for
     requeueing */
// 表项非空, 更新数据包的tc_index参数, 准备进行RED流控计算
  skb->tc_index = (skb->tc_index & ~GRED_VQ_MASK) | dp;
 }
 /* sum up all the qaves of prios <= to ours to get the new qave */
// PRIO模式而且非WRED模式下, 将所有PRIO值小于当前表项的PRIO的所有RED节点
// 的平均队列值累加作为平均队列长度
 if (!gred_wred_mode(t) && gred_rio_mode(t)) {
  int i;
// 遍历所有有效表项
  for (i = 0; i < t->DPs; i++) {
// 如果表项的prio小于当前表项的prio而且是非休眠状态
   if (t->tab[i] && t->tab[i]->prio < q->prio &&
       !red_is_idling(&t->tab[i]->parms))
// 累加平均队列长度
    qavg +=t->tab[i]->parms.qavg;
  }
 }
// 更新统计数据
 q->packetsin++;
 q->bytesin += skb->len;
// 如果是WRED模式, t赋值到q, 使用总体的RED参数
 if (gred_wred_mode(t))
  gred_load_wred_set(t, q);
// 计算队列平均值
 q->parms.qavg = red_calc_qavg(&q->parms, gred_backlog(t, q, sch));
// 如果在休眠, 停止休眠, 因为有数据了
 if (red_is_idling(&q->parms))
  red_end_of_idle_period(&q->parms);
// 如果是WRED模式, t赋值到q, 使用总体的RED参数
 if (gred_wred_mode(t))
  gred_store_wred_set(t, q);
// 根据平均队列长度计算RED算法动作结果
 switch (red_action(&q->parms, q->parms.qavg + qavg)) {
// 允许
  case RED_DONT_MARK:
   break;
  case RED_PROB_MARK:
// 概率标记
   sch->qstats.overlimits++;
// 如果没用ECN拥塞标志, 丢包
   if (!gred_use_ecn(t) || !INET_ECN_set_ce(skb)) {
    q->stats.prob_drop++;
    goto congestion_drop;
   }
// 允许入队
   q->stats.prob_mark++;
   break;
  case RED_HARD_MARK:
// 必须标记
   sch->qstats.overlimits++;
// 如果GRED设置HARDDROP标志或没使用ECN, 丢包
   if (gred_use_harddrop(t) || !gred_use_ecn(t) ||
       !INET_ECN_set_ce(skb)) {
    q->stats.forced_drop++;
    goto congestion_drop;
   }
// 允许入队
   q->stats.forced_mark++;
   break;
 }
// 如果当前虚拟队列中的数据长度不超过限制, 添加到数据包队列末尾
 if (q->backlog + skb->len <= q->limit) {
  q->backlog += skb->len;
  return qdisc_enqueue_tail(skb, sch);
 }
// 否则丢包
 q->stats.pdrop++;
drop:
 return qdisc_drop(skb, sch);
congestion_drop:
// 拥塞情况也丢包
 qdisc_drop(skb, sch);
 return NET_XMIT_CN;
}

5.8.6 重入队

static int gred_requeue(struct sk_buff *skb, struct Qdisc* sch)
{
// GRED私有数据
 struct gred_sched *t = qdisc_priv(sch);
 struct gred_sched_data *q;
// 根据skb的tc_index参数获取dp
 u16 dp = tc_index_to_dp(skb);
// 如果dp超过有效表项数或者对应位置的表项为空, 打印警告信息
 if (dp >= t->DPs || (q = t->tab[dp]) == NULL) {
  if (net_ratelimit())
   printk(KERN_WARNING "GRED: Unable to relocate VQ 0x%x "
          "for requeue, screwing up backlog.\n",
          tc_index_to_dp(skb));
 } else {
// 否则停止休眠, 数据量增加
  if (red_is_idling(&q->parms))
   red_end_of_idle_period(&q->parms);
  q->backlog += skb->len;
 }
// 进行标准的重入队操作, 直接添加到数据队列尾, 因为GRED实际只有一个数据队列
 return qdisc_requeue(skb, sch);
}
 
5.8.7 出队

static struct sk_buff *gred_dequeue(struct Qdisc* sch)
{
 struct sk_buff *skb;
// GRED私有数据
 struct gred_sched *t = qdisc_priv(sch);
// 从数据队列中取一个数据包
 skb = qdisc_dequeue_head(sch);
 if (skb) {
  struct gred_sched_data *q;
// 根据skb的tc_index参数获取dp表项索引的虚拟队列
  u16 dp = tc_index_to_dp(skb);
// 如果dp超过有效表项数或者对应位置的表项为空, 打印警告信息
  if (dp >= t->DPs || (q = t->tab[dp]) == NULL) {
   if (net_ratelimit())
    printk(KERN_WARNING "GRED: Unable to relocate "
           "VQ 0x%x after dequeue, screwing up "
           "backlog.\n", tc_index_to_dp(skb));
  } else {
// 更新该虚拟队列RED参数
// 减少队列数据量
   q->backlog -= skb->len;
// 如果没数据而且非WRED模式, 进行休眠
   if (!q->backlog && !gred_wred_mode(t))
    red_start_of_idle_period(&q->parms);
  }
// 返回数据包
  return skb;
 }
// 数据队列没数据包了, WRED模式下进入休眠
 if (gred_wred_mode(t) && !red_is_idling(&t->wred_set))
  red_start_of_idle_period(&t->wred_set);
 return NULL;
}
 
5.8.8 丢包

static unsigned int gred_drop(struct Qdisc* sch)
{
 struct sk_buff *skb;
// GRED私有数据
 struct gred_sched *t = qdisc_priv(sch);
// 从数据队列中取一个数据包
 skb = qdisc_dequeue_tail(sch);
 if (skb) {
// 取到数据包
  unsigned int len = skb->len;
  struct gred_sched_data *q;
// 根据skb包的tc_index转换为DP索引值
  u16 dp = tc_index_to_dp(skb);
// 如果DP值非法或该表项为空, 打印警告信息
  if (dp >= t->DPs || (q = t->tab[dp]) == NULL) {
   if (net_ratelimit())
    printk(KERN_WARNING "GRED: Unable to relocate "
           "VQ 0x%x while dropping, screwing up "
           "backlog.\n", tc_index_to_dp(skb));
  } else {
// 该虚拟队列数据更新
// 统计值更新
   q->backlog -= len;
   q->stats.other++;
// 如果等待队列已经空了, 在WRED模式下启动休眠
   if (!q->backlog && !gred_wred_mode(t))
    red_start_of_idle_period(&q->parms);
  }
// 丢弃数据包
  qdisc_drop(skb, sch);
  return len;
 }
// 队列空, 在WRED模式下启动休眠
 if (gred_wred_mode(t) && !red_is_idling(&t->wred_set))
  red_start_of_idle_period(&t->wred_set);
 return 0;
}
 

5.8.9 复位

static void gred_reset(struct Qdisc* sch)
{
 int i;
 struct gred_sched *t = qdisc_priv(sch);
// 标准队列复位
 qdisc_reset_queue(sch);
// 遍历所有DP
        for (i = 0; i < t->DPs; i++) {
  struct gred_sched_data *q = t->tab[i];
  if (!q)
   continue;
// 重新启动每个子RED结构
  red_restart(&q->parms);
// 虚拟队列等待队列计数清零
  q->backlog = 0;
 }
}
 
5.8.10 释放

static void gred_destroy(struct Qdisc *sch)
{
 struct gred_sched *table = qdisc_priv(sch);
 int i;
// 释放所有DP表项
// 最好重新赋值为NULL
 for (i = 0; i < table->DPs; i++) {
  if (table->tab[i])
   gred_destroy_vq(table->tab[i]);
 }
}
 
5.8.11 输出参数

static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
{
 struct gred_sched *table = qdisc_priv(sch);
 struct rtattr *parms, *opts = NULL;
 int i;
// 向TC输出的GRED算法参数选项结构
 struct tc_gred_sopt sopt = {
  .DPs = table->DPs,
  .def_DP = table->def,
  .grio = gred_rio_mode(table),
  .flags = table->red_flags,
 };
// 将sopt参数填到数据包
 opts = RTA_NEST(skb, TCA_OPTIONS);
 RTA_PUT(skb, TCA_GRED_DPS, sizeof(sopt), &sopt);
 parms = RTA_NEST(skb, TCA_GRED_PARMS);
// 遍历DP表项
 for (i = 0; i < MAX_DPs; i++) {
  struct gred_sched_data *q = table->tab[i];
// 准备填写GRED的qopt
  struct tc_gred_qopt opt;
  memset(&opt, 0, sizeof(opt));
  if (!q) {
   /* hack -- fix at some point with proper message
      This is how we indicate to tc that there is no VQ
      at this DP */
// 对于空表项, DP值设置为超过MAX_DPs的值, 其他参数都为0
   opt.DP = MAX_DPs + i;
   goto append_opt;
  }
// 填写qopt参数
  opt.limit = q->limit;
  opt.DP  = q->DP;
  opt.backlog = q->backlog;
  opt.prio = q->prio;
  opt.qth_min = q->parms.qth_min >> q->parms.Wlog;
  opt.qth_max = q->parms.qth_max >> q->parms.Wlog;
  opt.Wlog = q->parms.Wlog;
  opt.Plog = q->parms.Plog;
  opt.Scell_log = q->parms.Scell_log;
  opt.other = q->stats.other;
  opt.early = q->stats.prob_drop;
  opt.forced = q->stats.forced_drop;
  opt.pdrop = q->stats.pdrop;
  opt.packets = q->packetsin;
  opt.bytesin = q->bytesin;
  if (gred_wred_mode(table)) {
   q->parms.qidlestart =
    table->tab[table->def]->parms.qidlestart;
   q->parms.qavg = table->tab[table->def]->parms.qavg;
  }
// 计算平均队列
  opt.qave = red_calc_qavg(&q->parms, q->parms.qavg);
append_opt:
// 将qopt参数填到数据包中
  RTA_APPEND(skb, sizeof(opt), &opt);
 }
 RTA_NEST_END(skb, parms);
// 数据包返回, 包括sopt和MAX_DPs个qopt
 return RTA_NEST_END(skb, opts);
rtattr_failure:
 return RTA_NEST_CANCEL(skb, opts);
}
 

5.8.13 GRED类别操作

// 输出分类
static int red_dump_class(struct Qdisc *sch, unsigned long cl,
     struct sk_buff *skb, struct tcmsg *tcm)
{
 struct red_sched_data *q = qdisc_priv(sch);
 if (cl != 1)
  return -ENOENT;
// 设置tcm参数:
// handle或1
 tcm->tcm_handle |= TC_H_MIN(1);
// 信息为内部流控handle
 tcm->tcm_info = q->qdisc->handle;
 return 0;
}

// 嫁接, 增加叶子qdisc
static int red_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
       struct Qdisc **old)
{
// GRED私有数据
 struct red_sched_data *q = qdisc_priv(sch);
// 如果没定义新流控, 用noop_qdisc
 if (new == NULL)
  new = &noop_qdisc;
 sch_tree_lock(sch);
// 将当前GRED内部流控和新流控结构指针对换
 *old = xchg(&q->qdisc, new);
// 复位老流控结构
 qdisc_reset(*old);
// 流控队列长度清零
 sch->q.qlen = 0;
 sch_tree_unlock(sch);
 return 0;
}

// 获取叶子流控节点
static struct Qdisc *red_leaf(struct Qdisc *sch, unsigned long arg)
{
 struct red_sched_data *q = qdisc_priv(sch);
// 返回GRED内部流控: bfifo
 return q->qdisc;
}

// 引用计数
static unsigned long red_get(struct Qdisc *sch, u32 classid)
{
 return 1;
}
// 释放计数,空函数
static void red_put(struct Qdisc *sch, unsigned long arg)
{
 return;
}

// 更改类别, 无定义
static int red_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
       struct rtattr **tca, unsigned long *arg)
{
 return -ENOSYS;
}
// 删除节点, 无定义
static int red_delete(struct Qdisc *sch, unsigned long cl)
{
 return -ENOSYS;
}

// 遍历
static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker)
{
// 其实也说不上遍历, 因为就只执行一次
 if (!walker->stop) {
  if (walker->count >= walker->skip)
   if (walker->fn(sch, 1, walker) < 0) {
    walker->stop = 1;
    return;
   }
  walker->count++;
 }
}
// 查找分类过滤规则, 空函数
static struct tcf_proto **red_find_tcf(struct Qdisc *sch, unsigned long cl)
{
 return NULL;
}

...... 待续 ......

发表于: 2007-08-18,修改于: 2007-08-18 11:52,已浏览2576次,有评论4条 推荐 投诉
	网友: fcgao 	时间:2007-10-29 17:58:28 IP地址:218.80.238.★
	

你好, WRED算法和GRED算法有什么关系嘛?


	网友: yfydz 	时间:2007-10-30 13:00:55 IP地址:218.247.216.★
	

W是指什么?weight?本质应该差不多


	网友: fcgao 	时间:2007-10-30 13:31:55 IP地址:218.80.238.★
	

W指的就是Weight,在GRED中存在WRED模式和RIO模式,按照上面的解释,只在优先级相同的VQ中执行WRED模式,这个是什么意思,如果优先级相同,靠什么实现“Weight”。我还没有看完整个过程,只是对这些个概念(WRED模式/RIO模式,GRED实现)不是很明确它们的关系。

我感觉RIO模式才是WRED实现,使用优先级作为Weight。不知道我的理解是否正确?


	网友: yfydz 	时间:2007-10-31 13:03:08 IP地址:218.247.216.★
	

这东西光看代码的确不容易理解作者是怎么想的,也得自己实验看看

分享到:
评论

相关推荐

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    基于Linux LQL流量控制系统的研究与实现

    该方法摒弃了传统方法中所运用的TC命令解析,netlink传输,内核空间执行的3层结构,而直接在Linux内核的框架下,采用LQL库直接对内核进行操控,并改进了相关U32过滤器以对IP段的流量控制,从而实现对系统的智能流量控制。...

    Linux高级路由和流量控制

    15.8. 终极的流量控制:低延迟,高速上/下载 98 15.8.1. 为什么缺省设置不让人满意 99 15.8.2. 实际的脚本(CBQ) 100 15.8.3. 实际的脚本(HTB) 102 15.9. 为单个主机或子网限速 103 15.10. 一个完全NAT和QOS的范例...

    编写Linuxc操作系统设备驱动程序概述

    在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。Linux的网络系统主要是基于BSD unix的socket机制 。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据...

    xt_fset:扩展到Linux内核netfilter子系统(iptables)(插件),使您可以通过发送ICMP包远程操作linux内核ipset(向ipset中添加或删除一些ip地址)。

    xt_fset是linux内核netfilter子系统的内核模块和iptables扩展(插件),允许您通过发送控制ICMP数据包来远程操作linux内核ipset(在ipset中添加或删除一些ip地址)。 该插件的创建是对Linux内核netfilter子系统的...

    Linux Kernel v4.19.1 Stable.zip

    Linux Kernel主要新特性包括:合并了来自Android项目的内核代码,支持新的架构TI C6X,改进了Btrfs...网络优先控制组允许管理员动态设置网络流量的优先次序;支持EFI引导固件;改进内存管理,等等。 Linux Kernel截图

    Linux的高级路由和流量控制HOWTO-中文版

    虽然这些工具能够工作,但它们在 Linux2.2 和更高版本的内核上显 得有一些落伍。比如,现在 GRE 隧道已经成为了路由的一个主要概念,但却不 能通过上述工具来配置。 使用了 iproute2,隧道的配置与其他部分完全集成了。

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

    15.2.3 流量控制315 15.3 缓冲区管理和并发控制315 15.4 设备实例:以太网nic316 15.5 isa网络驱动程序321 15.6 atm321 15.7 网络吞吐量322 15.7.1 驱动程序性能322 15.7.2 协议性能323 15.8 查看...

    lksctp-rs:Rust 的 Linux 内核 SCTP 低级绑定

    流量控制和拥塞控制 此外,它还补充说: 多路复用流:通过单个连接,您可以多路复用多个信息流。 端点的多宿主:一个端点可以有多个 IP 地址,允许网络故障转移/可靠性(注意:它需要是一台机器,或者复制端点的...

    精通LINUX设备驱动程序开发

    311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:...

    基于Linux 的防火墙技术研究

    络地址转换、流量控制及高级的包处理等。Netfilter/Iptables 系统采用模块化的架构方式,其主要模块 有:通用框架Netfilter、数据包选择系统、连接跟踪系统及NAT系统等等。 2.1 Netfilter/Iptables 系统工作原理 ...

    Linux模拟网络丢包与延迟的方法

    netem 与 tc: netem 是 Linux 2.6 及以上...tc 是 Linux 系统中的一个工具,全名为traffic control(流量控制)。tc 可以用来控制 netem 的工作模式,也就是说,如果想使用 netem ,需要至少两个条件,一个是内核中的

    DarkShell_Linux-Win集群版V2014年

    Linux支持路由内核、2.6、3.1等普通内核,路由内核支持路由三大内核、Ubuntu、admin等,独立开发的Linux穿盾CC模式,SYN稳定发包100%,自启动,无需Root权限上线即可发包。 VIP版本攻击代码实时更新,通过服务器...

    ip route2 源码 第二代网络工具

    如果你仍在使用net-tools,而且尤其需要跟上新版Linux内核中的最新最重要的网络特性的话,那么是时候转到iproute2的阵营了。原因就在于使用iproute2可以做很多net-tools无法做到的事情。对于那些想要转到使用iproute...

    Linux C 一站式学习

    7.3. 流量控制 37. socket编程 1. 预备知识 1.1. 网络字节序 1.2. socket地址的数据类型及相关函数 2. 基于TCP协议的网络程序 2.1. 最简单的TCP网络程序 2.2. 错误处理与读写控制 2.3. 把client改为交互式输入 2.4. ...

Global site tag (gtag.js) - Google Analytics