]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/classq/classq.c
xnu-6153.61.1.tar.gz
[apple/xnu.git] / bsd / net / classq / classq.c
CommitLineData
316670eb 1/*
cb323159 2 * Copyright (c) 2007-2019 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/*
30 * Copyright (c) 1991-1997 Regents of the University of California.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 * notice, this list of conditions and the following disclaimer in the
40 * documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 * must display the following acknowledgement:
43 * This product includes software developed by the Network Research
44 * Group at Lawrence Berkeley Laboratory.
45 * 4. Neither the name of the University nor of the Laboratory may be used
46 * to endorse or promote products derived from this software without
47 * specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 */
61
62#include <sys/cdefs.h>
63#include <sys/param.h>
64#include <sys/mbuf.h>
65#include <sys/errno.h>
66#include <sys/random.h>
67#include <sys/kernel_types.h>
68#include <sys/sysctl.h>
69
70#include <net/if.h>
71#include <net/net_osdep.h>
72#include <net/classq/classq.h>
73
74#include <libkern/libkern.h>
75
5ba3f43e 76
0a7de745 77u_int32_t classq_verbose = 0; /* more noise if greater than 1 */
316670eb 78
0a7de745 79SYSCTL_NODE(_net, OID_AUTO, classq, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "classq");
316670eb 80
0a7de745
A
81SYSCTL_UINT(_net_classq, OID_AUTO, verbose, CTLFLAG_RW | CTLFLAG_LOCKED,
82 &classq_verbose, 0, "Class queue verbosity level");
316670eb
A
83
84void
5ba3f43e 85_qinit(class_queue_t *q, int type, int lim, classq_pkt_type_t ptype)
316670eb 86{
5ba3f43e
A
87 switch (ptype) {
88 case QP_MBUF:
89 MBUFQ_INIT(&qmbufq(q));
90 break;
91
92
93 default:
94 VERIFY(0);
95 /* NOTREACHED */
96 }
97
316670eb
A
98 qlimit(q) = lim;
99 qlen(q) = 0;
100 qsize(q) = 0;
101 qtype(q) = type;
5ba3f43e 102 qptype(q) = ptype;
316670eb
A
103 qstate(q) = QS_RUNNING;
104}
105
106/* add a packet at the tail of the queue */
107void
cb323159 108_addq(class_queue_t *q, classq_pkt_t *pkt)
316670eb 109{
5ba3f43e
A
110 uint32_t size = 0;
111
cb323159
A
112 ASSERT(pkt->cp_ptype == qptype(q));
113
5ba3f43e
A
114 switch (qptype(q)) {
115 case QP_MBUF: {
cb323159 116 struct mbuf *m = pkt->cp_mbuf;
5ba3f43e
A
117 MBUFQ_ENQUEUE(&qmbufq(q), m);
118 size = m_length(m);
119 break;
120 }
121
122
123 default:
124 VERIFY(0);
125 /* NOTREACHED */
cb323159 126 __builtin_unreachable();
5ba3f43e
A
127 }
128
316670eb
A
129 qlen(q)++;
130 VERIFY(qlen(q) != 0);
5ba3f43e 131 qsize(q) += size;
316670eb
A
132}
133
134/* add one or more packets at the tail of the queue */
135void
cb323159 136_addq_multi(class_queue_t *q, classq_pkt_t *pkt_head, classq_pkt_t *pkt_tail,
316670eb
A
137 u_int32_t cnt, u_int32_t size)
138{
cb323159
A
139 ASSERT(pkt_head->cp_ptype == qptype(q));
140 ASSERT(pkt_tail->cp_ptype == qptype(q));
5ba3f43e
A
141 switch (qptype(q)) {
142 case QP_MBUF: {
cb323159
A
143 struct mbuf *m_head = pkt_head->cp_mbuf;
144 struct mbuf *m_tail = pkt_tail->cp_mbuf;
5ba3f43e
A
145 MBUFQ_ENQUEUE_MULTI(&qmbufq(q), m_head, m_tail);
146 break;
147 }
148
149
150 default:
151 VERIFY(0);
152 /* NOTREACHED */
cb323159 153 __builtin_unreachable();
5ba3f43e
A
154 }
155
316670eb
A
156 qlen(q) += cnt;
157 qsize(q) += size;
158}
159
160/* get a packet at the head of the queue */
cb323159
A
161void
162_getq(class_queue_t *q, classq_pkt_t *pkt)
316670eb 163{
5ba3f43e
A
164 uint32_t pkt_len;
165
166 switch (qptype(q)) {
167 case QP_MBUF: {
cb323159
A
168 MBUFQ_DEQUEUE(&qmbufq(q), pkt->cp_mbuf);
169 if (__probable(pkt->cp_mbuf != NULL)) {
170 CLASSQ_PKT_INIT_MBUF(pkt, pkt->cp_mbuf);
171 pkt_len = m_length(pkt->cp_mbuf);
5ba3f43e
A
172 }
173 break;
174 }
175
176
177 default:
178 VERIFY(0);
179 /* NOTREACHED */
cb323159 180 __builtin_unreachable();
5ba3f43e 181 }
316670eb 182
cb323159 183 if (pkt->cp_mbuf == NULL) {
316670eb 184 VERIFY(qlen(q) == 0);
0a7de745 185 if (qsize(q) > 0) {
316670eb 186 qsize(q) = 0;
0a7de745 187 }
cb323159 188 return;
316670eb
A
189 }
190 VERIFY(qlen(q) > 0);
191 qlen(q)--;
192
193 /* qsize is an approximation, so adjust if necessary */
0a7de745 194 if (((int)qsize(q) - pkt_len) > 0) {
5ba3f43e 195 qsize(q) -= pkt_len;
0a7de745 196 } else if (qsize(q) != 0) {
316670eb 197 qsize(q) = 0;
0a7de745 198 }
316670eb
A
199}
200
cb323159
A
201static void
202_getq_flow_or_scidx(class_queue_t *q, classq_pkt_t *pkt, u_int32_t val,
203 boolean_t isflowid)
316670eb 204{
5ba3f43e
A
205 uint32_t pkt_len;
206
207 switch (qptype(q)) {
208 case QP_MBUF: {
209 struct mbuf *m, *m_tmp;
210
211 MBUFQ_FOREACH_SAFE(m, &qmbufq(q), m_tmp) {
212 if ((isflowid && (val == 0 ||
213 ((m->m_flags & M_PKTHDR) &&
214 m->m_pkthdr.pkt_flowid == val))) ||
215 (!isflowid &&
216 MBUF_SCIDX(mbuf_get_service_class(m)) < val)) {
217 /* remove it from the class queue */
218 MBUFQ_REMOVE(&qmbufq(q), m);
219 MBUFQ_NEXT(m) = NULL;
220 break;
221 }
222 }
cb323159
A
223 if (__probable(m != NULL)) {
224 CLASSQ_PKT_INIT_MBUF(pkt, m);
5ba3f43e 225 pkt_len = m_length(m);
316670eb 226 }
5ba3f43e 227 break;
316670eb
A
228 }
229
316670eb 230
5ba3f43e
A
231 default:
232 VERIFY(0);
233 /* NOTREACHED */
cb323159 234 __builtin_unreachable();
5ba3f43e
A
235 }
236
cb323159 237 if (pkt->cp_mbuf != NULL) {
316670eb
A
238 VERIFY(qlen(q) > 0);
239 qlen(q)--;
240
241 /* qsize is an approximation, so adjust if necessary */
0a7de745 242 if (((int)qsize(q) - pkt_len) > 0) {
5ba3f43e 243 qsize(q) -= pkt_len;
0a7de745 244 } else if (qsize(q) != 0) {
316670eb 245 qsize(q) = 0;
0a7de745 246 }
316670eb 247 }
39037602
A
248}
249
250/* get a packet of a specific flow beginning from the head of the queue */
cb323159
A
251void
252_getq_flow(class_queue_t *q, classq_pkt_t *pkt, u_int32_t flow)
39037602 253{
cb323159 254 return _getq_flow_or_scidx(q, pkt, flow, TRUE);
39037602
A
255}
256
257/* Get a packet whose MBUF_SCIDX() < scidx from head of queue */
cb323159
A
258void
259_getq_scidx_lt(class_queue_t *q, classq_pkt_t *pkt, u_int32_t scidx)
39037602 260{
cb323159 261 return _getq_flow_or_scidx(q, pkt, scidx, FALSE);
316670eb
A
262}
263
5ba3f43e 264/* get all packets (chained) starting from the head of the queue */
cb323159
A
265void
266_getq_all(class_queue_t *q, classq_pkt_t *first, classq_pkt_t *last,
267 u_int32_t *qlenp, u_int64_t *qsizep)
316670eb 268{
5ba3f43e
A
269 switch (qptype(q)) {
270 case QP_MBUF:
cb323159
A
271 first->cp_mbuf = MBUFQ_FIRST(&qmbufq(q));
272 if (__probable(first->cp_mbuf != NULL)) {
273 CLASSQ_PKT_INIT_MBUF(first, first->cp_mbuf);
274 }
0a7de745 275 if (last != NULL) {
cb323159
A
276 last->cp_mbuf = MBUFQ_LAST(&qmbufq(q));
277 if (__probable(last->cp_mbuf != NULL)) {
278 CLASSQ_PKT_INIT_MBUF(last, last->cp_mbuf);
279 }
0a7de745 280 }
5ba3f43e
A
281 MBUFQ_INIT(&qmbufq(q));
282 break;
316670eb 283
5ba3f43e
A
284
285 default:
286 VERIFY(0);
287 /* NOTREACHED */
cb323159 288 __builtin_unreachable();
5ba3f43e 289 }
39037602 290
0a7de745 291 if (qlenp != NULL) {
39037602 292 *qlenp = qlen(q);
0a7de745
A
293 }
294 if (qsizep != NULL) {
39037602 295 *qsizep = qsize(q);
0a7de745 296 }
39037602 297
316670eb
A
298 qlen(q) = 0;
299 qsize(q) = 0;
316670eb
A
300}
301
5ba3f43e
A
302static inline struct mbuf *
303_getq_tail_mbuf(class_queue_t *q)
316670eb 304{
5ba3f43e 305 struct mq_head *head = &qmbufq(q);
316670eb
A
306 struct mbuf *m = MBUFQ_LAST(head);
307
308 if (m != NULL) {
309 struct mbuf *n = MBUFQ_FIRST(head);
310
311 while (n != NULL) {
312 struct mbuf *next = MBUFQ_NEXT(n);
313 if (next == m) {
314 MBUFQ_NEXT(n) = NULL;
315 break;
316 }
317 n = next;
318 }
319 VERIFY(n != NULL ||
320 (qlen(q) == 1 && m == MBUFQ_FIRST(head)));
321 VERIFY(qlen(q) > 0);
322 --qlen(q);
323
324 /* qsize is an approximation, so adjust if necessary */
0a7de745 325 if (((int)qsize(q) - m_length(m)) > 0) {
316670eb 326 qsize(q) -= m_length(m);
0a7de745 327 } else if (qsize(q) != 0) {
316670eb 328 qsize(q) = 0;
0a7de745 329 }
316670eb
A
330
331 if (qempty(q)) {
39037602 332 VERIFY(m == MBUFQ_FIRST(head));
316670eb
A
333 MBUFQ_INIT(head);
334 } else {
335 VERIFY(n != NULL);
336 head->mq_last = &MBUFQ_NEXT(n);
337 }
338 }
0a7de745 339 return m;
316670eb
A
340}
341
5ba3f43e 342/* drop a packet at the tail of the queue */
cb323159
A
343void
344_getq_tail(class_queue_t *q, classq_pkt_t *pkt)
5ba3f43e 345{
5ba3f43e
A
346 switch (qptype(q)) {
347 case QP_MBUF:
cb323159
A
348 pkt->cp_mbuf = _getq_tail_mbuf(q);
349 if (__probable(pkt->cp_mbuf != NULL)) {
350 CLASSQ_PKT_INIT_MBUF(pkt, pkt->cp_mbuf);
351 }
5ba3f43e
A
352 break;
353
354 default:
355 VERIFY(0);
356 /* NOTREACHED */
cb323159 357 __builtin_unreachable();
5ba3f43e 358 }
5ba3f43e
A
359}
360
361static inline struct mbuf *
362_getq_random_mbuf(class_queue_t *q)
316670eb 363{
5ba3f43e 364 struct mq_head *head = &qmbufq(q);
316670eb
A
365 struct mbuf *m = NULL;
366 unsigned int n;
367 u_int32_t rnd;
368
5ba3f43e
A
369 /* XXX: Add support for Kernel packet when needed */
370 VERIFY((qptype(q) == QP_MBUF));
371
316670eb
A
372 n = qlen(q);
373 if (n == 0) {
374 VERIFY(MBUFQ_EMPTY(head));
0a7de745 375 if (qsize(q) > 0) {
316670eb 376 qsize(q) = 0;
0a7de745
A
377 }
378 return NULL;
316670eb
A
379 }
380
381 m = MBUFQ_FIRST(head);
0a7de745 382 read_frandom(&rnd, sizeof(rnd));
316670eb
A
383 n = (rnd % n) + 1;
384
385 if (n == 1) {
0a7de745 386 if ((MBUFQ_FIRST(head) = MBUFQ_NEXT(m)) == NULL) {
316670eb 387 (head)->mq_last = &MBUFQ_FIRST(head);
0a7de745 388 }
316670eb
A
389 } else {
390 struct mbuf *p = NULL;
391
392 VERIFY(n > 1);
393 while (n--) {
0a7de745 394 if (MBUFQ_NEXT(m) == NULL) {
316670eb 395 break;
0a7de745 396 }
316670eb
A
397 p = m;
398 m = MBUFQ_NEXT(m);
399 }
400 VERIFY(p != NULL && MBUFQ_NEXT(p) == m);
401
0a7de745 402 if ((MBUFQ_NEXT(p) = MBUFQ_NEXT(m)) == NULL) {
316670eb 403 (head)->mq_last = &MBUFQ_NEXT(p);
0a7de745 404 }
316670eb
A
405 }
406
407 VERIFY(qlen(q) > 0);
408 --qlen(q);
409
410 /* qsize is an approximation, so adjust if necessary */
0a7de745 411 if (((int)qsize(q) - m_length(m)) > 0) {
316670eb 412 qsize(q) -= m_length(m);
0a7de745 413 } else if (qsize(q) != 0) {
316670eb 414 qsize(q) = 0;
0a7de745 415 }
316670eb
A
416
417 MBUFQ_NEXT(m) = NULL;
418
0a7de745 419 return m;
316670eb
A
420}
421
5ba3f43e 422/* randomly select a packet in the queue */
cb323159
A
423void
424_getq_random(class_queue_t *q, classq_pkt_t *pkt)
5ba3f43e 425{
5ba3f43e
A
426 switch (qptype(q)) {
427 case QP_MBUF:
cb323159
A
428 pkt->cp_mbuf = _getq_random_mbuf(q);
429 if (__probable(pkt->cp_mbuf != NULL)) {
430 CLASSQ_PKT_INIT_MBUF(pkt, pkt->cp_mbuf);
431 }
5ba3f43e
A
432 break;
433
434 default:
435 VERIFY(0);
436 /* NOTREACHED */
cb323159 437 __builtin_unreachable();
5ba3f43e 438 }
5ba3f43e
A
439}
440
441static inline void
442_removeq_mbuf(class_queue_t *q, struct mbuf *m)
316670eb 443{
5ba3f43e 444 struct mq_head *head = &qmbufq(q);
316670eb
A
445 struct mbuf *m0, **mtail;
446
447 m0 = MBUFQ_FIRST(head);
0a7de745 448 if (m0 == NULL) {
316670eb 449 return;
0a7de745 450 }
316670eb
A
451
452 if (m0 != m) {
cb323159 453 while (m0 != NULL && MBUFQ_NEXT(m0) != m) {
316670eb
A
454 m0 = MBUFQ_NEXT(m0);
455 }
cb323159
A
456 if (m0 == NULL) {
457 return;
458 }
459
316670eb
A
460 mtail = &MBUFQ_NEXT(m0);
461 } else {
462 mtail = &MBUFQ_FIRST(head);
463 }
464
465 *mtail = MBUFQ_NEXT(m);
0a7de745 466 if (*mtail == NULL) {
316670eb 467 head->mq_last = mtail;
0a7de745 468 }
316670eb
A
469
470 VERIFY(qlen(q) > 0);
471 --qlen(q);
472
473 /* qsize is an approximation, so adjust if necessary */
0a7de745 474 if (((int)qsize(q) - m_length(m)) > 0) {
316670eb 475 qsize(q) -= m_length(m);
0a7de745 476 } else if (qsize(q) != 0) {
316670eb 477 qsize(q) = 0;
0a7de745 478 }
316670eb
A
479
480 MBUFQ_NEXT(m) = NULL;
481}
482
5ba3f43e
A
483/* remove a packet from the queue */
484void
cb323159 485_removeq(class_queue_t *q, classq_pkt_t *pkt)
5ba3f43e
A
486{
487 switch (qptype(q)) {
488 case QP_MBUF:
cb323159
A
489 ASSERT(pkt->cp_ptype == QP_MBUF);
490 _removeq_mbuf(q, pkt->cp_mbuf);
5ba3f43e
A
491 break;
492
493 default:
494 VERIFY(0);
495 /* NOTREACHED */
cb323159 496 __builtin_unreachable();
5ba3f43e
A
497 }
498}
499
316670eb
A
500void
501_flushq(class_queue_t *q)
502{
503 (void) _flushq_flow(q, 0, NULL, NULL);
504}
505
5ba3f43e
A
506static inline void
507_flushq_flow_mbuf(class_queue_t *q, u_int32_t flow, u_int32_t *cnt,
508 u_int32_t *len)
316670eb
A
509{
510 MBUFQ_HEAD(mq_freeq) freeq;
511 struct mbuf *m, *m_tmp;
512 u_int32_t c = 0, l = 0;
513
514 MBUFQ_INIT(&freeq);
515
5ba3f43e 516 MBUFQ_FOREACH_SAFE(m, &qmbufq(q), m_tmp) {
316670eb 517 if (flow == 0 || ((m->m_flags & M_PKTHDR) &&
39236c6e 518 m->m_pkthdr.pkt_flowid == flow)) {
316670eb 519 /* remove it from the class queue */
5ba3f43e 520 MBUFQ_REMOVE(&qmbufq(q), m);
316670eb
A
521 MBUFQ_NEXT(m) = NULL;
522
523 /* and add it to the free queue */
524 MBUFQ_ENQUEUE(&freeq, m);
525
526 l += m_length(m);
527 c++;
528 }
529 }
530 VERIFY(c == 0 || !MBUFQ_EMPTY(&freeq));
531
532 if (c > 0) {
533 VERIFY(qlen(q) >= c);
534 qlen(q) -= c;
535
536 /* qsize is an approximation, so adjust if necessary */
0a7de745 537 if (((int)qsize(q) - l) > 0) {
316670eb 538 qsize(q) -= l;
0a7de745 539 } else if (qsize(q) != 0) {
316670eb 540 qsize(q) = 0;
0a7de745 541 }
316670eb
A
542 }
543
0a7de745 544 if (!MBUFQ_EMPTY(&freeq)) {
316670eb 545 m_freem_list(MBUFQ_FIRST(&freeq));
0a7de745 546 }
316670eb 547
0a7de745 548 if (cnt != NULL) {
316670eb 549 *cnt = c;
0a7de745
A
550 }
551 if (len != NULL) {
316670eb 552 *len = l;
0a7de745 553 }
316670eb 554}
5ba3f43e 555
cb323159 556
5ba3f43e
A
557void
558_flushq_flow(class_queue_t *q, u_int32_t flow, u_int32_t *cnt, u_int32_t *len)
559{
560 switch (qptype(q)) {
561 case QP_MBUF:
562 _flushq_flow_mbuf(q, flow, cnt, len);
563 break;
564
565 default:
566 VERIFY(0);
567 /* NOTREACHED */
cb323159 568 __builtin_unreachable();
5ba3f43e
A
569 }
570}