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>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
26 static void explain_class(void)
29 "Usage: ... cbq bandwidth BPS rate BPS maxburst PKTS [ avpkt BYTES ]\n"
30 " [ minburst PKTS ] [ bounded ] [ isolated ]\n"
31 " [ allot BYTES ] [ mpu BYTES ] [ weight RATE ]\n"
32 " [ prio NUMBER ] [ cell BYTES ] [ ewma LOG ]\n"
33 " [ estimator INTERVAL TIME_CONSTANT ]\n"
34 " [ split CLASSID ] [ defmap MASK/CHANGE ]\n"
35 " [ overhead BYTES ] [ linklayer TYPE ]\n");
38 static void explain(void)
41 "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n"
42 " [ cell BYTES ] [ ewma LOG ]\n");
45 static void explain1(char *arg
)
47 fprintf(stderr
, "Illegal \"%s\"\n", arg
);
51 static int cbq_parse_opt(struct qdisc_util
*qu
, int argc
, char **argv
, struct nlmsghdr
*n
, const char *dev
)
53 struct tc_ratespec r
= {};
54 struct tc_cbq_lssopt lss
= {};
56 unsigned mpu
= 0, avpkt
= 0, allot
= 0;
57 unsigned short overhead
= 0;
58 unsigned int linklayer
= LINKLAYER_ETHERNET
; /* Assume ethernet */
64 if (matches(*argv
, "bandwidth") == 0 ||
65 matches(*argv
, "rate") == 0) {
67 if (strchr(*argv
, '%')) {
68 if (get_percent_rate(&r
.rate
, *argv
, dev
)) {
69 explain1("bandwidth");
72 } else if (get_rate(&r
.rate
, *argv
)) {
73 explain1("bandwidth");
76 } else if (matches(*argv
, "ewma") == 0) {
78 if (get_integer(&ewma_log
, *argv
, 0)) {
83 fprintf(stderr
, "ewma_log must be < 32\n");
86 } else if (matches(*argv
, "cell") == 0) {
91 if (get_size(&cell
, *argv
)) {
95 for (i
= 0; i
< 32; i
++)
99 fprintf(stderr
, "cell must be 2^n\n");
103 } else if (matches(*argv
, "avpkt") == 0) {
105 if (get_size(&avpkt
, *argv
)) {
109 } else if (matches(*argv
, "mpu") == 0) {
111 if (get_size(&mpu
, *argv
)) {
115 } else if (matches(*argv
, "allot") == 0) {
117 /* Accept and ignore "allot" for backward compatibility */
118 if (get_size(&allot
, *argv
)) {
122 } else if (matches(*argv
, "overhead") == 0) {
124 if (get_u16(&overhead
, *argv
, 10)) {
125 explain1("overhead"); return -1;
127 } else if (matches(*argv
, "linklayer") == 0) {
129 if (get_linklayer(&linklayer
, *argv
)) {
130 explain1("linklayer"); return -1;
132 } else if (matches(*argv
, "help") == 0) {
136 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
143 /* OK. All options are parsed. */
146 fprintf(stderr
, "CBQ: bandwidth is required parameter.\n");
150 fprintf(stderr
, "CBQ: \"avpkt\" is required.\n");
153 if (allot
< (avpkt
*3)/2)
157 r
.overhead
= overhead
;
158 if (tc_calc_rtable(&r
, rtab
, cell_log
, allot
, linklayer
) < 0) {
159 fprintf(stderr
, "CBQ: failed to calculate rate table.\n");
164 ewma_log
= TC_CBQ_DEF_EWMA
;
165 lss
.ewma_log
= ewma_log
;
166 lss
.maxidle
= tc_calc_xmittime(r
.rate
, avpkt
);
167 lss
.change
= TCF_CBQ_LSS_MAXIDLE
|TCF_CBQ_LSS_EWMA
|TCF_CBQ_LSS_AVPKT
;
170 tail
= addattr_nest(n
, 1024, TCA_OPTIONS
);
171 addattr_l(n
, 1024, TCA_CBQ_RATE
, &r
, sizeof(r
));
172 addattr_l(n
, 1024, TCA_CBQ_LSSOPT
, &lss
, sizeof(lss
));
173 addattr_l(n
, 3024, TCA_CBQ_RTAB
, rtab
, 1024);
177 for (i
= 0; i
< 256; i
++)
178 printf("%u ", rtab
[i
]);
181 addattr_nest_end(n
, tail
);
185 static int cbq_parse_class_opt(struct qdisc_util
*qu
, int argc
, char **argv
, struct nlmsghdr
*n
, const char *dev
)
187 int wrr_ok
= 0, fopt_ok
= 0;
188 struct tc_ratespec r
= {};
189 struct tc_cbq_lssopt lss
= {};
190 struct tc_cbq_wrropt wrr
= {};
191 struct tc_cbq_fopt fopt
= {};
196 unsigned int bndw
= 0;
197 unsigned minburst
= 0, maxburst
= 0;
198 unsigned short overhead
= 0;
199 unsigned int linklayer
= LINKLAYER_ETHERNET
; /* Assume ethernet */
203 if (matches(*argv
, "rate") == 0) {
205 if (strchr(*argv
, '%')) {
206 if (get_percent_rate(&r
.rate
, *argv
, dev
)) {
210 } else if (get_rate(&r
.rate
, *argv
)) {
214 } else if (matches(*argv
, "bandwidth") == 0) {
216 if (strchr(*argv
, '%')) {
217 if (get_percent_rate(&bndw
, *argv
, dev
)) {
218 explain1("bandwidth");
221 } else if (get_rate(&bndw
, *argv
)) {
222 explain1("bandwidth");
225 } else if (matches(*argv
, "minidle") == 0) {
227 if (get_u32(&lss
.minidle
, *argv
, 0)) {
231 lss
.change
|= TCF_CBQ_LSS_MINIDLE
;
232 } else if (matches(*argv
, "minburst") == 0) {
234 if (get_u32(&minburst
, *argv
, 0)) {
235 explain1("minburst");
238 lss
.change
|= TCF_CBQ_LSS_OFFTIME
;
239 } else if (matches(*argv
, "maxburst") == 0) {
241 if (get_u32(&maxburst
, *argv
, 0)) {
242 explain1("maxburst");
245 lss
.change
|= TCF_CBQ_LSS_MAXIDLE
;
246 } else if (matches(*argv
, "bounded") == 0) {
247 lss
.flags
|= TCF_CBQ_LSS_BOUNDED
;
248 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
249 } else if (matches(*argv
, "borrow") == 0) {
250 lss
.flags
&= ~TCF_CBQ_LSS_BOUNDED
;
251 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
252 } else if (matches(*argv
, "isolated") == 0) {
253 lss
.flags
|= TCF_CBQ_LSS_ISOLATED
;
254 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
255 } else if (matches(*argv
, "sharing") == 0) {
256 lss
.flags
&= ~TCF_CBQ_LSS_ISOLATED
;
257 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
258 } else if (matches(*argv
, "ewma") == 0) {
260 if (get_integer(&ewma_log
, *argv
, 0)) {
265 fprintf(stderr
, "ewma_log must be < 32\n");
268 lss
.change
|= TCF_CBQ_LSS_EWMA
;
269 } else if (matches(*argv
, "cell") == 0) {
274 if (get_size(&cell
, *argv
)) {
278 for (i
= 0; i
< 32; i
++)
282 fprintf(stderr
, "cell must be 2^n\n");
286 } else if (matches(*argv
, "prio") == 0) {
290 if (get_u32(&prio
, *argv
, 0)) {
294 if (prio
> TC_CBQ_MAXPRIO
) {
295 fprintf(stderr
, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO
);
300 } else if (matches(*argv
, "allot") == 0) {
302 if (get_size(&wrr
.allot
, *argv
)) {
306 } else if (matches(*argv
, "avpkt") == 0) {
308 if (get_size(&lss
.avpkt
, *argv
)) {
312 lss
.change
|= TCF_CBQ_LSS_AVPKT
;
313 } else if (matches(*argv
, "mpu") == 0) {
315 if (get_size(&mpu
, *argv
)) {
319 } else if (matches(*argv
, "weight") == 0) {
321 if (get_size(&wrr
.weight
, *argv
)) {
326 } else if (matches(*argv
, "split") == 0) {
328 if (get_tc_classid(&fopt
.split
, *argv
)) {
329 fprintf(stderr
, "Invalid split node ID.\n");
333 } else if (matches(*argv
, "defmap") == 0) {
337 err
= sscanf(*argv
, "%08x/%08x", &fopt
.defmap
, &fopt
.defchange
);
339 fprintf(stderr
, "Invalid defmap, should be MASK32[/MASK]\n");
345 } else if (matches(*argv
, "overhead") == 0) {
347 if (get_u16(&overhead
, *argv
, 10)) {
348 explain1("overhead"); return -1;
350 } else if (matches(*argv
, "linklayer") == 0) {
352 if (get_linklayer(&linklayer
, *argv
)) {
353 explain1("linklayer"); return -1;
355 } else if (matches(*argv
, "help") == 0) {
359 fprintf(stderr
, "What is \"%s\"?\n", *argv
);
366 /* OK. All options are parsed. */
368 /* 1. Prepare link sharing scheduler parameters */
370 unsigned int pktsize
= wrr
.allot
;
372 if (wrr
.allot
< (lss
.avpkt
*3)/2)
373 wrr
.allot
= (lss
.avpkt
*3)/2;
375 r
.overhead
= overhead
;
376 if (tc_calc_rtable(&r
, rtab
, cell_log
, pktsize
, linklayer
) < 0) {
377 fprintf(stderr
, "CBQ: failed to calculate rate table.\n");
382 ewma_log
= TC_CBQ_DEF_EWMA
;
383 lss
.ewma_log
= ewma_log
;
384 if (lss
.change
&(TCF_CBQ_LSS_OFFTIME
|TCF_CBQ_LSS_MAXIDLE
)) {
385 if (lss
.avpkt
== 0) {
386 fprintf(stderr
, "CBQ: avpkt is required for max/minburst.\n");
389 if (bndw
== 0 || r
.rate
== 0) {
390 fprintf(stderr
, "CBQ: bandwidth&rate are required for max/minburst.\n");
394 if (wrr
.priority
== 0 && (n
->nlmsg_flags
&NLM_F_EXCL
)) {
396 wrr
.priority
= TC_CBQ_MAXPRIO
;
398 wrr
.allot
= (lss
.avpkt
*3)/2;
402 wrr
.weight
= (wrr
.priority
== TC_CBQ_MAXPRIO
) ? 1 : r
.rate
;
403 if (wrr
.allot
== 0) {
404 fprintf(stderr
, "CBQ: \"allot\" is required to set WRR parameters.\n");
408 if (lss
.change
&TCF_CBQ_LSS_MAXIDLE
) {
409 lss
.maxidle
= tc_cbq_calc_maxidle(bndw
, r
.rate
, lss
.avpkt
, ewma_log
, maxburst
);
410 lss
.change
|= TCF_CBQ_LSS_MAXIDLE
;
411 lss
.change
|= TCF_CBQ_LSS_EWMA
|TCF_CBQ_LSS_AVPKT
;
413 if (lss
.change
&TCF_CBQ_LSS_OFFTIME
) {
414 lss
.offtime
= tc_cbq_calc_offtime(bndw
, r
.rate
, lss
.avpkt
, ewma_log
, minburst
);
415 lss
.change
|= TCF_CBQ_LSS_OFFTIME
;
416 lss
.change
|= TCF_CBQ_LSS_EWMA
|TCF_CBQ_LSS_AVPKT
;
418 if (lss
.change
&TCF_CBQ_LSS_MINIDLE
) {
419 lss
.minidle
<<= lss
.ewma_log
;
420 lss
.change
|= TCF_CBQ_LSS_EWMA
;
423 tail
= addattr_nest(n
, 1024, TCA_OPTIONS
);
425 lss
.change
|= TCF_CBQ_LSS_FLAGS
;
426 addattr_l(n
, 1024, TCA_CBQ_LSSOPT
, &lss
, sizeof(lss
));
429 addattr_l(n
, 1024, TCA_CBQ_WRROPT
, &wrr
, sizeof(wrr
));
431 addattr_l(n
, 1024, TCA_CBQ_FOPT
, &fopt
, sizeof(fopt
));
433 addattr_l(n
, 1024, TCA_CBQ_RATE
, &r
, sizeof(r
));
434 addattr_l(n
, 3024, TCA_CBQ_RTAB
, rtab
, 1024);
438 for (i
= 0; i
< 256; i
++)
439 printf("%u ", rtab
[i
]);
443 addattr_nest_end(n
, tail
);
448 static int cbq_print_opt(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*opt
)
450 struct rtattr
*tb
[TCA_CBQ_MAX
+1];
451 struct tc_ratespec
*r
= NULL
;
452 struct tc_cbq_lssopt
*lss
= NULL
;
453 struct tc_cbq_wrropt
*wrr
= NULL
;
454 struct tc_cbq_fopt
*fopt
= NULL
;
455 struct tc_cbq_ovl
*ovl
= NULL
;
456 unsigned int linklayer
;
464 parse_rtattr_nested(tb
, TCA_CBQ_MAX
, opt
);
466 if (tb
[TCA_CBQ_RATE
]) {
467 if (RTA_PAYLOAD(tb
[TCA_CBQ_RATE
]) < sizeof(*r
))
468 fprintf(stderr
, "CBQ: too short rate opt\n");
470 r
= RTA_DATA(tb
[TCA_CBQ_RATE
]);
472 if (tb
[TCA_CBQ_LSSOPT
]) {
473 if (RTA_PAYLOAD(tb
[TCA_CBQ_LSSOPT
]) < sizeof(*lss
))
474 fprintf(stderr
, "CBQ: too short lss opt\n");
476 lss
= RTA_DATA(tb
[TCA_CBQ_LSSOPT
]);
478 if (tb
[TCA_CBQ_WRROPT
]) {
479 if (RTA_PAYLOAD(tb
[TCA_CBQ_WRROPT
]) < sizeof(*wrr
))
480 fprintf(stderr
, "CBQ: too short wrr opt\n");
482 wrr
= RTA_DATA(tb
[TCA_CBQ_WRROPT
]);
484 if (tb
[TCA_CBQ_FOPT
]) {
485 if (RTA_PAYLOAD(tb
[TCA_CBQ_FOPT
]) < sizeof(*fopt
))
486 fprintf(stderr
, "CBQ: too short fopt\n");
488 fopt
= RTA_DATA(tb
[TCA_CBQ_FOPT
]);
490 if (tb
[TCA_CBQ_OVL_STRATEGY
]) {
491 if (RTA_PAYLOAD(tb
[TCA_CBQ_OVL_STRATEGY
]) < sizeof(*ovl
))
492 fprintf(stderr
, "CBQ: too short overlimit strategy %u/%u\n",
493 (unsigned int) RTA_PAYLOAD(tb
[TCA_CBQ_OVL_STRATEGY
]),
494 (unsigned int) sizeof(*ovl
));
496 ovl
= RTA_DATA(tb
[TCA_CBQ_OVL_STRATEGY
]);
502 print_rate(buf
, sizeof(buf
), r
->rate
);
503 fprintf(f
, "rate %s ", buf
);
504 linklayer
= (r
->linklayer
& TC_LINKLAYER_MASK
);
505 if (linklayer
> TC_LINKLAYER_ETHERNET
|| show_details
)
506 fprintf(f
, "linklayer %s ", sprint_linklayer(linklayer
, b2
));
508 fprintf(f
, "cell %ub ", 1<<r
->cell_log
);
510 fprintf(f
, "mpu %ub ", r
->mpu
);
512 fprintf(f
, "overhead %ub ", r
->overhead
);
515 if (lss
&& lss
->flags
) {
519 if (lss
->flags
&TCF_CBQ_LSS_BOUNDED
) {
520 fprintf(f
, "bounded");
523 if (lss
->flags
&TCF_CBQ_LSS_ISOLATED
) {
526 fprintf(f
, "isolated");
531 if (wrr
->priority
!= TC_CBQ_MAXPRIO
)
532 fprintf(f
, "prio %u", wrr
->priority
);
534 fprintf(f
, "prio no-transmit");
538 fprintf(f
, "/%u ", wrr
->cpriority
);
539 if (wrr
->weight
!= 1) {
540 print_rate(buf
, sizeof(buf
), wrr
->weight
);
541 fprintf(f
, "weight %s ", buf
);
544 fprintf(f
, "allot %ub ", wrr
->allot
);
547 if (lss
&& show_details
) {
548 fprintf(f
, "\nlevel %u ewma %u avpkt %ub ", lss
->level
, lss
->ewma_log
, lss
->avpkt
);
550 fprintf(f
, "maxidle %s ", sprint_ticks(lss
->maxidle
>>lss
->ewma_log
, b1
));
552 fprintf(f
, "[%08x] ", lss
->maxidle
);
554 if (lss
->minidle
!= 0x7fffffff) {
555 fprintf(f
, "minidle %s ", sprint_ticks(lss
->minidle
>>lss
->ewma_log
, b1
));
557 fprintf(f
, "[%08x] ", lss
->minidle
);
560 fprintf(f
, "offtime %s ", sprint_ticks(lss
->offtime
, b1
));
562 fprintf(f
, "[%08x] ", lss
->offtime
);
565 if (fopt
&& show_details
) {
568 print_tc_classid(buf
, sizeof(buf
), fopt
->split
);
569 fprintf(f
, "\nsplit %s ", buf
);
571 fprintf(f
, "defmap %08x", fopt
->defmap
);
577 static int cbq_print_xstats(struct qdisc_util
*qu
, FILE *f
, struct rtattr
*xstats
)
579 struct tc_cbq_xstats
*st
;
584 if (RTA_PAYLOAD(xstats
) < sizeof(*st
))
587 st
= RTA_DATA(xstats
);
588 fprintf(f
, " borrowed %u overactions %u avgidle %g undertime %g", st
->borrows
,
589 st
->overactions
, (double)st
->avgidle
, (double)st
->undertime
);
593 struct qdisc_util cbq_qdisc_util
= {
595 .parse_qopt
= cbq_parse_opt
,
596 .print_qopt
= cbq_print_opt
,
597 .print_xstats
= cbq_print_xstats
,
598 .parse_copt
= cbq_parse_class_opt
,
599 .print_copt
= cbq_print_opt
,