1 /* $KAME: altq_cbq.c,v 1.20 2004/04/17 10:54:48 kjc Exp $ */ 2 /* $DragonFly: src/sys/net/altq/altq_cbq.c,v 1.1 2005/02/11 22:25:57 joerg 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 #include "opt_altq.h" 35 #include "opt_inet.h" 36 #include "opt_inet6.h" 37 38 #ifdef ALTQ_CBQ /* cbq is enabled by ALTQ_CBQ option in opt_altq.h */ 39 40 #include <sys/param.h> 41 #include <sys/malloc.h> 42 #include <sys/mbuf.h> 43 #include <sys/socket.h> 44 #include <sys/systm.h> 45 #include <sys/proc.h> 46 #include <sys/callout.h> 47 #include <sys/errno.h> 48 #include <sys/time.h> 49 50 #include <net/if.h> 51 #include <net/ifq_var.h> 52 #include <netinet/in.h> 53 54 #include <net/pf/pfvar.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 *, uint32_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, uint32_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 /* clear out the classes now */ 131 do { 132 again = 0; 133 for (i = 0; i < CBQ_MAX_CLASSES; i++) { 134 if ((cl = cbqp->cbq_class_tbl[i]) != NULL) { 135 if (is_a_parent_class(cl)) 136 again++; 137 else { 138 cbq_class_destroy(cbqp, cl); 139 cbqp->cbq_class_tbl[i] = NULL; 140 if (cl == cbqp->ifnp.root_) 141 cbqp->ifnp.root_ = NULL; 142 if (cl == cbqp->ifnp.default_) 143 cbqp->ifnp.default_ = NULL; 144 } 145 } 146 } 147 } while (again); 148 149 return (0); 150 } 151 152 static int 153 cbq_request(struct ifaltq *ifq, int req, void *arg) 154 { 155 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 156 157 switch (req) { 158 case ALTRQ_PURGE: 159 cbq_purge(cbqp); 160 break; 161 } 162 return (0); 163 } 164 165 /* copy the stats info in rm_class to class_states_t */ 166 static void 167 get_class_stats(class_stats_t *statsp, struct rm_class *cl) 168 { 169 statsp->xmit_cnt = cl->stats_.xmit_cnt; 170 statsp->drop_cnt = cl->stats_.drop_cnt; 171 statsp->over = cl->stats_.over; 172 statsp->borrows = cl->stats_.borrows; 173 statsp->overactions = cl->stats_.overactions; 174 statsp->delays = cl->stats_.delays; 175 176 statsp->depth = cl->depth_; 177 statsp->priority = cl->pri_; 178 statsp->maxidle = cl->maxidle_; 179 statsp->minidle = cl->minidle_; 180 statsp->offtime = cl->offtime_; 181 statsp->qmax = qlimit(cl->q_); 182 statsp->ns_per_byte = cl->ns_per_byte_; 183 statsp->wrr_allot = cl->w_allotment_; 184 statsp->qcnt = qlen(cl->q_); 185 statsp->avgidle = cl->avgidle_; 186 187 statsp->qtype = qtype(cl->q_); 188 #ifdef ALTQ_RED 189 if (q_is_red(cl->q_)) 190 red_getstats(cl->red_, &statsp->red[0]); 191 #endif 192 #ifdef ALTQ_RIO 193 if (q_is_rio(cl->q_)) 194 rio_getstats((rio_t *)cl->red_, &statsp->red[0]); 195 #endif 196 } 197 198 int 199 cbq_pfattach(struct pf_altq *a) 200 { 201 struct ifnet *ifp; 202 int s, error; 203 204 if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL) 205 return (EINVAL); 206 s = splimp(); 207 error = altq_attach(&ifp->if_snd, ALTQT_CBQ, a->altq_disc, 208 cbq_enqueue, cbq_dequeue, cbq_request, NULL, NULL); 209 splx(s); 210 return (error); 211 } 212 213 int 214 cbq_add_altq(struct pf_altq *a) 215 { 216 cbq_state_t *cbqp; 217 struct ifnet *ifp; 218 219 if ((ifp = ifunit(a->ifname)) == NULL) 220 return (EINVAL); 221 if (!ifq_is_ready(&ifp->if_snd)) 222 return (ENODEV); 223 224 /* allocate and initialize cbq_state_t */ 225 cbqp = malloc(sizeof(*cbqp), M_ALTQ, M_WAITOK | M_ZERO); 226 callout_init(&cbqp->cbq_callout); 227 cbqp->cbq_qlen = 0; 228 cbqp->ifnp.ifq_ = &ifp->if_snd; /* keep the ifq */ 229 230 /* keep the state in pf_altq */ 231 a->altq_disc = cbqp; 232 233 return (0); 234 } 235 236 int 237 cbq_remove_altq(struct pf_altq *a) 238 { 239 cbq_state_t *cbqp; 240 241 if ((cbqp = a->altq_disc) == NULL) 242 return (EINVAL); 243 a->altq_disc = NULL; 244 245 cbq_clear_interface(cbqp); 246 247 if (cbqp->ifnp.default_) 248 cbq_class_destroy(cbqp, cbqp->ifnp.default_); 249 if (cbqp->ifnp.root_) 250 cbq_class_destroy(cbqp, cbqp->ifnp.root_); 251 252 /* deallocate cbq_state_t */ 253 free(cbqp, M_ALTQ); 254 255 return (0); 256 } 257 258 int 259 cbq_add_queue(struct pf_altq *a) 260 { 261 struct rm_class *borrow, *parent; 262 cbq_state_t *cbqp; 263 struct rm_class *cl; 264 struct cbq_opts *opts; 265 int i; 266 267 if ((cbqp = a->altq_disc) == NULL) 268 return (EINVAL); 269 if (a->qid == 0) 270 return (EINVAL); 271 272 /* 273 * find a free slot in the class table. if the slot matching 274 * the lower bits of qid is free, use this slot. otherwise, 275 * use the first free slot. 276 */ 277 i = a->qid % CBQ_MAX_CLASSES; 278 if (cbqp->cbq_class_tbl[i] != NULL) { 279 for (i = 0; i < CBQ_MAX_CLASSES; i++) 280 if (cbqp->cbq_class_tbl[i] == NULL) 281 break; 282 if (i == CBQ_MAX_CLASSES) 283 return (EINVAL); 284 } 285 286 opts = &a->pq_u.cbq_opts; 287 /* check parameters */ 288 if (a->priority >= CBQ_MAXPRI) 289 return (EINVAL); 290 291 /* Get pointers to parent and borrow classes. */ 292 parent = clh_to_clp(cbqp, a->parent_qid); 293 if (opts->flags & CBQCLF_BORROW) 294 borrow = parent; 295 else 296 borrow = NULL; 297 298 /* 299 * A class must borrow from it's parent or it can not 300 * borrow at all. Hence, borrow can be null. 301 */ 302 if (parent == NULL && (opts->flags & CBQCLF_ROOTCLASS) == 0) { 303 printf("cbq_add_queue: no parent class!\n"); 304 return (EINVAL); 305 } 306 307 if ((borrow != parent) && (borrow != NULL)) { 308 printf("cbq_add_class: borrow class != parent\n"); 309 return (EINVAL); 310 } 311 312 /* 313 * check parameters 314 */ 315 switch (opts->flags & CBQCLF_CLASSMASK) { 316 case CBQCLF_ROOTCLASS: 317 if (parent != NULL) 318 return (EINVAL); 319 if (cbqp->ifnp.root_) 320 return (EINVAL); 321 break; 322 case CBQCLF_DEFCLASS: 323 if (cbqp->ifnp.default_) 324 return (EINVAL); 325 break; 326 case 0: 327 if (a->qid == 0) 328 return (EINVAL); 329 break; 330 default: 331 /* more than two flags bits set */ 332 return (EINVAL); 333 } 334 335 /* 336 * create a class. if this is a root class, initialize the 337 * interface. 338 */ 339 if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_ROOTCLASS) { 340 rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp, opts->ns_per_byte, 341 cbqrestart, a->qlimit, RM_MAXQUEUED, 342 opts->maxidle, opts->minidle, opts->offtime, 343 opts->flags); 344 cl = cbqp->ifnp.root_; 345 } else { 346 cl = rmc_newclass(a->priority, 347 &cbqp->ifnp, opts->ns_per_byte, 348 rmc_delay_action, a->qlimit, parent, borrow, 349 opts->maxidle, opts->minidle, opts->offtime, 350 opts->pktsize, opts->flags); 351 } 352 if (cl == NULL) 353 return (ENOMEM); 354 355 /* return handle to user space. */ 356 cl->stats_.handle = a->qid; 357 cl->stats_.depth = cl->depth_; 358 359 /* save the allocated class */ 360 cbqp->cbq_class_tbl[i] = cl; 361 362 if ((opts->flags & CBQCLF_CLASSMASK) == CBQCLF_DEFCLASS) 363 cbqp->ifnp.default_ = cl; 364 365 return (0); 366 } 367 368 int 369 cbq_remove_queue(struct pf_altq *a) 370 { 371 struct rm_class *cl; 372 cbq_state_t *cbqp; 373 int i; 374 375 if ((cbqp = a->altq_disc) == NULL) 376 return (EINVAL); 377 378 if ((cl = clh_to_clp(cbqp, a->qid)) == NULL) 379 return (EINVAL); 380 381 /* if we are a parent class, then return an error. */ 382 if (is_a_parent_class(cl)) 383 return (EINVAL); 384 385 /* delete the class */ 386 rmc_delete_class(&cbqp->ifnp, cl); 387 388 /* 389 * free the class handle 390 */ 391 for (i = 0; i < CBQ_MAX_CLASSES; i++) 392 if (cbqp->cbq_class_tbl[i] == cl) { 393 cbqp->cbq_class_tbl[i] = NULL; 394 if (cl == cbqp->ifnp.root_) 395 cbqp->ifnp.root_ = NULL; 396 if (cl == cbqp->ifnp.default_) 397 cbqp->ifnp.default_ = NULL; 398 break; 399 } 400 401 return (0); 402 } 403 404 int 405 cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes) 406 { 407 cbq_state_t *cbqp; 408 struct rm_class *cl; 409 class_stats_t stats; 410 int error = 0; 411 412 if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL) 413 return (EBADF); 414 415 if ((cl = clh_to_clp(cbqp, a->qid)) == NULL) 416 return (EINVAL); 417 418 if (*nbytes < sizeof(stats)) 419 return (EINVAL); 420 421 get_class_stats(&stats, cl); 422 423 if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0) 424 return (error); 425 *nbytes = sizeof(stats); 426 return (0); 427 } 428 429 /* 430 * int 431 * cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pattr) 432 * - Queue data packets. 433 * 434 * cbq_enqueue is set to ifp->if_altqenqueue and called by an upper 435 * layer (e.g. ether_output). cbq_enqueue queues the given packet 436 * to the cbq, then invokes the driver's start routine. 437 * 438 * Assumptions: called in splimp 439 * Returns: 0 if the queueing is successful. 440 * ENOBUFS if a packet dropping occurred as a result of 441 * the queueing. 442 */ 443 444 static int 445 cbq_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr) 446 { 447 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 448 struct rm_class *cl; 449 int len; 450 451 /* grab class set by classifier */ 452 if ((m->m_flags & M_PKTHDR) == 0) { 453 /* should not happen */ 454 if_printf(ifq->altq_ifp, "altq: packet does not have pkthdr\n"); 455 m_freem(m); 456 return (ENOBUFS); 457 } 458 if (m->m_pkthdr.fw_flags & ALTQ_MBUF_TAGGED) 459 cl = clh_to_clp(cbqp, m->m_pkthdr.altq_qid); 460 else 461 cl = NULL; 462 if (cl == NULL) { 463 cl = cbqp->ifnp.default_; 464 if (cl == NULL) { 465 m_freem(m); 466 return (ENOBUFS); 467 } 468 } 469 cl->pktattr_ = NULL; 470 len = m_pktlen(m); 471 if (rmc_queue_packet(cl, m) != 0) { 472 /* drop occurred. some mbuf was freed in rmc_queue_packet. */ 473 PKTCNTR_ADD(&cl->stats_.drop_cnt, len); 474 return (ENOBUFS); 475 } 476 477 /* successfully queued. */ 478 ++cbqp->cbq_qlen; 479 ++ifq->ifq_len; 480 return (0); 481 } 482 483 static struct mbuf * 484 cbq_dequeue(struct ifaltq *ifq, int op) 485 { 486 cbq_state_t *cbqp = (cbq_state_t *)ifq->altq_disc; 487 struct mbuf *m; 488 489 m = rmc_dequeue_next(&cbqp->ifnp, op); 490 491 if (m && op == ALTDQ_REMOVE) { 492 --cbqp->cbq_qlen; /* decrement # of packets in cbq */ 493 --ifq->ifq_len; 494 495 /* Update the class. */ 496 rmc_update_class_util(&cbqp->ifnp); 497 } 498 return (m); 499 } 500 501 /* 502 * void 503 * cbqrestart(queue_t *) - Restart sending of data. 504 * called from rmc_restart in splimp via timeout after waking up 505 * a suspended class. 506 * Returns: NONE 507 */ 508 509 static void 510 cbqrestart(struct ifaltq *ifq) 511 { 512 cbq_state_t *cbqp; 513 struct ifnet *ifp; 514 515 if (!ifq_is_enabled(ifq)) 516 /* cbq must have been detached */ 517 return; 518 519 if ((cbqp = (cbq_state_t *)ifq->altq_disc) == NULL) 520 /* should not happen */ 521 return; 522 523 ifp = ifq->altq_ifp; 524 if (ifp->if_start && 525 cbqp->cbq_qlen > 0 && (ifp->if_flags & IFF_OACTIVE) == 0) 526 (*ifp->if_start)(ifp); 527 } 528 529 static void 530 cbq_purge(cbq_state_t *cbqp) 531 { 532 struct rm_class *cl; 533 int i; 534 535 for (i = 0; i < CBQ_MAX_CLASSES; i++) 536 if ((cl = cbqp->cbq_class_tbl[i]) != NULL) 537 rmc_dropall(cl); 538 if (ifq_is_enabled(cbqp->ifnp.ifq_)) 539 cbqp->ifnp.ifq_->ifq_len = 0; 540 } 541 542 #endif /* ALTQ_CBQ */ 543