plan 9 kernel history: overview | file list | diff list

2000/0617/port/devloopback.c (diff list | history)

port/devloopback.c on 2000/0617
2000/0617    
#include	"u.h" 
#include	"../port/lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"../port/error.h" 
 
#include	"netif.h" 
 
typedef struct Link	Link; 
typedef struct Loop	Loop; 
 
struct Link 
{ 
	Lock; 
 
	int	ref; 
 
	int	nodrop;		/* disable dropping on iq overflow */ 
	int	soverflows;	/* packets dropped because iq overflowed */ 
	int	drops;		/* packets deliberately dropped */ 
 
	long	delay0;		/* fastticks of delay in the link */ 
	long	delayn;		/* fastticks of delay per byte */ 
 
	Block	*tq;		/* transmission queue */ 
	Block	*tqtail; 
	vlong	tout;		/* time the last packet in tq is really out */ 
	vlong	tin;		/* time the head packet in tq enters the remote side  */ 
 
	Queue	*oq;		/* output queue from other side & packets in the link */ 
	Queue	*iq; 
}; 
 
struct Loop 
{ 
	QLock; 
	int	ref; 
	int	minmtu;		/* smallest block transmittable */ 
	Loop	*next; 
	ulong	path; 
	long	limit;		/* queue buffering limit */ 
	Link	link[2]; 
}; 
 
static struct 
{ 
	Lock; 
	ulong	path; 
} loopbackalloc; 
 
enum 
{ 
	Qdir, 
	Qctl, 
	Qstatus, 
	Qstats, 
	Qdata0, 
	Qdata1, 
 
	TMSIZE		= 8, 
 
	NLOOPBACKS	= 1, 
	LOOPBACKSIZE	= 32*1024,		/*ZZZ change to settable; size of queues */ 
}; 
 
Dirtab loopbackdir[] = 
{ 
	"ctl",		{Qctl},		0,			0222, 
	"status",	{Qstatus},	0,			0222, 
	"stats",	{Qstats},	0,			0444, 
	"data",		{Qdata0},	0,			0666, 
	"data1",	{Qdata1},	0,			0666, 
}; 
 
static Loop	loopbacks[NLOOPBACKS]; 
 
static void	looper(Loop *lb); 
static long	loopoput(Loop *lb, Link *link, Block *bp); 
static void	ptime(uchar *p, vlong t); 
static vlong	gtime(uchar *p); 
static void	closelink(Link *link, int dofree); 
static vlong	pushlink(Link *link, vlong now); 
static void	freelb(Loop *lb); 
 
static void 
loopbackinit(void) 
{ 
	int i; 
 
	for(i = 0; i < NLOOPBACKS; i++) 
		loopbacks[i].path = i; 
} 
 
static Chan* 
loopbackattach(char *spec) 
{ 
	Loop *lb; 
	Queue *q; 
	Chan *c; 
	int chan; 
 
	c = devattach('X', spec); 
	lb = &loopbacks[0]; 
 
	qlock(lb); 
	if(waserror()){ 
		qunlock(lb); 
		nexterror(); 
	} 
 
	lb->ref++; 
	if(lb->ref == 1){ 
		lb->limit = LOOPBACKSIZE; 
		for(chan = 0; chan < 2; chan++){ 
			q = qopen(lb->limit, 0, 0, 0); 
			lb->link[chan].iq = q; 
			if(q == nil){ 
				freelb(lb); 
				exhausted("memory"); 
			} 
			q = qopen(lb->limit, 0, 0, 0); 
			lb->link[chan].oq = q; 
			if(q == nil){ 
				freelb(lb); 
				exhausted("memory"); 
			} 
			lb->link[chan].nodrop = 1; 
		} 
	} 
	poperror(); 
	qunlock(lb); 
 
	c->qid = (Qid){CHDIR|NETQID(2*lb->path, Qdir), 0}; 
	c->aux = lb; 
	c->dev = 0; 
	return c; 
} 
 
static Chan* 
loopbackclone(Chan *c, Chan *nc) 
{ 
	Loop *lb; 
	int chan; 
 
	lb = c->aux; 
	nc = devclone(c, nc); 
	qlock(lb); 
	lb->ref++; 
	if(c->flag & COPEN){ 
		switch(chan = NETTYPE(c->qid.path)){ 
		case Qdata0: 
		case Qdata1: 
			chan -= Qdata0; 
			lb->link[chan].ref++; 
			break; 
		} 
	} 
	qunlock(lb); 
	return nc; 
} 
 
static int 
loopbackgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp) 
{ 
	Loop *lb; 
	int id, len, chan; 
 
	if(i == DEVDOTDOT){ 
		devdir(c, c->qid, "#X", 0, eve, CHDIR|0555, dp); 
		return 1; 
	} 
 
	id = NETID(c->qid.path); 
	if(i > 1) 
		id++; 
	if(tab==nil || i>=ntab) 
		return -1; 
	tab += i; 
	lb = c->aux; 
	switch(chan = tab->qid.path){ 
	case Qdata0: 
	case Qdata1: 
		chan -= Qdata0; 
		len = qlen(lb->link[chan].iq); 
		break; 
	default: 
		len = tab->length; 
		break; 
	} 
	devdir(c, (Qid){NETQID(id, tab->qid.path),0}, tab->name, len, eve, tab->perm, dp); 
	return 1; 
} 
 
 
static int 
loopbackwalk(Chan *c, char *name) 
{ 
	return devwalk(c, name, loopbackdir, nelem(loopbackdir), loopbackgen); 
} 
 
static void 
loopbackstat(Chan *c, char *db) 
{ 
	Loop *lb; 
	Dir dir; 
	int chan; 
 
	lb = c->aux; 
	switch(chan = NETTYPE(c->qid.path)){ 
	case Qdir: 
		devdir(c, c->qid, ".", nelem(loopbackdir)*DIRLEN, eve, CHDIR|0555, &dir); 
		break; 
	case Qdata0: 
	case Qdata1: 
		chan -= Qdata0; 
		devdir(c, c->qid, "data", qlen(lb->link[chan].iq), eve, 0660, &dir); 
		break; 
	default: 
		panic("loopbackstat"); 
	} 
	convD2M(&dir, db); 
} 
 
/* 
 *  if the stream doesn't exist, create it 
 */ 
static Chan* 
loopbackopen(Chan *c, int omode) 
{ 
	Loop *lb; 
	int chan; 
 
	if(c->qid.path & CHDIR){ 
		if(omode != OREAD) 
			error(Ebadarg); 
		c->mode = omode; 
		c->flag |= COPEN; 
		c->offset = 0; 
		return c; 
	} 
 
	lb = c->aux; 
	qlock(lb); 
	switch(chan = NETTYPE(c->qid.path)){ 
	case Qdata0: 
	case Qdata1: 
		chan -= Qdata0; 
		lb->link[chan].ref++; 
		break; 
	} 
	qunlock(lb); 
 
	c->mode = openmode(omode); 
	c->flag |= COPEN; 
	c->offset = 0; 
	return c; 
} 
 
static void 
loopbackclose(Chan *c) 
{ 
	Loop *lb; 
	int ref, chan; 
 
	lb = c->aux; 
 
	qlock(lb); 
	if(c->flag & COPEN){ 
		/* 
		 *  closing either side hangs up the stream 
		 */ 
		switch(chan = NETTYPE(c->qid.path)){ 
		case Qdata0: 
		case Qdata1: 
			chan -= Qdata0; 
			if(--lb->link[chan].ref == 0){ 
				qhangup(lb->link[chan ^ 1].oq, nil); 
				looper(lb); 
			} 
			break; 
		} 
	} 
 
 
	/* 
	 *  if both sides are closed, they are reusable 
	 */ 
	if(lb->link[0].ref == 0 && lb->link[1].ref == 0){ 
		for(chan = 0; chan < 2; chan++){ 
			closelink(&lb->link[chan], 0); 
			qreopen(lb->link[chan].iq); 
			qreopen(lb->link[chan].oq); 
		} 
	} 
	ref = --lb->ref; 
	if(ref == 0) 
		freelb(lb); 
	qunlock(lb); 
} 
 
static void 
freelb(Loop *lb) 
{ 
	int chan; 
 
	for(chan = 0; chan < 2; chan++) 
		closelink(&lb->link[chan], 1); 
} 
 
/* 
 * called with the Loop qlocked, 
 * so only pushlink can mess with the queues 
 */ 
static void 
closelink(Link *link, int dofree) 
{ 
	Queue *iq, *oq; 
	Block *bp; 
 
	ilock(link); 
	iq = link->iq; 
	oq = link->oq; 
	bp = link->tq; 
	link->tq = nil; 
	link->tqtail = nil; 
	link->tout = 0; 
	link->tin = 0; 
	iunlock(link); 
	if(iq != nil){ 
		qclose(iq); 
		if(dofree){ 
			ilock(link); 
			free(iq); 
			link->iq = nil; 
			iunlock(link); 
		} 
	} 
	if(oq != nil){ 
		qclose(oq); 
		if(dofree){ 
			ilock(link); 
			free(oq); 
			link->oq = nil; 
			iunlock(link); 
		} 
	} 
	freeblist(bp); 
} 
 
static long 
loopbackread(Chan *c, void *va, long n, vlong) 
{ 
	Loop *lb; 
	int chan; 
 
	lb = c->aux; 
//ZZZ ctl message to set q limit -- qsetlimit(q, limit) 
//ZZZ ctl message to set blocking/dropping	qnoblock(q, dropit) 
//ZZZ ctl message for delays 
	switch(chan = NETTYPE(c->qid.path)){ 
	case Qdir: 
		return devdirread(c, va, n, loopbackdir, nelem(loopbackdir), loopbackgen); 
	case Qdata0: 
	case Qdata1: 
		chan -= Qdata0; 
		return qread(lb->link[chan].iq, va, n); 
	default: 
		panic("loopbackread"); 
	} 
	return -1;	/* not reached */ 
} 
 
static Block* 
loopbackbread(Chan *c, long n, ulong offset) 
{ 
	Loop *lb; 
	int chan; 
 
	lb = c->aux; 
	switch(chan = NETTYPE(c->qid.path)){ 
	case Qdata0: 
	case Qdata1: 
		chan -= Qdata0; 
		return qbread(lb->link[chan].iq, n); 
	} 
 
	return devbread(c, n, offset); 
} 
 
static long 
loopbackbwrite(Chan *c, Block *bp, ulong off) 
{ 
	Loop *lb; 
	int chan; 
 
	lb = c->aux; 
	switch(chan = NETTYPE(c->qid.path)){ 
	case Qdata0: 
	case Qdata1: 
		chan -= Qdata0; 
		return loopoput(lb, &lb->link[chan ^ 1], bp); 
	default: 
		return devbwrite(c, bp, off); 
	} 
} 
 
static long 
loopbackwrite(Chan *c, void *va, long n, vlong off) 
{ 
	Block *bp; 
 
	if(!islo()) 
		print("loopbackwrite hi %lux\n", getcallerpc(&c)); 
 
	switch(NETTYPE(c->qid.path)){ 
	case Qdata0: 
	case Qdata1: 
		bp = allocb(n); 
		if(waserror()){ 
			freeb(bp); 
			nexterror(); 
		} 
		memmove(bp->wp, va, n); 
		poperror(); 
		bp->wp += n; 
		return loopbackbwrite(c, bp, off); 
	case Qctl: 
	default: 
		panic("loopbackwrite"); 
	} 
 
	return n; 
} 
 
static long 
loopoput(Loop *lb, Link *link, Block *bp) 
{ 
	long n; 
 
	n = BLEN(bp); 
 
	/* make it a single block with space for the loopback header */ 
	bp = padblock(bp, TMSIZE); 
	if(bp->next) 
		bp = concatblock(bp); 
	if(BLEN(bp) < lb->minmtu) 
		bp = adjustblock(bp, lb->minmtu); 
 
	qbwrite(link->oq, bp); 
	looper(lb); 
	return n; 
} 
 
/* 
 * move blocks between queues if they are ready. 
 * schedule an interrupt for the next interesting time 
 */ 
static void 
looper(Loop *lb) 
{ 
	vlong t, tt; 
 
	tt = fastticks(nil); 
again:; 
	t = pushlink(&lb->link[0], tt); 
	tt = pushlink(&lb->link[1], tt); 
	if(t > tt && tt) 
		t = tt; 
	if(t){ 
		tt = fastticks(nil); 
		if(tt <= t) 
			goto again; 
		//schedule an intr at tt-t fastticks 
	} 
} 
 
static vlong 
pushlink(Link *link, vlong now) 
{ 
	Block *bp; 
	vlong t; 
 
	/* 
	 * put another block in the link queue 
	 */ 
	ilock(link); 
	if(link->iq == nil || link->oq == nil){ 
		iunlock(link); 
		return 0; 
	} 
	t = link->tout; 
	if(!t || t < now){ 
		bp = qget(link->oq); 
		if(bp != nil){ 
			if(!t) 
				t = now; 
			link->tout = t + BLEN(bp) * link->delayn; 
			ptime(bp->rp, t + link->delay0); 
//ZZZ drop or introduce errors here 
			if(link->tq == nil) 
				link->tq = bp; 
			else 
				link->tqtail->next = bp; 
			link->tqtail = bp; 
		}else 
			link->tout = 0; 
	} 
 
	/* 
	 * put more blocks into the receive queue 
	 */ 
	t = 0; 
	while(bp = link->tq){ 
		t = gtime(bp->rp); 
		if(t > now) 
			break; 
		bp->rp += TMSIZE; 
		link->tq = bp->next; 
		bp->next = nil; 
		if(link->nodrop) 
			qpassnolim(link->iq, bp); 
		else if(qpass(link->iq, bp) < 0) 
			link->soverflows++; 
		t = 0; 
	} 
	if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq)) 
		qhangup(link->iq, nil); 
	link->tin = t; 
	if(!t || t < link->tout) 
		t = link->tout; 
	iunlock(link); 
	return t; 
} 
 
static void 
ptime(uchar *p, vlong t) 
{ 
	ulong tt; 
 
	tt = t >> 32; 
	p[0] = tt >> 24; 
	p[1] = tt >> 16; 
	p[2] = tt >> 8; 
	p[3] = tt; 
	tt = t; 
	p[4] = tt >> 24; 
	p[5] = tt >> 16; 
	p[6] = tt >> 8; 
	p[7] = tt; 
} 
 
static vlong 
gtime(uchar *p) 
{ 
	ulong t1, t2; 
 
	t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 
	t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]; 
	return ((vlong)t1 << 32) | t2; 
} 
 
Dev loopbackdevtab = { 
	'X', 
	"loopback", 
 
	devreset, 
	loopbackinit, 
	loopbackattach, 
	loopbackclone, 
	loopbackwalk, 
	loopbackstat, 
	loopbackopen, 
	devcreate, 
	loopbackclose, 
	loopbackread, 
	loopbackbread, 
	loopbackwrite, 
	loopbackbwrite, 
	devremove, 
	devwstat, 
}; 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)