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