发送队列的默认队列策略 (linux网络子系统学习 第十一节 )

http://yaoyang.blog.51cto.com/7657153/1305123

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。 http://yaoyang.blog.51cto.com/7657153/1305123

如果网络设备发送队列没有配置发送策略,内核就会使用默认的队列策略来进行报文的发送。

内核中定义实现了两种默认的队列策略,一种是给队列长度为零的队列使用的。一种是给队列长度不为0,但没配置队列策略的队列使用的。


初始化时使用的默认队列策略 noop_qdisc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*入队操作直接丢弃报文*/
static int noop_enqueue( struct sk_buff *skb, struct Qdisc * qdisc)
{
     kfree_skb(skb);
     return NET_XMIT_CN;
}
/*出队操作直接返回NULL*/
static struct sk_buff *noop_dequeue( struct Qdisc * qdisc)
{
     return NULL;
}
struct Qdisc_ops noop_qdisc_ops __read_mostly = {
     .id     =   "noop" ,
     .priv_size  =   0,
     .enqueue    =   noop_enqueue,
     .dequeue    =   noop_dequeue,
     .peek       =   noop_dequeue,
     .owner      =   THIS_MODULE,
};
static struct netdev_queue noop_netdev_queue = {
     .qdisc      =   &noop_qdisc,
     .qdisc_sleeping =   &noop_qdisc,
};
struct Qdisc noop_qdisc = {
     .enqueue    =   noop_enqueue,
     .dequeue    =   noop_dequeue,
     .flags      =   TCQ_F_BUILTIN,
     .ops        =   &noop_qdisc_ops,
     .list       =   LIST_HEAD_INIT(noop_qdisc.list),
     .dev_queue  =   &noop_netdev_queue,
};
EXPORT_SYMBOL(noop_qdisc);



零长度队列的默认队列策略 noqueue_qdisc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static struct Qdisc_ops noqueue_qdisc_ops __read_mostly = {
     .id     =   "noqueue" ,
     .priv_size  =   0,
     .enqueue    =   noop_enqueue,
     .dequeue    =   noop_dequeue,
     .peek       =   noop_dequeue,
     .owner      =   THIS_MODULE,
};
static struct netdev_queue noqueue_netdev_queue = {
     .qdisc      =   &noqueue_qdisc,
     .qdisc_sleeping =   &noqueue_qdisc,
};
static struct Qdisc noqueue_qdisc = {
     /*队列长度为零,入队函数置为空,
      *发送函数根据该字段是否为空来判断是否需要缓存报文
      */
     .enqueue    =   NULL,
     .dequeue    =   noop_dequeue,
     .flags      =   TCQ_F_BUILTIN,
     .ops        =   &noqueue_qdisc_ops,
     .list       =   LIST_HEAD_INIT(noqueue_qdisc.list),
     .q.lock     =   __SPIN_LOCK_UNLOCKED(noqueue_qdisc.q.lock),
     .dev_queue  =   &noqueue_netdev_queue,
};


单队列时使用的默认队列策略 pfifo_fast:

该默认队列策略使用三个优先级队列来管理报文的发送。根据skb->priority字段设置的优先级来决定报文的发送优先级。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*默认报文优先级队列个数为 3 个*/
#define PFIFO_FAST_BANDS 3
/*队列策略的私有数据结构
  * q:三个不同优先级的报文队列,数组下标越小优先级越高
  * bitmap:记录三个优先级队列中哪些有报文需要发送
  */
struct pfifo_fast_priv
{
     u32 bitmap;
     struct sk_buff_head q[PFIFO_FAST_BANDS];
};
/*根据priv->bitmap的值取出有报文要发送的队列号,
  *如果有多个队列都有报文要发送,返回优先级最高的队列号
  */
static const int bitmap2band[] = {-1, 0, 1, 0, 2, 0, 1, 0};
struct sk_buff_head *band2list( struct pfifo_fast_priv *priv,
                                int band)
{
     return priv->q + band;
}
/*根据skb->priority字段往队列策略优先级队列中的映射表*/
static const u8 prio2band[TC_PRIO_MAX+1] =
{ 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };

入队和出队的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static int pfifo_fast_enqueue( struct sk_buff *skb,
                               struct Qdisc* qdisc)
{
     /*如果队列没满,就缓存报文*/
     if (skb_queue_len(&qdisc->q)
         < qdisc_dev(qdisc)->tx_queue_len)
     {
         /*根据skb的优先级找到队列策略对应的优先级队列*/
         int band = prio2band[skb->priority & TC_PRIO_MAX];
         struct pfifo_fast_priv *priv = qdisc_priv(qdisc);
         struct sk_buff_head *list = band2list(priv, band);
         /*置位优先级队列对应的bitmap位*/
         priv->bitmap |= (1 << band);
         qdisc->q.qlen++;
                                                                                                                                                                                          
         /*把报文加入队列*/
         return __qdisc_enqueue_tail(skb, qdisc, list);
     }
     /*如果队列已经满了,丢弃报文*/
     return qdisc_drop(skb, qdisc);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static struct sk_buff *pfifo_fast_dequeue( struct Qdisc* qdisc)
{
     struct pfifo_fast_priv *priv = qdisc_priv(qdisc);
     /*找到有报文要发送的优先级最高的队列*/
     int band = bitmap2band[priv->bitmap];
     if (likely(band >= 0))
     {
         struct sk_buff_head *list = band2list(priv, band);
         /*从队列中取一个报文*/
         struct sk_buff *skb =
              __qdisc_dequeue_head(qdisc, list);
         qdisc->q.qlen--;
         /*如果队列为空,清除bitmap位*/
         if (skb_queue_empty(list))
         {
            priv->bitmap &= ~(1 << band);
         }
                                                                                                                                                                                      
         return skb;
     }
                                                                                                                                                                                  
     /*没有队列有报文要发送,返回NULL*/
     return NULL;
}


1
2
3
4
5
6
7
struct Qdisc_ops pfifo_fast_ops __read_mostly = {
     .id     =   "pfifo_fast" ,
     .priv_size  =   sizeof ( struct pfifo_fast_priv),
     .enqueue    =   pfifo_fast_enqueue,
     .dequeue    =   pfifo_fast_dequeue,
     .owner      =   THIS_MODULE,
};


队列策略的初始化:

在设备创建时会使用noop_qdisc来初始化发送队列的队列策略:

1
2
3
4
5
6
7
8
9
10
11
12
int register_netdevice( struct net_device *dev)
{
     。。。。。。
     dev_init_scheduler(dev);
     。。。。。。
}
void dev_init_scheduler( struct net_device *dev)
{
     dev->qdisc = &noop_qdisc;
     netdev_for_each_tx_queue(dev, dev_init_scheduler_queue,
                              &noop_qdisc);
}

当打开设备时,如果没创建队列策略,就会给创建一个默认的队列策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dev_open()
{
     。。。。。。
     dev_activate(dev);
     。。。。。。
}
void dev_activate( struct net_device *dev)
{
     /*如果dev使用的默认的qisc  noop_qdisc,
      *创建一个新的qdisc*/
     if (dev->qdisc == &noop_qdisc)
     {
     attach_default_qdiscs(dev);
     }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
static void attach_one_default_qdisc( struct net_device *dev,
                struct netdev_queue *dev_queue,
                void *_unused)
{
     struct Qdisc *qdisc;
     /*如果队列长度不为0,就创建一个发送队列策略pfifo_fast*/
     if (dev->tx_queue_len)
     {
         qdisc = qdisc_create_dflt(dev, dev_queue,
                          &pfifo_fast_ops, TC_H_ROOT);
         if (!qdisc)
         {
             printk(KERN_INFO "%s: activation failed\n" , dev->name);
             return ;
         }
         /* Can by-pass the queue discipline for default qdisc */
         qdisc->flags |= TCQ_F_CAN_BYPASS;
     }
     /*发送队列长度为0,就使用noqueue_qdisc*/
     else
     {
         qdisc =  &noqueue_qdisc;
     }
     dev_queue->qdisc_sleeping = qdisc;
}
static void attach_default_qdiscs( struct net_device *dev)
{
     struct netdev_queue *txq;
     struct Qdisc *qdisc;
     /*取得设备的第一个发送队列*/
     txq = netdev_get_tx_queue(dev, 0);
             
     /*如果设备只有一个发送队列或者发送队列长度为0,
      *调用attach_one_default_qdisc创建一个默认队列策略
      */
     if (!netif_is_multiqueue(dev)
         || dev->tx_queue_len == 0)
     {
         netdev_for_each_tx_queue(dev,
                  attach_one_default_qdisc, NULL);
         dev->qdisc = txq->qdisc_sleeping;
         atomic_inc(&dev->qdisc->refcnt);
     }
     else
     {
         qdisc = qdisc_create_dflt(dev, txq,
                         &mq_qdisc_ops, TC_H_ROOT);
         if (qdisc)
         {
             qdisc->ops->attach(qdisc);
             dev->qdisc = qdisc;
         }
     }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值