Chinaunix首页 | 论坛 | 博客
  • 博客访问: 637515
  • 博文数量: 404
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1237
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-03 10:45
文章分类

全部博文(404)

文章存档

2017年(1)

2016年(27)

2015年(39)

2014年(55)

2013年(66)

2012年(216)

分类:

2012-10-31 20:53:46

原文地址:qdisc执行分析 作者:lwchsz

  tc qdisc add dev eth0 handle 2 root  tbf burst 200kb rate 200kbps mtu 1540 latency 10

int tc_qdisc_modify(int cmd, unsigned flags, int argc, char **argv)
{
    struct qdisc_util *q = NULL;
    struct tc_estimator est;
    struct {
        struct tc_sizespec    szopts;
        __u16            *data;
    } stab;
    char  d[16];
    char  k[16];
    struct {
        struct nlmsghdr     n;
        struct tcmsg         t;
        char               buf[TCA_BUF_MAX];
    } req;

    memset(&req, 0, sizeof(req));
    memset(&stab, 0, sizeof(stab));
    memset(&est, 0, sizeof(est));
    memset(&d, 0, sizeof(d));
    memset(&k, 0, sizeof(k));

    req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
    req.n.nlmsg_flags = NLM_F_REQUEST|flags;
    req.n.nlmsg_type = cmd;
    req.t.tcm_family = AF_UNSPEC;

    while (argc > 0) {
        if (strcmp(*argv, "dev") == 0) {
            NEXT_ARG();
            if (d[0])
                duparg("dev", *argv);
            strncpy(d, *argv, sizeof(d)-1);//获取操作的设备eth0
        } else if (strcmp(*argv, "handle") == 0) {
            __u32 handle;
            if (req.t.tcm_handle)
                duparg("handle", *argv);
            NEXT_ARG();
            if (get_qdisc_handle(&handle, *argv))
                invarg(*argv, "invalid qdisc ID");
            req.t.tcm_handle = handle;//获取操作的句柄
        } else if (strcmp(*argv, "root") == 0) {
            if (req.t.tcm_parent) {
                fprintf(stderr, "Error: \"root\" is duplicate parent ID\n");
                return -1;
            }
            req.t.tcm_parent = TC_H_ROOT;//如果是根
#ifdef TC_H_INGRESS
        } else if (strcmp(*argv, "ingress") == 0) {
            if (req.t.tcm_parent) {
                fprintf(stderr, "Error: \"ingress\" is a duplicate parent ID\n");
                return -1;
            }
            req.t.tcm_parent = TC_H_INGRESS;//设置入口句柄
            strncpy(k, "ingress", sizeof(k)-1);
            q = get_qdisc_kind(k);
            req.t.tcm_handle = 0xffff0000;

            argc--; argv++;
            break;
#endif
        } else if (strcmp(*argv, "parent") == 0) {
            __u32 handle;
            NEXT_ARG();
            if (req.t.tcm_parent)
                duparg("parent", *argv);
            if (get_tc_classid(&handle, *argv))
                invarg(*argv, "invalid parent ID");
            req.t.tcm_parent = handle;//获取操作的父句柄
        } else if (matches(*argv, "estimator") == 0) {
            if (parse_estimator(&argc, &argv, &est))
                return -1;
        } else if (matches(*argv, "stab") == 0) {
            if (parse_size_table(&argc, &argv, &stab.szopts) < 0)
                return -1;
            continue;
        } else if (matches(*argv, "help") == 0) {
            usage();
        } else {
            strncpy(k, *argv, sizeof(k)-1);//获取操作的qdisc名称tbf

            q = get_qdisc_kind(k);
            argc--; argv++;
            break;//退出循环,然后把余下的选项交由相应的模块分析
        }
        argc--; argv++;
    }

    if (k[0])
        addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);//把qdisc名称放入TCA_KIND属性
    if (est.ewma_log)
        addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));

    if (q) {
        if (!q->parse_qopt) {
            fprintf(stderr, "qdisc '%s' does not support option parsing\n", k);
            return -1;
        }
        if (q->parse_qopt(q, argc, argv, &req.n))
            return 1;
    } else {
        if (argc) {
            if (matches(*argv, "help") == 0)
                usage();

            fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"tc qdisc help\".\n", *argv);
            return -1;
        }
    }

    if (check_size_table_opts(&stab.szopts)) {
        struct rtattr *tail;

        if (tc_calc_size_table(&stab.szopts, &stab.data) < 0) {
            fprintf(stderr, "failed to calculate size table.\n");
            return -1;
        }

        tail = NLMSG_TAIL(&req.n);
        addattr_l(&req.n, sizeof(req), TCA_STAB, NULL, 0);
        addattr_l(&req.n, sizeof(req), TCA_STAB_BASE, &stab.szopts,
              sizeof(stab.szopts));
        if (stab.data)
            addattr_l(&req.n, sizeof(req), TCA_STAB_DATA, stab.data,
                  stab.szopts.tsize * sizeof(__u16));
        tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail;
        if (stab.data)
            free(stab.data);
    }

    if (d[0])  {
        int idx;

         ll_init_map(&rth);

        if ((idx = ll_name_to_index(d)) == 0) {
            fprintf(stderr, "Cannot find device \"%s\"\n", d);
            return 1;
        }
        req.t.tcm_ifindex = idx;//获取操作设备的ifindex
    }
//把操作信息通过rtnetlink发给内核进行
     if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
        return 2;

    return 0;
}

内核收到后调用下面函数
static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
{
    struct tcmsg *tcm;
    struct rtattr **tca;
    struct net_device *dev;
    u32 clid;
    struct Qdisc *q, *p;
    int err;

replay:
    /* Reinit, just in case something touches this. */
    tcm = NLMSG_DATA(n);
    tca = arg;
    clid = tcm->tcm_parent;//获取操作的父句柄
    q = p = NULL;

    if ((dev = __dev_get_by_index(tcm->tcm_ifindex)) == NULL)//根据ifindex取得操作的设备
        return -ENODEV;

    if (clid) {
        if (clid != TC_H_ROOT) {//如果不是root
            if (clid != TC_H_INGRESS) {//如果不是NGRESS,查找对于的qdisc
                if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL)
                    return -ENOENT;
                q = qdisc_leaf(p, clid);//获取操作的qdisc
            } else { /*ingress */
                q = dev->qdisc_ingress;
            }
        } else {
            q = dev->qdisc_sleeping;//如果是根,则把qdisc_sleeping复制给q
        }

        /* It may be default qdisc, ignore it */
        if (q && q->handle == 0)如果是默认的pfifo_fast
            q = NULL;
//如果q为空,tcm->tcm_handle为0没有设置,或者,q->handle != tcm->tcm_handle
        if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) {
            if (tcm->tcm_handle) {//如果设置了handle
                if (q && !(n->nlmsg_flags&NLM_F_REPLACE))//如果q非空且不是替换
                    return -EEXIST;
                if (TC_H_MIN(tcm->tcm_handle))//判断handle合法性
                    return -EINVAL;
                if ((q = qdisc_lookup(dev, tcm->tcm_handle)) == NULL)//查找对应handle,不存在创建
                    goto create_n_graft;
                if (n->nlmsg_flags&NLM_F_EXCL)//找到但设置了NLM_F_EXCL排斥标志, 返回对象存在错误
                    return -EEXIST;
     //比较TC命令中的算法名称和Qdisc名称算法相同
                if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
                    return -EINVAL;
                if (q == p ||
                    (p && check_loop(q, p, 0)))
                    return -ELOOP;
                atomic_inc(&q->refcnt);
                goto graft;
            } else {
                if (q == NULL)
                    goto create_n_graft;

                /* This magic test requires explanation.
                 *
                 *   We know, that some child q is already
                 *   attached to this parent and have choice:
                 *   either to change it or to create/graft new one.
                 *
                 *   1. We are allowed to create/graft only
                 *   if CREATE and REPLACE flags are set.
                 *
                 *   2. If EXCL is set, requestor wanted to say,
                 *   that qdisc tcm_handle is not expected
                 *   to exist, so that we choose create/graft too.
                 *
                 *   3. The last case is when no flags are set.
                 *   Alas, it is sort of hole in API, we
                 *   cannot decide what to do unambiguously.
                 *   For now we select create/graft, if
                 *   user gave KIND, which does not match existing.
                 */
                if ((n->nlmsg_flags&NLM_F_CREATE) &&
                    (n->nlmsg_flags&NLM_F_REPLACE) &&
                    ((n->nlmsg_flags&NLM_F_EXCL) ||
                     (tca[TCA_KIND-1] &&
                      rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))))
                    goto create_n_graft;
            }
        }
    } else {//上面条件不成立,实际上是change命令
        if (!tcm->tcm_handle)
            return -EINVAL;
        q = qdisc_lookup(dev, tcm->tcm_handle);
    }

    /* Change qdisc parameters */
    if (q == NULL)
        return -ENOENT;
    if (n->nlmsg_flags&NLM_F_EXCL)
        return -EEXIST;
    if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id))
        return -EINVAL;
    err = qdisc_change(q, tca);
    if (err == 0)
        qdisc_notify(skb, n, clid, NULL, q);
    return err;

create_n_graft:
    if (!(n->nlmsg_flags&NLM_F_CREATE))
        return -ENOENT;
    if (clid == TC_H_INGRESS)
        q = qdisc_create(dev, tcm->tcm_parent, tca, &err);//创建qdisc
        else
        q = qdisc_create(dev, tcm->tcm_handle, tca, &err);
    if (q == NULL) {
        if (err == -EAGAIN)
            goto replay;
        return err;
    }

graft:
    if (1) {
        struct Qdisc *old_q = NULL;
        err = qdisc_graft(dev, p, clid, q, &old_q);//把创建的qdisc 移植相应节点
        if (err) {
            if (q) {
                spin_lock_bh(&dev->queue_lock);
                qdisc_destroy(q);
                spin_unlock_bh(&dev->queue_lock);
            }
            return err;
        }
        qdisc_notify(skb, n, clid, old_q, q);
        if (old_q) {
            spin_lock_bh(&dev->queue_lock);
            qdisc_destroy(old_q);
            spin_unlock_bh(&dev->queue_lock);
        }
    }
    return 0;
}
阅读(872) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~