pfifo_fast

pfifo_fast是Linux内核默认的无类队列规范,例如:
[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
  1. static struct Qdisc_ops pfifo_fast_ops = {
  2.         .next                =        NULL,
  3.         .cl_ops                =        NULL,
  4.         .id                =        "pfifo_fast",
  5.         .priv_size        =        3 * sizeof(struct sk_buff_head),
  6.         .enqueue        =        pfifo_fast_enqueue,
  7.         .dequeue        =        pfifo_fast_dequeue,
  8.         .requeue        =        pfifo_fast_requeue,
  9.         .init                =        pfifo_fast_init,
  10.         .reset                =        pfifo_fast_reset,
  11.         .dump                =        pfifo_fast_dump,
  12.         .owner                =        THIS_MODULE,
  13. };
复制代码
pfifo_fast的私有数据区事实上是一个数组,其结构是struct sk_buff_head,实质上是三个链表的首部,对应三个优先级。
初始化工作
  1. static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)
  2. {
  3.         int i;
  4.         struct sk_buff_head *list = qdisc_priv(qdisc);

  5.         for (i=0; i<3; i++)
  6.                 skb_queue_head_init(list+i);

  7.         return 0;
  8. }
复制代码
初始化三个链表首部

重置复位工作
  1. static void
  2. pfifo_fast_reset(struct Qdisc* qdisc)
  3. {
  4.         int prio;
  5.         struct sk_buff_head *list = qdisc_priv(qdisc);

  6.         for (prio=0; prio < 3; prio++)
  7.                 skb_queue_purge(list+prio);
  8.         qdisc->q.qlen = 0;
  9. }

  10. void skb_queue_purge(struct sk_buff_head *list)
  11. {
  12.         struct sk_buff *skb;
  13.         while ((skb = skb_dequeue(list)) != NULL)
  14.                 kfree_skb(skb);
  15. }
复制代码
实质上是清空这三个链表

出队操作
  1. static struct sk_buff *
  2. pfifo_fast_dequeue(struct Qdisc* qdisc)
  3. {
  4.         int prio;
  5.         struct sk_buff_head *list = qdisc_priv(qdisc);
  6.         struct sk_buff *skb;

  7.         for (prio = 0; prio < 3; prio++, list++) {
  8.                 skb = __skb_dequeue(list);
  9.                 if (skb) {
  10.                         qdisc->q.qlen--;
  11.                         return skb;
  12.                 }
  13.         }
  14.         return NULL;
  15. }
复制代码
是从优先级最高的开始(0为最高),遍历这三个链表,逐个出队。

关键是入队操作中,如何计算优先级了:
  1. static int
  2. pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
  3. {
  4.         struct sk_buff_head *list = qdisc_priv(qdisc);

  5.         list += prio2band[skb->priority&TC_PRIO_MAX];

  6.         if (list->qlen < qdisc->dev->tx_queue_len) {
  7.                 __skb_queue_tail(list, skb);
  8.                 qdisc->q.qlen++;
  9.                 qdisc->bstats.bytes += skb->len;
  10.                 qdisc->bstats.packets++;
  11.                 return 0;
  12.         }
  13.         qdisc->qstats.drops++;
  14.         kfree_skb(skb);
  15.         return NET_XMIT_DROP;
  16. }
复制代码
最核心的就是根据数据包的优先级,找到入队对应的链表:
  1. #define TC_PRIO_MAX                        15

  2. list += prio2band[skb->priority&TC_PRIO_MAX];
复制代码
优先级位图数组定义如下:
  1. static const u8 prio2band[TC_PRIO_MAX+1] =
  2.         { 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };
复制代码
也就是说,其返回值只可能是0,1,2,即数组中的某一个槽位。所以,关键问题是skb->priority,它直接反映了其在prio2hand中的对应关系:
优先值(低4位)为1,2,3,5时使用2号队列, 优先值(低4位)为6,7时使用0号队列, 其他值为1号队列。

对于转发的报文,skb的priority是根据tos来的设置:
  1. int ip_forward(struct sk_buff *skb)
  2. {
  3.         skb->priority = rt_tos2priority(iph->tos);
  4. }

  5. #define IPTOS_TOS_MASK                0x1E
  6. #define IPTOS_TOS(tos)                ((tos)&IPTOS_TOS_MASK)

  7. static inline char rt_tos2priority(u8 tos)
  8. {
  9.         return ip_tos2prio[IPTOS_TOS(tos)>>1];
  10. }
复制代码
因为tos字段高3位被忽略,只使用了中间四位,而低1位必须置为0,所以对于tos的计算即为
tos & 0x11110再右移1位。即IPTOS_TOS(tos)>>1。找到了在数组中的槽位,就可以得出计算
结果了:
  1. #define TC_PRIO_BESTEFFORT                0
  2. #define TC_PRIO_FILLER                        1
  3. #define TC_PRIO_BULK                        2
  4. #define TC_PRIO_INTERACTIVE_BULK        4
  5. #define TC_PRIO_INTERACTIVE                6
  6. #define TC_PRIO_CONTROL                        7

  7. #define ECN_OR_COST(class)        TC_PRIO_##class

  8. __u8 ip_tos2prio[16] = {
  9.         TC_PRIO_BESTEFFORT,                        //0->0        ->1
  10.         ECN_OR_COST(FILLER),                        //1->1        ->2
  11.         TC_PRIO_BESTEFFORT,                        //2->0        ->1
  12.         ECN_OR_COST(BESTEFFORT),                        //3->0        ->1
  13.         TC_PRIO_BULK,                                //4->2        ->2
  14.         ECN_OR_COST(BULK),                        //5->2        ->2
  15.         TC_PRIO_BULK,                                //6->2        ->2
  16.         ECN_OR_COST(BULK),                        //7->2        ->2
  17.         TC_PRIO_INTERACTIVE,                        //8->6        ->0
  18.         ECN_OR_COST(INTERACTIVE),                        //9->6        ->0
  19.         TC_PRIO_INTERACTIVE,                        //10->6        ->0
  20.         ECN_OR_COST(INTERACTIVE),                        //11->6        ->0
  21.         TC_PRIO_INTERACTIVE_BULK,                        //12->4        ->1
  22.         ECN_OR_COST(INTERACTIVE_BULK),                //13->4        ->1
  23.         TC_PRIO_INTERACTIVE_BULK,                        //14->4        ->1
  24.         ECN_OR_COST(INTERACTIVE_BULK)                //15->4        ->1
  25. };
复制代码
注意我对数组后面的注释,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,以实现合理的优先级,已经是一件很容易的事情了。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值