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