1 /* $NetBSD: altq_rio.c,v 1.21 2009/04/18 14:58:02 tsutsui Exp $ */ 2 /* $KAME: altq_rio.c,v 1.19 2005/04/13 03:44:25 suz Exp $ */ 3 4 /* 5 * Copyright (C) 1998-2003 6 * Sony Computer Science Laboratories Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 /* 30 * Copyright (c) 1990-1994 Regents of the University of California. 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. All advertising materials mentioning features or use of this software 42 * must display the following acknowledgement: 43 * This product includes software developed by the Computer Systems 44 * Engineering Group at Lawrence Berkeley Laboratory. 45 * 4. Neither the name of the University nor of the Laboratory may be used 46 * to endorse or promote products derived from this software without 47 * specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 */ 61 62 #include <sys/cdefs.h> 63 __KERNEL_RCSID(0, "$NetBSD: altq_rio.c,v 1.21 2009/04/18 14:58:02 tsutsui Exp $"); 64 65 #ifdef _KERNEL_OPT 66 #include "opt_altq.h" 67 #include "opt_inet.h" 68 #include "pf.h" 69 #endif 70 71 #ifdef ALTQ_RIO /* rio is enabled by ALTQ_RIO option in opt_altq.h */ 72 73 #include <sys/param.h> 74 #include <sys/malloc.h> 75 #include <sys/mbuf.h> 76 #include <sys/socket.h> 77 #include <sys/systm.h> 78 #include <sys/errno.h> 79 #include <sys/kauth.h> 80 #if 1 /* ALTQ3_COMPAT */ 81 #include <sys/proc.h> 82 #include <sys/sockio.h> 83 #include <sys/kernel.h> 84 #endif 85 86 #include <net/if.h> 87 88 #include <netinet/in.h> 89 #include <netinet/in_systm.h> 90 #include <netinet/ip.h> 91 #ifdef INET6 92 #include <netinet/ip6.h> 93 #endif 94 95 #if NPF > 0 96 #include <net/pfvar.h> 97 #endif 98 #include <altq/altq.h> 99 #include <altq/altq_cdnr.h> 100 #include <altq/altq_red.h> 101 #include <altq/altq_rio.h> 102 #ifdef ALTQ3_COMPAT 103 #include <altq/altq_conf.h> 104 #endif 105 106 /* 107 * RIO: RED with IN/OUT bit 108 * described in 109 * "Explicit Allocation of Best Effort Packet Delivery Service" 110 * David D. Clark and Wenjia Fang, MIT Lab for Computer Science 111 * http://diffserv.lcs.mit.edu/Papers/exp-alloc-ddc-wf.{ps,pdf} 112 * 113 * this implementation is extended to support more than 2 drop precedence 114 * values as described in RFC2597 (Assured Forwarding PHB Group). 115 * 116 */ 117 /* 118 * AF DS (differentiated service) codepoints. 119 * (classes can be mapped to CBQ or H-FSC classes.) 120 * 121 * 0 1 2 3 4 5 6 7 122 * +---+---+---+---+---+---+---+---+ 123 * | CLASS |DropPre| 0 | CU | 124 * +---+---+---+---+---+---+---+---+ 125 * 126 * class 1: 001 127 * class 2: 010 128 * class 3: 011 129 * class 4: 100 130 * 131 * low drop prec: 01 132 * medium drop prec: 10 133 * high drop prec: 11 134 */ 135 136 /* normal red parameters */ 137 #define W_WEIGHT 512 /* inverse of weight of EWMA (511/512) */ 138 /* q_weight = 0.00195 */ 139 140 /* red parameters for a slow link */ 141 #define W_WEIGHT_1 128 /* inverse of weight of EWMA (127/128) */ 142 /* q_weight = 0.0078125 */ 143 144 /* red parameters for a very slow link (e.g., dialup) */ 145 #define W_WEIGHT_2 64 /* inverse of weight of EWMA (63/64) */ 146 /* q_weight = 0.015625 */ 147 148 /* fixed-point uses 12-bit decimal places */ 149 #define FP_SHIFT 12 /* fixed-point shift */ 150 151 /* red parameters for drop probability */ 152 #define INV_P_MAX 10 /* inverse of max drop probability */ 153 #define TH_MIN 5 /* min threshold */ 154 #define TH_MAX 15 /* max threshold */ 155 156 #define RIO_LIMIT 60 /* default max queue lenght */ 157 #define RIO_STATS /* collect statistics */ 158 159 #define TV_DELTA(a, b, delta) { \ 160 register int xxs; \ 161 \ 162 delta = (a)->tv_usec - (b)->tv_usec; \ 163 if ((xxs = (a)->tv_sec - (b)->tv_sec) != 0) { \ 164 if (xxs < 0) { \ 165 delta = 60000000; \ 166 } else if (xxs > 4) { \ 167 if (xxs > 60) \ 168 delta = 60000000; \ 169 else \ 170 delta += xxs * 1000000; \ 171 } else while (xxs > 0) { \ 172 delta += 1000000; \ 173 xxs--; \ 174 } \ 175 } \ 176 } 177 178 #ifdef ALTQ3_COMPAT 179 /* rio_list keeps all rio_queue_t's allocated. */ 180 static rio_queue_t *rio_list = NULL; 181 #endif 182 /* default rio parameter values */ 183 static struct redparams default_rio_params[RIO_NDROPPREC] = { 184 /* th_min, th_max, inv_pmax */ 185 { TH_MAX * 2 + TH_MIN, TH_MAX * 3, INV_P_MAX }, /* low drop precedence */ 186 { TH_MAX + TH_MIN, TH_MAX * 2, INV_P_MAX }, /* medium drop precedence */ 187 { TH_MIN, TH_MAX, INV_P_MAX } /* high drop precedence */ 188 }; 189 190 /* internal function prototypes */ 191 static int dscp2index(u_int8_t); 192 #ifdef ALTQ3_COMPAT 193 static int rio_enqueue(struct ifaltq *, struct mbuf *, struct altq_pktattr *); 194 static struct mbuf *rio_dequeue(struct ifaltq *, int); 195 static int rio_request(struct ifaltq *, int, void *); 196 static int rio_detach(rio_queue_t *); 197 198 /* 199 * rio device interface 200 */ 201 altqdev_decl(rio); 202 203 #endif /* ALTQ3_COMPAT */ 204 205 rio_t * 206 rio_alloc(int weight, struct redparams *params, int flags, int pkttime) 207 { 208 rio_t *rp; 209 int w, i; 210 int npkts_per_sec; 211 212 rp = malloc(sizeof(rio_t), M_DEVBUF, M_WAITOK|M_ZERO); 213 if (rp == NULL) 214 return (NULL); 215 216 rp->rio_flags = flags; 217 if (pkttime == 0) 218 /* default packet time: 1000 bytes / 10Mbps * 8 * 1000000 */ 219 rp->rio_pkttime = 800; 220 else 221 rp->rio_pkttime = pkttime; 222 223 if (weight != 0) 224 rp->rio_weight = weight; 225 else { 226 /* use default */ 227 rp->rio_weight = W_WEIGHT; 228 229 /* when the link is very slow, adjust red parameters */ 230 npkts_per_sec = 1000000 / rp->rio_pkttime; 231 if (npkts_per_sec < 50) { 232 /* up to about 400Kbps */ 233 rp->rio_weight = W_WEIGHT_2; 234 } else if (npkts_per_sec < 300) { 235 /* up to about 2.4Mbps */ 236 rp->rio_weight = W_WEIGHT_1; 237 } 238 } 239 240 /* calculate wshift. weight must be power of 2 */ 241 w = rp->rio_weight; 242 for (i = 0; w > 1; i++) 243 w = w >> 1; 244 rp->rio_wshift = i; 245 w = 1 << rp->rio_wshift; 246 if (w != rp->rio_weight) { 247 printf("invalid weight value %d for red! use %d\n", 248 rp->rio_weight, w); 249 rp->rio_weight = w; 250 } 251 252 /* allocate weight table */ 253 rp->rio_wtab = wtab_alloc(rp->rio_weight); 254 255 for (i = 0; i < RIO_NDROPPREC; i++) { 256 struct dropprec_state *prec = &rp->rio_precstate[i]; 257 258 prec->avg = 0; 259 prec->idle = 1; 260 261 if (params == NULL || params[i].inv_pmax == 0) 262 prec->inv_pmax = default_rio_params[i].inv_pmax; 263 else 264 prec->inv_pmax = params[i].inv_pmax; 265 if (params == NULL || params[i].th_min == 0) 266 prec->th_min = default_rio_params[i].th_min; 267 else 268 prec->th_min = params[i].th_min; 269 if (params == NULL || params[i].th_max == 0) 270 prec->th_max = default_rio_params[i].th_max; 271 else 272 prec->th_max = params[i].th_max; 273 274 /* 275 * th_min_s and th_max_s are scaled versions of th_min 276 * and th_max to be compared with avg. 277 */ 278 prec->th_min_s = prec->th_min << (rp->rio_wshift + FP_SHIFT); 279 prec->th_max_s = prec->th_max << (rp->rio_wshift + FP_SHIFT); 280 281 /* 282 * precompute probability denominator 283 * probd = (2 * (TH_MAX-TH_MIN) / pmax) in fixed-point 284 */ 285 prec->probd = (2 * (prec->th_max - prec->th_min) 286 * prec->inv_pmax) << FP_SHIFT; 287 288 microtime(&prec->last); 289 } 290 291 return (rp); 292 } 293 294 void 295 rio_destroy(rio_t *rp) 296 { 297 wtab_destroy(rp->rio_wtab); 298 free(rp, M_DEVBUF); 299 } 300 301 void 302 rio_getstats(rio_t *rp, struct redstats *sp) 303 { 304 int i; 305 306 for (i = 0; i < RIO_NDROPPREC; i++) { 307 memcpy(sp, &rp->q_stats[i], sizeof(struct redstats)); 308 sp->q_avg = rp->rio_precstate[i].avg >> rp->rio_wshift; 309 sp++; 310 } 311 } 312 313 #if (RIO_NDROPPREC == 3) 314 /* 315 * internally, a drop precedence value is converted to an index 316 * starting from 0. 317 */ 318 static int 319 dscp2index(u_int8_t dscp) 320 { 321 int dpindex = dscp & AF_DROPPRECMASK; 322 323 if (dpindex == 0) 324 return (0); 325 return ((dpindex >> 3) - 1); 326 } 327 #endif 328 329 #if 1 330 /* 331 * kludge: when a packet is dequeued, we need to know its drop precedence 332 * in order to keep the queue length of each drop precedence. 333 * use m_pkthdr.rcvif to pass this info. 334 */ 335 #define RIOM_SET_PRECINDEX(m, idx) \ 336 do { (m)->m_pkthdr.rcvif = (struct ifnet *)((long)(idx)); } while (0) 337 #define RIOM_GET_PRECINDEX(m) \ 338 ({ long idx; idx = (long)((m)->m_pkthdr.rcvif); \ 339 (m)->m_pkthdr.rcvif = NULL; idx; }) 340 #endif 341 342 int 343 rio_addq(rio_t *rp, class_queue_t *q, struct mbuf *m, 344 struct altq_pktattr *pktattr) 345 { 346 int avg, droptype; 347 u_int8_t dsfield, odsfield; 348 int dpindex, i, n, t; 349 struct timeval now; 350 struct dropprec_state *prec; 351 352 dsfield = odsfield = read_dsfield(m, pktattr); 353 dpindex = dscp2index(dsfield); 354 355 /* 356 * update avg of the precedence states whose drop precedence 357 * is larger than or equal to the drop precedence of the packet 358 */ 359 now.tv_sec = 0; 360 for (i = dpindex; i < RIO_NDROPPREC; i++) { 361 prec = &rp->rio_precstate[i]; 362 avg = prec->avg; 363 if (prec->idle) { 364 prec->idle = 0; 365 if (now.tv_sec == 0) 366 microtime(&now); 367 t = (now.tv_sec - prec->last.tv_sec); 368 if (t > 60) 369 avg = 0; 370 else { 371 t = t * 1000000 + 372 (now.tv_usec - prec->last.tv_usec); 373 n = t / rp->rio_pkttime; 374 /* calculate (avg = (1 - Wq)^n * avg) */ 375 if (n > 0) 376 avg = (avg >> FP_SHIFT) * 377 pow_w(rp->rio_wtab, n); 378 } 379 } 380 381 /* run estimator. (avg is scaled by WEIGHT in fixed-point) */ 382 avg += (prec->qlen << FP_SHIFT) - (avg >> rp->rio_wshift); 383 prec->avg = avg; /* save the new value */ 384 /* 385 * count keeps a tally of arriving traffic that has not 386 * been dropped. 387 */ 388 prec->count++; 389 } 390 391 prec = &rp->rio_precstate[dpindex]; 392 avg = prec->avg; 393 394 /* see if we drop early */ 395 droptype = DTYPE_NODROP; 396 if (avg >= prec->th_min_s && prec->qlen > 1) { 397 if (avg >= prec->th_max_s) { 398 /* avg >= th_max: forced drop */ 399 droptype = DTYPE_FORCED; 400 } else if (prec->old == 0) { 401 /* first exceeds th_min */ 402 prec->count = 1; 403 prec->old = 1; 404 } else if (drop_early((avg - prec->th_min_s) >> rp->rio_wshift, 405 prec->probd, prec->count)) { 406 /* unforced drop by red */ 407 droptype = DTYPE_EARLY; 408 } 409 } else { 410 /* avg < th_min */ 411 prec->old = 0; 412 } 413 414 /* 415 * if the queue length hits the hard limit, it's a forced drop. 416 */ 417 if (droptype == DTYPE_NODROP && qlen(q) >= qlimit(q)) 418 droptype = DTYPE_FORCED; 419 420 if (droptype != DTYPE_NODROP) { 421 /* always drop incoming packet (as opposed to randomdrop) */ 422 for (i = dpindex; i < RIO_NDROPPREC; i++) 423 rp->rio_precstate[i].count = 0; 424 #ifdef RIO_STATS 425 if (droptype == DTYPE_EARLY) 426 rp->q_stats[dpindex].drop_unforced++; 427 else 428 rp->q_stats[dpindex].drop_forced++; 429 PKTCNTR_ADD(&rp->q_stats[dpindex].drop_cnt, m_pktlen(m)); 430 #endif 431 m_freem(m); 432 return (-1); 433 } 434 435 for (i = dpindex; i < RIO_NDROPPREC; i++) 436 rp->rio_precstate[i].qlen++; 437 438 /* save drop precedence index in mbuf hdr */ 439 RIOM_SET_PRECINDEX(m, dpindex); 440 441 if (rp->rio_flags & RIOF_CLEARDSCP) 442 dsfield &= ~DSCP_MASK; 443 444 if (dsfield != odsfield) 445 write_dsfield(m, pktattr, dsfield); 446 447 _addq(q, m); 448 449 #ifdef RIO_STATS 450 PKTCNTR_ADD(&rp->q_stats[dpindex].xmit_cnt, m_pktlen(m)); 451 #endif 452 return (0); 453 } 454 455 struct mbuf * 456 rio_getq(rio_t *rp, class_queue_t *q) 457 { 458 struct mbuf *m; 459 int dpindex, i; 460 461 if ((m = _getq(q)) == NULL) 462 return NULL; 463 464 dpindex = RIOM_GET_PRECINDEX(m); 465 for (i = dpindex; i < RIO_NDROPPREC; i++) { 466 if (--rp->rio_precstate[i].qlen == 0) { 467 if (rp->rio_precstate[i].idle == 0) { 468 rp->rio_precstate[i].idle = 1; 469 microtime(&rp->rio_precstate[i].last); 470 } 471 } 472 } 473 return (m); 474 } 475 476 #ifdef ALTQ3_COMPAT 477 int 478 rioopen(dev_t dev, int flag, int fmt, 479 struct lwp *l) 480 { 481 /* everything will be done when the queueing scheme is attached. */ 482 return 0; 483 } 484 485 int 486 rioclose(dev_t dev, int flag, int fmt, 487 struct lwp *l) 488 { 489 rio_queue_t *rqp; 490 int err, error = 0; 491 492 while ((rqp = rio_list) != NULL) { 493 /* destroy all */ 494 err = rio_detach(rqp); 495 if (err != 0 && error == 0) 496 error = err; 497 } 498 499 return error; 500 } 501 502 int 503 rioioctl(dev_t dev, ioctlcmd_t cmd, void *addr, int flag, 504 struct lwp *l) 505 { 506 rio_queue_t *rqp; 507 struct rio_interface *ifacep; 508 struct ifnet *ifp; 509 int error = 0; 510 511 /* check super-user privilege */ 512 switch (cmd) { 513 case RIO_GETSTATS: 514 break; 515 default: 516 #if (__FreeBSD_version > 400000) 517 if ((error = suser(p)) != 0) 518 return (error); 519 #else 520 if ((error = kauth_authorize_network(l->l_cred, 521 KAUTH_NETWORK_ALTQ, KAUTH_REQ_NETWORK_ALTQ_RIO, NULL, 522 NULL, NULL)) != 0) 523 return (error); 524 #endif 525 break; 526 } 527 528 switch (cmd) { 529 530 case RIO_ENABLE: 531 ifacep = (struct rio_interface *)addr; 532 if ((rqp = altq_lookup(ifacep->rio_ifname, ALTQT_RIO)) == NULL) { 533 error = EBADF; 534 break; 535 } 536 error = altq_enable(rqp->rq_ifq); 537 break; 538 539 case RIO_DISABLE: 540 ifacep = (struct rio_interface *)addr; 541 if ((rqp = altq_lookup(ifacep->rio_ifname, ALTQT_RIO)) == NULL) { 542 error = EBADF; 543 break; 544 } 545 error = altq_disable(rqp->rq_ifq); 546 break; 547 548 case RIO_IF_ATTACH: 549 ifp = ifunit(((struct rio_interface *)addr)->rio_ifname); 550 if (ifp == NULL) { 551 error = ENXIO; 552 break; 553 } 554 555 /* allocate and initialize rio_queue_t */ 556 rqp = malloc(sizeof(rio_queue_t), M_DEVBUF, M_WAITOK|M_ZERO); 557 if (rqp == NULL) { 558 error = ENOMEM; 559 break; 560 } 561 562 rqp->rq_q = malloc(sizeof(class_queue_t), M_DEVBUF, 563 M_WAITOK|M_ZERO); 564 if (rqp->rq_q == NULL) { 565 free(rqp, M_DEVBUF); 566 error = ENOMEM; 567 break; 568 } 569 570 rqp->rq_rio = rio_alloc(0, NULL, 0, 0); 571 if (rqp->rq_rio == NULL) { 572 free(rqp->rq_q, M_DEVBUF); 573 free(rqp, M_DEVBUF); 574 error = ENOMEM; 575 break; 576 } 577 578 rqp->rq_ifq = &ifp->if_snd; 579 qtail(rqp->rq_q) = NULL; 580 qlen(rqp->rq_q) = 0; 581 qlimit(rqp->rq_q) = RIO_LIMIT; 582 qtype(rqp->rq_q) = Q_RIO; 583 584 /* 585 * set RIO to this ifnet structure. 586 */ 587 error = altq_attach(rqp->rq_ifq, ALTQT_RIO, rqp, 588 rio_enqueue, rio_dequeue, rio_request, 589 NULL, NULL); 590 if (error) { 591 rio_destroy(rqp->rq_rio); 592 free(rqp->rq_q, M_DEVBUF); 593 free(rqp, M_DEVBUF); 594 break; 595 } 596 597 /* add this state to the rio list */ 598 rqp->rq_next = rio_list; 599 rio_list = rqp; 600 break; 601 602 case RIO_IF_DETACH: 603 ifacep = (struct rio_interface *)addr; 604 if ((rqp = altq_lookup(ifacep->rio_ifname, ALTQT_RIO)) == NULL) { 605 error = EBADF; 606 break; 607 } 608 error = rio_detach(rqp); 609 break; 610 611 case RIO_GETSTATS: 612 do { 613 struct rio_stats *q_stats; 614 rio_t *rp; 615 int i; 616 617 q_stats = (struct rio_stats *)addr; 618 if ((rqp = altq_lookup(q_stats->iface.rio_ifname, 619 ALTQT_RIO)) == NULL) { 620 error = EBADF; 621 break; 622 } 623 624 rp = rqp->rq_rio; 625 626 q_stats->q_limit = qlimit(rqp->rq_q); 627 q_stats->weight = rp->rio_weight; 628 q_stats->flags = rp->rio_flags; 629 630 for (i = 0; i < RIO_NDROPPREC; i++) { 631 q_stats->q_len[i] = rp->rio_precstate[i].qlen; 632 memcpy(&q_stats->q_stats[i], &rp->q_stats[i], 633 sizeof(struct redstats)); 634 q_stats->q_stats[i].q_avg = 635 rp->rio_precstate[i].avg >> rp->rio_wshift; 636 637 q_stats->q_params[i].inv_pmax 638 = rp->rio_precstate[i].inv_pmax; 639 q_stats->q_params[i].th_min 640 = rp->rio_precstate[i].th_min; 641 q_stats->q_params[i].th_max 642 = rp->rio_precstate[i].th_max; 643 } 644 } while (/*CONSTCOND*/ 0); 645 break; 646 647 case RIO_CONFIG: 648 do { 649 struct rio_conf *fc; 650 rio_t *new; 651 int s, limit, i; 652 653 fc = (struct rio_conf *)addr; 654 if ((rqp = altq_lookup(fc->iface.rio_ifname, 655 ALTQT_RIO)) == NULL) { 656 error = EBADF; 657 break; 658 } 659 660 new = rio_alloc(fc->rio_weight, &fc->q_params[0], 661 fc->rio_flags, fc->rio_pkttime); 662 if (new == NULL) { 663 error = ENOMEM; 664 break; 665 } 666 667 s = splnet(); 668 _flushq(rqp->rq_q); 669 limit = fc->rio_limit; 670 if (limit < fc->q_params[RIO_NDROPPREC-1].th_max) 671 limit = fc->q_params[RIO_NDROPPREC-1].th_max; 672 qlimit(rqp->rq_q) = limit; 673 674 rio_destroy(rqp->rq_rio); 675 rqp->rq_rio = new; 676 677 splx(s); 678 679 /* write back new values */ 680 fc->rio_limit = limit; 681 for (i = 0; i < RIO_NDROPPREC; i++) { 682 fc->q_params[i].inv_pmax = 683 rqp->rq_rio->rio_precstate[i].inv_pmax; 684 fc->q_params[i].th_min = 685 rqp->rq_rio->rio_precstate[i].th_min; 686 fc->q_params[i].th_max = 687 rqp->rq_rio->rio_precstate[i].th_max; 688 } 689 } while (/*CONSTCOND*/ 0); 690 break; 691 692 case RIO_SETDEFAULTS: 693 do { 694 struct redparams *rp; 695 int i; 696 697 rp = (struct redparams *)addr; 698 for (i = 0; i < RIO_NDROPPREC; i++) 699 default_rio_params[i] = rp[i]; 700 } while (/*CONSTCOND*/ 0); 701 break; 702 703 default: 704 error = EINVAL; 705 break; 706 } 707 708 return error; 709 } 710 711 static int 712 rio_detach(rio_queue_t *rqp) 713 { 714 rio_queue_t *tmp; 715 int error = 0; 716 717 if (ALTQ_IS_ENABLED(rqp->rq_ifq)) 718 altq_disable(rqp->rq_ifq); 719 720 if ((error = altq_detach(rqp->rq_ifq))) 721 return (error); 722 723 if (rio_list == rqp) 724 rio_list = rqp->rq_next; 725 else { 726 for (tmp = rio_list; tmp != NULL; tmp = tmp->rq_next) 727 if (tmp->rq_next == rqp) { 728 tmp->rq_next = rqp->rq_next; 729 break; 730 } 731 if (tmp == NULL) 732 printf("rio_detach: no state found in rio_list!\n"); 733 } 734 735 rio_destroy(rqp->rq_rio); 736 free(rqp->rq_q, M_DEVBUF); 737 free(rqp, M_DEVBUF); 738 return (error); 739 } 740 741 /* 742 * rio support routines 743 */ 744 static int 745 rio_request(struct ifaltq *ifq, int req, void *arg) 746 { 747 rio_queue_t *rqp = (rio_queue_t *)ifq->altq_disc; 748 749 switch (req) { 750 case ALTRQ_PURGE: 751 _flushq(rqp->rq_q); 752 if (ALTQ_IS_ENABLED(ifq)) 753 ifq->ifq_len = 0; 754 break; 755 } 756 return (0); 757 } 758 759 /* 760 * enqueue routine: 761 * 762 * returns: 0 when successfully queued. 763 * ENOBUFS when drop occurs. 764 */ 765 static int 766 rio_enqueue(struct ifaltq *ifq, struct mbuf *m, struct altq_pktattr *pktattr) 767 { 768 rio_queue_t *rqp = (rio_queue_t *)ifq->altq_disc; 769 int error = 0; 770 771 if (rio_addq(rqp->rq_rio, rqp->rq_q, m, pktattr) == 0) 772 ifq->ifq_len++; 773 else 774 error = ENOBUFS; 775 return error; 776 } 777 778 /* 779 * dequeue routine: 780 * must be called in splnet. 781 * 782 * returns: mbuf dequeued. 783 * NULL when no packet is available in the queue. 784 */ 785 786 static struct mbuf * 787 rio_dequeue(struct ifaltq *ifq, int op) 788 { 789 rio_queue_t *rqp = (rio_queue_t *)ifq->altq_disc; 790 struct mbuf *m = NULL; 791 792 if (op == ALTDQ_POLL) 793 return qhead(rqp->rq_q); 794 795 m = rio_getq(rqp->rq_rio, rqp->rq_q); 796 if (m != NULL) 797 ifq->ifq_len--; 798 return m; 799 } 800 801 #ifdef KLD_MODULE 802 803 static struct altqsw rio_sw = 804 {"rio", rioopen, rioclose, rioioctl}; 805 806 ALTQ_MODULE(altq_rio, ALTQT_RIO, &rio_sw); 807 MODULE_VERSION(altq_rio, 1); 808 MODULE_DEPEND(altq_rio, altq_red, 1, 1, 1); 809 810 #endif /* KLD_MODULE */ 811 #endif /* ALTQ3_COMPAT */ 812 813 #endif /* ALTQ_RIO */ 814