1 /*- 2 * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the SMCC Technology 18 * Development Group at Sun Microsystems, Inc. 19 * 20 * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or 21 * promote products derived from this software without specific prior 22 * written permission. 23 * 24 * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE 25 * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is 26 * provided "as is" without express or implied warranty of any kind. 27 * 28 * These notices must be retained in any copies of any part of this software. 29 * 30 * $KAME: altq_cbq.c,v 1.19 2003/09/17 14:23:25 kjc Exp $ 31 */ 32 33 #include "opt_altq.h" 34 #include "opt_inet.h" 35 #include "opt_inet6.h" 36 #ifdef ALTQ_CBQ /* cbq is enabled by ALTQ_CBQ option in opt_altq.h */ 37 38 #include <sys/param.h> 39 #include <sys/malloc.h> 40 #include <sys/mbuf.h> 41 #include <sys/socket.h> 42 #include <sys/systm.h> 43 #include <sys/proc.h> 44 #include <sys/errno.h> 45 #include <sys/time.h> 46 47 #include <net/if.h> 48 #include <net/if_var.h> 49 #include <net/if_private.h> 50 #include <netinet/in.h> 51 52 #include <netpfil/pf/pf.h> 53 #include <netpfil/pf/pf_altq.h> 54 #include <netpfil/pf/pf_mtag.h> 55 #include <net/altq/altq.h> 56 #include <net/altq/altq_cbq.h> 57 58 /* 59 * Forward Declarations. 60 */ 61 static int cbq_class_destroy(cbq_state_t *, struct rm_class *); 62 static struct rm_class *clh_to_clp(cbq_state_t *, u_int32_t); 63 static int cbq_clear_interface(cbq_state_t *); 64 static int cbq_request(struct ifaltq *, int, void *); 65 static int cbq_enqueue(struct ifaltq *, struct mbuf *, 66 struct altq_pktattr *); 67 static struct mbuf *cbq_dequeue(struct ifaltq *, int); 68 static void cbqrestart(struct ifaltq *); 69 static void get_class_stats(class_stats_t *, struct rm_class *); 70 static void cbq_purge(cbq_state_t *); 71 72 /* 73 * int 74 * cbq_class_destroy(cbq_mod_state_t *, struct rm_class *) - This 75 * function destroys a given traffic class. Before destroying 76 * the class, all traffic for that class is released. 77 */ 78 static int 79 cbq_class_destroy(cbq_state_t *cbqp, struct rm_class *cl) 80 { 81 int i; 82 83 /* delete the class */ 84 rmc_delete_class(&cbqp->ifnp, cl); 85 86 /* 87 * free the class handle 88 */ 89 for (i = 0; i < CBQ_MAX_CLASSES; i++) 90 if (cbqp->cbq_class_tbl[i] == cl) 91 cbqp->cbq_class_tbl[i] = NULL; 92 93 if (cl == cbqp->ifnp.root_) 94 cbqp->ifnp.root_ = NULL; 95 if (cl == cbqp->ifnp.default_) 96 cbqp->ifnp.default_ = NULL; 97 return (0); 98 } 99 100 /* convert class handle to class pointer */ 101 static struct rm_class * 102 clh_to_clp(cbq_state_t *cbqp, u_int32_t chandle) 103 { 104 int i; 105 struct rm_class *cl; 106 107 if (chandle == 0) 108 return (NULL); 109 /* 110 * first, try optimistically the slot matching the lower bits of 111 * the handle. if it fails, do the linear table search. 112 */ 113 i = chandle % CBQ_MAX_CLASSES; 114 if ((cl = cbqp->cbq_class_tbl[i]) != NULL && 115 cl->stats_.handle == chandle) 116 return (cl); 117 for (i = 0; i < CBQ_MAX_CLASSES; i++) 118 if ((cl = cbqp->cbq_class_tbl[i]) != NULL && 119 cl->stats_.handle == chandle) 120 return (cl); 121 return (NULL); 122 } 123 124 static int 125 cbq_clear_interface(cbq_state_t *cbqp) 126 { 127 int again, i; 128 struct rm_class *cl; 129 130 #ifdef ALTQ3_CLFIER_COMPAT 131 /* free the filters for this interface */ 132 acc_discard_filters(&cbqp->cbq_classifier, NULL, 1); 133 #endif 134 135 /* clear out the classes now */ 136 do { 137 again = 0; 138 for (i = 0; i < CBQ_MAX_CLASSES; i++) { 139 if ((cl = cbqp->cbq_class_tbl[i]) != NULL) { 140 if (is_a_parent_class(cl)) 141 again++; 142 else { 143 cbq_class_destroy(cbqp, cl); 144 cbqp->cbq_class_tbl[i] = NULL; 145 if (cl == cbqp->ifnp.root_) 146 cbqp->ifnp.root_ = NULL; 147 if (cl == cbqp->ifnp.default_) 148 cbqp->ifnp.default_ = NULL; 149 } 150 } 151 } 152 } while (again); 153 154 return (0); 155 } 156 157 static int 158 cbq_request(struct ifaltq *ifq, int req, void *arg) 159 { 160 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 161 162 IFQ_LOCK_ASSERT(ifq); 163 164 switch (req) { 165 case ALTRQ_PURGE: 166 cbq_purge(cbqp); 167 break; 168 } 169 return (0); 170 } 171 172 /* copy the stats info in rm_class to class_states_t */ 173 static void 174 get_class_stats(class_stats_t *statsp, struct rm_class *cl) 175 { 176 memset(statsp, 0, sizeof(*statsp)); 177 178 statsp->xmit_cnt = cl->stats_.xmit_cnt; 179 statsp->drop_cnt = cl->stats_.drop_cnt; 180 statsp->over = cl->stats_.over; 181 statsp->borrows = cl->stats_.borrows; 182 statsp->overactions = cl->stats_.overactions; 183 statsp->delays = cl->stats_.delays; 184 185 statsp->depth = cl->depth_; 186 statsp->priority = cl->pri_; 187 statsp->maxidle = cl->maxidle_; 188 statsp->minidle = cl->minidle_; 189 statsp->offtime = cl->offtime_; 190 statsp->qmax = qlimit(cl->q_); 191 statsp->ns_per_byte = cl->ns_per_byte_; 192 statsp->wrr_allot = cl->w_allotment_; 193 statsp->qcnt = qlen(cl->q_); 194 statsp->avgidle = cl->avgidle_; 195 196 statsp->qtype = qtype(cl->q_); 197 #ifdef ALTQ_RED 198 if (q_is_red(cl->q_)) 199 red_getstats(cl->red_, &statsp->red[0]); 200 #endif 201 #ifdef ALTQ_RIO 202 if (q_is_rio(cl->q_)) 203 rio_getstats((rio_t *)cl->red_, &statsp->red[0]); 204 #endif 205 #ifdef ALTQ_CODEL 206 if (q_is_codel(cl->q_)) 207 codel_getstats(cl->codel_, &statsp->codel); 208 #endif 209 } 210 211 int 212 cbq_pfattach(struct pf_altq *a) 213 { 214 struct ifnet *ifp; 215 int s, error; 216 217 if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL) 218 return (EINVAL); 219 s = splnet(); 220 error = altq_attach(&ifp->if_snd, ALTQT_CBQ, a->altq_disc, 221 cbq_enqueue, cbq_dequeue, cbq_request); 222 splx(s); 223 return (error); 224 } 225 226 int 227 cbq_add_altq(struct ifnet *ifp, struct pf_altq *a) 228 { 229 cbq_state_t *cbqp; 230 231 if (ifp == NULL) 232 return (EINVAL); 233 if (!ALTQ_IS_READY(&ifp->if_snd)) 234 return (ENODEV); 235 236 /* allocate and initialize cbq_state_t */ 237 cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_NOWAIT | M_ZERO); 238 if (cbqp == NULL) 239 return (ENOMEM); 240 CALLOUT_INIT(&cbqp->cbq_callout); 241 cbqp->cbq_qlen = 0; 242 cbqp->ifnp.ifq_ = &ifp->if_snd; /* keep the ifq */ 243 244 /* keep the state in pf_altq */ 245 a->altq_disc = cbqp; 246 247 return (0); 248 } 249 250 int 251 cbq_remove_altq(struct pf_altq *a) 252 { 253 cbq_state_t *cbqp; 254 255 if ((cbqp = a->altq_disc) == NULL) 256 return (EINVAL); 257 a->altq_disc = NULL; 258 259 cbq_clear_interface(cbqp); 260 261 if (cbqp->ifnp.default_) 262 cbq_class_destroy(cbqp, cbqp->ifnp.default_); 263 if (cbqp->ifnp.root_) 264 cbq_class_destroy(cbqp, cbqp->ifnp.root_); 265 266 /* deallocate cbq_state_t */ 267 free(cbqp, M_DEVBUF); 268 269 return (0); 270 } 271 272 int 273 cbq_add_queue(struct pf_altq *a) 274 { 275 struct rm_class *borrow, *parent; 276 cbq_state_t *cbqp; 277 struct rm_class *cl; 278 struct cbq_opts *opts; 279 int i; 280 281 if ((cbqp = a->altq_disc) == NULL) 282 return (EINVAL); 283 if (a->qid == 0) 284 return (EINVAL); 285 286 /* 287 * find a free slot in the class table. if the slot matching 288 * the lower bits of qid is free, use this slot. otherwise, 289 * use the first free slot. 290 */ 291 i = a->qid % CBQ_MAX_CLASSES; 292 if (cbqp->cbq_class_tbl[i] != NULL) { 293 for (i = 0; i < CBQ_MAX_CLASSES; i++) 294 if (cbqp->cbq_class_tbl[i] == NULL) 295 break; 296 if (i == CBQ_MAX_CLASSES) 297 return (EINVAL); 298 } 299 300 opts = &a->pq_u.cbq_opts; 301 /* check parameters */ 302 if (a->priority >= CBQ_MAXPRI) 303 return (EINVAL); 304 305 /* Get pointers to parent and borrow classes. */ 306 parent = clh_to_clp(cbqp, a->parent_qid); 307 if (opts->flags & CBQCLF_BORROW) 308 borrow = parent; 309 else 310 borrow = NULL; 311 312 /* 313 * A class must borrow from it's parent or it can not 314 * borrow at all. Hence, borrow can be null. 315 */ 316 if (parent == NULL && (opts->flags & CBQCLF_ROOTCLASS) == 0) { 317 printf("cbq_add_queue: no parent class!\n"); 318 return (EINVAL); 319 } 320 321 if ((borrow != parent) && (borrow != NULL)) { 322 printf("cbq_add_class: borrow class != parent\n"); 323 return (EINVAL); 324 } 325 326 /* 327 * check parameters 328 */ 329 switch (opts->flags & CBQCLF_CLASSMASK) { 330 case CBQCLF_ROOTCLASS: 331 if (parent != NULL) 332 return (EINVAL); 333 if (cbqp->ifnp.root_) 334 return (EINVAL); 335 break; 336 case CBQCLF_DEFCLASS: 337 if (cbqp->ifnp.default_) 338 return (EINVAL); 339 break; 340 case 0: 341 if (a->qid == 0) 342 return (EINVAL); 343 break; 344 default: 345 /* more than two flags bits set */ 346 return (EINVAL); 347 } 348 349 /* 350 * create a class. if this is a root class, initialize the 351 * interface. 352 */ 353 if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) { 354 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, opts->ns_per_byte, 355 cbqrestart, a->qlimit, RM_MAXQUEUED, 356 opts->maxidle, opts->minidle, opts->offtime, 357 opts->flags); 358 cl = cbqp->ifnp.root_; 359 } else { 360 cl = rmc_newclass(a->priority, 361 &cbqp->ifnp, opts->ns_per_byte, 362 rmc_delay_action, a->qlimit, parent, borrow, 363 opts->maxidle, opts->minidle, opts->offtime, 364 opts->pktsize, opts->flags); 365 } 366 if (cl == NULL) 367 return (ENOMEM); 368 369 /* return handle to user space. */ 370 cl->stats_.handle = a->qid; 371 cl->stats_.depth = cl->depth_; 372 373 /* save the allocated class */ 374 cbqp->cbq_class_tbl[i] = cl; 375 376 if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS) 377 cbqp->ifnp.default_ = cl; 378 379 return (0); 380 } 381 382 int 383 cbq_remove_queue(struct pf_altq *a) 384 { 385 struct rm_class *cl; 386 cbq_state_t *cbqp; 387 int i; 388 389 if ((cbqp = a->altq_disc) == NULL) 390 return (EINVAL); 391 392 if ((cl = clh_to_clp(cbqp, a->qid)) == NULL) 393 return (EINVAL); 394 395 /* if we are a parent class, then return an error. */ 396 if (is_a_parent_class(cl)) 397 return (EINVAL); 398 399 /* delete the class */ 400 rmc_delete_class(&cbqp->ifnp, cl); 401 402 /* 403 * free the class handle 404 */ 405 for (i = 0; i < CBQ_MAX_CLASSES; i++) 406 if (cbqp->cbq_class_tbl[i] == cl) { 407 cbqp->cbq_class_tbl[i] = NULL; 408 if (cl == cbqp->ifnp.root_) 409 cbqp->ifnp.root_ = NULL; 410 if (cl == cbqp->ifnp.default_) 411 cbqp->ifnp.default_ = NULL; 412 break; 413 } 414 415 return (0); 416 } 417 418 int 419 cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes, int version) 420 { 421 cbq_state_t *cbqp; 422 struct rm_class *cl; 423 class_stats_t stats; 424 int error = 0; 425 426 if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL) 427 return (EBADF); 428 429 if ((cl = clh_to_clp(cbqp, a->qid)) == NULL) 430 return (EINVAL); 431 432 if (*nbytes < sizeof(stats)) 433 return (EINVAL); 434 435 get_class_stats(&stats, cl); 436 437 if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0) 438 return (error); 439 *nbytes = sizeof(stats); 440 return (0); 441 } 442 443 /* 444 * int 445 * cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pattr) 446 * - Queue data packets. 447 * 448 * cbq_enqueue is set to ifp->if_altqenqueue and called by an upper 449 * layer (e.g. ether_output). cbq_enqueue queues the given packet 450 * to the cbq, then invokes the driver's start routine. 451 * 452 * Assumptions: called in splimp 453 * Returns: 0 if the queueing is successful. 454 * ENOBUFS if a packet dropping occurred as a result of 455 * the queueing. 456 */ 457 458 static int 459 cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr) 460 { 461 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 462 struct rm_class *cl; 463 struct pf_mtag *t; 464 int len; 465 466 IFQ_LOCK_ASSERT(ifq); 467 468 /* grab class set by classifier */ 469 if ((m->m_flags & M_PKTHDR) == 0) { 470 /* should not happen */ 471 printf("altq: packet for %s does not have pkthdr\n", 472 ifq->altq_ifp->if_xname); 473 m_freem(m); 474 return (ENOBUFS); 475 } 476 cl = NULL; 477 if ((t = pf_find_mtag(m)) != NULL) 478 cl = clh_to_clp(cbqp, t->qid); 479 if (cl == NULL) { 480 cl = cbqp->ifnp.default_; 481 if (cl == NULL) { 482 m_freem(m); 483 return (ENOBUFS); 484 } 485 } 486 cl->pktattr_ = NULL; 487 len = m_pktlen(m); 488 if (rmc_queue_packet(cl, m) != 0) { 489 /* drop occurred. some mbuf was freed in rmc_queue_packet. */ 490 PKTCNTR_ADD(&cl->stats_.drop_cnt, len); 491 return (ENOBUFS); 492 } 493 494 /* successfully queued. */ 495 ++cbqp->cbq_qlen; 496 IFQ_INC_LEN(ifq); 497 return (0); 498 } 499 500 static struct mbuf * 501 cbq_dequeue(struct ifaltq *ifq, int op) 502 { 503 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 504 struct mbuf *m; 505 506 IFQ_LOCK_ASSERT(ifq); 507 508 m = rmc_dequeue_next(&cbqp->ifnp, op); 509 510 if (m && op == ALTDQ_REMOVE) { 511 --cbqp->cbq_qlen; /* decrement # of packets in cbq */ 512 IFQ_DEC_LEN(ifq); 513 514 /* Update the class. */ 515 rmc_update_class_util(&cbqp->ifnp); 516 } 517 return (m); 518 } 519 520 /* 521 * void 522 * cbqrestart(queue_t *) - Restart sending of data. 523 * called from rmc_restart in splimp via timeout after waking up 524 * a suspended class. 525 * Returns: NONE 526 */ 527 528 static void 529 cbqrestart(struct ifaltq *ifq) 530 { 531 cbq_state_t *cbqp; 532 struct ifnet *ifp; 533 534 IFQ_LOCK_ASSERT(ifq); 535 536 if (!ALTQ_IS_ENABLED(ifq)) 537 /* cbq must have been detached */ 538 return; 539 540 if ((cbqp = (cbq_state_t *)ifq->altq_disc) == NULL) 541 /* should not happen */ 542 return; 543 544 ifp = ifq->altq_ifp; 545 if (ifp->if_start && 546 cbqp->cbq_qlen > 0 && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 547 IFQ_UNLOCK(ifq); 548 (*ifp->if_start)(ifp); 549 IFQ_LOCK(ifq); 550 } 551 } 552 553 static void cbq_purge(cbq_state_t *cbqp) 554 { 555 struct rm_class *cl; 556 int i; 557 558 for (i = 0; i < CBQ_MAX_CLASSES; i++) 559 if ((cl = cbqp->cbq_class_tbl[i]) != NULL) 560 rmc_dropall(cl); 561 if (ALTQ_IS_ENABLED(cbqp->ifnp.ifq_)) 562 cbqp->ifnp.ifq_->ifq_len = 0; 563 } 564 565 #endif /* ALTQ_CBQ */ 566