10a70aaf8SLuiz Otavio O Souza /* 20a70aaf8SLuiz Otavio O Souza * CoDel - The Controlled-Delay Active Queue Management algorithm 30a70aaf8SLuiz Otavio O Souza * 4c614d0a4SLuiz Otavio O Souza * Copyright (C) 2013 Ermal Luçi <eri@FreeBSD.org> 50a70aaf8SLuiz Otavio O Souza * Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com> 60a70aaf8SLuiz Otavio O Souza * Copyright (C) 2011-2012 Van Jacobson <van@pollere.net> 70a70aaf8SLuiz Otavio O Souza * Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net> 80a70aaf8SLuiz Otavio O Souza * Copyright (C) 2012 Eric Dumazet <edumazet@google.com> 90a70aaf8SLuiz Otavio O Souza * 100a70aaf8SLuiz Otavio O Souza * Redistribution and use in source and binary forms, with or without 110a70aaf8SLuiz Otavio O Souza * modification, are permitted provided that the following conditions 120a70aaf8SLuiz Otavio O Souza * are met: 130a70aaf8SLuiz Otavio O Souza * 1. Redistributions of source code must retain the above copyright 140a70aaf8SLuiz Otavio O Souza * notice, this list of conditions, and the following disclaimer, 150a70aaf8SLuiz Otavio O Souza * without modification. 160a70aaf8SLuiz Otavio O Souza * 2. Redistributions in binary form must reproduce the above copyright 170a70aaf8SLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer in the 180a70aaf8SLuiz Otavio O Souza * documentation and/or other materials provided with the distribution. 190a70aaf8SLuiz Otavio O Souza * 3. The names of the authors may not be used to endorse or promote products 200a70aaf8SLuiz Otavio O Souza * derived from this software without specific prior written permission. 210a70aaf8SLuiz Otavio O Souza * 220a70aaf8SLuiz Otavio O Souza * Alternatively, provided that this notice is retained in full, this 230a70aaf8SLuiz Otavio O Souza * software may be distributed under the terms of the GNU General 240a70aaf8SLuiz Otavio O Souza * Public License ("GPL") version 2, in which case the provisions of the 250a70aaf8SLuiz Otavio O Souza * GPL apply INSTEAD OF those given above. 260a70aaf8SLuiz Otavio O Souza * 270a70aaf8SLuiz Otavio O Souza * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 280a70aaf8SLuiz Otavio O Souza * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 290a70aaf8SLuiz Otavio O Souza * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 300a70aaf8SLuiz Otavio O Souza * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 310a70aaf8SLuiz Otavio O Souza * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 320a70aaf8SLuiz Otavio O Souza * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 330a70aaf8SLuiz Otavio O Souza * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 340a70aaf8SLuiz Otavio O Souza * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 350a70aaf8SLuiz Otavio O Souza * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 360a70aaf8SLuiz Otavio O Souza * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 370a70aaf8SLuiz Otavio O Souza * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 380a70aaf8SLuiz Otavio O Souza * DAMAGE. 390a70aaf8SLuiz Otavio O Souza * 400a70aaf8SLuiz Otavio O Souza * $FreeBSD$ 410a70aaf8SLuiz Otavio O Souza */ 420a70aaf8SLuiz Otavio O Souza #include "opt_altq.h" 430a70aaf8SLuiz Otavio O Souza #include "opt_inet.h" 440a70aaf8SLuiz Otavio O Souza #include "opt_inet6.h" 450a70aaf8SLuiz Otavio O Souza 460a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL /* CoDel is enabled by ALTQ_CODEL option in opt_altq.h */ 470a70aaf8SLuiz Otavio O Souza 480a70aaf8SLuiz Otavio O Souza #include <sys/param.h> 490a70aaf8SLuiz Otavio O Souza #include <sys/malloc.h> 500a70aaf8SLuiz Otavio O Souza #include <sys/mbuf.h> 510a70aaf8SLuiz Otavio O Souza #include <sys/socket.h> 520a70aaf8SLuiz Otavio O Souza #include <sys/systm.h> 530a70aaf8SLuiz Otavio O Souza 540a70aaf8SLuiz Otavio O Souza #include <net/if.h> 550a70aaf8SLuiz Otavio O Souza #include <net/if_var.h> 562c2b37adSJustin Hibbits #include <net/if_private.h> 570a70aaf8SLuiz Otavio O Souza #include <netinet/in.h> 580a70aaf8SLuiz Otavio O Souza 590a70aaf8SLuiz Otavio O Souza #include <netpfil/pf/pf.h> 600a70aaf8SLuiz Otavio O Souza #include <netpfil/pf/pf_altq.h> 610a70aaf8SLuiz Otavio O Souza #include <net/altq/if_altq.h> 620a70aaf8SLuiz Otavio O Souza #include <net/altq/altq.h> 630a70aaf8SLuiz Otavio O Souza #include <net/altq/altq_codel.h> 640a70aaf8SLuiz Otavio O Souza 650a70aaf8SLuiz Otavio O Souza static int codel_should_drop(struct codel *, class_queue_t *, 660a70aaf8SLuiz Otavio O Souza struct mbuf *, u_int64_t); 670a70aaf8SLuiz Otavio O Souza static void codel_Newton_step(struct codel_vars *); 680a70aaf8SLuiz Otavio O Souza static u_int64_t codel_control_law(u_int64_t t, u_int64_t, u_int32_t); 690a70aaf8SLuiz Otavio O Souza 700a70aaf8SLuiz Otavio O Souza #define codel_time_after(a, b) ((int64_t)(a) - (int64_t)(b) > 0) 710a70aaf8SLuiz Otavio O Souza #define codel_time_after_eq(a, b) ((int64_t)(a) - (int64_t)(b) >= 0) 720a70aaf8SLuiz Otavio O Souza #define codel_time_before(a, b) ((int64_t)(a) - (int64_t)(b) < 0) 730a70aaf8SLuiz Otavio O Souza #define codel_time_before_eq(a, b) ((int64_t)(a) - (int64_t)(b) <= 0) 740a70aaf8SLuiz Otavio O Souza 750a70aaf8SLuiz Otavio O Souza static int codel_request(struct ifaltq *, int, void *); 760a70aaf8SLuiz Otavio O Souza 770a70aaf8SLuiz Otavio O Souza static int codel_enqueue(struct ifaltq *, struct mbuf *, struct altq_pktattr *); 780a70aaf8SLuiz Otavio O Souza static struct mbuf *codel_dequeue(struct ifaltq *, int); 790a70aaf8SLuiz Otavio O Souza 800a70aaf8SLuiz Otavio O Souza int 810a70aaf8SLuiz Otavio O Souza codel_pfattach(struct pf_altq *a) 820a70aaf8SLuiz Otavio O Souza { 830a70aaf8SLuiz Otavio O Souza struct ifnet *ifp; 840a70aaf8SLuiz Otavio O Souza 850a70aaf8SLuiz Otavio O Souza if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL) 860a70aaf8SLuiz Otavio O Souza return (EINVAL); 870a70aaf8SLuiz Otavio O Souza 880a70aaf8SLuiz Otavio O Souza return (altq_attach(&ifp->if_snd, ALTQT_CODEL, a->altq_disc, 8927b2aa49SKristof Provost codel_enqueue, codel_dequeue, codel_request)); 900a70aaf8SLuiz Otavio O Souza } 910a70aaf8SLuiz Otavio O Souza 920a70aaf8SLuiz Otavio O Souza int 938f2ac656SPatrick Kelsey codel_add_altq(struct ifnet *ifp, struct pf_altq *a) 940a70aaf8SLuiz Otavio O Souza { 950a70aaf8SLuiz Otavio O Souza struct codel_if *cif; 960a70aaf8SLuiz Otavio O Souza struct codel_opts *opts; 970a70aaf8SLuiz Otavio O Souza 988f2ac656SPatrick Kelsey if (ifp == NULL) 990a70aaf8SLuiz Otavio O Souza return (EINVAL); 1000a70aaf8SLuiz Otavio O Souza if (!ALTQ_IS_READY(&ifp->if_snd)) 1010a70aaf8SLuiz Otavio O Souza return (ENODEV); 1020a70aaf8SLuiz Otavio O Souza 1030a70aaf8SLuiz Otavio O Souza opts = &a->pq_u.codel_opts; 1040a70aaf8SLuiz Otavio O Souza 1050a70aaf8SLuiz Otavio O Souza cif = malloc(sizeof(struct codel_if), M_DEVBUF, M_NOWAIT | M_ZERO); 1060a70aaf8SLuiz Otavio O Souza if (cif == NULL) 1070a70aaf8SLuiz Otavio O Souza return (ENOMEM); 1080a70aaf8SLuiz Otavio O Souza cif->cif_bandwidth = a->ifbandwidth; 1090a70aaf8SLuiz Otavio O Souza cif->cif_ifq = &ifp->if_snd; 1100a70aaf8SLuiz Otavio O Souza 1110a70aaf8SLuiz Otavio O Souza cif->cl_q = malloc(sizeof(class_queue_t), M_DEVBUF, M_NOWAIT | M_ZERO); 1120a70aaf8SLuiz Otavio O Souza if (cif->cl_q == NULL) { 1130a70aaf8SLuiz Otavio O Souza free(cif, M_DEVBUF); 1140a70aaf8SLuiz Otavio O Souza return (ENOMEM); 1150a70aaf8SLuiz Otavio O Souza } 1160a70aaf8SLuiz Otavio O Souza 1170a70aaf8SLuiz Otavio O Souza if (a->qlimit == 0) 1180a70aaf8SLuiz Otavio O Souza a->qlimit = 50; /* use default. */ 1190a70aaf8SLuiz Otavio O Souza qlimit(cif->cl_q) = a->qlimit; 1200a70aaf8SLuiz Otavio O Souza qtype(cif->cl_q) = Q_CODEL; 1210a70aaf8SLuiz Otavio O Souza qlen(cif->cl_q) = 0; 1220a70aaf8SLuiz Otavio O Souza qsize(cif->cl_q) = 0; 1230a70aaf8SLuiz Otavio O Souza 1240a70aaf8SLuiz Otavio O Souza if (opts->target == 0) 1250a70aaf8SLuiz Otavio O Souza opts->target = 5; 1260a70aaf8SLuiz Otavio O Souza if (opts->interval == 0) 1270a70aaf8SLuiz Otavio O Souza opts->interval = 100; 1280a70aaf8SLuiz Otavio O Souza cif->codel.params.target = machclk_freq * opts->target / 1000; 1290a70aaf8SLuiz Otavio O Souza cif->codel.params.interval = machclk_freq * opts->interval / 1000; 1300a70aaf8SLuiz Otavio O Souza cif->codel.params.ecn = opts->ecn; 1310a70aaf8SLuiz Otavio O Souza cif->codel.stats.maxpacket = 256; 1320a70aaf8SLuiz Otavio O Souza 1330a70aaf8SLuiz Otavio O Souza cif->cl_stats.qlength = qlen(cif->cl_q); 1340a70aaf8SLuiz Otavio O Souza cif->cl_stats.qlimit = qlimit(cif->cl_q); 1350a70aaf8SLuiz Otavio O Souza 1360a70aaf8SLuiz Otavio O Souza /* keep the state in pf_altq */ 1370a70aaf8SLuiz Otavio O Souza a->altq_disc = cif; 1380a70aaf8SLuiz Otavio O Souza 1390a70aaf8SLuiz Otavio O Souza return (0); 1400a70aaf8SLuiz Otavio O Souza } 1410a70aaf8SLuiz Otavio O Souza 1420a70aaf8SLuiz Otavio O Souza int 1430a70aaf8SLuiz Otavio O Souza codel_remove_altq(struct pf_altq *a) 1440a70aaf8SLuiz Otavio O Souza { 1450a70aaf8SLuiz Otavio O Souza struct codel_if *cif; 1460a70aaf8SLuiz Otavio O Souza 1470a70aaf8SLuiz Otavio O Souza if ((cif = a->altq_disc) == NULL) 1480a70aaf8SLuiz Otavio O Souza return (EINVAL); 1490a70aaf8SLuiz Otavio O Souza a->altq_disc = NULL; 1500a70aaf8SLuiz Otavio O Souza 1510a70aaf8SLuiz Otavio O Souza if (cif->cl_q) 1520a70aaf8SLuiz Otavio O Souza free(cif->cl_q, M_DEVBUF); 1530a70aaf8SLuiz Otavio O Souza free(cif, M_DEVBUF); 1540a70aaf8SLuiz Otavio O Souza 1550a70aaf8SLuiz Otavio O Souza return (0); 1560a70aaf8SLuiz Otavio O Souza } 1570a70aaf8SLuiz Otavio O Souza 1580a70aaf8SLuiz Otavio O Souza int 159249cc75fSPatrick Kelsey codel_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version) 1600a70aaf8SLuiz Otavio O Souza { 1610a70aaf8SLuiz Otavio O Souza struct codel_if *cif; 1620a70aaf8SLuiz Otavio O Souza struct codel_ifstats stats; 1630a70aaf8SLuiz Otavio O Souza int error = 0; 1640a70aaf8SLuiz Otavio O Souza 1650a70aaf8SLuiz Otavio O Souza if ((cif = altq_lookup(a->ifname, ALTQT_CODEL)) == NULL) 1660a70aaf8SLuiz Otavio O Souza return (EBADF); 1670a70aaf8SLuiz Otavio O Souza 1680a70aaf8SLuiz Otavio O Souza if (*nbytes < sizeof(stats)) 1690a70aaf8SLuiz Otavio O Souza return (EINVAL); 1700a70aaf8SLuiz Otavio O Souza 1710a70aaf8SLuiz Otavio O Souza stats = cif->cl_stats; 1720a70aaf8SLuiz Otavio O Souza stats.stats = cif->codel.stats; 1730a70aaf8SLuiz Otavio O Souza 1740a70aaf8SLuiz Otavio O Souza if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0) 1750a70aaf8SLuiz Otavio O Souza return (error); 1760a70aaf8SLuiz Otavio O Souza *nbytes = sizeof(stats); 1770a70aaf8SLuiz Otavio O Souza 1780a70aaf8SLuiz Otavio O Souza return (0); 1790a70aaf8SLuiz Otavio O Souza } 1800a70aaf8SLuiz Otavio O Souza 1810a70aaf8SLuiz Otavio O Souza static int 1820a70aaf8SLuiz Otavio O Souza codel_request(struct ifaltq *ifq, int req, void *arg) 1830a70aaf8SLuiz Otavio O Souza { 1840a70aaf8SLuiz Otavio O Souza struct codel_if *cif = (struct codel_if *)ifq->altq_disc; 1850a70aaf8SLuiz Otavio O Souza struct mbuf *m; 1860a70aaf8SLuiz Otavio O Souza 1870a70aaf8SLuiz Otavio O Souza IFQ_LOCK_ASSERT(ifq); 1880a70aaf8SLuiz Otavio O Souza 1890a70aaf8SLuiz Otavio O Souza switch (req) { 1900a70aaf8SLuiz Otavio O Souza case ALTRQ_PURGE: 1910a70aaf8SLuiz Otavio O Souza if (!ALTQ_IS_ENABLED(cif->cif_ifq)) 1920a70aaf8SLuiz Otavio O Souza break; 1930a70aaf8SLuiz Otavio O Souza 1940a70aaf8SLuiz Otavio O Souza if (qempty(cif->cl_q)) 1950a70aaf8SLuiz Otavio O Souza break; 1960a70aaf8SLuiz Otavio O Souza 1970a70aaf8SLuiz Otavio O Souza while ((m = _getq(cif->cl_q)) != NULL) { 1980a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m)); 1990a70aaf8SLuiz Otavio O Souza m_freem(m); 2000a70aaf8SLuiz Otavio O Souza IFQ_DEC_LEN(cif->cif_ifq); 2010a70aaf8SLuiz Otavio O Souza } 2020a70aaf8SLuiz Otavio O Souza cif->cif_ifq->ifq_len = 0; 2030a70aaf8SLuiz Otavio O Souza break; 2040a70aaf8SLuiz Otavio O Souza } 2050a70aaf8SLuiz Otavio O Souza 2060a70aaf8SLuiz Otavio O Souza return (0); 2070a70aaf8SLuiz Otavio O Souza } 2080a70aaf8SLuiz Otavio O Souza 2090a70aaf8SLuiz Otavio O Souza static int 2100a70aaf8SLuiz Otavio O Souza codel_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr) 2110a70aaf8SLuiz Otavio O Souza { 2120a70aaf8SLuiz Otavio O Souza 2130a70aaf8SLuiz Otavio O Souza struct codel_if *cif = (struct codel_if *) ifq->altq_disc; 2140a70aaf8SLuiz Otavio O Souza 2150a70aaf8SLuiz Otavio O Souza IFQ_LOCK_ASSERT(ifq); 2160a70aaf8SLuiz Otavio O Souza 2170a70aaf8SLuiz Otavio O Souza /* grab class set by classifier */ 2180a70aaf8SLuiz Otavio O Souza if ((m->m_flags & M_PKTHDR) == 0) { 2190a70aaf8SLuiz Otavio O Souza /* should not happen */ 2200a70aaf8SLuiz Otavio O Souza printf("altq: packet for %s does not have pkthdr\n", 2210a70aaf8SLuiz Otavio O Souza ifq->altq_ifp->if_xname); 2220a70aaf8SLuiz Otavio O Souza m_freem(m); 2230a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m)); 2240a70aaf8SLuiz Otavio O Souza return (ENOBUFS); 2250a70aaf8SLuiz Otavio O Souza } 2260a70aaf8SLuiz Otavio O Souza 2270a70aaf8SLuiz Otavio O Souza if (codel_addq(&cif->codel, cif->cl_q, m)) { 2280a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m)); 2290a70aaf8SLuiz Otavio O Souza return (ENOBUFS); 2300a70aaf8SLuiz Otavio O Souza } 2310a70aaf8SLuiz Otavio O Souza IFQ_INC_LEN(ifq); 2320a70aaf8SLuiz Otavio O Souza 2330a70aaf8SLuiz Otavio O Souza return (0); 2340a70aaf8SLuiz Otavio O Souza } 2350a70aaf8SLuiz Otavio O Souza 2360a70aaf8SLuiz Otavio O Souza static struct mbuf * 2370a70aaf8SLuiz Otavio O Souza codel_dequeue(struct ifaltq *ifq, int op) 2380a70aaf8SLuiz Otavio O Souza { 2390a70aaf8SLuiz Otavio O Souza struct codel_if *cif = (struct codel_if *)ifq->altq_disc; 2400a70aaf8SLuiz Otavio O Souza struct mbuf *m; 2410a70aaf8SLuiz Otavio O Souza 2420a70aaf8SLuiz Otavio O Souza IFQ_LOCK_ASSERT(ifq); 2430a70aaf8SLuiz Otavio O Souza 2440a70aaf8SLuiz Otavio O Souza if (IFQ_IS_EMPTY(ifq)) 2450a70aaf8SLuiz Otavio O Souza return (NULL); 2460a70aaf8SLuiz Otavio O Souza 2470a70aaf8SLuiz Otavio O Souza if (op == ALTDQ_POLL) 2480a70aaf8SLuiz Otavio O Souza return (qhead(cif->cl_q)); 2490a70aaf8SLuiz Otavio O Souza 2500a70aaf8SLuiz Otavio O Souza m = codel_getq(&cif->codel, cif->cl_q); 2510a70aaf8SLuiz Otavio O Souza if (m != NULL) { 2520a70aaf8SLuiz Otavio O Souza IFQ_DEC_LEN(ifq); 2530a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&cif->cl_stats.cl_xmitcnt, m_pktlen(m)); 2540a70aaf8SLuiz Otavio O Souza return (m); 2550a70aaf8SLuiz Otavio O Souza } 2560a70aaf8SLuiz Otavio O Souza 2570a70aaf8SLuiz Otavio O Souza return (NULL); 2580a70aaf8SLuiz Otavio O Souza } 2590a70aaf8SLuiz Otavio O Souza 2600a70aaf8SLuiz Otavio O Souza struct codel * 2610a70aaf8SLuiz Otavio O Souza codel_alloc(int target, int interval, int ecn) 2620a70aaf8SLuiz Otavio O Souza { 2630a70aaf8SLuiz Otavio O Souza struct codel *c; 2640a70aaf8SLuiz Otavio O Souza 2650a70aaf8SLuiz Otavio O Souza c = malloc(sizeof(*c), M_DEVBUF, M_NOWAIT | M_ZERO); 2660a70aaf8SLuiz Otavio O Souza if (c != NULL) { 2670a70aaf8SLuiz Otavio O Souza c->params.target = machclk_freq * target / 1000; 2680a70aaf8SLuiz Otavio O Souza c->params.interval = machclk_freq * interval / 1000; 2690a70aaf8SLuiz Otavio O Souza c->params.ecn = ecn; 2700a70aaf8SLuiz Otavio O Souza c->stats.maxpacket = 256; 2710a70aaf8SLuiz Otavio O Souza } 2720a70aaf8SLuiz Otavio O Souza 2730a70aaf8SLuiz Otavio O Souza return (c); 2740a70aaf8SLuiz Otavio O Souza } 2750a70aaf8SLuiz Otavio O Souza 2760a70aaf8SLuiz Otavio O Souza void 2770a70aaf8SLuiz Otavio O Souza codel_destroy(struct codel *c) 2780a70aaf8SLuiz Otavio O Souza { 2790a70aaf8SLuiz Otavio O Souza 2800a70aaf8SLuiz Otavio O Souza free(c, M_DEVBUF); 2810a70aaf8SLuiz Otavio O Souza } 2820a70aaf8SLuiz Otavio O Souza 2830a70aaf8SLuiz Otavio O Souza #define MTAG_CODEL 1438031249 2840a70aaf8SLuiz Otavio O Souza int 2850a70aaf8SLuiz Otavio O Souza codel_addq(struct codel *c, class_queue_t *q, struct mbuf *m) 2860a70aaf8SLuiz Otavio O Souza { 2870a70aaf8SLuiz Otavio O Souza struct m_tag *mtag; 2880a70aaf8SLuiz Otavio O Souza uint64_t *enqueue_time; 2890a70aaf8SLuiz Otavio O Souza 2900a70aaf8SLuiz Otavio O Souza if (qlen(q) < qlimit(q)) { 2910a70aaf8SLuiz Otavio O Souza mtag = m_tag_locate(m, MTAG_CODEL, 0, NULL); 292*d0b0424fSKristof Provost if (mtag == NULL) { 2930a70aaf8SLuiz Otavio O Souza mtag = m_tag_alloc(MTAG_CODEL, 0, sizeof(uint64_t), 2940a70aaf8SLuiz Otavio O Souza M_NOWAIT); 295*d0b0424fSKristof Provost if (mtag != NULL) 296*d0b0424fSKristof Provost m_tag_prepend(m, mtag); 297*d0b0424fSKristof Provost } 2980a70aaf8SLuiz Otavio O Souza if (mtag == NULL) { 2990a70aaf8SLuiz Otavio O Souza m_freem(m); 3000a70aaf8SLuiz Otavio O Souza return (-1); 3010a70aaf8SLuiz Otavio O Souza } 3020a70aaf8SLuiz Otavio O Souza enqueue_time = (uint64_t *)(mtag + 1); 3030a70aaf8SLuiz Otavio O Souza *enqueue_time = read_machclk(); 3040a70aaf8SLuiz Otavio O Souza _addq(q, m); 3050a70aaf8SLuiz Otavio O Souza return (0); 3060a70aaf8SLuiz Otavio O Souza } 3070a70aaf8SLuiz Otavio O Souza c->drop_overlimit++; 3080a70aaf8SLuiz Otavio O Souza m_freem(m); 3090a70aaf8SLuiz Otavio O Souza 3100a70aaf8SLuiz Otavio O Souza return (-1); 3110a70aaf8SLuiz Otavio O Souza } 3120a70aaf8SLuiz Otavio O Souza 3130a70aaf8SLuiz Otavio O Souza static int 3140a70aaf8SLuiz Otavio O Souza codel_should_drop(struct codel *c, class_queue_t *q, struct mbuf *m, 3150a70aaf8SLuiz Otavio O Souza u_int64_t now) 3160a70aaf8SLuiz Otavio O Souza { 3170a70aaf8SLuiz Otavio O Souza struct m_tag *mtag; 3180a70aaf8SLuiz Otavio O Souza uint64_t *enqueue_time; 3190a70aaf8SLuiz Otavio O Souza 3200a70aaf8SLuiz Otavio O Souza if (m == NULL) { 3210a70aaf8SLuiz Otavio O Souza c->vars.first_above_time = 0; 3220a70aaf8SLuiz Otavio O Souza return (0); 3230a70aaf8SLuiz Otavio O Souza } 3240a70aaf8SLuiz Otavio O Souza 3250a70aaf8SLuiz Otavio O Souza mtag = m_tag_locate(m, MTAG_CODEL, 0, NULL); 3260a70aaf8SLuiz Otavio O Souza if (mtag == NULL) { 3270a70aaf8SLuiz Otavio O Souza /* Only one warning per second. */ 3280a70aaf8SLuiz Otavio O Souza if (ppsratecheck(&c->last_log, &c->last_pps, 1)) 3290a70aaf8SLuiz Otavio O Souza printf("%s: could not found the packet mtag!\n", 3300a70aaf8SLuiz Otavio O Souza __func__); 3310a70aaf8SLuiz Otavio O Souza c->vars.first_above_time = 0; 3320a70aaf8SLuiz Otavio O Souza return (0); 3330a70aaf8SLuiz Otavio O Souza } 3340a70aaf8SLuiz Otavio O Souza enqueue_time = (uint64_t *)(mtag + 1); 3350a70aaf8SLuiz Otavio O Souza c->vars.ldelay = now - *enqueue_time; 3360a70aaf8SLuiz Otavio O Souza c->stats.maxpacket = MAX(c->stats.maxpacket, m_pktlen(m)); 3370a70aaf8SLuiz Otavio O Souza 3380a70aaf8SLuiz Otavio O Souza if (codel_time_before(c->vars.ldelay, c->params.target) || 3390a70aaf8SLuiz Otavio O Souza qsize(q) <= c->stats.maxpacket) { 3400a70aaf8SLuiz Otavio O Souza /* went below - stay below for at least interval */ 3410a70aaf8SLuiz Otavio O Souza c->vars.first_above_time = 0; 3420a70aaf8SLuiz Otavio O Souza return (0); 3430a70aaf8SLuiz Otavio O Souza } 3440a70aaf8SLuiz Otavio O Souza if (c->vars.first_above_time == 0) { 3450a70aaf8SLuiz Otavio O Souza /* just went above from below. If we stay above 3460a70aaf8SLuiz Otavio O Souza * for at least interval we'll say it's ok to drop 3470a70aaf8SLuiz Otavio O Souza */ 3480a70aaf8SLuiz Otavio O Souza c->vars.first_above_time = now + c->params.interval; 3490a70aaf8SLuiz Otavio O Souza return (0); 3500a70aaf8SLuiz Otavio O Souza } 3510a70aaf8SLuiz Otavio O Souza if (codel_time_after(now, c->vars.first_above_time)) 3520a70aaf8SLuiz Otavio O Souza return (1); 3530a70aaf8SLuiz Otavio O Souza 3540a70aaf8SLuiz Otavio O Souza return (0); 3550a70aaf8SLuiz Otavio O Souza } 3560a70aaf8SLuiz Otavio O Souza 3570a70aaf8SLuiz Otavio O Souza /* 3580a70aaf8SLuiz Otavio O Souza * Run a Newton method step: 3590a70aaf8SLuiz Otavio O Souza * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) 3600a70aaf8SLuiz Otavio O Souza * 3610a70aaf8SLuiz Otavio O Souza * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32 3620a70aaf8SLuiz Otavio O Souza */ 3630a70aaf8SLuiz Otavio O Souza static void 3640a70aaf8SLuiz Otavio O Souza codel_Newton_step(struct codel_vars *vars) 3650a70aaf8SLuiz Otavio O Souza { 3660a70aaf8SLuiz Otavio O Souza uint32_t invsqrt, invsqrt2; 3670a70aaf8SLuiz Otavio O Souza uint64_t val; 3680a70aaf8SLuiz Otavio O Souza 3690a70aaf8SLuiz Otavio O Souza /* sizeof_in_bits(rec_inv_sqrt) */ 3700a70aaf8SLuiz Otavio O Souza #define REC_INV_SQRT_BITS (8 * sizeof(u_int16_t)) 3710a70aaf8SLuiz Otavio O Souza /* needed shift to get a Q0.32 number from rec_inv_sqrt */ 3720a70aaf8SLuiz Otavio O Souza #define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS) 3730a70aaf8SLuiz Otavio O Souza 3740a70aaf8SLuiz Otavio O Souza invsqrt = ((u_int32_t)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT; 3750a70aaf8SLuiz Otavio O Souza invsqrt2 = ((u_int64_t)invsqrt * invsqrt) >> 32; 3760a70aaf8SLuiz Otavio O Souza val = (3LL << 32) - ((u_int64_t)vars->count * invsqrt2); 3770a70aaf8SLuiz Otavio O Souza val >>= 2; /* avoid overflow in following multiply */ 3780a70aaf8SLuiz Otavio O Souza val = (val * invsqrt) >> (32 - 2 + 1); 3790a70aaf8SLuiz Otavio O Souza 3800a70aaf8SLuiz Otavio O Souza vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT; 3810a70aaf8SLuiz Otavio O Souza } 3820a70aaf8SLuiz Otavio O Souza 3830a70aaf8SLuiz Otavio O Souza static u_int64_t 3840a70aaf8SLuiz Otavio O Souza codel_control_law(u_int64_t t, u_int64_t interval, u_int32_t rec_inv_sqrt) 3850a70aaf8SLuiz Otavio O Souza { 3860a70aaf8SLuiz Otavio O Souza 3870a70aaf8SLuiz Otavio O Souza return (t + (u_int32_t)(((u_int64_t)interval * 3880a70aaf8SLuiz Otavio O Souza (rec_inv_sqrt << REC_INV_SQRT_SHIFT)) >> 32)); 3890a70aaf8SLuiz Otavio O Souza } 3900a70aaf8SLuiz Otavio O Souza 3910a70aaf8SLuiz Otavio O Souza struct mbuf * 3920a70aaf8SLuiz Otavio O Souza codel_getq(struct codel *c, class_queue_t *q) 3930a70aaf8SLuiz Otavio O Souza { 3940a70aaf8SLuiz Otavio O Souza struct mbuf *m; 3950a70aaf8SLuiz Otavio O Souza u_int64_t now; 3960a70aaf8SLuiz Otavio O Souza int drop; 3970a70aaf8SLuiz Otavio O Souza 3980a70aaf8SLuiz Otavio O Souza if ((m = _getq(q)) == NULL) { 3990a70aaf8SLuiz Otavio O Souza c->vars.dropping = 0; 4000a70aaf8SLuiz Otavio O Souza return (m); 4010a70aaf8SLuiz Otavio O Souza } 4020a70aaf8SLuiz Otavio O Souza 4030a70aaf8SLuiz Otavio O Souza now = read_machclk(); 4040a70aaf8SLuiz Otavio O Souza drop = codel_should_drop(c, q, m, now); 4050a70aaf8SLuiz Otavio O Souza if (c->vars.dropping) { 4060a70aaf8SLuiz Otavio O Souza if (!drop) { 4070a70aaf8SLuiz Otavio O Souza /* sojourn time below target - leave dropping state */ 4080a70aaf8SLuiz Otavio O Souza c->vars.dropping = 0; 4090a70aaf8SLuiz Otavio O Souza } else if (codel_time_after_eq(now, c->vars.drop_next)) { 4100a70aaf8SLuiz Otavio O Souza /* It's time for the next drop. Drop the current 4110a70aaf8SLuiz Otavio O Souza * packet and dequeue the next. The dequeue might 4120a70aaf8SLuiz Otavio O Souza * take us out of dropping state. 4130a70aaf8SLuiz Otavio O Souza * If not, schedule the next drop. 4140a70aaf8SLuiz Otavio O Souza * A large backlog might result in drop rates so high 4150a70aaf8SLuiz Otavio O Souza * that the next drop should happen now, 4160a70aaf8SLuiz Otavio O Souza * hence the while loop. 4170a70aaf8SLuiz Otavio O Souza */ 4180a70aaf8SLuiz Otavio O Souza while (c->vars.dropping && 4190a70aaf8SLuiz Otavio O Souza codel_time_after_eq(now, c->vars.drop_next)) { 4200a70aaf8SLuiz Otavio O Souza c->vars.count++; /* don't care of possible wrap 4210a70aaf8SLuiz Otavio O Souza * since there is no more 4220a70aaf8SLuiz Otavio O Souza * divide */ 4230a70aaf8SLuiz Otavio O Souza codel_Newton_step(&c->vars); 4240a70aaf8SLuiz Otavio O Souza /* TODO ECN */ 4250a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&c->stats.drop_cnt, m_pktlen(m)); 4260a70aaf8SLuiz Otavio O Souza m_freem(m); 4270a70aaf8SLuiz Otavio O Souza m = _getq(q); 4280a70aaf8SLuiz Otavio O Souza if (!codel_should_drop(c, q, m, now)) 4290a70aaf8SLuiz Otavio O Souza /* leave dropping state */ 4300a70aaf8SLuiz Otavio O Souza c->vars.dropping = 0; 4310a70aaf8SLuiz Otavio O Souza else 4320a70aaf8SLuiz Otavio O Souza /* and schedule the next drop */ 4330a70aaf8SLuiz Otavio O Souza c->vars.drop_next = 4340a70aaf8SLuiz Otavio O Souza codel_control_law(c->vars.drop_next, 4350a70aaf8SLuiz Otavio O Souza c->params.interval, 4360a70aaf8SLuiz Otavio O Souza c->vars.rec_inv_sqrt); 4370a70aaf8SLuiz Otavio O Souza } 4380a70aaf8SLuiz Otavio O Souza } 4390a70aaf8SLuiz Otavio O Souza } else if (drop) { 4400a70aaf8SLuiz Otavio O Souza /* TODO ECN */ 4410a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&c->stats.drop_cnt, m_pktlen(m)); 4420a70aaf8SLuiz Otavio O Souza m_freem(m); 4430a70aaf8SLuiz Otavio O Souza 4440a70aaf8SLuiz Otavio O Souza m = _getq(q); 4450a70aaf8SLuiz Otavio O Souza drop = codel_should_drop(c, q, m, now); 4460a70aaf8SLuiz Otavio O Souza 4470a70aaf8SLuiz Otavio O Souza c->vars.dropping = 1; 4480a70aaf8SLuiz Otavio O Souza /* if min went above target close to when we last went below it 4490a70aaf8SLuiz Otavio O Souza * assume that the drop rate that controlled the queue on the 4500a70aaf8SLuiz Otavio O Souza * last cycle is a good starting point to control it now. 4510a70aaf8SLuiz Otavio O Souza */ 4520a70aaf8SLuiz Otavio O Souza if (codel_time_before(now - c->vars.drop_next, 4530a70aaf8SLuiz Otavio O Souza 16 * c->params.interval)) { 4540a70aaf8SLuiz Otavio O Souza c->vars.count = (c->vars.count - c->vars.lastcount) | 1; 4550a70aaf8SLuiz Otavio O Souza /* we dont care if rec_inv_sqrt approximation 4560a70aaf8SLuiz Otavio O Souza * is not very precise : 4570a70aaf8SLuiz Otavio O Souza * Next Newton steps will correct it quadratically. 4580a70aaf8SLuiz Otavio O Souza */ 4590a70aaf8SLuiz Otavio O Souza codel_Newton_step(&c->vars); 4600a70aaf8SLuiz Otavio O Souza } else { 4610a70aaf8SLuiz Otavio O Souza c->vars.count = 1; 4620a70aaf8SLuiz Otavio O Souza c->vars.rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT; 4630a70aaf8SLuiz Otavio O Souza } 4640a70aaf8SLuiz Otavio O Souza c->vars.lastcount = c->vars.count; 4650a70aaf8SLuiz Otavio O Souza c->vars.drop_next = codel_control_law(now, c->params.interval, 4660a70aaf8SLuiz Otavio O Souza c->vars.rec_inv_sqrt); 4670a70aaf8SLuiz Otavio O Souza } 4680a70aaf8SLuiz Otavio O Souza 4690a70aaf8SLuiz Otavio O Souza return (m); 4700a70aaf8SLuiz Otavio O Souza } 4710a70aaf8SLuiz Otavio O Souza 4720a70aaf8SLuiz Otavio O Souza void 4730a70aaf8SLuiz Otavio O Souza codel_getstats(struct codel *c, struct codel_stats *s) 4740a70aaf8SLuiz Otavio O Souza { 4750a70aaf8SLuiz Otavio O Souza *s = c->stats; 4760a70aaf8SLuiz Otavio O Souza } 4770a70aaf8SLuiz Otavio O Souza 4780a70aaf8SLuiz Otavio O Souza #endif /* ALTQ_CODEL */ 479