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");
35 fprintf(stderr, " [ overhead BYTES ] [ linklayer TYPE ]\n");
38 static void explain(void)
40 fprintf(stderr, "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n");
41 fprintf(stderr, " [ cell BYTES ] [ ewma LOG ]\n");
44 static void explain1(char *arg)
46 fprintf(stderr, "Illegal \"%s\"\n", arg);
49 #define usage() return(-1)
51 static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
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 */
63 memset(&lss, 0, sizeof(lss));
64 memset(&r, 0, sizeof(r));
67 if (matches(*argv, "bandwidth") == 0 ||
68 matches(*argv, "rate") == 0) {
70 if (get_rate(&r.rate, *argv)) {
71 explain1("bandwidth");
74 } else if (matches(*argv, "ewma") == 0) {
76 if (get_integer(&ewma_log, *argv, 0)) {
81 fprintf(stderr, "ewma_log must be < 32\n");
84 } else if (matches(*argv, "cell") == 0) {
88 if (get_size(&cell, *argv)) {
96 fprintf(stderr, "cell must be 2^n\n");
100 } else if (matches(*argv, "avpkt") == 0) {
102 if (get_size(&avpkt, *argv)) {
106 } else if (matches(*argv, "mpu") == 0) {
108 if (get_size(&mpu, *argv)) {
112 } else if (matches(*argv, "allot") == 0) {
114 /* Accept and ignore "allot" for backward compatibility */
115 if (get_size(&allot, *argv)) {
119 } else if (matches(*argv, "overhead") == 0) {
121 if (get_u16(&overhead, *argv, 10)) {
122 explain1("overhead"); return -1;
124 } else if (matches(*argv, "linklayer") == 0) {
126 if (get_linklayer(&linklayer, *argv)) {
127 explain1("linklayer"); return -1;
129 } else if (matches(*argv, "help") == 0) {
133 fprintf(stderr, "What is \"%s\"?\n", *argv);
140 /* OK. All options are parsed. */
143 fprintf(stderr, "CBQ: bandwidth is required parameter.\n");
147 fprintf(stderr, "CBQ: \"avpkt\" is required.\n");
150 if (allot < (avpkt*3)/2)
154 r.overhead = overhead;
155 if (tc_calc_rtable(&r, rtab, cell_log, allot, linklayer) < 0) {
156 fprintf(stderr, "CBQ: failed to calculate rate table.\n");
161 ewma_log = TC_CBQ_DEF_EWMA;
162 lss.ewma_log = ewma_log;
163 lss.maxidle = tc_calc_xmittime(r.rate, avpkt);
164 lss.change = TCF_CBQ_LSS_MAXIDLE|TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
167 tail = NLMSG_TAIL(n);
168 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
169 addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
170 addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
171 addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
174 for (i=0; i<256; i++)
175 printf("%u ", rtab[i]);
178 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
182 static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
184 int wrr_ok=0, fopt_ok=0;
185 struct tc_ratespec r;
186 struct tc_cbq_lssopt lss;
187 struct tc_cbq_wrropt wrr;
188 struct tc_cbq_fopt fopt;
189 struct tc_cbq_ovl ovl;
195 unsigned minburst=0, maxburst=0;
196 unsigned short overhead=0;
197 unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
200 memset(&r, 0, sizeof(r));
201 memset(&lss, 0, sizeof(lss));
202 memset(&wrr, 0, sizeof(wrr));
203 memset(&fopt, 0, sizeof(fopt));
204 memset(&ovl, 0, sizeof(ovl));
207 if (matches(*argv, "rate") == 0) {
209 if (get_rate(&r.rate, *argv)) {
213 } else if (matches(*argv, "bandwidth") == 0) {
215 if (get_rate(&bndw, *argv)) {
216 explain1("bandwidth");
219 } else if (matches(*argv, "minidle") == 0) {
221 if (get_u32(&lss.minidle, *argv, 0)) {
225 lss.change |= TCF_CBQ_LSS_MINIDLE;
226 } else if (matches(*argv, "minburst") == 0) {
228 if (get_u32(&minburst, *argv, 0)) {
229 explain1("minburst");
232 lss.change |= TCF_CBQ_LSS_OFFTIME;
233 } else if (matches(*argv, "maxburst") == 0) {
235 if (get_u32(&maxburst, *argv, 0)) {
236 explain1("maxburst");
239 lss.change |= TCF_CBQ_LSS_MAXIDLE;
240 } else if (matches(*argv, "bounded") == 0) {
241 lss.flags |= TCF_CBQ_LSS_BOUNDED;
242 lss.change |= TCF_CBQ_LSS_FLAGS;
243 } else if (matches(*argv, "borrow") == 0) {
244 lss.flags &= ~TCF_CBQ_LSS_BOUNDED;
245 lss.change |= TCF_CBQ_LSS_FLAGS;
246 } else if (matches(*argv, "isolated") == 0) {
247 lss.flags |= TCF_CBQ_LSS_ISOLATED;
248 lss.change |= TCF_CBQ_LSS_FLAGS;
249 } else if (matches(*argv, "sharing") == 0) {
250 lss.flags &= ~TCF_CBQ_LSS_ISOLATED;
251 lss.change |= TCF_CBQ_LSS_FLAGS;
252 } else if (matches(*argv, "ewma") == 0) {
254 if (get_integer(&ewma_log, *argv, 0)) {
259 fprintf(stderr, "ewma_log must be < 32\n");
262 lss.change |= TCF_CBQ_LSS_EWMA;
263 } else if (matches(*argv, "cell") == 0) {
267 if (get_size(&cell, *argv)) {
275 fprintf(stderr, "cell must be 2^n\n");
279 } else if (matches(*argv, "prio") == 0) {
282 if (get_u32(&prio, *argv, 0)) {
286 if (prio > TC_CBQ_MAXPRIO) {
287 fprintf(stderr, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO);
292 } else if (matches(*argv, "allot") == 0) {
294 if (get_size(&wrr.allot, *argv)) {
298 } else if (matches(*argv, "avpkt") == 0) {
300 if (get_size(&lss.avpkt, *argv)) {
304 lss.change |= TCF_CBQ_LSS_AVPKT;
305 } else if (matches(*argv, "mpu") == 0) {
307 if (get_size(&mpu, *argv)) {
311 } else if (matches(*argv, "weight") == 0) {
313 if (get_size(&wrr.weight, *argv)) {
318 } else if (matches(*argv, "split") == 0) {
320 if (get_tc_classid(&fopt.split, *argv)) {
321 fprintf(stderr, "Invalid split node ID.\n");
325 } else if (matches(*argv, "defmap") == 0) {
328 err = sscanf(*argv, "%08x/%08x", &fopt.defmap, &fopt.defchange);
330 fprintf(stderr, "Invalid defmap, should be MASK32[/MASK]\n");
336 } else if (matches(*argv, "overhead") == 0) {
338 if (get_u16(&overhead, *argv, 10)) {
339 explain1("overhead"); return -1;
341 } else if (matches(*argv, "linklayer") == 0) {
343 if (get_linklayer(&linklayer, *argv)) {
344 explain1("linklayer"); return -1;
346 } else if (matches(*argv, "help") == 0) {
350 fprintf(stderr, "What is \"%s\"?\n", *argv);
357 /* OK. All options are parsed. */
359 /* 1. Prepare link sharing scheduler parameters */
361 unsigned pktsize = wrr.allot;
362 if (wrr.allot < (lss.avpkt*3)/2)
363 wrr.allot = (lss.avpkt*3)/2;
365 r.overhead = overhead;
366 if (tc_calc_rtable(&r, rtab, cell_log, pktsize, linklayer) < 0) {
367 fprintf(stderr, "CBQ: failed to calculate rate table.\n");
372 ewma_log = TC_CBQ_DEF_EWMA;
373 lss.ewma_log = ewma_log;
374 if (lss.change&(TCF_CBQ_LSS_OFFTIME|TCF_CBQ_LSS_MAXIDLE)) {
375 if (lss.avpkt == 0) {
376 fprintf(stderr, "CBQ: avpkt is required for max/minburst.\n");
379 if (bndw==0 || r.rate == 0) {
380 fprintf(stderr, "CBQ: bandwidth&rate are required for max/minburst.\n");
384 if (wrr.priority == 0 && (n->nlmsg_flags&NLM_F_EXCL)) {
386 wrr.priority = TC_CBQ_MAXPRIO;
388 wrr.allot = (lss.avpkt*3)/2;
392 wrr.weight = (wrr.priority == TC_CBQ_MAXPRIO) ? 1 : r.rate;
393 if (wrr.allot == 0) {
394 fprintf(stderr, "CBQ: \"allot\" is required to set WRR parameters.\n");
398 if (lss.change&TCF_CBQ_LSS_MAXIDLE) {
399 lss.maxidle = tc_cbq_calc_maxidle(bndw, r.rate, lss.avpkt, ewma_log, maxburst);
400 lss.change |= TCF_CBQ_LSS_MAXIDLE;
401 lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
403 if (lss.change&TCF_CBQ_LSS_OFFTIME) {
404 lss.offtime = tc_cbq_calc_offtime(bndw, r.rate, lss.avpkt, ewma_log, minburst);
405 lss.change |= TCF_CBQ_LSS_OFFTIME;
406 lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
408 if (lss.change&TCF_CBQ_LSS_MINIDLE) {
409 lss.minidle <<= lss.ewma_log;
410 lss.change |= TCF_CBQ_LSS_EWMA;
413 tail = NLMSG_TAIL(n);
414 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
416 lss.change |= TCF_CBQ_LSS_FLAGS;
417 addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
420 addattr_l(n, 1024, TCA_CBQ_WRROPT, &wrr, sizeof(wrr));
422 addattr_l(n, 1024, TCA_CBQ_FOPT, &fopt, sizeof(fopt));
424 addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
425 addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
428 for (i=0; i<256; i++)
429 printf("%u ", rtab[i]);
433 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
438 static int cbq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
440 struct rtattr *tb[TCA_CBQ_MAX+1];
441 struct tc_ratespec *r = NULL;
442 struct tc_cbq_lssopt *lss = NULL;
443 struct tc_cbq_wrropt *wrr = NULL;
444 struct tc_cbq_fopt *fopt = NULL;
445 struct tc_cbq_ovl *ovl = NULL;
451 parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
453 if (tb[TCA_CBQ_RATE]) {
454 if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
455 fprintf(stderr, "CBQ: too short rate opt\n");
457 r = RTA_DATA(tb[TCA_CBQ_RATE]);
459 if (tb[TCA_CBQ_LSSOPT]) {
460 if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
461 fprintf(stderr, "CBQ: too short lss opt\n");
463 lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
465 if (tb[TCA_CBQ_WRROPT]) {
466 if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
467 fprintf(stderr, "CBQ: too short wrr opt\n");
469 wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
471 if (tb[TCA_CBQ_FOPT]) {
472 if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
473 fprintf(stderr, "CBQ: too short fopt\n");
475 fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
477 if (tb[TCA_CBQ_OVL_STRATEGY]) {
478 if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
479 fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n",
480 (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]),
481 (unsigned) sizeof(*ovl));
483 ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]);
488 print_rate(buf, sizeof(buf), r->rate);
489 fprintf(f, "rate %s ", buf);
491 fprintf(f, "cell %ub ", 1<<r->cell_log);
493 fprintf(f, "mpu %ub ", r->mpu);
495 fprintf(f, "overhead %ub ", r->overhead);
498 if (lss && lss->flags) {
501 if (lss->flags&TCF_CBQ_LSS_BOUNDED) {
502 fprintf(f, "bounded");
505 if (lss->flags&TCF_CBQ_LSS_ISOLATED) {
508 fprintf(f, "isolated");
513 if (wrr->priority != TC_CBQ_MAXPRIO)
514 fprintf(f, "prio %u", wrr->priority);
516 fprintf(f, "prio no-transmit");
519 fprintf(f, "/%u ", wrr->cpriority);
520 if (wrr->weight != 1) {
521 print_rate(buf, sizeof(buf), wrr->weight);
522 fprintf(f, "weight %s ", buf);
525 fprintf(f, "allot %ub ", wrr->allot);
528 if (lss && show_details) {
529 fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt);
531 fprintf(f, "maxidle %s ", sprint_ticks(lss->maxidle>>lss->ewma_log, b1));
533 fprintf(f, "[%08x] ", lss->maxidle);
535 if (lss->minidle!=0x7fffffff) {
536 fprintf(f, "minidle %s ", sprint_ticks(lss->minidle>>lss->ewma_log, b1));
538 fprintf(f, "[%08x] ", lss->minidle);
541 fprintf(f, "offtime %s ", sprint_ticks(lss->offtime, b1));
543 fprintf(f, "[%08x] ", lss->offtime);
546 if (fopt && show_details) {
548 print_tc_classid(buf, sizeof(buf), fopt->split);
549 fprintf(f, "\nsplit %s ", buf);
551 fprintf(f, "defmap %08x", fopt->defmap);
557 static int cbq_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
559 struct tc_cbq_xstats *st;
564 if (RTA_PAYLOAD(xstats) < sizeof(*st))
567 st = RTA_DATA(xstats);
568 fprintf(f, " borrowed %u overactions %u avgidle %g undertime %g", st->borrows,
569 st->overactions, (double)st->avgidle, (double)st->undertime);
573 struct qdisc_util cbq_qdisc_util = {
575 .parse_qopt = cbq_parse_opt,
576 .print_qopt = cbq_print_opt,
577 .print_xstats = cbq_print_xstats,
578 .parse_copt = cbq_parse_class_opt,
579 .print_copt = cbq_print_opt,