1/*
2 * Copyright (c) 2011-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
31 *
32 * This code is derived from software contributed to The DragonFly Project
33 * by Matthew Dillon <dillon@backplane.com>
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 *
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in
43 *    the documentation and/or other materials provided with the
44 *    distribution.
45 * 3. Neither the name of The DragonFly Project nor the names of its
46 *    contributors may be used to endorse or promote products derived
47 *    from this software without specific, prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
50 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
51 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
52 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
53 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
54 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
55 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
56 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
57 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
58 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
59 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * SUCH DAMAGE.
61 *
62 * $DragonFly: src/sys/net/altq/altq_fairq.c,v 1.2 2008/05/14 11:59:23 sephe Exp $
63 */
64/*
65 * Matt: I gutted altq_priq.c and used it as a skeleton on which to build
66 * fairq.  The fairq algorithm is completely different then priq, of course,
67 * but because I used priq's skeleton I believe I should include priq's
68 * copyright.
69 *
70 * Copyright (C) 2000-2003
71 *	Sony Computer Science Laboratories Inc.  All rights reserved.
72 *
73 * Redistribution and use in source and binary forms, with or without
74 * modification, are permitted provided that the following conditions
75 * are met:
76 * 1. Redistributions of source code must retain the above copyright
77 *    notice, this list of conditions and the following disclaimer.
78 * 2. Redistributions in binary form must reproduce the above copyright
79 *    notice, this list of conditions and the following disclaimer in the
80 *    documentation and/or other materials provided with the distribution.
81 *
82 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
83 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
84 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
85 * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
86 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
87 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
88 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
89 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
90 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
91 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
92 * SUCH DAMAGE.
93 */
94
95#if PF_ALTQ && PKTSCHED_FAIRQ
96
97#include <sys/cdefs.h>
98#include <sys/param.h>
99#include <sys/malloc.h>
100#include <sys/mbuf.h>
101#include <sys/systm.h>
102#include <sys/errno.h>
103#include <sys/kernel.h>
104
105#include <net/if.h>
106#include <net/pfvar.h>
107#include <net/net_osdep.h>
108#include <net/altq/altq.h>
109#include <net/altq/altq_fairq.h>
110#include <netinet/in.h>
111
112/*
113 * function prototypes
114 */
115static int altq_fairq_enqueue(struct ifaltq *, struct mbuf *);
116static struct mbuf *altq_fairq_dequeue(struct ifaltq *, enum altdq_op);
117static int altq_fairq_request(struct ifaltq *, enum altrq, void *);
118
119int
120altq_fairq_pfattach(struct pf_altq *a)
121{
122	struct ifnet *ifp;
123	int error;
124
125	lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
126
127	if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
128		return (EINVAL);
129
130	IFCQ_LOCK(&ifp->if_snd);
131	error = altq_attach(IFCQ_ALTQ(&ifp->if_snd), ALTQT_FAIRQ, a->altq_disc,
132	    altq_fairq_enqueue, altq_fairq_dequeue, NULL, altq_fairq_request);
133	IFCQ_UNLOCK(&ifp->if_snd);
134
135	return (error);
136}
137
138int
139altq_fairq_add(struct pf_altq *a)
140{
141	struct fairq_if *fif;
142	struct ifnet *ifp;
143
144	lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
145
146	if ((ifp = ifunit(a->ifname)) == NULL)
147		return (EINVAL);
148	if (!ALTQ_IS_READY(IFCQ_ALTQ(&ifp->if_snd)))
149		return (ENODEV);
150
151	fif = fairq_alloc(ifp, M_WAITOK, TRUE);
152	if (fif == NULL)
153		return (ENOMEM);
154
155	/* keep the state in pf_altq */
156	a->altq_disc = fif;
157
158	return (0);
159}
160
161int
162altq_fairq_remove(struct pf_altq *a)
163{
164	struct fairq_if *fif;
165
166	lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
167
168	if ((fif = a->altq_disc) == NULL)
169		return (EINVAL);
170	a->altq_disc = NULL;
171
172	return (fairq_destroy(fif));
173}
174
175int
176altq_fairq_add_queue(struct pf_altq *a)
177{
178	struct fairq_if *fif;
179	struct fairq_opts *opts = &a->pq_u.fairq_opts;
180	int err;
181
182	lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
183
184	if ((fif = a->altq_disc) == NULL)
185		return (EINVAL);
186
187	IFCQ_LOCK(fif->fif_ifq);
188	err = fairq_add_queue(fif, a->priority, a->qlimit, a->bandwidth,
189	    opts->nbuckets, opts->flags, opts->hogs_m1, opts->lssc_m1,
190	    opts->lssc_d, opts->lssc_m2, a->qid, NULL);
191	IFCQ_UNLOCK(fif->fif_ifq);
192
193	return (err);
194}
195
196int
197altq_fairq_remove_queue(struct pf_altq *a)
198{
199	struct fairq_if *fif;
200	int err;
201
202	lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
203
204	if ((fif = a->altq_disc) == NULL)
205		return (EINVAL);
206
207	IFCQ_LOCK(fif->fif_ifq);
208	err = fairq_remove_queue(fif, a->qid);
209	IFCQ_UNLOCK(fif->fif_ifq);
210
211	return (err);
212}
213
214int
215altq_fairq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
216{
217	struct ifclassq *ifq = NULL;
218	struct fairq_if *fif;
219	struct fairq_classstats stats;
220	int error = 0;
221
222	lck_mtx_assert(pf_lock, LCK_MTX_ASSERT_OWNED);
223
224	if ((unsigned)*nbytes < sizeof (stats))
225		return (EINVAL);
226
227	if ((fif = altq_lookup(a->ifname, ALTQT_FAIRQ)) == NULL)
228		return (EBADF);
229
230	ifq = fif->fif_ifq;
231	IFCQ_LOCK_ASSERT_HELD(ifq);	/* lock held by altq_lookup */
232	error = fairq_get_class_stats(fif, a->qid, &stats);
233	IFCQ_UNLOCK(ifq);
234	if (error != 0)
235		return (error);
236
237	if ((error = copyout((caddr_t)&stats, (user_addr_t)(uintptr_t)ubuf,
238	    sizeof (stats))) != 0)
239		return (error);
240
241	*nbytes = sizeof (stats);
242
243	return (0);
244}
245
246static int
247altq_fairq_request(struct ifaltq *altq, enum altrq req, void *arg)
248{
249	struct fairq_if *fif = (struct fairq_if *)altq->altq_disc;
250
251	switch (req) {
252	case ALTRQ_PURGE:
253		fairq_purge(fif);
254		break;
255
256	case ALTRQ_PURGE_SC:
257		/* not supported for ALTQ instance */
258		break;
259
260	case ALTRQ_EVENT:
261		fairq_event(fif, (cqev_t)arg);
262		break;
263
264	case ALTRQ_THROTTLE:
265	default:
266		break;
267	}
268	return (0);
269}
270
271/*
272 * altq_fairq_enqueue is an enqueue function to be registered to
273 * (*altq_enqueue) in struct ifaltq.
274 */
275static int
276altq_fairq_enqueue(struct ifaltq *altq, struct mbuf *m)
277{
278	/* grab class set by classifier */
279	if (!(m->m_flags & M_PKTHDR)) {
280		/* should not happen */
281		printf("%s: packet for %s does not have pkthdr\n", __func__,
282		    if_name(altq->altq_ifcq->ifcq_ifp));
283		m_freem(m);
284		return (ENOBUFS);
285	}
286
287	return (fairq_enqueue(altq->altq_disc, NULL, m, m_pftag(m)));
288}
289
290/*
291 * altq_fairq_dequeue is a dequeue function to be registered to
292 * (*altq_dequeue) in struct ifaltq.
293 *
294 * note: ALTDQ_POLL returns the next packet without removing the packet
295 *	from the queue.  ALTDQ_REMOVE is a normal dequeue operation.
296 *	ALTDQ_REMOVE must return the same packet if called immediately
297 *	after ALTDQ_POLL.
298 */
299static struct mbuf *
300altq_fairq_dequeue(struct ifaltq *altq, enum altdq_op op)
301{
302	return (fairq_dequeue(altq->altq_disc, (cqdq_op_t)op));
303}
304#endif /* PF_ALTQ && PKTSCHED_FAIRQ */
305