Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1673903
  • 博文数量: 782
  • 博客积分: 2455
  • 博客等级: 大尉
  • 技术积分: 4140
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-06 21:37
个人简介

Linux ,c/c++, web,前端,php,js

文章分类

全部博文(782)

文章存档

2015年(8)

2014年(28)

2013年(110)

2012年(307)

2011年(329)

分类:

2012-09-19 17:29:41

原文地址:tbf一些分析和理解 作者:lwchsz


static int tbf_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
{
    int ok=0;
    struct tc_tbf_qopt opt;
    __u32 rtab[256];
    __u32 ptab[256];
    unsigned buffer=0, mtu=0, mpu=0, latency=0;
    int Rcell_log=-1, Pcell_log = -1;
    unsigned short overhead=0;
    unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
    struct rtattr *tail;

    memset(&opt, 0, sizeof(opt));

    while (argc > 0) {
        if (matches(*argv, "limit") == 0) {//如果有limit选项分析
            NEXT_ARG();
            if (opt.limit || latency) {
                fprintf(stderr, "Double \"limit/latency\" spec\n");
                return -1;
            }
            if (get_size(&opt.limit, *argv)) {//得到的limit以字节为单位
                explain1("limit");
                return -1;
            }
            ok++;
        } else if (matches(*argv, "latency") == 0) {
            NEXT_ARG();
            if (opt.limit || latency) {
                fprintf(stderr, "Double \"limit/latency\" spec\n");
                return -1;
            }
            if (get_time(&latency, *argv)) {//得到的latency以us为单位
                explain1("latency");
                return -1;
            }
            ok++;
        } else if (matches(*argv, "burst") == 0 ||
            strcmp(*argv, "buffer") == 0 ||
            strcmp(*argv, "maxburst") == 0) {
            NEXT_ARG();
            if (buffer) {
                fprintf(stderr, "Double \"buffer/burst\" spec\n");
                return -1;
            }
 //得到的buff以字节为单位,Rcell_log则表示速率信元大小的幂指数
            if (get_size_and_cell(&buffer, &Rcell_log, *argv) < 0) {
                explain1("buffer");
                return -1;
            }
            ok++;
        } else if (strcmp(*argv, "mtu") == 0 ||
               strcmp(*argv, "minburst") == 0) {
            NEXT_ARG();
            if (mtu) {
                fprintf(stderr, "Double \"mtu/minburst\" spec\n");
                return -1;
            }
 //得到的mtu以字节为单位,PRcell_log则表示P桶速率信元大小的幂指数
            if (get_size_and_cell(&mtu, &Pcell_log, *argv) < 0) {
                explain1("mtu");
                return -1;
            }
            ok++;
        } else if (strcmp(*argv, "mpu") == 0) {
            NEXT_ARG();
            if (mpu) {
                fprintf(stderr, "Double \"mpu\" spec\n");
                return -1;
            }
            if (get_size(&mpu, *argv)) {
                explain1("mpu");
                return -1;
            }
            ok++;
        } else if (strcmp(*argv, "rate") == 0) {
            NEXT_ARG();
            if (opt.rate.rate) {
                fprintf(stderr, "Double \"rate\" spec\n");
                return -1;
            }
            if (get_rate(&opt.rate.rate, *argv)) {//得到的速率以B/s为单位
                explain1("rate");
                return -1;
            }
            ok++;
        } else if (matches(*argv, "peakrate") == 0) {
            NEXT_ARG();
            if (opt.peakrate.rate) {
                fprintf(stderr, "Double \"peakrate\" spec\n");
                return -1;
            }
            if (get_rate(&opt.peakrate.rate, *argv)) {
                explain1("peakrate");
                return -1;
            }
            ok++;
        } else if (matches(*argv, "overhead") == 0) {
            NEXT_ARG();
            if (overhead) {
                fprintf(stderr, "Double \"overhead\" spec\n");
                return -1;
            }
            if (get_u16(&overhead, *argv, 10)) {
                explain1("overhead"); return -1;
            }
        } else if (matches(*argv, "linklayer") == 0) {//获得链路类型,默认是以太网
            NEXT_ARG();
            if (get_linklayer(&linklayer, *argv)) {
                explain1("linklayer"); return -1;
            }
        } else if (strcmp(*argv, "help") == 0) {
            explain();
            return -1;
        } else {
            fprintf(stderr, "What is \"%s\"?\n", *argv);
            explain();
            return -1;
        }
        argc--; argv++;
    }

    if (!ok) {
        explain();
        return -1;
    }
//速率和buffer是必须的,通过他们可以计算令牌桶以us为单位的大小。
    if (opt.rate.rate == 0 || !buffer) {
        fprintf(stderr, "Both \"rate\" and \"burst\" are required.\n");
        return -1;
    }
    if (opt.peakrate.rate) {
        if (!mtu) {
            fprintf(stderr, "\"mtu\" is required, if \"peakrate\" is requested.\n");
            return -1;
        }
    }

    if (opt.limit == 0 && latency == 0) {
        fprintf(stderr, "Either \"limit\" or \"latency\" are required.\n");
        return -1;
    }

    if (opt.limit == 0) {
        double lim = opt.rate.rate*(double)latency/TIME_UNITS_PER_SEC + buffer;
        if (opt.peakrate.rate) {
            double lim2 = opt.peakrate.rate*(double)latency/TIME_UNITS_PER_SEC + mtu;
            if (lim2 < lim)
                lim = lim2;
        }
        opt.limit = lim;
    }

    opt.rate.mpu      = mpu;
    opt.rate.overhead = overhead;
    if (tc_calc_rtable(&opt.rate, rtab, Rcell_log, mtu, linklayer) < 0) {//计算各个信元的发送时间,每个信元代表一段长度包的发送所需时间
        fprintf(stderr, "TBF: failed to calculate rate table.\n");
        return -1;
    }
    opt.buffer = tc_calc_xmittime(opt.rate.rate, buffer);//计算令牌桶发送时间

    if (opt.peakrate.rate) {
        opt.peakrate.mpu      = mpu;
        opt.peakrate.overhead = overhead;
        if (tc_calc_rtable(&opt.peakrate, ptab, Pcell_log, mtu, linklayer) < 0) {
            fprintf(stderr, "TBF: failed to calculate peak rate table.\n");
            return -1;
        }
        opt.mtu = tc_calc_xmittime(opt.peakrate.rate, mtu);//计算峰值速率下,mtu发送时间
    }
//把所需传给内核的参数放入相应的属性中传给内核
    tail = NLMSG_TAIL(n);
    addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
    addattr_l(n, 2024, TCA_TBF_PARMS, &opt, sizeof(opt));
    addattr_l(n, 3024, TCA_TBF_RTAB, rtab, 1024);
    if (opt.peakrate.rate)
        addattr_l(n, 4096, TCA_TBF_PTAB, ptab, 1024);
    tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
    return 0;
}

int get_size_and_cell(unsigned *size, int *cell_log, char *str)
{
    char * slash = strchr(str, '/');

    if (slash)
        *slash = 0;

    if (get_size(size, str))
        return -1;

    if (slash) {
        int cell;
        int i;

        if (get_integer(&cell, slash+1, 0))
            return -1;
        *slash = '/';

        for (i=0; i<32; i++) {
            if ((1<                *cell_log = i;
                return 0;
            }
        }
        return -1;
    }
    return 0;
}

/*
   rtab[pkt_len>>cell_log] = pkt_xmit_time
 */

int tc_calc_rtable(struct tc_ratespec *r, __u32 *rtab,
           int cell_log, unsigned mtu,
           enum link_layer linklayer)
{
    int i;
    unsigned sz;
    unsigned bps = r->rate;
    unsigned mpu = r->mpu;

    if (mtu == 0)//如果没有设置mtu,则默认为2047
        mtu = 2047;

    if (cell_log < 0) {//如果没有指定相信cell大小,则自动根据mtu值计算cell的幂指数
        cell_log = 0;
        while ((mtu >> cell_log) > 255)//为什么与255做比较,因为下面rtab是256个元素的数组
            cell_log++;
    }

    for (i=0; i<256; i++) {
        sz = tc_adjust_size((i + 1) << cell_log, mpu, linklayer);//计算各个信元最大字节数
        rtab[i] = tc_calc_xmittime(bps, sz);//计算该信元发送时间
    }

    r->cell_align=-1; // Due to the sz calc
    r->cell_log=cell_log;//记录信元的幂指数,这样内核可以根据cell_log把包长度转换为发送所需的时间,也就是令牌数,内核令牌桶是以us为单位
    return cell_log;
}

static double clock_factor = 1;
#define TIME_UNITS_PER_SEC    1000000

unsigned tc_core_time2ktime(unsigned time)
{
    return time * clock_factor;
}

unsigned tc_calc_xmittime(unsigned rate, unsigned size)
{
    return tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/rate));
}

unsigned tc_adjust_size(unsigned sz, unsigned mpu, enum link_layer linklayer)
{
    if (sz < mpu)
        sz = mpu;

    switch (linklayer) {
    case LINKLAYER_ATM:
        return tc_align_to_atm(sz);
    case LINKLAYER_ETHERNET:
    default:
        // No size adjustments on Ethernet
        return sz;
    }
}

测试心得:
1.当用 tc qdisc add dev eth1 handle 2 root  tbf burst 200kb rate 200kbps mtu 1540 latency 10 命令设置速率为200kbps时,在xp上用用tftp从linux下载文件,速率为180kbps左右,比较接近,但用ftp时发现下载速率仅仅为3kBps左右,非常低,通过测试确定,在tcp方式下传到接口层的skb->len经常为2920B,大于接口的最带允许包长2047,所以在tbf_enqueue经常被丢弃。这样需要等到tcp超时才能重传,因此速率很低。
2.通过tc qdisc add dev eth0 handle 2 root  tbf burst 200kb rate 200kbps mtu 8000 latency 10命令把接口mtu改成较大值,这样久可以解决tcp下载速率低的问题
3.疑问,既然eth0接口mtu值为1518,为什么tcp传下来分手聚集skb的长度大于1518字节?
阅读(1186) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~