code: drawterm

Download patch

ref: 3b886fcd3ec063d39ad3f8a16cb7eb08576102ec
parent: 278bcb7edd5958d3bb83c0b13f0250446b7ed597
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sat Nov 12 10:54:14 EST 2016

kern: update qio and devmnt, remove mount cache stub

--- a/kern/Makefile
+++ b/kern/Makefile
@@ -5,7 +5,6 @@
 OFILES=\
 	alloc.$O\
 	allocb.$O\
-	cache.$O\
 	chan.$O\
 	data.$O\
 	dev.$O\
--- a/kern/allocb.c
+++ b/kern/allocb.c
@@ -7,15 +7,10 @@
 enum
 {
 	Hdrspc		= 64,		/* leave room for high-level headers */
+	Tlrspc		= 16,		/* extra room at the end for pad/crc/mac */
 	Bdead		= 0x51494F42,	/* "QIOB" */
 };
 
-struct
-{
-	Lock	lk;
-	ulong	bytes;
-} ialloc;
-
 static Block*
 _allocb(int size)
 {
@@ -22,12 +17,13 @@
 	Block *b;
 	uintptr addr;
 
+	size += Tlrspc;
 	if((b = mallocz(sizeof(Block)+size+Hdrspc, 0)) == nil)
 		return nil;
 
 	b->next = nil;
 	b->list = nil;
-	b->free = 0;
+	b->free = nil;
 	b->flag = 0;
 
 	/* align start of data portion by rounding up */
@@ -36,9 +32,9 @@
 	b->base = (uchar*)addr;
 
 	/* align end of data portion by rounding down */
-	b->lim = ((uchar*)b) + sizeof(Block)+size+Hdrspc;
-	addr = (uintptr)(b->lim);
-	addr = addr & ~(BLOCKALIGN-1);
+	b->lim = (uchar*)b + sizeof(Block)+size+Hdrspc;
+	addr = (uintptr)b->lim;
+	addr &= ~(BLOCKALIGN-1);
 	b->lim = (uchar*)addr;
 
 	/* leave sluff at beginning for added headers */
@@ -57,13 +53,11 @@
 
 	/*
 	 * Check in a process and wait until successful.
-	 * Can still error out of here, though.
 	 */
 	if(up == nil)
-		panic("allocb without up: %p\n", getcallerpc(&size));
-	if((b = _allocb(size)) == nil){
-		panic("allocb: no memory for %d bytes\n", size);
-	}
+		panic("allocb without up: %#p", getcallerpc(&size));
+	if((b = _allocb(size)) == nil)
+		panic("allocb: no memory for %d bytes", size);
 	setmalloctag(b, getcallerpc(&size));
 
 	return b;
@@ -73,28 +67,12 @@
 iallocb(int size)
 {
 	Block *b;
-	static int m1, m2;
 
-	if(ialloc.bytes > conf.ialloc){
-		if((m1++%10000)==0)
-			print("iallocb: limited %lud/%lud\n",
-				ialloc.bytes, conf.ialloc);
-		return 0;
-	}
-
-	if((b = _allocb(size)) == nil){
-		if((m2++%10000)==0)
-			print("iallocb: no memory %lud/%lud\n",
-				ialloc.bytes, conf.ialloc);
-		return nil;
-	}
+	if((b = _allocb(size)) == nil)
+		panic("iallocb: no memory for %d bytes", size);
 	setmalloctag(b, getcallerpc(&size));
 	b->flag = BINTR;
 
-	ilock(&ialloc.lk);
-	ialloc.bytes += b->lim - b->base;
-	iunlock(&ialloc.lk);
-
 	return b;
 }
 
@@ -110,15 +88,10 @@
 	 * drivers which perform non cache coherent DMA manage their own buffer
 	 * pool of uncached buffers and provide their own free routine.
 	 */
-	if(b->free) {
+	if(b->free != nil) {
 		b->free(b);
 		return;
 	}
-	if(b->flag & BINTR) {
-		ilock(&ialloc.lk);
-		ialloc.bytes -= b->lim - b->base;
-		iunlock(&ialloc.lk);
-	}
 
 	/* poison the block in case someone is still holding onto it */
 	b->next = dead;
@@ -136,30 +109,23 @@
 	void *dead = (void*)Bdead;
 
 	if(b == dead)
-		panic("checkb b %s %lux", msg, b);
+		panic("checkb b %s %#p", msg, b);
 	if(b->base == dead || b->lim == dead || b->next == dead
 	  || b->rp == dead || b->wp == dead){
-		print("checkb: base 0x%8.8luX lim 0x%8.8luX next 0x%8.8luX\n",
+		print("checkb: base %#p lim %#p next %#p\n",
 			b->base, b->lim, b->next);
-		print("checkb: rp 0x%8.8luX wp 0x%8.8luX\n", b->rp, b->wp);
-		panic("checkb dead: %s\n", msg);
+		print("checkb: rp %#p wp %#p\n", b->rp, b->wp);
+		panic("checkb dead: %s", msg);
 	}
 
 	if(b->base > b->lim)
-		panic("checkb 0 %s %lux %lux", msg, b->base, b->lim);
+		panic("checkb 0 %s %#p %#p", msg, b->base, b->lim);
 	if(b->rp < b->base)
-		panic("checkb 1 %s %lux %lux", msg, b->base, b->rp);
+		panic("checkb 1 %s %#p %#p", msg, b->base, b->rp);
 	if(b->wp < b->base)
-		panic("checkb 2 %s %lux %lux", msg, b->base, b->wp);
+		panic("checkb 2 %s %#p %#p", msg, b->base, b->wp);
 	if(b->rp > b->lim)
-		panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim);
+		panic("checkb 3 %s %#p %#p", msg, b->rp, b->lim);
 	if(b->wp > b->lim)
-		panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim);
-
-}
-
-void
-iallocsummary(void)
-{
-	print("ialloc %lud/%lud\n", ialloc.bytes, conf.ialloc);
+		panic("checkb 4 %s %#p %#p", msg, b->wp, b->lim);
 }
--- a/kern/cache.c
+++ /dev/null
@@ -1,46 +1,0 @@
-#include	"u.h"
-#include	"lib.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"error.h"
-
-
-void
-cinit(void)
-{
-}
-
-void
-copen(Chan *c)
-{
-	USED(c);
-}
-
-int
-cread(Chan *c, uchar *buf, int len, vlong off)
-{
-	USED(c);
-	USED(buf);
-	USED(len);
-	USED(off);
-
-	return 0;
-}
-
-void
-cupdate(Chan *c, uchar *buf, int len, vlong off)
-{
-	USED(c);
-	USED(buf);
-	USED(len);
-	USED(off);
-}
-
-void
-cwrite(Chan* c, uchar *buf, int len, vlong off)
-{
-	USED(c);
-	USED(buf);
-	USED(len);
-	USED(off);
-}
--- a/kern/devmnt.c
+++ b/kern/devmnt.c
@@ -24,15 +24,10 @@
 	Fcall	request;	/* Outgoing file system protocol message */
 	Fcall 	reply;		/* Incoming reply */
 	Mnt*	m;		/* Mount device during rpc */
-	Rendez	r;		/* Place to hang out */
-	uchar*	rpc;		/* I/O Data buffer */
-	uint		rpclen;	/* len of buffer */
-	Block	*b;		/* reply blocks */
-	char	done;		/* Rpc completed */
-	uvlong	stime;		/* start time for mnt statistics */
-	ulong	reqlen;		/* request length for mnt statistics */
-	ulong	replen;		/* reply length for mnt statistics */
+	Rendez*	z;		/* Place to hang out */
+	Block*	b;		/* reply blocks */
 	Mntrpc*	flushed;	/* message this one flushes */
+	char	done;		/* Rpc completed */
 };
 
 enum
@@ -42,42 +37,38 @@
 	NMASK = (64*1024)>>TAGSHIFT,
 };
 
-struct Mntalloc
+static struct Mntalloc
 {
-	Lock lk;
+	Lock	lk;
 	Mnt*	list;		/* Mount devices in use */
 	Mnt*	mntfree;	/* Free list */
 	Mntrpc*	rpcfree;
-	int	nrpcfree;
-	int	nrpcused;
+	ulong	nrpcfree;
+	ulong	nrpcused;
 	ulong	id;
 	ulong	tagmask[NMASK];
-}mntalloc;
+} mntalloc;
 
-void	mattach(Mnt*, Chan*, char*);
-Mnt*	mntchk(Chan*);
-void	mntdirfix(uchar*, Chan*);
-Mntrpc*	mntflushalloc(Mntrpc*, ulong);
-void	mntflushfree(Mnt*, Mntrpc*);
-void	mntfree(Mntrpc*);
-void	mntgate(Mnt*);
-void	mntpntfree(Mnt*);
-void	mntqrm(Mnt*, Mntrpc*);
-Mntrpc*	mntralloc(Chan*, ulong);
-long	mntrdwr(int, Chan*, void*, long, vlong);
-int	mntrpcread(Mnt*, Mntrpc*);
-void	mountio(Mnt*, Mntrpc*);
-void	mountmux(Mnt*, Mntrpc*);
-void	mountrpc(Mnt*, Mntrpc*);
-int	rpcattn(void*);
-Chan*	mntchan(void);
+static Chan*	mntchan(void);
+static Mnt*	mntchk(Chan*);
+static void	mntdirfix(uchar*, Chan*);
+static Mntrpc*	mntflushalloc(Mntrpc*);
+static Mntrpc*	mntflushfree(Mnt*, Mntrpc*);
+static void	mntfree(Mntrpc*);
+static void	mntgate(Mnt*);
+static void	mntqrm(Mnt*, Mntrpc*);
+static Mntrpc*	mntralloc(Chan*);
+static long	mntrdwr(int, Chan*, void*, long, vlong);
+static int	mntrpcread(Mnt*, Mntrpc*);
+static void	mountio(Mnt*, Mntrpc*);
+static void	mountmux(Mnt*, Mntrpc*);
+static void	mountrpc(Mnt*, Mntrpc*);
+static int	rpcattn(void*);
 
 char	Esbadstat[] = "invalid directory entry received from server";
-char Enoversion[] = "version not established for mount channel";
+char	Enoversion[] = "version not established for mount channel";
 
 
-void (*mntstats)(int, Chan*, uvlong, ulong);
-
 static void
 mntreset(void)
 {
@@ -87,14 +78,12 @@
 	fmtinstall('F', fcallfmt);
 	fmtinstall('D', dirfmt);
 /* We can't install %M since eipfmt does and is used in the kernel [sape] */
-
-	cinit();
 }
 
 /*
  * Version is not multiplexed: message sent only once per connection.
  */
-long
+int
 mntversion(Chan *c, char *version, int msize, int returnlen)
 {
 	Fcall f;
@@ -101,6 +90,7 @@
 	uchar *msg;
 	Mnt *m;
 	char *v;
+	Queue *q;
 	long k, l;
 	uvlong oo;
 	char buf[128];
@@ -167,7 +157,6 @@
 	unlock(&c->ref.lk);
 
 	l = devtab[c->type]->write(c, msg, k, oo);
-
 	if(l < k){
 		lock(&c->ref.lk);
 		c->offset -= k - l;
@@ -176,14 +165,15 @@
 	}
 
 	/* message sent; receive and decode reply */
-	k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
-	if(k <= 0)
-		error("EOF receiving fversion reply");
+	for(k = 0; k < BIT32SZ || (k < GBIT32(msg) && k < 8192+IOHDRSZ); k += l){
+		l = devtab[c->type]->read(c, msg+k, 8192+IOHDRSZ-k, c->offset);
+		if(l <= 0)
+			error("EOF receiving fversion reply");
+		lock(&c->ref.lk);
+		c->offset += l;
+		unlock(&c->ref.lk);
+	}
 
-	lock(&c->ref.lk);
-	c->offset += k;
-	unlock(&c->ref.lk);
-
 	l = convM2S(msg, k, &f);
 	if(l != k)
 		error("bad fversion conversion on reply");
@@ -199,40 +189,49 @@
 	k = strlen(f.version);
 	if(strncmp(f.version, v, k) != 0)
 		error("bad 9P version returned from server");
+	if(returnlen > 0 && returnlen < k)
+		error(Eshort);
 
+	v = nil;
+	kstrdup(&v, f.version);
+	q = qopen(10*MAXRPC, 0, nil, nil);
+	if(q == nil){
+		free(v);
+		exhausted("mount queues");
+	}
+
 	/* now build Mnt associated with this connection */
 	lock(&mntalloc.lk);
 	m = mntalloc.mntfree;
-	if(m != 0)
+	if(m != nil)
 		mntalloc.mntfree = m->list;
 	else {
+		unlock(&mntalloc.lk);
 		m = malloc(sizeof(Mnt));
-		if(m == 0) {
-			unlock(&mntalloc.lk);
+		if(m == nil) {
+			qfree(q);
+			free(v);
 			exhausted("mount devices");
 		}
+		lock(&mntalloc.lk);
 	}
 	m->list = mntalloc.list;
 	mntalloc.list = m;
-	m->version = nil;
-	kstrdup(&m->version, f.version);
+	m->version = v;
 	m->id = mntalloc.id++;
-	m->q = qopen(10*MAXRPC, 0, 0, nil);
+	m->q = q;
 	m->msize = f.msize;
 	unlock(&mntalloc.lk);
 
-	if(returnlen > 0){
-		if(returnlen < k)
-			error(Eshort);
-		memmove(version, f.version, k);
-	}
+	if(returnlen > 0)
+		memmove(version, f.version, k);	/* length was checked above */
 
 	poperror();	/* msg */
 	free(msg);
 
 	lock(&m->lk);
-	m->queue = 0;
-	m->rip = 0;
+	m->queue = nil;
+	m->rip = nil;
 
 	c->flag |= CMSG;
 	c->mux = m;
@@ -252,9 +251,8 @@
 	Mntrpc *r;
 
 	m = c->mux;
-
 	if(m == nil){
-		mntversion(c, VERSION9P, MAXRPC, 0);
+		mntversion(c, nil, 0, 0);
 		m = c->mux;
 		if(m == nil)
 			error(Enoversion);
@@ -269,8 +267,7 @@
 		nexterror();
 	}
 
-	r = mntralloc(0, m->msize);
-
+	r = mntralloc(c);
 	if(waserror()) {
 		mntfree(r);
 		nexterror();
@@ -287,6 +284,7 @@
 	incref(&m->c->ref);
 	c->mqid = c->qid;
 	c->mode = ORDWR;
+	c->iounit = m->msize-IOHDRSZ;
 
 	poperror();	/* r */
 	mntfree(r);
@@ -297,24 +295,13 @@
 
 }
 
-static Chan*
-mntattach(char *muxattach)
+Chan*
+mntattach(Chan *c, Chan *ac, char *spec, int flags)
 {
 	Mnt *m;
-	Chan *c;
 	Mntrpc *r;
-	struct bogus{
-		Chan	*chan;
-		Chan	*authchan;
-		char	*spec;
-		int	flags;
-	}bogus;
 
-	bogus = *((struct bogus *)muxattach);
-	c = bogus.chan;
-
 	m = c->mux;
-
 	if(m == nil){
 		mntversion(c, nil, 0, 0);
 		m = c->mux;
@@ -331,21 +318,19 @@
 		nexterror();
 	}
 
-	r = mntralloc(0, m->msize);
-
+	r = mntralloc(c);
 	if(waserror()) {
 		mntfree(r);
 		nexterror();
 	}
-
 	r->request.type = Tattach;
 	r->request.fid = c->fid;
-	if(bogus.authchan == nil)
+	if(ac == nil)
 		r->request.afid = NOFID;
 	else
-		r->request.afid = bogus.authchan->fid;
+		r->request.afid = ac->fid;
 	r->request.uname = up->user;
-	r->request.aname = bogus.spec;
+	r->request.aname = spec;
 	mountrpc(m, r);
 
 	c->qid = r->reply.qid;
@@ -358,12 +343,20 @@
 
 	poperror();	/* c */
 
-	if(bogus.flags&MCACHE)
+	if(flags&MCACHE)
 		c->flag |= CCACHE;
 	return c;
 }
 
-Chan*
+static Chan*
+noattach(char *s)
+{
+	USED(s);
+	error(Enoattach);
+	return nil;
+}
+
+static Chan*
 mntchan(void)
 {
 	Chan *c;
@@ -373,7 +366,7 @@
 	c->dev = mntalloc.id++;
 	unlock(&mntalloc.lk);
 
-	if(c->mchan)
+	if(c->mchan != nil)
 		panic("mntchan non-zero %p", c->mchan);
 	return c;
 }
@@ -401,7 +394,7 @@
 
 	alloc = 0;
 	m = mntchk(c);
-	r = mntralloc(c, m->msize);
+	r = mntralloc(c);
 	if(nc == nil){
 		nc = devclone(c);
 		/*
@@ -409,6 +402,7 @@
 		 * Therefore set type to 0 for now; rootclose is known to be safe.
 		 */
 		nc->type = 0;
+		nc->flag |= (c->flag & CCACHE);
 		alloc = 1;
 	}
 	wq->clone = nc;
@@ -468,7 +462,7 @@
 	if(n < BIT16SZ)
 		error(Eshortstat);
 	m = mntchk(c);
-	r = mntralloc(c, m->msize);
+	r = mntralloc(c);
 	if(waserror()) {
 		mntfree(r);
 		nexterror();
@@ -477,28 +471,9 @@
 	r->request.fid = c->fid;
 	mountrpc(m, r);
 
-/* r->reply.nstat is 16 bits
-	if(r->reply.nstat >= 1<<16)
-		error("returned stat buffer count too large");
-*/
-
 	if(r->reply.nstat > n){
-		/*
-		 * 12/31/2002 RSC
-		 * 
-		 * This should be nstat-2, which is the first two
-		 * bytes of the stat buffer.  But dirstat and dirfstat
-		 * depended on getting the full nstat (they didn't
-		 * add BIT16SZ themselves).  I fixed dirstat and dirfstat
-		 * but am leaving this unchanged for now.  After a
-		 * few months, once enough of the relevant binaries
-		 * have been recompiled for other reasons, we can
-		 * change this to nstat-2.  Devstat gets this right
-		 * (via convD2M).
-		 */
-		/* doesn't fit; just patch the count and return */
-		PBIT16((uchar*)dp, r->reply.nstat);
 		n = BIT16SZ;
+		PBIT16((uchar*)dp, r->reply.nstat-2);
 	}else{
 		n = r->reply.nstat;
 		memmove(dp, r->reply.stat, n);
@@ -517,7 +492,7 @@
 	Mntrpc *r;
 
 	m = mntchk(c);
-	r = mntralloc(c, m->msize);
+	r = mntralloc(c);
 	if(waserror()) {
 		mntfree(r);
 		nexterror();
@@ -541,9 +516,6 @@
 	poperror();
 	mntfree(r);
 
-	if(c->flag & CCACHE)
-		copen(c);
-
 	return c;
 }
 
@@ -566,12 +538,11 @@
 	Mntrpc *r;
 
 	m = mntchk(c);
-	r = mntralloc(c, m->msize);
-	if(waserror()){
+	r = mntralloc(c);
+	if(waserror()) {
 		mntfree(r);
 		nexterror();
 	}
-
 	r->request.type = t;
 	r->request.fid = c->fid;
 	mountrpc(m, r);
@@ -582,27 +553,22 @@
 void
 muxclose(Mnt *m)
 {
-	Mntrpc *q, *r;
+	Mnt *f, **l;
+	Mntrpc *r;
 
-	for(q = m->queue; q; q = r) {
-		r = q->list;
-		mntfree(q);
+	while((r = m->queue) != nil){
+		m->queue = r->list;
+		mntfree(r);
 	}
 	m->id = 0;
 	free(m->version);
 	m->version = nil;
-	mntpntfree(m);
-}
+	qfree(m->q);
+	m->q = nil;
 
-void
-mntpntfree(Mnt *m)
-{
-	Mnt *f, **l;
-	Queue *q;
-
 	lock(&mntalloc.lk);
 	l = &mntalloc.list;
-	for(f = *l; f; f = f->list) {
+	for(f = *l; f != nil; f = f->list) {
 		if(f == m) {
 			*l = m->list;
 			break;
@@ -611,10 +577,7 @@
 	}
 	m->list = mntalloc.mntfree;
 	mntalloc.mntfree = m;
-	q = m->q;
 	unlock(&mntalloc.lk);
-
-	qfree(q);
 }
 
 static void
@@ -636,7 +599,7 @@
 	Mntrpc *r;
 
 	m = mntchk(c);
-	r = mntralloc(c, m->msize);
+	r = mntralloc(c);
 	if(waserror()) {
 		mntfree(r);
 		nexterror();
@@ -655,32 +618,11 @@
 mntread(Chan *c, void *buf, long n, vlong off)
 {
 	uchar *p, *e;
-	int nc, cache, isdir, dirlen;
+	int dirlen;
 
-	isdir = 0;
-	cache = c->flag & CCACHE;
-	if(c->qid.type & QTDIR) {
-		cache = 0;
-		isdir = 1;
-	}
-
 	p = buf;
-	if(cache) {
-		nc = cread(c, buf, n, off);
-		if(nc > 0) {
-			n -= nc;
-			if(n == 0)
-				return nc;
-			p += nc;
-			off += nc;
-		}
-		n = mntrdwr(Tread, c, p, n, off);
-		cupdate(c, p, n, off);
-		return n + nc;
-	}
-
-	n = mntrdwr(Tread, c, buf, n, off);
-	if(isdir) {
+	n = mntrdwr(Tread, c, p, n, off);
+	if(c->qid.type & QTDIR) {
 		for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
 			dirlen = BIT16SZ+GBIT16(p);
 			if(p+dirlen > e)
@@ -700,23 +642,24 @@
 	return mntrdwr(Twrite, c, buf, n, off);
 }
 
-long
+static long
 mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
 {
 	Mnt *m;
  	Mntrpc *r;
 	char *uba;
-	int cache;
 	ulong cnt, nr, nreq;
 
 	m = mntchk(c);
 	uba = buf;
 	cnt = 0;
-	cache = c->flag & CCACHE;
-	if(c->qid.type & QTDIR)
-		cache = 0;
+
 	for(;;) {
-		r = mntralloc(c, m->msize);
+		nreq = n;
+		if(nreq > c->iounit)
+			nreq = c->iounit;
+
+		r = mntralloc(c);
 		if(waserror()) {
 			mntfree(r);
 			nexterror();
@@ -725,23 +668,16 @@
 		r->request.fid = c->fid;
 		r->request.offset = off;
 		r->request.data = uba;
-		nr = n;
-		if(nr > m->msize-IOHDRSZ)
-			nr = m->msize-IOHDRSZ;
-		r->request.count = nr;
+		r->request.count = nreq;
 		mountrpc(m, r);
-		nreq = r->request.count;
 		nr = r->reply.count;
 		if(nr > nreq)
 			nr = nreq;
-
 		if(type == Tread)
-			r->b = bl2mem((uchar*)uba, r->b, nr);
-		else if(cache)
-			cwrite(c, (uchar*)uba, nr, off);
-
-		poperror();
+			nr = readblist(r->b, (uchar*)uba, nr, 0);
 		mntfree(r);
+		poperror();
+
 		off += nr;
 		uba += nr;
 		cnt += nr;
@@ -752,10 +688,9 @@
 	return cnt;
 }
 
-void
+static void
 mountrpc(Mnt *m, Mntrpc *r)
 {
-	char *sn, *cn;
 	int t;
 
 	r->reply.tag = 0;
@@ -772,14 +707,8 @@
 	default:
 		if(t == r->request.type+1)
 			break;
-		sn = "?";
-		if(m->c->name != nil)
-			sn = m->c->name->s;
-		cn = "?";
-		if(r->c != nil && r->c->name != nil)
-			cn = r->c->name->s;
-		print("mnt: proc %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n",
-			up->pid, sn, cn,
+		print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
+			up->text, up->pid, chanpath(m->c), chanpath(r->c),
 			r, r->request.tag, r->request.fid, r->request.type,
 			r->reply.type, r->reply.tag);
 		error(Emountrpc);
@@ -786,22 +715,32 @@
 	}
 }
 
-void
+static void
 mountio(Mnt *m, Mntrpc *r)
 {
+	Block *b;
 	int n;
 
 	while(waserror()) {
 		if(m->rip == up)
 			mntgate(m);
-		if(strcmp(up->errstr, Eintr) != 0){
-			mntflushfree(m, r);
+		if(strcmp(up->errstr, Eintr) != 0 || waserror()){
+			r = mntflushfree(m, r);
+			switch(r->request.type){
+			case Tremove:
+			case Tclunk:
+				/* botch, abandon fid */ 
+				if(strcmp(up->errstr, Ehungup) != 0)
+					r->c->fid = 0;
+			}
 			nexterror();
 		}
-		r = mntflushalloc(r, m->msize);
+		r = mntflushalloc(r);
+		poperror();
 	}
 
 	lock(&m->lk);
+	r->z = &up->sleep;
 	r->m = m;
 	r->list = m->queue;
 	m->queue = r;
@@ -808,24 +747,30 @@
 	unlock(&m->lk);
 
 	/* Transmit a file system rpc */
-	if(m->msize == 0)
-		panic("msize");
-	n = convS2M(&r->request, r->rpc, m->msize);
-	if(n < 0)
-		panic("bad message type in mountio");
-	if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
+	n = sizeS2M(&r->request);
+	b = allocb(n);
+	if(waserror()){
+		freeb(b);
+		nexterror();
+	}
+	n = convS2M(&r->request, b->wp, n);
+	if(n <= 0 || n > m->msize) {
+		print("mountio: proc %s %lud: convS2M returned %d for tag %d fid %d T%d\n",
+			up->text, up->pid, n, r->request.tag, r->request.fid, r->request.type);
 		error(Emountrpc);
-	r->stime = fastticks(nil);
-	r->reqlen = n;
+	}
+	b->wp += n;
+	poperror();
+	devtab[m->c->type]->bwrite(m->c, b, 0);
 
 	/* Gate readers onto the mount point one at a time */
 	for(;;) {
 		lock(&m->lk);
-		if(m->rip == 0)
+		if(m->rip == nil)
 			break;
 		unlock(&m->lk);
-		sleep(&r->r, rpcattn, r);
-		if(r->done){
+		sleep(r->z, rpcattn, r);
+		if(r->done) {
 			poperror();
 			mntflushfree(m, r);
 			return;
@@ -850,18 +795,13 @@
 
 	while(qlen(m->q) < len){
 		b = devtab[m->c->type]->bread(m->c, m->msize, 0);
-		if(b == nil)
+		if(b == nil || qaddlist(m->q, b) == 0)
 			return -1;
-		if(BLEN(b) == 0){
-			freeblist(b);
-			return -1;
-		}
-		qaddlist(m->q, b);
 	}
 	return 0;
 }
 
-int
+static int
 mntrpcread(Mnt *m, Mntrpc *r)
 {
 	int i, t, len, hlen;
@@ -875,7 +815,7 @@
 		return -1;
 	nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
 
-	/* read in the rest of the message, avoid rediculous (for now) message sizes */
+	/* read in the rest of the message, avoid ridiculous (for now) message sizes */
 	len = GBIT32(nb->rp);
 	if(len > m->msize){
 		qdiscard(m->q, qlen(m->q));
@@ -933,49 +873,50 @@
 	return 0;
 }
 
-void
+static void
 mntgate(Mnt *m)
 {
 	Mntrpc *q;
 
 	lock(&m->lk);
-	m->rip = 0;
-	for(q = m->queue; q; q = q->list) {
+	m->rip = nil;
+	for(q = m->queue; q != nil; q = q->list) {
 		if(q->done == 0)
-		if(wakeup(&q->r))
+		if(wakeup(q->z))
 			break;
 	}
 	unlock(&m->lk);
 }
 
-void
+static void
 mountmux(Mnt *m, Mntrpc *r)
 {
 	Mntrpc **l, *q;
+	Rendez *z;
 
 	lock(&m->lk);
 	l = &m->queue;
-	for(q = *l; q; q = q->list) {
+	for(q = *l; q != nil; q = q->list) {
 		/* look for a reply to a message */
 		if(q->request.tag == r->reply.tag) {
 			*l = q->list;
-			if(q != r) {
-				/*
-				 * Completed someone else.
-				 * Trade pointers to receive buffer.
-				 */
-				q->reply = r->reply;
-				q->b = r->b;
-				r->b = nil;
+			if(q == r) {
+				q->done = 1;
+				unlock(&m->lk);
+				return;
 			}
+			/*
+			 * Completed someone else.
+			 * Trade pointers to receive buffer.
+			 */
+			q->reply = r->reply;
+			q->b = r->b;
+			r->b = nil;
+			z = q->z;
+			// coherence();
 			q->done = 1;
+			wakeup(z);
 			unlock(&m->lk);
-			if(mntstats != 0)
-				(*mntstats)(q->request.type,
-					m->c, q->stime,
-					q->reqlen + r->replen);
-			if(q != r)
-				wakeup(&q->r);
 			return;
 		}
 		l = &q->list;
@@ -988,13 +929,12 @@
  * Create a new flush request and chain the previous
  * requests from it
  */
-Mntrpc*
-mntflushalloc(Mntrpc *r, ulong iounit)
+static Mntrpc*
+mntflushalloc(Mntrpc *r)
 {
 	Mntrpc *fr;
 
-	fr = mntralloc(0, iounit);
-
+	fr = mntralloc(r->c);
 	fr->request.type = Tflush;
 	if(r->request.type == Tflush)
 		fr->request.oldtag = r->request.oldtag;
@@ -1010,26 +950,28 @@
  *  flush and the original message from the unanswered
  *  request queue.  Mark the original message as done
  *  and if it hasn't been answered set the reply to to
- *  Rflush.
+ *  Rflush. Return the original rpc.
  */
-void
+static Mntrpc*
 mntflushfree(Mnt *m, Mntrpc *r)
 {
 	Mntrpc *fr;
 
-	while(r){
+	while(r != nil){
 		fr = r->flushed;
 		if(!r->done){
 			r->reply.type = Rflush;
 			mntqrm(m, r);
 		}
-		if(fr)
-			mntfree(r);
+		if(fr == nil)
+			break;
+		mntfree(r);
 		r = fr;
 	}
+	return r;
 }
 
-int
+static int
 alloctag(void)
 {
 	int i, j;
@@ -1037,7 +979,7 @@
 
 	for(i = 0; i < NMASK; i++){
 		v = mntalloc.tagmask[i];
-		if(v == ~0)
+		if(v == ~0UL)
 			continue;
 		for(j = 0; j < 1<<TAGSHIFT; j++)
 			if((v & (1<<j)) == 0){
@@ -1049,52 +991,33 @@
 	return NOTAG;
 }
 
-void
+static void
 freetag(int t)
 {
 	mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
 }
 
-Mntrpc*
-mntralloc(Chan *c, ulong msize)
+static Mntrpc*
+mntralloc(Chan *c)
 {
 	Mntrpc *new;
 
-	lock(&mntalloc.lk);
-	new = mntalloc.rpcfree;
-	if(new == nil){
+	if(mntalloc.nrpcfree == 0) {
+	Alloc:
 		new = malloc(sizeof(Mntrpc));
+		if(new == nil)
+			exhausted("mount rpc header");
+		lock(&mntalloc.lk);
+		new->request.tag = alloctag();
+	} else {
+		lock(&mntalloc.lk);
+		new = mntalloc.rpcfree;
 		if(new == nil) {
 			unlock(&mntalloc.lk);
-			exhausted("mount rpc header");
+			goto Alloc;
 		}
-		/*
-		 * The header is split from the data buffer as
-		 * mountmux may swap the buffer with another header.
-		 */
-		new->rpc = mallocz(msize, 0);
-		if(new->rpc == nil){
-			free(new);
-			unlock(&mntalloc.lk);
-			exhausted("mount rpc buffer");
-		}
-		new->rpclen = msize;
-		new->request.tag = alloctag();
-	}
-	else {
 		mntalloc.rpcfree = new->list;
 		mntalloc.nrpcfree--;
-		if(new->rpclen < msize){
-			free(new->rpc);
-			new->rpc = mallocz(msize, 0);
-			if(new->rpc == nil){
-				free(new);
-				mntalloc.nrpcused--;
-				unlock(&mntalloc.lk);
-				exhausted("mount rpc buffer");
-			}
-			new->rpclen = msize;
-		}
 	}
 	mntalloc.nrpcused++;
 	unlock(&mntalloc.lk);
@@ -1105,27 +1028,25 @@
 	return new;
 }
 
-void
+static void
 mntfree(Mntrpc *r)
 {
-	if(r->b != nil)
-		freeblist(r->b);
+	freeblist(r->b);
 	lock(&mntalloc.lk);
-	if(mntalloc.nrpcfree >= 10){
-		free(r->rpc);
-		free(r);
-		freetag(r->request.tag);
-	}
-	else{
+	mntalloc.nrpcused--;
+	if(mntalloc.nrpcfree < 32) {
 		r->list = mntalloc.rpcfree;
 		mntalloc.rpcfree = r;
 		mntalloc.nrpcfree++;
+		unlock(&mntalloc.lk);
+		return;
 	}
-	mntalloc.nrpcused--;
+	freetag(r->request.tag);
 	unlock(&mntalloc.lk);
+	free(r);
 }
 
-void
+static void
 mntqrm(Mnt *m, Mntrpc *r)
 {
 	Mntrpc **l, *f;
@@ -1134,7 +1055,7 @@
 	r->done = 1;
 
 	l = &m->queue;
-	for(f = *l; f; f = f->list) {
+	for(f = *l; f != nil; f = f->list) {
 		if(f == r) {
 			*l = r->list;
 			break;
@@ -1144,23 +1065,21 @@
 	unlock(&m->lk);
 }
 
-Mnt*
+static Mnt*
 mntchk(Chan *c)
 {
 	Mnt *m;
 
 	/* This routine is mostly vestiges of prior lives; now it's just sanity checking */
-
 	if(c->mchan == nil)
-		panic("mntchk 1: nil mchan c %s\n", chanpath(c));
+		panic("mntchk 1: nil mchan c %s", chanpath(c));
 
 	m = c->mchan->mux;
-
 	if(m == nil)
 		print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
 
 	/*
-	 * Was it closed and reused (was error(Eshutdown); now, it can't happen)
+	 * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
 	 */
 	if(m->id == 0 || m->id >= c->dev)
 		panic("mntchk 3: can't happen");
@@ -1173,7 +1092,7 @@
  * reflect local values.  These entries are known to be
  * the first two in the Dir encoding after the count.
  */
-void
+static void
 mntdirfix(uchar *dirbuf, Chan *c)
 {
 	uint r;
@@ -1185,13 +1104,13 @@
 	PBIT32(dirbuf, c->dev);
 }
 
-int
+static int
 rpcattn(void *v)
 {
 	Mntrpc *r;
 
 	r = v;
-	return r->done || r->m->rip == 0;
+	return r->done || r->m->rip == nil;
 }
 
 Dev mntdevtab = {
@@ -1201,7 +1120,7 @@
 	mntreset,
 	devinit,
 	devshutdown,
-	mntattach,
+	noattach,
 	mntwalk,
 	mntstat,
 	mntopen,
--- a/kern/fns.h
+++ b/kern/fns.h
@@ -10,7 +10,6 @@
 int		anyhigher(void);
 int		anyready(void);
 Page*		auxpage(void);
-Block*		bl2mem(uchar*, Block*, int);
 int		blocklen(Block*);
 void		callwithureg(void(*)(Ureg*));
 char*		chanpath(Chan*);
@@ -27,7 +26,6 @@
 void		chanrec(Mnt*);
 void		checkalarms(void);
 void		checkb(Block*, char*);
-void		cinit(void);
 Chan*		cclone(Chan*);
 void		cclose(Chan*);
 char*	clipread(void);
@@ -45,14 +43,10 @@
 void		confinit1(int);
 int		consactive(void);
 extern void		(*consdebug)(void);
-void		copen(Chan*);
 Block*		concatblock(Block*);
 Block*		copyblock(Block*, int);
 void		copypage(Page*, Page*);
-int		cread(Chan*, uchar*, int, vlong);
 void		cunmount(Chan*, Chan*);
-void		cupdate(Chan*, uchar*, int, vlong);
-void		cwrite(Chan*, uchar*, int, vlong);
 ulong		dbgpc(Proc*);
 int		decref(Ref*);
 int		decrypt(void*, void*, int);
@@ -119,7 +113,6 @@
 void		hzsched(void);
 void		iallocinit(void);
 Block*		iallocb(int);
-void		iallocsummary(void);
 long		ibrk(ulong, int);
 void		ilock(Lock*);
 void		iunlock(Lock*);
@@ -161,8 +154,6 @@
 void*		mallocz(ulong, int);
 #define		malloc kmalloc
 void*		malloc(ulong);
-void		mallocsummary(void);
-Block*		mem2bl(uchar*, int);
 void		mfreeseg(Segment*, ulong, int);
 void		microdelay(int);
 void		mkqid(Qid*, vlong, ulong, int);
@@ -238,7 +229,8 @@
 void		putswap(Page*);
 ulong		pwait(Waitmsg*);
 Label*	pwaserror(void);
-void		qaddlist(Queue*, Block*);
+long		readblist(Block*, uchar *, long, long);
+int		qaddlist(Queue*, Block*);
 Block*		qbread(Queue*, int);
 long		qbwrite(Queue*, Block*);
 Queue*		qbypass(void (*)(void*, Block*), void*);
--- a/kern/qio.c
+++ b/kern/qio.c
@@ -10,18 +10,17 @@
 static ulong copyblockcnt;
 static ulong consumecnt;
 static ulong producecnt;
-static ulong qcopycnt;
 
-static int debugging;
-
 #define QDEBUG	if(0)
 
 /*
  *  IO queues
  */
+typedef struct Queue	Queue;
+
 struct Queue
 {
-	Lock lk;
+	Lock	lk;
 
 	Block*	bfirst;		/* buffer */
 	Block*	blast;
@@ -53,17 +52,6 @@
 
 uint	qiomaxatomic = Maxatomic;
 
-void
-ixsummary(void)
-{
-	debugging ^= 1;
-	iallocsummary();
-	print("pad %lud, concat %lud, pullup %lud, copy %lud\n",
-		padblockcnt, concatblockcnt, pullupblockcnt, copyblockcnt);
-	print("consume %lud, produce %lud, qcopy %lud\n",
-		consumecnt, producecnt, qcopycnt);
-}
-
 /*
  *  free a list of blocks
  */
@@ -72,9 +60,9 @@
 {
 	Block *next;
 
-	for(; b != 0; b = next){
+	for(; b != nil; b = next){
 		next = b->next;
-		b->next = 0;
+		b->next = nil;
 		freeb(b);
 	}
 }
@@ -88,40 +76,31 @@
 	int n;
 	Block *nbp;
 
-	QDEBUG checkb(bp, "padblock 1");
+	QDEBUG checkb(bp, "padblock 0");
 	if(size >= 0){
 		if(bp->rp - bp->base >= size){
 			bp->rp -= size;
 			return bp;
 		}
-
-		if(bp->next)
-			panic("padblock 0x%p", getcallerpc(&bp));
 		n = BLEN(bp);
-		padblockcnt++;
 		nbp = allocb(size+n);
 		nbp->rp += size;
 		nbp->wp = nbp->rp;
 		memmove(nbp->wp, bp->rp, n);
 		nbp->wp += n;
-		freeb(bp);
 		nbp->rp -= size;
 	} else {
 		size = -size;
-
-		if(bp->next)
-			panic("padblock 0x%p", getcallerpc(&bp));
-
 		if(bp->lim - bp->wp >= size)
 			return bp;
-
 		n = BLEN(bp);
-		padblockcnt++;
-		nbp = allocb(size+n);
+		nbp = allocb(n+size);
 		memmove(nbp->wp, bp->rp, n);
 		nbp->wp += n;
-		freeb(bp);
 	}
+	nbp->next = bp->next;
+	freeb(bp);
+	padblockcnt++;
 	QDEBUG checkb(nbp, "padblock 1");
 	return nbp;
 }
@@ -135,7 +114,7 @@
 	int len;
 
 	len = 0;
-	while(bp) {
+	while(bp != nil) {
 		len += BLEN(bp);
 		bp = bp->next;
 	}
@@ -151,7 +130,7 @@
 	int len;
 
 	len = 0;
-	while(bp) {
+	while(bp != nil) {
 		len += BALLOC(bp);
 		bp = bp->next;
 	}
@@ -159,7 +138,7 @@
 }
 
 /*
- *  copy the  string of blocks into
+ *  copy the string of blocks into
  *  a single block and free the string
  */
 Block*
@@ -166,21 +145,12 @@
 concatblock(Block *bp)
 {
 	int len;
-	Block *nb, *f;
 
-	if(bp->next == 0)
+	if(bp->next == nil)
 		return bp;
-
-	nb = allocb(blocklen(bp));
-	for(f = bp; f; f = f->next) {
-		len = BLEN(f);
-		memmove(nb->wp, f->rp, len);
-		nb->wp += len;
-	}
-	concatblockcnt += BLEN(nb);
-	freeblist(bp);
-	QDEBUG checkb(nb, "concatblock 1");
-	return nb;
+	len = blocklen(bp);
+	concatblockcnt += len;
+	return pullupblock(bp, len);
 }
 
 /*
@@ -189,8 +159,8 @@
 Block*
 pullupblock(Block *bp, int n)
 {
-	int i;
 	Block *nbp;
+	int i;
 
 	/*
 	 *  this should almost always be true, it's
@@ -213,11 +183,11 @@
 	 *  copy bytes from the trailing blocks into the first
 	 */
 	n -= BLEN(bp);
-	while((nbp = bp->next)){
+	while((nbp = bp->next) != nil){
+		pullupblockcnt++;
 		i = BLEN(nbp);
 		if(i > n) {
 			memmove(bp->wp, nbp->rp, n);
-			pullupblockcnt++;
 			bp->wp += n;
 			nbp->rp += n;
 			QDEBUG checkb(bp, "pullupblock 1");
@@ -225,14 +195,14 @@
 		} else {
 			/* shouldn't happen but why crash if it does */
 			if(i < 0){
-				print("pullup negative length packet\n");
+				print("pullup negative length packet, called from %#p\n",
+					getcallerpc(&bp));
 				i = 0;
 			}
 			memmove(bp->wp, nbp->rp, i);
-			pullupblockcnt++;
 			bp->wp += i;
 			bp->next = nbp->next;
-			nbp->next = 0;
+			nbp->next = nil;
 			freeb(nbp);
 			n -= i;
 			if(n == 0){
@@ -242,7 +212,7 @@
 		}
 	}
 	freeb(bp);
-	return 0;
+	return nil;
 }
 
 /*
@@ -295,7 +265,7 @@
 
 	bp->wp -= (BLEN(bp) - len);
 
-	if(bp->next) {
+	if(bp->next != nil) {
 		freeblist(bp->next);
 		bp->next = nil;
 	}
@@ -314,7 +284,7 @@
 
 	QDEBUG checkb(bp, "copyblock 0");
 	nbp = allocb(count);
-	for(; count > 0 && bp != 0; bp = bp->next){
+	for(; count > 0 && bp != nil; bp = bp->next){
 		l = BLEN(bp);
 		if(l > count)
 			l = count;
@@ -412,11 +382,11 @@
 		iunlock(&q->lk);
 		return nil;
 	}
+	QDEBUG checkb(b, "qget");
 	q->bfirst = b->next;
-	b->next = 0;
+	b->next = nil;
 	q->len -= BALLOC(b);
 	q->dlen -= BLEN(b);
-	QDEBUG checkb(b, "qget");
 
 	/* if writer flow controlled, restart */
 	if((q->state & Qflow) && q->len < q->limit/2){
@@ -439,7 +409,7 @@
 int
 qdiscard(Queue *q, int len)
 {
-	Block *b;
+	Block *b, *tofree = nil;
 	int dowakeup, n, sofar;
 
 	ilock(&q->lk);
@@ -451,10 +421,12 @@
 		n = BLEN(b);
 		if(n <= len - sofar){
 			q->bfirst = b->next;
-			b->next = 0;
 			q->len -= BALLOC(b);
 			q->dlen -= BLEN(b);
-			freeb(b);
+
+			/* remember to free this */
+			b->next = tofree;
+			tofree = b;
 		} else {
 			n = len - sofar;
 			b->rp += n;
@@ -483,6 +455,9 @@
 	if(dowakeup)
 		wakeup(&q->wr);
 
+	if(tofree != nil)
+		freeblist(tofree);
+
 	return sofar;
 }
 
@@ -492,10 +467,9 @@
 int
 qconsume(Queue *q, void *vp, int len)
 {
-	Block *b;
+	Block *b, *tofree = nil;
 	int n, dowakeup;
 	uchar *p = vp;
-	Block *tofree = nil;
 
 	/* sync with qwrite */
 	ilock(&q->lk);
@@ -502,10 +476,10 @@
 
 	for(;;) {
 		b = q->bfirst;
-		if(b == 0){
+		if(b == nil){
 			q->state |= Qstarve;
-			iunlock(&q->lk);
-			return -1;
+			len = -1;
+			goto out;
 		}
 		QDEBUG checkb(b, "qconsume 1");
 
@@ -520,10 +494,10 @@
 		tofree = b;
 	};
 
+	consumecnt += n;
 	if(n < len)
 		len = n;
 	memmove(p, b->rp, len);
-	consumecnt += n;
 	b->rp += len;
 	q->dlen -= len;
 
@@ -530,7 +504,6 @@
 	/* discard the block if we're done with it */
 	if((q->state & Qmsg) || len == n){
 		q->bfirst = b->next;
-		b->next = 0;
 		q->len -= BALLOC(b);
 		q->dlen -= BLEN(b);
 
@@ -539,6 +512,7 @@
 		tofree = b;
 	}
 
+out:
 	/* if writer flow controlled, restart */
 	if((q->state & Qflow) && q->len < q->limit/2){
 		q->state &= ~Qflow;
@@ -560,39 +534,23 @@
 int
 qpass(Queue *q, Block *b)
 {
-	int dlen, len, dowakeup;
+	int len, dowakeup;
 
 	/* sync with qread */
 	dowakeup = 0;
 	ilock(&q->lk);
 	if(q->len >= q->limit){
-		freeblist(b);
 		iunlock(&q->lk);
+		freeblist(b);
 		return -1;
 	}
 	if(q->state & Qclosed){
-		freeblist(b);
 		iunlock(&q->lk);
-		return BALLOC(b);
+		freeblist(b);
+		return 0;
 	}
 
-	/* add buffer to queue */
-	if(q->bfirst)
-		q->blast->next = b;
-	else
-		q->bfirst = b;
-	len = BALLOC(b);
-	dlen = BLEN(b);
-	QDEBUG checkb(b, "qpass");
-	while(b->next){
-		b = b->next;
-		QDEBUG checkb(b, "qpass");
-		len += BALLOC(b);
-		dlen += BLEN(b);
-	}
-	q->blast = b;
-	q->len += len;
-	q->dlen += dlen;
+	len = qaddlist(q, b);
 
 	if(q->len >= q->limit/2)
 		q->state |= Qflow;
@@ -612,7 +570,7 @@
 int
 qpassnolim(Queue *q, Block *b)
 {
-	int dlen, len, dowakeup;
+	int len, dowakeup;
 
 	/* sync with qread */
 	dowakeup = 0;
@@ -619,28 +577,12 @@
 	ilock(&q->lk);
 
 	if(q->state & Qclosed){
-		freeblist(b);
 		iunlock(&q->lk);
-		return BALLOC(b);
+		freeblist(b);
+		return 0;
 	}
 
-	/* add buffer to queue */
-	if(q->bfirst)
-		q->blast->next = b;
-	else
-		q->bfirst = b;
-	len = BALLOC(b);
-	dlen = BLEN(b);
-	QDEBUG checkb(b, "qpass");
-	while(b->next){
-		b = b->next;
-		QDEBUG checkb(b, "qpass");
-		len += BALLOC(b);
-		dlen += BLEN(b);
-	}
-	q->blast = b;
-	q->len += len;
-	q->dlen += dlen;
+	len = qaddlist(q, b);
 
 	if(q->len >= q->limit/2)
 		q->state |= Qflow;
@@ -667,8 +609,7 @@
 	Block **l, *nbp;
 	int n;
 
-	for(l = &bp; *l; l = &(*l)->next){
-		nbp = *l;
+	for(l = &bp; (nbp = *l) != nil; l = &(*l)->next){
 		n = BLEN(nbp);
 		if((n<<2) < BALLOC(nbp)){
 			*l = allocb(n);
@@ -689,6 +630,10 @@
 	int dowakeup;
 	uchar *p = vp;
 
+	b = iallocb(len);
+	if(b == nil)
+		return 0;
+
 	/* sync with qread */
 	dowakeup = 0;
 	ilock(&q->lk);
@@ -699,25 +644,12 @@
 		iunlock(&q->lk);
 		return -1;
 	}
+	producecnt += len;
 
 	/* save in buffer */
-	b = iallocb(len);
-	if(b == 0){
-		iunlock(&q->lk);
-		return 0;
-	}
 	memmove(b->wp, p, len);
-	producecnt += len;
 	b->wp += len;
-	if(q->bfirst)
-		q->blast->next = b;
-	else
-		q->bfirst = b;
-	q->blast = b;
-	/* b->next = 0; done by iallocb() */
-	q->len += BALLOC(b);
-	q->dlen += BLEN(b);
-	QDEBUG checkb(b, "qproduce");
+	qaddlist(q, b);
 
 	if(q->state & Qstarve){
 		q->state &= ~Qstarve;
@@ -740,49 +672,13 @@
 Block*
 qcopy(Queue *q, int len, ulong offset)
 {
-	int sofar;
-	int n;
-	Block *b, *nb;
-	uchar *p;
+	Block *b;
 
-	nb = allocb(len);
-
+	b = allocb(len);
 	ilock(&q->lk);
-
-	/* go to offset */
-	b = q->bfirst;
-	for(sofar = 0; ; sofar += n){
-		if(b == nil){
-			iunlock(&q->lk);
-			return nb;
-		}
-		n = BLEN(b);
-		if(sofar + n > offset){
-			p = b->rp + offset - sofar;
-			n -= offset - sofar;
-			break;
-		}
-		QDEBUG checkb(b, "qcopy");
-		b = b->next;
-	}
-
-	/* copy bytes from there */
-	for(sofar = 0; sofar < len;){
-		if(n > len - sofar)
-			n = len - sofar;
-		memmove(nb->wp, p, n);
-		qcopycnt += n;
-		sofar += n;
-		nb->wp += n;
-		b = b->next;
-		if(b == nil)
-			break;
-		n = BLEN(b);
-		p = b->rp;
-	}
+	b->wp += readblist(q->bfirst, b->wp, len, offset);
 	iunlock(&q->lk);
-
-	return nb;
+	return b;
 }
 
 /*
@@ -794,8 +690,8 @@
 	Queue *q;
 
 	q = malloc(sizeof(Queue));
-	if(q == 0)
-		return 0;
+	if(q == nil)
+		return nil;
 
 	q->limit = q->inilim = limit;
 	q->kick = kick;
@@ -816,8 +712,8 @@
 	Queue *q;
 
 	q = malloc(sizeof(Queue));
-	if(q == 0)
-		return 0;
+	if(q == nil)
+		return nil;
 
 	q->limit = 0;
 	q->arg = arg;
@@ -832,7 +728,7 @@
 {
 	Queue *q = a;
 
-	return (q->state & Qclosed) || q->bfirst != 0;
+	return (q->state & Qclosed) || q->bfirst != nil;
 }
 
 /*
@@ -864,21 +760,34 @@
 }
 
 /*
- * add a block list to a queue
+ * add a block list to a queue, return bytes added
  */
-void
+int
 qaddlist(Queue *q, Block *b)
 {
+	int len, dlen;
+
+	QDEBUG checkb(b, "qaddlist 1");
+
 	/* queue the block */
-	if(q->bfirst)
+	if(q->bfirst != nil)
 		q->blast->next = b;
 	else
 		q->bfirst = b;
-	q->len += blockalloclen(b);
-	q->dlen += blocklen(b);
-	while(b->next)
+
+	len = BALLOC(b);
+	dlen = BLEN(b);
+	while(b->next != nil){
 		b = b->next;
+		QDEBUG checkb(b, "qaddlist 2");
+
+		len += BALLOC(b);
+		dlen += BLEN(b);
+	}
 	q->blast = b;
+	q->len += len;
+	q->dlen += dlen;
+	return dlen;
 }
 
 /*
@@ -892,77 +801,45 @@
 	b = q->bfirst;
 	if(b == nil)
 		return nil;
+	QDEBUG checkb(b, "qremove");
 	q->bfirst = b->next;
 	b->next = nil;
 	q->dlen -= BLEN(b);
 	q->len -= BALLOC(b);
-	QDEBUG checkb(b, "qremove");
 	return b;
 }
 
 /*
  *  copy the contents of a string of blocks into
- *  memory.  emptied blocks are freed.  return
- *  pointer to first unconsumed block.
+ *  memory from an offset. blocklist kept unchanged.
+ *  return number of copied bytes.
  */
-Block*
-bl2mem(uchar *p, Block *b, int n)
+long
+readblist(Block *b, uchar *p, long n, long o)
 {
-	int i;
-	Block *next;
+	long m, r;
 
-	for(; b != nil; b = next){
-		i = BLEN(b);
-		if(i > n){
-			memmove(p, b->rp, n);
-			b->rp += n;
-			return b;
+	r = 0;
+	while(n > 0 && b != nil){
+		m = BLEN(b);
+		if(o >= m)
+			o -= m;
+		else {
+			m -= o;
+			if(n < m)
+				m = n;
+			memmove(p, b->rp + o, m);
+			p += m;
+			r += m;
+			n -= m;
+			o = 0;
 		}
-		memmove(p, b->rp, i);
-		n -= i;
-		p += i;
-		b->rp += i;
-		next = b->next;
-		freeb(b);
+		b = b->next;
 	}
-	return nil;
+	return r;
 }
 
 /*
- *  copy the contents of memory into a string of blocks.
- *  return nil on error.
- */
-Block*
-mem2bl(uchar *p, int len)
-{
-	int n;
-	Block *b, *first, **l;
-
-	first = nil;
-	l = &first;
-	if(waserror()){
-		freeblist(first);
-		nexterror();
-	}
-	do {
-		n = len;
-		if(n > Maxatomic)
-			n = Maxatomic;
-
-		*l = b = allocb(n);
-	/*	setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); */
-		memmove(b->wp, p, n);
-		b->wp += n;
-		p += n;
-		len -= n;
-		l = &b->next;
-	} while(len > 0);
-	poperror();
-
-	return first;
-}
-
-/*
  *  put a block back to the front of the queue
  *  called with q ilocked
  */
@@ -978,6 +855,35 @@
 }
 
 /*
+ *  cut off n bytes from the end of *h. return a new
+ *  block with the tail and change *h to refer to the
+ *  head.
+ */
+static Block*
+splitblock(Block **h, int n)
+{
+	Block *a, *b;
+	int m;
+
+	a = *h;
+	m = BLEN(a) - n;
+	if(m < n){
+		b = allocb(m);
+		memmove(b->wp, a->rp, m);
+		b->wp += m;
+		a->rp += m;
+		*h = b;
+		return a;
+	} else {
+		b = allocb(n);
+		a->wp -= n;
+		memmove(b->wp, a->wp, n);
+		b->wp += n;
+		return b;
+	}
+}
+
+/*
  *  flow control, get producer going again
  *  called with q ilocked
  */
@@ -996,7 +902,7 @@
 
 	/* wakeup flow controlled writers */
 	if(dowakeup){
-		if(q->kick)
+		if(q->kick != nil)
 			q->kick(q->arg);
 		wakeup(&q->wr);
 	}
@@ -1008,7 +914,7 @@
 Block*
 qbread(Queue *q, int len)
 {
-	Block *b, *nb;
+	Block *b;
 	int n;
 
 	qlock(&q->rlock);
@@ -1036,24 +942,21 @@
 	n = BLEN(b);
 
 	/* split block if it's too big and this is not a message queue */
-	nb = b;
 	if(n > len){
-		if((q->state&Qmsg) == 0){
-			n -= len;
-			b = allocb(n);
-			memmove(b->wp, nb->rp+len, n);
-			b->wp += n;
-			qputback(q, b);
-		}
-		nb->wp = nb->rp + len;
+		n -= len;
+		if((q->state & Qmsg) == 0)
+			qputback(q, splitblock(&b, n));
+		else
+			b->wp -= n;
 	}
 
 	/* restart producer */
 	qwakeup_iunlock(q);
 
-	poperror();
 	qunlock(&q->rlock);
-	return nb;
+	poperror();
+
+	return b;
 }
 
 /*
@@ -1063,7 +966,7 @@
 long
 qread(Queue *q, void *vp, int len)
 {
-	Block *b, *first, **l;
+	Block *b, *first, **last;
 	int m, n;
 
 	qlock(&q->rlock);
@@ -1088,32 +991,29 @@
 	}
 
 	/* if we get here, there's at least one block in the queue */
+	last = &first;
 	if(q->state & Qcoalesce){
 		/* when coalescing, 0 length blocks just go away */
 		b = q->bfirst;
-		if(BLEN(b) <= 0){
+		m = BLEN(b);
+		if(m <= 0){
 			freeb(qremove(q));
 			goto again;
 		}
 
 		/*  grab the first block plus as many
-		 *  following blocks as will completely
+		 *  following blocks as will partially
 		 *  fit in the read.
 		 */
 		n = 0;
-		l = &first;
-		m = BLEN(b);
 		for(;;) {
-			*l = qremove(q);
-			l = &b->next;
+			*last = qremove(q);
 			n += m;
-
-			b = q->bfirst;
-			if(b == nil)
+			if(n >= len || q->bfirst == nil)
 				break;
+			last = &b->next;
+			b = q->bfirst;
 			m = BLEN(b);
-			if(n+m > len)
-				break;
 		}
 	} else {
 		first = qremove(q);
@@ -1120,25 +1020,24 @@
 		n = BLEN(first);
 	}
 
-	/* copy to user space outside of the ilock */
-	iunlock(&q->lk);
-	b = bl2mem(vp, first, len);
-	ilock(&q->lk);
+	/* split last block if it's too big and this is not a message queue */
+	if(n > len && (q->state & Qmsg) == 0)
+		qputback(q, splitblock(last, n - len));
 
-	/* take care of any left over partial block */
-	if(b != nil){
-		n -= BLEN(b);
-		if(q->state & Qmsg)
-			freeb(b);
-		else
-			qputback(q, b);
-	}
-
 	/* restart producer */
 	qwakeup_iunlock(q);
 
-	poperror();
 	qunlock(&q->rlock);
+	poperror();
+
+	if(waserror()){
+		freeblist(first);
+		nexterror();
+	}
+	n = readblist(first, vp, len, 0);
+	freeblist(first);
+	poperror();
+
 	return n;
 }
 
@@ -1150,8 +1049,31 @@
 	return q->len < q->limit || (q->state & Qclosed);
 }
 
-ulong noblockcnt;
+/*
+ *  flow control, wait for queue to get below the limit
+ */
+static void
+qflow(Queue *q)
+{
+	for(;;){
+		if(q->noblock || qnotfull(q))
+			break;
 
+		ilock(&q->lk);
+		q->state |= Qflow;
+		iunlock(&q->lk);
+
+		qlock(&q->wlock);
+		if(waserror()){
+			qunlock(&q->wlock);
+			nexterror();
+		}
+		sleep(&q->wr, qnotfull, q);
+		qunlock(&q->wlock);
+		poperror();
+	}
+}
+
 /*
  *  add a block to a queue obeying flow control
  */
@@ -1158,24 +1080,19 @@
 long
 qbwrite(Queue *q, Block *b)
 {
-	int n, dowakeup;
+	int len, dowakeup;
 
-	n = BLEN(b);
-
-	if(q->bypass){
+	if(q->bypass != nil){
+		len = blocklen(b);
 		(*q->bypass)(q->arg, b);
-		return n;
+		return len;
 	}
 
 	dowakeup = 0;
-	qlock(&q->wlock);
 	if(waserror()){
-		if(b != nil)
-			freeb(b);
-		qunlock(&q->wlock);
+		freeblist(b);
 		nexterror();
 	}
-
 	ilock(&q->lk);
 
 	/* give up if the queue is closed */
@@ -1184,29 +1101,16 @@
 		error(q->err);
 	}
 
-	/* if nonblocking, don't queue over the limit */
-	if(q->len >= q->limit){
-		if(q->noblock){
-			iunlock(&q->lk);
-			freeb(b);
-			noblockcnt += n;
-			qunlock(&q->wlock);
-			poperror();
-			return n;
-		}
+	/* don't queue over the limit */
+	if(q->len >= q->limit && q->noblock){
+		iunlock(&q->lk);
+		poperror();
+		len = blocklen(b);
+		freeblist(b);
+		return len;
 	}
 
-	/* queue the block */
-	if(q->bfirst)
-		q->blast->next = b;
-	else
-		q->bfirst = b;
-	q->blast = b;
-	b->next = 0;
-	q->len += BALLOC(b);
-	q->dlen += n;
-	QDEBUG checkb(b, "qbwrite");
-	b = nil;
+	len = qaddlist(q, b);
 
 	/* make sure other end gets awakened */
 	if(q->state & Qstarve){
@@ -1214,49 +1118,26 @@
 		dowakeup = 1;
 	}
 	iunlock(&q->lk);
+	poperror();
 
 	/*  get output going again */
-	if(q->kick && (dowakeup || (q->state&Qkick)))
+	if(q->kick != nil && (dowakeup || (q->state&Qkick)))
 		q->kick(q->arg);
 
 	/* wakeup anyone consuming at the other end */
-	if(dowakeup){
+	if(dowakeup)
 		wakeup(&q->rr);
 
-		/* if we just wokeup a higher priority process, let it run */
 	/*
-		p = wakeup(&q->rr);
-		if(p != nil && p->priority > up->priority)
-			sched();
+	 *  flow control, before allowing the process to continue and
+	 *  queue more. We do this here so that postnote can only
+	 *  interrupt us after the data has been queued.  This means that
+	 *  things like 9p flushes and ssl messages will not be disrupted
+	 *  by software interrupts.
 	 */
-	}
+	qflow(q);
 
-	/*
-	 *  flow control, wait for queue to get below the limit
-	 *  before allowing the process to continue and queue
-	 *  more.  We do this here so that postnote can only
-	 *  interrupt us after the data has been queued.  This
-	 *  means that things like 9p flushes and ssl messages
-	 *  will not be disrupted by software interrupts.
-	 *
-	 *  Note - this is moderately dangerous since a process
-	 *  that keeps getting interrupted and rewriting will
-	 *  queue infinite crud.
-	 */
-	for(;;){
-		if(q->noblock || qnotfull(q))
-			break;
-
-		ilock(&q->lk);
-		q->state |= Qflow;
-		iunlock(&q->lk);
-		sleep(&q->wr, qnotfull, q);
-	}
-	USED(b);
-
-	qunlock(&q->wlock);
-	poperror();
-	return n;
+	return len;
 }
 
 /*
@@ -1270,8 +1151,16 @@
 	uchar *p = vp;
 
 	QDEBUG if(!islo())
-		print("qwrite hi %p\n", getcallerpc(&q));
+		print("qwrite hi %#p\n", getcallerpc(&q));
 
+	/* stop queue bloat before allocating blocks */
+	if(q->len/2 >= q->limit && q->noblock == 0 && q->bypass == nil){
+		while(waserror())
+			;
+		qflow(q);
+		poperror();
+	}
+
 	sofar = 0;
 	do {
 		n = len-sofar;
@@ -1279,7 +1168,6 @@
 			n = Maxatomic;
 
 		b = allocb(n);
-	/*	setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]); */
 		if(waserror()){
 			freeb(b);
 			nexterror();
@@ -1288,9 +1176,7 @@
 		poperror();
 		b->wp += n;
 
-		qbwrite(q, b);
-
-		sofar += n;
+		sofar += qbwrite(q, b);
 	} while(sofar < len && (q->state & Qmsg) == 0);
 
 	return len;
@@ -1323,15 +1209,17 @@
 
 		ilock(&q->lk);
 
-		QDEBUG checkb(b, "qiwrite");
-		if(q->bfirst)
-			q->blast->next = b;
-		else
-			q->bfirst = b;
-		q->blast = b;
-		q->len += BALLOC(b);
-		q->dlen += n;
+		/* we use an artificially high limit for kernel prints since anything
+		 * over the limit gets dropped
+		 */
+		if((q->state & Qclosed) != 0 || q->len/2 >= q->limit){
+			iunlock(&q->lk);
+			freeb(b);
+			break;
+		}
 
+		qaddlist(q, b);
+
 		if(q->state & Qstarve){
 			q->state &= ~Qstarve;
 			dowakeup = 1;
@@ -1340,7 +1228,7 @@
 		iunlock(&q->lk);
 
 		if(dowakeup){
-			if(q->kick)
+			if(q->kick != nil)
 				q->kick(q->arg);
 			wakeup(&q->rr);
 		}
@@ -1378,9 +1266,9 @@
 	ilock(&q->lk);
 	q->state |= Qclosed;
 	q->state &= ~(Qflow|Qstarve);
-	strcpy(q->err, Ehungup);
+	kstrcpy(q->err, Ehungup, ERRMAX);
 	bfirst = q->bfirst;
-	q->bfirst = 0;
+	q->bfirst = nil;
 	q->len = 0;
 	q->dlen = 0;
 	q->noblock = 0;
@@ -1404,10 +1292,9 @@
 	/* mark it */
 	ilock(&q->lk);
 	q->state |= Qclosed;
-	if(msg == 0 || *msg == 0)
-		strcpy(q->err, Ehungup);
-	else
-		strncpy(q->err, msg, ERRMAX-1);
+	if(msg == nil || *msg == '\0')
+		msg = Ehungup;
+	kstrcpy(q->err, msg, ERRMAX);
 	iunlock(&q->lk);
 
 	/* wake up readers/writers */
@@ -1467,7 +1354,7 @@
 int
 qcanread(Queue *q)
 {
-	return q->bfirst!=0;
+	return q->bfirst != nil;
 }
 
 /*
@@ -1499,7 +1386,7 @@
 	/* mark it */
 	ilock(&q->lk);
 	bfirst = q->bfirst;
-	q->bfirst = 0;
+	q->bfirst = nil;
 	q->len = 0;
 	q->dlen = 0;
 	iunlock(&q->lk);
@@ -1507,7 +1394,7 @@
 	/* free queued blocks */
 	freeblist(bfirst);
 
-	/* wake up readers/writers */
+	/* wake up writers */
 	wakeup(&q->wr);
 }
 
@@ -1515,10 +1402,4 @@
 qfull(Queue *q)
 {
 	return q->state & Qflow;
-}
-
-int
-qstate(Queue *q)
-{
-	return q->state;
 }