1/* 2 * Copyright (c) 2011-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 31 * 32 * This code is derived from software contributed to The DragonFly Project 33 * by Matthew Dillon <dillon@backplane.com> 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in 43 * the documentation and/or other materials provided with the 44 * distribution. 45 * 3. Neither the name of The DragonFly Project nor the names of its 46 * contributors may be used to endorse or promote products derived 47 * from this software without specific, prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 50 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 51 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 52 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 53 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 54 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 55 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 56 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 57 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 58 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 59 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 * 62 * $DragonFly: src/sys/net/altq/altq_fairq.c,v 1.2 2008/05/14 11:59:23 sephe Exp $ 63 */ 64/* 65 * Matt: I gutted altq_priq.c and used it as a skeleton on which to build 66 * fairq. The fairq algorithm is completely different then priq, of course, 67 * but because I used priq's skeleton I believe I should include priq's 68 * copyright. 69 * 70 * Copyright (C) 2000-2003 71 * Sony Computer Science Laboratories Inc. All rights reserved. 72 * 73 * Redistribution and use in source and binary forms, with or without 74 * modification, are permitted provided that the following conditions 75 * are met: 76 * 1. Redistributions of source code must retain the above copyright 77 * notice, this list of conditions and the following disclaimer. 78 * 2. Redistributions in binary form must reproduce the above copyright 79 * notice, this list of conditions and the following disclaimer in the 80 * documentation and/or other materials provided with the distribution. 81 * 82 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 83 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 84 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 85 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 86 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 87 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 88 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 89 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 90 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 91 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 92 * SUCH DAMAGE. 93 */ 94 95/* 96 * FAIRQ - take traffic classified by keep state (hashed into 97 * pf->pftag_flowhash) and bucketize it. Fairly extract 98 * the first packet from each bucket in a round-robin fashion. 99 * 100 * TODO - better overall qlimit support (right now it is per-bucket). 101 * - NOTE: red etc is per bucket, not overall. 102 * - better service curve support. 103 * 104 * EXAMPLE: 105 * 106 * altq on em0 fairq bandwidth 650Kb queue { std, bulk } 107 * queue std priority 3 bandwidth 200Kb \ 108 * fairq (buckets 64, default, hogs 1Kb) qlimit 50 109 * queue bulk priority 2 bandwidth 100Kb \ 110 * fairq (buckets 64, hogs 1Kb) qlimit 50 111 * 112 * NOTE: When the aggregate bandwidth is less than the link bandwidth 113 * any remaining bandwidth is dynamically assigned using the 114 * existing bandwidth specs as weightings. 115 * 116 * pass out on em0 from any to any keep state queue std 117 * pass out on em0 inet proto tcp ..... port ... keep state queue bulk 118 */ 119 120#if PKTSCHED_FAIRQ 121 122#include <sys/cdefs.h> 123#include <sys/param.h> 124#include <sys/malloc.h> 125#include <sys/mbuf.h> 126#include <sys/systm.h> 127#include <sys/errno.h> 128#include <sys/kernel.h> 129#include <sys/syslog.h> 130 131#include <kern/zalloc.h> 132 133#include <net/if.h> 134#include <net/net_osdep.h> 135 136#include <net/pktsched/pktsched_fairq.h> 137#include <netinet/in.h> 138 139/* 140 * function prototypes 141 */ 142#if 0 143static int fairq_enqueue_ifclassq(struct ifclassq *, struct mbuf *); 144static struct mbuf *fairq_dequeue_ifclassq(struct ifclassq *, cqdq_op_t); 145static int fairq_request_ifclassq(struct ifclassq *, cqrq_t, void *); 146#endif 147static int fairq_clear_interface(struct fairq_if *); 148static inline int fairq_addq(struct fairq_class *, struct mbuf *, 149 struct pf_mtag *); 150static inline struct mbuf *fairq_getq(struct fairq_class *, u_int64_t); 151static inline struct mbuf *fairq_pollq(struct fairq_class *, u_int64_t, int *); 152static fairq_bucket_t *fairq_selectq(struct fairq_class *, int); 153static void fairq_purgeq(struct fairq_if *, struct fairq_class *, u_int32_t, 154 u_int32_t *, u_int32_t *); 155static void fairq_updateq(struct fairq_if *, struct fairq_class *, cqev_t); 156static struct fairq_class *fairq_class_create(struct fairq_if *, int, u_int32_t, 157 u_int64_t, u_int32_t, int, u_int64_t, u_int64_t, u_int64_t, u_int64_t, 158 u_int32_t); 159static int fairq_class_destroy(struct fairq_if *, struct fairq_class *); 160static int fairq_destroy_locked(struct fairq_if *); 161static inline struct fairq_class *fairq_clh_to_clp(struct fairq_if *, 162 u_int32_t); 163static const char *fairq_style(struct fairq_if *); 164 165#define FAIRQ_ZONE_MAX 32 /* maximum elements in zone */ 166#define FAIRQ_ZONE_NAME "pktsched_fairq" /* zone name */ 167 168static unsigned int fairq_size; /* size of zone element */ 169static struct zone *fairq_zone; /* zone for fairq */ 170 171#define FAIRQ_CL_ZONE_MAX 32 /* maximum elements in zone */ 172#define FAIRQ_CL_ZONE_NAME "pktsched_fairq_cl" /* zone name */ 173 174static unsigned int fairq_cl_size; /* size of zone element */ 175static struct zone *fairq_cl_zone; /* zone for fairq */ 176 177void 178fairq_init(void) 179{ 180 fairq_size = sizeof (struct fairq_if); 181 fairq_zone = zinit(fairq_size, FAIRQ_ZONE_MAX * fairq_size, 182 0, FAIRQ_ZONE_NAME); 183 if (fairq_zone == NULL) { 184 panic("%s: failed allocating %s", __func__, FAIRQ_ZONE_NAME); 185 /* NOTREACHED */ 186 } 187 zone_change(fairq_zone, Z_EXPAND, TRUE); 188 zone_change(fairq_zone, Z_CALLERACCT, TRUE); 189 190 fairq_cl_size = sizeof (struct fairq_class); 191 fairq_cl_zone = zinit(fairq_cl_size, FAIRQ_CL_ZONE_MAX * fairq_cl_size, 192 0, FAIRQ_CL_ZONE_NAME); 193 if (fairq_cl_zone == NULL) { 194 panic("%s: failed allocating %s", __func__, FAIRQ_CL_ZONE_NAME); 195 /* NOTREACHED */ 196 } 197 zone_change(fairq_cl_zone, Z_EXPAND, TRUE); 198 zone_change(fairq_cl_zone, Z_CALLERACCT, TRUE); 199} 200 201struct fairq_if * 202fairq_alloc(struct ifnet *ifp, int how, boolean_t altq) 203{ 204 struct fairq_if *fif; 205 206 fif = (how == M_WAITOK) ? 207 zalloc(fairq_zone) : zalloc_noblock(fairq_zone); 208 if (fif == NULL) 209 return (NULL); 210 211 bzero(fif, fairq_size); 212 fif->fif_maxpri = -1; 213 fif->fif_ifq = &ifp->if_snd; 214 if (altq) 215 fif->fif_flags |= FAIRQIFF_ALTQ; 216 217 if (pktsched_verbose) { 218 log(LOG_DEBUG, "%s: %s scheduler allocated\n", 219 if_name(ifp), fairq_style(fif)); 220 } 221 222 return (fif); 223} 224 225int 226fairq_destroy(struct fairq_if *fif) 227{ 228 struct ifclassq *ifq = fif->fif_ifq; 229 int err; 230 231 IFCQ_LOCK(ifq); 232 err = fairq_destroy_locked(fif); 233 IFCQ_UNLOCK(ifq); 234 235 return (err); 236} 237 238static int 239fairq_destroy_locked(struct fairq_if *fif) 240{ 241 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 242 243 (void) fairq_clear_interface(fif); 244 245 if (pktsched_verbose) { 246 log(LOG_DEBUG, "%s: %s scheduler destroyed\n", 247 if_name(FAIRQIF_IFP(fif)), fairq_style(fif)); 248 } 249 250 zfree(fairq_zone, fif); 251 252 return (0); 253} 254 255/* 256 * bring the interface back to the initial state by discarding 257 * all the filters and classes. 258 */ 259static int 260fairq_clear_interface(struct fairq_if *fif) 261{ 262 struct fairq_class *cl; 263 int pri; 264 265 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 266 267 /* clear out the classes */ 268 for (pri = 0; pri <= fif->fif_maxpri; pri++) 269 if ((cl = fif->fif_classes[pri]) != NULL) 270 fairq_class_destroy(fif, cl); 271 272 return (0); 273} 274 275/* discard all the queued packets on the interface */ 276void 277fairq_purge(struct fairq_if *fif) 278{ 279 struct fairq_class *cl; 280 int pri; 281 282 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 283 284 for (pri = 0; pri <= fif->fif_maxpri; pri++) { 285 if ((cl = fif->fif_classes[pri]) != NULL && cl->cl_head) 286 fairq_purgeq(fif, cl, 0, NULL, NULL); 287 } 288#if !PF_ALTQ 289 /* 290 * This assertion is safe to be made only when PF_ALTQ is not 291 * configured; otherwise, IFCQ_LEN represents the sum of the 292 * packets managed by ifcq_disc and altq_disc instances, which 293 * is possible when transitioning between the two. 294 */ 295 VERIFY(IFCQ_LEN(fif->fif_ifq) == 0); 296#endif /* !PF_ALTQ */ 297} 298 299void 300fairq_event(struct fairq_if *fif, cqev_t ev) 301{ 302 struct fairq_class *cl; 303 int pri; 304 305 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 306 307 for (pri = 0; pri <= fif->fif_maxpri; pri++) 308 if ((cl = fif->fif_classes[pri]) != NULL) 309 fairq_updateq(fif, cl, ev); 310} 311 312int 313fairq_add_queue(struct fairq_if *fif, int priority, u_int32_t qlimit, 314 u_int64_t bandwidth, u_int32_t nbuckets, int flags, u_int64_t hogs_m1, 315 u_int64_t lssc_m1, u_int64_t lssc_d, u_int64_t lssc_m2, u_int32_t qid, 316 struct fairq_class **clp) 317{ 318 struct fairq_class *cl; 319 320 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 321 322 /* check parameters */ 323 if (priority >= FAIRQ_MAXPRI) 324 return (EINVAL); 325 if (bandwidth == 0 || (bandwidth / 8) == 0) 326 return (EINVAL); 327 if (fif->fif_classes[priority] != NULL) 328 return (EBUSY); 329 if (fairq_clh_to_clp(fif, qid) != NULL) 330 return (EBUSY); 331 332 cl = fairq_class_create(fif, priority, qlimit, bandwidth, 333 nbuckets, flags, hogs_m1, lssc_m1, lssc_d, lssc_m2, qid); 334 if (cl == NULL) 335 return (ENOMEM); 336 337 if (clp != NULL) 338 *clp = cl; 339 340 return (0); 341} 342 343static struct fairq_class * 344fairq_class_create(struct fairq_if *fif, int pri, u_int32_t qlimit, 345 u_int64_t bandwidth, u_int32_t nbuckets, int flags, u_int64_t hogs_m1, 346 u_int64_t lssc_m1, u_int64_t lssc_d, u_int64_t lssc_m2, u_int32_t qid) 347{ 348#pragma unused(lssc_d, lssc_m2) 349 struct ifnet *ifp; 350 struct ifclassq *ifq; 351 struct fairq_class *cl; 352 u_int32_t i; 353 354 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 355 356 /* Sanitize flags unless internally configured */ 357 if (fif->fif_flags & FAIRQIFF_ALTQ) 358 flags &= FARF_USERFLAGS; 359 360#if !CLASSQ_RED 361 if (flags & FARF_RED) { 362 log(LOG_ERR, "%s: %s RED not available!\n", 363 if_name(FAIRQIF_IFP(fif)), fairq_style(fif)); 364 return (NULL); 365 } 366#endif /* !CLASSQ_RED */ 367 368#if !CLASSQ_RIO 369 if (flags & FARF_RIO) { 370 log(LOG_ERR, "%s: %s RIO not available!\n", 371 if_name(FAIRQIF_IFP(fif)), fairq_style(fif)); 372 return (NULL); 373 } 374#endif /* CLASSQ_RIO */ 375 376#if !CLASSQ_BLUE 377 if (flags & FARF_BLUE) { 378 log(LOG_ERR, "%s: %s BLUE not available!\n", 379 if_name(FAIRQIF_IFP(fif)), fairq_style(fif)); 380 return (NULL); 381 } 382#endif /* CLASSQ_BLUE */ 383 384 /* These are mutually exclusive */ 385 if ((flags & (FARF_RED|FARF_RIO|FARF_BLUE|FARF_SFB)) && 386 (flags & (FARF_RED|FARF_RIO|FARF_BLUE|FARF_SFB)) != FARF_RED && 387 (flags & (FARF_RED|FARF_RIO|FARF_BLUE|FARF_SFB)) != FARF_RIO && 388 (flags & (FARF_RED|FARF_RIO|FARF_BLUE|FARF_SFB)) != FARF_BLUE && 389 (flags & (FARF_RED|FARF_RIO|FARF_BLUE|FARF_SFB)) != FARF_SFB) { 390 log(LOG_ERR, "%s: %s more than one RED|RIO|BLUE|SFB\n", 391 if_name(FAIRQIF_IFP(fif)), fairq_style(fif)); 392 return (NULL); 393 } 394 395 if (bandwidth == 0 || (bandwidth / 8) == 0) { 396 log(LOG_ERR, "%s: %s invalid data rate %llu\n", 397 if_name(FAIRQIF_IFP(fif)), fairq_style(fif), bandwidth); 398 return (NULL); 399 } 400 401 if (nbuckets == 0) 402 nbuckets = 256; 403 if (nbuckets > FAIRQ_MAX_BUCKETS) 404 nbuckets = FAIRQ_MAX_BUCKETS; 405 /* enforce power-of-2 size */ 406 while ((nbuckets ^ (nbuckets - 1)) != ((nbuckets << 1) - 1)) 407 ++nbuckets; 408 409 ifq = fif->fif_ifq; 410 ifp = FAIRQIF_IFP(fif); 411 412 if ((cl = fif->fif_classes[pri]) != NULL) { 413 /* modify the class instead of creating a new one */ 414 if (cl->cl_head) 415 fairq_purgeq(fif, cl, 0, NULL, NULL); 416#if CLASSQ_RIO 417 if (cl->cl_qtype == Q_RIO) 418 rio_destroy(cl->cl_rio); 419#endif /* CLASSQ_RIO */ 420#if CLASSQ_RED 421 if (cl->cl_qtype == Q_RED) 422 red_destroy(cl->cl_red); 423#endif /* CLASSQ_RED */ 424#if CLASSQ_BLUE 425 if (cl->cl_qtype == Q_BLUE) 426 blue_destroy(cl->cl_blue); 427#endif /* CLASSQ_BLUE */ 428 if (cl->cl_qtype == Q_SFB && cl->cl_sfb != NULL) 429 sfb_destroy(cl->cl_sfb); 430 cl->cl_qalg.ptr = NULL; 431 cl->cl_qtype = Q_DROPTAIL; 432 cl->cl_qstate = QS_RUNNING; 433 } else { 434 cl = zalloc(fairq_cl_zone); 435 if (cl == NULL) 436 goto err_ret; 437 bzero(cl, fairq_cl_size); 438 cl->cl_nbuckets = nbuckets; 439 cl->cl_nbucket_mask = nbuckets - 1; 440 441 cl->cl_buckets = _MALLOC(sizeof (struct fairq_bucket) * 442 cl->cl_nbuckets, M_DEVBUF, M_WAITOK|M_ZERO); 443 if (cl->cl_buckets == NULL) 444 goto err_buckets; 445 cl->cl_head = NULL; 446 } 447 448 fif->fif_classes[pri] = cl; 449 if (flags & FARF_DEFAULTCLASS) 450 fif->fif_default = cl; 451 if (qlimit == 0 || qlimit > IFCQ_MAXLEN(ifq)) { 452 qlimit = IFCQ_MAXLEN(ifq); 453 if (qlimit == 0) 454 qlimit = DEFAULT_QLIMIT; /* use default */ 455 } 456 cl->cl_qlimit = qlimit; 457 for (i = 0; i < cl->cl_nbuckets; ++i) { 458 _qinit(&cl->cl_buckets[i].queue, Q_DROPTAIL, qlimit); 459 } 460 cl->cl_bandwidth = bandwidth / 8; /* cvt to bytes per second */ 461 cl->cl_qtype = Q_DROPTAIL; 462 cl->cl_qstate = QS_RUNNING; 463 cl->cl_flags = flags; 464 cl->cl_pri = pri; 465 if (pri > fif->fif_maxpri) 466 fif->fif_maxpri = pri; 467 cl->cl_fif = fif; 468 cl->cl_handle = qid; 469 cl->cl_hogs_m1 = hogs_m1 / 8; 470 cl->cl_lssc_m1 = lssc_m1 / 8; /* NOT YET USED */ 471 cl->cl_bw_current = 0; 472 473 if (flags & (FARF_RED|FARF_RIO|FARF_BLUE|FARF_SFB)) { 474#if CLASSQ_RED || CLASSQ_RIO 475 u_int64_t ifbandwidth = ifnet_output_linkrate(ifp); 476 int pkttime; 477#endif /* CLASSQ_RED || CLASSQ_RIO */ 478 479 cl->cl_qflags = 0; 480 if (flags & FARF_ECN) { 481 if (flags & FARF_BLUE) 482 cl->cl_qflags |= BLUEF_ECN; 483 else if (flags & FARF_SFB) 484 cl->cl_qflags |= SFBF_ECN; 485 else if (flags & FARF_RED) 486 cl->cl_qflags |= REDF_ECN; 487 else if (flags & FARF_RIO) 488 cl->cl_qflags |= RIOF_ECN; 489 } 490 if (flags & FARF_FLOWCTL) { 491 if (flags & FARF_SFB) 492 cl->cl_qflags |= SFBF_FLOWCTL; 493 } 494 if (flags & FARF_CLEARDSCP) { 495 if (flags & FARF_RIO) 496 cl->cl_qflags |= RIOF_CLEARDSCP; 497 } 498#if CLASSQ_RED || CLASSQ_RIO 499 /* 500 * XXX: RED & RIO should be watching link speed and MTU 501 * events and recompute pkttime accordingly. 502 */ 503 if (ifbandwidth < 8) 504 pkttime = 1000 * 1000 * 1000; /* 1 sec */ 505 else 506 pkttime = (int64_t)ifp->if_mtu * 1000 * 1000 * 1000 / 507 (ifbandwidth / 8); 508 509 /* Test for exclusivity {RED,RIO,BLUE,SFB} was done above */ 510#if CLASSQ_RIO 511 if (flags & FARF_RIO) { 512 cl->cl_rio = 513 rio_alloc(ifp, 0, NULL, cl->cl_qflags, pkttime); 514 if (cl->cl_rio != NULL) 515 cl->cl_qtype = Q_RIO; 516 } 517#endif /* CLASSQ_RIO */ 518#if CLASSQ_RED 519 if (flags & FARF_RED) { 520 cl->cl_red = red_alloc(ifp, 0, 0, 521 cl->cl_qlimit * 10/100, 522 cl->cl_qlimit * 30/100, 523 cl->cl_qflags, pkttime); 524 if (cl->cl_red != NULL) 525 cl->cl_qtype = Q_RED; 526 } 527#endif /* CLASSQ_RED */ 528#endif /* CLASSQ_RED || CLASSQ_RIO */ 529#if CLASSQ_BLUE 530 if (flags & FARF_BLUE) { 531 cl->cl_blue = blue_alloc(ifp, 0, 0, cl->cl_qflags); 532 if (cl->cl_blue != NULL) 533 cl->cl_qtype = Q_BLUE; 534 } 535#endif /* CLASSQ_BLUE */ 536 if (flags & FARF_SFB) { 537 if (!(cl->cl_flags & FARF_LAZY)) 538 cl->cl_sfb = sfb_alloc(ifp, cl->cl_handle, 539 cl->cl_qlimit, cl->cl_qflags); 540 if (cl->cl_sfb != NULL || (cl->cl_flags & FARF_LAZY)) 541 cl->cl_qtype = Q_SFB; 542 } 543 } 544 545 if (pktsched_verbose) { 546 log(LOG_DEBUG, "%s: %s created qid=%d pri=%d qlimit=%d " 547 "flags=%b\n", if_name(ifp), fairq_style(fif), 548 cl->cl_handle, cl->cl_pri, cl->cl_qlimit, flags, FARF_BITS); 549 } 550 551 return (cl); 552 553err_buckets: 554 if (cl->cl_buckets != NULL) 555 _FREE(cl->cl_buckets, M_DEVBUF); 556err_ret: 557 if (cl != NULL) { 558 if (cl->cl_qalg.ptr != NULL) { 559#if CLASSQ_RIO 560 if (cl->cl_qtype == Q_RIO) 561 rio_destroy(cl->cl_rio); 562#endif /* CLASSQ_RIO */ 563#if CLASSQ_RED 564 if (cl->cl_qtype == Q_RED) 565 red_destroy(cl->cl_red); 566#endif /* CLASSQ_RED */ 567#if CLASSQ_BLUE 568 if (cl->cl_qtype == Q_BLUE) 569 blue_destroy(cl->cl_blue); 570#endif /* CLASSQ_BLUE */ 571 if (cl->cl_qtype == Q_SFB && cl->cl_sfb != NULL) 572 sfb_destroy(cl->cl_sfb); 573 cl->cl_qalg.ptr = NULL; 574 cl->cl_qtype = Q_DROPTAIL; 575 cl->cl_qstate = QS_RUNNING; 576 } 577 zfree(fairq_cl_zone, cl); 578 } 579 return (NULL); 580} 581 582int 583fairq_remove_queue(struct fairq_if *fif, u_int32_t qid) 584{ 585 struct fairq_class *cl; 586 587 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 588 589 if ((cl = fairq_clh_to_clp(fif, qid)) == NULL) 590 return (EINVAL); 591 592 return (fairq_class_destroy(fif, cl)); 593} 594 595static int 596fairq_class_destroy(struct fairq_if *fif, struct fairq_class *cl) 597{ 598 struct ifclassq *ifq = fif->fif_ifq; 599 int pri; 600 601 IFCQ_LOCK_ASSERT_HELD(ifq); 602 603 if (cl->cl_head) 604 fairq_purgeq(fif, cl, 0, NULL, NULL); 605 606 fif->fif_classes[cl->cl_pri] = NULL; 607 if (fif->fif_poll_cache == cl) 608 fif->fif_poll_cache = NULL; 609 if (fif->fif_maxpri == cl->cl_pri) { 610 for (pri = cl->cl_pri; pri >= 0; pri--) 611 if (fif->fif_classes[pri] != NULL) { 612 fif->fif_maxpri = pri; 613 break; 614 } 615 if (pri < 0) 616 fif->fif_maxpri = -1; 617 } 618 619 if (cl->cl_qalg.ptr != NULL) { 620#if CLASSQ_RIO 621 if (cl->cl_qtype == Q_RIO) 622 rio_destroy(cl->cl_rio); 623#endif /* CLASSQ_RIO */ 624#if CLASSQ_RED 625 if (cl->cl_qtype == Q_RED) 626 red_destroy(cl->cl_red); 627#endif /* CLASSQ_RED */ 628#if CLASSQ_BLUE 629 if (cl->cl_qtype == Q_BLUE) 630 blue_destroy(cl->cl_blue); 631#endif /* CLASSQ_BLUE */ 632 if (cl->cl_qtype == Q_SFB && cl->cl_sfb != NULL) 633 sfb_destroy(cl->cl_sfb); 634 cl->cl_qalg.ptr = NULL; 635 cl->cl_qtype = Q_DROPTAIL; 636 cl->cl_qstate = QS_RUNNING; 637 } 638 639 if (fif->fif_default == cl) 640 fif->fif_default = NULL; 641 642 if (pktsched_verbose) { 643 log(LOG_DEBUG, "%s: %s destroyed qid=%d pri=%d\n", 644 if_name(FAIRQIF_IFP(fif)), fairq_style(fif), 645 cl->cl_handle, cl->cl_pri); 646 } 647 648 _FREE(cl->cl_buckets, M_DEVBUF); 649 cl->cl_head = NULL; /* sanity */ 650 cl->cl_polled = NULL; /* sanity */ 651 cl->cl_buckets = NULL; /* sanity */ 652 653 zfree(fairq_cl_zone, cl); 654 655 return (0); 656} 657 658int 659fairq_enqueue(struct fairq_if *fif, struct fairq_class *cl, struct mbuf *m, 660 struct pf_mtag *t) 661{ 662 struct ifclassq *ifq = fif->fif_ifq; 663 int len, ret; 664 665 IFCQ_LOCK_ASSERT_HELD(ifq); 666 VERIFY(cl == NULL || cl->cl_fif == fif); 667 668 if (cl == NULL) { 669#if PF_ALTQ 670 cl = fairq_clh_to_clp(fif, t->pftag_qid); 671#else /* !PF_ALTQ */ 672 cl = fairq_clh_to_clp(fif, 0); 673#endif /* !PF_ALTQ */ 674 if (cl == NULL) { 675 cl = fif->fif_default; 676 if (cl == NULL) { 677 IFCQ_CONVERT_LOCK(ifq); 678 m_freem(m); 679 return (ENOBUFS); 680 } 681 } 682 } 683 684 cl->cl_flags |= FARF_HAS_PACKETS; 685 len = m_pktlen(m); 686 687 ret = fairq_addq(cl, m, t); 688 if (ret != 0) { 689 if (ret == CLASSQEQ_SUCCESS_FC) { 690 /* packet enqueued, return advisory feedback */ 691 ret = EQFULL; 692 } else { 693 VERIFY(ret == CLASSQEQ_DROPPED || 694 ret == CLASSQEQ_DROPPED_FC || 695 ret == CLASSQEQ_DROPPED_SP); 696 697 /* packet has been freed in fairq_addq */ 698 PKTCNTR_ADD(&cl->cl_dropcnt, 1, len); 699 IFCQ_DROP_ADD(ifq, 1, len); 700 switch (ret) { 701 case CLASSQEQ_DROPPED: 702 return (ENOBUFS); 703 case CLASSQEQ_DROPPED_FC: 704 return (EQFULL); 705 case CLASSQEQ_DROPPED_SP: 706 return (EQSUSPENDED); 707 } 708 /* NOT REACHED */ 709 } 710 } 711 IFCQ_INC_LEN(ifq); 712 713 /* successfully queued. */ 714 return (ret); 715} 716 717/* 718 * note: CLASSQDQ_POLL returns the next packet without removing the packet 719 * from the queue. CLASSQDQ_REMOVE is a normal dequeue operation. 720 * CLASSQDQ_REMOVE must return the same packet if called immediately 721 * after CLASSQDQ_POLL. 722 */ 723struct mbuf * 724fairq_dequeue(struct fairq_if *fif, cqdq_op_t op) 725{ 726 struct ifclassq *ifq = fif->fif_ifq; 727 struct fairq_class *cl; 728 struct fairq_class *best_cl; 729 struct mbuf *best_m; 730 struct mbuf *m; 731 u_int64_t cur_time = read_machclk(); 732 u_int32_t best_scale; 733 u_int32_t scale; 734 int pri; 735 int hit_limit; 736 737 IFCQ_LOCK_ASSERT_HELD(ifq); 738 739 if (IFCQ_IS_EMPTY(ifq)) { 740 /* no packet in the queue */ 741 return (NULL); 742 } 743 744 if (fif->fif_poll_cache && op == CLASSQDQ_REMOVE) { 745 best_cl = fif->fif_poll_cache; 746 m = fairq_getq(best_cl, cur_time); 747 fif->fif_poll_cache = NULL; 748 if (m != NULL) { 749 IFCQ_DEC_LEN(ifq); 750 IFCQ_XMIT_ADD(ifq, 1, m_pktlen(m)); 751 PKTCNTR_ADD(&best_cl->cl_xmitcnt, 1, m_pktlen(m)); 752 } 753 } else { 754 best_cl = NULL; 755 best_m = NULL; 756 best_scale = 0xFFFFFFFFU; 757 758 for (pri = fif->fif_maxpri; pri >= 0; pri--) { 759 if ((cl = fif->fif_classes[pri]) == NULL) 760 continue; 761 if ((cl->cl_flags & FARF_HAS_PACKETS) == 0) 762 continue; 763 m = fairq_pollq(cl, cur_time, &hit_limit); 764 if (m == NULL) { 765 cl->cl_flags &= ~FARF_HAS_PACKETS; 766 continue; 767 } 768 769 /* 770 * We can halt the search immediately if the queue 771 * did not hit its bandwidth limit. 772 */ 773 if (hit_limit == 0) { 774 best_cl = cl; 775 best_m = m; 776 break; 777 } 778 779 /* 780 * Otherwise calculate the scale factor and select 781 * the queue with the lowest scale factor. This 782 * apportions any unused bandwidth weighted by 783 * the relative bandwidth specification. 784 */ 785 scale = cl->cl_bw_current * 100 / cl->cl_bandwidth; 786 if (scale < best_scale) { 787 best_cl = cl; 788 best_m = m; 789 best_scale = scale; 790 } 791 } 792 793 if (op == CLASSQDQ_POLL) { 794 fif->fif_poll_cache = best_cl; 795 m = best_m; 796 } else if (best_cl != NULL) { 797 m = fairq_getq(best_cl, cur_time); 798 if (m != NULL) { 799 IFCQ_DEC_LEN(ifq); 800 IFCQ_XMIT_ADD(ifq, 1, m_pktlen(m)); 801 PKTCNTR_ADD(&best_cl->cl_xmitcnt, 1, 802 m_pktlen(m)); 803 } 804 } else { 805 m = NULL; 806 } 807 } 808 return (m); 809} 810 811static inline int 812fairq_addq(struct fairq_class *cl, struct mbuf *m, struct pf_mtag *t) 813{ 814 struct ifclassq *ifq = cl->cl_fif->fif_ifq; 815 fairq_bucket_t *b; 816 u_int32_t hash = m->m_pkthdr.pkt_flowid; 817 u_int32_t hindex; 818 u_int64_t bw; 819 820 IFCQ_LOCK_ASSERT_HELD(ifq); 821 822 /* 823 * If the packet doesn't have any keep state put it on the end of 824 * our queue. XXX this can result in out of order delivery. 825 */ 826 if (hash == 0) { 827 if (cl->cl_head) 828 b = cl->cl_head->prev; 829 else 830 b = &cl->cl_buckets[0]; 831 } else { 832 hindex = (hash & cl->cl_nbucket_mask); 833 b = &cl->cl_buckets[hindex]; 834 } 835 836 /* 837 * Add the bucket to the end of the circular list of active buckets. 838 * 839 * As a special case we add the bucket to the beginning of the list 840 * instead of the end if it was not previously on the list and if 841 * its traffic is less then the hog level. 842 */ 843 if (b->in_use == 0) { 844 b->in_use = 1; 845 if (cl->cl_head == NULL) { 846 cl->cl_head = b; 847 b->next = b; 848 b->prev = b; 849 } else { 850 b->next = cl->cl_head; 851 b->prev = cl->cl_head->prev; 852 b->prev->next = b; 853 b->next->prev = b; 854 855 if (b->bw_delta && cl->cl_hogs_m1) { 856 bw = b->bw_bytes * machclk_freq / b->bw_delta; 857 if (bw < cl->cl_hogs_m1) 858 cl->cl_head = b; 859 } 860 } 861 } 862 863#if CLASSQ_RIO 864 if (cl->cl_qtype == Q_RIO) 865 return (rio_addq(cl->cl_rio, &b->queue, m, t)); 866 else 867#endif /* CLASSQ_RIO */ 868#if CLASSQ_RED 869 if (cl->cl_qtype == Q_RED) 870 return (red_addq(cl->cl_red, &b->queue, m, t)); 871 else 872#endif /* CLASSQ_RED */ 873#if CLASSQ_BLUE 874 if (cl->cl_qtype == Q_BLUE) 875 return (blue_addq(cl->cl_blue, &b->queue, m, t)); 876 else 877#endif /* CLASSQ_BLUE */ 878 if (cl->cl_qtype == Q_SFB) { 879 if (cl->cl_sfb == NULL) { 880 struct ifnet *ifp = FAIRQIF_IFP(cl->cl_fif); 881 882 VERIFY(cl->cl_flags & FARF_LAZY); 883 IFCQ_CONVERT_LOCK(ifq); 884 885 cl->cl_sfb = sfb_alloc(ifp, cl->cl_handle, 886 cl->cl_qlimit, cl->cl_qflags); 887 if (cl->cl_sfb == NULL) { 888 /* fall back to droptail */ 889 cl->cl_qtype = Q_DROPTAIL; 890 cl->cl_flags &= ~FARF_SFB; 891 cl->cl_qflags &= ~(SFBF_ECN | SFBF_FLOWCTL); 892 893 log(LOG_ERR, "%s: %s SFB lazy allocation " 894 "failed for qid=%d pri=%d, falling back " 895 "to DROPTAIL\n", if_name(ifp), 896 fairq_style(cl->cl_fif), cl->cl_handle, 897 cl->cl_pri); 898 } 899 } 900 if (cl->cl_sfb != NULL) 901 return (sfb_addq(cl->cl_sfb, &b->queue, m, t)); 902 } else if (qlen(&b->queue) >= qlimit(&b->queue)) { 903 IFCQ_CONVERT_LOCK(ifq); 904 m_freem(m); 905 return (CLASSQEQ_DROPPED); 906 } 907 908#if PF_ECN 909 if (cl->cl_flags & FARF_CLEARDSCP) 910 write_dsfield(m, t, 0); 911#endif /* PF_ECN */ 912 913 _addq(&b->queue, m); 914 915 return (0); 916} 917 918static inline struct mbuf * 919fairq_getq(struct fairq_class *cl, u_int64_t cur_time) 920{ 921 fairq_bucket_t *b; 922 struct mbuf *m; 923 924 IFCQ_LOCK_ASSERT_HELD(cl->cl_fif->fif_ifq); 925 926 b = fairq_selectq(cl, 0); 927 if (b == NULL) 928 m = NULL; 929#if CLASSQ_RIO 930 else if (cl->cl_qtype == Q_RIO) 931 m = rio_getq(cl->cl_rio, &b->queue); 932#endif /* CLASSQ_RIO */ 933#if CLASSQ_RED 934 else if (cl->cl_qtype == Q_RED) 935 m = red_getq(cl->cl_red, &b->queue); 936#endif /* CLASSQ_RED */ 937#if CLASSQ_BLUE 938 else if (cl->cl_qtype == Q_BLUE) 939 m = blue_getq(cl->cl_blue, &b->queue); 940#endif /* CLASSQ_BLUE */ 941 else if (cl->cl_qtype == Q_SFB && cl->cl_sfb != NULL) 942 m = sfb_getq(cl->cl_sfb, &b->queue); 943 else 944 m = _getq(&b->queue); 945 946 /* 947 * Calculate the BW change 948 */ 949 if (m != NULL) { 950 u_int64_t delta; 951 952 /* 953 * Per-class bandwidth calculation 954 */ 955 delta = (cur_time - cl->cl_last_time); 956 if (delta > machclk_freq * 8) 957 delta = machclk_freq * 8; 958 cl->cl_bw_delta += delta; 959 cl->cl_bw_bytes += m->m_pkthdr.len; 960 cl->cl_last_time = cur_time; 961 if (cl->cl_bw_delta > machclk_freq) { 962 cl->cl_bw_delta -= cl->cl_bw_delta >> 2; 963 cl->cl_bw_bytes -= cl->cl_bw_bytes >> 2; 964 } 965 966 /* 967 * Per-bucket bandwidth calculation 968 */ 969 delta = (cur_time - b->last_time); 970 if (delta > machclk_freq * 8) 971 delta = machclk_freq * 8; 972 b->bw_delta += delta; 973 b->bw_bytes += m->m_pkthdr.len; 974 b->last_time = cur_time; 975 if (b->bw_delta > machclk_freq) { 976 b->bw_delta -= b->bw_delta >> 2; 977 b->bw_bytes -= b->bw_bytes >> 2; 978 } 979 } 980 return (m); 981} 982 983/* 984 * Figure out what the next packet would be if there were no limits. If 985 * this class hits its bandwidth limit *hit_limit is set to no-zero, otherwise 986 * it is set to 0. A non-NULL mbuf is returned either way. 987 */ 988static inline struct mbuf * 989fairq_pollq(struct fairq_class *cl, u_int64_t cur_time, int *hit_limit) 990{ 991 fairq_bucket_t *b; 992 struct mbuf *m; 993 u_int64_t delta; 994 u_int64_t bw; 995 996 IFCQ_LOCK_ASSERT_HELD(cl->cl_fif->fif_ifq); 997 998 *hit_limit = 0; 999 b = fairq_selectq(cl, 1); 1000 if (b == NULL) 1001 return (NULL); 1002 m = qhead(&b->queue); 1003 1004 /* 1005 * Did this packet exceed the class bandwidth? Calculate the 1006 * bandwidth component of the packet. 1007 * 1008 * - Calculate bytes per second 1009 */ 1010 delta = cur_time - cl->cl_last_time; 1011 if (delta > machclk_freq * 8) 1012 delta = machclk_freq * 8; 1013 cl->cl_bw_delta += delta; 1014 cl->cl_last_time = cur_time; 1015 if (cl->cl_bw_delta) { 1016 bw = cl->cl_bw_bytes * machclk_freq / cl->cl_bw_delta; 1017 1018 if (bw > cl->cl_bandwidth) 1019 *hit_limit = 1; 1020 cl->cl_bw_current = bw; 1021#if 0 1022 printf("BW %6lld relative to %6u %d queue 0x%llx\n", 1023 bw, cl->cl_bandwidth, *hit_limit, 1024 (uint64_t)VM_KERNEL_ADDRPERM(b)); 1025#endif 1026 } 1027 return (m); 1028} 1029 1030/* 1031 * Locate the next queue we want to pull a packet out of. This code 1032 * is also responsible for removing empty buckets from the circular list. 1033 */ 1034static fairq_bucket_t * 1035fairq_selectq(struct fairq_class *cl, int ispoll) 1036{ 1037 fairq_bucket_t *b; 1038 u_int64_t bw; 1039 1040 IFCQ_LOCK_ASSERT_HELD(cl->cl_fif->fif_ifq); 1041 1042 if (ispoll == 0 && cl->cl_polled) { 1043 b = cl->cl_polled; 1044 cl->cl_polled = NULL; 1045 return (b); 1046 } 1047 1048 while ((b = cl->cl_head) != NULL) { 1049 /* 1050 * Remove empty queues from consideration 1051 */ 1052 if (qempty(&b->queue)) { 1053 b->in_use = 0; 1054 cl->cl_head = b->next; 1055 if (cl->cl_head == b) { 1056 cl->cl_head = NULL; 1057 } else { 1058 b->next->prev = b->prev; 1059 b->prev->next = b->next; 1060 } 1061 continue; 1062 } 1063 1064 /* 1065 * Advance the round robin. Queues with bandwidths less 1066 * then the hog bandwidth are allowed to burst. 1067 */ 1068 if (cl->cl_hogs_m1 == 0) { 1069 cl->cl_head = b->next; 1070 } else if (b->bw_delta) { 1071 bw = b->bw_bytes * machclk_freq / b->bw_delta; 1072 if (bw >= cl->cl_hogs_m1) { 1073 cl->cl_head = b->next; 1074 } 1075 /* 1076 * XXX TODO - 1077 */ 1078 } 1079 1080 /* 1081 * Return bucket b. 1082 */ 1083 break; 1084 } 1085 if (ispoll) 1086 cl->cl_polled = b; 1087 return (b); 1088} 1089 1090static void 1091fairq_purgeq(struct fairq_if *fif, struct fairq_class *cl, u_int32_t flow, 1092 u_int32_t *packets, u_int32_t *bytes) 1093{ 1094 struct ifclassq *ifq = fif->fif_ifq; 1095 u_int32_t _cnt = 0, _len = 0; 1096 fairq_bucket_t *b; 1097 1098 IFCQ_LOCK_ASSERT_HELD(ifq); 1099 1100 /* become regular mutex before freeing mbufs */ 1101 IFCQ_CONVERT_LOCK(ifq); 1102 1103 while ((b = fairq_selectq(cl, 0)) != NULL) { 1104 u_int32_t cnt, len, qlen; 1105 1106 if ((qlen = qlen(&b->queue)) == 0) 1107 continue; 1108 1109#if CLASSQ_RIO 1110 if (cl->cl_qtype == Q_RIO) 1111 rio_purgeq(cl->cl_rio, &b->queue, flow, &cnt, &len); 1112 else 1113#endif /* CLASSQ_RIO */ 1114#if CLASSQ_RED 1115 if (cl->cl_qtype == Q_RED) 1116 red_purgeq(cl->cl_red, &b->queue, flow, &cnt, &len); 1117 else 1118#endif /* CLASSQ_RED */ 1119#if CLASSQ_BLUE 1120 if (cl->cl_qtype == Q_BLUE) 1121 blue_purgeq(cl->cl_blue, &b->queue, flow, &cnt, &len); 1122 else 1123#endif /* CLASSQ_BLUE */ 1124 if (cl->cl_qtype == Q_SFB && cl->cl_sfb != NULL) 1125 sfb_purgeq(cl->cl_sfb, &b->queue, flow, &cnt, &len); 1126 else 1127 _flushq_flow(&b->queue, flow, &cnt, &len); 1128 1129 if (cnt == 0) 1130 continue; 1131 1132 VERIFY(qlen(&b->queue) == (qlen - cnt)); 1133 1134 PKTCNTR_ADD(&cl->cl_dropcnt, cnt, len); 1135 IFCQ_DROP_ADD(ifq, cnt, len); 1136 1137 VERIFY(((signed)IFCQ_LEN(ifq) - cnt) >= 0); 1138 IFCQ_LEN(ifq) -= cnt; 1139 1140 _cnt += cnt; 1141 _len += len; 1142 1143 if (pktsched_verbose) { 1144 log(LOG_DEBUG, "%s: %s purge qid=%d pri=%d " 1145 "qlen=[%d,%d] cnt=%d len=%d flow=0x%x\n", 1146 if_name(FAIRQIF_IFP(fif)), fairq_style(fif), 1147 cl->cl_handle, cl->cl_pri, qlen, qlen(&b->queue), 1148 cnt, len, flow); 1149 } 1150 } 1151 1152 if (packets != NULL) 1153 *packets = _cnt; 1154 if (bytes != NULL) 1155 *bytes = _len; 1156} 1157 1158static void 1159fairq_updateq(struct fairq_if *fif, struct fairq_class *cl, cqev_t ev) 1160{ 1161 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 1162 1163 if (pktsched_verbose) { 1164 log(LOG_DEBUG, "%s: %s update qid=%d pri=%d event=%s\n", 1165 if_name(FAIRQIF_IFP(fif)), fairq_style(fif), 1166 cl->cl_handle, cl->cl_pri, ifclassq_ev2str(ev)); 1167 } 1168 1169#if CLASSQ_RIO 1170 if (cl->cl_qtype == Q_RIO) 1171 return (rio_updateq(cl->cl_rio, ev)); 1172#endif /* CLASSQ_RIO */ 1173#if CLASSQ_RED 1174 if (cl->cl_qtype == Q_RED) 1175 return (red_updateq(cl->cl_red, ev)); 1176#endif /* CLASSQ_RED */ 1177#if CLASSQ_BLUE 1178 if (cl->cl_qtype == Q_BLUE) 1179 return (blue_updateq(cl->cl_blue, ev)); 1180#endif /* CLASSQ_BLUE */ 1181 if (cl->cl_qtype == Q_SFB && cl->cl_sfb != NULL) 1182 return (sfb_updateq(cl->cl_sfb, ev)); 1183} 1184 1185int 1186fairq_get_class_stats(struct fairq_if *fif, u_int32_t qid, 1187 struct fairq_classstats *sp) 1188{ 1189 struct fairq_class *cl; 1190 fairq_bucket_t *b; 1191 1192 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 1193 1194 if ((cl = fairq_clh_to_clp(fif, qid)) == NULL) 1195 return (EINVAL); 1196 1197 sp->class_handle = cl->cl_handle; 1198 sp->priority = cl->cl_pri; 1199 sp->qlimit = cl->cl_qlimit; 1200 sp->xmit_cnt = cl->cl_xmitcnt; 1201 sp->drop_cnt = cl->cl_dropcnt; 1202 sp->qtype = cl->cl_qtype; 1203 sp->qstate = cl->cl_qstate; 1204 sp->qlength = 0; 1205 1206 if (cl->cl_head) { 1207 b = cl->cl_head; 1208 do { 1209 sp->qlength += qlen(&b->queue); 1210 b = b->next; 1211 } while (b != cl->cl_head); 1212 } 1213 1214#if CLASSQ_RED 1215 if (cl->cl_qtype == Q_RED) 1216 red_getstats(cl->cl_red, &sp->red[0]); 1217#endif /* CLASSQ_RED */ 1218#if CLASSQ_RIO 1219 if (cl->cl_qtype == Q_RIO) 1220 rio_getstats(cl->cl_rio, &sp->red[0]); 1221#endif /* CLASSQ_RIO */ 1222#if CLASSQ_BLUE 1223 if (cl->cl_qtype == Q_BLUE) 1224 blue_getstats(cl->cl_blue, &sp->blue); 1225#endif /* CLASSQ_BLUE */ 1226 if (cl->cl_qtype == Q_SFB && cl->cl_sfb != NULL) 1227 sfb_getstats(cl->cl_sfb, &sp->sfb); 1228 1229 return (0); 1230} 1231 1232/* convert a class handle to the corresponding class pointer */ 1233static inline struct fairq_class * 1234fairq_clh_to_clp(struct fairq_if *fif, u_int32_t chandle) 1235{ 1236 struct fairq_class *cl; 1237 int idx; 1238 1239 IFCQ_LOCK_ASSERT_HELD(fif->fif_ifq); 1240 1241 for (idx = fif->fif_maxpri; idx >= 0; idx--) 1242 if ((cl = fif->fif_classes[idx]) != NULL && 1243 cl->cl_handle == chandle) 1244 return (cl); 1245 1246 return (NULL); 1247} 1248 1249static const char * 1250fairq_style(struct fairq_if *fif) 1251{ 1252 return ((fif->fif_flags & FAIRQIFF_ALTQ) ? "ALTQ_FAIRQ" : "FAIRQ"); 1253} 1254 1255int 1256fairq_setup_ifclassq(struct ifclassq *ifq, u_int32_t flags) 1257{ 1258#pragma unused(ifq, flags) 1259 return (ENXIO); /* not yet */ 1260} 1261 1262int 1263fairq_teardown_ifclassq(struct ifclassq *ifq) 1264{ 1265 struct fairq_if *fif = ifq->ifcq_disc; 1266 int i; 1267 1268 IFCQ_LOCK_ASSERT_HELD(ifq); 1269 VERIFY(fif != NULL && ifq->ifcq_type == PKTSCHEDT_FAIRQ); 1270 1271 (void) fairq_destroy_locked(fif); 1272 1273 ifq->ifcq_disc = NULL; 1274 for (i = 0; i < IFCQ_SC_MAX; i++) { 1275 ifq->ifcq_disc_slots[i].qid = 0; 1276 ifq->ifcq_disc_slots[i].cl = NULL; 1277 } 1278 1279 return (ifclassq_detach(ifq)); 1280} 1281 1282int 1283fairq_getqstats_ifclassq(struct ifclassq *ifq, u_int32_t slot, 1284 struct if_ifclassq_stats *ifqs) 1285{ 1286 struct fairq_if *fif = ifq->ifcq_disc; 1287 1288 IFCQ_LOCK_ASSERT_HELD(ifq); 1289 VERIFY(ifq->ifcq_type == PKTSCHEDT_FAIRQ); 1290 1291 if (slot >= IFCQ_SC_MAX) 1292 return (EINVAL); 1293 1294 return (fairq_get_class_stats(fif, ifq->ifcq_disc_slots[slot].qid, 1295 &ifqs->ifqs_fairq_stats)); 1296} 1297#endif /* PKTSCHED_FAIRQ */ 1298