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