This commit was generated by cvs2svn to compensate for changes in r2587,
[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         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_integer(&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 = NLMSG_TAIL(n);
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 *) NLMSG_TAIL(n) - (void *) tail;
166         return 0;
167 }
168
169 static 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_integer(&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 = NLMSG_TAIL(n);
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 *) NLMSG_TAIL(n) - (void *) tail;
409         return 0;
410 }
411
412
413 static 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         parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
426
427         if (tb[TCA_CBQ_RATE]) {
428                 if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
429                         fprintf(stderr, "CBQ: too short rate opt\n");
430                 else
431                         r = RTA_DATA(tb[TCA_CBQ_RATE]);
432         }
433         if (tb[TCA_CBQ_LSSOPT]) {
434                 if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
435                         fprintf(stderr, "CBQ: too short lss opt\n");
436                 else
437                         lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
438         }
439         if (tb[TCA_CBQ_WRROPT]) {
440                 if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
441                         fprintf(stderr, "CBQ: too short wrr opt\n");
442                 else
443                         wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
444         }
445         if (tb[TCA_CBQ_FOPT]) {
446                 if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
447                         fprintf(stderr, "CBQ: too short fopt\n");
448                 else
449                         fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
450         }
451         if (tb[TCA_CBQ_OVL_STRATEGY]) {
452                 if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
453                         fprintf(stderr, "CBQ: too short overlimit strategy %u/%u\n",
454                                 (unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]), 
455                                 (unsigned) 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
529 static 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
545 struct qdisc_util cbq_qdisc_util = {
546         .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,
552 };
553