159cc4ca5SDavid du Colombier #include "u.h"
259cc4ca5SDavid du Colombier #include "../port/lib.h"
359cc4ca5SDavid du Colombier #include "mem.h"
459cc4ca5SDavid du Colombier #include "dat.h"
559cc4ca5SDavid du Colombier #include "fns.h"
659cc4ca5SDavid du Colombier #include "../port/error.h"
759cc4ca5SDavid du Colombier
859cc4ca5SDavid du Colombier typedef struct Link Link;
959cc4ca5SDavid du Colombier typedef struct Loop Loop;
1059cc4ca5SDavid du Colombier
1159cc4ca5SDavid du Colombier struct Link
1259cc4ca5SDavid du Colombier {
1359cc4ca5SDavid du Colombier Lock;
1459cc4ca5SDavid du Colombier
1559cc4ca5SDavid du Colombier int ref;
1659cc4ca5SDavid du Colombier
1759cc4ca5SDavid du Colombier long packets; /* total number of packets sent */
1859cc4ca5SDavid du Colombier long bytes; /* total number of bytes sent */
1959cc4ca5SDavid du Colombier int indrop; /* enable dropping on iq overflow */
2059cc4ca5SDavid du Colombier long soverflows; /* packets dropped because iq overflowed */
2159cc4ca5SDavid du Colombier long droprate; /* drop 1/droprate packets in tq */
2259cc4ca5SDavid du Colombier long drops; /* packets deliberately dropped */
2359cc4ca5SDavid du Colombier
2480ee5cbfSDavid du Colombier vlong delay0ns; /* nanosec of delay in the link */
2559cc4ca5SDavid du Colombier long delaynns; /* nanosec of delay per byte */
2659cc4ca5SDavid du Colombier
2759cc4ca5SDavid du Colombier Block *tq; /* transmission queue */
2859cc4ca5SDavid du Colombier Block *tqtail;
2959cc4ca5SDavid du Colombier vlong tout; /* time the last packet in tq is really out */
3059cc4ca5SDavid du Colombier vlong tin; /* time the head packet in tq enters the remote side */
3159cc4ca5SDavid du Colombier
3259cc4ca5SDavid du Colombier long limit; /* queue buffering limit */
3359cc4ca5SDavid du Colombier Queue *oq; /* output queue from other side & packets in the link */
3459cc4ca5SDavid du Colombier Queue *iq;
3559cc4ca5SDavid du Colombier
369a747e4fSDavid du Colombier Timer ci; /* time to move packets from next packet from oq */
3759cc4ca5SDavid du Colombier };
3859cc4ca5SDavid du Colombier
3959cc4ca5SDavid du Colombier struct Loop
4059cc4ca5SDavid du Colombier {
4159cc4ca5SDavid du Colombier QLock;
4259cc4ca5SDavid du Colombier int ref;
4359cc4ca5SDavid du Colombier int minmtu; /* smallest block transmittable */
4459cc4ca5SDavid du Colombier Loop *next;
4559cc4ca5SDavid du Colombier ulong path;
4659cc4ca5SDavid du Colombier Link link[2];
4759cc4ca5SDavid du Colombier };
4859cc4ca5SDavid du Colombier
4959cc4ca5SDavid du Colombier static struct
5059cc4ca5SDavid du Colombier {
5159cc4ca5SDavid du Colombier Lock;
5259cc4ca5SDavid du Colombier ulong path;
5359cc4ca5SDavid du Colombier } loopbackalloc;
5459cc4ca5SDavid du Colombier
5559cc4ca5SDavid du Colombier enum
5659cc4ca5SDavid du Colombier {
5759cc4ca5SDavid du Colombier Qtopdir= 1, /* top level directory */
5859cc4ca5SDavid du Colombier
5959cc4ca5SDavid du Colombier Qloopdir, /* loopback* directory */
6059cc4ca5SDavid du Colombier
6159cc4ca5SDavid du Colombier Qportdir, /* directory each end of the loop */
6259cc4ca5SDavid du Colombier Qctl,
6359cc4ca5SDavid du Colombier Qstatus,
6459cc4ca5SDavid du Colombier Qstats,
6559cc4ca5SDavid du Colombier Qdata,
6659cc4ca5SDavid du Colombier
6759cc4ca5SDavid du Colombier MaxQ,
6859cc4ca5SDavid du Colombier
6980ee5cbfSDavid du Colombier Nloopbacks = 5,
7059cc4ca5SDavid du Colombier
7159cc4ca5SDavid du Colombier Statelen = 23*1024, /* status buffer size */
7259cc4ca5SDavid du Colombier
7359cc4ca5SDavid du Colombier Tmsize = 8,
74e288d156SDavid du Colombier Delayn = 10000, /* default delays in ns */
7559cc4ca5SDavid du Colombier Delay0 = 2500000,
7659cc4ca5SDavid du Colombier
7759cc4ca5SDavid du Colombier Loopqlim = 32*1024, /* default size of queues */
7859cc4ca5SDavid du Colombier };
7959cc4ca5SDavid du Colombier
8059cc4ca5SDavid du Colombier static Dirtab loopportdir[] =
8159cc4ca5SDavid du Colombier {
8259cc4ca5SDavid du Colombier "ctl", {Qctl}, 0, 0222,
8359cc4ca5SDavid du Colombier "status", {Qstatus}, 0, 0444,
8459cc4ca5SDavid du Colombier "stats", {Qstats}, 0, 0444,
8559cc4ca5SDavid du Colombier "data", {Qdata}, 0, 0666,
8659cc4ca5SDavid du Colombier };
8759cc4ca5SDavid du Colombier static Dirtab loopdirs[MaxQ];
8859cc4ca5SDavid du Colombier
8959cc4ca5SDavid du Colombier static Loop loopbacks[Nloopbacks];
9059cc4ca5SDavid du Colombier
919a747e4fSDavid du Colombier #define TYPE(x) (((ulong)(x))&0xff)
929a747e4fSDavid du Colombier #define ID(x) (((ulong)(x))>>8)
939a747e4fSDavid du Colombier #define QID(x,y) ((((ulong)(x))<<8)|((ulong)(y)))
9459cc4ca5SDavid du Colombier
9559cc4ca5SDavid du Colombier static void looper(Loop *lb);
9659cc4ca5SDavid du Colombier static long loopoput(Loop *lb, Link *link, Block *bp);
9759cc4ca5SDavid du Colombier static void ptime(uchar *p, vlong t);
9859cc4ca5SDavid du Colombier static vlong gtime(uchar *p);
9959cc4ca5SDavid du Colombier static void closelink(Link *link, int dofree);
10059cc4ca5SDavid du Colombier static void pushlink(Link *link, vlong now);
10159cc4ca5SDavid du Colombier static void freelb(Loop *lb);
1029a747e4fSDavid du Colombier static void linkintr(Ureg*, Timer *ci);
10359cc4ca5SDavid du Colombier
10459cc4ca5SDavid du Colombier static void
loopbackinit(void)10559cc4ca5SDavid du Colombier loopbackinit(void)
10659cc4ca5SDavid du Colombier {
10759cc4ca5SDavid du Colombier int i;
10859cc4ca5SDavid du Colombier
10959cc4ca5SDavid du Colombier for(i = 0; i < Nloopbacks; i++)
11059cc4ca5SDavid du Colombier loopbacks[i].path = i;
11159cc4ca5SDavid du Colombier
11259cc4ca5SDavid du Colombier /* invert directory tables for non-directory entries */
11359cc4ca5SDavid du Colombier for(i=0; i<nelem(loopportdir); i++)
11459cc4ca5SDavid du Colombier loopdirs[loopportdir[i].qid.path] = loopportdir[i];
11559cc4ca5SDavid du Colombier }
11659cc4ca5SDavid du Colombier
11759cc4ca5SDavid du Colombier static Chan*
loopbackattach(char * spec)11859cc4ca5SDavid du Colombier loopbackattach(char *spec)
11959cc4ca5SDavid du Colombier {
1209a747e4fSDavid du Colombier Loop *volatile lb;
12159cc4ca5SDavid du Colombier Queue *q;
12259cc4ca5SDavid du Colombier Chan *c;
12359cc4ca5SDavid du Colombier int chan;
12459cc4ca5SDavid du Colombier int dev;
12559cc4ca5SDavid du Colombier
12659cc4ca5SDavid du Colombier dev = 0;
12759cc4ca5SDavid du Colombier if(spec != nil){
12859cc4ca5SDavid du Colombier dev = atoi(spec);
12959cc4ca5SDavid du Colombier if(dev >= Nloopbacks)
13059cc4ca5SDavid du Colombier error(Ebadspec);
13159cc4ca5SDavid du Colombier }
13259cc4ca5SDavid du Colombier
13359cc4ca5SDavid du Colombier c = devattach('X', spec);
13459cc4ca5SDavid du Colombier lb = &loopbacks[dev];
13559cc4ca5SDavid du Colombier
13659cc4ca5SDavid du Colombier qlock(lb);
13759cc4ca5SDavid du Colombier if(waserror()){
13859cc4ca5SDavid du Colombier lb->ref--;
13959cc4ca5SDavid du Colombier qunlock(lb);
14059cc4ca5SDavid du Colombier nexterror();
14159cc4ca5SDavid du Colombier }
14259cc4ca5SDavid du Colombier
14359cc4ca5SDavid du Colombier lb->ref++;
14459cc4ca5SDavid du Colombier if(lb->ref == 1){
14559cc4ca5SDavid du Colombier for(chan = 0; chan < 2; chan++){
146*2cca75a1SDavid du Colombier lb->link[chan].ci.mode = Trelative;
14759cc4ca5SDavid du Colombier lb->link[chan].ci.a = &lb->link[chan];
14859cc4ca5SDavid du Colombier lb->link[chan].ci.f = linkintr;
14959cc4ca5SDavid du Colombier lb->link[chan].limit = Loopqlim;
15059cc4ca5SDavid du Colombier q = qopen(lb->link[chan].limit, 0, 0, 0);
15159cc4ca5SDavid du Colombier lb->link[chan].iq = q;
15259cc4ca5SDavid du Colombier if(q == nil){
15359cc4ca5SDavid du Colombier freelb(lb);
15459cc4ca5SDavid du Colombier exhausted("memory");
15559cc4ca5SDavid du Colombier }
15659cc4ca5SDavid du Colombier q = qopen(lb->link[chan].limit, 0, 0, 0);
15759cc4ca5SDavid du Colombier lb->link[chan].oq = q;
15859cc4ca5SDavid du Colombier if(q == nil){
15959cc4ca5SDavid du Colombier freelb(lb);
16059cc4ca5SDavid du Colombier exhausted("memory");
16159cc4ca5SDavid du Colombier }
16259cc4ca5SDavid du Colombier lb->link[chan].indrop = 1;
16359cc4ca5SDavid du Colombier
16459cc4ca5SDavid du Colombier lb->link[chan].delaynns = Delayn;
16559cc4ca5SDavid du Colombier lb->link[chan].delay0ns = Delay0;
16659cc4ca5SDavid du Colombier }
16759cc4ca5SDavid du Colombier }
16859cc4ca5SDavid du Colombier poperror();
16959cc4ca5SDavid du Colombier qunlock(lb);
17059cc4ca5SDavid du Colombier
1719a747e4fSDavid du Colombier mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
17259cc4ca5SDavid du Colombier c->aux = lb;
17380ee5cbfSDavid du Colombier c->dev = dev;
17459cc4ca5SDavid du Colombier return c;
17559cc4ca5SDavid du Colombier }
17659cc4ca5SDavid du Colombier
17759cc4ca5SDavid du Colombier static int
loopbackgen(Chan * c,char *,Dirtab *,int,int i,Dir * dp)1789a747e4fSDavid du Colombier loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
17959cc4ca5SDavid du Colombier {
18059cc4ca5SDavid du Colombier Dirtab *tab;
18159cc4ca5SDavid du Colombier int len, type;
1829a747e4fSDavid du Colombier Qid qid;
18359cc4ca5SDavid du Colombier
18459cc4ca5SDavid du Colombier type = TYPE(c->qid.path);
18559cc4ca5SDavid du Colombier if(i == DEVDOTDOT){
18659cc4ca5SDavid du Colombier switch(type){
18759cc4ca5SDavid du Colombier case Qtopdir:
18859cc4ca5SDavid du Colombier case Qloopdir:
1899a747e4fSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev);
1909a747e4fSDavid du Colombier mkqid(&qid, QID(0, Qtopdir), 0, QTDIR);
1919a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
19259cc4ca5SDavid du Colombier break;
19359cc4ca5SDavid du Colombier case Qportdir:
1949a747e4fSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
1959a747e4fSDavid du Colombier mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
1969a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
19759cc4ca5SDavid du Colombier break;
19859cc4ca5SDavid du Colombier default:
1999a747e4fSDavid du Colombier panic("loopbackgen %llux", c->qid.path);
20059cc4ca5SDavid du Colombier }
20159cc4ca5SDavid du Colombier return 1;
20259cc4ca5SDavid du Colombier }
20359cc4ca5SDavid du Colombier
20459cc4ca5SDavid du Colombier switch(type){
20559cc4ca5SDavid du Colombier case Qtopdir:
20659cc4ca5SDavid du Colombier if(i != 0)
20759cc4ca5SDavid du Colombier return -1;
2089a747e4fSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
2099a747e4fSDavid du Colombier mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
2109a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
21159cc4ca5SDavid du Colombier return 1;
21259cc4ca5SDavid du Colombier case Qloopdir:
21359cc4ca5SDavid du Colombier if(i >= 2)
21459cc4ca5SDavid du Colombier return -1;
2159a747e4fSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "%d", i);
2169a747e4fSDavid du Colombier mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR);
2179a747e4fSDavid du Colombier devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
21859cc4ca5SDavid du Colombier return 1;
21959cc4ca5SDavid du Colombier case Qportdir:
22059cc4ca5SDavid du Colombier if(i >= nelem(loopportdir))
22159cc4ca5SDavid du Colombier return -1;
22259cc4ca5SDavid du Colombier tab = &loopportdir[i];
2239a747e4fSDavid du Colombier mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE);
2249a747e4fSDavid du Colombier devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
22559cc4ca5SDavid du Colombier return 1;
22659cc4ca5SDavid du Colombier default:
22759cc4ca5SDavid du Colombier /* non directory entries end up here; must be in lowest level */
2289a747e4fSDavid du Colombier if(c->qid.type & QTDIR)
22959cc4ca5SDavid du Colombier panic("loopbackgen: unexpected directory");
23059cc4ca5SDavid du Colombier if(i != 0)
23159cc4ca5SDavid du Colombier return -1;
23259cc4ca5SDavid du Colombier tab = &loopdirs[type];
23359cc4ca5SDavid du Colombier if(tab == nil)
23459cc4ca5SDavid du Colombier panic("loopbackgen: unknown type: %d", type);
23559cc4ca5SDavid du Colombier len = tab->length;
23659cc4ca5SDavid du Colombier devdir(c, c->qid, tab->name, len, eve, tab->perm, dp);
23759cc4ca5SDavid du Colombier return 1;
23859cc4ca5SDavid du Colombier }
23959cc4ca5SDavid du Colombier }
24059cc4ca5SDavid du Colombier
24159cc4ca5SDavid du Colombier
2429a747e4fSDavid du Colombier static Walkqid*
loopbackwalk(Chan * c,Chan * nc,char ** name,int nname)2439a747e4fSDavid du Colombier loopbackwalk(Chan *c, Chan *nc, char **name, int nname)
24459cc4ca5SDavid du Colombier {
2459a747e4fSDavid du Colombier Walkqid *wq;
2469a747e4fSDavid du Colombier Loop *lb;
2479a747e4fSDavid du Colombier
2489a747e4fSDavid du Colombier wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen);
2499a747e4fSDavid du Colombier if(wq != nil && wq->clone != nil && wq->clone != c){
2509a747e4fSDavid du Colombier lb = c->aux;
2519a747e4fSDavid du Colombier qlock(lb);
2529a747e4fSDavid du Colombier lb->ref++;
2539a747e4fSDavid du Colombier if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata)
2549a747e4fSDavid du Colombier lb->link[ID(c->qid.path)].ref++;
2559a747e4fSDavid du Colombier qunlock(lb);
2569a747e4fSDavid du Colombier }
2579a747e4fSDavid du Colombier return wq;
25859cc4ca5SDavid du Colombier }
25959cc4ca5SDavid du Colombier
2609a747e4fSDavid du Colombier static int
loopbackstat(Chan * c,uchar * db,int n)2619a747e4fSDavid du Colombier loopbackstat(Chan *c, uchar *db, int n)
26259cc4ca5SDavid du Colombier {
2639a747e4fSDavid du Colombier return devstat(c, db, n, nil, 0, loopbackgen);
26459cc4ca5SDavid du Colombier }
26559cc4ca5SDavid du Colombier
26659cc4ca5SDavid du Colombier /*
26759cc4ca5SDavid du Colombier * if the stream doesn't exist, create it
26859cc4ca5SDavid du Colombier */
26959cc4ca5SDavid du Colombier static Chan*
loopbackopen(Chan * c,int omode)27059cc4ca5SDavid du Colombier loopbackopen(Chan *c, int omode)
27159cc4ca5SDavid du Colombier {
27259cc4ca5SDavid du Colombier Loop *lb;
27359cc4ca5SDavid du Colombier
2749a747e4fSDavid du Colombier if(c->qid.type & QTDIR){
27559cc4ca5SDavid du Colombier if(omode != OREAD)
27659cc4ca5SDavid du Colombier error(Ebadarg);
27759cc4ca5SDavid du Colombier c->mode = omode;
27859cc4ca5SDavid du Colombier c->flag |= COPEN;
27959cc4ca5SDavid du Colombier c->offset = 0;
28059cc4ca5SDavid du Colombier return c;
28159cc4ca5SDavid du Colombier }
28259cc4ca5SDavid du Colombier
28359cc4ca5SDavid du Colombier lb = c->aux;
28459cc4ca5SDavid du Colombier qlock(lb);
28580ee5cbfSDavid du Colombier if(TYPE(c->qid.path) == Qdata){
28680ee5cbfSDavid du Colombier if(lb->link[ID(c->qid.path)].ref){
28780ee5cbfSDavid du Colombier qunlock(lb);
28880ee5cbfSDavid du Colombier error(Einuse);
28980ee5cbfSDavid du Colombier }
29059cc4ca5SDavid du Colombier lb->link[ID(c->qid.path)].ref++;
29180ee5cbfSDavid du Colombier }
29259cc4ca5SDavid du Colombier qunlock(lb);
29359cc4ca5SDavid du Colombier
29459cc4ca5SDavid du Colombier c->mode = openmode(omode);
29559cc4ca5SDavid du Colombier c->flag |= COPEN;
29659cc4ca5SDavid du Colombier c->offset = 0;
2979a747e4fSDavid du Colombier c->iounit = qiomaxatomic;
29859cc4ca5SDavid du Colombier return c;
29959cc4ca5SDavid du Colombier }
30059cc4ca5SDavid du Colombier
30159cc4ca5SDavid du Colombier static void
loopbackclose(Chan * c)30259cc4ca5SDavid du Colombier loopbackclose(Chan *c)
30359cc4ca5SDavid du Colombier {
30459cc4ca5SDavid du Colombier Loop *lb;
30559cc4ca5SDavid du Colombier int ref, chan;
30659cc4ca5SDavid du Colombier
30759cc4ca5SDavid du Colombier lb = c->aux;
30859cc4ca5SDavid du Colombier
30959cc4ca5SDavid du Colombier qlock(lb);
31059cc4ca5SDavid du Colombier
31159cc4ca5SDavid du Colombier /*
31259cc4ca5SDavid du Colombier * closing either side hangs up the stream
31359cc4ca5SDavid du Colombier */
31459cc4ca5SDavid du Colombier if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){
31559cc4ca5SDavid du Colombier chan = ID(c->qid.path);
31659cc4ca5SDavid du Colombier if(--lb->link[chan].ref == 0){
31759cc4ca5SDavid du Colombier qhangup(lb->link[chan ^ 1].oq, nil);
31859cc4ca5SDavid du Colombier looper(lb);
31959cc4ca5SDavid du Colombier }
32059cc4ca5SDavid du Colombier }
32159cc4ca5SDavid du Colombier
32259cc4ca5SDavid du Colombier
32359cc4ca5SDavid du Colombier /*
32459cc4ca5SDavid du Colombier * if both sides are closed, they are reusable
32559cc4ca5SDavid du Colombier */
32659cc4ca5SDavid du Colombier if(lb->link[0].ref == 0 && lb->link[1].ref == 0){
32759cc4ca5SDavid du Colombier for(chan = 0; chan < 2; chan++){
32859cc4ca5SDavid du Colombier closelink(&lb->link[chan], 0);
32959cc4ca5SDavid du Colombier qreopen(lb->link[chan].iq);
33059cc4ca5SDavid du Colombier qreopen(lb->link[chan].oq);
33159cc4ca5SDavid du Colombier qsetlimit(lb->link[chan].oq, lb->link[chan].limit);
33259cc4ca5SDavid du Colombier qsetlimit(lb->link[chan].iq, lb->link[chan].limit);
33359cc4ca5SDavid du Colombier }
33459cc4ca5SDavid du Colombier }
33559cc4ca5SDavid du Colombier ref = --lb->ref;
33659cc4ca5SDavid du Colombier if(ref == 0)
33759cc4ca5SDavid du Colombier freelb(lb);
33859cc4ca5SDavid du Colombier qunlock(lb);
33959cc4ca5SDavid du Colombier }
34059cc4ca5SDavid du Colombier
34159cc4ca5SDavid du Colombier static void
freelb(Loop * lb)34259cc4ca5SDavid du Colombier freelb(Loop *lb)
34359cc4ca5SDavid du Colombier {
34459cc4ca5SDavid du Colombier int chan;
34559cc4ca5SDavid du Colombier
34659cc4ca5SDavid du Colombier for(chan = 0; chan < 2; chan++)
34759cc4ca5SDavid du Colombier closelink(&lb->link[chan], 1);
34859cc4ca5SDavid du Colombier }
34959cc4ca5SDavid du Colombier
35059cc4ca5SDavid du Colombier /*
35159cc4ca5SDavid du Colombier * called with the Loop qlocked,
35259cc4ca5SDavid du Colombier * so only pushlink can mess with the queues
35359cc4ca5SDavid du Colombier */
35459cc4ca5SDavid du Colombier static void
closelink(Link * link,int dofree)35559cc4ca5SDavid du Colombier closelink(Link *link, int dofree)
35659cc4ca5SDavid du Colombier {
35759cc4ca5SDavid du Colombier Queue *iq, *oq;
35859cc4ca5SDavid du Colombier Block *bp;
35959cc4ca5SDavid du Colombier
36059cc4ca5SDavid du Colombier ilock(link);
36159cc4ca5SDavid du Colombier iq = link->iq;
36259cc4ca5SDavid du Colombier oq = link->oq;
36359cc4ca5SDavid du Colombier bp = link->tq;
36459cc4ca5SDavid du Colombier link->tq = nil;
36559cc4ca5SDavid du Colombier link->tqtail = nil;
36659cc4ca5SDavid du Colombier link->tout = 0;
36759cc4ca5SDavid du Colombier link->tin = 0;
3689a747e4fSDavid du Colombier timerdel(&link->ci);
36959cc4ca5SDavid du Colombier iunlock(link);
37059cc4ca5SDavid du Colombier if(iq != nil){
37159cc4ca5SDavid du Colombier qclose(iq);
37259cc4ca5SDavid du Colombier if(dofree){
37359cc4ca5SDavid du Colombier ilock(link);
37459cc4ca5SDavid du Colombier free(iq);
37559cc4ca5SDavid du Colombier link->iq = nil;
37659cc4ca5SDavid du Colombier iunlock(link);
37759cc4ca5SDavid du Colombier }
37859cc4ca5SDavid du Colombier }
37959cc4ca5SDavid du Colombier if(oq != nil){
38059cc4ca5SDavid du Colombier qclose(oq);
38159cc4ca5SDavid du Colombier if(dofree){
38259cc4ca5SDavid du Colombier ilock(link);
38359cc4ca5SDavid du Colombier free(oq);
38459cc4ca5SDavid du Colombier link->oq = nil;
38559cc4ca5SDavid du Colombier iunlock(link);
38659cc4ca5SDavid du Colombier }
38759cc4ca5SDavid du Colombier }
38859cc4ca5SDavid du Colombier freeblist(bp);
38959cc4ca5SDavid du Colombier }
39059cc4ca5SDavid du Colombier
39159cc4ca5SDavid du Colombier static long
loopbackread(Chan * c,void * va,long n,vlong offset)39259cc4ca5SDavid du Colombier loopbackread(Chan *c, void *va, long n, vlong offset)
39359cc4ca5SDavid du Colombier {
39459cc4ca5SDavid du Colombier Loop *lb;
39559cc4ca5SDavid du Colombier Link *link;
39659cc4ca5SDavid du Colombier char *buf;
39759cc4ca5SDavid du Colombier long rv;
39859cc4ca5SDavid du Colombier
39959cc4ca5SDavid du Colombier lb = c->aux;
40059cc4ca5SDavid du Colombier switch(TYPE(c->qid.path)){
40159cc4ca5SDavid du Colombier default:
40259cc4ca5SDavid du Colombier error(Eperm);
40359cc4ca5SDavid du Colombier return -1; /* not reached */
40459cc4ca5SDavid du Colombier case Qtopdir:
40559cc4ca5SDavid du Colombier case Qloopdir:
40659cc4ca5SDavid du Colombier case Qportdir:
40759cc4ca5SDavid du Colombier return devdirread(c, va, n, nil, 0, loopbackgen);
40859cc4ca5SDavid du Colombier case Qdata:
40959cc4ca5SDavid du Colombier return qread(lb->link[ID(c->qid.path)].iq, va, n);
41059cc4ca5SDavid du Colombier case Qstatus:
41159cc4ca5SDavid du Colombier link = &lb->link[ID(c->qid.path)];
41259cc4ca5SDavid du Colombier buf = smalloc(Statelen);
41380ee5cbfSDavid du Colombier rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns);
41459cc4ca5SDavid du Colombier rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit);
41559cc4ca5SDavid du Colombier rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop);
41659cc4ca5SDavid du Colombier snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate);
41759cc4ca5SDavid du Colombier rv = readstr(offset, va, n, buf);
41859cc4ca5SDavid du Colombier free(buf);
41959cc4ca5SDavid du Colombier break;
42059cc4ca5SDavid du Colombier case Qstats:
42159cc4ca5SDavid du Colombier link = &lb->link[ID(c->qid.path)];
42259cc4ca5SDavid du Colombier buf = smalloc(Statelen);
42359cc4ca5SDavid du Colombier rv = snprint(buf, Statelen, "packets: %ld\n", link->packets);
42459cc4ca5SDavid du Colombier rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes);
42559cc4ca5SDavid du Colombier rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops);
42659cc4ca5SDavid du Colombier snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows);
42759cc4ca5SDavid du Colombier rv = readstr(offset, va, n, buf);
42859cc4ca5SDavid du Colombier free(buf);
42959cc4ca5SDavid du Colombier break;
43059cc4ca5SDavid du Colombier }
43159cc4ca5SDavid du Colombier return rv;
43259cc4ca5SDavid du Colombier }
43359cc4ca5SDavid du Colombier
43459cc4ca5SDavid du Colombier static Block*
loopbackbread(Chan * c,long n,ulong offset)43559cc4ca5SDavid du Colombier loopbackbread(Chan *c, long n, ulong offset)
43659cc4ca5SDavid du Colombier {
43759cc4ca5SDavid du Colombier Loop *lb;
43859cc4ca5SDavid du Colombier
43959cc4ca5SDavid du Colombier lb = c->aux;
44059cc4ca5SDavid du Colombier if(TYPE(c->qid.path) == Qdata)
44159cc4ca5SDavid du Colombier return qbread(lb->link[ID(c->qid.path)].iq, n);
44259cc4ca5SDavid du Colombier
44359cc4ca5SDavid du Colombier return devbread(c, n, offset);
44459cc4ca5SDavid du Colombier }
44559cc4ca5SDavid du Colombier
44659cc4ca5SDavid du Colombier static long
loopbackbwrite(Chan * c,Block * bp,ulong off)44759cc4ca5SDavid du Colombier loopbackbwrite(Chan *c, Block *bp, ulong off)
44859cc4ca5SDavid du Colombier {
44959cc4ca5SDavid du Colombier Loop *lb;
45059cc4ca5SDavid du Colombier
45159cc4ca5SDavid du Colombier lb = c->aux;
45259cc4ca5SDavid du Colombier if(TYPE(c->qid.path) == Qdata)
45359cc4ca5SDavid du Colombier return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp);
45459cc4ca5SDavid du Colombier return devbwrite(c, bp, off);
45559cc4ca5SDavid du Colombier }
45659cc4ca5SDavid du Colombier
45759cc4ca5SDavid du Colombier static long
loopbackwrite(Chan * c,void * va,long n,vlong off)45859cc4ca5SDavid du Colombier loopbackwrite(Chan *c, void *va, long n, vlong off)
45959cc4ca5SDavid du Colombier {
46059cc4ca5SDavid du Colombier Loop *lb;
46159cc4ca5SDavid du Colombier Link *link;
4629a747e4fSDavid du Colombier Cmdbuf *volatile cb;
4639a747e4fSDavid du Colombier Block *volatile bp;
46480ee5cbfSDavid du Colombier vlong d0, d0ns;
46580ee5cbfSDavid du Colombier long dn, dnns;
46659cc4ca5SDavid du Colombier
46759cc4ca5SDavid du Colombier switch(TYPE(c->qid.path)){
46859cc4ca5SDavid du Colombier case Qdata:
46959cc4ca5SDavid du Colombier bp = allocb(n);
47059cc4ca5SDavid du Colombier if(waserror()){
47159cc4ca5SDavid du Colombier freeb(bp);
47259cc4ca5SDavid du Colombier nexterror();
47359cc4ca5SDavid du Colombier }
47459cc4ca5SDavid du Colombier memmove(bp->wp, va, n);
47559cc4ca5SDavid du Colombier poperror();
47659cc4ca5SDavid du Colombier bp->wp += n;
47759cc4ca5SDavid du Colombier return loopbackbwrite(c, bp, off);
47859cc4ca5SDavid du Colombier case Qctl:
47959cc4ca5SDavid du Colombier lb = c->aux;
48059cc4ca5SDavid du Colombier link = &lb->link[ID(c->qid.path)];
48159cc4ca5SDavid du Colombier cb = parsecmd(va, n);
4829a747e4fSDavid du Colombier if(waserror()){
4839a747e4fSDavid du Colombier free(cb);
4849a747e4fSDavid du Colombier nexterror();
4859a747e4fSDavid du Colombier }
48659cc4ca5SDavid du Colombier if(cb->nf < 1)
48759cc4ca5SDavid du Colombier error("short control request");
48859cc4ca5SDavid du Colombier if(strcmp(cb->f[0], "delay") == 0){
48959cc4ca5SDavid du Colombier if(cb->nf != 3)
49059cc4ca5SDavid du Colombier error("usage: delay latency bytedelay");
49180ee5cbfSDavid du Colombier d0ns = strtoll(cb->f[1], nil, 10);
49259cc4ca5SDavid du Colombier dnns = strtol(cb->f[2], nil, 10);
49359cc4ca5SDavid du Colombier
49459cc4ca5SDavid du Colombier /*
49559cc4ca5SDavid du Colombier * it takes about 20000 cycles on a pentium ii
49659cc4ca5SDavid du Colombier * to run pushlink; perhaps this should be accounted.
49759cc4ca5SDavid du Colombier */
49859cc4ca5SDavid du Colombier
49959cc4ca5SDavid du Colombier ilock(link);
50059cc4ca5SDavid du Colombier link->delay0ns = d0ns;
50159cc4ca5SDavid du Colombier link->delaynns = dnns;
50259cc4ca5SDavid du Colombier iunlock(link);
50359cc4ca5SDavid du Colombier }else if(strcmp(cb->f[0], "indrop") == 0){
50459cc4ca5SDavid du Colombier if(cb->nf != 2)
50559cc4ca5SDavid du Colombier error("usage: indrop [01]");
50659cc4ca5SDavid du Colombier ilock(link);
50759cc4ca5SDavid du Colombier link->indrop = strtol(cb->f[1], nil, 0) != 0;
50859cc4ca5SDavid du Colombier iunlock(link);
50959cc4ca5SDavid du Colombier }else if(strcmp(cb->f[0], "droprate") == 0){
51059cc4ca5SDavid du Colombier if(cb->nf != 2)
51159cc4ca5SDavid du Colombier error("usage: droprate ofn");
51259cc4ca5SDavid du Colombier ilock(link);
51359cc4ca5SDavid du Colombier link->droprate = strtol(cb->f[1], nil, 0);
51459cc4ca5SDavid du Colombier iunlock(link);
51559cc4ca5SDavid du Colombier }else if(strcmp(cb->f[0], "limit") == 0){
51659cc4ca5SDavid du Colombier if(cb->nf != 2)
51780ee5cbfSDavid du Colombier error("usage: limit maxqsize");
51859cc4ca5SDavid du Colombier ilock(link);
51959cc4ca5SDavid du Colombier link->limit = strtol(cb->f[1], nil, 0);
52059cc4ca5SDavid du Colombier qsetlimit(link->oq, link->limit);
52159cc4ca5SDavid du Colombier qsetlimit(link->iq, link->limit);
52259cc4ca5SDavid du Colombier iunlock(link);
52359cc4ca5SDavid du Colombier }else if(strcmp(cb->f[0], "reset") == 0){
52459cc4ca5SDavid du Colombier if(cb->nf != 1)
52559cc4ca5SDavid du Colombier error("usage: reset");
52659cc4ca5SDavid du Colombier ilock(link);
52759cc4ca5SDavid du Colombier link->packets = 0;
52859cc4ca5SDavid du Colombier link->bytes = 0;
52959cc4ca5SDavid du Colombier link->indrop = 0;
53059cc4ca5SDavid du Colombier link->soverflows = 0;
53159cc4ca5SDavid du Colombier link->drops = 0;
53259cc4ca5SDavid du Colombier iunlock(link);
53359cc4ca5SDavid du Colombier }else
53459cc4ca5SDavid du Colombier error("unknown control request");
5359a747e4fSDavid du Colombier poperror();
5369a747e4fSDavid du Colombier free(cb);
53759cc4ca5SDavid du Colombier break;
53859cc4ca5SDavid du Colombier default:
53959cc4ca5SDavid du Colombier error(Eperm);
54059cc4ca5SDavid du Colombier }
54159cc4ca5SDavid du Colombier
54259cc4ca5SDavid du Colombier return n;
54359cc4ca5SDavid du Colombier }
54459cc4ca5SDavid du Colombier
54559cc4ca5SDavid du Colombier static long
loopoput(Loop * lb,Link * link,Block * volatile bp)5469a747e4fSDavid du Colombier loopoput(Loop *lb, Link *link, Block *volatile bp)
54759cc4ca5SDavid du Colombier {
54859cc4ca5SDavid du Colombier long n;
54959cc4ca5SDavid du Colombier
55059cc4ca5SDavid du Colombier n = BLEN(bp);
55159cc4ca5SDavid du Colombier
55259cc4ca5SDavid du Colombier /* make it a single block with space for the loopback timing header */
5539a747e4fSDavid du Colombier if(waserror()){
5549a747e4fSDavid du Colombier freeb(bp);
5559a747e4fSDavid du Colombier nexterror();
5569a747e4fSDavid du Colombier }
55759cc4ca5SDavid du Colombier bp = padblock(bp, Tmsize);
55859cc4ca5SDavid du Colombier if(bp->next)
55959cc4ca5SDavid du Colombier bp = concatblock(bp);
56059cc4ca5SDavid du Colombier if(BLEN(bp) < lb->minmtu)
56159cc4ca5SDavid du Colombier bp = adjustblock(bp, lb->minmtu);
5629a747e4fSDavid du Colombier poperror();
563e288d156SDavid du Colombier ptime(bp->rp, todget(nil));
56459cc4ca5SDavid du Colombier
56559cc4ca5SDavid du Colombier link->packets++;
56659cc4ca5SDavid du Colombier link->bytes += n;
56759cc4ca5SDavid du Colombier
56859cc4ca5SDavid du Colombier qbwrite(link->oq, bp);
56959cc4ca5SDavid du Colombier
57059cc4ca5SDavid du Colombier looper(lb);
57159cc4ca5SDavid du Colombier return n;
57259cc4ca5SDavid du Colombier }
57359cc4ca5SDavid du Colombier
57459cc4ca5SDavid du Colombier static void
looper(Loop * lb)57559cc4ca5SDavid du Colombier looper(Loop *lb)
57659cc4ca5SDavid du Colombier {
57759cc4ca5SDavid du Colombier vlong t;
57859cc4ca5SDavid du Colombier int chan;
57959cc4ca5SDavid du Colombier
580e288d156SDavid du Colombier t = todget(nil);
58159cc4ca5SDavid du Colombier for(chan = 0; chan < 2; chan++)
58259cc4ca5SDavid du Colombier pushlink(&lb->link[chan], t);
58359cc4ca5SDavid du Colombier }
58459cc4ca5SDavid du Colombier
58559cc4ca5SDavid du Colombier static void
linkintr(Ureg *,Timer * ci)5869a747e4fSDavid du Colombier linkintr(Ureg*, Timer *ci)
58759cc4ca5SDavid du Colombier {
58859cc4ca5SDavid du Colombier Link *link;
58959cc4ca5SDavid du Colombier
59059cc4ca5SDavid du Colombier link = ci->a;
591e288d156SDavid du Colombier pushlink(link, ci->ns);
59259cc4ca5SDavid du Colombier }
59359cc4ca5SDavid du Colombier
59459cc4ca5SDavid du Colombier /*
59559cc4ca5SDavid du Colombier * move blocks between queues if they are ready.
59659cc4ca5SDavid du Colombier * schedule an interrupt for the next interesting time.
59759cc4ca5SDavid du Colombier *
59859cc4ca5SDavid du Colombier * must be called with the link ilocked.
59959cc4ca5SDavid du Colombier */
60059cc4ca5SDavid du Colombier static void
pushlink(Link * link,vlong now)60159cc4ca5SDavid du Colombier pushlink(Link *link, vlong now)
60259cc4ca5SDavid du Colombier {
60359cc4ca5SDavid du Colombier Block *bp;
60459cc4ca5SDavid du Colombier vlong tout, tin;
60559cc4ca5SDavid du Colombier
60659cc4ca5SDavid du Colombier /*
60759cc4ca5SDavid du Colombier * put another block in the link queue
60859cc4ca5SDavid du Colombier */
60959cc4ca5SDavid du Colombier ilock(link);
61059cc4ca5SDavid du Colombier if(link->iq == nil || link->oq == nil){
61159cc4ca5SDavid du Colombier iunlock(link);
61259cc4ca5SDavid du Colombier return;
61359cc4ca5SDavid du Colombier
61459cc4ca5SDavid du Colombier }
6159a747e4fSDavid du Colombier timerdel(&link->ci);
61659cc4ca5SDavid du Colombier
61759cc4ca5SDavid du Colombier /*
61859cc4ca5SDavid du Colombier * put more blocks into the xmit queue
61959cc4ca5SDavid du Colombier * use the time the last packet was supposed to go out
62059cc4ca5SDavid du Colombier * as the start time for the next packet, rather than
62159cc4ca5SDavid du Colombier * the current time. this more closely models a network
62259cc4ca5SDavid du Colombier * device which can queue multiple output packets.
62359cc4ca5SDavid du Colombier */
62459cc4ca5SDavid du Colombier tout = link->tout;
62559cc4ca5SDavid du Colombier if(!tout)
62659cc4ca5SDavid du Colombier tout = now;
62759cc4ca5SDavid du Colombier while(tout <= now){
62859cc4ca5SDavid du Colombier bp = qget(link->oq);
62959cc4ca5SDavid du Colombier if(bp == nil){
63059cc4ca5SDavid du Colombier tout = 0;
63159cc4ca5SDavid du Colombier break;
63259cc4ca5SDavid du Colombier }
63359cc4ca5SDavid du Colombier
63459cc4ca5SDavid du Colombier /*
63559cc4ca5SDavid du Colombier * can't send the packet before it gets queued
63659cc4ca5SDavid du Colombier */
63759cc4ca5SDavid du Colombier tin = gtime(bp->rp);
63859cc4ca5SDavid du Colombier if(tin > tout)
63959cc4ca5SDavid du Colombier tout = tin;
64059cc4ca5SDavid du Colombier tout = tout + (BLEN(bp) - Tmsize) * link->delayn;
64159cc4ca5SDavid du Colombier
64259cc4ca5SDavid du Colombier /*
64359cc4ca5SDavid du Colombier * drop packets
64459cc4ca5SDavid du Colombier */
64559cc4ca5SDavid du Colombier if(link->droprate && nrand(link->droprate) == 0)
64659cc4ca5SDavid du Colombier link->drops++;
64759cc4ca5SDavid du Colombier else{
648e288d156SDavid du Colombier ptime(bp->rp, tout + link->delay0ns);
64959cc4ca5SDavid du Colombier if(link->tq == nil)
65059cc4ca5SDavid du Colombier link->tq = bp;
65159cc4ca5SDavid du Colombier else
65259cc4ca5SDavid du Colombier link->tqtail->next = bp;
65359cc4ca5SDavid du Colombier link->tqtail = bp;
65459cc4ca5SDavid du Colombier }
65559cc4ca5SDavid du Colombier }
65659cc4ca5SDavid du Colombier
65759cc4ca5SDavid du Colombier /*
65859cc4ca5SDavid du Colombier * record the next time a packet can be sent,
65959cc4ca5SDavid du Colombier * but don't schedule an interrupt if none is waiting
66059cc4ca5SDavid du Colombier */
66159cc4ca5SDavid du Colombier link->tout = tout;
66259cc4ca5SDavid du Colombier if(!qcanread(link->oq))
66359cc4ca5SDavid du Colombier tout = 0;
66459cc4ca5SDavid du Colombier
66559cc4ca5SDavid du Colombier /*
66659cc4ca5SDavid du Colombier * put more blocks into the receive queue
66759cc4ca5SDavid du Colombier */
66859cc4ca5SDavid du Colombier tin = 0;
66959cc4ca5SDavid du Colombier while(bp = link->tq){
67059cc4ca5SDavid du Colombier tin = gtime(bp->rp);
67159cc4ca5SDavid du Colombier if(tin > now)
67259cc4ca5SDavid du Colombier break;
67359cc4ca5SDavid du Colombier bp->rp += Tmsize;
67459cc4ca5SDavid du Colombier link->tq = bp->next;
67559cc4ca5SDavid du Colombier bp->next = nil;
67659cc4ca5SDavid du Colombier if(!link->indrop)
67759cc4ca5SDavid du Colombier qpassnolim(link->iq, bp);
67859cc4ca5SDavid du Colombier else if(qpass(link->iq, bp) < 0)
67959cc4ca5SDavid du Colombier link->soverflows++;
68059cc4ca5SDavid du Colombier tin = 0;
68159cc4ca5SDavid du Colombier }
68259cc4ca5SDavid du Colombier if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq))
68359cc4ca5SDavid du Colombier qhangup(link->iq, nil);
68459cc4ca5SDavid du Colombier link->tin = tin;
68559cc4ca5SDavid du Colombier if(!tin || tin > tout && tout)
68659cc4ca5SDavid du Colombier tin = tout;
68759cc4ca5SDavid du Colombier
688*2cca75a1SDavid du Colombier link->ci.ns = tin - now;
68959cc4ca5SDavid du Colombier if(tin){
69059cc4ca5SDavid du Colombier if(tin < now)
69159cc4ca5SDavid du Colombier panic("loopback unfinished business");
6929a747e4fSDavid du Colombier timeradd(&link->ci);
69359cc4ca5SDavid du Colombier }
69459cc4ca5SDavid du Colombier iunlock(link);
69559cc4ca5SDavid du Colombier }
69659cc4ca5SDavid du Colombier
69759cc4ca5SDavid du Colombier static void
ptime(uchar * p,vlong t)69859cc4ca5SDavid du Colombier ptime(uchar *p, vlong t)
69959cc4ca5SDavid du Colombier {
70059cc4ca5SDavid du Colombier ulong tt;
70159cc4ca5SDavid du Colombier
70259cc4ca5SDavid du Colombier tt = t >> 32;
70359cc4ca5SDavid du Colombier p[0] = tt >> 24;
70459cc4ca5SDavid du Colombier p[1] = tt >> 16;
70559cc4ca5SDavid du Colombier p[2] = tt >> 8;
70659cc4ca5SDavid du Colombier p[3] = tt;
70759cc4ca5SDavid du Colombier tt = t;
70859cc4ca5SDavid du Colombier p[4] = tt >> 24;
70959cc4ca5SDavid du Colombier p[5] = tt >> 16;
71059cc4ca5SDavid du Colombier p[6] = tt >> 8;
71159cc4ca5SDavid du Colombier p[7] = tt;
71259cc4ca5SDavid du Colombier }
71359cc4ca5SDavid du Colombier
71459cc4ca5SDavid du Colombier static vlong
gtime(uchar * p)71559cc4ca5SDavid du Colombier gtime(uchar *p)
71659cc4ca5SDavid du Colombier {
71759cc4ca5SDavid du Colombier ulong t1, t2;
71859cc4ca5SDavid du Colombier
71959cc4ca5SDavid du Colombier t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
72059cc4ca5SDavid du Colombier t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
72159cc4ca5SDavid du Colombier return ((vlong)t1 << 32) | t2;
72259cc4ca5SDavid du Colombier }
72359cc4ca5SDavid du Colombier
72459cc4ca5SDavid du Colombier Dev loopbackdevtab = {
72559cc4ca5SDavid du Colombier 'X',
72659cc4ca5SDavid du Colombier "loopback",
72759cc4ca5SDavid du Colombier
72859cc4ca5SDavid du Colombier devreset,
72959cc4ca5SDavid du Colombier loopbackinit,
7309a747e4fSDavid du Colombier devshutdown,
73159cc4ca5SDavid du Colombier loopbackattach,
73259cc4ca5SDavid du Colombier loopbackwalk,
73359cc4ca5SDavid du Colombier loopbackstat,
73459cc4ca5SDavid du Colombier loopbackopen,
73559cc4ca5SDavid du Colombier devcreate,
73659cc4ca5SDavid du Colombier loopbackclose,
73759cc4ca5SDavid du Colombier loopbackread,
73859cc4ca5SDavid du Colombier loopbackbread,
73959cc4ca5SDavid du Colombier loopbackwrite,
74059cc4ca5SDavid du Colombier loopbackbwrite,
74159cc4ca5SDavid du Colombier devremove,
74259cc4ca5SDavid du Colombier devwstat,
74359cc4ca5SDavid du Colombier };
744