Chinaunix首页 | 论坛 | 博客
  • 博客访问: 317271
  • 博文数量: 239
  • 博客积分: 481
  • 博客等级: 下士
  • 技术积分: 1170
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-15 17:28
文章分类

全部博文(239)

文章存档

2014年(13)

2013年(6)

2012年(122)

2011年(98)

分类: LINUX

2014-02-17 11:30:31

原文地址:Linux内核中流量控制(2) 作者:Godbach

Linux内核中流量控制(2)

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn:
来源:http://yfydz.cublog.cn

5. 流控算法的具体实现

5.1 PFIFO_FAST

PFIFO_FAST是缺省的流控算法,网卡初始化时就是设置该算法为网卡的流控算法,算法比较简单,就
在net/sched/sch_generic.c中定义了,没在单独文件中定义。

5.1.1 操作结构定义
#define PFIFO_FAST_BANDS 3
static struct Qdisc_ops pfifo_fast_ops = {
 .id  = "pfifo_fast",
// 私有数据是3个skb数据包链表头
 .priv_size = PFIFO_FAST_BANDS * 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,
};

该算法中, 数据队列是3个, 流控算法就是将数据包输入特定的队列, 从特定队列中取数据包。

5.1.2 初始化

static int pfifo_fast_init(struct Qdisc *qdisc, struct rtattr *opt)
{
 int prio;
// qdisc私有数据指针, 数据包链表头
 struct sk_buff_head *list = qdisc_priv(qdisc);
// 初始化3个链表头
 for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
  skb_queue_head_init(list + prio);
 return 0;
}
5.1.3 入队

static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc* qdisc)
{
// 根据数据包的优先级参数挑一个队列头准备将数据包插入该队列
 struct sk_buff_head *list = prio2list(skb, qdisc);
// 如果当前队列中的数据包数量小于网卡设备允许的输出队列的数量
// 则将该数据包插入该队列
 if (skb_queue_len(list) < qdisc->dev->tx_queue_len) {
  qdisc->q.qlen++;
  return __qdisc_enqueue_tail(skb, qdisc, list);
 }
// 否则的话丢弃该数据包
 return qdisc_drop(skb, qdisc);
}
// 选队列处理
static inline struct sk_buff_head *prio2list(struct sk_buff *skb,
          struct Qdisc *qdisc)
{
/* qdisc私有数据指针, 数据包链表头
Godbach注:
函数qdisc_alloc分配空间的时候,除了分配Qdisc结构体的空间,同时还分配了ops->pri_size个字节空间。对于pfifo_fast,就是3个struct sk_buff_head的大小,用于随后保存它的是三个队列,而且这个三个队列起始地址就位于qdisc_alloc分配空间的最后,也就是通过qdisc_priv(qdisc):

static inline void *qdisc_priv(struct Qdisc *q)
{
    return (char *) q + QDISC_ALIGN(sizeof(struct Qdisc));
}
返回得到的地址。
*/
 struct sk_buff_head *list = qdisc_priv(qdisc);
// 根据数据包的优先权值确定队列头偏移值
// skb->priority是个32位整数, 只使用最后4位
 return list + prio2band[skb->priority & TC_PRIO_MAX];
}
// 优先权值到队列号的变换数组, 该数组体现算法内容, 通过修改该数组可以调整算法效果
// 该数组定义中, 优先值(低4位)为1,2,3,5时使用2号队列, 优先值(低4位)为6,7时使用0号
// 队列, 其他值为1号队列
// 在普通情况下skb->priority都是0, 所有应该只使用了1号队列
// 这个数组实际是根据RFC1349中定义的TOS类型值定义的, 在该RFC中TOS就是只有4位有效
static const u8 prio2band[TC_PRIO_MAX+1] =
 { 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 };

5.1.4 出队

static struct sk_buff *pfifo_fast_dequeue(struct Qdisc* qdisc)
{
 int prio;
 struct sk_buff_head *list = qdisc_priv(qdisc);
// 循环3个队列
 for (prio = 0; prio < PFIFO_FAST_BANDS; prio++) {
// 如果队列非空, 返回队列头的那个数据包
  if (!skb_queue_empty(list + prio)) {
   qdisc->q.qlen--;
   return __qdisc_dequeue_head(qdisc, list + prio);
  }
 }
 return NULL;
}

由此可见, 0号队列有最高优先级, 2号队列优先级最低, 只有高优先级队列中的数据都发送完后才发
送低优先级队列中的数据。

5.1.5 重入队

static int pfifo_fast_requeue(struct sk_buff *skb, struct Qdisc* qdisc)
{
// 队列长度递增
 qdisc->q.qlen++;
// 使用标准重入队函数将数据插回队列链表
 return __qdisc_requeue(skb, qdisc, prio2list(skb, qdisc));
}

5.1.6 复位
static void pfifo_fast_reset(struct Qdisc* qdisc)
{
 int prio;
 struct sk_buff_head *list = qdisc_priv(qdisc);
// 释放三个队列链表中的所有数据包
 for (prio = 0; prio < PFIFO_FAST_BANDS; prio++)
  __qdisc_reset_queue(qdisc, list + prio);
// 计数清零
 qdisc->qstats.backlog = 0;
 qdisc->q.qlen = 0;
}

5.1.7 输出

输出当前算法的内容信息, 由于PFIFO_FAST算法核心就是prio2band数组, 因此就是将该数组内容输
出到数据包供用户空间获取。
static int pfifo_fast_dump(struct Qdisc *qdisc, struct sk_buff *skb)
{
// TC优先权数组结构
 struct tc_prio_qopt opt = { .bands = PFIFO_FAST_BANDS };
// 将当前prio2band数组内容拷贝到选项数据中
 memcpy(&opt.priomap, prio2band, TC_PRIO_MAX+1);
// 将结构作为路由属性复制到数据包中供返回
 RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
 return skb->len;
rtattr_failure:
 return -1;
}
 
...... 待续 ......
阅读(736) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~