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