`

Linux内核中流量控制(22)

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

8. action动作操作

8.1 概述

tc action命令是用来定义数据包进行最终处理方法的命令, 其功能就象netfilter的target一样, 需要内核定义NET_CLS_ACT选项。

动作处理的基本api在net/sched/act_api.c中定义, 而各种动作方法在net/sched/act_*.c中定义。

8.2 数据结构
/* include/net/act_api.h */

// TCF通用结构, 实际是用来描述动作的, 这是通用结构, 每种动作结构还有自己的
// 私有数据
struct tcf_common {
 struct tcf_common  *tcfc_next;
 u32    tcfc_index;
 int    tcfc_refcnt;
 int    tcfc_bindcnt;
 u32    tcfc_capab;
 int    tcfc_action;
 struct tcf_t   tcfc_tm;
 struct gnet_stats_basic  tcfc_bstats;
 struct gnet_stats_queue  tcfc_qstats;
 struct gnet_stats_rate_est tcfc_rate_est;
 spinlock_t   *tcfc_stats_lock;
 spinlock_t   tcfc_lock;
};
#define tcf_next common.tcfc_next
#define tcf_index common.tcfc_index
#define tcf_refcnt common.tcfc_refcnt
#define tcf_bindcnt common.tcfc_bindcnt
#define tcf_capab common.tcfc_capab
#define tcf_action common.tcfc_action
#define tcf_tm  common.tcfc_tm
#define tcf_bstats common.tcfc_bstats
#define tcf_qstats common.tcfc_qstats
#define tcf_rate_est common.tcfc_rate_est
#define tcf_stats_lock common.tcfc_stats_lock
#define tcf_lock common.tcfc_lock

// action操作结果, 其实这是一个中间数据结构, 在操作过程中产生的
// 命令执行完其实也就释放了没必要一直保存
struct tc_action {
// 私有数据
 void   *priv;
// 操作结构
 struct tc_action_ops *ops;
// 类型
 __u32   type; /* for backward compat(TCA_OLD_COMPAT) */
// 阶数
 __u32   order;
// 动作链表下一项
 struct tc_action *next;
};
#define TCA_CAP_NONE 0
// action操作结构, 实际就是定义目标操作, 通常每个匹配操作都由一个静态tcf_action_ops
// 结构定义, 作为一个内核模块, 初始化事登记系统的链表
struct tc_action_ops {
// 链表中的下一项
 struct tc_action_ops *next;
 struct tcf_hashinfo *hinfo;
// 名称
 char    kind[IFNAMSIZ];
 __u32   type; /* TBD to match kind */
 __u32  capab;  /* capabilities includes 4 bit version */
 struct module  *owner;
// 动作
 int     (*act)(struct sk_buff *, struct tc_action *, struct tcf_result *);
// 获取统计参数
 int     (*get_stats)(struct sk_buff *, struct tc_action *);
// 输出
 int     (*dump)(struct sk_buff *, struct tc_action *, int, int);
// 清除
 int     (*cleanup)(struct tc_action *, int bind);
// 查找
 int     (*lookup)(struct tc_action *, u32);
// 初始化
 int     (*init)(struct rtattr *, struct rtattr *, struct tc_action *, int , int);
// 遍历
 int     (*walk)(struct sk_buff *, struct netlink_callback *, int, struct tc_action *);
};
 

8.3 初始化

/* net/sched/act_api.c */
static int __init tc_action_init(void)
{
 struct rtnetlink_link *link_p = rtnetlink_links[PF_UNSPEC];
 if (link_p) {
// 定义action操作处理函数
// 关于action的增加/删除/获取等操作
  link_p[RTM_NEWACTION-RTM_BASE].doit = tc_ctl_action;
  link_p[RTM_DELACTION-RTM_BASE].doit = tc_ctl_action;
  link_p[RTM_GETACTION-RTM_BASE].doit = tc_ctl_action;
  link_p[RTM_GETACTION-RTM_BASE].dumpit = tc_dump_action;
 }
 return 0;
}

8.4 filter控制

相关函数调用关系:
tc_ctl_action
  -> tcf_action_add
    -> tcf_action_init
      -> tcf_action_init_1
        -> tc_lookup_action_n
        -> action_ops->init
    -> tcf_add_notify
      -> tcf_action_dump
        -> tcf_action_dump_1
          -> tcf_action_dump_old
            -> a->ops->dump
  -> tcf_action_gd
    -> tca_action_flush
      -> create_a
    -> tcf_action_get_1
    -> act_get_notify
      -> tca_get_fill
        -> tcf_action_dump
      -> rtnl_unicast
    -> tca_get_fill
    -> tcf_action_destroy

8.4.1 编辑操作
// 用于增加, 修改, 删除, 获取动作结构
static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
{
// 属性配置参数
 struct rtattr **tca = arg;
// 用户空间进程的pid
 u32 pid = skb ? NETLINK_CB(skb).pid : 0;
 int ret = 0, ovr = 0;
// 动作参数为空, 返回非法参数错误
 if (tca[TCA_ACT_TAB-1] == NULL) {
  printk("tc_ctl_action: received NO action attribs\n");
  return -EINVAL;
 }
 /* n->nlmsg_flags&NLM_F_CREATE
  * */
 switch (n->nlmsg_type) {
 case RTM_NEWACTION:
// 新建
  /* we are going to assume all other flags
   * imply create only if it doesnt exist
   * Note that CREATE | EXCL implies that
   * but since we want avoid ambiguity (eg when flags
   * is zero) then just set this
   */
// 是否是修改操作
  if (n->nlmsg_flags&NLM_F_REPLACE)
   ovr = 1;
replay:
  ret = tcf_action_add(tca[TCA_ACT_TAB-1], n, pid, ovr);
// 如果操作结果是重来, 重新进行添加操作, 在相关动作的内核模块没插入内核时会出现这种情况
  if (ret == -EAGAIN)
   goto replay;
  break;
 case RTM_DELACTION:
// 删除动作
  ret = tca_action_gd(tca[TCA_ACT_TAB-1], n, pid, RTM_DELACTION);
  break;
 case RTM_GETACTION:
// 获取动作
  ret = tca_action_gd(tca[TCA_ACT_TAB-1], n, pid, RTM_GETACTION);
  break;
 default:
  BUG();
 }
 return ret;
}

8.4.2 增加
// 添加动作
static int
tcf_action_add(struct rtattr *rta, struct nlmsghdr *n, u32 pid, int ovr)
{
 int ret = 0;
 struct tc_action *act;
 struct tc_action *a;
 u32 seq = n->nlmsg_seq;
// 根据选项信息进行TCF动作结构的初始化, 生成动作链表, 注意, 这个链表只是中间结果
 act = tcf_action_init(rta, NULL, NULL, ovr, 0, &ret);
 if (act == NULL)
  goto done;
 /* dump then free all the actions after update; inserted policy
  * stays intact
  * */
// 向pid进程发送netlink通知消息
 ret = tcf_add_notify(act, pid, seq, RTM_NEWACTION, n->nlmsg_flags);
// 释放动作链表中的所有节点, 因为是中间结果, 没必要保存的
 for (a = act; a; a = act) {
// 断开链表
  act = a->next;
// 释放动作的内存空间
  kfree(a);
 }
done:
 return ret;
}

// 动作初始化
struct tc_action *tcf_action_init(struct rtattr *rta, struct rtattr *est,
                                  char *name, int ovr, int bind, int *err)
{
 struct rtattr *tb[TCA_ACT_MAX_PRIO+1];
 struct tc_action *head = NULL, *act, *act_prev = NULL;
 int i;
// 解析动作参数, TCA_ACT_MAX_PRIO为32
 if (rtattr_parse_nested(tb, TCA_ACT_MAX_PRIO, rta) < 0) {
  *err = -EINVAL;
  return head;
 }
// 循环所有参数项
 for (i=0; i < TCA_ACT_MAX_PRIO && tb[i]; i++) {
// 初始化一个动作结构
  act = tcf_action_init_1(tb[i], est, name, ovr, bind, err);
// 失败的话返回
  if (act == NULL)
   goto err;
// 动作顺序
  act->order = i+1;
// 加入一head为头指针的动作链表
  if (head == NULL)
   head = act;
  else
   act_prev->next = act;
  act_prev = act;
 }
// 返回链表头指针
 return head;
err:
// 错误处理, 释放当前链表所有节点
 if (head != NULL)
  tcf_action_destroy(head, bind);
 return NULL;
}

// 初始化单个动作
struct tc_action *tcf_action_init_1(struct rtattr *rta, struct rtattr *est,
                                    char *name, int ovr, int bind, int *err)
{
 struct tc_action *a;
 struct tc_action_ops *a_o;
 char act_name[IFNAMSIZ];
 struct rtattr *tb[TCA_ACT_MAX+1];
 struct rtattr *kind;
 *err = -EINVAL;
 if (name == NULL) {
// 名称参数为空, 从配置参数中提取名称
  if (rtattr_parse_nested(tb, TCA_ACT_MAX, rta) < 0)
   goto err_out;
// 动作名称
  kind = tb[TCA_ACT_KIND-1];
  if (kind == NULL)
   goto err_out;
// 名称复制
  if (rtattr_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ)
   goto err_out;
 } else {
// 名称复制
  if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ)
   goto err_out;
 }
// 根据名称查找动作操作结构
 a_o = tc_lookup_action_n(act_name);
 if (a_o == NULL) {
#ifdef CONFIG_KMOD
// 如果没找到, 进行模块请求操作, 动态插入和名称对应的动作模块
  rtnl_unlock();
  request_module("act_%s", act_name);
  rtnl_lock();
// 重新查找动作操作结构
  a_o = tc_lookup_action_n(act_name);
  /* We dropped the RTNL semaphore in order to
   * perform the module load.  So, even if we
   * succeeded in loading the module we have to
   * tell the caller to replay the request.  We
   * indicate this using -EAGAIN.
   */
// 找到, 返回EAGAIN错误, 重新操作
  if (a_o != NULL) {
   *err = -EAGAIN;
   goto err_mod;
  }
#endif
// 否则操作失败, 没有该类型的动作操作
  *err = -ENOENT;
  goto err_out;
 }
 *err = -ENOMEM;
// 分配动作空间
 a = kzalloc(sizeof(*a), GFP_KERNEL);
 if (a == NULL)
  goto err_mod;
 /* backward compatibility for policer */
// 调用动作操作结构的初始化函数进行初始化
 if (name == NULL)
  *err = a_o->init(tb[TCA_ACT_OPTIONS-1], est, a, ovr, bind);
 else
  *err = a_o->init(rta, est, a, ovr, bind);
 if (*err < 0)
  goto err_free;
 /* module count goes up only when brand new policy is created
    if it exists and is only bound to in a_o->init() then
    ACT_P_CREATED is not returned (a zero is).
 */
// 如果初始化结果不是ACT_P_CREATED, 减少模块计数
 if (*err != ACT_P_CREATED)
  module_put(a_o->owner);
// 操作结构指针复制
 a->ops = a_o;
 *err = 0;
 return a;
err_free:
 kfree(a);
err_mod:
 module_put(a_o->owner);
err_out:
 return NULL;
}

// 增加操作通告
static int tcf_add_notify(struct tc_action *a, u32 pid, u32 seq, int event,
                          u16 flags)
{
 struct tcamsg *t;
 struct nlmsghdr *nlh;
 struct sk_buff *skb;
 struct rtattr *x;
 unsigned char *b;
 int err = 0;
// 分配数据包用于发送到用户空间进程
 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 if (!skb)
  return -ENOBUFS;
// 数据缓存起始点
 b = (unsigned char *)skb->tail;
// 构造netlink头
 nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags);
// TC动作信息头
 t = NLMSG_DATA(nlh);
 t->tca_family = AF_UNSPEC;
 t->tca__pad1 = 0;
 t->tca__pad2 = 0;
 x = (struct rtattr*) skb->tail;
 RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
// 输出动作参数
 if (tcf_action_dump(skb, a, 0, 0) < 0)
  goto rtattr_failure;
// rtnetlink信息长度
 x->rta_len = skb->tail - (u8*)x;
// netlink信息长度
 nlh->nlmsg_len = skb->tail - b;
 NETLINK_CB(skb).dst_group = RTNLGRP_TC;
// 发送消息
 err = rtnetlink_send(skb, pid, RTNLGRP_TC, flags&NLM_F_ECHO);
 if (err > 0)
  err = 0;
 return err;
rtattr_failure:
nlmsg_failure:
 kfree_skb(skb);
 return -1;
}
 
// 动作参数输出
int
tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref)
{
 struct tc_action *a;
 int err = -EINVAL;
 unsigned char *b = skb->tail;
 struct rtattr *r ;
// 遍历动作链表
 while ((a = act) != NULL) {
// 当前数据末尾
  r = (struct rtattr*) skb->tail;
  act = a->next;
  RTA_PUT(skb, a->order, 0, NULL);
// 填充动作结构参数
  err = tcf_action_dump_1(skb, a, bind, ref);
  if (err < 0)
   goto errout;
// 该rtnetlink消息长度
  r->rta_len = skb->tail - (u8*)r;
 }
 return 0;
rtattr_failure:
 err = -EINVAL;
errout:
 skb_trim(skb, b - skb->data);
 return err;
}

// 输出单一动作结构参数
int
tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
{
 int err = -EINVAL;
 unsigned char *b = skb->tail;
 struct rtattr *r;
// 如果没有动作操作结构或操作结构中无dump输出函数, 返回失败
 if (a->ops == NULL || a->ops->dump == NULL)
  return err;
 RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind);
// 输出统计参数
 if (tcf_action_copy_stats(skb, a, 0))
  goto rtattr_failure;
 r = (struct rtattr*) skb->tail;
 RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
// 动作参数输出
 if ((err = tcf_action_dump_old(skb, a, bind, ref)) > 0) {
  r->rta_len = skb->tail - (u8*)r;
  return err;
 }
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}

// 调用动作操作结构的dump函数输出动作参数信息
int
tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
{
 int err = -EINVAL;
 if (a->ops == NULL || a->ops->dump == NULL)
  return err;
 return a->ops->dump(skb, a, bind, ref);
}

8.4.3 获取/删除动作

static int
tca_action_gd(struct rtattr *rta, struct nlmsghdr *n, u32 pid, int event)
{
 int i, ret = 0;
 struct rtattr *tb[TCA_ACT_MAX_PRIO+1];
 struct tc_action *head = NULL, *act, *act_prev = NULL;
// 选项参数解析
 if (rtattr_parse_nested(tb, TCA_ACT_MAX_PRIO, rta) < 0)
  return -EINVAL;
 if (event == RTM_DELACTION && n->nlmsg_flags&NLM_F_ROOT) {
// 删除根节点操作
  if (tb[0] != NULL && tb[1] == NULL)
// 删除全部动作
   return tca_action_flush(tb[0], n, pid);
 }
// 遍历所有参数
 for (i=0; i < TCA_ACT_MAX_PRIO && tb[i]; i++) {
// 获取一个动作
  act = tcf_action_get_1(tb[i], n, pid, &ret);
  if (act == NULL)
   goto err;
  act->order = i+1;
// 添加到以head为头的链表中
  if (head == NULL)
   head = act;
  else
   act_prev->next = act;
  act_prev = act;
 }
 if (event == RTM_GETACTION)
// 如果是获取动作, 发送获取的动作链表到用户空间进程
  ret = act_get_notify(pid, n, head, event);
 else { /* delete */
// 否则是删除操作
  struct sk_buff *skb;
// 分配一个skb数据包准备发送删除通告
  skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
  if (!skb) {
   ret = -ENOBUFS;
   goto err;
  }
// TC动作信息填充到skb数据包中
  if (tca_get_fill(skb, head, pid, n->nlmsg_seq, 0, event,
                   0, 1) <= 0) {
// 填充失败返回
   kfree_skb(skb);
   ret = -EINVAL;
   goto err;
  }
  /* now do the delete */
// 释放动作, 注意这里的释放和下面的cleanup_a()不同, 操作更复杂
  tcf_action_destroy(head, 0);
// 发送数据包到pid进程
  ret = rtnetlink_send(skb, pid, RTNLGRP_TC,
                       n->nlmsg_flags&NLM_F_ECHO);
  if (ret > 0)
   return 0;
  return ret;
 }
err:
// 删除head链表, 只是简单释放动态内存空间
 cleanup_a(head);
 return ret;
}

// 删除全部动作
static int tca_action_flush(struct rtattr *rta, struct nlmsghdr *n, u32 pid)
{
 struct sk_buff *skb;
 unsigned char *b;
 struct nlmsghdr *nlh;
 struct tcamsg *t;
 struct netlink_callback dcb;
 struct rtattr *x;
 struct rtattr *tb[TCA_ACT_MAX+1];
 struct rtattr *kind;
// 先分配一个动作结构, 阶数为0, 这个节点只是用来辅助操作用的, 其实可以是一个结构
// 只是为了减少所用的堆栈空间
 struct tc_action *a = create_a(0);
 int err = -EINVAL;
 if (a == NULL) {
  printk("tca_action_flush: couldnt create tc_action\n");
  return err;
 }
// 分配数据包
 skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 if (!skb) {
  printk("tca_action_flush: failed skb alloc\n");
  kfree(a);
  return -ENOBUFS;
 }
// 数据缓冲区起始点
 b = (unsigned char *)skb->tail;
// 解析参数
 if (rtattr_parse_nested(tb, TCA_ACT_MAX, rta) < 0)
  goto err_out;
// 动作类型
 kind = tb[TCA_ACT_KIND-1];
// 根据名称查找动作类型
 a->ops = tc_lookup_action(kind);
 if (a->ops == NULL)
  goto err_out;
// 填充netlink数据头
 nlh = NLMSG_PUT(skb, pid, n->nlmsg_seq, RTM_DELACTION, sizeof(*t));
 t = NLMSG_DATA(nlh);
 t->tca_family = AF_UNSPEC;
 t->tca__pad1 = 0;
 t->tca__pad2 = 0;
 x = (struct rtattr *) skb->tail;
 RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
// 调用动作操作结构的遍历操作函数进行删除, 其实最终调用的是tcf_del_walker
 err = a->ops->walk(skb, &dcb, RTM_DELACTION, a);
 if (err < 0)
  goto rtattr_failure;
// 实际数据长度
 x->rta_len = skb->tail - (u8 *) x;
 nlh->nlmsg_len = skb->tail - b;
 nlh->nlmsg_flags |= NLM_F_ROOT;
 module_put(a->ops->owner);
// 释放a节点
 kfree(a);
// 发送数据包
 err = rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);
 if (err > 0)
  return 0;
 return err;
rtattr_failure:
nlmsg_failure:
 module_put(a->ops->owner);
err_out:
 kfree_skb(skb);
 kfree(a);
 return err;
}

// 填充动作参数到数据包
static int
tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 pid, u32 seq,
             u16 flags, int event, int bind, int ref)
{
 struct tcamsg *t;
 struct nlmsghdr *nlh;
 unsigned char *b = skb->tail;
 struct rtattr *x;

// 构造netlink数据头
 nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*t), flags);
// TC动作信息
 t = NLMSG_DATA(nlh);
 t->tca_family = AF_UNSPEC;
 t->tca__pad1 = 0;
 t->tca__pad2 = 0;
// 属性参数结构指针
 x = (struct rtattr*) skb->tail;
 RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
// 动作结构输出
 if (tcf_action_dump(skb, a, bind, ref) < 0)
  goto rtattr_failure;
// 数据长度
 x->rta_len = skb->tail - (u8*)x;
// netlink消息长度
 nlh->nlmsg_len = skb->tail - b;
 return skb->len;
rtattr_failure:
nlmsg_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}

// 动作结构释放
void tcf_action_destroy(struct tc_action *act, int bind)
{
 struct tc_action *a;
// 遍历链表
 for (a = act; a; a = act) {
  if (a->ops && a->ops->cleanup) {
// 执行操作结构中的清除操作,
   if (a->ops->cleanup(a, bind) == ACT_P_DELETED)
    module_put(a->ops->owner);
// 断开节点
   act = act->next;
// 释放节点
   kfree(a);
  } else { /*FIXME: Remove later - catch insertion bugs*/
// 无清除操作, 直接断开链表, 释放节点
   printk("tcf_action_destroy: BUG? destroying NULL ops\n");
   act = act->next;
   kfree(a);
  }
 }
}

8.5 filter输出

static int
tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
{
 struct nlmsghdr *nlh;
// 数据包缓冲区起始点
 unsigned char *b = skb->tail;
 struct rtattr *x;
 struct tc_action_ops *a_o;
// 这个动作参数只是一个辅助作用
 struct tc_action a;
 int ret = 0;
 struct tcamsg *t = (struct tcamsg *) NLMSG_DATA(cb->nlh);
// 查找动作类型名称
 struct rtattr *kind = find_dump_kind(cb->nlh);
 if (kind == NULL) {
  printk("tc_dump_action: action bad kind\n");
  return 0;
 }
// 根据类型名称查找动作操作结构
 a_o = tc_lookup_action(kind);
 if (a_o == NULL) {
  return 0;
 }
// 结构清零
 memset(&a, 0, sizeof(struct tc_action));
// 动作操作结构赋值
 a.ops = a_o;
// 如果该操作结构中没有遍历函数, 失败
 if (a_o->walk == NULL) {
  printk("tc_dump_action: %s !capable of dumping table\n", a_o->kind);
  goto rtattr_failure;
 }
// 填写netlink头数据
 nlh = NLMSG_PUT(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
                 cb->nlh->nlmsg_type, sizeof(*t));
 t = NLMSG_DATA(nlh);
 t->tca_family = AF_UNSPEC;
 t->tca__pad1 = 0;
 t->tca__pad2 = 0;
 x = (struct rtattr *) skb->tail;
 RTA_PUT(skb, TCA_ACT_TAB, 0, NULL);
// 遍历所有action节点执行GET操作
 ret = a_o->walk(skb, cb, RTM_GETACTION, &a);
 if (ret < 0)
  goto rtattr_failure;
// 操作成功, 设置数据包长度
 if (ret > 0) {
  x->rta_len = skb->tail - (u8 *) x;
  ret = skb->len;
 } else
  skb_trim(skb, (u8*)x - skb->data);
// 填充的数据长度
 nlh->nlmsg_len = skb->tail - b;
 if (NETLINK_CB(cb->skb).pid && ret)
  nlh->nlmsg_flags |= NLM_F_MULTI;
// 减少模块引用, 因为在tc_lookup_action时增加了引用
 module_put(a_o->owner);
 return skb->len;
rtattr_failure:
nlmsg_failure:
 module_put(a_o->owner);
 skb_trim(skb, b - skb->data);
 return skb->len;
}

8.6 其他相关函数

8.6.1 遍历

// 通用遍历函数, 在一些动作操作结构中的walk函数就用这个函数
// 不过只进行删除和输出两种遍历操作
int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb,
         int type, struct tc_action *a)
{
// 哈希信息结构
 struct tcf_hashinfo *hinfo = a->ops->hinfo;
// 根据动作类型进行不同的遍历操作
 if (type == RTM_DELACTION) {
// 删除遍历
  return tcf_del_walker(skb, a, hinfo);
 } else if (type == RTM_GETACTION) {
// 输出遍历
  return tcf_dump_walker(skb, cb, a, hinfo);
 } else {
// 其他是非法参数
  printk("tcf_generic_walker: unknown action %d\n", type);
  return -EINVAL;
 }
}

// 输出遍历
static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb,
      struct tc_action *a, struct tcf_hashinfo *hinfo)
{
 struct tcf_common *p;
 int err = 0, index = -1,i = 0, s_i = 0, n_i = 0;
 struct rtattr *r ;
 read_lock(hinfo->lock);
// 起始
 s_i = cb->args[0];
// 遍历所有哈希表
 for (i = 0; i < (hinfo->hmask + 1); i++) {
// 链表头
  p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
// 遍历链表
  for (; p; p = p->tcfc_next) {
   index++;
// 是否是要跳过的数
   if (index < s_i)
    continue;
// 将common节点赋值为动作结构的私有数据
   a->priv = p;
   a->order = n_i;
// 数据缓冲区起始点
   r = (struct rtattr*) skb->tail;
   RTA_PUT(skb, a->order, 0, NULL);
// 填写一个动作结构参数
   err = tcf_action_dump_1(skb, a, 0, 0);
   if (err < 0) {
/ 操作失败, 中断循环
    index--;
    skb_trim(skb, (u8*)r - skb->data);
    goto done;
   }
// 该信息长度
   r->rta_len = skb->tail - (u8*)r;
// 统计计数
   n_i++;
   if (n_i >= TCA_ACT_MAX_PRIO)
    goto done;
  }
 }
done:
 read_unlock(hinfo->lock);
// 增加统计数
 if (n_i)
  cb->args[0] += n_i;
 return n_i;
rtattr_failure:
 skb_trim(skb, (u8*)r - skb->data);
 goto done;
}

// 删除遍历
static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a,
     struct tcf_hashinfo *hinfo)
{
 struct tcf_common *p, *s_p;
 struct rtattr *r ;
 int i= 0, n_i = 0;
// 数据缓冲区起始点
 r = (struct rtattr*) skb->tail;
// 填写动作节点的order和名称
 RTA_PUT(skb, a->order, 0, NULL);
 RTA_PUT(skb, TCA_KIND, IFNAMSIZ, a->ops->kind);
// 遍历所有common哈希表
 for (i = 0; i < (hinfo->hmask + 1); i++) {
// 链表头
  p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
// 遍历链表
  while (p != NULL) {
   s_p = p->tcfc_next;
// 释放common节点
   if (ACT_P_DELETED == tcf_hash_release(p, 0, hinfo))
     module_put(a->ops->owner);
// 计数
   n_i++;
   p = s_p;
  }
 }
// 删除的节点数
 RTA_PUT(skb, TCA_FCNT, 4, &n_i);
// 数据长度
 r->rta_len = skb->tail - (u8*)r;
 return n_i;
rtattr_failure:
 skb_trim(skb, (u8*)r - skb->data);
 return -EINVAL;
}

8.6.2 tcf_common结构相关操作

tcf_common结构是作为动作结构的私有数据(priv), 所有common结构都保存为哈希表, 是TC动作的具体实现, 所有common结构节点是通过哈希表链接在一起的.

// 根据索引号index查找common节点
struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo)
{
 struct tcf_common *p;
 read_lock(hinfo->lock);
// 遍历相应的哈希链表
 for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p;
      p = p->tcfc_next) {
// 如果索引号相同则返回之
  if (p->tcfc_index == index)
   break;
 }
 read_unlock(hinfo->lock);
 return p;
}
EXPORT_SYMBOL(tcf_hash_lookup);

// 获取新索引值
u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo)
{
// 起始值
 u32 val = *idx_gen;
 do {
// 数组增加
  if (++val == 0)
   val = 1;
// 根据索引号查找common节点, 找到的话继续循环知道找到没有被使用的节点
// 这有可能会死循环, 就是已经分配了4G个common节点的情况
 } while (tcf_hash_lookup(val, hinfo));
// 返回找到的索引号
 return (*idx_gen = val);
}
EXPORT_SYMBOL(tcf_hash_new_index);

// 查找common结构作为动作结构的私有数据
// 返回1表操作成功, 0表示失败
int tcf_hash_search(struct tc_action *a, u32 index)
{
 struct tcf_hashinfo *hinfo = a->ops->hinfo;
// 根据索引值查找common结构
 struct tcf_common *p = tcf_hash_lookup(index, hinfo);
 if (p) {
// 找到, 将common结构作为动作结构的私有数据
  a->priv = p;
  return 1;
 }
 return 0;
}
EXPORT_SYMBOL(tcf_hash_search);

// 根据index获取common结构, 返回NULL表示失败
struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, int bind,
      struct tcf_hashinfo *hinfo)
{
 struct tcf_common *p = NULL;
// 如果索引号非0, 查找相应的common节点
 if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) {
// 找到
  if (bind) {
// 绑定操作, 增加引用数
   p->tcfc_bindcnt++;
   p->tcfc_refcnt++;
  }
// 将common结构作为动作结构的私有数据
  a->priv = p;
 }
// 返回common结构
 return p;
}
EXPORT_SYMBOL(tcf_hash_check);

// 生成新common节点
struct tcf_common *tcf_hash_create(u32 index, struct rtattr *est, struct tc_action *a, int size, int bind, u32 *idx_gen, struct tcf_hashinfo *hinfo)
{
// 分配空间
 struct tcf_common *p = kzalloc(size, GFP_KERNEL);
 if (unlikely(!p))
  return p;
// 索引数初始化为1
 p->tcfc_refcnt = 1;
// 如果要绑定, 绑定数也初始化为1
 if (bind)
  p->tcfc_bindcnt = 1;
 spin_lock_init(&p->tcfc_lock);
// 统计锁
 p->tcfc_stats_lock = &p->tcfc_lock;
// 索引数
 p->tcfc_index = index ? index : tcf_hash_new_index(idx_gen, hinfo);
// 生成时间
 p->tcfc_tm.install = jiffies;
// 上次使用时间
 p->tcfc_tm.lastuse = jiffies;
#ifdef CONFIG_NET_ESTIMATOR
// 初始化估计器结构
 if (est)
  gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est,
      p->tcfc_stats_lock, est);
#endif
// 将该common节点作为动作结构的私有数据
 a->priv = (void *) p;
 return p;
}
EXPORT_SYMBOL(tcf_hash_create);

// 插入common节点
void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo)
{
// 计算哈希数
 unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
 write_lock_bh(hinfo->lock);
// 将新节点插头到链表头作为头节点
 p->tcfc_next = hinfo->htab[h];
 hinfo->htab[h] = p;
 write_unlock_bh(hinfo->lock);
}
EXPORT_SYMBOL(tcf_hash_insert);

// 释放
void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
{
// 计算哈希数
 unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
 struct tcf_common **p1p;
// 遍历哈希数指定的链表
 for (p1p = &hinfo->htab[h]; *p1p; p1p = &(*p1p)->tcfc_next) {
// 比较结构地址
  if (*p1p == p) {
// 找到
   write_lock_bh(hinfo->lock);
// 从链表中断开
   *p1p = p->tcfc_next;
   write_unlock_bh(hinfo->lock);
#ifdef CONFIG_NET_ESTIMATOR
// 释放估计器
   gen_kill_estimator(&p->tcfc_bstats,
        &p->tcfc_rate_est);
#endif
// 释放空间
   kfree(p);
   return;
  }
 }
 BUG_TRAP(0);
}
EXPORT_SYMBOL(tcf_hash_destroy);

// TCF哈希信息释放
int tcf_hash_release(struct tcf_common *p, int bind,
       struct tcf_hashinfo *hinfo)
{
 int ret = 0;
 if (p) {
// 如果已经是绑定的, 减少绑定数
  if (bind)
   p->tcfc_bindcnt--;
// 引用数减
  p->tcfc_refcnt--;
// 如果绑定数和引用数都减到0了, 释放common节点
         if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) {
   tcf_hash_destroy(p, hinfo);
   ret = 1;
  }
 }
 return ret;
}

8.6.3 登记/撤销

// 登记
int tcf_register_action(struct tc_action_ops *act)
{
 struct tc_action_ops *a, **ap;
 write_lock(&act_mod_lock);
// 遍历action操作链表, 这个链表中的动作操作结构应该都是静态量, 不是动态分配的
 for (ap = &act_base; (a = *ap) != NULL; ap = &a->next) {
// 比较动作的类型或名称是否相同
  if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
// 相同的话表示已经登记过了
   write_unlock(&act_mod_lock);
   return -EEXIST;
  }
 }
// 将新节点添加到链表末尾
 act->next = NULL;
 *ap = act;
 write_unlock(&act_mod_lock);
 return 0;
}

// 撤销
int tcf_unregister_action(struct tc_action_ops *act)
{
 struct tc_action_ops *a, **ap;
 int err = -ENOENT;
 write_lock(&act_mod_lock);
// 遍历action操作链表, 直接比较地址
 for (ap = &act_base; (a = *ap) != NULL; ap = &a->next)
  if (a == act)
   break;
 if (a) {
// 如果找到
// 从链表中断开
  *ap = a->next;
  a->next = NULL;
  err = 0;
 }
 write_unlock(&act_mod_lock);
 return err;
}

...... 待续 ......
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics