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,以实现合理的优先级,已经是一件很容易的事情了。
[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
它并非单纯意义上的“先进先出”,而是具有优先级调度的,“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
- 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 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
它不就是 prio2band 吗??
所以,我相信,现在要如何调校tos,以实现合理的优先级,已经是一件很容易的事情了。