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 #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 582