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