## diffname port/devloopback.c 2000/0617 ## diff -e /dev/null /n/emeliedump/2000/0617/sys/src/9/port/devloopback.c 0a #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "netif.h" typedef struct Link Link; typedef struct Loop Loop; struct Link { Lock; int ref; int nodrop; /* disable dropping on iq overflow */ int soverflows; /* packets dropped because iq overflowed */ int drops; /* packets deliberately dropped */ long delay0; /* fastticks of delay in the link */ long delayn; /* fastticks of delay per byte */ Block *tq; /* transmission queue */ Block *tqtail; vlong tout; /* time the last packet in tq is really out */ vlong tin; /* time the head packet in tq enters the remote side */ Queue *oq; /* output queue from other side & packets in the link */ Queue *iq; }; struct Loop { QLock; int ref; int minmtu; /* smallest block transmittable */ Loop *next; ulong path; long limit; /* queue buffering limit */ Link link[2]; }; static struct { Lock; ulong path; } loopbackalloc; enum { Qdir, Qctl, Qstatus, Qstats, Qdata0, Qdata1, TMSIZE = 8, NLOOPBACKS = 1, LOOPBACKSIZE = 32*1024, /*ZZZ change to settable; size of queues */ }; Dirtab loopbackdir[] = { "ctl", {Qctl}, 0, 0222, "status", {Qstatus}, 0, 0222, "stats", {Qstats}, 0, 0444, "data", {Qdata0}, 0, 0666, "data1", {Qdata1}, 0, 0666, }; static Loop loopbacks[NLOOPBACKS]; static void looper(Loop *lb); static long loopoput(Loop *lb, Link *link, Block *bp); static void ptime(uchar *p, vlong t); static vlong gtime(uchar *p); static void closelink(Link *link, int dofree); static vlong pushlink(Link *link, vlong now); static void freelb(Loop *lb); static void loopbackinit(void) { int i; for(i = 0; i < NLOOPBACKS; i++) loopbacks[i].path = i; } static Chan* loopbackattach(char *spec) { Loop *lb; Queue *q; Chan *c; int chan; c = devattach('X', spec); lb = &loopbacks[0]; qlock(lb); if(waserror()){ qunlock(lb); nexterror(); } lb->ref++; if(lb->ref == 1){ lb->limit = LOOPBACKSIZE; for(chan = 0; chan < 2; chan++){ q = qopen(lb->limit, 0, 0, 0); lb->link[chan].iq = q; if(q == nil){ freelb(lb); exhausted("memory"); } q = qopen(lb->limit, 0, 0, 0); lb->link[chan].oq = q; if(q == nil){ freelb(lb); exhausted("memory"); } lb->link[chan].nodrop = 1; } } poperror(); qunlock(lb); c->qid = (Qid){CHDIR|NETQID(2*lb->path, Qdir), 0}; c->aux = lb; c->dev = 0; return c; } static Chan* loopbackclone(Chan *c, Chan *nc) { Loop *lb; int chan; lb = c->aux; nc = devclone(c, nc); qlock(lb); lb->ref++; if(c->flag & COPEN){ switch(chan = NETTYPE(c->qid.path)){ case Qdata0: case Qdata1: chan -= Qdata0; lb->link[chan].ref++; break; } } qunlock(lb); return nc; } static int loopbackgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp) { Loop *lb; int id, len, chan; if(i == DEVDOTDOT){ devdir(c, c->qid, "#X", 0, eve, CHDIR|0555, dp); return 1; } id = NETID(c->qid.path); if(i > 1) id++; if(tab==nil || i>=ntab) return -1; tab += i; lb = c->aux; switch(chan = tab->qid.path){ case Qdata0: case Qdata1: chan -= Qdata0; len = qlen(lb->link[chan].iq); break; default: len = tab->length; break; } devdir(c, (Qid){NETQID(id, tab->qid.path),0}, tab->name, len, eve, tab->perm, dp); return 1; } static int loopbackwalk(Chan *c, char *name) { return devwalk(c, name, loopbackdir, nelem(loopbackdir), loopbackgen); } static void loopbackstat(Chan *c, char *db) { Loop *lb; Dir dir; int chan; lb = c->aux; switch(chan = NETTYPE(c->qid.path)){ case Qdir: devdir(c, c->qid, ".", nelem(loopbackdir)*DIRLEN, eve, CHDIR|0555, &dir); break; case Qdata0: case Qdata1: chan -= Qdata0; devdir(c, c->qid, "data", qlen(lb->link[chan].iq), eve, 0660, &dir); break; default: panic("loopbackstat"); } convD2M(&dir, db); } /* * if the stream doesn't exist, create it */ static Chan* loopbackopen(Chan *c, int omode) { Loop *lb; int chan; if(c->qid.path & CHDIR){ if(omode != OREAD) error(Ebadarg); c->mode = omode; c->flag |= COPEN; c->offset = 0; return c; } lb = c->aux; qlock(lb); switch(chan = NETTYPE(c->qid.path)){ case Qdata0: case Qdata1: chan -= Qdata0; lb->link[chan].ref++; break; } qunlock(lb); c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } static void loopbackclose(Chan *c) { Loop *lb; int ref, chan; lb = c->aux; qlock(lb); if(c->flag & COPEN){ /* * closing either side hangs up the stream */ switch(chan = NETTYPE(c->qid.path)){ case Qdata0: case Qdata1: chan -= Qdata0; if(--lb->link[chan].ref == 0){ qhangup(lb->link[chan ^ 1].oq, nil); looper(lb); } break; } } /* * if both sides are closed, they are reusable */ if(lb->link[0].ref == 0 && lb->link[1].ref == 0){ for(chan = 0; chan < 2; chan++){ closelink(&lb->link[chan], 0); qreopen(lb->link[chan].iq); qreopen(lb->link[chan].oq); } } ref = --lb->ref; if(ref == 0) freelb(lb); qunlock(lb); } static void freelb(Loop *lb) { int chan; for(chan = 0; chan < 2; chan++) closelink(&lb->link[chan], 1); } /* * called with the Loop qlocked, * so only pushlink can mess with the queues */ static void closelink(Link *link, int dofree) { Queue *iq, *oq; Block *bp; ilock(link); iq = link->iq; oq = link->oq; bp = link->tq; link->tq = nil; link->tqtail = nil; link->tout = 0; link->tin = 0; iunlock(link); if(iq != nil){ qclose(iq); if(dofree){ ilock(link); free(iq); link->iq = nil; iunlock(link); } } if(oq != nil){ qclose(oq); if(dofree){ ilock(link); free(oq); link->oq = nil; iunlock(link); } } freeblist(bp); } static long loopbackread(Chan *c, void *va, long n, vlong) { Loop *lb; int chan; lb = c->aux; //ZZZ ctl message to set q limit -- qsetlimit(q, limit) //ZZZ ctl message to set blocking/dropping qnoblock(q, dropit) //ZZZ ctl message for delays switch(chan = NETTYPE(c->qid.path)){ case Qdir: return devdirread(c, va, n, loopbackdir, nelem(loopbackdir), loopbackgen); case Qdata0: case Qdata1: chan -= Qdata0; return qread(lb->link[chan].iq, va, n); default: panic("loopbackread"); } return -1; /* not reached */ } static Block* loopbackbread(Chan *c, long n, ulong offset) { Loop *lb; int chan; lb = c->aux; switch(chan = NETTYPE(c->qid.path)){ case Qdata0: case Qdata1: chan -= Qdata0; return qbread(lb->link[chan].iq, n); } return devbread(c, n, offset); } static long loopbackbwrite(Chan *c, Block *bp, ulong off) { Loop *lb; int chan; lb = c->aux; switch(chan = NETTYPE(c->qid.path)){ case Qdata0: case Qdata1: chan -= Qdata0; return loopoput(lb, &lb->link[chan ^ 1], bp); default: return devbwrite(c, bp, off); } } static long loopbackwrite(Chan *c, void *va, long n, vlong off) { Block *bp; if(!islo()) print("loopbackwrite hi %lux\n", getcallerpc(&c)); switch(NETTYPE(c->qid.path)){ case Qdata0: case Qdata1: bp = allocb(n); if(waserror()){ freeb(bp); nexterror(); } memmove(bp->wp, va, n); poperror(); bp->wp += n; return loopbackbwrite(c, bp, off); case Qctl: default: panic("loopbackwrite"); } return n; } static long loopoput(Loop *lb, Link *link, Block *bp) { long n; n = BLEN(bp); /* make it a single block with space for the loopback header */ bp = padblock(bp, TMSIZE); if(bp->next) bp = concatblock(bp); if(BLEN(bp) < lb->minmtu) bp = adjustblock(bp, lb->minmtu); qbwrite(link->oq, bp); looper(lb); return n; } /* * move blocks between queues if they are ready. * schedule an interrupt for the next interesting time */ static void looper(Loop *lb) { vlong t, tt; tt = fastticks(nil); again:; t = pushlink(&lb->link[0], tt); tt = pushlink(&lb->link[1], tt); if(t > tt && tt) t = tt; if(t){ tt = fastticks(nil); if(tt <= t) goto again; //schedule an intr at tt-t fastticks } } static vlong pushlink(Link *link, vlong now) { Block *bp; vlong t; /* * put another block in the link queue */ ilock(link); if(link->iq == nil || link->oq == nil){ iunlock(link); return 0; } t = link->tout; if(!t || t < now){ bp = qget(link->oq); if(bp != nil){ if(!t) t = now; link->tout = t + BLEN(bp) * link->delayn; ptime(bp->rp, t + link->delay0); //ZZZ drop or introduce errors here if(link->tq == nil) link->tq = bp; else link->tqtail->next = bp; link->tqtail = bp; }else link->tout = 0; } /* * put more blocks into the receive queue */ t = 0; while(bp = link->tq){ t = gtime(bp->rp); if(t > now) break; bp->rp += TMSIZE; link->tq = bp->next; bp->next = nil; if(link->nodrop) qpassnolim(link->iq, bp); else if(qpass(link->iq, bp) < 0) link->soverflows++; t = 0; } if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq)) qhangup(link->iq, nil); link->tin = t; if(!t || t < link->tout) t = link->tout; iunlock(link); return t; } static void ptime(uchar *p, vlong t) { ulong tt; tt = t >> 32; p[0] = tt >> 24; p[1] = tt >> 16; p[2] = tt >> 8; p[3] = tt; tt = t; p[4] = tt >> 24; p[5] = tt >> 16; p[6] = tt >> 8; p[7] = tt; } static vlong gtime(uchar *p) { ulong t1, t2; t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; return ((vlong)t1 << 32) | t2; } Dev loopbackdevtab = { 'X', "loopback", devreset, loopbackinit, loopbackattach, loopbackclone, loopbackwalk, loopbackstat, loopbackopen, devcreate, loopbackclose, loopbackread, loopbackbread, loopbackwrite, loopbackbwrite, devremove, devwstat, }; . ## diffname port/devloopback.c 2000/0708 ## diff -e /n/emeliedump/2000/0617/sys/src/9/port/devloopback.c /n/emeliedump/2000/0708/sys/src/9/port/devloopback.c 533d 529,531c link->tin = tin; if(!tin || tin > tout && tout) tin = tout; link->ci.when = tin; if(tin){ if(tin < now) panic("loopback unfinished business"); cycintradd(&link->ci); } . 525c tin = 0; . 521c if(!link->indrop) . 518c bp->rp += Tmsize; . 515,516c tin = gtime(bp->rp); if(tin > now) . 513c tin = 0; . 510a * record the next time a packet can be sent, * but don't schedule an interrupt if none is waiting */ link->tout = tout; if(!qcanread(link->oq)) tout = 0; /* . 506,507c } . 495,500c if(bp == nil){ tout = 0; break; } /* * can't send the packet before it gets queued */ tin = gtime(bp->rp); if(tin > tout) tout = tin; tout = tout + (BLEN(bp) - Tmsize) * link->delayn; /* * drop packets */ if(link->droprate && nrand(link->droprate) == 0) link->drops++; else{ ptime(bp->rp, tout + link->delay0); . 492,493c cycintrdel(&link->ci); /* * put more blocks into the xmit queue * use the time the last packet was supposed to go out * as the start time for the next packet, rather than * the current time. this more closely models a network * device which can queue multiple output packets. */ tout = link->tout; if(!tout) tout = now; while(tout <= now){ . 490c return; . 482c vlong tout, tin; . 478c static void linkintr(Ureg*, Cycintr *ci) { Link *link; link = ci->a; pushlink(link, ci->when); } /* * move blocks between queues if they are ready. * schedule an interrupt for the next interesting time. * * must be called with the link ilocked. */ static void . 464,475c t = fastticks(nil); for(chan = 0; chan < 2; chan++) pushlink(&lb->link[chan], t); clockintrsched(); . 462c vlong t; int chan; . 455,458d 450a . 449a link->packets++; link->bytes += n; . 448a ptime(bp->rp, fastticks(nil)); . 443,444c /* make it a single block with space for the loopback timing header */ bp = padblock(bp, Tmsize); . 430c error(Eperm); . 428a lb = c->aux; link = &lb->link[ID(c->qid.path)]; cb = parsecmd(va, n); if(cb->nf < 1) error("short control request"); if(strcmp(cb->f[0], "delay") == 0){ if(cb->nf != 3) error("usage: delay latency bytedelay"); d0ns = strtol(cb->f[1], nil, 10); dnns = strtol(cb->f[2], nil, 10); /* * it takes about 20000 cycles on a pentium ii * to run pushlink; perhaps this should be accounted. */ d0 = NS2FASTHZ(d0ns); dn = NS2FASTHZ(dnns); ilock(link); link->delay0 = d0; link->delayn = dn; link->delay0ns = d0ns; link->delaynns = dnns; iunlock(link); }else if(strcmp(cb->f[0], "indrop") == 0){ if(cb->nf != 2) error("usage: indrop [01]"); ilock(link); link->indrop = strtol(cb->f[1], nil, 0) != 0; iunlock(link); }else if(strcmp(cb->f[0], "droprate") == 0){ if(cb->nf != 2) error("usage: droprate ofn"); ilock(link); link->droprate = strtol(cb->f[1], nil, 0); iunlock(link); }else if(strcmp(cb->f[0], "limit") == 0){ if(cb->nf != 2) error("usage: droprate ofn"); ilock(link); link->limit = strtol(cb->f[1], nil, 0); qsetlimit(link->oq, link->limit); qsetlimit(link->iq, link->limit); iunlock(link); }else if(strcmp(cb->f[0], "reset") == 0){ if(cb->nf != 1) error("usage: reset"); ilock(link); link->packets = 0; link->bytes = 0; link->indrop = 0; link->soverflows = 0; link->drops = 0; iunlock(link); }else error("unknown control request"); break; . 413,418c switch(TYPE(c->qid.path)){ case Qdata: . 411a long d0, dn, d0ns, dnns; . 410a Loop *lb; Link *link; Cmdbuf *cb; . 398,405c if(TYPE(c->qid.path) == Qdata) return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp); return devbwrite(c, bp, off); . 395d 381,386c if(TYPE(c->qid.path) == Qdata) return qbread(lb->link[ID(c->qid.path)].iq, n); . 378d 371c return rv; . 369c error(Eperm); return -1; /* not reached */ case Qtopdir: case Qloopdir: case Qportdir: return devdirread(c, va, n, nil, 0, loopbackgen); case Qdata: return qread(lb->link[ID(c->qid.path)].iq, va, n); case Qstatus: link = &lb->link[ID(c->qid.path)]; buf = smalloc(Statelen); rv = snprint(buf, Statelen, "delay %ld %ld\n", link->delay0ns, link->delaynns); rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit); rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop); snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate); rv = readstr(offset, va, n, buf); free(buf); break; case Qstats: link = &lb->link[ID(c->qid.path)]; buf = smalloc(Statelen); rv = snprint(buf, Statelen, "packets: %ld\n", link->packets); rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes); rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops); snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows); rv = readstr(offset, va, n, buf); free(buf); break; . 358,367c switch(TYPE(c->qid.path)){ . 355c Link *link; char *buf; long rv; . 352c loopbackread(Chan *c, void *va, long n, vlong offset) . 328a cycintrdel(&link->ci); . 293a qsetlimit(lb->link[chan].oq, lb->link[chan].limit); qsetlimit(lb->link[chan].iq, lb->link[chan].limit); . 269,281c /* * closing either side hangs up the stream */ if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){ chan = ID(c->qid.path); if(--lb->link[chan].ref == 0){ qhangup(lb->link[chan ^ 1].oq, nil); looper(lb); . 245,251c if(TYPE(c->qid.path) == Qdata) lb->link[ID(c->qid.path)].ref++; . 232d 205,222c devstat(c, db, nil, 0, loopbackgen); . 199c return devwalk(c, name, nil, 0, loopbackgen); . 191,192d 189c if(type == Qdata){ lb = c->aux; len = qlen(lb->link[ID(c->qid.path)].iq); } devdir(c, c->qid, tab->name, len, eve, tab->perm, dp); return 1; . 187a /* non directory entries end up here; must be in lowest level */ if(c->qid.path & CHDIR) panic("loopbackgen: unexpected directory"); if(i != 0) return -1; tab = &loopdirs[type]; if(tab == nil) panic("loopbackgen: unknown type: %d", type); . 174,186c switch(type){ case Qtopdir: if(i != 0) return -1; snprint(buf, sizeof(buf), "loopback%ld", c->dev); devdir(c, (Qid){QID(0, Qloopdir) | CHDIR, 0}, buf, 0, eve, 0555, dp); return 1; case Qloopdir: if(i >= 2) return -1; snprint(buf, sizeof(buf), "%d", i); devdir(c, (Qid){QID(i, QID(0, Qportdir)) | CHDIR, 0}, buf, 0, eve, 0555, dp); return 1; case Qportdir: if(i >= nelem(loopportdir)) return -1; tab = &loopportdir[i]; devdir(c, (Qid){QID(ID(c->qid.path), tab->qid.path), 0}, tab->name, tab->length, eve, tab->perm, dp); return 1; . 170c switch(type){ case Qtopdir: case Qloopdir: snprint(buf, sizeof(buf), "#X%ld", c->dev); devdir(c, (Qid){CHDIR|QID(0, Qtopdir), 0}, buf, 0, eve, 0555, dp); break; case Qportdir: snprint(buf, sizeof(buf), "loopback%ld", c->dev); devdir(c, (Qid){CHDIR|QID(0, Qloopdir), 0}, buf, 0, eve, 0555, dp); break; default: panic("loopbackgen %lux", c->qid.path); } . 168a type = TYPE(c->qid.path); . 167c Dirtab *tab; char buf[NAMELEN]; int len, type; . 164c loopbackgen(Chan *c, Dirtab*, int, int i, Dir *dp) . 150,158c if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) lb->link[ID(c->qid.path)].ref++; . 144d 134c c->qid = (Qid){CHDIR|QID(0, Qtopdir), 0}; . 128c lb->link[chan].indrop = 1; lb->link[chan].delayn = NS2FASTHZ(Delayn); lb->link[chan].delaynns = Delayn; lb->link[chan].delay0 = NS2FASTHZ(Delay0); lb->link[chan].delay0ns = Delay0; . 122c q = qopen(lb->link[chan].limit, 0, 0, 0); . 116c lb->link[chan].ci.a = &lb->link[chan]; lb->link[chan].ci.f = linkintr; lb->link[chan].limit = Loopqlim; q = qopen(lb->link[chan].limit, 0, 0, 0); . 114c fastticks(&fasthz); . 107a lb->ref--; . 104c lb = &loopbacks[dev]; . 102a if(!havecycintr()) error("can't time packets"); dev = 0; if(spec != nil){ dev = atoi(spec); if(dev >= Nloopbacks) error(Ebadspec); } . 101a int dev; . 92a /* invert directory tables for non-directory entries */ for(i=0; i>8) #define QID(x,y) (((x)<<8)|(y)) #define NS2FASTHZ(t) ((fasthz*(t))/1000000000); . 76c static Loop loopbacks[Nloopbacks]; . 74a static Dirtab loopdirs[MaxQ]; . 72,73c "data", {Qdata}, 0, 0666, . 70c "status", {Qstatus}, 0, 0444, . 67c static Dirtab loopportdir[] = . 63,64c Nloopbacks = 1, Statelen = 23*1024, /* status buffer size */ Tmsize = 8, Delayn = 10000, /* default delays */ Delay0 = 2500000, Loopqlim = 32*1024, /* default size of queues */ . 61c MaxQ, . 58,59c Qdata, . 54c Qtopdir= 1, /* top level directory */ Qloopdir, /* loopback* directory */ Qportdir, /* directory each end of the loop */ . 42d 32a Cycintr ci; /* time to move packets from next packet from oq */ . 30a long limit; /* queue buffering limit */ . 23,24c long delay0ns; /* nanosec of delay in the link */ long delaynns; /* nanosec of delay per byte */ long delay0; /* fastticks of delay */ long delayn; . 19,21c long packets; /* total number of packets sent */ long bytes; /* total number of bytes sent */ int indrop; /* enable dropping on iq overflow */ long soverflows; /* packets dropped because iq overflowed */ long droprate; /* drop 1/droprate packets in tq */ long drops; /* packets deliberately dropped */ . 8,9d ## diffname port/devloopback.c 2000/0726 ## diff -e /n/emeliedump/2000/0708/sys/src/9/port/devloopback.c /n/emeliedump/2000/0726/sys/src/9/port/devloopback.c 524c error("usage: limit maxqsize"); . ## diffname port/devloopback.c 2000/0727 ## diff -e /n/emeliedump/2000/0726/sys/src/9/port/devloopback.c /n/emeliedump/2000/0727/sys/src/9/port/devloopback.c 300a } . 299c if(TYPE(c->qid.path) == Qdata){ if(lb->link[ID(c->qid.path)].ref){ qunlock(lb); error(Einuse); } . ## diffname port/devloopback.c 2000/0912 ## diff -e /n/emeliedump/2000/0727/sys/src/9/port/devloopback.c /n/emeliedump/2000/0912/sys/src/9/port/devloopback.c 499c d0ns = strtoll(cb->f[1], nil, 10); . 477c vlong d0, d0ns; long dn, dnns; . 426c rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns); . 71c Nloopbacks = 5, . 26c vlong delay0; /* fastticks of delay */ . 24c vlong delay0ns; /* nanosec of delay in the link */ . ## diffname port/devloopback.c 2000/0913 ## diff -e /n/emeliedump/2000/0912/sys/src/9/port/devloopback.c /n/emeliedump/2000/0913/sys/src/9/port/devloopback.c 184c c->dev = dev; . ## diffname port/devloopback.c 2001/0331 ## diff -e /n/emeliedump/2000/0913/sys/src/9/port/devloopback.c /n/emeliedump/2001/0331/sys/src/9/port/devloopback.c 547a free(cb); . 494a if(waserror()){ free(cb); nexterror(); } . 475,476c Cmdbuf *volatile cb; Block *volatile bp; . 126c Loop *volatile lb; . ## diffname port/devloopback.c 2001/0504 ## diff -e /n/emeliedump/2001/0331/sys/src/9/port/devloopback.c /n/emeliedump/2001/0504/sys/src/9/port/devloopback.c 573a poperror(); . 568a if(waserror()){ freeb(bp); nexterror(); } . 562c loopoput(Loop *lb, Link *link, Block *volatile bp) . ## diffname port/devloopback.c 2001/0530 ## diff -e /n/emeliedump/2001/0504/sys/src/9/port/devloopback.c /n/emeliedump/2001/0530/sys/src/9/port/devloopback.c 748d 288c if(c->qid.type & QTDIR){ . 277c return devstat(c, db, n, nil, 0, loopbackgen); . 274,275c static int loopbackstat(Chan *c, uchar *db, int n) . 271c return devwalk(c, nc, name, nname, nil, 0, loopbackgen); . 268,269c static Walkqid* loopbackwalk(Chan *c, Chan *nc, char **name, int nname) . 250c if(c->qid.type & QTDIR) . 246c mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTDIR); devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); . 239,240c snprint(up->genbuf, sizeof(up->genbuf), "%d", i); mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); . 233,234c snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); . 220,221c snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); . 216,217c snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev); mkqid(&qid, QID(0, Qtopdir), 0, QTDIR); devdir(c, qid, up->genbuf, 0, eve, 0555, dp); . 209a Qid qid; . 208d 204c loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) . 182c mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); . 95,97c #define TYPE(x) (((ulong)(x))&0xff) #define ID(x) (((ulong)(x))>>8) #define QID(x,y) ((((ulong)(x))<<8)|((ulong)(y))) . ## diffname port/devloopback.c 2001/0601 ## diff -e /n/emeliedump/2001/0530/sys/src/9/port/devloopback.c /n/emeliedump/2001/0601/sys/src/9/port/devloopback.c 276c Walkqid *wq; Loop *lb; wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen); if(wq != nil && wq->clone != nil && wq->clone != c){ lb = c->aux; qlock(lb); lb->ref++; if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) lb->link[ID(c->qid.path)].ref++; qunlock(lb); } return wq; . 250c mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE); . 188,202d ## diffname port/devloopback.c 2001/0609 ## diff -e /n/emeliedump/2001/0601/sys/src/9/port/devloopback.c /n/emeliedump/2001/0609/sys/src/9/port/devloopback.c 553a poperror(); . ## diffname port/devloopback.c 2001/1207 ## diff -e /n/emeliedump/2001/0609/sys/src/9/port/devloopback.c /n/emeliedump/2001/1207/sys/src/9/port/devloopback.c 312a c->iounit = qiomaxatomic; . ## diffname port/devloopback.c 2002/0109 ## diff -e /n/emeliedump/2001/1207/sys/src/9/port/devloopback.c /n/emeliedump/2002/0109/sys/src/9/port/devloopback.c 750a devshutdown, . ## diffname port/devloopback.c 2002/0125 ## diff -e /n/emeliedump/2002/0109/sys/src/9/port/devloopback.c /n/emeliedump/2002/0125/sys/src/9/port/devloopback.c 211c panic("loopbackgen %llux", c->qid.path); . ## diffname port/devloopback.c 2002/0405 ## diff -e /n/emeliedump/2002/0125/sys/src/9/port/devloopback.c /n/emeliedump/2002/0405/sys/src/9/port/devloopback.c 713c timeradd(&link->ci); . 636c timerdel(&link->ci); . 607c linkintr(Ureg*, Timer *ci) . 384c timerdel(&link->ci); . 132c if(!havetimer()) . 108c static void linkintr(Ureg*, Timer *ci); . 38c Timer ci; /* time to move packets from next packet from oq */ . ## diffname port/devloopback.c 2002/0410 ## diff -e /n/emeliedump/2002/0405/sys/src/9/port/devloopback.c /n/emeliedump/2002/0410/sys/src/9/port/devloopback.c 132,134d ## diffname port/devloopback.c 2002/0413 ## diff -e /n/emeliedump/2002/0410/sys/src/9/port/devloopback.c /n/emeliedump/2002/0413/sys/src/9/port/devloopback.c 600d