Linux内核中流量控制(10)

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
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;
}

...... 待续 ......

猜你喜欢

转载自cxw06023273.iteye.com/blog/867332
今日推荐