altq_fifoq.c revision 1.7.12.3 1 /* $NetBSD: altq_fifoq.c,v 1.7.12.3 2006/09/25 03:56:59 peter Exp $ */
2 /* $KAME: altq_fifoq.c,v 1.12 2003/07/10 12:07:48 kjc Exp $ */
3
4 /*
5 * Copyright (C) 1997-2002
6 * Sony Computer Science Laboratories Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: altq_fifoq.c,v 1.7.12.3 2006/09/25 03:56:59 peter Exp $");
32
33 #ifdef _KERNEL_OPT
34 #include "opt_altq.h"
35 #endif
36
37 #ifdef ALTQ_FIFOQ /* fifoq is enabled by ALTQ_FIFOQ option in opt_altq.h */
38
39 /*
40 * FIFOQ is an altq sample implementation. There will be little
41 * need to use FIFOQ as an alternative queueing scheme.
42 * But this code is provided as a template for those who want to
43 * write their own queueing schemes.
44 */
45
46 #include <sys/param.h>
47 #include <sys/malloc.h>
48 #include <sys/mbuf.h>
49 #include <sys/socket.h>
50 #include <sys/sockio.h>
51 #include <sys/systm.h>
52 #include <sys/proc.h>
53 #include <sys/errno.h>
54 #include <sys/kernel.h>
55 #include <sys/kauth.h>
56
57 #include <net/if.h>
58 #include <net/if_types.h>
59 #include <netinet/in.h>
60
61 #include <altq/altq.h>
62 #include <altq/altq_conf.h>
63 #include <altq/altq_fifoq.h>
64
65 #ifdef ALTQ3_COMPAT
66
67 #define FIFOQ_STATS /* collect statistics */
68
69 /* fifoq_list keeps all fifoq_state_t's allocated. */
70 static fifoq_state_t *fifoq_list = NULL;
71
72 /* internal function prototypes */
73 static int fifoq_enqueue(struct ifaltq *, struct mbuf *,
74 struct altq_pktattr *);
75 static struct mbuf *fifoq_dequeue(struct ifaltq *, int);
76 static int fifoq_detach(fifoq_state_t *);
77 static int fifoq_request(struct ifaltq *, int, void *);
78 static void fifoq_purge(fifoq_state_t *);
79
80 /*
81 * fifoq device interface
82 */
83 altqdev_decl(fifoq);
84
85 int
86 fifoqopen(dev, flag, fmt, l)
87 dev_t dev;
88 int flag, fmt;
89 struct lwp *l;
90 {
91 /* everything will be done when the queueing scheme is attached. */
92 return 0;
93 }
94
95 /*
96 * there are 2 ways to act on close.
97 * detach-all-on-close:
98 * use for the daemon style approach. if the daemon dies, all the
99 * resource will be released.
100 * no-action-on-close:
101 * use for the command style approach. (e.g. fifoq on/off)
102 *
103 * note: close is called not on every close but when the last reference
104 * is removed (only once with multiple simultaneous references.)
105 */
106 int
107 fifoqclose(dev, flag, fmt, l)
108 dev_t dev;
109 int flag, fmt;
110 struct lwp *l;
111 {
112 fifoq_state_t *q;
113 int err, error = 0;
114
115 while ((q = fifoq_list) != NULL) {
116 /* destroy all */
117 err = fifoq_detach(q);
118 if (err != 0 && error == 0)
119 error = err;
120 }
121
122 return error;
123 }
124
125 int
126 fifoqioctl(dev, cmd, addr, flag, l)
127 dev_t dev;
128 ioctlcmd_t cmd;
129 caddr_t addr;
130 int flag;
131 struct lwp *l;
132 {
133 fifoq_state_t *q;
134 struct fifoq_interface *ifacep;
135 struct ifnet *ifp;
136 int error = 0;
137
138 /* check super-user privilege */
139 switch (cmd) {
140 case FIFOQ_GETSTATS:
141 break;
142 default:
143 #if (__FreeBSD_version > 400000)
144 if ((error = suser(p)) != 0)
145 return (error);
146 #else
147 if ((error = kauth_authorize_generic(l->l_cred,
148 KAUTH_GENERIC_ISSUSER, &l->l_acflag)) != 0)
149 return (error);
150 #endif
151 break;
152 }
153
154 switch (cmd) {
155 case FIFOQ_ENABLE:
156 ifacep = (struct fifoq_interface *)addr;
157 if ((q = altq_lookup(ifacep->fifoq_ifname, ALTQT_FIFOQ))
158 == NULL) {
159 error = EBADF;
160 break;
161 }
162 error = altq_enable(q->q_ifq);
163 break;
164
165 case FIFOQ_DISABLE:
166 ifacep = (struct fifoq_interface *)addr;
167 if ((q = altq_lookup(ifacep->fifoq_ifname, ALTQT_FIFOQ))
168 == NULL) {
169 error = EBADF;
170 break;
171 }
172 error = altq_disable(q->q_ifq);
173 break;
174
175 case FIFOQ_IF_ATTACH:
176 ifp = ifunit(((struct fifoq_interface *)addr)->fifoq_ifname);
177 if (ifp == NULL) {
178 error = ENXIO;
179 break;
180 }
181
182 /* allocate and initialize fifoq_state_t */
183 q = malloc(sizeof(fifoq_state_t), M_DEVBUF, M_WAITOK|M_ZERO);
184 if (q == NULL) {
185 error = ENOMEM;
186 break;
187 }
188
189 q->q_ifq = &ifp->if_snd;
190 q->q_head = q->q_tail = NULL;
191 q->q_len = 0;
192 q->q_limit = FIFOQ_LIMIT;
193
194 /*
195 * set FIFOQ to this ifnet structure.
196 */
197 error = altq_attach(q->q_ifq, ALTQT_FIFOQ, q,
198 fifoq_enqueue, fifoq_dequeue, fifoq_request,
199 NULL, NULL);
200 if (error) {
201 free(q, M_DEVBUF);
202 break;
203 }
204
205 /* add this state to the fifoq list */
206 q->q_next = fifoq_list;
207 fifoq_list = q;
208 break;
209
210 case FIFOQ_IF_DETACH:
211 ifacep = (struct fifoq_interface *)addr;
212 if ((q = altq_lookup(ifacep->fifoq_ifname, ALTQT_FIFOQ))
213 == NULL) {
214 error = EBADF;
215 break;
216 }
217 error = fifoq_detach(q);
218 break;
219
220 case FIFOQ_GETSTATS:
221 do {
222 struct fifoq_getstats *q_stats;
223
224 q_stats = (struct fifoq_getstats *)addr;
225 if ((q = altq_lookup(q_stats->iface.fifoq_ifname,
226 ALTQT_FIFOQ)) == NULL) {
227 error = EBADF;
228 break;
229 }
230
231 q_stats->q_len = q->q_len;
232 q_stats->q_limit = q->q_limit;
233 q_stats->xmit_cnt = q->q_stats.xmit_cnt;
234 q_stats->drop_cnt = q->q_stats.drop_cnt;
235 q_stats->period = q->q_stats.period;
236 } while (/*CONSTCOND*/ 0);
237 break;
238
239 case FIFOQ_CONFIG:
240 do {
241 struct fifoq_conf *fc;
242 int limit;
243
244 fc = (struct fifoq_conf *)addr;
245 if ((q = altq_lookup(fc->iface.fifoq_ifname,
246 ALTQT_FIFOQ)) == NULL) {
247 error = EBADF;
248 break;
249 }
250 limit = fc->fifoq_limit;
251 if (limit < 0)
252 limit = 0;
253 q->q_limit = limit;
254 fc->fifoq_limit = limit;
255 } while (/*CONSTCOND*/ 0);
256 break;
257
258 default:
259 error = EINVAL;
260 break;
261 }
262 return error;
263 }
264
265 /*
266 * fifoq support routines
267 */
268
269 /*
270 * enqueue routine:
271 *
272 * returns: 0 when successfully queued.
273 * ENOBUFS when drop occurs.
274 */
275 static int
276 fifoq_enqueue(ifq, m, pktattr)
277 struct ifaltq *ifq;
278 struct mbuf *m;
279 struct altq_pktattr *pktattr;
280 {
281 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc;
282
283 /* if the queue is full, drop the incoming packet(drop-tail) */
284 if (q->q_len >= q->q_limit) {
285 #ifdef FIFOQ_STATS
286 PKTCNTR_ADD(&q->q_stats.drop_cnt, m_pktlen(m));
287 #endif
288 m_freem(m);
289 return (ENOBUFS);
290 }
291
292 /* enqueue the packet at the taile of the queue */
293 m->m_nextpkt = NULL;
294 if (q->q_tail == NULL)
295 q->q_head = m;
296 else
297 q->q_tail->m_nextpkt = m;
298 q->q_tail = m;
299 q->q_len++;
300 ifq->ifq_len++;
301 return 0;
302 }
303
304 /*
305 * dequeue routine:
306 * must be called in splnet.
307 *
308 * returns: mbuf dequeued.
309 * NULL when no packet is available in the queue.
310 */
311 /*
312 * ALTDQ_PEEK is provided for drivers which need to know the next packet
313 * to send in advance.
314 * when ALTDQ_PEEK is specified, the next packet to be dequeued is
315 * returned without dequeueing the packet.
316 * when ALTDQ_DEQUEUE is called *immediately after* an ALTDQ_PEEK
317 * operation, the same packet should be returned.
318 */
319 static struct mbuf *
320 fifoq_dequeue(ifq, op)
321 struct ifaltq *ifq;
322 int op;
323 {
324 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc;
325 struct mbuf *m = NULL;
326
327 if (op == ALTDQ_POLL)
328 return (q->q_head);
329
330 if ((m = q->q_head) == NULL)
331 return (NULL);
332
333 if ((q->q_head = m->m_nextpkt) == NULL)
334 q->q_tail = NULL;
335 m->m_nextpkt = NULL;
336 q->q_len--;
337 ifq->ifq_len--;
338 #ifdef FIFOQ_STATS
339 PKTCNTR_ADD(&q->q_stats.xmit_cnt, m_pktlen(m));
340 if (q->q_len == 0)
341 q->q_stats.period++;
342 #endif
343 return (m);
344 }
345
346 static int
347 fifoq_request(ifq, req, arg)
348 struct ifaltq *ifq;
349 int req;
350 void *arg;
351 {
352 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc;
353
354 switch (req) {
355 case ALTRQ_PURGE:
356 fifoq_purge(q);
357 break;
358 }
359 return (0);
360 }
361
362
363 static int fifoq_detach(q)
364 fifoq_state_t *q;
365 {
366 fifoq_state_t *tmp;
367 int error = 0;
368
369 if (ALTQ_IS_ENABLED(q->q_ifq))
370 altq_disable(q->q_ifq);
371
372 fifoq_purge(q);
373
374 if ((error = altq_detach(q->q_ifq)))
375 return (error);
376
377 if (fifoq_list == q)
378 fifoq_list = q->q_next;
379 else {
380 for (tmp = fifoq_list; tmp != NULL; tmp = tmp->q_next)
381 if (tmp->q_next == q) {
382 tmp->q_next = q->q_next;
383 break;
384 }
385 if (tmp == NULL)
386 printf("fifoq_detach: no state in fifoq_list!\n");
387 }
388
389 free(q, M_DEVBUF);
390 return (error);
391 }
392
393 /*
394 * fifoq_purge
395 * should be called in splnet or after disabling the fifoq.
396 */
397 static void fifoq_purge(q)
398 fifoq_state_t *q;
399 {
400 struct mbuf *m;
401
402 while ((m = q->q_head) != NULL) {
403 q->q_head = m->m_nextpkt;
404 m_freem(m);
405 }
406 q->q_tail = NULL;
407 q->q_len = 0;
408 if (ALTQ_IS_ENABLED(q->q_ifq))
409 q->q_ifq->ifq_len = 0;
410 }
411
412 #ifdef KLD_MODULE
413
414 static struct altqsw fifoq_sw =
415 {"fifoq", fifoqopen, fifoqclose, fifoqioctl};
416
417 ALTQ_MODULE(altq_fifoq, ALTQT_FIFOQ, &fifoq_sw);
418
419 #endif /* KLD_MODULE */
420
421 #endif /* ALTQ3_COMPAT */
422 #endif /* ALTQ_FIFOQ */
423