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