xref: /freebsd/sys/net/altq/altq_codel.c (revision 0a70aaf8f5d93454d0940a09b94deecd7aa5fa0d)
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