(所有的这一切,写的不好,权当自己写着玩玩,聊以自慰-----csalp&cssalp)
2、 Qdisc
在linux中,向某个设备添加以qidsc的命令如下:
tc qdisc add dev DEV [parent qdisc_id root] [handle qdisc_id] qdisc [qdisc parameters] |
例如:
tc qdisc add dev eth0 root handle 1: htb
这里向eth0添加了一个htb,为root, 并且id=1
注意这里的id和系统的id是不一样的, 系统的id是char 数组,htb对应的是”htb”,而这里的id对应的是Qdisc中的handle。
再如:
tc qdisc add dev eth0 parent 1:10 handle 20:pfifo limit 2
这里也创建了一个qdisc, 它的被graft到parent id=1:10的点上,这个点可能是个qdisc,也可能是一个class。
(tc的具体介绍可以参见tc-how-to)
那么接下来看看系统是如何创建一个Qdisc的。
上面是关于Qdisc相关的操作,下面看看正主Qdisc的一些信息。
首先看看它的结构:
struct Qdisc { /* *enqueue&dequeue函数,enqueue成功是return 0 *dequeue成功时,返回要send的数据 */ int (*enqueue)(struct sk_buff *skb, struct Qdisc *dev); struct sk_buff * (*dequeue)(struct Qdisc *dev); unsigned flags; #define TCQ_F_BUILTIN 1 #define TCQ_F_THROTTLED 2 #define TCQ_F_INGRESS 4 #define TCQ_F_CAN_BYPASS 8 #define TCQ_F_MQROOT 16 #define TCQ_F_WARN_NONWC (1 << 16) int padded; /* *所以的qdisc的不同在它的Qdisc_ops不同, * 当Qdisc_ops *ops= htb_qdisc_ops *这时我们定义了一个HTB 的Qdics */
struct Qdisc_ops *ops; struct qdisc_size_table *stab; struct list_head list; u32 handle;//这里对应tc命令中的handle u32 parent;//这里指明了parent的id atomic_t refcnt; struct gnet_stats_rate_est rate_est;//用于rate的估计 int (*reshape_fail)(struct sk_buff *skb, struct Qdisc *q);
void *u32_node;
/* This field is deprecated, but it is still used by CBQ * and it will live until better solution will be invented. */ /* *qdisc使用双向链表,同样指向parent节点的指针 *和指向下一节点的next_sched指针 */ struct Qdisc *__parent; struct netdev_queue *dev_queue; struct Qdisc *next_sched; /* *gso_skb是与这个qdis相关的数据包, *它存放了由这个qdisc管理的所有数据 */ struct sk_buff *gso_skb; /* * For performance sake on SMP, we put highly modified fields at the end */ unsigned long state; struct sk_buff_head q; struct gnet_stats_basic_packed bstats; unsigned long __state; struct gnet_stats_queue qstats; struct rcu_head rcu_head; spinlock_t busylock; }; |
下面看看qdisc_create函数
在看create函数之前,要明白两点,1、所有的Qdisc的不同之处在于它的Qdisc_ops的不同,在前面注册Qdisc_ops部分,提到,如果创建的是HTB,那么Qdisc_ops一定是htb_qdisc_ops;2、每个qdisc都和特定的设备相关联,所有一个qdisc一定会找到一个dev,但是一个dev可能包含多个qdisc。
CODE:
static struct Qdisc * qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, struct Qdisc *p, u32 parent, u32 handle, struct nlattr **tca, int *errp) { /* *dev:创建的qdisc最终会关联到这个设备上 *tca:是找到想要的Qdisc_ops的关键 *handle:qdisc_id *parent:parent_id *p:parent */ int err; struct nlattr *kind = tca[TCA_KIND]; struct Qdisc *sch;//要创建的qdisc struct Qdisc_ops *ops; struct qdisc_size_table *stab; /* *首要的事情是找到想要的Qdisc_ops *函数qdiscc_lookup_ops通过kind来搜索qdisc_base中注册的所有qdisc_ops *直到找到为止,它会通过Qdisc_ops中的id来判断是否找到 */ ops = qdisc_lookup_ops(kind); #ifdef CONFIG_MODULES if (ops == NULL && kind != NULL) { /** *如果没有找到,但是确实存在,那么重新注册,安装这个moduel模块实现 */ char name[IFNAMSIZ]; if (nla_strlcpy(name, kind, IFNAMSIZ) < IFNAMSIZ) { /* 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. * We replay the request because the device may * go away in the mean time. */ rtnl_unlock(); request_module("sch_%s", name); rtnl_lock(); //ok, 安装这个moduel后,从新查找需要的Qdisc_ops ops = qdisc_lookup_ops(kind); if (ops != NULL) { /* We will try again qdisc_lookup_ops, * so don't keep a reference. */ module_put(ops->owner); err = -EAGAIN; goto err_out; } } } #endif
err = -ENOENT; if (ops == NULL) goto err_out; /** *到这里,找到了相应的Qdisc_ops, 那么就开始创建Qdisc了 *函数会将ops赋值给Qdisc中的ops *Qdisc的enqueue和dequeue函数也是ops中的enqueue和dequeue函数 *同时dev_queue也会赋值给Qdisc中的dev_queue,注意dev_queue包含了 *Qdisc所在的dev信息,即通过dev_queue就知道Qdisc的dev */ sch = qdisc_alloc(dev_queue, ops); if (IS_ERR(sch)) { err = PTR_ERR(sch); goto err_out2; } /** *初始化parent_id */ sch->parent = parent; /** * #define TC_H_UNSPEC (0U) *#define TC_H_ROOT (0xFFFFFFFFU) *#define TC_H_INGRESS (0xFFFFFFF1U) *0 4294967295 4294967281 *对于handle,要比parent要弹性些。Parent需要用户指定其正确性, *而handler系统会检查handle的值的有效性,当handle等于TC_H_INGRESS *或者为0时, 系统会数据分配一个handle:即qdisc_id *例如: Add:tc qdisc add dev eth0 root handle 1: htb default 12 Dump: tc qdisc ls dev eth0 Result: qdisc htb 1: r2q 10 default 12 direct_packets_stat 5
ADD: tc qdisc add dev eth0 root handle 0: htb default 12 DUMP: tc qdisc ls dev eth0 RESULT: qdisc htb 8002: r2q 10 default 12 direct_packets_stat 34 */ if (handle == TC_H_INGRESS) { sch->flags |= TCQ_F_INGRESS; handle = TC_H_MAKE(TC_H_INGRESS, 0); lockdep_set_class(qdisc_lock(sch), &qdisc_rx_lock); } else { if (handle == 0) { handle = qdisc_alloc_handle(dev); err = -ENOMEM; if (handle == 0) goto err_out3; } lockdep_set_class(qdisc_lock(sch), &qdisc_tx_lock); }
//将合法的handle赋值给Qdisc-> handle sch->handle = handle; /** *如果opt中不存在init函数,或者存在并且初始化的成功时 *然后做进一步的初始化,如果创建的是HTB, *那么init函数就是htb_init *最后返回创建的Qdisc */ if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) { if (tca[TCA_STAB]) { stab = qdisc_get_stab(tca[TCA_STAB]); if (IS_ERR(stab)) { err = PTR_ERR(stab); goto err_out4; } sch->stab = stab; } if (tca[TCA_RATE]) { spinlock_t *root_lock;
err = -EOPNOTSUPP; if (sch->flags & TCQ_F_MQROOT) goto err_out4;
if ((sch->parent != TC_H_ROOT) && !(sch->flags & TCQ_F_INGRESS) && (!p || !(p->flags & TCQ_F_MQROOT))) root_lock = qdisc_root_sleeping_lock(sch); else root_lock = qdisc_lock(sch);
err = gen_new_estimator(&sch->bstats, &sch->rate_est, root_lock, tca[TCA_RATE]); if (err) goto err_out4; }
qdisc_list_add(sch);
return sch; } err_out3: dev_put(dev); kfree((char *) sch - sch->padded); err_out2: module_put(ops->owner); err_out: *errp = err; return NULL;
err_out4: /* * Any broken qdiscs that would require a ops->reset() here? * The qdisc was never in action so it shouldn't be necessary. */ qdisc_put_stab(sch->stab); if (ops->destroy) ops->destroy(sch); goto err_out3; } |