/* * Copyright (c) 2007-2021 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1991-1997 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Network Research * Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #if SKYWALK #include #endif /* SKYWALK */ u_int32_t classq_verbose = 0; /* more noise if greater than 1 */ SYSCTL_NODE(_net, OID_AUTO, classq, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "classq"); SYSCTL_UINT(_net_classq, OID_AUTO, verbose, CTLFLAG_RW | CTLFLAG_LOCKED, &classq_verbose, 0, "Class queue verbosity level"); void _qinit(class_queue_t *q, int type, int lim, classq_pkt_type_t ptype) { switch (ptype) { case QP_MBUF: MBUFQ_INIT(&qmbufq(q)); break; #if SKYWALK case QP_PACKET: KPKTQ_INIT(&qkpktq(q)); break; #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ } qlimit(q) = lim; qlen(q) = 0; qsize(q) = 0; qtype(q) = type; qptype(q) = ptype; qstate(q) = QS_RUNNING; } /* add a packet at the tail of the queue */ void _addq(class_queue_t *q, classq_pkt_t *pkt) { uint32_t size = 0; ASSERT(pkt->cp_ptype == qptype(q)); switch (qptype(q)) { case QP_MBUF: { struct mbuf *m = pkt->cp_mbuf; MBUFQ_ENQUEUE(&qmbufq(q), m); size = m_length(m); break; } #if SKYWALK case QP_PACKET: { struct __kern_packet *kp = pkt->cp_kpkt; KPKTQ_ENQUEUE(&qkpktq(q), kp); size = kp->pkt_length; break; } #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } qlen(q)++; VERIFY(qlen(q) != 0); qsize(q) += size; } /* add one or more packets at the tail of the queue */ void _addq_multi(class_queue_t *q, classq_pkt_t *pkt_head, classq_pkt_t *pkt_tail, u_int32_t cnt, u_int64_t size) { ASSERT(pkt_head->cp_ptype == qptype(q)); ASSERT(pkt_tail->cp_ptype == qptype(q)); switch (qptype(q)) { case QP_MBUF: { struct mbuf *m_head = pkt_head->cp_mbuf; struct mbuf *m_tail = pkt_tail->cp_mbuf; MBUFQ_ENQUEUE_MULTI(&qmbufq(q), m_head, m_tail); break; } #if SKYWALK case QP_PACKET: { struct __kern_packet *kp_head = pkt_head->cp_kpkt; struct __kern_packet *kp_tail = pkt_tail->cp_kpkt; KPKTQ_ENQUEUE_MULTI(&qkpktq(q), kp_head, kp_tail, cnt); break; } #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } qlen(q) += cnt; qsize(q) += size; } /* get a packet at the head of the queue */ void _getq(class_queue_t *q, classq_pkt_t *pkt) { uint32_t pkt_len = 0; switch (qptype(q)) { case QP_MBUF: { MBUFQ_DEQUEUE(&qmbufq(q), pkt->cp_mbuf); if (__probable(pkt->cp_mbuf != NULL)) { CLASSQ_PKT_INIT_MBUF(pkt, pkt->cp_mbuf); pkt_len = m_length(pkt->cp_mbuf); } break; } #if SKYWALK case QP_PACKET: { KPKTQ_DEQUEUE(&qkpktq(q), pkt->cp_kpkt); if (__probable(pkt->cp_kpkt != NULL)) { CLASSQ_PKT_INIT_PACKET(pkt, pkt->cp_kpkt); pkt_len = pkt->cp_kpkt->pkt_length; } break; } #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } if (pkt->cp_mbuf == NULL) { VERIFY(qlen(q) == 0); if (qsize(q) > 0) { qsize(q) = 0; } return; } VERIFY(qlen(q) > 0); qlen(q)--; /* qsize is an approximation, so adjust if necessary */ if (((int)qsize(q) - pkt_len) > 0) { qsize(q) -= pkt_len; } else if (qsize(q) != 0) { qsize(q) = 0; } } static void _getq_flow_or_scidx(class_queue_t *q, classq_pkt_t *pkt, u_int32_t val, boolean_t isflowid) { uint32_t pkt_len = 0; switch (qptype(q)) { case QP_MBUF: { struct mbuf *m, *m_tmp; MBUFQ_FOREACH_SAFE(m, &qmbufq(q), m_tmp) { if ((isflowid && (val == 0 || ((m->m_flags & M_PKTHDR) && m->m_pkthdr.pkt_flowid == val))) || (!isflowid && MBUF_SCIDX(mbuf_get_service_class(m)) < val)) { /* remove it from the class queue */ MBUFQ_REMOVE(&qmbufq(q), m); MBUFQ_NEXT(m) = NULL; break; } } if (__probable(m != NULL)) { CLASSQ_PKT_INIT_MBUF(pkt, m); pkt_len = m_length(m); } break; } #if SKYWALK case QP_PACKET: { struct __kern_packet *kp, *kp_tmp; KPKTQ_FOREACH_SAFE(kp, &qkpktq(q), kp_tmp) { if ((isflowid && (val == 0 || (kp->pkt_flow_token == val))) || (!isflowid && MBUF_SCIDX(kp->pkt_svc_class) < val)) { /* remove it from the class queue */ KPKTQ_REMOVE(&qkpktq(q), kp); KPKTQ_NEXT(kp) = NULL; break; } } if (__probable(kp != NULL)) { CLASSQ_PKT_INIT_PACKET(pkt, kp); pkt_len = kp->pkt_length; } break; } #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } if (pkt->cp_mbuf != NULL) { VERIFY(qlen(q) > 0); qlen(q)--; /* qsize is an approximation, so adjust if necessary */ if (((int)qsize(q) - pkt_len) > 0) { qsize(q) -= pkt_len; } else if (qsize(q) != 0) { qsize(q) = 0; } } } /* get a packet of a specific flow beginning from the head of the queue */ void _getq_flow(class_queue_t *q, classq_pkt_t *pkt, u_int32_t flow) { return _getq_flow_or_scidx(q, pkt, flow, TRUE); } /* Get a packet whose MBUF_SCIDX() < scidx from head of queue */ void _getq_scidx_lt(class_queue_t *q, classq_pkt_t *pkt, u_int32_t scidx) { return _getq_flow_or_scidx(q, pkt, scidx, FALSE); } /* get all packets (chained) starting from the head of the queue */ void _getq_all(class_queue_t *q, classq_pkt_t *first, classq_pkt_t *last, u_int32_t *qlenp, u_int64_t *qsizep) { switch (qptype(q)) { case QP_MBUF: first->cp_mbuf = MBUFQ_FIRST(&qmbufq(q)); if (__probable(first->cp_mbuf != NULL)) { CLASSQ_PKT_INIT_MBUF(first, first->cp_mbuf); } if (last != NULL) { last->cp_mbuf = MBUFQ_LAST(&qmbufq(q)); if (__probable(last->cp_mbuf != NULL)) { CLASSQ_PKT_INIT_MBUF(last, last->cp_mbuf); } } MBUFQ_INIT(&qmbufq(q)); break; #if SKYWALK case QP_PACKET: first->cp_kpkt = KPKTQ_FIRST(&qkpktq(q)); if (__probable(first->cp_kpkt != NULL)) { CLASSQ_PKT_INIT_PACKET(first, first->cp_kpkt); } if (last != NULL) { last->cp_kpkt = KPKTQ_LAST(&qkpktq(q)); if (__probable(last->cp_kpkt != NULL)) { CLASSQ_PKT_INIT_PACKET(last, last->cp_kpkt); } } KPKTQ_INIT(&qkpktq(q)); break; #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } if (qlenp != NULL) { *qlenp = qlen(q); } if (qsizep != NULL) { *qsizep = qsize(q); } qlen(q) = 0; qsize(q) = 0; } static inline struct mbuf * _getq_tail_mbuf(class_queue_t *q) { struct mq_head *head = &qmbufq(q); struct mbuf *m = MBUFQ_LAST(head); if (m != NULL) { struct mbuf *n = MBUFQ_FIRST(head); while (n != NULL) { struct mbuf *next = MBUFQ_NEXT(n); if (next == m) { MBUFQ_NEXT(n) = NULL; break; } n = next; } VERIFY(n != NULL || (qlen(q) == 1 && m == MBUFQ_FIRST(head))); VERIFY(qlen(q) > 0); --qlen(q); /* qsize is an approximation, so adjust if necessary */ if (((int)qsize(q) - m_length(m)) > 0) { qsize(q) -= m_length(m); } else if (qsize(q) != 0) { qsize(q) = 0; } if (qempty(q)) { VERIFY(m == MBUFQ_FIRST(head)); MBUFQ_INIT(head); } else { VERIFY(n != NULL); head->mq_last = &MBUFQ_NEXT(n); } } return m; } /* drop a packet at the tail of the queue */ void _getq_tail(class_queue_t *q, classq_pkt_t *pkt) { switch (qptype(q)) { case QP_MBUF: pkt->cp_mbuf = _getq_tail_mbuf(q); if (__probable(pkt->cp_mbuf != NULL)) { CLASSQ_PKT_INIT_MBUF(pkt, pkt->cp_mbuf); } break; #if SKYWALK case QP_PACKET: /* XXX: Add support for Kernel packet when needed */ #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } } static inline struct mbuf * _getq_random_mbuf(class_queue_t *q) { struct mq_head *head = &qmbufq(q); struct mbuf *m = NULL; unsigned int n; u_int32_t rnd; /* XXX: Add support for Kernel packet when needed */ VERIFY((qptype(q) == QP_MBUF)); n = qlen(q); if (n == 0) { VERIFY(MBUFQ_EMPTY(head)); if (qsize(q) > 0) { qsize(q) = 0; } return NULL; } m = MBUFQ_FIRST(head); read_frandom(&rnd, sizeof(rnd)); n = (rnd % n) + 1; if (n == 1) { if ((MBUFQ_FIRST(head) = MBUFQ_NEXT(m)) == NULL) { (head)->mq_last = &MBUFQ_FIRST(head); } } else { struct mbuf *p = NULL; VERIFY(n > 1); while (n--) { if (MBUFQ_NEXT(m) == NULL) { break; } p = m; m = MBUFQ_NEXT(m); } VERIFY(p != NULL && MBUFQ_NEXT(p) == m); if ((MBUFQ_NEXT(p) = MBUFQ_NEXT(m)) == NULL) { (head)->mq_last = &MBUFQ_NEXT(p); } } VERIFY(qlen(q) > 0); --qlen(q); /* qsize is an approximation, so adjust if necessary */ if (((int)qsize(q) - m_length(m)) > 0) { qsize(q) -= m_length(m); } else if (qsize(q) != 0) { qsize(q) = 0; } MBUFQ_NEXT(m) = NULL; return m; } /* randomly select a packet in the queue */ void _getq_random(class_queue_t *q, classq_pkt_t *pkt) { switch (qptype(q)) { case QP_MBUF: pkt->cp_mbuf = _getq_random_mbuf(q); if (__probable(pkt->cp_mbuf != NULL)) { CLASSQ_PKT_INIT_MBUF(pkt, pkt->cp_mbuf); } break; #if SKYWALK case QP_PACKET: /* XXX: Add support for Kernel packet when needed */ #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } } static inline void _removeq_mbuf(class_queue_t *q, struct mbuf *m) { struct mq_head *head = &qmbufq(q); struct mbuf *m0, **mtail; m0 = MBUFQ_FIRST(head); if (m0 == NULL) { return; } if (m0 != m) { while (m0 != NULL && MBUFQ_NEXT(m0) != m) { m0 = MBUFQ_NEXT(m0); } if (m0 == NULL) { return; } mtail = &MBUFQ_NEXT(m0); } else { mtail = &MBUFQ_FIRST(head); } *mtail = MBUFQ_NEXT(m); if (*mtail == NULL) { head->mq_last = mtail; } VERIFY(qlen(q) > 0); --qlen(q); /* qsize is an approximation, so adjust if necessary */ if (((int)qsize(q) - m_length(m)) > 0) { qsize(q) -= m_length(m); } else if (qsize(q) != 0) { qsize(q) = 0; } MBUFQ_NEXT(m) = NULL; } /* remove a packet from the queue */ void _removeq(class_queue_t *q, classq_pkt_t *pkt) { switch (qptype(q)) { case QP_MBUF: ASSERT(pkt->cp_ptype == QP_MBUF); _removeq_mbuf(q, pkt->cp_mbuf); break; #if SKYWALK case QP_PACKET: /* XXX: Add support for Kernel packet when needed */ #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } } void _flushq(class_queue_t *q) { (void) _flushq_flow(q, 0, NULL, NULL); } static inline void _flushq_flow_mbuf(class_queue_t *q, u_int32_t flow, u_int32_t *cnt, u_int32_t *len) { MBUFQ_HEAD(mq_freeq) freeq; struct mbuf *m, *m_tmp; u_int32_t c = 0, l = 0; MBUFQ_INIT(&freeq); MBUFQ_FOREACH_SAFE(m, &qmbufq(q), m_tmp) { if (flow == 0 || ((m->m_flags & M_PKTHDR) && m->m_pkthdr.pkt_flowid == flow)) { /* remove it from the class queue */ MBUFQ_REMOVE(&qmbufq(q), m); MBUFQ_NEXT(m) = NULL; /* and add it to the free queue */ MBUFQ_ENQUEUE(&freeq, m); l += m_length(m); c++; } } VERIFY(c == 0 || !MBUFQ_EMPTY(&freeq)); if (c > 0) { VERIFY(qlen(q) >= c); qlen(q) -= c; /* qsize is an approximation, so adjust if necessary */ if (((int)qsize(q) - l) > 0) { qsize(q) -= l; } else if (qsize(q) != 0) { qsize(q) = 0; } } if (!MBUFQ_EMPTY(&freeq)) { m_freem_list(MBUFQ_FIRST(&freeq)); } if (cnt != NULL) { *cnt = c; } if (len != NULL) { *len = l; } } #if SKYWALK static inline void _flushq_flow_kpkt(class_queue_t *q, u_int32_t flow, u_int32_t *cnt, u_int32_t *len) { KPKTQ_HEAD(pq_freeq) freeq; struct __kern_packet *p, *p_tmp; u_int32_t c = 0, l = 0; KPKTQ_INIT(&freeq); KPKTQ_FOREACH_SAFE(p, &qkpktq(q), p_tmp) { if (flow == 0 || p->pkt_flow_token == flow) { /* remove it from the class queue */ KPKTQ_REMOVE(&qkpktq(q), p); KPKTQ_NEXT(p) = NULL; /* and add it to the free queue */ KPKTQ_ENQUEUE(&freeq, p); l += p->pkt_length; c++; } } VERIFY(c == 0 || !KPKTQ_EMPTY(&freeq)); if (c > 0) { VERIFY(qlen(q) >= c); qlen(q) -= c; /* qsize is an approximation, so adjust if necessary */ if (((int)qsize(q) - l) > 0) { qsize(q) -= l; } else if (qsize(q) != 0) { qsize(q) = 0; } } if (!KPKTQ_EMPTY(&freeq)) { pp_free_packet_chain(KPKTQ_FIRST(&freeq), NULL); } if (cnt != NULL) { *cnt = c; } if (len != NULL) { *len = l; } } #endif /* SKYWALK */ void _flushq_flow(class_queue_t *q, u_int32_t flow, u_int32_t *cnt, u_int32_t *len) { switch (qptype(q)) { case QP_MBUF: _flushq_flow_mbuf(q, flow, cnt, len); break; #if SKYWALK case QP_PACKET: _flushq_flow_kpkt(q, flow, cnt, len); break; #endif /* SKYWALK */ default: VERIFY(0); /* NOTREACHED */ __builtin_unreachable(); } }