Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/planix-v0/sys/src/9/port/devloopback.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
};