xref: /inferno-os/os/port/devloopback.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1*74a4d8c2SCharles.Forsyth #include	"u.h"
2*74a4d8c2SCharles.Forsyth #include	"../port/lib.h"
3*74a4d8c2SCharles.Forsyth #include	"mem.h"
4*74a4d8c2SCharles.Forsyth #include	"dat.h"
5*74a4d8c2SCharles.Forsyth #include	"fns.h"
6*74a4d8c2SCharles.Forsyth #include	"../port/error.h"
7*74a4d8c2SCharles.Forsyth 
8*74a4d8c2SCharles.Forsyth typedef struct Link	Link;
9*74a4d8c2SCharles.Forsyth typedef struct Loop	Loop;
10*74a4d8c2SCharles.Forsyth 
11*74a4d8c2SCharles.Forsyth struct Link
12*74a4d8c2SCharles.Forsyth {
13*74a4d8c2SCharles.Forsyth 	Lock;
14*74a4d8c2SCharles.Forsyth 
15*74a4d8c2SCharles.Forsyth 	int	ref;
16*74a4d8c2SCharles.Forsyth 
17*74a4d8c2SCharles.Forsyth 	long	packets;	/* total number of packets sent */
18*74a4d8c2SCharles.Forsyth 	long	bytes;		/* total number of bytes sent */
19*74a4d8c2SCharles.Forsyth 	int	indrop;		/* enable dropping on iq overflow */
20*74a4d8c2SCharles.Forsyth 	long	soverflows;	/* packets dropped because iq overflowed */
21*74a4d8c2SCharles.Forsyth 	long	droprate;	/* drop 1/droprate packets in tq */
22*74a4d8c2SCharles.Forsyth 	long	drops;		/* packets deliberately dropped */
23*74a4d8c2SCharles.Forsyth 
24*74a4d8c2SCharles.Forsyth 	vlong	delay0ns;	/* nanosec of delay in the link */
25*74a4d8c2SCharles.Forsyth 	long	delaynns;	/* nanosec of delay per byte */
26*74a4d8c2SCharles.Forsyth 
27*74a4d8c2SCharles.Forsyth 	Block	*tq;		/* transmission queue */
28*74a4d8c2SCharles.Forsyth 	Block	*tqtail;
29*74a4d8c2SCharles.Forsyth 	vlong	tout;		/* time the last packet in tq is really out */
30*74a4d8c2SCharles.Forsyth 	vlong	tin;		/* time the head packet in tq enters the remote side  */
31*74a4d8c2SCharles.Forsyth 
32*74a4d8c2SCharles.Forsyth 	long	limit;		/* queue buffering limit */
33*74a4d8c2SCharles.Forsyth 	Queue	*oq;		/* output queue from other side & packets in the link */
34*74a4d8c2SCharles.Forsyth 	Queue	*iq;
35*74a4d8c2SCharles.Forsyth 
36*74a4d8c2SCharles.Forsyth 	Timer	ci;		/* time to move packets from  next packet from oq */
37*74a4d8c2SCharles.Forsyth };
38*74a4d8c2SCharles.Forsyth 
39*74a4d8c2SCharles.Forsyth struct Loop
40*74a4d8c2SCharles.Forsyth {
41*74a4d8c2SCharles.Forsyth 	QLock;
42*74a4d8c2SCharles.Forsyth 	int	ref;
43*74a4d8c2SCharles.Forsyth 	int	minmtu;		/* smallest block transmittable */
44*74a4d8c2SCharles.Forsyth 	Loop	*next;
45*74a4d8c2SCharles.Forsyth 	ulong	path;
46*74a4d8c2SCharles.Forsyth 	Link	link[2];
47*74a4d8c2SCharles.Forsyth };
48*74a4d8c2SCharles.Forsyth 
49*74a4d8c2SCharles.Forsyth static struct
50*74a4d8c2SCharles.Forsyth {
51*74a4d8c2SCharles.Forsyth 	Lock;
52*74a4d8c2SCharles.Forsyth 	ulong	path;
53*74a4d8c2SCharles.Forsyth } loopbackalloc;
54*74a4d8c2SCharles.Forsyth 
55*74a4d8c2SCharles.Forsyth enum
56*74a4d8c2SCharles.Forsyth {
57*74a4d8c2SCharles.Forsyth 	Qtopdir=	1,		/* top level directory */
58*74a4d8c2SCharles.Forsyth 
59*74a4d8c2SCharles.Forsyth 	Qloopdir,			/* loopback* directory */
60*74a4d8c2SCharles.Forsyth 
61*74a4d8c2SCharles.Forsyth 	Qportdir,			/* directory each end of the loop */
62*74a4d8c2SCharles.Forsyth 	Qctl,
63*74a4d8c2SCharles.Forsyth 	Qstatus,
64*74a4d8c2SCharles.Forsyth 	Qstats,
65*74a4d8c2SCharles.Forsyth 	Qdata,
66*74a4d8c2SCharles.Forsyth 
67*74a4d8c2SCharles.Forsyth 	MaxQ,
68*74a4d8c2SCharles.Forsyth 
69*74a4d8c2SCharles.Forsyth 	Nloopbacks	= 5,
70*74a4d8c2SCharles.Forsyth 
71*74a4d8c2SCharles.Forsyth 	Statelen	= 23*1024,	/* status buffer size */
72*74a4d8c2SCharles.Forsyth 
73*74a4d8c2SCharles.Forsyth 	Tmsize		= 8,
74*74a4d8c2SCharles.Forsyth 	Delayn 		= 10000,	/* default delays in ns */
75*74a4d8c2SCharles.Forsyth 	Delay0 		= 2500000,
76*74a4d8c2SCharles.Forsyth 
77*74a4d8c2SCharles.Forsyth 	Loopqlim	= 32*1024,	/* default size of queues */
78*74a4d8c2SCharles.Forsyth };
79*74a4d8c2SCharles.Forsyth 
80*74a4d8c2SCharles.Forsyth static Dirtab loopportdir[] =
81*74a4d8c2SCharles.Forsyth {
82*74a4d8c2SCharles.Forsyth 	"ctl",		{Qctl},		0,			0222,
83*74a4d8c2SCharles.Forsyth 	"status",	{Qstatus},	0,			0444,
84*74a4d8c2SCharles.Forsyth 	"stats",	{Qstats},	0,			0444,
85*74a4d8c2SCharles.Forsyth 	"data",		{Qdata},	0,			0666,
86*74a4d8c2SCharles.Forsyth };
87*74a4d8c2SCharles.Forsyth static Dirtab loopdirs[MaxQ];
88*74a4d8c2SCharles.Forsyth 
89*74a4d8c2SCharles.Forsyth static Loop	loopbacks[Nloopbacks];
90*74a4d8c2SCharles.Forsyth 
91*74a4d8c2SCharles.Forsyth #define TYPE(x) 	(((ulong)(x))&0xff)
92*74a4d8c2SCharles.Forsyth #define ID(x) 		(((ulong)(x))>>8)
93*74a4d8c2SCharles.Forsyth #define QID(x,y) 	((((ulong)(x))<<8)|((ulong)(y)))
94*74a4d8c2SCharles.Forsyth 
95*74a4d8c2SCharles.Forsyth static void	looper(Loop *lb);
96*74a4d8c2SCharles.Forsyth static long	loopoput(Loop *lb, Link *link, Block *bp);
97*74a4d8c2SCharles.Forsyth static void	ptime(uchar *p, vlong t);
98*74a4d8c2SCharles.Forsyth static vlong	gtime(uchar *p);
99*74a4d8c2SCharles.Forsyth static void	closelink(Link *link, int dofree);
100*74a4d8c2SCharles.Forsyth static void	pushlink(Link *link, vlong now);
101*74a4d8c2SCharles.Forsyth static void	freelb(Loop *lb);
102*74a4d8c2SCharles.Forsyth static void	linkintr(Ureg*, Timer *ci);
103*74a4d8c2SCharles.Forsyth 
104*74a4d8c2SCharles.Forsyth static void
loopbackinit(void)105*74a4d8c2SCharles.Forsyth loopbackinit(void)
106*74a4d8c2SCharles.Forsyth {
107*74a4d8c2SCharles.Forsyth 	int i;
108*74a4d8c2SCharles.Forsyth 
109*74a4d8c2SCharles.Forsyth 	for(i = 0; i < Nloopbacks; i++)
110*74a4d8c2SCharles.Forsyth 		loopbacks[i].path = i;
111*74a4d8c2SCharles.Forsyth 
112*74a4d8c2SCharles.Forsyth 	/* invert directory tables for non-directory entries */
113*74a4d8c2SCharles.Forsyth 	for(i=0; i<nelem(loopportdir); i++)
114*74a4d8c2SCharles.Forsyth 		loopdirs[loopportdir[i].qid.path] = loopportdir[i];
115*74a4d8c2SCharles.Forsyth }
116*74a4d8c2SCharles.Forsyth 
117*74a4d8c2SCharles.Forsyth static Chan*
loopbackattach(char * spec)118*74a4d8c2SCharles.Forsyth loopbackattach(char *spec)
119*74a4d8c2SCharles.Forsyth {
120*74a4d8c2SCharles.Forsyth 	Loop *volatile lb;
121*74a4d8c2SCharles.Forsyth 	Queue *q;
122*74a4d8c2SCharles.Forsyth 	Chan *c;
123*74a4d8c2SCharles.Forsyth 	int chan;
124*74a4d8c2SCharles.Forsyth 	int dev;
125*74a4d8c2SCharles.Forsyth 
126*74a4d8c2SCharles.Forsyth 	dev = 0;
127*74a4d8c2SCharles.Forsyth 	if(spec != nil){
128*74a4d8c2SCharles.Forsyth 		dev = atoi(spec);
129*74a4d8c2SCharles.Forsyth 		if(dev >= Nloopbacks)
130*74a4d8c2SCharles.Forsyth 			error(Ebadspec);
131*74a4d8c2SCharles.Forsyth 	}
132*74a4d8c2SCharles.Forsyth 
133*74a4d8c2SCharles.Forsyth 	c = devattach('X', spec);
134*74a4d8c2SCharles.Forsyth 	lb = &loopbacks[dev];
135*74a4d8c2SCharles.Forsyth 
136*74a4d8c2SCharles.Forsyth 	qlock(lb);
137*74a4d8c2SCharles.Forsyth 	if(waserror()){
138*74a4d8c2SCharles.Forsyth 		lb->ref--;
139*74a4d8c2SCharles.Forsyth 		qunlock(lb);
140*74a4d8c2SCharles.Forsyth 		nexterror();
141*74a4d8c2SCharles.Forsyth 	}
142*74a4d8c2SCharles.Forsyth 
143*74a4d8c2SCharles.Forsyth 	lb->ref++;
144*74a4d8c2SCharles.Forsyth 	if(lb->ref == 1){
145*74a4d8c2SCharles.Forsyth 		for(chan = 0; chan < 2; chan++){
146*74a4d8c2SCharles.Forsyth 			lb->link[chan].ci.tmode = Tabsolute;
147*74a4d8c2SCharles.Forsyth 			lb->link[chan].ci.ta = &lb->link[chan];
148*74a4d8c2SCharles.Forsyth 			lb->link[chan].ci.tf = linkintr;
149*74a4d8c2SCharles.Forsyth 			lb->link[chan].limit = Loopqlim;
150*74a4d8c2SCharles.Forsyth 			q = qopen(lb->link[chan].limit, 0, 0, 0);
151*74a4d8c2SCharles.Forsyth 			lb->link[chan].iq = q;
152*74a4d8c2SCharles.Forsyth 			if(q == nil){
153*74a4d8c2SCharles.Forsyth 				freelb(lb);
154*74a4d8c2SCharles.Forsyth 				exhausted("memory");
155*74a4d8c2SCharles.Forsyth 			}
156*74a4d8c2SCharles.Forsyth 			q = qopen(lb->link[chan].limit, 0, 0, 0);
157*74a4d8c2SCharles.Forsyth 			lb->link[chan].oq = q;
158*74a4d8c2SCharles.Forsyth 			if(q == nil){
159*74a4d8c2SCharles.Forsyth 				freelb(lb);
160*74a4d8c2SCharles.Forsyth 				exhausted("memory");
161*74a4d8c2SCharles.Forsyth 			}
162*74a4d8c2SCharles.Forsyth 			lb->link[chan].indrop = 1;
163*74a4d8c2SCharles.Forsyth 
164*74a4d8c2SCharles.Forsyth 			lb->link[chan].delaynns = Delayn;
165*74a4d8c2SCharles.Forsyth 			lb->link[chan].delay0ns = Delay0;
166*74a4d8c2SCharles.Forsyth 		}
167*74a4d8c2SCharles.Forsyth 	}
168*74a4d8c2SCharles.Forsyth 	poperror();
169*74a4d8c2SCharles.Forsyth 	qunlock(lb);
170*74a4d8c2SCharles.Forsyth 
171*74a4d8c2SCharles.Forsyth 	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
172*74a4d8c2SCharles.Forsyth 	c->aux = lb;
173*74a4d8c2SCharles.Forsyth 	c->dev = dev;
174*74a4d8c2SCharles.Forsyth 	return c;
175*74a4d8c2SCharles.Forsyth }
176*74a4d8c2SCharles.Forsyth 
177*74a4d8c2SCharles.Forsyth static int
loopbackgen(Chan * c,char *,Dirtab *,int,int i,Dir * dp)178*74a4d8c2SCharles.Forsyth loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
179*74a4d8c2SCharles.Forsyth {
180*74a4d8c2SCharles.Forsyth 	Dirtab *tab;
181*74a4d8c2SCharles.Forsyth 	int len, type;
182*74a4d8c2SCharles.Forsyth 	Qid qid;
183*74a4d8c2SCharles.Forsyth 
184*74a4d8c2SCharles.Forsyth 	type = TYPE(c->qid.path);
185*74a4d8c2SCharles.Forsyth 	if(i == DEVDOTDOT){
186*74a4d8c2SCharles.Forsyth 		switch(type){
187*74a4d8c2SCharles.Forsyth 		case Qtopdir:
188*74a4d8c2SCharles.Forsyth 		case Qloopdir:
189*74a4d8c2SCharles.Forsyth 			snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev);
190*74a4d8c2SCharles.Forsyth 			mkqid(&qid, QID(0, Qtopdir), 0, QTDIR);
191*74a4d8c2SCharles.Forsyth 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
192*74a4d8c2SCharles.Forsyth 			break;
193*74a4d8c2SCharles.Forsyth 		case Qportdir:
194*74a4d8c2SCharles.Forsyth 			snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
195*74a4d8c2SCharles.Forsyth 			mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
196*74a4d8c2SCharles.Forsyth 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
197*74a4d8c2SCharles.Forsyth 			break;
198*74a4d8c2SCharles.Forsyth 		default:
199*74a4d8c2SCharles.Forsyth 			panic("loopbackgen %llux", c->qid.path);
200*74a4d8c2SCharles.Forsyth 		}
201*74a4d8c2SCharles.Forsyth 		return 1;
202*74a4d8c2SCharles.Forsyth 	}
203*74a4d8c2SCharles.Forsyth 
204*74a4d8c2SCharles.Forsyth 	switch(type){
205*74a4d8c2SCharles.Forsyth 	case Qtopdir:
206*74a4d8c2SCharles.Forsyth 		if(i != 0)
207*74a4d8c2SCharles.Forsyth 			return -1;
208*74a4d8c2SCharles.Forsyth 		snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
209*74a4d8c2SCharles.Forsyth 		mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
210*74a4d8c2SCharles.Forsyth 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
211*74a4d8c2SCharles.Forsyth 		return 1;
212*74a4d8c2SCharles.Forsyth 	case Qloopdir:
213*74a4d8c2SCharles.Forsyth 		if(i >= 2)
214*74a4d8c2SCharles.Forsyth 			return -1;
215*74a4d8c2SCharles.Forsyth 		snprint(up->genbuf, sizeof(up->genbuf), "%d", i);
216*74a4d8c2SCharles.Forsyth 		mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR);
217*74a4d8c2SCharles.Forsyth 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
218*74a4d8c2SCharles.Forsyth 		return 1;
219*74a4d8c2SCharles.Forsyth 	case Qportdir:
220*74a4d8c2SCharles.Forsyth 		if(i >= nelem(loopportdir))
221*74a4d8c2SCharles.Forsyth 			return -1;
222*74a4d8c2SCharles.Forsyth 		tab = &loopportdir[i];
223*74a4d8c2SCharles.Forsyth 		mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE);
224*74a4d8c2SCharles.Forsyth 		devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
225*74a4d8c2SCharles.Forsyth 		return 1;
226*74a4d8c2SCharles.Forsyth 	default:
227*74a4d8c2SCharles.Forsyth 		/* non directory entries end up here; must be in lowest level */
228*74a4d8c2SCharles.Forsyth 		if(c->qid.type & QTDIR)
229*74a4d8c2SCharles.Forsyth 			panic("loopbackgen: unexpected directory");
230*74a4d8c2SCharles.Forsyth 		if(i != 0)
231*74a4d8c2SCharles.Forsyth 			return -1;
232*74a4d8c2SCharles.Forsyth 		tab = &loopdirs[type];
233*74a4d8c2SCharles.Forsyth 		if(tab == nil)
234*74a4d8c2SCharles.Forsyth 			panic("loopbackgen: unknown type: %d", type);
235*74a4d8c2SCharles.Forsyth 		len = tab->length;
236*74a4d8c2SCharles.Forsyth 		devdir(c, c->qid, tab->name, len, eve, tab->perm, dp);
237*74a4d8c2SCharles.Forsyth 		return 1;
238*74a4d8c2SCharles.Forsyth 	}
239*74a4d8c2SCharles.Forsyth }
240*74a4d8c2SCharles.Forsyth 
241*74a4d8c2SCharles.Forsyth 
242*74a4d8c2SCharles.Forsyth static Walkqid*
loopbackwalk(Chan * c,Chan * nc,char ** name,int nname)243*74a4d8c2SCharles.Forsyth loopbackwalk(Chan *c, Chan *nc, char **name, int nname)
244*74a4d8c2SCharles.Forsyth {
245*74a4d8c2SCharles.Forsyth 	Walkqid *wq;
246*74a4d8c2SCharles.Forsyth 	Loop *lb;
247*74a4d8c2SCharles.Forsyth 
248*74a4d8c2SCharles.Forsyth 	wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen);
249*74a4d8c2SCharles.Forsyth 	if(wq != nil && wq->clone != nil && wq->clone != c){
250*74a4d8c2SCharles.Forsyth 		lb = c->aux;
251*74a4d8c2SCharles.Forsyth 		qlock(lb);
252*74a4d8c2SCharles.Forsyth 		lb->ref++;
253*74a4d8c2SCharles.Forsyth 		if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata)
254*74a4d8c2SCharles.Forsyth 			lb->link[ID(c->qid.path)].ref++;
255*74a4d8c2SCharles.Forsyth 		qunlock(lb);
256*74a4d8c2SCharles.Forsyth 	}
257*74a4d8c2SCharles.Forsyth 	return wq;
258*74a4d8c2SCharles.Forsyth }
259*74a4d8c2SCharles.Forsyth 
260*74a4d8c2SCharles.Forsyth static int
loopbackstat(Chan * c,uchar * db,int n)261*74a4d8c2SCharles.Forsyth loopbackstat(Chan *c, uchar *db, int n)
262*74a4d8c2SCharles.Forsyth {
263*74a4d8c2SCharles.Forsyth 	return devstat(c, db, n, nil, 0, loopbackgen);
264*74a4d8c2SCharles.Forsyth }
265*74a4d8c2SCharles.Forsyth 
266*74a4d8c2SCharles.Forsyth /*
267*74a4d8c2SCharles.Forsyth  *  if the stream doesn't exist, create it
268*74a4d8c2SCharles.Forsyth  */
269*74a4d8c2SCharles.Forsyth static Chan*
loopbackopen(Chan * c,int omode)270*74a4d8c2SCharles.Forsyth loopbackopen(Chan *c, int omode)
271*74a4d8c2SCharles.Forsyth {
272*74a4d8c2SCharles.Forsyth 	Loop *lb;
273*74a4d8c2SCharles.Forsyth 
274*74a4d8c2SCharles.Forsyth 	if(c->qid.type & QTDIR){
275*74a4d8c2SCharles.Forsyth 		if(omode != OREAD)
276*74a4d8c2SCharles.Forsyth 			error(Ebadarg);
277*74a4d8c2SCharles.Forsyth 		c->mode = omode;
278*74a4d8c2SCharles.Forsyth 		c->flag |= COPEN;
279*74a4d8c2SCharles.Forsyth 		c->offset = 0;
280*74a4d8c2SCharles.Forsyth 		return c;
281*74a4d8c2SCharles.Forsyth 	}
282*74a4d8c2SCharles.Forsyth 
283*74a4d8c2SCharles.Forsyth 	lb = c->aux;
284*74a4d8c2SCharles.Forsyth 	qlock(lb);
285*74a4d8c2SCharles.Forsyth 	if(TYPE(c->qid.path) == Qdata){
286*74a4d8c2SCharles.Forsyth 		if(lb->link[ID(c->qid.path)].ref){
287*74a4d8c2SCharles.Forsyth 			qunlock(lb);
288*74a4d8c2SCharles.Forsyth 			error(Einuse);
289*74a4d8c2SCharles.Forsyth 		}
290*74a4d8c2SCharles.Forsyth 		lb->link[ID(c->qid.path)].ref++;
291*74a4d8c2SCharles.Forsyth 	}
292*74a4d8c2SCharles.Forsyth 	qunlock(lb);
293*74a4d8c2SCharles.Forsyth 
294*74a4d8c2SCharles.Forsyth 	c->mode = openmode(omode);
295*74a4d8c2SCharles.Forsyth 	c->flag |= COPEN;
296*74a4d8c2SCharles.Forsyth 	c->offset = 0;
297*74a4d8c2SCharles.Forsyth 	c->iounit = qiomaxatomic;
298*74a4d8c2SCharles.Forsyth 	return c;
299*74a4d8c2SCharles.Forsyth }
300*74a4d8c2SCharles.Forsyth 
301*74a4d8c2SCharles.Forsyth static void
loopbackclose(Chan * c)302*74a4d8c2SCharles.Forsyth loopbackclose(Chan *c)
303*74a4d8c2SCharles.Forsyth {
304*74a4d8c2SCharles.Forsyth 	Loop *lb;
305*74a4d8c2SCharles.Forsyth 	int ref, chan;
306*74a4d8c2SCharles.Forsyth 
307*74a4d8c2SCharles.Forsyth 	lb = c->aux;
308*74a4d8c2SCharles.Forsyth 
309*74a4d8c2SCharles.Forsyth 	qlock(lb);
310*74a4d8c2SCharles.Forsyth 
311*74a4d8c2SCharles.Forsyth 	/*
312*74a4d8c2SCharles.Forsyth 	 * closing either side hangs up the stream
313*74a4d8c2SCharles.Forsyth 	 */
314*74a4d8c2SCharles.Forsyth 	if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){
315*74a4d8c2SCharles.Forsyth 		chan = ID(c->qid.path);
316*74a4d8c2SCharles.Forsyth 		if(--lb->link[chan].ref == 0){
317*74a4d8c2SCharles.Forsyth 			qhangup(lb->link[chan ^ 1].oq, nil);
318*74a4d8c2SCharles.Forsyth 			looper(lb);
319*74a4d8c2SCharles.Forsyth 		}
320*74a4d8c2SCharles.Forsyth 	}
321*74a4d8c2SCharles.Forsyth 
322*74a4d8c2SCharles.Forsyth 
323*74a4d8c2SCharles.Forsyth 	/*
324*74a4d8c2SCharles.Forsyth 	 *  if both sides are closed, they are reusable
325*74a4d8c2SCharles.Forsyth 	 */
326*74a4d8c2SCharles.Forsyth 	if(lb->link[0].ref == 0 && lb->link[1].ref == 0){
327*74a4d8c2SCharles.Forsyth 		for(chan = 0; chan < 2; chan++){
328*74a4d8c2SCharles.Forsyth 			closelink(&lb->link[chan], 0);
329*74a4d8c2SCharles.Forsyth 			qreopen(lb->link[chan].iq);
330*74a4d8c2SCharles.Forsyth 			qreopen(lb->link[chan].oq);
331*74a4d8c2SCharles.Forsyth 			qsetlimit(lb->link[chan].oq, lb->link[chan].limit);
332*74a4d8c2SCharles.Forsyth 			qsetlimit(lb->link[chan].iq, lb->link[chan].limit);
333*74a4d8c2SCharles.Forsyth 		}
334*74a4d8c2SCharles.Forsyth 	}
335*74a4d8c2SCharles.Forsyth 	ref = --lb->ref;
336*74a4d8c2SCharles.Forsyth 	if(ref == 0)
337*74a4d8c2SCharles.Forsyth 		freelb(lb);
338*74a4d8c2SCharles.Forsyth 	qunlock(lb);
339*74a4d8c2SCharles.Forsyth }
340*74a4d8c2SCharles.Forsyth 
341*74a4d8c2SCharles.Forsyth static void
freelb(Loop * lb)342*74a4d8c2SCharles.Forsyth freelb(Loop *lb)
343*74a4d8c2SCharles.Forsyth {
344*74a4d8c2SCharles.Forsyth 	int chan;
345*74a4d8c2SCharles.Forsyth 
346*74a4d8c2SCharles.Forsyth 	for(chan = 0; chan < 2; chan++)
347*74a4d8c2SCharles.Forsyth 		closelink(&lb->link[chan], 1);
348*74a4d8c2SCharles.Forsyth }
349*74a4d8c2SCharles.Forsyth 
350*74a4d8c2SCharles.Forsyth /*
351*74a4d8c2SCharles.Forsyth  * called with the Loop qlocked,
352*74a4d8c2SCharles.Forsyth  * so only pushlink can mess with the queues
353*74a4d8c2SCharles.Forsyth  */
354*74a4d8c2SCharles.Forsyth static void
closelink(Link * link,int dofree)355*74a4d8c2SCharles.Forsyth closelink(Link *link, int dofree)
356*74a4d8c2SCharles.Forsyth {
357*74a4d8c2SCharles.Forsyth 	Queue *iq, *oq;
358*74a4d8c2SCharles.Forsyth 	Block *bp;
359*74a4d8c2SCharles.Forsyth 
360*74a4d8c2SCharles.Forsyth 	ilock(link);
361*74a4d8c2SCharles.Forsyth 	iq = link->iq;
362*74a4d8c2SCharles.Forsyth 	oq = link->oq;
363*74a4d8c2SCharles.Forsyth 	bp = link->tq;
364*74a4d8c2SCharles.Forsyth 	link->tq = nil;
365*74a4d8c2SCharles.Forsyth 	link->tqtail = nil;
366*74a4d8c2SCharles.Forsyth 	link->tout = 0;
367*74a4d8c2SCharles.Forsyth 	link->tin = 0;
368*74a4d8c2SCharles.Forsyth 	timerdel(&link->ci);
369*74a4d8c2SCharles.Forsyth 	iunlock(link);
370*74a4d8c2SCharles.Forsyth 	if(iq != nil){
371*74a4d8c2SCharles.Forsyth 		qclose(iq);
372*74a4d8c2SCharles.Forsyth 		if(dofree){
373*74a4d8c2SCharles.Forsyth 			ilock(link);
374*74a4d8c2SCharles.Forsyth 			free(iq);
375*74a4d8c2SCharles.Forsyth 			link->iq = nil;
376*74a4d8c2SCharles.Forsyth 			iunlock(link);
377*74a4d8c2SCharles.Forsyth 		}
378*74a4d8c2SCharles.Forsyth 	}
379*74a4d8c2SCharles.Forsyth 	if(oq != nil){
380*74a4d8c2SCharles.Forsyth 		qclose(oq);
381*74a4d8c2SCharles.Forsyth 		if(dofree){
382*74a4d8c2SCharles.Forsyth 			ilock(link);
383*74a4d8c2SCharles.Forsyth 			free(oq);
384*74a4d8c2SCharles.Forsyth 			link->oq = nil;
385*74a4d8c2SCharles.Forsyth 			iunlock(link);
386*74a4d8c2SCharles.Forsyth 		}
387*74a4d8c2SCharles.Forsyth 	}
388*74a4d8c2SCharles.Forsyth 	freeblist(bp);
389*74a4d8c2SCharles.Forsyth }
390*74a4d8c2SCharles.Forsyth 
391*74a4d8c2SCharles.Forsyth static long
loopbackread(Chan * c,void * va,long n,vlong offset)392*74a4d8c2SCharles.Forsyth loopbackread(Chan *c, void *va, long n, vlong offset)
393*74a4d8c2SCharles.Forsyth {
394*74a4d8c2SCharles.Forsyth 	Loop *lb;
395*74a4d8c2SCharles.Forsyth 	Link *link;
396*74a4d8c2SCharles.Forsyth 	char *buf;
397*74a4d8c2SCharles.Forsyth 	long rv;
398*74a4d8c2SCharles.Forsyth 
399*74a4d8c2SCharles.Forsyth 	lb = c->aux;
400*74a4d8c2SCharles.Forsyth 	switch(TYPE(c->qid.path)){
401*74a4d8c2SCharles.Forsyth 	default:
402*74a4d8c2SCharles.Forsyth 		error(Eperm);
403*74a4d8c2SCharles.Forsyth 		return -1;	/* not reached */
404*74a4d8c2SCharles.Forsyth 	case Qtopdir:
405*74a4d8c2SCharles.Forsyth 	case Qloopdir:
406*74a4d8c2SCharles.Forsyth 	case Qportdir:
407*74a4d8c2SCharles.Forsyth 		return devdirread(c, va, n, nil, 0, loopbackgen);
408*74a4d8c2SCharles.Forsyth 	case Qdata:
409*74a4d8c2SCharles.Forsyth 		return qread(lb->link[ID(c->qid.path)].iq, va, n);
410*74a4d8c2SCharles.Forsyth 	case Qstatus:
411*74a4d8c2SCharles.Forsyth 		link = &lb->link[ID(c->qid.path)];
412*74a4d8c2SCharles.Forsyth 		buf = smalloc(Statelen);
413*74a4d8c2SCharles.Forsyth 		rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns);
414*74a4d8c2SCharles.Forsyth 		rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit);
415*74a4d8c2SCharles.Forsyth 		rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop);
416*74a4d8c2SCharles.Forsyth 		snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate);
417*74a4d8c2SCharles.Forsyth 		rv = readstr(offset, va, n, buf);
418*74a4d8c2SCharles.Forsyth 		free(buf);
419*74a4d8c2SCharles.Forsyth 		break;
420*74a4d8c2SCharles.Forsyth 	case Qstats:
421*74a4d8c2SCharles.Forsyth 		link = &lb->link[ID(c->qid.path)];
422*74a4d8c2SCharles.Forsyth 		buf = smalloc(Statelen);
423*74a4d8c2SCharles.Forsyth 		rv = snprint(buf, Statelen, "packets: %ld\n", link->packets);
424*74a4d8c2SCharles.Forsyth 		rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes);
425*74a4d8c2SCharles.Forsyth 		rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops);
426*74a4d8c2SCharles.Forsyth 		snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows);
427*74a4d8c2SCharles.Forsyth 		rv = readstr(offset, va, n, buf);
428*74a4d8c2SCharles.Forsyth 		free(buf);
429*74a4d8c2SCharles.Forsyth 		break;
430*74a4d8c2SCharles.Forsyth 	}
431*74a4d8c2SCharles.Forsyth 	return rv;
432*74a4d8c2SCharles.Forsyth }
433*74a4d8c2SCharles.Forsyth 
434*74a4d8c2SCharles.Forsyth static Block*
loopbackbread(Chan * c,long n,ulong offset)435*74a4d8c2SCharles.Forsyth loopbackbread(Chan *c, long n, ulong offset)
436*74a4d8c2SCharles.Forsyth {
437*74a4d8c2SCharles.Forsyth 	Loop *lb;
438*74a4d8c2SCharles.Forsyth 
439*74a4d8c2SCharles.Forsyth 	lb = c->aux;
440*74a4d8c2SCharles.Forsyth 	if(TYPE(c->qid.path) == Qdata)
441*74a4d8c2SCharles.Forsyth 		return qbread(lb->link[ID(c->qid.path)].iq, n);
442*74a4d8c2SCharles.Forsyth 
443*74a4d8c2SCharles.Forsyth 	return devbread(c, n, offset);
444*74a4d8c2SCharles.Forsyth }
445*74a4d8c2SCharles.Forsyth 
446*74a4d8c2SCharles.Forsyth static long
loopbackbwrite(Chan * c,Block * bp,ulong off)447*74a4d8c2SCharles.Forsyth loopbackbwrite(Chan *c, Block *bp, ulong off)
448*74a4d8c2SCharles.Forsyth {
449*74a4d8c2SCharles.Forsyth 	Loop *lb;
450*74a4d8c2SCharles.Forsyth 
451*74a4d8c2SCharles.Forsyth 	lb = c->aux;
452*74a4d8c2SCharles.Forsyth 	if(TYPE(c->qid.path) == Qdata)
453*74a4d8c2SCharles.Forsyth 		return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp);
454*74a4d8c2SCharles.Forsyth 	return devbwrite(c, bp, off);
455*74a4d8c2SCharles.Forsyth }
456*74a4d8c2SCharles.Forsyth 
457*74a4d8c2SCharles.Forsyth static long
loopbackwrite(Chan * c,void * va,long n,vlong off)458*74a4d8c2SCharles.Forsyth loopbackwrite(Chan *c, void *va, long n, vlong off)
459*74a4d8c2SCharles.Forsyth {
460*74a4d8c2SCharles.Forsyth 	Loop *lb;
461*74a4d8c2SCharles.Forsyth 	Link *link;
462*74a4d8c2SCharles.Forsyth 	Cmdbuf *volatile cb;
463*74a4d8c2SCharles.Forsyth 	Block *volatile bp;
464*74a4d8c2SCharles.Forsyth 	vlong d0ns;
465*74a4d8c2SCharles.Forsyth 	long dnns;
466*74a4d8c2SCharles.Forsyth 
467*74a4d8c2SCharles.Forsyth 	switch(TYPE(c->qid.path)){
468*74a4d8c2SCharles.Forsyth 	case Qdata:
469*74a4d8c2SCharles.Forsyth 		bp = allocb(n);
470*74a4d8c2SCharles.Forsyth 		if(waserror()){
471*74a4d8c2SCharles.Forsyth 			freeb(bp);
472*74a4d8c2SCharles.Forsyth 			nexterror();
473*74a4d8c2SCharles.Forsyth 		}
474*74a4d8c2SCharles.Forsyth 		memmove(bp->wp, va, n);
475*74a4d8c2SCharles.Forsyth 		poperror();
476*74a4d8c2SCharles.Forsyth 		bp->wp += n;
477*74a4d8c2SCharles.Forsyth 		return loopbackbwrite(c, bp, off);
478*74a4d8c2SCharles.Forsyth 	case Qctl:
479*74a4d8c2SCharles.Forsyth 		lb = c->aux;
480*74a4d8c2SCharles.Forsyth 		link = &lb->link[ID(c->qid.path)];
481*74a4d8c2SCharles.Forsyth 		cb = parsecmd(va, n);
482*74a4d8c2SCharles.Forsyth 		if(waserror()){
483*74a4d8c2SCharles.Forsyth 			free(cb);
484*74a4d8c2SCharles.Forsyth 			nexterror();
485*74a4d8c2SCharles.Forsyth 		}
486*74a4d8c2SCharles.Forsyth 		if(cb->nf < 1)
487*74a4d8c2SCharles.Forsyth 			error("short control request");
488*74a4d8c2SCharles.Forsyth 		if(strcmp(cb->f[0], "delay") == 0){
489*74a4d8c2SCharles.Forsyth 			if(cb->nf != 3)
490*74a4d8c2SCharles.Forsyth 				error("usage: delay latency bytedelay");
491*74a4d8c2SCharles.Forsyth 			d0ns = strtoll(cb->f[1], nil, 10);
492*74a4d8c2SCharles.Forsyth 			dnns = strtol(cb->f[2], nil, 10);
493*74a4d8c2SCharles.Forsyth 
494*74a4d8c2SCharles.Forsyth 			/*
495*74a4d8c2SCharles.Forsyth 			 * it takes about 20000 cycles on a pentium ii
496*74a4d8c2SCharles.Forsyth 			 * to run pushlink; perhaps this should be accounted.
497*74a4d8c2SCharles.Forsyth 			 */
498*74a4d8c2SCharles.Forsyth 
499*74a4d8c2SCharles.Forsyth 			ilock(link);
500*74a4d8c2SCharles.Forsyth 			link->delay0ns = d0ns;
501*74a4d8c2SCharles.Forsyth 			link->delaynns = dnns;
502*74a4d8c2SCharles.Forsyth 			iunlock(link);
503*74a4d8c2SCharles.Forsyth 		}else if(strcmp(cb->f[0], "indrop") == 0){
504*74a4d8c2SCharles.Forsyth 			if(cb->nf != 2)
505*74a4d8c2SCharles.Forsyth 				error("usage: indrop [01]");
506*74a4d8c2SCharles.Forsyth 			ilock(link);
507*74a4d8c2SCharles.Forsyth 			link->indrop = strtol(cb->f[1], nil, 0) != 0;
508*74a4d8c2SCharles.Forsyth 			iunlock(link);
509*74a4d8c2SCharles.Forsyth 		}else if(strcmp(cb->f[0], "droprate") == 0){
510*74a4d8c2SCharles.Forsyth 			if(cb->nf != 2)
511*74a4d8c2SCharles.Forsyth 				error("usage: droprate ofn");
512*74a4d8c2SCharles.Forsyth 			ilock(link);
513*74a4d8c2SCharles.Forsyth 			link->droprate = strtol(cb->f[1], nil, 0);
514*74a4d8c2SCharles.Forsyth 			iunlock(link);
515*74a4d8c2SCharles.Forsyth 		}else if(strcmp(cb->f[0], "limit") == 0){
516*74a4d8c2SCharles.Forsyth 			if(cb->nf != 2)
517*74a4d8c2SCharles.Forsyth 				error("usage: limit maxqsize");
518*74a4d8c2SCharles.Forsyth 			ilock(link);
519*74a4d8c2SCharles.Forsyth 			link->limit = strtol(cb->f[1], nil, 0);
520*74a4d8c2SCharles.Forsyth 			qsetlimit(link->oq, link->limit);
521*74a4d8c2SCharles.Forsyth 			qsetlimit(link->iq, link->limit);
522*74a4d8c2SCharles.Forsyth 			iunlock(link);
523*74a4d8c2SCharles.Forsyth 		}else if(strcmp(cb->f[0], "reset") == 0){
524*74a4d8c2SCharles.Forsyth 			if(cb->nf != 1)
525*74a4d8c2SCharles.Forsyth 				error("usage: reset");
526*74a4d8c2SCharles.Forsyth 			ilock(link);
527*74a4d8c2SCharles.Forsyth 			link->packets = 0;
528*74a4d8c2SCharles.Forsyth 			link->bytes = 0;
529*74a4d8c2SCharles.Forsyth 			link->indrop = 0;
530*74a4d8c2SCharles.Forsyth 			link->soverflows = 0;
531*74a4d8c2SCharles.Forsyth 			link->drops = 0;
532*74a4d8c2SCharles.Forsyth 			iunlock(link);
533*74a4d8c2SCharles.Forsyth 		}else
534*74a4d8c2SCharles.Forsyth 			error("unknown control request");
535*74a4d8c2SCharles.Forsyth 		poperror();
536*74a4d8c2SCharles.Forsyth 		free(cb);
537*74a4d8c2SCharles.Forsyth 		break;
538*74a4d8c2SCharles.Forsyth 	default:
539*74a4d8c2SCharles.Forsyth 		error(Eperm);
540*74a4d8c2SCharles.Forsyth 	}
541*74a4d8c2SCharles.Forsyth 
542*74a4d8c2SCharles.Forsyth 	return n;
543*74a4d8c2SCharles.Forsyth }
544*74a4d8c2SCharles.Forsyth 
545*74a4d8c2SCharles.Forsyth static long
loopoput(Loop * lb,Link * link,Block * volatile bp)546*74a4d8c2SCharles.Forsyth loopoput(Loop *lb, Link *link, Block *volatile bp)
547*74a4d8c2SCharles.Forsyth {
548*74a4d8c2SCharles.Forsyth 	long n;
549*74a4d8c2SCharles.Forsyth 
550*74a4d8c2SCharles.Forsyth 	n = BLEN(bp);
551*74a4d8c2SCharles.Forsyth 
552*74a4d8c2SCharles.Forsyth 	/* make it a single block with space for the loopback timing header */
553*74a4d8c2SCharles.Forsyth 	if(waserror()){
554*74a4d8c2SCharles.Forsyth 		freeb(bp);
555*74a4d8c2SCharles.Forsyth 		nexterror();
556*74a4d8c2SCharles.Forsyth 	}
557*74a4d8c2SCharles.Forsyth 	bp = padblock(bp, Tmsize);
558*74a4d8c2SCharles.Forsyth 	if(bp->next)
559*74a4d8c2SCharles.Forsyth 		bp = concatblock(bp);
560*74a4d8c2SCharles.Forsyth 	if(BLEN(bp) < lb->minmtu)
561*74a4d8c2SCharles.Forsyth 		bp = adjustblock(bp, lb->minmtu);
562*74a4d8c2SCharles.Forsyth 	poperror();
563*74a4d8c2SCharles.Forsyth 	ptime(bp->rp, todget(nil));
564*74a4d8c2SCharles.Forsyth 
565*74a4d8c2SCharles.Forsyth 	link->packets++;
566*74a4d8c2SCharles.Forsyth 	link->bytes += n;
567*74a4d8c2SCharles.Forsyth 
568*74a4d8c2SCharles.Forsyth 	qbwrite(link->oq, bp);
569*74a4d8c2SCharles.Forsyth 
570*74a4d8c2SCharles.Forsyth 	looper(lb);
571*74a4d8c2SCharles.Forsyth 	return n;
572*74a4d8c2SCharles.Forsyth }
573*74a4d8c2SCharles.Forsyth 
574*74a4d8c2SCharles.Forsyth static void
looper(Loop * lb)575*74a4d8c2SCharles.Forsyth looper(Loop *lb)
576*74a4d8c2SCharles.Forsyth {
577*74a4d8c2SCharles.Forsyth 	vlong t;
578*74a4d8c2SCharles.Forsyth 	int chan;
579*74a4d8c2SCharles.Forsyth 
580*74a4d8c2SCharles.Forsyth 	t = todget(nil);
581*74a4d8c2SCharles.Forsyth 	for(chan = 0; chan < 2; chan++)
582*74a4d8c2SCharles.Forsyth 		pushlink(&lb->link[chan], t);
583*74a4d8c2SCharles.Forsyth }
584*74a4d8c2SCharles.Forsyth 
585*74a4d8c2SCharles.Forsyth static void
linkintr(Ureg *,Timer * ci)586*74a4d8c2SCharles.Forsyth linkintr(Ureg*, Timer *ci)
587*74a4d8c2SCharles.Forsyth {
588*74a4d8c2SCharles.Forsyth 	Link *link;
589*74a4d8c2SCharles.Forsyth 
590*74a4d8c2SCharles.Forsyth 	link = ci->ta;
591*74a4d8c2SCharles.Forsyth 	pushlink(link, ci->tns);
592*74a4d8c2SCharles.Forsyth }
593*74a4d8c2SCharles.Forsyth 
594*74a4d8c2SCharles.Forsyth /*
595*74a4d8c2SCharles.Forsyth  * move blocks between queues if they are ready.
596*74a4d8c2SCharles.Forsyth  * schedule an interrupt for the next interesting time.
597*74a4d8c2SCharles.Forsyth  *
598*74a4d8c2SCharles.Forsyth  * must be called with the link ilocked.
599*74a4d8c2SCharles.Forsyth  */
600*74a4d8c2SCharles.Forsyth static void
pushlink(Link * link,vlong now)601*74a4d8c2SCharles.Forsyth pushlink(Link *link, vlong now)
602*74a4d8c2SCharles.Forsyth {
603*74a4d8c2SCharles.Forsyth 	Block *bp;
604*74a4d8c2SCharles.Forsyth 	vlong tout, tin;
605*74a4d8c2SCharles.Forsyth 
606*74a4d8c2SCharles.Forsyth 	/*
607*74a4d8c2SCharles.Forsyth 	 * put another block in the link queue
608*74a4d8c2SCharles.Forsyth 	 */
609*74a4d8c2SCharles.Forsyth 	ilock(link);
610*74a4d8c2SCharles.Forsyth 	if(link->iq == nil || link->oq == nil){
611*74a4d8c2SCharles.Forsyth 		iunlock(link);
612*74a4d8c2SCharles.Forsyth 		return;
613*74a4d8c2SCharles.Forsyth 
614*74a4d8c2SCharles.Forsyth 	}
615*74a4d8c2SCharles.Forsyth 	timerdel(&link->ci);
616*74a4d8c2SCharles.Forsyth 
617*74a4d8c2SCharles.Forsyth 	/*
618*74a4d8c2SCharles.Forsyth 	 * put more blocks into the xmit queue
619*74a4d8c2SCharles.Forsyth 	 * use the time the last packet was supposed to go out
620*74a4d8c2SCharles.Forsyth 	 * as the start time for the next packet, rather than
621*74a4d8c2SCharles.Forsyth 	 * the current time.  this more closely models a network
622*74a4d8c2SCharles.Forsyth 	 * device which can queue multiple output packets.
623*74a4d8c2SCharles.Forsyth 	 */
624*74a4d8c2SCharles.Forsyth 	tout = link->tout;
625*74a4d8c2SCharles.Forsyth 	if(!tout)
626*74a4d8c2SCharles.Forsyth 		tout = now;
627*74a4d8c2SCharles.Forsyth 	while(tout <= now){
628*74a4d8c2SCharles.Forsyth 		bp = qget(link->oq);
629*74a4d8c2SCharles.Forsyth 		if(bp == nil){
630*74a4d8c2SCharles.Forsyth 			tout = 0;
631*74a4d8c2SCharles.Forsyth 			break;
632*74a4d8c2SCharles.Forsyth 		}
633*74a4d8c2SCharles.Forsyth 
634*74a4d8c2SCharles.Forsyth 		/*
635*74a4d8c2SCharles.Forsyth 		 * can't send the packet before it gets queued
636*74a4d8c2SCharles.Forsyth 		 */
637*74a4d8c2SCharles.Forsyth 		tin = gtime(bp->rp);
638*74a4d8c2SCharles.Forsyth 		if(tin > tout)
639*74a4d8c2SCharles.Forsyth 			tout = tin;
640*74a4d8c2SCharles.Forsyth 		tout = tout + (BLEN(bp) - Tmsize) * link->delaynns;
641*74a4d8c2SCharles.Forsyth 
642*74a4d8c2SCharles.Forsyth 		/*
643*74a4d8c2SCharles.Forsyth 		 * drop packets
644*74a4d8c2SCharles.Forsyth 		 */
645*74a4d8c2SCharles.Forsyth 		if(link->droprate && nrand(link->droprate) == 0)
646*74a4d8c2SCharles.Forsyth 			link->drops++;
647*74a4d8c2SCharles.Forsyth 		else{
648*74a4d8c2SCharles.Forsyth 			ptime(bp->rp, tout + link->delay0ns);
649*74a4d8c2SCharles.Forsyth 			if(link->tq == nil)
650*74a4d8c2SCharles.Forsyth 				link->tq = bp;
651*74a4d8c2SCharles.Forsyth 			else
652*74a4d8c2SCharles.Forsyth 				link->tqtail->next = bp;
653*74a4d8c2SCharles.Forsyth 			link->tqtail = bp;
654*74a4d8c2SCharles.Forsyth 		}
655*74a4d8c2SCharles.Forsyth 	}
656*74a4d8c2SCharles.Forsyth 
657*74a4d8c2SCharles.Forsyth 	/*
658*74a4d8c2SCharles.Forsyth 	 * record the next time a packet can be sent,
659*74a4d8c2SCharles.Forsyth 	 * but don't schedule an interrupt if none is waiting
660*74a4d8c2SCharles.Forsyth 	 */
661*74a4d8c2SCharles.Forsyth 	link->tout = tout;
662*74a4d8c2SCharles.Forsyth 	if(!qcanread(link->oq))
663*74a4d8c2SCharles.Forsyth 		tout = 0;
664*74a4d8c2SCharles.Forsyth 
665*74a4d8c2SCharles.Forsyth 	/*
666*74a4d8c2SCharles.Forsyth 	 * put more blocks into the receive queue
667*74a4d8c2SCharles.Forsyth 	 */
668*74a4d8c2SCharles.Forsyth 	tin = 0;
669*74a4d8c2SCharles.Forsyth 	while(bp = link->tq){
670*74a4d8c2SCharles.Forsyth 		tin = gtime(bp->rp);
671*74a4d8c2SCharles.Forsyth 		if(tin > now)
672*74a4d8c2SCharles.Forsyth 			break;
673*74a4d8c2SCharles.Forsyth 		bp->rp += Tmsize;
674*74a4d8c2SCharles.Forsyth 		link->tq = bp->next;
675*74a4d8c2SCharles.Forsyth 		bp->next = nil;
676*74a4d8c2SCharles.Forsyth 		if(!link->indrop)
677*74a4d8c2SCharles.Forsyth 			qpassnolim(link->iq, bp);
678*74a4d8c2SCharles.Forsyth 		else if(qpass(link->iq, bp) < 0)
679*74a4d8c2SCharles.Forsyth 			link->soverflows++;
680*74a4d8c2SCharles.Forsyth 		tin = 0;
681*74a4d8c2SCharles.Forsyth 	}
682*74a4d8c2SCharles.Forsyth 	if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq))
683*74a4d8c2SCharles.Forsyth 		qhangup(link->iq, nil);
684*74a4d8c2SCharles.Forsyth 	link->tin = tin;
685*74a4d8c2SCharles.Forsyth 	if(!tin || tin > tout && tout)
686*74a4d8c2SCharles.Forsyth 		tin = tout;
687*74a4d8c2SCharles.Forsyth 
688*74a4d8c2SCharles.Forsyth 	link->ci.tns = tin;
689*74a4d8c2SCharles.Forsyth 	if(tin){
690*74a4d8c2SCharles.Forsyth 		if(tin < now)
691*74a4d8c2SCharles.Forsyth 			panic("loopback unfinished business");
692*74a4d8c2SCharles.Forsyth 		timeradd(&link->ci);
693*74a4d8c2SCharles.Forsyth 	}
694*74a4d8c2SCharles.Forsyth 	iunlock(link);
695*74a4d8c2SCharles.Forsyth }
696*74a4d8c2SCharles.Forsyth 
697*74a4d8c2SCharles.Forsyth static void
ptime(uchar * p,vlong t)698*74a4d8c2SCharles.Forsyth ptime(uchar *p, vlong t)
699*74a4d8c2SCharles.Forsyth {
700*74a4d8c2SCharles.Forsyth 	ulong tt;
701*74a4d8c2SCharles.Forsyth 
702*74a4d8c2SCharles.Forsyth 	tt = t >> 32;
703*74a4d8c2SCharles.Forsyth 	p[0] = tt >> 24;
704*74a4d8c2SCharles.Forsyth 	p[1] = tt >> 16;
705*74a4d8c2SCharles.Forsyth 	p[2] = tt >> 8;
706*74a4d8c2SCharles.Forsyth 	p[3] = tt;
707*74a4d8c2SCharles.Forsyth 	tt = t;
708*74a4d8c2SCharles.Forsyth 	p[4] = tt >> 24;
709*74a4d8c2SCharles.Forsyth 	p[5] = tt >> 16;
710*74a4d8c2SCharles.Forsyth 	p[6] = tt >> 8;
711*74a4d8c2SCharles.Forsyth 	p[7] = tt;
712*74a4d8c2SCharles.Forsyth }
713*74a4d8c2SCharles.Forsyth 
714*74a4d8c2SCharles.Forsyth static vlong
gtime(uchar * p)715*74a4d8c2SCharles.Forsyth gtime(uchar *p)
716*74a4d8c2SCharles.Forsyth {
717*74a4d8c2SCharles.Forsyth 	ulong t1, t2;
718*74a4d8c2SCharles.Forsyth 
719*74a4d8c2SCharles.Forsyth 	t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
720*74a4d8c2SCharles.Forsyth 	t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
721*74a4d8c2SCharles.Forsyth 	return ((vlong)t1 << 32) | t2;
722*74a4d8c2SCharles.Forsyth }
723*74a4d8c2SCharles.Forsyth 
724*74a4d8c2SCharles.Forsyth Dev loopbackdevtab = {
725*74a4d8c2SCharles.Forsyth 	'X',
726*74a4d8c2SCharles.Forsyth 	"loopback",
727*74a4d8c2SCharles.Forsyth 
728*74a4d8c2SCharles.Forsyth 	devreset,
729*74a4d8c2SCharles.Forsyth 	loopbackinit,
730*74a4d8c2SCharles.Forsyth 	devshutdown,
731*74a4d8c2SCharles.Forsyth 	loopbackattach,
732*74a4d8c2SCharles.Forsyth 	loopbackwalk,
733*74a4d8c2SCharles.Forsyth 	loopbackstat,
734*74a4d8c2SCharles.Forsyth 	loopbackopen,
735*74a4d8c2SCharles.Forsyth 	devcreate,
736*74a4d8c2SCharles.Forsyth 	loopbackclose,
737*74a4d8c2SCharles.Forsyth 	loopbackread,
738*74a4d8c2SCharles.Forsyth 	loopbackbread,
739*74a4d8c2SCharles.Forsyth 	loopbackwrite,
740*74a4d8c2SCharles.Forsyth 	loopbackbwrite,
741*74a4d8c2SCharles.Forsyth 	devremove,
742*74a4d8c2SCharles.Forsyth 	devwstat,
743*74a4d8c2SCharles.Forsyth };
744