msn: [email protected]
来源: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; } ...... 待续 ......