内核PFIFO_FAST的优先级计算
(2010-07-05 10:06:51)
标签:
it |
分类: QOS |
pfifo_fast是Linux内核默认的无类队列规范,例如:
它并非单纯意义上的“先进先出”,而是具有优先级调度的,“bands 3”,事实上指明它有3个优先级:0,1,2,0为最高。发送数据包时,先处理优先级最高的。
本文主要介绍了Linux如何计算这个优先级,也就是说,如何看懂后面那串,“priomap 1
2 2 2 1 2 0 0 1 1 1 1 1 1 1 1”,并能根据需要自行设置。
version:2.6.12
代码:net/shced/sch_generic.c
复制代码
pfifo_fast的私有数据区事实上是一个数组,其结构是struct
sk_buff_head,实质上是三个链表的首部,对应三个优先级。
初始化工作
复制代码
初始化三个链表首部
重置复位工作
复制代码
实质上是清空这三个链表
出队操作
复制代码
是从优先级最高的开始(0为最高),遍历这三个链表,逐个出队。
关键是入队操作中,如何计算优先级了:
复制代码
最核心的就是根据数据包的优先级,找到入队对应的链表:
复制代码
优先级位图数组定义如下:
复制代码
也就是说,其返回值只可能是0,1,2,即数组中的某一个槽位。所以,关键问题是skb->priority,它直接反映了其在prio2hand中的对应关系:
优先值(低4位)为1,2,3,5时使用2号队列, 优先值(低4位)为6,7时使用0号队列, 其他值为1号队列。
对于转发的报文,skb的priority是根据tos来的设置:
复制代码
因为tos字段高3位被忽略,只使用了中间四位,而低1位必须置为0,所以对于tos的计算即为
tos & 0x11110再右移1位。即IPTOS_TOS(tos)>>1。找到了在数组中的槽位,就可以得出计算
结果了:
复制代码
注 意我对数组后面的注释,0->0
->1,第一个值表示数组的索引,也就是tos的值。第二个值表示它对应了数组中的值,即最终计算到的skb->priority。第三个值
是根据第二个值在prio2band(上文红色部份)中查到的对应的pfifo_fast优先级的值。
最后看看一开始提到的:
priomap 1 2 2 2 1 2 0 0 1 1 1 1
1 1 1 1
它不就是 prio2band 吗??
所以,我相信,现在要如何调校tos,以实现合理的优先级,已经是一件很容易的事情了。
后记:关于Linux QoS架构和pfifo_fast的分析,有一篇入门文章推荐:
http://www.ibm.com/developerworks/cn/linux/kernel/l-qos/
呵呵,全剧终!
QUOTE:
[root@Kendo ~]# tc qdisc show dev eth0
qdisc pfifo_fast 0: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1
1 1 1 1
qdisc pfifo_fast 0: bands 3 priomap
它并非单纯意义上的“先进先出”,而是具有优先级调度的,“bands 3”,事实上指明它有3个优先级:0,1,2,0为最高。发送数据包时,先处理优先级最高的。
本文主要介绍了Linux如何计算这个优先级,也就是说,如何看懂后面那串,“priomap
version:2.6.12
代码:net/shced/sch_generic.c
- static struct Qdisc_ops pfifo_fast_ops = {
-
.next = NULL, -
.cl_ops = NULL, -
.id = "pfifo_fast", -
.priv_size = 3 * sizeof(struct sk_buff_head), -
.enqueue = pfifo_fast_enqueue, -
.dequeue = pfifo_fast_dequeue, -
.requeue = pfifo_fast_requeue, -
.init = pfifo_fast_init, -
.reset = pfifo_fast_reset, -
.dump = pfifo_fast_dump, -
.owner = THIS_MODULE, - };
初始化工作
- static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr
*opt)
- {
-
int i; -
struct sk_buff_head *list = qdisc_priv(qdisc); -
for (i=0; i<3; i++) -
skb_queue_head_init(list+i); -
return 0; - }
重置复位工作
- static void
- pfifo_fast_reset(struct Qdisc* qdisc)
- {
-
int prio; -
struct sk_buff_head *list = qdisc_priv(qdisc); -
for (prio=0; prio < 3; prio++) -
skb_queue_purge(list+prio); -
qdisc->q.qlen = 0; - }
- void skb_queue_purge(struct sk_buff_head *list)
- {
-
struct sk_buff *skb; -
while ((skb = skb_dequeue(list)) != NULL) -
kfree_skb(skb); - }
出队操作
- static struct sk_buff *
- pfifo_fast_dequeue(struct Qdisc* qdisc)
- {
-
int prio; -
struct sk_buff_head *list = qdisc_priv(qdisc); -
struct sk_buff *skb; -
for (prio = 0; prio < 3; prio++, list++) { -
skb = __skb_dequeue(list); -
if (skb) { -
qdisc->q.qlen--; -
return skb; -
} -
} -
return NULL; - }
关键是入队操作中,如何计算优先级了:
- static int
- pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc*
qdisc)
- {
-
struct sk_buff_head *list = qdisc_priv(qdisc); -
list += prio2band[skb->priority&TC_PRIO_MAX];
-
if (list->qlen < qdisc->dev->tx_queue_len) { -
__skb_queue_tail(list, skb); -
qdisc->q.qlen++; -
qdisc->bstats.bytes += skb->len; -
qdisc->bstats.packets++; -
return 0; -
} -
qdisc->qstats.drops++; -
kfree_skb(skb); -
return NET_XMIT_DROP; - }
- #define TC_PRIO_MAX
15 - list += prio2band[skb->priority&TC_PRIO_MAX];
- static const u8 prio2band[TC_PRIO_MAX+1] =
-
{ 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };
优先值(低4位)为1,2,3,5时使用2号队列, 优先值(低4位)为6,7时使用0号队列, 其他值为1号队列。
对于转发的报文,skb的priority是根据tos来的设置:
- int ip_forward(struct sk_buff *skb)
- {
-
skb->priority = rt_tos2priority(iph->tos); - }
- #define IPTOS_TOS_MASK
0x1E - #define IPTOS_TOS(tos)
((tos)&IPTOS_TOS_MASK) - static inline char rt_tos2priority(u8 tos)
- {
-
return ip_tos2prio[IPTOS_TOS(tos)>>1]; - }
tos & 0x11110再右移1位。即IPTOS_TOS(tos)>>1。找到了在数组中的槽位,就可以得出计算
结果了:
- #define TC_PRIO_BESTEFFORT
0 - #define TC_PRIO_FILLER
1 - #define TC_PRIO_BULK
2 - #define TC_PRIO_INTERACTIVE_BULK
4 - #define TC_PRIO_INTERACTIVE
6 - #define TC_PRIO_CONTROL
7 - #define ECN_OR_COST(class)
TC_PRIO_##class - __u8 ip_tos2prio[16] = {
-
TC_PRIO_BESTEFFORT, //0->0 ->1 -
ECN_OR_COST(FILLER), //1->1 ->2 -
TC_PRIO_BESTEFFORT, //2->0 ->1 -
ECN_OR_COST(BESTEFFORT), //3->0 ->1 -
TC_PRIO_BULK, //4->2 ->2 -
ECN_OR_COST(BULK), //5->2 ->2 -
TC_PRIO_BULK, //6->2 ->2 -
ECN_OR_COST(BULK), //7->2 ->2 -
TC_PRIO_INTERACTIVE, //8->6 ->0 -
ECN_OR_COST(INTERACTIVE), //9->6 ->0 -
TC_PRIO_INTERACTIVE, //10->6 ->0 -
ECN_OR_COST(INTERACTIVE), //11->6 ->0 -
TC_PRIO_INTERACTIVE_BULK, //12->4 ->1 -
ECN_OR_COST(INTERACTIVE_BULK), //13->4 ->1 -
TC_PRIO_INTERACTIVE_BULK, //14->4 ->1 -
ECN_OR_COST(INTERACTIVE_BULK) //15->4 ->1 - };
最后看看一开始提到的:
priomap
它不就是 prio2band 吗??
所以,我相信,现在要如何调校tos,以实现合理的优先级,已经是一件很容易的事情了。
后记:关于Linux QoS架构和pfifo_fast的分析,有一篇入门文章推荐:
http://www.ibm.com/developerworks/cn/linux/kernel/l-qos/
呵呵,全剧终!
后一篇:Linux内核中流量控制