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