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;
}
}
}
|