]>
Commit | Line | Data |
---|---|---|
316670eb | 1 | /* |
f427ee49 | 2 | * Copyright (c) 2011-2020 Apple Inc. All rights reserved. |
316670eb A |
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 | #include <sys/cdefs.h> | |
30 | ||
31 | #include <sys/param.h> | |
32 | #include <sys/malloc.h> | |
33 | #include <sys/mbuf.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> | |
39 | ||
cb323159 | 40 | #include <dev/random/randomdev.h> |
316670eb A |
41 | #include <net/if.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> | |
39037602 | 47 | #include <net/pktsched/pktsched_fq_codel.h> |
cb323159 | 48 | #include <net/pktsched/pktsched_netem.h> |
316670eb A |
49 | |
50 | #include <pexpert/pexpert.h> | |
51 | ||
5ba3f43e | 52 | |
316670eb A |
53 | u_int32_t machclk_freq = 0; |
54 | u_int64_t machclk_per_sec = 0; | |
cb323159 | 55 | u_int32_t pktsched_verbose = 0; /* more noise if greater than 1 */ |
316670eb A |
56 | |
57 | static void init_machclk(void); | |
58 | ||
0a7de745 | 59 | SYSCTL_NODE(_net, OID_AUTO, pktsched, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "pktsched"); |
316670eb | 60 | |
0a7de745 A |
61 | SYSCTL_UINT(_net_pktsched, OID_AUTO, verbose, CTLFLAG_RW | CTLFLAG_LOCKED, |
62 | &pktsched_verbose, 0, "Packet scheduler verbosity level"); | |
316670eb A |
63 | |
64 | void | |
65 | pktsched_init(void) | |
66 | { | |
67 | init_machclk(); | |
68 | if (machclk_freq == 0) { | |
69 | panic("%s: no CPU clock available!\n", __func__); | |
70 | /* NOTREACHED */ | |
71 | } | |
72 | ||
cb323159 | 73 | netem_init(); |
316670eb A |
74 | } |
75 | ||
76 | static void | |
77 | init_machclk(void) | |
78 | { | |
79 | /* | |
80 | * Initialize machclk_freq using the timerbase frequency | |
81 | * value from device specific info. | |
82 | */ | |
f427ee49 | 83 | machclk_freq = (uint32_t)gPEClockFrequencyInfo.timebase_frequency_hz; |
316670eb A |
84 | |
85 | clock_interval_to_absolutetime_interval(1, NSEC_PER_SEC, | |
86 | &machclk_per_sec); | |
87 | } | |
88 | ||
89 | u_int64_t | |
90 | pktsched_abs_to_nsecs(u_int64_t abstime) | |
91 | { | |
92 | u_int64_t nsecs; | |
93 | ||
94 | absolutetime_to_nanoseconds(abstime, &nsecs); | |
0a7de745 | 95 | return nsecs; |
316670eb A |
96 | } |
97 | ||
98 | u_int64_t | |
99 | pktsched_nsecs_to_abstime(u_int64_t nsecs) | |
100 | { | |
101 | u_int64_t abstime; | |
102 | ||
103 | nanoseconds_to_absolutetime(nsecs, &abstime); | |
0a7de745 | 104 | return abstime; |
316670eb A |
105 | } |
106 | ||
107 | int | |
5ba3f43e A |
108 | pktsched_setup(struct ifclassq *ifq, u_int32_t scheduler, u_int32_t sflags, |
109 | classq_pkt_type_t ptype) | |
316670eb A |
110 | { |
111 | int error = 0; | |
316670eb A |
112 | u_int32_t rflags; |
113 | ||
114 | IFCQ_LOCK_ASSERT_HELD(ifq); | |
115 | ||
116 | VERIFY(machclk_freq != 0); | |
117 | ||
118 | /* Nothing to do unless the scheduler type changes */ | |
0a7de745 A |
119 | if (ifq->ifcq_type == scheduler) { |
120 | return 0; | |
121 | } | |
316670eb | 122 | |
316670eb A |
123 | /* |
124 | * Remember the flags that need to be restored upon success, as | |
125 | * they may be cleared when we tear down existing scheduler. | |
126 | */ | |
127 | rflags = (ifq->ifcq_flags & IFCQF_ENABLED); | |
128 | ||
129 | if (ifq->ifcq_type != PKTSCHEDT_NONE) { | |
f427ee49 | 130 | pktsched_teardown(ifq); |
316670eb A |
131 | |
132 | /* Teardown should have succeeded */ | |
133 | VERIFY(ifq->ifcq_type == PKTSCHEDT_NONE); | |
134 | VERIFY(ifq->ifcq_disc == NULL); | |
316670eb A |
135 | } |
136 | ||
f427ee49 | 137 | error = fq_if_setup_ifclassq(ifq, sflags, ptype); |
0a7de745 | 138 | if (error == 0) { |
316670eb | 139 | ifq->ifcq_flags |= rflags; |
0a7de745 | 140 | } |
316670eb | 141 | |
0a7de745 | 142 | return error; |
316670eb A |
143 | } |
144 | ||
f427ee49 | 145 | void |
316670eb A |
146 | pktsched_teardown(struct ifclassq *ifq) |
147 | { | |
316670eb A |
148 | IFCQ_LOCK_ASSERT_HELD(ifq); |
149 | ||
150 | if_qflush(ifq->ifcq_ifp, 1); | |
151 | VERIFY(IFCQ_IS_EMPTY(ifq)); | |
152 | ||
153 | ifq->ifcq_flags &= ~IFCQF_ENABLED; | |
154 | ||
f427ee49 A |
155 | if (ifq->ifcq_type == PKTSCHEDT_FQ_CODEL) { |
156 | /* Could be PKTSCHEDT_NONE */ | |
157 | fq_if_teardown_ifclassq(ifq); | |
316670eb | 158 | } |
f427ee49 A |
159 | |
160 | return; | |
316670eb A |
161 | } |
162 | ||
163 | int | |
164 | pktsched_getqstats(struct ifclassq *ifq, u_int32_t qid, | |
165 | struct if_ifclassq_stats *ifqs) | |
166 | { | |
f427ee49 | 167 | int error = 0; |
316670eb A |
168 | |
169 | IFCQ_LOCK_ASSERT_HELD(ifq); | |
170 | ||
f427ee49 A |
171 | if (ifq->ifcq_type == PKTSCHEDT_FQ_CODEL) { |
172 | /* Could be PKTSCHEDT_NONE */ | |
39037602 | 173 | error = fq_if_getqstats_ifclassq(ifq, qid, ifqs); |
316670eb A |
174 | } |
175 | ||
0a7de745 | 176 | return error; |
316670eb | 177 | } |
5ba3f43e A |
178 | |
179 | void | |
cb323159 | 180 | pktsched_pkt_encap(pktsched_pkt_t *pkt, classq_pkt_t *cpkt) |
5ba3f43e | 181 | { |
cb323159 | 182 | pkt->pktsched_pkt = *cpkt; |
f427ee49 A |
183 | pkt->pktsched_tail = *cpkt; |
184 | pkt->pktsched_pcnt = 1; | |
5ba3f43e | 185 | |
cb323159 | 186 | switch (cpkt->cp_ptype) { |
5ba3f43e A |
187 | case QP_MBUF: |
188 | pkt->pktsched_plen = | |
cb323159 | 189 | (uint32_t)m_pktlen(pkt->pktsched_pkt_mbuf); |
5ba3f43e A |
190 | break; |
191 | ||
192 | ||
193 | default: | |
194 | VERIFY(0); | |
195 | /* NOTREACHED */ | |
cb323159 | 196 | __builtin_unreachable(); |
5ba3f43e A |
197 | } |
198 | } | |
199 | ||
f427ee49 A |
200 | void |
201 | pktsched_pkt_encap_chain(pktsched_pkt_t *pkt, classq_pkt_t *cpkt, | |
202 | classq_pkt_t *tail, uint32_t cnt, uint32_t bytes) | |
203 | { | |
204 | pkt->pktsched_pkt = *cpkt; | |
205 | pkt->pktsched_tail = *tail; | |
206 | pkt->pktsched_pcnt = cnt; | |
207 | pkt->pktsched_plen = bytes; | |
208 | ||
209 | switch (cpkt->cp_ptype) { | |
210 | case QP_MBUF: | |
211 | break; | |
212 | ||
213 | ||
214 | default: | |
215 | VERIFY(0); | |
216 | /* NOTREACHED */ | |
217 | __builtin_unreachable(); | |
218 | } | |
219 | } | |
220 | ||
cb323159 A |
221 | int |
222 | pktsched_clone_pkt(pktsched_pkt_t *pkt1, pktsched_pkt_t *pkt2) | |
223 | { | |
224 | struct mbuf *m1, *m2; | |
225 | ||
226 | ASSERT(pkt1 != NULL); | |
227 | ASSERT(pkt1->pktsched_pkt_mbuf != NULL); | |
f427ee49 A |
228 | ASSERT(pkt1->pktsched_pcnt == 1); |
229 | ||
cb323159 A |
230 | /* allow in place clone, but make sure pkt2->pktsched_pkt won't leak */ |
231 | ASSERT((pkt1 == pkt2 && pkt1->pktsched_pkt_mbuf == | |
232 | pkt2->pktsched_pkt_mbuf) || (pkt1 != pkt2 && | |
233 | pkt2->pktsched_pkt_mbuf == NULL)); | |
234 | ||
235 | switch (pkt1->pktsched_ptype) { | |
236 | case QP_MBUF: | |
237 | m1 = (struct mbuf *)pkt1->pktsched_pkt_mbuf; | |
238 | m2 = m_dup(m1, M_NOWAIT); | |
239 | if (__improbable(m2 == NULL)) { | |
240 | return ENOBUFS; | |
241 | } | |
242 | pkt2->pktsched_pkt_mbuf = m2; | |
243 | break; | |
244 | ||
245 | ||
246 | default: | |
247 | VERIFY(0); | |
248 | /* NOTREACHED */ | |
249 | __builtin_unreachable(); | |
250 | } | |
251 | ||
252 | pkt2->pktsched_plen = pkt1->pktsched_plen; | |
253 | pkt2->pktsched_ptype = pkt1->pktsched_ptype; | |
f427ee49 A |
254 | pkt2->pktsched_tail = pkt2->pktsched_pkt; |
255 | pkt2->pktsched_pcnt = 1; | |
cb323159 A |
256 | return 0; |
257 | } | |
258 | ||
259 | void | |
260 | pktsched_corrupt_packet(pktsched_pkt_t *pkt) | |
261 | { | |
262 | struct mbuf *m = NULL; | |
263 | uint8_t *data = NULL; | |
264 | uint32_t data_len = 0; | |
265 | uint32_t rand32, rand_off, rand_bit; | |
266 | ||
267 | switch (pkt->pktsched_ptype) { | |
268 | case QP_MBUF: | |
269 | m = pkt->pktsched_pkt_mbuf; | |
270 | data = mtod(m, uint8_t *); | |
271 | data_len = m->m_pkthdr.len; | |
272 | break; | |
273 | ||
274 | default: | |
275 | /* NOTREACHED */ | |
276 | VERIFY(0); | |
277 | __builtin_unreachable(); | |
278 | } | |
279 | ||
280 | read_frandom(&rand32, sizeof(rand32)); | |
281 | rand_bit = rand32 & 0x8; | |
282 | rand_off = (rand32 >> 3) % data_len; | |
283 | data[rand_off] ^= 1 << rand_bit; | |
284 | } | |
285 | ||
5ba3f43e A |
286 | void |
287 | pktsched_free_pkt(pktsched_pkt_t *pkt) | |
288 | { | |
f427ee49 A |
289 | uint32_t cnt = pkt->pktsched_pcnt; |
290 | ASSERT(cnt != 0); | |
291 | ||
5ba3f43e | 292 | switch (pkt->pktsched_ptype) { |
f427ee49 A |
293 | case QP_MBUF: { |
294 | struct mbuf *m; | |
5ba3f43e | 295 | |
f427ee49 A |
296 | m = pkt->pktsched_pkt_mbuf; |
297 | if (cnt == 1) { | |
298 | VERIFY(m->m_nextpkt == NULL); | |
299 | } else { | |
300 | VERIFY(m->m_nextpkt != NULL); | |
301 | } | |
302 | m_freem_list(m); | |
303 | break; | |
304 | } | |
5ba3f43e A |
305 | |
306 | default: | |
307 | VERIFY(0); | |
308 | /* NOTREACHED */ | |
cb323159 | 309 | __builtin_unreachable(); |
5ba3f43e | 310 | } |
cb323159 | 311 | pkt->pktsched_pkt = CLASSQ_PKT_INITIALIZER(pkt->pktsched_pkt); |
f427ee49 | 312 | pkt->pktsched_tail = CLASSQ_PKT_INITIALIZER(pkt->pktsched_tail); |
5ba3f43e | 313 | pkt->pktsched_plen = 0; |
f427ee49 | 314 | pkt->pktsched_pcnt = 0; |
5ba3f43e A |
315 | } |
316 | ||
317 | mbuf_svc_class_t | |
318 | pktsched_get_pkt_svc(pktsched_pkt_t *pkt) | |
319 | { | |
320 | mbuf_svc_class_t svc = MBUF_SC_UNSPEC; | |
321 | ||
322 | switch (pkt->pktsched_ptype) { | |
323 | case QP_MBUF: | |
cb323159 | 324 | svc = m_get_service_class(pkt->pktsched_pkt_mbuf); |
5ba3f43e A |
325 | break; |
326 | ||
327 | ||
328 | default: | |
329 | VERIFY(0); | |
330 | /* NOTREACHED */ | |
cb323159 | 331 | __builtin_unreachable(); |
5ba3f43e A |
332 | } |
333 | ||
0a7de745 | 334 | return svc; |
5ba3f43e A |
335 | } |
336 | ||
337 | void | |
cb323159 | 338 | pktsched_get_pkt_vars(pktsched_pkt_t *pkt, volatile uint32_t **flags, |
5ba3f43e | 339 | uint64_t **timestamp, uint32_t *flowid, uint8_t *flowsrc, uint8_t *proto, |
f427ee49 | 340 | uint32_t *comp_gencnt) |
5ba3f43e A |
341 | { |
342 | switch (pkt->pktsched_ptype) { | |
343 | case QP_MBUF: { | |
cb323159 | 344 | struct pkthdr *pkth = &(pkt->pktsched_pkt_mbuf->m_pkthdr); |
5ba3f43e | 345 | |
0a7de745 | 346 | if (flags != NULL) { |
5ba3f43e | 347 | *flags = &pkth->pkt_flags; |
0a7de745 A |
348 | } |
349 | if (timestamp != NULL) { | |
5ba3f43e | 350 | *timestamp = &pkth->pkt_timestamp; |
0a7de745 A |
351 | } |
352 | if (flowid != NULL) { | |
5ba3f43e | 353 | *flowid = pkth->pkt_flowid; |
0a7de745 A |
354 | } |
355 | if (flowsrc != NULL) { | |
5ba3f43e | 356 | *flowsrc = pkth->pkt_flowsrc; |
0a7de745 A |
357 | } |
358 | if (proto != NULL) { | |
5ba3f43e | 359 | *proto = pkth->pkt_proto; |
0a7de745 | 360 | } |
f427ee49 A |
361 | if (comp_gencnt != NULL) { |
362 | *comp_gencnt = pkth->comp_gencnt; | |
0a7de745 | 363 | } |
5ba3f43e A |
364 | |
365 | break; | |
366 | } | |
367 | ||
368 | ||
369 | default: | |
370 | VERIFY(0); | |
371 | /* NOTREACHED */ | |
cb323159 | 372 | __builtin_unreachable(); |
5ba3f43e A |
373 | } |
374 | } | |
375 | ||
376 | struct flowadv_fcentry * | |
377 | pktsched_alloc_fcentry(pktsched_pkt_t *pkt, struct ifnet *ifp, int how) | |
378 | { | |
379 | #pragma unused(ifp) | |
380 | struct flowadv_fcentry *fce = NULL; | |
381 | ||
382 | switch (pkt->pktsched_ptype) { | |
383 | case QP_MBUF: { | |
cb323159 | 384 | struct mbuf *m = pkt->pktsched_pkt_mbuf; |
5ba3f43e A |
385 | |
386 | fce = flowadv_alloc_entry(how); | |
0a7de745 | 387 | if (fce == NULL) { |
5ba3f43e | 388 | break; |
0a7de745 | 389 | } |
5ba3f43e | 390 | |
0a7de745 A |
391 | _CASSERT(sizeof(m->m_pkthdr.pkt_flowid) == |
392 | sizeof(fce->fce_flowid)); | |
5ba3f43e A |
393 | |
394 | fce->fce_flowsrc_type = m->m_pkthdr.pkt_flowsrc; | |
395 | fce->fce_flowid = m->m_pkthdr.pkt_flowid; | |
396 | break; | |
397 | } | |
398 | ||
399 | ||
400 | default: | |
401 | VERIFY(0); | |
402 | /* NOTREACHED */ | |
cb323159 | 403 | __builtin_unreachable(); |
5ba3f43e A |
404 | } |
405 | ||
0a7de745 | 406 | return fce; |
5ba3f43e A |
407 | } |
408 | ||
409 | uint32_t * | |
410 | pktsched_get_pkt_sfb_vars(pktsched_pkt_t *pkt, uint32_t **sfb_flags) | |
411 | { | |
412 | uint32_t *hashp = NULL; | |
413 | ||
414 | switch (pkt->pktsched_ptype) { | |
415 | case QP_MBUF: { | |
cb323159 | 416 | struct pkthdr *pkth = &(pkt->pktsched_pkt_mbuf->m_pkthdr); |
5ba3f43e | 417 | |
0a7de745 A |
418 | _CASSERT(sizeof(pkth->pkt_mpriv_hash) == sizeof(uint32_t)); |
419 | _CASSERT(sizeof(pkth->pkt_mpriv_flags) == sizeof(uint32_t)); | |
5ba3f43e A |
420 | *sfb_flags = &pkth->pkt_mpriv_flags; |
421 | hashp = &pkth->pkt_mpriv_hash; | |
422 | break; | |
423 | } | |
424 | ||
425 | ||
426 | default: | |
427 | VERIFY(0); | |
428 | /* NOTREACHED */ | |
cb323159 | 429 | __builtin_unreachable(); |
5ba3f43e A |
430 | } |
431 | ||
0a7de745 | 432 | return hashp; |
5ba3f43e | 433 | } |