linux学习中
分类: LINUX
2013-12-16 17:19:26
LINUX协议栈详解 流控TC
在数据包发送一节提到了流控TC的实现,现在进一步研究。
数据包发送是通过dev_queue_xmit实现的,
if (q->enqueue) {
rc = __dev_xmit_skb(skb, q, dev, txq);
goto out;
}
如果有enqueue则说明进行流控,否则直接发送。
参考函数__dev_xmit_skb,我们可以看到对skb进行了enqueue操作,并__qdisc_run开始触发软中断。
struct Qdisc_ops定义了QoS相关的操作,最主要的是enqueue和dequeue。
当一个网卡注册时,调用int register_netdevice(struct net_device *dev),负责初始化网卡TC的是
现在关注一下noop_qdisc,初始化注册的QoS,看代码会发现其实里面是空操作,或者仅仅是将调用kfree_skb将skb释放掉。这种状况将会在ifconfig up后激活设备改变,dev_activate判断,如果发现是noop_qdisc,则attach_default_qdiscs负责给再次赋值。
如果网卡设置的是单队列,且队列大小为0,则为noqueue_qdisc;
单队列,单队列大小不为0,则pfifo_fast_ops;
多队列,队列大小为0,则noqueue_qdisc;
否则为mq_qdisc_ops,以上就是在默认情况下的策略。
那么,如果我们自己写模块进行TC操作,怎么办呢?接下来我们分析一下sch_red.c
在module_init上,register_qdisc,将ops加入到qdisc_base中,具体使用是TC命令最后通过netlink调用tc_modify_qdisc来实现的。
Qdisc_ops结构体中的函数指针使用过程中调用顺序是
ops->init
ops->attach
ops->change
ops->enqueue
ops->dequeue
ops->reset
ops->destroy
接下来主要分析一下
struct Qdisc_ops pfifo_fast_ops __read_mostly = {
.id = "pfifo_fast",
.priv_size = sizeof(structpfifo_fast_priv),
.enqueue = pfifo_fast_enqueue,
.dequeue = pfifo_fast_dequeue,
.peek = pfifo_fast_peek,
.init = pfifo_fast_init,
.reset = pfifo_fast_reset,
.dump = pfifo_fast_dump,
.owner = THIS_MODULE,
};
这是LINUX下缺省的策略。
pfifo_fast_init 初始化函数
pfifo_fast_enqueue,如果当前队列中的frame数要小于队列中最大的数,则通过skb->priority算出band,并通过band索引策略的那个队列中,最后加入到队列中。如果超出最大限制,则释放掉skb。
pfifo_fast_dequeue则通过priv->bitmap索引数组,队列分别是0到2一次发送出去。
pfifo_fast_peek仅仅取出数据,但不从队列中出对。
pfifo_fast_reset最后reset掉。
现在回到dev_queue_xmit函数
1, dev_pick_tx选择发送队列
2, q->enqueue 压队列
3, qdisc_restart,dequeue_skb出队列
4, dev_hard_start_xmit发送
5, 没有发送成功,则将skb保存到gso_skb中,下一次dequeue再次发送这个skb
还剩下一个问题,那就是dev_ingress_queue中的ingress流控是怎么回事?
上面大体介绍了TC系统的内核实现,TC系统是非常复杂的。
将TC命令如何使用
后面还会继续深入的研究,研究的方式是从TC命令、内核实现,最后自己写一个模块来进行流控。