LINUX TC:HTB相关源码 收藏
HTB(hierarchy token buffer)是linux tc(traffic control)模块中的排队队列的一种。它的配置比CBQ要简单.同时实现功能也很强大。下面,就来看看,HTB在linux中的源码。
1、 Qdisc_ops的注册
先从module_init函数看起(遵从fudan_abc的建议)
static int __init htb_module_init(void)
{
return register_qdisc(&htb_qdisc_ops);
}
上面的code会调用register_qdisc函数,将htb_qdisc_ops注册到系统中,那么htb_qdisc_ops包含了那些内容:
static struct Qdisc_ops htb_qdisc_ops __read_mostly = {
.cl_ops = &htb_class_ops,
.id = "htb",
.priv_size = sizeof(struct htb_sched),
.enqueue = htb_enqueue,
.dequeue = htb_dequeue,
.peek = qdisc_peek_dequeued,
.drop = htb_drop,
.init = htb_init,
.reset = htb_reset,
.destroy = htb_destroy,
.dump = htb_dump,
.owner = THIS_MODULE,
};
可以看出,htb_qdisc_ops其实就是注册了htb管理queue的函数,最重要的莫过于:enqueue 和dequeue函数,它们作用如同它们的名字一样。那么到底将htb_qdisc_ops注册到那了呢?
这就要看看register函数了
static struct Qdisc_ops *qdisc_base;
/*
*qdisc_base 就是系统维护所以qdisc所使用的变量,系统中的所有的qdisc都要
*注册到这变量变量中
*在struct Qdisc_ops中,包含了成员(struct Qdisc_ops *)next
*也就是,所以的qdisc是以链表的形式存在的
*/
int register_qdisc(struct Qdisc_ops *qops)
{
struct Qdisc_ops *q, **qp;
int rc = -EEXIST;
write_lock(&qdisc_mod_lock);
/*
*首先,检测这个qdisc是否已经注册过了,这是通过比较id实现的,
*id的类型是char 数组:char id[IFNAMSIZ];IFNAMESIZ=16
*htb的id=”htb”
*/
for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next)
if (!strcmp(qops->id, q->id))
goto out;
/*
*然后检测ops中的enqueue、dequeue、peek函数,
*如果这些函数都没有被初始化,将使用noop_qdisc_ops函数来初始化
*noop_qdisc_ops也是Qdisc_ops结构,
*它的作用就像是定义了Qdisc_ops的默认值
*/
if (qops->enqueue == NULL)
qops->enqueue = noop_qdisc_ops.enqueue;
if (qops->peek == NULL) {
if (qops->dequeue == NULL)
qops->peek = noop_qdisc_ops.peek;
else
goto out_einval;
}
if (qops->dequeue == NULL)
qops->dequeue = noop_qdisc_ops.dequeue;
/*
*然后检测cl_ops成员。
*cl_ops是结构Qdisc_class_ops,
*它定义了用于管理挂载到这个qdisc下的所有class(或者qdisc)
*/
if (qops->cl_ops) {
const struct Qdisc_class_ops *cops = qops->cl_ops;
if (!(cops->get && cops->put && cops->walk && cops->leaf))
goto out_einval;
if (cops->tcf_chain && !(cops->bind_tcf && cops->unbind_tcf))
goto out_einval;
}
/*
*最后将新的qops插入到链表的尾部:*qp = qops;
*这样就注册完成了
*/
qops->next = NULL;
*qp = qops;
rc = 0;
out:
write_unlock(&qdisc_mod_lock);
return rc;
out_einval:
rc = -EINVAL;
goto out;
}
EXPORT_SYMBOL(register_qdisc);
Qdisc_class_ops是管理这个tree的,那么看看htb的cl_ops有哪些函数:
static const struct Qdisc_class_ops htb_class_ops = {
.graft = htb_graft,
.leaf = htb_leaf,
.qlen_notify = htb_qlen_notify,
.get = htb_get,
.put = htb_put,
.change = htb_change_class,
.delete = htb_delete,
.walk = htb_walk,
.tcf_chain = htb_find_tcf,
.bind_tcf = htb_bind_filter,
.unbind_tcf = htb_unbind_filter,
.dump = htb_dump_class,
.dump_stats = htb_dump_class_stats,
};
我们知道,tc qdisc命令添加qdisc到某个设备后,为了对数据包进行分类,需要使用tc filter 来添加fitler到某个qdisc, 当数据包来时,通过fitler来区分数据包,并转发到不同的subqdisc 或者subclass。而绑定fitler都是通过函数:bind_tcf来实现的。