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

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

2000/0617/sys/src/9/port/devloopback.c:5,122000/0708/sys/src/9/port/devloopback.c:5,10 (short | long)
2000/0617    
#include	"fns.h" 
#include	"../port/error.h" 
 
#include	"netif.h" 
                 
typedef struct Link	Link; 
typedef struct Loop	Loop; 
 
2000/0617/sys/src/9/port/devloopback.c:16,272000/0708/sys/src/9/port/devloopback.c:14,30
2000/0617    
 
	int	ref; 
 
	int	nodrop;		/* disable dropping on iq overflow */ 
	int	soverflows;	/* packets dropped because iq overflowed */ 
	int	drops;		/* packets deliberately dropped */ 
2000/0708    
	long	packets;	/* total number of packets sent */ 
	long	bytes;		/* total number of bytes sent */ 
	int	indrop;		/* enable dropping on iq overflow */ 
	long	soverflows;	/* packets dropped because iq overflowed */ 
	long	droprate;	/* drop 1/droprate packets in tq */ 
	long	drops;		/* packets deliberately dropped */ 
2000/0617    
 
	long	delay0;		/* fastticks of delay in the link */ 
	long	delayn;		/* fastticks of delay per byte */ 
2000/0708    
	long	delay0ns;	/* nanosec of delay in the link */ 
	long	delaynns;	/* nanosec of delay per byte */ 
	long	delay0;		/* fastticks of delay */ 
	long	delayn; 
2000/0617    
 
	Block	*tq;		/* transmission queue */ 
	Block	*tqtail; 
2000/0617/sys/src/9/port/devloopback.c:28,352000/0708/sys/src/9/port/devloopback.c:31,41
2000/0617    
	vlong	tout;		/* time the last packet in tq is really out */ 
	vlong	tin;		/* time the head packet in tq enters the remote side  */ 
 
2000/0708    
	long	limit;		/* queue buffering limit */ 
2000/0617    
	Queue	*oq;		/* output queue from other side & packets in the link */ 
	Queue	*iq; 
2000/0708    
 
	Cycintr	ci;		/* time to move packets from  next packet from oq */ 
2000/0617    
}; 
 
struct Loop 
2000/0617/sys/src/9/port/devloopback.c:39,452000/0708/sys/src/9/port/devloopback.c:45,50
2000/0617    
	int	minmtu;		/* smallest block transmittable */ 
	Loop	*next; 
	ulong	path; 
	long	limit;		/* queue buffering limit */ 
	Link	link[2]; 
}; 
 
2000/0617/sys/src/9/port/devloopback.c:51,872000/0708/sys/src/9/port/devloopback.c:56,111
2000/0617    
 
enum 
{ 
	Qdir, 
2000/0708    
	Qtopdir=	1,		/* top level directory */ 
 
	Qloopdir,			/* loopback* directory */ 
 
	Qportdir,			/* directory each end of the loop */ 
2000/0617    
	Qctl, 
	Qstatus, 
	Qstats, 
	Qdata0, 
	Qdata1, 
2000/0708    
	Qdata, 
2000/0617    
 
	TMSIZE		= 8, 
2000/0708    
	MaxQ, 
2000/0617    
 
	NLOOPBACKS	= 1, 
	LOOPBACKSIZE	= 32*1024,		/*ZZZ change to settable; size of queues */ 
2000/0708    
	Nloopbacks	= 1, 
 
	Statelen	= 23*1024,	/* status buffer size */ 
 
	Tmsize		= 8, 
	Delayn 		= 10000,	/* default delays */ 
	Delay0 		= 2500000, 
 
	Loopqlim	= 32*1024,	/* default size of queues */ 
2000/0617    
}; 
 
Dirtab loopbackdir[] = 
2000/0708    
static Dirtab loopportdir[] = 
2000/0617    
{ 
	"ctl",		{Qctl},		0,			0222, 
	"status",	{Qstatus},	0,			0222, 
2000/0708    
	"status",	{Qstatus},	0,			0444, 
2000/0617    
	"stats",	{Qstats},	0,			0444, 
	"data",		{Qdata0},	0,			0666, 
	"data1",	{Qdata1},	0,			0666, 
2000/0708    
	"data",		{Qdata},	0,			0666, 
2000/0617    
}; 
2000/0708    
static Dirtab loopdirs[MaxQ]; 
2000/0617    
 
static Loop	loopbacks[NLOOPBACKS]; 
2000/0708    
static Loop	loopbacks[Nloopbacks]; 
2000/0617    
 
2000/0708    
static uvlong	fasthz; 
 
#define TYPE(x) 	((x)&0xff) 
#define ID(x) 		(((x)&~CHDIR)>>8) 
#define QID(x,y) 	(((x)<<8)|(y)) 
 
#define NS2FASTHZ(t)	((fasthz*(t))/1000000000); 
 
2000/0617    
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); 
2000/0708    
static void	pushlink(Link *link, vlong now); 
2000/0617    
static void	freelb(Loop *lb); 
2000/0708    
static void	linkintr(Ureg*, Cycintr *ci); 
2000/0617    
 
static void 
loopbackinit(void) 
2000/0617/sys/src/9/port/devloopback.c:88,952000/0708/sys/src/9/port/devloopback.c:112,123
2000/0617    
{ 
	int i; 
 
	for(i = 0; i < NLOOPBACKS; i++) 
2000/0708    
	for(i = 0; i < Nloopbacks; i++) 
2000/0617    
		loopbacks[i].path = i; 
2000/0708    
 
	/* invert directory tables for non-directory entries */ 
	for(i=0; i<nelem(loopportdir); i++) 
		loopdirs[loopportdir[i].qid.path] = loopportdir[i]; 
2000/0617    
} 
 
static Chan* 
2000/0617/sys/src/9/port/devloopback.c:99,1102000/0708/sys/src/9/port/devloopback.c:127,150
2000/0617    
	Queue *q; 
	Chan *c; 
	int chan; 
2000/0708    
	int dev; 
2000/0617    
 
2000/0708    
	if(!havecycintr()) 
		error("can't time packets"); 
 
	dev = 0; 
	if(spec != nil){ 
		dev = atoi(spec); 
		if(dev >= Nloopbacks) 
			error(Ebadspec); 
	} 
 
2000/0617    
	c = devattach('X', spec); 
	lb = &loopbacks[0]; 
2000/0708    
	lb = &loopbacks[dev]; 
2000/0617    
 
	qlock(lb); 
	if(waserror()){ 
2000/0708    
		lb->ref--; 
2000/0617    
		qunlock(lb); 
		nexterror(); 
	} 
2000/0617/sys/src/9/port/devloopback.c:111,1372000/0708/sys/src/9/port/devloopback.c:151,185
2000/0617    
 
	lb->ref++; 
	if(lb->ref == 1){ 
		lb->limit = LOOPBACKSIZE; 
2000/0708    
		fastticks(&fasthz); 
2000/0617    
		for(chan = 0; chan < 2; chan++){ 
			q = qopen(lb->limit, 0, 0, 0); 
2000/0708    
			lb->link[chan].ci.a = &lb->link[chan]; 
			lb->link[chan].ci.f = linkintr; 
			lb->link[chan].limit = Loopqlim; 
			q = qopen(lb->link[chan].limit, 0, 0, 0); 
2000/0617    
			lb->link[chan].iq = q; 
			if(q == nil){ 
				freelb(lb); 
				exhausted("memory"); 
			} 
			q = qopen(lb->limit, 0, 0, 0); 
2000/0708    
			q = qopen(lb->link[chan].limit, 0, 0, 0); 
2000/0617    
			lb->link[chan].oq = q; 
			if(q == nil){ 
				freelb(lb); 
				exhausted("memory"); 
			} 
			lb->link[chan].nodrop = 1; 
2000/0708    
			lb->link[chan].indrop = 1; 
 
			lb->link[chan].delayn = NS2FASTHZ(Delayn); 
			lb->link[chan].delaynns = Delayn; 
			lb->link[chan].delay0 = NS2FASTHZ(Delay0); 
			lb->link[chan].delay0ns = Delay0; 
2000/0617    
		} 
	} 
	poperror(); 
	qunlock(lb); 
 
	c->qid = (Qid){CHDIR|NETQID(2*lb->path, Qdir), 0}; 
2000/0708    
	c->qid = (Qid){CHDIR|QID(0, Qtopdir), 0}; 
2000/0617    
	c->aux = lb; 
	c->dev = 0; 
	return c; 
2000/0617/sys/src/9/port/devloopback.c:141,1952000/0708/sys/src/9/port/devloopback.c:189,267
2000/0617    
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; 
		} 
	} 
2000/0708    
	if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) 
		lb->link[ID(c->qid.path)].ref++; 
2000/0617    
	qunlock(lb); 
	return nc; 
} 
 
static int 
loopbackgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp) 
2000/0708    
loopbackgen(Chan *c, Dirtab*, int, int i, Dir *dp) 
2000/0617    
{ 
	Loop *lb; 
	int id, len, chan; 
2000/0708    
	Dirtab *tab; 
	char buf[NAMELEN]; 
	int len, type; 
2000/0617    
 
2000/0708    
	type = TYPE(c->qid.path); 
2000/0617    
	if(i == DEVDOTDOT){ 
		devdir(c, c->qid, "#X", 0, eve, CHDIR|0555, dp); 
2000/0708    
		switch(type){ 
		case Qtopdir: 
		case Qloopdir: 
			snprint(buf, sizeof(buf), "#X%ld", c->dev); 
			devdir(c, (Qid){CHDIR|QID(0, Qtopdir), 0}, buf, 0, eve, 0555, dp); 
			break; 
		case Qportdir: 
			snprint(buf, sizeof(buf), "loopback%ld", c->dev); 
			devdir(c, (Qid){CHDIR|QID(0, Qloopdir), 0}, buf, 0, eve, 0555, dp); 
			break; 
		default: 
			panic("loopbackgen %lux", c->qid.path); 
		} 
2000/0617    
		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; 
2000/0708    
	switch(type){ 
	case Qtopdir: 
		if(i != 0) 
			return -1; 
		snprint(buf, sizeof(buf), "loopback%ld", c->dev); 
		devdir(c, (Qid){QID(0, Qloopdir) | CHDIR, 0}, buf, 0, eve, 0555, dp); 
		return 1; 
	case Qloopdir: 
		if(i >= 2) 
			return -1; 
		snprint(buf, sizeof(buf), "%d", i); 
		devdir(c, (Qid){QID(i, QID(0, Qportdir)) | CHDIR, 0}, buf, 0, eve, 0555, dp); 
		return 1; 
	case Qportdir: 
		if(i >= nelem(loopportdir)) 
			return -1; 
		tab = &loopportdir[i]; 
		devdir(c, (Qid){QID(ID(c->qid.path), tab->qid.path), 0}, tab->name, tab->length, eve, tab->perm, dp); 
		return 1; 
2000/0617    
	default: 
2000/0708    
		/* non directory entries end up here; must be in lowest level */ 
		if(c->qid.path & CHDIR) 
			panic("loopbackgen: unexpected directory");	 
		if(i != 0) 
			return -1; 
		tab = &loopdirs[type]; 
		if(tab == nil) 
			panic("loopbackgen: unknown type: %d", type); 
2000/0617    
		len = tab->length; 
		break; 
2000/0708    
		if(type == Qdata){ 
			lb = c->aux; 
			len = qlen(lb->link[ID(c->qid.path)].iq); 
		} 
		devdir(c, c->qid, tab->name, len, eve, tab->perm, dp); 
		return 1; 
2000/0617    
	} 
	devdir(c, (Qid){NETQID(id, tab->qid.path),0}, tab->name, len, eve, tab->perm, dp); 
	return 1; 
} 
 
 
2000/0617/sys/src/9/port/devloopback.c:196,2252000/0708/sys/src/9/port/devloopback.c:268,280
2000/0617    
static int 
loopbackwalk(Chan *c, char *name) 
{ 
	return devwalk(c, name, loopbackdir, nelem(loopbackdir), loopbackgen); 
2000/0708    
	return devwalk(c, name, nil, 0, loopbackgen); 
2000/0617    
} 
 
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); 
2000/0708    
	devstat(c, db, nil, 0, loopbackgen); 
2000/0617    
} 
 
/* 
2000/0617/sys/src/9/port/devloopback.c:229,2352000/0708/sys/src/9/port/devloopback.c:284,289
2000/0617    
loopbackopen(Chan *c, int omode) 
{ 
	Loop *lb; 
	int chan; 
 
	if(c->qid.path & CHDIR){ 
		if(omode != OREAD) 
2000/0617/sys/src/9/port/devloopback.c:242,2542000/0708/sys/src/9/port/devloopback.c:296,303
2000/0617    
 
	lb = c->aux; 
	qlock(lb); 
	switch(chan = NETTYPE(c->qid.path)){ 
	case Qdata0: 
	case Qdata1: 
		chan -= Qdata0; 
		lb->link[chan].ref++; 
		break; 
	} 
2000/0708    
	if(TYPE(c->qid.path) == Qdata) 
		lb->link[ID(c->qid.path)].ref++; 
2000/0617    
	qunlock(lb); 
 
	c->mode = openmode(omode); 
2000/0617/sys/src/9/port/devloopback.c:266,2842000/0708/sys/src/9/port/devloopback.c:315,329
2000/0617    
	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; 
2000/0708    
 
	/* 
	 * closing either side hangs up the stream 
	 */ 
	if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){ 
		chan = ID(c->qid.path); 
		if(--lb->link[chan].ref == 0){ 
			qhangup(lb->link[chan ^ 1].oq, nil); 
			looper(lb); 
2000/0617    
		} 
	} 
 
2000/0617/sys/src/9/port/devloopback.c:291,2962000/0708/sys/src/9/port/devloopback.c:336,343
2000/0617    
			closelink(&lb->link[chan], 0); 
			qreopen(lb->link[chan].iq); 
			qreopen(lb->link[chan].oq); 
2000/0708    
			qsetlimit(lb->link[chan].oq, lb->link[chan].limit); 
			qsetlimit(lb->link[chan].iq, lb->link[chan].limit); 
2000/0617    
		} 
	} 
	ref = --lb->ref; 
2000/0617/sys/src/9/port/devloopback.c:326,3312000/0708/sys/src/9/port/devloopback.c:373,379
2000/0617    
	link->tqtail = nil; 
	link->tout = 0; 
	link->tin = 0; 
2000/0708    
	cycintrdel(&link->ci); 
2000/0617    
	iunlock(link); 
	if(iq != nil){ 
		qclose(iq); 
2000/0617/sys/src/9/port/devloopback.c:349,3742000/0708/sys/src/9/port/devloopback.c:397,442
2000/0617    
} 
 
static long 
loopbackread(Chan *c, void *va, long n, vlong) 
2000/0708    
loopbackread(Chan *c, void *va, long n, vlong offset) 
2000/0617    
{ 
	Loop *lb; 
	int chan; 
2000/0708    
	Link *link; 
	char *buf; 
	long rv; 
2000/0617    
 
	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); 
2000/0708    
	switch(TYPE(c->qid.path)){ 
2000/0617    
	default: 
		panic("loopbackread"); 
2000/0708    
		error(Eperm); 
		return -1;	/* not reached */ 
	case Qtopdir: 
	case Qloopdir: 
	case Qportdir: 
		return devdirread(c, va, n, nil, 0, loopbackgen); 
	case Qdata: 
		return qread(lb->link[ID(c->qid.path)].iq, va, n); 
	case Qstatus: 
		link = &lb->link[ID(c->qid.path)]; 
		buf = smalloc(Statelen); 
		rv = snprint(buf, Statelen, "delay %ld %ld\n", link->delay0ns, link->delaynns); 
		rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit); 
		rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop); 
		snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate); 
		rv = readstr(offset, va, n, buf); 
		free(buf); 
		break; 
	case Qstats: 
		link = &lb->link[ID(c->qid.path)]; 
		buf = smalloc(Statelen); 
		rv = snprint(buf, Statelen, "packets: %ld\n", link->packets); 
		rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes); 
		rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops); 
		snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows); 
		rv = readstr(offset, va, n, buf); 
		free(buf); 
		break; 
2000/0617    
	} 
	return -1;	/* not reached */ 
2000/0708    
	return rv; 
2000/0617    
} 
 
static Block* 
2000/0617/sys/src/9/port/devloopback.c:375,3892000/0708/sys/src/9/port/devloopback.c:443,452
2000/0617    
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); 
	} 
2000/0708    
	if(TYPE(c->qid.path) == Qdata) 
		return qbread(lb->link[ID(c->qid.path)].iq, n); 
2000/0617    
 
	return devbread(c, n, offset); 
} 
2000/0617/sys/src/9/port/devloopback.c:392,4212000/0708/sys/src/9/port/devloopback.c:455,478
2000/0617    
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); 
	} 
2000/0708    
	if(TYPE(c->qid.path) == Qdata) 
		return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp); 
	return devbwrite(c, bp, off); 
2000/0617    
} 
 
static long 
loopbackwrite(Chan *c, void *va, long n, vlong off) 
{ 
2000/0708    
	Loop *lb; 
	Link *link; 
	Cmdbuf *cb; 
2000/0617    
	Block *bp; 
2000/0708    
	long d0, dn, d0ns, dnns; 
2000/0617    
 
	if(!islo()) 
		print("loopbackwrite hi %lux\n", getcallerpc(&c)); 
                 
	switch(NETTYPE(c->qid.path)){ 
	case Qdata0: 
	case Qdata1: 
2000/0708    
	switch(TYPE(c->qid.path)){ 
	case Qdata: 
2000/0617    
		bp = allocb(n); 
		if(waserror()){ 
			freeb(bp); 
2000/0617/sys/src/9/port/devloopback.c:426,4332000/0708/sys/src/9/port/devloopback.c:483,547
2000/0617    
		bp->wp += n; 
		return loopbackbwrite(c, bp, off); 
	case Qctl: 
2000/0708    
		lb = c->aux; 
		link = &lb->link[ID(c->qid.path)]; 
		cb = parsecmd(va, n); 
		if(cb->nf < 1) 
			error("short control request"); 
		if(strcmp(cb->f[0], "delay") == 0){ 
			if(cb->nf != 3) 
				error("usage: delay latency bytedelay"); 
			d0ns = strtol(cb->f[1], nil, 10); 
			dnns = strtol(cb->f[2], nil, 10); 
 
			/* 
			 * it takes about 20000 cycles on a pentium ii 
			 * to run pushlink; perhaps this should be accounted. 
			 */ 
			d0 = NS2FASTHZ(d0ns); 
			dn = NS2FASTHZ(dnns); 
 
			ilock(link); 
			link->delay0 = d0; 
			link->delayn = dn; 
			link->delay0ns = d0ns; 
			link->delaynns = dnns; 
			iunlock(link); 
		}else if(strcmp(cb->f[0], "indrop") == 0){ 
			if(cb->nf != 2) 
				error("usage: indrop [01]"); 
			ilock(link); 
			link->indrop = strtol(cb->f[1], nil, 0) != 0; 
			iunlock(link); 
		}else if(strcmp(cb->f[0], "droprate") == 0){ 
			if(cb->nf != 2) 
				error("usage: droprate ofn"); 
			ilock(link); 
			link->droprate = strtol(cb->f[1], nil, 0); 
			iunlock(link); 
		}else if(strcmp(cb->f[0], "limit") == 0){ 
			if(cb->nf != 2) 
				error("usage: droprate ofn"); 
			ilock(link); 
			link->limit = strtol(cb->f[1], nil, 0); 
			qsetlimit(link->oq, link->limit); 
			qsetlimit(link->iq, link->limit); 
			iunlock(link); 
		}else if(strcmp(cb->f[0], "reset") == 0){ 
			if(cb->nf != 1) 
				error("usage: reset"); 
			ilock(link); 
			link->packets = 0; 
			link->bytes = 0; 
			link->indrop = 0; 
			link->soverflows = 0; 
			link->drops = 0; 
			iunlock(link); 
		}else 
			error("unknown control request"); 
		break; 
2000/0617    
	default: 
		panic("loopbackwrite"); 
2000/0708    
		error(Eperm); 
2000/0617    
	} 
 
	return n; 
2000/0617/sys/src/9/port/devloopback.c:440,4852000/0708/sys/src/9/port/devloopback.c:554,608
2000/0617    
 
	n = BLEN(bp); 
 
	/* make it a single block with space for the loopback header */ 
	bp = padblock(bp, TMSIZE); 
2000/0708    
	/* make it a single block with space for the loopback timing header */ 
	bp = padblock(bp, Tmsize); 
2000/0617    
	if(bp->next) 
		bp = concatblock(bp); 
	if(BLEN(bp) < lb->minmtu) 
		bp = adjustblock(bp, lb->minmtu); 
2000/0708    
	ptime(bp->rp, fastticks(nil)); 
2000/0617    
 
2000/0708    
	link->packets++; 
	link->bytes += n; 
 
2000/0617    
	qbwrite(link->oq, bp); 
2000/0708    
 
2000/0617    
	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; 
2000/0708    
	vlong t; 
	int chan; 
2000/0617    
 
	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 
	} 
2000/0708    
	t = fastticks(nil); 
	for(chan = 0; chan < 2; chan++) 
		pushlink(&lb->link[chan], t); 
	clockintrsched(); 
2000/0617    
} 
 
static vlong 
2000/0708    
static void 
linkintr(Ureg*, Cycintr *ci) 
{ 
	Link *link; 
 
	link = ci->a; 
	pushlink(link, ci->when); 
} 
 
/* 
 * move blocks between queues if they are ready. 
 * schedule an interrupt for the next interesting time. 
 * 
 * must be called with the link ilocked. 
 */ 
static void 
2000/0617    
pushlink(Link *link, vlong now) 
{ 
	Block *bp; 
	vlong t; 
2000/0708    
	vlong tout, tin; 
2000/0617    
 
	/* 
	 * put another block in the link queue 
2000/0617/sys/src/9/port/devloopback.c:487,5362000/0708/sys/src/9/port/devloopback.c:610,698
2000/0617    
	ilock(link); 
	if(link->iq == nil || link->oq == nil){ 
		iunlock(link); 
		return 0; 
2000/0708    
		return; 
 
2000/0617    
	} 
	t = link->tout; 
	if(!t || t < now){ 
2000/0708    
	cycintrdel(&link->ci); 
 
	/* 
	 * put more blocks into the xmit queue 
	 * use the time the last packet was supposed to go out 
	 * as the start time for the next packet, rather than 
	 * the current time.  this more closely models a network 
	 * device which can queue multiple output packets. 
	 */ 
	tout = link->tout; 
	if(!tout) 
		tout = now; 
	while(tout <= now){ 
2000/0617    
		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 
2000/0708    
		if(bp == nil){ 
			tout = 0; 
			break; 
		} 
 
		/* 
		 * can't send the packet before it gets queued 
		 */ 
		tin = gtime(bp->rp); 
		if(tin > tout) 
			tout = tin; 
		tout = tout + (BLEN(bp) - Tmsize) * link->delayn; 
 
		/* 
		 * drop packets 
		 */ 
		if(link->droprate && nrand(link->droprate) == 0) 
			link->drops++; 
		else{ 
			ptime(bp->rp, tout + link->delay0); 
2000/0617    
			if(link->tq == nil) 
				link->tq = bp; 
			else 
				link->tqtail->next = bp; 
			link->tqtail = bp; 
		}else 
			link->tout = 0; 
2000/0708    
		} 
2000/0617    
	} 
 
	/* 
2000/0708    
	 * record the next time a packet can be sent, 
	 * but don't schedule an interrupt if none is waiting 
	 */ 
	link->tout = tout; 
	if(!qcanread(link->oq)) 
		tout = 0; 
 
	/* 
2000/0617    
	 * put more blocks into the receive queue 
	 */ 
	t = 0; 
2000/0708    
	tin = 0; 
2000/0617    
	while(bp = link->tq){ 
		t = gtime(bp->rp); 
		if(t > now) 
2000/0708    
		tin = gtime(bp->rp); 
		if(tin > now) 
2000/0617    
			break; 
		bp->rp += TMSIZE; 
2000/0708    
		bp->rp += Tmsize; 
2000/0617    
		link->tq = bp->next; 
		bp->next = nil; 
		if(link->nodrop) 
2000/0708    
		if(!link->indrop) 
2000/0617    
			qpassnolim(link->iq, bp); 
		else if(qpass(link->iq, bp) < 0) 
			link->soverflows++; 
		t = 0; 
2000/0708    
		tin = 0; 
2000/0617    
	} 
	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; 
2000/0708    
	link->tin = tin; 
	if(!tin || tin > tout && tout) 
		tin = tout; 
 
	link->ci.when = tin; 
	if(tin){ 
		if(tin < now) 
			panic("loopback unfinished business"); 
		cycintradd(&link->ci); 
	} 
2000/0617    
	iunlock(link); 
	return t; 
} 
 
static void 
2000/0708/sys/src/9/port/devloopback.c:521,5272000/0726/sys/src/9/port/devloopback.c:521,527 (short | long)
2000/0708    
			iunlock(link); 
		}else if(strcmp(cb->f[0], "limit") == 0){ 
			if(cb->nf != 2) 
				error("usage: droprate ofn"); 
2000/0726    
				error("usage: limit maxqsize"); 
2000/0708    
			ilock(link); 
			link->limit = strtol(cb->f[1], nil, 0); 
			qsetlimit(link->oq, link->limit); 
2000/0726/sys/src/9/port/devloopback.c:296,3032000/0727/sys/src/9/port/devloopback.c:296,308 (short | long)
2000/0617    
 
	lb = c->aux; 
	qlock(lb); 
2000/0708    
	if(TYPE(c->qid.path) == Qdata) 
2000/0727    
	if(TYPE(c->qid.path) == Qdata){ 
		if(lb->link[ID(c->qid.path)].ref){ 
			qunlock(lb); 
			error(Einuse); 
		} 
2000/0708    
		lb->link[ID(c->qid.path)].ref++; 
2000/0727    
	} 
2000/0617    
	qunlock(lb); 
 
	c->mode = openmode(omode); 
2000/0727/sys/src/9/port/devloopback.c:21,292000/0912/sys/src/9/port/devloopback.c:21,29 (short | long)
2000/0708    
	long	droprate;	/* drop 1/droprate packets in tq */ 
	long	drops;		/* packets deliberately dropped */ 
2000/0617    
 
2000/0708    
	long	delay0ns;	/* nanosec of delay in the link */ 
2000/0912    
	vlong	delay0ns;	/* nanosec of delay in the link */ 
2000/0708    
	long	delaynns;	/* nanosec of delay per byte */ 
	long	delay0;		/* fastticks of delay */ 
2000/0912    
	vlong	delay0;		/* fastticks of delay */ 
2000/0708    
	long	delayn; 
2000/0617    
 
	Block	*tq;		/* transmission queue */ 
2000/0727/sys/src/9/port/devloopback.c:68,742000/0912/sys/src/9/port/devloopback.c:68,74
2000/0617    
 
2000/0708    
	MaxQ, 
2000/0617    
 
2000/0708    
	Nloopbacks	= 1, 
2000/0912    
	Nloopbacks	= 5, 
2000/0708    
 
	Statelen	= 23*1024,	/* status buffer size */ 
 
2000/0727/sys/src/9/port/devloopback.c:423,4292000/0912/sys/src/9/port/devloopback.c:423,429
2000/0708    
	case Qstatus: 
		link = &lb->link[ID(c->qid.path)]; 
		buf = smalloc(Statelen); 
		rv = snprint(buf, Statelen, "delay %ld %ld\n", link->delay0ns, link->delaynns); 
2000/0912    
		rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns); 
2000/0708    
		rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit); 
		rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop); 
		snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate); 
2000/0727/sys/src/9/port/devloopback.c:474,4802000/0912/sys/src/9/port/devloopback.c:474,481
2000/0708    
	Link *link; 
	Cmdbuf *cb; 
2000/0617    
	Block *bp; 
2000/0708    
	long d0, dn, d0ns, dnns; 
2000/0912    
	vlong d0, d0ns; 
	long dn, dnns; 
2000/0617    
 
2000/0708    
	switch(TYPE(c->qid.path)){ 
	case Qdata: 
2000/0727/sys/src/9/port/devloopback.c:496,5022000/0912/sys/src/9/port/devloopback.c:497,503
2000/0708    
		if(strcmp(cb->f[0], "delay") == 0){ 
			if(cb->nf != 3) 
				error("usage: delay latency bytedelay"); 
			d0ns = strtol(cb->f[1], nil, 10); 
2000/0912    
			d0ns = strtoll(cb->f[1], nil, 10); 
2000/0708    
			dnns = strtol(cb->f[2], nil, 10); 
 
			/* 
2000/0912/sys/src/9/port/devloopback.c:181,1872000/0913/sys/src/9/port/devloopback.c:181,187 (short | long)
2000/0617    
 
2000/0708    
	c->qid = (Qid){CHDIR|QID(0, Qtopdir), 0}; 
2000/0617    
	c->aux = lb; 
	c->dev = 0; 
2000/0913    
	c->dev = dev; 
2000/0617    
	return c; 
} 
 
2000/0913/sys/src/9/port/devloopback.c:123,1292001/0331/sys/src/9/port/devloopback.c:123,129 (short | long)
2000/0617    
static Chan* 
loopbackattach(char *spec) 
{ 
	Loop *lb; 
2001/0331    
	Loop *volatile lb; 
2000/0617    
	Queue *q; 
	Chan *c; 
	int chan; 
2000/0913/sys/src/9/port/devloopback.c:472,4792001/0331/sys/src/9/port/devloopback.c:472,479
2000/0617    
{ 
2000/0708    
	Loop *lb; 
	Link *link; 
	Cmdbuf *cb; 
2000/0617    
	Block *bp; 
2001/0331    
	Cmdbuf *volatile cb; 
	Block *volatile bp; 
2000/0912    
	vlong d0, d0ns; 
	long dn, dnns; 
2000/0617    
 
2000/0913/sys/src/9/port/devloopback.c:492,4972001/0331/sys/src/9/port/devloopback.c:492,501
2000/0708    
		lb = c->aux; 
		link = &lb->link[ID(c->qid.path)]; 
		cb = parsecmd(va, n); 
2001/0331    
		if(waserror()){ 
			free(cb); 
			nexterror(); 
		} 
2000/0708    
		if(cb->nf < 1) 
			error("short control request"); 
		if(strcmp(cb->f[0], "delay") == 0){ 
2000/0913/sys/src/9/port/devloopback.c:545,5502001/0331/sys/src/9/port/devloopback.c:549,555
2000/0708    
			iunlock(link); 
		}else 
			error("unknown control request"); 
2001/0331    
		free(cb); 
2000/0708    
		break; 
2000/0617    
	default: 
2000/0708    
		error(Eperm); 
2001/0331/sys/src/9/port/devloopback.c:559,5652001/0504/sys/src/9/port/devloopback.c:559,565 (short | long)
2000/0617    
} 
 
static long 
loopoput(Loop *lb, Link *link, Block *bp) 
2001/0504    
loopoput(Loop *lb, Link *link, Block *volatile bp) 
2000/0617    
{ 
	long n; 
 
2001/0331/sys/src/9/port/devloopback.c:566,5762001/0504/sys/src/9/port/devloopback.c:566,581
2000/0617    
	n = BLEN(bp); 
 
2000/0708    
	/* make it a single block with space for the loopback timing header */ 
2001/0504    
	if(waserror()){ 
		freeb(bp); 
		nexterror(); 
	} 
2000/0708    
	bp = padblock(bp, Tmsize); 
2000/0617    
	if(bp->next) 
		bp = concatblock(bp); 
	if(BLEN(bp) < lb->minmtu) 
		bp = adjustblock(bp, lb->minmtu); 
2001/0504    
	poperror(); 
2000/0708    
	ptime(bp->rp, fastticks(nil)); 
2000/0617    
 
2000/0708    
	link->packets++; 
2001/0504/sys/src/9/port/devloopback.c:92,1002001/0530/sys/src/9/port/devloopback.c:92,100 (short | long)
2000/0617    
 
2000/0708    
static uvlong	fasthz; 
 
#define TYPE(x) 	((x)&0xff) 
#define ID(x) 		(((x)&~CHDIR)>>8) 
#define QID(x,y) 	(((x)<<8)|(y)) 
2001/0530    
#define TYPE(x) 	(((ulong)(x))&0xff) 
#define ID(x) 		(((ulong)(x))>>8) 
#define QID(x,y) 	((((ulong)(x))<<8)|((ulong)(y))) 
2000/0708    
 
#define NS2FASTHZ(t)	((fasthz*(t))/1000000000); 
 
2001/0504/sys/src/9/port/devloopback.c:179,1852001/0530/sys/src/9/port/devloopback.c:179,185
2000/0617    
	poperror(); 
	qunlock(lb); 
 
2000/0708    
	c->qid = (Qid){CHDIR|QID(0, Qtopdir), 0}; 
2001/0530    
	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); 
2000/0617    
	c->aux = lb; 
2000/0913    
	c->dev = dev; 
2000/0617    
	return c; 
2001/0504/sys/src/9/port/devloopback.c:201,2122001/0530/sys/src/9/port/devloopback.c:201,212
2000/0617    
} 
 
static int 
2000/0708    
loopbackgen(Chan *c, Dirtab*, int, int i, Dir *dp) 
2001/0530    
loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) 
2000/0617    
{ 
	Loop *lb; 
2000/0708    
	Dirtab *tab; 
	char buf[NAMELEN]; 
	int len, type; 
2001/0530    
	Qid qid; 
2000/0617    
 
2000/0708    
	type = TYPE(c->qid.path); 
2000/0617    
	if(i == DEVDOTDOT){ 
2001/0504/sys/src/9/port/devloopback.c:213,2242001/0530/sys/src/9/port/devloopback.c:213,226
2000/0708    
		switch(type){ 
		case Qtopdir: 
		case Qloopdir: 
			snprint(buf, sizeof(buf), "#X%ld", c->dev); 
			devdir(c, (Qid){CHDIR|QID(0, Qtopdir), 0}, buf, 0, eve, 0555, dp); 
2001/0530    
			snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev); 
			mkqid(&qid, QID(0, Qtopdir), 0, QTDIR); 
			devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 
2000/0708    
			break; 
		case Qportdir: 
			snprint(buf, sizeof(buf), "loopback%ld", c->dev); 
			devdir(c, (Qid){CHDIR|QID(0, Qloopdir), 0}, buf, 0, eve, 0555, dp); 
2001/0530    
			snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); 
			mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); 
			devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 
2000/0708    
			break; 
		default: 
			panic("loopbackgen %lux", c->qid.path); 
2001/0504/sys/src/9/port/devloopback.c:230,2532001/0530/sys/src/9/port/devloopback.c:232,258
2000/0708    
	case Qtopdir: 
		if(i != 0) 
			return -1; 
		snprint(buf, sizeof(buf), "loopback%ld", c->dev); 
		devdir(c, (Qid){QID(0, Qloopdir) | CHDIR, 0}, buf, 0, eve, 0555, dp); 
2001/0530    
		snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev); 
		mkqid(&qid, QID(0, Qloopdir), 0, QTDIR); 
		devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 
2000/0708    
		return 1; 
	case Qloopdir: 
		if(i >= 2) 
			return -1; 
		snprint(buf, sizeof(buf), "%d", i); 
		devdir(c, (Qid){QID(i, QID(0, Qportdir)) | CHDIR, 0}, buf, 0, eve, 0555, dp); 
2001/0530    
		snprint(up->genbuf, sizeof(up->genbuf), "%d", i); 
		mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR); 
		devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 
2000/0708    
		return 1; 
	case Qportdir: 
		if(i >= nelem(loopportdir)) 
			return -1; 
		tab = &loopportdir[i]; 
		devdir(c, (Qid){QID(ID(c->qid.path), tab->qid.path), 0}, tab->name, tab->length, eve, tab->perm, dp); 
2001/0530    
		mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTDIR); 
		devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); 
2000/0708    
		return 1; 
2000/0617    
	default: 
2000/0708    
		/* non directory entries end up here; must be in lowest level */ 
		if(c->qid.path & CHDIR) 
2001/0530    
		if(c->qid.type & QTDIR) 
2000/0708    
			panic("loopbackgen: unexpected directory");	 
		if(i != 0) 
			return -1; 
2001/0504/sys/src/9/port/devloopback.c:265,2802001/0530/sys/src/9/port/devloopback.c:270,285
2000/0617    
} 
 
 
static int 
loopbackwalk(Chan *c, char *name) 
2001/0530    
static Walkqid* 
loopbackwalk(Chan *c, Chan *nc, char **name, int nname) 
2000/0617    
{ 
2000/0708    
	return devwalk(c, name, nil, 0, loopbackgen); 
2001/0530    
	return devwalk(c, nc, name, nname, nil, 0, loopbackgen); 
2000/0617    
} 
 
static void 
loopbackstat(Chan *c, char *db) 
2001/0530    
static int 
loopbackstat(Chan *c, uchar *db, int n) 
2000/0617    
{ 
2000/0708    
	devstat(c, db, nil, 0, loopbackgen); 
2001/0530    
	return devstat(c, db, n, nil, 0, loopbackgen); 
2000/0617    
} 
 
/* 
2001/0504/sys/src/9/port/devloopback.c:285,2912001/0530/sys/src/9/port/devloopback.c:290,296
2000/0617    
{ 
	Loop *lb; 
 
	if(c->qid.path & CHDIR){ 
2001/0530    
	if(c->qid.type & QTDIR){ 
2000/0617    
		if(omode != OREAD) 
			error(Ebadarg); 
		c->mode = omode; 
2001/0504/sys/src/9/port/devloopback.c:745,7512001/0530/sys/src/9/port/devloopback.c:750,755
2000/0617    
	devreset, 
	loopbackinit, 
	loopbackattach, 
	loopbackclone, 
	loopbackwalk, 
	loopbackstat, 
	loopbackopen, 
2001/0530/sys/src/9/port/devloopback.c:185,2052001/0601/sys/src/9/port/devloopback.c:185,190 (short | long)
2000/0617    
	return c; 
} 
 
static Chan* 
loopbackclone(Chan *c, Chan *nc) 
{ 
	Loop *lb; 
                 
	lb = c->aux; 
	nc = devclone(c, nc); 
	qlock(lb); 
	lb->ref++; 
2000/0708    
	if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) 
		lb->link[ID(c->qid.path)].ref++; 
2000/0617    
	qunlock(lb); 
	return nc; 
} 
                 
static int 
2001/0530    
loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp) 
2000/0617    
{ 
2001/0530/sys/src/9/port/devloopback.c:247,2532001/0601/sys/src/9/port/devloopback.c:232,238
2000/0708    
		if(i >= nelem(loopportdir)) 
			return -1; 
		tab = &loopportdir[i]; 
2001/0530    
		mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTDIR); 
2001/0601    
		mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE); 
2001/0530    
		devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp); 
2000/0708    
		return 1; 
2000/0617    
	default: 
2001/0530/sys/src/9/port/devloopback.c:273,2792001/0601/sys/src/9/port/devloopback.c:258,276
2001/0530    
static Walkqid* 
loopbackwalk(Chan *c, Chan *nc, char **name, int nname) 
2000/0617    
{ 
2001/0530    
	return devwalk(c, nc, name, nname, nil, 0, loopbackgen); 
2001/0601    
	Walkqid *wq; 
	Loop *lb; 
 
	wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen); 
	if(wq != nil && wq->clone != nil && wq->clone != c){ 
		lb = c->aux; 
		qlock(lb); 
		lb->ref++; 
		if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata) 
			lb->link[ID(c->qid.path)].ref++; 
		qunlock(lb); 
	} 
	return wq; 
2000/0617    
} 
 
2001/0530    
static int 
2001/0601/sys/src/9/port/devloopback.c:551,5562001/0609/sys/src/9/port/devloopback.c:551,557 (short | long)
2000/0708    
			iunlock(link); 
		}else 
			error("unknown control request"); 
2001/0609    
		poperror(); 
2001/0331    
		free(cb); 
2000/0708    
		break; 
2000/0617    
	default: 
2001/0609/sys/src/9/port/devloopback.c:310,3152001/1207/sys/src/9/port/devloopback.c:310,316 (short | long)
2000/0617    
	c->mode = openmode(omode); 
	c->flag |= COPEN; 
	c->offset = 0; 
2001/1207    
	c->iounit = qiomaxatomic; 
2000/0617    
	return c; 
} 
 
2001/1207/sys/src/9/port/devloopback.c:748,7532002/0109/sys/src/9/port/devloopback.c:748,754 (short | long)
Add devshutdown.
rsc Fri Mar 4 12:44:25 2005
2000/0617    
 
	devreset, 
	loopbackinit, 
2002/0109    
	devshutdown, 
2000/0617    
	loopbackattach, 
	loopbackwalk, 
	loopbackstat, 
2002/0109/sys/src/9/port/devloopback.c:208,2142002/0125/sys/src/9/port/devloopback.c:208,214 (short | long)
Bug fix: print format (serious).
rsc Fri Mar 4 12:44:25 2005
2001/0530    
			devdir(c, qid, up->genbuf, 0, eve, 0555, dp); 
2000/0708    
			break; 
		default: 
			panic("loopbackgen %lux", c->qid.path); 
2002/0125    
			panic("loopbackgen %llux", c->qid.path); 
2000/0708    
		} 
2000/0617    
		return 1; 
	} 
2002/0125/sys/src/9/port/devloopback.c:35,412002/0405/sys/src/9/port/devloopback.c:35,41 (short | long)
2000/0617    
	Queue	*oq;		/* output queue from other side & packets in the link */ 
	Queue	*iq; 
2000/0708    
 
	Cycintr	ci;		/* time to move packets from  next packet from oq */ 
2002/0405    
	Timer	ci;		/* time to move packets from  next packet from oq */ 
2000/0617    
}; 
 
struct Loop 
2002/0125/sys/src/9/port/devloopback.c:105,1112002/0405/sys/src/9/port/devloopback.c:105,111
2000/0617    
static void	closelink(Link *link, int dofree); 
2000/0708    
static void	pushlink(Link *link, vlong now); 
2000/0617    
static void	freelb(Loop *lb); 
2000/0708    
static void	linkintr(Ureg*, Cycintr *ci); 
2002/0405    
static void	linkintr(Ureg*, Timer *ci); 
2000/0617    
 
static void 
loopbackinit(void) 
2002/0125/sys/src/9/port/devloopback.c:129,1352002/0405/sys/src/9/port/devloopback.c:129,135
2000/0617    
	int chan; 
2000/0708    
	int dev; 
2000/0617    
 
2000/0708    
	if(!havecycintr()) 
2002/0405    
	if(!havetimer()) 
2000/0708    
		error("can't time packets"); 
 
	dev = 0; 
2002/0125/sys/src/9/port/devloopback.c:381,3872002/0405/sys/src/9/port/devloopback.c:381,387
2000/0617    
	link->tqtail = nil; 
	link->tout = 0; 
	link->tin = 0; 
2000/0708    
	cycintrdel(&link->ci); 
2002/0405    
	timerdel(&link->ci); 
2000/0617    
	iunlock(link); 
	if(iq != nil){ 
		qclose(iq); 
2002/0125/sys/src/9/port/devloopback.c:604,6102002/0405/sys/src/9/port/devloopback.c:604,610
2000/0617    
} 
 
2000/0708    
static void 
linkintr(Ureg*, Cycintr *ci) 
2002/0405    
linkintr(Ureg*, Timer *ci) 
2000/0708    
{ 
	Link *link; 
 
2002/0125/sys/src/9/port/devloopback.c:633,6392002/0405/sys/src/9/port/devloopback.c:633,639
2000/0708    
		return; 
 
2000/0617    
	} 
2000/0708    
	cycintrdel(&link->ci); 
2002/0405    
	timerdel(&link->ci); 
2000/0708    
 
	/* 
	 * put more blocks into the xmit queue 
2002/0125/sys/src/9/port/devloopback.c:710,7162002/0405/sys/src/9/port/devloopback.c:710,716
2000/0708    
	if(tin){ 
		if(tin < now) 
			panic("loopback unfinished business"); 
		cycintradd(&link->ci); 
2002/0405    
		timeradd(&link->ci); 
2000/0708    
	} 
2000/0617    
	iunlock(link); 
} 
2002/0405/sys/src/9/port/devloopback.c:129,1372002/0410/sys/src/9/port/devloopback.c:129,134 (short | long)
2000/0617    
	int chan; 
2000/0708    
	int dev; 
2000/0617    
 
2002/0405    
	if(!havetimer()) 
2000/0708    
		error("can't time packets"); 
                 
	dev = 0; 
	if(spec != nil){ 
		dev = atoi(spec); 
2002/0410/sys/src/9/port/devloopback.c:597,6032002/0413/sys/src/9/port/devloopback.c:597,602 (short | long)
2000/0708    
	t = fastticks(nil); 
	for(chan = 0; chan < 2; chan++) 
		pushlink(&lb->link[chan], t); 
	clockintrsched(); 
2000/0617    
} 
 
2000/0708    
static void 


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