1 /*- 2 * Codel/FQ_Codel and PIE/FQ-PIE Code: 3 * Copyright (C) 2016 Centre for Advanced Internet Architectures, 4 * Swinburne University of Technology, Melbourne, Australia. 5 * Portions of this code were made possible in part by a gift from 6 * The Comcast Innovation Fund. 7 * Implemented by Rasool Al-Saadi <ralsaadi@swin.edu.au> 8 * 9 * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa 10 * Portions Copyright (c) 2000 Akamba Corp. 11 * All rights reserved 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 /* 39 * Configuration and internal object management for dummynet. 40 */ 41 42 #include "opt_inet6.h" 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/malloc.h> 47 #include <sys/mbuf.h> 48 #include <sys/kernel.h> 49 #include <sys/lock.h> 50 #include <sys/module.h> 51 #include <sys/mutex.h> 52 #include <sys/priv.h> 53 #include <sys/proc.h> 54 #include <sys/rwlock.h> 55 #include <sys/socket.h> 56 #include <sys/socketvar.h> 57 #include <sys/time.h> 58 #include <sys/taskqueue.h> 59 #include <net/if.h> /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ 60 #include <netinet/in.h> 61 #include <netinet/ip_var.h> /* ip_output(), IP_FORWARDING */ 62 #include <netinet/ip_fw.h> 63 #include <netinet/ip_dummynet.h> 64 65 #include <netpfil/ipfw/ip_fw_private.h> 66 #include <netpfil/ipfw/dn_heap.h> 67 #include <netpfil/ipfw/ip_dn_private.h> 68 #ifdef NEW_AQM 69 #include <netpfil/ipfw/dn_aqm.h> 70 #endif 71 #include <netpfil/ipfw/dn_sched.h> 72 73 /* which objects to copy */ 74 #define DN_C_LINK 0x01 75 #define DN_C_SCH 0x02 76 #define DN_C_FLOW 0x04 77 #define DN_C_FS 0x08 78 #define DN_C_QUEUE 0x10 79 80 /* we use this argument in case of a schk_new */ 81 struct schk_new_arg { 82 struct dn_alg *fp; 83 struct dn_sch *sch; 84 }; 85 86 /*---- callout hooks. ----*/ 87 static struct callout dn_timeout; 88 static int dn_gone; 89 static struct task dn_task; 90 static struct taskqueue *dn_tq = NULL; 91 92 static void 93 dummynet(void *arg) 94 { 95 96 (void)arg; /* UNUSED */ 97 taskqueue_enqueue(dn_tq, &dn_task); 98 } 99 100 void 101 dn_reschedule(void) 102 { 103 104 if (dn_gone != 0) 105 return; 106 callout_reset_sbt(&dn_timeout, tick_sbt, 0, dummynet, NULL, 107 C_HARDCLOCK | C_DIRECT_EXEC); 108 } 109 /*----- end of callout hooks -----*/ 110 111 #ifdef NEW_AQM 112 /* Return AQM descriptor for given type or name. */ 113 static struct dn_aqm * 114 find_aqm_type(int type, char *name) 115 { 116 struct dn_aqm *d; 117 118 SLIST_FOREACH(d, &dn_cfg.aqmlist, next) { 119 if (d->type == type || (name && !strcasecmp(d->name, name))) 120 return d; 121 } 122 return NULL; /* not found */ 123 } 124 #endif 125 126 /* Return a scheduler descriptor given the type or name. */ 127 static struct dn_alg * 128 find_sched_type(int type, char *name) 129 { 130 struct dn_alg *d; 131 132 SLIST_FOREACH(d, &dn_cfg.schedlist, next) { 133 if (d->type == type || (name && !strcasecmp(d->name, name))) 134 return d; 135 } 136 return NULL; /* not found */ 137 } 138 139 int 140 ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg) 141 { 142 int oldv = *v; 143 const char *op = NULL; 144 if (dflt < lo) 145 dflt = lo; 146 if (dflt > hi) 147 dflt = hi; 148 if (oldv < lo) { 149 *v = dflt; 150 op = "Bump"; 151 } else if (oldv > hi) { 152 *v = hi; 153 op = "Clamp"; 154 } else 155 return *v; 156 if (op && msg) 157 printf("%s %s to %d (was %d)\n", op, msg, *v, oldv); 158 return *v; 159 } 160 161 /*---- flow_id mask, hash and compare functions ---*/ 162 /* 163 * The flow_id includes the 5-tuple, the queue/pipe number 164 * which we store in the extra area in host order, 165 * and for ipv6 also the flow_id6. 166 * XXX see if we want the tos byte (can store in 'flags') 167 */ 168 static struct ipfw_flow_id * 169 flow_id_mask(struct ipfw_flow_id *mask, struct ipfw_flow_id *id) 170 { 171 int is_v6 = IS_IP6_FLOW_ID(id); 172 173 id->dst_port &= mask->dst_port; 174 id->src_port &= mask->src_port; 175 id->proto &= mask->proto; 176 id->extra &= mask->extra; 177 if (is_v6) { 178 APPLY_MASK(&id->dst_ip6, &mask->dst_ip6); 179 APPLY_MASK(&id->src_ip6, &mask->src_ip6); 180 id->flow_id6 &= mask->flow_id6; 181 } else { 182 id->dst_ip &= mask->dst_ip; 183 id->src_ip &= mask->src_ip; 184 } 185 return id; 186 } 187 188 /* computes an OR of two masks, result in dst and also returned */ 189 static struct ipfw_flow_id * 190 flow_id_or(struct ipfw_flow_id *src, struct ipfw_flow_id *dst) 191 { 192 int is_v6 = IS_IP6_FLOW_ID(dst); 193 194 dst->dst_port |= src->dst_port; 195 dst->src_port |= src->src_port; 196 dst->proto |= src->proto; 197 dst->extra |= src->extra; 198 if (is_v6) { 199 #define OR_MASK(_d, _s) \ 200 (_d)->__u6_addr.__u6_addr32[0] |= (_s)->__u6_addr.__u6_addr32[0]; \ 201 (_d)->__u6_addr.__u6_addr32[1] |= (_s)->__u6_addr.__u6_addr32[1]; \ 202 (_d)->__u6_addr.__u6_addr32[2] |= (_s)->__u6_addr.__u6_addr32[2]; \ 203 (_d)->__u6_addr.__u6_addr32[3] |= (_s)->__u6_addr.__u6_addr32[3]; 204 OR_MASK(&dst->dst_ip6, &src->dst_ip6); 205 OR_MASK(&dst->src_ip6, &src->src_ip6); 206 #undef OR_MASK 207 dst->flow_id6 |= src->flow_id6; 208 } else { 209 dst->dst_ip |= src->dst_ip; 210 dst->src_ip |= src->src_ip; 211 } 212 return dst; 213 } 214 215 static int 216 nonzero_mask(struct ipfw_flow_id *m) 217 { 218 if (m->dst_port || m->src_port || m->proto || m->extra) 219 return 1; 220 if (IS_IP6_FLOW_ID(m)) { 221 return 222 m->dst_ip6.__u6_addr.__u6_addr32[0] || 223 m->dst_ip6.__u6_addr.__u6_addr32[1] || 224 m->dst_ip6.__u6_addr.__u6_addr32[2] || 225 m->dst_ip6.__u6_addr.__u6_addr32[3] || 226 m->src_ip6.__u6_addr.__u6_addr32[0] || 227 m->src_ip6.__u6_addr.__u6_addr32[1] || 228 m->src_ip6.__u6_addr.__u6_addr32[2] || 229 m->src_ip6.__u6_addr.__u6_addr32[3] || 230 m->flow_id6; 231 } else { 232 return m->dst_ip || m->src_ip; 233 } 234 } 235 236 /* XXX we may want a better hash function */ 237 static uint32_t 238 flow_id_hash(struct ipfw_flow_id *id) 239 { 240 uint32_t i; 241 242 if (IS_IP6_FLOW_ID(id)) { 243 uint32_t *d = (uint32_t *)&id->dst_ip6; 244 uint32_t *s = (uint32_t *)&id->src_ip6; 245 i = (d[0] ) ^ (d[1]) ^ 246 (d[2] ) ^ (d[3]) ^ 247 (d[0] >> 15) ^ (d[1] >> 15) ^ 248 (d[2] >> 15) ^ (d[3] >> 15) ^ 249 (s[0] << 1) ^ (s[1] << 1) ^ 250 (s[2] << 1) ^ (s[3] << 1) ^ 251 (s[0] << 16) ^ (s[1] << 16) ^ 252 (s[2] << 16) ^ (s[3] << 16) ^ 253 (id->dst_port << 1) ^ (id->src_port) ^ 254 (id->extra) ^ 255 (id->proto ) ^ (id->flow_id6); 256 } else { 257 i = (id->dst_ip) ^ (id->dst_ip >> 15) ^ 258 (id->src_ip << 1) ^ (id->src_ip >> 16) ^ 259 (id->extra) ^ 260 (id->dst_port << 1) ^ (id->src_port) ^ (id->proto); 261 } 262 return i; 263 } 264 265 /* Like bcmp, returns 0 if ids match, 1 otherwise. */ 266 static int 267 flow_id_cmp(struct ipfw_flow_id *id1, struct ipfw_flow_id *id2) 268 { 269 int is_v6 = IS_IP6_FLOW_ID(id1); 270 271 if (!is_v6) { 272 if (IS_IP6_FLOW_ID(id2)) 273 return 1; /* different address families */ 274 275 return (id1->dst_ip == id2->dst_ip && 276 id1->src_ip == id2->src_ip && 277 id1->dst_port == id2->dst_port && 278 id1->src_port == id2->src_port && 279 id1->proto == id2->proto && 280 id1->extra == id2->extra) ? 0 : 1; 281 } 282 /* the ipv6 case */ 283 return ( 284 !bcmp(&id1->dst_ip6,&id2->dst_ip6, sizeof(id1->dst_ip6)) && 285 !bcmp(&id1->src_ip6,&id2->src_ip6, sizeof(id1->src_ip6)) && 286 id1->dst_port == id2->dst_port && 287 id1->src_port == id2->src_port && 288 id1->proto == id2->proto && 289 id1->extra == id2->extra && 290 id1->flow_id6 == id2->flow_id6) ? 0 : 1; 291 } 292 /*--------- end of flow-id mask, hash and compare ---------*/ 293 294 /*--- support functions for the qht hashtable ---- 295 * Entries are hashed by flow-id 296 */ 297 static uint32_t 298 q_hash(uintptr_t key, int flags, void *arg) 299 { 300 /* compute the hash slot from the flow id */ 301 struct ipfw_flow_id *id = (flags & DNHT_KEY_IS_OBJ) ? 302 &((struct dn_queue *)key)->ni.fid : 303 (struct ipfw_flow_id *)key; 304 305 return flow_id_hash(id); 306 } 307 308 static int 309 q_match(void *obj, uintptr_t key, int flags, void *arg) 310 { 311 struct dn_queue *o = (struct dn_queue *)obj; 312 struct ipfw_flow_id *id2; 313 314 if (flags & DNHT_KEY_IS_OBJ) { 315 /* compare pointers */ 316 id2 = &((struct dn_queue *)key)->ni.fid; 317 } else { 318 id2 = (struct ipfw_flow_id *)key; 319 } 320 return (0 == flow_id_cmp(&o->ni.fid, id2)); 321 } 322 323 /* 324 * create a new queue instance for the given 'key'. 325 */ 326 static void * 327 q_new(uintptr_t key, int flags, void *arg) 328 { 329 struct dn_queue *q, *template = arg; 330 struct dn_fsk *fs = template->fs; 331 int size = sizeof(*q) + fs->sched->fp->q_datalen; 332 333 q = malloc(size, M_DUMMYNET, M_NOWAIT | M_ZERO); 334 if (q == NULL) { 335 D("no memory for new queue"); 336 return NULL; 337 } 338 339 set_oid(&q->ni.oid, DN_QUEUE, size); 340 if (fs->fs.flags & DN_QHT_HASH) 341 q->ni.fid = *(struct ipfw_flow_id *)key; 342 q->fs = fs; 343 q->_si = template->_si; 344 q->_si->q_count++; 345 346 if (fs->sched->fp->new_queue) 347 fs->sched->fp->new_queue(q); 348 349 #ifdef NEW_AQM 350 /* call AQM init function after creating a queue*/ 351 if (fs->aqmfp && fs->aqmfp->init) 352 if(fs->aqmfp->init(q)) 353 D("unable to init AQM for fs %d", fs->fs.fs_nr); 354 #endif 355 dn_cfg.queue_count++; 356 357 return q; 358 } 359 360 /* 361 * Notify schedulers that a queue is going away. 362 * If (flags & DN_DESTROY), also free the packets. 363 * The version for callbacks is called q_delete_cb(). 364 */ 365 static void 366 dn_delete_queue(struct dn_queue *q, int flags) 367 { 368 struct dn_fsk *fs = q->fs; 369 370 #ifdef NEW_AQM 371 /* clean up AQM status for queue 'q' 372 * cleanup here is called just with MULTIQUEUE 373 */ 374 if (fs && fs->aqmfp && fs->aqmfp->cleanup) 375 fs->aqmfp->cleanup(q); 376 #endif 377 // D("fs %p si %p\n", fs, q->_si); 378 /* notify the parent scheduler that the queue is going away */ 379 if (fs && fs->sched->fp->free_queue) 380 fs->sched->fp->free_queue(q); 381 q->_si->q_count--; 382 q->_si = NULL; 383 if (flags & DN_DESTROY) { 384 if (q->mq.head) 385 dn_free_pkts(q->mq.head); 386 bzero(q, sizeof(*q)); // safety 387 free(q, M_DUMMYNET); 388 dn_cfg.queue_count--; 389 } 390 } 391 392 static int 393 q_delete_cb(void *q, void *arg) 394 { 395 int flags = (int)(uintptr_t)arg; 396 dn_delete_queue(q, flags); 397 return (flags & DN_DESTROY) ? DNHT_SCAN_DEL : 0; 398 } 399 400 /* 401 * calls dn_delete_queue/q_delete_cb on all queues, 402 * which notifies the parent scheduler and possibly drains packets. 403 * flags & DN_DESTROY: drains queues and destroy qht; 404 */ 405 static void 406 qht_delete(struct dn_fsk *fs, int flags) 407 { 408 ND("fs %d start flags %d qht %p", 409 fs->fs.fs_nr, flags, fs->qht); 410 if (!fs->qht) 411 return; 412 if (fs->fs.flags & DN_QHT_HASH) { 413 dn_ht_scan(fs->qht, q_delete_cb, (void *)(uintptr_t)flags); 414 if (flags & DN_DESTROY) { 415 dn_ht_free(fs->qht, 0); 416 fs->qht = NULL; 417 } 418 } else { 419 dn_delete_queue((struct dn_queue *)(fs->qht), flags); 420 if (flags & DN_DESTROY) 421 fs->qht = NULL; 422 } 423 } 424 425 /* 426 * Find and possibly create the queue for a MULTIQUEUE scheduler. 427 * We never call it for !MULTIQUEUE (the queue is in the sch_inst). 428 */ 429 struct dn_queue * 430 ipdn_q_find(struct dn_fsk *fs, struct dn_sch_inst *si, 431 struct ipfw_flow_id *id) 432 { 433 struct dn_queue template; 434 435 template._si = si; 436 template.fs = fs; 437 438 if (fs->fs.flags & DN_QHT_HASH) { 439 struct ipfw_flow_id masked_id; 440 if (fs->qht == NULL) { 441 fs->qht = dn_ht_init(NULL, fs->fs.buckets, 442 offsetof(struct dn_queue, q_next), 443 q_hash, q_match, q_new); 444 if (fs->qht == NULL) 445 return NULL; 446 } 447 masked_id = *id; 448 flow_id_mask(&fs->fsk_mask, &masked_id); 449 return dn_ht_find(fs->qht, (uintptr_t)&masked_id, 450 DNHT_INSERT, &template); 451 } else { 452 if (fs->qht == NULL) 453 fs->qht = q_new(0, 0, &template); 454 return (struct dn_queue *)fs->qht; 455 } 456 } 457 /*--- end of queue hash table ---*/ 458 459 /*--- support functions for the sch_inst hashtable ---- 460 * 461 * These are hashed by flow-id 462 */ 463 static uint32_t 464 si_hash(uintptr_t key, int flags, void *arg) 465 { 466 /* compute the hash slot from the flow id */ 467 struct ipfw_flow_id *id = (flags & DNHT_KEY_IS_OBJ) ? 468 &((struct dn_sch_inst *)key)->ni.fid : 469 (struct ipfw_flow_id *)key; 470 471 return flow_id_hash(id); 472 } 473 474 static int 475 si_match(void *obj, uintptr_t key, int flags, void *arg) 476 { 477 struct dn_sch_inst *o = obj; 478 struct ipfw_flow_id *id2; 479 480 id2 = (flags & DNHT_KEY_IS_OBJ) ? 481 &((struct dn_sch_inst *)key)->ni.fid : 482 (struct ipfw_flow_id *)key; 483 return flow_id_cmp(&o->ni.fid, id2) == 0; 484 } 485 486 /* 487 * create a new instance for the given 'key' 488 * Allocate memory for instance, delay line and scheduler private data. 489 */ 490 static void * 491 si_new(uintptr_t key, int flags, void *arg) 492 { 493 struct dn_schk *s = arg; 494 struct dn_sch_inst *si; 495 int l = sizeof(*si) + s->fp->si_datalen; 496 497 si = malloc(l, M_DUMMYNET, M_NOWAIT | M_ZERO); 498 if (si == NULL) 499 goto error; 500 501 /* Set length only for the part passed up to userland. */ 502 set_oid(&si->ni.oid, DN_SCH_I, sizeof(struct dn_flow)); 503 set_oid(&(si->dline.oid), DN_DELAY_LINE, 504 sizeof(struct delay_line)); 505 /* mark si and dline as outside the event queue */ 506 si->ni.oid.id = si->dline.oid.id = -1; 507 508 si->sched = s; 509 si->dline.si = si; 510 511 if (s->fp->new_sched && s->fp->new_sched(si)) { 512 D("new_sched error"); 513 goto error; 514 } 515 if (s->sch.flags & DN_HAVE_MASK) 516 si->ni.fid = *(struct ipfw_flow_id *)key; 517 518 #ifdef NEW_AQM 519 /* init AQM status for !DN_MULTIQUEUE sched*/ 520 if (!(s->fp->flags & DN_MULTIQUEUE)) 521 if (s->fs->aqmfp && s->fs->aqmfp->init) 522 if(s->fs->aqmfp->init((struct dn_queue *)(si + 1))) { 523 D("unable to init AQM for fs %d", s->fs->fs.fs_nr); 524 goto error; 525 } 526 #endif 527 528 dn_cfg.si_count++; 529 return si; 530 531 error: 532 if (si) { 533 bzero(si, sizeof(*si)); // safety 534 free(si, M_DUMMYNET); 535 } 536 return NULL; 537 } 538 539 /* 540 * Callback from siht to delete all scheduler instances. Remove 541 * si and delay line from the system heap, destroy all queues. 542 * We assume that all flowset have been notified and do not 543 * point to us anymore. 544 */ 545 static int 546 si_destroy(void *_si, void *arg) 547 { 548 struct dn_sch_inst *si = _si; 549 struct dn_schk *s = si->sched; 550 struct delay_line *dl = &si->dline; 551 552 if (dl->oid.subtype) /* remove delay line from event heap */ 553 heap_extract(&dn_cfg.evheap, dl); 554 dn_free_pkts(dl->mq.head); /* drain delay line */ 555 if (si->kflags & DN_ACTIVE) /* remove si from event heap */ 556 heap_extract(&dn_cfg.evheap, si); 557 558 #ifdef NEW_AQM 559 /* clean up AQM status for !DN_MULTIQUEUE sched 560 * Note that all queues belong to fs were cleaned up in fsk_detach. 561 * When drain_scheduler is called s->fs and q->fs are pointing 562 * to a correct fs, so we can use fs in this case. 563 */ 564 if (!(s->fp->flags & DN_MULTIQUEUE)) { 565 struct dn_queue *q = (struct dn_queue *)(si + 1); 566 if (q->aqm_status && q->fs->aqmfp) 567 if (q->fs->aqmfp->cleanup) 568 q->fs->aqmfp->cleanup(q); 569 } 570 #endif 571 if (s->fp->free_sched) 572 s->fp->free_sched(si); 573 bzero(si, sizeof(*si)); /* safety */ 574 free(si, M_DUMMYNET); 575 dn_cfg.si_count--; 576 return DNHT_SCAN_DEL; 577 } 578 579 /* 580 * Find the scheduler instance for this packet. If we need to apply 581 * a mask, do on a local copy of the flow_id to preserve the original. 582 * Assume siht is always initialized if we have a mask. 583 */ 584 struct dn_sch_inst * 585 ipdn_si_find(struct dn_schk *s, struct ipfw_flow_id *id) 586 { 587 588 if (s->sch.flags & DN_HAVE_MASK) { 589 struct ipfw_flow_id id_t = *id; 590 flow_id_mask(&s->sch.sched_mask, &id_t); 591 return dn_ht_find(s->siht, (uintptr_t)&id_t, 592 DNHT_INSERT, s); 593 } 594 if (!s->siht) 595 s->siht = si_new(0, 0, s); 596 return (struct dn_sch_inst *)s->siht; 597 } 598 599 /* callback to flush credit for the scheduler instance */ 600 static int 601 si_reset_credit(void *_si, void *arg) 602 { 603 struct dn_sch_inst *si = _si; 604 struct dn_link *p = &si->sched->link; 605 606 si->credit = p->burst + (dn_cfg.io_fast ? p->bandwidth : 0); 607 return 0; 608 } 609 610 static void 611 schk_reset_credit(struct dn_schk *s) 612 { 613 if (s->sch.flags & DN_HAVE_MASK) 614 dn_ht_scan(s->siht, si_reset_credit, NULL); 615 else if (s->siht) 616 si_reset_credit(s->siht, NULL); 617 } 618 /*---- end of sch_inst hashtable ---------------------*/ 619 620 /*------------------------------------------------------- 621 * flowset hash (fshash) support. Entries are hashed by fs_nr. 622 * New allocations are put in the fsunlinked list, from which 623 * they are removed when they point to a specific scheduler. 624 */ 625 static uint32_t 626 fsk_hash(uintptr_t key, int flags, void *arg) 627 { 628 uint32_t i = !(flags & DNHT_KEY_IS_OBJ) ? key : 629 ((struct dn_fsk *)key)->fs.fs_nr; 630 631 return ( (i>>8)^(i>>4)^i ); 632 } 633 634 static int 635 fsk_match(void *obj, uintptr_t key, int flags, void *arg) 636 { 637 struct dn_fsk *fs = obj; 638 int i = !(flags & DNHT_KEY_IS_OBJ) ? key : 639 ((struct dn_fsk *)key)->fs.fs_nr; 640 641 return (fs->fs.fs_nr == i); 642 } 643 644 static void * 645 fsk_new(uintptr_t key, int flags, void *arg) 646 { 647 struct dn_fsk *fs; 648 649 fs = malloc(sizeof(*fs), M_DUMMYNET, M_NOWAIT | M_ZERO); 650 if (fs) { 651 set_oid(&fs->fs.oid, DN_FS, sizeof(fs->fs)); 652 dn_cfg.fsk_count++; 653 fs->drain_bucket = 0; 654 SLIST_INSERT_HEAD(&dn_cfg.fsu, fs, sch_chain); 655 } 656 return fs; 657 } 658 659 #ifdef NEW_AQM 660 /* callback function for cleaning up AQM queue status belongs to a flowset 661 * connected to scheduler instance '_si' (for !DN_MULTIQUEUE only). 662 */ 663 static int 664 si_cleanup_q(void *_si, void *arg) 665 { 666 struct dn_sch_inst *si = _si; 667 668 if (!(si->sched->fp->flags & DN_MULTIQUEUE)) { 669 if (si->sched->fs->aqmfp && si->sched->fs->aqmfp->cleanup) 670 si->sched->fs->aqmfp->cleanup((struct dn_queue *) (si+1)); 671 } 672 return 0; 673 } 674 675 /* callback to clean up queue AQM status.*/ 676 static int 677 q_cleanup_q(void *_q, void *arg) 678 { 679 struct dn_queue *q = _q; 680 q->fs->aqmfp->cleanup(q); 681 return 0; 682 } 683 684 /* Clean up all AQM queues status belongs to flowset 'fs' and then 685 * deconfig AQM for flowset 'fs' 686 */ 687 static void 688 aqm_cleanup_deconfig_fs(struct dn_fsk *fs) 689 { 690 struct dn_sch_inst *si; 691 692 /* clean up AQM status for all queues for !DN_MULTIQUEUE sched*/ 693 if (fs->fs.fs_nr > DN_MAX_ID) { 694 if (fs->sched && !(fs->sched->fp->flags & DN_MULTIQUEUE)) { 695 if (fs->sched->sch.flags & DN_HAVE_MASK) 696 dn_ht_scan(fs->sched->siht, si_cleanup_q, NULL); 697 else { 698 /* single si i.e. no sched mask */ 699 si = (struct dn_sch_inst *) fs->sched->siht; 700 if (si && fs->aqmfp && fs->aqmfp->cleanup) 701 fs->aqmfp->cleanup((struct dn_queue *) (si+1)); 702 } 703 } 704 } 705 706 /* clean up AQM status for all queues for DN_MULTIQUEUE sched*/ 707 if (fs->sched && fs->sched->fp->flags & DN_MULTIQUEUE && fs->qht) { 708 if (fs->fs.flags & DN_QHT_HASH) 709 dn_ht_scan(fs->qht, q_cleanup_q, NULL); 710 else 711 fs->aqmfp->cleanup((struct dn_queue *)(fs->qht)); 712 } 713 714 /* deconfig AQM */ 715 if(fs->aqmcfg && fs->aqmfp && fs->aqmfp->deconfig) 716 fs->aqmfp->deconfig(fs); 717 } 718 #endif 719 720 /* 721 * detach flowset from its current scheduler. Flags as follows: 722 * DN_DETACH removes from the fsk_list 723 * DN_DESTROY deletes individual queues 724 * DN_DELETE_FS destroys the flowset (otherwise goes in unlinked). 725 */ 726 static void 727 fsk_detach(struct dn_fsk *fs, int flags) 728 { 729 if (flags & DN_DELETE_FS) 730 flags |= DN_DESTROY; 731 ND("fs %d from sched %d flags %s %s %s", 732 fs->fs.fs_nr, fs->fs.sched_nr, 733 (flags & DN_DELETE_FS) ? "DEL_FS":"", 734 (flags & DN_DESTROY) ? "DEL":"", 735 (flags & DN_DETACH) ? "DET":""); 736 if (flags & DN_DETACH) { /* detach from the list */ 737 struct dn_fsk_head *h; 738 h = fs->sched ? &fs->sched->fsk_list : &dn_cfg.fsu; 739 SLIST_REMOVE(h, fs, dn_fsk, sch_chain); 740 } 741 /* Free the RED parameters, they will be recomputed on 742 * subsequent attach if needed. 743 */ 744 if (fs->w_q_lookup) 745 free(fs->w_q_lookup, M_DUMMYNET); 746 fs->w_q_lookup = NULL; 747 qht_delete(fs, flags); 748 #ifdef NEW_AQM 749 aqm_cleanup_deconfig_fs(fs); 750 #endif 751 752 if (fs->sched && fs->sched->fp->free_fsk) 753 fs->sched->fp->free_fsk(fs); 754 fs->sched = NULL; 755 if (flags & DN_DELETE_FS) { 756 bzero(fs, sizeof(*fs)); /* safety */ 757 free(fs, M_DUMMYNET); 758 dn_cfg.fsk_count--; 759 } else { 760 SLIST_INSERT_HEAD(&dn_cfg.fsu, fs, sch_chain); 761 } 762 } 763 764 /* 765 * Detach or destroy all flowsets in a list. 766 * flags specifies what to do: 767 * DN_DESTROY: flush all queues 768 * DN_DELETE_FS: DN_DESTROY + destroy flowset 769 * DN_DELETE_FS implies DN_DESTROY 770 */ 771 static void 772 fsk_detach_list(struct dn_fsk_head *h, int flags) 773 { 774 struct dn_fsk *fs; 775 int n = 0; /* only for stats */ 776 777 ND("head %p flags %x", h, flags); 778 while ((fs = SLIST_FIRST(h))) { 779 SLIST_REMOVE_HEAD(h, sch_chain); 780 n++; 781 fsk_detach(fs, flags); 782 } 783 ND("done %d flowsets", n); 784 } 785 786 /* 787 * called on 'queue X delete' -- removes the flowset from fshash, 788 * deletes all queues for the flowset, and removes the flowset. 789 */ 790 static int 791 delete_fs(int i, int locked) 792 { 793 struct dn_fsk *fs; 794 int err = 0; 795 796 if (!locked) 797 DN_BH_WLOCK(); 798 fs = dn_ht_find(dn_cfg.fshash, i, DNHT_REMOVE, NULL); 799 ND("fs %d found %p", i, fs); 800 if (fs) { 801 fsk_detach(fs, DN_DETACH | DN_DELETE_FS); 802 err = 0; 803 } else 804 err = EINVAL; 805 if (!locked) 806 DN_BH_WUNLOCK(); 807 return err; 808 } 809 810 /*----- end of flowset hashtable support -------------*/ 811 812 /*------------------------------------------------------------ 813 * Scheduler hash. When searching by index we pass sched_nr, 814 * otherwise we pass struct dn_sch * which is the first field in 815 * struct dn_schk so we can cast between the two. We use this trick 816 * because in the create phase (but it should be fixed). 817 */ 818 static uint32_t 819 schk_hash(uintptr_t key, int flags, void *_arg) 820 { 821 uint32_t i = !(flags & DNHT_KEY_IS_OBJ) ? key : 822 ((struct dn_schk *)key)->sch.sched_nr; 823 return ( (i>>8)^(i>>4)^i ); 824 } 825 826 static int 827 schk_match(void *obj, uintptr_t key, int flags, void *_arg) 828 { 829 struct dn_schk *s = (struct dn_schk *)obj; 830 int i = !(flags & DNHT_KEY_IS_OBJ) ? key : 831 ((struct dn_schk *)key)->sch.sched_nr; 832 return (s->sch.sched_nr == i); 833 } 834 835 /* 836 * Create the entry and intialize with the sched hash if needed. 837 * Leave s->fp unset so we can tell whether a dn_ht_find() returns 838 * a new object or a previously existing one. 839 */ 840 static void * 841 schk_new(uintptr_t key, int flags, void *arg) 842 { 843 struct schk_new_arg *a = arg; 844 struct dn_schk *s; 845 int l = sizeof(*s) +a->fp->schk_datalen; 846 847 s = malloc(l, M_DUMMYNET, M_NOWAIT | M_ZERO); 848 if (s == NULL) 849 return NULL; 850 set_oid(&s->link.oid, DN_LINK, sizeof(s->link)); 851 s->sch = *a->sch; // copy initial values 852 s->link.link_nr = s->sch.sched_nr; 853 SLIST_INIT(&s->fsk_list); 854 /* initialize the hash table or create the single instance */ 855 s->fp = a->fp; /* si_new needs this */ 856 s->drain_bucket = 0; 857 if (s->sch.flags & DN_HAVE_MASK) { 858 s->siht = dn_ht_init(NULL, s->sch.buckets, 859 offsetof(struct dn_sch_inst, si_next), 860 si_hash, si_match, si_new); 861 if (s->siht == NULL) { 862 free(s, M_DUMMYNET); 863 return NULL; 864 } 865 } 866 s->fp = NULL; /* mark as a new scheduler */ 867 dn_cfg.schk_count++; 868 return s; 869 } 870 871 /* 872 * Callback for sched delete. Notify all attached flowsets to 873 * detach from the scheduler, destroy the internal flowset, and 874 * all instances. The scheduler goes away too. 875 * arg is 0 (only detach flowsets and destroy instances) 876 * DN_DESTROY (detach & delete queues, delete schk) 877 * or DN_DELETE_FS (delete queues and flowsets, delete schk) 878 */ 879 static int 880 schk_delete_cb(void *obj, void *arg) 881 { 882 struct dn_schk *s = obj; 883 #if 0 884 int a = (int)arg; 885 ND("sched %d arg %s%s", 886 s->sch.sched_nr, 887 a&DN_DESTROY ? "DEL ":"", 888 a&DN_DELETE_FS ? "DEL_FS":""); 889 #endif 890 fsk_detach_list(&s->fsk_list, arg ? DN_DESTROY : 0); 891 /* no more flowset pointing to us now */ 892 if (s->sch.flags & DN_HAVE_MASK) { 893 dn_ht_scan(s->siht, si_destroy, NULL); 894 dn_ht_free(s->siht, 0); 895 } else if (s->siht) 896 si_destroy(s->siht, NULL); 897 if (s->profile) { 898 free(s->profile, M_DUMMYNET); 899 s->profile = NULL; 900 } 901 s->siht = NULL; 902 if (s->fp->destroy) 903 s->fp->destroy(s); 904 bzero(s, sizeof(*s)); // safety 905 free(obj, M_DUMMYNET); 906 dn_cfg.schk_count--; 907 return DNHT_SCAN_DEL; 908 } 909 910 /* 911 * called on a 'sched X delete' command. Deletes a single scheduler. 912 * This is done by removing from the schedhash, unlinking all 913 * flowsets and deleting their traffic. 914 */ 915 static int 916 delete_schk(int i) 917 { 918 struct dn_schk *s; 919 920 s = dn_ht_find(dn_cfg.schedhash, i, DNHT_REMOVE, NULL); 921 ND("%d %p", i, s); 922 if (!s) 923 return EINVAL; 924 delete_fs(i + DN_MAX_ID, 1); /* first delete internal fs */ 925 /* then detach flowsets, delete traffic */ 926 schk_delete_cb(s, (void*)(uintptr_t)DN_DESTROY); 927 return 0; 928 } 929 /*--- end of schk hashtable support ---*/ 930 931 static int 932 copy_obj(char **start, char *end, void *_o, const char *msg, int i) 933 { 934 struct dn_id *o = _o; 935 int have = end - *start; 936 937 if (have < o->len || o->len == 0 || o->type == 0) { 938 D("(WARN) type %d %s %d have %d need %d", 939 o->type, msg, i, have, o->len); 940 return 1; 941 } 942 ND("type %d %s %d len %d", o->type, msg, i, o->len); 943 bcopy(_o, *start, o->len); 944 if (o->type == DN_LINK) { 945 /* Adjust burst parameter for link */ 946 struct dn_link *l = (struct dn_link *)*start; 947 l->burst = div64(l->burst, 8 * hz); 948 l->delay = l->delay * 1000 / hz; 949 } else if (o->type == DN_SCH) { 950 /* Set id->id to the number of instances */ 951 struct dn_schk *s = _o; 952 struct dn_id *id = (struct dn_id *)(*start); 953 id->id = (s->sch.flags & DN_HAVE_MASK) ? 954 dn_ht_entries(s->siht) : (s->siht ? 1 : 0); 955 } 956 *start += o->len; 957 return 0; 958 } 959 960 /* Specific function to copy a queue. 961 * Copies only the user-visible part of a queue (which is in 962 * a struct dn_flow), and sets len accordingly. 963 */ 964 static int 965 copy_obj_q(char **start, char *end, void *_o, const char *msg, int i) 966 { 967 struct dn_id *o = _o; 968 int have = end - *start; 969 int len = sizeof(struct dn_flow); /* see above comment */ 970 971 if (have < len || o->len == 0 || o->type != DN_QUEUE) { 972 D("ERROR type %d %s %d have %d need %d", 973 o->type, msg, i, have, len); 974 return 1; 975 } 976 ND("type %d %s %d len %d", o->type, msg, i, len); 977 bcopy(_o, *start, len); 978 ((struct dn_id*)(*start))->len = len; 979 *start += len; 980 return 0; 981 } 982 983 static int 984 copy_q_cb(void *obj, void *arg) 985 { 986 struct dn_queue *q = obj; 987 struct copy_args *a = arg; 988 struct dn_flow *ni = (struct dn_flow *)(*a->start); 989 if (copy_obj_q(a->start, a->end, &q->ni, "queue", -1)) 990 return DNHT_SCAN_END; 991 ni->oid.type = DN_FLOW; /* override the DN_QUEUE */ 992 ni->oid.id = si_hash((uintptr_t)&ni->fid, 0, NULL); 993 return 0; 994 } 995 996 static int 997 copy_q(struct copy_args *a, struct dn_fsk *fs, int flags) 998 { 999 if (!fs->qht) 1000 return 0; 1001 if (fs->fs.flags & DN_QHT_HASH) 1002 dn_ht_scan(fs->qht, copy_q_cb, a); 1003 else 1004 copy_q_cb(fs->qht, a); 1005 return 0; 1006 } 1007 1008 /* 1009 * This routine only copies the initial part of a profile ? XXX 1010 */ 1011 static int 1012 copy_profile(struct copy_args *a, struct dn_profile *p) 1013 { 1014 int have = a->end - *a->start; 1015 /* XXX here we check for max length */ 1016 int profile_len = sizeof(struct dn_profile) - 1017 ED_MAX_SAMPLES_NO*sizeof(int); 1018 1019 if (p == NULL) 1020 return 0; 1021 if (have < profile_len) { 1022 D("error have %d need %d", have, profile_len); 1023 return 1; 1024 } 1025 bcopy(p, *a->start, profile_len); 1026 ((struct dn_id *)(*a->start))->len = profile_len; 1027 *a->start += profile_len; 1028 return 0; 1029 } 1030 1031 static int 1032 copy_flowset(struct copy_args *a, struct dn_fsk *fs, int flags) 1033 { 1034 struct dn_fs *ufs = (struct dn_fs *)(*a->start); 1035 if (!fs) 1036 return 0; 1037 ND("flowset %d", fs->fs.fs_nr); 1038 if (copy_obj(a->start, a->end, &fs->fs, "flowset", fs->fs.fs_nr)) 1039 return DNHT_SCAN_END; 1040 ufs->oid.id = (fs->fs.flags & DN_QHT_HASH) ? 1041 dn_ht_entries(fs->qht) : (fs->qht ? 1 : 0); 1042 if (flags) { /* copy queues */ 1043 copy_q(a, fs, 0); 1044 } 1045 return 0; 1046 } 1047 1048 static int 1049 copy_si_cb(void *obj, void *arg) 1050 { 1051 struct dn_sch_inst *si = obj; 1052 struct copy_args *a = arg; 1053 struct dn_flow *ni = (struct dn_flow *)(*a->start); 1054 if (copy_obj(a->start, a->end, &si->ni, "inst", 1055 si->sched->sch.sched_nr)) 1056 return DNHT_SCAN_END; 1057 ni->oid.type = DN_FLOW; /* override the DN_SCH_I */ 1058 ni->oid.id = si_hash((uintptr_t)si, DNHT_KEY_IS_OBJ, NULL); 1059 return 0; 1060 } 1061 1062 static int 1063 copy_si(struct copy_args *a, struct dn_schk *s, int flags) 1064 { 1065 if (s->sch.flags & DN_HAVE_MASK) 1066 dn_ht_scan(s->siht, copy_si_cb, a); 1067 else if (s->siht) 1068 copy_si_cb(s->siht, a); 1069 return 0; 1070 } 1071 1072 /* 1073 * compute a list of children of a scheduler and copy up 1074 */ 1075 static int 1076 copy_fsk_list(struct copy_args *a, struct dn_schk *s, int flags) 1077 { 1078 struct dn_fsk *fs; 1079 struct dn_id *o; 1080 uint32_t *p; 1081 1082 int n = 0, space = sizeof(*o); 1083 SLIST_FOREACH(fs, &s->fsk_list, sch_chain) { 1084 if (fs->fs.fs_nr < DN_MAX_ID) 1085 n++; 1086 } 1087 space += n * sizeof(uint32_t); 1088 DX(3, "sched %d has %d flowsets", s->sch.sched_nr, n); 1089 if (a->end - *(a->start) < space) 1090 return DNHT_SCAN_END; 1091 o = (struct dn_id *)(*(a->start)); 1092 o->len = space; 1093 *a->start += o->len; 1094 o->type = DN_TEXT; 1095 p = (uint32_t *)(o+1); 1096 SLIST_FOREACH(fs, &s->fsk_list, sch_chain) 1097 if (fs->fs.fs_nr < DN_MAX_ID) 1098 *p++ = fs->fs.fs_nr; 1099 return 0; 1100 } 1101 1102 static int 1103 copy_data_helper(void *_o, void *_arg) 1104 { 1105 struct copy_args *a = _arg; 1106 uint32_t *r = a->extra->r; /* start of first range */ 1107 uint32_t *lim; /* first invalid pointer */ 1108 int n; 1109 1110 lim = (uint32_t *)((char *)(a->extra) + a->extra->o.len); 1111 1112 if (a->type == DN_LINK || a->type == DN_SCH) { 1113 /* pipe|sched show, we receive a dn_schk */ 1114 struct dn_schk *s = _o; 1115 1116 n = s->sch.sched_nr; 1117 if (a->type == DN_SCH && n >= DN_MAX_ID) 1118 return 0; /* not a scheduler */ 1119 if (a->type == DN_LINK && n <= DN_MAX_ID) 1120 return 0; /* not a pipe */ 1121 1122 /* see if the object is within one of our ranges */ 1123 for (;r < lim; r += 2) { 1124 if (n < r[0] || n > r[1]) 1125 continue; 1126 /* Found a valid entry, copy and we are done */ 1127 if (a->flags & DN_C_LINK) { 1128 if (copy_obj(a->start, a->end, 1129 &s->link, "link", n)) 1130 return DNHT_SCAN_END; 1131 if (copy_profile(a, s->profile)) 1132 return DNHT_SCAN_END; 1133 if (copy_flowset(a, s->fs, 0)) 1134 return DNHT_SCAN_END; 1135 } 1136 if (a->flags & DN_C_SCH) { 1137 if (copy_obj(a->start, a->end, 1138 &s->sch, "sched", n)) 1139 return DNHT_SCAN_END; 1140 /* list all attached flowsets */ 1141 if (copy_fsk_list(a, s, 0)) 1142 return DNHT_SCAN_END; 1143 } 1144 if (a->flags & DN_C_FLOW) 1145 copy_si(a, s, 0); 1146 break; 1147 } 1148 } else if (a->type == DN_FS) { 1149 /* queue show, skip internal flowsets */ 1150 struct dn_fsk *fs = _o; 1151 1152 n = fs->fs.fs_nr; 1153 if (n >= DN_MAX_ID) 1154 return 0; 1155 /* see if the object is within one of our ranges */ 1156 for (;r < lim; r += 2) { 1157 if (n < r[0] || n > r[1]) 1158 continue; 1159 if (copy_flowset(a, fs, 0)) 1160 return DNHT_SCAN_END; 1161 copy_q(a, fs, 0); 1162 break; /* we are done */ 1163 } 1164 } 1165 return 0; 1166 } 1167 1168 static inline struct dn_schk * 1169 locate_scheduler(int i) 1170 { 1171 return dn_ht_find(dn_cfg.schedhash, i, 0, NULL); 1172 } 1173 1174 /* 1175 * red parameters are in fixed point arithmetic. 1176 */ 1177 static int 1178 config_red(struct dn_fsk *fs) 1179 { 1180 int64_t s, idle, weight, w0; 1181 int t, i; 1182 1183 fs->w_q = fs->fs.w_q; 1184 fs->max_p = fs->fs.max_p; 1185 ND("called"); 1186 /* Doing stuff that was in userland */ 1187 i = fs->sched->link.bandwidth; 1188 s = (i <= 0) ? 0 : 1189 hz * dn_cfg.red_avg_pkt_size * 8 * SCALE(1) / i; 1190 1191 idle = div64((s * 3) , fs->w_q); /* s, fs->w_q scaled; idle not scaled */ 1192 fs->lookup_step = div64(idle , dn_cfg.red_lookup_depth); 1193 /* fs->lookup_step not scaled, */ 1194 if (!fs->lookup_step) 1195 fs->lookup_step = 1; 1196 w0 = weight = SCALE(1) - fs->w_q; //fs->w_q scaled 1197 1198 for (t = fs->lookup_step; t > 1; --t) 1199 weight = SCALE_MUL(weight, w0); 1200 fs->lookup_weight = (int)(weight); // scaled 1201 1202 /* Now doing stuff that was in kerneland */ 1203 fs->min_th = SCALE(fs->fs.min_th); 1204 fs->max_th = SCALE(fs->fs.max_th); 1205 1206 if (fs->fs.max_th == fs->fs.min_th) 1207 fs->c_1 = fs->max_p; 1208 else 1209 fs->c_1 = SCALE((int64_t)(fs->max_p)) / (fs->fs.max_th - fs->fs.min_th); 1210 fs->c_2 = SCALE_MUL(fs->c_1, SCALE(fs->fs.min_th)); 1211 1212 if (fs->fs.flags & DN_IS_GENTLE_RED) { 1213 fs->c_3 = (SCALE(1) - fs->max_p) / fs->fs.max_th; 1214 fs->c_4 = SCALE(1) - 2 * fs->max_p; 1215 } 1216 1217 /* If the lookup table already exist, free and create it again. */ 1218 if (fs->w_q_lookup) { 1219 free(fs->w_q_lookup, M_DUMMYNET); 1220 fs->w_q_lookup = NULL; 1221 } 1222 if (dn_cfg.red_lookup_depth == 0) { 1223 printf("\ndummynet: net.inet.ip.dummynet.red_lookup_depth" 1224 "must be > 0\n"); 1225 fs->fs.flags &= ~DN_IS_RED; 1226 fs->fs.flags &= ~DN_IS_GENTLE_RED; 1227 return (EINVAL); 1228 } 1229 fs->lookup_depth = dn_cfg.red_lookup_depth; 1230 fs->w_q_lookup = (u_int *)malloc(fs->lookup_depth * sizeof(int), 1231 M_DUMMYNET, M_NOWAIT); 1232 if (fs->w_q_lookup == NULL) { 1233 printf("dummynet: sorry, cannot allocate red lookup table\n"); 1234 fs->fs.flags &= ~DN_IS_RED; 1235 fs->fs.flags &= ~DN_IS_GENTLE_RED; 1236 return(ENOSPC); 1237 } 1238 1239 /* Fill the lookup table with (1 - w_q)^x */ 1240 fs->w_q_lookup[0] = SCALE(1) - fs->w_q; 1241 1242 for (i = 1; i < fs->lookup_depth; i++) 1243 fs->w_q_lookup[i] = 1244 SCALE_MUL(fs->w_q_lookup[i - 1], fs->lookup_weight); 1245 1246 if (dn_cfg.red_avg_pkt_size < 1) 1247 dn_cfg.red_avg_pkt_size = 512; 1248 fs->avg_pkt_size = dn_cfg.red_avg_pkt_size; 1249 if (dn_cfg.red_max_pkt_size < 1) 1250 dn_cfg.red_max_pkt_size = 1500; 1251 fs->max_pkt_size = dn_cfg.red_max_pkt_size; 1252 ND("exit"); 1253 return 0; 1254 } 1255 1256 /* Scan all flowset attached to this scheduler and update red */ 1257 static void 1258 update_red(struct dn_schk *s) 1259 { 1260 struct dn_fsk *fs; 1261 SLIST_FOREACH(fs, &s->fsk_list, sch_chain) { 1262 if (fs && (fs->fs.flags & DN_IS_RED)) 1263 config_red(fs); 1264 } 1265 } 1266 1267 /* attach flowset to scheduler s, possibly requeue */ 1268 static void 1269 fsk_attach(struct dn_fsk *fs, struct dn_schk *s) 1270 { 1271 ND("remove fs %d from fsunlinked, link to sched %d", 1272 fs->fs.fs_nr, s->sch.sched_nr); 1273 SLIST_REMOVE(&dn_cfg.fsu, fs, dn_fsk, sch_chain); 1274 fs->sched = s; 1275 SLIST_INSERT_HEAD(&s->fsk_list, fs, sch_chain); 1276 if (s->fp->new_fsk) 1277 s->fp->new_fsk(fs); 1278 /* XXX compute fsk_mask */ 1279 fs->fsk_mask = fs->fs.flow_mask; 1280 if (fs->sched->sch.flags & DN_HAVE_MASK) 1281 flow_id_or(&fs->sched->sch.sched_mask, &fs->fsk_mask); 1282 if (fs->qht) { 1283 /* 1284 * we must drain qht according to the old 1285 * type, and reinsert according to the new one. 1286 * The requeue is complex -- in general we need to 1287 * reclassify every single packet. 1288 * For the time being, let's hope qht is never set 1289 * when we reach this point. 1290 */ 1291 D("XXX TODO requeue from fs %d to sch %d", 1292 fs->fs.fs_nr, s->sch.sched_nr); 1293 fs->qht = NULL; 1294 } 1295 /* set the new type for qht */ 1296 if (nonzero_mask(&fs->fsk_mask)) 1297 fs->fs.flags |= DN_QHT_HASH; 1298 else 1299 fs->fs.flags &= ~DN_QHT_HASH; 1300 1301 /* XXX config_red() can fail... */ 1302 if (fs->fs.flags & DN_IS_RED) 1303 config_red(fs); 1304 } 1305 1306 /* update all flowsets which may refer to this scheduler */ 1307 static void 1308 update_fs(struct dn_schk *s) 1309 { 1310 struct dn_fsk *fs, *tmp; 1311 1312 SLIST_FOREACH_SAFE(fs, &dn_cfg.fsu, sch_chain, tmp) { 1313 if (s->sch.sched_nr != fs->fs.sched_nr) { 1314 D("fs %d for sch %d not %d still unlinked", 1315 fs->fs.fs_nr, fs->fs.sched_nr, 1316 s->sch.sched_nr); 1317 continue; 1318 } 1319 fsk_attach(fs, s); 1320 } 1321 } 1322 1323 #ifdef NEW_AQM 1324 /* Retrieve AQM configurations to ipfw userland 1325 */ 1326 static int 1327 get_aqm_parms(struct sockopt *sopt) 1328 { 1329 struct dn_extra_parms *ep; 1330 struct dn_fsk *fs; 1331 size_t sopt_valsize; 1332 int l, err = 0; 1333 1334 sopt_valsize = sopt->sopt_valsize; 1335 l = sizeof(*ep); 1336 if (sopt->sopt_valsize < l) { 1337 D("bad len sopt->sopt_valsize %d len %d", 1338 (int) sopt->sopt_valsize , l); 1339 err = EINVAL; 1340 return err; 1341 } 1342 ep = malloc(l, M_DUMMYNET, M_WAITOK); 1343 if(!ep) { 1344 err = ENOMEM ; 1345 return err; 1346 } 1347 do { 1348 err = sooptcopyin(sopt, ep, l, l); 1349 if(err) 1350 break; 1351 sopt->sopt_valsize = sopt_valsize; 1352 if (ep->oid.len < l) { 1353 err = EINVAL; 1354 break; 1355 } 1356 1357 fs = dn_ht_find(dn_cfg.fshash, ep->nr, 0, NULL); 1358 if (!fs) { 1359 D("fs %d not found", ep->nr); 1360 err = EINVAL; 1361 break; 1362 } 1363 1364 if (fs->aqmfp && fs->aqmfp->getconfig) { 1365 if(fs->aqmfp->getconfig(fs, ep)) { 1366 D("Error while trying to get AQM params"); 1367 err = EINVAL; 1368 break; 1369 } 1370 ep->oid.len = l; 1371 err = sooptcopyout(sopt, ep, l); 1372 } 1373 }while(0); 1374 1375 free(ep, M_DUMMYNET); 1376 return err; 1377 } 1378 1379 /* Retrieve AQM configurations to ipfw userland 1380 */ 1381 static int 1382 get_sched_parms(struct sockopt *sopt) 1383 { 1384 struct dn_extra_parms *ep; 1385 struct dn_schk *schk; 1386 size_t sopt_valsize; 1387 int l, err = 0; 1388 1389 sopt_valsize = sopt->sopt_valsize; 1390 l = sizeof(*ep); 1391 if (sopt->sopt_valsize < l) { 1392 D("bad len sopt->sopt_valsize %d len %d", 1393 (int) sopt->sopt_valsize , l); 1394 err = EINVAL; 1395 return err; 1396 } 1397 ep = malloc(l, M_DUMMYNET, M_WAITOK); 1398 if(!ep) { 1399 err = ENOMEM ; 1400 return err; 1401 } 1402 do { 1403 err = sooptcopyin(sopt, ep, l, l); 1404 if(err) 1405 break; 1406 sopt->sopt_valsize = sopt_valsize; 1407 if (ep->oid.len < l) { 1408 err = EINVAL; 1409 break; 1410 } 1411 1412 schk = locate_scheduler(ep->nr); 1413 if (!schk) { 1414 D("sched %d not found", ep->nr); 1415 err = EINVAL; 1416 break; 1417 } 1418 1419 if (schk->fp && schk->fp->getconfig) { 1420 if(schk->fp->getconfig(schk, ep)) { 1421 D("Error while trying to get sched params"); 1422 err = EINVAL; 1423 break; 1424 } 1425 ep->oid.len = l; 1426 err = sooptcopyout(sopt, ep, l); 1427 } 1428 }while(0); 1429 free(ep, M_DUMMYNET); 1430 1431 return err; 1432 } 1433 1434 /* Configure AQM for flowset 'fs'. 1435 * extra parameters are passed from userland. 1436 */ 1437 static int 1438 config_aqm(struct dn_fsk *fs, struct dn_extra_parms *ep, int busy) 1439 { 1440 int err = 0; 1441 1442 do { 1443 /* no configurations */ 1444 if (!ep) { 1445 err = 0; 1446 break; 1447 } 1448 1449 /* no AQM for this flowset*/ 1450 if (!strcmp(ep->name,"")) { 1451 err = 0; 1452 break; 1453 } 1454 if (ep->oid.len < sizeof(*ep)) { 1455 D("short aqm len %d", ep->oid.len); 1456 err = EINVAL; 1457 break; 1458 } 1459 1460 if (busy) { 1461 D("Unable to configure flowset, flowset busy!"); 1462 err = EINVAL; 1463 break; 1464 } 1465 1466 /* deconfigure old aqm if exist */ 1467 if (fs->aqmcfg && fs->aqmfp && fs->aqmfp->deconfig) { 1468 aqm_cleanup_deconfig_fs(fs); 1469 } 1470 1471 if (!(fs->aqmfp = find_aqm_type(0, ep->name))) { 1472 D("AQM functions not found for type %s!", ep->name); 1473 fs->fs.flags &= ~DN_IS_AQM; 1474 err = EINVAL; 1475 break; 1476 } else 1477 fs->fs.flags |= DN_IS_AQM; 1478 1479 if (ep->oid.subtype != DN_AQM_PARAMS) { 1480 D("Wrong subtype"); 1481 err = EINVAL; 1482 break; 1483 } 1484 1485 if (fs->aqmfp->config) { 1486 err = fs->aqmfp->config(fs, ep, ep->oid.len); 1487 if (err) { 1488 D("Unable to configure AQM for FS %d", fs->fs.fs_nr ); 1489 fs->fs.flags &= ~DN_IS_AQM; 1490 fs->aqmfp = NULL; 1491 break; 1492 } 1493 } 1494 } while(0); 1495 1496 return err; 1497 } 1498 #endif 1499 1500 /* 1501 * Configuration -- to preserve backward compatibility we use 1502 * the following scheme (N is 65536) 1503 * NUMBER SCHED LINK FLOWSET 1504 * 1 .. N-1 (1)WFQ (2)WFQ (3)queue 1505 * N+1 .. 2N-1 (4)FIFO (5)FIFO (6)FIFO for sched 1..N-1 1506 * 2N+1 .. 3N-1 -- -- (7)FIFO for sched N+1..2N-1 1507 * 1508 * "pipe i config" configures #1, #2 and #3 1509 * "sched i config" configures #1 and possibly #6 1510 * "queue i config" configures #3 1511 * #1 is configured with 'pipe i config' or 'sched i config' 1512 * #2 is configured with 'pipe i config', and created if not 1513 * existing with 'sched i config' 1514 * #3 is configured with 'queue i config' 1515 * #4 is automatically configured after #1, can only be FIFO 1516 * #5 is automatically configured after #2 1517 * #6 is automatically created when #1 is !MULTIQUEUE, 1518 * and can be updated. 1519 * #7 is automatically configured after #2 1520 */ 1521 1522 /* 1523 * configure a link (and its FIFO instance) 1524 */ 1525 static int 1526 config_link(struct dn_link *p, struct dn_id *arg) 1527 { 1528 int i; 1529 1530 if (p->oid.len != sizeof(*p)) { 1531 D("invalid pipe len %d", p->oid.len); 1532 return EINVAL; 1533 } 1534 i = p->link_nr; 1535 if (i <= 0 || i >= DN_MAX_ID) 1536 return EINVAL; 1537 /* 1538 * The config program passes parameters as follows: 1539 * bw = bits/second (0 means no limits), 1540 * delay = ms, must be translated into ticks. 1541 * qsize = slots/bytes 1542 * burst ??? 1543 */ 1544 p->delay = (p->delay * hz) / 1000; 1545 /* Scale burst size: bytes -> bits * hz */ 1546 p->burst *= 8 * hz; 1547 1548 DN_BH_WLOCK(); 1549 /* do it twice, base link and FIFO link */ 1550 for (; i < 2*DN_MAX_ID; i += DN_MAX_ID) { 1551 struct dn_schk *s = locate_scheduler(i); 1552 if (s == NULL) { 1553 DN_BH_WUNLOCK(); 1554 D("sched %d not found", i); 1555 return EINVAL; 1556 } 1557 /* remove profile if exists */ 1558 if (s->profile) { 1559 free(s->profile, M_DUMMYNET); 1560 s->profile = NULL; 1561 } 1562 /* copy all parameters */ 1563 s->link.oid = p->oid; 1564 s->link.link_nr = i; 1565 s->link.delay = p->delay; 1566 if (s->link.bandwidth != p->bandwidth) { 1567 /* XXX bandwidth changes, need to update red params */ 1568 s->link.bandwidth = p->bandwidth; 1569 update_red(s); 1570 } 1571 s->link.burst = p->burst; 1572 schk_reset_credit(s); 1573 } 1574 dn_cfg.id++; 1575 DN_BH_WUNLOCK(); 1576 return 0; 1577 } 1578 1579 /* 1580 * configure a flowset. Can be called from inside with locked=1, 1581 */ 1582 static struct dn_fsk * 1583 config_fs(struct dn_fs *nfs, struct dn_id *arg, int locked) 1584 { 1585 int i; 1586 struct dn_fsk *fs; 1587 1588 if (nfs->oid.len != sizeof(*nfs)) { 1589 D("invalid flowset len %d", nfs->oid.len); 1590 return NULL; 1591 } 1592 i = nfs->fs_nr; 1593 if (i <= 0 || i >= 3*DN_MAX_ID) 1594 return NULL; 1595 ND("flowset %d", i); 1596 /* XXX other sanity checks */ 1597 if (nfs->flags & DN_QSIZE_BYTES) { 1598 ipdn_bound_var(&nfs->qsize, 16384, 1599 1500, dn_cfg.byte_limit, NULL); // "queue byte size"); 1600 } else { 1601 ipdn_bound_var(&nfs->qsize, 50, 1602 1, dn_cfg.slot_limit, NULL); // "queue slot size"); 1603 } 1604 if (nfs->flags & DN_HAVE_MASK) { 1605 /* make sure we have some buckets */ 1606 ipdn_bound_var((int *)&nfs->buckets, dn_cfg.hash_size, 1607 1, dn_cfg.max_hash_size, "flowset buckets"); 1608 } else { 1609 nfs->buckets = 1; /* we only need 1 */ 1610 } 1611 if (!locked) 1612 DN_BH_WLOCK(); 1613 do { /* exit with break when done */ 1614 struct dn_schk *s; 1615 int flags = nfs->sched_nr ? DNHT_INSERT : 0; 1616 int j; 1617 int oldc = dn_cfg.fsk_count; 1618 fs = dn_ht_find(dn_cfg.fshash, i, flags, NULL); 1619 if (fs == NULL) { 1620 D("missing sched for flowset %d", i); 1621 break; 1622 } 1623 /* grab some defaults from the existing one */ 1624 if (nfs->sched_nr == 0) /* reuse */ 1625 nfs->sched_nr = fs->fs.sched_nr; 1626 for (j = 0; j < sizeof(nfs->par)/sizeof(nfs->par[0]); j++) { 1627 if (nfs->par[j] == -1) /* reuse */ 1628 nfs->par[j] = fs->fs.par[j]; 1629 } 1630 if (bcmp(&fs->fs, nfs, sizeof(*nfs)) == 0) { 1631 ND("flowset %d unchanged", i); 1632 #ifdef NEW_AQM 1633 /* reconfigure AQM as the parameters can be changed. 1634 * we consider the flowsetis busy if it has scheduler instance(s) 1635 */ 1636 s = locate_scheduler(nfs->sched_nr); 1637 config_aqm(fs, (struct dn_extra_parms *) arg, 1638 s != NULL && s->siht != NULL); 1639 #endif 1640 break; /* no change, nothing to do */ 1641 } 1642 if (oldc != dn_cfg.fsk_count) /* new item */ 1643 dn_cfg.id++; 1644 s = locate_scheduler(nfs->sched_nr); 1645 /* detach from old scheduler if needed, preserving 1646 * queues if we need to reattach. Then update the 1647 * configuration, and possibly attach to the new sched. 1648 */ 1649 DX(2, "fs %d changed sched %d@%p to %d@%p", 1650 fs->fs.fs_nr, 1651 fs->fs.sched_nr, fs->sched, nfs->sched_nr, s); 1652 if (fs->sched) { 1653 int flags = s ? DN_DETACH : (DN_DETACH | DN_DESTROY); 1654 flags |= DN_DESTROY; /* XXX temporary */ 1655 fsk_detach(fs, flags); 1656 } 1657 fs->fs = *nfs; /* copy configuration */ 1658 #ifdef NEW_AQM 1659 fs->aqmfp = NULL; 1660 config_aqm(fs, (struct dn_extra_parms *) arg, s != NULL && s->siht != NULL); 1661 #endif 1662 if (s != NULL) 1663 fsk_attach(fs, s); 1664 } while (0); 1665 if (!locked) 1666 DN_BH_WUNLOCK(); 1667 return fs; 1668 } 1669 1670 /* 1671 * config/reconfig a scheduler and its FIFO variant. 1672 * For !MULTIQUEUE schedulers, also set up the flowset. 1673 * 1674 * On reconfigurations (detected because s->fp is set), 1675 * detach existing flowsets preserving traffic, preserve link, 1676 * and delete the old scheduler creating a new one. 1677 */ 1678 static int 1679 config_sched(struct dn_sch *_nsch, struct dn_id *arg) 1680 { 1681 struct dn_schk *s; 1682 struct schk_new_arg a; /* argument for schk_new */ 1683 int i; 1684 struct dn_link p; /* copy of oldlink */ 1685 struct dn_profile *pf = NULL; /* copy of old link profile */ 1686 /* Used to preserv mask parameter */ 1687 struct ipfw_flow_id new_mask; 1688 int new_buckets = 0; 1689 int new_flags = 0; 1690 int pipe_cmd; 1691 int err = ENOMEM; 1692 1693 a.sch = _nsch; 1694 if (a.sch->oid.len != sizeof(*a.sch)) { 1695 D("bad sched len %d", a.sch->oid.len); 1696 return EINVAL; 1697 } 1698 i = a.sch->sched_nr; 1699 if (i <= 0 || i >= DN_MAX_ID) 1700 return EINVAL; 1701 /* make sure we have some buckets */ 1702 if (a.sch->flags & DN_HAVE_MASK) 1703 ipdn_bound_var((int *)&a.sch->buckets, dn_cfg.hash_size, 1704 1, dn_cfg.max_hash_size, "sched buckets"); 1705 /* XXX other sanity checks */ 1706 bzero(&p, sizeof(p)); 1707 1708 pipe_cmd = a.sch->flags & DN_PIPE_CMD; 1709 a.sch->flags &= ~DN_PIPE_CMD; //XXX do it even if is not set? 1710 if (pipe_cmd) { 1711 /* Copy mask parameter */ 1712 new_mask = a.sch->sched_mask; 1713 new_buckets = a.sch->buckets; 1714 new_flags = a.sch->flags; 1715 } 1716 DN_BH_WLOCK(); 1717 again: /* run twice, for wfq and fifo */ 1718 /* 1719 * lookup the type. If not supplied, use the previous one 1720 * or default to WF2Q+. Otherwise, return an error. 1721 */ 1722 dn_cfg.id++; 1723 a.fp = find_sched_type(a.sch->oid.subtype, a.sch->name); 1724 if (a.fp != NULL) { 1725 /* found. Lookup or create entry */ 1726 s = dn_ht_find(dn_cfg.schedhash, i, DNHT_INSERT, &a); 1727 } else if (a.sch->oid.subtype == 0 && !a.sch->name[0]) { 1728 /* No type. search existing s* or retry with WF2Q+ */ 1729 s = dn_ht_find(dn_cfg.schedhash, i, 0, &a); 1730 if (s != NULL) { 1731 a.fp = s->fp; 1732 /* Scheduler exists, skip to FIFO scheduler 1733 * if command was pipe config... 1734 */ 1735 if (pipe_cmd) 1736 goto next; 1737 } else { 1738 /* New scheduler, create a wf2q+ with no mask 1739 * if command was pipe config... 1740 */ 1741 if (pipe_cmd) { 1742 /* clear mask parameter */ 1743 bzero(&a.sch->sched_mask, sizeof(new_mask)); 1744 a.sch->buckets = 0; 1745 a.sch->flags &= ~DN_HAVE_MASK; 1746 } 1747 a.sch->oid.subtype = DN_SCHED_WF2QP; 1748 goto again; 1749 } 1750 } else { 1751 D("invalid scheduler type %d %s", 1752 a.sch->oid.subtype, a.sch->name); 1753 err = EINVAL; 1754 goto error; 1755 } 1756 /* normalize name and subtype */ 1757 a.sch->oid.subtype = a.fp->type; 1758 bzero(a.sch->name, sizeof(a.sch->name)); 1759 strlcpy(a.sch->name, a.fp->name, sizeof(a.sch->name)); 1760 if (s == NULL) { 1761 D("cannot allocate scheduler %d", i); 1762 goto error; 1763 } 1764 /* restore existing link if any */ 1765 if (p.link_nr) { 1766 s->link = p; 1767 if (!pf || pf->link_nr != p.link_nr) { /* no saved value */ 1768 s->profile = NULL; /* XXX maybe not needed */ 1769 } else { 1770 s->profile = malloc(sizeof(struct dn_profile), 1771 M_DUMMYNET, M_NOWAIT | M_ZERO); 1772 if (s->profile == NULL) { 1773 D("cannot allocate profile"); 1774 goto error; //XXX 1775 } 1776 bcopy(pf, s->profile, sizeof(*pf)); 1777 } 1778 } 1779 p.link_nr = 0; 1780 if (s->fp == NULL) { 1781 DX(2, "sched %d new type %s", i, a.fp->name); 1782 } else if (s->fp != a.fp || 1783 bcmp(a.sch, &s->sch, sizeof(*a.sch)) ) { 1784 /* already existing. */ 1785 DX(2, "sched %d type changed from %s to %s", 1786 i, s->fp->name, a.fp->name); 1787 DX(4, " type/sub %d/%d -> %d/%d", 1788 s->sch.oid.type, s->sch.oid.subtype, 1789 a.sch->oid.type, a.sch->oid.subtype); 1790 if (s->link.link_nr == 0) 1791 D("XXX WARNING link 0 for sched %d", i); 1792 p = s->link; /* preserve link */ 1793 if (s->profile) {/* preserve profile */ 1794 if (!pf) 1795 pf = malloc(sizeof(*pf), 1796 M_DUMMYNET, M_NOWAIT | M_ZERO); 1797 if (pf) /* XXX should issue a warning otherwise */ 1798 bcopy(s->profile, pf, sizeof(*pf)); 1799 } 1800 /* remove from the hash */ 1801 dn_ht_find(dn_cfg.schedhash, i, DNHT_REMOVE, NULL); 1802 /* Detach flowsets, preserve queues. */ 1803 // schk_delete_cb(s, NULL); 1804 // XXX temporarily, kill queues 1805 schk_delete_cb(s, (void *)DN_DESTROY); 1806 goto again; 1807 } else { 1808 DX(4, "sched %d unchanged type %s", i, a.fp->name); 1809 } 1810 /* complete initialization */ 1811 s->sch = *a.sch; 1812 s->fp = a.fp; 1813 s->cfg = arg; 1814 // XXX schk_reset_credit(s); 1815 /* create the internal flowset if needed, 1816 * trying to reuse existing ones if available 1817 */ 1818 if (!(s->fp->flags & DN_MULTIQUEUE) && !s->fs) { 1819 s->fs = dn_ht_find(dn_cfg.fshash, i, 0, NULL); 1820 if (!s->fs) { 1821 struct dn_fs fs; 1822 bzero(&fs, sizeof(fs)); 1823 set_oid(&fs.oid, DN_FS, sizeof(fs)); 1824 fs.fs_nr = i + DN_MAX_ID; 1825 fs.sched_nr = i; 1826 s->fs = config_fs(&fs, NULL, 1 /* locked */); 1827 } 1828 if (!s->fs) { 1829 schk_delete_cb(s, (void *)DN_DESTROY); 1830 D("error creating internal fs for %d", i); 1831 goto error; 1832 } 1833 } 1834 /* call init function after the flowset is created */ 1835 if (s->fp->config) 1836 s->fp->config(s); 1837 update_fs(s); 1838 next: 1839 if (i < DN_MAX_ID) { /* now configure the FIFO instance */ 1840 i += DN_MAX_ID; 1841 if (pipe_cmd) { 1842 /* Restore mask parameter for FIFO */ 1843 a.sch->sched_mask = new_mask; 1844 a.sch->buckets = new_buckets; 1845 a.sch->flags = new_flags; 1846 } else { 1847 /* sched config shouldn't modify the FIFO scheduler */ 1848 if (dn_ht_find(dn_cfg.schedhash, i, 0, &a) != NULL) { 1849 /* FIFO already exist, don't touch it */ 1850 err = 0; /* and this is not an error */ 1851 goto error; 1852 } 1853 } 1854 a.sch->sched_nr = i; 1855 a.sch->oid.subtype = DN_SCHED_FIFO; 1856 bzero(a.sch->name, sizeof(a.sch->name)); 1857 goto again; 1858 } 1859 err = 0; 1860 error: 1861 DN_BH_WUNLOCK(); 1862 if (pf) 1863 free(pf, M_DUMMYNET); 1864 return err; 1865 } 1866 1867 /* 1868 * attach a profile to a link 1869 */ 1870 static int 1871 config_profile(struct dn_profile *pf, struct dn_id *arg) 1872 { 1873 struct dn_schk *s; 1874 int i, olen, err = 0; 1875 1876 if (pf->oid.len < sizeof(*pf)) { 1877 D("short profile len %d", pf->oid.len); 1878 return EINVAL; 1879 } 1880 i = pf->link_nr; 1881 if (i <= 0 || i >= DN_MAX_ID) 1882 return EINVAL; 1883 /* XXX other sanity checks */ 1884 DN_BH_WLOCK(); 1885 for (; i < 2*DN_MAX_ID; i += DN_MAX_ID) { 1886 s = locate_scheduler(i); 1887 1888 if (s == NULL) { 1889 err = EINVAL; 1890 break; 1891 } 1892 dn_cfg.id++; 1893 /* 1894 * If we had a profile and the new one does not fit, 1895 * or it is deleted, then we need to free memory. 1896 */ 1897 if (s->profile && (pf->samples_no == 0 || 1898 s->profile->oid.len < pf->oid.len)) { 1899 free(s->profile, M_DUMMYNET); 1900 s->profile = NULL; 1901 } 1902 if (pf->samples_no == 0) 1903 continue; 1904 /* 1905 * new profile, possibly allocate memory 1906 * and copy data. 1907 */ 1908 if (s->profile == NULL) 1909 s->profile = malloc(pf->oid.len, 1910 M_DUMMYNET, M_NOWAIT | M_ZERO); 1911 if (s->profile == NULL) { 1912 D("no memory for profile %d", i); 1913 err = ENOMEM; 1914 break; 1915 } 1916 /* preserve larger length XXX double check */ 1917 olen = s->profile->oid.len; 1918 if (olen < pf->oid.len) 1919 olen = pf->oid.len; 1920 bcopy(pf, s->profile, pf->oid.len); 1921 s->profile->oid.len = olen; 1922 } 1923 DN_BH_WUNLOCK(); 1924 return err; 1925 } 1926 1927 /* 1928 * Delete all objects: 1929 */ 1930 static void 1931 dummynet_flush(void) 1932 { 1933 1934 /* delete all schedulers and related links/queues/flowsets */ 1935 dn_ht_scan(dn_cfg.schedhash, schk_delete_cb, 1936 (void *)(uintptr_t)DN_DELETE_FS); 1937 /* delete all remaining (unlinked) flowsets */ 1938 DX(4, "still %d unlinked fs", dn_cfg.fsk_count); 1939 dn_ht_free(dn_cfg.fshash, DNHT_REMOVE); 1940 fsk_detach_list(&dn_cfg.fsu, DN_DELETE_FS); 1941 /* Reinitialize system heap... */ 1942 heap_init(&dn_cfg.evheap, 16, offsetof(struct dn_id, id)); 1943 } 1944 1945 /* 1946 * Main handler for configuration. We are guaranteed to be called 1947 * with an oid which is at least a dn_id. 1948 * - the first object is the command (config, delete, flush, ...) 1949 * - config_link must be issued after the corresponding config_sched 1950 * - parameters (DN_TXT) for an object must precede the object 1951 * processed on a config_sched. 1952 */ 1953 int 1954 do_config(void *p, int l) 1955 { 1956 struct dn_id *next, *o; 1957 int err = 0, err2 = 0; 1958 struct dn_id *arg = NULL; 1959 uintptr_t *a; 1960 1961 o = p; 1962 if (o->id != DN_API_VERSION) { 1963 D("invalid api version got %d need %d", 1964 o->id, DN_API_VERSION); 1965 return EINVAL; 1966 } 1967 for (; l >= sizeof(*o); o = next) { 1968 struct dn_id *prev = arg; 1969 if (o->len < sizeof(*o) || l < o->len) { 1970 D("bad len o->len %d len %d", o->len, l); 1971 err = EINVAL; 1972 break; 1973 } 1974 l -= o->len; 1975 next = (struct dn_id *)((char *)o + o->len); 1976 err = 0; 1977 switch (o->type) { 1978 default: 1979 D("cmd %d not implemented", o->type); 1980 break; 1981 1982 #ifdef EMULATE_SYSCTL 1983 /* sysctl emulation. 1984 * if we recognize the command, jump to the correct 1985 * handler and return 1986 */ 1987 case DN_SYSCTL_SET: 1988 err = kesysctl_emu_set(p, l); 1989 return err; 1990 #endif 1991 1992 case DN_CMD_CONFIG: /* simply a header */ 1993 break; 1994 1995 case DN_CMD_DELETE: 1996 /* the argument is in the first uintptr_t after o */ 1997 a = (uintptr_t *)(o+1); 1998 if (o->len < sizeof(*o) + sizeof(*a)) { 1999 err = EINVAL; 2000 break; 2001 } 2002 switch (o->subtype) { 2003 case DN_LINK: 2004 /* delete base and derived schedulers */ 2005 DN_BH_WLOCK(); 2006 err = delete_schk(*a); 2007 err2 = delete_schk(*a + DN_MAX_ID); 2008 DN_BH_WUNLOCK(); 2009 if (!err) 2010 err = err2; 2011 break; 2012 2013 default: 2014 D("invalid delete type %d", 2015 o->subtype); 2016 err = EINVAL; 2017 break; 2018 2019 case DN_FS: 2020 err = (*a <1 || *a >= DN_MAX_ID) ? 2021 EINVAL : delete_fs(*a, 0) ; 2022 break; 2023 } 2024 break; 2025 2026 case DN_CMD_FLUSH: 2027 DN_BH_WLOCK(); 2028 dummynet_flush(); 2029 DN_BH_WUNLOCK(); 2030 break; 2031 case DN_TEXT: /* store argument the next block */ 2032 prev = NULL; 2033 arg = o; 2034 break; 2035 case DN_LINK: 2036 err = config_link((struct dn_link *)o, arg); 2037 break; 2038 case DN_PROFILE: 2039 err = config_profile((struct dn_profile *)o, arg); 2040 break; 2041 case DN_SCH: 2042 err = config_sched((struct dn_sch *)o, arg); 2043 break; 2044 case DN_FS: 2045 err = (NULL==config_fs((struct dn_fs *)o, arg, 0)); 2046 break; 2047 } 2048 if (prev) 2049 arg = NULL; 2050 if (err != 0) 2051 break; 2052 } 2053 return err; 2054 } 2055 2056 static int 2057 compute_space(struct dn_id *cmd, struct copy_args *a) 2058 { 2059 int x = 0, need = 0; 2060 int profile_size = sizeof(struct dn_profile) - 2061 ED_MAX_SAMPLES_NO*sizeof(int); 2062 2063 /* NOTE about compute space: 2064 * NP = dn_cfg.schk_count 2065 * NSI = dn_cfg.si_count 2066 * NF = dn_cfg.fsk_count 2067 * NQ = dn_cfg.queue_count 2068 * - ipfw pipe show 2069 * (NP/2)*(dn_link + dn_sch + dn_id + dn_fs) only half scheduler 2070 * link, scheduler template, flowset 2071 * integrated in scheduler and header 2072 * for flowset list 2073 * (NSI)*(dn_flow) all scheduler instance (includes 2074 * the queue instance) 2075 * - ipfw sched show 2076 * (NP/2)*(dn_link + dn_sch + dn_id + dn_fs) only half scheduler 2077 * link, scheduler template, flowset 2078 * integrated in scheduler and header 2079 * for flowset list 2080 * (NSI * dn_flow) all scheduler instances 2081 * (NF * sizeof(uint_32)) space for flowset list linked to scheduler 2082 * (NQ * dn_queue) all queue [XXXfor now not listed] 2083 * - ipfw queue show 2084 * (NF * dn_fs) all flowset 2085 * (NQ * dn_queue) all queues 2086 */ 2087 switch (cmd->subtype) { 2088 default: 2089 return -1; 2090 /* XXX where do LINK and SCH differ ? */ 2091 /* 'ipfw sched show' could list all queues associated to 2092 * a scheduler. This feature for now is disabled 2093 */ 2094 case DN_LINK: /* pipe show */ 2095 x = DN_C_LINK | DN_C_SCH | DN_C_FLOW; 2096 need += dn_cfg.schk_count * 2097 (sizeof(struct dn_fs) + profile_size) / 2; 2098 need += dn_cfg.fsk_count * sizeof(uint32_t); 2099 break; 2100 case DN_SCH: /* sched show */ 2101 need += dn_cfg.schk_count * 2102 (sizeof(struct dn_fs) + profile_size) / 2; 2103 need += dn_cfg.fsk_count * sizeof(uint32_t); 2104 x = DN_C_SCH | DN_C_LINK | DN_C_FLOW; 2105 break; 2106 case DN_FS: /* queue show */ 2107 x = DN_C_FS | DN_C_QUEUE; 2108 break; 2109 case DN_GET_COMPAT: /* compatibility mode */ 2110 need = dn_compat_calc_size(); 2111 break; 2112 } 2113 a->flags = x; 2114 if (x & DN_C_SCH) { 2115 need += dn_cfg.schk_count * sizeof(struct dn_sch) / 2; 2116 /* NOT also, each fs might be attached to a sched */ 2117 need += dn_cfg.schk_count * sizeof(struct dn_id) / 2; 2118 } 2119 if (x & DN_C_FS) 2120 need += dn_cfg.fsk_count * sizeof(struct dn_fs); 2121 if (x & DN_C_LINK) { 2122 need += dn_cfg.schk_count * sizeof(struct dn_link) / 2; 2123 } 2124 /* 2125 * When exporting a queue to userland, only pass up the 2126 * struct dn_flow, which is the only visible part. 2127 */ 2128 2129 if (x & DN_C_QUEUE) 2130 need += dn_cfg.queue_count * sizeof(struct dn_flow); 2131 if (x & DN_C_FLOW) 2132 need += dn_cfg.si_count * (sizeof(struct dn_flow)); 2133 return need; 2134 } 2135 2136 /* 2137 * If compat != NULL dummynet_get is called in compatibility mode. 2138 * *compat will be the pointer to the buffer to pass to ipfw 2139 */ 2140 int 2141 dummynet_get(struct sockopt *sopt, void **compat) 2142 { 2143 int have, i, need, error; 2144 char *start = NULL, *buf; 2145 size_t sopt_valsize; 2146 struct dn_id *cmd; 2147 struct copy_args a; 2148 struct copy_range r; 2149 int l = sizeof(struct dn_id); 2150 2151 bzero(&a, sizeof(a)); 2152 bzero(&r, sizeof(r)); 2153 2154 /* save and restore original sopt_valsize around copyin */ 2155 sopt_valsize = sopt->sopt_valsize; 2156 2157 cmd = &r.o; 2158 2159 if (!compat) { 2160 /* copy at least an oid, and possibly a full object */ 2161 error = sooptcopyin(sopt, cmd, sizeof(r), sizeof(*cmd)); 2162 sopt->sopt_valsize = sopt_valsize; 2163 if (error) 2164 goto done; 2165 l = cmd->len; 2166 #ifdef EMULATE_SYSCTL 2167 /* sysctl emulation. */ 2168 if (cmd->type == DN_SYSCTL_GET) 2169 return kesysctl_emu_get(sopt); 2170 #endif 2171 if (l > sizeof(r)) { 2172 /* request larger than default, allocate buffer */ 2173 cmd = malloc(l, M_DUMMYNET, M_WAITOK); 2174 error = sooptcopyin(sopt, cmd, l, l); 2175 sopt->sopt_valsize = sopt_valsize; 2176 if (error) 2177 goto done; 2178 } 2179 } else { /* compatibility */ 2180 error = 0; 2181 cmd->type = DN_CMD_GET; 2182 cmd->len = sizeof(struct dn_id); 2183 cmd->subtype = DN_GET_COMPAT; 2184 // cmd->id = sopt_valsize; 2185 D("compatibility mode"); 2186 } 2187 2188 #ifdef NEW_AQM 2189 /* get AQM params */ 2190 if(cmd->subtype == DN_AQM_PARAMS) { 2191 error = get_aqm_parms(sopt); 2192 goto done; 2193 /* get Scheduler params */ 2194 } else if (cmd->subtype == DN_SCH_PARAMS) { 2195 error = get_sched_parms(sopt); 2196 goto done; 2197 } 2198 #endif 2199 2200 a.extra = (struct copy_range *)cmd; 2201 if (cmd->len == sizeof(*cmd)) { /* no range, create a default */ 2202 uint32_t *rp = (uint32_t *)(cmd + 1); 2203 cmd->len += 2* sizeof(uint32_t); 2204 rp[0] = 1; 2205 rp[1] = DN_MAX_ID - 1; 2206 if (cmd->subtype == DN_LINK) { 2207 rp[0] += DN_MAX_ID; 2208 rp[1] += DN_MAX_ID; 2209 } 2210 } 2211 /* Count space (under lock) and allocate (outside lock). 2212 * Exit with lock held if we manage to get enough buffer. 2213 * Try a few times then give up. 2214 */ 2215 for (have = 0, i = 0; i < 10; i++) { 2216 DN_BH_WLOCK(); 2217 need = compute_space(cmd, &a); 2218 2219 /* if there is a range, ignore value from compute_space() */ 2220 if (l > sizeof(*cmd)) 2221 need = sopt_valsize - sizeof(*cmd); 2222 2223 if (need < 0) { 2224 DN_BH_WUNLOCK(); 2225 error = EINVAL; 2226 goto done; 2227 } 2228 need += sizeof(*cmd); 2229 cmd->id = need; 2230 if (have >= need) 2231 break; 2232 2233 DN_BH_WUNLOCK(); 2234 if (start) 2235 free(start, M_DUMMYNET); 2236 start = NULL; 2237 if (need > sopt_valsize) 2238 break; 2239 2240 have = need; 2241 start = malloc(have, M_DUMMYNET, M_WAITOK | M_ZERO); 2242 } 2243 2244 if (start == NULL) { 2245 if (compat) { 2246 *compat = NULL; 2247 error = 1; // XXX 2248 } else { 2249 error = sooptcopyout(sopt, cmd, sizeof(*cmd)); 2250 } 2251 goto done; 2252 } 2253 ND("have %d:%d sched %d, %d:%d links %d, %d:%d flowsets %d, " 2254 "%d:%d si %d, %d:%d queues %d", 2255 dn_cfg.schk_count, sizeof(struct dn_sch), DN_SCH, 2256 dn_cfg.schk_count, sizeof(struct dn_link), DN_LINK, 2257 dn_cfg.fsk_count, sizeof(struct dn_fs), DN_FS, 2258 dn_cfg.si_count, sizeof(struct dn_flow), DN_SCH_I, 2259 dn_cfg.queue_count, sizeof(struct dn_queue), DN_QUEUE); 2260 sopt->sopt_valsize = sopt_valsize; 2261 a.type = cmd->subtype; 2262 2263 if (compat == NULL) { 2264 bcopy(cmd, start, sizeof(*cmd)); 2265 ((struct dn_id*)(start))->len = sizeof(struct dn_id); 2266 buf = start + sizeof(*cmd); 2267 } else 2268 buf = start; 2269 a.start = &buf; 2270 a.end = start + have; 2271 /* start copying other objects */ 2272 if (compat) { 2273 a.type = DN_COMPAT_PIPE; 2274 dn_ht_scan(dn_cfg.schedhash, copy_data_helper_compat, &a); 2275 a.type = DN_COMPAT_QUEUE; 2276 dn_ht_scan(dn_cfg.fshash, copy_data_helper_compat, &a); 2277 } else if (a.type == DN_FS) { 2278 dn_ht_scan(dn_cfg.fshash, copy_data_helper, &a); 2279 } else { 2280 dn_ht_scan(dn_cfg.schedhash, copy_data_helper, &a); 2281 } 2282 DN_BH_WUNLOCK(); 2283 2284 if (compat) { 2285 *compat = start; 2286 sopt->sopt_valsize = buf - start; 2287 /* free() is done by ip_dummynet_compat() */ 2288 start = NULL; //XXX hack 2289 } else { 2290 error = sooptcopyout(sopt, start, buf - start); 2291 } 2292 done: 2293 if (cmd && cmd != &r.o) 2294 free(cmd, M_DUMMYNET); 2295 if (start) 2296 free(start, M_DUMMYNET); 2297 return error; 2298 } 2299 2300 /* Callback called on scheduler instance to delete it if idle */ 2301 static int 2302 drain_scheduler_cb(void *_si, void *arg) 2303 { 2304 struct dn_sch_inst *si = _si; 2305 2306 if ((si->kflags & DN_ACTIVE) || si->dline.mq.head != NULL) 2307 return 0; 2308 2309 if (si->sched->fp->flags & DN_MULTIQUEUE) { 2310 if (si->q_count == 0) 2311 return si_destroy(si, NULL); 2312 else 2313 return 0; 2314 } else { /* !DN_MULTIQUEUE */ 2315 if ((si+1)->ni.length == 0) 2316 return si_destroy(si, NULL); 2317 else 2318 return 0; 2319 } 2320 return 0; /* unreachable */ 2321 } 2322 2323 /* Callback called on scheduler to check if it has instances */ 2324 static int 2325 drain_scheduler_sch_cb(void *_s, void *arg) 2326 { 2327 struct dn_schk *s = _s; 2328 2329 if (s->sch.flags & DN_HAVE_MASK) { 2330 dn_ht_scan_bucket(s->siht, &s->drain_bucket, 2331 drain_scheduler_cb, NULL); 2332 s->drain_bucket++; 2333 } else { 2334 if (s->siht) { 2335 if (drain_scheduler_cb(s->siht, NULL) == DNHT_SCAN_DEL) 2336 s->siht = NULL; 2337 } 2338 } 2339 return 0; 2340 } 2341 2342 /* Called every tick, try to delete a 'bucket' of scheduler */ 2343 void 2344 dn_drain_scheduler(void) 2345 { 2346 dn_ht_scan_bucket(dn_cfg.schedhash, &dn_cfg.drain_sch, 2347 drain_scheduler_sch_cb, NULL); 2348 dn_cfg.drain_sch++; 2349 } 2350 2351 /* Callback called on queue to delete if it is idle */ 2352 static int 2353 drain_queue_cb(void *_q, void *arg) 2354 { 2355 struct dn_queue *q = _q; 2356 2357 if (q->ni.length == 0) { 2358 dn_delete_queue(q, DN_DESTROY); 2359 return DNHT_SCAN_DEL; /* queue is deleted */ 2360 } 2361 2362 return 0; /* queue isn't deleted */ 2363 } 2364 2365 /* Callback called on flowset used to check if it has queues */ 2366 static int 2367 drain_queue_fs_cb(void *_fs, void *arg) 2368 { 2369 struct dn_fsk *fs = _fs; 2370 2371 if (fs->fs.flags & DN_QHT_HASH) { 2372 /* Flowset has a hash table for queues */ 2373 dn_ht_scan_bucket(fs->qht, &fs->drain_bucket, 2374 drain_queue_cb, NULL); 2375 fs->drain_bucket++; 2376 } else { 2377 /* No hash table for this flowset, null the pointer 2378 * if the queue is deleted 2379 */ 2380 if (fs->qht) { 2381 if (drain_queue_cb(fs->qht, NULL) == DNHT_SCAN_DEL) 2382 fs->qht = NULL; 2383 } 2384 } 2385 return 0; 2386 } 2387 2388 /* Called every tick, try to delete a 'bucket' of queue */ 2389 void 2390 dn_drain_queue(void) 2391 { 2392 /* scan a bucket of flowset */ 2393 dn_ht_scan_bucket(dn_cfg.fshash, &dn_cfg.drain_fs, 2394 drain_queue_fs_cb, NULL); 2395 dn_cfg.drain_fs++; 2396 } 2397 2398 /* 2399 * Handler for the various dummynet socket options 2400 */ 2401 static int 2402 ip_dn_ctl(struct sockopt *sopt) 2403 { 2404 void *p = NULL; 2405 int error, l; 2406 2407 error = priv_check(sopt->sopt_td, PRIV_NETINET_DUMMYNET); 2408 if (error) 2409 return (error); 2410 2411 /* Disallow sets in really-really secure mode. */ 2412 if (sopt->sopt_dir == SOPT_SET) { 2413 error = securelevel_ge(sopt->sopt_td->td_ucred, 3); 2414 if (error) 2415 return (error); 2416 } 2417 2418 switch (sopt->sopt_name) { 2419 default : 2420 D("dummynet: unknown option %d", sopt->sopt_name); 2421 error = EINVAL; 2422 break; 2423 2424 case IP_DUMMYNET_FLUSH: 2425 case IP_DUMMYNET_CONFIGURE: 2426 case IP_DUMMYNET_DEL: /* remove a pipe or queue */ 2427 case IP_DUMMYNET_GET: 2428 D("dummynet: compat option %d", sopt->sopt_name); 2429 error = ip_dummynet_compat(sopt); 2430 break; 2431 2432 case IP_DUMMYNET3 : 2433 if (sopt->sopt_dir == SOPT_GET) { 2434 error = dummynet_get(sopt, NULL); 2435 break; 2436 } 2437 l = sopt->sopt_valsize; 2438 if (l < sizeof(struct dn_id) || l > 12000) { 2439 D("argument len %d invalid", l); 2440 break; 2441 } 2442 p = malloc(l, M_TEMP, M_WAITOK); // XXX can it fail ? 2443 error = sooptcopyin(sopt, p, l, l); 2444 if (error) 2445 break ; 2446 error = do_config(p, l); 2447 break; 2448 } 2449 2450 if (p != NULL) 2451 free(p, M_TEMP); 2452 2453 return error ; 2454 } 2455 2456 2457 static void 2458 ip_dn_init(void) 2459 { 2460 if (dn_cfg.init_done) 2461 return; 2462 printf("DUMMYNET %p with IPv6 initialized (100409)\n", curvnet); 2463 dn_cfg.init_done = 1; 2464 /* Set defaults here. MSVC does not accept initializers, 2465 * and this is also useful for vimages 2466 */ 2467 /* queue limits */ 2468 dn_cfg.slot_limit = 100; /* Foot shooting limit for queues. */ 2469 dn_cfg.byte_limit = 1024 * 1024; 2470 dn_cfg.expire = 1; 2471 2472 /* RED parameters */ 2473 dn_cfg.red_lookup_depth = 256; /* default lookup table depth */ 2474 dn_cfg.red_avg_pkt_size = 512; /* default medium packet size */ 2475 dn_cfg.red_max_pkt_size = 1500; /* default max packet size */ 2476 2477 /* hash tables */ 2478 dn_cfg.max_hash_size = 65536; /* max in the hash tables */ 2479 dn_cfg.hash_size = 64; /* default hash size */ 2480 2481 /* create hash tables for schedulers and flowsets. 2482 * In both we search by key and by pointer. 2483 */ 2484 dn_cfg.schedhash = dn_ht_init(NULL, dn_cfg.hash_size, 2485 offsetof(struct dn_schk, schk_next), 2486 schk_hash, schk_match, schk_new); 2487 dn_cfg.fshash = dn_ht_init(NULL, dn_cfg.hash_size, 2488 offsetof(struct dn_fsk, fsk_next), 2489 fsk_hash, fsk_match, fsk_new); 2490 2491 /* bucket index to drain object */ 2492 dn_cfg.drain_fs = 0; 2493 dn_cfg.drain_sch = 0; 2494 2495 heap_init(&dn_cfg.evheap, 16, offsetof(struct dn_id, id)); 2496 SLIST_INIT(&dn_cfg.fsu); 2497 SLIST_INIT(&dn_cfg.schedlist); 2498 2499 DN_LOCK_INIT(); 2500 2501 TASK_INIT(&dn_task, 0, dummynet_task, curvnet); 2502 dn_tq = taskqueue_create_fast("dummynet", M_WAITOK, 2503 taskqueue_thread_enqueue, &dn_tq); 2504 taskqueue_start_threads(&dn_tq, 1, PI_NET, "dummynet"); 2505 2506 callout_init(&dn_timeout, 1); 2507 dn_reschedule(); 2508 2509 /* Initialize curr_time adjustment mechanics. */ 2510 getmicrouptime(&dn_cfg.prev_t); 2511 } 2512 2513 static void 2514 ip_dn_destroy(int last) 2515 { 2516 DN_BH_WLOCK(); 2517 /* ensure no more callouts are started */ 2518 dn_gone = 1; 2519 2520 /* check for last */ 2521 if (last) { 2522 ND("removing last instance\n"); 2523 ip_dn_ctl_ptr = NULL; 2524 ip_dn_io_ptr = NULL; 2525 } 2526 2527 dummynet_flush(); 2528 DN_BH_WUNLOCK(); 2529 2530 callout_drain(&dn_timeout); 2531 taskqueue_drain(dn_tq, &dn_task); 2532 taskqueue_free(dn_tq); 2533 2534 dn_ht_free(dn_cfg.schedhash, 0); 2535 dn_ht_free(dn_cfg.fshash, 0); 2536 heap_free(&dn_cfg.evheap); 2537 2538 DN_LOCK_DESTROY(); 2539 } 2540 2541 static int 2542 dummynet_modevent(module_t mod, int type, void *data) 2543 { 2544 2545 if (type == MOD_LOAD) { 2546 if (ip_dn_io_ptr) { 2547 printf("DUMMYNET already loaded\n"); 2548 return EEXIST ; 2549 } 2550 ip_dn_init(); 2551 ip_dn_ctl_ptr = ip_dn_ctl; 2552 ip_dn_io_ptr = dummynet_io; 2553 return 0; 2554 } else if (type == MOD_UNLOAD) { 2555 ip_dn_destroy(1 /* last */); 2556 return 0; 2557 } else 2558 return EOPNOTSUPP; 2559 } 2560 2561 /* modevent helpers for the modules */ 2562 static int 2563 load_dn_sched(struct dn_alg *d) 2564 { 2565 struct dn_alg *s; 2566 2567 if (d == NULL) 2568 return 1; /* error */ 2569 ip_dn_init(); /* just in case, we need the lock */ 2570 2571 /* Check that mandatory funcs exists */ 2572 if (d->enqueue == NULL || d->dequeue == NULL) { 2573 D("missing enqueue or dequeue for %s", d->name); 2574 return 1; 2575 } 2576 2577 /* Search if scheduler already exists */ 2578 DN_BH_WLOCK(); 2579 SLIST_FOREACH(s, &dn_cfg.schedlist, next) { 2580 if (strcmp(s->name, d->name) == 0) { 2581 D("%s already loaded", d->name); 2582 break; /* scheduler already exists */ 2583 } 2584 } 2585 if (s == NULL) 2586 SLIST_INSERT_HEAD(&dn_cfg.schedlist, d, next); 2587 DN_BH_WUNLOCK(); 2588 D("dn_sched %s %sloaded", d->name, s ? "not ":""); 2589 return s ? 1 : 0; 2590 } 2591 2592 static int 2593 unload_dn_sched(struct dn_alg *s) 2594 { 2595 struct dn_alg *tmp, *r; 2596 int err = EINVAL; 2597 2598 ND("called for %s", s->name); 2599 2600 DN_BH_WLOCK(); 2601 SLIST_FOREACH_SAFE(r, &dn_cfg.schedlist, next, tmp) { 2602 if (strcmp(s->name, r->name) != 0) 2603 continue; 2604 ND("ref_count = %d", r->ref_count); 2605 err = (r->ref_count != 0) ? EBUSY : 0; 2606 if (err == 0) 2607 SLIST_REMOVE(&dn_cfg.schedlist, r, dn_alg, next); 2608 break; 2609 } 2610 DN_BH_WUNLOCK(); 2611 D("dn_sched %s %sunloaded", s->name, err ? "not ":""); 2612 return err; 2613 } 2614 2615 int 2616 dn_sched_modevent(module_t mod, int cmd, void *arg) 2617 { 2618 struct dn_alg *sch = arg; 2619 2620 if (cmd == MOD_LOAD) 2621 return load_dn_sched(sch); 2622 else if (cmd == MOD_UNLOAD) 2623 return unload_dn_sched(sch); 2624 else 2625 return EINVAL; 2626 } 2627 2628 static moduledata_t dummynet_mod = { 2629 "dummynet", dummynet_modevent, NULL 2630 }; 2631 2632 #define DN_SI_SUB SI_SUB_PROTO_FIREWALL 2633 #define DN_MODEV_ORD (SI_ORDER_ANY - 128) /* after ipfw */ 2634 DECLARE_MODULE(dummynet, dummynet_mod, DN_SI_SUB, DN_MODEV_ORD); 2635 MODULE_DEPEND(dummynet, ipfw, 3, 3, 3); 2636 MODULE_VERSION(dummynet, 3); 2637 2638 /* 2639 * Starting up. Done in order after dummynet_modevent() has been called. 2640 * VNET_SYSINIT is also called for each existing vnet and each new vnet. 2641 */ 2642 //VNET_SYSINIT(vnet_dn_init, DN_SI_SUB, DN_MODEV_ORD+2, ip_dn_init, NULL); 2643 2644 /* 2645 * Shutdown handlers up shop. These are done in REVERSE ORDER, but still 2646 * after dummynet_modevent() has been called. Not called on reboot. 2647 * VNET_SYSUNINIT is also called for each exiting vnet as it exits. 2648 * or when the module is unloaded. 2649 */ 2650 //VNET_SYSUNINIT(vnet_dn_uninit, DN_SI_SUB, DN_MODEV_ORD+2, ip_dn_destroy, NULL); 2651 2652 #ifdef NEW_AQM 2653 2654 /* modevent helpers for the AQM modules */ 2655 static int 2656 load_dn_aqm(struct dn_aqm *d) 2657 { 2658 struct dn_aqm *aqm=NULL; 2659 2660 if (d == NULL) 2661 return 1; /* error */ 2662 ip_dn_init(); /* just in case, we need the lock */ 2663 2664 /* Check that mandatory funcs exists */ 2665 if (d->enqueue == NULL || d->dequeue == NULL) { 2666 D("missing enqueue or dequeue for %s", d->name); 2667 return 1; 2668 } 2669 2670 /* Search if AQM already exists */ 2671 DN_BH_WLOCK(); 2672 SLIST_FOREACH(aqm, &dn_cfg.aqmlist, next) { 2673 if (strcmp(aqm->name, d->name) == 0) { 2674 D("%s already loaded", d->name); 2675 break; /* AQM already exists */ 2676 } 2677 } 2678 if (aqm == NULL) 2679 SLIST_INSERT_HEAD(&dn_cfg.aqmlist, d, next); 2680 DN_BH_WUNLOCK(); 2681 D("dn_aqm %s %sloaded", d->name, aqm ? "not ":""); 2682 return aqm ? 1 : 0; 2683 } 2684 2685 2686 /* Callback to clean up AQM status for queues connected to a flowset 2687 * and then deconfigure the flowset. 2688 * This function is called before an AQM module is unloaded 2689 */ 2690 static int 2691 fs_cleanup(void *_fs, void *arg) 2692 { 2693 struct dn_fsk *fs = _fs; 2694 uint32_t type = *(uint32_t *)arg; 2695 2696 if (fs->aqmfp && fs->aqmfp->type == type) 2697 aqm_cleanup_deconfig_fs(fs); 2698 2699 return 0; 2700 } 2701 2702 static int 2703 unload_dn_aqm(struct dn_aqm *aqm) 2704 { 2705 struct dn_aqm *tmp, *r; 2706 int err = EINVAL; 2707 err = 0; 2708 ND("called for %s", aqm->name); 2709 2710 DN_BH_WLOCK(); 2711 2712 /* clean up AQM status and deconfig flowset */ 2713 dn_ht_scan(dn_cfg.fshash, fs_cleanup, &aqm->type); 2714 2715 SLIST_FOREACH_SAFE(r, &dn_cfg.aqmlist, next, tmp) { 2716 if (strcmp(aqm->name, r->name) != 0) 2717 continue; 2718 ND("ref_count = %d", r->ref_count); 2719 err = (r->ref_count != 0 || r->cfg_ref_count != 0) ? EBUSY : 0; 2720 if (err == 0) 2721 SLIST_REMOVE(&dn_cfg.aqmlist, r, dn_aqm, next); 2722 break; 2723 } 2724 DN_BH_WUNLOCK(); 2725 D("%s %sunloaded", aqm->name, err ? "not ":""); 2726 if (err) 2727 D("ref_count=%d, cfg_ref_count=%d", r->ref_count, r->cfg_ref_count); 2728 return err; 2729 } 2730 2731 int 2732 dn_aqm_modevent(module_t mod, int cmd, void *arg) 2733 { 2734 struct dn_aqm *aqm = arg; 2735 2736 if (cmd == MOD_LOAD) 2737 return load_dn_aqm(aqm); 2738 else if (cmd == MOD_UNLOAD) 2739 return unload_dn_aqm(aqm); 2740 else 2741 return EINVAL; 2742 } 2743 #endif 2744 2745 /* end of file */ 2746 2747