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字节?
阅读(4412) | 评论(0) | 转发(1) |