Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/port/qio.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname port/qio.c 1993/0526
## diff -e /dev/null /n/fornaxdump/1993/0526/sys/src/brazil/port/qio.c
0a
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

typedef struct Chunk	Chunk;
typedef	struct Chunkl	Chunkl;
typedef	struct Arena	Arena;

enum
{
	Minpow= 7,
	Maxpow=	12,
};

struct Chunk
{
	Chunk	*next;
};

struct Chunkl
{
	Lock;
	Chunk	*first;
	int	have;
	int	goal;
	int	hist;
};

struct Arena
{
	Chunkl	alloc[Maxpow+1];
	Chunkl	freed;
	Rendez r;
};

static Arena arena;

/*
 *  Manage interrupt level memory allocation.
 */
static void
iallockproc(void *arg)
{
	Chunk *p, *first, **l;
	Chunkl *cl;
	int pow, x, i;

	USED(arg);
	for(;;){
		tsleep(&arena.r, return0, 0, 500);

		/* really free what was freed at interrupt level */
		cl = &arena.freed;
		if(cl->first){
			x = splhi();
			lock(cl);
			first = cl->first;
			cl->first = 0;
			unlock(cl);
			splx(x);
	
			for(; first; first = p){
				p = first->next;
				free(first);
			}
		}

		/* make sure we have blocks available for interrupt level */
		for(pow = Minpow; pow <= Maxpow; pow++){
			cl = &arena.alloc[pow];

			/*
			 *  if we've been ahead of the game for a while
			 *  start giving blocks back to the general pool
			 */
			if(cl->have >= cl->goal){
				cl->hist = ((cl->hist<<1) | 1) & 0xff;
				if(cl->hist == 0xff && cl->goal > 8)
					cl->goal--;
				continue;
			} else
				cl->hist <<= 1;

			/*
			 *  increase goal if we've been drained, decrease
			 *  goal if we've had lots of blocks twice in a row.
			 */
			if(cl->have == 0)
				cl->goal += cl->goal>>2;

			first = 0;
			l = &first;
			for(i = x = cl->goal - cl->have; x > 0; x--){
				p = malloc(1<<pow);
				if(p == 0)
					break;
				*l = p;
				l = &p->next;
			}
			if(first){
				x = splhi();
				lock(cl);
				*l = cl->first;
				cl->first = first;
				cl->have += i;
				unlock(cl);
				splx(x);
			}
		}
	}
}

void
iallocinit(void)
{
	int pow;
	Chunkl *cl;

	for(pow = Minpow; pow <= Maxpow; pow++){
		cl = &arena.alloc[pow];
		cl->goal = Maxpow-pow + 4;
	}

	/* start garbage collector */
	kproc("iallockproc", iallockproc, 0);
}

Block*
iallocb(int size)
{
	int pow;
	Chunkl *cl;
	Chunk *p;
	Block *b;

	size += sizeof(Block);
	for(pow = Minpow; pow <= Maxpow; pow++)
		if(size <= (1<<pow)){
			cl = &arena.alloc[pow];
			lock(cl);
			p = cl->first;
			if(p){
				cl->have--;
				cl->first = p->next;
			}
			unlock(cl);
			b = (Block *)p;
			b->base = (uchar*)(b+1);
			b->wp = b->rp = b->base;
			b->lim = b->base + (1<<pow) - sizeof(Block);
			return b;
		}
	panic("iallocb %d\n", size);
	return 0;			/* not reached */
}

void
ifree(void *a)
{
	Chunk *p;
	Chunkl *cl;

	cl = &arena.freed;
	p = a;
	lock(cl);
	p->next = cl->first;
	cl->first = p;
	unlock(cl);
}

/*
 *  allocate queues and blocks
 */
Block*
allocb(int size)
{
	Block *b;

	b = malloc(sizeof(Block) + size);
	if(b == 0)
		exhausted("Blocks");

	b->base = (uchar*)(b+1);
	b->rp = b->wp = b->base;
	b->lim = b->base + size;

	return b;
}

/*
 *  Interrupt level copy out of a queue, return # bytes copied.  If drop is
 *  set, any bytes left in a block afer a consume are discarded.
 */
int
qconsume(Queue *q, uchar *p, int len)
{
	Block *b;
	int n;

	lock(q);
	b = q->bfirst;
	if(b == 0){
		q->state |= Qstarve;
		unlock(q);
		return -1;
	}
	n = BLEN(b);
	if(n < len)
		len = n;
	memmove(p, b->rp, len);
	if((q->state&Qmsg) || len == n)
		q->bfirst = b->next;
	else
		b->rp += len;
	q->len -= len;

	/* wakeup flow controlled writers (with a bit of histeresis) */
	if(q->len+len >= q->limit && q->len < q->limit/2)
		wakeup(&q->r);

	unlock(q);

	if((q->state&Qmsg) || len == n)
		ifree(b);

	return len;
}

static int
qproduce0(Queue *q, uchar *p, int len)
{
	Block *b;
	int n;

	lock(q);
	b = q->rfirst;
	if(b){
		/* hand to waiting receiver */
		n = b->lim - b->wp;
		if(n < len)
			len = n;
		memmove(b->wp, p, len);
		b->wp += len;
		q->rfirst = b->next;
		wakeup(&b->r);
		unlock(q);
		return len;
	}

	/* no waiting receivers, room in buffer? */
	if(q->len >= q->limit){
		unlock(q);
		return -1;
	}

	/* save in buffer */
	b = q->bfirst;
	if((q->state&Qmsg)==0 && b && b->lim-b->wp <= len){
		memmove(b->wp, p, len);
		b->wp += len;
	} else {
		b = iallocb(len);
		if(b == 0){
			unlock(q);
			return -1;
		}
		b->wp += len;
		memmove(b->rp, p, len);
		if(q->bfirst)
			q->blast->next = b;
		else
			q->bfirst = b;
		q->blast = b;
	}
	q->len += len;
	unlock(q);
	return len;
}

int
qproduce(Queue *q, uchar *p, int len)
{
	int n, sofar;

	if(q->state&Qmsg)
		return qproduce0(q, p, len);

	for(sofar = 0; sofar < len; sofar += n){
		n = qproduce0(q, p+sofar, len-sofar);
		if(n < 0)
			break;
	}
	return sofar;
}

/*
 *  called by non-interrupt code
 */
Queue*
qopen(int limit, void (*kick)(void*), void *arg)
{
	Queue *q;

	q = malloc(sizeof(Queue));
	if(q == 0)
		exhausted("Queues");
	q->limit = limit;
	q->kick = kick;
	q->arg = arg;
	q->state = Qmsg;

	return q;
}

static int
bfilled(void *a)
{
	Block *b = a;

	return b->wp - b->rp;
}

long
qread(Queue *q, char *p, int len)
{
	Block *b, *bb;
	int x, n;

	qlock(&q->rlock);

	/* ... to be replaced by a kmapping if need be */
	b = allocb(len);

	x = splhi();
	lock(q);
	bb = q->bfirst;
	if(bb == 0){
		/* wait for our block to be filled */
		if(q->rfirst)
			q->rlast->next = b;
		else
			q->rfirst = b;
		q->rlast = b;
		unlock(q);
		splx(x);
		qunlock(&q->rlock);
		sleep(&b->r, bfilled, b);
		n = BLEN(b);
		memmove(p, b->rp, n);
		free(b);
		return n;
	}

	/* copy from a buffered block */
	q->bfirst = bb->next;
	n = BLEN(bb);
	if(n > len)
		n = len;
	q->len -= n;
	unlock(q);
	splx(x);
	memmove(p, bb->rp, n);
	bb->rp += n;

	/* free it or put it back */
	if(drop || bb->rp == bb->wp)
		free(bb);
	else {
		x = splhi();
		lock(q);
		bb->next = q->bfirst;
		q->bfirst = bb;
		unlock(q);
		splx(x);
	}
	qunlock(&q->rlock);
	free(b);
	return n;
}

static int
qnotfull(void *a)
{
	Queue *q = a;

	return q->len < q->limit;
}

static long
qwrite0(Queue *q, char *p, int len)
{
	Block *b, *bb;
	int x, n;

	b = allocb(len);

	x = splhi();
	lock(q);
	bb = q->rfirst;
	if(bb){
		/* hand to waiting receiver */
		n = bb->lim - bb->wp;
		q->rfirst = bb->next;
		unlock(q);
		splx(x);

		if(n < len)
			len = n;
		memmove(bb->wp, p, len);
		bb->wp += len;
		wakeup(&bb->r);

		free(b);
		return len;
	}
		
	memmove(b->rp, p, len);
	b->wp += len;

	/* flow control */
	if(!qnotfull(q))
		sleep(&q->r, qnotfull, q);

	x = splhi();
	lock(q);
	if(q->bfirst)
		q->blast->next = b;
	else
		q->bfirst = b;
	q->blast = b;
	q->len += len;
	if((q->state & Qstarve) && q->kick){
		q->state &= ~Qstarve;
		(*q->kick)(q->arg);
	}
	unlock(q);
	splx(x);

	return len;
}

long
qwrite(Queue *q, char *p, int len)
{
	int n, sofar;

	qlock(&q->wlock);
	if(waserror()){
		qunlock(&q->wlock);
		nexterror();
	}

	if(q->state&Qmsg){
		sofar = qwrite0(q, p, len);
	} else {
		for(sofar = 0; sofar < len; sofar += n){
			n = qwrite0(q, p+sofar, len-sofar);
			if(n < 0)
				break;
		}
	}

	poperror();
	qunlock(&q->wlock);
	return sofar;
}
.
## diffname port/qio.c 1993/0527
## diff -e /n/fornaxdump/1993/0526/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0527/sys/src/brazil/port/qio.c
468c
	poperror();

	return n;
.
466d
456,463c
	/* flow control */
	sleep(&q->r, qnotfull, q);

	n = qwrite0(q, p, len, b);
	if(n != len){
		/* no readers and we need a buffer */
		i = len - n;
		b = allocb(i);
		memmove(b->wp, p + n, i);
		b->wp += n;
		n += qwrite0(q, p + n, i, b);
.
449a
	/*
	 *  If there are no readers, grab a buffer and copy
	 *  into it before locking anything down.  This
	 *  provides the highest concurrency but we will
	 *  sometimes be wrong: after locking we may either
	 *  have to throw away or allocate one.
	 */
	if(q->rfirst == 0){
		b = allocb(len);
		memmove(b->wp, p, len);
		b->wp += len;
	} else
		b = 0;

	/* ensure atomic writes */
.
448c
	int n, i;
	Block *b;
.
444a
static int
qnotfull(void *a)
{
	Queue *q = a;

	return q->len < q->limit;
}

.
423,425c
	/* buffer what ever is left */
	if(b == 0){
		/* we should have alloc'd, return to qwrite and have it do it */
		unlock(q);
		splx(x);
		qwtoofew++;
		return sofar;
	}
	b->rp += sofar;
.
419,421d
416,417c
		sofar += n;
		if(sofar == len){
			if(b){
				free(b);	/* we were wrong to allocate */
				qwtoomany++;
			}
			return len;
		}
.
410,413c
		n = bb->lim - bb->wp;
		if(n > len-sofar)
			n = len - sofar;
		memmove(bb->wp, p+sofar, n);
		bb->wp += n;
		bb->flag |= Bfilled;
.
405d
402,403c

	sofar = 0;
	while(bb = q->rfirst){
.
398,399c
	/* sync with qconsume/qread */
.
395,396c
	Block *bb;
	int x, n, sofar;
.
393c
qwrite0(Queue *q, char *p, int len, Block *b)
.
389,391d
384,387c
ulong qwtoomany;
ulong qwtoofew;
.
380c
	if(b){
		qrtoomany++;
		free(b);
	}
.
378a

	poperror();
.
368,369c
	/* free it or put it back on the queue */
	if(bb->rp >= bb->wp || (q->state&Qmsg))
.
364a

	/* do this outside of the lock(q)! */
.
353a

.
352a
		poperror();
.
349a
		poperror();

		if(waserror()){
			/* on error, unlink us from the chain */
			x = splhi();
			lock(q);
			l = &q->rfirst;
			for(bb = q->rfirst; bb; bb = bb->next){
				if(b == bb){
					*l = bb->next;
					break;
				} else
					l = &bb->next;
			}
			unlock(q);
			splx(x);
			free(b);
			nexterror();
		}

		/* wait for the producer */
.
341c
		if(b == 0){
			/* we guessed wrong, drop the locks and try again */
			unlock(q);
			splx(x);
			qrtoofew++;
			goto retry;
		}

		/* add ourselves to the list of readers */
.
338a

.
336a
	/* sync with qwrite/qproduce */
.
334,335c
	/*
	 *  If there are no buffered blocks, allocate a block
	 *  for the qproducer/qwrite to fill.  This is
	 *  optimistic and and we will
	 *  sometimes be wrong: after locking we may either
	 *  have to throw away or allocate one.
	 *
	 *  We hope to replace the allocb with a kmap later on.
	 */
retry:
	if(q->bfirst == 0)
		b = allocb(len);
.
332a
	b = 0;
	if(waserror()){
		qunlock(&q->rlock);
		if(b)
			free(b);
		nexterror();
	}
.
329c
	Block *b, *bb, **l;
.
323c
	return b->flag & Bfilled;
.
317a
ulong qrtoomany;
ulong qrtoofew;

.
295c
		sofar += n;
	} while(sofar < len && (q->state & Qmsg) == 0);
.
288,292c
	sofar = 0;
	do {
		n = qproduce0(q, p + sofar, len - sofar);
.
279a

.
271c
		b->flag |= Bfilled;
.
269a
		memmove(b->wp, p, len);
.
263a
		b->flag |= Bfilled;
.
261c
	if((q->state & Qmsg) == 0 && b && b->lim - b->wp <= len){
.
249d
247c
		b->flag |= Bfilled;
.
241a
		q->rfirst = b->next;
		unlock(q);
.
238a

.
237a
	/* sync with qread */
.
226c
	if((q->state & Qmsg) || len == n)
.
214c
	if((q->state & Qmsg) || len == n)
.
203a

.
202a
	/* sync with qwrite */
.
188a
	b->flag = 0;
.
153a
			b->flag = 0;
.
148a
			cl->have--;
			cl->first = p->next;
.
145,147c
			if(p == 0){
				unlock(cl);
				return 0;
.
## diffname port/qio.c 1993/0528
## diff -e /n/fornaxdump/1993/0527/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0528/sys/src/brazil/port/qio.c
572c
	/* wake up readers/writers */
	wakeup(&q->rr);
	wakeup(&q->wr);
}

/*
 *  mark a queue as no longer hung up
 */
void
qreopen(Queue *q)
{
	q->state &= ~Qclosed;
.
569,570c
	/* mark it */
	x = splhi();
	lock(q);
	q->state |= Qclosed;
	unlock(q);
	splx(x);
.
559,567c
/*
 *  Mark a queue as closed.  Wakeup any readers.  Don't remove queued
 *  blocks.
 */
void
qhangup(Queue *q)
{
	int x;
.
556,557c
	/* wake up readers/writers */
	wakeup(&q->rr);
	wakeup(&q->wr);
}
.
529,553c
	/* free queued blocks */
	while(b = bfirst){
		bfirst = b->next;
		free(b);
.
526,527c
	/* mark it */
	x = splhi();
	lock(q);
	q->state |= Qclosed;
	bfirst = q->bfirst;
	q->bfirst = 0;
	unlock(q);
	splx(x);
.
524c
	int x;
	Block *b, *bfirst;
.
521,522c
/*
 *  Mark a queue as closed.  No further IO is permitted.
 *  All blocks are released.
 */
void
qclose(Queue *q)
.
517a
	if(dowakeup)
		wakeup(&q->rr);

.
513,514c
		dowakeup = 1;
	} else
		dowakeup = 0;

.
511c

	if(q->state & Qstarve){
.
503,509c
	b->next = q->bfirst;
	q->bfirst = b;
.
501d
498,499c
		error(Ehungup);
.
493,495c
	x = splhi();
	lock(q);

	if(q->state & Qclosed){
.
483,490c
	/* flow control */
	while(!qnotfull(q)){
		qlock(&q->wlock);
		q->state |= Qflow;
		sleep(&q->wr, qnotfull, q);
		qunlock(&q->wlock);
.
475,481c
	b = allocb(len);
	memmove(b->wp, p, len);
	b->wp += len;
.
468,473c
/*
 *  write to a queue.  if no reader blocks are posted
 *  queue the data.
 */
long
qwrite(Queue *q, char *p, int len)
{
	int x, dowakeup;
	Block *b;
.
464,466c
	return q->len < q->limit;
}
.
461,462c
	Queue *q = a;
.
455,459c
static int
qnotfull(void *a)
.
448,451d
445a
	/* wakeup flow controlled writers (with a bit of histeresis) */
	if(dowakeup)
		wakeup(&q->wr);

.
440,441c
		b->next = q->bfirst;
		q->bfirst = b;
		q->len += BLEN(b);
.
434,436c
	/* free it or put it what's left on the queue */
	if(b->rp >= b->wp || (q->state&Qmsg))
		free(b);
.
431,432c
	if(n > len)
		n = len;
	memmove(p, b->rp, n);
	b->rp += n;
.
426a

	/* if writer flow controlled, restart */
	if((q->state & Qflow) && q->len < q->limit/2){
		q->state &= ~Qflow;
		dowakeup = 1;
	} else
		dowakeup = 0;
.
421,425c
	/* remove a buffered block */
	q->bfirst = b->next;
	n = BLEN(b);
.
390,418c
		sleep(&q->rr, notempty, q);
.
382,387c
		b = q->bfirst;
		if(b)
			break;
		q->state |= Qstarve;
.
378,379c
			return 0;
.
368,375c
		if(q->state & Qclosed){
.
355,366c
	/* wait for data */
	for(;;){
		/* sync with qwrite/qproduce */
		x = splhi();
		lock(q);
.
350,351d
347d
343,344c
	Block *b;
	int x, n, dowakeup;
.
339a
/*
 *  read a queue.  if no data is queued, post a Block
 *  and wait on its Rendez.
 */
.
337c
	return q->bfirst != 0;
.
335c
	Queue *q = a;
.
333c
notempty(void *a)
.
329,331d
320c
		return 0;

	memset(q, 0, sizeof(Queue));
.
295,309d
291a
	if(dowakeup)
		wakeup(&q->rr);

.
289a
	if(q->state & Qstarve){
		q->state &= ~Qstarve;
		dowakeup = 1;
	} else
		dowakeup = 0;
.
282d
247,261d
242c
	int dowakeup;
.
238,239c
int
qproduce(Queue *q, uchar *p, int len)
.
231a
	if(dowakeup)
		wakeup(&q->wr);

	/* discard the block if we're done with it */
.
226,228c
	/* if writer flow controlled, restart */
	if((q->state & Qflow) && q->len < q->limit/2){
		q->state &= ~Qflow;
		dowakeup = 1;
	} else
		dowakeup = 0;
.
215a

.
205c
	int n, dowakeup;
.
188a
	memset(b, 0, sizeof(Block));
.
156d
152a
			memset(b, 0, sizeof(Block));
.
## diffname port/qio.c 1993/0530
## diff -e /n/fornaxdump/1993/0528/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0530/sys/src/brazil/port/qio.c
519a
}

/*
 *  return bytes queued
 */
int
qlen(Queue *q)
{
	return q->len;
.
312c
	q->state = msg ? Qmsg : 0;
.
300c
qopen(int limit, int msg, void (*kick)(void*), void *arg)
.
41a
 *  IO queues
 */
typedef struct Block	Block;
typedef struct Queue	Queue;

struct Block
{
	Block	*next;

	uchar	*rp;			/* first unconsumed byte */
	uchar	*wp;			/* first empty byte */
	uchar	*lim;			/* 1 past the end of the buffer */
	uchar	*base;			/* start of the buffer */
	uchar	flag;
};
#define BLEN(b)		((b)->wp - (b)->rp)

struct Queue
{
	Lock;

	Block	*bfirst;	/* buffer */
	Block	*blast;

	int	len;		/* bytes in queue */
	int	limit;		/* max bytes in queue */
	int	state;

	void	(*kick)(void*);	/* restart output */
	void	*arg;		/* argument to kick */

	QLock	rlock;		/* mutex for reading processes */
	Rendez	rr;		/* process waiting to read */
	QLock	wlock;		/* mutex for writing processes */
	Rendez	wr;		/* process waiting to write */
};

enum
{
	/* Block.flag */
	Bfilled=1,		/* block filled */

	/* Queue.state */	
	Qstarve=	(1<<0),		/* consumer starved */
	Qmsg=		(1<<1),		/* message stream */
	Qclosed=	(1<<2),
	Qflow=		(1<<3),
};

/*
.
7a
/*
 *  interrupt level memory allocation
 */
.
## diffname port/qio.c 1993/0601
## diff -e /n/fornaxdump/1993/0530/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0601/sys/src/brazil/port/qio.c
581a
}

/*
 *  return true if we can read without blocking
 */
int
qcanread(Queue *q)
{
	return q->bfirst!=0;
.
572a
	q->state |= Qstarve;
.
510a
	}
.
509c
	if(dowakeup){
		if(q->kick)
			(*q->kick)(q->arg);
.
496,497c
	if(q->bfirst)
		q->blast->next = b;
	else
		q->bfirst = b;
	q->blast = b;
.
480a
		if(nowait)
			return len;
.
473a
	uchar *p = vp;
.
470c
qwrite(Queue *q, void *vp, int len, int nowait)
.
386a
	uchar *p = vp;
.
383c
qread(Queue *q, void *vp, int len)
.
365a
	q->state |= Qstarve;
.
344a
	}
.
343c
	if(dowakeup){
		if(q->kick)
			(*q->kick)(q->arg);
.
304a
	uchar *p = vp;
.
301c
qproduce(Queue *q, void *vp, int len)
.
259a
	uchar *p = vp;
.
256c
qconsume(Queue *q, void *vp, int len)
.
## diffname port/qio.c 1993/0725
## diff -e /n/fornaxdump/1993/0601/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0725/sys/src/brazil/port/qio.c
442c
	/* free it or put what's left on the queue */
.
409a
print("Qclosed %lux\n", q);
.
181c
	kproc("ialloc", iallockproc, 0);
.
## diffname port/qio.c 1993/0727
## diff -e /n/fornaxdump/1993/0725/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0727/sys/src/brazil/port/qio.c
410d
## diffname port/qio.c 1993/0728
## diff -e /n/fornaxdump/1993/0727/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0728/sys/src/brazil/port/qio.c
413,415d
406a
		b = q->bfirst;
		if(b)
			break;

.
## diffname port/qio.c 1993/0804
## diff -e /n/fornaxdump/1993/0728/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0804/sys/src/brazil/port/qio.c
552,554c
	while(bfirst){
		b = bfirst->next;
		free(bfirst);
		bfirst = b;
.
244c
	b->rp = b->base;
	b->wp = b->base;
.
242d
211a
	}

.
208c
			b->wp = b->base;
			b->rp = b->base;
.
193c
	for(pow = Minpow; pow <= Maxpow; pow++){
.
152a

.
149c
			i = cl->goal - cl->have;
			for(x = i; x > 0; x--){
.
120a
				first = p;
.
118c
			while(first != 0) {
.
## diffname port/qio.c 1993/0811
## diff -e /n/fornaxdump/1993/0804/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0811/sys/src/brazil/port/qio.c
559a
		poison(bfirst);
.
451a
	}
.
450c
	if(b->rp >= b->wp || (q->state&Qmsg)) {
		poison(b);
.
303c
	}
.
301c
	if((q->state & Qmsg) || len == n) {
		poison(b);
.
93a
void
poison(Block *b)
{
	b->next = (void*)0xdeadbabe;
	b->rp = (void*)0xdeadbabe;
	b->wp = (void*)0xdeadbabe;
	b->lim = (void*)0xdeadbabe;
	b->base = (void*)0xdeadbabe;
}

.
## diffname port/qio.c 1993/0819
## diff -e /n/fornaxdump/1993/0811/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0819/sys/src/brazil/port/qio.c
212a
				wakeup(&arena.r);
.
211a
				cl->wanted++;
.
196a
void
ixsummary(void)
{
	int pow;
	Chunkl *cl;

	print("size	have/goal\n");
	for(pow = Minpow; pow <= Maxpow; pow++){
		cl = &arena.alloc[pow];
		print("%d	%d/%d\n", 1<<pow, cl->have, cl->goal);
	}
	print("\n");
}

.
168a
			i -= x;
.
155,156c
			if(cl->have == 0){
				i = cl->goal>>2;
				if(cl->wanted > i)
					cl->goal += cl->wanted;
				else
					cl->goal += i;
				cl->wanted = 0;
			}
.
152,153c
			 *  increase goal if we've been drained.
.
144,145c
				cl->hist = ((cl->hist<<1) | 1) & 0xffff;
				if(cl->hist == 0xffff && cl->goal > 32)
.
32a
	int	wanted;
.
## diffname port/qio.c 1993/0908
## diff -e /n/fornaxdump/1993/0819/sys/src/brazil/port/qio.c /n/fornaxdump/1993/0908/sys/src/brazil/port/qio.c
634a
	q->eof = 0;
.
453a
			poperror();
			qunlock(&q->rlock);
			if(++q->eof > 3)
				error(Ehungup);
.
411a
	q->eof = 0;
.
72a
	int	eof;		/* number of eofs read by user */
.
## diffname port/qio.c 1993/1102
## diff -e /n/fornaxdump/1993/0908/sys/src/brazil/port/qio.c /n/fornaxdump/1993/1102/sys/src/brazil/port/qio.c
198c
		cl->goal = Maxpow-pow + 16;
.
159c
					cl->goal += 2*cl->wanted;
.
157c
				i = cl->goal>>1;
.
## diffname port/qio.c 1993/1103
## diff -e /n/fornaxdump/1993/1102/sys/src/brazil/port/qio.c /n/fornaxdump/1993/1103/sys/src/brazil/port/qio.c
368c
			return -2;
.
## diffname port/qio.c 1993/1227
## diff -e /n/fornaxdump/1993/1103/sys/src/brazil/port/qio.c /n/fornaxdump/1993/1227/sys/src/brazil/port/qio.c
543a
		poperror();
.
540a
		if(waserror()) {
			qunlock(&q->wlock);
			nexterror();
		}
.
## diffname port/qio.c 1994/0208
## diff -e /n/fornaxdump/1993/1227/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0208/sys/src/brazil/port/qio.c
573,581c
	qunlock(&q->wlock);
	poperror();
.
567,571c
		sofar += n;
	} while(sofar < len && (q->state & Qmsg) == 0);
.
560,565c
		if(dowakeup){
			if(q->kick)
				(*q->kick)(q->arg);
			wakeup(&q->rr);
		}
.
557,558d
554c
		b = allocb(n);
		memmove(b->wp, p+sofar, n);
		b->wp += n;
	
		/* flow control */
		while(!qnotfull(q)){
			if(nowait){
				free(b);
				qunlock(&q->wlock);
				poperror();
				return len;
			}
			q->state |= Qflow;
			sleep(&q->wr, qnotfull, q);
		}
	
		x = splhi();
		lock(q);
	
		if(q->state & Qclosed){
			unlock(q);
			splx(x);
			error(Ehungup);
		}
	
		if(q->syncbuf){
			/* we guessed wrong and did an extra copy */
			if(n > q->synclen)
				n = q->synclen;
			memmove(q->syncbuf, b->rp, n);
			q->synclen = n;
			q->syncbuf = 0;
			dowakeup = 1;
			free(b);
		} else {
			/* we guessed right, queue it */
			if(q->bfirst)
				q->blast->next = b;
			else
				q->bfirst = b;
			q->blast = b;
			q->len += n;
	
			if(q->state & Qstarve){
				q->state &= ~Qstarve;
				dowakeup = 1;
			}
		}

.
551,552c
	do {
		n = len-sofar;
		if(n > 128*1024)
			n = 128*1024;
.
545,548d
543c
			poperror();
			return len;
.
536,541c
	if(waserror()){
		qunlock(&q->wlock);
		nexterror();
	};
	qlock(&q->wlock);

	sofar = 0;
	if(q->syncbuf){
		if(len < q->synclen)
			sofar = len;
		else
			sofar = q->synclen;

		memmove(q->syncbuf, p, sofar);
		q->synclen = sofar;
		q->syncbuf = 0;
		wakeup(&q->rr);

		if(len == sofar || (q->state & Qmsg)){
.
532,534c
	dowakeup = 0;
.
528c
	int n, sofar, x, dowakeup;
.
523a
 *
 *  all copies should be outside of spl since they can fault.
.
493,494c
	} else {
.
463,466c
		if(globalmem(vp)){
			/* just let the writer fill the buffer directly */
			q->synclen = len;
			q->syncbuf = vp;
			unlock(q);
			splx(x);
			sleep(&q->rr, filled, q);
			len = q->synclen;
			poperror();
			qunlock(&q->rlock);
			return len;
		} else {
			q->state |= Qstarve;
			unlock(q);
			splx(x);
			sleep(&q->rr, notempty, q);
		}
.
438a
		/* can't let go if the buffer is in use */
		if(q->syncbuf){
			qlock(&q->wlock);
			x = splhi();
			lock(q);
			q->syncbuf = 0;
			unlock(q);
			splx(x);
			qunlock(&q->wlock);
		}
.
418a
filled(void *a)
{
	Queue *q = a;

	return q->syncbuf == 0;
}

static int
.
382,383c
	}
.
351a
	if(q->syncbuf){
		/* synchronous communications, just copy into buffer */
		if(len < q->synclen)
			q->synclen = len;
		i = q->synclen;
		memmove(q->syncbuf, p, i);
		q->syncbuf = 0;		/* tell reader buffer is full */
		len -= i;
		if(len <= 0 || (q->state & Qmsg)){
			unlock(q);
			wakeup(&q->rr);
			return i;
		}

		/* queue anything that's left */
		dowakeup = 1;
		p += i;
	}

.
349a
	dowakeup = 0;
.
346c
	int i, dowakeup;
.
285c
	b->lim = ((uchar*)b) + size;
.
282c
	addr = (ulong)b;
	addr = (addr + sizeof(Block) + 7) & ~7;
	b->base = (uchar*)addr;
.
278c
	size += sizeof(Block) + 7;
	b = malloc(size);
.
276a
	ulong addr;
.
271c
 *  allocate queues and blocks (round data base address to 64 bit boundary)
.
247c
			b->lim = ((uchar*)b) + (1<<pow);
.
244c
			addr = (ulong)b;
			addr = (addr + sizeof(Block) + 7) & ~7;
			b->base = (uchar*)addr;
.
241a

.
227c
	size += sizeof(Block) + 7;
.
222a
	ulong addr;
.
218a
/*
 *  interrupt time allocation (round data base address to 64 bit boundary)
 */
.
201c
}

void
iallocinit(void)
{
	/* start garbage collector/creator */
.
198c
		cl->goal = Maxpow-pow + 32;
		cl->first = 0;
		for(; cl->have < cl->goal; cl->have++){
			p = malloc(1<<pow);
			p->next = cl->first;
			cl->first = p;
		}
.
195a
	/* start with a bunch of initial blocks */
.
194a
	Chunk *p;
.
191c
qinit(void)
.
81a

	uchar	*syncbuf;	/* synchronous IO buffer */
	int	synclen;	/* syncbuf length */
.
## diffname port/qio.c 1994/0215
## diff -e /n/fornaxdump/1994/0208/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0215/sys/src/brazil/port/qio.c
594c
	return q->len < q->limit || (q->state & Qclosed);
.
## diffname port/qio.c 1994/0219
## diff -e /n/fornaxdump/1994/0215/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0219/sys/src/brazil/port/qio.c
723a
	q->len = 0;
.
## diffname port/qio.c 1994/0222
## diff -e /n/fornaxdump/1994/0219/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0222/sys/src/brazil/port/qio.c
666c

checkb(b, "qwrite");
.
565a
checkb(b, "qread 2");
.
544a
checkb(b, "qread 1");
.
425a
checkb(b, "qproduce");

.
424a
	memmove(b->wp, p, len);
	b->wp += len;
	if(q->bfirst)
		q->blast->next = b;
	else
		q->bfirst = b;
	q->blast = b;
.
406,423c
	b = iallocb(len);
	if(b == 0){
		unlock(q);
		return -2;
.
360a
checkb(b, "qconsume 2");
.
345,346c
	b->rp += len;
.
337a
checkb(b, "qconsume 1");
.
313d
99a
checkb(Block *b, char *msg)
{
	if(b->base > b->lim)
		panic("checkb 0 %s %lux %lux", msg, b->base, b->lim);
	if(b->rp < b->base)
		panic("checkb 1 %s %lux %lux", msg, b->base, b->rp);
	if(b->wp < b->base)
		panic("checkb 2 %s %lux %lux", msg, b->base, b->wp);
	if(b->rp > b->lim)
		panic("checkb 3 %s %lux %lux", msg, b->rp, b->lim);
	if(b->wp > b->lim)
		panic("checkb 4 %s %lux %lux", msg, b->wp, b->lim);
}

void
.
## diffname port/qio.c 1994/0306
## diff -e /n/fornaxdump/1994/0222/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0306/sys/src/brazil/port/qio.c
50,60d
48d
## diffname port/qio.c 1994/0309
## diff -e /n/fornaxdump/1994/0306/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0309/sys/src/brazil/port/qio.c
49d
## diffname port/qio.c 1994/0311
## diff -e /n/fornaxdump/1994/0309/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0311/sys/src/brazil/port/qio.c
371a
qpass(Queue *q, Block *b)
{
	int s, i, len;
	int dowakeup;

	s = splhi();

	len = BLEN(b);

	/* sync with qread */
	dowakeup = 0;
	lock(q);

	if(q->syncbuf){
		/* synchronous communications, just copy into buffer */
		if(len < q->synclen)
			q->synclen = len;
		i = q->synclen;
		memmove(q->syncbuf, b->rp, i);
		q->syncbuf = 0;		/* tell reader buffer is full */
		len -= i;
		if(len <= 0 || (q->state & Qmsg)){
			unlock(q);
			wakeup(&q->rr);
			ifree(b);
			splx(s);
			return i;
		}

		/* queue anything that's left */
		dowakeup = 1;
		b->rp += i;
	}

	/* no waiting receivers, room in buffer? */
	if(q->len >= q->limit){
		unlock(q);
		splx(s);
		return -1;
	}

	/* save in buffer */
	if(q->bfirst)
		q->blast->next = b;
	else
		q->bfirst = b;
	q->blast = b;
	q->len += len;
checkb(b, "qproduce");

	if(q->state & Qstarve){
		q->state &= ~Qstarve;
		dowakeup = 1;
	}
	unlock(q);

	if(dowakeup){
		if(q->kick)
			(*q->kick)(q->arg);
		wakeup(&q->rr);
	}
	splx(s);

	return len;
}

int
.
## diffname port/qio.c 1994/0312
## diff -e /n/fornaxdump/1994/0311/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0312/sys/src/brazil/port/qio.c
420c
checkb(b, "qpass");
.
374,375c
	int s, i, len, dowakeup;
.
## diffname port/qio.c 1994/0319
## diff -e /n/fornaxdump/1994/0312/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0319/sys/src/brazil/port/qio.c
208a
			if(p == 0)
				panic("qinit");
.
205c
		cl->goal = 0;
		if(pow < 12)
			cl->goal = Maxpow-pow + 32;

.
200a
	Chunkl *cl;
.
199d
18c
	Maxpow=	16,
.
## diffname port/qio.c 1994/0320
## diff -e /n/fornaxdump/1994/0319/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0320/sys/src/brazil/port/qio.c
400c
			free(b);
.
208d
205c
		cl->goal = 4;
.
167a
				if(cl->goal > 5000)
					cl->goal = 5000;
.
## diffname port/qio.c 1994/0321
## diff -e /n/fornaxdump/1994/0320/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0321/sys/src/brazil/port/qio.c
293,298c
	p = &pool[bp->size];
	lock(p);
	bp->next = p->list;
	p->list = bp;
	p->have++;
	unlock(p);
.
290,291c
	Pool *p;
.
288c
ifreeb(Block *bp)
.
282,284c
	p->have--;
	p->list = bp->next;
	unlock(p);
	bp->wp = b->base;
	bp->rp = b->base;
	bp->list = 0;
	bp->next = 0;
	return bp;
.
280a
		return 0;
.
271,279c
	if(pow == Maxpow)
		return 0;

	p = &pool[pow];
	lock(p);
	bp = p->list;
	if(p == 0) {
		p->want++;
		unlock(p);
		if(attention == 0) {
			attention++;
			newcallback(iallocmgr);
.
255,269c
	for(pow = Minpow; pow < Maxpow; pow++)
		if(size >= (1<<pow))
			break;
.
250,253c
	Block *bp;
.
244c
 *  interrupt time allocation
.
237,238c
		cl = &pool[pow];
		print("%d	%d/%d\n", 1<<pow, p->have, p->goal);
.
233c
	Pool *p;
.
223,229d
200,219d
193a
		p->had = p->have;
		p->want = 0;		
.
183,192c
			spllo();
			lock(p);
			unlock(p);
			splhi();
.
179,181c
				addr = (ulong)b;
				addr = (addr+sizeof(Block)+(BY2V-1)) & ~(BY2V-1);
				b->base = (uchar*)addr;
				b->rp = b->base;
				b->wp = b->base;
				b->lim = ((uchar*)b)+size;
				b->size = pow;
				n++;
.
171,177c
			splhi();
		}
		else {
			spllo();
			n = 0;
			s = sizeof(Block)+(1<<pow)+(BY2V-1);
			while(delta--) {
				b = malloc(s);
				if(b == 0)
.
142,169c
		if(delta < 0) {
			lock(p);
			p->have -= delta;
			bp = p->list;
			while(delta--)
				p->list = p->list->next;
			unlock(p);
			spllo();
			while(bp) {
				next = bp->next;
				free(bp);
				bp = next;
.
125,140c
		/* Low pass filter */
		delta = 3 * (p->had - p->have);
		delta = (delta/2) + p->want;
.
121,123c
	attention = 0;
	spllo();
	for(pow = Minpow; pow <= Maxpow; pow++) {
		p = &pool[pow];
.
117,119c
	int pow;
.
115c
iallocmgr(void)
.
70c
	uchar*	syncbuf;	/* synchronous IO buffer */
.
63c
	void*	arg;		/* argument to kick */
.
54,55c
	Block*	bfirst;	/* buffer */
	Block*	blast;
.
36,44d
34a
Pool	pool[Maxpow];
.
32,33d
30a
	int	want;
.
29c
	Block*	list;
	int	had;
.
23,27d
21c
struct Pool
.
17,18c
	Minpow	= 7,
	Maxpow	= 16,
.
8,13d
## diffname port/qio.c 1994/0322
## diff -e /n/fornaxdump/1994/0321/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0322/sys/src/brazil/port/qio.c
723,724c
		freeb(bfirst);
.
667c
			freeb(b);
.
640c
				freeb(b);
.
559,560c
		freeb(b);
.
531c
			while(q->bfirst == 0)
				sleep(&q->rr, notempty, q);
.
522c
			while(q->syncbuf != 0)
				sleep(&q->rr, filled, q);
.
331,337d
325d
321c
			freeb(b);
.
305d
303d
290,293c
	if((q->state & Qmsg) || len == n)
		freeb(b);
.
246,247c
 *  Interrupt level copy out of a queue, return # bytes copied.
.
236c
	addr = ROUND(addr + sizeof(Block), BY2V);
.
233a
	memset(b, 0, sizeof(Block));
.
230,231c
	size = sizeof(Block) + size + (BY2V-1);
	b = mallocz(size, 0);
.
211,218c
	print("ialloc %d/%d\n", ialloc.bytes, conf.ialloc);
.
209c
ixsummary(void)
.
179,205c
	free(b);
.
170,177c
	/* poison the block in case someone is still holding onto it */
	b->next = (void*)0xdeadbabe;
	b->rp = (void*)0xdeadbabe;
	b->wp = (void*)0xdeadbabe;
	b->lim = (void*)0xdeadbabe;
	b->base = (void*)0xdeadbabe;
.
167,168d
159,165c
	if(b->intr){
		ilock(&ialloc);
		ialloc.bytes -= b->lim - b->base;
		iunlock(&ialloc);
.
157c
freeb(Block *b)
.
151,153c
	return b;
.
109,149c
	lock(&ialloc);
	ialloc.bytes += b->lim - b->base;
	unlock(&ialloc);
.
105,107c
	addr = (ulong)b;
	addr = ROUND(addr + sizeof(Block), BY2V);
	b->base = (uchar*)addr;
	b->rp = b->base;
	b->wp = b->base;
	b->lim = ((uchar*)b) + size;
	b->intr = 1;
.
100,103c
	size = sizeof(Block) + size + (BY2V-1);
	if(ialloc.bytes > conf.ialloc){
iprint("whoops %d/%d\n", ialloc.bytes, conf.ialloc);
		return 0;
	}
	b = mallocz(size, 0);
	if(b == 0){
iprint("malloc %d/%d\n", ialloc.bytes, conf.ialloc);
		return 0;
	}
	memset(b, 0, sizeof(Block));
.
98c
	Block *b;
	ulong addr;
.
95,96c
Block*
iallocb(int size)
.
93c
 *  interrupt time allocation
.
82,91d
18,24c
	ulong	bytes;
} ialloc;
.
11,16d
8,9c
struct
.
## diffname port/qio.c 1994/0323
## diff -e /n/fornaxdump/1994/0322/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0323/sys/src/brazil/port/qio.c
644,645c
	iunlock(q);
.
641,642c
	ilock(q);
.
638,639d
616,617c
	iunlock(q);
.
610,611c
	ilock(q);
.
606d
582,583c
		iunlock(q);
.
552,553c
			iunlock(q);
.
548,549c
		ilock(q);
.
496c
	int n, sofar, dowakeup;
.
491c
 *  all copies should be outside of ilock since they can fault.
.
466,467c
		iunlock(q);
.
461,462c
		ilock(q);
.
447,448c
	iunlock(q);
.
428,431c
			iunlock(q);
			sleep(&q->rr, notempty, q);
.
418,419c
			iunlock(q);
.
405,406c
			iunlock(q);
.
397,398c
		ilock(q);
.
386,387c
			iunlock(q);
.
383,384c
			ilock(q);
.
375c
	int n, dowakeup;
.
364c
	return (q->state & Qclosed) || q->bfirst != 0;
.
267a
if(debuging) print("qproduce %d\n", len);
.
256d
249c
	iunlock(q);
.
228d
225c
			iunlock(q);
.
214c
	ilock(q);
.
210d
208c
	int i, len, dowakeup;
.
128c
	debuging ^= 1;
	print("ialloc %d/%d %d\n", ialloc.bytes, conf.ialloc, debuging);
.
13a
static int debuging;

.
## diffname port/qio.c 1994/0324
## diff -e /n/fornaxdump/1994/0323/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0324/sys/src/brazil/port/qio.c
463a
	}
.
462c
	if(dowakeup){
		if(q->kick)
			(*q->kick)(q->arg);
.
436c
	if((q->state & Qflow) && q->len < q->limit){
.
268d
245a
	if(q->len >= q->limit)
		q->state |= Qflow;

.
233a
		/* queue anything that's left */
.
232d
226c
		if(len <= 0 || (q->state & Qmsg)) {
.
## diffname port/qio.c 1994/0327
## diff -e /n/fornaxdump/1994/0324/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0327/sys/src/brazil/port/qio.c
658a
 * return space remaining before flow control
 */
int
qwindow(Queue *q)
{
	int l;

	l = q->limit - q->len;
	if(l < 0)
		l = 0;
	return l;
}

/*
.
## diffname port/qio.c 1994/0328
## diff -e /n/fornaxdump/1994/0327/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0328/sys/src/brazil/port/qio.c
438c
	if((q->state & Qflow) && q->len < q->limit/2){
.
## diffname port/qio.c 1994/0331
## diff -e /n/fornaxdump/1994/0328/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0331/sys/src/brazil/port/qio.c
418,419c
			sleep(&q->rr, filled, q);
.
## diffname port/qio.c 1994/0418
## diff -e /n/fornaxdump/1994/0331/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0418/sys/src/brazil/port/qio.c
246c
	if(q->len >= q->limit/2)
.
## diffname port/qio.c 1994/0505
## diff -e /n/fornaxdump/1994/0418/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0505/sys/src/brazil/port/qio.c
111c
	if(b->intr) {
.
88c
		iprint("iallocb: no memory %d/%d\n", ialloc.bytes, conf.ialloc);
.
83c
		iprint("ialloc limit exceeded %d/%d\n", ialloc.bytes, conf.ialloc);
.
## diffname port/qio.c 1994/0507
## diff -e /n/fornaxdump/1994/0505/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0507/sys/src/brazil/port/qio.c
111c
	if(b->flag & BINTR) {
.
103c
	iunlock(&ialloc);
.
101c
	ilock(&ialloc);
.
99c
	b->flag = BINTR;
.
83c
		iprint("ialloc limit %d/%d\n", ialloc.bytes, conf.ialloc);
.
51,54c
	Qstarve		= (1<<0),	/* consumer starved */
	Qmsg		= (1<<1),	/* message stream */
	Qclosed		= (1<<2),
	Qflow		= (1<<3),
.
47,49d
## diffname port/qio.c 1994/0804
## diff -e /n/fornaxdump/1994/0507/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0804/sys/src/brazil/port/qio.c
545a
		poperror();
.
533a
				poperror();
.
524a
		if(waserror()){
			freeb(b);
			nexterror();
		}
.
497c
	}
.
340d
## diffname port/qio.c 1994/0902
## diff -e /n/fornaxdump/1994/0804/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0902/sys/src/brazil/port/qio.c
679a
}

/*
 *  change queue limit
 */
void
qsetlimit(Queue *q, int limit)
{
	q->limit = limit;
}

/*
 *  set blocking/nonblocking
 */
void
qnoblock(Queue *q, int onoff)
{
	q->noblock = onoff;
.
647a
	q->noblock = 0;
	q->limit = q->inilim;
.
552c
		QDEBUG checkb(b, "qwrite");
.
533c
			if(q->noblock){
.
485c
qwrite(Queue *q, void *vp, int len)
.
446c
	QDEBUG checkb(b, "qread 2");
.
425c
	QDEBUG checkb(b, "qread 1");
.
340c
	q->limit = q->inilim = limit;
.
311c
	QDEBUG checkb(b, "qproduce");
.
241c
	QDEBUG checkb(b, "qpass");
.
198c
	QDEBUG checkb(b, "qconsume 2");
.
175c
	QDEBUG checkb(b, "qconsume 1");
.
30a
	int	noblock;	/* true if writes return immediately when q full */
.
29a
	int	inilim;		/* initial limit */
.
15a
#define QDEBUG if(0)

.
## diffname port/qio.c 1994/0927
## diff -e /n/fornaxdump/1994/0902/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0927/sys/src/brazil/port/qio.c
703a
}

/*
 *  flush the output queue
 */
void
qflush(Queue *q)
{
	Block *b, *bfirst;

	/* mark it */
	ilock(q);
	bfirst = q->bfirst;
	q->bfirst = 0;
	q->len = 0;
	iunlock(q);

	/* free queued blocks */
	while(bfirst){
		b = bfirst->next;
		freeb(bfirst);
		bfirst = b;
	}

	/* wake up readers/writers */
	wakeup(&q->wr);
.
652d
611a
	q->noblock = 0;
.
## diffname port/qio.c 1994/0929
## diff -e /n/fornaxdump/1994/0927/sys/src/brazil/port/qio.c /n/fornaxdump/1994/0929/sys/src/brazil/port/qio.c
574c

.
549c

.
547c

.
534c

.
## diffname port/qio.c 1994/1026
## diff -e /n/fornaxdump/1994/0929/sys/src/brazil/port/qio.c /n/fornaxdump/1994/1026/sys/src/brazil/port/qio.c
421a
			poperror();
.
420d
417a
			syncwait = 1; USED(syncwait);
.
386a
			if(q->syncbuf == 0){
				/* we got some data before the interrupt, queue it */
				b = allocb(q->synclen);
				memmove(b->wp, vp, q->synclen);
				b->wp += q->synclen;
				if(q->bfirst == 0)
					q->blast = b;
				q->bfirst = b;
				q->len += q->synclen;
			}
.
384c
		if(syncwait){
.
381a
	syncwait = 0;
.
378c
	int n, dowakeup, syncwait;
.
## diffname port/qio.c 1994/1103
## diff -e /n/fornaxdump/1994/1026/sys/src/brazil/port/qio.c /n/fornaxdump/1994/1103/sys/src/brazil/port/qio.c
392a
				b->next = q->bfirst;
.
## diffname port/qio.c 1994/1116
## diff -e /n/fornaxdump/1994/1103/sys/src/brazil/port/qio.c /n/fornaxdump/1994/1116/sys/src/brazil/port/qio.c
469a
		if(q->bfirst == 0)
			q->blast = b;
.
463a

.
431a
			poperror();
.
430c
			if(waserror()){
				/* sync with qwrite() & qproduce() */
				qlock(&q->wlock);
				ilock(q);
				if(q->syncbuf == 0){
					/* we got some data before the interrupt */
					b = allocb(q->synclen);
					memmove(b->wp, vp, q->synclen);
					b->wp += q->synclen;
					b->next = q->bfirst;
					if(q->bfirst == 0)
						q->blast = b;
					q->bfirst = b;
					q->len += q->synclen;
				}
				q->syncbuf = 0;
				iunlock(q);
				qunlock(&q->wlock);
				nexterror();
			}
.
384,402d
382d
378c
	int n, dowakeup;
.
303a
		q->state |= Qflow;
.
296a
		q->state |= Qflow;
.
## diffname port/qio.c 1994/1117
## diff -e /n/fornaxdump/1994/1116/sys/src/brazil/port/qio.c /n/fornaxdump/1994/1117/sys/src/brazil/port/qio.c
329d
325,327c
	if(dowakeup)
.
307c
		return 0;
.
305d
260d
256,258c
	if(dowakeup)
.
## diffname port/qio.c 1994/1124
## diff -e /n/fornaxdump/1994/1117/sys/src/brazil/port/qio.c /n/fornaxdump/1994/1124/sys/src/brazil/port/qio.c
740a
}

int
qstate(Queue *q)
{
	return q->state;
.
608a
 *  used by print() to write to a queue
 */
int
qiwrite(Queue *q, void *vp, int len)
{
	int n, sofar, dowakeup;
	Block *b;
	uchar *p = vp;

	dowakeup = 0;

	sofar = 0;
	do {
		n = len-sofar;
		if(n > 128*1024)
			n = 128*1024;

		b = allocb(n);
		memmove(b->wp, p+sofar, n);
		b->wp += n;

		ilock(q);

		QDEBUG checkb(b, "qiwrite");
		if(q->syncbuf){
			/* we guessed wrong and did an extra copy */
			if(n > q->synclen)
				n = q->synclen;
			memmove(q->syncbuf, b->rp, n);
			q->synclen = n;
			q->syncbuf = 0;
			dowakeup = 1;
			freeb(b);
		} else {
			/* we guessed right, queue it */
			if(q->bfirst)
				q->blast->next = b;
			else
				q->bfirst = b;
			q->blast = b;
			q->len += n;

			if(q->state & Qstarve){
				q->state &= ~Qstarve;
				dowakeup = 1;
			}
		}

		iunlock(q);

		if(dowakeup){
			if(q->kick)
				(*q->kick)(q->arg);
			wakeup(&q->rr);
		}

		sofar += n;
	} while(sofar < len && (q->state & Qmsg) == 0);

	return len;
}

/*
.
181c
		n = BLEN(b);
		if(n > 0)
			break;
		q->bfirst = b->next;
		freeb(b);
	};

.
173,179c
	for(;;) {
		b = q->bfirst;
		if(b == 0){
			q->state |= Qstarve;
			unlock(q);
			return -1;
		}
		QDEBUG checkb(b, "qconsume 1");
.
## diffname port/qio.c 1995/0101
## diff -e /n/fornaxdump/1994/1124/sys/src/brazil/port/qio.c /n/fornaxdump/1995/0101/sys/src/brazil/port/qio.c
111a
	/*
	 * drivers which perform non cache coherent DMA manage their
	 * own buffer pools, so they provide their own free routines.
	 */
	if(b->free) {
		b->free(b);
		return;
	}
.
## diffname port/qio.c 1995/0104
## diff -e /n/fornaxdump/1995/0101/sys/src/brazil/port/qio.c /n/fornaxdump/1995/0104/sys/src/brazil/port/qio.c
522a
	if((getstatus()&IE) == 0)
		print("qwrite hi %lux\n", getcallerpc(q));

.
## diffname port/qio.c 1995/0108
## diff -e /n/fornaxdump/1995/0104/sys/src/brazil/port/qio.c /n/fornaxdump/1995/0108/sys/src/brazil/port/qio.c
523,524c
if((getstatus()&IE) == 0)
print("qwrite hi %lux\n", getcallerpc(q));
.
114c
	 * own buffer pool and provide their own free routine.
.
## diffname port/qio.c 1995/0125
## diff -e /n/fornaxdump/1995/0108/sys/src/brazil/port/qio.c /n/fornaxdump/1995/0125/sys/src/brazil/port/qio.c
488c
	/* wakeup flow controlled writers (with a bit of histeria) */
.
113,114c
	 * drivers which perform non cache coherent DMA manage their own buffer 
	 * pool of uncached buffers and provide their own free routine.
.
84c
		iprint("iallocb: limited %d/%d\n", ialloc.bytes, conf.ialloc);
.
27c
	Block*	bfirst;		/* buffer */
.
## diffname port/qio.c 1995/0714
## diff -e /n/fornaxdump/1995/0125/sys/src/brazil/port/qio.c /n/fornaxdump/1995/0714/sys/src/brazil/port/qio.c
669,672c
		if(q->state & Qstarve){
			q->state &= ~Qstarve;
			dowakeup = 1;
.
651,667c
		if(q->bfirst)
			q->blast->next = b;
		else
			q->bfirst = b;
		q->blast = b;
		q->len += n;
.
627c
 *  used by print() to write to a queue.  Since we may be splhi or not in
 *  a process, don't qlock.
.
621,622d
577,617d
564,575c
		qbwrite(q, b);
.
561a
		poperror();
.
550a
	ilock(q);

	if(q->state & Qclosed){
		iunlock(q);
		error(Ehungup);
	}

	if(q->bfirst)
		q->blast->next = b;
	else
		q->bfirst = b;
	q->blast = b;
	q->len += n;

	if(q->state & Qstarve){
		q->state &= ~Qstarve;
		dowakeup = 1;
	}

	iunlock(q);

	if(dowakeup){
		if(q->kick)
			(*q->kick)(q->arg);
		wakeup(&q->rr);
	}

	qunlock(&q->wlock);
	poperror();
	return n;
}

/*
 *  write to a queue.  only 128k at a time is atomic.
 */
long
qwrite(Queue *q, void *vp, int len)
{
	int n, sofar;
	Block *b;
	uchar *p = vp;

	QDEBUG if((getstatus()&IE) == 0)
		print("qwrite hi %lux\n", getcallerpc(q));

	sofar = 0;
.
548a
		q->state |= Qflow;
		sleep(&q->wr, qnotfull, q);
.
547c
			return n;
.
532,544c
	/* flow control */
	while(!qnotfull(q)){
		if(q->noblock){
			freeb(b);
.
523,525d
521a
	n = BLEN(b);
.
517,519c
	int n, dowakeup;
.
515c
qbwrite(Queue *q, Block *b)
.
509,512c
 *  add a block to a queue obeying flow control
.
499a
/*
 *  read a queue.  if no data is queued, post a Block
 *  and wait on its Rendez.
 */
long
qread(Queue *q, void *vp, int len)
{
	Block *b;

	b = qbread(q, len);
	if(b == 0)
		return 0;

	len = BLEN(b);
	memmove(vp, b->rp, len);
	freeb(b);
	return len;
}

.
497c
	return nb;
.
488c
	iunlock(q);

	/* wakeup flow controlled writers */
.
473,485c
			b->next = q->bfirst;
			if(q->bfirst == 0)
				q->blast = b;
			q->bfirst = b;
			q->len += n;
		}
		nb->wp = nb->rp + len;
.
467,471c
	/* split block if its too big and this is not a message oriented 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;
.
465d
452d
415,450c
		q->state |= Qstarve;	/* flag requesting producer to wake me */
		iunlock(q);
		sleep(&q->rr, notempty, q);
.
389d
387c
	Block *b, *nb;
.
384,385c
Block*
qbread(Queue *q, int len)
.
381,382c
 *  get next block from a queue (up to a limit)
.
365,372d
288,306d
281c
	int dowakeup;
.
234,252d
227c
	int len, dowakeup;
.
44,46d
## diffname port/qio.c 1995/0902
## diff -e /n/fornaxdump/1995/0714/sys/src/brazil/port/qio.c /n/fornaxdump/1995/0902/sys/src/brazil/port/qio.c
162a
}

ulong bpadoverhead;

/*
 *  pad a block to the front
 */
Block*
bpad(Block *bp, int size)
{
	int n;
	Block *nbp;

	if(bp->rp - bp->base > size)
		return bp;

	n = bp->wp - bp->rp;
	bpadoverhead += n;
	nbp = allocb(size+n);
	nbp->rp += size;
	nbp->wp = nbp->rp;
	memmove(nbp->wp, bp->rp, n);
	nbp->wp += n;
	freeb(bp);
	return nbp;
.
159,160c
	n = b->lim - b->base - size;
	b->rp += n & ~(BY2V-1);
	b->wp = b->rp;
.
157a
	b->lim = ((uchar*)b) + msize(b);
.
149,150c
	n = sizeof(Block) + size + (BY2V-1);
	b = mallocz(n, 0);
.
147a
	int n;
.
141c
 *  allocate blocks (round data base address to 64 bit boundary).
 *  if mallocz gives us more than we asked for, leave room at the front
 *  for header.
.
## diffname port/qio.c 1995/0917
## diff -e /n/fornaxdump/1995/0902/sys/src/brazil/port/qio.c /n/fornaxdump/1995/0917/sys/src/brazil/port/qio.c
419a
			ilock(q);
.
414a
			iunlock(q);

.
## diffname port/qio.c 1995/1121
## diff -e /n/fornaxdump/1995/0917/sys/src/brazil/port/qio.c /n/fornaxdump/1995/1121/sys/src/brazil/port/qio.c
184,191c
		n = bp->wp - bp->rp;
		bpadoverhead += n;
		nbp = allocb(size+n);
		nbp->rp += size;
		nbp->wp = nbp->rp;
		memmove(nbp->wp, bp->rp, n);
		nbp->wp += n;
		freeb(bp);
	} else {
		size = -size;

		if(bp->lim - bp->wp >= size)
			return bp;

		n = bp->wp - bp->rp;
		bpadoverhead += n;
		nbp = allocb(size+n);
		memmove(nbp->wp, bp->rp, n);
		nbp->wp += n;
		freeb(bp);
	}
.
181,182c
	if(size >= 0){
		if(bp->rp - bp->base >= size)
			return bp;
.
176c
padblock(Block *bp, int size)
.
173c
 *  pad a block to the front (or the back if size is negative)
.
## diffname port/qio.c 1995/1217
## diff -e /n/fornaxdump/1995/1121/sys/src/brazil/port/qio.c /n/fornaxdump/1995/1217/sys/src/brazil/port/qio.c
775a

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

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

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

	/*
	 *  copy bytes from the trailing blocks into the first
	 */
	n -= BLEN(bp);
	while(nbp = bp->next){
		i = BLEN(nbp);
		if(i >= n) {
			memmove(bp->wp, nbp->rp, n);
			bp->wp += n;
			nbp->rp += n;
			return bp;
		}
		else {
			memmove(bp->wp, nbp->rp, i);
			bp->wp += i;
			bp->next = nbp->next;
			nbp->next = 0;
			freeb(nbp);
			n -= i;
		}
	}
	freeb(bp);
	return 0;
}
.
## diffname port/qio.c 1996/0305
## diff -e /n/fornaxdump/1995/1217/sys/src/brazil/port/qio.c /n/fornaxdump/1996/0305/sys/src/brazil/port/qio.c
519a
		freeb(b);
.
## diffname port/qio.c 1996/0611
## diff -e /n/fornaxdump/1996/0305/sys/src/brazil/port/qio.c /n/fornaxdump/1996/0611/sys/src/brazil/port/qio.c
822a
			if(n == 0)
				return bp;
.
810c
		if(i > n) {
.
## diffname port/qio.c 1996/0731
## diff -e /n/fornaxdump/1996/0611/sys/src/brazil/port/qio.c /n/fornaxdump/1996/0731/sys/src/brazil/port/qio.c
200c
		padblockoverhead += n;
.
192a
		nbp->rp -= size;
.
186c
		padblockoverhead += n;
.
183a
		}
.
182c
		if(bp->rp - bp->base >= size){
			bp->rp -= size;
.
170c
ulong padblockoverhead;
.
## diffname port/qio.c 1996/1225
## diff -e /n/fornaxdump/1996/0731/sys/src/brazil/port/qio.c /n/fornaxdump/1996/1225/sys/src/brazil/port/qio.c
592c
long
.
## diffname port/qio.c 1997/0327
## diff -e /n/fornaxdump/1996/1225/sys/src/brazil/port/qio.c /n/emeliedump/1997/0327/sys/src/brazil/port/qio.c
787,831c
	return q->state;
.
781,785c
int
qstate(Queue *q)
.
778c
	return q->state & Qflow;
.
776c
qfull(Queue *q)
.
765,769c
	freeblist(bfirst);
.
755c
	Block *bfirst;
.
679a
	if(msg == 0 || *msg == 0)
		strcpy(q->err, Ehungup);
	else
		strncpy(q->err, msg, ERRLEN-1);
.
675c
qhangup(Queue *q, char *msg)
.
659,663c
	freeblist(bfirst);
.
651a
	strcpy(q->err, Ehungup);
.
648a
	if(q == nil)
		return;

.
647c
	Block *bfirst;
.
640a
 *  be extremely careful when calling this,
 *  as there is no reference accounting
 */
void
qfree(Queue *q)
{
	qclose(q);
	free(q);
}

/*
.
630c
				q->kick(q->arg);
.
592c
int
.
562c
	QDEBUG if(islo() == 0)
.
555c
int
.
543c
			q->kick(q->arg);
.
531a
	b->next = 0;
.
524c
		error(q->err);
.
454c
			q->kick(q->arg);
.
419a
	QDEBUG checkb(b, "qbread 1");
.
417a
	b->next = 0;
.
407c
				error(q->err);
.
400a
		}
.
399c
		if(b){
			QDEBUG checkb(b, "qbread 0");
.
348a
 *  copy from offset in the queue
 */
Block*
qcopy(Queue *q, int len, ulong offset)
{
	int sofar;
	int n;
	Block *b, *nb;
	uchar *p;

	nb = allocb(len);

	lock(q);

	/* go to offset */
	b = q->bfirst;
	for(sofar = 0; ; sofar += n){
		if(b == nil){
			unlock(q);
			return nb;
		}
		n = BLEN(b);
		if(sofar + n > offset){
			p = b->rp + offset - sofar;
			n -= offset - sofar;
			break;
		}
		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;
	}
	unlock(q);

	return nb;
}

/*
.
332a
	/* b->next = 0; done by iallocb() */
.
326a
	producecnt += len;
.
285d
282a
	len = BLEN(b);
	QDEBUG checkb(b, "qpass");
	while(b->next){
		b = b->next;
		QDEBUG checkb(b, "qpass");
		len += BLEN(b);
	}
.
278c
	/* add buffer to queue */
.
273d
264a
	}
.
263c
	if((q->state & Qmsg) || len == n){
		b->next = 0;
.
243a
	consumecnt += n;
.
209c

	/* if writer flow controlled, restart */
	if((q->state & Qflow) && q->len < q->limit/2){
		q->state &= ~Qflow;
		dowakeup = 1;
	} else
		dowakeup = 0;

	unlock(q);

	if(dowakeup)
		wakeup(&q->wr);
.
202,207c
	iunlock(q);

	if(dowakeup)
		wakeup(&q->wr);

	return b;
}

/*
 *  throw away the next 'len' bytes in the queue
 */
void
qdiscard(Queue *q, int len)
{
	Block *b;
	int dowakeup, n, sofar;

	lock(q);
	for(sofar = 0; sofar < len; sofar += n){
		b = q->bfirst;
		if(b == nil)
			break;
		n = BLEN(b);
		if(n <= len - sofar){
			q->bfirst = b->next;
			b->next = 0;
			freeb(b);
		} else {
			n = len - sofar;
			b->rp += n;
		}
		q->len -= n;
.
199,200c
	/* if writer flow controlled, restart */
	if((q->state & Qflow) && q->len < q->limit/2){
		q->state &= ~Qflow;
		dowakeup = 1;
	} else
		dowakeup = 0;
.
187,197c
	b = q->bfirst;
	if(b == 0){
		q->state |= Qstarve;
		iunlock(q);
		return 0;
	}
	q->bfirst = b->next;
	b->next = 0;
	q->len -= BLEN(b);
.
181,185c
	/* sync with qwrite */
	ilock(q);
.
178,179c
	int dowakeup;
	Block *b;
.
176c
qget(Queue *q)
.
173c
 *  get next block from a queue, return null if nothing there
.
170,171d
137a
	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);
.
133a
freeblist(Block *b)
{
	Block *next;

	for(; b != 0; b = next){
		next = b->next;
		b->next = 0;
		freeb(b);
	}
}

/*
 *  pad a block to the front (or the back if size is negative)
 */
Block*
padblock(Block *bp, int size)
{
	int n;
	Block *nbp;

	if(size >= 0){
		if(bp->rp - bp->base >= size){
			bp->rp -= size;
			return bp;
		}

		n = BLEN(bp);
		padblockcnt += n;
		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->lim - bp->wp >= size)
			return bp;

		n = BLEN(bp);
		padblockcnt += n;
		nbp = allocb(size+n);
		memmove(nbp->wp, bp->rp, n);
		nbp->wp += n;
		freeb(bp);
	}
	return nbp;
}

/*
 *  return count of bytes in a string of blocks
 */
int
blocklen(Block *bp)
{
	int len;

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

/*
 *  copy the  string of blocks into
 *  a single block and free the string
 */
Block*
concatblock(Block *bp)
{
	int len;
	Block *nb, *f;

	if(bp->next == 0)
		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);
	return nb;
}

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

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

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

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

/*
 *  trim to len bytes starting at offset
 */
Block *
trimblock(Block *bp, int offset, int len)
{
	ulong l;
	Block *nb, *startb;

	if(blocklen(bp) < offset+len) {
		freeblist(bp);
		return nil;
	}

	while((l = BLEN(bp)) < offset) {
		offset -= l;
		nb = bp->next;
		bp->next = nil;
		freeb(bp);
		bp = nb;
	}

	startb = bp;
	bp->rp += offset;

	while((l = BLEN(bp)) < len) {
		len -= l;
		bp = bp->next;
	}

	bp->wp -= (BLEN(bp) - len);

	if(bp->next) {
		freeblist(bp->next);
		bp->next = nil;
	}

	return startb;
}

/*
 *  copy 'count' bytes into a new block
 */
Block*
copyblock(Block *bp, int count)
{
	int l;
	Block *nbp;

	nbp = allocb(count);
	for(; count > 0 && bp != 0; bp = bp->next){
		l = BLEN(bp);
		if(l > count)
			l = count;
		memmove(nbp->wp, bp->rp, l);
		nbp->wp += l;
		count -= l;
	}
	if(count > 0){
		memset(nbp->wp, 0, count);
		nbp->wp += count;
	}
	copyblockcnt += count;

	return nbp;
}

/*
 *  throw away up to count bytes from a
 *  list of blocks.  Return count of bytes
 *  thrown away.
 */
int
pullblock(Block **bph, int count)
{
	Block *bp;
	int n, bytes;

	bytes = 0;
	if(bph == nil)
		return 0;

	while(*bph != nil && count != 0) {
		bp = *bph;
		n = BLEN(bp);
		if(count < n)
			n = count;
		bytes += n;
		count -= n;
		bp->rp += n;
		if(BLEN(bp) == 0) {
			*bph = bp->next;
			bp->next = nil;
			freeb(bp);
		}
	}
	return bytes;
}

void
.
132a
/*
 *  free a list of blocks
 */
.
110c
	 * drivers which perform non cache coherent DMA manage their own buffer
.
67a

	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",
			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);
	}
.
57a
	void *dead = (void*)0xDEADBABE;

	if(b == dead)
		panic("checkb b %s %lux", msg, b);

.
48c
	/* Queue.state */
.
43a

	char	err[ERRLEN];
.
16c
#define QDEBUG	if(0)
.
13a
static ulong padblockcnt;
static ulong concatblockcnt;
static ulong pullupblockcnt;
static ulong copyblockcnt;
static ulong consumecnt;
static ulong producecnt;
static ulong qcopycnt;

.
## diffname port/qio.c 1997/0404
## diff -e /n/emeliedump/1997/0327/sys/src/brazil/port/qio.c /n/emeliedump/1997/0404/sys/src/brazil/port/qio.c
588a
	if(q->len >= q->limit){
		freeb(b);
		iunlock(q);
		return -1;
	}
.
## diffname port/qio.c 1997/0410
## diff -e /n/emeliedump/1997/0404/sys/src/brazil/port/qio.c /n/emeliedump/1997/0410/sys/src/brazil/port/qio.c
718c
	iunlock(q);
.
692c
			iunlock(q);
.
686c
	ilock(q);
.
665c
	iunlock(q);
.
646c
		iunlock(q);
.
639c
		iunlock(q);
.
634c
	ilock(q);
.
567c
	iunlock(q);
.
539c
			iunlock(q);
.
533c
	ilock(q);
.
516c
	iunlock(q);
.
492c
	ilock(q);
.
## diffname port/qio.c 1997/0806
## diff -e /n/emeliedump/1997/0410/sys/src/brazil/port/qio.c /n/emeliedump/1997/0806/sys/src/brazil/port/qio.c
1045a
	q->state &= ~(Qflow|Qstarve);
.
## diffname port/qio.c 1997/0925
## diff -e /n/emeliedump/1997/0806/sys/src/brazil/port/qio.c /n/emeliedump/1997/0925/sys/src/brazil/port/qio.c
403,443d
372a
Block*
adjustblock(Block* bp, int len)
{
	int n;

	if(len < 0){
		freeb(bp);
		return nil;
	}

	if(bp->rp+len > bp->lim)
		return copyblock(bp, len);

	n = BLEN(bp);
	if(len > n)
		memset(bp->wp, 0, len-n);
	bp->wp = bp->rp+len;

	return bp;
}


.
368c
	copyblockcnt++;
.
292c
			pullupblockcnt++;
.
285c
			pullupblockcnt++;
.
202c
		padblockcnt++;
.
187c
		padblockcnt++;
.
118,119c
	n = b->lim - b->base - size;
	b->rp += n & ~(BY2V-1);
	b->wp = b->rp;

.
116a
	b->lim = ((uchar*)b) + msize(b);
.
110c
		return nil;
.
107,108c

	n = sizeof(Block) + size + (BY2V-1);
	b = mallocz(n, 0);
	if(b == nil){
.
102d
100a
	int n;
.
93a
 *  allocate blocks (round data base address to 64 bit boundary).
 *  if mallocz gives us more than we asked for, leave room at the front
 *  for header.
 */
Block*
allocb(int size)
{
	Block *b;
	ulong addr;
	int n;

	n = sizeof(Block) + size + (BY2V-1);
	b = mallocz(n+Hdrspc, 0);
	if(b == 0)
		exhausted("Blocks");
	memset(b, 0, sizeof(Block));

	addr = (ulong)b;
	addr = ROUND(addr + sizeof(Block), BY2V);
	b->base = (uchar*)addr;
	b->lim = ((uchar*)b) + msize(b);
	b->rp = b->base;
	n = b->lim - b->base - size;
	b->rp += n & ~(BY2V-1);
	b->wp = b->rp;

	return b;
}

/*
.
92a
void
ixsummary(void)
{
	debuging ^= 1;
	print("ialloc %d/%d %d\n", ialloc.bytes, conf.ialloc, debuging);
	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);
}

.
62a

	Hdrspc		= 64,		/* leave room for high-level headers */
.
## diffname port/qio.c 1997/0926
## diff -e /n/emeliedump/1997/0925/sys/src/brazil/port/qio.c /n/emeliedump/1997/0926/sys/src/brazil/port/qio.c
152c
	b = mallocz(n+Hdrspc, 0);
.
## diffname port/qio.c 1997/1007
## diff -e /n/emeliedump/1997/0926/sys/src/brazil/port/qio.c /n/emeliedump/1997/1007/sys/src/brazil/port/qio.c
431,432c
	if(bp->rp+len > bp->lim){
		nbp = copyblock(bp, len);
		freeblist(bp);

		return nbp;
	}
.
424a
	Block *nbp;
.
## diffname port/qio.c 1997/1102
## diff -e /n/emeliedump/1997/1007/sys/src/brazil/port/qio.c /n/emeliedump/1997/1102/sys/src/brazil/port/qio.c
841c
	/* split block if it's too big and this is not a message-oriented queue */
.
## diffname port/qio.c 1997/1104
## diff -e /n/emeliedump/1997/1102/sys/src/brazil/port/qio.c /n/emeliedump/1997/1104/sys/src/brazil/port/qio.c
891a
poot("Q 4", 0);
.
890a
poot("Q 3", 0);
.
885a
poot("Q 2", 0);
.
884a
poot("Q 1", 0);
.
872a
poot("QB end", nb);
.
842a
poot("QB 2", len);
.
826a
poot("QB 1", len);
.
824a
poot("QB after sleep", len);
.
823a
poot("QB before sleep", len);
.
795a
poot("QB start", len);
.
150a
	if(size < 0)
		panic("iallocb < 0");
.
117a
	if(size < 0)
		panic("allocb < 0");
.
24c
#define QDEBUG	if(1)
.
## diffname port/qio.c 1997/1105
## diff -e /n/emeliedump/1997/1104/sys/src/brazil/port/qio.c /n/emeliedump/1997/1105/sys/src/brazil/port/qio.c
905d
903d
897d
895d
882d
851d
834d
831d
829d
800d
153,154d
118,119d
24c
#define QDEBUG	if(0)
.
## diffname port/qio.c 1998/0328
## diff -e /n/emeliedump/1997/1105/sys/src/brazil/port/qio.c /n/emeliedump/1998/0328/sys/src/brazil/port/qio.c
1126a
	iunlock(q);
.
1122a
	ilock(q);
.
932,939d
928a
		iunlock(q);
.
927a

.
922a
			iunlock(q);
.
921c
	for(;;){
		ilock(q);

		if(q->state & Qclosed){
			iunlock(q);
			freeb(b);
			error(q->err);
		}

		if(q->len < q->limit)
			break;

.
918d
913a
	qlock(&q->wlock);
.
774a
	iunlock(q);
.
768a
	ilock(q);
.
## diffname port/qio.c 1998/0605
## diff -e /n/emeliedump/1998/0328/sys/src/brazil/port/qio.c /n/emeliedump/1998/0605/sys/src/brazil/port/qio.c
954a
	QDEBUG checkb(b, "qbwrite");
.
734a
		QDEBUG checkb(b, "qcopy");
.
530a
		QDEBUG checkb(b, "qdiscard");
.
500a
	QDEBUG checkb(b, "qget");
.
470a
		QDEBUG checkb(bp, "pullblock ");
.
442a
	QDEBUG checkb(bp, "adjustblock 2");
.
434a
		QDEBUG checkb(nbp, "adjustblock 1");
.
416a
	QDEBUG checkb(nbp, "copyblock 1");
.
402a
	QDEBUG checkb(bp, "copyblock 0");
.
362a
	QDEBUG checkb(bp, "trimblock 1");
.
347a
			}
.
346c
			if(n == 0){
				QDEBUG checkb(bp, "pullupblock 2");
.
335a
			QDEBUG checkb(bp, "pullupblock 1");
.
295a
	QDEBUG checkb(nb, "concatblock 1");
.
255a
	QDEBUG checkb(nbp, "padblock 1");
.
248a
		if(bp->next)
			panic("padblock 0x%uX", getcallerpc(bp));
.
233a
		if(bp->next)
			panic("padblock 0x%uX", getcallerpc(bp));
.
227a
	QDEBUG checkb(bp, "padblock 1");
.
195,199c
	b->next = dead;
	b->rp = dead;
	b->wp = dead;
	b->lim = dead;
	b->base = dead;
.
179a
	void *dead = (void*)Bdead;

.
98,99c
	debugging ^= 1;
	print("ialloc %d/%d %d\n", ialloc.bytes, conf.ialloc, debugging);
.
86,92d
73a
	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",
			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);
	}
.
70c
	void *dead = (void*)Bdead;
.
64a

	Bdead		= 0x51494F42,	/* "QIOB" */
.
22c
static int debugging;
.
## diffname port/qio.c 1998/0703
## diff -e /n/emeliedump/1998/0605/sys/src/brazil/port/qio.c /n/emeliedump/1998/0703/sys/src/brazil/port/qio.c
798a
	q->noblock = 0;
.
## diffname port/qio.c 1998/0825
## diff -e /n/emeliedump/1998/0703/sys/src/brazil/port/qio.c /n/emeliedump/1998/0825/sys/src/brazil/port/qio.c
101c
	print("ialloc %lud/%lud %d\n", ialloc.bytes, conf.ialloc, debugging);
.
## diffname port/qio.c 1998/0918
## diff -e /n/emeliedump/1998/0825/sys/src/brazil/port/qio.c /n/emeliedump/1998/0918/sys/src/brazil/port/qio.c
1170c
	int len;
	Block *b;

	len = 0;
	for(b = q->bfirst; b; b= b->next)
		len += BLEN(b);
	return len;
.
1072a
checkqlen(q);
.
1066c
		q->len += BALLOC(b);
.
985a
checkqlen(q);
.
978c
	q->len += BALLOC(b);
.
887a
checkqlen(q);
.
883c
			q->len += BALLOC(b);
.
857c
	q->len -= BALLOC(b);
.
720a
checkqlen(q);
.
714c
	q->len += BALLOC(b);
.
681a
qpassnolim(Queue *q, Block *b)
{
	int len, dowakeup;

	/* sync with qread */
	dowakeup = 0;
	ilock(q);

	/* add buffer to queue */
	if(q->bfirst)
		q->blast->next = b;
	else
		q->bfirst = b;
	len = BALLOC(b);
	QDEBUG checkb(b, "qpass");
	while(b->next){
		b = b->next;
		QDEBUG checkb(b, "qpass");
		len += BALLOC(b);
	}
	q->blast = b;
	q->len += len;

	if(q->len >= q->limit/2)
		q->state |= Qflow;

	if(q->state & Qstarve){
		q->state &= ~Qstarve;
		dowakeup = 1;
	}
checkqlen(q);
	iunlock(q);

	if(dowakeup)
		wakeup(&q->rr);

	return len;
}

/*
 *  if the allocated space is way out of line with the used
 *  space, reallocate to a smaller block
 */
Block*
packblock(Block *bp)
{
	int len, alen;
	Block *nbp;

	len = alen = 0;
	for(nbp = bp; nbp; nbp = bp->next){
		len += BLEN(bp);
		alen += BALLOC(bp);
	}

	if(alen >= (len<<2)){
		nbp = allocb(len);
		nbp->next = bp;
		return pullupblock(nbp, len);
	}

	return bp;
}

int
.
672a
checkqlen(q);
.
661c
		len += BALLOC(b);
.
656c
	len = BALLOC(b);
.
646c
		freeblist(b);
.
631a
		q->len -= BALLOC(b);
.
614d
603a
		q->len -= BALLOC(b);
.
562d
556a
			q->len -= BALLOC(b);
.
520c
	q->len -= BALLOC(b);
.
97a
checkqlen(Queue *q)
{
	int len;
	Block *b;

	len = 0;
	for(b = q->bfirst; b; b = b->next)
		len += BALLOC(b);
	if(len != q->len)
		panic("checkqlen");
}

void
.
## diffname port/qio.c 1998/0922
## diff -e /n/emeliedump/1998/0918/sys/src/brazil/port/qio.c /n/emeliedump/1998/0922/sys/src/brazil/port/qio.c
1316a
	q->dlen = 0;
.
1254,1260c
	return q->dlen;
.
1149a
		q->dlen += n;
.
1060a
	q->dlen += n;
.
1021d
964a
			q->dlen += n;
.
937a
	q->dlen -= n;
.
794a
	q->dlen += BLEN(b);
.
718a
	q->dlen += dlen;
.
715a
		dlen += BLEN(b);
.
710a
	dlen = BLEN(b);
.
699c
	int dlen, len, dowakeup;
.
678a
	q->dlen += dlen;
.
675a
		dlen += BLEN(b);
.
670a
	dlen = BLEN(b);
.
654c
	int dlen, len, dowakeup;
.
645a
		q->dlen -= BLEN(b);
.
627a
	q->dlen -= len;
.
574a
			q->dlen -= n;
.
570a
			q->dlen -= BLEN(b);
.
533a
	q->dlen -= BLEN(b);
.
38c
	int	len;		/* bytes allocated to queue */
	int	dlen;		/* data bytes in queue */
.
## diffname port/qio.c 1998/0923
## diff -e /n/emeliedump/1998/0922/sys/src/brazil/port/qio.c /n/emeliedump/1998/0923/sys/src/brazil/port/qio.c
1172d
1083d
984d
814d
764,769d
758,761c
	for(l = &bp; *l; l = &(*l)->next){
		nbp = *l;
		n = BLEN(nbp);
		if((n<<2) < BALLOC(nbp)){
			*l = allocb(n);
			memmove((*l)->wp, nbp->rp, n);
			(*l)->wp += n;
			(*l)->next = nbp->next;
			freeb(nbp);
		}
.
755,756c
	Block **l, *nbp;
	int n;
.
739d
696d
99,111d
## diffname port/qio.c 1998/0929
## diff -e /n/emeliedump/1998/0923/sys/src/brazil/port/qio.c /n/emeliedump/1998/0929/sys/src/brazil/port/qio.c
1198a
	q->dlen = 0;
.
## diffname port/qio.c 1998/1127
## diff -e /n/emeliedump/1998/0929/sys/src/brazil/port/qio.c /n/emeliedump/1998/1127/sys/src/brazil/port/qio.c
1229a
}

/*
 *  return non-zero if the q is hungup
 */
int
qisclosed(Queue *q)
{
	return q->state & Qclosed;
.
157c
		print("iallocb: no memory %d/%d\n",
			ialloc.bytes, conf.ialloc);
.
150c
		print("iallocb: limited %d/%d\n",
			ialloc.bytes, conf.ialloc);
.
## diffname port/qio.c 1998/1128
## diff -e /n/emeliedump/1998/1127/sys/src/brazil/port/qio.c /n/emeliedump/1998/1128/sys/src/brazil/port/qio.c
158c
		print("iallocb: no memory %lud/%lud\n",
.
150c
		print("iallocb: limited %lud/%lud\n",
.
## diffname port/qio.c 1999/0115
## diff -e /n/emeliedump/1998/1128/sys/src/brazil/port/qio.c /n/emeliedump/1999/0115/sys/src/brazil/port/qio.c
968a

	/* if writer flow controlled, restart */
	if((q->state & Qflow) && q->len < q->limit/2){
		q->state &= ~Qflow;
		dowakeup = 1;
	} else
		dowakeup = 0;
.
941,947d
800a

	if(q->len >= q->limit)
		q->state |= Qflow;
.
635,642c
	if(tofree != nil)
		freeblist(tofree);

.
622a
	/* discard the block if we're done with it */
	if((q->state & Qmsg) || len == n){
		b->next = 0;
		q->len -= BALLOC(b);
		q->dlen -= BLEN(b);

		/* remember to free this */
		b->next = tofree;
		tofree = b;
	}

.
611c

		/* remember to free this */
		b->next = tofree;
		tofree = b;
.
592a
	Block *tofree = nil;
.
## diffname port/qio.c 1999/0219
## diff -e /n/emeliedump/1999/0115/sys/src/brazil/port/qio.c /n/emeliedump/1999/0219/sys/src/brazil/port/qio.c
1146,1147c
		if(n > Maxatomic)
			n = Maxatomic;
.
1110,1111c
		if(n > Maxatomic)
			n = Maxatomic;
.
1095c
 *  write to a queue.  only Maxatomic bytes at a time is atomic.
.
67a

	Maxatomic	= 32*1024,
.
## diffname port/qio.c 1999/0311
## diff -e /n/emeliedump/1999/0219/sys/src/brazil/port/qio.c /n/emeliedump/1999/0311/sys/src/brazil/port/qio.c
261,262d
257a
		if(bp->next)
			panic("padblock 0x%uX", getcallerpc(bp));

.
## diffname port/qio.c 1999/0501
## diff -e /n/emeliedump/1999/0311/sys/src/brazil/port/qio.c /n/emeliedump/1999/0501/sys/src/brazil/port/qio.c
1108c
		print("qwrite hi %lux\n", getcallerpc(&q));
.
259c
			panic("padblock 0x%uX", getcallerpc(&bp));
.
245c
			panic("padblock 0x%uX", getcallerpc(&bp));
.
## diffname port/qio.c 1999/0522
## diff -e /n/emeliedump/1999/0501/sys/src/brazil/port/qio.c /n/emeliedump/1999/0522/sys/src/brazil/port/qio.c
133,135c
	addr = (ulong)(b->lim);
	addr = addr & ~(BLOCKALIGN-1);
	b->lim = (uchar*)addr;

	/* leave sluff at beginning for added headers */
	b->rp = b->lim - ROUND(size, BLOCKALIGN);
	if(b->rp < b->base)
		panic("allocb");
.
131a

	/* align end of data portion by rounding down */
.
130c
	addr = ROUND(addr + sizeof(Block), BLOCKALIGN);
.
128a
	/* align start of data portion by rounding up */
.
123c
	n = sizeof(Block) + size;
.
## diffname port/qio.c 1999/0527
## diff -e /n/emeliedump/1999/0522/sys/src/brazil/port/qio.c /n/emeliedump/1999/0527/sys/src/brazil/port/qio.c
109,218d
104c
	iallocsummary();
.
73,100d
65,68d
8,13d
## diffname port/qio.c 1999/0603
## diff -e /n/emeliedump/1999/0527/sys/src/brazil/port/qio.c /n/emeliedump/1999/0603/sys/src/brazil/port/qio.c
976a
		tagwithpc(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]);
.
## diffname port/qio.c 1999/0714
## diff -e /n/emeliedump/1999/0603/sys/src/brazil/port/qio.c /n/emeliedump/1999/0714/sys/src/brazil/port/qio.c
977c
		setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]);
.
## diffname port/qio.c 2000/0913
## diff -e /n/emeliedump/1999/0714/sys/src/brazil/port/qio.c /n/emeliedump/2000/0913/sys/src/9/port/qio.c
937a
	b = nil;
.
909d
898a
		if(b != nil)
			freeb(b);
.
## diffname port/qio.c 2000/0914
## diff -e /n/emeliedump/2000/0913/sys/src/9/port/qio.c /n/emeliedump/2000/0914/sys/src/9/port/qio.c
952a

	/*
	 *  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 quued.  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);
		q->state |= Qflow;
		iunlock(q);
		sleep(&q->wr, qnotfull, q);
	}
	USED(b);
.
945d
940a
	/* make sure other end gets awakened */
.
929a
	/* queue the block */
.
924,927d
914,916c
	/* if nonblocking, don't queue over the limit */
	if(q->len >= q->limit){
.
909,912c
	/* give up if the queue is closed */
	if(q->state & Qclosed){
		iunlock(q);
		error(q->err);
	}
.
905,907c
	ilock(q);
.
## diffname port/qio.c 2001/0127
## diff -e /n/emeliedump/2000/0914/sys/src/9/port/qio.c /n/emeliedump/2001/0127/sys/src/9/port/qio.c
873,876c
	if((q->state & Qcoalesce) == 0){
		b = qbread(q, len);
		if(b == 0)
			return 0;

		m = BLEN(b);
		memmove(p, b->rp, m);
		freeb(b);
		return m;
	}

	for(e = p + len; p < e; p += m){
		b = qbread(q, e-p);
		if(b == 0)
			return 0;

		m = BLEN(b);
		memmove(p, b->rp, m);
		freeb(b);
	}

	return p-(uchar*)vp;
.
869,871c
	p = vp;
.
867a
	int m;
	uchar *p;
	uchar *e;
.
752c
	q->state = 0;
	if(msg > 0)
		q->state |= Qmsg;
	else if(msg < 0)
		q->state |= Qcoalesce;
	
.
57a
	Qcoalesce	= (1<<4),	/* coallesce packets on read */
.
## diffname port/qio.c 2001/0128
## diff -e /n/emeliedump/2001/0127/sys/src/9/port/qio.c /n/emeliedump/2001/0128/sys/src/9/port/qio.c
901c
	/* take care of any left over partial block */
	if(b != nil){
		if(q->state & Qmsg)
			freeb(b);
		else
			qputback(q, b);
	}

	/* restart producer */
	qwakeup_iunlock(q);

	poperror();
	qunlock(&q->rlock);
	return n;
.
899a
	ilock(q);
.
897a
		p += m;
		n += m;
		next = b->next;
		b->next = nil;
.
896a
		if(m > len-n){
			m = len - n;
			n = len;
			memmove(p, b->rp, m);
			b->rp += m;
			break;
		}
.
891,895c
	/* copy to user space outside of the ilock */
	iunlock(q);
	n = 0;
	for(b = first; b != nil; b = next){
.
886,888c
		n = 0;
		for(;;) {
			*l = qremove(q);
			l = &b->next;
			n += m;
			b = q->bfirst;
			if(b == nil)
				break;
			m = BLEN(b);
			if(n+m > len)
				break;
		}
	} else {
		first = qremove(q);
.
884a
	/* if we get here, there's at least one block in the queue */
	if(q->state & Qcoalesce){
		/* when coalescing, 0 length blocks just go away */
		if(q->dlen <= 0){
			freeb(qremove(q));
			goto again;
		}

		/*  grab the first block plus as many
		 *  following blocks as will completely
		 *  fit in the read.
		 */
		l = &first;
		b = q->bfirst;
.
880,883c
	ilock(q);
again:
	switch(qwait(q)){
	case 0:
		/* queue closed */
		iunlock(q);
		qunlock(&q->rlock);
		poperror();
		return 0;
	case -1:
		/* multiple reads on a closed queue */
		iunlock(q);
		error(q->err);
	}
.
878c
	qlock(&q->rlock);
	if(waserror()){
		qunlock(&q->rlock);
		nexterror();
	}
.
873,876c
	Block *b, *first, *next, **l;
	int m, n;
	uchar *p = vp;
.
860a
/*
 *  get next block from a queue (up to a limit)
 */
Block*
qbread(Queue *q, int len)
{
	Block *b, *nb;
	int n;

	qlock(&q->rlock);
	if(waserror()){
		qunlock(&q->rlock);
		nexterror();
	}

	ilock(q);
	switch(qwait(q)){
	case 0:
		/* queue closed */
		iunlock(q);
		qunlock(&q->rlock);
		poperror();
		return nil;
	case -1:
		/* multiple reads on a closed queue */
		iunlock(q);
		error(q->err);
	}

	/* if we get here, there's at least one block in the queue */
	b = qremove(q);
	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;
	}

	/* restart producer */
	qwakeup_iunlock(q);

.
859a
}
.
849,850c
	}
.
844a
/*
 *  flow control, get producer going again
 *  called with q ilocked
 */
static void
qwakeup_iunlock(Queue *q)
{
	int dowakeup = 0;

.
843a
	b->next = q->bfirst;
	if(q->bfirst == nil)
		q->blast = b;
	q->bfirst = b;
	q->len += BALLOC(b);
	q->dlen += BLEN(b);
}
.
823,842c
/*
 *  put a block back to the front of the queue
 *  called with q ilocked
 */
static void
qputback(Queue *q, Block *b)
{
	if(q->state & (Qclosed|Qmsg)){
		freeb(b);
		return;
.
821c
	QDEBUG checkb(b, "qremove");
	return b;
}
.
817,819c
	b->next = nil;
	q->dlen -= BLEN(b);
.
815c
/*
 *  called with q ilocked
 */
static Block*
qremove(Queue *q)
{
	Block *b;

	b = q->bfirst;
	if(b == nil)
		return nil;
.
813a
	return 1;
}
.
812a
		ilock(q);
.
806c
				return -1;
.
802,804d
792,794d
784,789d
781,782c
	Block *b;
.
778,779c
static int
qwait(Queue *q)
.
776c
 *  wait for the queue to be non-empty or closed.
 *  called with q ilocked.
.
## diffname port/qio.c 2001/0203
## diff -e /n/emeliedump/2001/0128/sys/src/9/port/qio.c /n/emeliedump/2001/0203/sys/src/9/port/qio.c
1020c
	return p-s;
.
1000d
994a
			p += m;
.
991,993c
		if(m > e - p){
			m = e - p;
.
988c
	p = s;
.
979c
			if(p+m > e)
.
974c
			p += m;

.
970d
968d
958c
		b = q->bfirst;
		if(BLEN(b) <= 0){
.
933a
	s = p = vp;
	e = s+len;

.
931,932c
	int m;
	uchar *s, *e, *p;
.
832,835d
790d
786,788c
		if(q->bfirst != nil)
.
782,783d
## diffname port/qio.c 2001/0207
## diff -e /n/emeliedump/2001/0203/sys/src/9/port/qio.c /n/emeliedump/2001/0207/sys/src/9/port/qio.c
1083c
		p = wakeup(&q->rr);

		/* if we just wokeup a higher priority process, let it run */
		if(p != nil && p->priority > up->priority)
			sched();
.
1030a
	Proc *p;
.
## diffname port/qio.c 2001/0306
## diff -e /n/emeliedump/2001/0207/sys/src/9/port/qio.c /n/emeliedump/2001/0306/sys/src/9/port/qio.c
445a

	return sofar;
.
409c
int
.
## diffname port/qio.c 2001/0317
## diff -e /n/emeliedump/2001/0306/sys/src/9/port/qio.c /n/emeliedump/2001/0317/sys/src/9/port/qio.c
576a

	if(q->state & Qclosed){
		freeblist(b);
		iunlock(q);
		return BALLOC(b);
	}
.
534a
	if(q->state & Qclosed){
		freeblist(b);
		iunlock(q);
		return BALLOC(b);
	}
.
## diffname port/qio.c 2001/0504
## diff -e /n/emeliedump/2001/0317/sys/src/9/port/qio.c /n/emeliedump/2001/0504/sys/src/9/port/qio.c
1219c
	return sofar;
.
1188c
		b = iallocb(n);
		if(b == nil)
			break;
.
## diffname port/qio.c 2001/0527
## diff -e /n/emeliedump/2001/0504/sys/src/9/port/qio.c /n/emeliedump/2001/0527/sys/src/9/port/qio.c
1280c
		strncpy(q->err, msg, ERRMAX-1);
.
48c
	char	err[ERRMAX];
.
## diffname port/qio.c 2001/0602
## diff -e /n/emeliedump/2001/0527/sys/src/9/port/qio.c /n/emeliedump/2001/0602/sys/src/9/port/qio.c
837c
void
.
817c
Block*
.
814a
 * add a block to a queue
 */
void
qadd(Queue *q, Block *b)
{
	/* 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 += BLEN(b);
}

/*
.
235a
 *  make sure the first block has at least n bytes
 */
Block*
pullupqueue(Queue *q, int n)
{
	Block *b;

	if(BLEN(q->bfirst) >= n)
		return q->bfirst;
	q->bfirst = pullupblock(q->bfirst, n);
	for(b = q->bfirst; b != nil && b->next != nil; b = b->next)
		;
	q->blast = b;
	return q->bfirst;
}

/*
.
## diffname port/qio.c 2001/0619
## diff -e /n/emeliedump/2001/0602/sys/src/9/port/qio.c /n/emeliedump/2001/0619/sys/src/9/port/qio.c
1196,1198c
	}
	poperror();
.
1179,1194c
	b = mem2bl(vp, len);
	if(waserror()){
		freeblist(b);
		nexterror();
	}
	for(; b != nil; b = next){
		next = b->next;
		b->next = nil;
.
1176c
	QDEBUG if(!islo())
.
1172,1174c
	Block *b, *next;
.
1060c
	return n;
.
1048a
		n -= BLEN(b);
.
1029,1044c
	b = bl2mem(vp, first, len);
.
1024a
		n = BLEN(first);
.
1020c
			if(n+m > len)
.
1014c
			n += m;
.
1008a
		n = 0;
.
972,974d
968,970c
	Block *b, *first, **l;
	int m, n;
.
867a
 *  copy the contents of a string of blocks into
 *  memory.  emptied blocks are freed.  return
 *  pointer to first unconsumed block.
 */
Block*
bl2mem(uchar *p, Block *b, int n)
{
	int i;
	Block *next;

	for(; b != nil; b = next){
		i = BLEN(b);
		if(i > n){
			memmove(p, b->rp, n);
			b->rp += n;
			return b;
		}
		memmove(p, b->rp, i);
		n -= i;
		p += i;
		b->rp += i;
		next = b->next;
		freeb(b);
	}
	return nil;
}

/*
 *  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;
}

/*
.
## diffname port/qio.c 2001/0624
## diff -e /n/emeliedump/2001/0619/sys/src/9/port/qio.c /n/emeliedump/2001/0624/sys/src/9/port/qio.c
1225c
		freeblist(next);
.
1222a
	next = nil;
.
## diffname port/qio.c 2001/0728
## diff -e /n/emeliedump/2001/0624/sys/src/9/port/qio.c /n/emeliedump/2001/0728/sys/src/9/port/qio.c
819a
			if(*q->err && strcmp(q->err, Ehungup) != 0)
				return -1;
.
## diffname port/qio.c 2001/0804
## diff -e /n/emeliedump/2001/0728/sys/src/9/port/qio.c /n/emeliedump/2001/0804/sys/src/9/port/qio.c
1190c
	 *  interrupt us after the data has been queued.  This
.
## diffname port/qio.c 2001/0805
## diff -e /n/emeliedump/2001/0804/sys/src/9/port/qio.c /n/emeliedump/2001/0805/sys/src/9/port/qio.c
1235,1236c

		sofar += n;
	} while(sofar < len && (q->state & Qmsg) == 0);
.
1225,1233c
	sofar = 0;
	do {
		n = len-sofar;
		if(n > Maxatomic)
			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();
		}
		memmove(b->wp, p+sofar, n);
		poperror();
		b->wp += n;

.
1220c
	int n, sofar;
	Block *b;
	uchar *p = vp;
.
## diffname port/qio.c 2001/0825
## diff -e /n/emeliedump/2001/0805/sys/src/9/port/qio.c /n/emeliedump/2001/0825/sys/src/9/port/qio.c
845,847d
843a
	q->len += blockalloclen(b);
	q->dlen += blocklen(b);
	while(b->next)
		b = b->next;
.
837c
qaddlist(Queue *q, Block *b)
.
834c
 * add a block list to a queue
.
152a
 * return count of space in blocks
 */
int
blockalloclen(Block *bp)
{
	int len;

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

/*
.
## diffname port/qio.c 2001/0904
## diff -e /n/emeliedump/2001/0825/sys/src/9/port/qio.c /n/emeliedump/2001/0904/sys/src/9/port/qio.c
62a
uint	qiomaxatomic = Maxatomic;

.
## diffname port/qio.c 2002/0114
## diff -e /n/emeliedump/2001/0904/sys/src/9/port/qio.c /n/emeliedump/2002/0114/sys/src/9/port/qio.c
122c
			panic("padblock 0x%luX", getcallerpc(&bp));
.
108c
			panic("padblock 0x%luX", getcallerpc(&bp));
.
## diffname port/qio.c 2002/0613
## diff -e /n/emeliedump/2002/0114/sys/src/9/port/qio.c /n/emeliedump/2002/0613/sys/src/9/port/qio.c
60c
	Maxatomic	= 64*1024,
.
## diffname port/qio.c 2002/0711
## diff -e /n/emeliedump/2002/0613/sys/src/9/port/qio.c /n/emeliedump/2002/0711/sys/src/9/port/qio.c
1196,1197d
1194a
	/*  get output going again */
	if(q->kick && (dowakeup || (q->state&Qkick)))
		q->kick(q->arg);

	/* wakeup anyone consuming at the other end */
.
801,805c
	q->state = msg;
.
53,59d
## diffname port/qio.c 2002/0712
## diff -e /n/emeliedump/2002/0711/sys/src/9/port/qio.c /n/emeliedump/2002/0712/sys/src/9/port/qio.c
1450a
print("nonblocking %d\n", onoff);
.
## diffname port/qio.c 2002/0713
## diff -e /n/emeliedump/2002/0712/sys/src/9/port/qio.c /n/emeliedump/2002/0713/sys/src/9/port/qio.c
1451d
1158a
			noblockcnt += n;
.
1126a
ulong noblockcnt;

.
## diffname port/qio.c 2002/0714
## diff -e /n/emeliedump/2002/0713/sys/src/9/port/qio.c /n/emeliedump/2002/0714/sys/src/9/port/qio.c
463,464c
	/*
	 *  if writer flow controlled, restart
	 *
	 *  This used to be
	 *	q->len < q->limit/2
	 *  but it slows down tcp too much for certain write sizes.
	 *  I really don't understand it completely.  It may be
	 *  due to the queue draining so fast that the transmission
	 *  stalls waiting for the app to produce more data.  - presotto
	 */
	if((q->state & Qflow) && q->len < q->limit){
.
## diffname port/qio.c 2002/1011
## diff -e /n/emeliedump/2002/0714/sys/src/9/port/qio.c /n/emeliedump/2002/1011/sys/src/9/port/qio.c
531a
		q->bfirst = b->next;
.
525,526d
486a
long
qblen(Queue *q)
{
	Block *b;
	int n;
	Block *tofree = nil;

	ilock(q);

	for(;;) {
		b = q->bfirst;
		if(b == 0){
			q->state |= Qstarve;
			iunlock(q);
			return -1;
		}
		QDEBUG checkb(b, "qblen 1");

		n = BLEN(b);
		if(n > 0)
			break;
		q->bfirst = b->next;
		q->len -= BALLOC(b);

		/* remember to free this */
		b->next = tofree;
		tofree = b;
	};
	iunlock(q);

	if(tofree != nil)
		freeblist(tofree);

	return len;
}

.
411c
		return nil;
.
408c
	if(b == nil){
.
## diffname port/qio.c 2002/1012
## diff -e /n/emeliedump/2002/1011/sys/src/9/port/qio.c /n/emeliedump/2002/1012/sys/src/9/port/qio.c
486a
/*
 *  Return length that next qconsume will return
 */
.
## diffname port/qio.c 2002/1014
## diff -e /n/emeliedump/2002/1012/sys/src/9/port/qio.c /n/emeliedump/2002/1014/sys/src/9/port/qio.c
524a
#endif sape
.
486a
#ifdef sape
.
## diffname port/qio.c 2002/1015
## diff -e /n/emeliedump/2002/1014/sys/src/9/port/qio.c /n/emeliedump/2002/1015/sys/src/9/port/qio.c
526d
524c
	return n;
.
487d
## diffname port/qio.c 2002/1113
## diff -e /n/emeliedump/2002/1015/sys/src/9/port/qio.c /n/emeliedump/2002/1113/sys/src/9/port/qio.c
488,526d
## diffname port/qio.c 2003/0220
## diff -e /n/emeliedump/2002/1113/sys/src/9/port/qio.c /n/emeliedump/2003/0220/sys/src/9/port/qio.c
1147a

	if(q->bypass){
		(*q->bypass)(q->arg, b);
		return n;
	}

	dowakeup = 0;
.
1146d
811a
/* open a queue to be bypassed */
Queue*
qbypass(void (*bypass)(void*, Block*), void *arg)
{
	Queue *q;

	q = malloc(sizeof(Queue));
	if(q == 0)
		return 0;

	q->limit = 0;
	q->arg = arg;
	q->bypass = bypass;
	q->state = 0;

	return q;
}

.
807d
798d
40a
	void	(*bypass)(void*, Block*);	/* bypass queue altogether */
.
## diffname port/qio.c 2003/0304
## diff -e /n/emeliedump/2003/0220/sys/src/9/port/qio.c /n/emeliedump/2003/0304/sys/src/9/port/qio.c
228,229c
		} else {
			/* shouldn't happen but why crash if it does */
			if(i < 0){
				print("pullup negative length packet\n");
				i = 0;
			}
.

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].