4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
27 static void explain_class(void)
29 fprintf(stderr
, "Usage: ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]\n");
30 fprintf(stderr
, " [ minburst PKTS ] [ bounded ] [ isolated ]\n");
31 fprintf(stderr
, " [ allot BYTES ] [ mpu BYTES ] [ weight RATE ]\n");
32 fprintf(stderr
, " [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]\n");
33 fprintf(stderr
, " [ estimator INTERVAL TIME_CONSTANT ]\n");
34 fprintf(stderr
, " [ split CLASSID ] [ defmap MASK/CHANGE ]\n");
37 static void explain(void)
39 fprintf(stderr
, "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n");
40 fprintf(stderr
, " [ cell BYTES ] [ ewma LOG ]\n");
43 static void explain1(char *arg
)
45 fprintf(stderr
, "Illegal \"%s\"\n", arg
);
48 #define usage() return(-1)
50 static int cbq_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
, struct nlmsghdr
*n
)
53 struct tc_cbq_lssopt lss
;
55 unsigned mpu
=0, avpkt
=0, allot
=0;
60 memset(&lss
, 0, sizeof(lss
));
61 memset(&r
, 0, sizeof(r
));
64 if (strcmp(*argv
, "bandwidth") == 0 ||
65 strcmp(*argv
, "rate") == 0) {
67 if (get_rate(&r
.rate
, *argv
)) {
68 explain1("bandwidth");
71 } else if (strcmp(*argv
, "ewma") == 0) {
73 if (get_unsigned(&ewma_log
, *argv
, 0)) {
78 fprintf(stderr
, "ewma_log must be < 32\n");
81 } else if (strcmp(*argv
, "cell") == 0) {
85 if (get_size(&cell
, *argv
)) {
93 fprintf(stderr
, "cell must be 2^n\n");
97 } else if (strcmp(*argv
, "avpkt") == 0) {
99 if (get_size(&avpkt
, *argv
)) {
103 } else if (strcmp(*argv
, "mpu") == 0) {
105 if (get_size(&mpu
, *argv
)) {
109 } else if (strcmp(*argv
, "allot") == 0) {
111 /* Accept and ignore "allot" for backward compatibility */
112 if (get_size(&allot
, *argv
)) {
116 } else if (strcmp(*argv
, "help") == 0) {
120 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
127 /* OK. All options are parsed. */
130 fprintf(stderr
, "CBQ: bandwidth is required parameter.\n");
134 fprintf(stderr
, "CBQ: \"avpkt\" is required.\n");
137 if (allot
< (avpkt
*3)/2)
140 if ((cell_log
= tc_calc_rtable(r
.rate
, rtab
, cell_log
, allot
, mpu
)) < 0) {
141 fprintf(stderr
, "CBQ: failed to calculate rate table.\n");
144 r
.cell_log
= cell_log
;
148 ewma_log
= TC_CBQ_DEF_EWMA
;
149 lss
.ewma_log
= ewma_log
;
150 lss
.maxidle
= tc_cbq_calc_maxidle(r
.rate
, r
.rate
, avpkt
, lss
.ewma_log
, 0);
151 lss
.change
= TCF_CBQ_LSS_MAXIDLE
|TCF_CBQ_LSS_EWMA
|TCF_CBQ_LSS_AVPKT
;
154 tail
= NLMSG_TAIL(n
);
155 addattr_l(n
, 1024, TCA_OPTIONS
, NULL
, 0);
156 addattr_l(n
, 1024, TCA_CBQ_RATE
, &r
, sizeof(r
));
157 addattr_l(n
, 1024, TCA_CBQ_LSSOPT
, &lss
, sizeof(lss
));
158 addattr_l(n
, 3024, TCA_CBQ_RTAB
, rtab
, 1024);
161 for (i
=0; i
<256; i
++)
162 printf("%u ", rtab
[i
]);
165 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
169 static int cbq_parse_class_opt(struct qdisc_util
*qu
, int argc
, char **argv
, struct nlmsghdr
*n
)
171 int wrr_ok
=0, fopt_ok
=0;
172 struct tc_ratespec r
;
173 struct tc_cbq_lssopt lss
;
174 struct tc_cbq_wrropt wrr
;
175 struct tc_cbq_fopt fopt
;
176 struct tc_cbq_ovl ovl
;
182 unsigned minburst
=0, maxburst
=0;
185 memset(&r
, 0, sizeof(r
));
186 memset(&lss
, 0, sizeof(lss
));
187 memset(&wrr
, 0, sizeof(wrr
));
188 memset(&fopt
, 0, sizeof(fopt
));
189 memset(&ovl
, 0, sizeof(ovl
));
192 if (strcmp(*argv
, "rate") == 0) {
194 if (get_rate(&r
.rate
, *argv
)) {
198 } else if (strcmp(*argv
, "bandwidth") == 0) {
200 if (get_rate(&bndw
, *argv
)) {
201 explain1("bandwidth");
204 } else if (strcmp(*argv
, "minidle") == 0) {
206 if (get_u32(&lss
.minidle
, *argv
, 0)) {
210 lss
.change
|= TCF_CBQ_LSS_MINIDLE
;
211 } else if (strcmp(*argv
, "minburst") == 0) {
213 if (get_u32(&minburst
, *argv
, 0)) {
214 explain1("minburst");
217 lss
.change
|= TCF_CBQ_LSS_OFFTIME
;
218 } else if (strcmp(*argv
, "maxburst") == 0) {
220 if (get_u32(&maxburst
, *argv
, 0)) {
221 explain1("maxburst");
224 lss
.change
|= TCF_CBQ_LSS_MAXIDLE
;
225 } else if (strcmp(*argv
, "bounded") == 0) {
226 lss
.flags
|= TCF_CBQ_LSS_BOUNDED
;
227 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
228 } else if (strcmp(*argv
, "borrow") == 0) {
229 lss
.flags
&= ~TCF_CBQ_LSS_BOUNDED
;
230 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
231 } else if (strcmp(*argv
, "isolated") == 0) {
232 lss
.flags
|= TCF_CBQ_LSS_ISOLATED
;
233 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
234 } else if (strcmp(*argv
, "sharing") == 0) {
235 lss
.flags
&= ~TCF_CBQ_LSS_ISOLATED
;
236 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
237 } else if (strcmp(*argv
, "ewma") == 0) {
239 if (get_u32(&ewma_log
, *argv
, 0)) {
244 fprintf(stderr
, "ewma_log must be < 32\n");
247 lss
.change
|= TCF_CBQ_LSS_EWMA
;
248 } else if (strcmp(*argv
, "cell") == 0) {
252 if (get_size(&cell
, *argv
)) {
260 fprintf(stderr
, "cell must be 2^n\n");
264 } else if (strcmp(*argv
, "prio") == 0) {
267 if (get_u32(&prio
, *argv
, 0)) {
271 if (prio
> TC_CBQ_MAXPRIO
) {
272 fprintf(stderr
, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO
);
277 } else if (strcmp(*argv
, "allot") == 0) {
279 if (get_size(&wrr
.allot
, *argv
)) {
283 } else if (strcmp(*argv
, "avpkt") == 0) {
285 if (get_size(&lss
.avpkt
, *argv
)) {
289 lss
.change
|= TCF_CBQ_LSS_AVPKT
;
290 } else if (strcmp(*argv
, "mpu") == 0) {
292 if (get_size(&mpu
, *argv
)) {
296 } else if (strcmp(*argv
, "weight") == 0) {
298 if (get_size(&wrr
.weight
, *argv
)) {
303 } else if (strcmp(*argv
, "split") == 0) {
305 if (get_tc_classid(&fopt
.split
, *argv
)) {
306 fprintf(stderr
, "Invalid split node ID.\n");
310 } else if (strcmp(*argv
, "defmap") == 0) {
313 err
= sscanf(*argv
, "%08x/%08x", &fopt
.defmap
, &fopt
.defchange
);
315 fprintf(stderr
, "Invalid defmap, should be MASK32[/MASK]\n");
321 } else if (strcmp(*argv
, "help") == 0) {
325 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
332 /* OK. All options are parsed. */
334 /* 1. Prepare link sharing scheduler parameters */
336 unsigned pktsize
= wrr
.allot
;
337 if (wrr
.allot
< (lss
.avpkt
*3)/2)
338 wrr
.allot
= (lss
.avpkt
*3)/2;
339 if ((cell_log
= tc_calc_rtable(r
.rate
, rtab
, cell_log
, pktsize
, mpu
)) < 0) {
340 fprintf(stderr
, "CBQ: failed to calculate rate table.\n");
343 r
.cell_log
= cell_log
;
347 ewma_log
= TC_CBQ_DEF_EWMA
;
348 lss
.ewma_log
= ewma_log
;
349 if (lss
.change
&(TCF_CBQ_LSS_OFFTIME
|TCF_CBQ_LSS_MAXIDLE
)) {
350 if (lss
.avpkt
== 0) {
351 fprintf(stderr
, "CBQ: avpkt is required for max/minburst.\n");
354 if (bndw
==0 || r
.rate
== 0) {
355 fprintf(stderr
, "CBQ: bandwidth&rate are required for max/minburst.\n");
359 if (wrr
.priority
== 0 && (n
->nlmsg_flags
&NLM_F_EXCL
)) {
361 wrr
.priority
= TC_CBQ_MAXPRIO
;
363 wrr
.allot
= (lss
.avpkt
*3)/2;
367 wrr
.weight
= (wrr
.priority
== TC_CBQ_MAXPRIO
) ? 1 : r
.rate
;
368 if (wrr
.allot
== 0) {
369 fprintf(stderr
, "CBQ: \"allot\" is required to set WRR parameters.\n");
373 if (lss
.change
&TCF_CBQ_LSS_MAXIDLE
) {
374 lss
.maxidle
= tc_cbq_calc_maxidle(bndw
, r
.rate
, lss
.avpkt
, ewma_log
, maxburst
);
375 lss
.change
|= TCF_CBQ_LSS_MAXIDLE
;
376 lss
.change
|= TCF_CBQ_LSS_EWMA
|TCF_CBQ_LSS_AVPKT
;
378 if (lss
.change
&TCF_CBQ_LSS_OFFTIME
) {
379 lss
.offtime
= tc_cbq_calc_offtime(bndw
, r
.rate
, lss
.avpkt
, ewma_log
, minburst
);
380 lss
.change
|= TCF_CBQ_LSS_OFFTIME
;
381 lss
.change
|= TCF_CBQ_LSS_EWMA
|TCF_CBQ_LSS_AVPKT
;
383 if (lss
.change
&TCF_CBQ_LSS_MINIDLE
) {
384 lss
.minidle
<<= lss
.ewma_log
;
385 lss
.change
|= TCF_CBQ_LSS_EWMA
;
388 tail
= NLMSG_TAIL(n
);
389 addattr_l(n
, 1024, TCA_OPTIONS
, NULL
, 0);
391 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
392 addattr_l(n
, 1024, TCA_CBQ_LSSOPT
, &lss
, sizeof(lss
));
395 addattr_l(n
, 1024, TCA_CBQ_WRROPT
, &wrr
, sizeof(wrr
));
397 addattr_l(n
, 1024, TCA_CBQ_FOPT
, &fopt
, sizeof(fopt
));
399 addattr_l(n
, 1024, TCA_CBQ_RATE
, &r
, sizeof(r
));
400 addattr_l(n
, 3024, TCA_CBQ_RTAB
, rtab
, 1024);
403 for (i
=0; i
<256; i
++)
404 printf("%u ", rtab
[i
]);
408 tail
->rta_len
= (void *) NLMSG_TAIL(n
) - (void *) tail
;
413 static int cbq_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
415 struct rtattr
*tb
[TCA_CBQ_MAX
+1];
416 struct tc_ratespec
*r
= NULL
;
417 struct tc_cbq_lssopt
*lss
= NULL
;
418 struct tc_cbq_wrropt
*wrr
= NULL
;
419 struct tc_cbq_fopt
*fopt
= NULL
;
420 struct tc_cbq_ovl
*ovl
= NULL
;
425 memset(tb
, 0, sizeof(tb
));
426 parse_rtattr(tb
, TCA_CBQ_MAX
, RTA_DATA(opt
), RTA_PAYLOAD(opt
));
428 if (tb
[TCA_CBQ_RATE
]) {
429 if (RTA_PAYLOAD(tb
[TCA_CBQ_RATE
]) < sizeof(*r
))
430 fprintf(stderr
, "CBQ: too short rate opt\n");
432 r
= RTA_DATA(tb
[TCA_CBQ_RATE
]);
434 if (tb
[TCA_CBQ_LSSOPT
]) {
435 if (RTA_PAYLOAD(tb
[TCA_CBQ_LSSOPT
]) < sizeof(*lss
))
436 fprintf(stderr
, "CBQ: too short lss opt\n");
438 lss
= RTA_DATA(tb
[TCA_CBQ_LSSOPT
]);
440 if (tb
[TCA_CBQ_WRROPT
]) {
441 if (RTA_PAYLOAD(tb
[TCA_CBQ_WRROPT
]) < sizeof(*wrr
))
442 fprintf(stderr
, "CBQ: too short wrr opt\n");
444 wrr
= RTA_DATA(tb
[TCA_CBQ_WRROPT
]);
446 if (tb
[TCA_CBQ_FOPT
]) {
447 if (RTA_PAYLOAD(tb
[TCA_CBQ_FOPT
]) < sizeof(*fopt
))
448 fprintf(stderr
, "CBQ: too short fopt\n");
450 fopt
= RTA_DATA(tb
[TCA_CBQ_FOPT
]);
452 if (tb
[TCA_CBQ_OVL_STRATEGY
]) {
453 if (RTA_PAYLOAD(tb
[TCA_CBQ_OVL_STRATEGY
]) < sizeof(*ovl
))
454 fprintf(stderr
, "CBQ: too short overlimit strategy %u/%u\n",
455 (unsigned) RTA_PAYLOAD(tb
[TCA_CBQ_OVL_STRATEGY
]),
456 (unsigned) sizeof(*ovl
));
458 ovl
= RTA_DATA(tb
[TCA_CBQ_OVL_STRATEGY
]);
463 print_rate(buf
, sizeof(buf
), r
->rate
);
464 fprintf(f
, "rate %s ", buf
);
466 fprintf(f
, "cell %ub ", 1<<r
->cell_log
);
468 fprintf(f
, "mpu %ub ", r
->mpu
);
471 if (lss
&& lss
->flags
) {
474 if (lss
->flags
&TCF_CBQ_LSS_BOUNDED
) {
475 fprintf(f
, "bounded");
478 if (lss
->flags
&TCF_CBQ_LSS_ISOLATED
) {
481 fprintf(f
, "isolated");
486 if (wrr
->priority
!= TC_CBQ_MAXPRIO
)
487 fprintf(f
, "prio %u", wrr
->priority
);
489 fprintf(f
, "prio no-transmit");
492 fprintf(f
, "/%u ", wrr
->cpriority
);
493 if (wrr
->weight
!= 1) {
494 print_rate(buf
, sizeof(buf
), wrr
->weight
);
495 fprintf(f
, "weight %s ", buf
);
498 fprintf(f
, "allot %ub ", wrr
->allot
);
501 if (lss
&& show_details
) {
502 fprintf(f
, "\nlevel %u ewma %u avpkt %ub ", lss
->level
, lss
->ewma_log
, lss
->avpkt
);
504 fprintf(f
, "maxidle %luus ", tc_core_tick2usec(lss
->maxidle
>>lss
->ewma_log
));
506 fprintf(f
, "[%08x] ", lss
->maxidle
);
508 if (lss
->minidle
!=0x7fffffff) {
509 fprintf(f
, "minidle %luus ", tc_core_tick2usec(lss
->minidle
>>lss
->ewma_log
));
511 fprintf(f
, "[%08x] ", lss
->minidle
);
514 fprintf(f
, "offtime %luus ", tc_core_tick2usec(lss
->offtime
));
516 fprintf(f
, "[%08x] ", lss
->offtime
);
519 if (fopt
&& show_details
) {
521 print_tc_classid(buf
, sizeof(buf
), fopt
->split
);
522 fprintf(f
, "\nsplit %s ", buf
);
524 fprintf(f
, "defmap %08x", fopt
->defmap
);
530 static int cbq_print_xstats(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*xstats
)
532 struct tc_cbq_xstats
*st
;
537 if (RTA_PAYLOAD(xstats
) < sizeof(*st
))
540 st
= RTA_DATA(xstats
);
541 fprintf(f
, " borrowed %u overactions %u avgidle %g undertime %g", st
->borrows
,
542 st
->overactions
, (double)st
->avgidle
, (double)st
->undertime
);
546 struct qdisc_util cbq_qdisc_util
= {
548 .parse_qopt
= cbq_parse_opt
,
549 .print_qopt
= cbq_print_opt
,
550 .print_xstats
= cbq_print_xstats
,
551 .parse_copt
= cbq_parse_class_opt
,
552 .print_copt
= cbq_print_opt
,