~kris/9p

9hist

5ec26afdbaa0cc7d7464cdae45ddbb39394e4723 — David du Colombier 35 years ago f05569f
Plan 9 from Bell Labs 1991-08-11
5 files changed, 321 insertions(+), 152 deletions(-)

M pc/devfloppy.c
M pc/devhard.c
M pc/io.h
M port/devmnt.c
M port/stream.c
M pc/devfloppy.c => pc/devfloppy.c +6 -6
@@ 172,7 172,7 @@ Dirtab floppydir[]={
	"fd3data",		{Qdata + 3},	0,	0600,
	"fd3struct",		{Qstruct + 3},	8,	0600,
};
#define NFDIR	(sizeof(floppydir)/sizeof(Dirtab))
#define NFDIR	2	/* directory entries/drive */

#define k64(x) (((ulong)(x))>>16)
void


@@ 201,7 201,7 @@ floppyreset(void)
	for(dp = floppy.d; dp < &floppy.d[conf.nfloppy]; dp++){
		dp->dev = dp - floppy.d;
		dp->t = &floppytype[0];		/* default type */
		floppydir[2*dp->dev].length = dp->t->cap;
		floppydir[NFDIR*dp->dev].length = dp->t->cap;
		dp->motoron = 1;
		dp->cyl = -1;		/* because we don't know */
		motoroff(dp);


@@ 244,19 244,19 @@ floppyclone(Chan *c, Chan *nc)
int
floppywalk(Chan *c, char *name)
{
	return devwalk(c, name, floppydir, NFDIR, devgen);
	return devwalk(c, name, floppydir, conf.nfloppy*NFDIR, devgen);
}

void
floppystat(Chan *c, char *dp)
{
	devstat(c, dp, floppydir, NFDIR, devgen);
	devstat(c, dp, floppydir, conf.nfloppy*NFDIR, devgen);
}

Chan*
floppyopen(Chan *c, int omode)
{
	return devopen(c, omode, floppydir, NFDIR, devgen);
	return devopen(c, omode, floppydir, conf.nfloppy*NFDIR, devgen);
}

void


@@ 304,7 304,7 @@ floppyread(Chan *c, void *a, long n)
	uchar *aa = a;

	if(c->qid.path == CHDIR)
		return devdirread(c, a, n, floppydir, NFDIR, devgen);
		return devdirread(c, a, n, floppydir, conf.nfloppy*NFDIR, devgen);

	rv = 0;
	dp = &floppy.d[c->qid.path & ~Qmask];

M pc/devhard.c => pc/devhard.c +271 -128
@@ 14,31 14,35 @@ enum
{
	/* ports */
	Pbase=		0x1F0,
	Pdata=		Pbase+0,	/* data port (16 bits) */
	Perror=		Pbase+1,	/* error port */
	Pcount=		Pbase+2,	/* sector count port */
	Psector=	Pbase+3,	/* sector number port */
	Pcyllsb=	Pbase+4,	/* least significant byte cylinder # */
	Pcylmsb=	Pbase+5,	/* most significant byte cylinder # */
	Pdh=		Pbase+6,	/* drive/head port */
	Pstatus=	Pbase+7,	/* status port */
	Pdata=		0,	/* data port (16 bits) */
	Perror=		1,	/* error port (read) */
	Pbmode=		1,	/* buffer mode port (write) */
	Pcount=		2,	/* sector count port */
	Psector=	3,	/* sector number port */
	Pcyllsb=	4,	/* least significant byte cylinder # */
	Pcylmsb=	5,	/* most significant byte cylinder # */
	Pdh=		6,	/* drive/head port */
	Pstatus=	7,	/* status port (read) */
	 Sbusy=		 (1<<7),
	 Sready=	 (1<<6),
	 Sdrq=		 (1<<5),
	 Serr=		 (1<<0),
	Pcmd=		Pbase+7,	/* cmd port */
	Pcmd=		7,	/* cmd port (write) */

	/* commands */
	Crecal=		0x10,
	Cread=		0x20,
	Cwrite=		0x30,
	Cident=		0xEC,
	Csetbuf=	0xEF,

	/* file types */
	Qdir=		0,
	Qdata=		(1<<1),
	Qstruct=	(2<<1),
	Qmask=		(3<<1),
	Qdata=		(1<<4),
	Qstruct=	(2<<4),
	Qmask=		(3<<4),

	Maxxfer=	4*1024,		/* maximum transfer size/cmd */
};

/*


@@ 84,7 88,8 @@ struct Ident
 */
struct Drive
{
	int	dev;
	Controller *cp;
	int	drive;
	int	confused;	/* needs to be recalibrated (or worse) */
	int	online;



@@ 94,10 99,7 @@ struct Drive
	int	heads;		/* heads/cyl */
	long	cyl;		/* cylinders/drive */

	int	tcyl;		/* target cylinder */
	int	thead;		/* target head */
	int	tsec;		/* target sector */
	long	len;		/* size of xfer */
	Ident	id;		/* disk properties */
};

/*


@@ 107,67 109,109 @@ struct Controller
{
	QLock;			/* exclusive access to the drive */

	int	intr;		/* true if interrupt occured */
	int	status;		/* status of last interupt */
	Rendez	r;		/* wait here for command termination */
	int	confused;	/* needs to be recalibrated (or worse) */
	int	pbase;		/* base port */

	Drive	*d;
	Ident	id;
	/*
	 *  current operation
	 */
	int	cmd;		/* current command */
	Rendez	r;		/* wait here for command termination */
	char	*buf;		/* xfer buffer */
	int	tcyl;		/* target cylinder */
	int	thead;		/* target head */
	int	tsec;		/* target sector */
	int	tbyte;		/* target byte */
	int	len;		/* length of transfer (bytes) */
	int	secs;		/* sectors to be xferred */
	int	sofar;		/* bytes transferred so far */
	int	status;
	int	error;
	Drive	*dp;		/* drive being accessed */
};

Controller	hard;

Dirtab harddir[]={
	"hddata",		{Qdata},	0,	0600,
	"hdstruct",		{Qstruct},	8,	0600,
};
#define NHDIR	(sizeof(harddir)/sizeof(Dirtab))
Controller	*hardc;
Drive		*hard;
Dirtab 		*harddir;
#define NHDIR	2	/* directory entries/drive */

static void	hardintr(Ureg*);
static long	hardxfer(Drive*, int, void*, long, long);
static long	hardident(Drive*);
static void	hardpos(Drive*, long);
static void	hardsetbuf(Drive*, int);

/*
 *  we assume drives 0 and 1 are on the first controller, 2 and 3 on the
 *  second, etc.
 */
void
hardreset(void)
{
	Drive *dp;

	hard.d = ialloc(conf.nhard * sizeof(Drive), 0);
	for(dp = hard.d; dp < &hard.d[conf.nhard]; dp++){
		dp->dev = dp - hard.d;
	Controller *cp;
	int drive;
	Dirtab *dir;

	hard = ialloc(conf.nhard * sizeof(Drive), 0);
	hardc = ialloc(((conf.nhard+1)/2 + 1) * sizeof(Controller), 0);
	dir = harddir = ialloc(NHDIR * conf.nhard * sizeof(Dirtab), 0);
	
	for(drive = 0; drive < conf.nhard; drive++){
		dp = &hard[drive];
		cp = &hardc[drive/2];
		dp->drive = drive&1;
		dp->online = 0;
		dp->cp = cp;
		if((drive&1) == 0){
			cp->buf = ialloc(Maxxfer, 0);
			cp->cmd = 0;
			cp->pbase = Pbase + (cp-hardc)*8;	/* BUG!! guessing */
			setvec(Hardvec + (cp-hardc)*8, hardintr); /* BUG!! guessing */
		}
		sprint(harddir[drive*2].name, "hd%ddata", drive);
		dir->length = 0;
		dir->qid.path = Qdata + drive;
		dir->perm = 0600;
		dir++;
		sprint(dir->name, "hd%dstruct", drive);
		dir->length = 8;
		dir->qid.path = Qstruct + drive;
		dir->perm = 0600;
		dir++;
	}

	setvec(Hardvec, hardintr);
}

void
hardinit(void)
{
	qunlock(&hard);
}

/*
 *  Get the characteristics of each drive.  Mark unresponsive ones
 *  off line.
 */
Chan*
hardattach(char *spec)
{
	Drive *dp;

	qlock(&hard);
	for(dp = hard.d; dp < &hard.d[conf.nhard]; dp++){
	for(dp = hard; dp < &hard[conf.nhard]; dp++){
		if(!waserror()){
			hardsetbuf(dp, 1);
			hardident(dp);
			dp->cyl = hard.id.lcyls;
			dp->heads = hard.id.lheads;
			dp->sectors = hard.id.ls2t;
			dp->cyl = dp->id.lcyls;
			dp->heads = dp->id.lheads;
			dp->sectors = dp->id.ls2t;
			dp->bytes = 512;
			dp->cap = dp->bytes * dp->cyl * dp->heads * dp->sectors;
			harddir[NHDIR*dp->drive].length = dp->cap;
print("drive %d online\n", dp - hard);
			dp->online = 1;
			poperror();
		}
		} else
			dp->online = 0;
	}
	qunlock(&hard);

	return devattach('h', spec);
}



@@ 180,19 224,19 @@ hardclone(Chan *c, Chan *nc)
int
hardwalk(Chan *c, char *name)
{
	return devwalk(c, name, harddir, NHDIR, devgen);
	return devwalk(c, name, harddir, conf.nhard*NHDIR, devgen);
}

void
hardstat(Chan *c, char *dp)
{
	devstat(c, dp, harddir, NHDIR, devgen);
	devstat(c, dp, harddir, conf.nhard*NHDIR, devgen);
}

Chan*
hardopen(Chan *c, int omode)
{
	return devopen(c, omode, harddir, NHDIR, devgen);
	return devopen(c, omode, harddir, conf.nhard*NHDIR, devgen);
}

void


@@ 235,10 279,10 @@ hardread(Chan *c, void *a, long n)
	uchar *aa = a;

	if(c->qid.path == CHDIR)
		return devdirread(c, a, n, harddir, NHDIR, devgen);
		return devdirread(c, a, n, harddir, conf.nhard*NHDIR, devgen);

	rv = 0;
	dp = &hard.d[c->qid.path & ~Qmask];
	dp = &hard[c->qid.path & ~Qmask];
	switch ((int)(c->qid.path & Qmask)) {
	case Qdata:
		for(rv = 0; rv < n; rv += i){


@@ 270,7 314,7 @@ hardwrite(Chan *c, void *a, long n)
	uchar *aa = a;

	rv = 0;
	dp = &hard.d[c->qid.path & ~Qmask];
	dp = &hard[c->qid.path & ~Qmask];
	switch ((int)(c->qid.path & Qmask)) {
	case Qdata:
		for(rv = 0; rv < n; rv += i){


@@ 292,113 336,212 @@ hardwrite(Chan *c, void *a, long n)
 *  did an interrupt happen?
 */
static int
interrupted(void *a)
cmddone(void *a)
{
	return hard.intr;
	Controller *cp;

	return cp->cmd == 0;
}

/*
 *  get parameters from the drive
 *  start a disk transfer.  hardintr will performa all the iterative
 *  parts.
 */
static long
hardident(Drive *dp)
{
	hard.intr = 0;
	outb(Pdh, dp->dev<<4);
	outb(Pcmd, Cident);
	sleep(&hard.r, interrupted, 0);
	inss(Pdata, &hard.id, 512/2);
}

static long
hardxfer(Drive *dp, int cmd, void *va, long off, long len)
{
	int secs;
	int i;
	uchar *aa = va;
	Controller *cp;
	int err;
	int lsec;
	int cyl;

	if(off % dp->bytes)
		errors("bad offset");
	if(dp->online == 0)
		errors("disk offline");
	if(len % dp->bytes)
		errors("bad length");
		errors("bad length");	/* BUG - this shouldn't be a problem */
	if(off % dp->bytes)
		errors("bad offset");	/* BUG - this shouldn't be a problem */

	cp = dp->cp;
	qlock(cp);
	if(waserror()){
		qunlock(&hard);
		qunlock(cp);
		nexterror();
	}
	qlock(&hard);
	dp->len = len;
	hardpos(dp, off);
	secs = dp->len/dp->bytes;

	outb(Pcount, secs);
	outb(Psector, dp->tsec);
	outb(Pdh, (1<<5) | (dp->dev<<4) | dp->thead);
	outb(Pcyllsb, dp->tcyl);
	outb(Pcylmsb, dp->tcyl>>8);
	outb(Pcmd, cmd);

	if(cmd == Cwrite)
		outss(Pdata, aa, dp->bytes/2);
	for(i = 0; i < secs; i++){
		hard.intr = 0;
		sleep(&hard.r, interrupted, 0);
		if(hard.status & Serr)
			errors("disk error");
		if(cmd == Cread){
			if((hard.status & Sdrq) == 0)
				panic("disk read");
			inss(Pdata, aa + i*dp->bytes, dp->bytes/2);
		} else {
			if((hard.status & Sdrq) == 0){
				if(i+1 != secs)
					panic("disk write");
			} else
				outss(Pdata, aa + (i+1)*dp->bytes, dp->bytes/2);
		}

	/*
	 *  calculate the physical address of off
	 */
	lsec = off/dp->bytes;
	cp->tcyl = lsec/(dp->sectors*dp->heads);
	cp->tsec = (lsec % dp->sectors) + 1;
	cp->thead = (lsec/dp->sectors) % dp->heads;

	/*
	 *  can't xfer across cylinder boundaries.
	 */
	lsec = (off+len)/dp->bytes;
	cyl = lsec/(dp->sectors*dp->heads);
	if(cyl == cp->tcyl)
		cp->len = len;
	else
		cp->len = cyl*dp->sectors*dp->heads*dp->bytes - off;

	/*
	 *  wait for the controller to accept commands
	 */
	while(inb(cp->pbase+Pstatus) & Sbusy)
		;

	/*
	 *  start the transfer
	 */
	cp->secs = cp->len/dp->bytes;
	cp->sofar = 0;
	cp->cmd = cmd;
	outb(cp->pbase+Pcount, cp->secs);
	outb(cp->pbase+Psector, cp->tsec);
	outb(cp->pbase+Pdh, (1<<5) | (dp->drive<<4) | cp->thead);
	outb(cp->pbase+Pcyllsb, cp->tcyl);
	outb(cp->pbase+Pcylmsb, cp->tcyl>>8);
	outb(cp->pbase+Pcmd, cmd);

	if(cmd == Cwrite){
		memmove(cp->buf, va, cp->len);
		outss(Pdata, cp->buf, dp->bytes/2);
	}
	qunlock(&hard);
	sleep(&cp->r, cmddone, cp);
	if(cp->status & Serr){
print("hd%d err: status %lux, err %lux\n", dp-hard, cp->status, cp->error);
print("\ttcyl %d, tsec %d, thead %d\n", cp->tcyl, cp->tsec, cp->thead);
print("\tsecs %d, sofar %d\n", cp->secs, cp->sofar);
		errors("disk I/O error");
	}
	if(cmd == Cread)
		memmove(va, cp->buf, cp->len);

	poperror();
	qunlock(cp);
	return cp->len;
}

/*
 *  take/clear a disk interrupt
 *  set read ahead mode (1 == on, 0 == off)
 */
static void
hardintr(Ureg *ur)
hardsetbuf(Drive *dp, int on)
{
	hard.status = inb(Pstatus);
	if(hard.status & Sbusy)
		panic("disk busy");
	hard.intr = 1;
	wakeup(&hard.r);
	Controller *cp = dp->cp;

	qlock(cp);
	if(waserror()){
		qunlock(cp);
		nexterror();
	}

	outb(cp->pbase+Pbmode, on ? 0xAA : 0x55);
	outb(cp->pbase+Pdh, (1<<5) | dp->drive<<4);
	outb(cp->pbase+Pcmd, Csetbuf);

	poperror();
	qunlock(cp);
}

/*
 *  calculate physical address of a logical byte offset into the disk
 *
 *  truncate dp->len if it crosses a cylinder boundary
 *  get parameters from the drive
 */
static void
hardpos(Drive *dp, long off)
static long
hardident(Drive *dp)
{
	int lsec;
	int end;
	int cyl;
	Controller *cp;

	lsec = off/dp->bytes;
	dp->tcyl = lsec/(dp->sectors*dp->heads);
	dp->tsec = (lsec % dp->sectors) + 1;
	dp->thead = (lsec/dp->sectors) % dp->heads;
	cp = dp->cp;
	qlock(cp);
	if(waserror()){
		qunlock(cp);
		nexterror();
	}

	cp->len = 512;
	cp->secs = 1;
	cp->sofar = 0;
	cp->cmd = Cident;
	outb(cp->pbase+Pdh, (1<<5) | dp->drive<<4);
	outb(cp->pbase+Pcmd, Cident);
	sleep(&cp->r, cmddone, cp);
	if(cp->status & Serr){
print("bad disk magic\n");
		errors("disk I/O error");
	}
	memmove(&dp->id, cp->buf, cp->len);

	if(dp->id.magic != 0xA5A){
print("bad disk magic\n");
		errors("bad disk magic");
	}

if((dp->id.interface & 0x4000) == 0)
	print("lookaheads disabled\n");	

	poperror();
	qunlock(cp);
}

/*
 *  we get an interrupt for every sector transferred
 */
static void
hardintr(Ureg *ur)
{
	Controller *cp;

	/*
	 *  can't read across cylinder boundaries.
	 *  if so, decrement the bytes to be read.
 	 *  BUG!! if there is ever more than one controller, we need a way to
	 *	  distinguish which interrupted
	 */
	lsec = (off+dp->len)/dp->bytes;
	cyl = lsec/(dp->sectors*dp->heads);
	if(cyl != dp->tcyl){
		dp->len -= (lsec % dp->sectors)*dp->bytes;
		dp->len -= ((lsec/dp->sectors) % dp->heads)*dp->bytes*dp->sectors;
	cp = &hardc[0];

	cp->status = inb(cp->pbase+Pstatus);
	switch(cp->cmd){
	case Cwrite:
		if(cp->status & Serr){
			cp->cmd = 0;
			cp->error = inb(cp->pbase+Perror);
			wakeup(&cp->r);
			return;
		}
		cp->sofar++;
		if(cp->sofar != cp->secs){
			while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
				;
			outss(cp->pbase+Pdata, &cp->buf[cp->sofar*cp->dp->bytes],
				cp->dp->bytes/2);
		} else{
			cp->cmd = 0;
			wakeup(&cp->r);
		}
		break;
	case Cread:
	case Cident:
		if(cp->status & Serr){
			cp->cmd = 0;
			cp->error = inb(cp->pbase+Perror);
			wakeup(&cp->r);
			return;
		}
		while((inb(cp->pbase+Pstatus) & Sdrq) == 0)
			;
		inss(cp->pbase+Pdata, &cp->buf[cp->sofar*cp->dp->bytes],
			cp->dp->bytes/2);
		cp->sofar++;
		if(cp->sofar == cp->secs){
			cp->cmd = 0;
			wakeup(&cp->r);
		}
		break;
	default:
		print("wierd disk interrupt\n");
		break;
	}
	wakeup(&cp->r);
}


M pc/io.h => pc/io.h +1 -1
@@ 7,8 7,8 @@ enum
	Int0vec=	16,		/* first 8259 */
	 Clockvec=	Int0vec+0,	/*  clock interrupts */
	 Kbdvec=	Int0vec+1,	/*  keyboard interrupts */
	 Uart0vec=	Int0vec+4,	/*  serial line */
	 Uart1vec=	Int0vec+3,	/*  modem line */
	 Uart0vec=	Int0vec+4,	/*  serial line */
	 Floppyvec=	Int0vec+6,	/*  floppy interrupts */
	Int1vec=	Int0vec+8,	/* second 8259 */
	 Mousevec=	Int1vec+4,	/*  mouse interrupt */

M port/devmnt.c => port/devmnt.c +24 -2
@@ 37,6 37,7 @@ typedef struct Mntbuf Mntbuf;
struct Mntbuf
{
	Mntbuf	*next;
int pid;
	char	buf[BUFSIZE+BITROUND]; 	/* BUG */
};



@@ 50,6 51,7 @@ struct Mnthdr
{
	Mnthdr	*next;		/* in free list or writers list */
	Mnthdr	*prev;		/* in writers list only */
int pid;
	short	active;
	short	flushing;	/* a Tflush has been sent */
	Fcall	thdr;


@@ 93,6 95,7 @@ loop:
	lock(&mntbufalloc);
	if(mb = mntbufalloc.free){		/* assign = */
		mntbufalloc.free = mb->next;
mb->pid = u->p->pid;
		unlock(&mntbufalloc);
		return mb;
	}


@@ 109,6 112,7 @@ loop:
void
mbfree(Mntbuf *mb)
{
if(mb->pid != u->p->pid)print("mbfree pid\n");
	lock(&mntbufalloc);
	mb->next = mntbufalloc.free;
	mntbufalloc.free = mb;


@@ 127,7 131,9 @@ loop:
if(mh->active) print("mh->active\n");
if(mh->flushing) print("mh->flushing\n");
if(mh->mbr) print("mh->mbr\n");
if(mh->readreply) print("mh->readreply\n");
		mh->mbr = 0;
mh->pid = u->p->pid;
		unlock(&mnthdralloc);
		return mh;
	}


@@ 144,6 150,7 @@ if(mh->mbr) print("mh->mbr\n");
void
mhfree(Mnthdr *mh)
{
if(mh->pid != u->p->pid)print("mhfree pid %d\n", mh->flushing);
	if(mh->flushing)
		return;
	mh->active = 0;


@@ 623,6 630,7 @@ mntflush(Mnt *m, Mnthdr *omh)	/* queue is unlocked */
	Mnthdr *mh;

	if(omh->thdr.type == Tflush){
print("flush flush\n");
		omh->flushing = 0;
		return;
	}


@@ 650,8 658,10 @@ mnterrdequeue(Mnt *m, Mnthdr *mh)	/* queue is unlocked */
	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 */
{ Mnthdr *h; for(h=q->writer; h; h=h->next) if(h->p==u->p)print("reader and writer error\n"); }
		w = q->writer;
		if(w){
			mntwunlink(q, w);


@@ 686,9 696,11 @@ mntxmit(Mnt *m, Mnthdr *mh)
	mbw = mballoc();
	if(waserror()){			/* 1 */
		if(mh->mbr){
if(mh->mbr->pid != u->p->pid) print("top waserror\n");
			mbfree(mh->mbr);
			mh->mbr = 0;
		}
if(mbw->pid != u->p->pid) print("top waserror mbw\n");
		mbfree(mbw);
		nexterror();
	}


@@ 839,6 851,7 @@ mntxmit(Mnt *m, Mnthdr *mh)
		if(tag<0 || tag>=conf.nmnthdr){
			print("unknown tag %d\n", tag);
	FreeRead:
if(mh->mbr->pid != u->p->pid) print("FreeRead\n");
			mbfree(mh->mbr);
			mh->mbr = 0;
			goto Read;


@@ 848,7 861,8 @@ mntxmit(Mnt *m, Mnthdr *mh)
			goto FreeRead;
		if(mh->rhdr.type != Rerror)
		if(mh->rhdr.type != w->thdr.type+1){
			print(" t%c ", devchar[m->q->msg->type]);
			print(" t%c %d %d ", devchar[m->q->msg->type],
				mh->rhdr.type, w->thdr.type+1);
			goto FreeRead;
		}
		w->mbr = mh->mbr;


@@ 856,6 870,9 @@ mntxmit(Mnt *m, Mnthdr *mh)
		memmove(&w->rhdr, &mh->rhdr, sizeof mh->rhdr);
		mntwunlink(q, w);
		w->readreply = 1;
lock(&w->r);
if(w->r.p) w->mbr->pid = w->p->pid;
unlock(&w->r);
		wakeup(&w->r);
		goto Read;
	}else{


@@ 877,8 894,12 @@ mntxmit(Mnt *m, Mnthdr *mh)
		USED(qlocked);
		qlock(q);
		qlocked = 1;
		if(q->reader == u->p)	/* i got promoted */
		mh->readreply = 0;
		if(q->reader == u->p){	/* i got promoted */
{ Mnthdr *h; for(h=q->writer; h; h=h->next) if(h->p==u->p)print("reader and writer promotion\n"); }
			goto Read;
}
if(mh->mbr->pid != u->p->pid) print("after promotion %d\n", mh->thdr.type);
		mh->active = 0;
		USED(qlocked);
		qunlock(q);


@@ 908,6 929,7 @@ mntxmit(Mnt *m, Mnthdr *mh)
			error(Ebadcnt);
		memmove(mh->thdr.data, mh->rhdr.data, mh->rhdr.count);
	}
if(mh->mbr->pid != u->p->pid) print("tail\n");
	mbfree(mh->mbr);
	mh->mbr = 0;
	mbfree(mbw);

M port/stream.c => port/stream.c +19 -15
@@ 1094,6 1094,7 @@ long
streamread(Chan *c, void *vbuf, long n)
{
	Block *bp;
	Block *tofree;
	Stream *s;
	Queue *q;
	int left, i;


@@ 1108,13 1109,18 @@ streamread(Chan *c, void *vbuf, long n)
	s = c->stream;
	left = n;
	qlock(&s->rdlock);
	tofree = 0;
	if(waserror()){
		/*
		 *  notes will flush the rest of any partially
		 *  read message.
		 *  put any partially read message back into the
		 *  queue
		 */
		if(n != left)
			s->flushmsg = 1;
		while(tofree){
			bp = tofree;
			tofree = bp->next;
			bp->next = 0;
			putbq(q, bp);
		}
		qunlock(&s->rdlock);
		nexterror();
	}


@@ 1139,23 1145,15 @@ streamread(Chan *c, void *vbuf, long n)
			continue;
		}

		if(s->flushmsg){
			if(bp->flags & S_DELIM)
				s->flushmsg = 0;
			freeb(bp);
			continue;
		}

		i = BLEN(bp);
		if(i <= left){
			memmove(buf, bp->rptr, i);
			left -= i;
			buf += i;
			if(bp->flags & S_DELIM){
				freeb(bp);
			bp->next = tofree;
			tofree = bp;
			if(bp->flags & S_DELIM)
				break;
			} else
				freeb(bp);
		} else {
			memmove(buf, bp->rptr, left);
			bp->rptr += left;


@@ 1164,6 1162,12 @@ streamread(Chan *c, void *vbuf, long n)
		}
	}

	/*
	 *  free completely read blocks
	 */
	if(tofree)
		freeb(tofree);

	qunlock(&s->rdlock);
	poperror();
	return n - left;