1 /* $NetBSD: altq_fifoq.c,v 1.16 2007/03/04 05:59:01 christos 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.16 2007/03/04 05:59:01 christos 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_t dev, int flag, int fmt, 87 struct lwp *l) 88 { 89 /* everything will be done when the queueing scheme is attached. */ 90 return 0; 91 } 92 93 /* 94 * there are 2 ways to act on close. 95 * detach-all-on-close: 96 * use for the daemon style approach. if the daemon dies, all the 97 * resource will be released. 98 * no-action-on-close: 99 * use for the command style approach. (e.g. fifoq on/off) 100 * 101 * note: close is called not on every close but when the last reference 102 * is removed (only once with multiple simultaneous references.) 103 */ 104 int 105 fifoqclose(dev_t dev, int flag, int fmt, 106 struct lwp *l) 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_t dev, ioctlcmd_t cmd, void *addr, int flag, 123 struct lwp *l) 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 = kauth_authorize_network(l->l_cred, 140 KAUTH_NETWORK_ALTQ, KAUTH_REQ_NETWORK_ALTQ_FIFOQ, NULL, 141 NULL, NULL)) != 0) 142 return (error); 143 #endif 144 break; 145 } 146 147 switch (cmd) { 148 case FIFOQ_ENABLE: 149 ifacep = (struct fifoq_interface *)addr; 150 if ((q = altq_lookup(ifacep->fifoq_ifname, ALTQT_FIFOQ)) 151 == NULL) { 152 error = EBADF; 153 break; 154 } 155 error = altq_enable(q->q_ifq); 156 break; 157 158 case FIFOQ_DISABLE: 159 ifacep = (struct fifoq_interface *)addr; 160 if ((q = altq_lookup(ifacep->fifoq_ifname, ALTQT_FIFOQ)) 161 == NULL) { 162 error = EBADF; 163 break; 164 } 165 error = altq_disable(q->q_ifq); 166 break; 167 168 case FIFOQ_IF_ATTACH: 169 ifp = ifunit(((struct fifoq_interface *)addr)->fifoq_ifname); 170 if (ifp == NULL) { 171 error = ENXIO; 172 break; 173 } 174 175 /* allocate and initialize fifoq_state_t */ 176 q = malloc(sizeof(fifoq_state_t), M_DEVBUF, M_WAITOK|M_ZERO); 177 if (q == NULL) { 178 error = ENOMEM; 179 break; 180 } 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 (/*CONSTCOND*/ 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 (/*CONSTCOND*/ 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(struct ifaltq *ifq, struct mbuf *m, 270 struct altq_pktattr *pktattr) 271 { 272 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc; 273 274 /* if the queue is full, drop the incoming packet(drop-tail) */ 275 if (q->q_len >= q->q_limit) { 276 #ifdef FIFOQ_STATS 277 PKTCNTR_ADD(&q->q_stats.drop_cnt, m_pktlen(m)); 278 #endif 279 m_freem(m); 280 return (ENOBUFS); 281 } 282 283 /* enqueue the packet at the taile of the queue */ 284 m->m_nextpkt = NULL; 285 if (q->q_tail == NULL) 286 q->q_head = m; 287 else 288 q->q_tail->m_nextpkt = m; 289 q->q_tail = m; 290 q->q_len++; 291 ifq->ifq_len++; 292 return 0; 293 } 294 295 /* 296 * dequeue routine: 297 * must be called in splnet. 298 * 299 * returns: mbuf dequeued. 300 * NULL when no packet is available in the queue. 301 */ 302 /* 303 * ALTDQ_PEEK is provided for drivers which need to know the next packet 304 * to send in advance. 305 * when ALTDQ_PEEK is specified, the next packet to be dequeued is 306 * returned without dequeueing the packet. 307 * when ALTDQ_DEQUEUE is called *immediately after* an ALTDQ_PEEK 308 * operation, the same packet should be returned. 309 */ 310 static struct mbuf * 311 fifoq_dequeue(struct ifaltq *ifq, int op) 312 { 313 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc; 314 struct mbuf *m = NULL; 315 316 if (op == ALTDQ_POLL) 317 return (q->q_head); 318 319 if ((m = q->q_head) == NULL) 320 return (NULL); 321 322 if ((q->q_head = m->m_nextpkt) == NULL) 323 q->q_tail = NULL; 324 m->m_nextpkt = NULL; 325 q->q_len--; 326 ifq->ifq_len--; 327 #ifdef FIFOQ_STATS 328 PKTCNTR_ADD(&q->q_stats.xmit_cnt, m_pktlen(m)); 329 if (q->q_len == 0) 330 q->q_stats.period++; 331 #endif 332 return (m); 333 } 334 335 static int 336 fifoq_request(struct ifaltq *ifq, int req, void *arg) 337 { 338 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc; 339 340 switch (req) { 341 case ALTRQ_PURGE: 342 fifoq_purge(q); 343 break; 344 } 345 return (0); 346 } 347 348 349 static int 350 fifoq_detach(fifoq_state_t *q) 351 { 352 fifoq_state_t *tmp; 353 int error = 0; 354 355 if (ALTQ_IS_ENABLED(q->q_ifq)) 356 altq_disable(q->q_ifq); 357 358 fifoq_purge(q); 359 360 if ((error = altq_detach(q->q_ifq))) 361 return (error); 362 363 if (fifoq_list == q) 364 fifoq_list = q->q_next; 365 else { 366 for (tmp = fifoq_list; tmp != NULL; tmp = tmp->q_next) 367 if (tmp->q_next == q) { 368 tmp->q_next = q->q_next; 369 break; 370 } 371 if (tmp == NULL) 372 printf("fifoq_detach: no state in fifoq_list!\n"); 373 } 374 375 free(q, M_DEVBUF); 376 return (error); 377 } 378 379 /* 380 * fifoq_purge 381 * should be called in splnet or after disabling the fifoq. 382 */ 383 static void 384 fifoq_purge(fifoq_state_t *q) 385 { 386 struct mbuf *m; 387 388 while ((m = q->q_head) != NULL) { 389 q->q_head = m->m_nextpkt; 390 m_freem(m); 391 } 392 q->q_tail = NULL; 393 q->q_len = 0; 394 if (ALTQ_IS_ENABLED(q->q_ifq)) 395 q->q_ifq->ifq_len = 0; 396 } 397 398 #ifdef KLD_MODULE 399 400 static struct altqsw fifoq_sw = 401 {"fifoq", fifoqopen, fifoqclose, fifoqioctl}; 402 403 ALTQ_MODULE(altq_fifoq, ALTQT_FIFOQ, &fifoq_sw); 404 405 #endif /* KLD_MODULE */ 406 407 #endif /* ALTQ3_COMPAT */ 408 #endif /* ALTQ_FIFOQ */ 409