1 /*
2 * q_cbq.c CBQ.
3 *
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.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <syslog.h>
17 #include <fcntl.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
21 #include <string.h>
22
23 #include "utils.h"
24 #include "tc_util.h"
25 #include "tc_cbq.h"
26
explain_class(void)27 static void explain_class(void)
28 {
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");
36 }
37
explain(void)38 static void explain(void)
39 {
40 fprintf(stderr, "Usage: ... cbq bandwidth BPS avpkt BYTES [ mpu BYTES ]\n");
41 fprintf(stderr, " [ cell BYTES ] [ ewma LOG ]\n");
42 }
43
explain1(char * arg)44 static void explain1(char *arg)
45 {
46 fprintf(stderr, "Illegal \"%s\"\n", arg);
47 }
48
49
cbq_parse_opt(struct qdisc_util * qu,int argc,char ** argv,struct nlmsghdr * n)50 static int cbq_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
51 {
52 struct tc_ratespec r;
53 struct tc_cbq_lssopt lss;
54 __u32 rtab[256];
55 unsigned mpu=0, avpkt=0, allot=0;
56 unsigned short overhead=0;
57 unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
58 int cell_log=-1;
59 int ewma_log=-1;
60 struct rtattr *tail;
61
62 memset(&lss, 0, sizeof(lss));
63 memset(&r, 0, sizeof(r));
64
65 while (argc > 0) {
66 if (matches(*argv, "bandwidth") == 0 ||
67 matches(*argv, "rate") == 0) {
68 NEXT_ARG();
69 if (get_rate(&r.rate, *argv)) {
70 explain1("bandwidth");
71 return -1;
72 }
73 } else if (matches(*argv, "ewma") == 0) {
74 NEXT_ARG();
75 if (get_integer(&ewma_log, *argv, 0)) {
76 explain1("ewma");
77 return -1;
78 }
79 if (ewma_log > 31) {
80 fprintf(stderr, "ewma_log must be < 32\n");
81 return -1;
82 }
83 } else if (matches(*argv, "cell") == 0) {
84 unsigned cell;
85 int i;
86 NEXT_ARG();
87 if (get_size(&cell, *argv)) {
88 explain1("cell");
89 return -1;
90 }
91 for (i=0; i<32; i++)
92 if ((1<<i) == cell)
93 break;
94 if (i>=32) {
95 fprintf(stderr, "cell must be 2^n\n");
96 return -1;
97 }
98 cell_log = i;
99 } else if (matches(*argv, "avpkt") == 0) {
100 NEXT_ARG();
101 if (get_size(&avpkt, *argv)) {
102 explain1("avpkt");
103 return -1;
104 }
105 } else if (matches(*argv, "mpu") == 0) {
106 NEXT_ARG();
107 if (get_size(&mpu, *argv)) {
108 explain1("mpu");
109 return -1;
110 }
111 } else if (matches(*argv, "allot") == 0) {
112 NEXT_ARG();
113 /* Accept and ignore "allot" for backward compatibility */
114 if (get_size(&allot, *argv)) {
115 explain1("allot");
116 return -1;
117 }
118 } else if (matches(*argv, "overhead") == 0) {
119 NEXT_ARG();
120 if (get_u16(&overhead, *argv, 10)) {
121 explain1("overhead"); return -1;
122 }
123 } else if (matches(*argv, "linklayer") == 0) {
124 NEXT_ARG();
125 if (get_linklayer(&linklayer, *argv)) {
126 explain1("linklayer"); return -1;
127 }
128 } else if (matches(*argv, "help") == 0) {
129 explain();
130 return -1;
131 } else {
132 fprintf(stderr, "What is \"%s\"?\n", *argv);
133 explain();
134 return -1;
135 }
136 argc--; argv++;
137 }
138
139 /* OK. All options are parsed. */
140
141 if (r.rate == 0) {
142 fprintf(stderr, "CBQ: bandwidth is required parameter.\n");
143 return -1;
144 }
145 if (avpkt == 0) {
146 fprintf(stderr, "CBQ: \"avpkt\" is required.\n");
147 return -1;
148 }
149 if (allot < (avpkt*3)/2)
150 allot = (avpkt*3)/2;
151
152 r.mpu = mpu;
153 r.overhead = overhead;
154 if (tc_calc_rtable(&r, rtab, cell_log, allot, linklayer) < 0) {
155 fprintf(stderr, "CBQ: failed to calculate rate table.\n");
156 return -1;
157 }
158
159 if (ewma_log < 0)
160 ewma_log = TC_CBQ_DEF_EWMA;
161 lss.ewma_log = ewma_log;
162 lss.maxidle = tc_calc_xmittime(r.rate, avpkt);
163 lss.change = TCF_CBQ_LSS_MAXIDLE|TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
164 lss.avpkt = avpkt;
165
166 tail = NLMSG_TAIL(n);
167 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
168 addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
169 addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
170 addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
171 if (show_raw) {
172 int i;
173 for (i=0; i<256; i++)
174 printf("%u ", rtab[i]);
175 printf("\n");
176 }
177 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
178 return 0;
179 }
180
cbq_parse_class_opt(struct qdisc_util * qu,int argc,char ** argv,struct nlmsghdr * n)181 static int cbq_parse_class_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
182 {
183 int wrr_ok=0, fopt_ok=0;
184 struct tc_ratespec r;
185 struct tc_cbq_lssopt lss;
186 struct tc_cbq_wrropt wrr;
187 struct tc_cbq_fopt fopt;
188 struct tc_cbq_ovl ovl;
189 __u32 rtab[256];
190 unsigned mpu=0;
191 int cell_log=-1;
192 int ewma_log=-1;
193 unsigned bndw = 0;
194 unsigned minburst=0, maxburst=0;
195 unsigned short overhead=0;
196 unsigned int linklayer = LINKLAYER_ETHERNET; /* Assume ethernet */
197 struct rtattr *tail;
198
199 memset(&r, 0, sizeof(r));
200 memset(&lss, 0, sizeof(lss));
201 memset(&wrr, 0, sizeof(wrr));
202 memset(&fopt, 0, sizeof(fopt));
203 memset(&ovl, 0, sizeof(ovl));
204
205 while (argc > 0) {
206 if (matches(*argv, "rate") == 0) {
207 NEXT_ARG();
208 if (get_rate(&r.rate, *argv)) {
209 explain1("rate");
210 return -1;
211 }
212 } else if (matches(*argv, "bandwidth") == 0) {
213 NEXT_ARG();
214 if (get_rate(&bndw, *argv)) {
215 explain1("bandwidth");
216 return -1;
217 }
218 } else if (matches(*argv, "minidle") == 0) {
219 NEXT_ARG();
220 if (get_u32(&lss.minidle, *argv, 0)) {
221 explain1("minidle");
222 return -1;
223 }
224 lss.change |= TCF_CBQ_LSS_MINIDLE;
225 } else if (matches(*argv, "minburst") == 0) {
226 NEXT_ARG();
227 if (get_u32(&minburst, *argv, 0)) {
228 explain1("minburst");
229 return -1;
230 }
231 lss.change |= TCF_CBQ_LSS_OFFTIME;
232 } else if (matches(*argv, "maxburst") == 0) {
233 NEXT_ARG();
234 if (get_u32(&maxburst, *argv, 0)) {
235 explain1("maxburst");
236 return -1;
237 }
238 lss.change |= TCF_CBQ_LSS_MAXIDLE;
239 } else if (matches(*argv, "bounded") == 0) {
240 lss.flags |= TCF_CBQ_LSS_BOUNDED;
241 lss.change |= TCF_CBQ_LSS_FLAGS;
242 } else if (matches(*argv, "borrow") == 0) {
243 lss.flags &= ~TCF_CBQ_LSS_BOUNDED;
244 lss.change |= TCF_CBQ_LSS_FLAGS;
245 } else if (matches(*argv, "isolated") == 0) {
246 lss.flags |= TCF_CBQ_LSS_ISOLATED;
247 lss.change |= TCF_CBQ_LSS_FLAGS;
248 } else if (matches(*argv, "sharing") == 0) {
249 lss.flags &= ~TCF_CBQ_LSS_ISOLATED;
250 lss.change |= TCF_CBQ_LSS_FLAGS;
251 } else if (matches(*argv, "ewma") == 0) {
252 NEXT_ARG();
253 if (get_integer(&ewma_log, *argv, 0)) {
254 explain1("ewma");
255 return -1;
256 }
257 if (ewma_log > 31) {
258 fprintf(stderr, "ewma_log must be < 32\n");
259 return -1;
260 }
261 lss.change |= TCF_CBQ_LSS_EWMA;
262 } else if (matches(*argv, "cell") == 0) {
263 unsigned cell;
264 int i;
265 NEXT_ARG();
266 if (get_size(&cell, *argv)) {
267 explain1("cell");
268 return -1;
269 }
270 for (i=0; i<32; i++)
271 if ((1<<i) == cell)
272 break;
273 if (i>=32) {
274 fprintf(stderr, "cell must be 2^n\n");
275 return -1;
276 }
277 cell_log = i;
278 } else if (matches(*argv, "prio") == 0) {
279 unsigned prio;
280 NEXT_ARG();
281 if (get_u32(&prio, *argv, 0)) {
282 explain1("prio");
283 return -1;
284 }
285 if (prio > TC_CBQ_MAXPRIO) {
286 fprintf(stderr, "\"prio\" must be number in the range 1...%d\n", TC_CBQ_MAXPRIO);
287 return -1;
288 }
289 wrr.priority = prio;
290 wrr_ok++;
291 } else if (matches(*argv, "allot") == 0) {
292 NEXT_ARG();
293 if (get_size(&wrr.allot, *argv)) {
294 explain1("allot");
295 return -1;
296 }
297 } else if (matches(*argv, "avpkt") == 0) {
298 NEXT_ARG();
299 if (get_size(&lss.avpkt, *argv)) {
300 explain1("avpkt");
301 return -1;
302 }
303 lss.change |= TCF_CBQ_LSS_AVPKT;
304 } else if (matches(*argv, "mpu") == 0) {
305 NEXT_ARG();
306 if (get_size(&mpu, *argv)) {
307 explain1("mpu");
308 return -1;
309 }
310 } else if (matches(*argv, "weight") == 0) {
311 NEXT_ARG();
312 if (get_size(&wrr.weight, *argv)) {
313 explain1("weight");
314 return -1;
315 }
316 wrr_ok++;
317 } else if (matches(*argv, "split") == 0) {
318 NEXT_ARG();
319 if (get_tc_classid(&fopt.split, *argv)) {
320 fprintf(stderr, "Invalid split node ID.\n");
321 return -1;
322 }
323 fopt_ok++;
324 } else if (matches(*argv, "defmap") == 0) {
325 int err;
326 NEXT_ARG();
327 err = sscanf(*argv, "%08x/%08x", &fopt.defmap, &fopt.defchange);
328 if (err < 1) {
329 fprintf(stderr, "Invalid defmap, should be MASK32[/MASK]\n");
330 return -1;
331 }
332 if (err == 1)
333 fopt.defchange = ~0;
334 fopt_ok++;
335 } else if (matches(*argv, "overhead") == 0) {
336 NEXT_ARG();
337 if (get_u16(&overhead, *argv, 10)) {
338 explain1("overhead"); return -1;
339 }
340 } else if (matches(*argv, "linklayer") == 0) {
341 NEXT_ARG();
342 if (get_linklayer(&linklayer, *argv)) {
343 explain1("linklayer"); return -1;
344 }
345 } else if (matches(*argv, "help") == 0) {
346 explain_class();
347 return -1;
348 } else {
349 fprintf(stderr, "What is \"%s\"?\n", *argv);
350 explain_class();
351 return -1;
352 }
353 argc--; argv++;
354 }
355
356 /* OK. All options are parsed. */
357
358 /* 1. Prepare link sharing scheduler parameters */
359 if (r.rate) {
360 unsigned pktsize = wrr.allot;
361 if (wrr.allot < (lss.avpkt*3)/2)
362 wrr.allot = (lss.avpkt*3)/2;
363 r.mpu = mpu;
364 r.overhead = overhead;
365 if (tc_calc_rtable(&r, rtab, cell_log, pktsize, linklayer) < 0) {
366 fprintf(stderr, "CBQ: failed to calculate rate table.\n");
367 return -1;
368 }
369 }
370 if (ewma_log < 0)
371 ewma_log = TC_CBQ_DEF_EWMA;
372 lss.ewma_log = ewma_log;
373 if (lss.change&(TCF_CBQ_LSS_OFFTIME|TCF_CBQ_LSS_MAXIDLE)) {
374 if (lss.avpkt == 0) {
375 fprintf(stderr, "CBQ: avpkt is required for max/minburst.\n");
376 return -1;
377 }
378 if (bndw==0 || r.rate == 0) {
379 fprintf(stderr, "CBQ: bandwidth&rate are required for max/minburst.\n");
380 return -1;
381 }
382 }
383 if (wrr.priority == 0 && (n->nlmsg_flags&NLM_F_EXCL)) {
384 wrr_ok = 1;
385 wrr.priority = TC_CBQ_MAXPRIO;
386 if (wrr.allot == 0)
387 wrr.allot = (lss.avpkt*3)/2;
388 }
389 if (wrr_ok) {
390 if (wrr.weight == 0)
391 wrr.weight = (wrr.priority == TC_CBQ_MAXPRIO) ? 1 : r.rate;
392 if (wrr.allot == 0) {
393 fprintf(stderr, "CBQ: \"allot\" is required to set WRR parameters.\n");
394 return -1;
395 }
396 }
397 if (lss.change&TCF_CBQ_LSS_MAXIDLE) {
398 lss.maxidle = tc_cbq_calc_maxidle(bndw, r.rate, lss.avpkt, ewma_log, maxburst);
399 lss.change |= TCF_CBQ_LSS_MAXIDLE;
400 lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
401 }
402 if (lss.change&TCF_CBQ_LSS_OFFTIME) {
403 lss.offtime = tc_cbq_calc_offtime(bndw, r.rate, lss.avpkt, ewma_log, minburst);
404 lss.change |= TCF_CBQ_LSS_OFFTIME;
405 lss.change |= TCF_CBQ_LSS_EWMA|TCF_CBQ_LSS_AVPKT;
406 }
407 if (lss.change&TCF_CBQ_LSS_MINIDLE) {
408 lss.minidle <<= lss.ewma_log;
409 lss.change |= TCF_CBQ_LSS_EWMA;
410 }
411
412 tail = NLMSG_TAIL(n);
413 addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
414 if (lss.change) {
415 lss.change |= TCF_CBQ_LSS_FLAGS;
416 addattr_l(n, 1024, TCA_CBQ_LSSOPT, &lss, sizeof(lss));
417 }
418 if (wrr_ok)
419 addattr_l(n, 1024, TCA_CBQ_WRROPT, &wrr, sizeof(wrr));
420 if (fopt_ok)
421 addattr_l(n, 1024, TCA_CBQ_FOPT, &fopt, sizeof(fopt));
422 if (r.rate) {
423 addattr_l(n, 1024, TCA_CBQ_RATE, &r, sizeof(r));
424 addattr_l(n, 3024, TCA_CBQ_RTAB, rtab, 1024);
425 if (show_raw) {
426 int i;
427 for (i=0; i<256; i++)
428 printf("%u ", rtab[i]);
429 printf("\n");
430 }
431 }
432 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
433 return 0;
434 }
435
436
cbq_print_opt(struct qdisc_util * qu,FILE * f,struct rtattr * opt)437 static int cbq_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
438 {
439 struct rtattr *tb[TCA_CBQ_MAX+1];
440 struct tc_ratespec *r = NULL;
441 struct tc_cbq_lssopt *lss = NULL;
442 struct tc_cbq_wrropt *wrr = NULL;
443 struct tc_cbq_fopt *fopt = NULL;
444 struct tc_cbq_ovl *ovl = NULL;
445 SPRINT_BUF(b1);
446
447 if (opt == NULL)
448 return 0;
449
450 parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
451
452 if (tb[TCA_CBQ_RATE]) {
453 if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
454 fprintf(stderr, "CBQ: too short rate opt\n");
455 else
456 r = RTA_DATA(tb[TCA_CBQ_RATE]);
457 }
458 if (tb[TCA_CBQ_LSSOPT]) {
459 if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
460 fprintf(stderr, "CBQ: too short lss opt\n");
461 else
462 lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
463 }
464 if (tb[TCA_CBQ_WRROPT]) {
465 if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
466 fprintf(stderr, "CBQ: too short wrr opt\n");
467 else
468 wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
469 }
470 if (tb[TCA_CBQ_FOPT]) {
471 if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
472 fprintf(stderr, "CBQ: too short fopt\n");
473 else
474 fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
475 }
476 if (tb[TCA_CBQ_OVL_STRATEGY]) {
477 if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
478 fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n",
479 (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]),
480 (unsigned) sizeof(*ovl));
481 else
482 ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]);
483 }
484
485 if (r) {
486 char buf[64];
487 print_rate(buf, sizeof(buf), r->rate);
488 fprintf(f, "rate %s ", buf);
489 if (show_details) {
490 fprintf(f, "cell %ub ", 1<<r->cell_log);
491 if (r->mpu)
492 fprintf(f, "mpu %ub ", r->mpu);
493 if (r->overhead)
494 fprintf(f, "overhead %ub ", r->overhead);
495 }
496 }
497 if (lss && lss->flags) {
498 int comma=0;
499 fprintf(f, "(");
500 if (lss->flags&TCF_CBQ_LSS_BOUNDED) {
501 fprintf(f, "bounded");
502 comma=1;
503 }
504 if (lss->flags&TCF_CBQ_LSS_ISOLATED) {
505 if (comma)
506 fprintf(f, ",");
507 fprintf(f, "isolated");
508 }
509 fprintf(f, ") ");
510 }
511 if (wrr) {
512 if (wrr->priority != TC_CBQ_MAXPRIO)
513 fprintf(f, "prio %u", wrr->priority);
514 else
515 fprintf(f, "prio no-transmit");
516 if (show_details) {
517 char buf[64];
518 fprintf(f, "/%u ", wrr->cpriority);
519 if (wrr->weight != 1) {
520 print_rate(buf, sizeof(buf), wrr->weight);
521 fprintf(f, "weight %s ", buf);
522 }
523 if (wrr->allot)
524 fprintf(f, "allot %ub ", wrr->allot);
525 }
526 }
527 if (lss && show_details) {
528 fprintf(f, "\nlevel %u ewma %u avpkt %ub ", lss->level, lss->ewma_log, lss->avpkt);
529 if (lss->maxidle) {
530 fprintf(f, "maxidle %s ", sprint_ticks(lss->maxidle>>lss->ewma_log, b1));
531 if (show_raw)
532 fprintf(f, "[%08x] ", lss->maxidle);
533 }
534 if (lss->minidle!=0x7fffffff) {
535 fprintf(f, "minidle %s ", sprint_ticks(lss->minidle>>lss->ewma_log, b1));
536 if (show_raw)
537 fprintf(f, "[%08x] ", lss->minidle);
538 }
539 if (lss->offtime) {
540 fprintf(f, "offtime %s ", sprint_ticks(lss->offtime, b1));
541 if (show_raw)
542 fprintf(f, "[%08x] ", lss->offtime);
543 }
544 }
545 if (fopt && show_details) {
546 char buf[64];
547 print_tc_classid(buf, sizeof(buf), fopt->split);
548 fprintf(f, "\nsplit %s ", buf);
549 if (fopt->defmap) {
550 fprintf(f, "defmap %08x", fopt->defmap);
551 }
552 }
553 return 0;
554 }
555
cbq_print_xstats(struct qdisc_util * qu,FILE * f,struct rtattr * xstats)556 static int cbq_print_xstats(struct qdisc_util *qu, FILE *f, struct rtattr *xstats)
557 {
558 struct tc_cbq_xstats *st;
559
560 if (xstats == NULL)
561 return 0;
562
563 if (RTA_PAYLOAD(xstats) < sizeof(*st))
564 return -1;
565
566 st = RTA_DATA(xstats);
567 fprintf(f, " borrowed %u overactions %u avgidle %g undertime %g", st->borrows,
568 st->overactions, (double)st->avgidle, (double)st->undertime);
569 return 0;
570 }
571
572 struct qdisc_util cbq_qdisc_util = {
573 .id = "cbq",
574 .parse_qopt = cbq_parse_opt,
575 .print_qopt = cbq_print_opt,
576 .print_xstats = cbq_print_xstats,
577 .parse_copt = cbq_parse_class_opt,
578 .print_copt = cbq_print_opt,
579 };
580
581