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