2 #include "../port/lib.h"
6 #include "../port/error.h"
8 typedef struct Link Link;
9 typedef struct Loop Loop;
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 */
24 vlong delay0ns; /* nanosec of delay in the link */
25 long delaynns; /* nanosec of delay per byte */
27 Block *tq; /* transmission queue */
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 */
32 long limit; /* queue buffering limit */
33 Queue *oq; /* output queue from other side & packets in the link */
36 Timer ci; /* time to move packets from next packet from oq */
43 int minmtu; /* smallest block transmittable */
57 Qtopdir= 1, /* top level directory */
59 Qloopdir, /* loopback* directory */
61 Qportdir, /* directory each end of the loop */
71 Statelen = 23*1024, /* status buffer size */
74 Delayn = 10000, /* default delays in ns */
77 Loopqlim = 32*1024, /* default size of queues */
80 static Dirtab loopportdir[] =
82 "ctl", {Qctl}, 0, 0222,
83 "status", {Qstatus}, 0, 0444,
84 "stats", {Qstats}, 0, 0444,
85 "data", {Qdata}, 0, 0666,
87 static Dirtab loopdirs[MaxQ];
89 static Loop loopbacks[Nloopbacks];
91 #define TYPE(x) (((ulong)(x))&0xff)
92 #define ID(x) (((ulong)(x))>>8)
93 #define QID(x,y) ((((ulong)(x))<<8)|((ulong)(y)))
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);
109 for(i = 0; i < Nloopbacks; i++)
110 loopbacks[i].path = i;
112 /* invert directory tables for non-directory entries */
113 for(i=0; i<nelem(loopportdir); i++)
114 loopdirs[loopportdir[i].qid.path] = loopportdir[i];
118 loopbackattach(char *spec)
129 if(dev >= Nloopbacks)
133 c = devattach('X', spec);
139 lb = &loopbacks[dev];
149 for(chan = 0; chan < 2; chan++){
150 lb->link[chan].ci.mode = Trelative;
151 lb->link[chan].ci.a = &lb->link[chan];
152 lb->link[chan].ci.f = linkintr;
153 lb->link[chan].limit = Loopqlim;
154 q = qopen(lb->link[chan].limit, 0, 0, 0);
155 lb->link[chan].iq = q;
160 q = qopen(lb->link[chan].limit, 0, 0, 0);
161 lb->link[chan].oq = q;
166 lb->link[chan].indrop = 1;
168 lb->link[chan].delaynns = Delayn;
169 lb->link[chan].delay0ns = Delay0;
177 mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
184 loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
190 type = TYPE(c->qid.path);
195 snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev);
196 mkqid(&qid, QID(0, Qtopdir), 0, QTDIR);
197 devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
200 snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
201 mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
202 devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
205 panic("loopbackgen %llux", c->qid.path);
214 snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
215 mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
216 devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
221 snprint(up->genbuf, sizeof(up->genbuf), "%d", i);
222 mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR);
223 devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
226 if(i >= nelem(loopportdir))
228 tab = &loopportdir[i];
229 mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE);
230 devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
233 /* non directory entries end up here; must be in lowest level */
234 if(c->qid.type & QTDIR)
235 panic("loopbackgen: unexpected directory");
238 tab = &loopdirs[type];
240 panic("loopbackgen: unknown type: %d", type);
242 devdir(c, c->qid, tab->name, len, eve, tab->perm, dp);
249 loopbackwalk(Chan *c, Chan *nc, char **name, int nname)
254 wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen);
255 if(wq != nil && wq->clone != nil && wq->clone != c){
259 if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata)
260 lb->link[ID(c->qid.path)].ref++;
267 loopbackstat(Chan *c, uchar *db, int n)
269 return devstat(c, db, n, nil, 0, loopbackgen);
273 * if the stream doesn't exist, create it
276 loopbackopen(Chan *c, int omode)
280 if(c->qid.type & QTDIR){
291 if(TYPE(c->qid.path) == Qdata){
292 if(lb->link[ID(c->qid.path)].ref){
296 lb->link[ID(c->qid.path)].ref++;
300 c->mode = openmode(omode);
303 c->iounit = qiomaxatomic;
308 loopbackclose(Chan *c)
318 * closing either side hangs up the stream
320 if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){
321 chan = ID(c->qid.path);
322 if(--lb->link[chan].ref == 0){
323 qhangup(lb->link[chan ^ 1].oq, nil);
330 * if both sides are closed, they are reusable
332 if(lb->link[0].ref == 0 && lb->link[1].ref == 0){
333 for(chan = 0; chan < 2; chan++){
334 closelink(&lb->link[chan], 0);
335 qreopen(lb->link[chan].iq);
336 qreopen(lb->link[chan].oq);
337 qsetlimit(lb->link[chan].oq, lb->link[chan].limit);
338 qsetlimit(lb->link[chan].iq, lb->link[chan].limit);
352 for(chan = 0; chan < 2; chan++)
353 closelink(&lb->link[chan], 1);
357 * called with the Loop qlocked,
358 * so only pushlink can mess with the queues
361 closelink(Link *link, int dofree)
398 loopbackread(Chan *c, void *va, long n, vlong offset)
406 switch(TYPE(c->qid.path)){
409 return -1; /* not reached */
413 return devdirread(c, va, n, nil, 0, loopbackgen);
415 return qread(lb->link[ID(c->qid.path)].iq, va, n);
417 link = &lb->link[ID(c->qid.path)];
418 buf = smalloc(Statelen);
419 rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns);
420 rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit);
421 rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop);
422 snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate);
423 rv = readstr(offset, va, n, buf);
427 link = &lb->link[ID(c->qid.path)];
428 buf = smalloc(Statelen);
429 rv = snprint(buf, Statelen, "packets: %ld\n", link->packets);
430 rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes);
431 rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops);
432 snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows);
433 rv = readstr(offset, va, n, buf);
441 loopbackbread(Chan *c, long n, ulong offset)
446 if(TYPE(c->qid.path) == Qdata)
447 return qbread(lb->link[ID(c->qid.path)].iq, n);
449 return devbread(c, n, offset);
453 loopbackbwrite(Chan *c, Block *bp, ulong off)
458 if(TYPE(c->qid.path) == Qdata)
459 return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp);
460 return devbwrite(c, bp, off);
464 loopbackwrite(Chan *c, void *va, long n, vlong off)
473 switch(TYPE(c->qid.path)){
480 memmove(bp->wp, va, n);
483 return loopbackbwrite(c, bp, off);
486 link = &lb->link[ID(c->qid.path)];
487 cb = parsecmd(va, n);
493 error("short control request");
494 if(strcmp(cb->f[0], "delay") == 0){
496 error("usage: delay latency bytedelay");
497 d0ns = strtoll(cb->f[1], nil, 10);
498 dnns = strtol(cb->f[2], nil, 10);
501 * it takes about 20000 cycles on a pentium ii
502 * to run pushlink; perhaps this should be accounted.
506 link->delay0ns = d0ns;
507 link->delaynns = dnns;
509 }else if(strcmp(cb->f[0], "indrop") == 0){
511 error("usage: indrop [01]");
513 link->indrop = strtol(cb->f[1], nil, 0) != 0;
515 }else if(strcmp(cb->f[0], "droprate") == 0){
517 error("usage: droprate ofn");
519 link->droprate = strtol(cb->f[1], nil, 0);
521 }else if(strcmp(cb->f[0], "limit") == 0){
523 error("usage: limit maxqsize");
525 link->limit = strtol(cb->f[1], nil, 0);
526 qsetlimit(link->oq, link->limit);
527 qsetlimit(link->iq, link->limit);
529 }else if(strcmp(cb->f[0], "reset") == 0){
531 error("usage: reset");
536 link->soverflows = 0;
540 error("unknown control request");
552 loopoput(Loop *lb, Link *link, Block *bp)
556 bp = padblock(bp, Tmsize);
557 if(BLEN(bp) < lb->minmtu)
558 bp = adjustblock(bp, lb->minmtu);
559 ptime(bp->rp, todget(nil));
564 qbwrite(link->oq, bp);
577 for(chan = 0; chan < 2; chan++)
578 pushlink(&lb->link[chan], t);
582 linkintr(Ureg*, Timer *ci)
587 pushlink(link, ci->ns);
591 * move blocks between queues if they are ready.
592 * schedule an interrupt for the next interesting time.
594 * must be called with the link ilocked.
597 pushlink(Link *link, vlong now)
603 * put another block in the link queue
606 if(link->iq == nil || link->oq == nil){
614 * put more blocks into the xmit queue
615 * use the time the last packet was supposed to go out
616 * as the start time for the next packet, rather than
617 * the current time. this more closely models a network
618 * device which can queue multiple output packets.
631 * can't send the packet before it gets queued
636 tout = tout + (BLEN(bp) - Tmsize) * link->delayn;
641 if(link->droprate && nrand(link->droprate) == 0)
644 ptime(bp->rp, tout + link->delay0ns);
648 link->tqtail->next = bp;
654 * record the next time a packet can be sent,
655 * but don't schedule an interrupt if none is waiting
658 if(!qcanread(link->oq))
662 * put more blocks into the receive queue
665 while(bp = link->tq){
673 qpassnolim(link->iq, bp);
674 else if(qpass(link->iq, bp) < 0)
678 if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq))
679 qhangup(link->iq, nil);
681 if(!tin || tin > tout && tout)
684 link->ci.ns = tin - now;
687 panic("loopback unfinished business");
694 ptime(uchar *p, vlong t)
715 t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
716 t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
717 return ((vlong)t1 << 32) | t2;
720 Dev loopbackdevtab = {