xref: /freebsd/sys/net/altq/altq_fairq.c (revision 2ff63af9b88c7413b7d71715b5532625752a248e)
1a5b789f6SErmal Luçi /*
2a5b789f6SErmal Luçi  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3a5b789f6SErmal Luçi  *
4a5b789f6SErmal Luçi  * This code is derived from software contributed to The DragonFly Project
5a5b789f6SErmal Luçi  * by Matthew Dillon <dillon@backplane.com>
6a5b789f6SErmal Luçi  *
7a5b789f6SErmal Luçi  * Redistribution and use in source and binary forms, with or without
8a5b789f6SErmal Luçi  * modification, are permitted provided that the following conditions
9a5b789f6SErmal Luçi  * are met:
10a5b789f6SErmal Luçi  *
11a5b789f6SErmal Luçi  * 1. Redistributions of source code must retain the above copyright
12a5b789f6SErmal Luçi  *    notice, this list of conditions and the following disclaimer.
13a5b789f6SErmal Luçi  * 2. Redistributions in binary form must reproduce the above copyright
14a5b789f6SErmal Luçi  *    notice, this list of conditions and the following disclaimer in
15a5b789f6SErmal Luçi  *    the documentation and/or other materials provided with the
16a5b789f6SErmal Luçi  *    distribution.
17a5b789f6SErmal Luçi  * 3. Neither the name of The DragonFly Project nor the names of its
18a5b789f6SErmal Luçi  *    contributors may be used to endorse or promote products derived
19a5b789f6SErmal Luçi  *    from this software without specific, prior written permission.
20a5b789f6SErmal Luçi  *
21a5b789f6SErmal Luçi  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22a5b789f6SErmal Luçi  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23a5b789f6SErmal Luçi  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24a5b789f6SErmal Luçi  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25a5b789f6SErmal Luçi  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26a5b789f6SErmal Luçi  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27a5b789f6SErmal Luçi  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28a5b789f6SErmal Luçi  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29a5b789f6SErmal Luçi  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30a5b789f6SErmal Luçi  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31a5b789f6SErmal Luçi  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32a5b789f6SErmal Luçi  * SUCH DAMAGE.
33a5b789f6SErmal Luçi  *
34a5b789f6SErmal Luçi  * $DragonFly: src/sys/net/altq/altq_fairq.c,v 1.1 2008/04/06 18:58:15 dillon Exp $
35a5b789f6SErmal Luçi  */
36a5b789f6SErmal Luçi /*
37a5b789f6SErmal Luçi  * Matt: I gutted altq_priq.c and used it as a skeleton on which to build
38a5b789f6SErmal Luçi  * fairq.  The fairq algorithm is completely different then priq, of course,
39a5b789f6SErmal Luçi  * but because I used priq's skeleton I believe I should include priq's
40a5b789f6SErmal Luçi  * copyright.
41a5b789f6SErmal Luçi  *
42a5b789f6SErmal Luçi  * Copyright (C) 2000-2003
43a5b789f6SErmal Luçi  *	Sony Computer Science Laboratories Inc.  All rights reserved.
44a5b789f6SErmal Luçi  *
45a5b789f6SErmal Luçi  * Redistribution and use in source and binary forms, with or without
46a5b789f6SErmal Luçi  * modification, are permitted provided that the following conditions
47a5b789f6SErmal Luçi  * are met:
48a5b789f6SErmal Luçi  * 1. Redistributions of source code must retain the above copyright
49a5b789f6SErmal Luçi  *    notice, this list of conditions and the following disclaimer.
50a5b789f6SErmal Luçi  * 2. Redistributions in binary form must reproduce the above copyright
51a5b789f6SErmal Luçi  *    notice, this list of conditions and the following disclaimer in the
52a5b789f6SErmal Luçi  *    documentation and/or other materials provided with the distribution.
53a5b789f6SErmal Luçi  *
54a5b789f6SErmal Luçi  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
55a5b789f6SErmal Luçi  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56a5b789f6SErmal Luçi  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57a5b789f6SErmal Luçi  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
58a5b789f6SErmal Luçi  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59a5b789f6SErmal Luçi  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60a5b789f6SErmal Luçi  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61a5b789f6SErmal Luçi  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62a5b789f6SErmal Luçi  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63a5b789f6SErmal Luçi  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64a5b789f6SErmal Luçi  * SUCH DAMAGE.
65a5b789f6SErmal Luçi  */
66a5b789f6SErmal Luçi 
67a5b789f6SErmal Luçi /*
68a5b789f6SErmal Luçi  * FAIRQ - take traffic classified by keep state (hashed into
69a5b789f6SErmal Luçi  * mbuf->m_pkthdr.altq_state_hash) and bucketize it.  Fairly extract
70a5b789f6SErmal Luçi  * the first packet from each bucket in a round-robin fashion.
71a5b789f6SErmal Luçi  *
72a5b789f6SErmal Luçi  * TODO - better overall qlimit support (right now it is per-bucket).
73a5b789f6SErmal Luçi  *	- NOTE: red etc is per bucket, not overall.
74a5b789f6SErmal Luçi  *	- better service curve support.
75a5b789f6SErmal Luçi  *
76a5b789f6SErmal Luçi  * EXAMPLE:
77a5b789f6SErmal Luçi  *
78a5b789f6SErmal Luçi  *  altq on em0 fairq bandwidth 650Kb queue { std, bulk }
79a5b789f6SErmal Luçi  *  queue std  priority 3 bandwidth 400Kb \
80a5b789f6SErmal Luçi  *	fairq (buckets 64, default, hogs 1Kb) qlimit 50
81a5b789f6SErmal Luçi  *  queue bulk priority 2 bandwidth 100Kb \
82a5b789f6SErmal Luçi  *	fairq (buckets 64, hogs 1Kb) qlimit 50
83a5b789f6SErmal Luçi  *
84a5b789f6SErmal Luçi  *  pass out on em0 from any to any keep state queue std
85a5b789f6SErmal Luçi  *  pass out on em0 inet proto tcp ..... port ... keep state queue bulk
86a5b789f6SErmal Luçi  */
87a5b789f6SErmal Luçi #include "opt_altq.h"
88a5b789f6SErmal Luçi #include "opt_inet.h"
89a5b789f6SErmal Luçi #include "opt_inet6.h"
90a5b789f6SErmal Luçi 
91a5b789f6SErmal Luçi #ifdef ALTQ_FAIRQ  /* fairq is enabled in the kernel conf */
92a5b789f6SErmal Luçi 
93a5b789f6SErmal Luçi #include <sys/param.h>
94a5b789f6SErmal Luçi #include <sys/malloc.h>
95a5b789f6SErmal Luçi #include <sys/mbuf.h>
96a5b789f6SErmal Luçi #include <sys/socket.h>
97a5b789f6SErmal Luçi #include <sys/sockio.h>
98a5b789f6SErmal Luçi #include <sys/systm.h>
99a5b789f6SErmal Luçi #include <sys/proc.h>
100a5b789f6SErmal Luçi #include <sys/errno.h>
101a5b789f6SErmal Luçi #include <sys/kernel.h>
102a5b789f6SErmal Luçi #include <sys/queue.h>
103a5b789f6SErmal Luçi 
104a5b789f6SErmal Luçi #include <net/if.h>
105cd2bc2efSErmal Luçi #include <net/if_var.h>
106*2c2b37adSJustin Hibbits #include <net/if_private.h>
107a5b789f6SErmal Luçi #include <netinet/in.h>
108a5b789f6SErmal Luçi 
109cd2bc2efSErmal Luçi #include <netpfil/pf/pf.h>
110cd2bc2efSErmal Luçi #include <netpfil/pf/pf_altq.h>
111cd2bc2efSErmal Luçi #include <netpfil/pf/pf_mtag.h>
112a5b789f6SErmal Luçi #include <net/altq/altq.h>
113a5b789f6SErmal Luçi #include <net/altq/altq_fairq.h>
114a5b789f6SErmal Luçi 
115a5b789f6SErmal Luçi /*
116a5b789f6SErmal Luçi  * function prototypes
117a5b789f6SErmal Luçi  */
118a5b789f6SErmal Luçi static int	fairq_clear_interface(struct fairq_if *);
119a5b789f6SErmal Luçi static int	fairq_request(struct ifaltq *, int, void *);
120a5b789f6SErmal Luçi static void	fairq_purge(struct fairq_if *);
121a5b789f6SErmal Luçi static struct fairq_class *fairq_class_create(struct fairq_if *, int, int, u_int, struct fairq_opts *, int);
122a5b789f6SErmal Luçi static int	fairq_class_destroy(struct fairq_class *);
123a5b789f6SErmal Luçi static int	fairq_enqueue(struct ifaltq *, struct mbuf *, struct altq_pktattr *);
124a5b789f6SErmal Luçi static struct mbuf *fairq_dequeue(struct ifaltq *, int);
125a5b789f6SErmal Luçi 
126a5b789f6SErmal Luçi static int	fairq_addq(struct fairq_class *, struct mbuf *, u_int32_t);
127a5b789f6SErmal Luçi static struct mbuf *fairq_getq(struct fairq_class *, uint64_t);
128a5b789f6SErmal Luçi static struct mbuf *fairq_pollq(struct fairq_class *, uint64_t, int *);
129a5b789f6SErmal Luçi static fairq_bucket_t *fairq_selectq(struct fairq_class *, int);
130a5b789f6SErmal Luçi static void	fairq_purgeq(struct fairq_class *);
131a5b789f6SErmal Luçi 
132a5b789f6SErmal Luçi static void	get_class_stats(struct fairq_classstats *, struct fairq_class *);
133a5b789f6SErmal Luçi static struct fairq_class *clh_to_clp(struct fairq_if *, uint32_t);
134a5b789f6SErmal Luçi 
135a5b789f6SErmal Luçi int
fairq_pfattach(struct pf_altq * a)136a5b789f6SErmal Luçi fairq_pfattach(struct pf_altq *a)
137a5b789f6SErmal Luçi {
138a5b789f6SErmal Luçi 	struct ifnet *ifp;
139a5b789f6SErmal Luçi 	int error;
140a5b789f6SErmal Luçi 
141a5b789f6SErmal Luçi 	if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
142a5b789f6SErmal Luçi 		return (EINVAL);
143a5b789f6SErmal Luçi 
144a5b789f6SErmal Luçi 	error = altq_attach(&ifp->if_snd, ALTQT_FAIRQ, a->altq_disc,
14527b2aa49SKristof Provost 	    fairq_enqueue, fairq_dequeue, fairq_request);
146a5b789f6SErmal Luçi 
147a5b789f6SErmal Luçi 	return (error);
148a5b789f6SErmal Luçi }
149a5b789f6SErmal Luçi 
150a5b789f6SErmal Luçi int
fairq_add_altq(struct ifnet * ifp,struct pf_altq * a)1518f2ac656SPatrick Kelsey fairq_add_altq(struct ifnet *ifp, struct pf_altq *a)
152a5b789f6SErmal Luçi {
153a5b789f6SErmal Luçi 	struct fairq_if *pif;
154a5b789f6SErmal Luçi 
1558f2ac656SPatrick Kelsey 	if (ifp == NULL)
156a5b789f6SErmal Luçi 		return (EINVAL);
157a5b789f6SErmal Luçi 	if (!ALTQ_IS_READY(&ifp->if_snd))
158a5b789f6SErmal Luçi 		return (ENODEV);
159a5b789f6SErmal Luçi 
160a5b789f6SErmal Luçi 	pif = malloc(sizeof(struct fairq_if),
161a5b789f6SErmal Luçi 			M_DEVBUF, M_WAITOK | M_ZERO);
162a5b789f6SErmal Luçi 	pif->pif_bandwidth = a->ifbandwidth;
163a5b789f6SErmal Luçi 	pif->pif_maxpri = -1;
164a5b789f6SErmal Luçi 	pif->pif_ifq = &ifp->if_snd;
165a5b789f6SErmal Luçi 
166a5b789f6SErmal Luçi 	/* keep the state in pf_altq */
167a5b789f6SErmal Luçi 	a->altq_disc = pif;
168a5b789f6SErmal Luçi 
169a5b789f6SErmal Luçi 	return (0);
170a5b789f6SErmal Luçi }
171a5b789f6SErmal Luçi 
172a5b789f6SErmal Luçi int
fairq_remove_altq(struct pf_altq * a)173a5b789f6SErmal Luçi fairq_remove_altq(struct pf_altq *a)
174a5b789f6SErmal Luçi {
175a5b789f6SErmal Luçi 	struct fairq_if *pif;
176a5b789f6SErmal Luçi 
177a5b789f6SErmal Luçi 	if ((pif = a->altq_disc) == NULL)
178a5b789f6SErmal Luçi 		return (EINVAL);
179a5b789f6SErmal Luçi 	a->altq_disc = NULL;
180a5b789f6SErmal Luçi 
181a5b789f6SErmal Luçi 	fairq_clear_interface(pif);
182a5b789f6SErmal Luçi 
183a5b789f6SErmal Luçi 	free(pif, M_DEVBUF);
184a5b789f6SErmal Luçi 	return (0);
185a5b789f6SErmal Luçi }
186a5b789f6SErmal Luçi 
187a5b789f6SErmal Luçi int
fairq_add_queue(struct pf_altq * a)188a5b789f6SErmal Luçi fairq_add_queue(struct pf_altq *a)
189a5b789f6SErmal Luçi {
190a5b789f6SErmal Luçi 	struct fairq_if *pif;
191a5b789f6SErmal Luçi 	struct fairq_class *cl;
192a5b789f6SErmal Luçi 
193a5b789f6SErmal Luçi 	if ((pif = a->altq_disc) == NULL)
194a5b789f6SErmal Luçi 		return (EINVAL);
195a5b789f6SErmal Luçi 
196a5b789f6SErmal Luçi 	/* check parameters */
197a5b789f6SErmal Luçi 	if (a->priority >= FAIRQ_MAXPRI)
198a5b789f6SErmal Luçi 		return (EINVAL);
199a5b789f6SErmal Luçi 	if (a->qid == 0)
200a5b789f6SErmal Luçi 		return (EINVAL);
201a5b789f6SErmal Luçi 	if (pif->pif_classes[a->priority] != NULL)
202a5b789f6SErmal Luçi 		return (EBUSY);
203a5b789f6SErmal Luçi 	if (clh_to_clp(pif, a->qid) != NULL)
204a5b789f6SErmal Luçi 		return (EBUSY);
205a5b789f6SErmal Luçi 
206a5b789f6SErmal Luçi 	cl = fairq_class_create(pif, a->priority, a->qlimit, a->bandwidth,
207a5b789f6SErmal Luçi 			       &a->pq_u.fairq_opts, a->qid);
208a5b789f6SErmal Luçi 	if (cl == NULL)
209a5b789f6SErmal Luçi 		return (ENOMEM);
210a5b789f6SErmal Luçi 
211a5b789f6SErmal Luçi 	return (0);
212a5b789f6SErmal Luçi }
213a5b789f6SErmal Luçi 
214a5b789f6SErmal Luçi int
fairq_remove_queue(struct pf_altq * a)215a5b789f6SErmal Luçi fairq_remove_queue(struct pf_altq *a)
216a5b789f6SErmal Luçi {
217a5b789f6SErmal Luçi 	struct fairq_if *pif;
218a5b789f6SErmal Luçi 	struct fairq_class *cl;
219a5b789f6SErmal Luçi 
220a5b789f6SErmal Luçi 	if ((pif = a->altq_disc) == NULL)
221a5b789f6SErmal Luçi 		return (EINVAL);
222a5b789f6SErmal Luçi 
223a5b789f6SErmal Luçi 	if ((cl = clh_to_clp(pif, a->qid)) == NULL)
224a5b789f6SErmal Luçi 		return (EINVAL);
225a5b789f6SErmal Luçi 
226a5b789f6SErmal Luçi 	return (fairq_class_destroy(cl));
227a5b789f6SErmal Luçi }
228a5b789f6SErmal Luçi 
229a5b789f6SErmal Luçi int
fairq_getqstats(struct pf_altq * a,void * ubuf,int * nbytes,int version)230249cc75fSPatrick Kelsey fairq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version)
231a5b789f6SErmal Luçi {
232a5b789f6SErmal Luçi 	struct fairq_if *pif;
233a5b789f6SErmal Luçi 	struct fairq_class *cl;
234a5b789f6SErmal Luçi 	struct fairq_classstats stats;
235a5b789f6SErmal Luçi 	int error = 0;
236a5b789f6SErmal Luçi 
237a5b789f6SErmal Luçi 	if ((pif = altq_lookup(a->ifname, ALTQT_FAIRQ)) == NULL)
238a5b789f6SErmal Luçi 		return (EBADF);
239a5b789f6SErmal Luçi 
240a5b789f6SErmal Luçi 	if ((cl = clh_to_clp(pif, a->qid)) == NULL)
241a5b789f6SErmal Luçi 		return (EINVAL);
242a5b789f6SErmal Luçi 
243a5b789f6SErmal Luçi 	if (*nbytes < sizeof(stats))
244a5b789f6SErmal Luçi 		return (EINVAL);
245a5b789f6SErmal Luçi 
246a5b789f6SErmal Luçi 	get_class_stats(&stats, cl);
247a5b789f6SErmal Luçi 
248a5b789f6SErmal Luçi 	if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
249a5b789f6SErmal Luçi 		return (error);
250a5b789f6SErmal Luçi 	*nbytes = sizeof(stats);
251a5b789f6SErmal Luçi 	return (0);
252a5b789f6SErmal Luçi }
253a5b789f6SErmal Luçi 
254a5b789f6SErmal Luçi /*
255a5b789f6SErmal Luçi  * bring the interface back to the initial state by discarding
256a5b789f6SErmal Luçi  * all the filters and classes.
257a5b789f6SErmal Luçi  */
258a5b789f6SErmal Luçi static int
fairq_clear_interface(struct fairq_if * pif)259a5b789f6SErmal Luçi fairq_clear_interface(struct fairq_if *pif)
260a5b789f6SErmal Luçi {
261a5b789f6SErmal Luçi 	struct fairq_class *cl;
262a5b789f6SErmal Luçi 	int pri;
263a5b789f6SErmal Luçi 
264a5b789f6SErmal Luçi 	/* clear out the classes */
265a5b789f6SErmal Luçi 	for (pri = 0; pri <= pif->pif_maxpri; pri++) {
266a5b789f6SErmal Luçi 		if ((cl = pif->pif_classes[pri]) != NULL)
267a5b789f6SErmal Luçi 			fairq_class_destroy(cl);
268a5b789f6SErmal Luçi 	}
269a5b789f6SErmal Luçi 
270a5b789f6SErmal Luçi 	return (0);
271a5b789f6SErmal Luçi }
272a5b789f6SErmal Luçi 
273a5b789f6SErmal Luçi static int
fairq_request(struct ifaltq * ifq,int req,void * arg)274a5b789f6SErmal Luçi fairq_request(struct ifaltq *ifq, int req, void *arg)
275a5b789f6SErmal Luçi {
276a5b789f6SErmal Luçi 	struct fairq_if *pif = (struct fairq_if *)ifq->altq_disc;
277a5b789f6SErmal Luçi 
278a5b789f6SErmal Luçi 	IFQ_LOCK_ASSERT(ifq);
279a5b789f6SErmal Luçi 
280a5b789f6SErmal Luçi 	switch (req) {
281a5b789f6SErmal Luçi 	case ALTRQ_PURGE:
282a5b789f6SErmal Luçi 		fairq_purge(pif);
283a5b789f6SErmal Luçi 		break;
284a5b789f6SErmal Luçi 	}
285a5b789f6SErmal Luçi 	return (0);
286a5b789f6SErmal Luçi }
287a5b789f6SErmal Luçi 
288a5b789f6SErmal Luçi /* discard all the queued packets on the interface */
289a5b789f6SErmal Luçi static void
fairq_purge(struct fairq_if * pif)290a5b789f6SErmal Luçi fairq_purge(struct fairq_if *pif)
291a5b789f6SErmal Luçi {
292a5b789f6SErmal Luçi 	struct fairq_class *cl;
293a5b789f6SErmal Luçi 	int pri;
294a5b789f6SErmal Luçi 
295a5b789f6SErmal Luçi 	for (pri = 0; pri <= pif->pif_maxpri; pri++) {
296a5b789f6SErmal Luçi 		if ((cl = pif->pif_classes[pri]) != NULL && cl->cl_head)
297a5b789f6SErmal Luçi 			fairq_purgeq(cl);
298a5b789f6SErmal Luçi 	}
299a5b789f6SErmal Luçi 	if (ALTQ_IS_ENABLED(pif->pif_ifq))
300a5b789f6SErmal Luçi 		pif->pif_ifq->ifq_len = 0;
301a5b789f6SErmal Luçi }
302a5b789f6SErmal Luçi 
303a5b789f6SErmal Luçi static struct fairq_class *
fairq_class_create(struct fairq_if * pif,int pri,int qlimit,u_int bandwidth,struct fairq_opts * opts,int qid)304a5b789f6SErmal Luçi fairq_class_create(struct fairq_if *pif, int pri, int qlimit,
305a5b789f6SErmal Luçi 		   u_int bandwidth, struct fairq_opts *opts, int qid)
306a5b789f6SErmal Luçi {
307a5b789f6SErmal Luçi 	struct fairq_class *cl;
308a5b789f6SErmal Luçi 	int flags = opts->flags;
309a5b789f6SErmal Luçi 	u_int nbuckets = opts->nbuckets;
310a5b789f6SErmal Luçi 	int i;
311a5b789f6SErmal Luçi 
312a5b789f6SErmal Luçi #ifndef ALTQ_RED
313a5b789f6SErmal Luçi 	if (flags & FARF_RED) {
314a5b789f6SErmal Luçi #ifdef ALTQ_DEBUG
315a5b789f6SErmal Luçi 		printf("fairq_class_create: RED not configured for FAIRQ!\n");
316a5b789f6SErmal Luçi #endif
317a5b789f6SErmal Luçi 		return (NULL);
318a5b789f6SErmal Luçi 	}
319a5b789f6SErmal Luçi #endif
3200a70aaf8SLuiz Otavio O Souza #ifndef ALTQ_CODEL
3210a70aaf8SLuiz Otavio O Souza 	if (flags & FARF_CODEL) {
3220a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_DEBUG
3230a70aaf8SLuiz Otavio O Souza 		printf("fairq_class_create: CODEL not configured for FAIRQ!\n");
3240a70aaf8SLuiz Otavio O Souza #endif
3250a70aaf8SLuiz Otavio O Souza 		return (NULL);
3260a70aaf8SLuiz Otavio O Souza 	}
3270a70aaf8SLuiz Otavio O Souza #endif
328a5b789f6SErmal Luçi 	if (nbuckets == 0)
329a5b789f6SErmal Luçi 		nbuckets = 256;
330a5b789f6SErmal Luçi 	if (nbuckets > FAIRQ_MAX_BUCKETS)
331a5b789f6SErmal Luçi 		nbuckets = FAIRQ_MAX_BUCKETS;
332a5b789f6SErmal Luçi 	/* enforce power-of-2 size */
333a5b789f6SErmal Luçi 	while ((nbuckets ^ (nbuckets - 1)) != ((nbuckets << 1) - 1))
334a5b789f6SErmal Luçi 		++nbuckets;
335a5b789f6SErmal Luçi 
336a5b789f6SErmal Luçi 	if ((cl = pif->pif_classes[pri]) != NULL) {
337a5b789f6SErmal Luçi 		/* modify the class instead of creating a new one */
338a5b789f6SErmal Luçi 		IFQ_LOCK(cl->cl_pif->pif_ifq);
339a5b789f6SErmal Luçi 		if (cl->cl_head)
340a5b789f6SErmal Luçi 			fairq_purgeq(cl);
341a5b789f6SErmal Luçi 		IFQ_UNLOCK(cl->cl_pif->pif_ifq);
342a5b789f6SErmal Luçi #ifdef ALTQ_RIO
343a5b789f6SErmal Luçi 		if (cl->cl_qtype == Q_RIO)
344a5b789f6SErmal Luçi 			rio_destroy((rio_t *)cl->cl_red);
345a5b789f6SErmal Luçi #endif
346a5b789f6SErmal Luçi #ifdef ALTQ_RED
347a5b789f6SErmal Luçi 		if (cl->cl_qtype == Q_RED)
348a5b789f6SErmal Luçi 			red_destroy(cl->cl_red);
349a5b789f6SErmal Luçi #endif
3500a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL
3510a70aaf8SLuiz Otavio O Souza 		if (cl->cl_qtype == Q_CODEL)
3520a70aaf8SLuiz Otavio O Souza 			codel_destroy(cl->cl_codel);
3530a70aaf8SLuiz Otavio O Souza #endif
354a5b789f6SErmal Luçi 	} else {
355a5b789f6SErmal Luçi 		cl = malloc(sizeof(struct fairq_class),
356a5b789f6SErmal Luçi 				M_DEVBUF, M_WAITOK | M_ZERO);
357a5b789f6SErmal Luçi 		cl->cl_nbuckets = nbuckets;
358a5b789f6SErmal Luçi 		cl->cl_nbucket_mask = nbuckets - 1;
359a5b789f6SErmal Luçi 
360a5b789f6SErmal Luçi 		cl->cl_buckets = malloc(
361a5b789f6SErmal Luçi 			sizeof(struct fairq_bucket) * cl->cl_nbuckets,
362a5b789f6SErmal Luçi 			M_DEVBUF, M_WAITOK | M_ZERO);
363a5b789f6SErmal Luçi 		cl->cl_head = NULL;
364a5b789f6SErmal Luçi 	}
365a5b789f6SErmal Luçi 
366a5b789f6SErmal Luçi 	pif->pif_classes[pri] = cl;
367a5b789f6SErmal Luçi 	if (flags & FARF_DEFAULTCLASS)
368a5b789f6SErmal Luçi 		pif->pif_default = cl;
369a5b789f6SErmal Luçi 	if (qlimit == 0)
370a5b789f6SErmal Luçi 		qlimit = 50;  /* use default */
371a5b789f6SErmal Luçi 	cl->cl_qlimit = qlimit;
372a5b789f6SErmal Luçi 	for (i = 0; i < cl->cl_nbuckets; ++i) {
373a5b789f6SErmal Luçi 		qlimit(&cl->cl_buckets[i].queue) = qlimit;
374a5b789f6SErmal Luçi 	}
375a5b789f6SErmal Luçi 	cl->cl_bandwidth = bandwidth / 8;
376a5b789f6SErmal Luçi 	cl->cl_qtype = Q_DROPTAIL;
377a5b789f6SErmal Luçi 	cl->cl_flags = flags & FARF_USERFLAGS;
378a5b789f6SErmal Luçi 	cl->cl_pri = pri;
379a5b789f6SErmal Luçi 	if (pri > pif->pif_maxpri)
380a5b789f6SErmal Luçi 		pif->pif_maxpri = pri;
381a5b789f6SErmal Luçi 	cl->cl_pif = pif;
382a5b789f6SErmal Luçi 	cl->cl_handle = qid;
383a5b789f6SErmal Luçi 	cl->cl_hogs_m1 = opts->hogs_m1 / 8;
384a5b789f6SErmal Luçi 	cl->cl_lssc_m1 = opts->lssc_m1 / 8;	/* NOT YET USED */
385a5b789f6SErmal Luçi 
386a5b789f6SErmal Luçi #ifdef ALTQ_RED
387a5b789f6SErmal Luçi 	if (flags & (FARF_RED|FARF_RIO)) {
388a5b789f6SErmal Luçi 		int red_flags, red_pkttime;
389a5b789f6SErmal Luçi 
390a5b789f6SErmal Luçi 		red_flags = 0;
391a5b789f6SErmal Luçi 		if (flags & FARF_ECN)
392a5b789f6SErmal Luçi 			red_flags |= REDF_ECN;
393a5b789f6SErmal Luçi #ifdef ALTQ_RIO
394a5b789f6SErmal Luçi 		if (flags & FARF_CLEARDSCP)
395a5b789f6SErmal Luçi 			red_flags |= RIOF_CLEARDSCP;
396a5b789f6SErmal Luçi #endif
397a5b789f6SErmal Luçi 		if (pif->pif_bandwidth < 8)
398a5b789f6SErmal Luçi 			red_pkttime = 1000 * 1000 * 1000; /* 1 sec */
399a5b789f6SErmal Luçi 		else
400a5b789f6SErmal Luçi 			red_pkttime = (int64_t)pif->pif_ifq->altq_ifp->if_mtu
401a5b789f6SErmal Luçi 			  * 1000 * 1000 * 1000 / (pif->pif_bandwidth / 8);
402a5b789f6SErmal Luçi #ifdef ALTQ_RIO
403a5b789f6SErmal Luçi 		if (flags & FARF_RIO) {
404a5b789f6SErmal Luçi 			cl->cl_red = (red_t *)rio_alloc(0, NULL,
405a5b789f6SErmal Luçi 						red_flags, red_pkttime);
406a5b789f6SErmal Luçi 			if (cl->cl_red != NULL)
407a5b789f6SErmal Luçi 				cl->cl_qtype = Q_RIO;
408a5b789f6SErmal Luçi 		} else
409a5b789f6SErmal Luçi #endif
410a5b789f6SErmal Luçi 		if (flags & FARF_RED) {
411a5b789f6SErmal Luçi 			cl->cl_red = red_alloc(0, 0,
412a5b789f6SErmal Luçi 			    cl->cl_qlimit * 10/100,
413a5b789f6SErmal Luçi 			    cl->cl_qlimit * 30/100,
414a5b789f6SErmal Luçi 			    red_flags, red_pkttime);
415a5b789f6SErmal Luçi 			if (cl->cl_red != NULL)
416a5b789f6SErmal Luçi 				cl->cl_qtype = Q_RED;
417a5b789f6SErmal Luçi 		}
418a5b789f6SErmal Luçi 	}
419a5b789f6SErmal Luçi #endif /* ALTQ_RED */
4200a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL
4210a70aaf8SLuiz Otavio O Souza 	if (flags & FARF_CODEL) {
4220a70aaf8SLuiz Otavio O Souza 		cl->cl_codel = codel_alloc(5, 100, 0);
4230a70aaf8SLuiz Otavio O Souza 		if (cl->cl_codel != NULL)
4240a70aaf8SLuiz Otavio O Souza 			cl->cl_qtype = Q_CODEL;
4250a70aaf8SLuiz Otavio O Souza 	}
4260a70aaf8SLuiz Otavio O Souza #endif
427a5b789f6SErmal Luçi 
428a5b789f6SErmal Luçi 	return (cl);
429a5b789f6SErmal Luçi }
430a5b789f6SErmal Luçi 
431a5b789f6SErmal Luçi static int
fairq_class_destroy(struct fairq_class * cl)432a5b789f6SErmal Luçi fairq_class_destroy(struct fairq_class *cl)
433a5b789f6SErmal Luçi {
434a5b789f6SErmal Luçi 	struct fairq_if *pif;
435a5b789f6SErmal Luçi 	int pri;
436a5b789f6SErmal Luçi 
437a5b789f6SErmal Luçi 	IFQ_LOCK(cl->cl_pif->pif_ifq);
438a5b789f6SErmal Luçi 
439a5b789f6SErmal Luçi 	if (cl->cl_head)
440a5b789f6SErmal Luçi 		fairq_purgeq(cl);
441a5b789f6SErmal Luçi 
442a5b789f6SErmal Luçi 	pif = cl->cl_pif;
443a5b789f6SErmal Luçi 	pif->pif_classes[cl->cl_pri] = NULL;
444a5b789f6SErmal Luçi 	if (pif->pif_poll_cache == cl)
445a5b789f6SErmal Luçi 		pif->pif_poll_cache = NULL;
446a5b789f6SErmal Luçi 	if (pif->pif_maxpri == cl->cl_pri) {
447a5b789f6SErmal Luçi 		for (pri = cl->cl_pri; pri >= 0; pri--)
448a5b789f6SErmal Luçi 			if (pif->pif_classes[pri] != NULL) {
449a5b789f6SErmal Luçi 				pif->pif_maxpri = pri;
450a5b789f6SErmal Luçi 				break;
451a5b789f6SErmal Luçi 			}
452a5b789f6SErmal Luçi 		if (pri < 0)
453a5b789f6SErmal Luçi 			pif->pif_maxpri = -1;
454a5b789f6SErmal Luçi 	}
455a5b789f6SErmal Luçi 	IFQ_UNLOCK(cl->cl_pif->pif_ifq);
456a5b789f6SErmal Luçi 
457a5b789f6SErmal Luçi 	if (cl->cl_red != NULL) {
458a5b789f6SErmal Luçi #ifdef ALTQ_RIO
459a5b789f6SErmal Luçi 		if (cl->cl_qtype == Q_RIO)
460a5b789f6SErmal Luçi 			rio_destroy((rio_t *)cl->cl_red);
461a5b789f6SErmal Luçi #endif
462a5b789f6SErmal Luçi #ifdef ALTQ_RED
463a5b789f6SErmal Luçi 		if (cl->cl_qtype == Q_RED)
464a5b789f6SErmal Luçi 			red_destroy(cl->cl_red);
465a5b789f6SErmal Luçi #endif
4660a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL
4670a70aaf8SLuiz Otavio O Souza 		if (cl->cl_qtype == Q_CODEL)
4680a70aaf8SLuiz Otavio O Souza 			codel_destroy(cl->cl_codel);
4690a70aaf8SLuiz Otavio O Souza #endif
470a5b789f6SErmal Luçi 	}
471a5b789f6SErmal Luçi 	free(cl->cl_buckets, M_DEVBUF);
472a5b789f6SErmal Luçi 	free(cl, M_DEVBUF);
473a5b789f6SErmal Luçi 
474a5b789f6SErmal Luçi 	return (0);
475a5b789f6SErmal Luçi }
476a5b789f6SErmal Luçi 
477a5b789f6SErmal Luçi /*
478a5b789f6SErmal Luçi  * fairq_enqueue is an enqueue function to be registered to
479a5b789f6SErmal Luçi  * (*altq_enqueue) in struct ifaltq.
480a5b789f6SErmal Luçi  */
481a5b789f6SErmal Luçi static int
fairq_enqueue(struct ifaltq * ifq,struct mbuf * m,struct altq_pktattr * pktattr)482a5b789f6SErmal Luçi fairq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr)
483a5b789f6SErmal Luçi {
484a5b789f6SErmal Luçi 	struct fairq_if *pif = (struct fairq_if *)ifq->altq_disc;
485a5b789f6SErmal Luçi 	struct fairq_class *cl = NULL; /* Make compiler happy */
486a5b789f6SErmal Luçi 	struct pf_mtag *t;
487a5b789f6SErmal Luçi 	u_int32_t qid_hash = 0;
488a5b789f6SErmal Luçi 	int len;
489a5b789f6SErmal Luçi 
490a5b789f6SErmal Luçi 	IFQ_LOCK_ASSERT(ifq);
491a5b789f6SErmal Luçi 
492a5b789f6SErmal Luçi 	/* grab class set by classifier */
493a5b789f6SErmal Luçi 	if ((m->m_flags & M_PKTHDR) == 0) {
494a5b789f6SErmal Luçi 		/* should not happen */
495a5b789f6SErmal Luçi 		printf("altq: packet for %s does not have pkthdr\n",
496a5b789f6SErmal Luçi 			ifq->altq_ifp->if_xname);
497a5b789f6SErmal Luçi 		m_freem(m);
498a5b789f6SErmal Luçi 		return (ENOBUFS);
499a5b789f6SErmal Luçi 	}
500a5b789f6SErmal Luçi 
501a5b789f6SErmal Luçi 	if ((t = pf_find_mtag(m)) != NULL) {
502a5b789f6SErmal Luçi 		cl = clh_to_clp(pif, t->qid);
503a5b789f6SErmal Luçi 		qid_hash = t->qid_hash;
504a5b789f6SErmal Luçi 	}
505a5b789f6SErmal Luçi 	if (cl == NULL) {
506a5b789f6SErmal Luçi 		cl = pif->pif_default;
507a5b789f6SErmal Luçi 		if (cl == NULL) {
508a5b789f6SErmal Luçi 			m_freem(m);
509a5b789f6SErmal Luçi 			return (ENOBUFS);
510a5b789f6SErmal Luçi 		}
511a5b789f6SErmal Luçi 	}
512a5b789f6SErmal Luçi 	cl->cl_flags |= FARF_HAS_PACKETS;
513a5b789f6SErmal Luçi 	cl->cl_pktattr = NULL;
514a5b789f6SErmal Luçi 	len = m_pktlen(m);
515a5b789f6SErmal Luçi 	if (fairq_addq(cl, m, qid_hash) != 0) {
516a5b789f6SErmal Luçi 		/* drop occurred.  mbuf was freed in fairq_addq. */
517a5b789f6SErmal Luçi 		PKTCNTR_ADD(&cl->cl_dropcnt, len);
518a5b789f6SErmal Luçi 		return (ENOBUFS);
519a5b789f6SErmal Luçi 	}
520a5b789f6SErmal Luçi 	IFQ_INC_LEN(ifq);
521a5b789f6SErmal Luçi 
522a5b789f6SErmal Luçi 	return (0);
523a5b789f6SErmal Luçi }
524a5b789f6SErmal Luçi 
525a5b789f6SErmal Luçi /*
526a5b789f6SErmal Luçi  * fairq_dequeue is a dequeue function to be registered to
527a5b789f6SErmal Luçi  * (*altq_dequeue) in struct ifaltq.
528a5b789f6SErmal Luçi  *
529a5b789f6SErmal Luçi  * note: ALTDQ_POLL returns the next packet without removing the packet
530a5b789f6SErmal Luçi  *	from the queue.  ALTDQ_REMOVE is a normal dequeue operation.
531a5b789f6SErmal Luçi  *	ALTDQ_REMOVE must return the same packet if called immediately
532a5b789f6SErmal Luçi  *	after ALTDQ_POLL.
533a5b789f6SErmal Luçi  */
534a5b789f6SErmal Luçi static struct mbuf *
fairq_dequeue(struct ifaltq * ifq,int op)535a5b789f6SErmal Luçi fairq_dequeue(struct ifaltq *ifq, int op)
536a5b789f6SErmal Luçi {
537a5b789f6SErmal Luçi 	struct fairq_if *pif = (struct fairq_if *)ifq->altq_disc;
538a5b789f6SErmal Luçi 	struct fairq_class *cl;
539a5b789f6SErmal Luçi 	struct fairq_class *best_cl;
540a5b789f6SErmal Luçi 	struct mbuf *best_m;
541a5b789f6SErmal Luçi 	struct mbuf *m = NULL;
542a5b789f6SErmal Luçi 	uint64_t cur_time = read_machclk();
543a5b789f6SErmal Luçi 	int pri;
544a5b789f6SErmal Luçi 	int hit_limit;
545a5b789f6SErmal Luçi 
546a5b789f6SErmal Luçi 	IFQ_LOCK_ASSERT(ifq);
547a5b789f6SErmal Luçi 
548a5b789f6SErmal Luçi 	if (IFQ_IS_EMPTY(ifq)) {
549a5b789f6SErmal Luçi 		return (NULL);
550a5b789f6SErmal Luçi 	}
551a5b789f6SErmal Luçi 
552a5b789f6SErmal Luçi 	if (pif->pif_poll_cache && op == ALTDQ_REMOVE) {
553a5b789f6SErmal Luçi 		best_cl = pif->pif_poll_cache;
554a5b789f6SErmal Luçi 		m = fairq_getq(best_cl, cur_time);
555a5b789f6SErmal Luçi 		pif->pif_poll_cache = NULL;
556a5b789f6SErmal Luçi 		if (m) {
557a5b789f6SErmal Luçi 			IFQ_DEC_LEN(ifq);
558a5b789f6SErmal Luçi 			PKTCNTR_ADD(&best_cl->cl_xmitcnt, m_pktlen(m));
559a5b789f6SErmal Luçi 			return (m);
560a5b789f6SErmal Luçi 		}
561a5b789f6SErmal Luçi 	} else {
562a5b789f6SErmal Luçi 		best_cl = NULL;
563a5b789f6SErmal Luçi 		best_m = NULL;
564a5b789f6SErmal Luçi 
565a5b789f6SErmal Luçi 		for (pri = pif->pif_maxpri;  pri >= 0; pri--) {
566a5b789f6SErmal Luçi 			if ((cl = pif->pif_classes[pri]) == NULL)
567a5b789f6SErmal Luçi 				continue;
568a5b789f6SErmal Luçi 			if ((cl->cl_flags & FARF_HAS_PACKETS) == 0)
569a5b789f6SErmal Luçi 				continue;
570a5b789f6SErmal Luçi 			m = fairq_pollq(cl, cur_time, &hit_limit);
571a5b789f6SErmal Luçi 			if (m == NULL) {
572a5b789f6SErmal Luçi 				cl->cl_flags &= ~FARF_HAS_PACKETS;
573a5b789f6SErmal Luçi 				continue;
574a5b789f6SErmal Luçi 			}
575a5b789f6SErmal Luçi 
576a5b789f6SErmal Luçi 			/*
577a5b789f6SErmal Luçi 			 * Only override the best choice if we are under
578a5b789f6SErmal Luçi 			 * the BW limit.
579a5b789f6SErmal Luçi 			 */
580a5b789f6SErmal Luçi 			if (hit_limit == 0 || best_cl == NULL) {
581a5b789f6SErmal Luçi 				best_cl = cl;
582a5b789f6SErmal Luçi 				best_m = m;
583a5b789f6SErmal Luçi 			}
584a5b789f6SErmal Luçi 
585a5b789f6SErmal Luçi 			/*
586a5b789f6SErmal Luçi 			 * Remember the highest priority mbuf in case we
587a5b789f6SErmal Luçi 			 * do not find any lower priority mbufs.
588a5b789f6SErmal Luçi 			 */
589a5b789f6SErmal Luçi 			if (hit_limit)
590a5b789f6SErmal Luçi 				continue;
591a5b789f6SErmal Luçi 			break;
592a5b789f6SErmal Luçi 		}
593a5b789f6SErmal Luçi 		if (op == ALTDQ_POLL) {
594a5b789f6SErmal Luçi 			pif->pif_poll_cache = best_cl;
595a5b789f6SErmal Luçi 			m = best_m;
596a5b789f6SErmal Luçi 		} else if (best_cl) {
597a5b789f6SErmal Luçi 			m = fairq_getq(best_cl, cur_time);
598a5b789f6SErmal Luçi 			if (m != NULL) {
599a5b789f6SErmal Luçi 				IFQ_DEC_LEN(ifq);
600a5b789f6SErmal Luçi 				PKTCNTR_ADD(&best_cl->cl_xmitcnt, m_pktlen(m));
601a5b789f6SErmal Luçi 			}
602a5b789f6SErmal Luçi 		}
603a5b789f6SErmal Luçi 		return (m);
604a5b789f6SErmal Luçi 	}
605a5b789f6SErmal Luçi 	return (NULL);
606a5b789f6SErmal Luçi }
607a5b789f6SErmal Luçi 
608a5b789f6SErmal Luçi static int
fairq_addq(struct fairq_class * cl,struct mbuf * m,u_int32_t bucketid)609a5b789f6SErmal Luçi fairq_addq(struct fairq_class *cl, struct mbuf *m, u_int32_t bucketid)
610a5b789f6SErmal Luçi {
611a5b789f6SErmal Luçi 	fairq_bucket_t *b;
612a5b789f6SErmal Luçi 	u_int hindex;
613a5b789f6SErmal Luçi 	uint64_t bw;
614a5b789f6SErmal Luçi 
615a5b789f6SErmal Luçi 	/*
616a5b789f6SErmal Luçi 	 * If the packet doesn't have any keep state put it on the end of
617a5b789f6SErmal Luçi 	 * our queue.  XXX this can result in out of order delivery.
618a5b789f6SErmal Luçi 	 */
619a5b789f6SErmal Luçi 	if (bucketid == 0) {
620a5b789f6SErmal Luçi 		if (cl->cl_head)
621a5b789f6SErmal Luçi 			b = cl->cl_head->prev;
622a5b789f6SErmal Luçi 		else
623a5b789f6SErmal Luçi 			b = &cl->cl_buckets[0];
624a5b789f6SErmal Luçi 	} else {
625a5b789f6SErmal Luçi 		hindex = bucketid & cl->cl_nbucket_mask;
626a5b789f6SErmal Luçi 		b = &cl->cl_buckets[hindex];
627a5b789f6SErmal Luçi 	}
628a5b789f6SErmal Luçi 
629a5b789f6SErmal Luçi 	/*
630a5b789f6SErmal Luçi 	 * Add the bucket to the end of the circular list of active buckets.
631a5b789f6SErmal Luçi 	 *
632a5b789f6SErmal Luçi 	 * As a special case we add the bucket to the beginning of the list
633a5b789f6SErmal Luçi 	 * instead of the end if it was not previously on the list and if
634a5b789f6SErmal Luçi 	 * its traffic is less then the hog level.
635a5b789f6SErmal Luçi 	 */
636a5b789f6SErmal Luçi 	if (b->in_use == 0) {
637a5b789f6SErmal Luçi 		b->in_use = 1;
638a5b789f6SErmal Luçi 		if (cl->cl_head == NULL) {
639a5b789f6SErmal Luçi 			cl->cl_head = b;
640a5b789f6SErmal Luçi 			b->next = b;
641a5b789f6SErmal Luçi 			b->prev = b;
642a5b789f6SErmal Luçi 		} else {
643a5b789f6SErmal Luçi 			b->next = cl->cl_head;
644a5b789f6SErmal Luçi 			b->prev = cl->cl_head->prev;
645a5b789f6SErmal Luçi 			b->prev->next = b;
646a5b789f6SErmal Luçi 			b->next->prev = b;
647a5b789f6SErmal Luçi 
648a5b789f6SErmal Luçi 			if (b->bw_delta && cl->cl_hogs_m1) {
649a5b789f6SErmal Luçi 				bw = b->bw_bytes * machclk_freq / b->bw_delta;
650a5b789f6SErmal Luçi 				if (bw < cl->cl_hogs_m1)
651a5b789f6SErmal Luçi 					cl->cl_head = b;
652a5b789f6SErmal Luçi 			}
653a5b789f6SErmal Luçi 		}
654a5b789f6SErmal Luçi 	}
655a5b789f6SErmal Luçi 
656a5b789f6SErmal Luçi #ifdef ALTQ_RIO
657a5b789f6SErmal Luçi 	if (cl->cl_qtype == Q_RIO)
658a5b789f6SErmal Luçi 		return rio_addq((rio_t *)cl->cl_red, &b->queue, m, cl->cl_pktattr);
659a5b789f6SErmal Luçi #endif
660a5b789f6SErmal Luçi #ifdef ALTQ_RED
661a5b789f6SErmal Luçi 	if (cl->cl_qtype == Q_RED)
662a5b789f6SErmal Luçi 		return red_addq(cl->cl_red, &b->queue, m, cl->cl_pktattr);
663a5b789f6SErmal Luçi #endif
6640a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL
6650a70aaf8SLuiz Otavio O Souza 	if (cl->cl_qtype == Q_CODEL)
6660a70aaf8SLuiz Otavio O Souza 		return codel_addq(cl->cl_codel, &b->queue, m);
6670a70aaf8SLuiz Otavio O Souza #endif
668a5b789f6SErmal Luçi 	if (qlen(&b->queue) >= qlimit(&b->queue)) {
669a5b789f6SErmal Luçi 		m_freem(m);
670a5b789f6SErmal Luçi 		return (-1);
671a5b789f6SErmal Luçi 	}
672a5b789f6SErmal Luçi 
673a5b789f6SErmal Luçi 	if (cl->cl_flags & FARF_CLEARDSCP)
674a5b789f6SErmal Luçi 		write_dsfield(m, cl->cl_pktattr, 0);
675a5b789f6SErmal Luçi 
676a5b789f6SErmal Luçi 	_addq(&b->queue, m);
677a5b789f6SErmal Luçi 
678a5b789f6SErmal Luçi 	return (0);
679a5b789f6SErmal Luçi }
680a5b789f6SErmal Luçi 
681a5b789f6SErmal Luçi static struct mbuf *
fairq_getq(struct fairq_class * cl,uint64_t cur_time)682a5b789f6SErmal Luçi fairq_getq(struct fairq_class *cl, uint64_t cur_time)
683a5b789f6SErmal Luçi {
684a5b789f6SErmal Luçi 	fairq_bucket_t *b;
685a5b789f6SErmal Luçi 	struct mbuf *m;
686a5b789f6SErmal Luçi 
687a5b789f6SErmal Luçi 	b = fairq_selectq(cl, 0);
688a5b789f6SErmal Luçi 	if (b == NULL)
689a5b789f6SErmal Luçi 		m = NULL;
690a5b789f6SErmal Luçi #ifdef ALTQ_RIO
691a5b789f6SErmal Luçi 	else if (cl->cl_qtype == Q_RIO)
692a5b789f6SErmal Luçi 		m = rio_getq((rio_t *)cl->cl_red, &b->queue);
693a5b789f6SErmal Luçi #endif
694a5b789f6SErmal Luçi #ifdef ALTQ_RED
695a5b789f6SErmal Luçi 	else if (cl->cl_qtype == Q_RED)
696a5b789f6SErmal Luçi 		m = red_getq(cl->cl_red, &b->queue);
697a5b789f6SErmal Luçi #endif
6980a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL
6990a70aaf8SLuiz Otavio O Souza 	else if (cl->cl_qtype == Q_CODEL)
7000a70aaf8SLuiz Otavio O Souza 		m = codel_getq(cl->cl_codel, &b->queue);
7010a70aaf8SLuiz Otavio O Souza #endif
702a5b789f6SErmal Luçi 	else
703a5b789f6SErmal Luçi 		m = _getq(&b->queue);
704a5b789f6SErmal Luçi 
705a5b789f6SErmal Luçi 	/*
706a5b789f6SErmal Luçi 	 * Calculate the BW change
707a5b789f6SErmal Luçi 	 */
708a5b789f6SErmal Luçi 	if (m != NULL) {
709a5b789f6SErmal Luçi 		uint64_t delta;
710a5b789f6SErmal Luçi 
711a5b789f6SErmal Luçi 		/*
712a5b789f6SErmal Luçi 		 * Per-class bandwidth calculation
713a5b789f6SErmal Luçi 		 */
714a5b789f6SErmal Luçi 		delta = (cur_time - cl->cl_last_time);
715a5b789f6SErmal Luçi 		if (delta > machclk_freq * 8)
716a5b789f6SErmal Luçi 			delta = machclk_freq * 8;
717a5b789f6SErmal Luçi 		cl->cl_bw_delta += delta;
718a5b789f6SErmal Luçi 		cl->cl_bw_bytes += m->m_pkthdr.len;
719a5b789f6SErmal Luçi 		cl->cl_last_time = cur_time;
720a5b789f6SErmal Luçi 		cl->cl_bw_delta -= cl->cl_bw_delta >> 3;
721a5b789f6SErmal Luçi 		cl->cl_bw_bytes -= cl->cl_bw_bytes >> 3;
722a5b789f6SErmal Luçi 
723a5b789f6SErmal Luçi 		/*
724a5b789f6SErmal Luçi 		 * Per-bucket bandwidth calculation
725a5b789f6SErmal Luçi 		 */
726a5b789f6SErmal Luçi 		delta = (cur_time - b->last_time);
727a5b789f6SErmal Luçi 		if (delta > machclk_freq * 8)
728a5b789f6SErmal Luçi 			delta = machclk_freq * 8;
729a5b789f6SErmal Luçi 		b->bw_delta += delta;
730a5b789f6SErmal Luçi 		b->bw_bytes += m->m_pkthdr.len;
731a5b789f6SErmal Luçi 		b->last_time = cur_time;
732a5b789f6SErmal Luçi 		b->bw_delta -= b->bw_delta >> 3;
733a5b789f6SErmal Luçi 		b->bw_bytes -= b->bw_bytes >> 3;
734a5b789f6SErmal Luçi 	}
735a5b789f6SErmal Luçi 	return(m);
736a5b789f6SErmal Luçi }
737a5b789f6SErmal Luçi 
738a5b789f6SErmal Luçi /*
739a5b789f6SErmal Luçi  * Figure out what the next packet would be if there were no limits.  If
740a5b789f6SErmal Luçi  * this class hits its bandwidth limit *hit_limit is set to no-zero, otherwise
741a5b789f6SErmal Luçi  * it is set to 0.  A non-NULL mbuf is returned either way.
742a5b789f6SErmal Luçi  */
743a5b789f6SErmal Luçi static struct mbuf *
fairq_pollq(struct fairq_class * cl,uint64_t cur_time,int * hit_limit)744a5b789f6SErmal Luçi fairq_pollq(struct fairq_class *cl, uint64_t cur_time, int *hit_limit)
745a5b789f6SErmal Luçi {
746a5b789f6SErmal Luçi 	fairq_bucket_t *b;
747a5b789f6SErmal Luçi 	struct mbuf *m;
748a5b789f6SErmal Luçi 	uint64_t delta;
749a5b789f6SErmal Luçi 	uint64_t bw;
750a5b789f6SErmal Luçi 
751a5b789f6SErmal Luçi 	*hit_limit = 0;
752a5b789f6SErmal Luçi 	b = fairq_selectq(cl, 1);
753a5b789f6SErmal Luçi 	if (b == NULL)
754a5b789f6SErmal Luçi 		return(NULL);
755a5b789f6SErmal Luçi 	m = qhead(&b->queue);
756a5b789f6SErmal Luçi 
757a5b789f6SErmal Luçi 	/*
758a5b789f6SErmal Luçi 	 * Did this packet exceed the class bandwidth?  Calculate the
759a5b789f6SErmal Luçi 	 * bandwidth component of the packet.
760a5b789f6SErmal Luçi 	 *
761a5b789f6SErmal Luçi 	 * - Calculate bytes per second
762a5b789f6SErmal Luçi 	 */
763a5b789f6SErmal Luçi 	delta = cur_time - cl->cl_last_time;
764a5b789f6SErmal Luçi 	if (delta > machclk_freq * 8)
765a5b789f6SErmal Luçi 		delta = machclk_freq * 8;
766a5b789f6SErmal Luçi 	cl->cl_bw_delta += delta;
767a5b789f6SErmal Luçi 	cl->cl_last_time = cur_time;
768a5b789f6SErmal Luçi 	if (cl->cl_bw_delta) {
769a5b789f6SErmal Luçi 		bw = cl->cl_bw_bytes * machclk_freq / cl->cl_bw_delta;
770a5b789f6SErmal Luçi 
771a5b789f6SErmal Luçi 		if (bw > cl->cl_bandwidth)
772a5b789f6SErmal Luçi 			*hit_limit = 1;
773a5b789f6SErmal Luçi #ifdef ALTQ_DEBUG
7749656119dSBjoern A. Zeeb 		printf("BW %6ju relative to %6u %d queue %p\n",
7759656119dSBjoern A. Zeeb 			(uintmax_t)bw, cl->cl_bandwidth, *hit_limit, b);
776a5b789f6SErmal Luçi #endif
777a5b789f6SErmal Luçi 	}
778a5b789f6SErmal Luçi 	return(m);
779a5b789f6SErmal Luçi }
780a5b789f6SErmal Luçi 
781a5b789f6SErmal Luçi /*
782a5b789f6SErmal Luçi  * Locate the next queue we want to pull a packet out of.  This code
783a5b789f6SErmal Luçi  * is also responsible for removing empty buckets from the circular list.
784a5b789f6SErmal Luçi  */
785a5b789f6SErmal Luçi static
786a5b789f6SErmal Luçi fairq_bucket_t *
fairq_selectq(struct fairq_class * cl,int ispoll)787a5b789f6SErmal Luçi fairq_selectq(struct fairq_class *cl, int ispoll)
788a5b789f6SErmal Luçi {
789a5b789f6SErmal Luçi 	fairq_bucket_t *b;
790a5b789f6SErmal Luçi 	uint64_t bw;
791a5b789f6SErmal Luçi 
792a5b789f6SErmal Luçi 	if (ispoll == 0 && cl->cl_polled) {
793a5b789f6SErmal Luçi 		b = cl->cl_polled;
794a5b789f6SErmal Luçi 		cl->cl_polled = NULL;
795a5b789f6SErmal Luçi 		return(b);
796a5b789f6SErmal Luçi 	}
797a5b789f6SErmal Luçi 
798a5b789f6SErmal Luçi 	while ((b = cl->cl_head) != NULL) {
799a5b789f6SErmal Luçi 		/*
800a5b789f6SErmal Luçi 		 * Remove empty queues from consideration
801a5b789f6SErmal Luçi 		 */
802a5b789f6SErmal Luçi 		if (qempty(&b->queue)) {
803a5b789f6SErmal Luçi 			b->in_use = 0;
804a5b789f6SErmal Luçi 			cl->cl_head = b->next;
805a5b789f6SErmal Luçi 			if (cl->cl_head == b) {
806a5b789f6SErmal Luçi 				cl->cl_head = NULL;
807a5b789f6SErmal Luçi 			} else {
808a5b789f6SErmal Luçi 				b->next->prev = b->prev;
809a5b789f6SErmal Luçi 				b->prev->next = b->next;
810a5b789f6SErmal Luçi 			}
811a5b789f6SErmal Luçi 			continue;
812a5b789f6SErmal Luçi 		}
813a5b789f6SErmal Luçi 
814a5b789f6SErmal Luçi 		/*
815a5b789f6SErmal Luçi 		 * Advance the round robin.  Queues with bandwidths less
816a5b789f6SErmal Luçi 		 * then the hog bandwidth are allowed to burst.
817a5b789f6SErmal Luçi 		 */
818a5b789f6SErmal Luçi 		if (cl->cl_hogs_m1 == 0) {
819a5b789f6SErmal Luçi 			cl->cl_head = b->next;
820a5b789f6SErmal Luçi 		} else if (b->bw_delta) {
821a5b789f6SErmal Luçi 			bw = b->bw_bytes * machclk_freq / b->bw_delta;
822a5b789f6SErmal Luçi 			if (bw >= cl->cl_hogs_m1) {
823a5b789f6SErmal Luçi 				cl->cl_head = b->next;
824a5b789f6SErmal Luçi 			}
825a5b789f6SErmal Luçi 			/*
826a5b789f6SErmal Luçi 			 * XXX TODO -
827a5b789f6SErmal Luçi 			 */
828a5b789f6SErmal Luçi 		}
829a5b789f6SErmal Luçi 
830a5b789f6SErmal Luçi 		/*
831a5b789f6SErmal Luçi 		 * Return bucket b.
832a5b789f6SErmal Luçi 		 */
833a5b789f6SErmal Luçi 		break;
834a5b789f6SErmal Luçi 	}
835a5b789f6SErmal Luçi 	if (ispoll)
836a5b789f6SErmal Luçi 		cl->cl_polled = b;
837a5b789f6SErmal Luçi 	return(b);
838a5b789f6SErmal Luçi }
839a5b789f6SErmal Luçi 
840a5b789f6SErmal Luçi static void
fairq_purgeq(struct fairq_class * cl)841a5b789f6SErmal Luçi fairq_purgeq(struct fairq_class *cl)
842a5b789f6SErmal Luçi {
843a5b789f6SErmal Luçi 	fairq_bucket_t *b;
844a5b789f6SErmal Luçi 	struct mbuf *m;
845a5b789f6SErmal Luçi 
846a5b789f6SErmal Luçi 	while ((b = fairq_selectq(cl, 0)) != NULL) {
847a5b789f6SErmal Luçi 		while ((m = _getq(&b->queue)) != NULL) {
848a5b789f6SErmal Luçi 			PKTCNTR_ADD(&cl->cl_dropcnt, m_pktlen(m));
849a5b789f6SErmal Luçi 			m_freem(m);
850a5b789f6SErmal Luçi 		}
851a5b789f6SErmal Luçi 		ASSERT(qlen(&b->queue) == 0);
852a5b789f6SErmal Luçi 	}
853a5b789f6SErmal Luçi }
854a5b789f6SErmal Luçi 
855a5b789f6SErmal Luçi static void
get_class_stats(struct fairq_classstats * sp,struct fairq_class * cl)856a5b789f6SErmal Luçi get_class_stats(struct fairq_classstats *sp, struct fairq_class *cl)
857a5b789f6SErmal Luçi {
858a5b789f6SErmal Luçi 	fairq_bucket_t *b;
859a5b789f6SErmal Luçi 
860a5b789f6SErmal Luçi 	sp->class_handle = cl->cl_handle;
861a5b789f6SErmal Luçi 	sp->qlimit = cl->cl_qlimit;
862a5b789f6SErmal Luçi 	sp->xmit_cnt = cl->cl_xmitcnt;
863a5b789f6SErmal Luçi 	sp->drop_cnt = cl->cl_dropcnt;
864a5b789f6SErmal Luçi 	sp->qtype = cl->cl_qtype;
865a5b789f6SErmal Luçi 	sp->qlength = 0;
866a5b789f6SErmal Luçi 
867a5b789f6SErmal Luçi 	if (cl->cl_head) {
868a5b789f6SErmal Luçi 		b = cl->cl_head;
869a5b789f6SErmal Luçi 		do {
870a5b789f6SErmal Luçi 			sp->qlength += qlen(&b->queue);
871a5b789f6SErmal Luçi 			b = b->next;
872a5b789f6SErmal Luçi 		} while (b != cl->cl_head);
873a5b789f6SErmal Luçi 	}
874a5b789f6SErmal Luçi 
875a5b789f6SErmal Luçi #ifdef ALTQ_RED
876a5b789f6SErmal Luçi 	if (cl->cl_qtype == Q_RED)
877a5b789f6SErmal Luçi 		red_getstats(cl->cl_red, &sp->red[0]);
878a5b789f6SErmal Luçi #endif
879a5b789f6SErmal Luçi #ifdef ALTQ_RIO
880a5b789f6SErmal Luçi 	if (cl->cl_qtype == Q_RIO)
881a5b789f6SErmal Luçi 		rio_getstats((rio_t *)cl->cl_red, &sp->red[0]);
882a5b789f6SErmal Luçi #endif
8830a70aaf8SLuiz Otavio O Souza #ifdef ALTQ_CODEL
8840a70aaf8SLuiz Otavio O Souza 	if (cl->cl_qtype == Q_CODEL)
8850a70aaf8SLuiz Otavio O Souza 		codel_getstats(cl->cl_codel, &sp->codel);
8860a70aaf8SLuiz Otavio O Souza #endif
887a5b789f6SErmal Luçi }
888a5b789f6SErmal Luçi 
889a5b789f6SErmal Luçi /* convert a class handle to the corresponding class pointer */
890a5b789f6SErmal Luçi static struct fairq_class *
clh_to_clp(struct fairq_if * pif,uint32_t chandle)891a5b789f6SErmal Luçi clh_to_clp(struct fairq_if *pif, uint32_t chandle)
892a5b789f6SErmal Luçi {
893a5b789f6SErmal Luçi 	struct fairq_class *cl;
894a5b789f6SErmal Luçi 	int idx;
895a5b789f6SErmal Luçi 
896a5b789f6SErmal Luçi 	if (chandle == 0)
897a5b789f6SErmal Luçi 		return (NULL);
898a5b789f6SErmal Luçi 
899a5b789f6SErmal Luçi 	for (idx = pif->pif_maxpri; idx >= 0; idx--)
900a5b789f6SErmal Luçi 		if ((cl = pif->pif_classes[idx]) != NULL &&
901a5b789f6SErmal Luçi 		    cl->cl_handle == chandle)
902a5b789f6SErmal Luçi 			return (cl);
903a5b789f6SErmal Luçi 
904a5b789f6SErmal Luçi 	return (NULL);
905a5b789f6SErmal Luçi }
906a5b789f6SErmal Luçi 
907a5b789f6SErmal Luçi #endif /* ALTQ_FAIRQ */
908