1 /* $NetBSD: altq_fifoq.c,v 1.19 2025/01/08 13:00:04 joe 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.19 2025/01/08 13:00:04 joe 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 ((error = kauth_authorize_network(l->l_cred, 135 KAUTH_NETWORK_ALTQ, KAUTH_REQ_NETWORK_ALTQ_FIFOQ, NULL, 136 NULL, NULL)) != 0) 137 return error; 138 break; 139 } 140 141 switch (cmd) { 142 case FIFOQ_ENABLE: 143 ifacep = (struct fifoq_interface *)addr; 144 if ((q = altq_lookup(ifacep->fifoq_ifname, ALTQT_FIFOQ)) 145 == NULL) { 146 error = EBADF; 147 break; 148 } 149 error = altq_enable(q->q_ifq); 150 break; 151 152 case FIFOQ_DISABLE: 153 ifacep = (struct fifoq_interface *)addr; 154 if ((q = altq_lookup(ifacep->fifoq_ifname, ALTQT_FIFOQ)) 155 == NULL) { 156 error = EBADF; 157 break; 158 } 159 error = altq_disable(q->q_ifq); 160 break; 161 162 case FIFOQ_IF_ATTACH: 163 ifp = ifunit(((struct fifoq_interface *)addr)->fifoq_ifname); 164 if (ifp == NULL) { 165 error = ENXIO; 166 break; 167 } 168 169 /* allocate and initialize fifoq_state_t */ 170 q = malloc(sizeof(fifoq_state_t), M_DEVBUF, M_WAITOK|M_ZERO); 171 if (q == NULL) { 172 error = ENOMEM; 173 break; 174 } 175 176 q->q_ifq = &ifp->if_snd; 177 q->q_head = q->q_tail = NULL; 178 q->q_len = 0; 179 q->q_limit = FIFOQ_LIMIT; 180 181 /* 182 * set FIFOQ to this ifnet structure. 183 */ 184 error = altq_attach(q->q_ifq, ALTQT_FIFOQ, q, 185 fifoq_enqueue, fifoq_dequeue, fifoq_request, 186 NULL, NULL); 187 if (error) { 188 free(q, M_DEVBUF); 189 break; 190 } 191 192 /* add this state to the fifoq list */ 193 q->q_next = fifoq_list; 194 fifoq_list = q; 195 break; 196 197 case FIFOQ_IF_DETACH: 198 ifacep = (struct fifoq_interface *)addr; 199 if ((q = altq_lookup(ifacep->fifoq_ifname, ALTQT_FIFOQ)) 200 == NULL) { 201 error = EBADF; 202 break; 203 } 204 error = fifoq_detach(q); 205 break; 206 207 case FIFOQ_GETSTATS: 208 do { 209 struct fifoq_getstats *q_stats; 210 211 q_stats = (struct fifoq_getstats *)addr; 212 if ((q = altq_lookup(q_stats->iface.fifoq_ifname, 213 ALTQT_FIFOQ)) == NULL) { 214 error = EBADF; 215 break; 216 } 217 218 q_stats->q_len = q->q_len; 219 q_stats->q_limit = q->q_limit; 220 q_stats->xmit_cnt = q->q_stats.xmit_cnt; 221 q_stats->drop_cnt = q->q_stats.drop_cnt; 222 q_stats->period = q->q_stats.period; 223 } while (/*CONSTCOND*/ 0); 224 break; 225 226 case FIFOQ_CONFIG: 227 do { 228 struct fifoq_conf *fc; 229 int limit; 230 231 fc = (struct fifoq_conf *)addr; 232 if ((q = altq_lookup(fc->iface.fifoq_ifname, 233 ALTQT_FIFOQ)) == NULL) { 234 error = EBADF; 235 break; 236 } 237 limit = fc->fifoq_limit; 238 if (limit < 0) 239 limit = 0; 240 q->q_limit = limit; 241 fc->fifoq_limit = limit; 242 } while (/*CONSTCOND*/ 0); 243 break; 244 245 default: 246 error = EINVAL; 247 break; 248 } 249 return error; 250 } 251 252 /* 253 * fifoq support routines 254 */ 255 256 /* 257 * enqueue routine: 258 * 259 * returns: 0 when successfully queued. 260 * ENOBUFS when drop occurs. 261 */ 262 static int 263 fifoq_enqueue(struct ifaltq *ifq, struct mbuf *m) 264 { 265 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc; 266 267 /* if the queue is full, drop the incoming packet(drop-tail) */ 268 if (q->q_len >= q->q_limit) { 269 #ifdef FIFOQ_STATS 270 PKTCNTR_ADD(&q->q_stats.drop_cnt, m_pktlen(m)); 271 #endif 272 m_freem(m); 273 return ENOBUFS; 274 } 275 276 /* enqueue the packet at the taile of the queue */ 277 m->m_nextpkt = NULL; 278 if (q->q_tail == NULL) 279 q->q_head = m; 280 else 281 q->q_tail->m_nextpkt = m; 282 q->q_tail = m; 283 q->q_len++; 284 ifq->ifq_len++; 285 return 0; 286 } 287 288 /* 289 * dequeue routine: 290 * must be called in splnet. 291 * 292 * returns: mbuf dequeued. 293 * NULL when no packet is available in the queue. 294 */ 295 /* 296 * ALTDQ_PEEK is provided for drivers which need to know the next packet 297 * to send in advance. 298 * when ALTDQ_PEEK is specified, the next packet to be dequeued is 299 * returned without dequeueing the packet. 300 * when ALTDQ_DEQUEUE is called *immediately after* an ALTDQ_PEEK 301 * operation, the same packet should be returned. 302 */ 303 static struct mbuf * 304 fifoq_dequeue(struct ifaltq *ifq, int op) 305 { 306 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc; 307 struct mbuf *m = NULL; 308 309 if (op == ALTDQ_POLL) 310 return (q->q_head); 311 312 if ((m = q->q_head) == NULL) 313 return NULL; 314 315 if ((q->q_head = m->m_nextpkt) == NULL) 316 q->q_tail = NULL; 317 m->m_nextpkt = NULL; 318 q->q_len--; 319 ifq->ifq_len--; 320 #ifdef FIFOQ_STATS 321 PKTCNTR_ADD(&q->q_stats.xmit_cnt, m_pktlen(m)); 322 if (q->q_len == 0) 323 q->q_stats.period++; 324 #endif 325 return m; 326 } 327 328 static int 329 fifoq_request(struct ifaltq *ifq, int req, void *arg) 330 { 331 fifoq_state_t *q = (fifoq_state_t *)ifq->altq_disc; 332 333 switch (req) { 334 case ALTRQ_PURGE: 335 fifoq_purge(q); 336 break; 337 } 338 return 0; 339 } 340 341 342 static int 343 fifoq_detach(fifoq_state_t *q) 344 { 345 fifoq_state_t *tmp; 346 int error = 0; 347 348 if (ALTQ_IS_ENABLED(q->q_ifq)) 349 altq_disable(q->q_ifq); 350 351 fifoq_purge(q); 352 353 if ((error = altq_detach(q->q_ifq))) 354 return error; 355 356 if (fifoq_list == q) 357 fifoq_list = q->q_next; 358 else { 359 for (tmp = fifoq_list; tmp != NULL; tmp = tmp->q_next) 360 if (tmp->q_next == q) { 361 tmp->q_next = q->q_next; 362 break; 363 } 364 if (tmp == NULL) 365 printf("fifoq_detach: no state in fifoq_list!\n"); 366 } 367 368 free(q, M_DEVBUF); 369 return error; 370 } 371 372 /* 373 * fifoq_purge 374 * should be called in splnet or after disabling the fifoq. 375 */ 376 static void 377 fifoq_purge(fifoq_state_t *q) 378 { 379 struct mbuf *m; 380 381 while ((m = q->q_head) != NULL) { 382 q->q_head = m->m_nextpkt; 383 m_freem(m); 384 } 385 q->q_tail = NULL; 386 q->q_len = 0; 387 if (ALTQ_IS_ENABLED(q->q_ifq)) 388 q->q_ifq->ifq_len = 0; 389 } 390 391 #ifdef KLD_MODULE 392 393 static struct altqsw fifoq_sw = 394 {"fifoq", fifoqopen, fifoqclose, fifoqioctl}; 395 396 ALTQ_MODULE(altq_fifoq, ALTQT_FIFOQ, &fifoq_sw); 397 398 #endif /* KLD_MODULE */ 399 400 #endif /* ALTQ3_COMPAT */ 401 #endif /* ALTQ_FIFOQ */ 402