1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "../port/error.h" 7 8 typedef struct Link Link; 9 typedef struct Loop Loop; 10 11 struct Link 12 { 13 Lock; 14 15 int ref; 16 17 long packets; /* total number of packets sent */ 18 long bytes; /* total number of bytes sent */ 19 int indrop; /* enable dropping on iq overflow */ 20 long soverflows; /* packets dropped because iq overflowed */ 21 long droprate; /* drop 1/droprate packets in tq */ 22 long drops; /* packets deliberately dropped */ 23 24 vlong delay0ns; /* nanosec of delay in the link */ 25 long delaynns; /* nanosec of delay per byte */ 26 27 Block *tq; /* transmission queue */ 28 Block *tqtail; 29 vlong tout; /* time the last packet in tq is really out */ 30 vlong tin; /* time the head packet in tq enters the remote side */ 31 32 long limit; /* queue buffering limit */ 33 Queue *oq; /* output queue from other side & packets in the link */ 34 Queue *iq; 35 36 Timer ci; /* time to move packets from next packet from oq */ 37 }; 38 39 struct Loop 40 { 41 QLock; 42 int ref; 43 int minmtu; /* smallest block transmittable */ 44 Loop *next; 45 ulong path; 46 Link link[2]; 47 }; 48 49 static struct 50 { 51 Lock; 52 ulong path; 53 } loopbackalloc; 54 55 enum 56 { 57 Qtopdir= 1, /* top level directory */ 58 59 Qloopdir, /* loopback* directory */ 60 61 Qportdir, /* directory each end of the loop */ 62 Qctl, 63 Qstatus, 64 Qstats, 65 Qdata, 66 67 MaxQ, 68 69 Nloopbacks = 5, 70 71 Statelen = 23*1024, /* status buffer size */ 72 73 Tmsize = 8, 74 Delayn = 10000, /* default delays in ns */ 75 Delay0 = 2500000, 76 77 Loopqlim = 32*1024, /* default size of queues */ 78 }; 79 80 static Dirtab loopportdir[] = 81 { 82 "ctl", {Qctl}, 0, 0222, 83 "status", {Qstatus}, 0, 0444, 84 "stats", {Qstats}, 0, 0444, 85 "data", {Qdata}, 0, 0666, 86 }; 87 static Dirtab loopdirs[MaxQ]; 88 89 static Loop loopbacks[Nloopbacks]; 90 91 #define TYPE(x) (((ulong)(x))&0xff) 92 #define ID(x) (((ulong)(x))>>8) 93 #define QID(x,y) ((((ulong)(x))<<8)|((ulong)(y))) 94 95 static void looper(Loop *lb); 96 static long loopoput(Loop *lb, Link *link, Block *bp); 97 static void ptime(uchar *p, vlong t); 98 static vlong gtime(uchar *p); 99 static void closelink(Link *link, int dofree); 100 static void pushlink(Link *link, vlong now); 101 static void freelb(Loop *lb); 102 static void linkintr(Ureg*, Timer *ci); 103 104 static void 105 loopbackinit(void) 106 { 107 int i; 108 109 for(i = 0; i < Nloopbacks; i++) 110 loopbacks[i].path = i; 111 112 /* invert directory tables for non-directory entries */ 113 for(i=0; i<nelem(loopportdir); i++) 114 loopdirs[loopportdir[i].qid.path] = loopportdir[i]; 115 } 116 117 static Chan* 118 loopbackattach(char *spec) 119 { 120 Loop *volatile lb; 121 Queue *q; 122 Chan *c; 123 int chan; 124 int dev; 125 126 dev = 0; 127 if(spec != nil){ 128 dev = atoi(spec); 129 if(dev >= Nloopbacks) 130 error(Ebadspec); 131 } 132 133 c = devattach('X', spec); 134 lb = &loopbacks[dev]; 135 136 qlock(lb); 137 if(waserror()){ 138 lb->ref--; 139 qunlock(lb); 140 nexterror(); 141 } 142 143 lb->ref++; 144 if(lb->ref == 1){ 145 for(chan = 0; chan < 2; chan++){ 146 lb->link[chan].ci.tmode = Tabsolute; 147 lb->link[chan].ci.ta = &lb->link[chan]; 148 lb->link[chan].ci.tf = linkintr; 149 lb->link[chan].limit = Loopqlim; 150 q = qopen(lb->link[chan].limit, 0, 0, 0); 151 lb->link[chan].iq = q; 152 if(q == nil){ 153 freelb(lb); 154 exhausted("memory"); 155 } 156 q = qopen(lb->link[chan].limit, 0, 0, 0); 157 lb->link[chan].oq = q; 158 if(q == nil){ 159 freelb(lb); 160 exhausted("memory"); 161 } 162 lb->link[chan].indrop = 1; 163 164 lb->link[chan].delaynns = Delayn; 165 lb->link[chan].delay0ns = Delay0; 166 } 167 } 168 poperror(); 169 qunlock(lb); 170 171 mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); 172 c->aux = lb; 173 c->dev = dev; 174 return c; 175 } 176 177 static int 178 loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) 179 { 180 Dirtab *tab; 181 int len, type; 182 Qid qid; 183 184 type = TYPE(c->qid.path); 185 if(i == DEVDOTDOT){ 186 switch(type){ 187 case Qtopdir: 188 case Qloopdir: 189 snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev); 190 mkqid(&qid, QID(0, Qtopdir), 0, QTDIR); 191 devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 192 break; 193 case Qportdir: 194 snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); 195 mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); 196 devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 197 break; 198 default: 199 panic("loopbackgen %llux", c->qid.path); 200 } 201 return 1; 202 } 203 204 switch(type){ 205 case Qtopdir: 206 if(i != 0) 207 return -1; 208 snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); 209 mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); 210 devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 211 return 1; 212 case Qloopdir: 213 if(i >= 2) 214 return -1; 215 snprint(up->genbuf, sizeof(up->genbuf), "%d", i); 216 mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR); 217 devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 218 return 1; 219 case Qportdir: 220 if(i >= nelem(loopportdir)) 221 return -1; 222 tab = &loopportdir[i]; 223 mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE); 224 devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); 225 return 1; 226 default: 227 /* non directory entries end up here; must be in lowest level */ 228 if(c->qid.type & QTDIR) 229 panic("loopbackgen: unexpected directory"); 230 if(i != 0) 231 return -1; 232 tab = &loopdirs[type]; 233 if(tab == nil) 234 panic("loopbackgen: unknown type: %d", type); 235 len = tab->length; 236 devdir(c, c->qid, tab->name, len, eve, tab->perm, dp); 237 return 1; 238 } 239 } 240 241 242 static Walkqid* 243 loopbackwalk(Chan *c, Chan *nc, char **name, int nname) 244 { 245 Walkqid *wq; 246 Loop *lb; 247 248 wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen); 249 if(wq != nil && wq->clone != nil && wq->clone != c){ 250 lb = c->aux; 251 qlock(lb); 252 lb->ref++; 253 if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) 254 lb->link[ID(c->qid.path)].ref++; 255 qunlock(lb); 256 } 257 return wq; 258 } 259 260 static int 261 loopbackstat(Chan *c, uchar *db, int n) 262 { 263 return devstat(c, db, n, nil, 0, loopbackgen); 264 } 265 266 /* 267 * if the stream doesn't exist, create it 268 */ 269 static Chan* 270 loopbackopen(Chan *c, int omode) 271 { 272 Loop *lb; 273 274 if(c->qid.type & QTDIR){ 275 if(omode != OREAD) 276 error(Ebadarg); 277 c->mode = omode; 278 c->flag |= COPEN; 279 c->offset = 0; 280 return c; 281 } 282 283 lb = c->aux; 284 qlock(lb); 285 if(TYPE(c->qid.path) == Qdata){ 286 if(lb->link[ID(c->qid.path)].ref){ 287 qunlock(lb); 288 error(Einuse); 289 } 290 lb->link[ID(c->qid.path)].ref++; 291 } 292 qunlock(lb); 293 294 c->mode = openmode(omode); 295 c->flag |= COPEN; 296 c->offset = 0; 297 c->iounit = qiomaxatomic; 298 return c; 299 } 300 301 static void 302 loopbackclose(Chan *c) 303 { 304 Loop *lb; 305 int ref, chan; 306 307 lb = c->aux; 308 309 qlock(lb); 310 311 /* 312 * closing either side hangs up the stream 313 */ 314 if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){ 315 chan = ID(c->qid.path); 316 if(--lb->link[chan].ref == 0){ 317 qhangup(lb->link[chan ^ 1].oq, nil); 318 looper(lb); 319 } 320 } 321 322 323 /* 324 * if both sides are closed, they are reusable 325 */ 326 if(lb->link[0].ref == 0 && lb->link[1].ref == 0){ 327 for(chan = 0; chan < 2; chan++){ 328 closelink(&lb->link[chan], 0); 329 qreopen(lb->link[chan].iq); 330 qreopen(lb->link[chan].oq); 331 qsetlimit(lb->link[chan].oq, lb->link[chan].limit); 332 qsetlimit(lb->link[chan].iq, lb->link[chan].limit); 333 } 334 } 335 ref = --lb->ref; 336 if(ref == 0) 337 freelb(lb); 338 qunlock(lb); 339 } 340 341 static void 342 freelb(Loop *lb) 343 { 344 int chan; 345 346 for(chan = 0; chan < 2; chan++) 347 closelink(&lb->link[chan], 1); 348 } 349 350 /* 351 * called with the Loop qlocked, 352 * so only pushlink can mess with the queues 353 */ 354 static void 355 closelink(Link *link, int dofree) 356 { 357 Queue *iq, *oq; 358 Block *bp; 359 360 ilock(link); 361 iq = link->iq; 362 oq = link->oq; 363 bp = link->tq; 364 link->tq = nil; 365 link->tqtail = nil; 366 link->tout = 0; 367 link->tin = 0; 368 timerdel(&link->ci); 369 iunlock(link); 370 if(iq != nil){ 371 qclose(iq); 372 if(dofree){ 373 ilock(link); 374 free(iq); 375 link->iq = nil; 376 iunlock(link); 377 } 378 } 379 if(oq != nil){ 380 qclose(oq); 381 if(dofree){ 382 ilock(link); 383 free(oq); 384 link->oq = nil; 385 iunlock(link); 386 } 387 } 388 freeblist(bp); 389 } 390 391 static long 392 loopbackread(Chan *c, void *va, long n, vlong offset) 393 { 394 Loop *lb; 395 Link *link; 396 char *buf; 397 long rv; 398 399 lb = c->aux; 400 switch(TYPE(c->qid.path)){ 401 default: 402 error(Eperm); 403 return -1; /* not reached */ 404 case Qtopdir: 405 case Qloopdir: 406 case Qportdir: 407 return devdirread(c, va, n, nil, 0, loopbackgen); 408 case Qdata: 409 return qread(lb->link[ID(c->qid.path)].iq, va, n); 410 case Qstatus: 411 link = &lb->link[ID(c->qid.path)]; 412 buf = smalloc(Statelen); 413 rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns); 414 rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit); 415 rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop); 416 snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate); 417 rv = readstr(offset, va, n, buf); 418 free(buf); 419 break; 420 case Qstats: 421 link = &lb->link[ID(c->qid.path)]; 422 buf = smalloc(Statelen); 423 rv = snprint(buf, Statelen, "packets: %ld\n", link->packets); 424 rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes); 425 rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops); 426 snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows); 427 rv = readstr(offset, va, n, buf); 428 free(buf); 429 break; 430 } 431 return rv; 432 } 433 434 static Block* 435 loopbackbread(Chan *c, long n, ulong offset) 436 { 437 Loop *lb; 438 439 lb = c->aux; 440 if(TYPE(c->qid.path) == Qdata) 441 return qbread(lb->link[ID(c->qid.path)].iq, n); 442 443 return devbread(c, n, offset); 444 } 445 446 static long 447 loopbackbwrite(Chan *c, Block *bp, ulong off) 448 { 449 Loop *lb; 450 451 lb = c->aux; 452 if(TYPE(c->qid.path) == Qdata) 453 return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp); 454 return devbwrite(c, bp, off); 455 } 456 457 static long 458 loopbackwrite(Chan *c, void *va, long n, vlong off) 459 { 460 Loop *lb; 461 Link *link; 462 Cmdbuf *volatile cb; 463 Block *volatile bp; 464 vlong d0ns; 465 long dnns; 466 467 switch(TYPE(c->qid.path)){ 468 case Qdata: 469 bp = allocb(n); 470 if(waserror()){ 471 freeb(bp); 472 nexterror(); 473 } 474 memmove(bp->wp, va, n); 475 poperror(); 476 bp->wp += n; 477 return loopbackbwrite(c, bp, off); 478 case Qctl: 479 lb = c->aux; 480 link = &lb->link[ID(c->qid.path)]; 481 cb = parsecmd(va, n); 482 if(waserror()){ 483 free(cb); 484 nexterror(); 485 } 486 if(cb->nf < 1) 487 error("short control request"); 488 if(strcmp(cb->f[0], "delay") == 0){ 489 if(cb->nf != 3) 490 error("usage: delay latency bytedelay"); 491 d0ns = strtoll(cb->f[1], nil, 10); 492 dnns = strtol(cb->f[2], nil, 10); 493 494 /* 495 * it takes about 20000 cycles on a pentium ii 496 * to run pushlink; perhaps this should be accounted. 497 */ 498 499 ilock(link); 500 link->delay0ns = d0ns; 501 link->delaynns = dnns; 502 iunlock(link); 503 }else if(strcmp(cb->f[0], "indrop") == 0){ 504 if(cb->nf != 2) 505 error("usage: indrop [01]"); 506 ilock(link); 507 link->indrop = strtol(cb->f[1], nil, 0) != 0; 508 iunlock(link); 509 }else if(strcmp(cb->f[0], "droprate") == 0){ 510 if(cb->nf != 2) 511 error("usage: droprate ofn"); 512 ilock(link); 513 link->droprate = strtol(cb->f[1], nil, 0); 514 iunlock(link); 515 }else if(strcmp(cb->f[0], "limit") == 0){ 516 if(cb->nf != 2) 517 error("usage: limit maxqsize"); 518 ilock(link); 519 link->limit = strtol(cb->f[1], nil, 0); 520 qsetlimit(link->oq, link->limit); 521 qsetlimit(link->iq, link->limit); 522 iunlock(link); 523 }else if(strcmp(cb->f[0], "reset") == 0){ 524 if(cb->nf != 1) 525 error("usage: reset"); 526 ilock(link); 527 link->packets = 0; 528 link->bytes = 0; 529 link->indrop = 0; 530 link->soverflows = 0; 531 link->drops = 0; 532 iunlock(link); 533 }else 534 error("unknown control request"); 535 poperror(); 536 free(cb); 537 break; 538 default: 539 error(Eperm); 540 } 541 542 return n; 543 } 544 545 static long 546 loopoput(Loop *lb, Link *link, Block *volatile bp) 547 { 548 long n; 549 550 n = BLEN(bp); 551 552 /* make it a single block with space for the loopback timing header */ 553 if(waserror()){ 554 freeb(bp); 555 nexterror(); 556 } 557 bp = padblock(bp, Tmsize); 558 if(bp->next) 559 bp = concatblock(bp); 560 if(BLEN(bp) < lb->minmtu) 561 bp = adjustblock(bp, lb->minmtu); 562 poperror(); 563 ptime(bp->rp, todget(nil)); 564 565 link->packets++; 566 link->bytes += n; 567 568 qbwrite(link->oq, bp); 569 570 looper(lb); 571 return n; 572 } 573 574 static void 575 looper(Loop *lb) 576 { 577 vlong t; 578 int chan; 579 580 t = todget(nil); 581 for(chan = 0; chan < 2; chan++) 582 pushlink(&lb->link[chan], t); 583 } 584 585 static void 586 linkintr(Ureg*, Timer *ci) 587 { 588 Link *link; 589 590 link = ci->ta; 591 pushlink(link, ci->tns); 592 } 593 594 /* 595 * move blocks between queues if they are ready. 596 * schedule an interrupt for the next interesting time. 597 * 598 * must be called with the link ilocked. 599 */ 600 static void 601 pushlink(Link *link, vlong now) 602 { 603 Block *bp; 604 vlong tout, tin; 605 606 /* 607 * put another block in the link queue 608 */ 609 ilock(link); 610 if(link->iq == nil || link->oq == nil){ 611 iunlock(link); 612 return; 613 614 } 615 timerdel(&link->ci); 616 617 /* 618 * put more blocks into the xmit queue 619 * use the time the last packet was supposed to go out 620 * as the start time for the next packet, rather than 621 * the current time. this more closely models a network 622 * device which can queue multiple output packets. 623 */ 624 tout = link->tout; 625 if(!tout) 626 tout = now; 627 while(tout <= now){ 628 bp = qget(link->oq); 629 if(bp == nil){ 630 tout = 0; 631 break; 632 } 633 634 /* 635 * can't send the packet before it gets queued 636 */ 637 tin = gtime(bp->rp); 638 if(tin > tout) 639 tout = tin; 640 tout = tout + (BLEN(bp) - Tmsize) * link->delaynns; 641 642 /* 643 * drop packets 644 */ 645 if(link->droprate && nrand(link->droprate) == 0) 646 link->drops++; 647 else{ 648 ptime(bp->rp, tout + link->delay0ns); 649 if(link->tq == nil) 650 link->tq = bp; 651 else 652 link->tqtail->next = bp; 653 link->tqtail = bp; 654 } 655 } 656 657 /* 658 * record the next time a packet can be sent, 659 * but don't schedule an interrupt if none is waiting 660 */ 661 link->tout = tout; 662 if(!qcanread(link->oq)) 663 tout = 0; 664 665 /* 666 * put more blocks into the receive queue 667 */ 668 tin = 0; 669 while(bp = link->tq){ 670 tin = gtime(bp->rp); 671 if(tin > now) 672 break; 673 bp->rp += Tmsize; 674 link->tq = bp->next; 675 bp->next = nil; 676 if(!link->indrop) 677 qpassnolim(link->iq, bp); 678 else if(qpass(link->iq, bp) < 0) 679 link->soverflows++; 680 tin = 0; 681 } 682 if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq)) 683 qhangup(link->iq, nil); 684 link->tin = tin; 685 if(!tin || tin > tout && tout) 686 tin = tout; 687 688 link->ci.tns = tin; 689 if(tin){ 690 if(tin < now) 691 panic("loopback unfinished business"); 692 timeradd(&link->ci); 693 } 694 iunlock(link); 695 } 696 697 static void 698 ptime(uchar *p, vlong t) 699 { 700 ulong tt; 701 702 tt = t >> 32; 703 p[0] = tt >> 24; 704 p[1] = tt >> 16; 705 p[2] = tt >> 8; 706 p[3] = tt; 707 tt = t; 708 p[4] = tt >> 24; 709 p[5] = tt >> 16; 710 p[6] = tt >> 8; 711 p[7] = tt; 712 } 713 714 static vlong 715 gtime(uchar *p) 716 { 717 ulong t1, t2; 718 719 t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 720 t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; 721 return ((vlong)t1 << 32) | t2; 722 } 723 724 Dev loopbackdevtab = { 725 'X', 726 "loopback", 727 728 devreset, 729 loopbackinit, 730 devshutdown, 731 loopbackattach, 732 loopbackwalk, 733 loopbackstat, 734 loopbackopen, 735 devcreate, 736 loopbackclose, 737 loopbackread, 738 loopbackbread, 739 loopbackwrite, 740 loopbackbwrite, 741 devremove, 742 devwstat, 743 }; 744