| plan 9 kernel history: overview | file list | diff list |
2000/0708/port/devloopback.c (diff list | history)
| 2000/0617/sys/src/9/port/devloopback.c:5,12 – 2000/0708/sys/src/9/port/devloopback.c:5,10 (short | long | prev | next) | ||
| 2000/0617 | #include "fns.h" #include "../port/error.h" | |
| 2000/0617/sys/src/9/port/devloopback.c:16,27 – 2000/0708/sys/src/9/port/devloopback.c:14,30 | ||
| 2000/0617 | int ref; | |
| 2000/0708 | 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 */ | |
| 2000/0617 |
| |
| 2000/0708 | long delay0ns; /* nanosec of delay in the link */ long delaynns; /* nanosec of delay per byte */ long delay0; /* fastticks of delay */ long delayn; | |
| 2000/0617 | Block *tq; /* transmission queue */ Block *tqtail; | |
| 2000/0617/sys/src/9/port/devloopback.c:28,35 – 2000/0708/sys/src/9/port/devloopback.c:31,41 | ||
| 2000/0617 | vlong tout; /* time the last packet in tq is really out */ vlong tin; /* time the head packet in tq enters the remote side */ | |
| 2000/0708 | long limit; /* queue buffering limit */ | |
| 2000/0617 | Queue *oq; /* output queue from other side & packets in the link */ Queue *iq; | |
| 2000/0708 | Cycintr ci; /* time to move packets from next packet from oq */ | |
| 2000/0617 | }; struct Loop | |
| 2000/0617/sys/src/9/port/devloopback.c:39,45 – 2000/0708/sys/src/9/port/devloopback.c:45,50 | ||
| 2000/0617 | int minmtu; /* smallest block transmittable */ Loop *next; ulong path; | |
| 2000/0617/sys/src/9/port/devloopback.c:51,87 – 2000/0708/sys/src/9/port/devloopback.c:56,111 | ||
| 2000/0617 | enum { | |
| 2000/0708 | Qtopdir= 1, /* top level directory */ Qloopdir, /* loopback* directory */ Qportdir, /* directory each end of the loop */ | |
| 2000/0617 | Qctl, Qstatus, Qstats, | |
| 2000/0708 | Qdata, | |
| 2000/0617 |
| |
| 2000/0708 | MaxQ, | |
| 2000/0617 |
| |
| 2000/0708 | Nloopbacks = 1, Statelen = 23*1024, /* status buffer size */ Tmsize = 8, Delayn = 10000, /* default delays */ Delay0 = 2500000, Loopqlim = 32*1024, /* default size of queues */ | |
| 2000/0617 | }; | |
| 2000/0708 | static Dirtab loopportdir[] = | |
| 2000/0617 | { "ctl", {Qctl}, 0, 0222, | |
| 2000/0708 | "status", {Qstatus}, 0, 0444, | |
| 2000/0617 | "stats", {Qstats}, 0, 0444, | |
| 2000/0708 | "data", {Qdata}, 0, 0666, | |
| 2000/0617 | }; | |
| 2000/0708 | static Dirtab loopdirs[MaxQ]; | |
| 2000/0617 |
| |
| 2000/0708 | static Loop loopbacks[Nloopbacks]; | |
| 2000/0617 | ||
| 2000/0708 | static uvlong fasthz; #define TYPE(x) ((x)&0xff) #define ID(x) (((x)&~CHDIR)>>8) #define QID(x,y) (((x)<<8)|(y)) #define NS2FASTHZ(t) ((fasthz*(t))/1000000000); | |
| 2000/0617 | 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); | |
| 2000/0708 | static void pushlink(Link *link, vlong now); | |
| 2000/0617 | static void freelb(Loop *lb); | |
| 2000/0708 | static void linkintr(Ureg*, Cycintr *ci); | |
| 2000/0617 | static void loopbackinit(void) | |
| 2000/0617/sys/src/9/port/devloopback.c:88,95 – 2000/0708/sys/src/9/port/devloopback.c:112,123 | ||
| 2000/0617 | { int i; | |
| 2000/0708 | for(i = 0; i < Nloopbacks; i++) | |
| 2000/0617 | loopbacks[i].path = i; | |
| 2000/0708 | /* invert directory tables for non-directory entries */ for(i=0; i<nelem(loopportdir); i++) loopdirs[loopportdir[i].qid.path] = loopportdir[i]; | |
| 2000/0617 | } static Chan* | |
| 2000/0617/sys/src/9/port/devloopback.c:99,110 – 2000/0708/sys/src/9/port/devloopback.c:127,150 | ||
| 2000/0617 | Queue *q; Chan *c; int chan; | |
| 2000/0708 | int dev; | |
| 2000/0617 | ||
| 2000/0708 | if(!havecycintr()) error("can't time packets"); dev = 0; if(spec != nil){ dev = atoi(spec); if(dev >= Nloopbacks) error(Ebadspec); } | |
| 2000/0617 | c = devattach('X', spec); | |
| 2000/0708 | lb = &loopbacks[dev]; | |
| 2000/0617 | qlock(lb); if(waserror()){ | |
| 2000/0708 | lb->ref--; | |
| 2000/0617 | qunlock(lb); nexterror(); } | |
| 2000/0617/sys/src/9/port/devloopback.c:111,137 – 2000/0708/sys/src/9/port/devloopback.c:151,185 | ||
| 2000/0617 | lb->ref++; if(lb->ref == 1){ | |
| 2000/0708 | fastticks(&fasthz); | |
| 2000/0617 | for(chan = 0; chan < 2; chan++){ | |
| 2000/0708 | 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); | |
| 2000/0617 | lb->link[chan].iq = q; if(q == nil){ freelb(lb); exhausted("memory"); } | |
| 2000/0708 | q = qopen(lb->link[chan].limit, 0, 0, 0); | |
| 2000/0617 | lb->link[chan].oq = q; if(q == nil){ freelb(lb); exhausted("memory"); } | |
| 2000/0708 | 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; | |
| 2000/0617 | } } poperror(); qunlock(lb); | |
| 2000/0708 | c->qid = (Qid){CHDIR|QID(0, Qtopdir), 0}; | |
| 2000/0617 | c->aux = lb; c->dev = 0; return c; | |
| 2000/0617/sys/src/9/port/devloopback.c:141,195 – 2000/0708/sys/src/9/port/devloopback.c:189,267 | ||
| 2000/0617 | loopbackclone(Chan *c, Chan *nc) { Loop *lb; | |
| 2000/0708 | if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) lb->link[ID(c->qid.path)].ref++; | |
| 2000/0617 | qunlock(lb); return nc; } static int | |
| 2000/0708 | loopbackgen(Chan *c, Dirtab*, int, int i, Dir *dp) | |
| 2000/0617 | { Loop *lb; | |
| 2000/0708 | Dirtab *tab; char buf[NAMELEN]; int len, type; | |
| 2000/0617 | ||
| 2000/0708 | type = TYPE(c->qid.path); | |
| 2000/0617 | if(i == DEVDOTDOT){ | |
| 2000/0708 | 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); } | |
| 2000/0617 | return 1; } | |
| 2000/0708 | 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; | |
| 2000/0617 | default: | |
| 2000/0708 | /* 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); | |
| 2000/0617 | len = tab->length; | |
| 2000/0708 | 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; | |
| 2000/0617 | } | |
| 2000/0617/sys/src/9/port/devloopback.c:196,225 – 2000/0708/sys/src/9/port/devloopback.c:268,280 | ||
| 2000/0617 | static int loopbackwalk(Chan *c, char *name) { | |
| 2000/0708 | return devwalk(c, name, nil, 0, loopbackgen); | |
| 2000/0617 | } static void loopbackstat(Chan *c, char *db) { | |
| 2000/0708 | devstat(c, db, nil, 0, loopbackgen); | |
| 2000/0617 | } /* | |
| 2000/0617/sys/src/9/port/devloopback.c:229,235 – 2000/0708/sys/src/9/port/devloopback.c:284,289 | ||
| 2000/0617 | loopbackopen(Chan *c, int omode) { Loop *lb; | |
| 2000/0617/sys/src/9/port/devloopback.c:242,254 – 2000/0708/sys/src/9/port/devloopback.c:296,303 | ||
| 2000/0617 | lb = c->aux; qlock(lb); | |
| 2000/0708 | if(TYPE(c->qid.path) == Qdata) lb->link[ID(c->qid.path)].ref++; | |
| 2000/0617 | qunlock(lb); c->mode = openmode(omode); | |
| 2000/0617/sys/src/9/port/devloopback.c:266,284 – 2000/0708/sys/src/9/port/devloopback.c:315,329 | ||
| 2000/0617 | lb = c->aux; qlock(lb); | |
| 2000/0708 | /* * 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); | |
| 2000/0617 | } } | |
| 2000/0617/sys/src/9/port/devloopback.c:291,296 – 2000/0708/sys/src/9/port/devloopback.c:336,343 | ||
| 2000/0617 | closelink(&lb->link[chan], 0); qreopen(lb->link[chan].iq); qreopen(lb->link[chan].oq); | |
| 2000/0708 | qsetlimit(lb->link[chan].oq, lb->link[chan].limit); qsetlimit(lb->link[chan].iq, lb->link[chan].limit); | |
| 2000/0617 | } } ref = --lb->ref; | |
| 2000/0617/sys/src/9/port/devloopback.c:326,331 – 2000/0708/sys/src/9/port/devloopback.c:373,379 | ||
| 2000/0617 | link->tqtail = nil; link->tout = 0; link->tin = 0; | |
| 2000/0708 | cycintrdel(&link->ci); | |
| 2000/0617 | iunlock(link); if(iq != nil){ qclose(iq); | |
| 2000/0617/sys/src/9/port/devloopback.c:349,374 – 2000/0708/sys/src/9/port/devloopback.c:397,442 | ||
| 2000/0617 | } static long | |
| 2000/0708 | loopbackread(Chan *c, void *va, long n, vlong offset) | |
| 2000/0617 | { Loop *lb; | |
| 2000/0708 | Link *link; char *buf; long rv; | |
| 2000/0617 | lb = c->aux; | |
| 2000/0708 | switch(TYPE(c->qid.path)){ | |
| 2000/0617 | default: | |
| 2000/0708 | 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; | |
| 2000/0617 | } | |
| 2000/0708 | return rv; | |
| 2000/0617 | } static Block* | |
| 2000/0617/sys/src/9/port/devloopback.c:375,389 – 2000/0708/sys/src/9/port/devloopback.c:443,452 | ||
| 2000/0617 | loopbackbread(Chan *c, long n, ulong offset) { Loop *lb; | |
| 2000/0708 | if(TYPE(c->qid.path) == Qdata) return qbread(lb->link[ID(c->qid.path)].iq, n); | |
| 2000/0617 | return devbread(c, n, offset); } | |
| 2000/0617/sys/src/9/port/devloopback.c:392,421 – 2000/0708/sys/src/9/port/devloopback.c:455,478 | ||
| 2000/0617 | loopbackbwrite(Chan *c, Block *bp, ulong off) { Loop *lb; | |
| 2000/0708 | if(TYPE(c->qid.path) == Qdata) return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp); return devbwrite(c, bp, off); | |
| 2000/0617 | } static long loopbackwrite(Chan *c, void *va, long n, vlong off) { | |
| 2000/0708 | Loop *lb; Link *link; Cmdbuf *cb; | |
| 2000/0617 | Block *bp; | |
| 2000/0708 | long d0, dn, d0ns, dnns; | |
| 2000/0617 |
| |
| 2000/0708 | switch(TYPE(c->qid.path)){ case Qdata: | |
| 2000/0617 | bp = allocb(n); if(waserror()){ freeb(bp); | |
| 2000/0617/sys/src/9/port/devloopback.c:426,433 – 2000/0708/sys/src/9/port/devloopback.c:483,547 | ||
| 2000/0617 | bp->wp += n; return loopbackbwrite(c, bp, off); case Qctl: | |
| 2000/0708 | 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; | |
| 2000/0617 | default: | |
| 2000/0708 | error(Eperm); | |
| 2000/0617 | } return n; | |
| 2000/0617/sys/src/9/port/devloopback.c:440,485 – 2000/0708/sys/src/9/port/devloopback.c:554,608 | ||
| 2000/0617 | n = BLEN(bp); | |
| 2000/0708 | /* make it a single block with space for the loopback timing header */ bp = padblock(bp, Tmsize); | |
| 2000/0617 | if(bp->next) bp = concatblock(bp); if(BLEN(bp) < lb->minmtu) bp = adjustblock(bp, lb->minmtu); | |
| 2000/0708 | ptime(bp->rp, fastticks(nil)); | |
| 2000/0617 | ||
| 2000/0708 | link->packets++; link->bytes += n; | |
| 2000/0617 | qbwrite(link->oq, bp); | |
| 2000/0708 | ||
| 2000/0617 | looper(lb); return n; } | |
| 2000/0708 | vlong t; int chan; | |
| 2000/0617 |
| |
| 2000/0708 | t = fastticks(nil); for(chan = 0; chan < 2; chan++) pushlink(&lb->link[chan], t); clockintrsched(); | |
| 2000/0617 | } | |
| 2000/0708 | 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 | |
| 2000/0617 | pushlink(Link *link, vlong now) { Block *bp; | |
| 2000/0708 | vlong tout, tin; | |
| 2000/0617 | /* * put another block in the link queue | |
| 2000/0617/sys/src/9/port/devloopback.c:487,536 – 2000/0708/sys/src/9/port/devloopback.c:610,698 | ||
| 2000/0617 | ilock(link); if(link->iq == nil || link->oq == nil){ iunlock(link); | |
| 2000/0708 | return; | |
| 2000/0617 | } | |
| 2000/0708 | 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){ | |
| 2000/0617 | bp = qget(link->oq); | |
| 2000/0708 | 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); | |
| 2000/0617 | if(link->tq == nil) link->tq = bp; else link->tqtail->next = bp; link->tqtail = bp; | |
| 2000/0708 | } | |
| 2000/0617 | } /* | |
| 2000/0708 | * 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; /* | |
| 2000/0617 | * put more blocks into the receive queue */ | |
| 2000/0708 | tin = 0; | |
| 2000/0617 | while(bp = link->tq){ | |
| 2000/0708 | tin = gtime(bp->rp); if(tin > now) | |
| 2000/0617 | break; | |
| 2000/0708 | bp->rp += Tmsize; | |
| 2000/0617 | link->tq = bp->next; bp->next = nil; | |
| 2000/0708 | if(!link->indrop) | |
| 2000/0617 | qpassnolim(link->iq, bp); else if(qpass(link->iq, bp) < 0) link->soverflows++; | |
| 2000/0708 | tin = 0; | |
| 2000/0617 | } if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq)) qhangup(link->iq, nil); | |
| 2000/0708 | 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); } | |
| 2000/0617 | iunlock(link); | |