xref: /netbsd/sys/altq/altq_fifoq.c (revision bf9ec67e)
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