1 /* $NetBSD: qop_hfsc.c,v 1.5 2002/03/05 04:11:53 itojun Exp $ */ 2 /* $KAME: qop_hfsc.c,v 1.7 2001/12/03 08:20:55 kjc Exp $ */ 3 /* 4 * Copyright (C) 1999-2000 5 * Sony Computer Science Laboratories, Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/socket.h> 31 #include <sys/sockio.h> 32 #include <sys/ioctl.h> 33 #include <sys/fcntl.h> 34 #include <net/if.h> 35 #include <netinet/in.h> 36 #include <arpa/inet.h> 37 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <unistd.h> 41 #include <stddef.h> 42 #include <string.h> 43 #include <ctype.h> 44 #include <errno.h> 45 #include <syslog.h> 46 #include <netdb.h> 47 48 #include <altq/altq.h> 49 #include <altq/altq_hfsc.h> 50 #include "altq_qop.h" 51 #include "qop_hfsc.h" 52 53 static int read_sc(int *, char ***, int *, u_int *, u_int *, u_int *); 54 static int qop_hfsc_enable_hook(struct ifinfo *); 55 static int qop_hfsc_delete_class_hook(struct classinfo *); 56 static int validate_sc(struct service_curve *); 57 58 static void gsc_add_sc(struct gen_sc *, struct service_curve *); 59 static void gsc_sub_sc(struct gen_sc *, struct service_curve *); 60 static int is_gsc_under_sc(struct gen_sc *, struct service_curve *); 61 static void gsc_destroy(struct gen_sc *); 62 static struct segment *gsc_getentry(struct gen_sc *, double); 63 static int gsc_add_seg(struct gen_sc *, double, double, double, double); 64 static int gsc_sub_seg(struct gen_sc *, double, double, double, double); 65 static void gsc_compress(struct gen_sc *); 66 static double sc_x2y(struct service_curve *, double); 67 68 static int hfsc_attach(struct ifinfo *); 69 static int hfsc_detach(struct ifinfo *); 70 static int hfsc_clear(struct ifinfo *); 71 static int hfsc_enable(struct ifinfo *); 72 static int hfsc_disable(struct ifinfo *); 73 static int hfsc_add_class(struct classinfo *); 74 static int hfsc_modify_class(struct classinfo *, void *); 75 static int hfsc_delete_class(struct classinfo *); 76 static int hfsc_add_filter(struct fltrinfo *); 77 static int hfsc_delete_filter(struct fltrinfo *); 78 79 #define HFSC_DEVICE "/dev/altq/hfsc" 80 81 static int hfsc_fd = -1; 82 static int hfsc_refcount = 0; 83 84 static struct qdisc_ops hfsc_qdisc = { 85 ALTQT_HFSC, 86 "hfsc", 87 hfsc_attach, 88 hfsc_detach, 89 hfsc_clear, 90 hfsc_enable, 91 hfsc_disable, 92 hfsc_add_class, 93 hfsc_modify_class, 94 hfsc_delete_class, 95 hfsc_add_filter, 96 hfsc_delete_filter, 97 }; 98 99 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) 100 101 /* 102 * parser interface 103 */ 104 int 105 hfsc_interface_parser(const char *ifname, int argc, char **argv) 106 { 107 u_int bandwidth = 100000000; /* 100Mbps */ 108 u_int tbrsize = 0; 109 int flags = 0; 110 111 /* 112 * process options 113 */ 114 while (argc > 0) { 115 if (EQUAL(*argv, "bandwidth")) { 116 argc--; argv++; 117 if (argc > 0) 118 bandwidth = atobps(*argv); 119 } else if (EQUAL(*argv, "tbrsize")) { 120 argc--; argv++; 121 if (argc > 0) 122 tbrsize = atobytes(*argv); 123 } else if (EQUAL(*argv, "hfsc")) { 124 /* just skip */ 125 } else { 126 LOG(LOG_ERR, 0, "Unknown keyword '%s'", *argv); 127 return (0); 128 } 129 argc--; argv++; 130 } 131 132 if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0) 133 return (0); 134 135 if (qcmd_hfsc_add_if(ifname, bandwidth, flags) != 0) 136 return (0); 137 return (1); 138 } 139 140 int 141 hfsc_class_parser(const char *ifname, const char *class_name, 142 const char *parent_name, int argc, char **argv) 143 { 144 u_int m1, d, m2, rm1, rd, rm2, fm1, fd, fm2; 145 int qlimit = 50; 146 int flags = 0, admission = 0; 147 int type = 0, error; 148 149 rm1 = rd = rm2 = fm1 = fd = fm2 = 0; 150 while (argc > 0) { 151 if (*argv[0] == '[') { 152 if (read_sc(&argc, &argv, &type, &m1, &d, &m2) != 0) { 153 LOG(LOG_ERR, 0, 154 "Bad service curve in %s, line %d", 155 altqconfigfile, line_no); 156 return (0); 157 } 158 if (type & HFSC_REALTIMESC) { 159 rm1 = m1; rd = d; rm2 = m2; 160 } 161 if (type & HFSC_LINKSHARINGSC) { 162 fm1 = m1; fd = d; fm2 = m2; 163 } 164 } else if (EQUAL(*argv, "pshare")) { 165 argc--; argv++; 166 if (argc > 0) { 167 struct ifinfo *ifinfo; 168 u_int pshare; 169 170 pshare = (u_int)strtoul(*argv, NULL, 0); 171 if ((ifinfo = ifname2ifinfo(ifname)) != NULL) { 172 fm2 = ifinfo->bandwidth / 100 * pshare; 173 type |= HFSC_LINKSHARINGSC; 174 } 175 } 176 } else if (EQUAL(*argv, "grate")) { 177 argc--; argv++; 178 if (argc > 0) { 179 rm2 = atobps(*argv); 180 type |= HFSC_REALTIMESC; 181 } 182 } else if (EQUAL(*argv, "qlimit")) { 183 argc--; argv++; 184 if (argc > 0) 185 qlimit = strtoul(*argv, NULL, 0); 186 } else if (EQUAL(*argv, "default")) { 187 flags |= HFCF_DEFAULTCLASS; 188 } else if (EQUAL(*argv, "admission")) { 189 argc--; argv++; 190 if (argc > 0) { 191 if (EQUAL(*argv, "guaranteed") 192 || EQUAL(*argv, "cntlload")) 193 admission = 1; 194 else if (EQUAL(*argv, "none")) { 195 /* nothing */ 196 } else { 197 LOG(LOG_ERR, 0, 198 "unknown admission type - %s, line %d", 199 *argv, line_no); 200 return (0); 201 } 202 } 203 } else if (EQUAL(*argv, "red")) { 204 flags |= HFCF_RED; 205 } else if (EQUAL(*argv, "ecn")) { 206 flags |= HFCF_ECN; 207 } else if (EQUAL(*argv, "rio")) { 208 flags |= HFCF_RIO; 209 } else if (EQUAL(*argv, "cleardscp")) { 210 flags |= HFCF_CLEARDSCP; 211 } else { 212 LOG(LOG_ERR, 0, 213 "Unknown keyword '%s' in %s, line %d", 214 *argv, altqconfigfile, line_no); 215 return (0); 216 } 217 218 argc--; argv++; 219 } 220 221 if (type == 0) { 222 LOG(LOG_ERR, 0, 223 "hfsc: service curve not specified in %s, line %d", 224 altqconfigfile, line_no); 225 return (0); 226 } 227 228 if ((flags & HFCF_ECN) && (flags & (HFCF_RED|HFCF_RIO)) == 0) 229 flags |= HFCF_RED; 230 231 /* 232 * if the link-sharing service curve is diffrent from 233 * the real-time service curve, we first create a class with the 234 * smaller service curve and then modify the other service curve. 235 */ 236 if (rm2 <= fm2) { 237 m1 = rm1; d = rd; m2 = rm2; 238 } else { 239 m1 = fm1; d = fd; m2 = fm2; 240 } 241 error = qcmd_hfsc_add_class(ifname, class_name, parent_name, 242 m1, d, m2, qlimit, flags); 243 244 if (error == 0 && (rm1 != fm1 || rd != fd || rm2 != fm2)) { 245 if (rm2 <= fm2) { 246 m1 = fm1; d = fd; m2 = fm2; type = HFSC_LINKSHARINGSC; 247 } else { 248 m1 = rm1; d = rd; m2 = rm2; type = HFSC_REALTIMESC; 249 } 250 error = qcmd_hfsc_modify_class(ifname, class_name, 251 m1, d, m2, type); 252 } 253 254 if (error == 0 && admission) { 255 /* this is a special class for rsvp */ 256 struct ifinfo *ifinfo = ifname2ifinfo(ifname); 257 struct classinfo *clinfo = clname2clinfo(ifinfo, class_name); 258 259 if (ifinfo->resv_class != NULL) { 260 LOG(LOG_ERR, 0, 261 "more than one admission class specified: %s", 262 class_name); 263 return (0); 264 } 265 ifinfo->resv_class = clinfo; 266 } 267 268 if (error) { 269 LOG(LOG_ERR, errno, "hfsc_class_parser: %s", 270 qoperror(error)); 271 return (0); 272 } 273 return (1); 274 } 275 276 /* 277 * read service curve parameters 278 * '[' <type> <m1> <d> <m2> ']' 279 * type := "sc", "rt", or "ls" 280 */ 281 static int 282 read_sc(int *argcp, char ***argvp, int *type, u_int *m1, u_int *d, u_int *m2) 283 { 284 int argc = *argcp; 285 char **argv = *argvp; 286 char *cp; 287 288 cp = *argv; 289 if (*cp++ != '[') 290 return (-1); 291 if (*cp == '\0') { 292 cp = *++argv; --argc; 293 } 294 if (*cp == 's' || *cp == 'S') 295 *type = HFSC_DEFAULTSC; 296 else if (*cp == 'r' || *cp == 'R') 297 *type = HFSC_REALTIMESC; 298 else if (*cp == 'l' || *cp == 'L') 299 *type = HFSC_LINKSHARINGSC; 300 else 301 return (-1); 302 cp = *++argv; --argc; 303 *m1 = atobps(cp); 304 cp = *++argv; --argc; 305 *d = (u_int)strtoul(cp, NULL, 0); 306 cp = *++argv; --argc; 307 *m2 = atobps(cp); 308 if (strchr(cp, ']') == NULL) { 309 cp = *++argv; --argc; 310 if (*cp != ']') 311 return (-1); 312 } 313 *argcp = argc; 314 *argvp = argv; 315 return (0); 316 } 317 318 /* 319 * qcmd api 320 */ 321 int 322 qcmd_hfsc_add_if(const char *ifname, u_int bandwidth, int flags) 323 { 324 int error; 325 326 error = qop_hfsc_add_if(NULL, ifname, bandwidth, flags); 327 if (error != 0) 328 LOG(LOG_ERR, errno, "%s: can't add hfsc on interface '%s'", 329 qoperror(error), ifname); 330 return (error); 331 } 332 333 int 334 qcmd_hfsc_add_class(const char *ifname, const char *class_name, 335 const char *parent_name, u_int m1, u_int d, u_int m2, 336 int qlimit, int flags) 337 { 338 struct ifinfo *ifinfo; 339 struct classinfo *parent = NULL; 340 struct service_curve sc; 341 int error = 0; 342 343 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 344 error = QOPERR_BADIF; 345 346 if (error == 0 && 347 (parent = clname2clinfo(ifinfo, parent_name)) == NULL) 348 error = QOPERR_BADCLASS; 349 350 sc.m1 = m1; 351 sc.d = d; 352 sc.m2 = m2; 353 354 if (error == 0) 355 error = qop_hfsc_add_class(NULL, class_name, ifinfo, parent, 356 &sc, qlimit, flags); 357 if (error != 0) 358 LOG(LOG_ERR, errno, 359 "hfsc: %s: can't add class '%s' on interface '%s'", 360 qoperror(error), class_name, ifname); 361 return (error); 362 } 363 364 int 365 qcmd_hfsc_modify_class(const char *ifname, const char *class_name, 366 u_int m1, u_int d, u_int m2, int sctype) 367 { 368 struct ifinfo *ifinfo; 369 struct classinfo *clinfo; 370 struct service_curve sc; 371 372 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 373 return (QOPERR_BADIF); 374 375 if ((clinfo = clname2clinfo(ifinfo, class_name)) == NULL) 376 return (QOPERR_BADCLASS); 377 378 sc.m1 = m1; 379 sc.d = d; 380 sc.m2 = m2; 381 382 return qop_hfsc_modify_class(clinfo, &sc, sctype); 383 } 384 385 /* 386 * qop api 387 */ 388 int 389 qop_hfsc_add_if(struct ifinfo **rp, const char *ifname, 390 u_int bandwidth, int flags) 391 { 392 struct ifinfo *ifinfo = NULL; 393 struct hfsc_ifinfo *hfsc_ifinfo = NULL; 394 struct service_curve sc; 395 int error; 396 397 if ((hfsc_ifinfo = calloc(1, sizeof(*hfsc_ifinfo))) == NULL) 398 return (QOPERR_NOMEM); 399 400 error = qop_add_if(&ifinfo, ifname, bandwidth, 401 &hfsc_qdisc, hfsc_ifinfo); 402 if (error != 0) 403 goto err_ret; 404 405 /* set enable hook */ 406 ifinfo->enable_hook = qop_hfsc_enable_hook; 407 408 /* create a dummy root class */ 409 sc.m1 = bandwidth; 410 sc.d = 0; 411 sc.m2 = bandwidth; 412 if ((error = qop_hfsc_add_class(&hfsc_ifinfo->root_class, "root", 413 ifinfo, NULL, &sc, 0, 0)) != 0) { 414 LOG(LOG_ERR, errno, 415 "hfsc: %s: can't create dummy root class on %s!", 416 qoperror(error), ifname); 417 (void)qop_delete_if(ifinfo); 418 return (QOPERR_CLASS); 419 } 420 421 if (rp != NULL) 422 *rp = ifinfo; 423 return (0); 424 425 err_ret: 426 if (hfsc_ifinfo != NULL) { 427 free(hfsc_ifinfo); 428 if (ifinfo != NULL) 429 ifinfo->private = NULL; 430 } 431 return (error); 432 } 433 434 #define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) 435 436 int 437 qop_hfsc_add_class(struct classinfo **rp, const char *class_name, 438 struct ifinfo *ifinfo, struct classinfo *parent, 439 struct service_curve *sc, int qlimit, int flags) 440 { 441 struct classinfo *clinfo; 442 struct hfsc_ifinfo *hfsc_ifinfo; 443 struct hfsc_classinfo *hfsc_clinfo = NULL, *parent_clinfo = NULL; 444 int error; 445 446 hfsc_ifinfo = ifinfo->private; 447 if ((flags & HFCF_DEFAULTCLASS) && hfsc_ifinfo->default_class != NULL) 448 return (QOPERR_CLASS_INVAL); 449 450 if (validate_sc(sc) != 0) 451 return (QOPERR_INVAL); 452 453 /* admission control */ 454 if (parent != NULL && !is_sc_null(sc)) { 455 parent_clinfo = parent->private; 456 gsc_add_sc(&parent_clinfo->gen_rsc, sc); 457 gsc_add_sc(&parent_clinfo->gen_fsc, sc); 458 if (!is_gsc_under_sc(&parent_clinfo->gen_rsc, 459 &parent_clinfo->rsc) || 460 !is_gsc_under_sc(&parent_clinfo->gen_fsc, 461 &parent_clinfo->fsc)) { 462 /* admission control failure */ 463 error = QOPERR_ADMISSION_NOBW; 464 goto err_ret; 465 } 466 } 467 468 if ((hfsc_clinfo = calloc(1, sizeof(*hfsc_clinfo))) == NULL) { 469 error = QOPERR_NOMEM; 470 goto err_ret; 471 } 472 473 hfsc_clinfo->rsc = *sc; 474 hfsc_clinfo->fsc = *sc; 475 LIST_INIT(&hfsc_clinfo->gen_rsc); 476 LIST_INIT(&hfsc_clinfo->gen_fsc); 477 hfsc_clinfo->qlimit = qlimit; 478 hfsc_clinfo->flags = flags; 479 480 if ((error = qop_add_class(&clinfo, class_name, ifinfo, parent, 481 hfsc_clinfo)) != 0) 482 goto err_ret; 483 484 /* set delete hook */ 485 clinfo->delete_hook = qop_hfsc_delete_class_hook; 486 487 if (flags & HFCF_DEFAULTCLASS) 488 hfsc_ifinfo->default_class = clinfo; 489 490 if (parent == NULL) { 491 /* 492 * if this is a root class, reserve 20% of the real-time 493 * bandwidth for safety. 494 * many network cards are not able to saturate the wire, 495 * and if we allocate real-time traffic more than the 496 * maximum sending rate of the card, hfsc is no longer 497 * able to meet the delay bound requirements. 498 */ 499 hfsc_clinfo->rsc.m1 = hfsc_clinfo->rsc.m1 / 10 * 8; 500 hfsc_clinfo->rsc.m2 = hfsc_clinfo->rsc.m2 / 10 * 8; 501 } 502 503 if (rp != NULL) 504 *rp = clinfo; 505 return (0); 506 507 err_ret: 508 /* cancel admission control */ 509 if (parent != NULL && !is_sc_null(sc)) { 510 gsc_sub_sc(&parent_clinfo->gen_rsc, sc); 511 gsc_sub_sc(&parent_clinfo->gen_fsc, sc); 512 } 513 514 if (hfsc_clinfo != NULL) { 515 free(hfsc_clinfo); 516 clinfo->private = NULL; 517 } 518 519 return (error); 520 } 521 522 /* 523 * this is called from qop_delete_class() before a class is destroyed 524 * for discipline specific cleanup. 525 */ 526 static int 527 qop_hfsc_delete_class_hook(struct classinfo *clinfo) 528 { 529 struct hfsc_classinfo *hfsc_clinfo, *parent_clinfo; 530 531 hfsc_clinfo = clinfo->private; 532 533 /* cancel admission control */ 534 if (clinfo->parent != NULL) { 535 parent_clinfo = clinfo->parent->private; 536 537 gsc_sub_sc(&parent_clinfo->gen_rsc, &hfsc_clinfo->rsc); 538 gsc_sub_sc(&parent_clinfo->gen_fsc, &hfsc_clinfo->fsc); 539 } 540 541 gsc_destroy(&hfsc_clinfo->gen_rsc); 542 gsc_destroy(&hfsc_clinfo->gen_fsc); 543 return (0); 544 } 545 546 int 547 qop_hfsc_modify_class(struct classinfo *clinfo, 548 struct service_curve *sc, int sctype) 549 { 550 struct hfsc_classinfo *hfsc_clinfo, *parent_clinfo; 551 struct service_curve rsc, fsc; 552 int error; 553 554 if (validate_sc(sc) != 0) 555 return (QOPERR_INVAL); 556 557 hfsc_clinfo = clinfo->private; 558 if (clinfo->parent == NULL) 559 return (QOPERR_CLASS_INVAL); 560 parent_clinfo = clinfo->parent->private; 561 562 /* save old service curves */ 563 rsc = hfsc_clinfo->rsc; 564 fsc = hfsc_clinfo->fsc; 565 566 /* admission control */ 567 if (sctype & HFSC_REALTIMESC) { 568 if (!is_gsc_under_sc(&hfsc_clinfo->gen_rsc, sc)) { 569 /* admission control failure */ 570 return (QOPERR_ADMISSION); 571 } 572 573 gsc_sub_sc(&parent_clinfo->gen_rsc, &hfsc_clinfo->rsc); 574 gsc_add_sc(&parent_clinfo->gen_rsc, sc); 575 if (!is_gsc_under_sc(&parent_clinfo->gen_rsc, 576 &parent_clinfo->rsc)) { 577 /* admission control failure */ 578 gsc_sub_sc(&parent_clinfo->gen_rsc, sc); 579 gsc_add_sc(&parent_clinfo->gen_rsc, &hfsc_clinfo->rsc); 580 return (QOPERR_ADMISSION_NOBW); 581 } 582 hfsc_clinfo->rsc = *sc; 583 } 584 if (sctype & HFSC_LINKSHARINGSC) { 585 if (!is_gsc_under_sc(&hfsc_clinfo->gen_fsc, sc)) { 586 /* admission control failure */ 587 return (QOPERR_ADMISSION); 588 } 589 590 gsc_sub_sc(&parent_clinfo->gen_fsc, &hfsc_clinfo->fsc); 591 gsc_add_sc(&parent_clinfo->gen_fsc, sc); 592 if (!is_gsc_under_sc(&parent_clinfo->gen_fsc, 593 &parent_clinfo->fsc)) { 594 /* admission control failure */ 595 gsc_sub_sc(&parent_clinfo->gen_fsc, sc); 596 gsc_add_sc(&parent_clinfo->gen_fsc, &hfsc_clinfo->fsc); 597 return (QOPERR_ADMISSION_NOBW); 598 } 599 hfsc_clinfo->fsc = *sc; 600 } 601 602 error = qop_modify_class(clinfo, (void *)((long)sctype)); 603 if (error == 0) 604 return (0); 605 606 /* modify failed!, restore the old service curves */ 607 if (sctype & HFSC_REALTIMESC) { 608 gsc_sub_sc(&parent_clinfo->gen_rsc, sc); 609 gsc_add_sc(&parent_clinfo->gen_rsc, &rsc); 610 hfsc_clinfo->rsc = rsc; 611 } 612 if (sctype & HFSC_LINKSHARINGSC) { 613 gsc_sub_sc(&parent_clinfo->gen_fsc, sc); 614 gsc_add_sc(&parent_clinfo->gen_fsc, &fsc); 615 hfsc_clinfo->fsc = fsc; 616 } 617 return (error); 618 } 619 620 /* 621 * sanity check at enabling hfsc: 622 * 1. there must one default class for an interface 623 * 2. the default class must be a leaf class 624 * 3. an internal class should not have filters 625 * (rule 2 and 3 are due to the fact that the hfsc link-sharing algorithm 626 * do not schedule internal classes.) 627 */ 628 static int 629 qop_hfsc_enable_hook(struct ifinfo *ifinfo) 630 { 631 struct hfsc_ifinfo *hfsc_ifinfo; 632 struct classinfo *clinfo; 633 634 hfsc_ifinfo = ifinfo->private; 635 if (hfsc_ifinfo->default_class == NULL) { 636 LOG(LOG_ERR, 0, "hfsc: no default class on interface %s!", 637 ifinfo->ifname); 638 return (QOPERR_CLASS); 639 } else if (hfsc_ifinfo->default_class->child != NULL) { 640 LOG(LOG_ERR, 0, "hfsc: default class on %s must be a leaf!", 641 ifinfo->ifname); 642 return (QOPERR_CLASS); 643 } 644 645 LIST_FOREACH(clinfo, &ifinfo->cllist, next) { 646 if (clinfo->child != NULL && !LIST_EMPTY(&clinfo->fltrlist)) { 647 LOG(LOG_ERR, 0, 648 "hfsc: internal class \"%s\" should not have a filter!", 649 clinfo->clname); 650 return (QOPERR_CLASS); 651 } 652 } 653 654 return (0); 655 } 656 657 static int 658 validate_sc(struct service_curve *sc) 659 { 660 /* the 1st segment of a concave curve must be zero */ 661 if (sc->m1 < sc->m2 && sc->m1 != 0) { 662 LOG(LOG_ERR, 0, "m1 must be 0 for convex!"); 663 return (-1); 664 } 665 return (0); 666 } 667 668 /* 669 * admission control using generalized service curve 670 */ 671 #define INFINITY 1e500 /* IEEE: positive infinity */ 672 673 /* add a new service curve to a generilized service curve */ 674 static void 675 gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc) 676 { 677 if (is_sc_null(sc)) 678 return; 679 if (sc->d != 0) 680 gsc_add_seg(gsc, 0, 0, (double)sc->d, (double)sc->m1); 681 gsc_add_seg(gsc, (double)sc->d, 0, INFINITY, (double)sc->m2); 682 } 683 684 /* subtract a service curve from a generilized service curve */ 685 static void 686 gsc_sub_sc(struct gen_sc *gsc, struct service_curve *sc) 687 { 688 if (is_sc_null(sc)) 689 return; 690 if (sc->d != 0) 691 gsc_sub_seg(gsc, 0, 0, (double)sc->d, (double)sc->m1); 692 gsc_sub_seg(gsc, (double)sc->d, 0, INFINITY, (double)sc->m2); 693 } 694 695 /* 696 * check whether all points of a generalized service curve have 697 * their y-coordinates no larger than a given two-piece linear 698 * service curve. 699 */ 700 static int 701 is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc) 702 { 703 struct segment *s, *last, *end; 704 double y; 705 706 if (is_sc_null(sc)) { 707 if (LIST_EMPTY(gsc)) 708 return (1); 709 LIST_FOREACH(s, gsc, _next) { 710 if (s->m != 0) 711 return (0); 712 } 713 return (1); 714 } 715 /* 716 * gsc has a dummy entry at the end with x = INFINITY. 717 * loop through up to this dummy entry. 718 */ 719 end = gsc_getentry(gsc, INFINITY); 720 if (end == NULL) 721 return (1); 722 last = NULL; 723 for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) { 724 if (s->y > sc_x2y(sc, s->x)) 725 return (0); 726 last = s; 727 } 728 /* last now holds the real last segment */ 729 if (last == NULL) 730 return (1); 731 if (last->m > sc->m2) 732 return (0); 733 if (last->x < sc->d && last->m > sc->m1) { 734 y = last->y + (sc->d - last->x) * last->m; 735 if (y > sc_x2y(sc, sc->d)) 736 return (0); 737 } 738 return (1); 739 } 740 741 static void 742 gsc_destroy(struct gen_sc *gsc) 743 { 744 struct segment *s; 745 746 while ((s = LIST_FIRST(gsc)) != NULL) { 747 LIST_REMOVE(s, _next); 748 free(s); 749 } 750 } 751 752 /* 753 * return a segment entry starting at x. 754 * if gsc has no entry starting at x, a new entry is created at x. 755 */ 756 static struct segment * 757 gsc_getentry(struct gen_sc *gsc, double x) 758 { 759 struct segment *new, *prev, *s; 760 761 prev = NULL; 762 LIST_FOREACH(s, gsc, _next) { 763 if (s->x == x) 764 return (s); /* matching entry found */ 765 else if (s->x < x) 766 prev = s; 767 else 768 break; 769 } 770 771 /* we have to create a new entry */ 772 if ((new = calloc(1, sizeof(struct segment))) == NULL) 773 return (NULL); 774 775 new->x = x; 776 if (x == INFINITY || s == NULL) 777 new->d = 0; 778 else if (s->x == INFINITY) 779 new->d = INFINITY; 780 else 781 new->d = s->x - x; 782 if (prev == NULL) { 783 /* insert the new entry at the head of the list */ 784 new->y = 0; 785 new->m = 0; 786 LIST_INSERT_HEAD(gsc, new, _next); 787 } else { 788 /* 789 * the start point intersects with the segment pointed by 790 * prev. divide prev into 2 segments 791 */ 792 if (x == INFINITY) { 793 prev->d = INFINITY; 794 if (prev->m == 0) 795 new->y = prev->y; 796 else 797 new->y = INFINITY; 798 } else { 799 prev->d = x - prev->x; 800 new->y = prev->d * prev->m + prev->y; 801 } 802 new->m = prev->m; 803 LIST_INSERT_AFTER(prev, new, _next); 804 } 805 return (new); 806 } 807 808 /* add a segment to a generalized service curve */ 809 static int 810 gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m) 811 { 812 struct segment *start, *end, *s; 813 double x2; 814 815 if (d == INFINITY) 816 x2 = INFINITY; 817 else 818 x2 = x + d; 819 start = gsc_getentry(gsc, x); 820 end = gsc_getentry(gsc, x2); 821 if (start == NULL || end == NULL) 822 return (-1); 823 824 for (s = start; s != end; s = LIST_NEXT(s, _next)) { 825 s->m += m; 826 s->y += y + (s->x - x) * m; 827 } 828 829 end = gsc_getentry(gsc, INFINITY); 830 for (; s != end; s = LIST_NEXT(s, _next)) { 831 s->y += m * d; 832 } 833 834 return (0); 835 } 836 837 /* subtract a segment from a generalized service curve */ 838 static int 839 gsc_sub_seg(struct gen_sc *gsc, double x, double y, double d, double m) 840 { 841 if (gsc_add_seg(gsc, x, y, d, -m) < 0) 842 return (-1); 843 gsc_compress(gsc); 844 return (0); 845 } 846 847 /* 848 * collapse adjacent segments with the same slope 849 */ 850 static void 851 gsc_compress(struct gen_sc *gsc) 852 { 853 struct segment *s, *next; 854 855 again: 856 LIST_FOREACH(s, gsc, _next) { 857 858 if ((next = LIST_NEXT(s, _next)) == NULL) { 859 if (LIST_FIRST(gsc) == s && s->m == 0) { 860 /* 861 * if this is the only entry and its 862 * slope is 0, it's a remaining dummy 863 * entry. we can discard it. 864 */ 865 LIST_REMOVE(s, _next); 866 free(s); 867 } 868 break; 869 } 870 871 if (s->x == next->x) { 872 /* discard this entry */ 873 LIST_REMOVE(s, _next); 874 free(s); 875 goto again; 876 } else if (s->m == next->m) { 877 /* join the two entries */ 878 if (s->d != INFINITY && next->d != INFINITY) 879 s->d += next->d; 880 LIST_REMOVE(next, _next); 881 free(next); 882 goto again; 883 } 884 } 885 } 886 887 /* get y-projection of a service curve */ 888 static double 889 sc_x2y(struct service_curve *sc, double x) 890 { 891 double y; 892 893 if (x <= (double)sc->d) 894 /* y belongs to the 1st segment */ 895 y = x * (double)sc->m1; 896 else 897 /* y belongs to the 2nd segment */ 898 y = (double)sc->d * (double)sc->m1 899 + (x - (double)sc->d) * (double)sc->m2; 900 return (y); 901 } 902 903 /* 904 * system call interfaces for qdisc_ops 905 */ 906 static int 907 hfsc_attach(struct ifinfo *ifinfo) 908 { 909 struct hfsc_attach attach; 910 911 if (hfsc_fd < 0 && 912 (hfsc_fd = open(HFSC_DEVICE, O_RDWR)) < 0 && 913 (hfsc_fd = open_module(HFSC_DEVICE, O_RDWR)) < 0) { 914 LOG(LOG_ERR, errno, "HFSC open"); 915 return (QOPERR_SYSCALL); 916 } 917 918 hfsc_refcount++; 919 memset(&attach, 0, sizeof(attach)); 920 strncpy(attach.iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ); 921 attach.bandwidth = ifinfo->bandwidth; 922 923 if (ioctl(hfsc_fd, HFSC_IF_ATTACH, &attach) < 0) 924 return (QOPERR_SYSCALL); 925 return (0); 926 } 927 928 static int 929 hfsc_detach(struct ifinfo *ifinfo) 930 { 931 struct hfsc_interface iface; 932 933 memset(&iface, 0, sizeof(iface)); 934 strncpy(iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ); 935 936 if (ioctl(hfsc_fd, HFSC_IF_DETACH, &iface) < 0) 937 return (QOPERR_SYSCALL); 938 939 if (--hfsc_refcount == 0) { 940 close(hfsc_fd); 941 hfsc_fd = -1; 942 } 943 return (0); 944 } 945 946 static int 947 hfsc_clear(struct ifinfo *ifinfo) 948 { 949 struct hfsc_interface iface; 950 951 memset(&iface, 0, sizeof(iface)); 952 strncpy(iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ); 953 954 if (ioctl(hfsc_fd, HFSC_CLEAR_HIERARCHY, &iface) < 0) 955 return (QOPERR_SYSCALL); 956 return (0); 957 } 958 959 static int 960 hfsc_enable(struct ifinfo *ifinfo) 961 { 962 struct hfsc_interface iface; 963 964 memset(&iface, 0, sizeof(iface)); 965 strncpy(iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ); 966 967 if (ioctl(hfsc_fd, HFSC_ENABLE, &iface) < 0) 968 return (QOPERR_SYSCALL); 969 return (0); 970 } 971 972 static int 973 hfsc_disable(struct ifinfo *ifinfo) 974 { 975 struct hfsc_interface iface; 976 977 memset(&iface, 0, sizeof(iface)); 978 strncpy(iface.hfsc_ifname, ifinfo->ifname, IFNAMSIZ); 979 980 if (ioctl(hfsc_fd, HFSC_DISABLE, &iface) < 0) 981 return (QOPERR_SYSCALL); 982 return (0); 983 } 984 985 static int 986 hfsc_add_class(struct classinfo *clinfo) 987 { 988 struct hfsc_add_class class_add; 989 struct hfsc_classinfo *hfsc_clinfo; 990 struct hfsc_ifinfo *hfsc_ifinfo; 991 992 /* root class is a dummy class */ 993 if (clinfo->parent == NULL) { 994 clinfo->handle = HFSC_ROOTCLASS_HANDLE; 995 return (0); 996 } 997 998 hfsc_ifinfo = clinfo->ifinfo->private; 999 hfsc_clinfo = clinfo->private; 1000 1001 memset(&class_add, 0, sizeof(class_add)); 1002 strncpy(class_add.iface.hfsc_ifname, clinfo->ifinfo->ifname, IFNAMSIZ); 1003 if (clinfo->parent == hfsc_ifinfo->root_class) 1004 class_add.parent_handle = HFSC_ROOTCLASS_HANDLE; 1005 else 1006 class_add.parent_handle = clinfo->parent->handle; 1007 class_add.service_curve = hfsc_clinfo->rsc; 1008 class_add.qlimit = hfsc_clinfo->qlimit; 1009 class_add.flags = hfsc_clinfo->flags; 1010 if (ioctl(hfsc_fd, HFSC_ADD_CLASS, &class_add) < 0) { 1011 clinfo->handle = HFSC_NULLCLASS_HANDLE; 1012 return (QOPERR_SYSCALL); 1013 } 1014 clinfo->handle = class_add.class_handle; 1015 return (0); 1016 } 1017 1018 static int 1019 hfsc_modify_class(struct classinfo *clinfo, void *arg) 1020 { 1021 struct hfsc_modify_class class_mod; 1022 struct hfsc_classinfo *hfsc_clinfo; 1023 long sctype; 1024 1025 sctype = (long)arg; 1026 hfsc_clinfo = clinfo->private; 1027 1028 memset(&class_mod, 0, sizeof(class_mod)); 1029 strncpy(class_mod.iface.hfsc_ifname, clinfo->ifinfo->ifname, IFNAMSIZ); 1030 class_mod.class_handle = clinfo->handle; 1031 if (sctype & HFSC_REALTIMESC) 1032 class_mod.service_curve = hfsc_clinfo->rsc; 1033 else if (sctype & HFSC_LINKSHARINGSC) 1034 class_mod.service_curve = hfsc_clinfo->fsc; 1035 else 1036 return (QOPERR_INVAL); 1037 class_mod.sctype = sctype; 1038 1039 if (ioctl(hfsc_fd, HFSC_MOD_CLASS, &class_mod) < 0) 1040 return (QOPERR_SYSCALL); 1041 return (0); 1042 } 1043 1044 static int 1045 hfsc_delete_class(struct classinfo *clinfo) 1046 { 1047 struct hfsc_delete_class class_delete; 1048 1049 if (clinfo->handle == HFSC_NULLCLASS_HANDLE || 1050 clinfo->handle == HFSC_ROOTCLASS_HANDLE) 1051 return (0); 1052 1053 memset(&class_delete, 0, sizeof(class_delete)); 1054 strncpy(class_delete.iface.hfsc_ifname, clinfo->ifinfo->ifname, 1055 IFNAMSIZ); 1056 class_delete.class_handle = clinfo->handle; 1057 1058 if (ioctl(hfsc_fd, HFSC_DEL_CLASS, &class_delete) < 0) 1059 return (QOPERR_SYSCALL); 1060 return (0); 1061 } 1062 1063 static int 1064 hfsc_add_filter(struct fltrinfo *fltrinfo) 1065 { 1066 struct hfsc_add_filter fltr_add; 1067 1068 memset(&fltr_add, 0, sizeof(fltr_add)); 1069 strncpy(fltr_add.iface.hfsc_ifname, fltrinfo->clinfo->ifinfo->ifname, 1070 IFNAMSIZ); 1071 fltr_add.class_handle = fltrinfo->clinfo->handle; 1072 fltr_add.filter = fltrinfo->fltr; 1073 1074 if (ioctl(hfsc_fd, HFSC_ADD_FILTER, &fltr_add) < 0) 1075 return (QOPERR_SYSCALL); 1076 fltrinfo->handle = fltr_add.filter_handle; 1077 return (0); 1078 } 1079 1080 static int 1081 hfsc_delete_filter(struct fltrinfo *fltrinfo) 1082 { 1083 struct hfsc_delete_filter fltr_del; 1084 1085 memset(&fltr_del, 0, sizeof(fltr_del)); 1086 strncpy(fltr_del.iface.hfsc_ifname, fltrinfo->clinfo->ifinfo->ifname, 1087 IFNAMSIZ); 1088 fltr_del.filter_handle = fltrinfo->handle; 1089 1090 if (ioctl(hfsc_fd, HFSC_DEL_FILTER, &fltr_del) < 0) 1091 return (QOPERR_SYSCALL); 1092 return (0); 1093 } 1094 1095 1096