2 * Copyright (c) 2011-2019 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 #include <sys/cdefs.h>
31 #include <sys/param.h>
32 #include <sys/malloc.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/errno.h>
37 #include <sys/mcache.h>
38 #include <sys/sysctl.h>
40 #include <dev/random/randomdev.h>
42 #include <net/if_var.h>
43 #include <net/if_dl.h>
44 #include <net/if_types.h>
45 #include <net/net_osdep.h>
46 #include <net/pktsched/pktsched.h>
47 #include <net/pktsched/pktsched_tcq.h>
48 #include <net/pktsched/pktsched_qfq.h>
49 #include <net/pktsched/pktsched_fq_codel.h>
50 #include <net/pktsched/pktsched_netem.h>
52 #include <pexpert/pexpert.h>
55 u_int32_t machclk_freq
= 0;
56 u_int64_t machclk_per_sec
= 0;
57 u_int32_t pktsched_verbose
= 0; /* more noise if greater than 1 */
59 static void init_machclk(void);
61 SYSCTL_NODE(_net
, OID_AUTO
, pktsched
, CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, "pktsched");
63 SYSCTL_UINT(_net_pktsched
, OID_AUTO
, verbose
, CTLFLAG_RW
| CTLFLAG_LOCKED
,
64 &pktsched_verbose
, 0, "Packet scheduler verbosity level");
70 if (machclk_freq
== 0) {
71 panic("%s: no CPU clock available!\n", __func__
);
84 * Initialize machclk_freq using the timerbase frequency
85 * value from device specific info.
87 machclk_freq
= gPEClockFrequencyInfo
.timebase_frequency_hz
;
89 clock_interval_to_absolutetime_interval(1, NSEC_PER_SEC
,
94 pktsched_abs_to_nsecs(u_int64_t abstime
)
98 absolutetime_to_nanoseconds(abstime
, &nsecs
);
103 pktsched_nsecs_to_abstime(u_int64_t nsecs
)
107 nanoseconds_to_absolutetime(nsecs
, &abstime
);
112 pktsched_setup(struct ifclassq
*ifq
, u_int32_t scheduler
, u_int32_t sflags
,
113 classq_pkt_type_t ptype
)
118 IFCQ_LOCK_ASSERT_HELD(ifq
);
120 VERIFY(machclk_freq
!= 0);
122 /* Nothing to do unless the scheduler type changes */
123 if (ifq
->ifcq_type
== scheduler
) {
128 * Remember the flags that need to be restored upon success, as
129 * they may be cleared when we tear down existing scheduler.
131 rflags
= (ifq
->ifcq_flags
& IFCQF_ENABLED
);
133 if (ifq
->ifcq_type
!= PKTSCHEDT_NONE
) {
134 (void) pktsched_teardown(ifq
);
136 /* Teardown should have succeeded */
137 VERIFY(ifq
->ifcq_type
== PKTSCHEDT_NONE
);
138 VERIFY(ifq
->ifcq_disc
== NULL
);
139 VERIFY(ifq
->ifcq_enqueue
== NULL
);
140 VERIFY(ifq
->ifcq_dequeue
== NULL
);
141 VERIFY(ifq
->ifcq_dequeue_sc
== NULL
);
142 VERIFY(ifq
->ifcq_request
== NULL
);
147 error
= tcq_setup_ifclassq(ifq
, sflags
, ptype
);
151 error
= qfq_setup_ifclassq(ifq
, sflags
, ptype
);
153 case PKTSCHEDT_FQ_CODEL
:
154 error
= fq_if_setup_ifclassq(ifq
, sflags
, ptype
);
162 ifq
->ifcq_flags
|= rflags
;
169 pktsched_teardown(struct ifclassq
*ifq
)
173 IFCQ_LOCK_ASSERT_HELD(ifq
);
175 if_qflush(ifq
->ifcq_ifp
, 1);
176 VERIFY(IFCQ_IS_EMPTY(ifq
));
178 ifq
->ifcq_flags
&= ~IFCQF_ENABLED
;
180 switch (ifq
->ifcq_type
) {
185 error
= tcq_teardown_ifclassq(ifq
);
189 error
= qfq_teardown_ifclassq(ifq
);
192 case PKTSCHEDT_FQ_CODEL
:
193 error
= fq_if_teardown_ifclassq(ifq
);
203 pktsched_getqstats(struct ifclassq
*ifq
, u_int32_t qid
,
204 struct if_ifclassq_stats
*ifqs
)
208 IFCQ_LOCK_ASSERT_HELD(ifq
);
210 switch (ifq
->ifcq_type
) {
212 error
= tcq_getqstats_ifclassq(ifq
, qid
, ifqs
);
216 error
= qfq_getqstats_ifclassq(ifq
, qid
, ifqs
);
219 case PKTSCHEDT_FQ_CODEL
:
220 error
= fq_if_getqstats_ifclassq(ifq
, qid
, ifqs
);
231 pktsched_pkt_encap(pktsched_pkt_t
*pkt
, classq_pkt_t
*cpkt
)
233 pkt
->pktsched_pkt
= *cpkt
;
235 switch (cpkt
->cp_ptype
) {
238 (uint32_t)m_pktlen(pkt
->pktsched_pkt_mbuf
);
245 __builtin_unreachable();
250 pktsched_clone_pkt(pktsched_pkt_t
*pkt1
, pktsched_pkt_t
*pkt2
)
252 struct mbuf
*m1
, *m2
;
254 ASSERT(pkt1
!= NULL
);
255 ASSERT(pkt1
->pktsched_pkt_mbuf
!= NULL
);
256 /* allow in place clone, but make sure pkt2->pktsched_pkt won't leak */
257 ASSERT((pkt1
== pkt2
&& pkt1
->pktsched_pkt_mbuf
==
258 pkt2
->pktsched_pkt_mbuf
) || (pkt1
!= pkt2
&&
259 pkt2
->pktsched_pkt_mbuf
== NULL
));
261 switch (pkt1
->pktsched_ptype
) {
263 m1
= (struct mbuf
*)pkt1
->pktsched_pkt_mbuf
;
264 m2
= m_dup(m1
, M_NOWAIT
);
265 if (__improbable(m2
== NULL
)) {
268 pkt2
->pktsched_pkt_mbuf
= m2
;
275 __builtin_unreachable();
278 pkt2
->pktsched_plen
= pkt1
->pktsched_plen
;
279 pkt2
->pktsched_ptype
= pkt1
->pktsched_ptype
;
284 pktsched_corrupt_packet(pktsched_pkt_t
*pkt
)
286 struct mbuf
*m
= NULL
;
287 uint8_t *data
= NULL
;
288 uint32_t data_len
= 0;
289 uint32_t rand32
, rand_off
, rand_bit
;
291 switch (pkt
->pktsched_ptype
) {
293 m
= pkt
->pktsched_pkt_mbuf
;
294 data
= mtod(m
, uint8_t *);
295 data_len
= m
->m_pkthdr
.len
;
301 __builtin_unreachable();
304 read_frandom(&rand32
, sizeof(rand32
));
305 rand_bit
= rand32
& 0x8;
306 rand_off
= (rand32
>> 3) % data_len
;
307 data
[rand_off
] ^= 1 << rand_bit
;
311 pktsched_free_pkt(pktsched_pkt_t
*pkt
)
313 switch (pkt
->pktsched_ptype
) {
315 m_freem(pkt
->pktsched_pkt_mbuf
);
322 __builtin_unreachable();
325 pkt
->pktsched_pkt
= CLASSQ_PKT_INITIALIZER(pkt
->pktsched_pkt
);
326 pkt
->pktsched_plen
= 0;
330 pktsched_get_pkt_svc(pktsched_pkt_t
*pkt
)
332 mbuf_svc_class_t svc
= MBUF_SC_UNSPEC
;
334 switch (pkt
->pktsched_ptype
) {
336 svc
= m_get_service_class(pkt
->pktsched_pkt_mbuf
);
343 __builtin_unreachable();
350 pktsched_get_pkt_vars(pktsched_pkt_t
*pkt
, volatile uint32_t **flags
,
351 uint64_t **timestamp
, uint32_t *flowid
, uint8_t *flowsrc
, uint8_t *proto
,
352 uint32_t *tcp_start_seq
)
354 switch (pkt
->pktsched_ptype
) {
356 struct pkthdr
*pkth
= &(pkt
->pktsched_pkt_mbuf
->m_pkthdr
);
359 *flags
= &pkth
->pkt_flags
;
361 if (timestamp
!= NULL
) {
362 *timestamp
= &pkth
->pkt_timestamp
;
364 if (flowid
!= NULL
) {
365 *flowid
= pkth
->pkt_flowid
;
367 if (flowsrc
!= NULL
) {
368 *flowsrc
= pkth
->pkt_flowsrc
;
371 *proto
= pkth
->pkt_proto
;
374 * caller should use this value only if PKTF_START_SEQ
375 * is set in the mbuf packet flags
377 if (tcp_start_seq
!= NULL
) {
378 *tcp_start_seq
= pkth
->tx_start_seq
;
388 __builtin_unreachable();
392 struct flowadv_fcentry
*
393 pktsched_alloc_fcentry(pktsched_pkt_t
*pkt
, struct ifnet
*ifp
, int how
)
396 struct flowadv_fcentry
*fce
= NULL
;
398 switch (pkt
->pktsched_ptype
) {
400 struct mbuf
*m
= pkt
->pktsched_pkt_mbuf
;
402 fce
= flowadv_alloc_entry(how
);
407 _CASSERT(sizeof(m
->m_pkthdr
.pkt_flowid
) ==
408 sizeof(fce
->fce_flowid
));
410 fce
->fce_flowsrc_type
= m
->m_pkthdr
.pkt_flowsrc
;
411 fce
->fce_flowid
= m
->m_pkthdr
.pkt_flowid
;
419 __builtin_unreachable();
426 pktsched_get_pkt_sfb_vars(pktsched_pkt_t
*pkt
, uint32_t **sfb_flags
)
428 uint32_t *hashp
= NULL
;
430 switch (pkt
->pktsched_ptype
) {
432 struct pkthdr
*pkth
= &(pkt
->pktsched_pkt_mbuf
->m_pkthdr
);
434 _CASSERT(sizeof(pkth
->pkt_mpriv_hash
) == sizeof(uint32_t));
435 _CASSERT(sizeof(pkth
->pkt_mpriv_flags
) == sizeof(uint32_t));
436 *sfb_flags
= &pkth
->pkt_mpriv_flags
;
437 hashp
= &pkth
->pkt_mpriv_hash
;
445 __builtin_unreachable();