Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1259373
  • 博文数量: 254
  • 博客积分: 1586
  • 博客等级: 上尉
  • 技术积分: 2295
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-15 16:38
个人简介

linux学习中

文章分类

全部博文(254)

文章存档

2016年(6)

2015年(2)

2014年(74)

2013年(93)

2012年(12)

2011年(2)

2010年(51)

2009年(14)

分类: 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的是

 

  1. void dev_init_scheduler(struct net_device*dev)  
  2. {  
  3.          dev->qdisc= &noop_qdisc;  
  4.          netdev_for_each_tx_queue(dev,dev_init_scheduler_queue, &noop_qdisc);  
  5.          if(dev_ingress_queue(dev))  
  6.                    dev_init_scheduler_queue(dev,dev_ingress_queue(dev), &noop_qdisc);  
  7.    
  8.          setup_timer(&dev->watchdog_timer,dev_watchdog, (unsigned long)dev);  
  9. }  

dev的qdisc被初始化为noop_qdisc,netdev_for_each_tx_queue负责将net_device中的传输队列初始化:dev->num_tx_queues队列数目,dev->_tx队列,如果有ingress队列,则也同时初始化,这里我们可以看到,入队列只有一个,叫dev->ingress_queue。


现在关注一下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命令、内核实现,最后自己写一个模块来进行流控。 

LINUX协议栈详解 流控TC

阅读(2270) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~