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