日期:2014-05-16 浏览次数:20686 次
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;