1/* $FreeBSD$ */ 2/* $KAME: altq_cbq.c,v 1.19 2003/09/17 14:23:25 kjc Exp $ */ 3 4/* 5 * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the SMCC Technology 21 * Development Group at Sun Microsystems, Inc. 22 * 23 * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or 24 * promote products derived from this software without specific prior 25 * written permission. 26 * 27 * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE 28 * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is 29 * provided "as is" without express or implied warranty of any kind. 30 * 31 * These notices must be retained in any copies of any part of this software. 32 */ 33 34#if defined(__FreeBSD__) || defined(__NetBSD__) 35#include "opt_altq.h" 36#include "opt_inet.h" 37#ifdef __FreeBSD__ 38#include "opt_inet6.h" 39#endif 40#endif /* __FreeBSD__ || __NetBSD__ */ 41#ifdef ALTQ_CBQ /* cbq is enabled by ALTQ_CBQ option in opt_altq.h */ 42 43#include <sys/param.h> 44#include <sys/malloc.h> 45#include <sys/mbuf.h> 46#include <sys/socket.h> 47#include <sys/systm.h> 48#include <sys/proc.h> 49#include <sys/errno.h> 50#include <sys/time.h> 51#ifdef ALTQ3_COMPAT 52#include <sys/uio.h> 53#include <sys/kernel.h> 54#endif 55 56#include <net/if.h> 57#include <netinet/in.h> 58 59#include <net/pfvar.h> 60#include <altq/altq.h> 61#include <altq/altq_cbq.h> 62#ifdef ALTQ3_COMPAT 63#include <altq/altq_conf.h> 64#endif 65 66#ifdef ALTQ3_COMPAT 67/* 68 * Local Data structures. 69 */ 70static cbq_state_t *cbq_list = NULL; 71#endif 72 73/* 74 * Forward Declarations. 75 */ 76static int cbq_class_destroy(cbq_state_t *, struct rm_class *); 77static struct rm_class *clh_to_clp(cbq_state_t *, u_int32_t); 78static int cbq_clear_interface(cbq_state_t *); 79static int cbq_request(struct ifaltq *, int, void *); 80static int cbq_enqueue(struct ifaltq *, struct mbuf *, 81 struct altq_pktattr *); 82static struct mbuf *cbq_dequeue(struct ifaltq *, int); 83static void cbqrestart(struct ifaltq *); 84static void get_class_stats(class_stats_t *, struct rm_class *); 85static void cbq_purge(cbq_state_t *); 86#ifdef ALTQ3_COMPAT 87static int cbq_add_class(struct cbq_add_class *); 88static int cbq_delete_class(struct cbq_delete_class *); 89static int cbq_modify_class(struct cbq_modify_class *); 90static int cbq_class_create(cbq_state_t *, struct cbq_add_class *, 91 struct rm_class *, struct rm_class *); 92static int cbq_clear_hierarchy(struct cbq_interface *); 93static int cbq_set_enable(struct cbq_interface *, int); 94static int cbq_ifattach(struct cbq_interface *); 95static int cbq_ifdetach(struct cbq_interface *); 96static int cbq_getstats(struct cbq_getstats *); 97 98static int cbq_add_filter(struct cbq_add_filter *); 99static int cbq_delete_filter(struct cbq_delete_filter *); 100#endif /* ALTQ3_COMPAT */ 101 102/* 103 * int 104 * cbq_class_destroy(cbq_mod_state_t *, struct rm_class *) - This 105 * function destroys a given traffic class. Before destroying 106 * the class, all traffic for that class is released. 107 */ 108static int 109cbq_class_destroy(cbq_state_t *cbqp, struct rm_class *cl) 110{ 111 int i; 112 113 /* delete the class */ 114 rmc_delete_class(&cbqp->ifnp, cl); 115 116 /* 117 * free the class handle 118 */ 119 for (i = 0; i < CBQ_MAX_CLASSES; i++) 120 if (cbqp->cbq_class_tbl[i] == cl) 121 cbqp->cbq_class_tbl[i] = NULL; 122 123 if (cl == cbqp->ifnp.root_) 124 cbqp->ifnp.root_ = NULL; 125 if (cl == cbqp->ifnp.default_) 126 cbqp->ifnp.default_ = NULL; 127#ifdef ALTQ3_COMPAT 128 if (cl == cbqp->ifnp.ctl_) 129 cbqp->ifnp.ctl_ = NULL; 130#endif 131 return (0); 132} 133 134/* convert class handle to class pointer */ 135static struct rm_class * 136clh_to_clp(cbq_state_t *cbqp, u_int32_t chandle) 137{ 138 int i; 139 struct rm_class *cl; 140 141 if (chandle == 0) 142 return (NULL); 143 /* 144 * first, try optimistically the slot matching the lower bits of 145 * the handle. if it fails, do the linear table search. 146 */ 147 i = chandle % CBQ_MAX_CLASSES; 148 if ((cl = cbqp->cbq_class_tbl[i]) != NULL && 149 cl->stats_.handle == chandle) 150 return (cl); 151 for (i = 0; i < CBQ_MAX_CLASSES; i++) 152 if ((cl = cbqp->cbq_class_tbl[i]) != NULL && 153 cl->stats_.handle == chandle) 154 return (cl); 155 return (NULL); 156} 157 158static int 159cbq_clear_interface(cbq_state_t *cbqp) 160{ 161 int again, i; 162 struct rm_class *cl; 163 164#ifdef ALTQ3_CLFIER_COMPAT 165 /* free the filters for this interface */ 166 acc_discard_filters(&cbqp->cbq_classifier, NULL, 1); 167#endif 168 169 /* clear out the classes now */ 170 do { 171 again = 0; 172 for (i = 0; i < CBQ_MAX_CLASSES; i++) { 173 if ((cl = cbqp->cbq_class_tbl[i]) != NULL) { 174 if (is_a_parent_class(cl)) 175 again++; 176 else { 177 cbq_class_destroy(cbqp, cl); 178 cbqp->cbq_class_tbl[i] = NULL; 179 if (cl == cbqp->ifnp.root_) 180 cbqp->ifnp.root_ = NULL; 181 if (cl == cbqp->ifnp.default_) 182 cbqp->ifnp.default_ = NULL; 183#ifdef ALTQ3_COMPAT 184 if (cl == cbqp->ifnp.ctl_) 185 cbqp->ifnp.ctl_ = NULL; 186#endif 187 } 188 } 189 } 190 } while (again); 191 192 return (0); 193} 194 195static int 196cbq_request(struct ifaltq *ifq, int req, void *arg) 197{ 198 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 199 200 IFQ_LOCK_ASSERT(ifq); 201 202 switch (req) { 203 case ALTRQ_PURGE: 204 cbq_purge(cbqp); 205 break; 206 } 207 return (0); 208} 209 210/* copy the stats info in rm_class to class_states_t */ 211static void 212get_class_stats(class_stats_t *statsp, struct rm_class *cl) 213{ 214 statsp->xmit_cnt = cl->stats_.xmit_cnt; 215 statsp->drop_cnt = cl->stats_.drop_cnt; 216 statsp->over = cl->stats_.over; 217 statsp->borrows = cl->stats_.borrows; 218 statsp->overactions = cl->stats_.overactions; 219 statsp->delays = cl->stats_.delays; 220 221 statsp->depth = cl->depth_; 222 statsp->priority = cl->pri_; 223 statsp->maxidle = cl->maxidle_; 224 statsp->minidle = cl->minidle_; 225 statsp->offtime = cl->offtime_; 226 statsp->qmax = qlimit(cl->q_); 227 statsp->ns_per_byte = cl->ns_per_byte_; 228 statsp->wrr_allot = cl->w_allotment_; 229 statsp->qcnt = qlen(cl->q_); 230 statsp->avgidle = cl->avgidle_; 231 232 statsp->qtype = qtype(cl->q_); 233#ifdef ALTQ_RED 234 if (q_is_red(cl->q_)) 235 red_getstats(cl->red_, &statsp->red[0]); 236#endif 237#ifdef ALTQ_RIO 238 if (q_is_rio(cl->q_)) 239 rio_getstats((rio_t *)cl->red_, &statsp->red[0]); 240#endif 241} 242 243int 244cbq_pfattach(struct pf_altq *a) 245{ 246 struct ifnet *ifp; 247 int s, error; 248 249 if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL) 250 return (EINVAL); 251#ifdef __NetBSD__ 252 s = splnet(); 253#else 254 s = splimp(); 255#endif 256 error = altq_attach(&ifp->if_snd, ALTQT_CBQ, a->altq_disc, 257 cbq_enqueue, cbq_dequeue, cbq_request, NULL, NULL); 258 splx(s); 259 return (error); 260} 261 262int 263cbq_add_altq(struct pf_altq *a) 264{ 265 cbq_state_t *cbqp; 266 struct ifnet *ifp; 267 268 if ((ifp = ifunit(a->ifname)) == NULL) 269 return (EINVAL); 270 if (!ALTQ_IS_READY(&ifp->if_snd)) 271 return (ENODEV); 272 273 /* allocate and initialize cbq_state_t */ 274 cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_WAITOK); 275 if (cbqp == NULL) 276 return (ENOMEM); 277 bzero(cbqp, sizeof(cbq_state_t)); 278 CALLOUT_INIT(&cbqp->cbq_callout); 279 cbqp->cbq_qlen = 0; 280 cbqp->ifnp.ifq_ = &ifp->if_snd; /* keep the ifq */ 281 282 /* keep the state in pf_altq */ 283 a->altq_disc = cbqp; 284 285 return (0); 286} 287 288int 289cbq_remove_altq(struct pf_altq *a) 290{ 291 cbq_state_t *cbqp; 292 293 if ((cbqp = a->altq_disc) == NULL) 294 return (EINVAL); 295 a->altq_disc = NULL; 296 297 cbq_clear_interface(cbqp); 298 299 if (cbqp->ifnp.default_) 300 cbq_class_destroy(cbqp, cbqp->ifnp.default_); 301 if (cbqp->ifnp.root_) 302 cbq_class_destroy(cbqp, cbqp->ifnp.root_); 303 304 /* deallocate cbq_state_t */ 305 free(cbqp, M_DEVBUF); 306 307 return (0); 308} 309 310int 311cbq_add_queue(struct pf_altq *a) 312{ 313 struct rm_class *borrow, *parent; 314 cbq_state_t *cbqp; 315 struct rm_class *cl; 316 struct cbq_opts *opts; 317 int i; 318 319 if ((cbqp = a->altq_disc) == NULL) 320 return (EINVAL); 321 if (a->qid == 0) 322 return (EINVAL); 323 324 /* 325 * find a free slot in the class table. if the slot matching 326 * the lower bits of qid is free, use this slot. otherwise, 327 * use the first free slot. 328 */ 329 i = a->qid % CBQ_MAX_CLASSES; 330 if (cbqp->cbq_class_tbl[i] != NULL) { 331 for (i = 0; i < CBQ_MAX_CLASSES; i++) 332 if (cbqp->cbq_class_tbl[i] == NULL) 333 break; 334 if (i == CBQ_MAX_CLASSES) 335 return (EINVAL); 336 } 337 338 opts = &a->pq_u.cbq_opts; 339 /* check parameters */ 340 if (a->priority >= CBQ_MAXPRI) 341 return (EINVAL); 342 343 /* Get pointers to parent and borrow classes. */ 344 parent = clh_to_clp(cbqp, a->parent_qid); 345 if (opts->flags & CBQCLF_BORROW) 346 borrow = parent; 347 else 348 borrow = NULL; 349 350 /* 351 * A class must borrow from it's parent or it can not 352 * borrow at all. Hence, borrow can be null. 353 */ 354 if (parent == NULL && (opts->flags & CBQCLF_ROOTCLASS) == 0) { 355 printf("cbq_add_queue: no parent class!\n"); 356 return (EINVAL); 357 } 358 359 if ((borrow != parent) && (borrow != NULL)) { 360 printf("cbq_add_class: borrow class != parent\n"); 361 return (EINVAL); 362 } 363 364 /* 365 * check parameters 366 */ 367 switch (opts->flags & CBQCLF_CLASSMASK) { 368 case CBQCLF_ROOTCLASS: 369 if (parent != NULL) 370 return (EINVAL); 371 if (cbqp->ifnp.root_) 372 return (EINVAL); 373 break; 374 case CBQCLF_DEFCLASS: 375 if (cbqp->ifnp.default_) 376 return (EINVAL); 377 break; 378 case 0: 379 if (a->qid == 0) 380 return (EINVAL); 381 break; 382 default: 383 /* more than two flags bits set */ 384 return (EINVAL); 385 } 386 387 /* 388 * create a class. if this is a root class, initialize the 389 * interface. 390 */ 391 if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) { 392 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, opts->ns_per_byte, 393 cbqrestart, a->qlimit, RM_MAXQUEUED, 394 opts->maxidle, opts->minidle, opts->offtime, 395 opts->flags); 396 cl = cbqp->ifnp.root_; 397 } else { 398 cl = rmc_newclass(a->priority, 399 &cbqp->ifnp, opts->ns_per_byte, 400 rmc_delay_action, a->qlimit, parent, borrow, 401 opts->maxidle, opts->minidle, opts->offtime, 402 opts->pktsize, opts->flags); 403 } 404 if (cl == NULL) 405 return (ENOMEM); 406 407 /* return handle to user space. */ 408 cl->stats_.handle = a->qid; 409 cl->stats_.depth = cl->depth_; 410 411 /* save the allocated class */ 412 cbqp->cbq_class_tbl[i] = cl; 413 414 if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS) 415 cbqp->ifnp.default_ = cl; 416 417 return (0); 418} 419 420int 421cbq_remove_queue(struct pf_altq *a) 422{ 423 struct rm_class *cl; 424 cbq_state_t *cbqp; 425 int i; 426 427 if ((cbqp = a->altq_disc) == NULL) 428 return (EINVAL); 429 430 if ((cl = clh_to_clp(cbqp, a->qid)) == NULL) 431 return (EINVAL); 432 433 /* if we are a parent class, then return an error. */ 434 if (is_a_parent_class(cl)) 435 return (EINVAL); 436 437 /* delete the class */ 438 rmc_delete_class(&cbqp->ifnp, cl); 439 440 /* 441 * free the class handle 442 */ 443 for (i = 0; i < CBQ_MAX_CLASSES; i++) 444 if (cbqp->cbq_class_tbl[i] == cl) { 445 cbqp->cbq_class_tbl[i] = NULL; 446 if (cl == cbqp->ifnp.root_) 447 cbqp->ifnp.root_ = NULL; 448 if (cl == cbqp->ifnp.default_) 449 cbqp->ifnp.default_ = NULL; 450 break; 451 } 452 453 return (0); 454} 455 456int 457cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) 458{ 459 cbq_state_t *cbqp; 460 struct rm_class *cl; 461 class_stats_t stats; 462 int error = 0; 463 464 if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL) 465 return (EBADF); 466 467 if ((cl = clh_to_clp(cbqp, a->qid)) == NULL) 468 return (EINVAL); 469 470 if (*nbytes < sizeof(stats)) 471 return (EINVAL); 472 473 get_class_stats(&stats, cl); 474 475 if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0) 476 return (error); 477 *nbytes = sizeof(stats); 478 return (0); 479} 480 481/* 482 * int 483 * cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pattr) 484 * - Queue data packets. 485 * 486 * cbq_enqueue is set to ifp->if_altqenqueue and called by an upper 487 * layer (e.g. ether_output). cbq_enqueue queues the given packet 488 * to the cbq, then invokes the driver's start routine. 489 * 490 * Assumptions: called in splimp 491 * Returns: 0 if the queueing is successful. 492 * ENOBUFS if a packet dropping occurred as a result of 493 * the queueing. 494 */ 495 496static int 497cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr) 498{ 499 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 500 struct rm_class *cl; 501 struct pf_mtag *t; 502 int len; 503 504 IFQ_LOCK_ASSERT(ifq); 505 506 /* grab class set by classifier */ 507 if ((m->m_flags & M_PKTHDR) == 0) { 508 /* should not happen */ 509 printf("altq: packet for %s does not have pkthdr\n", 510 ifq->altq_ifp->if_xname); 511 m_freem(m); 512 return (ENOBUFS); 513 } 514 cl = NULL; 515 if ((t = pf_find_mtag(m)) != NULL) 516 cl = clh_to_clp(cbqp, t->qid); 517#ifdef ALTQ3_COMPAT 518 else if ((ifq->altq_flags & ALTQF_CLASSIFY) && pktattr != NULL) 519 cl = pktattr->pattr_class; 520#endif 521 if (cl == NULL) { 522 cl = cbqp->ifnp.default_; 523 if (cl == NULL) { 524 m_freem(m); 525 return (ENOBUFS); 526 } 527 } 528#ifdef ALTQ3_COMPAT 529 if (pktattr != NULL) 530 cl->pktattr_ = pktattr; /* save proto hdr used by ECN */ 531 else 532#endif 533 cl->pktattr_ = NULL; 534 len = m_pktlen(m); 535 if (rmc_queue_packet(cl, m) != 0) { 536 /* drop occurred. some mbuf was freed in rmc_queue_packet. */ 537 PKTCNTR_ADD(&cl->stats_.drop_cnt, len); 538 return (ENOBUFS); 539 } 540 541 /* successfully queued. */ 542 ++cbqp->cbq_qlen; 543 IFQ_INC_LEN(ifq); 544 return (0); 545} 546 547static struct mbuf * 548cbq_dequeue(struct ifaltq *ifq, int op) 549{ 550 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 551 struct mbuf *m; 552 553 IFQ_LOCK_ASSERT(ifq); 554 555 m = rmc_dequeue_next(&cbqp->ifnp, op); 556 557 if (m && op == ALTDQ_REMOVE) { 558 --cbqp->cbq_qlen; /* decrement # of packets in cbq */ 559 IFQ_DEC_LEN(ifq); 560 561 /* Update the class. */ 562 rmc_update_class_util(&cbqp->ifnp); 563 } 564 return (m); 565} 566 567/* 568 * void 569 * cbqrestart(queue_t *) - Restart sending of data. 570 * called from rmc_restart in splimp via timeout after waking up 571 * a suspended class. 572 * Returns: NONE 573 */ 574 575static void 576cbqrestart(struct ifaltq *ifq) 577{ 578 cbq_state_t *cbqp; 579 struct ifnet *ifp; 580 581 IFQ_LOCK_ASSERT(ifq); 582 583 if (!ALTQ_IS_ENABLED(ifq)) 584 /* cbq must have been detached */ 585 return; 586 587 if ((cbqp = (cbq_state_t *)ifq->altq_disc) == NULL) 588 /* should not happen */ 589 return; 590 591 ifp = ifq->altq_ifp; 592 if (ifp->if_start && 593 cbqp->cbq_qlen > 0 && (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { 594 IFQ_UNLOCK(ifq); 595 (*ifp->if_start)(ifp); 596 IFQ_LOCK(ifq); 597 } 598} 599 600static void cbq_purge(cbq_state_t *cbqp) 601{ 602 struct rm_class *cl; 603 int i; 604 605 for (i = 0; i < CBQ_MAX_CLASSES; i++) 606 if ((cl = cbqp->cbq_class_tbl[i]) != NULL) 607 rmc_dropall(cl); 608 if (ALTQ_IS_ENABLED(cbqp->ifnp.ifq_)) 609 cbqp->ifnp.ifq_->ifq_len = 0; 610} 611#ifdef ALTQ3_COMPAT 612 613static int 614cbq_add_class(acp) 615 struct cbq_add_class *acp; 616{ 617 char *ifacename; 618 struct rm_class *borrow, *parent; 619 cbq_state_t *cbqp; 620 621 ifacename = acp->cbq_iface.cbq_ifacename; 622 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 623 return (EBADF); 624 625 /* check parameters */ 626 if (acp->cbq_class.priority >= CBQ_MAXPRI || 627 acp->cbq_class.maxq > CBQ_MAXQSIZE) 628 return (EINVAL); 629 630 /* Get pointers to parent and borrow classes. */ 631 parent = clh_to_clp(cbqp, acp->cbq_class.parent_class_handle); 632 borrow = clh_to_clp(cbqp, acp->cbq_class.borrow_class_handle); 633 634 /* 635 * A class must borrow from it's parent or it can not 636 * borrow at all. Hence, borrow can be null. 637 */ 638 if (parent == NULL && (acp->cbq_class.flags & CBQCLF_ROOTCLASS) == 0) { 639 printf("cbq_add_class: no parent class!\n"); 640 return (EINVAL); 641 } 642 643 if ((borrow != parent) && (borrow != NULL)) { 644 printf("cbq_add_class: borrow class != parent\n"); 645 return (EINVAL); 646 } 647 648 return cbq_class_create(cbqp, acp, parent, borrow); 649} 650 651static int 652cbq_delete_class(dcp) 653 struct cbq_delete_class *dcp; 654{ 655 char *ifacename; 656 struct rm_class *cl; 657 cbq_state_t *cbqp; 658 659 ifacename = dcp->cbq_iface.cbq_ifacename; 660 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 661 return (EBADF); 662 663 if ((cl = clh_to_clp(cbqp, dcp->cbq_class_handle)) == NULL) 664 return (EINVAL); 665 666 /* if we are a parent class, then return an error. */ 667 if (is_a_parent_class(cl)) 668 return (EINVAL); 669 670 /* if a filter has a reference to this class delete the filter */ 671 acc_discard_filters(&cbqp->cbq_classifier, cl, 0); 672 673 return cbq_class_destroy(cbqp, cl); 674} 675 676static int 677cbq_modify_class(acp) 678 struct cbq_modify_class *acp; 679{ 680 char *ifacename; 681 struct rm_class *cl; 682 cbq_state_t *cbqp; 683 684 ifacename = acp->cbq_iface.cbq_ifacename; 685 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 686 return (EBADF); 687 688 /* Get pointer to this class */ 689 if ((cl = clh_to_clp(cbqp, acp->cbq_class_handle)) == NULL) 690 return (EINVAL); 691 692 if (rmc_modclass(cl, acp->cbq_class.nano_sec_per_byte, 693 acp->cbq_class.maxq, acp->cbq_class.maxidle, 694 acp->cbq_class.minidle, acp->cbq_class.offtime, 695 acp->cbq_class.pktsize) < 0) 696 return (EINVAL); 697 return (0); 698} 699 700/* 701 * struct rm_class * 702 * cbq_class_create(cbq_mod_state_t *cbqp, struct cbq_add_class *acp, 703 * struct rm_class *parent, struct rm_class *borrow) 704 * 705 * This function create a new traffic class in the CBQ class hierarchy of 706 * given paramters. The class that created is either the root, default, 707 * or a new dynamic class. If CBQ is not initilaized, the the root class 708 * will be created. 709 */ 710static int 711cbq_class_create(cbqp, acp, parent, borrow) 712 cbq_state_t *cbqp; 713 struct cbq_add_class *acp; 714 struct rm_class *parent, *borrow; 715{ 716 struct rm_class *cl; 717 cbq_class_spec_t *spec = &acp->cbq_class; 718 u_int32_t chandle; 719 int i; 720 721 /* 722 * allocate class handle 723 */ 724 for (i = 1; i < CBQ_MAX_CLASSES; i++) 725 if (cbqp->cbq_class_tbl[i] == NULL) 726 break; 727 if (i == CBQ_MAX_CLASSES) 728 return (EINVAL); 729 chandle = i; /* use the slot number as class handle */ 730 731 /* 732 * create a class. if this is a root class, initialize the 733 * interface. 734 */ 735 if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) { 736 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, spec->nano_sec_per_byte, 737 cbqrestart, spec->maxq, RM_MAXQUEUED, 738 spec->maxidle, spec->minidle, spec->offtime, 739 spec->flags); 740 cl = cbqp->ifnp.root_; 741 } else { 742 cl = rmc_newclass(spec->priority, 743 &cbqp->ifnp, spec->nano_sec_per_byte, 744 rmc_delay_action, spec->maxq, parent, borrow, 745 spec->maxidle, spec->minidle, spec->offtime, 746 spec->pktsize, spec->flags); 747 } 748 if (cl == NULL) 749 return (ENOMEM); 750 751 /* return handle to user space. */ 752 acp->cbq_class_handle = chandle; 753 754 cl->stats_.handle = chandle; 755 cl->stats_.depth = cl->depth_; 756 757 /* save the allocated class */ 758 cbqp->cbq_class_tbl[i] = cl; 759 760 if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS) 761 cbqp->ifnp.default_ = cl; 762 if ((spec->flags & CBQCLF_CLASSMASK) == CBQCLF_CTLCLASS) 763 cbqp->ifnp.ctl_ = cl; 764 765 return (0); 766} 767 768static int 769cbq_add_filter(afp) 770 struct cbq_add_filter *afp; 771{ 772 char *ifacename; 773 cbq_state_t *cbqp; 774 struct rm_class *cl; 775 776 ifacename = afp->cbq_iface.cbq_ifacename; 777 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 778 return (EBADF); 779 780 /* Get the pointer to class. */ 781 if ((cl = clh_to_clp(cbqp, afp->cbq_class_handle)) == NULL) 782 return (EINVAL); 783 784 return acc_add_filter(&cbqp->cbq_classifier, &afp->cbq_filter, 785 cl, &afp->cbq_filter_handle); 786} 787 788static int 789cbq_delete_filter(dfp) 790 struct cbq_delete_filter *dfp; 791{ 792 char *ifacename; 793 cbq_state_t *cbqp; 794 795 ifacename = dfp->cbq_iface.cbq_ifacename; 796 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 797 return (EBADF); 798 799 return acc_delete_filter(&cbqp->cbq_classifier, 800 dfp->cbq_filter_handle); 801} 802 803/* 804 * cbq_clear_hierarchy deletes all classes and their filters on the 805 * given interface. 806 */ 807static int 808cbq_clear_hierarchy(ifacep) 809 struct cbq_interface *ifacep; 810{ 811 char *ifacename; 812 cbq_state_t *cbqp; 813 814 ifacename = ifacep->cbq_ifacename; 815 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 816 return (EBADF); 817 818 return cbq_clear_interface(cbqp); 819} 820 821/* 822 * static int 823 * cbq_set_enable(struct cbq_enable *ep) - this function processed the 824 * ioctl request to enable class based queueing. It searches the list 825 * of interfaces for the specified interface and then enables CBQ on 826 * that interface. 827 * 828 * Returns: 0, for no error. 829 * EBADF, for specified inteface not found. 830 */ 831 832static int 833cbq_set_enable(ep, enable) 834 struct cbq_interface *ep; 835 int enable; 836{ 837 int error = 0; 838 cbq_state_t *cbqp; 839 char *ifacename; 840 841 ifacename = ep->cbq_ifacename; 842 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 843 return (EBADF); 844 845 switch (enable) { 846 case ENABLE: 847 if (cbqp->ifnp.root_ == NULL || cbqp->ifnp.default_ == NULL || 848 cbqp->ifnp.ctl_ == NULL) { 849 if (cbqp->ifnp.root_ == NULL) 850 printf("No Root Class for %s\n", ifacename); 851 if (cbqp->ifnp.default_ == NULL) 852 printf("No Default Class for %s\n", ifacename); 853 if (cbqp->ifnp.ctl_ == NULL) 854 printf("No Control Class for %s\n", ifacename); 855 error = EINVAL; 856 } else if ((error = altq_enable(cbqp->ifnp.ifq_)) == 0) { 857 cbqp->cbq_qlen = 0; 858 } 859 break; 860 861 case DISABLE: 862 error = altq_disable(cbqp->ifnp.ifq_); 863 break; 864 } 865 return (error); 866} 867 868static int 869cbq_getstats(gsp) 870 struct cbq_getstats *gsp; 871{ 872 char *ifacename; 873 int i, n, nclasses; 874 cbq_state_t *cbqp; 875 struct rm_class *cl; 876 class_stats_t stats, *usp; 877 int error = 0; 878 879 ifacename = gsp->iface.cbq_ifacename; 880 nclasses = gsp->nclasses; 881 usp = gsp->stats; 882 883 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 884 return (EBADF); 885 if (nclasses <= 0) 886 return (EINVAL); 887 888 for (n = 0, i = 0; n < nclasses && i < CBQ_MAX_CLASSES; n++, i++) { 889 while ((cl = cbqp->cbq_class_tbl[i]) == NULL) 890 if (++i >= CBQ_MAX_CLASSES) 891 goto out; 892 893 get_class_stats(&stats, cl); 894 stats.handle = cl->stats_.handle; 895 896 if ((error = copyout((caddr_t)&stats, (caddr_t)usp++, 897 sizeof(stats))) != 0) 898 return (error); 899 } 900 901 out: 902 gsp->nclasses = n; 903 return (error); 904} 905 906static int 907cbq_ifattach(ifacep) 908 struct cbq_interface *ifacep; 909{ 910 int error = 0; 911 char *ifacename; 912 cbq_state_t *new_cbqp; 913 struct ifnet *ifp; 914 915 ifacename = ifacep->cbq_ifacename; 916 if ((ifp = ifunit(ifacename)) == NULL) 917 return (ENXIO); 918 if (!ALTQ_IS_READY(&ifp->if_snd)) 919 return (ENXIO); 920 921 /* allocate and initialize cbq_state_t */ 922 new_cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_WAITOK); 923 if (new_cbqp == NULL) 924 return (ENOMEM); 925 bzero(new_cbqp, sizeof(cbq_state_t)); 926 CALLOUT_INIT(&new_cbqp->cbq_callout); 927 928 new_cbqp->cbq_qlen = 0; 929 new_cbqp->ifnp.ifq_ = &ifp->if_snd; /* keep the ifq */ 930 931 /* 932 * set CBQ to this ifnet structure. 933 */ 934 error = altq_attach(&ifp->if_snd, ALTQT_CBQ, new_cbqp, 935 cbq_enqueue, cbq_dequeue, cbq_request, 936 &new_cbqp->cbq_classifier, acc_classify); 937 if (error) { 938 free(new_cbqp, M_DEVBUF); 939 return (error); 940 } 941 942 /* prepend to the list of cbq_state_t's. */ 943 new_cbqp->cbq_next = cbq_list; 944 cbq_list = new_cbqp; 945 946 return (0); 947} 948 949static int 950cbq_ifdetach(ifacep) 951 struct cbq_interface *ifacep; 952{ 953 char *ifacename; 954 cbq_state_t *cbqp; 955 956 ifacename = ifacep->cbq_ifacename; 957 if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL) 958 return (EBADF); 959 960 (void)cbq_set_enable(ifacep, DISABLE); 961 962 cbq_clear_interface(cbqp); 963 964 /* remove CBQ from the ifnet structure. */ 965 (void)altq_detach(cbqp->ifnp.ifq_); 966 967 /* remove from the list of cbq_state_t's. */ 968 if (cbq_list == cbqp) 969 cbq_list = cbqp->cbq_next; 970 else { 971 cbq_state_t *cp; 972 973 for (cp = cbq_list; cp != NULL; cp = cp->cbq_next) 974 if (cp->cbq_next == cbqp) { 975 cp->cbq_next = cbqp->cbq_next; 976 break; 977 } 978 ASSERT(cp != NULL); 979 } 980 981 /* deallocate cbq_state_t */ 982 free(cbqp, M_DEVBUF); 983 984 return (0); 985} 986 987/* 988 * cbq device interface 989 */ 990 991altqdev_decl(cbq); 992 993int 994cbqopen(dev, flag, fmt, p) 995 dev_t dev; 996 int flag, fmt; 997#if (__FreeBSD_version > 500000) 998 struct thread *p; 999#else 1000 struct proc *p; 1001#endif 1002{ 1003 return (0); 1004} 1005 1006int 1007cbqclose(dev, flag, fmt, p) 1008 dev_t dev; 1009 int flag, fmt; 1010#if (__FreeBSD_version > 500000) 1011 struct thread *p; 1012#else 1013 struct proc *p; 1014#endif 1015{ 1016 struct ifnet *ifp; 1017 struct cbq_interface iface; 1018 int err, error = 0; 1019 1020 while (cbq_list) { 1021 ifp = cbq_list->ifnp.ifq_->altq_ifp; 1022 sprintf(iface.cbq_ifacename, "%s", ifp->if_xname); 1023 err = cbq_ifdetach(&iface); 1024 if (err != 0 && error == 0) 1025 error = err; 1026 } 1027 1028 return (error); 1029} 1030 1031int 1032cbqioctl(dev, cmd, addr, flag, p) 1033 dev_t dev; 1034 ioctlcmd_t cmd; 1035 caddr_t addr; 1036 int flag; 1037#if (__FreeBSD_version > 500000) 1038 struct thread *p; 1039#else 1040 struct proc *p; 1041#endif 1042{ 1043 int error = 0; 1044 1045 /* check cmd for superuser only */ 1046 switch (cmd) { 1047 case CBQ_GETSTATS: 1048 /* currently only command that an ordinary user can call */ 1049 break; 1050 default: 1051#if (__FreeBSD_version > 700000) 1052 error = priv_check(p, PRIV_ALTQ_MANAGE); 1053#elsif (__FreeBSD_version > 400000) 1054 error = suser(p); 1055#else 1056 error = suser(p->p_ucred, &p->p_acflag); 1057#endif 1058 if (error) 1059 return (error); 1060 break; 1061 } 1062 1063 switch (cmd) { 1064 1065 case CBQ_ENABLE: 1066 error = cbq_set_enable((struct cbq_interface *)addr, ENABLE); 1067 break; 1068 1069 case CBQ_DISABLE: 1070 error = cbq_set_enable((struct cbq_interface *)addr, DISABLE); 1071 break; 1072 1073 case CBQ_ADD_FILTER: 1074 error = cbq_add_filter((struct cbq_add_filter *)addr); 1075 break; 1076 1077 case CBQ_DEL_FILTER: 1078 error = cbq_delete_filter((struct cbq_delete_filter *)addr); 1079 break; 1080 1081 case CBQ_ADD_CLASS: 1082 error = cbq_add_class((struct cbq_add_class *)addr); 1083 break; 1084 1085 case CBQ_DEL_CLASS: 1086 error = cbq_delete_class((struct cbq_delete_class *)addr); 1087 break; 1088 1089 case CBQ_MODIFY_CLASS: 1090 error = cbq_modify_class((struct cbq_modify_class *)addr); 1091 break; 1092 1093 case CBQ_CLEAR_HIERARCHY: 1094 error = cbq_clear_hierarchy((struct cbq_interface *)addr); 1095 break; 1096 1097 case CBQ_IF_ATTACH: 1098 error = cbq_ifattach((struct cbq_interface *)addr); 1099 break; 1100 1101 case CBQ_IF_DETACH: 1102 error = cbq_ifdetach((struct cbq_interface *)addr); 1103 break; 1104 1105 case CBQ_GETSTATS: 1106 error = cbq_getstats((struct cbq_getstats *)addr); 1107 break; 1108 1109 default: 1110 error = EINVAL; 1111 break; 1112 } 1113 1114 return error; 1115} 1116 1117#if 0 1118/* for debug */ 1119static void cbq_class_dump(int); 1120 1121static void cbq_class_dump(i) 1122 int i; 1123{ 1124 struct rm_class *cl; 1125 rm_class_stats_t *s; 1126 struct _class_queue_ *q; 1127 1128 if (cbq_list == NULL) { 1129 printf("cbq_class_dump: no cbq_state found\n"); 1130 return; 1131 } 1132 cl = cbq_list->cbq_class_tbl[i]; 1133 1134 printf("class %d cl=%p\n", i, cl); 1135 if (cl != NULL) { 1136 s = &cl->stats_; 1137 q = cl->q_; 1138 1139 printf("pri=%d, depth=%d, maxrate=%d, allotment=%d\n", 1140 cl->pri_, cl->depth_, cl->maxrate_, cl->allotment_); 1141 printf("w_allotment=%d, bytes_alloc=%d, avgidle=%d, maxidle=%d\n", 1142 cl->w_allotment_, cl->bytes_alloc_, cl->avgidle_, 1143 cl->maxidle_); 1144 printf("minidle=%d, offtime=%d, sleeping=%d, leaf=%d\n", 1145 cl->minidle_, cl->offtime_, cl->sleeping_, cl->leaf_); 1146 printf("handle=%d, depth=%d, packets=%d, bytes=%d\n", 1147 s->handle, s->depth, 1148 (int)s->xmit_cnt.packets, (int)s->xmit_cnt.bytes); 1149 printf("over=%d\n, borrows=%d, drops=%d, overactions=%d, delays=%d\n", 1150 s->over, s->borrows, (int)s->drop_cnt.packets, 1151 s->overactions, s->delays); 1152 printf("tail=%p, head=%p, qlen=%d, qlim=%d, qthresh=%d,qtype=%d\n", 1153 q->tail_, q->head_, q->qlen_, q->qlim_, 1154 q->qthresh_, q->qtype_); 1155 } 1156} 1157#endif /* 0 */ 1158 1159#ifdef KLD_MODULE 1160 1161static struct altqsw cbq_sw = 1162 {"cbq", cbqopen, cbqclose, cbqioctl}; 1163 1164ALTQ_MODULE(altq_cbq, ALTQT_CBQ, &cbq_sw); 1165MODULE_DEPEND(altq_cbq, altq_red, 1, 1, 1); 1166MODULE_DEPEND(altq_cbq, altq_rio, 1, 1, 1); 1167 1168#endif /* KLD_MODULE */ 1169#endif /* ALTQ3_COMPAT */ 1170 1171#endif /* ALTQ_CBQ */ 1172