Linux内核中流量控制(10)

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

5.10 TEQL("True" (or "trivial") link equalizer.)

TEQL流控方法是比较特殊的一个算法,在net/sched/sch_teql.c中定义, 使用时不需要tc提供参数, 该算法是构造一个虚拟网卡, 将物理网卡加入到这个虚拟网卡中实现多网卡的流量均衡, 这和bonding有点象, 不过这些物理网卡的都保持各自的地址, 不用相同。

5.10.1 teql操作结构定义

// teql私有数据结构
struct teql_sched_data
{
// 下一个流控节点
struct Qdisc *next;
// 指向teql属主
struct teql_master *m;
// 路由cache
struct neighbour *ncache;
// 数据队列
struct sk_buff_head q;
};

// teql网卡私有数据
struct teql_master
{
// 第一个元素必须是流控操作结构, 这样两个结构指针类型可以互换
struct Qdisc_ops qops;
struct net_device *dev;
// 从流控节点
struct Qdisc *slaves;
struct list_head master_list;
struct net_device_stats stats;
};

一些定义:

// 下一个从流控节点
#define NEXT_SLAVE(q) (((struct teql_sched_data*)qdisc_priv(q))->next)
// 虚拟网卡链表
static LIST_HEAD(master_dev_list);
// 最大平衡器数量, 缺省为1个, 对应虚拟网卡teql0, 可在插入模块时设置该参数
static int max_equalizers = 1;
module_param(max_equalizers, int, 0);
MODULE_PARM_DESC(max_equalizers, "Max number of link equalizers");


5.10.1 初始化

TEQL没有象其他流控算法那样明确定义流控结构, 而是先定义虚拟网卡, 然后定义该网卡的流控算法为TEQL。

static int __init teql_init(void)
{
int i;
int err = -ENODEV;
// 最多建立max_equalizers个teql*虚拟网卡
for (i = 0; i < max_equalizers; i++) {
struct net_device *dev;
struct teql_master *master;
// 分配teql网卡名, teql_master_setup为网卡初始化函数
dev = alloc_netdev(sizeof(struct teql_master),
"teql%d", teql_master_setup);
if (!dev) {
err = -ENOMEM;
break;
}
// 登记该teql网卡
if ((err = register_netdev(dev))) {
free_netdev(dev);
break;
}
// 初始化teql的流控算法信息
master = netdev_priv(dev);
// 流控算法名称为虚拟网卡名称, 如teql0, 这和其他流控算法不同
strlcpy(master->qops.id, dev->name, IFNAMSIZ);
// 登记该流控操作结构
err = register_qdisc(&master->qops);
if (err) {
// 错误时释放网卡信息
unregister_netdev(dev);
free_netdev(dev);
break;
}
// 将新建虚拟网卡添加到虚拟网卡链表
list_add_tail(&master->master_list, &master_dev_list);
}
return i ? 0 : err;
}

// teql网卡初始化
static __init void teql_master_setup(struct net_device *dev)
{
// teql结构作为网卡私有数据
struct teql_master *master = netdev_priv(dev);
// 流控操作
struct Qdisc_ops *ops = &master->qops;
// 网卡设备回指
master->dev = dev;
ops->priv_size = sizeof(struct teql_sched_data);
// 流控操作结构初始化
ops->enqueue = teql_enqueue;
ops->dequeue = teql_dequeue;
ops->requeue = teql_requeue;
ops->init = teql_qdisc_init;
ops->reset = teql_reset;
ops->destroy = teql_destroy;
ops->owner = THIS_MODULE;
// 虚拟网卡设备初始化
dev->open = teql_master_open;
dev->hard_start_xmit = teql_master_xmit;
dev->stop = teql_master_close;
dev->get_stats = teql_master_stats;
dev->change_mtu = teql_master_mtu;
dev->type = ARPHRD_VOID;
dev->mtu = 1500;
dev->tx_queue_len = 100;
// 缺省虚拟网卡是不响应ARP信息的
dev->flags = IFF_NOARP;
// 硬件头长度至少32, 也可能是48或96
dev->hard_header_len = LL_MAX_HEADER;
SET_MODULE_OWNER(dev);
}

// teql释放
static void __exit teql_exit(void)
{
struct teql_master *master, *nxt;
// 遍历虚拟网卡链表
list_for_each_entry_safe(master, nxt, &master_dev_list, master_list) {
// 从链表中断开
list_del(&master->master_list);
// 释放流控操作
unregister_qdisc(&master->qops);
// 从系统网卡链表断开
unregister_netdev(master->dev);
// 释放网卡
free_netdev(master->dev);
}
}

5.10.3 初始化

// 初始化是将物理网卡和虚拟网卡联系起来, 但不需要其他参数
// 在使用TC定义某物理网卡的流控为teql算法时调用
// 这里的sch应该是使用teql的ops的流控
static int teql_qdisc_init(struct Qdisc *sch, struct rtattr *opt)
{
// 物理网卡
struct net_device *dev = sch->dev;
// m->dev是虚拟网卡
struct teql_master *m = (struct teql_master*)sch->ops;
// teql私有数据
struct teql_sched_data *q = qdisc_priv(sch);
// 如果物理网卡硬件地址长度超过虚拟网卡硬件地址长度, 失败
// 虚拟网卡硬件地址应该能容纳物理网卡硬件地址
if (dev->hard_header_len > m->dev->hard_header_len)
return -EINVAL;
// 物理网卡和虚拟网卡相同, 回环了
if (m->dev == dev)
return -ELOOP;
// teql属主
q->m = m;
// 初始化数据包队列
skb_queue_head_init(&q->q);
// slave链表非空情况
if (m->slaves) {
// 虚拟网卡启动情况
if (m->dev->flags & IFF_UP) {
// 物理网卡不是PPP的虚拟网卡也应该不是PPP的
if ((m->dev->flags&IFF_POINTOPOINT && !(dev->flags&IFF_POINTOPOINT))
// 物理网卡不支持广播, 虚拟网卡也不应该支持广播
|| (m->dev->flags&IFF_BROADCAST && !(dev->flags&IFF_BROADCAST))
// 物理网卡不支持多播, 虚拟网卡也不应该支持多播
|| (m->dev->flags&IFF_MULTICAST && !(dev->flags&IFF_MULTICAST))
// 物理网卡的MTU应该不小于虚拟网卡的MTU
|| dev->mtu < m->dev->mtu)
return -EINVAL;
} else {
// 虚拟网卡没启动, 根据物理网卡属性调整虚拟网卡属性
if (!(dev->flags&IFF_POINTOPOINT))
m->dev->flags &= ~IFF_POINTOPOINT;
if (!(dev->flags&IFF_BROADCAST))
m->dev->flags &= ~IFF_BROADCAST;
if (!(dev->flags&IFF_MULTICAST))
m->dev->flags &= ~IFF_MULTICAST;
if (dev->mtu < m->dev->mtu)
m->dev->mtu = dev->mtu;
}
// 将当前流控节点插入链表
q->next = NEXT_SLAVE(m->slaves);
NEXT_SLAVE(m->slaves) = sch;
} else {
// slave链表空, 该sch作为链表头
q->next = sch;
m->slaves = sch;
// 初始化虚拟网卡MTU和网卡标志
m->dev->mtu = dev->mtu;
m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK);
}
return 0;
}

5.10.5 入队

static int
teql_enqueue(struct sk_buff *skb, struct Qdisc* sch)
{
// 物理网卡
struct net_device *dev = sch->dev;
// TEQL私有数据
struct teql_sched_data *q = qdisc_priv(sch);
// 将数据包挂接到TEQL队列数据包链表末尾
__skb_queue_tail(&q->q, skb);
// 队列长度不超过限制的情况下更新统计数据, 返回成功
if (q->q.qlen <= dev->tx_queue_len) {
sch->bstats.bytes += skb->len;
sch->bstats.packets++;
return 0;
}
// 队列长度过大, 从链表中断开数据包, 丢包
// 应该先检查队列长度, 这样就不用进行队列挂接操作了
__skb_unlink(skb, &q->q);
kfree_skb(skb);
sch->qstats.drops++;
return NET_XMIT_DROP;
}

5.10.6 重入队

static int
teql_requeue(struct sk_buff *skb, struct Qdisc* sch)
{
// teql私有数据
struct teql_sched_data *q = qdisc_priv(sch);
// 直接将数据挂接到链表头
__skb_queue_head(&q->q, skb);
sch->qstats.requeues++;
return 0;
}

5.10.7 出队

static struct sk_buff *
teql_dequeue(struct Qdisc* sch)
{
// teql私有数据
struct teql_sched_data *dat = qdisc_priv(sch);
struct sk_buff *skb;
// 数据包出队
skb = __skb_dequeue(&dat->q);
if (skb == NULL) {
// 如果没取到数据包的情况, 队列空
// 虚拟网卡teql*, dev和dev->qdisc->dev应该是相同的嘛
struct net_device *m = dat->m->dev->qdisc->dev;
if (m) {
dat->m->slaves = sch;
// 唤醒虚拟网卡队列
netif_wake_queue(m);
}
}
// 流控结构队列长度是teql队列长度和虚拟网卡队列的长度之和
// 虚拟网卡本身也带流控, 缺省是pfifo_fast, 可以修改为其他流控算法
sch->q.qlen = dat->q.qlen + dat->m->dev->qdisc->q.qlen;
return skb;
}


5.10.9 复位

static void
teql_reset(struct Qdisc* sch)
{
// teql私有数据
struct teql_sched_data *dat = qdisc_priv(sch);
// 清除teql内部队列数据
skb_queue_purge(&dat->q);
sch->q.qlen = 0;
// 释放邻居路由缓存
teql_neigh_release(xchg(&dat->ncache, NULL));
}

5.10.10 释放

static void
teql_destroy(struct Qdisc* sch)
{
struct Qdisc *q, *prev;
// teql私有数据
struct teql_sched_data *dat = qdisc_priv(sch);
struct teql_master *master = dat->m;
// 遍历从流控节点链表查找sch
if ((prev = master->slaves) != NULL) {
do {
q = NEXT_SLAVE(prev);
// 找到指定的流控节点
if (q == sch) {
// 从链表中断开
NEXT_SLAVE(prev) = NEXT_SLAVE(q);
if (q == master->slaves) {
// 如果是链表头, 进行相应链表头处理
master->slaves = NEXT_SLAVE(q);
if (q == master->slaves) {
// 如果链表中只有这个节点了, 复位设备的流控
master->slaves = NULL;
spin_lock_bh(&master->dev->queue_lock);
qdisc_reset(master->dev->qdisc);
spin_unlock_bh(&master->dev->queue_lock);
}
}
// 释放队列中数据包
skb_queue_purge(&dat->q);
// 释放邻居路由
teql_neigh_release(xchg(&dat->ncache, NULL));
break;
}

} while ((prev = q) != master->slaves);
}
}


5.10.13 teql网卡操作

// 打开网卡
static int teql_master_open(struct net_device *dev)
{
struct Qdisc * q;
struct teql_master *m = netdev_priv(dev);
// mtu初始值
int mtu = 0xFFFE;
// 初始网卡标志
unsigned flags = IFF_NOARP|IFF_MULTICAST;
// 如果没有从流控节点, 返回失败
// 也就是必须先用tc将某物理网卡的流控设置为teql0后才能让teql0网卡up
// tc qdisc add dev eth0 root teql0
// ifconfig teql0 up
if (m->slaves == NULL)
return -EUNATCH;
flags = FMASK;
q = m->slaves;
// 遍历所有从流控节点, 也就是附着于teql0各物理网卡的流控节点
// 用于调整虚拟网卡的MTU和标志参数
do {
// slave是物理网卡
struct net_device *slave = q->dev;
if (slave == NULL)
return -EUNATCH;
// mtu不能超过物理网卡的MTU
if (slave->mtu < mtu)
mtu = slave->mtu;
// 物理网卡的硬件地址长度不能超过虚拟网卡的硬件地址长度
if (slave->hard_header_len > LL_MAX_HEADER)
return -EINVAL;
/* If all the slaves are BROADCAST, master is BROADCAST
If all the slaves are PtP, master is PtP
Otherwise, master is NBMA.
*/
// 根据物理网卡标志调整虚拟网卡标志
if (!(slave->flags&IFF_POINTOPOINT))
flags &= ~IFF_POINTOPOINT;
if (!(slave->flags&IFF_BROADCAST))
flags &= ~IFF_BROADCAST;
if (!(slave->flags&IFF_MULTICAST))
flags &= ~IFF_MULTICAST;
} while ((q = NEXT_SLAVE(q)) != m->slaves);
// 设置虚拟网卡的MTU和标志
m->dev->mtu = mtu;
m->dev->flags = (m->dev->flags&~FMASK) | flags;
// 网卡队列启动
netif_start_queue(m->dev);
return 0;
}

// 网卡关闭, 应该是ifconfig teql0 down
static int teql_master_close(struct net_device *dev)
{
// 停止网卡队列
netif_stop_queue(dev);
return 0;
}
// 返回虚拟网卡的统计参数值
static struct net_device_stats *teql_master_stats(struct net_device *dev)
{
// teql参数
struct teql_master *m = netdev_priv(dev);
// 返回统计结构
return &m->stats;
}

// 调整虚拟网卡的MTU
static int teql_master_mtu(struct net_device *dev, int new_mtu)
{
struct teql_master *m = netdev_priv(dev);
struct Qdisc *q;
if (new_mtu < 68)
return -EINVAL;
q = m->slaves;
if (q) {
do {
if (new_mtu > q->dev->mtu)
return -EINVAL;
} while ((q=NEXT_SLAVE(q)) != m->slaves);
}
dev->mtu = new_mtu;
return 0;
}

// teql*网卡的hard_start_xmit函数, 实际的数据包发送处理
// dev为teql*网卡
// 发送是取一个物理网卡来实际发送数据
static int teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct teql_master *master = netdev_priv(dev);
struct Qdisc *start, *q;
int busy;
int nores;
int len = skb->len;
struct sk_buff *skb_res = NULL;
// 从流控节点链表的起始节点, 也就是各物理网卡的流控节点
start = master->slaves;
restart:
nores = 0;
busy = 0;
// 没有物理网卡, 丢包
if ((q = start) == NULL)
goto drop;
do {
// 实际的物理网卡
struct net_device *slave = q->dev;

// 如果该物理网卡的流控不是teql, 跳过, qdisc_sleeping保存有效流控
// 因为在网线拔掉后网卡的当前流控会更新为noop_disc
if (slave->qdisc_sleeping != q)
continue;
// 物理网卡的队列停或网卡没运行, 设置忙标志, 跳过
if (netif_queue_stopped(slave) || ! netif_running(slave)) {
busy = 1;
continue;
}
// 在该物理网卡进行邻居解析操作, ARP查询
switch (teql_resolve(skb, skb_res, slave)) {
case 0:
// 发送成功
if (netif_tx_trylock(slave)) {
// 调用物理网卡的hard_start_xmit函数真正地发送数据包
if (!netif_queue_stopped(slave) &&
slave->hard_start_xmit(skb, slave) == 0) {
// 发送成功
netif_tx_unlock(slave);
// 更新下一个物理网卡流控节点, 实现网卡间的流量均衡, 是轮询算法
master->slaves = NEXT_SLAVE(q);
netif_wake_queue(dev);
// 发送统计更新
master->stats.tx_packets++;
master->stats.tx_bytes += len;
return 0;
}
netif_tx_unlock(slave);
}
// 加锁失败, 设置忙标志
if (netif_queue_stopped(dev))
busy = 1;
break;
case 1:
// 该网卡发送失败, slave更新到下一个物理网卡, 下一个包将准备从下一个网卡发出
master->slaves = NEXT_SLAVE(q);
return 0;
default:
// 其他情况设置nores(no result)标志为1
nores = 1;
break;
}
// 解析操作失败, 恢复skb数据包为网络层次数据包, 因为上面可能已经把数据包
// push成以太包了
__skb_pull(skb, skb->nh.raw - skb->data);
} while ((q = NEXT_SLAVE(q)) != start);
if (nores && skb_res == NULL) {
// 如果没结果, 而且只进行了一次, 更新skb_res为当前skb, 重新发送
skb_res = skb;
goto restart;
}
if (busy) {
// 如果网卡忙, 停止队列, 返回1
netif_stop_queue(dev);
return 1;
}
// 发送失败, 丢包
master->stats.tx_errors++;
drop:
master->stats.tx_dropped++;
dev_kfree_skb(skb);
return 0;
}

// 实际调用的还是__teql_resolve
static __inline__ int
teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev)
{
if (dev->hard_header == NULL ||
skb->dst == NULL ||
skb->dst->neighbour == NULL)
return 0;
return __teql_resolve(skb, skb_res, dev);
}

static int
__teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev)
{
// teql私有数据
struct teql_sched_data *q = qdisc_priv(dev->qdisc);
// 网卡路由的邻居
struct neighbour *mn = skb->dst->neighbour;
// teql结构的邻居缓存
struct neighbour *n = q->ncache;
// 网卡邻居表为空, 返回失败
if (mn->tbl == NULL)
return -EINVAL;
// 如果teql的邻居表等于网卡邻居表
if (n && n->tbl == mn->tbl &&
memcmp(n->primary_key, mn->primary_key, mn->tbl->key_len) == 0) {
// 增加trql邻居计数
atomic_inc(&n->refcnt);
} else {
// 重新查询teql几个的邻居缓存
n = __neigh_lookup_errno(mn->tbl, mn->primary_key, dev);
if (IS_ERR(n))
return PTR_ERR(n);
}
if (neigh_event_send(n, skb_res) == 0) {
// 发送成功
int err;
read_lock(&n->lock);
err = dev->hard_header(skb, dev, ntohs(skb->protocol), n->ha, NULL, skb->len);
read_unlock(&n->lock);
if (err < 0) {
neigh_release(n);
return -EINVAL;
}
// 交换teql结构的cache为新的缓存n, 释放老缓存
teql_neigh_release(xchg(&q->ncache, n));
return 0;
}
// 发送失败, 释放缓存n
neigh_release(n);
// 如果skb_res为空, 准备重新再来
return (skb_res == NULL) ? -EAGAIN : 1;
}

...... 待续 ......
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值