日期:2014-05-16  浏览次数:20611 次

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,