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