1*0a70aaf8SLuiz Otavio O Souza /* 2*0a70aaf8SLuiz Otavio O Souza * CoDel - The Controlled-Delay Active Queue Management algorithm 3*0a70aaf8SLuiz Otavio O Souza * 4*0a70aaf8SLuiz Otavio O Souza * Copyright (C) 2013 Ermal Luci <eri@FreeBSD.org> 5*0a70aaf8SLuiz Otavio O Souza * Copyright (C) 2011-2012 Kathleen Nichols <nichols@pollere.com> 6*0a70aaf8SLuiz Otavio O Souza * Copyright (C) 2011-2012 Van Jacobson <van@pollere.net> 7*0a70aaf8SLuiz Otavio O Souza * Copyright (C) 2012 Michael D. Taht <dave.taht@bufferbloat.net> 8*0a70aaf8SLuiz Otavio O Souza * Copyright (C) 2012 Eric Dumazet <edumazet@google.com> 9*0a70aaf8SLuiz Otavio O Souza * 10*0a70aaf8SLuiz Otavio O Souza * Redistribution and use in source and binary forms, with or without 11*0a70aaf8SLuiz Otavio O Souza * modification, are permitted provided that the following conditions 12*0a70aaf8SLuiz Otavio O Souza * are met: 13*0a70aaf8SLuiz Otavio O Souza * 1. Redistributions of source code must retain the above copyright 14*0a70aaf8SLuiz Otavio O Souza * notice, this list of conditions, and the following disclaimer, 15*0a70aaf8SLuiz Otavio O Souza * without modification. 16*0a70aaf8SLuiz Otavio O Souza * 2. Redistributions in binary form must reproduce the above copyright 17*0a70aaf8SLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer in the 18*0a70aaf8SLuiz Otavio O Souza * documentation and/or other materials provided with the distribution. 19*0a70aaf8SLuiz Otavio O Souza * 3. The names of the authors may not be used to endorse or promote products 20*0a70aaf8SLuiz Otavio O Souza * derived from this software without specific prior written permission. 21*0a70aaf8SLuiz Otavio O Souza * 22*0a70aaf8SLuiz Otavio O Souza * Alternatively, provided that this notice is retained in full, this 23*0a70aaf8SLuiz Otavio O Souza * software may be distributed under the terms of the GNU General 24*0a70aaf8SLuiz Otavio O Souza * Public License ("GPL") version 2, in which case the provisions of the 25*0a70aaf8SLuiz Otavio O Souza * GPL apply INSTEAD OF those given above. 26*0a70aaf8SLuiz Otavio O Souza * 27*0a70aaf8SLuiz Otavio O Souza * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28*0a70aaf8SLuiz Otavio O Souza * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 29*0a70aaf8SLuiz Otavio O Souza * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 30*0a70aaf8SLuiz Otavio O Souza * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 31*0a70aaf8SLuiz Otavio O Souza * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 32*0a70aaf8SLuiz Otavio O Souza * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 33*0a70aaf8SLuiz Otavio O Souza * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 34*0a70aaf8SLuiz Otavio O Souza * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 35*0a70aaf8SLuiz Otavio O Souza * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36*0a70aaf8SLuiz Otavio O Souza * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 37*0a70aaf8SLuiz Otavio O Souza * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 38*0a70aaf8SLuiz Otavio O Souza * DAMAGE. 39*0a70aaf8SLuiz Otavio O Souza * 40*0a70aaf8SLuiz Otavio O Souza * $FreeBSD$ 41*0a70aaf8SLuiz Otavio O Souza */ 42*0a70aaf8SLuiz Otavio O Souza #include "opt_altq.h" 43*0a70aaf8SLuiz Otavio O Souza #include "opt_inet.h" 44*0a70aaf8SLuiz Otavio O Souza #include "opt_inet6.h" 45*0a70aaf8SLuiz Otavio O Souza 46*0a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL /* CoDel is enabled by ALTQ_CODEL option in opt_altq.h */ 47*0a70aaf8SLuiz Otavio O Souza 48*0a70aaf8SLuiz Otavio O Souza #include <sys/param.h> 49*0a70aaf8SLuiz Otavio O Souza #include <sys/malloc.h> 50*0a70aaf8SLuiz Otavio O Souza #include <sys/mbuf.h> 51*0a70aaf8SLuiz Otavio O Souza #include <sys/socket.h> 52*0a70aaf8SLuiz Otavio O Souza #include <sys/systm.h> 53*0a70aaf8SLuiz Otavio O Souza 54*0a70aaf8SLuiz Otavio O Souza #include <net/if.h> 55*0a70aaf8SLuiz Otavio O Souza #include <net/if_var.h> 56*0a70aaf8SLuiz Otavio O Souza #include <netinet/in.h> 57*0a70aaf8SLuiz Otavio O Souza 58*0a70aaf8SLuiz Otavio O Souza #include <netpfil/pf/pf.h> 59*0a70aaf8SLuiz Otavio O Souza #include <netpfil/pf/pf_altq.h> 60*0a70aaf8SLuiz Otavio O Souza #include <net/altq/if_altq.h> 61*0a70aaf8SLuiz Otavio O Souza #include <net/altq/altq.h> 62*0a70aaf8SLuiz Otavio O Souza #include <net/altq/altq_codel.h> 63*0a70aaf8SLuiz Otavio O Souza 64*0a70aaf8SLuiz Otavio O Souza static int codel_should_drop(struct codel *, class_queue_t *, 65*0a70aaf8SLuiz Otavio O Souza struct mbuf *, u_int64_t); 66*0a70aaf8SLuiz Otavio O Souza static void codel_Newton_step(struct codel_vars *); 67*0a70aaf8SLuiz Otavio O Souza static u_int64_t codel_control_law(u_int64_t t, u_int64_t, u_int32_t); 68*0a70aaf8SLuiz Otavio O Souza 69*0a70aaf8SLuiz Otavio O Souza #define codel_time_after(a, b) ((int64_t)(a) - (int64_t)(b) > 0) 70*0a70aaf8SLuiz Otavio O Souza #define codel_time_after_eq(a, b) ((int64_t)(a) - (int64_t)(b) >= 0) 71*0a70aaf8SLuiz Otavio O Souza #define codel_time_before(a, b) ((int64_t)(a) - (int64_t)(b) < 0) 72*0a70aaf8SLuiz Otavio O Souza #define codel_time_before_eq(a, b) ((int64_t)(a) - (int64_t)(b) <= 0) 73*0a70aaf8SLuiz Otavio O Souza 74*0a70aaf8SLuiz Otavio O Souza static int codel_request(struct ifaltq *, int, void *); 75*0a70aaf8SLuiz Otavio O Souza 76*0a70aaf8SLuiz Otavio O Souza static int codel_enqueue(struct ifaltq *, struct mbuf *, struct altq_pktattr *); 77*0a70aaf8SLuiz Otavio O Souza static struct mbuf *codel_dequeue(struct ifaltq *, int); 78*0a70aaf8SLuiz Otavio O Souza 79*0a70aaf8SLuiz Otavio O Souza int 80*0a70aaf8SLuiz Otavio O Souza codel_pfattach(struct pf_altq *a) 81*0a70aaf8SLuiz Otavio O Souza { 82*0a70aaf8SLuiz Otavio O Souza struct ifnet *ifp; 83*0a70aaf8SLuiz Otavio O Souza 84*0a70aaf8SLuiz Otavio O Souza if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL) 85*0a70aaf8SLuiz Otavio O Souza return (EINVAL); 86*0a70aaf8SLuiz Otavio O Souza 87*0a70aaf8SLuiz Otavio O Souza return (altq_attach(&ifp->if_snd, ALTQT_CODEL, a->altq_disc, 88*0a70aaf8SLuiz Otavio O Souza codel_enqueue, codel_dequeue, codel_request, NULL, NULL)); 89*0a70aaf8SLuiz Otavio O Souza } 90*0a70aaf8SLuiz Otavio O Souza 91*0a70aaf8SLuiz Otavio O Souza int 92*0a70aaf8SLuiz Otavio O Souza codel_add_altq(struct pf_altq *a) 93*0a70aaf8SLuiz Otavio O Souza { 94*0a70aaf8SLuiz Otavio O Souza struct codel_if *cif; 95*0a70aaf8SLuiz Otavio O Souza struct ifnet *ifp; 96*0a70aaf8SLuiz Otavio O Souza struct codel_opts *opts; 97*0a70aaf8SLuiz Otavio O Souza 98*0a70aaf8SLuiz Otavio O Souza if ((ifp = ifunit(a->ifname)) == NULL) 99*0a70aaf8SLuiz Otavio O Souza return (EINVAL); 100*0a70aaf8SLuiz Otavio O Souza if (!ALTQ_IS_READY(&ifp->if_snd)) 101*0a70aaf8SLuiz Otavio O Souza return (ENODEV); 102*0a70aaf8SLuiz Otavio O Souza 103*0a70aaf8SLuiz Otavio O Souza opts = &a->pq_u.codel_opts; 104*0a70aaf8SLuiz Otavio O Souza 105*0a70aaf8SLuiz Otavio O Souza cif = malloc(sizeof(struct codel_if), M_DEVBUF, M_NOWAIT | M_ZERO); 106*0a70aaf8SLuiz Otavio O Souza if (cif == NULL) 107*0a70aaf8SLuiz Otavio O Souza return (ENOMEM); 108*0a70aaf8SLuiz Otavio O Souza cif->cif_bandwidth = a->ifbandwidth; 109*0a70aaf8SLuiz Otavio O Souza cif->cif_ifq = &ifp->if_snd; 110*0a70aaf8SLuiz Otavio O Souza 111*0a70aaf8SLuiz Otavio O Souza cif->cl_q = malloc(sizeof(class_queue_t), M_DEVBUF, M_NOWAIT | M_ZERO); 112*0a70aaf8SLuiz Otavio O Souza if (cif->cl_q == NULL) { 113*0a70aaf8SLuiz Otavio O Souza free(cif, M_DEVBUF); 114*0a70aaf8SLuiz Otavio O Souza return (ENOMEM); 115*0a70aaf8SLuiz Otavio O Souza } 116*0a70aaf8SLuiz Otavio O Souza 117*0a70aaf8SLuiz Otavio O Souza if (a->qlimit == 0) 118*0a70aaf8SLuiz Otavio O Souza a->qlimit = 50; /* use default. */ 119*0a70aaf8SLuiz Otavio O Souza qlimit(cif->cl_q) = a->qlimit; 120*0a70aaf8SLuiz Otavio O Souza qtype(cif->cl_q) = Q_CODEL; 121*0a70aaf8SLuiz Otavio O Souza qlen(cif->cl_q) = 0; 122*0a70aaf8SLuiz Otavio O Souza qsize(cif->cl_q) = 0; 123*0a70aaf8SLuiz Otavio O Souza 124*0a70aaf8SLuiz Otavio O Souza if (opts->target == 0) 125*0a70aaf8SLuiz Otavio O Souza opts->target = 5; 126*0a70aaf8SLuiz Otavio O Souza if (opts->interval == 0) 127*0a70aaf8SLuiz Otavio O Souza opts->interval = 100; 128*0a70aaf8SLuiz Otavio O Souza cif->codel.params.target = machclk_freq * opts->target / 1000; 129*0a70aaf8SLuiz Otavio O Souza cif->codel.params.interval = machclk_freq * opts->interval / 1000; 130*0a70aaf8SLuiz Otavio O Souza cif->codel.params.ecn = opts->ecn; 131*0a70aaf8SLuiz Otavio O Souza cif->codel.stats.maxpacket = 256; 132*0a70aaf8SLuiz Otavio O Souza 133*0a70aaf8SLuiz Otavio O Souza cif->cl_stats.qlength = qlen(cif->cl_q); 134*0a70aaf8SLuiz Otavio O Souza cif->cl_stats.qlimit = qlimit(cif->cl_q); 135*0a70aaf8SLuiz Otavio O Souza 136*0a70aaf8SLuiz Otavio O Souza /* keep the state in pf_altq */ 137*0a70aaf8SLuiz Otavio O Souza a->altq_disc = cif; 138*0a70aaf8SLuiz Otavio O Souza 139*0a70aaf8SLuiz Otavio O Souza return (0); 140*0a70aaf8SLuiz Otavio O Souza } 141*0a70aaf8SLuiz Otavio O Souza 142*0a70aaf8SLuiz Otavio O Souza int 143*0a70aaf8SLuiz Otavio O Souza codel_remove_altq(struct pf_altq *a) 144*0a70aaf8SLuiz Otavio O Souza { 145*0a70aaf8SLuiz Otavio O Souza struct codel_if *cif; 146*0a70aaf8SLuiz Otavio O Souza 147*0a70aaf8SLuiz Otavio O Souza if ((cif = a->altq_disc) == NULL) 148*0a70aaf8SLuiz Otavio O Souza return (EINVAL); 149*0a70aaf8SLuiz Otavio O Souza a->altq_disc = NULL; 150*0a70aaf8SLuiz Otavio O Souza 151*0a70aaf8SLuiz Otavio O Souza if (cif->cl_q) 152*0a70aaf8SLuiz Otavio O Souza free(cif->cl_q, M_DEVBUF); 153*0a70aaf8SLuiz Otavio O Souza free(cif, M_DEVBUF); 154*0a70aaf8SLuiz Otavio O Souza 155*0a70aaf8SLuiz Otavio O Souza return (0); 156*0a70aaf8SLuiz Otavio O Souza } 157*0a70aaf8SLuiz Otavio O Souza 158*0a70aaf8SLuiz Otavio O Souza int 159*0a70aaf8SLuiz Otavio O Souza codel_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) 160*0a70aaf8SLuiz Otavio O Souza { 161*0a70aaf8SLuiz Otavio O Souza struct codel_if *cif; 162*0a70aaf8SLuiz Otavio O Souza struct codel_ifstats stats; 163*0a70aaf8SLuiz Otavio O Souza int error = 0; 164*0a70aaf8SLuiz Otavio O Souza 165*0a70aaf8SLuiz Otavio O Souza if ((cif = altq_lookup(a->ifname, ALTQT_CODEL)) == NULL) 166*0a70aaf8SLuiz Otavio O Souza return (EBADF); 167*0a70aaf8SLuiz Otavio O Souza 168*0a70aaf8SLuiz Otavio O Souza if (*nbytes < sizeof(stats)) 169*0a70aaf8SLuiz Otavio O Souza return (EINVAL); 170*0a70aaf8SLuiz Otavio O Souza 171*0a70aaf8SLuiz Otavio O Souza stats = cif->cl_stats; 172*0a70aaf8SLuiz Otavio O Souza stats.stats = cif->codel.stats; 173*0a70aaf8SLuiz Otavio O Souza 174*0a70aaf8SLuiz Otavio O Souza if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0) 175*0a70aaf8SLuiz Otavio O Souza return (error); 176*0a70aaf8SLuiz Otavio O Souza *nbytes = sizeof(stats); 177*0a70aaf8SLuiz Otavio O Souza 178*0a70aaf8SLuiz Otavio O Souza return (0); 179*0a70aaf8SLuiz Otavio O Souza } 180*0a70aaf8SLuiz Otavio O Souza 181*0a70aaf8SLuiz Otavio O Souza static int 182*0a70aaf8SLuiz Otavio O Souza codel_request(struct ifaltq *ifq, int req, void *arg) 183*0a70aaf8SLuiz Otavio O Souza { 184*0a70aaf8SLuiz Otavio O Souza struct codel_if *cif = (struct codel_if *)ifq->altq_disc; 185*0a70aaf8SLuiz Otavio O Souza struct mbuf *m; 186*0a70aaf8SLuiz Otavio O Souza 187*0a70aaf8SLuiz Otavio O Souza IFQ_LOCK_ASSERT(ifq); 188*0a70aaf8SLuiz Otavio O Souza 189*0a70aaf8SLuiz Otavio O Souza switch (req) { 190*0a70aaf8SLuiz Otavio O Souza case ALTRQ_PURGE: 191*0a70aaf8SLuiz Otavio O Souza if (!ALTQ_IS_ENABLED(cif->cif_ifq)) 192*0a70aaf8SLuiz Otavio O Souza break; 193*0a70aaf8SLuiz Otavio O Souza 194*0a70aaf8SLuiz Otavio O Souza if (qempty(cif->cl_q)) 195*0a70aaf8SLuiz Otavio O Souza break; 196*0a70aaf8SLuiz Otavio O Souza 197*0a70aaf8SLuiz Otavio O Souza while ((m = _getq(cif->cl_q)) != NULL) { 198*0a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m)); 199*0a70aaf8SLuiz Otavio O Souza m_freem(m); 200*0a70aaf8SLuiz Otavio O Souza IFQ_DEC_LEN(cif->cif_ifq); 201*0a70aaf8SLuiz Otavio O Souza } 202*0a70aaf8SLuiz Otavio O Souza cif->cif_ifq->ifq_len = 0; 203*0a70aaf8SLuiz Otavio O Souza break; 204*0a70aaf8SLuiz Otavio O Souza } 205*0a70aaf8SLuiz Otavio O Souza 206*0a70aaf8SLuiz Otavio O Souza return (0); 207*0a70aaf8SLuiz Otavio O Souza } 208*0a70aaf8SLuiz Otavio O Souza 209*0a70aaf8SLuiz Otavio O Souza static int 210*0a70aaf8SLuiz Otavio O Souza codel_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr) 211*0a70aaf8SLuiz Otavio O Souza { 212*0a70aaf8SLuiz Otavio O Souza 213*0a70aaf8SLuiz Otavio O Souza struct codel_if *cif = (struct codel_if *) ifq->altq_disc; 214*0a70aaf8SLuiz Otavio O Souza 215*0a70aaf8SLuiz Otavio O Souza IFQ_LOCK_ASSERT(ifq); 216*0a70aaf8SLuiz Otavio O Souza 217*0a70aaf8SLuiz Otavio O Souza /* grab class set by classifier */ 218*0a70aaf8SLuiz Otavio O Souza if ((m->m_flags & M_PKTHDR) == 0) { 219*0a70aaf8SLuiz Otavio O Souza /* should not happen */ 220*0a70aaf8SLuiz Otavio O Souza printf("altq: packet for %s does not have pkthdr\n", 221*0a70aaf8SLuiz Otavio O Souza ifq->altq_ifp->if_xname); 222*0a70aaf8SLuiz Otavio O Souza m_freem(m); 223*0a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m)); 224*0a70aaf8SLuiz Otavio O Souza return (ENOBUFS); 225*0a70aaf8SLuiz Otavio O Souza } 226*0a70aaf8SLuiz Otavio O Souza 227*0a70aaf8SLuiz Otavio O Souza if (codel_addq(&cif->codel, cif->cl_q, m)) { 228*0a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m)); 229*0a70aaf8SLuiz Otavio O Souza return (ENOBUFS); 230*0a70aaf8SLuiz Otavio O Souza } 231*0a70aaf8SLuiz Otavio O Souza IFQ_INC_LEN(ifq); 232*0a70aaf8SLuiz Otavio O Souza 233*0a70aaf8SLuiz Otavio O Souza return (0); 234*0a70aaf8SLuiz Otavio O Souza } 235*0a70aaf8SLuiz Otavio O Souza 236*0a70aaf8SLuiz Otavio O Souza static struct mbuf * 237*0a70aaf8SLuiz Otavio O Souza codel_dequeue(struct ifaltq *ifq, int op) 238*0a70aaf8SLuiz Otavio O Souza { 239*0a70aaf8SLuiz Otavio O Souza struct codel_if *cif = (struct codel_if *)ifq->altq_disc; 240*0a70aaf8SLuiz Otavio O Souza struct mbuf *m; 241*0a70aaf8SLuiz Otavio O Souza 242*0a70aaf8SLuiz Otavio O Souza IFQ_LOCK_ASSERT(ifq); 243*0a70aaf8SLuiz Otavio O Souza 244*0a70aaf8SLuiz Otavio O Souza if (IFQ_IS_EMPTY(ifq)) 245*0a70aaf8SLuiz Otavio O Souza return (NULL); 246*0a70aaf8SLuiz Otavio O Souza 247*0a70aaf8SLuiz Otavio O Souza if (op == ALTDQ_POLL) 248*0a70aaf8SLuiz Otavio O Souza return (qhead(cif->cl_q)); 249*0a70aaf8SLuiz Otavio O Souza 250*0a70aaf8SLuiz Otavio O Souza 251*0a70aaf8SLuiz Otavio O Souza m = codel_getq(&cif->codel, cif->cl_q); 252*0a70aaf8SLuiz Otavio O Souza if (m != NULL) { 253*0a70aaf8SLuiz Otavio O Souza IFQ_DEC_LEN(ifq); 254*0a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&cif->cl_stats.cl_xmitcnt, m_pktlen(m)); 255*0a70aaf8SLuiz Otavio O Souza return (m); 256*0a70aaf8SLuiz Otavio O Souza } 257*0a70aaf8SLuiz Otavio O Souza 258*0a70aaf8SLuiz Otavio O Souza return (NULL); 259*0a70aaf8SLuiz Otavio O Souza } 260*0a70aaf8SLuiz Otavio O Souza 261*0a70aaf8SLuiz Otavio O Souza struct codel * 262*0a70aaf8SLuiz Otavio O Souza codel_alloc(int target, int interval, int ecn) 263*0a70aaf8SLuiz Otavio O Souza { 264*0a70aaf8SLuiz Otavio O Souza struct codel *c; 265*0a70aaf8SLuiz Otavio O Souza 266*0a70aaf8SLuiz Otavio O Souza c = malloc(sizeof(*c), M_DEVBUF, M_NOWAIT | M_ZERO); 267*0a70aaf8SLuiz Otavio O Souza if (c != NULL) { 268*0a70aaf8SLuiz Otavio O Souza c->params.target = machclk_freq * target / 1000; 269*0a70aaf8SLuiz Otavio O Souza c->params.interval = machclk_freq * interval / 1000; 270*0a70aaf8SLuiz Otavio O Souza c->params.ecn = ecn; 271*0a70aaf8SLuiz Otavio O Souza c->stats.maxpacket = 256; 272*0a70aaf8SLuiz Otavio O Souza } 273*0a70aaf8SLuiz Otavio O Souza 274*0a70aaf8SLuiz Otavio O Souza return (c); 275*0a70aaf8SLuiz Otavio O Souza } 276*0a70aaf8SLuiz Otavio O Souza 277*0a70aaf8SLuiz Otavio O Souza void 278*0a70aaf8SLuiz Otavio O Souza codel_destroy(struct codel *c) 279*0a70aaf8SLuiz Otavio O Souza { 280*0a70aaf8SLuiz Otavio O Souza 281*0a70aaf8SLuiz Otavio O Souza free(c, M_DEVBUF); 282*0a70aaf8SLuiz Otavio O Souza } 283*0a70aaf8SLuiz Otavio O Souza 284*0a70aaf8SLuiz Otavio O Souza #define MTAG_CODEL 1438031249 285*0a70aaf8SLuiz Otavio O Souza int 286*0a70aaf8SLuiz Otavio O Souza codel_addq(struct codel *c, class_queue_t *q, struct mbuf *m) 287*0a70aaf8SLuiz Otavio O Souza { 288*0a70aaf8SLuiz Otavio O Souza struct m_tag *mtag; 289*0a70aaf8SLuiz Otavio O Souza uint64_t *enqueue_time; 290*0a70aaf8SLuiz Otavio O Souza 291*0a70aaf8SLuiz Otavio O Souza if (qlen(q) < qlimit(q)) { 292*0a70aaf8SLuiz Otavio O Souza mtag = m_tag_locate(m, MTAG_CODEL, 0, NULL); 293*0a70aaf8SLuiz Otavio O Souza if (mtag == NULL) 294*0a70aaf8SLuiz Otavio O Souza mtag = m_tag_alloc(MTAG_CODEL, 0, sizeof(uint64_t), 295*0a70aaf8SLuiz Otavio O Souza M_NOWAIT); 296*0a70aaf8SLuiz Otavio O Souza if (mtag == NULL) { 297*0a70aaf8SLuiz Otavio O Souza m_freem(m); 298*0a70aaf8SLuiz Otavio O Souza return (-1); 299*0a70aaf8SLuiz Otavio O Souza } 300*0a70aaf8SLuiz Otavio O Souza enqueue_time = (uint64_t *)(mtag + 1); 301*0a70aaf8SLuiz Otavio O Souza *enqueue_time = read_machclk(); 302*0a70aaf8SLuiz Otavio O Souza m_tag_prepend(m, mtag); 303*0a70aaf8SLuiz Otavio O Souza _addq(q, m); 304*0a70aaf8SLuiz Otavio O Souza return (0); 305*0a70aaf8SLuiz Otavio O Souza } 306*0a70aaf8SLuiz Otavio O Souza c->drop_overlimit++; 307*0a70aaf8SLuiz Otavio O Souza m_freem(m); 308*0a70aaf8SLuiz Otavio O Souza 309*0a70aaf8SLuiz Otavio O Souza return (-1); 310*0a70aaf8SLuiz Otavio O Souza } 311*0a70aaf8SLuiz Otavio O Souza 312*0a70aaf8SLuiz Otavio O Souza static int 313*0a70aaf8SLuiz Otavio O Souza codel_should_drop(struct codel *c, class_queue_t *q, struct mbuf *m, 314*0a70aaf8SLuiz Otavio O Souza u_int64_t now) 315*0a70aaf8SLuiz Otavio O Souza { 316*0a70aaf8SLuiz Otavio O Souza struct m_tag *mtag; 317*0a70aaf8SLuiz Otavio O Souza uint64_t *enqueue_time; 318*0a70aaf8SLuiz Otavio O Souza 319*0a70aaf8SLuiz Otavio O Souza if (m == NULL) { 320*0a70aaf8SLuiz Otavio O Souza c->vars.first_above_time = 0; 321*0a70aaf8SLuiz Otavio O Souza return (0); 322*0a70aaf8SLuiz Otavio O Souza } 323*0a70aaf8SLuiz Otavio O Souza 324*0a70aaf8SLuiz Otavio O Souza mtag = m_tag_locate(m, MTAG_CODEL, 0, NULL); 325*0a70aaf8SLuiz Otavio O Souza if (mtag == NULL) { 326*0a70aaf8SLuiz Otavio O Souza /* Only one warning per second. */ 327*0a70aaf8SLuiz Otavio O Souza if (ppsratecheck(&c->last_log, &c->last_pps, 1)) 328*0a70aaf8SLuiz Otavio O Souza printf("%s: could not found the packet mtag!\n", 329*0a70aaf8SLuiz Otavio O Souza __func__); 330*0a70aaf8SLuiz Otavio O Souza c->vars.first_above_time = 0; 331*0a70aaf8SLuiz Otavio O Souza return (0); 332*0a70aaf8SLuiz Otavio O Souza } 333*0a70aaf8SLuiz Otavio O Souza enqueue_time = (uint64_t *)(mtag + 1); 334*0a70aaf8SLuiz Otavio O Souza c->vars.ldelay = now - *enqueue_time; 335*0a70aaf8SLuiz Otavio O Souza c->stats.maxpacket = MAX(c->stats.maxpacket, m_pktlen(m)); 336*0a70aaf8SLuiz Otavio O Souza 337*0a70aaf8SLuiz Otavio O Souza if (codel_time_before(c->vars.ldelay, c->params.target) || 338*0a70aaf8SLuiz Otavio O Souza qsize(q) <= c->stats.maxpacket) { 339*0a70aaf8SLuiz Otavio O Souza /* went below - stay below for at least interval */ 340*0a70aaf8SLuiz Otavio O Souza c->vars.first_above_time = 0; 341*0a70aaf8SLuiz Otavio O Souza return (0); 342*0a70aaf8SLuiz Otavio O Souza } 343*0a70aaf8SLuiz Otavio O Souza if (c->vars.first_above_time == 0) { 344*0a70aaf8SLuiz Otavio O Souza /* just went above from below. If we stay above 345*0a70aaf8SLuiz Otavio O Souza * for at least interval we'll say it's ok to drop 346*0a70aaf8SLuiz Otavio O Souza */ 347*0a70aaf8SLuiz Otavio O Souza c->vars.first_above_time = now + c->params.interval; 348*0a70aaf8SLuiz Otavio O Souza return (0); 349*0a70aaf8SLuiz Otavio O Souza } 350*0a70aaf8SLuiz Otavio O Souza if (codel_time_after(now, c->vars.first_above_time)) 351*0a70aaf8SLuiz Otavio O Souza return (1); 352*0a70aaf8SLuiz Otavio O Souza 353*0a70aaf8SLuiz Otavio O Souza return (0); 354*0a70aaf8SLuiz Otavio O Souza } 355*0a70aaf8SLuiz Otavio O Souza 356*0a70aaf8SLuiz Otavio O Souza /* 357*0a70aaf8SLuiz Otavio O Souza * Run a Newton method step: 358*0a70aaf8SLuiz Otavio O Souza * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2) 359*0a70aaf8SLuiz Otavio O Souza * 360*0a70aaf8SLuiz Otavio O Souza * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32 361*0a70aaf8SLuiz Otavio O Souza */ 362*0a70aaf8SLuiz Otavio O Souza static void 363*0a70aaf8SLuiz Otavio O Souza codel_Newton_step(struct codel_vars *vars) 364*0a70aaf8SLuiz Otavio O Souza { 365*0a70aaf8SLuiz Otavio O Souza uint32_t invsqrt, invsqrt2; 366*0a70aaf8SLuiz Otavio O Souza uint64_t val; 367*0a70aaf8SLuiz Otavio O Souza 368*0a70aaf8SLuiz Otavio O Souza /* sizeof_in_bits(rec_inv_sqrt) */ 369*0a70aaf8SLuiz Otavio O Souza #define REC_INV_SQRT_BITS (8 * sizeof(u_int16_t)) 370*0a70aaf8SLuiz Otavio O Souza /* needed shift to get a Q0.32 number from rec_inv_sqrt */ 371*0a70aaf8SLuiz Otavio O Souza #define REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS) 372*0a70aaf8SLuiz Otavio O Souza 373*0a70aaf8SLuiz Otavio O Souza invsqrt = ((u_int32_t)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT; 374*0a70aaf8SLuiz Otavio O Souza invsqrt2 = ((u_int64_t)invsqrt * invsqrt) >> 32; 375*0a70aaf8SLuiz Otavio O Souza val = (3LL << 32) - ((u_int64_t)vars->count * invsqrt2); 376*0a70aaf8SLuiz Otavio O Souza val >>= 2; /* avoid overflow in following multiply */ 377*0a70aaf8SLuiz Otavio O Souza val = (val * invsqrt) >> (32 - 2 + 1); 378*0a70aaf8SLuiz Otavio O Souza 379*0a70aaf8SLuiz Otavio O Souza vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT; 380*0a70aaf8SLuiz Otavio O Souza } 381*0a70aaf8SLuiz Otavio O Souza 382*0a70aaf8SLuiz Otavio O Souza static u_int64_t 383*0a70aaf8SLuiz Otavio O Souza codel_control_law(u_int64_t t, u_int64_t interval, u_int32_t rec_inv_sqrt) 384*0a70aaf8SLuiz Otavio O Souza { 385*0a70aaf8SLuiz Otavio O Souza 386*0a70aaf8SLuiz Otavio O Souza return (t + (u_int32_t)(((u_int64_t)interval * 387*0a70aaf8SLuiz Otavio O Souza (rec_inv_sqrt << REC_INV_SQRT_SHIFT)) >> 32)); 388*0a70aaf8SLuiz Otavio O Souza } 389*0a70aaf8SLuiz Otavio O Souza 390*0a70aaf8SLuiz Otavio O Souza struct mbuf * 391*0a70aaf8SLuiz Otavio O Souza codel_getq(struct codel *c, class_queue_t *q) 392*0a70aaf8SLuiz Otavio O Souza { 393*0a70aaf8SLuiz Otavio O Souza struct mbuf *m; 394*0a70aaf8SLuiz Otavio O Souza u_int64_t now; 395*0a70aaf8SLuiz Otavio O Souza int drop; 396*0a70aaf8SLuiz Otavio O Souza 397*0a70aaf8SLuiz Otavio O Souza if ((m = _getq(q)) == NULL) { 398*0a70aaf8SLuiz Otavio O Souza c->vars.dropping = 0; 399*0a70aaf8SLuiz Otavio O Souza return (m); 400*0a70aaf8SLuiz Otavio O Souza } 401*0a70aaf8SLuiz Otavio O Souza 402*0a70aaf8SLuiz Otavio O Souza now = read_machclk(); 403*0a70aaf8SLuiz Otavio O Souza drop = codel_should_drop(c, q, m, now); 404*0a70aaf8SLuiz Otavio O Souza if (c->vars.dropping) { 405*0a70aaf8SLuiz Otavio O Souza if (!drop) { 406*0a70aaf8SLuiz Otavio O Souza /* sojourn time below target - leave dropping state */ 407*0a70aaf8SLuiz Otavio O Souza c->vars.dropping = 0; 408*0a70aaf8SLuiz Otavio O Souza } else if (codel_time_after_eq(now, c->vars.drop_next)) { 409*0a70aaf8SLuiz Otavio O Souza /* It's time for the next drop. Drop the current 410*0a70aaf8SLuiz Otavio O Souza * packet and dequeue the next. The dequeue might 411*0a70aaf8SLuiz Otavio O Souza * take us out of dropping state. 412*0a70aaf8SLuiz Otavio O Souza * If not, schedule the next drop. 413*0a70aaf8SLuiz Otavio O Souza * A large backlog might result in drop rates so high 414*0a70aaf8SLuiz Otavio O Souza * that the next drop should happen now, 415*0a70aaf8SLuiz Otavio O Souza * hence the while loop. 416*0a70aaf8SLuiz Otavio O Souza */ 417*0a70aaf8SLuiz Otavio O Souza while (c->vars.dropping && 418*0a70aaf8SLuiz Otavio O Souza codel_time_after_eq(now, c->vars.drop_next)) { 419*0a70aaf8SLuiz Otavio O Souza c->vars.count++; /* don't care of possible wrap 420*0a70aaf8SLuiz Otavio O Souza * since there is no more 421*0a70aaf8SLuiz Otavio O Souza * divide */ 422*0a70aaf8SLuiz Otavio O Souza codel_Newton_step(&c->vars); 423*0a70aaf8SLuiz Otavio O Souza /* TODO ECN */ 424*0a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&c->stats.drop_cnt, m_pktlen(m)); 425*0a70aaf8SLuiz Otavio O Souza m_freem(m); 426*0a70aaf8SLuiz Otavio O Souza m = _getq(q); 427*0a70aaf8SLuiz Otavio O Souza if (!codel_should_drop(c, q, m, now)) 428*0a70aaf8SLuiz Otavio O Souza /* leave dropping state */ 429*0a70aaf8SLuiz Otavio O Souza c->vars.dropping = 0; 430*0a70aaf8SLuiz Otavio O Souza else 431*0a70aaf8SLuiz Otavio O Souza /* and schedule the next drop */ 432*0a70aaf8SLuiz Otavio O Souza c->vars.drop_next = 433*0a70aaf8SLuiz Otavio O Souza codel_control_law(c->vars.drop_next, 434*0a70aaf8SLuiz Otavio O Souza c->params.interval, 435*0a70aaf8SLuiz Otavio O Souza c->vars.rec_inv_sqrt); 436*0a70aaf8SLuiz Otavio O Souza } 437*0a70aaf8SLuiz Otavio O Souza } 438*0a70aaf8SLuiz Otavio O Souza } else if (drop) { 439*0a70aaf8SLuiz Otavio O Souza /* TODO ECN */ 440*0a70aaf8SLuiz Otavio O Souza PKTCNTR_ADD(&c->stats.drop_cnt, m_pktlen(m)); 441*0a70aaf8SLuiz Otavio O Souza m_freem(m); 442*0a70aaf8SLuiz Otavio O Souza 443*0a70aaf8SLuiz Otavio O Souza m = _getq(q); 444*0a70aaf8SLuiz Otavio O Souza drop = codel_should_drop(c, q, m, now); 445*0a70aaf8SLuiz Otavio O Souza 446*0a70aaf8SLuiz Otavio O Souza c->vars.dropping = 1; 447*0a70aaf8SLuiz Otavio O Souza /* if min went above target close to when we last went below it 448*0a70aaf8SLuiz Otavio O Souza * assume that the drop rate that controlled the queue on the 449*0a70aaf8SLuiz Otavio O Souza * last cycle is a good starting point to control it now. 450*0a70aaf8SLuiz Otavio O Souza */ 451*0a70aaf8SLuiz Otavio O Souza if (codel_time_before(now - c->vars.drop_next, 452*0a70aaf8SLuiz Otavio O Souza 16 * c->params.interval)) { 453*0a70aaf8SLuiz Otavio O Souza c->vars.count = (c->vars.count - c->vars.lastcount) | 1; 454*0a70aaf8SLuiz Otavio O Souza /* we dont care if rec_inv_sqrt approximation 455*0a70aaf8SLuiz Otavio O Souza * is not very precise : 456*0a70aaf8SLuiz Otavio O Souza * Next Newton steps will correct it quadratically. 457*0a70aaf8SLuiz Otavio O Souza */ 458*0a70aaf8SLuiz Otavio O Souza codel_Newton_step(&c->vars); 459*0a70aaf8SLuiz Otavio O Souza } else { 460*0a70aaf8SLuiz Otavio O Souza c->vars.count = 1; 461*0a70aaf8SLuiz Otavio O Souza c->vars.rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT; 462*0a70aaf8SLuiz Otavio O Souza } 463*0a70aaf8SLuiz Otavio O Souza c->vars.lastcount = c->vars.count; 464*0a70aaf8SLuiz Otavio O Souza c->vars.drop_next = codel_control_law(now, c->params.interval, 465*0a70aaf8SLuiz Otavio O Souza c->vars.rec_inv_sqrt); 466*0a70aaf8SLuiz Otavio O Souza } 467*0a70aaf8SLuiz Otavio O Souza 468*0a70aaf8SLuiz Otavio O Souza return (m); 469*0a70aaf8SLuiz Otavio O Souza } 470*0a70aaf8SLuiz Otavio O Souza 471*0a70aaf8SLuiz Otavio O Souza void 472*0a70aaf8SLuiz Otavio O Souza codel_getstats(struct codel *c, struct codel_stats *s) 473*0a70aaf8SLuiz Otavio O Souza { 474*0a70aaf8SLuiz Otavio O Souza *s = c->stats; 475*0a70aaf8SLuiz Otavio O Souza } 476*0a70aaf8SLuiz Otavio O Souza 477*0a70aaf8SLuiz Otavio O Souza #endif /* ALTQ_CODEL */ 478