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