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

Linux内核中流量控制(13)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

5.11.9 入队

static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
 int ret;
// HTB私有数据结构
 struct htb_sched *q = qdisc_priv(sch);
// 对数据包进行分类
 struct htb_class *cl = htb_classify(skb, sch, &ret);
 if (cl == HTB_DIRECT) {
// 分类结果是直接发送
  /* enqueue to helper queue */
// 如果直接发送队列中的数据包长度小于队列限制值, 将数据包添加到队列末尾
  if (q->direct_queue.qlen < q->direct_qlen) {
   __skb_queue_tail(&q->direct_queue, skb);
   q->direct_pkts++;
  } else {
// 否则丢弃数据包
   kfree_skb(skb);
   sch->qstats.drops++;
   return NET_XMIT_DROP;
  }
#ifdef CONFIG_NET_CLS_ACT
// 定义了NET_CLS_ACT的情况(支持分类动作)
 } else if (!cl) {
// 分类没有结果, 丢包
  if (ret == NET_XMIT_BYPASS)
   sch->qstats.drops++;
  kfree_skb(skb);
  return ret;
#endif
// 有分类结果, 进行分类相关的叶子节点流控结构的入队操作
 } else if (cl->un.leaf.q->enqueue(skb, cl->un.leaf.q) !=
     NET_XMIT_SUCCESS) {
// 入队不成功的话丢包
  sch->qstats.drops++;
  cl->qstats.drops++;
  return NET_XMIT_DROP;
 } else {
// 入队成功, 分类结构的包数字节数的统计数增加
  cl->bstats.packets++;
  cl->bstats.bytes += skb->len;
// 激活HTB类别, 建立该类别的数据提供树, 这样dequeue时可以从中取数据包
// 只有类别节点的模式是可发送和可租借的情况下才会激活, 如果节点是阻塞
// 模式, 则不会被激活
  htb_activate(q, cl);
 }
// HTB流控结构统计数更新, 入队成功
 sch->q.qlen++;
 sch->bstats.packets++;
 sch->bstats.bytes += skb->len;
 return NET_XMIT_SUCCESS;
}
 
大部分情况下数据包都不会进入直接处理队列, 而是进入各类别叶子节点, 因此入队的成功与否就在于叶子节点使用何种流控算法, 大都应该可以入队成功的, 入队不涉及类别节点模式的调整。

5.11.10 重入队

/* TODO: requeuing packet charges it to policers again !! */
static int htb_requeue(struct sk_buff *skb, struct Qdisc *sch)
{
// HTB私有数据结构
 struct htb_sched *q = qdisc_priv(sch);
 int ret = NET_XMIT_SUCCESS;
// 对数据包进行HTB分类
 struct htb_class *cl = htb_classify(skb, sch, &ret);
 struct sk_buff *tskb;
// 是直接处理或无类别数据包
 if (cl == HTB_DIRECT || !cl) {
// 如果当前直接队列没满而且是直接处理包, 就添加在直接处理队列表头
  /* enqueue to helper queue */
  if (q->direct_queue.qlen < q->direct_qlen && cl) {
   __skb_queue_head(&q->direct_queue, skb);
  } else {
// 队列慢或者无类别包
// 添加到队列头
   __skb_queue_head(&q->direct_queue, skb);
// 从队列尾取一个数据包丢弃
   tskb = __skb_dequeue_tail(&q->direct_queue);
   kfree_skb(tskb);
   sch->qstats.drops++;
   return NET_XMIT_CN;
  }
// 分类成功, 使用类别结构对应叶子流控节点的重入队操作
 } else if (cl->un.leaf.q->ops->requeue(skb, cl->un.leaf.q) !=
     NET_XMIT_SUCCESS) {
// 重入队失败, 丢包
  sch->qstats.drops++;
  cl->qstats.drops++;
  return NET_XMIT_DROP;
 } else
// 重入队成功, 激活类别结构, 构造数据包提供树
  htb_activate(q, cl);
// 队列统计信息更新
 sch->q.qlen++;
 sch->qstats.requeues++;
 return NET_XMIT_SUCCESS;
}
 
5.11.11 出队

HTB的出队是个非常复杂的处理过程, 函数调用过程为:
htb_dequeue
  -> __skb_dequeue
  -> htb_do_events
    -> htb_safe_rb_erase
    -> htb_change_class_mode
    -> htb_add_to_wait_tree
  -> htb_dequeue_tree
    -> htb_lookup_leaf
    -> htb_deactivate
    -> q->dequeue
    -> htb_next_rb_node
    -> htb_charge_class
      -> htb_change_class_mode
      -> htb_safe_rb_erase
      -> htb_add_to_wait_tree
  -> htb_delay_by
 
static struct sk_buff *htb_dequeue(struct Qdisc *sch)
{
 struct sk_buff *skb = NULL;
// HTB私有数据结构
 struct htb_sched *q = qdisc_priv(sch);
 int level;
 long min_delay;
// 保存当前时间滴答数
 q->jiffies = jiffies;
 /* try to dequeue direct packets as high prio (!) to minimize cpu work */
// 先从当前直接发送队列取数据包, 直接发送队列中的数据有最高优先级, 可以说没有流量限制
 skb = __skb_dequeue(&q->direct_queue);
 if (skb != NULL) {
// 取到数据包, 更新参数, 非阻塞, 返回
  sch->flags &= ~TCQ_F_THROTTLED;
  sch->q.qlen--;
  return skb;
 }
// 如果HTB流控结构队列长度为0, 返回空
 if (!sch->q.qlen)
  goto fin;
// 获取当前有效时间值
 PSCHED_GET_TIME(q->now);
// 最小延迟值初始化为最大整数
 min_delay = LONG_MAX;
 q->nwc_hit = 0;
// 遍历树的所有层次, 从叶子节点开始
 for (level = 0; level < TC_HTB_MAXDEPTH; level++) {
  /* common case optimization - skip event handler quickly */
  int m;