日期:2014-05-16 浏览次数:20836 次
6. 类别操作
6.1 概述
类别操作是通过tc class命令来完成的, 当网卡使用的流控算法是可分类的(如HTB, CBQ等)时候使用, 功能是对Qdisc根节点进行划分, 定义出分类树, 同时可定义每个类别的流量限制参数,但具体那些数据属于哪一类则是通过tc filter命令来实现。
分类举例,以下命令在eth0上设置HTB流控,设置了3个类别,分别定义了各个类别的流量限制:
tc qdisc add dev eth0 root handle 1: htb default 12
tc class add dev eth0 parent 1: classid 1:1 htb rate 100kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 10kbps ceil 100kbps
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 60kbps ceil 100kbps
类别操作的具体实现实际是通过Qdisc的类别操作来完成的, 下面的处理仅仅是一个接口处理而已, 而具体的Qdisc类别操作函数已经在分析Qdisc时介绍了, 所以也没有引入新的数据结构。
6.2 初始化
前面5.15.1节中的初始化处理已经包括了类别的初始化:
......
// class操作, 也就是对应tc class add/delete/modify/get等操作
link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass;
link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass;
link_p[RTM_GETTCLASS-RTM_BASE].doit = tc_ctl_tclass;
link_p[RTM_GETTCLASS-RTM_BASE].dumpit = tc_dump_tclass;
......
6.3 类别控制操作
/* net/sched/sch_api.c */
static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
{
struct tcmsg *tcm = NLMSG_DATA(n);
struct rtattr **tca = arg;
struct net_device *dev;
struct Qdisc *q = NULL;
struct Qdisc_class_ops *cops;
unsigned long cl = 0;
unsigned long new_cl;
// parent id
u32 pid = tcm->tcm_parent;
// class id
u32 clid = tcm->tcm_handle;
// qdisc id: 初始化位类别id的高16位
u32 qid = TC_H_MAJ(clid);
int err;
// 根据TC信息中的网卡索引值查找网卡
if ((dev = __dev_get_by_index(tcm->tcm_ifindex)) == NULL)
return -ENODEV;
/*
// 以下是tc class的parent参数取值的说明
parent == TC_H_UNSPEC - unspecified parent.
parent == TC_H_ROOT - class is root, which has no parent.
parent == X:0 - parent is root class.
parent == X:Y - parent is a node in hierarchy.
parent == 0:Y - parent is X:Y, where X:0 is qdisc.
// 以下是tc class的classid参数取值的说明
handle == 0:0 - generate handle from kernel pool.
handle == 0:Y - class is X:Y, where X:0 is qdisc.
handle == X:Y - clear.
handle == X:0 - root class.
*/
/* Step 1. Determine qdisc handle X:0 */
if (pid != TC_H_ROOT) {
// parent id非根节点的情况
u32 qid1 = TC_H_MAJ(pid);
if (qid && qid1) {
/* If both majors are known, they must be identical. */
if (qid != qid1)
return -EINVAL;
} else if (qid1) {
qid = qid1;
} else if (qid == 0)
qid = dev->qdisc_sleeping->handle;
/* Now qid is genuine qdisc handle consistent
both with parent and child.
TC_H_MAJ(pid) still may be unspecified, complete it now.
*/
if (pid)
pid = TC_H_MAKE(qid, pid);
} else {
// 为根节点, 如果当前qid为0, 更新为设备的qdisc_sleeping的handle
if (qid == 0)
qid = dev->qdisc_sleeping->handle;
}
/* OK. Locate qdisc */
// 根据qid查找该dev上的Qdisc指针, 找不到的话返回失败
if ((q = qdisc_lookup(dev, qid)) == NULL)
return -ENOENT;
/* An check that it supports classes */
// 获取Qdisc的类别操作指针
cops = q->ops->cl_ops;
// 如果Qdisc是非分类的, 类别操作结构指针位空, 返回失败
if (cops == NULL)
return -EINVAL;
/* Now try to get class */
// 生成合法的类别ID
if (clid == 0) {
if (pid == TC_H_ROOT)
clid = qid;
} else
clid = TC_H_MAKE(qid, clid);
// 如果clid非0, 调用get函数获取该类别, 增加类别的引用计数
// cl虽然定义是unsigned long, 但实际是个指针的数值
if (clid)
cl = cops->get(q, clid);
if (cl == 0) {
// 类别为空
err = -ENOENT;
// 如果netlink命令不是新建类别的话, 返回错误
if (n->nlmsg_type != RTM_NEWTCLASS || !(n->nlmsg_flags&NLM_F_CREATE))
goto out;
} else {
// 获取类别成功, 根据netlink命令类型进行相关操作
switch (n->nlmsg_type) {
case RTM_NEWTCLASS:
// 新建class
err = -EEXIST;
// 如果设置了互斥标志, 返回错误, 因为现在该class已经存在
if (n->nlmsg_flags&NLM_F_EXCL)
goto out;
break;
case RTM_DELTCLASS:
// 删除class
err = cops->delete(q, cl);
if (err == 0)
tclass_notify(skb, n, q, cl, RTM_DELTCLASS);
goto out;
case RTM_GETTCLASS:
// 获取class信息, 进行class通知操作