~kris/9p

9hist

654edeace8256b43d92d62f989435dbcc1dd7f0f — David du Colombier 35 years ago 0b77a8c
Plan 9 from Bell Labs 1991-09-04
11 files changed, 875 insertions(+), 656 deletions(-)

M pc/bbmalloc.c
M pc/devuart.c
M pc/io.h
M pc/kbd.c
M pc/l.s
M pc/trap.c
M pc/vga.c
M port/devmnt.c
M port/devnonet.c
M port/stream.c
M port/sysfile.c
M pc/bbmalloc.c => pc/bbmalloc.c +29 -55
@@ 3,77 3,51 @@
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"

/*
 * Allocate memory for use in kernel bitblts.
 * The allocated memory must have a flushed instruction
 * cache, and the data cache must be flushed by bbdflush().
 * To avoid the need for frequent cache flushes, the memory
 * is allocated out of an arena, and the i-cache is only
 * flushed when it has to be reused.  By returning an
 * address in non-cached space, the need for flushing the
 * d-cache is avoided.
 *
 * On the safari, all this is unnecessary, since the
 * only icache is a miniscule prefetch buffer.
 *
 * Currently, the only kernel users of bitblt are devbit,
 * print, and the cursor stuff in devbit.  The cursor
 * can get drawn at clock interrupt time, so it might need
 * to bbmalloc while another bitblt is going on.
 *
 * This code will have to be interlocked if we ever get
 * a multiprocessor with a bitmapped display.
 */
static	char	bbarena[10000];
static	char	bbused[10];

/* a 0->3 bitblt can take 900 words */
enum {
	nbbarena=8192	/* number of words in an arena */
};

static ulong	bbarena[nbbarena];
static ulong	*bbcur = bbarena;
static ulong	*bblast = 0;

void *
bbmalloc(int nbytes)
void*
bbmalloc(int n)
{
	int nw;
	int s;
	ulong *ans;
	char *a;
	int s, i;

	nw = nbytes/sizeof(long);
	s = splhi();
	if(bbcur + nw > &bbarena[nbbarena])
		ans = bbarena;
	else
		ans = bbcur;
	bbcur = ans + nw;
	a = memchr(bbused, 0, sizeof(bbused));
	if(a) {
		i = a - bbused;
		a = bbarena + i*n;
		if(a+n <= bbarena+sizeof(bbarena)) {
			bbused[i] = 1;
			splx(s);
			return a;
		}
	}
	splx(s);
/*
	if(ans == bbarena)
		icflush(ans, sizeof(bbarena));
*/
	bblast = ans;
	ans = (void *)ans;
	return ans;
	print("too many bbmallocs\n");
	return bbarena;
}

void
bbfree(void *p, int n)
bbfree(void *va, int n)
{
	ulong *up;
	int s;
	int s, i;

	s = splhi();
	if(p == bblast)
		bbcur = (ulong *)(((char *)bblast) + n);
	i = ((char*)va - bbarena) /n;
	if(i >= 0 && i < sizeof(bbused) && bbused[i]){
		bbused[i] = 0;
		splx(s);
		return;
	}
	splx(s);
	print("sanity bbfree\n");
}

void *
void*
bbdflush(void *p, int n)
{
	return p;
}


M pc/devuart.c => pc/devuart.c +4 -1
@@ 259,7 259,7 @@ uartintr(Uart *up)
			break;
	
		case 0:	/* modem status */
			l = uartrdreg(up, Mstat);
			uartrdreg(up, Mstat);
			break;
	
		default:


@@ 529,6 529,9 @@ uartkproc(void *a)
	int n;
	ulong frame, overrun;

	frame = 0;
	overrun = 0;

	if(waserror())
		print("uartkproc got an error\n");


M pc/io.h => pc/io.h +1 -1
@@ 4,7 4,7 @@
enum
{
	Faultvec=	14,		/* page fault */
	Int0vec=	16,		/* first 8259 */
	Int0vec=	24,		/* first 8259 */
	 Clockvec=	Int0vec+0,	/*  clock interrupts */
	 Kbdvec=	Int0vec+1,	/*  keyboard interrupts */
	 Uart1vec=	Int0vec+3,	/*  modem line */

M pc/kbd.c => pc/kbd.c +13 -10
@@ 277,16 277,19 @@ mousecmd(int cmd)
	unsigned int c;
	int tries;

	for(tries=0; tries < 10; tries++){
		if(outready() < 0)
			return -1;
		outb(Cmd, 0xD4);
		if(outready() < 0)
			return -1;
		outb(Data, cmd);
		if(inready() < 0)
			return -1;
		c = inb(Data);
	c = 0;
	do{
		for(tries=0; tries < 10; tries++){
			if(outready() < 0)
				return -1;
			outb(Cmd, 0xD4);
			if(outready() < 0)
				return -1;
			outb(Data, cmd);
			if(inready() < 0)
				return -1;
			c = inb(Data);
		}
	} while(c == 0xFE);
	if(c != 0xFA)
		return -1;

M pc/l.s => pc/l.s +32 -28
@@ 343,34 343,6 @@ TEXT	intr16(SB),$0
	PUSHL	$0
	PUSHL	$16
	JMP	intrcommon
TEXT	intr17(SB),$0
	PUSHL	$0
	PUSHL	$17
	JMP	intrcommon
TEXT	intr18(SB),$0
	PUSHL	$0
	PUSHL	$18
	JMP	intrcommon
TEXT	intr19(SB),$0
	PUSHL	$0
	PUSHL	$19
	JMP	intrcommon
TEXT	intr20(SB),$0
	PUSHL	$0
	PUSHL	$20
	JMP	intrcommon
TEXT	intr21(SB),$0
	PUSHL	$0
	PUSHL	$21
	JMP	intrcommon
TEXT	intr22(SB),$0
	PUSHL	$0
	PUSHL	$22
	JMP	intrcommon
TEXT	intr23(SB),$0
	PUSHL	$0
	PUSHL	$23
	JMP	intrcommon
TEXT	intr24(SB),$0
	PUSHL	$0
	PUSHL	$24


@@ 403,6 375,38 @@ TEXT	intr31(SB),$0
	PUSHL	$0
	PUSHL	$31
	JMP	intrcommon
TEXT	intr32(SB),$0
	PUSHL	$0
	PUSHL	$16
	JMP	intrcommon
TEXT	intr33(SB),$0
	PUSHL	$0
	PUSHL	$33
	JMP	intrcommon
TEXT	intr34(SB),$0
	PUSHL	$0
	PUSHL	$34
	JMP	intrcommon
TEXT	intr35(SB),$0
	PUSHL	$0
	PUSHL	$35
	JMP	intrcommon
TEXT	intr36(SB),$0
	PUSHL	$0
	PUSHL	$36
	JMP	intrcommon
TEXT	intr37(SB),$0
	PUSHL	$0
	PUSHL	$37
	JMP	intrcommon
TEXT	intr38(SB),$0
	PUSHL	$0
	PUSHL	$38
	JMP	intrcommon
TEXT	intr39(SB),$0
	PUSHL	$0
	PUSHL	$39
	JMP	intrcommon
TEXT	intr64(SB),$0
	PUSHL	$0
	PUSHL	$64

M pc/trap.c => pc/trap.c +15 -13
@@ 14,10 14,11 @@ void	intr0(void), intr1(void), intr2(void), intr3(void);
void	intr4(void), intr5(void), intr6(void), intr7(void);
void	intr8(void), intr9(void), intr10(void), intr11(void);
void	intr12(void), intr13(void), intr14(void), intr15(void);
void	intr16(void), intr17(void), intr18(void), intr19(void);
void	intr20(void), intr21(void), intr22(void), intr23(void);
void	intr16(void);
void	intr24(void), intr25(void), intr26(void), intr27(void);
void	intr28(void), intr29(void), intr30(void), intr31(void);
void	intr32(void), intr33(void), intr34(void), intr35(void);
void	intr36(void), intr37(void), intr38(void), intr39(void);
void	intr64(void);
void	intrbad(void);



@@ 78,11 79,11 @@ trapinit(void)
	/*
	 *  set all interrupts to panics
	 */
	for(i = 32; i < 256; i++)
	for(i = 0; i < 256; i++)
		sethvec(i, intrbad, SEGTG, 0);

	/*
	 *  set the standard traps
	 *  80386 processor (and coprocessor) traps
	 */
	sethvec(0, intr0, SEGTG, 0);
	sethvec(1, intr1, SEGTG, 0);


@@ 100,18 101,11 @@ trapinit(void)
	sethvec(13, intr13, SEGTG, 0);
	sethvec(14, intr14, SEGTG, 0);
	sethvec(15, intr15, SEGTG, 0);
	sethvec(16, intr16, SEGTG, 0);

	/*
	 *  set the standard devices
	 *  device interrupts
	 */
	sethvec(16, intr16, SEGIG, 0);
	sethvec(17, intr17, SEGIG, 0);
	sethvec(18, intr18, SEGIG, 0);
	sethvec(19, intr19, SEGIG, 0);
	sethvec(20, intr20, SEGIG, 0);
	sethvec(21, intr21, SEGIG, 0);
	sethvec(22, intr22, SEGIG, 0);
	sethvec(23, intr23, SEGIG, 0);
	sethvec(24, intr24, SEGIG, 0);
	sethvec(25, intr25, SEGIG, 0);
	sethvec(26, intr26, SEGIG, 0);


@@ 120,6 114,14 @@ trapinit(void)
	sethvec(29, intr29, SEGIG, 0);
	sethvec(30, intr30, SEGIG, 0);
	sethvec(31, intr31, SEGIG, 0);
	sethvec(32, intr32, SEGIG, 0);
	sethvec(33, intr33, SEGIG, 0);
	sethvec(34, intr34, SEGIG, 0);
	sethvec(35, intr35, SEGIG, 0);
	sethvec(36, intr36, SEGIG, 0);
	sethvec(37, intr37, SEGIG, 0);
	sethvec(38, intr38, SEGIG, 0);
	sethvec(39, intr39, SEGIG, 0);

	/*
	 *  system calls

M pc/vga.c => pc/vga.c +1 -4
@@ 148,13 148,10 @@ munch(void)
void
screeninit(void)
{
	uchar *display;
	int i, j, k;
	int c;
	ulong *l;

	display = (uchar *)SCREENMEM;

	arout(Acpe, 0x0f);	/* enable all planes */
	arout(Amode, 0x01);	/* graphics mode - 4 bit pixels */
	grout(Gmisc, 0x01);	/* graphics mode */


@@ 268,7 265,7 @@ hwcursmove(int x, int y)
}

void
mouseclock(void)	/* called splhi */
mouseclock(void)
{
	mouseupdate(1);
}

M port/devmnt.c => port/devmnt.c +775 -539
@@ 4,108 4,268 @@
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"

#include	"devtab.h"

#include	"fcall.h"

typedef struct Mntrpc Mntrpc;
typedef struct Mnt Mnt;

struct Mntrpc
{
	Mntrpc	*list;		/* Free/pending list */
	Fcall	request;	/* Outgoing file system protocol message */
	Fcall	reply;		/* Incoming reply */
	Mnt	*m;		/* Mount device during rpc */
	Rendez	r;		/* Place to hang out */
	char	*rpc;		/* I/O Data buffer */
	char	done;		/* Rpc completed */
	char	bfree;		/* Buffer may be freed after flush */
	char	flushed;	/* Flush was sent */
	ushort	flushtag;	/* Tag to send flush on */
	ushort	flushbase;	/* Base tag of flush window for this buffer */
	char	flush[MAXMSG];	/* Somewhere to build flush */
};
#define		NTAG	65536	/*  1 <= tag < NTAG */

typedef struct Mnt	Mnt;
typedef struct Mnthdr	Mnthdr;
typedef struct MntQ	MntQ;

struct Mnt
{
	Ref;			/* Count of attached channels */
	Chan	*c;		/* Channel to file service */
	Proc	*rip;		/* Reader in progress */
	Mntrpc	*queue;		/* Queue of pending requests on this channel */
	int	id;		/* Multiplexor id for channel check */
	Mnt	*list;		/* Free list */
	char	mux;		/* Set if the device aleady does the multiplexing */
	Ref;			/* for number of chans, incl. mntpt but not msg */
	ulong	mntid;		/* serial # */
	Chan	*mntpt;		/* channel in user's name space */
	MntQ	*q;
};

struct MntQ
{
	Ref;
	QLock;			/* for access */
	MntQ	*next;		/* for allocation */
	Chan	*msg;		/* for reading and writing messages */
	Proc	*reader;	/* process reading response */
	Mnthdr	*writer;	/* queue of headers of written messages */
};

#define	BITROUND 256
#define	BUFSIZE	(MAXFDATA+MAXMSG)
typedef struct Mntbuf Mntbuf;
struct Mntbuf
{
	Mntbuf	*next;
	char	buf[BUFSIZE+BITROUND]; 	/* BUG */
};

struct Mntalloc
struct
{
	Lock;
	Mnt	*mntfree;
	Mnt	*mntarena;
	Mntrpc	*rpcfree;
	int	id;
}mntalloc;

#define BITBOTCH	256
#define MAXRPC		(MAXFDATA+MAXMSG+BITBOTCH)
#define limit(n, max)	(n > max ? max : n)

Chan 	*mattach(Mnt*, char*, char*);
Mntrpc	*mntralloc(void);
void	mntfree(Mntrpc*);
int	rpcattn(Mntrpc*);
void	mountrpc(Mnt*, Mntrpc*);
void	mountio(Mnt*, Mntrpc*);
Mnt	*mntchk(Chan*);
void	mountmux(Mnt*, Mntrpc*);
long	mntrdwr(int , Chan*, void*,long , ulong);
int	mntflush(Mnt*, Mntrpc*);
void	mntqrm(Mnt*, Mntrpc*);
void	mntdirfix(uchar*, Chan*);
void	mntgate(Mnt*);
void	mntrpcread(Mnt*, Mntrpc*);

enum
{
	Tagspace = 1,
	Flushspace = 64,
	Flushtag = 512,
	Mntbuf	*free;
}mntbufalloc;

struct Mnthdr
{
	Mnthdr	*next;		/* in free list or writers list */
	Mnthdr	*prev;		/* in writers list only */
	char	active;
	char	flushing;	/* a Tflush has been sent */
	short	seq;
	Fcall	thdr;
	Fcall	rhdr;
	Rendez	r;
	Proc	*p;
	Mntbuf	*mbr;
	int	readreply;	/* true if we are reader or our reply has come */
};

struct
{
	Lock;
	Mnthdr	*arena;
	Mnthdr	*head;
	Mnthdr	*tail;
}mnthdralloc;

struct
{
	Lock;
	QLock;
	MntQ	*arena;
	MntQ	*free;
}mntqalloc;

struct
{
	Lock;
	long	id;
}mntid;

Mnt	*mnt;
void	mntxmit(Mnt*, Mnthdr*);

Mntbuf*
mballoc(void)
{
	Mntbuf *mb;

loop:
	lock(&mntbufalloc);
	if(mb = mntbufalloc.free){		/* assign = */
		mntbufalloc.free = mb->next;
		unlock(&mntbufalloc);
		return mb;
	}
	unlock(&mntbufalloc);
	print("no mntbufs\n");
	if(u == 0)
		panic("mballoc");
	u->p->state = Wakeme;
	alarm(1000, wakeme, u->p);
	sched();
	goto loop;
}

void
mbfree(Mntbuf *mb)
{
	lock(&mntbufalloc);
	mb->next = mntbufalloc.free;
	mntbufalloc.free = mb;
	unlock(&mntbufalloc);
}

Mnthdr*
mhalloc(Mnt *m)
{
	Mnthdr *mh;
	int seq;

loop:
	lock(&mnthdralloc);
	if(mh = mnthdralloc.head){		/* assign = */
		mnthdralloc.head = mh->next;
		if(mnthdralloc.head)
			mnthdralloc.head->prev = 0;
		else
			mnthdralloc.tail = 0;
		unlock(&mnthdralloc);
		mh->mbr = 0;
		seq = ++mh->seq;
		if(seq == (1<<7)){
			mh->seq = 1;
			seq = 1;
		}
		mh->thdr.tag = (((mh-mnthdralloc.arena)<<7)|seq) & (NTAG-1);
		return mh;
	}
	unlock(&mnthdralloc);
	print("no mnthdrs\n");
	if(u == 0)
		panic("mhalloc");
	u->p->state = Wakeme;
	alarm(1000, wakeme, u->p);
	sched();
	goto loop;
}

void
mhfree(Mnthdr *mh)
{
	if(mh->flushing)
		return;
	lock(&mnthdralloc);
	mh->active = 0;
	mh->thdr.tag = 0;
	mh->next = 0;
	mh->prev = mnthdralloc.tail;
	if(mnthdralloc.tail)
		mnthdralloc.tail->next = mh;
	else
		mnthdralloc.head = mh;
	mnthdralloc.tail = mh;
	unlock(&mnthdralloc);
}

MntQ*
mqalloc(Chan *msg)	/* mntqalloc is qlocked */
{
	MntQ *q;

	if(q = mntqalloc.free){		/* assign = */
		mntqalloc.free = q->next;
		lock(q);
		q->ref = 1;
		q->msg = msg;
		unlock(q);
		incref(msg);
		q->writer = 0;
		q->reader = 0;
		return q;
	}
	panic("no mntqs\n");			/* there MUST be enough */
}

void
mqfree(MntQ *mq)
{
	Chan *msg = 0;

	lock(mq);
	if(--mq->ref == 0){
		msg = mq->msg;
		mq->msg = 0;
		lock(&mntqalloc);
		mq->next = mntqalloc.free;
		mntqalloc.free = mq;
		unlock(&mntqalloc);
	}
	unlock(mq);
	if(msg)		/* after locks are down */
		close(msg);
}

Mnt*
mntdev(Chan *c, int noerr)
{
	Mnt *m;
	int i;

	m = &mnt[c->mntindex];
	if(m->mntid==c->dev && m->q!=0)
		return m;
	if(noerr)
		return 0;
	print("mntdev shutdown %d %d %d %lux\n", c->dev, c->mntindex,
			m->mntid, m->q);
	error(Eshutdown);
}

void
mntreset(void)
{
	Mnt *me, *md;
	Mntrpc *re, *rd;
	ushort tag, ftag;

	mntalloc.mntarena = ialloc(conf.nmntdev*sizeof(Mnt), 0);
	mntalloc.mntfree = mntalloc.mntarena;
	me = &mntalloc.mntfree[conf.nmntdev];
	for(md = mntalloc.mntfree; md < me; md++)
		md->list = md+1;
	me[-1].list = 0;

	if(conf.nmntbuf > Flushtag) {
		print("devmnt: buffers limited to %d\n", Flushtag);
		conf.nmntbuf = Flushtag;
	}

	tag = Tagspace;
	ftag = Flushtag;
	mntalloc.rpcfree = ialloc(conf.nmntbuf*sizeof(Mntrpc), 0);
	re = &mntalloc.rpcfree[conf.nmntbuf];
	for(rd = mntalloc.rpcfree; rd < re; rd++) {
		rd->list = rd+1;
		rd->request.tag = tag++;
		rd->flushbase = ftag;
		rd->flushtag = ftag;
		ftag += Flushspace;
		rd->rpc = ialloc(MAXRPC, 0);
	}
	re[-1].list = 0;

	mntalloc.id = 1;
	int i;
	Mntbuf *mb;
	Mnthdr *mh;
	MntQ *mq;

	if(conf.nmnthdr > 512){
		print("conf.nmnthdr is %d set to 512\n", conf.nmnthdr);
		conf.nmnthdr = 512;
	}
	mnt = ialloc(conf.nmntdev*sizeof(Mnt), 0);

	mb = ialloc(conf.nmntbuf*sizeof(Mntbuf), 0);
	mntbufalloc.free = mb;
	for(i=0; i<conf.nmntbuf; i++,mb++)
		mb->next = mb+1;
	--mb;
	mb->next = 0;

	mh = ialloc(conf.nmnthdr*sizeof(Mnthdr), 0);
	mnthdralloc.arena = mh;
	mnthdralloc.head = mh;
	for(i=0; i<conf.nmnthdr; i++,mh++){
		mh->seq = 0;
		mh->next = mh+1;
		mh->prev = mh-1;
	}
	--mh;
	mnthdralloc.tail = mh;
	mh->next = 0;
	mnthdralloc.head->prev = 0;

	mq = ialloc(conf.nmntdev*sizeof(MntQ), 0);
	mntqalloc.arena = mq;
	mntqalloc.free = mq;
	for(i=0; i<conf.nmntdev; i++,mq++)
		mq->next = mq+1;
	--mq;
	mq->next = 0;
}

void


@@ 114,86 274,79 @@ mntinit(void)
}

Chan*
mntattach(char *muxattach)
mntattach(char *crud)
{
	Mnt *m, *e;
	int i;
	Mnt *m, *mm;
	Mnthdr *mh;
	MntQ *q;
	Chan *c, *cm;
	struct bogus{
		Chan	*chan;
		char	*spec;
		char	*auth;
	}bogus;

	bogus = *((struct bogus *)muxattach);
	e = &mntalloc.mntarena[conf.nmntdev];
	for(m = mntalloc.mntarena; m < e; m++) {
		if(m->c == bogus.chan && m->id) {
			lock(m);
			if(m->ref > 0 && m->id && m->c == bogus.chan) {
				m->ref++;
				unlock(m);
				return mattach(m, bogus.spec, bogus.auth);
			}
			unlock(m);	
		}
	}
	lock(&mntalloc);
	if(mntalloc.mntfree == 0) {
		unlock(&mntalloc);
		error(Enomntdev);
	bogus = *((struct bogus *)crud);

	m = mnt;
	for(i=0; i<conf.nmntdev; i++,m++){
		lock(m);
		if(m->ref == 0)
			goto Found;
		unlock(m);
	}
	m = mntalloc.mntfree;
	mntalloc.mntfree = m->list;	
	m->id = mntalloc.id++;
	lock(m);
	unlock(&mntalloc);
	error(Enomntdev);

    Found:
	m->ref = 1;
	m->queue = 0;
	m->rip = 0;
	m->c = bogus.chan;

	switch(devchar[m->c->type]) {
	case 'H':			/* Hotrod */
	case '3':			/* BIT3 */
		m->mux = 1;
		break;
	default:
		m->mux = 0;
	}
	incref(m->c);
	unlock(m);
	lock(&mntid);
	m->mntid = ++mntid.id;
	unlock(&mntid);
	c = devattach('M', bogus.spec);
	c->dev = m->mntid;
	c->mntindex = m-mnt;
	m->mntpt = c;
	cm = bogus.chan;

	/*
	 * Look for queue to same msg channel
	 */
	q = mntqalloc.arena;
	qlock(&mntqalloc);
	for(i=0; i<conf.nmntdev; i++,q++)
		if(q->msg==cm){
			lock(q);
			if(q->ref && q->msg==cm){
				m->q = q;
				q->ref++;
				unlock(q);
				goto out;
			}
			unlock(q);
		}
	m->q = mqalloc(cm);

	return mattach(m, bogus.spec, bogus.auth);
}

Chan *
mattach(Mnt *m, char *spec, char *auth)
{
	Chan *c;
	Mntrpc *r;

	r = mntralloc();

	c = devattach('M', spec);
	c->dev = m->id;
	c->mntindex = m-mntalloc.mntarena;

    out:
	qunlock(&mntqalloc);
	mh = mhalloc(m);
	if(waserror()){
		mntfree(r);
		mhfree(mh);
		close(c);
		nexterror();
	}
	r->request.type = Tattach;
	r->request.fid = c->fid;
	memmove(r->request.uname, u->p->pgrp->user, NAMELEN);
	strncpy(r->request.aname, spec, NAMELEN);
	strncpy(r->request.auth, auth, NAMELEN);
	mountrpc(m, r);

	c->qid = r->reply.qid;
	c->mchan = m->c;
	mh->thdr.type = Tattach;
	mh->thdr.fid = c->fid;
	memmove(mh->thdr.uname, u->p->pgrp->user, NAMELEN);
	strcpy(mh->thdr.aname, bogus.spec);
	strcpy(mh->thdr.auth, bogus.auth);
	mntxmit(m, mh);
	c->qid = mh->rhdr.qid;
	c->mchan = m->q->msg;
	c->mqid = c->qid;
	mhfree(mh);
	poperror();
	mntfree(r);
	return c;
}



@@ 201,27 354,28 @@ Chan*
mntclone(Chan *c, Chan *nc)
{
	Mnt *m;
	Mntrpc *r;
	int alloc = 0;
	Mnthdr *mh;
	int new;

	m = mntchk(c);
	r = mntralloc();
	if(nc == 0) {
	new = 0;
	if(nc == 0){
		nc = newchan();
		alloc = 1;
		new = 1;
		if(waserror()){
			close(nc);
			nexterror();
		}
	}
	m = mntdev(c, 0);
	mh = mhalloc(m);
	if(waserror()){
		mntfree(r);
		if(alloc)
			close(nc);
		mhfree(mh);
		nexterror();
	}

	r->request.type = Tclone;
	r->request.fid = c->fid;
	r->request.newfid = nc->fid;
	mountrpc(m, r);

	mh->thdr.type = Tclone;
	mh->thdr.fid = c->fid;
	mh->thdr.newfid = nc->fid;
	mntxmit(m, mh);
	nc->type = c->type;
	nc->dev = c->dev;
	nc->qid = c->qid;


@@ 231,12 385,14 @@ mntclone(Chan *c, Chan *nc)
	nc->mnt = c->mnt;
	nc->mountid = c->mountid;
	nc->aux = c->aux;
	nc->mntindex = c->mntindex;
	nc->mchan = c->mchan;
	nc->mqid = c->qid;
	incref(m);

	mhfree(mh);
	poperror();
	mntfree(r);
	if(new)
		poperror();
	incref(m);
	return nc;
}



@@ 244,71 400,73 @@ int
mntwalk(Chan *c, char *name)
{
	Mnt *m;
	Mntrpc *r;

	m = mntchk(c);
	r = mntralloc();
	if(waserror()) {
		mntfree(r);
		return 0;
	Mnthdr *mh;
	int found;

	found = 1;
	m = mntdev(c, 0);
	mh = mhalloc(m);
	mh->thdr.type = Twalk;
	mh->thdr.fid = c->fid;
	strcpy(mh->thdr.name, name);
	if(waserror()){	/* BUG: can check type of error? */
		found = 0;
		goto Out;
	}
	r->request.type = Twalk;
	r->request.fid = c->fid;
	strncpy(r->request.name, name, NAMELEN);
	mountrpc(m, r);

	c->qid = r->reply.qid;

	mntxmit(m, mh);
	c->qid = mh->rhdr.qid;
	poperror();
	mntfree(r);
	return 1;
    Out:
	mhfree(mh);
	return found;
}

void	 
mntstat(Chan *c, char *dp)
{
	Mnt *m;
	Mntrpc *r;
	Mnthdr *mh;

	m = mntchk(c);
	r = mntralloc();
	if(waserror()) {
		mntfree(r);
	m = mntdev(c, 0);
	mh = mhalloc(m);
	if(waserror()){
		mhfree(mh);
		nexterror();
	}
	r->request.type = Tstat;
	r->request.fid = c->fid;
	mountrpc(m, r);

	memmove(dp, r->reply.stat, DIRLEN);
	mntdirfix((uchar*)dp, c);
	mh->thdr.type = Tstat;
	mh->thdr.fid = c->fid;
	mntxmit(m, mh);
	memmove(dp, mh->rhdr.stat, DIRLEN);
	dp[DIRLEN-4] = devchar[c->type];
	dp[DIRLEN-3] = 0;
	dp[DIRLEN-2] = c->dev;
	dp[DIRLEN-1] = c->dev>>8;
	mhfree(mh);
	poperror();
	mntfree(r);
}

Chan*
mntopen(Chan *c, int omode)
{
	Mnt *m;
	Mntrpc *r;
	Mnthdr *mh;

	m = mntchk(c);
	r = mntralloc();
	if(waserror()) {
		mntfree(r);
	m = mntdev(c, 0);
	mh = mhalloc(m);
	if(waserror()){
		mhfree(mh);
		nexterror();
	}
	r->request.type = Topen;
	r->request.fid = c->fid;
	r->request.mode = omode;
	mountrpc(m, r);

	c->qid = r->reply.qid;
	mh->thdr.type = Topen;
	mh->thdr.fid = c->fid;
	mh->thdr.mode = omode;
	mntxmit(m, mh);
	c->qid = mh->rhdr.qid;
	mhfree(mh);
	poperror();
	c->offset = 0;
	c->mode = openmode(omode);
	c->flag |= COPEN;
	poperror();
	mntfree(r);
	return c;
}



@@ 316,58 474,60 @@ void
mntcreate(Chan *c, char *name, int omode, ulong perm)
{
	Mnt *m;
	Mntrpc *r;
	Mnthdr *mh;

	m = mntchk(c);
	r = mntralloc();
	if(waserror()) {
		mntfree(r);
	m = mntdev(c, 0);
	mh = mhalloc(m);
	if(waserror()){
		mhfree(mh);
		nexterror();
	}
	r->request.type = Tcreate;
	r->request.fid = c->fid;
	r->request.mode = omode;
	r->request.perm = perm;
	strncpy(r->request.name, name, NAMELEN);
	mountrpc(m, r);

	c->qid = r->reply.qid;
	mh->thdr.type = Tcreate;
	mh->thdr.fid = c->fid;
	strcpy(mh->thdr.name, name);
	mh->thdr.mode = omode;
	mh->thdr.perm = perm;
	mntxmit(m, mh);
	c->qid = mh->rhdr.qid;
	mhfree(mh);
	poperror();
	c->flag |= COPEN;
	c->mode = openmode(omode);
	poperror();
	mntfree(r);
	c->qid = mh->rhdr.qid;
}

void	 
mntclunk(Chan *c, int t)
{
	Mnt *m;
	Mntrpc *r, *n, *q;
		
	m = mntchk(c);
	r = mntralloc();
	if(waserror()){
		mntfree(r);
		if(decref(m) == 0) {
			for(q = m->queue; q; q = r) {
				r = q->list;
				q->flushed = 0;
				mntfree(q);
			}
			m->id = 0;
			close(m->c);
			lock(&mntalloc);
			m->list = mntalloc.mntfree;
			mntalloc.mntfree = m;
			unlock(&mntalloc);
		}
		return;
	}

	r->request.type = t;
	r->request.fid = c->fid;
	mountrpc(m, r);
	nexterror();
	Mnthdr *mh;
	MntQ *q;
	int waserr;

	m = mntdev(c, 0);
	mh = mhalloc(m);
	mh->thdr.type = t;
	mh->thdr.fid = c->fid;
	waserr = 0;
	if(waserror())		/* gotta clean up as if there wasn't */
		waserr = 1;
	else
		mntxmit(m, mh);
	mhfree(mh);
	if(c == m->mntpt)
		m->mntpt = 0;
	lock(m);
	if(--m->ref == 0){		/* BUG: need to hang up all pending i/o */
		q = m->q;
		m->q = 0;
		m->mntid = 0;
		unlock(m);		/* mqfree can take time */
		mqfree(q);
	}else
		unlock(m);
	if(waserr)
		nexterror();
	poperror();
}

void


@@ 376,357 536,433 @@ mntclose(Chan *c)
	mntclunk(c, Tclunk);
}

void	 
mntremove(Chan *c)
{
	mntclunk(c, Tremove);
}

void
mntwstat(Chan *c, char *dp)
long
mntreadwrite(Chan *c, void *vbuf, long n, int type, ulong offset)
{
	Mnt *m;
	Mntrpc *r;

	m = mntchk(c);
	r = mntralloc();
	if(waserror()) {
		mntfree(r);
	Mnthdr *mh;
	long nt, nr, count;
	char *buf;

	buf = vbuf;
	count = 0;
	m = mntdev(c, 0);
	mh = mhalloc(m);
	if(waserror()){
		mhfree(mh);
		nexterror();
	}
	r->request.type = Twstat;
	r->request.fid = c->fid;
	memmove(r->request.stat, dp, DIRLEN);
	mountrpc(m, r);
	mh->thdr.type = type;
	mh->thdr.fid = c->fid;
    Loop:
	nt = n;
	if(nt > MAXFDATA)
		nt = MAXFDATA;
	mh->thdr.offset = offset;
	mh->thdr.count = nt;
	mh->thdr.data = buf;
	mntxmit(m, mh);
	nr = mh->rhdr.count;
	offset += nr;
	count += nr;
	buf += nr;
	n -= nr;
	if(n && nr==nt)
		goto Loop;
	mhfree(mh);
	poperror();
	mntfree(r);
	return count;
}

long	 
mntread(Chan *c, void *buf, long n, ulong offset)
{
	uchar *p, *e;

	n = mntrdwr(Tread, c, buf, n, offset);
	if(c->qid.path & CHDIR) 
		for(p = (uchar*)buf, e = &p[n]; p < e; p += DIRLEN)
			mntdirfix(p, c);

	long i;
	uchar *b;

	n = mntreadwrite(c, buf, n, Tread, offset);
	if(c->qid.path & CHDIR){
		b = (uchar*)buf;
		for(i=n-DIRLEN; i>=0; i-=DIRLEN){
			b[DIRLEN-4] = devchar[c->type];
			b[DIRLEN-3] = 0;
			b[DIRLEN-2] = c->dev;
			b[DIRLEN-1] = c->dev>>8;
			b += DIRLEN;
		}
	}
	return n;
}

long	 
mntwrite(Chan *c, void *buf, long n, ulong offset)
{
	return mntrdwr(Twrite, c, buf, n, offset);	
	return mntreadwrite(c, buf, n, Twrite, offset);
}

long
mntrdwr(int type, Chan *c, void *buf, long n, ulong offset)
void	 
mntremove(Chan *c)
{
	mntclunk(c, Tremove);
}

void
mntwstat(Chan *c, char *dp)
{
	Mnt *m;
	Mntrpc *r;
	ulong cnt, nr;
	char *uba;

	r = mntralloc();
	m = mntchk(c);
	if(waserror()) {
		mntfree(r);
	Mnthdr *mh;

	m = mntdev(c, 0);
	mh = mhalloc(m);
	if(waserror()){
		mhfree(mh);
		nexterror();
	}
	r->request.type = type;
	r->request.fid = c->fid;
	r->request.offset = offset;
	uba = buf;
	for(cnt = 0; n; n -= nr) {
		r->request.data = uba;
		r->request.count = limit(n, MAXFDATA);
		mountrpc(m, r);
		nr = r->reply.count;
		if(type == Tread)
			memmove(uba, r->reply.data, nr);
		r->request.offset += nr;
		uba += nr;
		cnt += nr;
		if(nr != r->request.count)
			break;
	}
	mh->thdr.type = Twstat;
	mh->thdr.fid = c->fid;
	memmove(mh->thdr.stat, dp, DIRLEN);
	mntxmit(m, mh);
	mhfree(mh);
	poperror();
	mntfree(r);
	return cnt;
}

void
mountrpc(Mnt *m, Mntrpc *r)
{
	mountio(m, r);
	if(r->reply.type == Rerror)
		errors(r->reply.ename);
	if(r->reply.type != r->request.type+1) {
		print("devmnt: mismatched reply T%d R%d tags req %d fls %d rep %d\n",
		r->request.type, r->reply.type, r->request.tag, r->flushtag, r->reply.tag);
			errors("protocol error");
mntwunlink(MntQ *q, Mnthdr *w)		/* queue is locked and w is a writer */
{
	if(w->next)
		w->next->prev = w->prev;
	if(w->prev)
		w->prev->next = w->next;
	else{
		q->writer = w->next;
		if(q->writer)
			q->writer->prev = 0;
	}
}

/*
 * m->q is unlocked.  Send Tflush message to flush omh->tag.
 * Cut off all errors.   Caller will free omh
 */
void
mountio(Mnt *m, Mntrpc *r)
mntflush(Mnt *m, Mnthdr *omh)	/* queue is unlocked */
{
	int n;
	Mnthdr *mh;

	lock(m);
	r->m = m;
	r->list = m->queue;
	m->queue = r;
	unlock(m);

	/* Transmit a file system rpc */
	n = convS2M(&r->request, r->rpc);
	if(waserror()) {
		mntqrm(m, r);
		nexterror();
	}
	if((*devtab[m->c->type].write)(m->c, r->rpc, n, 0) != n)
		error(Eshortmsg);
	poperror();

	if(m->mux) {
		mntrpcread(m, r);
	if(omh->thdr.type == Tflush){
		omh->flushing = 0;
		return;
	}

	/* Gate readers onto the mount point one at a time */
	for(;;) {
		lock(m);
		if(m->rip == 0)
			break;
		unlock(m);
		if(waserror()) {
			if(mntflush(m, r) == 0)
				nexterror();
			continue;
		}
		sleep(&r->r, rpcattn, r);
		poperror();
		if(r->done)
			return;
	}
	m->rip = u->p;
	unlock(m);

	while(r->done == 0) {
		mntrpcread(m, r);
		mountmux(m, r);
	mh = mhalloc(m);
	if(waserror()){
		omh->flushing = 0;
		mhfree(mh);
		return;		/* no more errors please */
	}
	mntgate(m);
	mh->thdr.type = Tflush;
	mh->thdr.oldtag = omh->thdr.tag;
	mntxmit(m, mh);
	omh->flushing = 0;
	mhfree(mh);
	poperror();
}

void
mntrpcread(Mnt *m, Mntrpc *r)
{
	int n;

	for(;;) {
		if(waserror()) {
			if(mntflush(m, r) == 0) {
				if(m->mux == 0)
					mntgate(m);
				nexterror();
			}
			continue;
mnterrdequeue(Mnt *m, Mnthdr *mh)	/* queue is unlocked */
{
	Mnthdr *w;
	MntQ *q;

	mh->flushing = 1;
	q = m->q;
	qlock(q);
	mh->readreply = 0;
	/* take self from queue if necessary */
	if(q->reader == u->p){	/* advance a writer to reader */
		w = q->writer;
		if(w){
			mntwunlink(q, w);
			q->reader = w->p;
			wakeup(&w->r);
		}else{
			q->reader = 0;
			q->writer = 0;
		}
		n = (*devtab[m->c->type].read)(m->c, r->rpc, MAXRPC, 0);
		poperror();
		if(n == 0)
			continue;
		if(convM2S(r->rpc, &r->reply, n) != 0)
			break;
	}
	}else
		mntwunlink(q, mh);
	qunlock(q);
	mntflush(m, mh);
}

void
mntgate(Mnt *m)
int
mntreadreply(void *a)
{
	Mntrpc *q;

	lock(m);
	m->rip = 0;
	for(q = m->queue; q; q = q->list)
		if(q->done == 0) {
			lock(&q->r);
			if(q->r.p) {
				unlock(&q->r);
				unlock(m);
				wakeup(&q->r);
				return;
			}
			unlock(&q->r);
		}
	unlock(m);
	return ((Mnthdr *)a)->readreply;
}

void
mountmux(Mnt *m, Mntrpc *r)
{
	Mntrpc **l, *q;
	int done;
	char *dp;

	lock(m);
	l = &m->queue;
	for(q = *l; q; q = q->list) {
		if(q->request.tag == r->reply.tag) {
			if(q->flushed == 0)
				*l = q->list;
			q->done = 1;
			unlock(m);
			goto dispatch;
mntxmit(Mnt *m, Mnthdr *mh)
{
	ulong n;
	Mntbuf *mbw;
	Mnthdr *w, *ow, *h;
	MntQ *q;
	int qlocked, tag, written;

	if(&qlocked);	/* force qlocked not to be registerized */
	mh->mbr = 0;
	mbw = mballoc();
	if(waserror()){			/* 1 */
		if(mh->mbr){
			mbfree(mh->mbr);
			mh->mbr = 0;
		}
		if(q->flushtag == r->reply.tag) {
			*l = q->list;
			q->flushed = 0;
			done = q->done;
			q->done = 1;
			unlock(m);
			if(done == 0) {
				r->reply.type = Rerror;
				strcpy(r->reply.ename, errstrtab[Eintr]);
				goto dispatch;
			}
			if(q->bfree)
				mntfree(q);
			return;
		}
		l = &q->list;
		mbfree(mbw);
		nexterror();
	}
	unlock(m);
	return;

dispatch:
	if(q != r) {		/* Completed someone else */
		dp = q->rpc;
		q->rpc = r->rpc;
		r->rpc = dp;
		memmove(&q->reply, &r->reply, sizeof(Fcall));
		wakeup(&q->r);					
	n = convS2M(&mh->thdr, mbw->buf);
	q = m->q;
	if(q == 0)
		error(Eshutdown);
#ifdef	BIT3
	/*
	 * Bit3 and Hotrod do their own multiplexing.  (Well, the file server does.)
	 * The code is different enough that it's broken out separately here.
	 */
	if(devchar[q->msg->type]!='3' && devchar[q->msg->type]!='H')
		goto Normal;

	incref(q);
	if(waserror()){		/* 2 */
		mqfree(q);
		nexterror();
	}

}

int
mntflush(Mnt *m, Mntrpc *r)
{
	Fcall flush;
	int n;

	r->flushtag++;
	if((r->flushtag-r->flushbase) == Flushspace)
		r->flushtag -= Flushspace;

	flush.type = Tflush;
	flush.tag = r->flushtag;
	flush.oldtag = r->request.tag;
	n = convS2M(&flush, r->flush);

	if(waserror()) {
		if(strcmp(u->error, errstrtab[Eintr]) == 0)
			return 1;
		mntqrm(m, r);
		return 0;
	if((*devtab[q->msg->type].write)(q->msg, mbw->buf, n, 0) != n){
		print("short write in mntxmit\n");
		error(Eshortmsg);
	}
	(*devtab[m->c->type].write)(m->c, r->flush, n, 0);
	poperror();
	lock(m);
	if(!r->done)
		r->flushed = 1;
	unlock(m);
	return 1;
}

Mntrpc *
mntralloc(void)
{
	Mntrpc *new;

	for(;;) {
		lock(&mntalloc);
		if(new = mntalloc.rpcfree) {
			mntalloc.rpcfree = new->list;
			unlock(&mntalloc);
			new->done = 0;
			new->bfree = 0;
			return new;
		}
		unlock(&mntalloc);
		resrcwait("no mount buffers");
	/*
	 * Read response
	 */
	if(waserror()){		/* 3 */
		mntflush(m, mh);
		nexterror();
	}
	mh->mbr = mballoc();
	n = (*devtab[q->msg->type].read)(q->msg, mh->mbr->buf, BUFSIZE, 0);
	poperror();		/* 3 */
	mqfree(q);
	poperror();		/* 2 */

	if(convM2S(mh->mbr->buf, &mh->rhdr, n) == 0){
		print("format error in mntxmit\n");
		error(Ebadmsg);
	}
}

void
mntfree(Mntrpc *r)
{
	Mntrpc *q;
	Mnt *m, *e;
	int i;

	r->bfree = 1;
	if(r->flushed)
		return;

	lock(&mntalloc);
	r->list = mntalloc.rpcfree;
	mntalloc.rpcfree = r;
	unlock(&mntalloc);
}
	/*
	 * Various checks
	 */
	if(mh->rhdr.tag != mh->thdr.tag){
		print("tag mismatch %d %d\n", mh->rhdr.tag, mh->thdr.tag);
		error(Ebadmsg);
	}
	if(mh->rhdr.type == Rerror){
		if(m->mntpt)
			errors(mh->rhdr.ename);
		error(Eshutdown);
	}
	if(mh->rhdr.type != mh->thdr.type+1){
		print("type mismatch %d %d\n", mh->rhdr.type, mh->thdr.type+1);
		error(Ebadmsg);
	}
	if(mh->rhdr.fid != mh->thdr.fid){
		print("fid mismatch %d %d type %d\n", mh->rhdr.fid, mh->thdr.fid, mh->rhdr.type);
		error(Ebadmsg);
	}

void
mntqrm(Mnt *m, Mntrpc *r)
{
	Mntrpc **l, *f;
	/*
	 * Copy out on read
	 */
	if(mh->thdr.type == Tread)
		memmove(mh->thdr.data, mh->rhdr.data, mh->rhdr.count);
	mbfree(mh->mbr);
	mh->mbr = 0;
	mbfree(mbw);
	poperror();		/* 1 */
	return;

	lock(m);
	r->done = 1;
	r->flushed = 0;

	l = &m->queue;
	for(f = *l; f; f = f->list) {
		if(f == r) {
			*l = r->list;
			break;
    Normal:
#endif
	incref(q);
	qlock(q);
	qlocked = 1;
	if(waserror()){		/* 2 */
		if(qlocked)
			qunlock(q);
		mqfree(q);
		nexterror();
	}
	mh->readreply = 0;
	mh->active = 1;
	if((*devtab[q->msg->type].write)(q->msg, mbw->buf, n, 0) != n){
		print("short write in mntxmit\n");
		error(Eshortmsg);
	}
	if(q->reader == 0){		/* i will read */
		q->reader = u->p;
    Read:
		USED(qlocked);
		qunlock(q);
		qlocked = 0;
		if(waserror()){		/* 3 */
			mnterrdequeue(m, mh);
			nexterror();
		}
		mh->mbr = mballoc();
		do{
			n = (*devtab[q->msg->type].read)(q->msg, mh->mbr->buf, BUFSIZE, 0);
		}while(n == 0);
		poperror();		/* 3 */
		if(convM2S(mh->mbr->buf, &mh->rhdr, n) == 0){
			/* BUG? IS THIS RIGHT? IGNORE AND RETRY */
			print(" MR ");
			qlock(q);
			qlocked = 1;
			goto FreeRead;
		}
		/*
		 * Response might not be mine
		 */
		USED(qlocked);
		qlock(q);
		qlocked = 1;
		tag = mh->rhdr.tag;
		if(tag == mh->thdr.tag){	/* it's mine */
			if(mh->rhdr.type != Rerror)
			if(mh->rhdr.type != mh->thdr.type+1){
				print("mail rob: '%s xxT(%d)%c %d %d'\n", u->p->text,
					tag, devchar[m->q->msg->type],
					mh->rhdr.type, mh->thdr.type+1);
				goto FreeRead;
			}
			q->reader = 0;
			if(w = q->writer){	/* advance a writer to reader */
				mntwunlink(q, w);
				q->reader = w->p;
				w->readreply = 1;
				wakeup(&w->r);
			}
			mh->active = 0;
			USED(qlocked);
			qunlock(q);
			qlocked = 0;
			goto Respond;
		}
		/*
		 * Hand response to correct recipient
		 */
		if(tag==0 || tag>=NTAG){
			print("unknown tag %d\n", tag);
	FreeRead:
			mbfree(mh->mbr);
			mh->mbr = 0;
			goto Read;
		}
		/*
		 * Find writer in queue
		 */
		for(w=q->writer; w; w=w->next)
			if(w->thdr.tag == tag)
				goto Inqueue;
		goto FreeRead;
	Inqueue:
		if(w->flushing || !w->active)	/* nothing to do; mntflush will clean up */
			goto FreeRead;
		if(mh->rhdr.type != Rerror)
		if(mh->rhdr.type != w->thdr.type+1){
			print("mail rob: '%s xxw(%d)%c %d %d'\n",
				u->p->text, tag, devchar[m->q->msg->type],
				mh->rhdr.type, w->thdr.type+1);
			goto FreeRead;
		}
		l = &f->list;
		w->mbr = mh->mbr;
		mh->mbr = 0;
		memmove(&w->rhdr, &mh->rhdr, sizeof mh->rhdr);
		mntwunlink(q, w);
		w->readreply = 1;
		wakeup(&w->r);
		goto Read;
	}else{
		mh->p = u->p;
		/* put self in queue */
		mh->next = q->writer;
		mh->prev = 0;
		if(q->writer)
			q->writer->prev = mh;
		q->writer = mh;
		qunlock(q);
		qlocked = 0;
		if(waserror()){		/* interrupted sleep */
			mnterrdequeue(m, mh);
			nexterror();
		}
		sleep(&mh->r, mntreadreply, mh);
		poperror();
		USED(qlocked);
		qlock(q);
		qlocked = 1;
		mh->readreply = 0;
		if(q->reader == u->p)	/* i got promoted */
			goto Read;
		mh->active = 0;
		USED(qlocked);
		qunlock(q);
		qlocked = 0;
		goto Respond;
	}
	unlock(m);
}

Mnt *
mntchk(Chan *c)
{
	Mnt *m;

	m = &mntalloc.mntarena[c->mntindex];
	if(m->id != c->dev)
    Respond:
	mqfree(q);
	poperror();		/* 2 */
	if(mh->rhdr.type == Rerror){
		if(m->mntpt)
			errors(mh->rhdr.ename);
		error(Eshutdown);
	return m;
}

void
mntdirfix(uchar *dirbuf, Chan *c)
{
	dirbuf[DIRLEN-4] = devchar[c->type];
	dirbuf[DIRLEN-3] = 0;
	dirbuf[DIRLEN-2] = c->dev;
	dirbuf[DIRLEN-1] = c->dev>>8;
}

int
rpcattn(Mntrpc *r)
{
	return r->done || r->m->rip == 0;
	}
	/*
	 * Copy out on read
	 */
	if(mh->thdr.type == Tread){
		if(mh->rhdr.count > mh->thdr.count)
			error(Ebadcnt);
		memmove(mh->thdr.data, mh->rhdr.data, mh->rhdr.count);
	}
	mbfree(mh->mbr);
	mh->mbr = 0;
	mbfree(mbw);
	USED(qlocked);
	poperror();		/* 1 */
}

void
mntdump(void)
{
}
	int i;
	MntQ *q;
	Mnthdr *h;
	Proc *p;

	for(i=0; i<conf.nmntdev; i++){
		q = &mntqalloc.arena[i];
		if(!q->msg)
			continue;
		p = q->reader;
		print("q rdr %d wrtr ", p? p->pid : 0);
		for(h=q->writer; h; h=h->next)
			print("(%lux %lux %d %d)", h, &h->r, h->thdr.tag,
				(p=h->p)? p->pid : 0);
		print("\n");
	}
}

M port/devnonet.c => port/devnonet.c +2 -1
@@ 948,7 948,8 @@ norack(Noconv *cp, int mid)
	cp->rexmit = 0;
	cp->lastacked = mid;
	mp->acked = 1;
	freeb(mp->first);
	if(mp->first)
		freeb(mp->first);
	mp->first = 0;

	/*

M port/stream.c => port/stream.c +1 -0
@@ 1095,6 1095,7 @@ streamread(Chan *c, void *vbuf, long n)
	left = n;
	qlock(&s->rdlock);
	tofree = 0;
	q = 0;
	if(waserror()){
		/*
		 *  put any partially read message back into the

M port/sysfile.c => port/sysfile.c +2 -4
@@ 34,7 34,7 @@ fdtochan(int fd, int mode)
{
	Chan *c;

	USED(c);
	c = 0;
	if(fd<0 || NFD<=fd || (c = u->p->fgrp->fd[fd])==0)
		error(Ebadfd);
	if(mode<0 || c->mode==ORDWR)


@@ 183,9 183,7 @@ fdclose(int fd, int flag)
long
sysclose(ulong *arg)
{
	Chan *c;

	c = fdtochan(arg[0], -1);
	fdtochan(arg[0], -1);
	fdclose(arg[0], 0);

	return 0;