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 pf_altq *a) 227 { 228 cbq_state_t *cbqp; 229 struct ifnet *ifp; 230 231 if ((ifp = ifunit(a->ifname)) == 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