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