]>
git.saurik.com Git - apple/xnu.git/blob - bsd/net/pktsched/pktsched_cbq.c
41b1f8ede3276ba4f1501b26dcf5fd8e8707a298
2 * Copyright (c) 2007-2012 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 /* $OpenBSD: altq_cbq.c,v 1.23 2007/09/13 20:40:02 chl Exp $ */
30 /* $KAME: altq_cbq.c,v 1.9 2000/12/14 08:12:45 thorpej Exp $ */
33 * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved.
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the SMCC Technology
49 * Development Group at Sun Microsystems, Inc.
51 * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
52 * promote products derived from this software without specific prior
55 * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
56 * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is
57 * provided "as is" without express or implied warranty of any kind.
59 * These notices must be retained in any copies of any part of this software.
64 #include <sys/cdefs.h>
65 #include <sys/param.h>
66 #include <sys/malloc.h>
68 #include <sys/systm.h>
69 #include <sys/errno.h>
70 #include <sys/kernel.h>
71 #include <sys/syslog.h>
73 #include <kern/zalloc.h>
76 #include <net/net_osdep.h>
78 #include <net/pktsched/pktsched_cbq.h>
79 #include <netinet/in.h>
82 * Forward Declarations.
85 static int cbq_enqueue_ifclassq(struct ifclassq
*, struct mbuf
*);
86 static struct mbuf
*cbq_dequeue_ifclassq(struct ifclassq
*, cqdq_op_t
);
87 static int cbq_request_ifclassq(struct ifclassq
*, cqrq_t
, void *);
89 static int cbq_class_destroy(cbq_state_t
*, struct rm_class
*);
90 static int cbq_destroy_locked(cbq_state_t
*);
91 static struct rm_class
*cbq_clh_to_clp(cbq_state_t
*, u_int32_t
);
92 static const char *cbq_style(cbq_state_t
*);
93 static int cbq_clear_interface(cbq_state_t
*);
94 static void cbqrestart(struct ifclassq
*);
96 #define CBQ_ZONE_MAX 32 /* maximum elements in zone */
97 #define CBQ_ZONE_NAME "pktsched_cbq" /* zone name */
99 static unsigned int cbq_size
; /* size of zone element */
100 static struct zone
*cbq_zone
; /* zone for cbq */
105 _CASSERT(CBQCLF_RED
== RMCF_RED
);
106 _CASSERT(CBQCLF_ECN
== RMCF_ECN
);
107 _CASSERT(CBQCLF_RIO
== RMCF_RIO
);
108 _CASSERT(CBQCLF_FLOWVALVE
== RMCF_FLOWVALVE
);
109 _CASSERT(CBQCLF_CLEARDSCP
== RMCF_CLEARDSCP
);
110 _CASSERT(CBQCLF_WRR
== RMCF_WRR
);
111 _CASSERT(CBQCLF_EFFICIENT
== RMCF_EFFICIENT
);
112 _CASSERT(CBQCLF_BLUE
== RMCF_BLUE
);
113 _CASSERT(CBQCLF_SFB
== RMCF_SFB
);
114 _CASSERT(CBQCLF_FLOWCTL
== RMCF_FLOWCTL
);
115 _CASSERT(CBQCLF_LAZY
== RMCF_LAZY
);
117 cbq_size
= sizeof (cbq_state_t
);
118 cbq_zone
= zinit(cbq_size
, CBQ_ZONE_MAX
* cbq_size
, 0, CBQ_ZONE_NAME
);
119 if (cbq_zone
== NULL
) {
120 panic("%s: failed allocating %s", __func__
, CBQ_ZONE_NAME
);
123 zone_change(cbq_zone
, Z_EXPAND
, TRUE
);
124 zone_change(cbq_zone
, Z_CALLERACCT
, TRUE
);
130 cbq_alloc(struct ifnet
*ifp
, int how
, boolean_t altq
)
134 /* allocate and initialize cbq_state_t */
135 cbqp
= (how
== M_WAITOK
) ? zalloc(cbq_zone
) : zalloc_noblock(cbq_zone
);
139 bzero(cbqp
, cbq_size
);
140 CALLOUT_INIT(&cbqp
->cbq_callout
);
142 cbqp
->ifnp
.ifq_
= &ifp
->if_snd
; /* keep the ifclassq */
144 cbqp
->cbq_flags
|= CBQSF_ALTQ
;
146 if (pktsched_verbose
) {
147 log(LOG_DEBUG
, "%s: %s scheduler allocated\n",
148 if_name(ifp
), cbq_style(cbqp
));
155 cbq_destroy(cbq_state_t
*cbqp
)
157 struct ifclassq
*ifq
= cbqp
->ifnp
.ifq_
;
161 err
= cbq_destroy_locked(cbqp
);
168 cbq_destroy_locked(cbq_state_t
*cbqp
)
170 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
172 (void) cbq_clear_interface(cbqp
);
174 if (pktsched_verbose
) {
175 log(LOG_DEBUG
, "%s: %s scheduler destroyed\n",
176 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
));
179 if (cbqp
->ifnp
.default_
)
180 cbq_class_destroy(cbqp
, cbqp
->ifnp
.default_
);
181 if (cbqp
->ifnp
.root_
)
182 cbq_class_destroy(cbqp
, cbqp
->ifnp
.root_
);
184 /* deallocate cbq_state_t */
185 zfree(cbq_zone
, cbqp
);
191 cbq_add_queue(cbq_state_t
*cbqp
, u_int32_t qlimit
, u_int32_t priority
,
192 u_int32_t minburst
, u_int32_t maxburst
, u_int32_t pktsize
,
193 u_int32_t maxpktsize
, u_int32_t ns_per_byte
, u_int32_t maxidle
, int minidle
,
194 u_int32_t offtime
, u_int32_t flags
, u_int32_t parent_qid
, u_int32_t qid
,
195 struct rm_class
**clp
)
197 #pragma unused(minburst, maxburst, maxpktsize)
198 struct rm_class
*borrow
, *parent
;
202 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
204 /* Sanitize flags unless internally configured */
205 if (cbqp
->cbq_flags
& CBQSF_ALTQ
)
206 flags
&= CBQCLF_USERFLAGS
;
209 * find a free slot in the class table. if the slot matching
210 * the lower bits of qid is free, use this slot. otherwise,
211 * use the first free slot.
213 i
= qid
% CBQ_MAX_CLASSES
;
214 if (cbqp
->cbq_class_tbl
[i
] != NULL
) {
215 for (i
= 0; i
< CBQ_MAX_CLASSES
; i
++)
216 if (cbqp
->cbq_class_tbl
[i
] == NULL
)
218 if (i
== CBQ_MAX_CLASSES
)
222 /* check parameters */
223 if (priority
>= CBQ_MAXPRI
)
226 if (ns_per_byte
== 0) {
227 log(LOG_ERR
, "%s: %s invalid inverse data rate\n",
228 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
));
232 /* Get pointers to parent and borrow classes. */
233 parent
= cbq_clh_to_clp(cbqp
, parent_qid
);
234 if (flags
& CBQCLF_BORROW
)
240 * A class must borrow from its parent or it can not
241 * borrow at all. Hence, borrow can be null.
243 if (parent
== NULL
&& (flags
& CBQCLF_ROOTCLASS
) == 0) {
244 log(LOG_ERR
, "%s: %s no parent class!\n",
245 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
));
249 if ((borrow
!= parent
) && (borrow
!= NULL
)) {
250 log(LOG_ERR
, "%s: %s borrow class != parent\n",
251 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
));
258 switch (flags
& CBQCLF_CLASSMASK
) {
259 case CBQCLF_ROOTCLASS
:
260 if (parent
!= NULL
) {
261 log(LOG_ERR
, "%s: %s parent exists\n",
262 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
));
265 if (cbqp
->ifnp
.root_
) {
266 log(LOG_ERR
, "%s: %s root class exists\n",
267 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
));
271 case CBQCLF_DEFCLASS
:
272 if (cbqp
->ifnp
.default_
) {
273 log(LOG_ERR
, "%s: %s default class exists\n",
274 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
));
281 /* more than two flags bits set */
282 log(LOG_ERR
, "%s: %s invalid class flags 0x%x\n",
283 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
),
284 (flags
& CBQCLF_CLASSMASK
));
289 * create a class. if this is a root class, initialize the
292 if ((flags
& CBQCLF_CLASSMASK
) == CBQCLF_ROOTCLASS
) {
293 error
= rmc_init(cbqp
->ifnp
.ifq_
, &cbqp
->ifnp
, ns_per_byte
,
294 cbqrestart
, qid
, qlimit
, RM_MAXQUEUED
, maxidle
, minidle
,
298 cl
= cbqp
->ifnp
.root_
;
300 cl
= rmc_newclass(priority
, &cbqp
->ifnp
, ns_per_byte
,
301 rmc_delay_action
, qid
, qlimit
, parent
, borrow
, maxidle
,
302 minidle
, offtime
, pktsize
, flags
);
307 /* return handle to user space. */
308 cl
->stats_
.handle
= qid
;
309 cl
->stats_
.depth
= cl
->depth_
;
311 /* save the allocated class */
312 cbqp
->cbq_class_tbl
[i
] = cl
;
314 if ((flags
& CBQCLF_CLASSMASK
) == CBQCLF_DEFCLASS
)
315 cbqp
->ifnp
.default_
= cl
;
320 if (pktsched_verbose
) {
321 log(LOG_DEBUG
, "%s: %s created qid=%d pri=%d qlimit=%d "
322 "flags=%b\n", if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
),
323 qid
, priority
, qlimit
, flags
, CBQCLF_BITS
);
330 cbq_remove_queue(cbq_state_t
*cbqp
, u_int32_t qid
)
335 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
337 if ((cl
= cbq_clh_to_clp(cbqp
, qid
)) == NULL
)
340 /* if we are a parent class, then return an error. */
341 if (RMC_IS_A_PARENT_CLASS(cl
))
344 /* delete the class */
345 rmc_delete_class(&cbqp
->ifnp
, cl
);
348 * free the class handle
350 for (i
= 0; i
< CBQ_MAX_CLASSES
; i
++) {
351 if (cbqp
->cbq_class_tbl
[i
] == cl
) {
352 cbqp
->cbq_class_tbl
[i
] = NULL
;
353 if (cl
== cbqp
->ifnp
.root_
)
354 cbqp
->ifnp
.root_
= NULL
;
355 if (cl
== cbqp
->ifnp
.default_
)
356 cbqp
->ifnp
.default_
= NULL
;
365 * cbq_class_destroy(cbq_mod_state_t *, struct rm_class *) - This
366 * function destroys a given traffic class. Before destroying
367 * the class, all traffic for that class is released.
370 cbq_class_destroy(cbq_state_t
*cbqp
, struct rm_class
*cl
)
374 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
376 if (pktsched_verbose
) {
377 log(LOG_DEBUG
, "%s: %s destroyed qid=%d pri=%d\n",
378 if_name(CBQS_IFP(cbqp
)), cbq_style(cbqp
),
379 cl
->stats_
.handle
, cl
->pri_
);
382 /* delete the class */
383 rmc_delete_class(&cbqp
->ifnp
, cl
);
386 * free the class handle
388 for (i
= 0; i
< CBQ_MAX_CLASSES
; i
++)
389 if (cbqp
->cbq_class_tbl
[i
] == cl
)
390 cbqp
->cbq_class_tbl
[i
] = NULL
;
392 if (cl
== cbqp
->ifnp
.root_
)
393 cbqp
->ifnp
.root_
= NULL
;
394 if (cl
== cbqp
->ifnp
.default_
)
395 cbqp
->ifnp
.default_
= NULL
;
400 /* convert class handle to class pointer */
401 static struct rm_class
*
402 cbq_clh_to_clp(cbq_state_t
*cbqp
, u_int32_t chandle
)
407 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
410 * first, try optimistically the slot matching the lower bits of
411 * the handle. if it fails, do the linear table search.
413 i
= chandle
% CBQ_MAX_CLASSES
;
414 if ((cl
= cbqp
->cbq_class_tbl
[i
]) != NULL
&&
415 cl
->stats_
.handle
== chandle
)
417 for (i
= 0; i
< CBQ_MAX_CLASSES
; i
++)
418 if ((cl
= cbqp
->cbq_class_tbl
[i
]) != NULL
&&
419 cl
->stats_
.handle
== chandle
)
425 cbq_style(cbq_state_t
*cbqp
)
427 return ((cbqp
->cbq_flags
& CBQSF_ALTQ
) ? "ALTQ_CBQ" : "CBQ");
431 cbq_clear_interface(cbq_state_t
*cbqp
)
436 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
438 /* clear out the classes now */
441 for (i
= 0; i
< CBQ_MAX_CLASSES
; i
++) {
442 if ((cl
= cbqp
->cbq_class_tbl
[i
]) != NULL
) {
443 if (RMC_IS_A_PARENT_CLASS(cl
))
446 cbq_class_destroy(cbqp
, cl
);
447 cbqp
->cbq_class_tbl
[i
] = NULL
;
448 if (cl
== cbqp
->ifnp
.root_
)
449 cbqp
->ifnp
.root_
= NULL
;
450 if (cl
== cbqp
->ifnp
.default_
)
451 cbqp
->ifnp
.default_
= NULL
;
460 /* copy the stats info in rm_class to class_states_t */
462 cbq_get_class_stats(cbq_state_t
*cbqp
, u_int32_t qid
, class_stats_t
*statsp
)
466 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
468 if ((cl
= cbq_clh_to_clp(cbqp
, qid
)) == NULL
)
471 statsp
->xmit_cnt
= cl
->stats_
.xmit_cnt
;
472 statsp
->drop_cnt
= cl
->stats_
.drop_cnt
;
473 statsp
->over
= cl
->stats_
.over
;
474 statsp
->borrows
= cl
->stats_
.borrows
;
475 statsp
->overactions
= cl
->stats_
.overactions
;
476 statsp
->delays
= cl
->stats_
.delays
;
478 statsp
->depth
= cl
->depth_
;
479 statsp
->priority
= cl
->pri_
;
480 statsp
->maxidle
= cl
->maxidle_
;
481 statsp
->minidle
= cl
->minidle_
;
482 statsp
->offtime
= cl
->offtime_
;
483 statsp
->qmax
= qlimit(&cl
->q_
);
484 statsp
->ns_per_byte
= cl
->ns_per_byte_
;
485 statsp
->wrr_allot
= cl
->w_allotment_
;
486 statsp
->qcnt
= qlen(&cl
->q_
);
487 statsp
->avgidle
= cl
->avgidle_
;
489 statsp
->qtype
= qtype(&cl
->q_
);
490 statsp
->qstate
= qstate(&cl
->q_
);
492 if (q_is_red(&cl
->q_
))
493 red_getstats(cl
->red_
, &statsp
->red
[0]);
494 #endif /* CLASSQ_RED */
496 if (q_is_rio(&cl
->q_
))
497 rio_getstats(cl
->rio_
, &statsp
->red
[0]);
498 #endif /* CLASSQ_RIO */
500 if (q_is_blue(&cl
->q_
))
501 blue_getstats(cl
->blue_
, &statsp
->blue
);
502 #endif /* CLASSQ_BLUE */
503 if (q_is_sfb(&cl
->q_
) && cl
->sfb_
!= NULL
)
504 sfb_getstats(cl
->sfb_
, &statsp
->sfb
);
510 cbq_enqueue(cbq_state_t
*cbqp
, struct rm_class
*cl
, struct mbuf
*m
,
513 struct ifclassq
*ifq
= cbqp
->ifnp
.ifq_
;
516 IFCQ_LOCK_ASSERT_HELD(ifq
);
518 /* grab class set by classifier */
519 if (!(m
->m_flags
& M_PKTHDR
)) {
520 /* should not happen */
521 log(LOG_ERR
, "%s: packet for %s does not have pkthdr\n",
522 if_name(ifq
->ifcq_ifp
));
523 IFCQ_CONVERT_LOCK(ifq
);
530 cl
= cbq_clh_to_clp(cbqp
, t
->pftag_qid
);
532 cl
= cbq_clh_to_clp(cbqp
, 0);
533 #endif /* !PF_ALTQ */
535 cl
= cbqp
->ifnp
.default_
;
537 IFCQ_CONVERT_LOCK(ifq
);
546 ret
= rmc_queue_packet(cl
, m
, t
);
548 if (ret
== CLASSQEQ_SUCCESS_FC
) {
549 /* packet enqueued, return advisory feedback */
552 VERIFY(ret
== CLASSQEQ_DROPPED
||
553 ret
== CLASSQEQ_DROPPED_FC
||
554 ret
== CLASSQEQ_DROPPED_SP
);
555 /* packet has been freed in rmc_queue_packet */
556 PKTCNTR_ADD(&cl
->stats_
.drop_cnt
, 1, len
);
557 IFCQ_DROP_ADD(ifq
, 1, len
);
559 case CLASSQEQ_DROPPED
:
561 case CLASSQEQ_DROPPED_FC
:
563 case CLASSQEQ_DROPPED_SP
:
564 return (EQSUSPENDED
);
570 /* successfully queued. */
573 IFCQ_INC_BYTES(ifq
, len
);
579 cbq_dequeue(cbq_state_t
*cbqp
, cqdq_op_t op
)
581 struct ifclassq
*ifq
= cbqp
->ifnp
.ifq_
;
584 IFCQ_LOCK_ASSERT_HELD(ifq
);
586 m
= rmc_dequeue_next(&cbqp
->ifnp
, op
);
588 if (m
&& op
== CLASSQDQ_REMOVE
) {
589 --cbqp
->cbq_qlen
; /* decrement # of packets in cbq */
591 IFCQ_DEC_BYTES(ifq
, m_pktlen(m
));
592 IFCQ_XMIT_ADD(ifq
, 1, m_pktlen(m
));
594 /* Update the class. */
595 rmc_update_class_util(&cbqp
->ifnp
);
602 * cbqrestart(queue_t *) - Restart sending of data.
603 * called from rmc_restart via timeout after waking up
609 cbqrestart(struct ifclassq
*ifq
)
614 qlen
= IFCQ_LEN(ifq
);
618 ifnet_start(ifq
->ifcq_ifp
);
622 cbq_purge(cbq_state_t
*cbqp
)
627 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
629 for (i
= 0; i
< CBQ_MAX_CLASSES
; i
++) {
630 if ((cl
= cbqp
->cbq_class_tbl
[i
]) != NULL
) {
631 if (!qempty(&cl
->q_
) && pktsched_verbose
) {
632 log(LOG_DEBUG
, "%s: %s purge qid=%d pri=%d "
633 "qlen=%d\n", if_name(CBQS_IFP(cbqp
)),
634 cbq_style(cbqp
), cl
->stats_
.handle
,
635 cl
->pri_
, qlen(&cl
->q_
));
643 cbq_event(cbq_state_t
*cbqp
, cqev_t ev
)
648 IFCQ_LOCK_ASSERT_HELD(cbqp
->ifnp
.ifq_
);
650 for (i
= 0; i
< CBQ_MAX_CLASSES
; i
++) {
651 if ((cl
= cbqp
->cbq_class_tbl
[i
]) != NULL
) {
652 if (pktsched_verbose
) {
653 log(LOG_DEBUG
, "%s: %s update qid=%d pri=%d "
654 "event=%s\n", if_name(CBQS_IFP(cbqp
)),
655 cbq_style(cbqp
), cl
->stats_
.handle
,
656 cl
->pri_
, ifclassq_ev2str(ev
));
664 cqb_setup_ifclassq(struct ifclassq
*ifq
, u_int32_t flags
)
666 #pragma unused(ifq, flags)
667 return (ENXIO
); /* not yet */
671 cbq_teardown_ifclassq(struct ifclassq
*ifq
)
673 cbq_state_t
*cbqp
= ifq
->ifcq_disc
;
676 IFCQ_LOCK_ASSERT_HELD(ifq
);
677 VERIFY(cbqp
!= NULL
&& ifq
->ifcq_type
== PKTSCHEDT_CBQ
);
679 (void) cbq_destroy_locked(cbqp
);
681 ifq
->ifcq_disc
= NULL
;
682 for (i
= 0; i
< IFCQ_SC_MAX
; i
++) {
683 ifq
->ifcq_disc_slots
[i
].qid
= 0;
684 ifq
->ifcq_disc_slots
[i
].cl
= NULL
;
687 return (ifclassq_detach(ifq
));
691 cbq_getqstats_ifclassq(struct ifclassq
*ifq
, u_int32_t slot
,
692 struct if_ifclassq_stats
*ifqs
)
694 cbq_state_t
*cbqp
= ifq
->ifcq_disc
;
696 IFCQ_LOCK_ASSERT_HELD(ifq
);
697 VERIFY(ifq
->ifcq_type
== PKTSCHEDT_CBQ
);
699 if (slot
>= IFCQ_SC_MAX
)
702 return (cbq_get_class_stats(cbqp
, ifq
->ifcq_disc_slots
[slot
].qid
,
703 &ifqs
->ifqs_cbq_stats
));
705 #endif /* PKTSCHED_CBQ */