xref: /freebsd/sys/net/altq/altq_codel.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
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 #include "opt_altq.h"
410a70aaf8SLuiz Otavio O Souza #include "opt_inet.h"
420a70aaf8SLuiz Otavio O Souza #include "opt_inet6.h"
430a70aaf8SLuiz Otavio O Souza 
440a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL  /* CoDel is enabled by ALTQ_CODEL option in opt_altq.h */
450a70aaf8SLuiz Otavio O Souza 
460a70aaf8SLuiz Otavio O Souza #include <sys/param.h>
470a70aaf8SLuiz Otavio O Souza #include <sys/malloc.h>
480a70aaf8SLuiz Otavio O Souza #include <sys/mbuf.h>
490a70aaf8SLuiz Otavio O Souza #include <sys/socket.h>
500a70aaf8SLuiz Otavio O Souza #include <sys/systm.h>
510a70aaf8SLuiz Otavio O Souza 
520a70aaf8SLuiz Otavio O Souza #include <net/if.h>
530a70aaf8SLuiz Otavio O Souza #include <net/if_var.h>
542c2b37adSJustin Hibbits #include <net/if_private.h>
550a70aaf8SLuiz Otavio O Souza #include <netinet/in.h>
560a70aaf8SLuiz Otavio O Souza 
570a70aaf8SLuiz Otavio O Souza #include <netpfil/pf/pf.h>
580a70aaf8SLuiz Otavio O Souza #include <netpfil/pf/pf_altq.h>
590a70aaf8SLuiz Otavio O Souza #include <net/altq/if_altq.h>
600a70aaf8SLuiz Otavio O Souza #include <net/altq/altq.h>
610a70aaf8SLuiz Otavio O Souza #include <net/altq/altq_codel.h>
620a70aaf8SLuiz Otavio O Souza 
630a70aaf8SLuiz Otavio O Souza static int		 codel_should_drop(struct codel *, class_queue_t *,
640a70aaf8SLuiz Otavio O Souza 			    struct mbuf *, u_int64_t);
650a70aaf8SLuiz Otavio O Souza static void		 codel_Newton_step(struct codel_vars *);
660a70aaf8SLuiz Otavio O Souza static u_int64_t	 codel_control_law(u_int64_t t, u_int64_t, u_int32_t);
670a70aaf8SLuiz Otavio O Souza 
680a70aaf8SLuiz Otavio O Souza #define	codel_time_after(a, b)		((int64_t)(a) - (int64_t)(b) > 0)
690a70aaf8SLuiz Otavio O Souza #define	codel_time_after_eq(a, b)	((int64_t)(a) - (int64_t)(b) >= 0)
700a70aaf8SLuiz Otavio O Souza #define	codel_time_before(a, b)		((int64_t)(a) - (int64_t)(b) < 0)
710a70aaf8SLuiz Otavio O Souza #define	codel_time_before_eq(a, b)	((int64_t)(a) - (int64_t)(b) <= 0)
720a70aaf8SLuiz Otavio O Souza 
730a70aaf8SLuiz Otavio O Souza static int codel_request(struct ifaltq *, int, void *);
740a70aaf8SLuiz Otavio O Souza 
750a70aaf8SLuiz Otavio O Souza static int codel_enqueue(struct ifaltq *, struct mbuf *, struct altq_pktattr *);
760a70aaf8SLuiz Otavio O Souza static struct mbuf *codel_dequeue(struct ifaltq *, int);
770a70aaf8SLuiz Otavio O Souza 
780a70aaf8SLuiz Otavio O Souza int
codel_pfattach(struct pf_altq * a)790a70aaf8SLuiz Otavio O Souza codel_pfattach(struct pf_altq *a)
800a70aaf8SLuiz Otavio O Souza {
810a70aaf8SLuiz Otavio O Souza 	struct ifnet *ifp;
820a70aaf8SLuiz Otavio O Souza 
830a70aaf8SLuiz Otavio O Souza 	if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
840a70aaf8SLuiz Otavio O Souza 		return (EINVAL);
850a70aaf8SLuiz Otavio O Souza 
860a70aaf8SLuiz Otavio O Souza 	return (altq_attach(&ifp->if_snd, ALTQT_CODEL, a->altq_disc,
8727b2aa49SKristof Provost 	    codel_enqueue, codel_dequeue, codel_request));
880a70aaf8SLuiz Otavio O Souza }
890a70aaf8SLuiz Otavio O Souza 
900a70aaf8SLuiz Otavio O Souza int
codel_add_altq(struct ifnet * ifp,struct pf_altq * a)918f2ac656SPatrick Kelsey codel_add_altq(struct ifnet *ifp, struct pf_altq *a)
920a70aaf8SLuiz Otavio O Souza {
930a70aaf8SLuiz Otavio O Souza 	struct codel_if	*cif;
940a70aaf8SLuiz Otavio O Souza 	struct codel_opts	*opts;
950a70aaf8SLuiz Otavio O Souza 
968f2ac656SPatrick Kelsey 	if (ifp == NULL)
970a70aaf8SLuiz Otavio O Souza 		return (EINVAL);
980a70aaf8SLuiz Otavio O Souza 	if (!ALTQ_IS_READY(&ifp->if_snd))
990a70aaf8SLuiz Otavio O Souza 		return (ENODEV);
1000a70aaf8SLuiz Otavio O Souza 
1010a70aaf8SLuiz Otavio O Souza 	opts = &a->pq_u.codel_opts;
1020a70aaf8SLuiz Otavio O Souza 
1030a70aaf8SLuiz Otavio O Souza 	cif = malloc(sizeof(struct codel_if), M_DEVBUF, M_NOWAIT | M_ZERO);
1040a70aaf8SLuiz Otavio O Souza 	if (cif == NULL)
1050a70aaf8SLuiz Otavio O Souza 		return (ENOMEM);
1060a70aaf8SLuiz Otavio O Souza 	cif->cif_bandwidth = a->ifbandwidth;
1070a70aaf8SLuiz Otavio O Souza 	cif->cif_ifq = &ifp->if_snd;
1080a70aaf8SLuiz Otavio O Souza 
1090a70aaf8SLuiz Otavio O Souza 	cif->cl_q = malloc(sizeof(class_queue_t), M_DEVBUF, M_NOWAIT | M_ZERO);
1100a70aaf8SLuiz Otavio O Souza 	if (cif->cl_q == NULL) {
1110a70aaf8SLuiz Otavio O Souza 		free(cif, M_DEVBUF);
1120a70aaf8SLuiz Otavio O Souza 		return (ENOMEM);
1130a70aaf8SLuiz Otavio O Souza 	}
1140a70aaf8SLuiz Otavio O Souza 
1150a70aaf8SLuiz Otavio O Souza 	if (a->qlimit == 0)
1160a70aaf8SLuiz Otavio O Souza 		a->qlimit = 50;	/* use default. */
1170a70aaf8SLuiz Otavio O Souza 	qlimit(cif->cl_q) = a->qlimit;
1180a70aaf8SLuiz Otavio O Souza 	qtype(cif->cl_q) = Q_CODEL;
1190a70aaf8SLuiz Otavio O Souza 	qlen(cif->cl_q) = 0;
1200a70aaf8SLuiz Otavio O Souza 	qsize(cif->cl_q) = 0;
1210a70aaf8SLuiz Otavio O Souza 
1220a70aaf8SLuiz Otavio O Souza 	if (opts->target == 0)
1230a70aaf8SLuiz Otavio O Souza 		opts->target = 5;
1240a70aaf8SLuiz Otavio O Souza 	if (opts->interval == 0)
1250a70aaf8SLuiz Otavio O Souza 		opts->interval = 100;
1260a70aaf8SLuiz Otavio O Souza 	cif->codel.params.target = machclk_freq * opts->target / 1000;
1270a70aaf8SLuiz Otavio O Souza 	cif->codel.params.interval = machclk_freq * opts->interval / 1000;
1280a70aaf8SLuiz Otavio O Souza 	cif->codel.params.ecn = opts->ecn;
1290a70aaf8SLuiz Otavio O Souza 	cif->codel.stats.maxpacket = 256;
1300a70aaf8SLuiz Otavio O Souza 
1310a70aaf8SLuiz Otavio O Souza 	cif->cl_stats.qlength = qlen(cif->cl_q);
1320a70aaf8SLuiz Otavio O Souza 	cif->cl_stats.qlimit = qlimit(cif->cl_q);
1330a70aaf8SLuiz Otavio O Souza 
1340a70aaf8SLuiz Otavio O Souza 	/* keep the state in pf_altq */
1350a70aaf8SLuiz Otavio O Souza 	a->altq_disc = cif;
1360a70aaf8SLuiz Otavio O Souza 
1370a70aaf8SLuiz Otavio O Souza 	return (0);
1380a70aaf8SLuiz Otavio O Souza }
1390a70aaf8SLuiz Otavio O Souza 
1400a70aaf8SLuiz Otavio O Souza int
codel_remove_altq(struct pf_altq * a)1410a70aaf8SLuiz Otavio O Souza codel_remove_altq(struct pf_altq *a)
1420a70aaf8SLuiz Otavio O Souza {
1430a70aaf8SLuiz Otavio O Souza 	struct codel_if *cif;
1440a70aaf8SLuiz Otavio O Souza 
1450a70aaf8SLuiz Otavio O Souza 	if ((cif = a->altq_disc) == NULL)
1460a70aaf8SLuiz Otavio O Souza 		return (EINVAL);
1470a70aaf8SLuiz Otavio O Souza 	a->altq_disc = NULL;
1480a70aaf8SLuiz Otavio O Souza 
1490a70aaf8SLuiz Otavio O Souza 	if (cif->cl_q)
1500a70aaf8SLuiz Otavio O Souza 		free(cif->cl_q, M_DEVBUF);
1510a70aaf8SLuiz Otavio O Souza 	free(cif, M_DEVBUF);
1520a70aaf8SLuiz Otavio O Souza 
1530a70aaf8SLuiz Otavio O Souza 	return (0);
1540a70aaf8SLuiz Otavio O Souza }
1550a70aaf8SLuiz Otavio O Souza 
1560a70aaf8SLuiz Otavio O Souza int
codel_getqstats(struct pf_altq * a,void * ubuf,int * nbytes,int version)157249cc75fSPatrick Kelsey codel_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version)
1580a70aaf8SLuiz Otavio O Souza {
1590a70aaf8SLuiz Otavio O Souza 	struct codel_if *cif;
1600a70aaf8SLuiz Otavio O Souza 	struct codel_ifstats stats;
1610a70aaf8SLuiz Otavio O Souza 	int error = 0;
1620a70aaf8SLuiz Otavio O Souza 
1630a70aaf8SLuiz Otavio O Souza 	if ((cif = altq_lookup(a->ifname, ALTQT_CODEL)) == NULL)
1640a70aaf8SLuiz Otavio O Souza 		return (EBADF);
1650a70aaf8SLuiz Otavio O Souza 
1660a70aaf8SLuiz Otavio O Souza 	if (*nbytes < sizeof(stats))
1670a70aaf8SLuiz Otavio O Souza 		return (EINVAL);
1680a70aaf8SLuiz Otavio O Souza 
1690a70aaf8SLuiz Otavio O Souza 	stats = cif->cl_stats;
1700a70aaf8SLuiz Otavio O Souza 	stats.stats = cif->codel.stats;
1710a70aaf8SLuiz Otavio O Souza 
1720a70aaf8SLuiz Otavio O Souza 	if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
1730a70aaf8SLuiz Otavio O Souza 		return (error);
1740a70aaf8SLuiz Otavio O Souza 	*nbytes = sizeof(stats);
1750a70aaf8SLuiz Otavio O Souza 
1760a70aaf8SLuiz Otavio O Souza 	return (0);
1770a70aaf8SLuiz Otavio O Souza }
1780a70aaf8SLuiz Otavio O Souza 
1790a70aaf8SLuiz Otavio O Souza static int
codel_request(struct ifaltq * ifq,int req,void * arg)1800a70aaf8SLuiz Otavio O Souza codel_request(struct ifaltq *ifq, int req, void *arg)
1810a70aaf8SLuiz Otavio O Souza {
1820a70aaf8SLuiz Otavio O Souza 	struct codel_if	*cif = (struct codel_if *)ifq->altq_disc;
1830a70aaf8SLuiz Otavio O Souza 	struct mbuf *m;
1840a70aaf8SLuiz Otavio O Souza 
1850a70aaf8SLuiz Otavio O Souza 	IFQ_LOCK_ASSERT(ifq);
1860a70aaf8SLuiz Otavio O Souza 
1870a70aaf8SLuiz Otavio O Souza 	switch (req) {
1880a70aaf8SLuiz Otavio O Souza 	case ALTRQ_PURGE:
1890a70aaf8SLuiz Otavio O Souza 		if (!ALTQ_IS_ENABLED(cif->cif_ifq))
1900a70aaf8SLuiz Otavio O Souza 			break;
1910a70aaf8SLuiz Otavio O Souza 
1920a70aaf8SLuiz Otavio O Souza 		if (qempty(cif->cl_q))
1930a70aaf8SLuiz Otavio O Souza 			break;
1940a70aaf8SLuiz Otavio O Souza 
1950a70aaf8SLuiz Otavio O Souza 		while ((m = _getq(cif->cl_q)) != NULL) {
1960a70aaf8SLuiz Otavio O Souza 			PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m));
1970a70aaf8SLuiz Otavio O Souza 			m_freem(m);
1980a70aaf8SLuiz Otavio O Souza 			IFQ_DEC_LEN(cif->cif_ifq);
1990a70aaf8SLuiz Otavio O Souza 		}
2000a70aaf8SLuiz Otavio O Souza 		cif->cif_ifq->ifq_len = 0;
2010a70aaf8SLuiz Otavio O Souza 		break;
2020a70aaf8SLuiz Otavio O Souza 	}
2030a70aaf8SLuiz Otavio O Souza 
2040a70aaf8SLuiz Otavio O Souza 	return (0);
2050a70aaf8SLuiz Otavio O Souza }
2060a70aaf8SLuiz Otavio O Souza 
2070a70aaf8SLuiz Otavio O Souza static int
codel_enqueue(struct ifaltq * ifq,struct mbuf * m,struct altq_pktattr * pktattr)2080a70aaf8SLuiz Otavio O Souza codel_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr)
2090a70aaf8SLuiz Otavio O Souza {
2100a70aaf8SLuiz Otavio O Souza 
2110a70aaf8SLuiz Otavio O Souza 	struct codel_if *cif = (struct codel_if *) ifq->altq_disc;
2120a70aaf8SLuiz Otavio O Souza 
2130a70aaf8SLuiz Otavio O Souza 	IFQ_LOCK_ASSERT(ifq);
2140a70aaf8SLuiz Otavio O Souza 
2150a70aaf8SLuiz Otavio O Souza 	/* grab class set by classifier */
2160a70aaf8SLuiz Otavio O Souza 	if ((m->m_flags & M_PKTHDR) == 0) {
2170a70aaf8SLuiz Otavio O Souza 		/* should not happen */
2180a70aaf8SLuiz Otavio O Souza 		printf("altq: packet for %s does not have pkthdr\n",
2190a70aaf8SLuiz Otavio O Souza 		   ifq->altq_ifp->if_xname);
2200a70aaf8SLuiz Otavio O Souza 		m_freem(m);
2210a70aaf8SLuiz Otavio O Souza 		PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m));
2220a70aaf8SLuiz Otavio O Souza 		return (ENOBUFS);
2230a70aaf8SLuiz Otavio O Souza 	}
2240a70aaf8SLuiz Otavio O Souza 
2250a70aaf8SLuiz Otavio O Souza 	if (codel_addq(&cif->codel, cif->cl_q, m)) {
2260a70aaf8SLuiz Otavio O Souza 		PKTCNTR_ADD(&cif->cl_stats.cl_dropcnt, m_pktlen(m));
2270a70aaf8SLuiz Otavio O Souza 		return (ENOBUFS);
2280a70aaf8SLuiz Otavio O Souza 	}
2290a70aaf8SLuiz Otavio O Souza 	IFQ_INC_LEN(ifq);
2300a70aaf8SLuiz Otavio O Souza 
2310a70aaf8SLuiz Otavio O Souza 	return (0);
2320a70aaf8SLuiz Otavio O Souza }
2330a70aaf8SLuiz Otavio O Souza 
2340a70aaf8SLuiz Otavio O Souza static struct mbuf *
codel_dequeue(struct ifaltq * ifq,int op)2350a70aaf8SLuiz Otavio O Souza codel_dequeue(struct ifaltq *ifq, int op)
2360a70aaf8SLuiz Otavio O Souza {
2370a70aaf8SLuiz Otavio O Souza 	struct codel_if *cif = (struct codel_if *)ifq->altq_disc;
2380a70aaf8SLuiz Otavio O Souza 	struct mbuf *m;
2390a70aaf8SLuiz Otavio O Souza 
2400a70aaf8SLuiz Otavio O Souza 	IFQ_LOCK_ASSERT(ifq);
2410a70aaf8SLuiz Otavio O Souza 
2420a70aaf8SLuiz Otavio O Souza 	if (IFQ_IS_EMPTY(ifq))
2430a70aaf8SLuiz Otavio O Souza 		return (NULL);
2440a70aaf8SLuiz Otavio O Souza 
2450a70aaf8SLuiz Otavio O Souza 	if (op == ALTDQ_POLL)
2460a70aaf8SLuiz Otavio O Souza 		return (qhead(cif->cl_q));
2470a70aaf8SLuiz Otavio O Souza 
2480a70aaf8SLuiz Otavio O Souza 	m = codel_getq(&cif->codel, cif->cl_q);
2490a70aaf8SLuiz Otavio O Souza 	if (m != NULL) {
2500a70aaf8SLuiz Otavio O Souza 		IFQ_DEC_LEN(ifq);
2510a70aaf8SLuiz Otavio O Souza 		PKTCNTR_ADD(&cif->cl_stats.cl_xmitcnt, m_pktlen(m));
2520a70aaf8SLuiz Otavio O Souza 		return (m);
2530a70aaf8SLuiz Otavio O Souza 	}
2540a70aaf8SLuiz Otavio O Souza 
2550a70aaf8SLuiz Otavio O Souza 	return (NULL);
2560a70aaf8SLuiz Otavio O Souza }
2570a70aaf8SLuiz Otavio O Souza 
2580a70aaf8SLuiz Otavio O Souza struct codel *
codel_alloc(int target,int interval,int ecn)2590a70aaf8SLuiz Otavio O Souza codel_alloc(int target, int interval, int ecn)
2600a70aaf8SLuiz Otavio O Souza {
2610a70aaf8SLuiz Otavio O Souza 	struct codel *c;
2620a70aaf8SLuiz Otavio O Souza 
2630a70aaf8SLuiz Otavio O Souza 	c = malloc(sizeof(*c), M_DEVBUF, M_NOWAIT | M_ZERO);
2640a70aaf8SLuiz Otavio O Souza 	if (c != NULL) {
2650a70aaf8SLuiz Otavio O Souza 		c->params.target = machclk_freq * target / 1000;
2660a70aaf8SLuiz Otavio O Souza 		c->params.interval = machclk_freq * interval / 1000;
2670a70aaf8SLuiz Otavio O Souza 		c->params.ecn = ecn;
2680a70aaf8SLuiz Otavio O Souza 		c->stats.maxpacket = 256;
2690a70aaf8SLuiz Otavio O Souza 	}
2700a70aaf8SLuiz Otavio O Souza 
2710a70aaf8SLuiz Otavio O Souza 	return (c);
2720a70aaf8SLuiz Otavio O Souza }
2730a70aaf8SLuiz Otavio O Souza 
2740a70aaf8SLuiz Otavio O Souza void
codel_destroy(struct codel * c)2750a70aaf8SLuiz Otavio O Souza codel_destroy(struct codel *c)
2760a70aaf8SLuiz Otavio O Souza {
2770a70aaf8SLuiz Otavio O Souza 
2780a70aaf8SLuiz Otavio O Souza 	free(c, M_DEVBUF);
2790a70aaf8SLuiz Otavio O Souza }
2800a70aaf8SLuiz Otavio O Souza 
2810a70aaf8SLuiz Otavio O Souza #define	MTAG_CODEL	1438031249
2820a70aaf8SLuiz Otavio O Souza int
codel_addq(struct codel * c,class_queue_t * q,struct mbuf * m)2830a70aaf8SLuiz Otavio O Souza codel_addq(struct codel *c, class_queue_t *q, struct mbuf *m)
2840a70aaf8SLuiz Otavio O Souza {
2850a70aaf8SLuiz Otavio O Souza 	struct m_tag *mtag;
2860a70aaf8SLuiz Otavio O Souza 	uint64_t *enqueue_time;
2870a70aaf8SLuiz Otavio O Souza 
2880a70aaf8SLuiz Otavio O Souza 	if (qlen(q) < qlimit(q)) {
2890a70aaf8SLuiz Otavio O Souza 		mtag = m_tag_locate(m, MTAG_CODEL, 0, NULL);
290*d0b0424fSKristof Provost 		if (mtag == NULL) {
2910a70aaf8SLuiz Otavio O Souza 			mtag = m_tag_alloc(MTAG_CODEL, 0, sizeof(uint64_t),
2920a70aaf8SLuiz Otavio O Souza 			    M_NOWAIT);
293*d0b0424fSKristof Provost 			if (mtag != NULL)
294*d0b0424fSKristof Provost 				m_tag_prepend(m, mtag);
295*d0b0424fSKristof Provost 		}
2960a70aaf8SLuiz Otavio O Souza 		if (mtag == NULL) {
2970a70aaf8SLuiz Otavio O Souza 			m_freem(m);
2980a70aaf8SLuiz Otavio O Souza 			return (-1);
2990a70aaf8SLuiz Otavio O Souza 		}
3000a70aaf8SLuiz Otavio O Souza 		enqueue_time = (uint64_t *)(mtag + 1);
3010a70aaf8SLuiz Otavio O Souza 		*enqueue_time = read_machclk();
3020a70aaf8SLuiz Otavio O Souza 		_addq(q, m);
3030a70aaf8SLuiz Otavio O Souza 		return (0);
3040a70aaf8SLuiz Otavio O Souza 	}
3050a70aaf8SLuiz Otavio O Souza 	c->drop_overlimit++;
3060a70aaf8SLuiz Otavio O Souza 	m_freem(m);
3070a70aaf8SLuiz Otavio O Souza 
3080a70aaf8SLuiz Otavio O Souza 	return (-1);
3090a70aaf8SLuiz Otavio O Souza }
3100a70aaf8SLuiz Otavio O Souza 
3110a70aaf8SLuiz Otavio O Souza static int
codel_should_drop(struct codel * c,class_queue_t * q,struct mbuf * m,u_int64_t now)3120a70aaf8SLuiz Otavio O Souza codel_should_drop(struct codel *c, class_queue_t *q, struct mbuf *m,
3130a70aaf8SLuiz Otavio O Souza     u_int64_t now)
3140a70aaf8SLuiz Otavio O Souza {
3150a70aaf8SLuiz Otavio O Souza 	struct m_tag *mtag;
3160a70aaf8SLuiz Otavio O Souza 	uint64_t *enqueue_time;
3170a70aaf8SLuiz Otavio O Souza 
3180a70aaf8SLuiz Otavio O Souza 	if (m == NULL) {
3190a70aaf8SLuiz Otavio O Souza 		c->vars.first_above_time = 0;
3200a70aaf8SLuiz Otavio O Souza 		return (0);
3210a70aaf8SLuiz Otavio O Souza 	}
3220a70aaf8SLuiz Otavio O Souza 
3230a70aaf8SLuiz Otavio O Souza 	mtag = m_tag_locate(m, MTAG_CODEL, 0, NULL);
3240a70aaf8SLuiz Otavio O Souza 	if (mtag == NULL) {
3250a70aaf8SLuiz Otavio O Souza 		/* Only one warning per second. */
3260a70aaf8SLuiz Otavio O Souza 		if (ppsratecheck(&c->last_log, &c->last_pps, 1))
3270a70aaf8SLuiz Otavio O Souza 			printf("%s: could not found the packet mtag!\n",
3280a70aaf8SLuiz Otavio O Souza 			    __func__);
3290a70aaf8SLuiz Otavio O Souza 		c->vars.first_above_time = 0;
3300a70aaf8SLuiz Otavio O Souza 		return (0);
3310a70aaf8SLuiz Otavio O Souza 	}
3320a70aaf8SLuiz Otavio O Souza 	enqueue_time = (uint64_t *)(mtag + 1);
3330a70aaf8SLuiz Otavio O Souza 	c->vars.ldelay = now - *enqueue_time;
3340a70aaf8SLuiz Otavio O Souza 	c->stats.maxpacket = MAX(c->stats.maxpacket, m_pktlen(m));
3350a70aaf8SLuiz Otavio O Souza 
3360a70aaf8SLuiz Otavio O Souza 	if (codel_time_before(c->vars.ldelay, c->params.target) ||
3370a70aaf8SLuiz Otavio O Souza 	    qsize(q) <= c->stats.maxpacket) {
3380a70aaf8SLuiz Otavio O Souza 		/* went below - stay below for at least interval */
3390a70aaf8SLuiz Otavio O Souza 		c->vars.first_above_time = 0;
3400a70aaf8SLuiz Otavio O Souza 		return (0);
3410a70aaf8SLuiz Otavio O Souza 	}
3420a70aaf8SLuiz Otavio O Souza 	if (c->vars.first_above_time == 0) {
3430a70aaf8SLuiz Otavio O Souza 		/* just went above from below. If we stay above
3440a70aaf8SLuiz Otavio O Souza 		 * for at least interval we'll say it's ok to drop
3450a70aaf8SLuiz Otavio O Souza 		 */
3460a70aaf8SLuiz Otavio O Souza 		c->vars.first_above_time = now + c->params.interval;
3470a70aaf8SLuiz Otavio O Souza 		return (0);
3480a70aaf8SLuiz Otavio O Souza 	}
3490a70aaf8SLuiz Otavio O Souza 	if (codel_time_after(now, c->vars.first_above_time))
3500a70aaf8SLuiz Otavio O Souza 		return (1);
3510a70aaf8SLuiz Otavio O Souza 
3520a70aaf8SLuiz Otavio O Souza 	return (0);
3530a70aaf8SLuiz Otavio O Souza }
3540a70aaf8SLuiz Otavio O Souza 
3550a70aaf8SLuiz Otavio O Souza /*
3560a70aaf8SLuiz Otavio O Souza  * Run a Newton method step:
3570a70aaf8SLuiz Otavio O Souza  * new_invsqrt = (invsqrt / 2) * (3 - count * invsqrt^2)
3580a70aaf8SLuiz Otavio O Souza  *
3590a70aaf8SLuiz Otavio O Souza  * Here, invsqrt is a fixed point number (< 1.0), 32bit mantissa, aka Q0.32
3600a70aaf8SLuiz Otavio O Souza  */
3610a70aaf8SLuiz Otavio O Souza static void
codel_Newton_step(struct codel_vars * vars)3620a70aaf8SLuiz Otavio O Souza codel_Newton_step(struct codel_vars *vars)
3630a70aaf8SLuiz Otavio O Souza {
3640a70aaf8SLuiz Otavio O Souza 	uint32_t invsqrt, invsqrt2;
3650a70aaf8SLuiz Otavio O Souza 	uint64_t val;
3660a70aaf8SLuiz Otavio O Souza 
3670a70aaf8SLuiz Otavio O Souza /* sizeof_in_bits(rec_inv_sqrt) */
3680a70aaf8SLuiz Otavio O Souza #define	REC_INV_SQRT_BITS (8 * sizeof(u_int16_t))
3690a70aaf8SLuiz Otavio O Souza /* needed shift to get a Q0.32 number from rec_inv_sqrt */
3700a70aaf8SLuiz Otavio O Souza #define	REC_INV_SQRT_SHIFT (32 - REC_INV_SQRT_BITS)
3710a70aaf8SLuiz Otavio O Souza 
3720a70aaf8SLuiz Otavio O Souza 	invsqrt = ((u_int32_t)vars->rec_inv_sqrt) << REC_INV_SQRT_SHIFT;
3730a70aaf8SLuiz Otavio O Souza 	invsqrt2 = ((u_int64_t)invsqrt * invsqrt) >> 32;
3740a70aaf8SLuiz Otavio O Souza 	val = (3LL << 32) - ((u_int64_t)vars->count * invsqrt2);
3750a70aaf8SLuiz Otavio O Souza 	val >>= 2; /* avoid overflow in following multiply */
3760a70aaf8SLuiz Otavio O Souza 	val = (val * invsqrt) >> (32 - 2 + 1);
3770a70aaf8SLuiz Otavio O Souza 
3780a70aaf8SLuiz Otavio O Souza 	vars->rec_inv_sqrt = val >> REC_INV_SQRT_SHIFT;
3790a70aaf8SLuiz Otavio O Souza }
3800a70aaf8SLuiz Otavio O Souza 
3810a70aaf8SLuiz Otavio O Souza static u_int64_t
codel_control_law(u_int64_t t,u_int64_t interval,u_int32_t rec_inv_sqrt)3820a70aaf8SLuiz Otavio O Souza codel_control_law(u_int64_t t, u_int64_t interval, u_int32_t rec_inv_sqrt)
3830a70aaf8SLuiz Otavio O Souza {
3840a70aaf8SLuiz Otavio O Souza 
3850a70aaf8SLuiz Otavio O Souza 	return (t + (u_int32_t)(((u_int64_t)interval *
3860a70aaf8SLuiz Otavio O Souza 	    (rec_inv_sqrt << REC_INV_SQRT_SHIFT)) >> 32));
3870a70aaf8SLuiz Otavio O Souza }
3880a70aaf8SLuiz Otavio O Souza 
3890a70aaf8SLuiz Otavio O Souza struct mbuf *
codel_getq(struct codel * c,class_queue_t * q)3900a70aaf8SLuiz Otavio O Souza codel_getq(struct codel *c, class_queue_t *q)
3910a70aaf8SLuiz Otavio O Souza {
3920a70aaf8SLuiz Otavio O Souza 	struct mbuf	*m;
3930a70aaf8SLuiz Otavio O Souza 	u_int64_t	 now;
3940a70aaf8SLuiz Otavio O Souza 	int		 drop;
3950a70aaf8SLuiz Otavio O Souza 
3960a70aaf8SLuiz Otavio O Souza 	if ((m = _getq(q)) == NULL) {
3970a70aaf8SLuiz Otavio O Souza 		c->vars.dropping = 0;
3980a70aaf8SLuiz Otavio O Souza 		return (m);
3990a70aaf8SLuiz Otavio O Souza 	}
4000a70aaf8SLuiz Otavio O Souza 
4010a70aaf8SLuiz Otavio O Souza 	now = read_machclk();
4020a70aaf8SLuiz Otavio O Souza 	drop = codel_should_drop(c, q, m, now);
4030a70aaf8SLuiz Otavio O Souza 	if (c->vars.dropping) {
4040a70aaf8SLuiz Otavio O Souza 		if (!drop) {
4050a70aaf8SLuiz Otavio O Souza 			/* sojourn time below target - leave dropping state */
4060a70aaf8SLuiz Otavio O Souza 			c->vars.dropping = 0;
4070a70aaf8SLuiz Otavio O Souza 		} else if (codel_time_after_eq(now, c->vars.drop_next)) {
4080a70aaf8SLuiz Otavio O Souza 			/* It's time for the next drop. Drop the current
4090a70aaf8SLuiz Otavio O Souza 			 * packet and dequeue the next. The dequeue might
4100a70aaf8SLuiz Otavio O Souza 			 * take us out of dropping state.
4110a70aaf8SLuiz Otavio O Souza 			 * If not, schedule the next drop.
4120a70aaf8SLuiz Otavio O Souza 			 * A large backlog might result in drop rates so high
4130a70aaf8SLuiz Otavio O Souza 			 * that the next drop should happen now,
4140a70aaf8SLuiz Otavio O Souza 			 * hence the while loop.
4150a70aaf8SLuiz Otavio O Souza 			 */
4160a70aaf8SLuiz Otavio O Souza 			while (c->vars.dropping &&
4170a70aaf8SLuiz Otavio O Souza 			    codel_time_after_eq(now, c->vars.drop_next)) {
4180a70aaf8SLuiz Otavio O Souza 				c->vars.count++; /* don't care of possible wrap
4190a70aaf8SLuiz Otavio O Souza 						  * since there is no more
4200a70aaf8SLuiz Otavio O Souza 						  * divide */
4210a70aaf8SLuiz Otavio O Souza 				codel_Newton_step(&c->vars);
4220a70aaf8SLuiz Otavio O Souza 				/* TODO ECN */
4230a70aaf8SLuiz Otavio O Souza 				PKTCNTR_ADD(&c->stats.drop_cnt, m_pktlen(m));
4240a70aaf8SLuiz Otavio O Souza 				m_freem(m);
4250a70aaf8SLuiz Otavio O Souza 				m = _getq(q);
4260a70aaf8SLuiz Otavio O Souza 				if (!codel_should_drop(c, q, m, now))
4270a70aaf8SLuiz Otavio O Souza 					/* leave dropping state */
4280a70aaf8SLuiz Otavio O Souza 					c->vars.dropping = 0;
4290a70aaf8SLuiz Otavio O Souza 				else
4300a70aaf8SLuiz Otavio O Souza 					/* and schedule the next drop */
4310a70aaf8SLuiz Otavio O Souza 					c->vars.drop_next =
4320a70aaf8SLuiz Otavio O Souza 					    codel_control_law(c->vars.drop_next,
4330a70aaf8SLuiz Otavio O Souza 						c->params.interval,
4340a70aaf8SLuiz Otavio O Souza 						c->vars.rec_inv_sqrt);
4350a70aaf8SLuiz Otavio O Souza 			}
4360a70aaf8SLuiz Otavio O Souza 		}
4370a70aaf8SLuiz Otavio O Souza 	} else if (drop) {
4380a70aaf8SLuiz Otavio O Souza 		/* TODO ECN */
4390a70aaf8SLuiz Otavio O Souza 		PKTCNTR_ADD(&c->stats.drop_cnt, m_pktlen(m));
4400a70aaf8SLuiz Otavio O Souza 		m_freem(m);
4410a70aaf8SLuiz Otavio O Souza 
4420a70aaf8SLuiz Otavio O Souza 		m = _getq(q);
4430a70aaf8SLuiz Otavio O Souza 		drop = codel_should_drop(c, q, m, now);
4440a70aaf8SLuiz Otavio O Souza 
4450a70aaf8SLuiz Otavio O Souza 		c->vars.dropping = 1;
4460a70aaf8SLuiz Otavio O Souza 		/* if min went above target close to when we last went below it
4470a70aaf8SLuiz Otavio O Souza 		 * assume that the drop rate that controlled the queue on the
4480a70aaf8SLuiz Otavio O Souza 		 * last cycle is a good starting point to control it now.
4490a70aaf8SLuiz Otavio O Souza 		 */
4500a70aaf8SLuiz Otavio O Souza 		if (codel_time_before(now - c->vars.drop_next,
4510a70aaf8SLuiz Otavio O Souza 		    16 * c->params.interval)) {
4520a70aaf8SLuiz Otavio O Souza 			c->vars.count = (c->vars.count - c->vars.lastcount) | 1;
4530a70aaf8SLuiz Otavio O Souza 			/* we dont care if rec_inv_sqrt approximation
4540a70aaf8SLuiz Otavio O Souza 			 * is not very precise :
4550a70aaf8SLuiz Otavio O Souza 			 * Next Newton steps will correct it quadratically.
4560a70aaf8SLuiz Otavio O Souza 			 */
4570a70aaf8SLuiz Otavio O Souza 			codel_Newton_step(&c->vars);
4580a70aaf8SLuiz Otavio O Souza 		} else {
4590a70aaf8SLuiz Otavio O Souza 			c->vars.count = 1;
4600a70aaf8SLuiz Otavio O Souza 			c->vars.rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
4610a70aaf8SLuiz Otavio O Souza 		}
4620a70aaf8SLuiz Otavio O Souza 		c->vars.lastcount = c->vars.count;
4630a70aaf8SLuiz Otavio O Souza 		c->vars.drop_next = codel_control_law(now, c->params.interval,
4640a70aaf8SLuiz Otavio O Souza 		    c->vars.rec_inv_sqrt);
4650a70aaf8SLuiz Otavio O Souza 	}
4660a70aaf8SLuiz Otavio O Souza 
4670a70aaf8SLuiz Otavio O Souza 	return (m);
4680a70aaf8SLuiz Otavio O Souza }
4690a70aaf8SLuiz Otavio O Souza 
4700a70aaf8SLuiz Otavio O Souza void
codel_getstats(struct codel * c,struct codel_stats * s)4710a70aaf8SLuiz Otavio O Souza codel_getstats(struct codel *c, struct codel_stats *s)
4720a70aaf8SLuiz Otavio O Souza {
4730a70aaf8SLuiz Otavio O Souza 	*s = c->stats;
4740a70aaf8SLuiz Otavio O Souza }
4750a70aaf8SLuiz Otavio O Souza 
4760a70aaf8SLuiz Otavio O Souza #endif /* ALTQ_CODEL */
477