LINUX TC:HTB相关源码

 (所有的这一切,写的不好,权当自己写着玩玩,聊以自慰-----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是不一样的, 系统的idchar 数组,htb对应的是”htb”,而这里的id对应的是Qdisc中的handle

再如:

tc qdisc add dev eth0 parent 1:10 handle 20pfifo limit 2

这里也创建了一个qdisc 它的被graftparent 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;//这里指明了parentid

       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_ops2、每个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

*Qdiscenqueuedequeue函数也是ops中的enqueuedequeue函数

*同时dev_queue也会赋值给Qdisc中的dev_queue,注意dev_queue包含了

*Qdisc所在的dev信息,即通过dev_queue就知道Qdiscdev

*/

       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;

}

 

你可能感兴趣的:(LINUX TC:HTB相关源码)