1 1.31 joe /* $NetBSD: altq_hfsc.c,v 1.31 2025/01/08 13:00:04 joe Exp $ */ 2 1.19 peter /* $KAME: altq_hfsc.c,v 1.26 2005/04/13 03:44:24 suz Exp $ */ 3 1.1 thorpej 4 1.1 thorpej /* 5 1.1 thorpej * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved. 6 1.1 thorpej * 7 1.1 thorpej * Permission to use, copy, modify, and distribute this software and 8 1.1 thorpej * its documentation is hereby granted (including for commercial or 9 1.1 thorpej * for-profit use), provided that both the copyright notice and this 10 1.1 thorpej * permission notice appear in all copies of the software, derivative 11 1.19 peter * works, or modified versions, and any portions thereof. 12 1.1 thorpej * 13 1.1 thorpej * THIS SOFTWARE IS EXPERIMENTAL AND IS KNOWN TO HAVE BUGS, SOME OF 14 1.1 thorpej * WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON PROVIDES THIS 15 1.1 thorpej * SOFTWARE IN ITS ``AS IS'' CONDITION, AND ANY EXPRESS OR IMPLIED 16 1.1 thorpej * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 1.1 thorpej * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 1.1 thorpej * DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 19 1.1 thorpej * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 1.1 thorpej * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 22 1.1 thorpej * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 1.1 thorpej * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 1.1 thorpej * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 25 1.1 thorpej * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 1.1 thorpej * DAMAGE. 27 1.1 thorpej * 28 1.1 thorpej * Carnegie Mellon encourages (but does not require) users of this 29 1.1 thorpej * software to return any improvements or extensions that they make, 30 1.1 thorpej * and to grant Carnegie Mellon the rights to redistribute these 31 1.1 thorpej * changes without encumbrance. 32 1.1 thorpej */ 33 1.1 thorpej /* 34 1.1 thorpej * H-FSC is described in Proceedings of SIGCOMM'97, 35 1.10 perry * "A Hierarchical Fair Service Curve Algorithm for Link-Sharing, 36 1.1 thorpej * Real-Time and Priority Service" 37 1.1 thorpej * by Ion Stoica, Hui Zhang, and T. S. Eugene Ng. 38 1.19 peter * 39 1.19 peter * Oleg Cherevko <olwi (at) aq.ml.com.ua> added the upperlimit for link-sharing. 40 1.19 peter * when a class has an upperlimit, the fit-time is computed from the 41 1.19 peter * upperlimit service curve. the link-sharing scheduler does not schedule 42 1.19 peter * a class whose fit-time exceeds the current time. 43 1.1 thorpej */ 44 1.5 lukem 45 1.5 lukem #include <sys/cdefs.h> 46 1.31 joe __KERNEL_RCSID(0, "$NetBSD: altq_hfsc.c,v 1.31 2025/01/08 13:00:04 joe Exp $"); 47 1.1 thorpej 48 1.19 peter #ifdef _KERNEL_OPT 49 1.1 thorpej #include "opt_altq.h" 50 1.1 thorpej #include "opt_inet.h" 51 1.20 peter #include "pf.h" 52 1.1 thorpej #endif 53 1.1 thorpej 54 1.1 thorpej #ifdef ALTQ_HFSC /* hfsc is enabled by ALTQ_HFSC option in opt_altq.h */ 55 1.1 thorpej 56 1.1 thorpej #include <sys/param.h> 57 1.1 thorpej #include <sys/malloc.h> 58 1.1 thorpej #include <sys/mbuf.h> 59 1.1 thorpej #include <sys/socket.h> 60 1.19 peter #include <sys/systm.h> 61 1.19 peter #include <sys/errno.h> 62 1.19 peter #include <sys/queue.h> 63 1.19 peter #if 1 /* ALTQ3_COMPAT */ 64 1.1 thorpej #include <sys/sockio.h> 65 1.1 thorpej #include <sys/proc.h> 66 1.1 thorpej #include <sys/kernel.h> 67 1.19 peter #endif /* ALTQ3_COMPAT */ 68 1.16 christos #include <sys/kauth.h> 69 1.1 thorpej 70 1.1 thorpej #include <net/if.h> 71 1.19 peter #include <netinet/in.h> 72 1.1 thorpej 73 1.20 peter #if NPF > 0 74 1.19 peter #include <net/pfvar.h> 75 1.20 peter #endif 76 1.1 thorpej #include <altq/altq.h> 77 1.19 peter #include <altq/altq_hfsc.h> 78 1.19 peter #ifdef ALTQ3_COMPAT 79 1.1 thorpej #include <altq/altq_conf.h> 80 1.19 peter #endif 81 1.1 thorpej 82 1.1 thorpej /* 83 1.1 thorpej * function prototypes 84 1.1 thorpej */ 85 1.19 peter static int hfsc_clear_interface(struct hfsc_if *); 86 1.19 peter static int hfsc_request(struct ifaltq *, int, void *); 87 1.19 peter static void hfsc_purge(struct hfsc_if *); 88 1.19 peter static struct hfsc_class *hfsc_class_create(struct hfsc_if *, 89 1.19 peter struct service_curve *, struct service_curve *, struct service_curve *, 90 1.19 peter struct hfsc_class *, int, int, int); 91 1.19 peter static int hfsc_class_destroy(struct hfsc_class *); 92 1.19 peter static struct hfsc_class *hfsc_nextclass(struct hfsc_class *); 93 1.26 knakahar static int hfsc_enqueue(struct ifaltq *, struct mbuf *); 94 1.19 peter static struct mbuf *hfsc_dequeue(struct ifaltq *, int); 95 1.19 peter 96 1.19 peter static int hfsc_addq(struct hfsc_class *, struct mbuf *); 97 1.19 peter static struct mbuf *hfsc_getq(struct hfsc_class *); 98 1.19 peter static struct mbuf *hfsc_pollq(struct hfsc_class *); 99 1.19 peter static void hfsc_purgeq(struct hfsc_class *); 100 1.19 peter 101 1.19 peter static void update_cfmin(struct hfsc_class *); 102 1.19 peter static void set_active(struct hfsc_class *, int); 103 1.19 peter static void set_passive(struct hfsc_class *); 104 1.19 peter 105 1.19 peter static void init_ed(struct hfsc_class *, int); 106 1.19 peter static void update_ed(struct hfsc_class *, int); 107 1.19 peter static void update_d(struct hfsc_class *, int); 108 1.19 peter static void init_vf(struct hfsc_class *, int); 109 1.19 peter static void update_vf(struct hfsc_class *, int, u_int64_t); 110 1.19 peter static ellist_t *ellist_alloc(void); 111 1.19 peter static void ellist_destroy(ellist_t *); 112 1.19 peter static void ellist_insert(struct hfsc_class *); 113 1.19 peter static void ellist_remove(struct hfsc_class *); 114 1.19 peter static void ellist_update(struct hfsc_class *); 115 1.19 peter struct hfsc_class *ellist_get_mindl(ellist_t *, u_int64_t); 116 1.19 peter static actlist_t *actlist_alloc(void); 117 1.19 peter static void actlist_destroy(actlist_t *); 118 1.19 peter static void actlist_insert(struct hfsc_class *); 119 1.19 peter static void actlist_remove(struct hfsc_class *); 120 1.19 peter static void actlist_update(struct hfsc_class *); 121 1.19 peter 122 1.19 peter static struct hfsc_class *actlist_firstfit(struct hfsc_class *, 123 1.19 peter u_int64_t); 124 1.19 peter 125 1.19 peter static inline u_int64_t seg_x2y(u_int64_t, u_int64_t); 126 1.19 peter static inline u_int64_t seg_y2x(u_int64_t, u_int64_t); 127 1.19 peter static inline u_int64_t m2sm(u_int); 128 1.19 peter static inline u_int64_t m2ism(u_int); 129 1.19 peter static inline u_int64_t d2dx(u_int); 130 1.19 peter static u_int sm2m(u_int64_t); 131 1.19 peter static u_int dx2d(u_int64_t); 132 1.19 peter 133 1.19 peter static void sc2isc(struct service_curve *, struct internal_sc *); 134 1.19 peter static void rtsc_init(struct runtime_sc *, struct internal_sc *, 135 1.19 peter u_int64_t, u_int64_t); 136 1.19 peter static u_int64_t rtsc_y2x(struct runtime_sc *, u_int64_t); 137 1.19 peter static u_int64_t rtsc_x2y(struct runtime_sc *, u_int64_t); 138 1.19 peter static void rtsc_min(struct runtime_sc *, struct internal_sc *, 139 1.19 peter u_int64_t, u_int64_t); 140 1.19 peter 141 1.19 peter static void get_class_stats(struct hfsc_classstats *, 142 1.19 peter struct hfsc_class *); 143 1.19 peter static struct hfsc_class *clh_to_clp(struct hfsc_if *, u_int32_t); 144 1.19 peter 145 1.19 peter 146 1.19 peter #ifdef ALTQ3_COMPAT 147 1.19 peter static struct hfsc_if *hfsc_attach(struct ifaltq *, u_int); 148 1.25 christos static void hfsc_detach(struct hfsc_if *); 149 1.19 peter static int hfsc_class_modify(struct hfsc_class *, struct service_curve *, 150 1.19 peter struct service_curve *, struct service_curve *); 151 1.19 peter 152 1.19 peter static int hfsccmd_if_attach(struct hfsc_attach *); 153 1.19 peter static int hfsccmd_if_detach(struct hfsc_interface *); 154 1.19 peter static int hfsccmd_add_class(struct hfsc_add_class *); 155 1.19 peter static int hfsccmd_delete_class(struct hfsc_delete_class *); 156 1.19 peter static int hfsccmd_modify_class(struct hfsc_modify_class *); 157 1.19 peter static int hfsccmd_add_filter(struct hfsc_add_filter *); 158 1.19 peter static int hfsccmd_delete_filter(struct hfsc_delete_filter *); 159 1.19 peter static int hfsccmd_class_stats(struct hfsc_class_stats *); 160 1.19 peter 161 1.19 peter altqdev_decl(hfsc); 162 1.19 peter #endif /* ALTQ3_COMPAT */ 163 1.1 thorpej 164 1.1 thorpej /* 165 1.1 thorpej * macros 166 1.1 thorpej */ 167 1.1 thorpej #define is_a_parent_class(cl) ((cl)->cl_children != NULL) 168 1.1 thorpej 169 1.19 peter #define HT_INFINITY 0xffffffffffffffffLL /* infinite time value */ 170 1.19 peter 171 1.19 peter #ifdef ALTQ3_COMPAT 172 1.1 thorpej /* hif_list keeps all hfsc_if's allocated. */ 173 1.1 thorpej static struct hfsc_if *hif_list = NULL; 174 1.19 peter #endif /* ALTQ3_COMPAT */ 175 1.19 peter 176 1.20 peter #if NPF > 0 177 1.19 peter int 178 1.19 peter hfsc_pfattach(struct pf_altq *a) 179 1.19 peter { 180 1.19 peter struct ifnet *ifp; 181 1.19 peter int s, error; 182 1.19 peter 183 1.19 peter if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL) 184 1.31 joe return EINVAL; 185 1.19 peter s = splnet(); 186 1.19 peter error = altq_attach(&ifp->if_snd, ALTQT_HFSC, a->altq_disc, 187 1.19 peter hfsc_enqueue, hfsc_dequeue, hfsc_request, NULL, NULL); 188 1.19 peter splx(s); 189 1.31 joe return error; 190 1.19 peter } 191 1.1 thorpej 192 1.19 peter int 193 1.19 peter hfsc_add_altq(struct pf_altq *a) 194 1.1 thorpej { 195 1.1 thorpej struct hfsc_if *hif; 196 1.19 peter struct ifnet *ifp; 197 1.19 peter 198 1.19 peter if ((ifp = ifunit(a->ifname)) == NULL) 199 1.31 joe return EINVAL; 200 1.19 peter if (!ALTQ_IS_READY(&ifp->if_snd)) 201 1.31 joe return ENODEV; 202 1.1 thorpej 203 1.13 christos hif = malloc(sizeof(struct hfsc_if), M_DEVBUF, M_WAITOK|M_ZERO); 204 1.1 thorpej if (hif == NULL) 205 1.31 joe return ENOMEM; 206 1.1 thorpej 207 1.1 thorpej hif->hif_eligible = ellist_alloc(); 208 1.1 thorpej if (hif->hif_eligible == NULL) { 209 1.13 christos free(hif, M_DEVBUF); 210 1.31 joe return ENOMEM; 211 1.1 thorpej } 212 1.1 thorpej 213 1.19 peter hif->hif_ifq = &ifp->if_snd; 214 1.19 peter 215 1.19 peter /* keep the state in pf_altq */ 216 1.19 peter a->altq_disc = hif; 217 1.19 peter 218 1.31 joe return 0; 219 1.19 peter } 220 1.19 peter 221 1.19 peter int 222 1.19 peter hfsc_remove_altq(struct pf_altq *a) 223 1.19 peter { 224 1.19 peter struct hfsc_if *hif; 225 1.19 peter 226 1.19 peter if ((hif = a->altq_disc) == NULL) 227 1.31 joe return EINVAL; 228 1.19 peter a->altq_disc = NULL; 229 1.19 peter 230 1.19 peter (void)hfsc_clear_interface(hif); 231 1.19 peter (void)hfsc_class_destroy(hif->hif_rootclass); 232 1.19 peter 233 1.19 peter ellist_destroy(hif->hif_eligible); 234 1.19 peter 235 1.19 peter free(hif, M_DEVBUF); 236 1.19 peter 237 1.31 joe return 0; 238 1.19 peter } 239 1.19 peter 240 1.19 peter int 241 1.19 peter hfsc_add_queue(struct pf_altq *a) 242 1.19 peter { 243 1.19 peter struct hfsc_if *hif; 244 1.19 peter struct hfsc_class *cl, *parent; 245 1.19 peter struct hfsc_opts *opts; 246 1.19 peter struct service_curve rtsc, lssc, ulsc; 247 1.19 peter 248 1.19 peter if ((hif = a->altq_disc) == NULL) 249 1.31 joe return EINVAL; 250 1.19 peter 251 1.19 peter opts = &a->pq_u.hfsc_opts; 252 1.19 peter 253 1.19 peter if (a->parent_qid == HFSC_NULLCLASS_HANDLE && 254 1.19 peter hif->hif_rootclass == NULL) 255 1.19 peter parent = NULL; 256 1.19 peter else if ((parent = clh_to_clp(hif, a->parent_qid)) == NULL) 257 1.31 joe return EINVAL; 258 1.19 peter 259 1.19 peter if (a->qid == 0) 260 1.31 joe return EINVAL; 261 1.19 peter 262 1.19 peter if (clh_to_clp(hif, a->qid) != NULL) 263 1.31 joe return EBUSY; 264 1.1 thorpej 265 1.19 peter rtsc.m1 = opts->rtsc_m1; 266 1.19 peter rtsc.d = opts->rtsc_d; 267 1.19 peter rtsc.m2 = opts->rtsc_m2; 268 1.19 peter lssc.m1 = opts->lssc_m1; 269 1.19 peter lssc.d = opts->lssc_d; 270 1.19 peter lssc.m2 = opts->lssc_m2; 271 1.19 peter ulsc.m1 = opts->ulsc_m1; 272 1.19 peter ulsc.d = opts->ulsc_d; 273 1.19 peter ulsc.m2 = opts->ulsc_m2; 274 1.1 thorpej 275 1.19 peter cl = hfsc_class_create(hif, &rtsc, &lssc, &ulsc, 276 1.19 peter parent, a->qlimit, opts->flags, a->qid); 277 1.19 peter if (cl == NULL) 278 1.31 joe return ENOMEM; 279 1.1 thorpej 280 1.31 joe return 0; 281 1.1 thorpej } 282 1.1 thorpej 283 1.19 peter int 284 1.19 peter hfsc_remove_queue(struct pf_altq *a) 285 1.19 peter { 286 1.1 thorpej struct hfsc_if *hif; 287 1.19 peter struct hfsc_class *cl; 288 1.19 peter 289 1.19 peter if ((hif = a->altq_disc) == NULL) 290 1.31 joe return EINVAL; 291 1.19 peter 292 1.19 peter if ((cl = clh_to_clp(hif, a->qid)) == NULL) 293 1.31 joe return EINVAL; 294 1.19 peter 295 1.19 peter return (hfsc_class_destroy(cl)); 296 1.19 peter } 297 1.19 peter 298 1.19 peter int 299 1.19 peter hfsc_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) 300 1.1 thorpej { 301 1.19 peter struct hfsc_if *hif; 302 1.19 peter struct hfsc_class *cl; 303 1.19 peter struct hfsc_classstats stats; 304 1.19 peter int error = 0; 305 1.1 thorpej 306 1.19 peter if ((hif = altq_lookup(a->ifname, ALTQT_HFSC)) == NULL) 307 1.31 joe return EBADF; 308 1.10 perry 309 1.19 peter if ((cl = clh_to_clp(hif, a->qid)) == NULL) 310 1.31 joe return EINVAL; 311 1.1 thorpej 312 1.19 peter if (*nbytes < sizeof(stats)) 313 1.31 joe return EINVAL; 314 1.1 thorpej 315 1.27 riastrad memset(&stats, 0, sizeof(stats)); 316 1.19 peter get_class_stats(&stats, cl); 317 1.1 thorpej 318 1.23 christos if ((error = copyout((void *)&stats, ubuf, sizeof(stats))) != 0) 319 1.31 joe return error; 320 1.19 peter *nbytes = sizeof(stats); 321 1.31 joe return 0; 322 1.1 thorpej } 323 1.20 peter #endif /* NPF > 0 */ 324 1.1 thorpej 325 1.1 thorpej /* 326 1.1 thorpej * bring the interface back to the initial state by discarding 327 1.1 thorpej * all the filters and classes except the root class. 328 1.1 thorpej */ 329 1.1 thorpej static int 330 1.19 peter hfsc_clear_interface(struct hfsc_if *hif) 331 1.1 thorpej { 332 1.1 thorpej struct hfsc_class *cl; 333 1.1 thorpej 334 1.19 peter #ifdef ALTQ3_COMPAT 335 1.1 thorpej /* free the filters for this interface */ 336 1.1 thorpej acc_discard_filters(&hif->hif_classifier, NULL, 1); 337 1.19 peter #endif 338 1.1 thorpej 339 1.1 thorpej /* clear out the classes */ 340 1.19 peter while (hif->hif_rootclass != NULL && 341 1.19 peter (cl = hif->hif_rootclass->cl_children) != NULL) { 342 1.1 thorpej /* 343 1.1 thorpej * remove the first leaf class found in the hierarchy 344 1.1 thorpej * then start over 345 1.1 thorpej */ 346 1.1 thorpej for (; cl != NULL; cl = hfsc_nextclass(cl)) { 347 1.1 thorpej if (!is_a_parent_class(cl)) { 348 1.1 thorpej (void)hfsc_class_destroy(cl); 349 1.1 thorpej break; 350 1.1 thorpej } 351 1.1 thorpej } 352 1.1 thorpej } 353 1.10 perry 354 1.31 joe return 0; 355 1.1 thorpej } 356 1.1 thorpej 357 1.1 thorpej static int 358 1.22 christos hfsc_request(struct ifaltq *ifq, int req, void *arg) 359 1.1 thorpej { 360 1.1 thorpej struct hfsc_if *hif = (struct hfsc_if *)ifq->altq_disc; 361 1.1 thorpej 362 1.1 thorpej switch (req) { 363 1.1 thorpej case ALTRQ_PURGE: 364 1.1 thorpej hfsc_purge(hif); 365 1.1 thorpej break; 366 1.1 thorpej } 367 1.31 joe return 0; 368 1.1 thorpej } 369 1.1 thorpej 370 1.1 thorpej /* discard all the queued packets on the interface */ 371 1.1 thorpej static void 372 1.19 peter hfsc_purge(struct hfsc_if *hif) 373 1.1 thorpej { 374 1.1 thorpej struct hfsc_class *cl; 375 1.1 thorpej 376 1.1 thorpej for (cl = hif->hif_rootclass; cl != NULL; cl = hfsc_nextclass(cl)) 377 1.1 thorpej if (!qempty(cl->cl_q)) 378 1.1 thorpej hfsc_purgeq(cl); 379 1.1 thorpej if (ALTQ_IS_ENABLED(hif->hif_ifq)) 380 1.1 thorpej hif->hif_ifq->ifq_len = 0; 381 1.1 thorpej } 382 1.1 thorpej 383 1.1 thorpej struct hfsc_class * 384 1.19 peter hfsc_class_create(struct hfsc_if *hif, struct service_curve *rsc, 385 1.19 peter struct service_curve *fsc, struct service_curve *usc, 386 1.19 peter struct hfsc_class *parent, int qlimit, int flags, int qid) 387 1.1 thorpej { 388 1.1 thorpej struct hfsc_class *cl, *p; 389 1.19 peter int i, s; 390 1.19 peter 391 1.19 peter if (hif->hif_classes >= HFSC_MAX_CLASSES) 392 1.31 joe return NULL; 393 1.1 thorpej 394 1.1 thorpej #ifndef ALTQ_RED 395 1.1 thorpej if (flags & HFCF_RED) { 396 1.19 peter #ifdef ALTQ_DEBUG 397 1.1 thorpej printf("hfsc_class_create: RED not configured for HFSC!\n"); 398 1.19 peter #endif 399 1.31 joe return NULL; 400 1.1 thorpej } 401 1.1 thorpej #endif 402 1.1 thorpej 403 1.13 christos cl = malloc(sizeof(struct hfsc_class), M_DEVBUF, M_WAITOK|M_ZERO); 404 1.1 thorpej if (cl == NULL) 405 1.31 joe return NULL; 406 1.1 thorpej 407 1.13 christos cl->cl_q = malloc(sizeof(class_queue_t), M_DEVBUF, M_WAITOK|M_ZERO); 408 1.1 thorpej if (cl->cl_q == NULL) 409 1.1 thorpej goto err_ret; 410 1.1 thorpej 411 1.1 thorpej cl->cl_actc = actlist_alloc(); 412 1.1 thorpej if (cl->cl_actc == NULL) 413 1.1 thorpej goto err_ret; 414 1.1 thorpej 415 1.1 thorpej if (qlimit == 0) 416 1.1 thorpej qlimit = 50; /* use default */ 417 1.1 thorpej qlimit(cl->cl_q) = qlimit; 418 1.1 thorpej qtype(cl->cl_q) = Q_DROPTAIL; 419 1.1 thorpej qlen(cl->cl_q) = 0; 420 1.1 thorpej cl->cl_flags = flags; 421 1.1 thorpej #ifdef ALTQ_RED 422 1.1 thorpej if (flags & (HFCF_RED|HFCF_RIO)) { 423 1.1 thorpej int red_flags, red_pkttime; 424 1.19 peter u_int m2; 425 1.19 peter 426 1.19 peter m2 = 0; 427 1.19 peter if (rsc != NULL && rsc->m2 > m2) 428 1.19 peter m2 = rsc->m2; 429 1.19 peter if (fsc != NULL && fsc->m2 > m2) 430 1.19 peter m2 = fsc->m2; 431 1.19 peter if (usc != NULL && usc->m2 > m2) 432 1.19 peter m2 = usc->m2; 433 1.1 thorpej 434 1.1 thorpej red_flags = 0; 435 1.1 thorpej if (flags & HFCF_ECN) 436 1.1 thorpej red_flags |= REDF_ECN; 437 1.1 thorpej #ifdef ALTQ_RIO 438 1.1 thorpej if (flags & HFCF_CLEARDSCP) 439 1.1 thorpej red_flags |= RIOF_CLEARDSCP; 440 1.1 thorpej #endif 441 1.19 peter if (m2 < 8) 442 1.1 thorpej red_pkttime = 1000 * 1000 * 1000; /* 1 sec */ 443 1.1 thorpej else 444 1.1 thorpej red_pkttime = (int64_t)hif->hif_ifq->altq_ifp->if_mtu 445 1.19 peter * 1000 * 1000 * 1000 / (m2 / 8); 446 1.1 thorpej if (flags & HFCF_RED) { 447 1.19 peter cl->cl_red = red_alloc(0, 0, 448 1.19 peter qlimit(cl->cl_q) * 10/100, 449 1.19 peter qlimit(cl->cl_q) * 30/100, 450 1.19 peter red_flags, red_pkttime); 451 1.1 thorpej if (cl->cl_red != NULL) 452 1.1 thorpej qtype(cl->cl_q) = Q_RED; 453 1.1 thorpej } 454 1.1 thorpej #ifdef ALTQ_RIO 455 1.1 thorpej else { 456 1.1 thorpej cl->cl_red = (red_t *)rio_alloc(0, NULL, 457 1.19 peter red_flags, red_pkttime); 458 1.1 thorpej if (cl->cl_red != NULL) 459 1.1 thorpej qtype(cl->cl_q) = Q_RIO; 460 1.1 thorpej } 461 1.1 thorpej #endif 462 1.1 thorpej } 463 1.1 thorpej #endif /* ALTQ_RED */ 464 1.1 thorpej 465 1.19 peter if (rsc != NULL && (rsc->m1 != 0 || rsc->m2 != 0)) { 466 1.13 christos cl->cl_rsc = malloc(sizeof(struct internal_sc), M_DEVBUF, 467 1.13 christos M_WAITOK|M_ZERO); 468 1.1 thorpej if (cl->cl_rsc == NULL) 469 1.1 thorpej goto err_ret; 470 1.19 peter sc2isc(rsc, cl->cl_rsc); 471 1.1 thorpej rtsc_init(&cl->cl_deadline, cl->cl_rsc, 0, 0); 472 1.1 thorpej rtsc_init(&cl->cl_eligible, cl->cl_rsc, 0, 0); 473 1.19 peter } 474 1.19 peter if (fsc != NULL && (fsc->m1 != 0 || fsc->m2 != 0)) { 475 1.13 christos cl->cl_fsc = malloc(sizeof(struct internal_sc), M_DEVBUF, 476 1.13 christos M_WAITOK|M_ZERO); 477 1.1 thorpej if (cl->cl_fsc == NULL) 478 1.1 thorpej goto err_ret; 479 1.19 peter sc2isc(fsc, cl->cl_fsc); 480 1.1 thorpej rtsc_init(&cl->cl_virtual, cl->cl_fsc, 0, 0); 481 1.1 thorpej } 482 1.19 peter if (usc != NULL && (usc->m1 != 0 || usc->m2 != 0)) { 483 1.19 peter cl->cl_usc = malloc(sizeof(struct internal_sc), M_DEVBUF, 484 1.19 peter M_WAITOK|M_ZERO); 485 1.19 peter if (cl->cl_usc == NULL) 486 1.19 peter goto err_ret; 487 1.19 peter sc2isc(usc, cl->cl_usc); 488 1.19 peter rtsc_init(&cl->cl_ulimit, cl->cl_usc, 0, 0); 489 1.19 peter } 490 1.1 thorpej 491 1.1 thorpej cl->cl_id = hif->hif_classid++; 492 1.19 peter cl->cl_handle = qid; 493 1.1 thorpej cl->cl_hif = hif; 494 1.1 thorpej cl->cl_parent = parent; 495 1.1 thorpej 496 1.3 thorpej s = splnet(); 497 1.1 thorpej hif->hif_classes++; 498 1.19 peter 499 1.19 peter /* 500 1.19 peter * find a free slot in the class table. if the slot matching 501 1.19 peter * the lower bits of qid is free, use this slot. otherwise, 502 1.19 peter * use the first free slot. 503 1.19 peter */ 504 1.19 peter i = qid % HFSC_MAX_CLASSES; 505 1.19 peter if (hif->hif_class_tbl[i] == NULL) 506 1.19 peter hif->hif_class_tbl[i] = cl; 507 1.19 peter else { 508 1.19 peter for (i = 0; i < HFSC_MAX_CLASSES; i++) 509 1.19 peter if (hif->hif_class_tbl[i] == NULL) { 510 1.19 peter hif->hif_class_tbl[i] = cl; 511 1.19 peter break; 512 1.19 peter } 513 1.19 peter if (i == HFSC_MAX_CLASSES) { 514 1.19 peter splx(s); 515 1.19 peter goto err_ret; 516 1.19 peter } 517 1.19 peter } 518 1.19 peter 519 1.1 thorpej if (flags & HFCF_DEFAULTCLASS) 520 1.1 thorpej hif->hif_defaultclass = cl; 521 1.1 thorpej 522 1.1 thorpej if (parent == NULL) { 523 1.1 thorpej /* this is root class */ 524 1.19 peter hif->hif_rootclass = cl; 525 1.19 peter } else { 526 1.19 peter /* add this class to the children list of the parent */ 527 1.19 peter if ((p = parent->cl_children) == NULL) 528 1.19 peter parent->cl_children = cl; 529 1.19 peter else { 530 1.19 peter while (p->cl_siblings != NULL) 531 1.19 peter p = p->cl_siblings; 532 1.19 peter p->cl_siblings = cl; 533 1.19 peter } 534 1.1 thorpej } 535 1.1 thorpej splx(s); 536 1.1 thorpej 537 1.31 joe return cl; 538 1.1 thorpej 539 1.1 thorpej err_ret: 540 1.1 thorpej if (cl->cl_actc != NULL) 541 1.1 thorpej actlist_destroy(cl->cl_actc); 542 1.1 thorpej if (cl->cl_red != NULL) { 543 1.1 thorpej #ifdef ALTQ_RIO 544 1.1 thorpej if (q_is_rio(cl->cl_q)) 545 1.1 thorpej rio_destroy((rio_t *)cl->cl_red); 546 1.1 thorpej #endif 547 1.1 thorpej #ifdef ALTQ_RED 548 1.1 thorpej if (q_is_red(cl->cl_q)) 549 1.1 thorpej red_destroy(cl->cl_red); 550 1.1 thorpej #endif 551 1.1 thorpej } 552 1.1 thorpej if (cl->cl_fsc != NULL) 553 1.13 christos free(cl->cl_fsc, M_DEVBUF); 554 1.1 thorpej if (cl->cl_rsc != NULL) 555 1.13 christos free(cl->cl_rsc, M_DEVBUF); 556 1.19 peter if (cl->cl_usc != NULL) 557 1.19 peter free(cl->cl_usc, M_DEVBUF); 558 1.1 thorpej if (cl->cl_q != NULL) 559 1.13 christos free(cl->cl_q, M_DEVBUF); 560 1.13 christos free(cl, M_DEVBUF); 561 1.31 joe return NULL; 562 1.1 thorpej } 563 1.1 thorpej 564 1.1 thorpej static int 565 1.19 peter hfsc_class_destroy(struct hfsc_class *cl) 566 1.1 thorpej { 567 1.19 peter int i, s; 568 1.19 peter 569 1.19 peter if (cl == NULL) 570 1.31 joe return 0; 571 1.1 thorpej 572 1.1 thorpej if (is_a_parent_class(cl)) 573 1.31 joe return EBUSY; 574 1.1 thorpej 575 1.3 thorpej s = splnet(); 576 1.1 thorpej 577 1.19 peter #ifdef ALTQ3_COMPAT 578 1.1 thorpej /* delete filters referencing to this class */ 579 1.1 thorpej acc_discard_filters(&cl->cl_hif->hif_classifier, cl, 0); 580 1.19 peter #endif /* ALTQ3_COMPAT */ 581 1.1 thorpej 582 1.1 thorpej if (!qempty(cl->cl_q)) 583 1.1 thorpej hfsc_purgeq(cl); 584 1.1 thorpej 585 1.1 thorpej if (cl->cl_parent == NULL) { 586 1.1 thorpej /* this is root class */ 587 1.1 thorpej } else { 588 1.1 thorpej struct hfsc_class *p = cl->cl_parent->cl_children; 589 1.1 thorpej 590 1.1 thorpej if (p == cl) 591 1.1 thorpej cl->cl_parent->cl_children = cl->cl_siblings; 592 1.1 thorpej else do { 593 1.1 thorpej if (p->cl_siblings == cl) { 594 1.1 thorpej p->cl_siblings = cl->cl_siblings; 595 1.1 thorpej break; 596 1.1 thorpej } 597 1.1 thorpej } while ((p = p->cl_siblings) != NULL); 598 1.1 thorpej ASSERT(p != NULL); 599 1.1 thorpej } 600 1.19 peter 601 1.19 peter for (i = 0; i < HFSC_MAX_CLASSES; i++) 602 1.19 peter if (cl->cl_hif->hif_class_tbl[i] == cl) { 603 1.19 peter cl->cl_hif->hif_class_tbl[i] = NULL; 604 1.19 peter break; 605 1.19 peter } 606 1.19 peter 607 1.1 thorpej cl->cl_hif->hif_classes--; 608 1.1 thorpej splx(s); 609 1.1 thorpej 610 1.1 thorpej actlist_destroy(cl->cl_actc); 611 1.1 thorpej 612 1.1 thorpej if (cl->cl_red != NULL) { 613 1.1 thorpej #ifdef ALTQ_RIO 614 1.1 thorpej if (q_is_rio(cl->cl_q)) 615 1.1 thorpej rio_destroy((rio_t *)cl->cl_red); 616 1.1 thorpej #endif 617 1.1 thorpej #ifdef ALTQ_RED 618 1.1 thorpej if (q_is_red(cl->cl_q)) 619 1.1 thorpej red_destroy(cl->cl_red); 620 1.1 thorpej #endif 621 1.1 thorpej } 622 1.19 peter 623 1.19 peter if (cl == cl->cl_hif->hif_rootclass) 624 1.19 peter cl->cl_hif->hif_rootclass = NULL; 625 1.19 peter if (cl == cl->cl_hif->hif_defaultclass) 626 1.19 peter cl->cl_hif->hif_defaultclass = NULL; 627 1.19 peter 628 1.19 peter if (cl->cl_usc != NULL) 629 1.19 peter free(cl->cl_usc, M_DEVBUF); 630 1.1 thorpej if (cl->cl_fsc != NULL) 631 1.13 christos free(cl->cl_fsc, M_DEVBUF); 632 1.1 thorpej if (cl->cl_rsc != NULL) 633 1.13 christos free(cl->cl_rsc, M_DEVBUF); 634 1.13 christos free(cl->cl_q, M_DEVBUF); 635 1.13 christos free(cl, M_DEVBUF); 636 1.1 thorpej 637 1.31 joe return 0; 638 1.1 thorpej } 639 1.1 thorpej 640 1.1 thorpej /* 641 1.1 thorpej * hfsc_nextclass returns the next class in the tree. 642 1.1 thorpej * usage: 643 1.19 peter * for (cl = hif->hif_rootclass; cl != NULL; cl = hfsc_nextclass(cl)) 644 1.1 thorpej * do_something; 645 1.1 thorpej */ 646 1.1 thorpej static struct hfsc_class * 647 1.19 peter hfsc_nextclass(struct hfsc_class *cl) 648 1.1 thorpej { 649 1.1 thorpej if (cl->cl_children != NULL) 650 1.1 thorpej cl = cl->cl_children; 651 1.1 thorpej else if (cl->cl_siblings != NULL) 652 1.1 thorpej cl = cl->cl_siblings; 653 1.1 thorpej else { 654 1.1 thorpej while ((cl = cl->cl_parent) != NULL) 655 1.1 thorpej if (cl->cl_siblings) { 656 1.1 thorpej cl = cl->cl_siblings; 657 1.1 thorpej break; 658 1.1 thorpej } 659 1.1 thorpej } 660 1.1 thorpej 661 1.31 joe return cl; 662 1.1 thorpej } 663 1.1 thorpej 664 1.1 thorpej /* 665 1.1 thorpej * hfsc_enqueue is an enqueue function to be registered to 666 1.1 thorpej * (*altq_enqueue) in struct ifaltq. 667 1.1 thorpej */ 668 1.10 perry static int 669 1.26 knakahar hfsc_enqueue(struct ifaltq *ifq, struct mbuf *m) 670 1.1 thorpej { 671 1.26 knakahar struct altq_pktattr pktattr; 672 1.1 thorpej struct hfsc_if *hif = (struct hfsc_if *)ifq->altq_disc; 673 1.1 thorpej struct hfsc_class *cl; 674 1.19 peter struct m_tag *t; 675 1.1 thorpej int len; 676 1.1 thorpej 677 1.1 thorpej /* grab class set by classifier */ 678 1.19 peter if ((m->m_flags & M_PKTHDR) == 0) { 679 1.19 peter /* should not happen */ 680 1.19 peter printf("altq: packet for %s does not have pkthdr\n", 681 1.19 peter ifq->altq_ifp->if_xname); 682 1.19 peter m_freem(m); 683 1.31 joe return ENOBUFS; 684 1.19 peter } 685 1.19 peter cl = NULL; 686 1.28 maxv if ((t = m_tag_find(m, PACKET_TAG_ALTQ_QID)) != NULL) 687 1.19 peter cl = clh_to_clp(hif, ((struct altq_tag *)(t+1))->qid); 688 1.19 peter #ifdef ALTQ3_COMPAT 689 1.26 knakahar else if ((ifq->altq_flags & ALTQF_CLASSIFY)) 690 1.26 knakahar cl = m->m_pkthdr.pattr_class; 691 1.19 peter #endif 692 1.19 peter if (cl == NULL || is_a_parent_class(cl)) { 693 1.1 thorpej cl = hif->hif_defaultclass; 694 1.19 peter if (cl == NULL) { 695 1.19 peter m_freem(m); 696 1.19 peter return (ENOBUFS); 697 1.19 peter } 698 1.19 peter } 699 1.19 peter #ifdef ALTQ3_COMPAT 700 1.26 knakahar if (m->m_pkthdr.pattr_af != AF_UNSPEC) { 701 1.26 knakahar pktattr.pattr_class = m->m_pkthdr.pattr_class; 702 1.26 knakahar pktattr.pattr_af = m->m_pkthdr.pattr_af; 703 1.26 knakahar pktattr.pattr_hdr = m->m_pkthdr.pattr_hdr; 704 1.26 knakahar 705 1.26 knakahar cl->cl_pktattr = &pktattr; /* save proto hdr used by ECN */ 706 1.26 knakahar } else 707 1.19 peter #endif 708 1.19 peter cl->cl_pktattr = NULL; 709 1.1 thorpej len = m_pktlen(m); 710 1.1 thorpej if (hfsc_addq(cl, m) != 0) { 711 1.1 thorpej /* drop occurred. mbuf was freed in hfsc_addq. */ 712 1.1 thorpej PKTCNTR_ADD(&cl->cl_stats.drop_cnt, len); 713 1.31 joe return ENOBUFS; 714 1.1 thorpej } 715 1.1 thorpej IFQ_INC_LEN(ifq); 716 1.1 thorpej cl->cl_hif->hif_packets++; 717 1.1 thorpej 718 1.1 thorpej /* successfully queued. */ 719 1.1 thorpej if (qlen(cl->cl_q) == 1) 720 1.1 thorpej set_active(cl, m_pktlen(m)); 721 1.1 thorpej 722 1.31 joe return 0; 723 1.1 thorpej } 724 1.1 thorpej 725 1.1 thorpej /* 726 1.1 thorpej * hfsc_dequeue is a dequeue function to be registered to 727 1.1 thorpej * (*altq_dequeue) in struct ifaltq. 728 1.1 thorpej * 729 1.1 thorpej * note: ALTDQ_POLL returns the next packet without removing the packet 730 1.1 thorpej * from the queue. ALTDQ_REMOVE is a normal dequeue operation. 731 1.1 thorpej * ALTDQ_REMOVE must return the same packet if called immediately 732 1.1 thorpej * after ALTDQ_POLL. 733 1.1 thorpej */ 734 1.1 thorpej static struct mbuf * 735 1.19 peter hfsc_dequeue(struct ifaltq *ifq, int op) 736 1.1 thorpej { 737 1.1 thorpej struct hfsc_if *hif = (struct hfsc_if *)ifq->altq_disc; 738 1.1 thorpej struct hfsc_class *cl; 739 1.1 thorpej struct mbuf *m; 740 1.1 thorpej int len, next_len; 741 1.1 thorpej int realtime = 0; 742 1.19 peter u_int64_t cur_time; 743 1.1 thorpej 744 1.1 thorpej if (hif->hif_packets == 0) 745 1.1 thorpej /* no packet in the tree */ 746 1.31 joe return NULL; 747 1.1 thorpej 748 1.19 peter cur_time = read_machclk(); 749 1.19 peter 750 1.1 thorpej if (op == ALTDQ_REMOVE && hif->hif_pollcache != NULL) { 751 1.10 perry 752 1.1 thorpej cl = hif->hif_pollcache; 753 1.1 thorpej hif->hif_pollcache = NULL; 754 1.1 thorpej /* check if the class was scheduled by real-time criteria */ 755 1.19 peter if (cl->cl_rsc != NULL) 756 1.1 thorpej realtime = (cl->cl_e <= cur_time); 757 1.1 thorpej } else { 758 1.1 thorpej /* 759 1.1 thorpej * if there are eligible classes, use real-time criteria. 760 1.1 thorpej * find the class with the minimum deadline among 761 1.1 thorpej * the eligible classes. 762 1.1 thorpej */ 763 1.19 peter if ((cl = ellist_get_mindl(hif->hif_eligible, cur_time)) 764 1.19 peter != NULL) { 765 1.1 thorpej realtime = 1; 766 1.1 thorpej } else { 767 1.19 peter #ifdef ALTQ_DEBUG 768 1.19 peter int fits = 0; 769 1.19 peter #endif 770 1.1 thorpej /* 771 1.1 thorpej * use link-sharing criteria 772 1.1 thorpej * get the class with the minimum vt in the hierarchy 773 1.1 thorpej */ 774 1.1 thorpej cl = hif->hif_rootclass; 775 1.1 thorpej while (is_a_parent_class(cl)) { 776 1.19 peter 777 1.19 peter cl = actlist_firstfit(cl, cur_time); 778 1.19 peter if (cl == NULL) { 779 1.19 peter #ifdef ALTQ_DEBUG 780 1.19 peter if (fits > 0) 781 1.19 peter printf("%d fit but none found\n",fits); 782 1.19 peter #endif 783 1.1 thorpej return (NULL); 784 1.19 peter } 785 1.19 peter /* 786 1.19 peter * update parent's cl_cvtmin. 787 1.19 peter * don't update if the new vt is smaller. 788 1.19 peter */ 789 1.19 peter if (cl->cl_parent->cl_cvtmin < cl->cl_vt) 790 1.19 peter cl->cl_parent->cl_cvtmin = cl->cl_vt; 791 1.19 peter #ifdef ALTQ_DEBUG 792 1.19 peter fits++; 793 1.19 peter #endif 794 1.1 thorpej } 795 1.1 thorpej } 796 1.1 thorpej 797 1.1 thorpej if (op == ALTDQ_POLL) { 798 1.1 thorpej hif->hif_pollcache = cl; 799 1.1 thorpej m = hfsc_pollq(cl); 800 1.31 joe return m; 801 1.1 thorpej } 802 1.1 thorpej } 803 1.1 thorpej 804 1.1 thorpej m = hfsc_getq(cl); 805 1.19 peter if (m == NULL) 806 1.19 peter panic("hfsc_dequeue:"); 807 1.1 thorpej len = m_pktlen(m); 808 1.1 thorpej cl->cl_hif->hif_packets--; 809 1.1 thorpej IFQ_DEC_LEN(ifq); 810 1.1 thorpej PKTCNTR_ADD(&cl->cl_stats.xmit_cnt, len); 811 1.1 thorpej 812 1.19 peter update_vf(cl, len, cur_time); 813 1.1 thorpej if (realtime) 814 1.1 thorpej cl->cl_cumul += len; 815 1.1 thorpej 816 1.1 thorpej if (!qempty(cl->cl_q)) { 817 1.1 thorpej if (cl->cl_rsc != NULL) { 818 1.1 thorpej /* update ed */ 819 1.1 thorpej next_len = m_pktlen(qhead(cl->cl_q)); 820 1.10 perry 821 1.1 thorpej if (realtime) 822 1.1 thorpej update_ed(cl, next_len); 823 1.1 thorpej else 824 1.1 thorpej update_d(cl, next_len); 825 1.1 thorpej } 826 1.1 thorpej } else { 827 1.1 thorpej /* the class becomes passive */ 828 1.1 thorpej set_passive(cl); 829 1.1 thorpej } 830 1.1 thorpej 831 1.31 joe return m; 832 1.1 thorpej } 833 1.1 thorpej 834 1.1 thorpej static int 835 1.19 peter hfsc_addq(struct hfsc_class *cl, struct mbuf *m) 836 1.1 thorpej { 837 1.1 thorpej 838 1.1 thorpej #ifdef ALTQ_RIO 839 1.1 thorpej if (q_is_rio(cl->cl_q)) 840 1.1 thorpej return rio_addq((rio_t *)cl->cl_red, cl->cl_q, 841 1.1 thorpej m, cl->cl_pktattr); 842 1.1 thorpej #endif 843 1.1 thorpej #ifdef ALTQ_RED 844 1.1 thorpej if (q_is_red(cl->cl_q)) 845 1.1 thorpej return red_addq(cl->cl_red, cl->cl_q, m, cl->cl_pktattr); 846 1.1 thorpej #endif 847 1.1 thorpej if (qlen(cl->cl_q) >= qlimit(cl->cl_q)) { 848 1.1 thorpej m_freem(m); 849 1.31 joe return -1; 850 1.1 thorpej } 851 1.1 thorpej 852 1.1 thorpej if (cl->cl_flags & HFCF_CLEARDSCP) 853 1.1 thorpej write_dsfield(m, cl->cl_pktattr, 0); 854 1.1 thorpej 855 1.1 thorpej _addq(cl->cl_q, m); 856 1.1 thorpej 857 1.31 joe return 0; 858 1.1 thorpej } 859 1.1 thorpej 860 1.1 thorpej static struct mbuf * 861 1.19 peter hfsc_getq(struct hfsc_class *cl) 862 1.1 thorpej { 863 1.1 thorpej #ifdef ALTQ_RIO 864 1.1 thorpej if (q_is_rio(cl->cl_q)) 865 1.1 thorpej return rio_getq((rio_t *)cl->cl_red, cl->cl_q); 866 1.1 thorpej #endif 867 1.1 thorpej #ifdef ALTQ_RED 868 1.1 thorpej if (q_is_red(cl->cl_q)) 869 1.1 thorpej return red_getq(cl->cl_red, cl->cl_q); 870 1.1 thorpej #endif 871 1.1 thorpej return _getq(cl->cl_q); 872 1.1 thorpej } 873 1.1 thorpej 874 1.1 thorpej static struct mbuf * 875 1.19 peter hfsc_pollq(struct hfsc_class *cl) 876 1.1 thorpej { 877 1.1 thorpej return qhead(cl->cl_q); 878 1.1 thorpej } 879 1.1 thorpej 880 1.1 thorpej static void 881 1.19 peter hfsc_purgeq(struct hfsc_class *cl) 882 1.1 thorpej { 883 1.1 thorpej struct mbuf *m; 884 1.1 thorpej 885 1.1 thorpej if (qempty(cl->cl_q)) 886 1.1 thorpej return; 887 1.1 thorpej 888 1.1 thorpej while ((m = _getq(cl->cl_q)) != NULL) { 889 1.1 thorpej PKTCNTR_ADD(&cl->cl_stats.drop_cnt, m_pktlen(m)); 890 1.1 thorpej m_freem(m); 891 1.19 peter cl->cl_hif->hif_packets--; 892 1.19 peter IFQ_DEC_LEN(cl->cl_hif->hif_ifq); 893 1.1 thorpej } 894 1.1 thorpej ASSERT(qlen(cl->cl_q) == 0); 895 1.10 perry 896 1.19 peter update_vf(cl, 0, 0); /* remove cl from the actlist */ 897 1.1 thorpej set_passive(cl); 898 1.1 thorpej } 899 1.1 thorpej 900 1.10 perry static void 901 1.19 peter set_active(struct hfsc_class *cl, int len) 902 1.1 thorpej { 903 1.1 thorpej if (cl->cl_rsc != NULL) 904 1.1 thorpej init_ed(cl, len); 905 1.1 thorpej if (cl->cl_fsc != NULL) 906 1.19 peter init_vf(cl, len); 907 1.1 thorpej 908 1.1 thorpej cl->cl_stats.period++; 909 1.1 thorpej } 910 1.1 thorpej 911 1.10 perry static void 912 1.19 peter set_passive(struct hfsc_class *cl) 913 1.1 thorpej { 914 1.1 thorpej if (cl->cl_rsc != NULL) 915 1.1 thorpej ellist_remove(cl); 916 1.1 thorpej 917 1.19 peter /* 918 1.19 peter * actlist is now handled in update_vf() so that update_vf(cl, 0, 0) 919 1.19 peter * needs to be called explicitly to remove a class from actlist 920 1.19 peter */ 921 1.1 thorpej } 922 1.1 thorpej 923 1.10 perry static void 924 1.19 peter init_ed(struct hfsc_class *cl, int next_len) 925 1.1 thorpej { 926 1.1 thorpej u_int64_t cur_time; 927 1.1 thorpej 928 1.1 thorpej cur_time = read_machclk(); 929 1.1 thorpej 930 1.1 thorpej /* update the deadline curve */ 931 1.1 thorpej rtsc_min(&cl->cl_deadline, cl->cl_rsc, cur_time, cl->cl_cumul); 932 1.1 thorpej 933 1.1 thorpej /* 934 1.1 thorpej * update the eligible curve. 935 1.1 thorpej * for concave, it is equal to the deadline curve. 936 1.1 thorpej * for convex, it is a linear curve with slope m2. 937 1.1 thorpej */ 938 1.1 thorpej cl->cl_eligible = cl->cl_deadline; 939 1.1 thorpej if (cl->cl_rsc->sm1 <= cl->cl_rsc->sm2) { 940 1.1 thorpej cl->cl_eligible.dx = 0; 941 1.1 thorpej cl->cl_eligible.dy = 0; 942 1.1 thorpej } 943 1.1 thorpej 944 1.1 thorpej /* compute e and d */ 945 1.1 thorpej cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul); 946 1.1 thorpej cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len); 947 1.1 thorpej 948 1.1 thorpej ellist_insert(cl); 949 1.1 thorpej } 950 1.1 thorpej 951 1.10 perry static void 952 1.19 peter update_ed(struct hfsc_class *cl, int next_len) 953 1.1 thorpej { 954 1.1 thorpej cl->cl_e = rtsc_y2x(&cl->cl_eligible, cl->cl_cumul); 955 1.1 thorpej cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len); 956 1.1 thorpej 957 1.1 thorpej ellist_update(cl); 958 1.1 thorpej } 959 1.1 thorpej 960 1.10 perry static void 961 1.19 peter update_d(struct hfsc_class *cl, int next_len) 962 1.1 thorpej { 963 1.1 thorpej cl->cl_d = rtsc_y2x(&cl->cl_deadline, cl->cl_cumul + next_len); 964 1.1 thorpej } 965 1.1 thorpej 966 1.10 perry static void 967 1.22 christos init_vf(struct hfsc_class *cl, int len) 968 1.1 thorpej { 969 1.19 peter struct hfsc_class *max_cl, *p; 970 1.19 peter u_int64_t vt, f, cur_time; 971 1.19 peter int go_active; 972 1.19 peter 973 1.19 peter cur_time = 0; 974 1.19 peter go_active = 1; 975 1.19 peter for ( ; cl->cl_parent != NULL; cl = cl->cl_parent) { 976 1.1 thorpej 977 1.19 peter if (go_active && cl->cl_nactive++ == 0) 978 1.19 peter go_active = 1; 979 1.19 peter else 980 1.19 peter go_active = 0; 981 1.10 perry 982 1.19 peter if (go_active) { 983 1.19 peter max_cl = actlist_last(cl->cl_parent->cl_actc); 984 1.19 peter if (max_cl != NULL) { 985 1.19 peter /* 986 1.19 peter * set vt to the average of the min and max 987 1.19 peter * classes. if the parent's period didn't 988 1.19 peter * change, don't decrease vt of the class. 989 1.19 peter */ 990 1.19 peter vt = max_cl->cl_vt; 991 1.19 peter if (cl->cl_parent->cl_cvtmin != 0) 992 1.19 peter vt = (cl->cl_parent->cl_cvtmin + vt)/2; 993 1.19 peter 994 1.19 peter if (cl->cl_parent->cl_vtperiod != 995 1.19 peter cl->cl_parentperiod || vt > cl->cl_vt) 996 1.19 peter cl->cl_vt = vt; 997 1.19 peter } else { 998 1.19 peter /* 999 1.19 peter * first child for a new parent backlog period. 1000 1.19 peter * add parent's cvtmax to vtoff of children 1001 1.19 peter * to make a new vt (vtoff + vt) larger than 1002 1.19 peter * the vt in the last period for all children. 1003 1.19 peter */ 1004 1.19 peter vt = cl->cl_parent->cl_cvtmax; 1005 1.19 peter for (p = cl->cl_parent->cl_children; p != NULL; 1006 1.19 peter p = p->cl_siblings) 1007 1.19 peter p->cl_vtoff += vt; 1008 1.19 peter cl->cl_vt = 0; 1009 1.19 peter cl->cl_parent->cl_cvtmax = 0; 1010 1.19 peter cl->cl_parent->cl_cvtmin = 0; 1011 1.19 peter } 1012 1.19 peter cl->cl_initvt = cl->cl_vt; 1013 1.1 thorpej 1014 1.19 peter /* update the virtual curve */ 1015 1.19 peter vt = cl->cl_vt + cl->cl_vtoff; 1016 1.19 peter rtsc_min(&cl->cl_virtual, cl->cl_fsc, vt, cl->cl_total); 1017 1.19 peter if (cl->cl_virtual.x == vt) { 1018 1.19 peter cl->cl_virtual.x -= cl->cl_vtoff; 1019 1.19 peter cl->cl_vtoff = 0; 1020 1.19 peter } 1021 1.19 peter cl->cl_vtadj = 0; 1022 1.1 thorpej 1023 1.19 peter cl->cl_vtperiod++; /* increment vt period */ 1024 1.19 peter cl->cl_parentperiod = cl->cl_parent->cl_vtperiod; 1025 1.19 peter if (cl->cl_parent->cl_nactive == 0) 1026 1.19 peter cl->cl_parentperiod++; 1027 1.19 peter cl->cl_f = 0; 1028 1.19 peter 1029 1.19 peter actlist_insert(cl); 1030 1.19 peter 1031 1.19 peter if (cl->cl_usc != NULL) { 1032 1.19 peter /* class has upper limit curve */ 1033 1.19 peter if (cur_time == 0) 1034 1.19 peter cur_time = read_machclk(); 1035 1.19 peter 1036 1.19 peter /* update the ulimit curve */ 1037 1.19 peter rtsc_min(&cl->cl_ulimit, cl->cl_usc, cur_time, 1038 1.19 peter cl->cl_total); 1039 1.19 peter /* compute myf */ 1040 1.19 peter cl->cl_myf = rtsc_y2x(&cl->cl_ulimit, 1041 1.19 peter cl->cl_total); 1042 1.19 peter cl->cl_myfadj = 0; 1043 1.19 peter } 1044 1.1 thorpej } 1045 1.1 thorpej 1046 1.19 peter if (cl->cl_myf > cl->cl_cfmin) 1047 1.19 peter f = cl->cl_myf; 1048 1.19 peter else 1049 1.19 peter f = cl->cl_cfmin; 1050 1.19 peter if (f != cl->cl_f) { 1051 1.19 peter cl->cl_f = f; 1052 1.19 peter update_cfmin(cl->cl_parent); 1053 1.19 peter } 1054 1.1 thorpej } 1055 1.1 thorpej } 1056 1.1 thorpej 1057 1.10 perry static void 1058 1.19 peter update_vf(struct hfsc_class *cl, int len, u_int64_t cur_time) 1059 1.1 thorpej { 1060 1.19 peter u_int64_t f, myf_bound, delta; 1061 1.19 peter int go_passive; 1062 1.19 peter 1063 1.19 peter go_passive = qempty(cl->cl_q); 1064 1.19 peter 1065 1.19 peter for (; cl->cl_parent != NULL; cl = cl->cl_parent) { 1066 1.1 thorpej 1067 1.1 thorpej cl->cl_total += len; 1068 1.1 thorpej 1069 1.19 peter if (cl->cl_fsc == NULL || cl->cl_nactive == 0) 1070 1.19 peter continue; 1071 1.19 peter 1072 1.19 peter if (go_passive && --cl->cl_nactive == 0) 1073 1.19 peter go_passive = 1; 1074 1.19 peter else 1075 1.19 peter go_passive = 0; 1076 1.19 peter 1077 1.19 peter if (go_passive) { 1078 1.19 peter /* no more active child, going passive */ 1079 1.19 peter 1080 1.19 peter /* update cvtmax of the parent class */ 1081 1.19 peter if (cl->cl_vt > cl->cl_parent->cl_cvtmax) 1082 1.19 peter cl->cl_parent->cl_cvtmax = cl->cl_vt; 1083 1.19 peter 1084 1.19 peter /* remove this class from the vt list */ 1085 1.19 peter actlist_remove(cl); 1086 1.19 peter 1087 1.19 peter update_cfmin(cl->cl_parent); 1088 1.19 peter 1089 1.19 peter continue; 1090 1.19 peter } 1091 1.19 peter 1092 1.19 peter /* 1093 1.19 peter * update vt and f 1094 1.19 peter */ 1095 1.19 peter cl->cl_vt = rtsc_y2x(&cl->cl_virtual, cl->cl_total) 1096 1.19 peter - cl->cl_vtoff + cl->cl_vtadj; 1097 1.19 peter 1098 1.19 peter /* 1099 1.19 peter * if vt of the class is smaller than cvtmin, 1100 1.19 peter * the class was skipped in the past due to non-fit. 1101 1.19 peter * if so, we need to adjust vtadj. 1102 1.19 peter */ 1103 1.19 peter if (cl->cl_vt < cl->cl_parent->cl_cvtmin) { 1104 1.19 peter cl->cl_vtadj += cl->cl_parent->cl_cvtmin - cl->cl_vt; 1105 1.19 peter cl->cl_vt = cl->cl_parent->cl_cvtmin; 1106 1.19 peter } 1107 1.19 peter 1108 1.19 peter /* update the vt list */ 1109 1.19 peter actlist_update(cl); 1110 1.19 peter 1111 1.19 peter if (cl->cl_usc != NULL) { 1112 1.19 peter cl->cl_myf = cl->cl_myfadj 1113 1.19 peter + rtsc_y2x(&cl->cl_ulimit, cl->cl_total); 1114 1.19 peter 1115 1.19 peter /* 1116 1.19 peter * if myf lags behind by more than one clock tick 1117 1.19 peter * from the current time, adjust myfadj to prevent 1118 1.19 peter * a rate-limited class from going greedy. 1119 1.19 peter * in a steady state under rate-limiting, myf 1120 1.19 peter * fluctuates within one clock tick. 1121 1.19 peter */ 1122 1.19 peter myf_bound = cur_time - machclk_per_tick; 1123 1.19 peter if (cl->cl_myf < myf_bound) { 1124 1.19 peter delta = cur_time - cl->cl_myf; 1125 1.19 peter cl->cl_myfadj += delta; 1126 1.19 peter cl->cl_myf += delta; 1127 1.19 peter } 1128 1.19 peter } 1129 1.1 thorpej 1130 1.19 peter /* cl_f is max(cl_myf, cl_cfmin) */ 1131 1.19 peter if (cl->cl_myf > cl->cl_cfmin) 1132 1.19 peter f = cl->cl_myf; 1133 1.19 peter else 1134 1.19 peter f = cl->cl_cfmin; 1135 1.19 peter if (f != cl->cl_f) { 1136 1.19 peter cl->cl_f = f; 1137 1.19 peter update_cfmin(cl->cl_parent); 1138 1.1 thorpej } 1139 1.19 peter } 1140 1.19 peter } 1141 1.19 peter 1142 1.19 peter static void 1143 1.19 peter update_cfmin(struct hfsc_class *cl) 1144 1.19 peter { 1145 1.19 peter struct hfsc_class *p; 1146 1.19 peter u_int64_t cfmin; 1147 1.1 thorpej 1148 1.19 peter if (TAILQ_EMPTY(cl->cl_actc)) { 1149 1.19 peter cl->cl_cfmin = 0; 1150 1.19 peter return; 1151 1.19 peter } 1152 1.19 peter cfmin = HT_INFINITY; 1153 1.19 peter TAILQ_FOREACH(p, cl->cl_actc, cl_actlist) { 1154 1.19 peter if (p->cl_f == 0) { 1155 1.19 peter cl->cl_cfmin = 0; 1156 1.19 peter return; 1157 1.19 peter } 1158 1.19 peter if (p->cl_f < cfmin) 1159 1.19 peter cfmin = p->cl_f; 1160 1.1 thorpej } 1161 1.19 peter cl->cl_cfmin = cfmin; 1162 1.1 thorpej } 1163 1.1 thorpej 1164 1.1 thorpej /* 1165 1.1 thorpej * TAILQ based ellist and actlist implementation 1166 1.1 thorpej * (ion wanted to make a calendar queue based implementation) 1167 1.1 thorpej */ 1168 1.1 thorpej /* 1169 1.1 thorpej * eligible list holds backlogged classes being sorted by their eligible times. 1170 1.1 thorpej * there is one eligible list per interface. 1171 1.1 thorpej */ 1172 1.1 thorpej 1173 1.1 thorpej static ellist_t * 1174 1.19 peter ellist_alloc(void) 1175 1.1 thorpej { 1176 1.1 thorpej ellist_t *head; 1177 1.10 perry 1178 1.13 christos head = malloc(sizeof(ellist_t), M_DEVBUF, M_WAITOK); 1179 1.19 peter TAILQ_INIT(head); 1180 1.31 joe return head; 1181 1.1 thorpej } 1182 1.1 thorpej 1183 1.1 thorpej static void 1184 1.19 peter ellist_destroy(ellist_t *head) 1185 1.1 thorpej { 1186 1.13 christos free(head, M_DEVBUF); 1187 1.1 thorpej } 1188 1.1 thorpej 1189 1.10 perry static void 1190 1.19 peter ellist_insert(struct hfsc_class *cl) 1191 1.1 thorpej { 1192 1.1 thorpej struct hfsc_if *hif = cl->cl_hif; 1193 1.1 thorpej struct hfsc_class *p; 1194 1.1 thorpej 1195 1.1 thorpej /* check the last entry first */ 1196 1.1 thorpej if ((p = TAILQ_LAST(hif->hif_eligible, _eligible)) == NULL || 1197 1.1 thorpej p->cl_e <= cl->cl_e) { 1198 1.1 thorpej TAILQ_INSERT_TAIL(hif->hif_eligible, cl, cl_ellist); 1199 1.1 thorpej return; 1200 1.1 thorpej } 1201 1.1 thorpej 1202 1.1 thorpej TAILQ_FOREACH(p, hif->hif_eligible, cl_ellist) { 1203 1.1 thorpej if (cl->cl_e < p->cl_e) { 1204 1.1 thorpej TAILQ_INSERT_BEFORE(p, cl, cl_ellist); 1205 1.1 thorpej return; 1206 1.1 thorpej } 1207 1.1 thorpej } 1208 1.1 thorpej ASSERT(0); /* should not reach here */ 1209 1.1 thorpej } 1210 1.1 thorpej 1211 1.10 perry static void 1212 1.19 peter ellist_remove(struct hfsc_class *cl) 1213 1.1 thorpej { 1214 1.1 thorpej struct hfsc_if *hif = cl->cl_hif; 1215 1.10 perry 1216 1.1 thorpej TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist); 1217 1.1 thorpej } 1218 1.1 thorpej 1219 1.10 perry static void 1220 1.19 peter ellist_update(struct hfsc_class *cl) 1221 1.1 thorpej { 1222 1.1 thorpej struct hfsc_if *hif = cl->cl_hif; 1223 1.1 thorpej struct hfsc_class *p, *last; 1224 1.1 thorpej 1225 1.1 thorpej /* 1226 1.1 thorpej * the eligible time of a class increases monotonically. 1227 1.1 thorpej * if the next entry has a larger eligible time, nothing to do. 1228 1.1 thorpej */ 1229 1.1 thorpej p = TAILQ_NEXT(cl, cl_ellist); 1230 1.1 thorpej if (p == NULL || cl->cl_e <= p->cl_e) 1231 1.1 thorpej return; 1232 1.1 thorpej 1233 1.1 thorpej /* check the last entry */ 1234 1.1 thorpej last = TAILQ_LAST(hif->hif_eligible, _eligible); 1235 1.1 thorpej ASSERT(last != NULL); 1236 1.1 thorpej if (last->cl_e <= cl->cl_e) { 1237 1.1 thorpej TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist); 1238 1.1 thorpej TAILQ_INSERT_TAIL(hif->hif_eligible, cl, cl_ellist); 1239 1.1 thorpej return; 1240 1.1 thorpej } 1241 1.1 thorpej 1242 1.1 thorpej /* 1243 1.1 thorpej * the new position must be between the next entry 1244 1.1 thorpej * and the last entry 1245 1.1 thorpej */ 1246 1.1 thorpej while ((p = TAILQ_NEXT(p, cl_ellist)) != NULL) { 1247 1.1 thorpej if (cl->cl_e < p->cl_e) { 1248 1.1 thorpej TAILQ_REMOVE(hif->hif_eligible, cl, cl_ellist); 1249 1.1 thorpej TAILQ_INSERT_BEFORE(p, cl, cl_ellist); 1250 1.1 thorpej return; 1251 1.1 thorpej } 1252 1.1 thorpej } 1253 1.1 thorpej ASSERT(0); /* should not reach here */ 1254 1.1 thorpej } 1255 1.1 thorpej 1256 1.1 thorpej /* find the class with the minimum deadline among the eligible classes */ 1257 1.1 thorpej struct hfsc_class * 1258 1.19 peter ellist_get_mindl(ellist_t *head, u_int64_t cur_time) 1259 1.1 thorpej { 1260 1.1 thorpej struct hfsc_class *p, *cl = NULL; 1261 1.1 thorpej 1262 1.1 thorpej TAILQ_FOREACH(p, head, cl_ellist) { 1263 1.1 thorpej if (p->cl_e > cur_time) 1264 1.1 thorpej break; 1265 1.1 thorpej if (cl == NULL || p->cl_d < cl->cl_d) 1266 1.1 thorpej cl = p; 1267 1.1 thorpej } 1268 1.31 joe return cl; 1269 1.1 thorpej } 1270 1.1 thorpej 1271 1.1 thorpej /* 1272 1.1 thorpej * active children list holds backlogged child classes being sorted 1273 1.1 thorpej * by their virtual time. 1274 1.1 thorpej * each intermediate class has one active children list. 1275 1.1 thorpej */ 1276 1.1 thorpej static actlist_t * 1277 1.19 peter actlist_alloc(void) 1278 1.1 thorpej { 1279 1.1 thorpej actlist_t *head; 1280 1.10 perry 1281 1.13 christos head = malloc(sizeof(actlist_t), M_DEVBUF, M_WAITOK); 1282 1.19 peter TAILQ_INIT(head); 1283 1.31 joe return head; 1284 1.1 thorpej } 1285 1.1 thorpej 1286 1.1 thorpej static void 1287 1.19 peter actlist_destroy(actlist_t *head) 1288 1.1 thorpej { 1289 1.13 christos free(head, M_DEVBUF); 1290 1.1 thorpej } 1291 1.10 perry static void 1292 1.19 peter actlist_insert(struct hfsc_class *cl) 1293 1.1 thorpej { 1294 1.1 thorpej struct hfsc_class *p; 1295 1.1 thorpej 1296 1.1 thorpej /* check the last entry first */ 1297 1.1 thorpej if ((p = TAILQ_LAST(cl->cl_parent->cl_actc, _active)) == NULL 1298 1.1 thorpej || p->cl_vt <= cl->cl_vt) { 1299 1.1 thorpej TAILQ_INSERT_TAIL(cl->cl_parent->cl_actc, cl, cl_actlist); 1300 1.1 thorpej return; 1301 1.1 thorpej } 1302 1.1 thorpej 1303 1.1 thorpej TAILQ_FOREACH(p, cl->cl_parent->cl_actc, cl_actlist) { 1304 1.1 thorpej if (cl->cl_vt < p->cl_vt) { 1305 1.1 thorpej TAILQ_INSERT_BEFORE(p, cl, cl_actlist); 1306 1.1 thorpej return; 1307 1.1 thorpej } 1308 1.1 thorpej } 1309 1.1 thorpej ASSERT(0); /* should not reach here */ 1310 1.1 thorpej } 1311 1.1 thorpej 1312 1.10 perry static void 1313 1.19 peter actlist_remove(struct hfsc_class *cl) 1314 1.1 thorpej { 1315 1.1 thorpej TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist); 1316 1.1 thorpej } 1317 1.1 thorpej 1318 1.1 thorpej static void 1319 1.19 peter actlist_update(struct hfsc_class *cl) 1320 1.1 thorpej { 1321 1.1 thorpej struct hfsc_class *p, *last; 1322 1.1 thorpej 1323 1.1 thorpej /* 1324 1.1 thorpej * the virtual time of a class increases monotonically during its 1325 1.1 thorpej * backlogged period. 1326 1.1 thorpej * if the next entry has a larger virtual time, nothing to do. 1327 1.1 thorpej */ 1328 1.1 thorpej p = TAILQ_NEXT(cl, cl_actlist); 1329 1.19 peter if (p == NULL || cl->cl_vt < p->cl_vt) 1330 1.1 thorpej return; 1331 1.1 thorpej 1332 1.1 thorpej /* check the last entry */ 1333 1.1 thorpej last = TAILQ_LAST(cl->cl_parent->cl_actc, _active); 1334 1.1 thorpej ASSERT(last != NULL); 1335 1.1 thorpej if (last->cl_vt <= cl->cl_vt) { 1336 1.1 thorpej TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist); 1337 1.1 thorpej TAILQ_INSERT_TAIL(cl->cl_parent->cl_actc, cl, cl_actlist); 1338 1.1 thorpej return; 1339 1.1 thorpej } 1340 1.1 thorpej 1341 1.1 thorpej /* 1342 1.1 thorpej * the new position must be between the next entry 1343 1.1 thorpej * and the last entry 1344 1.1 thorpej */ 1345 1.1 thorpej while ((p = TAILQ_NEXT(p, cl_actlist)) != NULL) { 1346 1.1 thorpej if (cl->cl_vt < p->cl_vt) { 1347 1.1 thorpej TAILQ_REMOVE(cl->cl_parent->cl_actc, cl, cl_actlist); 1348 1.1 thorpej TAILQ_INSERT_BEFORE(p, cl, cl_actlist); 1349 1.1 thorpej return; 1350 1.1 thorpej } 1351 1.1 thorpej } 1352 1.1 thorpej ASSERT(0); /* should not reach here */ 1353 1.1 thorpej } 1354 1.1 thorpej 1355 1.19 peter static struct hfsc_class * 1356 1.19 peter actlist_firstfit(struct hfsc_class *cl, u_int64_t cur_time) 1357 1.19 peter { 1358 1.19 peter struct hfsc_class *p; 1359 1.19 peter 1360 1.19 peter TAILQ_FOREACH(p, cl->cl_actc, cl_actlist) { 1361 1.19 peter if (p->cl_f <= cur_time) 1362 1.31 joe return p; 1363 1.19 peter } 1364 1.31 joe return NULL; 1365 1.19 peter } 1366 1.19 peter 1367 1.1 thorpej /* 1368 1.1 thorpej * service curve support functions 1369 1.1 thorpej * 1370 1.1 thorpej * external service curve parameters 1371 1.1 thorpej * m: bits/sec 1372 1.1 thorpej * d: msec 1373 1.1 thorpej * internal service curve parameters 1374 1.1 thorpej * sm: (bytes/tsc_interval) << SM_SHIFT 1375 1.1 thorpej * ism: (tsc_count/byte) << ISM_SHIFT 1376 1.1 thorpej * dx: tsc_count 1377 1.1 thorpej * 1378 1.1 thorpej * SM_SHIFT and ISM_SHIFT are scaled in order to keep effective digits. 1379 1.1 thorpej * we should be able to handle 100K-1Gbps linkspeed with 200Hz-1GHz CPU 1380 1.1 thorpej * speed. SM_SHIFT and ISM_SHIFT are selected to have at least 3 effective 1381 1.1 thorpej * digits in decimal using the following table. 1382 1.1 thorpej * 1383 1.19 peter * bits/sec 100Kbps 1Mbps 10Mbps 100Mbps 1Gbps 1384 1.1 thorpej * ----------+------------------------------------------------------- 1385 1.1 thorpej * bytes/nsec 12.5e-6 125e-6 1250e-6 12500e-6 125000e-6 1386 1.1 thorpej * sm(500MHz) 25.0e-6 250e-6 2500e-6 25000e-6 250000e-6 1387 1.1 thorpej * sm(200MHz) 62.5e-6 625e-6 6250e-6 62500e-6 625000e-6 1388 1.10 perry * 1389 1.1 thorpej * nsec/byte 80000 8000 800 80 8 1390 1.1 thorpej * ism(500MHz) 40000 4000 400 40 4 1391 1.1 thorpej * ism(200MHz) 16000 1600 160 16 1.6 1392 1.1 thorpej */ 1393 1.1 thorpej #define SM_SHIFT 24 1394 1.1 thorpej #define ISM_SHIFT 10 1395 1.1 thorpej 1396 1.19 peter #define SM_MASK ((1LL << SM_SHIFT) - 1) 1397 1.19 peter #define ISM_MASK ((1LL << ISM_SHIFT) - 1) 1398 1.1 thorpej 1399 1.12 perry static inline u_int64_t 1400 1.19 peter seg_x2y(u_int64_t x, u_int64_t sm) 1401 1.1 thorpej { 1402 1.1 thorpej u_int64_t y; 1403 1.1 thorpej 1404 1.19 peter /* 1405 1.19 peter * compute 1406 1.19 peter * y = x * sm >> SM_SHIFT 1407 1.19 peter * but divide it for the upper and lower bits to avoid overflow 1408 1.19 peter */ 1409 1.19 peter y = (x >> SM_SHIFT) * sm + (((x & SM_MASK) * sm) >> SM_SHIFT); 1410 1.31 joe return y; 1411 1.1 thorpej } 1412 1.1 thorpej 1413 1.12 perry static inline u_int64_t 1414 1.19 peter seg_y2x(u_int64_t y, u_int64_t ism) 1415 1.1 thorpej { 1416 1.1 thorpej u_int64_t x; 1417 1.1 thorpej 1418 1.1 thorpej if (y == 0) 1419 1.1 thorpej x = 0; 1420 1.19 peter else if (ism == HT_INFINITY) 1421 1.19 peter x = HT_INFINITY; 1422 1.19 peter else { 1423 1.19 peter x = (y >> ISM_SHIFT) * ism 1424 1.19 peter + (((y & ISM_MASK) * ism) >> ISM_SHIFT); 1425 1.19 peter } 1426 1.31 joe return x; 1427 1.1 thorpej } 1428 1.1 thorpej 1429 1.12 perry static inline u_int64_t 1430 1.19 peter m2sm(u_int m) 1431 1.1 thorpej { 1432 1.1 thorpej u_int64_t sm; 1433 1.1 thorpej 1434 1.1 thorpej sm = ((u_int64_t)m << SM_SHIFT) / 8 / machclk_freq; 1435 1.31 joe return sm; 1436 1.1 thorpej } 1437 1.1 thorpej 1438 1.12 perry static inline u_int64_t 1439 1.19 peter m2ism(u_int m) 1440 1.1 thorpej { 1441 1.1 thorpej u_int64_t ism; 1442 1.1 thorpej 1443 1.1 thorpej if (m == 0) 1444 1.19 peter ism = HT_INFINITY; 1445 1.1 thorpej else 1446 1.1 thorpej ism = ((u_int64_t)machclk_freq << ISM_SHIFT) * 8 / m; 1447 1.31 joe return ism; 1448 1.1 thorpej } 1449 1.1 thorpej 1450 1.12 perry static inline u_int64_t 1451 1.19 peter d2dx(u_int d) 1452 1.1 thorpej { 1453 1.1 thorpej u_int64_t dx; 1454 1.10 perry 1455 1.1 thorpej dx = ((u_int64_t)d * machclk_freq) / 1000; 1456 1.31 joe return dx; 1457 1.1 thorpej } 1458 1.1 thorpej 1459 1.10 perry static u_int 1460 1.19 peter sm2m(u_int64_t sm) 1461 1.1 thorpej { 1462 1.1 thorpej u_int64_t m; 1463 1.1 thorpej 1464 1.1 thorpej m = (sm * 8 * machclk_freq) >> SM_SHIFT; 1465 1.1 thorpej return ((u_int)m); 1466 1.1 thorpej } 1467 1.1 thorpej 1468 1.10 perry static u_int 1469 1.19 peter dx2d(u_int64_t dx) 1470 1.1 thorpej { 1471 1.1 thorpej u_int64_t d; 1472 1.1 thorpej 1473 1.1 thorpej d = dx * 1000 / machclk_freq; 1474 1.1 thorpej return ((u_int)d); 1475 1.1 thorpej } 1476 1.1 thorpej 1477 1.10 perry static void 1478 1.19 peter sc2isc(struct service_curve *sc, struct internal_sc *isc) 1479 1.1 thorpej { 1480 1.1 thorpej isc->sm1 = m2sm(sc->m1); 1481 1.1 thorpej isc->ism1 = m2ism(sc->m1); 1482 1.1 thorpej isc->dx = d2dx(sc->d); 1483 1.1 thorpej isc->dy = seg_x2y(isc->dx, isc->sm1); 1484 1.1 thorpej isc->sm2 = m2sm(sc->m2); 1485 1.1 thorpej isc->ism2 = m2ism(sc->m2); 1486 1.1 thorpej } 1487 1.1 thorpej 1488 1.1 thorpej /* 1489 1.1 thorpej * initialize the runtime service curve with the given internal 1490 1.1 thorpej * service curve starting at (x, y). 1491 1.1 thorpej */ 1492 1.10 perry static void 1493 1.19 peter rtsc_init(struct runtime_sc *rtsc, struct internal_sc * isc, u_int64_t x, 1494 1.19 peter u_int64_t y) 1495 1.1 thorpej { 1496 1.1 thorpej rtsc->x = x; 1497 1.1 thorpej rtsc->y = y; 1498 1.1 thorpej rtsc->sm1 = isc->sm1; 1499 1.1 thorpej rtsc->ism1 = isc->ism1; 1500 1.1 thorpej rtsc->dx = isc->dx; 1501 1.1 thorpej rtsc->dy = isc->dy; 1502 1.1 thorpej rtsc->sm2 = isc->sm2; 1503 1.1 thorpej rtsc->ism2 = isc->ism2; 1504 1.1 thorpej } 1505 1.1 thorpej 1506 1.1 thorpej /* 1507 1.1 thorpej * calculate the y-projection of the runtime service curve by the 1508 1.1 thorpej * given x-projection value 1509 1.1 thorpej */ 1510 1.10 perry static u_int64_t 1511 1.19 peter rtsc_y2x(struct runtime_sc *rtsc, u_int64_t y) 1512 1.1 thorpej { 1513 1.1 thorpej u_int64_t x; 1514 1.1 thorpej 1515 1.1 thorpej if (y < rtsc->y) 1516 1.1 thorpej x = rtsc->x; 1517 1.1 thorpej else if (y <= rtsc->y + rtsc->dy) { 1518 1.1 thorpej /* x belongs to the 1st segment */ 1519 1.1 thorpej if (rtsc->dy == 0) 1520 1.1 thorpej x = rtsc->x + rtsc->dx; 1521 1.1 thorpej else 1522 1.1 thorpej x = rtsc->x + seg_y2x(y - rtsc->y, rtsc->ism1); 1523 1.1 thorpej } else { 1524 1.1 thorpej /* x belongs to the 2nd segment */ 1525 1.1 thorpej x = rtsc->x + rtsc->dx 1526 1.1 thorpej + seg_y2x(y - rtsc->y - rtsc->dy, rtsc->ism2); 1527 1.1 thorpej } 1528 1.31 joe return x; 1529 1.1 thorpej } 1530 1.1 thorpej 1531 1.10 perry static u_int64_t 1532 1.19 peter rtsc_x2y(struct runtime_sc *rtsc, u_int64_t x) 1533 1.1 thorpej { 1534 1.1 thorpej u_int64_t y; 1535 1.1 thorpej 1536 1.1 thorpej if (x <= rtsc->x) 1537 1.1 thorpej y = rtsc->y; 1538 1.1 thorpej else if (x <= rtsc->x + rtsc->dx) 1539 1.1 thorpej /* y belongs to the 1st segment */ 1540 1.1 thorpej y = rtsc->y + seg_x2y(x - rtsc->x, rtsc->sm1); 1541 1.1 thorpej else 1542 1.1 thorpej /* y belongs to the 2nd segment */ 1543 1.1 thorpej y = rtsc->y + rtsc->dy 1544 1.1 thorpej + seg_x2y(x - rtsc->x - rtsc->dx, rtsc->sm2); 1545 1.31 joe return y; 1546 1.1 thorpej } 1547 1.1 thorpej 1548 1.1 thorpej /* 1549 1.1 thorpej * update the runtime service curve by taking the minimum of the current 1550 1.1 thorpej * runtime service curve and the service curve starting at (x, y). 1551 1.1 thorpej */ 1552 1.10 perry static void 1553 1.19 peter rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u_int64_t x, 1554 1.19 peter u_int64_t y) 1555 1.1 thorpej { 1556 1.1 thorpej u_int64_t y1, y2, dx, dy; 1557 1.1 thorpej 1558 1.1 thorpej if (isc->sm1 <= isc->sm2) { 1559 1.1 thorpej /* service curve is convex */ 1560 1.1 thorpej y1 = rtsc_x2y(rtsc, x); 1561 1.1 thorpej if (y1 < y) 1562 1.1 thorpej /* the current rtsc is smaller */ 1563 1.1 thorpej return; 1564 1.1 thorpej rtsc->x = x; 1565 1.1 thorpej rtsc->y = y; 1566 1.1 thorpej return; 1567 1.1 thorpej } 1568 1.1 thorpej 1569 1.1 thorpej /* 1570 1.1 thorpej * service curve is concave 1571 1.1 thorpej * compute the two y values of the current rtsc 1572 1.1 thorpej * y1: at x 1573 1.1 thorpej * y2: at (x + dx) 1574 1.1 thorpej */ 1575 1.1 thorpej y1 = rtsc_x2y(rtsc, x); 1576 1.1 thorpej if (y1 <= y) { 1577 1.1 thorpej /* rtsc is below isc, no change to rtsc */ 1578 1.1 thorpej return; 1579 1.1 thorpej } 1580 1.1 thorpej 1581 1.1 thorpej y2 = rtsc_x2y(rtsc, x + isc->dx); 1582 1.1 thorpej if (y2 >= y + isc->dy) { 1583 1.1 thorpej /* rtsc is above isc, replace rtsc by isc */ 1584 1.1 thorpej rtsc->x = x; 1585 1.1 thorpej rtsc->y = y; 1586 1.1 thorpej rtsc->dx = isc->dx; 1587 1.1 thorpej rtsc->dy = isc->dy; 1588 1.1 thorpej return; 1589 1.1 thorpej } 1590 1.1 thorpej 1591 1.1 thorpej /* 1592 1.1 thorpej * the two curves intersect 1593 1.1 thorpej * compute the offsets (dx, dy) using the reverse 1594 1.1 thorpej * function of seg_x2y() 1595 1.1 thorpej * seg_x2y(dx, sm1) == seg_x2y(dx, sm2) + (y1 - y) 1596 1.1 thorpej */ 1597 1.1 thorpej dx = ((y1 - y) << SM_SHIFT) / (isc->sm1 - isc->sm2); 1598 1.1 thorpej /* 1599 1.1 thorpej * check if (x, y1) belongs to the 1st segment of rtsc. 1600 1.1 thorpej * if so, add the offset. 1601 1.10 perry */ 1602 1.1 thorpej if (rtsc->x + rtsc->dx > x) 1603 1.1 thorpej dx += rtsc->x + rtsc->dx - x; 1604 1.1 thorpej dy = seg_x2y(dx, isc->sm1); 1605 1.1 thorpej 1606 1.1 thorpej rtsc->x = x; 1607 1.1 thorpej rtsc->y = y; 1608 1.1 thorpej rtsc->dx = dx; 1609 1.1 thorpej rtsc->dy = dy; 1610 1.1 thorpej return; 1611 1.1 thorpej } 1612 1.1 thorpej 1613 1.19 peter static void 1614 1.19 peter get_class_stats(struct hfsc_classstats *sp, struct hfsc_class *cl) 1615 1.19 peter { 1616 1.19 peter sp->class_id = cl->cl_id; 1617 1.19 peter sp->class_handle = cl->cl_handle; 1618 1.19 peter 1619 1.19 peter if (cl->cl_rsc != NULL) { 1620 1.19 peter sp->rsc.m1 = sm2m(cl->cl_rsc->sm1); 1621 1.19 peter sp->rsc.d = dx2d(cl->cl_rsc->dx); 1622 1.19 peter sp->rsc.m2 = sm2m(cl->cl_rsc->sm2); 1623 1.19 peter } else { 1624 1.19 peter sp->rsc.m1 = 0; 1625 1.19 peter sp->rsc.d = 0; 1626 1.19 peter sp->rsc.m2 = 0; 1627 1.19 peter } 1628 1.19 peter if (cl->cl_fsc != NULL) { 1629 1.19 peter sp->fsc.m1 = sm2m(cl->cl_fsc->sm1); 1630 1.19 peter sp->fsc.d = dx2d(cl->cl_fsc->dx); 1631 1.19 peter sp->fsc.m2 = sm2m(cl->cl_fsc->sm2); 1632 1.19 peter } else { 1633 1.19 peter sp->fsc.m1 = 0; 1634 1.19 peter sp->fsc.d = 0; 1635 1.19 peter sp->fsc.m2 = 0; 1636 1.19 peter } 1637 1.19 peter if (cl->cl_usc != NULL) { 1638 1.19 peter sp->usc.m1 = sm2m(cl->cl_usc->sm1); 1639 1.19 peter sp->usc.d = dx2d(cl->cl_usc->dx); 1640 1.19 peter sp->usc.m2 = sm2m(cl->cl_usc->sm2); 1641 1.19 peter } else { 1642 1.19 peter sp->usc.m1 = 0; 1643 1.19 peter sp->usc.d = 0; 1644 1.19 peter sp->usc.m2 = 0; 1645 1.19 peter } 1646 1.19 peter 1647 1.19 peter sp->total = cl->cl_total; 1648 1.19 peter sp->cumul = cl->cl_cumul; 1649 1.19 peter 1650 1.19 peter sp->d = cl->cl_d; 1651 1.19 peter sp->e = cl->cl_e; 1652 1.19 peter sp->vt = cl->cl_vt; 1653 1.19 peter sp->f = cl->cl_f; 1654 1.19 peter 1655 1.19 peter sp->initvt = cl->cl_initvt; 1656 1.19 peter sp->vtperiod = cl->cl_vtperiod; 1657 1.19 peter sp->parentperiod = cl->cl_parentperiod; 1658 1.19 peter sp->nactive = cl->cl_nactive; 1659 1.19 peter sp->vtoff = cl->cl_vtoff; 1660 1.19 peter sp->cvtmax = cl->cl_cvtmax; 1661 1.19 peter sp->myf = cl->cl_myf; 1662 1.19 peter sp->cfmin = cl->cl_cfmin; 1663 1.19 peter sp->cvtmin = cl->cl_cvtmin; 1664 1.19 peter sp->myfadj = cl->cl_myfadj; 1665 1.19 peter sp->vtadj = cl->cl_vtadj; 1666 1.19 peter 1667 1.19 peter sp->cur_time = read_machclk(); 1668 1.19 peter sp->machclk_freq = machclk_freq; 1669 1.19 peter 1670 1.19 peter sp->qlength = qlen(cl->cl_q); 1671 1.19 peter sp->qlimit = qlimit(cl->cl_q); 1672 1.19 peter sp->xmit_cnt = cl->cl_stats.xmit_cnt; 1673 1.19 peter sp->drop_cnt = cl->cl_stats.drop_cnt; 1674 1.19 peter sp->period = cl->cl_stats.period; 1675 1.19 peter 1676 1.19 peter sp->qtype = qtype(cl->cl_q); 1677 1.19 peter #ifdef ALTQ_RED 1678 1.19 peter if (q_is_red(cl->cl_q)) 1679 1.19 peter red_getstats(cl->cl_red, &sp->red[0]); 1680 1.19 peter #endif 1681 1.19 peter #ifdef ALTQ_RIO 1682 1.19 peter if (q_is_rio(cl->cl_q)) 1683 1.19 peter rio_getstats((rio_t *)cl->cl_red, &sp->red[0]); 1684 1.19 peter #endif 1685 1.19 peter } 1686 1.19 peter 1687 1.19 peter /* convert a class handle to the corresponding class pointer */ 1688 1.19 peter static struct hfsc_class * 1689 1.19 peter clh_to_clp(struct hfsc_if *hif, u_int32_t chandle) 1690 1.19 peter { 1691 1.19 peter int i; 1692 1.19 peter struct hfsc_class *cl; 1693 1.19 peter 1694 1.19 peter if (chandle == 0) 1695 1.31 joe return NULL; 1696 1.19 peter /* 1697 1.19 peter * first, try optimistically the slot matching the lower bits of 1698 1.19 peter * the handle. if it fails, do the linear table search. 1699 1.19 peter */ 1700 1.19 peter i = chandle % HFSC_MAX_CLASSES; 1701 1.19 peter if ((cl = hif->hif_class_tbl[i]) != NULL && cl->cl_handle == chandle) 1702 1.31 joe return cl; 1703 1.19 peter for (i = 0; i < HFSC_MAX_CLASSES; i++) 1704 1.19 peter if ((cl = hif->hif_class_tbl[i]) != NULL && 1705 1.19 peter cl->cl_handle == chandle) 1706 1.31 joe return cl; 1707 1.31 joe return NULL; 1708 1.19 peter } 1709 1.19 peter 1710 1.19 peter #ifdef ALTQ3_COMPAT 1711 1.19 peter static struct hfsc_if * 1712 1.22 christos hfsc_attach(struct ifaltq *ifq, u_int bandwidth) 1713 1.19 peter { 1714 1.19 peter struct hfsc_if *hif; 1715 1.19 peter 1716 1.19 peter hif = malloc(sizeof(struct hfsc_if), M_DEVBUF, M_WAITOK|M_ZERO); 1717 1.19 peter if (hif == NULL) 1718 1.31 joe return NULL; 1719 1.19 peter 1720 1.19 peter hif->hif_eligible = ellist_alloc(); 1721 1.19 peter if (hif->hif_eligible == NULL) { 1722 1.19 peter free(hif, M_DEVBUF); 1723 1.19 peter return NULL; 1724 1.19 peter } 1725 1.19 peter 1726 1.19 peter hif->hif_ifq = ifq; 1727 1.19 peter 1728 1.19 peter /* add this state to the hfsc list */ 1729 1.19 peter hif->hif_next = hif_list; 1730 1.19 peter hif_list = hif; 1731 1.19 peter 1732 1.31 joe return hif; 1733 1.19 peter } 1734 1.19 peter 1735 1.25 christos static void 1736 1.19 peter hfsc_detach(struct hfsc_if *hif) 1737 1.19 peter { 1738 1.19 peter (void)hfsc_clear_interface(hif); 1739 1.19 peter (void)hfsc_class_destroy(hif->hif_rootclass); 1740 1.19 peter 1741 1.19 peter /* remove this interface from the hif list */ 1742 1.19 peter if (hif_list == hif) 1743 1.19 peter hif_list = hif->hif_next; 1744 1.19 peter else { 1745 1.19 peter struct hfsc_if *h; 1746 1.19 peter 1747 1.19 peter for (h = hif_list; h != NULL; h = h->hif_next) 1748 1.19 peter if (h->hif_next == hif) { 1749 1.19 peter h->hif_next = hif->hif_next; 1750 1.19 peter break; 1751 1.19 peter } 1752 1.19 peter ASSERT(h != NULL); 1753 1.19 peter } 1754 1.19 peter 1755 1.19 peter ellist_destroy(hif->hif_eligible); 1756 1.19 peter 1757 1.19 peter free(hif, M_DEVBUF); 1758 1.19 peter } 1759 1.19 peter 1760 1.19 peter static int 1761 1.19 peter hfsc_class_modify(struct hfsc_class *cl, struct service_curve *rsc, 1762 1.19 peter struct service_curve *fsc, struct service_curve *usc) 1763 1.19 peter { 1764 1.19 peter struct internal_sc *rsc_tmp, *fsc_tmp, *usc_tmp; 1765 1.19 peter u_int64_t cur_time; 1766 1.19 peter int s; 1767 1.19 peter 1768 1.19 peter rsc_tmp = fsc_tmp = usc_tmp = NULL; 1769 1.19 peter if (rsc != NULL && (rsc->m1 != 0 || rsc->m2 != 0) && 1770 1.19 peter cl->cl_rsc == NULL) { 1771 1.19 peter rsc_tmp = malloc(sizeof(struct internal_sc), M_DEVBUF, 1772 1.19 peter M_WAITOK); 1773 1.19 peter if (rsc_tmp == NULL) 1774 1.31 joe return ENOMEM; 1775 1.19 peter } 1776 1.19 peter if (fsc != NULL && (fsc->m1 != 0 || fsc->m2 != 0) && 1777 1.19 peter cl->cl_fsc == NULL) { 1778 1.19 peter fsc_tmp = malloc(sizeof(struct internal_sc), M_DEVBUF, 1779 1.19 peter M_WAITOK); 1780 1.19 peter if (fsc_tmp == NULL) 1781 1.31 joe return ENOMEM; 1782 1.19 peter } 1783 1.19 peter if (usc != NULL && (usc->m1 != 0 || usc->m2 != 0) && 1784 1.19 peter cl->cl_usc == NULL) { 1785 1.19 peter usc_tmp = malloc(sizeof(struct internal_sc), M_DEVBUF, 1786 1.19 peter M_WAITOK); 1787 1.19 peter if (usc_tmp == NULL) 1788 1.31 joe return ENOMEM; 1789 1.19 peter } 1790 1.19 peter 1791 1.19 peter cur_time = read_machclk(); 1792 1.19 peter s = splnet(); 1793 1.19 peter 1794 1.19 peter if (rsc != NULL) { 1795 1.19 peter if (rsc->m1 == 0 && rsc->m2 == 0) { 1796 1.19 peter if (cl->cl_rsc != NULL) { 1797 1.19 peter if (!qempty(cl->cl_q)) 1798 1.19 peter hfsc_purgeq(cl); 1799 1.19 peter free(cl->cl_rsc, M_DEVBUF); 1800 1.19 peter cl->cl_rsc = NULL; 1801 1.19 peter } 1802 1.19 peter } else { 1803 1.19 peter if (cl->cl_rsc == NULL) 1804 1.19 peter cl->cl_rsc = rsc_tmp; 1805 1.19 peter sc2isc(rsc, cl->cl_rsc); 1806 1.19 peter rtsc_init(&cl->cl_deadline, cl->cl_rsc, cur_time, 1807 1.19 peter cl->cl_cumul); 1808 1.19 peter cl->cl_eligible = cl->cl_deadline; 1809 1.19 peter if (cl->cl_rsc->sm1 <= cl->cl_rsc->sm2) { 1810 1.19 peter cl->cl_eligible.dx = 0; 1811 1.19 peter cl->cl_eligible.dy = 0; 1812 1.19 peter } 1813 1.19 peter } 1814 1.19 peter } 1815 1.19 peter 1816 1.19 peter if (fsc != NULL) { 1817 1.19 peter if (fsc->m1 == 0 && fsc->m2 == 0) { 1818 1.19 peter if (cl->cl_fsc != NULL) { 1819 1.19 peter if (!qempty(cl->cl_q)) 1820 1.19 peter hfsc_purgeq(cl); 1821 1.19 peter free(cl->cl_fsc, M_DEVBUF); 1822 1.19 peter cl->cl_fsc = NULL; 1823 1.19 peter } 1824 1.19 peter } else { 1825 1.19 peter if (cl->cl_fsc == NULL) 1826 1.19 peter cl->cl_fsc = fsc_tmp; 1827 1.19 peter sc2isc(fsc, cl->cl_fsc); 1828 1.19 peter rtsc_init(&cl->cl_virtual, cl->cl_fsc, cl->cl_vt, 1829 1.19 peter cl->cl_total); 1830 1.19 peter } 1831 1.19 peter } 1832 1.19 peter 1833 1.19 peter if (usc != NULL) { 1834 1.19 peter if (usc->m1 == 0 && usc->m2 == 0) { 1835 1.19 peter if (cl->cl_usc != NULL) { 1836 1.19 peter free(cl->cl_usc, M_DEVBUF); 1837 1.19 peter cl->cl_usc = NULL; 1838 1.19 peter cl->cl_myf = 0; 1839 1.19 peter } 1840 1.19 peter } else { 1841 1.19 peter if (cl->cl_usc == NULL) 1842 1.19 peter cl->cl_usc = usc_tmp; 1843 1.19 peter sc2isc(usc, cl->cl_usc); 1844 1.19 peter rtsc_init(&cl->cl_ulimit, cl->cl_usc, cur_time, 1845 1.19 peter cl->cl_total); 1846 1.19 peter } 1847 1.19 peter } 1848 1.19 peter 1849 1.19 peter if (!qempty(cl->cl_q)) { 1850 1.19 peter if (cl->cl_rsc != NULL) 1851 1.19 peter update_ed(cl, m_pktlen(qhead(cl->cl_q))); 1852 1.19 peter if (cl->cl_fsc != NULL) 1853 1.19 peter update_vf(cl, 0, cur_time); 1854 1.19 peter /* is this enough? */ 1855 1.19 peter } 1856 1.19 peter 1857 1.19 peter splx(s); 1858 1.19 peter 1859 1.31 joe return 0; 1860 1.19 peter } 1861 1.19 peter 1862 1.1 thorpej /* 1863 1.1 thorpej * hfsc device interface 1864 1.1 thorpej */ 1865 1.1 thorpej int 1866 1.22 christos hfscopen(dev_t dev, int flag, int fmt, 1867 1.22 christos struct lwp *l) 1868 1.1 thorpej { 1869 1.1 thorpej if (machclk_freq == 0) 1870 1.1 thorpej init_machclk(); 1871 1.1 thorpej 1872 1.1 thorpej if (machclk_freq == 0) { 1873 1.9 wiz printf("hfsc: no CPU clock available!\n"); 1874 1.31 joe return ENXIO; 1875 1.1 thorpej } 1876 1.1 thorpej 1877 1.1 thorpej /* everything will be done when the queueing scheme is attached. */ 1878 1.1 thorpej return 0; 1879 1.1 thorpej } 1880 1.1 thorpej 1881 1.1 thorpej int 1882 1.22 christos hfscclose(dev_t dev, int flag, int fmt, 1883 1.22 christos struct lwp *l) 1884 1.1 thorpej { 1885 1.1 thorpej struct hfsc_if *hif; 1886 1.1 thorpej 1887 1.1 thorpej while ((hif = hif_list) != NULL) { 1888 1.1 thorpej /* destroy all */ 1889 1.1 thorpej if (ALTQ_IS_ENABLED(hif->hif_ifq)) 1890 1.1 thorpej altq_disable(hif->hif_ifq); 1891 1.1 thorpej 1892 1.25 christos int error = altq_detach(hif->hif_ifq); 1893 1.25 christos switch (error) { 1894 1.25 christos case 0: 1895 1.25 christos case ENXIO: /* already disabled */ 1896 1.25 christos break; 1897 1.25 christos default: 1898 1.25 christos return error; 1899 1.25 christos } 1900 1.25 christos hfsc_detach(hif); 1901 1.1 thorpej } 1902 1.1 thorpej 1903 1.25 christos return 0; 1904 1.1 thorpej } 1905 1.1 thorpej 1906 1.1 thorpej int 1907 1.23 christos hfscioctl(dev_t dev, ioctlcmd_t cmd, void *addr, int flag, 1908 1.18 christos struct lwp *l) 1909 1.1 thorpej { 1910 1.1 thorpej struct hfsc_if *hif; 1911 1.1 thorpej struct hfsc_interface *ifacep; 1912 1.1 thorpej int error = 0; 1913 1.1 thorpej 1914 1.1 thorpej /* check super-user privilege */ 1915 1.1 thorpej switch (cmd) { 1916 1.1 thorpej case HFSC_GETSTATS: 1917 1.1 thorpej break; 1918 1.1 thorpej default: 1919 1.21 elad if ((error = kauth_authorize_network(l->l_cred, 1920 1.21 elad KAUTH_NETWORK_ALTQ, KAUTH_REQ_NETWORK_ALTQ_HFSC, NULL, 1921 1.21 elad NULL, NULL)) != 0) 1922 1.31 joe return error; 1923 1.1 thorpej break; 1924 1.1 thorpej } 1925 1.10 perry 1926 1.1 thorpej switch (cmd) { 1927 1.1 thorpej 1928 1.1 thorpej case HFSC_IF_ATTACH: 1929 1.1 thorpej error = hfsccmd_if_attach((struct hfsc_attach *)addr); 1930 1.1 thorpej break; 1931 1.1 thorpej 1932 1.1 thorpej case HFSC_IF_DETACH: 1933 1.1 thorpej error = hfsccmd_if_detach((struct hfsc_interface *)addr); 1934 1.1 thorpej break; 1935 1.1 thorpej 1936 1.1 thorpej case HFSC_ENABLE: 1937 1.1 thorpej case HFSC_DISABLE: 1938 1.1 thorpej case HFSC_CLEAR_HIERARCHY: 1939 1.1 thorpej ifacep = (struct hfsc_interface *)addr; 1940 1.1 thorpej if ((hif = altq_lookup(ifacep->hfsc_ifname, 1941 1.1 thorpej ALTQT_HFSC)) == NULL) { 1942 1.1 thorpej error = EBADF; 1943 1.1 thorpej break; 1944 1.1 thorpej } 1945 1.1 thorpej 1946 1.1 thorpej switch (cmd) { 1947 1.1 thorpej 1948 1.1 thorpej case HFSC_ENABLE: 1949 1.1 thorpej if (hif->hif_defaultclass == NULL) { 1950 1.19 peter #ifdef ALTQ_DEBUG 1951 1.1 thorpej printf("hfsc: no default class\n"); 1952 1.1 thorpej #endif 1953 1.1 thorpej error = EINVAL; 1954 1.1 thorpej break; 1955 1.1 thorpej } 1956 1.1 thorpej error = altq_enable(hif->hif_ifq); 1957 1.1 thorpej break; 1958 1.1 thorpej 1959 1.1 thorpej case HFSC_DISABLE: 1960 1.1 thorpej error = altq_disable(hif->hif_ifq); 1961 1.1 thorpej break; 1962 1.1 thorpej 1963 1.1 thorpej case HFSC_CLEAR_HIERARCHY: 1964 1.1 thorpej hfsc_clear_interface(hif); 1965 1.1 thorpej break; 1966 1.1 thorpej } 1967 1.1 thorpej break; 1968 1.1 thorpej 1969 1.1 thorpej case HFSC_ADD_CLASS: 1970 1.1 thorpej error = hfsccmd_add_class((struct hfsc_add_class *)addr); 1971 1.1 thorpej break; 1972 1.1 thorpej 1973 1.1 thorpej case HFSC_DEL_CLASS: 1974 1.1 thorpej error = hfsccmd_delete_class((struct hfsc_delete_class *)addr); 1975 1.1 thorpej break; 1976 1.1 thorpej 1977 1.1 thorpej case HFSC_MOD_CLASS: 1978 1.1 thorpej error = hfsccmd_modify_class((struct hfsc_modify_class *)addr); 1979 1.1 thorpej break; 1980 1.1 thorpej 1981 1.1 thorpej case HFSC_ADD_FILTER: 1982 1.1 thorpej error = hfsccmd_add_filter((struct hfsc_add_filter *)addr); 1983 1.1 thorpej break; 1984 1.1 thorpej 1985 1.1 thorpej case HFSC_DEL_FILTER: 1986 1.1 thorpej error = hfsccmd_delete_filter((struct hfsc_delete_filter *)addr); 1987 1.1 thorpej break; 1988 1.1 thorpej 1989 1.1 thorpej case HFSC_GETSTATS: 1990 1.1 thorpej error = hfsccmd_class_stats((struct hfsc_class_stats *)addr); 1991 1.1 thorpej break; 1992 1.1 thorpej 1993 1.1 thorpej default: 1994 1.1 thorpej error = EINVAL; 1995 1.1 thorpej break; 1996 1.1 thorpej } 1997 1.1 thorpej return error; 1998 1.1 thorpej } 1999 1.1 thorpej 2000 1.1 thorpej static int 2001 1.19 peter hfsccmd_if_attach(struct hfsc_attach *ap) 2002 1.1 thorpej { 2003 1.1 thorpej struct hfsc_if *hif; 2004 1.1 thorpej struct ifnet *ifp; 2005 1.1 thorpej int error; 2006 1.10 perry 2007 1.1 thorpej if ((ifp = ifunit(ap->iface.hfsc_ifname)) == NULL) 2008 1.31 joe return ENXIO; 2009 1.1 thorpej 2010 1.1 thorpej if ((hif = hfsc_attach(&ifp->if_snd, ap->bandwidth)) == NULL) 2011 1.31 joe return ENOMEM; 2012 1.10 perry 2013 1.1 thorpej /* 2014 1.1 thorpej * set HFSC to this ifnet structure. 2015 1.1 thorpej */ 2016 1.1 thorpej if ((error = altq_attach(&ifp->if_snd, ALTQT_HFSC, hif, 2017 1.1 thorpej hfsc_enqueue, hfsc_dequeue, hfsc_request, 2018 1.1 thorpej &hif->hif_classifier, acc_classify)) != 0) 2019 1.25 christos hfsc_detach(hif); 2020 1.1 thorpej 2021 1.31 joe return error; 2022 1.1 thorpej } 2023 1.1 thorpej 2024 1.1 thorpej static int 2025 1.19 peter hfsccmd_if_detach(struct hfsc_interface *ap) 2026 1.1 thorpej { 2027 1.1 thorpej struct hfsc_if *hif; 2028 1.1 thorpej int error; 2029 1.1 thorpej 2030 1.1 thorpej if ((hif = altq_lookup(ap->hfsc_ifname, ALTQT_HFSC)) == NULL) 2031 1.31 joe return EBADF; 2032 1.10 perry 2033 1.1 thorpej if (ALTQ_IS_ENABLED(hif->hif_ifq)) 2034 1.1 thorpej altq_disable(hif->hif_ifq); 2035 1.1 thorpej 2036 1.1 thorpej if ((error = altq_detach(hif->hif_ifq))) 2037 1.31 joe return error; 2038 1.1 thorpej 2039 1.25 christos hfsc_detach(hif); 2040 1.25 christos return 0; 2041 1.1 thorpej } 2042 1.1 thorpej 2043 1.1 thorpej static int 2044 1.19 peter hfsccmd_add_class(struct hfsc_add_class *ap) 2045 1.1 thorpej { 2046 1.1 thorpej struct hfsc_if *hif; 2047 1.1 thorpej struct hfsc_class *cl, *parent; 2048 1.19 peter int i; 2049 1.1 thorpej 2050 1.1 thorpej if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) 2051 1.31 joe return EBADF; 2052 1.1 thorpej 2053 1.19 peter if (ap->parent_handle == HFSC_NULLCLASS_HANDLE && 2054 1.19 peter hif->hif_rootclass == NULL) 2055 1.19 peter parent = NULL; 2056 1.19 peter else if ((parent = clh_to_clp(hif, ap->parent_handle)) == NULL) 2057 1.31 joe return EINVAL; 2058 1.19 peter 2059 1.19 peter /* assign a class handle (use a free slot number for now) */ 2060 1.19 peter for (i = 1; i < HFSC_MAX_CLASSES; i++) 2061 1.19 peter if (hif->hif_class_tbl[i] == NULL) 2062 1.19 peter break; 2063 1.19 peter if (i == HFSC_MAX_CLASSES) 2064 1.31 joe return EBUSY; 2065 1.10 perry 2066 1.19 peter if ((cl = hfsc_class_create(hif, &ap->service_curve, NULL, NULL, 2067 1.19 peter parent, ap->qlimit, ap->flags, i)) == NULL) 2068 1.31 joe return ENOMEM; 2069 1.10 perry 2070 1.1 thorpej /* return a class handle to the user */ 2071 1.19 peter ap->class_handle = i; 2072 1.19 peter 2073 1.31 joe return 0; 2074 1.1 thorpej } 2075 1.1 thorpej 2076 1.1 thorpej static int 2077 1.19 peter hfsccmd_delete_class(struct hfsc_delete_class *ap) 2078 1.1 thorpej { 2079 1.1 thorpej struct hfsc_if *hif; 2080 1.1 thorpej struct hfsc_class *cl; 2081 1.1 thorpej 2082 1.1 thorpej if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) 2083 1.31 joe return EBADF; 2084 1.1 thorpej 2085 1.1 thorpej if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL) 2086 1.31 joe return EINVAL; 2087 1.10 perry 2088 1.1 thorpej return hfsc_class_destroy(cl); 2089 1.1 thorpej } 2090 1.1 thorpej 2091 1.1 thorpej static int 2092 1.19 peter hfsccmd_modify_class(struct hfsc_modify_class *ap) 2093 1.1 thorpej { 2094 1.1 thorpej struct hfsc_if *hif; 2095 1.1 thorpej struct hfsc_class *cl; 2096 1.1 thorpej struct service_curve *rsc = NULL; 2097 1.1 thorpej struct service_curve *fsc = NULL; 2098 1.19 peter struct service_curve *usc = NULL; 2099 1.1 thorpej 2100 1.1 thorpej if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) 2101 1.31 joe return EBADF; 2102 1.1 thorpej 2103 1.1 thorpej if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL) 2104 1.31 joe return EINVAL; 2105 1.1 thorpej 2106 1.1 thorpej if (ap->sctype & HFSC_REALTIMESC) 2107 1.1 thorpej rsc = &ap->service_curve; 2108 1.1 thorpej if (ap->sctype & HFSC_LINKSHARINGSC) 2109 1.1 thorpej fsc = &ap->service_curve; 2110 1.19 peter if (ap->sctype & HFSC_UPPERLIMITSC) 2111 1.19 peter usc = &ap->service_curve; 2112 1.1 thorpej 2113 1.19 peter return hfsc_class_modify(cl, rsc, fsc, usc); 2114 1.1 thorpej } 2115 1.1 thorpej 2116 1.1 thorpej static int 2117 1.19 peter hfsccmd_add_filter(struct hfsc_add_filter *ap) 2118 1.1 thorpej { 2119 1.1 thorpej struct hfsc_if *hif; 2120 1.1 thorpej struct hfsc_class *cl; 2121 1.1 thorpej 2122 1.1 thorpej if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) 2123 1.31 joe return EBADF; 2124 1.1 thorpej 2125 1.1 thorpej if ((cl = clh_to_clp(hif, ap->class_handle)) == NULL) 2126 1.31 joe return EINVAL; 2127 1.1 thorpej 2128 1.1 thorpej if (is_a_parent_class(cl)) { 2129 1.19 peter #ifdef ALTQ_DEBUG 2130 1.1 thorpej printf("hfsccmd_add_filter: not a leaf class!\n"); 2131 1.1 thorpej #endif 2132 1.31 joe return EINVAL; 2133 1.1 thorpej } 2134 1.1 thorpej 2135 1.1 thorpej return acc_add_filter(&hif->hif_classifier, &ap->filter, 2136 1.1 thorpej cl, &ap->filter_handle); 2137 1.1 thorpej } 2138 1.1 thorpej 2139 1.1 thorpej static int 2140 1.19 peter hfsccmd_delete_filter(struct hfsc_delete_filter *ap) 2141 1.1 thorpej { 2142 1.1 thorpej struct hfsc_if *hif; 2143 1.1 thorpej 2144 1.1 thorpej if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) 2145 1.31 joe return EBADF; 2146 1.1 thorpej 2147 1.1 thorpej return acc_delete_filter(&hif->hif_classifier, 2148 1.1 thorpej ap->filter_handle); 2149 1.1 thorpej } 2150 1.1 thorpej 2151 1.1 thorpej static int 2152 1.19 peter hfsccmd_class_stats(struct hfsc_class_stats *ap) 2153 1.1 thorpej { 2154 1.1 thorpej struct hfsc_if *hif; 2155 1.1 thorpej struct hfsc_class *cl; 2156 1.19 peter struct hfsc_classstats stats, *usp; 2157 1.1 thorpej int n, nclasses, error; 2158 1.10 perry 2159 1.1 thorpej if ((hif = altq_lookup(ap->iface.hfsc_ifname, ALTQT_HFSC)) == NULL) 2160 1.31 joe return EBADF; 2161 1.1 thorpej 2162 1.1 thorpej ap->cur_time = read_machclk(); 2163 1.19 peter ap->machclk_freq = machclk_freq; 2164 1.1 thorpej ap->hif_classes = hif->hif_classes; 2165 1.1 thorpej ap->hif_packets = hif->hif_packets; 2166 1.1 thorpej 2167 1.1 thorpej /* skip the first N classes in the tree */ 2168 1.1 thorpej nclasses = ap->nskip; 2169 1.1 thorpej for (cl = hif->hif_rootclass, n = 0; cl != NULL && n < nclasses; 2170 1.1 thorpej cl = hfsc_nextclass(cl), n++) 2171 1.1 thorpej ; 2172 1.1 thorpej if (n != nclasses) 2173 1.31 joe return EINVAL; 2174 1.1 thorpej 2175 1.1 thorpej /* then, read the next N classes in the tree */ 2176 1.1 thorpej nclasses = ap->nclasses; 2177 1.1 thorpej usp = ap->stats; 2178 1.1 thorpej for (n = 0; cl != NULL && n < nclasses; cl = hfsc_nextclass(cl), n++) { 2179 1.1 thorpej 2180 1.29 riastrad memset(&stats, 0, sizeof(stats)); 2181 1.1 thorpej get_class_stats(&stats, cl); 2182 1.10 perry 2183 1.23 christos if ((error = copyout((void *)&stats, (void *)usp++, 2184 1.1 thorpej sizeof(stats))) != 0) 2185 1.31 joe return error; 2186 1.1 thorpej } 2187 1.1 thorpej 2188 1.1 thorpej ap->nclasses = n; 2189 1.1 thorpej 2190 1.31 joe return 0; 2191 1.1 thorpej } 2192 1.1 thorpej 2193 1.1 thorpej #ifdef KLD_MODULE 2194 1.1 thorpej 2195 1.1 thorpej static struct altqsw hfsc_sw = 2196 1.1 thorpej {"hfsc", hfscopen, hfscclose, hfscioctl}; 2197 1.1 thorpej 2198 1.1 thorpej ALTQ_MODULE(altq_hfsc, ALTQT_HFSC, &hfsc_sw); 2199 1.19 peter MODULE_DEPEND(altq_hfsc, altq_red, 1, 1, 1); 2200 1.19 peter MODULE_DEPEND(altq_hfsc, altq_rio, 1, 1, 1); 2201 1.1 thorpej 2202 1.1 thorpej #endif /* KLD_MODULE */ 2203 1.19 peter #endif /* ALTQ3_COMPAT */ 2204 1.1 thorpej 2205 1.1 thorpej #endif /* ALTQ_HFSC */ 2206