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