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 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 545struct qdisc_util cbq_util = { 546 NULL, 547 "cbq", 548 cbq_parse_opt, 549 cbq_print_opt, 550 cbq_print_xstats, 551 552 cbq_parse_class_opt, 553 cbq_print_opt, 554}; 555 556