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);
134 lb = &loopbacks[dev];
145 for(chan = 0; chan < 2; chan++){
146 lb->link[chan].ci.mode = Trelative;
147 lb->link[chan].ci.a = &lb->link[chan];
148 lb->link[chan].ci.f = linkintr;
149 lb->link[chan].limit = Loopqlim;
150 q = qopen(lb->link[chan].limit, 0, 0, 0);
151 lb->link[chan].iq = q;
156 q = qopen(lb->link[chan].limit, 0, 0, 0);
157 lb->link[chan].oq = q;
162 lb->link[chan].indrop = 1;
164 lb->link[chan].delaynns = Delayn;
165 lb->link[chan].delay0ns = Delay0;
171 mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
178 loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
184 type = TYPE(c->qid.path);
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);
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);
199 panic("loopbackgen %llux", c->qid.path);
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);
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);
220 if(i >= nelem(loopportdir))
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);
227 /* non directory entries end up here; must be in lowest level */
228 if(c->qid.type & QTDIR)
229 panic("loopbackgen: unexpected directory");
232 tab = &loopdirs[type];
234 panic("loopbackgen: unknown type: %d", type);
236 devdir(c, c->qid, tab->name, len, eve, tab->perm, dp);
243 loopbackwalk(Chan *c, Chan *nc, char **name, int nname)
248 wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen);
249 if(wq != nil && wq->clone != nil && wq->clone != c){
253 if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata)
254 lb->link[ID(c->qid.path)].ref++;
261 loopbackstat(Chan *c, uchar *db, int n)
263 return devstat(c, db, n, nil, 0, loopbackgen);
267 * if the stream doesn't exist, create it
270 loopbackopen(Chan *c, int omode)
274 if(c->qid.type & QTDIR){
285 if(TYPE(c->qid.path) == Qdata){
286 if(lb->link[ID(c->qid.path)].ref){
290 lb->link[ID(c->qid.path)].ref++;
294 c->mode = openmode(omode);
297 c->iounit = qiomaxatomic;
302 loopbackclose(Chan *c)
312 * closing either side hangs up the stream
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);
324 * if both sides are closed, they are reusable
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);
346 for(chan = 0; chan < 2; chan++)
347 closelink(&lb->link[chan], 1);
351 * called with the Loop qlocked,
352 * so only pushlink can mess with the queues
355 closelink(Link *link, int dofree)
392 loopbackread(Chan *c, void *va, long n, vlong offset)
400 switch(TYPE(c->qid.path)){
403 return -1; /* not reached */
407 return devdirread(c, va, n, nil, 0, loopbackgen);
409 return qread(lb->link[ID(c->qid.path)].iq, va, n);
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);
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);
435 loopbackbread(Chan *c, long n, ulong offset)
440 if(TYPE(c->qid.path) == Qdata)
441 return qbread(lb->link[ID(c->qid.path)].iq, n);
443 return devbread(c, n, offset);
447 loopbackbwrite(Chan *c, Block *bp, ulong off)
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);
458 loopbackwrite(Chan *c, void *va, long n, vlong off)
467 switch(TYPE(c->qid.path)){
474 memmove(bp->wp, va, n);
477 return loopbackbwrite(c, bp, off);
480 link = &lb->link[ID(c->qid.path)];
481 cb = parsecmd(va, n);
487 error("short control request");
488 if(strcmp(cb->f[0], "delay") == 0){
490 error("usage: delay latency bytedelay");
491 d0ns = strtoll(cb->f[1], nil, 10);
492 dnns = strtol(cb->f[2], nil, 10);
495 * it takes about 20000 cycles on a pentium ii
496 * to run pushlink; perhaps this should be accounted.
500 link->delay0ns = d0ns;
501 link->delaynns = dnns;
503 }else if(strcmp(cb->f[0], "indrop") == 0){
505 error("usage: indrop [01]");
507 link->indrop = strtol(cb->f[1], nil, 0) != 0;
509 }else if(strcmp(cb->f[0], "droprate") == 0){
511 error("usage: droprate ofn");
513 link->droprate = strtol(cb->f[1], nil, 0);
515 }else if(strcmp(cb->f[0], "limit") == 0){
517 error("usage: limit maxqsize");
519 link->limit = strtol(cb->f[1], nil, 0);
520 qsetlimit(link->oq, link->limit);
521 qsetlimit(link->iq, link->limit);
523 }else if(strcmp(cb->f[0], "reset") == 0){
525 error("usage: reset");
530 link->soverflows = 0;
534 error("unknown control request");
546 loopoput(Loop *lb, Link *link, Block *volatile bp)
552 /* make it a single block with space for the loopback timing header */
557 bp = padblock(bp, Tmsize);
559 bp = concatblock(bp);
560 if(BLEN(bp) < lb->minmtu)
561 bp = adjustblock(bp, lb->minmtu);
563 ptime(bp->rp, todget(nil));
568 qbwrite(link->oq, bp);
581 for(chan = 0; chan < 2; chan++)
582 pushlink(&lb->link[chan], t);
586 linkintr(Ureg*, Timer *ci)
591 pushlink(link, ci->ns);
595 * move blocks between queues if they are ready.
596 * schedule an interrupt for the next interesting time.
598 * must be called with the link ilocked.
601 pushlink(Link *link, vlong now)
607 * put another block in the link queue
610 if(link->iq == nil || link->oq == nil){
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.
635 * can't send the packet before it gets queued
640 tout = tout + (BLEN(bp) - Tmsize) * link->delayn;
645 if(link->droprate && nrand(link->droprate) == 0)
648 ptime(bp->rp, tout + link->delay0ns);
652 link->tqtail->next = bp;
658 * record the next time a packet can be sent,
659 * but don't schedule an interrupt if none is waiting
662 if(!qcanread(link->oq))
666 * put more blocks into the receive queue
669 while(bp = link->tq){
677 qpassnolim(link->iq, bp);
678 else if(qpass(link->iq, bp) < 0)
682 if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq))
683 qhangup(link->iq, nil);
685 if(!tin || tin > tout && tout)
688 link->ci.ns = tin - now;
691 panic("loopback unfinished business");
698 ptime(uchar *p, vlong t)
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;
724 Dev loopbackdevtab = {