~kris/9p

9hist

6e1a0b8aa22f1a085d8a24c83a6e3f90c6c34a58 — David du Colombier 34 years ago d94140b
Plan 9 from Bell Labs 1992-06-23
M gnot/devincon.c => gnot/devincon.c +13 -11
@@ 409,7 409,7 @@ inconread(Chan *c, void *buf, long n, ulong offset)
		i = &incon[c->dev];
		sprint(b, "in: %d\nout: %d\noverflow: %d\ncrc: %d\nwait: %d\n", i->in,
			i->out, i->overflow, i->crc, i->wait);
		return stringread(buf, n, b, offset);
		return readstr(offset, buf, n, b);
	} else
		return streamread(c, buf, n);
}


@@ 525,6 525,13 @@ inconoput(Queue *q, Block *bp)
			freeb(bp);
		return;
	}
	if(BLEN(bp) < 3){
		bp = pullup(bp, 3);
		if(bp == 0){
			print("inconoput pullup failed\n");
			return;
		}
	}

	/*
	 *  get a whole message before handing bytes to the device


@@ 542,11 549,6 @@ inconoput(Queue *q, Block *bp)
	 *  parse message
	 */
	bp = getq(q);
	if(bp->wptr - bp->rptr < 3){
		freemsg(q, bp);
		qunlock(&ip->xmit);
		return;
	}
	chan = bp->rptr[0] | (bp->rptr[1]<<8);
	ctl = bp->rptr[2];
	bp->rptr += 3;


@@ 767,15 769,15 @@ nextin(Incon *ip, unsigned int c)
	int next;

	bp = ip->inb[ip->wi];
	bp->base[0] = ip->chan;
	bp->base[1] = ip->chan>>8;
	bp->base[2] = c;
	bp->rptr[0] = ip->chan;
	bp->rptr[1] = ip->chan>>8;
	bp->rptr[2] = c;
	if(incondebug)
		print("<-(%d)%uo %d\n", ip->chan, c, bp->wptr-bp->rptr);

	next = (ip->wi+1)%Nin;
	if(next == ip->ri){
		bp->wptr = bp->base+3;
		bp->wptr = bp->rptr+3;
		return bp;
	}
	ip->wi = next;


@@ 862,7 864,7 @@ rdpackets(Incon *ip)
		}
	}	
	bp->wptr = p;
	if(bp->wptr != bp->base+3)
	if(bp->wptr != bp->rptr+3)
		nextin(ip, 0);

	if(first != ip->wi)/**/

M gnot/main.c => gnot/main.c +4 -2
@@ 161,8 161,10 @@ userinit(void)

	p = newproc();
	p->pgrp = newpgrp();
	p->egrp = newegrp();
	p->fgrp = newfgrp();
	p->egrp = smalloc(sizeof(Egrp));
	p->egrp->ref = 1;
	p->fgrp = smalloc(sizeof(Fgrp));
	p->fgrp->ref = 1;
	p->procmode = 0640;

	strcpy(p->text, "*init*");

M pc/devincon.c => pc/devincon.c +7 -5
@@ 730,6 730,13 @@ inconoput(Queue *q, Block *bp)
			freeb(bp);
		return;
	}
	if(BLEN(bp) < 3){
		bp = pullup(bp, 3);
		if(bp == 0){
			print("inconoput pullup failed\n");
			return;
		}
	}

	/*
	 *  get a whole message before handing bytes to the device


@@ 747,11 754,6 @@ inconoput(Queue *q, Block *bp)
	 *  parse message
	 */
	bp = getq(q);
	if(bp->wptr - bp->rptr < 3){
		freemsg(q, bp);
		qunlock(&ip->xmit);
		return;
	}
	chan = bp->rptr[0] | (bp->rptr[1]<<8);
	ctl = bp->rptr[2];
	bp->rptr += 3;

M port/alloc.c => port/alloc.c +12 -1
@@ 166,11 166,11 @@ xalloc(ulong size)
				h->link = xlists.flist;
				xlists.flist = h;
			}
			unlock(&xlists);
			p = KADDR(p);
			memset(p, 0, size);
			p->magix = Magichole;
			p->size = size;
			unlock(&xlists);
			return p->data;
		}
		l = &h->link;


@@ 298,6 298,17 @@ smalloc(ulong size)
	return p;
}

int
msize(void *ptr)
{
	Bucket *bp;

	bp = (Bucket*)((ulong)ptr - bdatoff);
	if(bp->magic != Magic2n)
		panic("msize");
	return 1<<bp->size;
}

void
free(void *ptr)
{

M port/devarp.c => port/devarp.c +1 -1
@@ 240,7 240,7 @@ arpread(Chan *c, void *a, long n, ulong offset)
		sprint(buf, "hits: %d miss: %d failed: %d\n",
			arpstats.hit, arpstats.miss, arpstats.failed);

		return stringread(a, n, buf, offset);
		return readstr(offset, a, n, buf);
	default:
		n=0;
		break;

M port/devdk.c => port/devdk.c +77 -134
@@ 15,7 15,7 @@ typedef struct Line	Line;
typedef struct Dk	Dk;

enum {
	Maxlines = 256,
	Maxdk = 4,
};

/*


@@ 84,6 84,7 @@ struct Dkmsg {

struct Line {
	QLock;
	Netprot;		/* stat info */
	int	lineno;
	Rendez	r;		/* wait here for dial */
	int	state;		/* dial state */


@@ 104,11 105,13 @@ struct Line {
 *  dkmux line discipline is pushed onto.
 */
struct Dk {
	QLock;
	Lock;
	QLock	netlock;
	Network	net;

	QLock	csclock;
	Chan	*csc;

	int	ref;
	Lock;
	int	opened;

	char	name[64];	/* dk name */


@@ 122,10 125,6 @@ struct Dk {
	Rendez	timer;
	int	closeall;	/* set when we receive a closeall message */
	Rendez	closeallr;	/* wait here for a closeall */
	Network	net;
	Netprot *prot;

	Block	*alloc;		/* blocks containing Line structs */
};
static Dk *dk;
static Lock dklock;


@@ 209,7 208,6 @@ extern Qinfo dkinfo;
 *  the datakit multiplexor stream module definition
 */
static void dkmuxopen(Queue *, Stream *);
static void dkmuxclose(Queue *);
static void dkmuxoput(Queue *, Block *);
static void dkmuxiput(Queue *, Block *);
Qinfo dkmuxinfo =


@@ 217,14 215,14 @@ Qinfo dkmuxinfo =
	dkmuxiput,
	dkmuxoput,
	dkmuxopen,
	dkmuxclose,
	0,
	"dkmux"
};

/*
 *  Look for a dk struct with a name.  If none exists,  create one.
 */ 
static Dk *
static Dk*
dkalloc(char *name, int ncsc, int lines)
{
	Dk *dp;


@@ 234,12 232,12 @@ dkalloc(char *name, int ncsc, int lines)

	lock(&dklock);
	freep = 0;
	for(dp = dk; dp < &dk[conf.dkif]; dp++){
	for(dp = dk; dp < &dk[Maxdk]; dp++){
		if(strcmp(name, dp->name) == 0){
			unlock(&dklock);
			return dp;
		}
		if(dp->name[0] == 0 && dp->ref == 0)
		if(dp->name[0] == 0)
			freep = dp;
	}
	if(freep == 0 || lines == 0){


@@ 247,9 245,6 @@ dkalloc(char *name, int ncsc, int lines)
		error(Enoifc);
	}

	/*
 	 *  init the structures
	 */
	dp = freep;
	dp->opened = 0;
	dp->s = 0;


@@ 258,18 253,10 @@ dkalloc(char *name, int ncsc, int lines)
	strncpy(dp->name, name, sizeof(freep->name));

	/*
	 *  allocate memory for line structures
	 *  allocate memory for array of pointers to lines.
	 *  line structures are allocated as needed.
	 */
	dp->linep = (Line **)xalloc(sizeof(Line*) * dp->lines);
	if(dp->linep == 0)
		error(Enomem);
	lp = xalloc(dp->lines*sizeof(Line));
	if(lp == 0)
		error(Enomem);
	for(i = 0; i < dp->lines; i++) {
		lp->lineno = i;
		dp->linep[i] = lp++;
	}
	dp->linep = smalloc(sizeof(Line*) * dp->lines);

	/*
	 *  fill in the network structure


@@ 280,7 267,6 @@ dkalloc(char *name, int ncsc, int lines)
	dp->net.protop = &urpinfo;
	dp->net.listen = dklisten;
	dp->net.clone = dkcloneline;
	dp->net.prot = dp->prot;
	dp->net.ninfo = 5;
	dp->net.info[0].name = "addr";
	dp->net.info[0].fill = dkfilladdr;


@@ 298,50 284,42 @@ dkalloc(char *name, int ncsc, int lines)
}

/*
 *  a new dkmux.  hold the stream in place so it can never be closed down.
 *  allocate a line if it doesn't exist
 */
static void
dkmuxopen(Queue *q, Stream *s)
static Line*
linealloc(Dk *dp, int lineno, int dolock)
{
	RD(q)->ptr = s;
	WR(q)->ptr = 0;
	Line *lp;

	s->opens++;		/* Hold this queue in place */
	s->inuse++;
	if(dp->opened == 0)
		error(Enoifc);
	if(dolock)
		qlock(&dp->netlock);
	if(lineno > dp->lines)
		panic("linealloc");
	lp = dp->linep[lineno];
	if(lp == 0){
		lp = smalloc(sizeof(Line));
		lp->lineno = lineno;
		netadd(&dp->net, lp, lineno);
		dp->linep[lineno] = lp;
	}
	if(dolock)
		qunlock(&dp->netlock);
	return lp;
}

/*
 *  close down a dkmux, this shouldn't happen
 *  a new dkmux.  hold the stream in place so it can never be closed down.
 */
static void
dkmuxclose(Queue *q)
dkmuxopen(Queue *q, Stream *s)
{
	Dk *dp;
	int i;

	dp = WR(q)->ptr;
	if(dp == 0)
		return;
	dp->name[0] = 0;

	/*
	 *  disallow new dkstopens() on this line.
	 *  the lock syncs with dkstopen().
	 */
	lock(dp);
	dp->opened = 0;
	unlock(dp);

	/*
	 *  hang up all datakit connections
	 */
	for(i=dp->ncsc; i < dp->lines; i++)
		dkhangup(dp->linep[i]);
	RD(q)->ptr = s;
	WR(q)->ptr = 0;

	/*
	 *  wakeup the timer so it can die
	 */
	wakeup(&dp->timer);
	s->opens++;		/* Hold this queue in place */
	s->inuse++;
}

/*


@@ 399,8 377,8 @@ dkmuxiput(Queue *q, Block *bp)
		return;
	}

	lp = dp->linep[line];
	if(canqlock(lp)){
	lp = linealloc(dp, line, 1);
	if(lp && canqlock(lp)){
		if(lp->rq)
			PUTNEXT(lp->rq, bp);
		else{


@@ 442,13 420,6 @@ dkstopen(Queue *q, Stream *s)
	dp = &dk[s->dev];
	q->other->ptr = q->ptr = lp = dp->linep[s->id];
	lp->dp = dp;
	lock(dp);
	dp->ref++;
	if(dp->opened==0 || streamenter(dp->s)<0){
		unlock(dp);
		error(Ehungup);
	}
	unlock(dp);
	lp->rq = q;
	if(lp->state==Lclosed)
		lp->state = Lopened;


@@ 476,11 447,6 @@ dkstclose(Queue *q)
	}

	/*
	 *  decrement ref count on mux'd line
	 */
	streamexit(dp->s, 0);

	/*
	 *  these states don't need the datakit
	 */
	switch(lp->state){


@@ 534,14 500,8 @@ out:

	if(lp->lineno == dp->ncsc)
		dp->csc = 0;
	netdisown(&dp->net, lp->lineno);
	netdisown(lp);
	lp->window = 0;

	lock(dp);
	dp->ref--;
	if(dp->ref == 0)
		freeb(dp->alloc);
	unlock(dp);
}

/*


@@ 557,7 517,8 @@ dkiput(Queue *q, Block *bp)
 *  we assume that each put is a message.
 *
 *  add a 2 byte channel number to the start of each message,
 *  low order byte first.
 *  low order byte first.  Make sure the first block contains
 *  both the 2 channel bytes and the control byte.
 */
static void
dkoput(Queue *q, Block *bp)


@@ 706,9 667,8 @@ void
dkreset(void)
{
	int i;
	dk = (Dk*)xalloc(conf.dkif*sizeof(Dk));
	for(i = 0; i < conf.dkif; i++)
		dk[i].prot = (Netprot*)xalloc(conf.nurp*sizeof(Netprot));

	dk = (Dk*)xalloc(Maxdk*sizeof(Dk));
	newqinfo(&dkmuxinfo);
}



@@ 759,7 719,11 @@ dkstat(Chan *c, char *dp)
Chan*
dkopen(Chan *c, int omode)
{
	return netopen(c, omode, &dk[c->dev].net);
	Dk *dp;

	dp = &dk[c->dev];
	linealloc(dp, STREAMID(c->qid.path), 1);
	return netopen(c, omode, &dp->net);
}

void	 


@@ 866,22 830,21 @@ dkcloneline(Chan *c)
	/*
	 *  get an unused device and open its control file
	 */
	qlock(&dp->netlock);
	for(line = dp->ncsc+1; line < dp->lines; line++){
		lp = dp->linep[line];
		if(lp->state == Lclosed && canqlock(lp)){
			if(lp->state != Lclosed){
				qunlock(lp);
				continue;
			}
		if(lp == 0 || lp->state == Lclosed){
			lp = linealloc(dp, line, 0);
			lp->state = Lopened;

			/* current user becomes owner */
			netown(&dp->net, lp->lineno, u->p->user, 0);
			netown(lp, u->p->user, 0);

			qunlock(lp);
			qunlock(&dp->netlock);
			return lp->lineno;
		}
	}
	qunlock(&dp->netlock);
	error(Enodev);
	return -1;		/* never reached */
}


@@ 911,12 874,14 @@ dkopenline(Dk *dp, int line)
static Chan*
dkopencsc(Dk *dp)
{
	qlock(dp);
	Line *lp;

	qlock(&dp->csclock);
	if(dp->csc == 0)
		dp->csc = dkopenline(dp, dp->ncsc);
	else
		incref(dp->csc);
	qunlock(dp);
	qunlock(&dp->csclock);
	return dp->csc;
}



@@ 999,7 964,7 @@ dkcall(int type, Chan *c, char *addr, char *nuser, char *machine)
	
	line = STREAMID(c->qid.path);
	dp = &dk[c->dev];
	lp = dp->linep[line];
	lp = linealloc(dp, line, 1);

	/*
	 *  only dial on virgin lines


@@ 1247,7 1212,7 @@ dklisten(Chan *c)
			print("dklisten: illegal line %d\n", lineno);
			continue;
		}
		lp = dp->linep[lineno];
		lp = linealloc(dp, lineno, 1);
		ts = strtoul(field[1], 0, 0);

		/*


@@ 1321,7 1286,7 @@ dklisten(Chan *c)
		lp->state = Lconnected;

		/* listener becomes owner */
		netown(&dp->net, lp->lineno, dp->prot[from].owner, 0);
		netown(lp, dp->linep[from]->owner, 0);

		qunlock(lp);
		close(dc);


@@ 1340,12 1305,12 @@ static void
dkanswer(Chan *c, int line, int code)
{
	char reply[64];
	Dk *dp;
	Chan *dc;
	Line *lp;
	Dk *dp;

	dp = &dk[c->dev];
	lp = dp->linep[line];
	lp = linealloc(dp, line, 1);

	/*
	 *  open the data file (c is a control file)


@@ 1394,7 1359,7 @@ dkwindow(Chan *c)
	long wins;
	Line *lp;

	lp = dk[c->dev].linep[STREAMID(c->qid.path)];
	lp = linealloc(&dk[c->dev], STREAMID(c->qid.path), 1);
	if(lp->window == 0)
		lp->window = 64;
	sprint(buf, "init %d %d", lp->window, Streamhi);


@@ 1488,12 1453,11 @@ dkchgmesg(Chan *c, Dk *dp, Dkmsg *dialp, int line)
	switch (dialp->srv) {

	case D_CLOSE:		/* remote shutdown */
		if (line <= 0 || line >= dp->lines) {
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0) {
			/* tell controller this line is not in use */
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
			return;
		}
		lp = dp->linep[line];
		switch (lp->state) {

		case Ldialing:


@@ 1524,12 1488,11 @@ dkchgmesg(Chan *c, Dk *dp, Dkmsg *dialp, int line)
		break;
	
	case D_ISCLOSED:	/* acknowledging a local shutdown */
		if (line <= 0 || line >= dp->lines) {
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0) {
			/* tell controller this line is not in use */
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
			return;
		}
		lp = dp->linep[line];
		switch (lp->state) {
		case Llclose:
		case Lclosed:


@@ 1550,6 1513,8 @@ dkchgmesg(Chan *c, Dk *dp, Dkmsg *dialp, int line)
		 */
		for(line = dp->ncsc+1; line < dp->lines; line++){
			lp = dp->linep[line];
			if(lp == 0)
				continue;
			switch (lp->state) {
	
			case Ldialing:


@@ 1595,10 1560,9 @@ dkreplymesg(Dk *dp, Dkmsg *dialp, int line)

	DPRINT("dkreplymesg(%d)\n", line);

	if(line < 0 || line >= dp->lines)
	if(line < 0 || line >= dp->lines || (lp = dp->linep[line]) == 0)
		return;

	lp = dp->linep[line];
	if(lp->state != Ldialing)
		return;



@@ 1637,34 1601,11 @@ dktimer(void *a)
	Chan *c;

	dp = (Dk *)a;
	c = 0;
	if(waserror()){
		/*
		 *  hang up any calls waiting for the dk
		 */
		for (i=dp->ncsc+1; i<dp->lines; i++){
			lp = dp->linep[i];
			switch(lp->state){
			case Llclose:
				lp->state = Lclosed;
				break;

			case Ldialing:
				dkreplymesg(dp, (Dkmsg *)0, i);
				break;
			}
		}
		if(c)
			close(c);
		return;
	}

	c = dkopencsc(dp);

	for(;;){
		if(dp->opened==0)
			error(Ehungup);
	while(waserror());

	for(;;){
		/*
		 * send keep alive
		 */


@@ 1677,6 1618,8 @@ dktimer(void *a)
		 */
		for (i=dp->ncsc+1; i<dp->lines; i++){
			lp = dp->linep[i];
			if(lp == 0)
				continue;
			switch(lp->state){
			case Llclose:
				dkmesg(c, T_CHG, D_CLOSE, i, 0);

M port/devenv.c => port/devenv.c +115 -267
@@ 7,42 7,14 @@

#include	"devtab.h"

struct Envval
enum
{
	Envval	*next;		/* for hashing & easy deletion from hash list */
	Envval	*prev;
	ulong	len;		/* length of val that is valid */
	int	ref;
	char	*val;
	Maxenvsize = 16300,
};

enum{
	MAXENV	= (BY2PG - sizeof(Envval)),
	EVHASH	= 64,
	EVFREE	= 16,
	ALIGN	= 16,
};

struct
{
	Envval	*free[EVFREE+1];
	char	*block;			/* the free page we are allocating from */
	char	*lim;			/* end of block */
	int	npage;			/* total pages gotten from newpage() */
}envalloc;

QLock	evlock;
Envval	evhash[EVHASH];
char	*evscratch;		/* for constructing the contents of a file */

Envval	*newev(char*, ulong);
Envval	*evalloc(ulong);
void	evfree(Envval*);

void
envreset(void)
{
	evscratch = xalloc(BY2PG);
}

void


@@ 54,24 26,22 @@ int
envgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp)
{
	Egrp *eg;
	Env *e;
	int ans;
	Evalue *e;

	eg = u->p->egrp;
	qlock(&eg->ev);
	if(s >= eg->nenv)
		ans = -1;
	else{
		e = &eg->etab[s];
		if(!e->name)
			ans = 0;
		else{
			devdir(c, (Qid){s+1, (ulong)e->name}, e->name->val, e->val? e->val->len : 0, eve, 0666, dp);
			ans = 1;
		}
	qlock(eg);

	for(e = eg->entries; e && s; e = e->link)
		s--;

	if(e == 0) {
		qunlock(eg);
		return -1;
	}
	qunlock(&eg->ev);
	return ans;

	devdir(c, (Qid){e->path, 0}, e->name, e->len, eve, 0666, dp);
	qunlock(eg);
	return 1;
}

Chan*


@@ 103,30 73,31 @@ Chan *
envopen(Chan *c, int omode)
{
	Egrp *eg;
	Env *e;
	int mode;

	mode = openmode(omode);
	Evalue *e;
	
	eg = u->p->egrp;
	if(c->qid.path & CHDIR){
		if(omode != OREAD)
			error(Eperm);
	}else{
		eg = u->p->egrp;
		qlock(&eg->ev);
		e = &eg->etab[c->qid.path-1];
		if(!e->name){
			qunlock(&eg->ev);
	}
	else {
		qlock(eg);
		for(e = eg->entries; e; e = e->link)
			if(e->path == c->qid.path)
				break;

		if(e == 0) {
			qunlock(eg);
			error(Enonexist);
		}
		if(omode == (OWRITE|OTRUNC) && e->val){
			qlock(&evlock);
			evfree(e->val);
			qunlock(&evlock);
			e->val = 0;
		if(omode == (OWRITE|OTRUNC) && e->value) {
			free(e->value);
			e->value = 0;
			e->len = 0;
		}
		qunlock(&eg->ev);
		qunlock(eg);
	}
	c->mode = mode;
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;


@@ 136,39 107,36 @@ void
envcreate(Chan *c, char *name, int omode, ulong perm)
{
	Egrp *eg;
	Env *e, *ne;
	int i;
	Evalue *e;

	if(c->qid.path != CHDIR)
		error(Eperm);

	omode = openmode(omode);
	eg = u->p->egrp;
	qlock(&eg->ev);
	e = eg->etab;
	ne = 0;
	for(i = 0; i < eg->nenv; i++, e++)
		if(e->name == 0)
			ne = e;
		else if(strcmp(e->name->val, name) == 0){
			qunlock(&eg->ev);
			error(Einuse);
		}
	if(ne)
		e = ne;
	else if(eg->nenv == conf.npgenv){
		qunlock(&eg->ev);
		print("out of egroup envs\n");
		error(Enoenv);

	qlock(eg);
	if(waserror()) {
		qunlock(eg);
		nexterror();
	}
	i = e - eg->etab + 1;
	e->val = 0;
	qlock(&evlock);
	e->name = newev(name, strlen(name)+1);
	qunlock(&evlock);
	if(i > eg->nenv)
		eg->nenv = i;
	qunlock(&eg->ev);
	c->qid = (Qid){i, 0};

	for(e = eg->entries; e; e = e->link)
		if(strcmp(e->name, name) == 0)
			error(Einuse);

	e = smalloc(sizeof(Evalue));
	e->name = smalloc(strlen(name)+1);
	strcpy(e->name, name);

	e->path = ++eg->path;
	e->link = eg->entries;
	eg->entries = e;
	c->qid = (Qid){e->path, 0};
	
	qunlock(eg);
	poperror();

	c->offset = 0;
	c->mode = omode;
	c->flag |= COPEN;


@@ 178,19 146,32 @@ void
envremove(Chan *c)
{
	Egrp *eg;
	Env *e;
	Evalue *e, **l;

	if(c->qid.path & CHDIR)
		error(Eperm);

	eg = u->p->egrp;
	qlock(&eg->ev);
	e = &eg->etab[c->qid.path-1];
	if(!e->name){
		qunlock(&eg->ev);
	qlock(eg);

	l = &eg->entries;
	for(e = *l; e; e = e->link) {
		if(e->path == c->qid.path)
			break;
		l = &e->link;
	}

	if(e == 0) {
		qunlock(eg);
		error(Enonexist);
	}
	envpgclose(e);
	qunlock(&eg->ev);

	*l = e->link;
	qunlock(eg);
	free(e->name);
	if(e->value)
		free(e->value);
	free(e);
}

void


@@ 206,206 187,73 @@ envclose(Chan * c)
	USED(c);
}

void
envpgcopy(Env *t, Env *f)
{
	qlock(&evlock);
	if(t->name = f->name)
		t->name->ref++;
	if(t->val = f->val)
		t->val->ref++;
	qunlock(&evlock);
}

void
envpgclose(Env *e)
{
	qlock(&evlock);
	if(e->name)
		evfree(e->name);
	if(e->val)
		evfree(e->val);
	e->name = e->val = 0;
	qunlock(&evlock);
}

long
envread(Chan *c, void *a, long n, ulong offset)
{
	Egrp *eg;
	Env *e;
	Envval *ev;
	long vn;
	Evalue *e;

	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, 0, 0, envgen);

	eg = u->p->egrp;
	qlock(&eg->ev);
	e = &eg->etab[c->qid.path-1];
	if(!e->name){
		qunlock(&eg->ev);
	qlock(eg);
	for(e = eg->entries; e; e = e->link)
		if(e->path == c->qid.path)
			break;

	if(e == 0) {
		qunlock(eg);
		error(Enonexist);
	}
	ev = e->val;
	vn = ev ? ev->len : 0;
	if(offset + n > vn)
		n = vn - offset;

	if(offset + n > e->len)
		n = e->len - offset;
	if(n <= 0)
		n = 0;
	else
		memmove(a, ev->val + offset, n);
	qunlock(&eg->ev);
		memmove(a, e->value+offset, n);
	qunlock(eg);
	return n;
}

long
envwrite(Chan *c, void *a, long n, ulong offset)
{
	char *s;
	int vend;
	Egrp *eg;
	Env *e;
	Envval *ev;
	ulong olen;
	Evalue *e;

	if(n <= 0)
		return 0;
	olen = (offset + n + ALIGN - 1) & ~(ALIGN - 1);
	if(olen > MAXENV)

	vend = offset+n;
	if(vend > Maxenvsize)
		error(Etoobig);
	eg = u->p->egrp;
	qlock(&eg->ev);
	e = &eg->etab[c->qid.path-1];
	if(!e->name){
		qunlock(&eg->ev);
		error(Enonexist);
	}
	ev = e->val;
	olen = ev ? ev->len : 0;
	qlock(&evlock);
	if(offset == 0 && n >= olen)
		e->val = newev(a, n);
	else{
		if(olen > offset)
			olen = offset;
		if(ev)
			memmove(evscratch, ev->val, olen);
		if(olen < offset)
			memset(evscratch + olen, '\0', offset - olen);
		memmove(evscratch + offset, a, n);
		e->val = newev(evscratch, offset + n);
	}
	if(ev)
		evfree(ev);
	qunlock(&evlock);
	qunlock(&eg->ev);
	return n;
}

/*
 * called with evlock qlocked
 */
Envval *
newev(char *s, ulong n)
{
	Envval *ev;
	uchar *t;
	int h;

	h = 0;
	for(t = (uchar*)s; t - (uchar*)s < n; t++)
		h = (h << 1) ^ *t;
	h &= EVHASH - 1;
	for(ev = evhash[h].next; ev; ev = ev->next)
		if(ev->len == n && memcmp(ev->val, s, n) == 0){
			ev->ref++;
			return ev;
		}
	ev = evalloc(n);
	ev->len = n;
	memmove(ev->val, s, n);
	if(ev->next = evhash[h].next)
		ev->next->prev = ev;
	evhash[h].next = ev;
	ev->prev = &evhash[h];
	return ev;
}
	eg = u->p->egrp;
	qlock(eg);
	for(e = eg->entries; e; e = e->link)
		if(e->path == c->qid.path)
			break;

/*
 * called only from newev
 */
Envval *
evalloc(ulong n)
{
	Envval *ev, **p;
	char *b, *lim;
	ulong size;

	size = (n + ALIGN - 1) & ~(ALIGN - 1);
	n = (size - 1) / ALIGN;
	p = &envalloc.free[n < EVFREE ? n : EVFREE];
	for(ev = *p; ev; ev = *p){
		if(ev->len == size){
			*p = ev->next;
			ev->ref = 1;
			return ev;
		}
		p = &ev->next;
	if(e == 0) {
		qunlock(eg);
		error(Enonexist);
	}

	/*
	 * make sure we have enough space to allocate the buffer.
	 * if not, use the remaining space for the smallest buffers
	 */
	if(size > MAXENV)
		panic("evalloc");
	b = envalloc.block;
	lim = envalloc.lim;
	if(!b || lim < b + size + sizeof *ev){
		p = &envalloc.free[0];
		while(lim >= b + ALIGN + sizeof *ev){
			ev = (Envval*)b;
			ev->len = ALIGN;
			ev->val = b + sizeof *ev;
			ev->next = *p;
			*p = ev;
			b += ALIGN + sizeof *ev;
		}
		b = (char*)VA(kmap(newpage(0, 0, 0)));
		envalloc.npage++;
		envalloc.lim = b + BY2PG;
	if(vend > e->len) {
		s = smalloc(offset+n);
		memmove(s, e->value, e->len);
		if(e->value)
			free(e->value);
		e->value = s;
		e->len = vend;
	}
	
	ev = (Envval*)b;
	ev->val = b + sizeof *ev;
	ev->ref = 1;
	envalloc.block = b + size + sizeof *ev;
	return ev;
}

/*
 * called with evlock qlocked
 */
void
evfree(Envval *ev)
{
	int n;

	if(--ev->ref > 0)
		return;

	if(ev->prev)
		ev->prev->next = ev->next;
	else
		panic("evfree");
	if(ev->next)
		ev->next->prev = ev->prev;
	n = (ev->len + ALIGN - 1) & ~(ALIGN - 1);
	ev->len = n;
	n = (n - 1) / ALIGN;
	if(n > EVFREE)
		n = EVFREE;
	ev->next = envalloc.free[n];
	ev->prev = 0;
	envalloc.free[n] = ev;
	memmove(e->value+offset, a, n);
	qunlock(eg);
	return n;
}

/*

M port/devip.c => port/devip.c +3 -5
@@ 57,9 57,9 @@ ipinitnet(Network *np, Qinfo *stproto, Ipconv *cp)
	int j;

	for(j = 0; j < conf.ip; j++, cp++){
		cp->index = j;
		cp->stproto = stproto;
		cp->net = np;
		netadd(np, cp, j);
	}
	np->name = stproto->name;
	np->nconv = conf.ip;


@@ 68,7 68,6 @@ ipinitnet(Network *np, Qinfo *stproto, Ipconv *cp)
	if(stproto != &udpinfo)
		np->listen = iplisten;
	np->clone = ipclonecon;
	np->prot = (Netprot *)xalloc(sizeof(Netprot) * conf.ip);
	np->ninfo = 3;
	np->info[0].name = "remote";
	np->info[0].fill = ipremotefill;


@@ 172,10 171,9 @@ ipincoming(Ipconv *base, Ipconv *from)
				continue;
			}
			if(from)	/* copy ownership from listening channel */
				netown(new->net, new->index,
				       new->net->prot[from->index].owner, 0);
				netown(new, from->owner, 0);
			else		/* current user becomes owner */
				netown(new->net, new->index, u->p->user, 0);
				netown(new, u->p->user, 0);

			new->ref = 1;
			qunlock(new);

M port/devlance.c => port/devlance.c +6 -4
@@ 76,6 76,7 @@ typedef struct Ethertype	Ethertype;
struct Ethertype
{
	QLock;
	Netprot;			/* stat info */
	int		type;		/* ethernet type */
	int		prom;		/* promiscuous mode */
	Queue		*q;


@@ 96,7 97,6 @@ typedef struct {
	int	all;		/* number of channels listening to all packets */
	int	wedged;		/* the lance is wedged */
	Network	net;
	Netprot	prot[Ntypes];

	int	inited;
	uchar	*lmp;		/* location of parity test */


@@ 268,7 268,7 @@ lancestclose(Queue *q)
	et->q = 0;
	et->prom = 0;
	et->inuse = 0;
	netdisown(&l.net, et - l.e);
	netdisown(et);
}

/*


@@ 416,6 416,7 @@ void
lancereset(void)
{
	static int already;
	int i;

	if(already == 0){
		already = 1;


@@ 428,11 429,12 @@ lancereset(void)
		l.net.listen = 0;
		l.net.clone = lanceclonecon;
		l.net.ninfo = 2;
		l.net.prot = l.prot;
		l.net.info[0].name = "stats";
		l.net.info[0].fill = lancestatsfill;
		l.net.info[1].name = "type";
		l.net.info[1].fill = lancetypefill;
		for(i = 0; i < Ntypes; i++)
			netadd(&l.net, &l.e[i], i);

		memset(l.bcast, 0xff, sizeof l.bcast);
	}


@@ 671,7 673,7 @@ lanceclonecon(Chan *c)
			continue;
		}
		e->inuse = 1;
		netown(&l.net, e - l.e, u->p->user, 0);
		netown(e, u->p->user, 0);
		qunlock(e);
		return e - l.e;
	}

M port/devpipe.c => port/devpipe.c +2 -4
@@ 161,9 161,7 @@ pipeopen(Chan *c, int omode)
		remote->devq->ptr = local;
		local->devq->other->next = remote->devq;
		remote->devq->other->next = local->devq;
	}
	else
	if(local->opens == 1){
	} else if(local->opens == 1){
		/*
		 *  keep other side around till last close of this side
		 */


@@ 321,6 319,6 @@ getpipe(ulong path)
		}
	}
	unlock(&pipealloc);
	panic("getpipe");
	error(Enonexist);
	return 0;
}

M port/devscc.c => port/devscc.c +1 -1
@@ 754,7 754,7 @@ sccread(Chan *c, void *buf, long n, ulong offset)
		return streamread(c, buf, n);
	case Sctlqid:
		sprint(b, "%d", STREAMID(c->qid.path));
		return stringread(buf, n, b, offset);
		return readstr(offset, buf, n, b);
	}
	error(Egreg);
	return 0;	/* not reached */

M port/ipdat.h => port/ipdat.h +1 -1
@@ 278,8 278,8 @@ struct Reseq
struct Ipconv
{
	QLock;				/* Ref count lock */
	Netprot;			/* stat info */
	int 	ref;
	int	index;
	Qinfo	*stproto;		/* Stream protocol for this device */
	Network	*net;			/* user level network interface */
	Ipaddr	dst;			/* Destination from connect */

M port/net.c => port/net.c +51 -18
@@ 15,6 15,20 @@ enum
};

/*
 *  find protection structure
 */
static Netprot*
findprot(Network *np, int id)
{
	Netprot *p;

	for(p = np->prot; p; p = p->next)
		if(p->id == id)
			break;
	return p;
}

/*
 *  generate a 3 level directory
 */
int


@@ 24,7 38,6 @@ netgen(Chan *c, void *vp, int ntab, int i, Dir *dp)
	char buf[32];
	Network *np = vp;
	int t;
	int id;
	Netprot *p;
	int perm;
	char *o;


@@ 61,9 74,8 @@ netgen(Chan *c, void *vp, int ntab, int i, Dir *dp)
	}

	/* third level depends on the number of info files */
	id = STREAMID(c->qid.path);
	p = &np->prot[id];
	if(*p->owner){
	p = findprot(np, STREAMID(c->qid.path));
	if(p && *p->owner){
		o = p->owner;
		perm = p->mode;
	} else {


@@ 153,7 165,9 @@ netwstat(Chan *c, char *db, Network *np)
	Dir dir;
	Netprot *p;

	p = &np->prot[STREAMID(c->qid.path)];
	p = findprot(np, STREAMID(c->qid.path));
	if(p == 0)
		error(Enonexist);
	lock(np);
	if(strncmp(p->owner, u->p->user, NAMELEN)){
		unlock(np);


@@ 169,6 183,7 @@ Chan *
netopen(Chan *c, int omode, Network *np)
{
	int id = 0;
	Netprot *p;

	if(c->qid.path & CHDIR){
		if(omode != OREAD)


@@ 199,7 214,9 @@ netopen(Chan *c, int omode, Network *np)
			streamopen(c, np->devp);
			if(np->protop && c->stream->devq->next->info != np->protop)
				pushq(c->stream, np->protop);
			if(netown(np, id, u->p->user, omode&7) < 0)
			p = findprot(np, id);
if(p == 0) print("netopen: can't find %d\n", id);
			if(netown(p, u->p->user, omode&7) < 0)
				error(Eperm);
			break;
		}


@@ 227,19 244,36 @@ netread(Chan *c, void *a, long n, ulong offset, Network *np)
		error(Ebadusefd);

	(*np->info[t-Qinf].fill)(c, buf, sizeof(buf));
	return stringread(a, n, buf, offset);
	return readstr(offset, a, n, buf);
}

void
netadd(Network *np, Netprot *p, int id)
{
	Netprot **l, *pp;

	memset(p, 0, sizeof(Netprot));
	p->id = id;

	l = &np->prot;
	for(pp = np->prot; pp; pp = pp->next){
		if(pp->id == id)
			panic("netadd");
		l = &pp->next;
	}
	*l = p;
}

Lock netlock;

int
netown(Network *np, int id, char *o, int omode)
netown(Netprot *p, char *o, int omode)
{
	static int access[] = { 0400, 0200, 0600, 0100 };
	Netprot *p;
	int mode;
	int t;

	p = &np->prot[id];
	lock(np);
	lock(&netlock);
	if(*p->owner){
		if(strncmp(o, p->owner, NAMELEN) == 0)	/* User */
			mode = p->mode;


@@ 250,22 284,21 @@ netown(Network *np, int id, char *o, int omode)

		t = access[omode&3];
		if((t & mode) == t){
			unlock(np);
			unlock(&netlock);
			return 0;
		} else {
			unlock(np);
			unlock(&netlock);
			return -1;
		}
	}
	strncpy(p->owner, o, NAMELEN);
	np->prot[id].mode = 0660;
	unlock(np);
	p->mode = 0660;
	unlock(&netlock);
	return 0;
}

void
netdisown(Network *np, int id)
netdisown(Netprot *p)
{
if(np == 0) panic("np == 0");
	*np->prot[id].owner = 0;
	p->owner[0] = 0;
}

M port/pgrp.c => port/pgrp.c +28 -41
@@ 52,29 52,6 @@ newpgrp(void)
	return p;
}

Egrp*
newegrp(void)
{
	Egrp *e;

	e = smalloc(sizeof(Egrp)+sizeof(Env)*conf.npgenv);

	/* This is a sleazy hack to make malloc work .. devenv need rewriting. */
	e->etab = (Env*)((uchar*)e+sizeof(Egrp));
	e->ref = 1;
	return e;
}

Fgrp*
newfgrp(void)
{
	Fgrp *f;

	f = smalloc(sizeof(Fgrp));
	f->ref = 1;
	return f;
}

Fgrp*
dupfgrp(Fgrp *f)
{


@@ 82,7 59,8 @@ dupfgrp(Fgrp *f)
	Chan *c;
	int i;

	new = newfgrp();
	new = smalloc(sizeof(Fgrp));
	new->ref = 1;

	lock(f);
	new->maxfd = f->maxfd;


@@ 139,14 117,16 @@ closepgrp(Pgrp *p)
void
closeegrp(Egrp *eg)
{
	Env *e;
	int i;
	Evalue *e, *next;

	if(decref(eg) == 0) {
		e = eg->etab;
		for(i=0; i<eg->nenv; i++, e++)
			envpgclose(e);

		for(e = eg->entries; e; e = next) {
			next = e->link;
			free(e->name);
			if(e->value)
				free(e->value);
			free(e);
		}
		free(eg);
	}
}


@@ 183,17 163,24 @@ newmount(Mhead *mh, Chan *to)
void
envcpy(Egrp *to, Egrp *from)
{
	Env *te, *fe;
	int i, nenv;

	qlock(&from->ev);
	nenv = from->nenv;
	to->nenv = nenv;
	te = to->etab;
	fe = from->etab;
	for(i=0; i < nenv; i++, te++, fe++)
		envpgcopy(te, fe);
	qunlock(&from->ev);
	Evalue **l, *ne, *e;

	l = &to->entries;
	qlock(from);
	for(e = from->entries; e; e = e->link) {
		ne = smalloc(sizeof(Evalue));
		ne->name = smalloc(strlen(e->name)+1);
		strcpy(ne->name, e->name);
		if(e->value) {
			ne->value = smalloc(e->len);
			memmove(ne->value, e->value, e->len);
			ne->len = e->len;
		}
		ne->path = ++to->path;
		*l = ne;
		l = &ne->link;
	}
	qunlock(from);
}

void

M port/portdat.h => port/portdat.h +16 -12
@@ 6,8 6,7 @@ typedef struct Crypt	Crypt;
typedef struct Dev	Dev;
typedef struct Dirtab	Dirtab;
typedef struct Egrp	Egrp;
typedef struct Env	Env;
typedef struct Envval	Envval;
typedef struct Evalue	Evalue;
typedef struct Etherpkt	Etherpkt;
typedef struct Fgrp	Fgrp;
typedef struct Image	Image;


@@ 192,12 191,6 @@ struct Dirtab
	long	perm;
};

struct Env
{
	Envval	*name;
	Envval	*val;
};

/*
 *  Ethernet packet buffers.
 */


@@ 394,9 387,18 @@ struct Pgrp
struct Egrp
{
	Ref;
	int	nenv;			/* highest active env table entry, +1 */
	QLock	ev;			/* for all of etab */
	Env	*etab;
	QLock;
	Evalue	*entries;
	ulong	path;
};

struct Evalue
{
	char	*name;
	char	*value;
	int	len;
	ulong	path;
	Evalue	*link;
};

#define	NFD	100


@@ 651,6 653,8 @@ enum
 */
struct Netprot
{
	int	id;
	Netprot	*next;		/* linked list of protections */
	ulong	mode;
	char	owner[NAMELEN];
};


@@ 672,7 676,7 @@ struct Network
	int	(*clone)(Chan*);
	int	ninfo;
	Netinf	info[5];
	Netprot	*prot;			/* protections */
	Netprot	*prot;			/* linked list of protections */
};
#define MAJOR(q) ((q) >> 8)
#define MINOR(q) ((q) & 0xff)

M port/portfns.h => port/portfns.h +6 -8
@@ 28,7 28,6 @@ void		confinit1(int);
int		consactive(void);
void		consdebug(void);
Block*		copyb(Block*, int);
Env*		copyenv(Env*, int);
void		copypage(Page*, Page*);
int		decref(Ref*);
int		decrypt(void*, void*, int);


@@ 50,8 49,6 @@ void		duppage(Page*);
void		dupswap(Page*);
int		encrypt(void*, void*, int);
void		envcpy(Egrp*, Egrp*);
void		envpgclose(Env*);
void		envpgcopy(Env*, Env*);
int		eqchan(Chan*, Chan*, long);
int		eqqid(Qid, Qid);
void		error(char*);


@@ 114,19 111,19 @@ int		mount(Chan*, Chan*, int);
void		mountfree(Mount*);
void		mouseclock(void);
int		mouseputc(IOQ*, int);
int		msize(void*);
Chan*		namec(char*, int, int, ulong);
void		nameok(char*);
void		netdisown(Network*, int);
void		netdisown(Netprot*);
int		netgen(Chan*, void*, int, int, Dir*);
Chan*		netopen(Chan*, int, Network*);
int		netown(Network*, int, char*, int);
int		netown(Netprot*, char*, int);
void		netadd(Network*, Netprot*, int);
long		netread(Chan*, void*, long, ulong, Network*);
void		netstat(Chan*, char*, Network*);
int		netwalk(Chan*, char*, Network*);
void		netwstat(Chan*, char*, Network*);
Chan*		newchan(void);
Egrp*		newegrp(void);
Fgrp*		newfgrp(void);
Mount*		newmount(Mhead*, Chan*);
Page*		newpage(int, Segment **, ulong);
Pgrp*		newpgrp(void);


@@ 180,6 177,7 @@ Qinfo*		qinfofind(char*);
void		qlock(QLock*);
void		qunlock(QLock*);
int		readnum(ulong, char*, ulong, ulong, int);
int		readstr(ulong, char*, ulong, char*);
void		ready(Proc*);
void		relocateseg(Segment*, ulong);
void		resched(char*);


@@ 213,7 211,7 @@ void		splx(int);
int		streamclose(Chan*);
int		streamclose1(Stream*);
int		streamenter(Stream*);
int		streamexit(Stream*, int);
void		streamexit(Stream*, int);
Devgen		streamgen;
void		streaminit(void);
void		streaminit0(void);

M port/stasync.c => port/stasync.c +34 -17
@@ 24,8 24,12 @@ enum
	Escape
};

typedef struct Async {
typedef struct Async Async;
struct Async
{
	QLock;
	Async	*list;
	int	id;

	int	inuse;
	Queue	*wq;


@@ 51,9 55,16 @@ typedef struct Async {
	ulong	badescape;
	ulong	in;		/* bytes in */
	ulong	out;		/* bytes out */
} Async;
};

int nasync;

Async *async;
/* list of allocated async structures (never freed) */
struct
{
	Lock;
	Async *async;
} asyncalloc;

/*
 *  async stream module definition


@@ 97,7 108,6 @@ static ushort crc_table[256] = {
static void
asyncreset(void)
{
	async = (Async *)xalloc(conf.nasync*sizeof(Async));
}

/*


@@ 110,14 120,21 @@ asyncopen(Queue *q, Stream *s)

	DPRINT("asyncopen %d\n", s->dev);

	for(ap = async; ap < &async[conf.nasync]; ap++){
	for(ap = asyncalloc.async; ap; ap = ap->list){
		qlock(ap);
		if(ap->inuse == 0)
			break;
		qunlock(ap);
	}
	if(ap == &async[conf.nasync])
		exhausted("async stream modules");
	if(ap == 0){
		ap = smalloc(sizeof(Async));
		qlock(ap);
		lock(&asyncalloc);
		ap->list = asyncalloc.async;
		asyncalloc.async = ap;
		ap->id = nasync++;
		unlock(&asyncalloc);
	}
	q->ptr = q->other->ptr = ap;

	ap->inuse = 1;


@@ 141,7 158,7 @@ asyncclose(Queue * q)
{
	Async *ap = (Async *)q->ptr;

	DPRINT("asyncstclose %d\n", ap-async);
	DPRINT("asyncstclose %d\n", ap->id);
	qlock(ap);
	ap->inuse = 0;
	qunlock(ap);


@@ 166,7 183,7 @@ freemsg(Queue *q, Block *bp)
static void
showframe(char *t, Async *ap, uchar *buf, int n)
{
	kprint("a%d %s [", ap-async, t);
	kprint("a%d %s [", ap->id, t);
	while (--n >= 0)
		kprint(" %2.2ux", *buf++);
	kprint(" ]\n");


@@ 284,7 301,7 @@ asyncoput(Queue *q, Block *bp)

	if(asyncdebug > 1)
		kprint("a%d->(%d)%3.3uo %d\n",
			ap-async, chan, ctl, bp->wptr-bp->rptr);
			ap->id, chan, ctl, bp->wptr-bp->rptr);

	/*
	 *  send the 8 bit data


@@ 336,7 353,7 @@ asdeliver(Queue *q, Async *ap)
	chan = *p++ & 0x7e;
	chan = (chan<<5)|((*p++ & 0x7e)>>1);
	if(chan==0) {
		DPRINT("a%d deliver chan 0\n", ap-async);
		DPRINT("a%d deliver chan 0\n", ap->id);
		ap->chan0++;
		return;
	}


@@ 355,7 372,7 @@ asdeliver(Queue *q, Async *ap)
				bp->rptr[2] = c;
				if(asyncdebug > 1)
					kprint("a%d<-(%d)%3.3uo %d\n",
						ap-async, chan, bp->rptr[2],
						ap->id, chan, bp->rptr[2],
						bp->wptr - bp->rptr - 3);
				PUTNEXT(q, bp);
				bp = 0;


@@ 368,7 385,7 @@ asdeliver(Queue *q, Async *ap)
	if(bp) {
		if(asyncdebug > 1)
			kprint("a%d<-(%d)%3.3uo %d\n",
				ap-async, chan, bp->rptr[2],
				ap->id, chan, bp->rptr[2],
				bp->wptr - bp->rptr - 3);
		PUTNEXT(q, bp);
	}


@@ 411,7 428,7 @@ asynciput(Queue *q, Block *bp)
			}
		Datachar:
			if(ap->icount >= MAXFRAME) {
				DPRINT("a%d pkt too long\n", ap-async);
				DPRINT("a%d pkt too long\n", ap->id);
				ap->toolong++;
				state = Hunt;
				break;


@@ 426,12 443,12 @@ asynciput(Queue *q, Block *bp)
				if(asyncdebug > 2)
					showframe("in", ap, ap->buf, ap->icount);
				if(ap->icount < 5) {
					DPRINT("a%d pkt too short\n", ap-async);
					DPRINT("a%d pkt too short\n", ap->id);
					if(asyncdebug && asyncdebug<=2)
						showframe("shortin", ap, ap->buf, ap->icount);
					ap->tooshort++;
				} else if(ap->icrc != 0) {
					DPRINT("a%d bad crc\n", ap-async);
					DPRINT("a%d bad crc\n", ap->id);
					if(asyncdebug && asyncdebug<=2)
						showframe("badin", ap, ap->buf, ap->icount);
					ap->badcrc++;


@@ 445,7 462,7 @@ asynciput(Queue *q, Block *bp)
				state = Data;
				goto Datachar;
			default:
				DPRINT("a%d bad escape\n", ap-async);
				DPRINT("a%d bad escape\n", ap->id);
				ap->badescape++;
				state = Hunt;
				break;

M port/stip.c => port/stip.c +1 -1
@@ 145,7 145,7 @@ ipetherclose(Queue *q)

	ipc = (Ipconv *)(q->ptr);
	if(ipc){
		netdisown(ipc->net, ipc->index);
		netdisown(ipc);
		ipc->ref = 0;
	}
}

M port/stream.c => port/stream.c +460 -512
@@ 7,85 7,28 @@
#include	"../port/error.h"
#include	"devtab.h"

enum {
	Nclass=4,	/* number of block classes */
};

/*
 *  process end line discipline
 */
static void stputq(Queue*, Block*);
Qinfo procinfo =
{
	stputq,
	nullput,
	0,
	0,
	"process"
};

/*
 *  line disciplines that can be pushed
 */
static Qinfo *lds;

Stream *slist;
Queue *qlist;
static Lock garbagelock;

/*
 *  Allocate streams, queues, and blocks.  Allocate n block classes with
 *	1/2(m+1) to class m < n-1
 *	1/2(n-1) to class n-1
 */
void
streaminit(void)
{

	/*
	 *  allocate queues, streams
	 */
	slist = (Stream *)xalloc(conf.nstream * sizeof(Stream));
	qlist = (Queue *)xalloc(conf.nqueue * sizeof(Queue));

	/*
	 *  make stream modules available
	 */
	streaminit0();
}

/*
 *  make known a stream module and call its initialization routine, if
 *  it has one.
 *  Part 1) Blocks
 */
void
newqinfo(Qinfo *qi)
{
	if(qi->next)
		panic("newqinfo: already configured");

	qi->next = lds;
	lds = qi;
	if(qi->reset)
		(*qi->reset)();
}

/*
 *  allocate a block
 *  Allocate a block.  Put the data portion at the end of the smalloc'd
 *  chunk so that it can easily grow from the front to add protocol
 *  headers.  Thank Larry Peterson for the suggestion.
 */
Block *
allocb(ulong size)
{
	Block *bp;
	uchar *data;
	uchar *base, *lim;

	bp = smalloc(sizeof(Block)+size);

	data = (uchar*)bp + sizeof(Block);
	bp->rptr = data;
	bp->wptr = data;
	bp->base = data;
	bp->lim = data+size;
	base = (uchar*)bp + sizeof(Block);
	lim = (uchar*)bp + msize(bp);
	bp->wptr = bp->rptr = lim - size;
	bp->base = base;
	bp->lim = lim;
	bp->flags = 0;
	bp->next = 0;
	bp->list = 0;


@@ 95,7 38,7 @@ allocb(ulong size)

/*
 *  Free a block (or list of blocks).  Poison its pointers so that
 *  someone trying to access it after freeing will cause a dump.
 *  someone trying to access it after freeing will cause a panic.
 */
void
freeb(Block *bp)


@@ 112,7 55,8 @@ freeb(Block *bp)
}

/*
 *  pad a block to the front with n bytes
 *  Pad a block to the front with n bytes.  This is used to add protocol
 *  headers to the front of blocks.
 */
Block *
padb(Block *bp, int n)


@@ 132,6 76,253 @@ padb(Block *bp, int n)
} 

/*
 *  make sure the first block has n bytes
 */
Block *
pullup(Block *bp, int n)
{
	Block *nbp;
	int i;

	/*
	 *  this should almost always be true, the rest it
	 *  just for to avoid every caller checking.
	 */
	if(BLEN(bp) >= n)
		return bp;

	/*
	 *  if not enough room in the first block,
	 *  add another to the front of the list.
	 */
	if(bp->lim - bp->rptr < n){
		nbp = allocb(n);
		nbp->next = bp;
		bp = nbp;
	}

	/*
	 *  copy bytes from the trailing blocks into the first
	 */
	n -= BLEN(bp);
	while(nbp = bp->next){
		i = BLEN(nbp);
		if(i >= n) {
			memmove(bp->wptr, nbp->rptr, n);
			bp->wptr += n;
			nbp->rptr += n;
			return bp;
		} else {
			memmove(bp->wptr, nbp->rptr, i);
			bp->wptr += i;
			bp->next = nbp->next;
			nbp->next = 0;
			freeb(nbp);
			n -= i;
		}
	}
	freeb(bp);
	return 0;
}

/*
 *  return the number of data bytes of a list of blocks
 */
int
blen(Block *bp)
{
	int len;

	len = 0;
	while(bp) {
		len += BLEN(bp);
		bp = bp->next;
	}

	return len;
}

/*
 *  round a block chain to some even number of bytes.  Used
 *  by devip.c becuase all IP packets must have an even number
 *  of bytes.
 *
 *  The last block in the returned chain will have S_DELIM set.
 */
int
bround(Block *bp, int amount)
{
	Block *last;
	int len, pad;

	len = 0;
	SET(last);	/* Ken's magic */

	while(bp) {
		len += BLEN(bp);
		last = bp;
		bp = bp->next;
	}

	pad = ((len + amount) & ~amount) - len;
	if(pad) {
		if(last->lim - last->wptr >= pad){
			memset(last->wptr, 0, pad);
			last->wptr += pad;
		} else {
			last->next = allocb(pad);
			last->flags &= ~S_DELIM;
			last = last->next;
			last->wptr += pad;
			last->flags |= S_DELIM;
		}
	}

	return len + pad;
}

/*
 *  expand a block list to be one block, len bytes long.  used by
 *  ethernet routines.
 */
Block*
expandb(Block *bp, int len)
{
	Block *nbp, *new;
	int i;
	ulong delim = 0;

	new = allocb(len);
	if(new == 0){
		freeb(bp);
		return 0;
	}

	/*
	 *  copy bytes into new block
	 */
	for(nbp = bp; len>0 && nbp; nbp = nbp->next){
		delim = nbp->flags & S_DELIM;
		i = BLEN(nbp);
		if(i > len) {
			memmove(new->wptr, nbp->rptr, len);
			new->wptr += len;
			break;
		} else {
			memmove(new->wptr, nbp->rptr, i);
			new->wptr += i;
			len -= i;
		}
	}
	if(len){
		memset(new->wptr, 0, len);
		new->wptr += len;
	}
	new->flags |= delim;
	freeb(bp);
	return new;

}

/*
 *  make a copy of the first 'count' bytes of a block chain.  Use
 *  by transport protocols.
 */
Block *
copyb(Block *bp, int count)
{
	Block *nb, *head, **p;
	int l;

	p = &head;
	while(count) {
		l = BLEN(bp);
		if(count < l)
			l = count;
		nb = allocb(l);
		if(nb == 0)
			panic("copyb.1");
		memmove(nb->wptr, bp->rptr, l);
		nb->wptr += l;
		count -= l;
		if(bp->flags & S_DELIM)
			nb->flags |= S_DELIM;
		*p = nb;
		p = &nb->next;
		bp = bp->next;
		if(bp == 0)
			break;
	}
	if(count) {
		nb = allocb(count);
		if(nb == 0)
			panic("copyb.2");
		memset(nb->wptr, 0, count);
		nb->wptr += count;
		nb->flags |= S_DELIM;
		*p = nb;
	}
	if(blen(head) == 0)
		print("copyb: zero length\n");

	return head;
}

/*
 *  Part 2) Queues
 */

/*
 *  process end line discipline
 */
static void stputq(Queue*, Block*);
Qinfo procinfo =
{
	stputq,
	nullput,
	0,
	0,
	"process"
};

/*
 *  line disciplines that can be pushed
 */
static Qinfo *lds;

/*
 *  make known a stream module and call its initialization routine, if
 *  it has one.
 */
void
newqinfo(Qinfo *qi)
{
	if(qi->next)
		panic("newqinfo: already configured");

	qi->next = lds;
	lds = qi;
	if(qi->reset)
		(*qi->reset)();
}

/*
 *  find the info structure for line discipline 'name'
 */
Qinfo *
qinfofind(char *name)
{
	Qinfo *qi;

	if(name == 0)
		return 0;
	for(qi = lds; qi; qi = qi->next)
		if(strcmp(qi->name, name)==0)
			return qi;
	return 0;
}

/*
 *  allocate a pair of queues.  flavor them with the requested put routines.
 *  the `QINUSE' flag on the read side is the only one used.
 */


@@ 140,20 331,7 @@ allocq(Qinfo *qi)
{
	Queue *q, *wq;

	for(q=qlist; q<&qlist[conf.nqueue]; q++, q++) {
		if(q->flag == 0){
			if(canlock(q)){
				if(q->flag == 0)
					break;
				unlock(q);
			}
		}
	}

	if(q == &qlist[conf.nqueue]){
		print("no more queues\n");
		exhausted("queues");
	}
	q = smalloc(2*sizeof(Queue));

	q->flag = QINUSE;
	q->r.p = 0;


@@ 173,16 351,14 @@ allocq(Qinfo *qi)
	wq->len = wq->nb = 0;
	wq->rp = &wq->r;

	unlock(q);

	return q;
}

/*
 *  flush a queue
 *  free a queue
 */
static void
flushq(Queue *q)
freeq(Queue *q)
{
	Block *bp;



@@ 192,13 368,14 @@ flushq(Queue *q)
	q = WR(q);
	while(bp = getq(q))
		freeb(bp);
	free(RD(q));
}

/*
 *  free a queue
 *  flush a queue
 */
static void
freeq(Queue *q)
flushq(Queue *q)
{
	Block *bp;



@@ 208,7 385,6 @@ freeq(Queue *q)
	q = WR(q);
	while(bp = getq(q))
		freeb(bp);
	RD(q)->flag = 0;
}

/*


@@ 298,52 474,6 @@ putq(Queue *q, Block *bp)
}

int
blen(Block *bp)
{
	int len;

	len = 0;
	while(bp) {
		len += BLEN(bp);
		bp = bp->next;
	}

	return len;
}

/*
 * bround - round a block chain to some 2^n number of bytes
 */
int
bround(Block *bp, int amount)
{
	Block *last;
	int len, pad;

	len = 0;
	SET(last);	/* Ken's magic */

	while(bp) {
		len += BLEN(bp);
		last = bp;
		bp = bp->next;
	}

	pad = ((len + amount) & ~amount) - len;
	if(pad) {
		last->next = allocb(pad);
		last->flags &= ~S_DELIM;
		last = last->next;
		memset(last->wptr, 0, pad);
		last->wptr += pad;
		last->flags |= S_DELIM;
		
	}

	return len + pad;
}

int
putb(Blist *q, Block *bp)
{
	int delim;


@@ 440,210 570,36 @@ getb(Blist *q)

	bp = q->first;
	if(bp) {
		q->first = bp->next;
		if(q->first == 0)
			q->last = 0;
		q->len -= BLEN(bp);
		bp->next = 0;
	}
	return bp;
}

/*
 *  make sure the first block has n bytes
 */
Block *
pullup(Block *bp, int n)
{
	Block *nbp;
	int i;

	/*
	 *  this should almost always be true, the rest it
	 *  just for to avoid every caller checking.
	 */
	if(BLEN(bp) >= n)
		return bp;

	/*
	 *  if not enough room in the first block,
	 *  add another to the front of the list.
	 */
	if(bp->lim - bp->rptr < n){
		nbp = allocb(n);
		nbp->next = bp;
		bp = nbp;
	}

	/*
	 *  copy bytes from the trailing blocks into the first
	 */
	n -= BLEN(bp);
	while(nbp = bp->next){
		i = BLEN(nbp);
		if(i >= n) {
			memmove(bp->wptr, nbp->rptr, n);
			bp->wptr += n;
			nbp->rptr += n;
			return bp;
		} else {
			memmove(bp->wptr, nbp->rptr, i);
			bp->wptr += i;
			bp->next = nbp->next;
			nbp->next = 0;
			freeb(nbp);
		}
	}
	freeb(bp);
	return 0;
}

/*
 *  expand a block list to be one block, len bytes long
 */
Block*
expandb(Block *bp, int len)
{
	Block *nbp, *new;
	int i;
	ulong delim = 0;

	new = allocb(len);
	if(new == 0){
		freeb(bp);
		return 0;
	}

	/*
	 *  copy bytes into new block
	 */
	for(nbp = bp; len>0 && nbp; nbp = nbp->next){
		delim = nbp->flags & S_DELIM;
		i = BLEN(nbp);
		if(i > len) {
			memmove(new->wptr, nbp->rptr, len);
			new->wptr += len;
			break;
		} else {
			memmove(new->wptr, nbp->rptr, i);
			new->wptr += i;
			len -= i;
		}
	}
	if(len){
		memset(new->wptr, 0, len);
		new->wptr += len;
	}
	new->flags |= delim;
	freeb(bp);
	return new;

}

/*
 *  grow the front of a list of blocks by n bytes
 */
Block *
prepend(Block *bp, int n)
{
	Block *nbp;

	if(bp->base && (bp->rptr - bp->base)>=n){
		/*
		 *  room for channel number in first block of message
		 */
		bp->rptr -= n;
		return bp;
	} else {
		/*
		 *  make new block, put message number at end
		 */
		nbp = allocb(2);
		nbp->next = bp;
		nbp->wptr = nbp->lim;
		nbp->rptr = nbp->wptr - n;
		return nbp;
	}
}


/*
 *  put a block into the bit bucket
 */
void
nullput(Queue *q, Block *bp)
{
	USED(q);
	if(bp->type == M_HANGUP)
		freeb(bp);
	else {
		freeb(bp);
		error(Ehungup);
	}
}

/*
 *  find the info structure for line discipline 'name'
 */
Qinfo *
qinfofind(char *name)
{
	Qinfo *qi;

	if(name == 0)
		return 0;
	for(qi = lds; qi; qi = qi->next)
		if(strcmp(qi->name, name)==0)
			return qi;
	return 0;
}

/*
 *  send a hangup up a stream
 */
static void
hangup(Stream *s)
{
	Block *bp;

	bp = allocb(0);
	bp->type = M_HANGUP;
	(*s->devq->put)(s->devq, bp);
		q->first = bp->next;
		if(q->first == 0)
			q->last = 0;
		q->len -= BLEN(bp);
		bp->next = 0;
	}
	return bp;
}


/*
 *  parse a string and return a pointer to the second element if the 
 *  first matches name.  bp->rptr will be updated to point to the
 *  second element.
 *
 *  return 0 if no match.
 *
 *  it is assumed that the block data is null terminated.  streamwrite
 *  guarantees this.
 *  put a block into the bit bucket
 */
int
streamparse(char *name, Block *bp)
void
nullput(Queue *q, Block *bp)
{
	int len;

	len = strlen(name);
	if(BLEN(bp) < len)
		return 0;
	if(strncmp(name, (char *)bp->rptr, len)==0){
		if(bp->rptr[len] == ' ')
			bp->rptr += len+1;
		else if(bp->rptr[len])
			return 0;
		else
			bp->rptr += len;
		while(*bp->rptr==' ' && bp->wptr>bp->rptr)
			bp->rptr++;
		return 1;
	USED(q);
	if(bp->type == M_HANGUP)
		freeb(bp);
	else {
		freeb(bp);
		error(Ehungup);
	}
	return 0;
}

/*
 *  Part 3) Streams
 */

/*
 *  the per stream directory structure
 */
Dirtab streamdir[]={


@@ 652,6 608,34 @@ Dirtab streamdir[]={
};

/*
 *  hash buckets containing all streams
 */
enum
{
	Nbits=	5,
	Nhash=	1<<Nbits,
	Nmask=	Nhash-1,
};
typedef struct Sthash Sthash;
struct Sthash
{
	QLock;
	Stream	*s;
};
static Sthash ht[Nhash];

static void	hangup(Stream*);

void
streaminit(void)
{
	/*
	 *  make stream modules available
	 */
	streaminit0();
}

/*
 *  A stream device consists of the contents of streamdir plus
 *  any directory supplied by the actual device.
 *


@@ 678,6 662,15 @@ streamgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp)
}

/*
 *  return a hash bucket for a stream
 */
static Sthash*
hash(int type, int dev, int id)
{
	return &ht[(type*7*7 + dev*7 + id) & Nmask];
}

/*
 *  create a new stream, if noopen is non-zero, don't increment the open count
 */
Stream *


@@ 685,46 678,61 @@ streamnew(ushort type, ushort dev, ushort id, Qinfo *qi, int noopen)
{
	Stream *s;
	Queue *q;
	Sthash *hb;

	hb = hash(type, dev, id);

	/*
	 *  find a free stream struct
	 *  if the stream already exists, just increment the reference counts.
	 */
	for(s = slist; s < &slist[conf.nstream]; s++) {
		if(s->inuse == 0){
			if(canqlock(s)){
				if(s->inuse == 0)
					break;
	qlock(hb);
	for(s = hb->s; s; s = s->next) {
		if(s->type == type && s->dev == dev && s->id == id){
			s->inuse++;
			qunlock(hb);
			if(noopen == 0){
				qlock(s);
				s->opens++;
				qunlock(s);
			}
			return s;
		}
	}
	if(s == &slist[conf.nstream]){
		print("no more streams\n");
		exhausted("streams");
	}
	if(waserror()){
		qunlock(s);
		streamclose1(s);
		nexterror();
	}

	/*
	 *  identify the stream
	 *  create and init a new stream
	 */
	s = smalloc(sizeof(Stream));
	s->inuse = 1;
	s->type = type;
	s->dev = dev;
	s->id = id;
	s->err = 0;
	s->hread = 0;
	s->next = hb->s;
	hb->s = s;

	/*
	 *  The ordering of these 2 instructions is very important.
	 *  It makes sure we finish the stream initialization before
	 *  anyone else can access it.
	 */
	qlock(s);
	qunlock(hb);

	if(waserror()){
		qunlock(s);
		streamclose1(s);
		nexterror();
	}

	/*
 	 *  hang a device and process q off the stream
	 */
	s->inuse = 1;
	if(noopen)
		s->opens = 0;
	else
		s->opens = 1;
	s->hread = 0;
	q = allocq(&procinfo);
	WR(q)->ptr = s;
	RD(q)->ptr = s;


@@ 745,72 753,58 @@ streamnew(ushort type, ushort dev, ushort id, Qinfo *qi, int noopen)
}

/*
 *  (Re)open a stream.  If this is the first open, create a stream.
 *  Associate a stream with a channel
 */
void
streamopen(Chan *c, Qinfo *qi)
{
	Stream *s;
	Queue *q;

	/*
	 *  if the stream already exists, just increment the reference counts.
	 */
	for(s = slist; s < &slist[conf.nstream]; s++) {
		if(s->inuse && s->type == c->type && s->dev == c->dev
		   && s->id == STREAMID(c->qid.path)){
			qlock(s);
			if(s->inuse && s->type == c->type
			&& s->dev == c->dev
		 	&& s->id == STREAMID(c->qid.path)){
				s->inuse++;
				s->opens++;
				c->stream = s;
				qunlock(s);
				return;
			}
			qunlock(s);
		}
	}

	/*
	 *  create a new stream
	 */
	c->stream = streamnew(c->type, c->dev, STREAMID(c->qid.path), qi, 0);
}

/*
 *  Enter a stream.  Increment the reference count so it can't disappear
 *  under foot.
 *  Enter a stream only if the stream exists and is open.  Increment the
 *  reference count so it can't disappear under foot.
 *
 *  Return -1 if the stream no longer exists or is not opened.
 */
int
streamenter(Stream *s)
{
	qlock(s);
	if(s->opens == 0){
		qunlock(s);
		return -1;
	}
	s->inuse++;
	qunlock(s);
	return 0;
	Sthash *hb;
	Stream *ns;

	hb = hash(s->type, s->dev, s->id);
	qlock(hb);
	for(ns = hb->s; ns; ns = ns->next)
		if(s->type == ns->type && s->dev == ns->dev && s->id == ns->id){
			s->inuse++;
			qunlock(hb);
			if(s->opens == 0){
				streamexit(s, 1);
				return -1;
			}
			return 0;
		}
	qunlock(hb);
	return -1;
}

/*
 *  Decrement the reference count on a stream.  If the count is
 *  zero, free the stream.
 */
int
void
streamexit(Stream *s, int locked)
{
	Queue *q;
	Queue *nq;
	int rv;
	char *name;
	Sthash *hb;
	Stream **l, *ns;

	if(!locked)
		qlock(s);
	if(s->inuse == 1){
	hb = hash(s->type, s->dev, s->id);
	qlock(hb);
	if(s->inuse-- == 1){
		if(s->opens != 0)
			panic("streamexit %d %s\n", s->opens, s->devq->info->name);



@@ 821,20 815,28 @@ streamexit(Stream *s, int locked)
			nq = q->next;
			freeq(q);
		}
		s->id = s->dev = s->type = 0;
		if(s->err)
			freeb(s->err);

		/*
		 *  unchain it from the hash bucket and free
		 */
		l = &hb->s;
		for(ns = hb->s; ns; ns = ns->next){
			if(s == ns){
				*l = s->next;
				break;
			}
			l = &ns->next;
		}
		free(s);
	}
	s->inuse--;
	rv = s->inuse;
	if(!locked)
		qunlock(s);
	return rv;
	qunlock(hb);
}

/*
 *  On the last close of a stream, for each queue on the
 *  stream release its blocks and call its close routine.
 *  Decrement the open count.  When it goes to zero, call the close
 *  routines for each queue in the stream.
 */
int
streamclose1(Stream *s)


@@ 844,10 846,10 @@ streamclose1(Stream *s)
	int rv;

	/*
	 *  decrement the reference count
	 *  decrement the open count
	 */
	qlock(s);
	if(s->opens == 1){
	if(s->opens-- == 1){
		/*
		 *  descend the stream closing the queues
		 */


@@ 874,13 876,13 @@ streamclose1(Stream *s)
			flushq(q);
		}
	}
	rv = --(s->opens);
	rv = s->opens;
	qunlock(s);

	/*
	 *  leave it and free it
	 */
	streamexit(s, 1);
	qunlock(s);
	return rv;
}
int


@@ 940,37 942,19 @@ stputq(Queue *q, Block *bp)
}

/*
 *  read a string.  update the offset accordingly.
 */
long
stringread(uchar *buf, long n, char *str, ulong offset)
{
	long i;

	i = strlen(str);
	i -= offset;
	if(i<n)
		n = i;
	if(n<0)
		return 0;
	memmove(buf, str + offset, n);
	return n;
}

/*
 *  return the stream id
 */
long
streamctlread(Chan *c, void *vbuf, long n)
{
	uchar *buf = vbuf;
	char *buf = vbuf;
	char num[32];
	Stream *s;

	s = c->stream;
	if(STREAMTYPE(c->qid.path) == Sctlqid){
		sprint(num, "%d", s->id);
		return stringread(buf, n, num, c->offset);
		return readstr(c->offset, buf, n, num);
	} else {
		if(CHDIR & c->qid.path)
			return devdirread(c, vbuf, n, 0, 0, streamgen);


@@ 1245,26 1229,6 @@ streamwrite(Chan *c, void *a, long n, int docopy)
}

/*
 *  like andrew's getmfields but no hidden state
 */
int
getfields(char *lp, char **fields, int n, char sep)
{
	int i;

	for(i=0; lp && *lp && i<n; i++){
		while(*lp == sep)
			*lp++=0;
		if(*lp == 0)
			break;
		fields[i]=lp;
		while(*lp && *lp != sep)
			lp++;
	}
	return i;
}

/*
 *  stat a stream.  the length is the number of bytes up to the
 *  first delimiter.
 */


@@ 1295,88 1259,72 @@ streamstat(Chan *c, char *db, char *name)
	convD2M(&dir, db);
}

Block *
copyb(Block *bp, int count)
/*
 *  send a hangup up a stream
 */
static void
hangup(Stream *s)
{
	Block *nb, *head, **p;
	int l;
	Block *bp;

	p = &head;
	while(count) {
		l = BLEN(bp);
		if(count < l)
			l = count;
		nb = allocb(l);
		if(nb == 0)
			panic("copyb.1");
		memmove(nb->wptr, bp->rptr, l);
		nb->wptr += l;
		count -= l;
		if(bp->flags & S_DELIM)
			nb->flags |= S_DELIM;
		*p = nb;
		p = &nb->next;
		bp = bp->next;
		if(bp == 0)
			break;
	}
	if(count) {
		nb = allocb(count);
		if(nb == 0)
			panic("copyb.2");
		memset(nb->wptr, 0, count);
		nb->wptr += count;
		nb->flags |= S_DELIM;
		*p = nb;
	}
	if(blen(head) == 0)
		print("copyb: zero length\n");
	bp = allocb(0);
	bp->type = M_HANGUP;
	(*s->devq->put)(s->devq, bp);
}

	return head;
/*
 *  parse a string and return a pointer to the second element if the 
 *  first matches name.  bp->rptr will be updated to point to the
 *  second element.
 *
 *  return 0 if no match.
 *
 *  it is assumed that the block data is null terminated.  streamwrite
 *  guarantees this.
 */
int
streamparse(char *name, Block *bp)
{
	int len;

	len = strlen(name);
	if(BLEN(bp) < len)
		return 0;
	if(strncmp(name, (char *)bp->rptr, len)==0){
		if(bp->rptr[len] == ' ')
			bp->rptr += len+1;
		else if(bp->rptr[len])
			return 0;
		else
			bp->rptr += len;
		while(*bp->rptr==' ' && bp->wptr>bp->rptr)
			bp->rptr++;
		return 1;
	}
	return 0;
}

/*
 *  Dump all block information of how many blocks are in which queues
 *  like andrew's getmfields but no hidden state
 */
void
dumpblocks(Queue *q, char c)
int
getfields(char *lp, char **fields, int n, char sep)
{
	Block *bp;
	uchar *cp;
	int i;

	lock(q);
	for(bp = q->first; bp; bp = bp->next){
		print("%c %c%d%c", c, bp->type == M_DATA ? 'd' : 'c',
			bp->wptr-bp->rptr, (bp->flags&S_DELIM)?'D':' ');
		for(cp = bp->rptr; cp<bp->wptr && cp<bp->rptr+30; cp++)
			print(" %.2x", *cp);
		print("\n");
	for(i=0; lp && *lp && i<n; i++){
		while(*lp == sep)
			*lp++=0;
		if(*lp == 0)
			break;
		fields[i]=lp;
		while(*lp && *lp != sep)
			lp++;
	}
	unlock(q);
	return i;
}

void
dumpqueues(void)
{
	Queue *q;
	int count, qcount;
	Block *bp;

	print("\n");
	qcount = 0;
	for(q = qlist; q < qlist + conf.nqueue; q++, q++){
		if(!(q->flag & QINUSE))
			continue;
		qcount++;
		print("%10s %ux  R n %d l %d f %ux r %ux ",
			q->info->name, q,
			q->nb, q->len, q->flag, &(q->r));
		print("  W n %d l %d f %ux r %ux next %lux put %lux Rz %lux", 
			WR(q)->nb, WR(q)->len,
			WR(q)->flag, &(WR(q)->r), q->next, q->put, q->rp);
		print("\n");
		dumpblocks(q, 'R');
		dumpblocks(WR(q), 'W');
	}
	print("%d queues\n", qcount);
}

M port/sturp.c => port/sturp.c +22 -26
@@ 34,6 34,7 @@ struct urpstat {

struct Urp {
	QLock;
	Urp	*list;		/* list of all urp structures */
	short	state;		/* flags */
	Rendez	r;		/* process waiting for output to finish */



@@ 60,7 61,13 @@ struct Urp {
	ulong	timer;		/* timeout for xmit */
	int	rexmit;
};
Urp	*urp;

/* list of allocated urp structures (never freed) */
struct
{
	Lock;
	Urp	*urp;
} urpalloc;

Rendez	urpkr;
QLock	urpkl;


@@ 135,7 142,6 @@ Qinfo urpinfo =
static void
urpreset(void)
{
	urp = (Urp *)xalloc(conf.nurp*sizeof(Urp));
}

static void


@@ 155,9 161,9 @@ urpopen(Queue *q, Stream *s)
	}

	/*
	 *  find a free urp structure
	 *  find an unused urp structure
	 */
	for(up = urp; up < &urp[conf.nurp]; up++){
	for(up = urpalloc.urp; up; up = up->list){
		if(up->state == 0){
			qlock(up);
			if(up->state == 0)


@@ 165,15 171,17 @@ urpopen(Queue *q, Stream *s)
			qunlock(up);
		}
	}
	if(up == &urp[conf.nurp]){
		q->ptr = 0;
		WR(q)->ptr = 0;
		exhausted("urp structures");
	if(up == 0){
		/*
		 *  none available, create a new one, they are never freed
		 */
		up = smalloc(sizeof(Urp));
		qlock(up);
		lock(&urpalloc);
		up->list = urpalloc.urp;
		urpalloc.urp = up;
		unlock(&urpalloc);
	}
/*
	q->flag |= QDEBUG;
	q->other->flag |= QDEBUG;
*/
	q->ptr = q->other->ptr = up;
	q->rp = &urpkr;
	up->rq = q;


@@ 993,16 1001,15 @@ initinput(Urp *up, int window)
static void
urpkproc(void *arg)
{
	Urp *up, *eup;
	Urp *up;

	USED(arg);

	if(waserror())
		;

	eup = urp + conf.nurp;
	for(;;){
		for(up = urp; up < eup; up++){
		for(up = urpalloc.urp; up; up = up->list){
			if(up->state==0 || (up->state&HUNGUP))
				continue;
			if(!canqlock(up))


@@ 1044,17 1051,6 @@ urpvomit(char *msg, Urp* up)
		up->rq->next->len);
}

int
urpdump(void)
{
	Urp *up;

	for(up = urp; up < &urp[conf.nurp]; up++)
		if(up->rq)
			urpvomit("", up);
	return 0;
}

void
urpfillstats(Chan *c, char *buf, int len)
{

M port/sysproc.c => port/sysproc.c +11 -9
@@ 51,7 51,8 @@ sysrfork(ulong *arg)
			if((flag & (RFENVG|RFCENVG)) == (RFENVG|RFCENVG))
				error(Ebadarg);
			oeg = p->egrp;
			p->egrp = newegrp();
			p->egrp = smalloc(sizeof(Egrp));
			p->egrp->ref = 1;
			if(flag & RFENVG)
				envcpy(p->egrp, oeg);
			closeegrp(oeg);


@@ 64,7 65,8 @@ sysrfork(ulong *arg)
		else
		if(flag & RFCFDG) {
			ofg = p->fgrp;
			p->fgrp = newfgrp();
			p->fgrp = smalloc(sizeof(Fgrp));
			p->fgrp->ref = 1;
			closefgrp(ofg);
		}
		if(flag & RFNOTEG)


@@ 110,8 112,10 @@ sysrfork(ulong *arg)
	if(flag & (RFFDG|RFCFDG)) {
		if(flag & RFFDG)
			p->fgrp = dupfgrp(parent->fgrp);
		else
			p->fgrp = newfgrp();
		else {
			p->fgrp = smalloc(sizeof(Fgrp));
			p->fgrp->ref = 1;
		}
	}
	else {
		p->fgrp = parent->fgrp;


@@ 136,12 140,10 @@ sysrfork(ulong *arg)

	/* Environment group */
	if(flag & (RFENVG|RFCENVG)) {
		if(flag & RFENVG) {
			p->egrp = newegrp();
		p->egrp = smalloc(sizeof(Egrp));
		p->egrp->ref = 1;
		if(flag & RFENVG)
			envcpy(p->egrp, parent->egrp);
		}
		else
			p->egrp = newegrp();
	}
	else {
		p->egrp = parent->egrp;

M power/conf.h => power/conf.h +1 -8
@@ 4,16 4,9 @@ Conftab conftab[] = {
	{"npage0", &conf.npage0 },
	{"npage1", &conf.npage1 },
	{"npage", &conf.npage },
	{"upages", &conf.upages },
	{"nimage", &conf.nimage },
	{"nswap", &conf.nswap },
	{"nenv", &conf.nenv },
	{"nenvchar", &conf.nenvchar },
	{"npgenv", &conf.npgenv },
	{"nstream", &conf.nstream },
	{"nqueue", &conf.nqueue },
	{"nsrv", &conf.nsrv },
	{"nurp", &conf.nurp },
	{"nasync", &conf.nasync },
	{"base0", &conf.base0 },
	{"base1", &conf.base1 },
	{"copymode", &conf.copymode },

M power/dat.h => power/dat.h +0 -8
@@ 43,14 43,6 @@ struct Conf
	ulong	upages;		/* user page pool */
	ulong	nimage;		/* number of page cache image headers */
	ulong	nswap;		/* number of swap pages */
	ulong	nenv;		/* distinct environment values */
	ulong	nenvchar;	/* environment text storage */
	ulong	npgenv;		/* environment files per process group */
	ulong	nstream;	/* streams */
	ulong	nqueue;		/* stream queues */
	ulong	nsrv;		/* public servers (devsrv.c) */
	ulong	nurp;		/* max urp conversations */
	ulong	nasync;		/* number of async protocol modules */
	ulong	base0;		/* base of bank 0 */
	ulong	base1;		/* base of bank 1 */
	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */

M power/devhs.c => power/devhs.c +7 -6
@@ 341,6 341,13 @@ hsoput(Queue *q, Block *bp)
		freeb(bp);
		return;
	}
	if(BLEN(bp) < 3){
		bp = pullup(bp, 3);
		if(bp == 0){
			print("hsoput pullup failed\n");
			return;
		}
	}

	/*
	 *  get a whole message before handing bytes to the device


@@ 363,12 370,6 @@ hsoput(Queue *q, Block *bp)
	 *  parse message
	 */
	bp = getq(q);
	if(bp->wptr - bp->rptr < 3){
		freemsg(q, bp);
		qunlock(&hp->xmit);
		poperror();
		return;
	}
	chan = CHNO | bp->rptr[0] | (bp->rptr[1]<<8);
	ctl = bp->rptr[2];
	bp->rptr += 3;

M power/main.c => power/main.c +0 -7
@@ 30,7 30,6 @@ main(void)
	printinit();
	duartspecial(0, &printq, &kbdq, 9600);
	pageinit();
xsummary();
	tlbinit();
	vecinit();
	procinit0();


@@ 536,15 535,9 @@ confinit(void)
	 */
	conf.nmach = 1;
	conf.nproc = 100;
	conf.npgenv = 4 * conf.nproc;
	conf.nenv = 4 * conf.nproc;
	conf.nenvchar = 20 * conf.nenv;
	conf.nswap = 262144;
	conf.nimage = 200;
	conf.nstream = 2 * conf.nproc;
	conf.nurp = 25;
	conf.dkif = 1;
	conf.nqueue = 5 * conf.nstream;
	conf.ipif = 8;
	conf.ip = 64;
	conf.arp = 32;

M ss/dat.h => ss/dat.h +0 -10
@@ 67,16 67,6 @@ struct Conf
	ulong	nimage;		/* number of page cache image headers */
	ulong	nswap;		/* number of swap blocks */
	ulong	upages;		/* number of user pages */
	int	nenv;		/* distinct environment values */
	int	nenvchar;	/* environment text storage */
	int	npgenv;		/* environment files per process group */
	int	nstream;	/* streams */
	int	nqueue;		/* stream queues */
	int	nsrv;		/* public servers (devsrv.c) */
	int	nbitmap;	/* bitmap structs (devbit.c) */
	int	nbitbyte;	/* bytes of bitmap data (devbit.c) */
	int	nfont;		/* GFont structs (devbit.c) */
	int	nsubfont;	/* Gsubfont structs (devbit.c) */
	int	nurp;		/* max urp conversations */
	int	nasync;		/* number of async protocol modules */
	int	copymode;	/* 0 is copy on write, 1 is copy on reference */

M ss/main.c => ss/main.c +4 -9
@@ 128,8 128,10 @@ userinit(void)

	p = newproc();
	p->pgrp = newpgrp();
	p->egrp = newegrp();
	p->fgrp = newfgrp();
	p->egrp = smalloc(sizeof(Egrp));
	p->egrp->ref = 1;
	p->fgrp = smalloc(sizeof(Fgrp));
	p->fgrp->ref = 1;
	p->procmode = 0640;

	strcpy(p->text, "*init*");


@@ 210,13 212,6 @@ confinit(void)
	conf.nproc = 50*mul;
	conf.nswap = 4096;
	conf.nimage = 50;
	conf.nenv = 100*mul;
	conf.nenvchar = 8000*mul;
	conf.npgenv = 200*mul;
	conf.nstream = 40 + 32*mul;
	conf.nqueue = 5 * conf.nstream;
	conf.nurp = 32;
	conf.nasync = 1;
	conf.copymode = 0;		/* copy on write */
	conf.ipif = 8;
	conf.ip = 64;