免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1120 | 回复: 0
打印 上一主题 下一主题

Linux内核中流量控制(22) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-10-07 19:11 |只看该作者 |倒序浏览
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)
// 循环所有参数项
for (i=0; i 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) = 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
/* 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) 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 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) nlmsg_flags&NLM_F_ROOT) {
// 删除根节点操作
  if (tb[0] != NULL && tb[1] == NULL)
// 删除全部动作
   return tca_action_flush(tb[0], n, pid);
}
// 遍历所有参数
for (i=0; i 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)
  /* 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) 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 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) 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) {
  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 hmask + 1); i++) {
// 链表头
  p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
// 遍历链表
  for (; p; p = p->tcfc_next) {
   index++;
// 是否是要跳过的数
   if (index 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 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 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 tcfc_refcnt
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;
}
...... 待续 ......

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/12313/showart_396262.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP