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