日期:2014-05-16  浏览次数:20790 次

Linux内核中流量控制(12)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
5.11.3  HTB一些操作函数

5.11.3.1 转换函数

/* TODO: maybe compute rate when size is too large .. or drop ? */
// 将长度转换为令牌数
static inline long L2T(struct htb_class *cl, struct qdisc_rate_table *rate,
      int size)
{
// 根据大小计算合适的槽位
 int slot = size >> rate->rate.cell_log;
// 如果超过了255, 限制为255
 if (slot > 255) {
  cl->xstats.giants++;
  slot = 255;
 }
 return rate->data[slot];
}
 

// HTB哈希计算, 限制哈希结果小于16, 因为只有16个HASH表, 这个大小是定死的
static inline int htb_hash(u32 h)
{
#if HTB_HSIZE != 16
#error "Declare new hash for your HTB_HSIZE"
#endif
 h ^= h >> 8;  /* stolen from cbq_hash */
 h ^= h >> 4;
 return h & 0xf;
}

5.11.3.2 查询函数

/* find class in global hash table using given handle */
// 根据句柄handle查找HTB节点
static inline struct htb_class *htb_find(u32 handle, struct Qdisc *sch)
{
// HTB私有数据结构
 struct htb_sched *q = qdisc_priv(sch);
 struct hlist_node *p;
 struct htb_class *cl;
 if (TC_H_MAJ(handle) != sch->handle)
  return NULL;
// 根据句柄计算哈希值, 然后遍历该哈希链表
 hlist_for_each_entry(cl, p, q->hash + htb_hash(handle), hlist) {
// 查找类别ID和句柄handle相等的HTB节点返回
  if (cl->classid == handle)
   return cl;
 }
 return NULL;
}
 
5.11.3.3 分类函数

/**
 * htb_classify - classify a packet into class
 *
 * It returns NULL if the packet should be dropped or -1 if the packet
 * should be passed directly thru. In all other cases leaf class is returned.
 * We allow direct class selection by classid in priority. The we examine
 * filters in qdisc and in inner nodes (if higher filter points to the inner
 * node). If we end up with classid MAJOR:0 we enqueue the skb into special
 * internal fifo (direct). These packets then go directly thru. If we still
 * have no valid leaf we try to use MAJOR:default leaf. It still unsuccessfull
 * then finish and return direct queue.
 */
#define HTB_DIRECT (struct htb_class*)-1
// 获取HTB类别结构的ID
static inline u32 htb_classid(struct htb_class *cl)
{
// 如果类别结构有效(非空而且不是直接通过), 返回其类别ID, 否则返回TC_H_UNSPEC
// 表示没指定类别ID
 return (cl && cl != HTB_DIRECT) ? cl->classid : TC_H_UNSPEC;
}

// HTB分类操作, 对数据包进行分类, 然后根据类别进行相关操作
// 返回NULL表示没找到, 返回-1表示是直接通过(不分类)的数据包
static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
          int *qerr)
{
// HTB私有结构
 struct htb_sched *q = qdisc_priv(sch);
 struct htb_class *cl;
// 分类规则处理结果
 struct tcf_result res;
// 分类过滤规则表
 struct tcf_proto *tcf;
 int result;
 /* allow to select class by setting skb->priority to valid classid;
    note that nfmark can be used too by attaching filter fw with no
    rules in it */
// 如果数据包优先权值就等于流控节点和句柄handle, 属于根节点操作, 直接处理
 if (skb->priority == sch->handle)
  return HTB_DIRECT; /* X:0 (direct flow) selected */

// 查找和数据包优先权值对应的HTB叶子节点, 找到则返回
 if ((cl = htb_find(skb->priority, sch)) != NULL && cl->level == 0)
  return cl;

// 以下处理是没有找到和skb->priority直接对应的HTB叶子节点, 应该说实际应用中大部分
// 都是skb->priority为0的, 所以一般都会运行到这里
 *qerr = NET_XMIT_BYPASS;
 tcf = q->filter_list;
// 进行标准TC分类, 分类方法由TC命令定义的规则来实现
 while (tcf && (result = tc_classify(skb, tcf, &res)) >= 0) {
#ifdef CONFIG_NET_CLS_ACT
// 定义了可对分类结果进行动作的内核选项的情况
  switch (result) {
  case TC_ACT_QUEUED:
  case TC_ACT_STOLEN:
// 发送成功
   *qerr = NET_XMIT_SUCCESS;
// 丢包
  case TC_ACT_SHOT:
   return NULL;
  }
#elif defined(CONFIG_NET_CLS_POLICE)
// 没定义NET_CLS_ACT而定义了NET_CLS_POLICE的情况
// 如果分类结果是TC_POLICE_SHOT, 属于HTB直接处理
  if (result == TC_POLICE_SHOT)
   return HTB_DIRECT;
#endif
// 如果分类结果为空
  if ((cl = (void *)res.class) == NULL) {
// 如果分类结果的ID等于流控句柄, 直接处理
   if (res.classid == sch->handle)
    return HTB_DIRECT; /* X:0 (direct flow) */
// 再根据结果的类别ID查找HTB叶子节点, 找不到的话退出循环
   if ((cl = htb_find(res.classid, sch)) == NULL)
    break; /* filter selected invalid classid */
  }
// 分类找到的情况, 如果是叶子节点, 直接返回
  if (!cl->level)
   return cl; /* we hit leaf; re