`

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网络体系结构 Linux内核中网络协议的设计与实现

    在Linux内核中,TCP和UDP模块处理连接建立、数据传输、流量控制和拥塞控制等问题。 5. **应用层**:这一层包含各种应用协议,如HTTP、FTP、SMTP等,它们直接与用户交互。Linux内核通过socket API为上层应用提供了与...

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

    【基于Linux内核的BT流量控制的原理与实现】 Linux操作系统以其开源、可定制的特点,在系统开发领域广泛应用,尤其在网络流量控制方面具有显著优势。针对BitTorrent(BT)这种大量占用带宽的P2P协议,Linux内核提供...

    Linux内核完全注释V3.0_linux内核_linux_

    8. **模块化设计**:Linux内核的模块加载和卸载机制,允许动态添加或移除功能。 9. **安全性与权限**:用户和用户组的概念,访问控制机制(如POSIX权限、SELinux),以及内核的安全模型。 10. **内核配置与编译**...

    深入分析Linux内核源码

    通过分析源码,我们可以了解到数据包的接收与发送过程,理解TCP连接的建立与断开、拥塞控制、流量控制等机制,这对于网络编程和网络故障排查非常有帮助。 此外,Linux内核还涉及中断处理、设备驱动、I/O管理等多个...

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

    【基于Linux内核扩展模块的P2P流量控制】这篇文献主要探讨了如何在Linux操作系统中,通过内核扩展模块来实现对P2P流量的有效控制。P2P(Peer-to-Peer)技术的兴起改变了互联网的中心化结构,使得资源分享更为便捷,...

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

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

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

    基于LQL库的流量控制方法可以直接在Linux内核的框架下实现,而不需要使用传统方法中的TC命令解析、netlink传输和内核空间执行的三层结构。这可以提高流量控制的效率和可靠性,同时也可以减少流量控制的延迟和资源...

    Linux内核修炼之道精华版

    书中的内容涵盖了从内核基础到高级技术的方方面面,为那些希望提升Linux内核理解和开发能力的读者提供了宝贵的资源。在本文中,我们将探讨几个关键的知识点,包括Linux内核的基本结构、进程管理、内存管理和设备驱动...

    Linux内核源码(2.6.24)

    2.6.24版本在网络方面加强了IPv6的支持,并改进了网络流量控制算法。 6. **安全与权限管理**:Linux内核采用了用户和组的概念,通过权限系统(如chmod、chown等)来控制文件访问。此外,还有SELinux(Security-...

    深入理解linux内核word版本

    接着,作者深入剖析了网络设备数据结构net_device,它包含了设备的配置信息、统计信息、状态标志以及各种管理列表和流量控制字段,这些细节揭示了网络设备如何在内核中被抽象和管理。 通过以上内容,我们可以看到,...

    Linux内核情景分析(上下全集高清版)

    它处理网络数据的接收和发送,进行网络层的路由选择,以及传输层的拥塞控制和流量控制。 5. **设备驱动**:设备驱动程序是内核与硬件之间的桥梁,使得内核能够控制硬件设备。Linux内核支持大量设备驱动,包括块设备...

    linux高级路由和流量控制HOWTO中文版(牛老师译)

    在Linux操作系统中,高级路由和流量控制是网络管理员和系统管理员必须掌握的关键技能。这篇文档“Linux高级路由和流量控制HOWTO中文版”由牛老师翻译,为读者提供了深入理解这些概念的宝贵资源。以下是对其中核心...

    基于Linux的网络流量控制机制

    该模型内置于Linux内核中,并利用队列算法对不同服务质量(Quality of Service, QoS)需求的数据流进行分类,以提供灵活且差异化的服务。实验结果表明,采用该流量控制模型后,网络性能显著提高,并能很好地适应未来...

    《Linux内核源码剖析 TCP IP实现(上册) 樊东东 莫澜 pdf扫描版.

    同时,还会讨论TCP的流量控制和拥塞控制机制,如滑动窗口、慢启动、快速重传和快速恢复算法等,这些都是保证网络通信质量和效率的关键。 其次,关于IP协议,书里会涉及IP地址、子网掩码、路由选择等概念,以及IP分...

    TC(linux下流量控制工具)详细说明及应用实例借鉴.pdf

    TC 工具基于 Linux 内核的网络设备驱动程序,通过对网络设备的控制,来实现流量控制。TC 的工作原理可以分为以下三个阶段: 1. 流量控制方式:TC 提供了多种流量控制方式,包括 Token Bucket Filter(TBF)、...

    TC(linux下流量控制工具)详细说明及应用实例.pdf

    TC(Linux 下流量控制工具)详细说明及应用实例 TC 是 Linux 下的一种流量控制工具,用于控制和管理网络流量。它提供了一个灵活的方式来管理网络带宽、延迟和丢包率等网络性能参数,以满足不同应用场景的需求。 TC...

    linux内核协议栈源码解析(2.6.18内核)

    2. **TCP/IP协议**:在传输层,TCP(传输控制协议)提供可靠的数据传输服务,通过确认、重传和流量控制确保数据的完整性和顺序。IP(互联网协议)在网络层负责数据包的路由和分片,是互联网的基础协议。 3. **套接...

    linux内核中sock和socket数据结构

    Linux内核中的sock和socket数据结构是网络编程的核心组成部分,它们是实现网络通信的基础构件。在Linux操作系统中,网络通信的实现依赖于BSD套接字接口,而这一接口在内核中是通过sock和socket数据结构来实现的。 ...

Global site tag (gtag.js) - Google Analytics