日期:2014-05-16 浏览次数:20607 次
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通知操作