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