xref: /plan9/sys/src/9/port/qio.c (revision b8a11165c6411897fdf740a1fb2f87ac0998b113)
17dd7cddfSDavid du Colombier #include	"u.h"
27dd7cddfSDavid du Colombier #include	"../port/lib.h"
37dd7cddfSDavid du Colombier #include	"mem.h"
47dd7cddfSDavid du Colombier #include	"dat.h"
57dd7cddfSDavid du Colombier #include	"fns.h"
67dd7cddfSDavid du Colombier #include	"../port/error.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier static ulong padblockcnt;
97dd7cddfSDavid du Colombier static ulong concatblockcnt;
107dd7cddfSDavid du Colombier static ulong pullupblockcnt;
117dd7cddfSDavid du Colombier static ulong copyblockcnt;
127dd7cddfSDavid du Colombier static ulong consumecnt;
137dd7cddfSDavid du Colombier static ulong producecnt;
147dd7cddfSDavid du Colombier static ulong qcopycnt;
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier static int debugging;
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier #define QDEBUG	if(0)
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier /*
217dd7cddfSDavid du Colombier  *  IO queues
227dd7cddfSDavid du Colombier  */
237dd7cddfSDavid du Colombier typedef struct Queue	Queue;
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier struct Queue
267dd7cddfSDavid du Colombier {
277dd7cddfSDavid du Colombier 	Lock;
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier 	Block*	bfirst;		/* buffer */
307dd7cddfSDavid du Colombier 	Block*	blast;
317dd7cddfSDavid du Colombier 
327dd7cddfSDavid du Colombier 	int	len;		/* bytes allocated to queue */
337dd7cddfSDavid du Colombier 	int	dlen;		/* data bytes in queue */
347dd7cddfSDavid du Colombier 	int	limit;		/* max bytes in queue */
357dd7cddfSDavid du Colombier 	int	inilim;		/* initial limit */
367dd7cddfSDavid du Colombier 	int	state;
377dd7cddfSDavid du Colombier 	int	noblock;	/* true if writes return immediately when q full */
387dd7cddfSDavid du Colombier 	int	eof;		/* number of eofs read by user */
397dd7cddfSDavid du Colombier 
407dd7cddfSDavid du Colombier 	void	(*kick)(void*);	/* restart output */
41e6c6b7f8SDavid du Colombier 	void	(*bypass)(void*, Block*);	/* bypass queue altogether */
427dd7cddfSDavid du Colombier 	void*	arg;		/* argument to kick */
437dd7cddfSDavid du Colombier 
447dd7cddfSDavid du Colombier 	QLock	rlock;		/* mutex for reading processes */
457dd7cddfSDavid du Colombier 	Rendez	rr;		/* process waiting to read */
467dd7cddfSDavid du Colombier 	QLock	wlock;		/* mutex for writing processes */
477dd7cddfSDavid du Colombier 	Rendez	wr;		/* process waiting to write */
487dd7cddfSDavid du Colombier 
499a747e4fSDavid du Colombier 	char	err[ERRMAX];
507dd7cddfSDavid du Colombier };
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier enum
537dd7cddfSDavid du Colombier {
543ff48bf5SDavid du Colombier 	Maxatomic	= 64*1024,
557dd7cddfSDavid du Colombier };
567dd7cddfSDavid du Colombier 
579a747e4fSDavid du Colombier uint	qiomaxatomic = Maxatomic;
589a747e4fSDavid du Colombier 
597dd7cddfSDavid du Colombier void
ixsummary(void)607dd7cddfSDavid du Colombier ixsummary(void)
617dd7cddfSDavid du Colombier {
627dd7cddfSDavid du Colombier 	debugging ^= 1;
637dd7cddfSDavid du Colombier 	iallocsummary();
647dd7cddfSDavid du Colombier 	print("pad %lud, concat %lud, pullup %lud, copy %lud\n",
657dd7cddfSDavid du Colombier 		padblockcnt, concatblockcnt, pullupblockcnt, copyblockcnt);
667dd7cddfSDavid du Colombier 	print("consume %lud, produce %lud, qcopy %lud\n",
677dd7cddfSDavid du Colombier 		consumecnt, producecnt, qcopycnt);
687dd7cddfSDavid du Colombier }
697dd7cddfSDavid du Colombier 
707dd7cddfSDavid du Colombier /*
717dd7cddfSDavid du Colombier  *  free a list of blocks
727dd7cddfSDavid du Colombier  */
737dd7cddfSDavid du Colombier void
freeblist(Block * b)747dd7cddfSDavid du Colombier freeblist(Block *b)
757dd7cddfSDavid du Colombier {
767dd7cddfSDavid du Colombier 	Block *next;
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier 	for(; b != 0; b = next){
797dd7cddfSDavid du Colombier 		next = b->next;
80*b8a11165SDavid du Colombier 		if(b->ref == 1)
81*b8a11165SDavid du Colombier 			b->next = nil;
827dd7cddfSDavid du Colombier 		freeb(b);
837dd7cddfSDavid du Colombier 	}
847dd7cddfSDavid du Colombier }
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier /*
877dd7cddfSDavid du Colombier  *  pad a block to the front (or the back if size is negative)
887dd7cddfSDavid du Colombier  */
897dd7cddfSDavid du Colombier Block*
padblock(Block * bp,int size)907dd7cddfSDavid du Colombier padblock(Block *bp, int size)
917dd7cddfSDavid du Colombier {
927dd7cddfSDavid du Colombier 	int n;
937dd7cddfSDavid du Colombier 	Block *nbp;
947dd7cddfSDavid du Colombier 
957dd7cddfSDavid du Colombier 	QDEBUG checkb(bp, "padblock 1");
967dd7cddfSDavid du Colombier 	if(size >= 0){
977dd7cddfSDavid du Colombier 		if(bp->rp - bp->base >= size){
987dd7cddfSDavid du Colombier 			bp->rp -= size;
997dd7cddfSDavid du Colombier 			return bp;
1007dd7cddfSDavid du Colombier 		}
1017dd7cddfSDavid du Colombier 
1027dd7cddfSDavid du Colombier 		if(bp->next)
103*b8a11165SDavid du Colombier 			panic("padblock %#p", getcallerpc(&bp));
1047dd7cddfSDavid du Colombier 		n = BLEN(bp);
1057dd7cddfSDavid du Colombier 		padblockcnt++;
1067dd7cddfSDavid du Colombier 		nbp = allocb(size+n);
1077dd7cddfSDavid du Colombier 		nbp->rp += size;
1087dd7cddfSDavid du Colombier 		nbp->wp = nbp->rp;
1097dd7cddfSDavid du Colombier 		memmove(nbp->wp, bp->rp, n);
1107dd7cddfSDavid du Colombier 		nbp->wp += n;
1117dd7cddfSDavid du Colombier 		freeb(bp);
1127dd7cddfSDavid du Colombier 		nbp->rp -= size;
1137dd7cddfSDavid du Colombier 	} else {
1147dd7cddfSDavid du Colombier 		size = -size;
1157dd7cddfSDavid du Colombier 
1167dd7cddfSDavid du Colombier 		if(bp->next)
117*b8a11165SDavid du Colombier 			panic("padblock %#p", getcallerpc(&bp));
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier 		if(bp->lim - bp->wp >= size)
1207dd7cddfSDavid du Colombier 			return bp;
1217dd7cddfSDavid du Colombier 
1227dd7cddfSDavid du Colombier 		n = BLEN(bp);
1237dd7cddfSDavid du Colombier 		padblockcnt++;
1247dd7cddfSDavid du Colombier 		nbp = allocb(size+n);
1257dd7cddfSDavid du Colombier 		memmove(nbp->wp, bp->rp, n);
1267dd7cddfSDavid du Colombier 		nbp->wp += n;
1277dd7cddfSDavid du Colombier 		freeb(bp);
1287dd7cddfSDavid du Colombier 	}
1297dd7cddfSDavid du Colombier 	QDEBUG checkb(nbp, "padblock 1");
1307dd7cddfSDavid du Colombier 	return nbp;
1317dd7cddfSDavid du Colombier }
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier /*
1347dd7cddfSDavid du Colombier  *  return count of bytes in a string of blocks
1357dd7cddfSDavid du Colombier  */
1367dd7cddfSDavid du Colombier int
blocklen(Block * bp)1377dd7cddfSDavid du Colombier blocklen(Block *bp)
1387dd7cddfSDavid du Colombier {
1397dd7cddfSDavid du Colombier 	int len;
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier 	len = 0;
1427dd7cddfSDavid du Colombier 	while(bp) {
1437dd7cddfSDavid du Colombier 		len += BLEN(bp);
1447dd7cddfSDavid du Colombier 		bp = bp->next;
1457dd7cddfSDavid du Colombier 	}
1467dd7cddfSDavid du Colombier 	return len;
1477dd7cddfSDavid du Colombier }
1487dd7cddfSDavid du Colombier 
1497dd7cddfSDavid du Colombier /*
1509a747e4fSDavid du Colombier  * return count of space in blocks
1519a747e4fSDavid du Colombier  */
1529a747e4fSDavid du Colombier int
blockalloclen(Block * bp)1539a747e4fSDavid du Colombier blockalloclen(Block *bp)
1549a747e4fSDavid du Colombier {
1559a747e4fSDavid du Colombier 	int len;
1569a747e4fSDavid du Colombier 
1579a747e4fSDavid du Colombier 	len = 0;
1589a747e4fSDavid du Colombier 	while(bp) {
1599a747e4fSDavid du Colombier 		len += BALLOC(bp);
1609a747e4fSDavid du Colombier 		bp = bp->next;
1619a747e4fSDavid du Colombier 	}
1629a747e4fSDavid du Colombier 	return len;
1639a747e4fSDavid du Colombier }
1649a747e4fSDavid du Colombier 
1659a747e4fSDavid du Colombier /*
1667dd7cddfSDavid du Colombier  *  copy the  string of blocks into
1677dd7cddfSDavid du Colombier  *  a single block and free the string
1687dd7cddfSDavid du Colombier  */
1697dd7cddfSDavid du Colombier Block*
concatblock(Block * bp)1707dd7cddfSDavid du Colombier concatblock(Block *bp)
1717dd7cddfSDavid du Colombier {
1727dd7cddfSDavid du Colombier 	int len;
1737dd7cddfSDavid du Colombier 	Block *nb, *f;
1747dd7cddfSDavid du Colombier 
1757dd7cddfSDavid du Colombier 	if(bp->next == 0)
1767dd7cddfSDavid du Colombier 		return bp;
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier 	nb = allocb(blocklen(bp));
1797dd7cddfSDavid du Colombier 	for(f = bp; f; f = f->next) {
1807dd7cddfSDavid du Colombier 		len = BLEN(f);
1817dd7cddfSDavid du Colombier 		memmove(nb->wp, f->rp, len);
1827dd7cddfSDavid du Colombier 		nb->wp += len;
1837dd7cddfSDavid du Colombier 	}
1847dd7cddfSDavid du Colombier 	concatblockcnt += BLEN(nb);
1857dd7cddfSDavid du Colombier 	freeblist(bp);
1867dd7cddfSDavid du Colombier 	QDEBUG checkb(nb, "concatblock 1");
1877dd7cddfSDavid du Colombier 	return nb;
1887dd7cddfSDavid du Colombier }
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier /*
1917dd7cddfSDavid du Colombier  *  make sure the first block has at least n bytes
1927dd7cddfSDavid du Colombier  */
1937dd7cddfSDavid du Colombier Block*
pullupblock(Block * bp,int n)1947dd7cddfSDavid du Colombier pullupblock(Block *bp, int n)
1957dd7cddfSDavid du Colombier {
1967dd7cddfSDavid du Colombier 	int i;
1977dd7cddfSDavid du Colombier 	Block *nbp;
1987dd7cddfSDavid du Colombier 
1997dd7cddfSDavid du Colombier 	/*
2007dd7cddfSDavid du Colombier 	 *  this should almost always be true, it's
2017dd7cddfSDavid du Colombier 	 *  just to avoid every caller checking.
2027dd7cddfSDavid du Colombier 	 */
2037dd7cddfSDavid du Colombier 	if(BLEN(bp) >= n)
2047dd7cddfSDavid du Colombier 		return bp;
2057dd7cddfSDavid du Colombier 
2067dd7cddfSDavid du Colombier 	/*
2077dd7cddfSDavid du Colombier 	 *  if not enough room in the first block,
2087dd7cddfSDavid du Colombier 	 *  add another to the front of the list.
2097dd7cddfSDavid du Colombier 	 */
2107dd7cddfSDavid du Colombier 	if(bp->lim - bp->rp < n){
2117dd7cddfSDavid du Colombier 		nbp = allocb(n);
2127dd7cddfSDavid du Colombier 		nbp->next = bp;
2137dd7cddfSDavid du Colombier 		bp = nbp;
2147dd7cddfSDavid du Colombier 	}
2157dd7cddfSDavid du Colombier 
2167dd7cddfSDavid du Colombier 	/*
2177dd7cddfSDavid du Colombier 	 *  copy bytes from the trailing blocks into the first
2187dd7cddfSDavid du Colombier 	 */
2197dd7cddfSDavid du Colombier 	n -= BLEN(bp);
2207dd7cddfSDavid du Colombier 	while(nbp = bp->next){
2217dd7cddfSDavid du Colombier 		i = BLEN(nbp);
2227dd7cddfSDavid du Colombier 		if(i > n) {
2237dd7cddfSDavid du Colombier 			memmove(bp->wp, nbp->rp, n);
2247dd7cddfSDavid du Colombier 			pullupblockcnt++;
2257dd7cddfSDavid du Colombier 			bp->wp += n;
2267dd7cddfSDavid du Colombier 			nbp->rp += n;
2277dd7cddfSDavid du Colombier 			QDEBUG checkb(bp, "pullupblock 1");
2287dd7cddfSDavid du Colombier 			return bp;
229a6a9e072SDavid du Colombier 		} else {
230a6a9e072SDavid du Colombier 			/* shouldn't happen but why crash if it does */
231a6a9e072SDavid du Colombier 			if(i < 0){
232*b8a11165SDavid du Colombier 				print("pullup negative length packet, called from %#p\n",
233a66c99ebSDavid du Colombier 					getcallerpc(&bp));
234a6a9e072SDavid du Colombier 				i = 0;
2357dd7cddfSDavid du Colombier 			}
2367dd7cddfSDavid du Colombier 			memmove(bp->wp, nbp->rp, i);
2377dd7cddfSDavid du Colombier 			pullupblockcnt++;
2387dd7cddfSDavid du Colombier 			bp->wp += i;
2397dd7cddfSDavid du Colombier 			bp->next = nbp->next;
2407dd7cddfSDavid du Colombier 			nbp->next = 0;
2417dd7cddfSDavid du Colombier 			freeb(nbp);
2427dd7cddfSDavid du Colombier 			n -= i;
2437dd7cddfSDavid du Colombier 			if(n == 0){
2447dd7cddfSDavid du Colombier 				QDEBUG checkb(bp, "pullupblock 2");
2457dd7cddfSDavid du Colombier 				return bp;
2467dd7cddfSDavid du Colombier 			}
2477dd7cddfSDavid du Colombier 		}
2487dd7cddfSDavid du Colombier 	}
2497dd7cddfSDavid du Colombier 	freeb(bp);
2507dd7cddfSDavid du Colombier 	return 0;
2517dd7cddfSDavid du Colombier }
2527dd7cddfSDavid du Colombier 
2537dd7cddfSDavid du Colombier /*
2549a747e4fSDavid du Colombier  *  make sure the first block has at least n bytes
2559a747e4fSDavid du Colombier  */
2569a747e4fSDavid du Colombier Block*
pullupqueue(Queue * q,int n)2579a747e4fSDavid du Colombier pullupqueue(Queue *q, int n)
2589a747e4fSDavid du Colombier {
2599a747e4fSDavid du Colombier 	Block *b;
2609a747e4fSDavid du Colombier 
2619a747e4fSDavid du Colombier 	if(BLEN(q->bfirst) >= n)
2629a747e4fSDavid du Colombier 		return q->bfirst;
2639a747e4fSDavid du Colombier 	q->bfirst = pullupblock(q->bfirst, n);
2649a747e4fSDavid du Colombier 	for(b = q->bfirst; b != nil && b->next != nil; b = b->next)
2659a747e4fSDavid du Colombier 		;
2669a747e4fSDavid du Colombier 	q->blast = b;
2679a747e4fSDavid du Colombier 	return q->bfirst;
2689a747e4fSDavid du Colombier }
2699a747e4fSDavid du Colombier 
2709a747e4fSDavid du Colombier /*
2717dd7cddfSDavid du Colombier  *  trim to len bytes starting at offset
2727dd7cddfSDavid du Colombier  */
2737dd7cddfSDavid du Colombier Block *
trimblock(Block * bp,int offset,int len)2747dd7cddfSDavid du Colombier trimblock(Block *bp, int offset, int len)
2757dd7cddfSDavid du Colombier {
2767dd7cddfSDavid du Colombier 	ulong l;
2777dd7cddfSDavid du Colombier 	Block *nb, *startb;
2787dd7cddfSDavid du Colombier 
2797dd7cddfSDavid du Colombier 	QDEBUG checkb(bp, "trimblock 1");
2807dd7cddfSDavid du Colombier 	if(blocklen(bp) < offset+len) {
2817dd7cddfSDavid du Colombier 		freeblist(bp);
2827dd7cddfSDavid du Colombier 		return nil;
2837dd7cddfSDavid du Colombier 	}
2847dd7cddfSDavid du Colombier 
2857dd7cddfSDavid du Colombier 	while((l = BLEN(bp)) < offset) {
2867dd7cddfSDavid du Colombier 		offset -= l;
2877dd7cddfSDavid du Colombier 		nb = bp->next;
2887dd7cddfSDavid du Colombier 		bp->next = nil;
2897dd7cddfSDavid du Colombier 		freeb(bp);
2907dd7cddfSDavid du Colombier 		bp = nb;
2917dd7cddfSDavid du Colombier 	}
2927dd7cddfSDavid du Colombier 
2937dd7cddfSDavid du Colombier 	startb = bp;
2947dd7cddfSDavid du Colombier 	bp->rp += offset;
2957dd7cddfSDavid du Colombier 
2967dd7cddfSDavid du Colombier 	while((l = BLEN(bp)) < len) {
2977dd7cddfSDavid du Colombier 		len -= l;
2987dd7cddfSDavid du Colombier 		bp = bp->next;
2997dd7cddfSDavid du Colombier 	}
3007dd7cddfSDavid du Colombier 
3017dd7cddfSDavid du Colombier 	bp->wp -= (BLEN(bp) - len);
3027dd7cddfSDavid du Colombier 
3037dd7cddfSDavid du Colombier 	if(bp->next) {
3047dd7cddfSDavid du Colombier 		freeblist(bp->next);
3057dd7cddfSDavid du Colombier 		bp->next = nil;
3067dd7cddfSDavid du Colombier 	}
3077dd7cddfSDavid du Colombier 
3087dd7cddfSDavid du Colombier 	return startb;
3097dd7cddfSDavid du Colombier }
3107dd7cddfSDavid du Colombier 
3117dd7cddfSDavid du Colombier /*
3127dd7cddfSDavid du Colombier  *  copy 'count' bytes into a new block
3137dd7cddfSDavid du Colombier  */
3147dd7cddfSDavid du Colombier Block*
copyblock(Block * bp,int count)3157dd7cddfSDavid du Colombier copyblock(Block *bp, int count)
3167dd7cddfSDavid du Colombier {
3177dd7cddfSDavid du Colombier 	int l;
3187dd7cddfSDavid du Colombier 	Block *nbp;
3197dd7cddfSDavid du Colombier 
3207dd7cddfSDavid du Colombier 	QDEBUG checkb(bp, "copyblock 0");
3217dd7cddfSDavid du Colombier 	nbp = allocb(count);
3227dd7cddfSDavid du Colombier 	for(; count > 0 && bp != 0; bp = bp->next){
3237dd7cddfSDavid du Colombier 		l = BLEN(bp);
3247dd7cddfSDavid du Colombier 		if(l > count)
3257dd7cddfSDavid du Colombier 			l = count;
3267dd7cddfSDavid du Colombier 		memmove(nbp->wp, bp->rp, l);
3277dd7cddfSDavid du Colombier 		nbp->wp += l;
3287dd7cddfSDavid du Colombier 		count -= l;
3297dd7cddfSDavid du Colombier 	}
3307dd7cddfSDavid du Colombier 	if(count > 0){
3317dd7cddfSDavid du Colombier 		memset(nbp->wp, 0, count);
3327dd7cddfSDavid du Colombier 		nbp->wp += count;
3337dd7cddfSDavid du Colombier 	}
3347dd7cddfSDavid du Colombier 	copyblockcnt++;
3357dd7cddfSDavid du Colombier 	QDEBUG checkb(nbp, "copyblock 1");
3367dd7cddfSDavid du Colombier 
3377dd7cddfSDavid du Colombier 	return nbp;
3387dd7cddfSDavid du Colombier }
3397dd7cddfSDavid du Colombier 
3407dd7cddfSDavid du Colombier Block*
adjustblock(Block * bp,int len)3417dd7cddfSDavid du Colombier adjustblock(Block* bp, int len)
3427dd7cddfSDavid du Colombier {
3437dd7cddfSDavid du Colombier 	int n;
3447dd7cddfSDavid du Colombier 	Block *nbp;
3457dd7cddfSDavid du Colombier 
3467dd7cddfSDavid du Colombier 	if(len < 0){
3477dd7cddfSDavid du Colombier 		freeb(bp);
3487dd7cddfSDavid du Colombier 		return nil;
3497dd7cddfSDavid du Colombier 	}
3507dd7cddfSDavid du Colombier 
3517dd7cddfSDavid du Colombier 	if(bp->rp+len > bp->lim){
3527dd7cddfSDavid du Colombier 		nbp = copyblock(bp, len);
3537dd7cddfSDavid du Colombier 		freeblist(bp);
3547dd7cddfSDavid du Colombier 		QDEBUG checkb(nbp, "adjustblock 1");
3557dd7cddfSDavid du Colombier 
3567dd7cddfSDavid du Colombier 		return nbp;
3577dd7cddfSDavid du Colombier 	}
3587dd7cddfSDavid du Colombier 
3597dd7cddfSDavid du Colombier 	n = BLEN(bp);
3607dd7cddfSDavid du Colombier 	if(len > n)
3617dd7cddfSDavid du Colombier 		memset(bp->wp, 0, len-n);
3627dd7cddfSDavid du Colombier 	bp->wp = bp->rp+len;
3637dd7cddfSDavid du Colombier 	QDEBUG checkb(bp, "adjustblock 2");
3647dd7cddfSDavid du Colombier 
3657dd7cddfSDavid du Colombier 	return bp;
3667dd7cddfSDavid du Colombier }
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier 
3697dd7cddfSDavid du Colombier /*
3707dd7cddfSDavid du Colombier  *  throw away up to count bytes from a
3717dd7cddfSDavid du Colombier  *  list of blocks.  Return count of bytes
3727dd7cddfSDavid du Colombier  *  thrown away.
3737dd7cddfSDavid du Colombier  */
3747dd7cddfSDavid du Colombier int
pullblock(Block ** bph,int count)3757dd7cddfSDavid du Colombier pullblock(Block **bph, int count)
3767dd7cddfSDavid du Colombier {
3777dd7cddfSDavid du Colombier 	Block *bp;
3787dd7cddfSDavid du Colombier 	int n, bytes;
3797dd7cddfSDavid du Colombier 
3807dd7cddfSDavid du Colombier 	bytes = 0;
3817dd7cddfSDavid du Colombier 	if(bph == nil)
3827dd7cddfSDavid du Colombier 		return 0;
3837dd7cddfSDavid du Colombier 
3847dd7cddfSDavid du Colombier 	while(*bph != nil && count != 0) {
3857dd7cddfSDavid du Colombier 		bp = *bph;
3867dd7cddfSDavid du Colombier 		n = BLEN(bp);
3877dd7cddfSDavid du Colombier 		if(count < n)
3887dd7cddfSDavid du Colombier 			n = count;
3897dd7cddfSDavid du Colombier 		bytes += n;
3907dd7cddfSDavid du Colombier 		count -= n;
3917dd7cddfSDavid du Colombier 		bp->rp += n;
3927dd7cddfSDavid du Colombier 		QDEBUG checkb(bp, "pullblock ");
3937dd7cddfSDavid du Colombier 		if(BLEN(bp) == 0) {
3947dd7cddfSDavid du Colombier 			*bph = bp->next;
3957dd7cddfSDavid du Colombier 			bp->next = nil;
3967dd7cddfSDavid du Colombier 			freeb(bp);
3977dd7cddfSDavid du Colombier 		}
3987dd7cddfSDavid du Colombier 	}
3997dd7cddfSDavid du Colombier 	return bytes;
4007dd7cddfSDavid du Colombier }
4017dd7cddfSDavid du Colombier 
4027dd7cddfSDavid du Colombier /*
4037dd7cddfSDavid du Colombier  *  get next block from a queue, return null if nothing there
4047dd7cddfSDavid du Colombier  */
4057dd7cddfSDavid du Colombier Block*
qget(Queue * q)4067dd7cddfSDavid du Colombier qget(Queue *q)
4077dd7cddfSDavid du Colombier {
4087dd7cddfSDavid du Colombier 	int dowakeup;
4097dd7cddfSDavid du Colombier 	Block *b;
4107dd7cddfSDavid du Colombier 
4117dd7cddfSDavid du Colombier 	/* sync with qwrite */
4127dd7cddfSDavid du Colombier 	ilock(q);
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier 	b = q->bfirst;
415d9306527SDavid du Colombier 	if(b == nil){
4167dd7cddfSDavid du Colombier 		q->state |= Qstarve;
4177dd7cddfSDavid du Colombier 		iunlock(q);
418d9306527SDavid du Colombier 		return nil;
4197dd7cddfSDavid du Colombier 	}
4207dd7cddfSDavid du Colombier 	q->bfirst = b->next;
4217dd7cddfSDavid du Colombier 	b->next = 0;
4227dd7cddfSDavid du Colombier 	q->len -= BALLOC(b);
4237dd7cddfSDavid du Colombier 	q->dlen -= BLEN(b);
4247dd7cddfSDavid du Colombier 	QDEBUG checkb(b, "qget");
4257dd7cddfSDavid du Colombier 
4267dd7cddfSDavid du Colombier 	/* if writer flow controlled, restart */
4277dd7cddfSDavid du Colombier 	if((q->state & Qflow) && q->len < q->limit/2){
4287dd7cddfSDavid du Colombier 		q->state &= ~Qflow;
4297dd7cddfSDavid du Colombier 		dowakeup = 1;
4307dd7cddfSDavid du Colombier 	} else
4317dd7cddfSDavid du Colombier 		dowakeup = 0;
4327dd7cddfSDavid du Colombier 
4337dd7cddfSDavid du Colombier 	iunlock(q);
4347dd7cddfSDavid du Colombier 
4357dd7cddfSDavid du Colombier 	if(dowakeup)
4367dd7cddfSDavid du Colombier 		wakeup(&q->wr);
4377dd7cddfSDavid du Colombier 
4387dd7cddfSDavid du Colombier 	return b;
4397dd7cddfSDavid du Colombier }
4407dd7cddfSDavid du Colombier 
4417dd7cddfSDavid du Colombier /*
4427dd7cddfSDavid du Colombier  *  throw away the next 'len' bytes in the queue
4437dd7cddfSDavid du Colombier  */
44480ee5cbfSDavid du Colombier int
qdiscard(Queue * q,int len)4457dd7cddfSDavid du Colombier qdiscard(Queue *q, int len)
4467dd7cddfSDavid du Colombier {
4477dd7cddfSDavid du Colombier 	Block *b;
4487dd7cddfSDavid du Colombier 	int dowakeup, n, sofar;
4497dd7cddfSDavid du Colombier 
4507dd7cddfSDavid du Colombier 	ilock(q);
4517dd7cddfSDavid du Colombier 	for(sofar = 0; sofar < len; sofar += n){
4527dd7cddfSDavid du Colombier 		b = q->bfirst;
4537dd7cddfSDavid du Colombier 		if(b == nil)
4547dd7cddfSDavid du Colombier 			break;
4557dd7cddfSDavid du Colombier 		QDEBUG checkb(b, "qdiscard");
4567dd7cddfSDavid du Colombier 		n = BLEN(b);
4577dd7cddfSDavid du Colombier 		if(n <= len - sofar){
4587dd7cddfSDavid du Colombier 			q->bfirst = b->next;
4597dd7cddfSDavid du Colombier 			b->next = 0;
4607dd7cddfSDavid du Colombier 			q->len -= BALLOC(b);
4617dd7cddfSDavid du Colombier 			q->dlen -= BLEN(b);
4627dd7cddfSDavid du Colombier 			freeb(b);
4637dd7cddfSDavid du Colombier 		} else {
4647dd7cddfSDavid du Colombier 			n = len - sofar;
4657dd7cddfSDavid du Colombier 			b->rp += n;
4667dd7cddfSDavid du Colombier 			q->dlen -= n;
4677dd7cddfSDavid du Colombier 		}
4687dd7cddfSDavid du Colombier 	}
4697dd7cddfSDavid du Colombier 
4703ff48bf5SDavid du Colombier 	/*
4713ff48bf5SDavid du Colombier 	 *  if writer flow controlled, restart
4723ff48bf5SDavid du Colombier 	 *
4733ff48bf5SDavid du Colombier 	 *  This used to be
4743ff48bf5SDavid du Colombier 	 *	q->len < q->limit/2
4753ff48bf5SDavid du Colombier 	 *  but it slows down tcp too much for certain write sizes.
4763ff48bf5SDavid du Colombier 	 *  I really don't understand it completely.  It may be
4773ff48bf5SDavid du Colombier 	 *  due to the queue draining so fast that the transmission
4783ff48bf5SDavid du Colombier 	 *  stalls waiting for the app to produce more data.  - presotto
4793ff48bf5SDavid du Colombier 	 */
4803ff48bf5SDavid du Colombier 	if((q->state & Qflow) && q->len < q->limit){
4817dd7cddfSDavid du Colombier 		q->state &= ~Qflow;
4827dd7cddfSDavid du Colombier 		dowakeup = 1;
4837dd7cddfSDavid du Colombier 	} else
4847dd7cddfSDavid du Colombier 		dowakeup = 0;
4857dd7cddfSDavid du Colombier 
4867dd7cddfSDavid du Colombier 	iunlock(q);
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier 	if(dowakeup)
4897dd7cddfSDavid du Colombier 		wakeup(&q->wr);
49080ee5cbfSDavid du Colombier 
49180ee5cbfSDavid du Colombier 	return sofar;
4927dd7cddfSDavid du Colombier }
4937dd7cddfSDavid du Colombier 
4947dd7cddfSDavid du Colombier /*
4957dd7cddfSDavid du Colombier  *  Interrupt level copy out of a queue, return # bytes copied.
4967dd7cddfSDavid du Colombier  */
4977dd7cddfSDavid du Colombier int
qconsume(Queue * q,void * vp,int len)4987dd7cddfSDavid du Colombier qconsume(Queue *q, void *vp, int len)
4997dd7cddfSDavid du Colombier {
5007dd7cddfSDavid du Colombier 	Block *b;
5017dd7cddfSDavid du Colombier 	int n, dowakeup;
5027dd7cddfSDavid du Colombier 	uchar *p = vp;
5037dd7cddfSDavid du Colombier 	Block *tofree = nil;
5047dd7cddfSDavid du Colombier 
5057dd7cddfSDavid du Colombier 	/* sync with qwrite */
5067dd7cddfSDavid du Colombier 	ilock(q);
5077dd7cddfSDavid du Colombier 
5087dd7cddfSDavid du Colombier 	for(;;) {
5097dd7cddfSDavid du Colombier 		b = q->bfirst;
5107dd7cddfSDavid du Colombier 		if(b == 0){
5117dd7cddfSDavid du Colombier 			q->state |= Qstarve;
5127dd7cddfSDavid du Colombier 			iunlock(q);
5137dd7cddfSDavid du Colombier 			return -1;
5147dd7cddfSDavid du Colombier 		}
5157dd7cddfSDavid du Colombier 		QDEBUG checkb(b, "qconsume 1");
5167dd7cddfSDavid du Colombier 
5177dd7cddfSDavid du Colombier 		n = BLEN(b);
5187dd7cddfSDavid du Colombier 		if(n > 0)
5197dd7cddfSDavid du Colombier 			break;
5207dd7cddfSDavid du Colombier 		q->bfirst = b->next;
5217dd7cddfSDavid du Colombier 		q->len -= BALLOC(b);
5227dd7cddfSDavid du Colombier 
5237dd7cddfSDavid du Colombier 		/* remember to free this */
5247dd7cddfSDavid du Colombier 		b->next = tofree;
5257dd7cddfSDavid du Colombier 		tofree = b;
5267dd7cddfSDavid du Colombier 	};
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 	if(n < len)
5297dd7cddfSDavid du Colombier 		len = n;
5307dd7cddfSDavid du Colombier 	memmove(p, b->rp, len);
5317dd7cddfSDavid du Colombier 	consumecnt += n;
5327dd7cddfSDavid du Colombier 	b->rp += len;
5337dd7cddfSDavid du Colombier 	q->dlen -= len;
5347dd7cddfSDavid du Colombier 
5357dd7cddfSDavid du Colombier 	/* discard the block if we're done with it */
5367dd7cddfSDavid du Colombier 	if((q->state & Qmsg) || len == n){
537d9306527SDavid du Colombier 		q->bfirst = b->next;
5387dd7cddfSDavid du Colombier 		b->next = 0;
5397dd7cddfSDavid du Colombier 		q->len -= BALLOC(b);
5407dd7cddfSDavid du Colombier 		q->dlen -= BLEN(b);
5417dd7cddfSDavid du Colombier 
5427dd7cddfSDavid du Colombier 		/* remember to free this */
5437dd7cddfSDavid du Colombier 		b->next = tofree;
5447dd7cddfSDavid du Colombier 		tofree = b;
5457dd7cddfSDavid du Colombier 	}
5467dd7cddfSDavid du Colombier 
5477dd7cddfSDavid du Colombier 	/* if writer flow controlled, restart */
5487dd7cddfSDavid du Colombier 	if((q->state & Qflow) && q->len < q->limit/2){
5497dd7cddfSDavid du Colombier 		q->state &= ~Qflow;
5507dd7cddfSDavid du Colombier 		dowakeup = 1;
5517dd7cddfSDavid du Colombier 	} else
5527dd7cddfSDavid du Colombier 		dowakeup = 0;
5537dd7cddfSDavid du Colombier 
5547dd7cddfSDavid du Colombier 	iunlock(q);
5557dd7cddfSDavid du Colombier 
5567dd7cddfSDavid du Colombier 	if(dowakeup)
5577dd7cddfSDavid du Colombier 		wakeup(&q->wr);
5587dd7cddfSDavid du Colombier 
5597dd7cddfSDavid du Colombier 	if(tofree != nil)
5607dd7cddfSDavid du Colombier 		freeblist(tofree);
5617dd7cddfSDavid du Colombier 
5627dd7cddfSDavid du Colombier 	return len;
5637dd7cddfSDavid du Colombier }
5647dd7cddfSDavid du Colombier 
5657dd7cddfSDavid du Colombier int
qpass(Queue * q,Block * b)5667dd7cddfSDavid du Colombier qpass(Queue *q, Block *b)
5677dd7cddfSDavid du Colombier {
5687dd7cddfSDavid du Colombier 	int dlen, len, dowakeup;
5697dd7cddfSDavid du Colombier 
5707dd7cddfSDavid du Colombier 	/* sync with qread */
5717dd7cddfSDavid du Colombier 	dowakeup = 0;
5727dd7cddfSDavid du Colombier 	ilock(q);
5737dd7cddfSDavid du Colombier 	if(q->len >= q->limit){
5747dd7cddfSDavid du Colombier 		freeblist(b);
5757dd7cddfSDavid du Colombier 		iunlock(q);
5767dd7cddfSDavid du Colombier 		return -1;
5777dd7cddfSDavid du Colombier 	}
57880ee5cbfSDavid du Colombier 	if(q->state & Qclosed){
579a66c99ebSDavid du Colombier 		len = BALLOC(b);
58080ee5cbfSDavid du Colombier 		freeblist(b);
58180ee5cbfSDavid du Colombier 		iunlock(q);
582a66c99ebSDavid du Colombier 		return len;
58380ee5cbfSDavid du Colombier 	}
5847dd7cddfSDavid du Colombier 
5857dd7cddfSDavid du Colombier 	/* add buffer to queue */
5867dd7cddfSDavid du Colombier 	if(q->bfirst)
5877dd7cddfSDavid du Colombier 		q->blast->next = b;
5887dd7cddfSDavid du Colombier 	else
5897dd7cddfSDavid du Colombier 		q->bfirst = b;
5907dd7cddfSDavid du Colombier 	len = BALLOC(b);
5917dd7cddfSDavid du Colombier 	dlen = BLEN(b);
5927dd7cddfSDavid du Colombier 	QDEBUG checkb(b, "qpass");
5937dd7cddfSDavid du Colombier 	while(b->next){
5947dd7cddfSDavid du Colombier 		b = b->next;
5957dd7cddfSDavid du Colombier 		QDEBUG checkb(b, "qpass");
5967dd7cddfSDavid du Colombier 		len += BALLOC(b);
5977dd7cddfSDavid du Colombier 		dlen += BLEN(b);
5987dd7cddfSDavid du Colombier 	}
5997dd7cddfSDavid du Colombier 	q->blast = b;
6007dd7cddfSDavid du Colombier 	q->len += len;
6017dd7cddfSDavid du Colombier 	q->dlen += dlen;
6027dd7cddfSDavid du Colombier 
6037dd7cddfSDavid du Colombier 	if(q->len >= q->limit/2)
6047dd7cddfSDavid du Colombier 		q->state |= Qflow;
6057dd7cddfSDavid du Colombier 
6067dd7cddfSDavid du Colombier 	if(q->state & Qstarve){
6077dd7cddfSDavid du Colombier 		q->state &= ~Qstarve;
6087dd7cddfSDavid du Colombier 		dowakeup = 1;
6097dd7cddfSDavid du Colombier 	}
6107dd7cddfSDavid du Colombier 	iunlock(q);
6117dd7cddfSDavid du Colombier 
6127dd7cddfSDavid du Colombier 	if(dowakeup)
6137dd7cddfSDavid du Colombier 		wakeup(&q->rr);
6147dd7cddfSDavid du Colombier 
6157dd7cddfSDavid du Colombier 	return len;
6167dd7cddfSDavid du Colombier }
6177dd7cddfSDavid du Colombier 
6187dd7cddfSDavid du Colombier int
qpassnolim(Queue * q,Block * b)6197dd7cddfSDavid du Colombier qpassnolim(Queue *q, Block *b)
6207dd7cddfSDavid du Colombier {
6217dd7cddfSDavid du Colombier 	int dlen, len, dowakeup;
6227dd7cddfSDavid du Colombier 
6237dd7cddfSDavid du Colombier 	/* sync with qread */
6247dd7cddfSDavid du Colombier 	dowakeup = 0;
6257dd7cddfSDavid du Colombier 	ilock(q);
6267dd7cddfSDavid du Colombier 
62780ee5cbfSDavid du Colombier 	if(q->state & Qclosed){
62880ee5cbfSDavid du Colombier 		freeblist(b);
62980ee5cbfSDavid du Colombier 		iunlock(q);
63080ee5cbfSDavid du Colombier 		return BALLOC(b);
63180ee5cbfSDavid du Colombier 	}
63280ee5cbfSDavid du Colombier 
6337dd7cddfSDavid du Colombier 	/* add buffer to queue */
6347dd7cddfSDavid du Colombier 	if(q->bfirst)
6357dd7cddfSDavid du Colombier 		q->blast->next = b;
6367dd7cddfSDavid du Colombier 	else
6377dd7cddfSDavid du Colombier 		q->bfirst = b;
6387dd7cddfSDavid du Colombier 	len = BALLOC(b);
6397dd7cddfSDavid du Colombier 	dlen = BLEN(b);
6407dd7cddfSDavid du Colombier 	QDEBUG checkb(b, "qpass");
6417dd7cddfSDavid du Colombier 	while(b->next){
6427dd7cddfSDavid du Colombier 		b = b->next;
6437dd7cddfSDavid du Colombier 		QDEBUG checkb(b, "qpass");
6447dd7cddfSDavid du Colombier 		len += BALLOC(b);
6457dd7cddfSDavid du Colombier 		dlen += BLEN(b);
6467dd7cddfSDavid du Colombier 	}
6477dd7cddfSDavid du Colombier 	q->blast = b;
6487dd7cddfSDavid du Colombier 	q->len += len;
6497dd7cddfSDavid du Colombier 	q->dlen += dlen;
6507dd7cddfSDavid du Colombier 
6517dd7cddfSDavid du Colombier 	if(q->len >= q->limit/2)
6527dd7cddfSDavid du Colombier 		q->state |= Qflow;
6537dd7cddfSDavid du Colombier 
6547dd7cddfSDavid du Colombier 	if(q->state & Qstarve){
6557dd7cddfSDavid du Colombier 		q->state &= ~Qstarve;
6567dd7cddfSDavid du Colombier 		dowakeup = 1;
6577dd7cddfSDavid du Colombier 	}
6587dd7cddfSDavid du Colombier 	iunlock(q);
6597dd7cddfSDavid du Colombier 
6607dd7cddfSDavid du Colombier 	if(dowakeup)
6617dd7cddfSDavid du Colombier 		wakeup(&q->rr);
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier 	return len;
6647dd7cddfSDavid du Colombier }
6657dd7cddfSDavid du Colombier 
6667dd7cddfSDavid du Colombier /*
6677dd7cddfSDavid du Colombier  *  if the allocated space is way out of line with the used
6687dd7cddfSDavid du Colombier  *  space, reallocate to a smaller block
6697dd7cddfSDavid du Colombier  */
6707dd7cddfSDavid du Colombier Block*
packblock(Block * bp)6717dd7cddfSDavid du Colombier packblock(Block *bp)
6727dd7cddfSDavid du Colombier {
6737dd7cddfSDavid du Colombier 	Block **l, *nbp;
6747dd7cddfSDavid du Colombier 	int n;
6757dd7cddfSDavid du Colombier 
6767dd7cddfSDavid du Colombier 	for(l = &bp; *l; l = &(*l)->next){
6777dd7cddfSDavid du Colombier 		nbp = *l;
6787dd7cddfSDavid du Colombier 		n = BLEN(nbp);
6797dd7cddfSDavid du Colombier 		if((n<<2) < BALLOC(nbp)){
6807dd7cddfSDavid du Colombier 			*l = allocb(n);
6817dd7cddfSDavid du Colombier 			memmove((*l)->wp, nbp->rp, n);
6827dd7cddfSDavid du Colombier 			(*l)->wp += n;
6837dd7cddfSDavid du Colombier 			(*l)->next = nbp->next;
6847dd7cddfSDavid du Colombier 			freeb(nbp);
6857dd7cddfSDavid du Colombier 		}
6867dd7cddfSDavid du Colombier 	}
6877dd7cddfSDavid du Colombier 
6887dd7cddfSDavid du Colombier 	return bp;
6897dd7cddfSDavid du Colombier }
6907dd7cddfSDavid du Colombier 
6917dd7cddfSDavid du Colombier int
qproduce(Queue * q,void * vp,int len)6927dd7cddfSDavid du Colombier qproduce(Queue *q, void *vp, int len)
6937dd7cddfSDavid du Colombier {
6947dd7cddfSDavid du Colombier 	Block *b;
6957dd7cddfSDavid du Colombier 	int dowakeup;
6967dd7cddfSDavid du Colombier 	uchar *p = vp;
6977dd7cddfSDavid du Colombier 
6987dd7cddfSDavid du Colombier 	/* sync with qread */
6997dd7cddfSDavid du Colombier 	dowakeup = 0;
7007dd7cddfSDavid du Colombier 	ilock(q);
7017dd7cddfSDavid du Colombier 
7027dd7cddfSDavid du Colombier 	/* no waiting receivers, room in buffer? */
7037dd7cddfSDavid du Colombier 	if(q->len >= q->limit){
7047dd7cddfSDavid du Colombier 		q->state |= Qflow;
7057dd7cddfSDavid du Colombier 		iunlock(q);
7067dd7cddfSDavid du Colombier 		return -1;
7077dd7cddfSDavid du Colombier 	}
7087dd7cddfSDavid du Colombier 
7097dd7cddfSDavid du Colombier 	/* save in buffer */
7107dd7cddfSDavid du Colombier 	b = iallocb(len);
7117dd7cddfSDavid du Colombier 	if(b == 0){
7127dd7cddfSDavid du Colombier 		iunlock(q);
7137dd7cddfSDavid du Colombier 		return 0;
7147dd7cddfSDavid du Colombier 	}
7157dd7cddfSDavid du Colombier 	memmove(b->wp, p, len);
7167dd7cddfSDavid du Colombier 	producecnt += len;
7177dd7cddfSDavid du Colombier 	b->wp += len;
7187dd7cddfSDavid du Colombier 	if(q->bfirst)
7197dd7cddfSDavid du Colombier 		q->blast->next = b;
7207dd7cddfSDavid du Colombier 	else
7217dd7cddfSDavid du Colombier 		q->bfirst = b;
7227dd7cddfSDavid du Colombier 	q->blast = b;
7237dd7cddfSDavid du Colombier 	/* b->next = 0; done by iallocb() */
7247dd7cddfSDavid du Colombier 	q->len += BALLOC(b);
7257dd7cddfSDavid du Colombier 	q->dlen += BLEN(b);
7267dd7cddfSDavid du Colombier 	QDEBUG checkb(b, "qproduce");
7277dd7cddfSDavid du Colombier 
7287dd7cddfSDavid du Colombier 	if(q->state & Qstarve){
7297dd7cddfSDavid du Colombier 		q->state &= ~Qstarve;
7307dd7cddfSDavid du Colombier 		dowakeup = 1;
7317dd7cddfSDavid du Colombier 	}
7327dd7cddfSDavid du Colombier 
7337dd7cddfSDavid du Colombier 	if(q->len >= q->limit)
7347dd7cddfSDavid du Colombier 		q->state |= Qflow;
7357dd7cddfSDavid du Colombier 	iunlock(q);
7367dd7cddfSDavid du Colombier 
7377dd7cddfSDavid du Colombier 	if(dowakeup)
7387dd7cddfSDavid du Colombier 		wakeup(&q->rr);
7397dd7cddfSDavid du Colombier 
7407dd7cddfSDavid du Colombier 	return len;
7417dd7cddfSDavid du Colombier }
7427dd7cddfSDavid du Colombier 
7437dd7cddfSDavid du Colombier /*
7447dd7cddfSDavid du Colombier  *  copy from offset in the queue
7457dd7cddfSDavid du Colombier  */
7467dd7cddfSDavid du Colombier Block*
qcopy(Queue * q,int len,ulong offset)7477dd7cddfSDavid du Colombier qcopy(Queue *q, int len, ulong offset)
7487dd7cddfSDavid du Colombier {
7497dd7cddfSDavid du Colombier 	int sofar;
7507dd7cddfSDavid du Colombier 	int n;
7517dd7cddfSDavid du Colombier 	Block *b, *nb;
7527dd7cddfSDavid du Colombier 	uchar *p;
7537dd7cddfSDavid du Colombier 
7547dd7cddfSDavid du Colombier 	nb = allocb(len);
7557dd7cddfSDavid du Colombier 
7567dd7cddfSDavid du Colombier 	ilock(q);
7577dd7cddfSDavid du Colombier 
7587dd7cddfSDavid du Colombier 	/* go to offset */
7597dd7cddfSDavid du Colombier 	b = q->bfirst;
7607dd7cddfSDavid du Colombier 	for(sofar = 0; ; sofar += n){
7617dd7cddfSDavid du Colombier 		if(b == nil){
7627dd7cddfSDavid du Colombier 			iunlock(q);
7637dd7cddfSDavid du Colombier 			return nb;
7647dd7cddfSDavid du Colombier 		}
7657dd7cddfSDavid du Colombier 		n = BLEN(b);
7667dd7cddfSDavid du Colombier 		if(sofar + n > offset){
7677dd7cddfSDavid du Colombier 			p = b->rp + offset - sofar;
7687dd7cddfSDavid du Colombier 			n -= offset - sofar;
7697dd7cddfSDavid du Colombier 			break;
7707dd7cddfSDavid du Colombier 		}
7717dd7cddfSDavid du Colombier 		QDEBUG checkb(b, "qcopy");
7727dd7cddfSDavid du Colombier 		b = b->next;
7737dd7cddfSDavid du Colombier 	}
7747dd7cddfSDavid du Colombier 
7757dd7cddfSDavid du Colombier 	/* copy bytes from there */
7767dd7cddfSDavid du Colombier 	for(sofar = 0; sofar < len;){
7777dd7cddfSDavid du Colombier 		if(n > len - sofar)
7787dd7cddfSDavid du Colombier 			n = len - sofar;
7797dd7cddfSDavid du Colombier 		memmove(nb->wp, p, n);
7807dd7cddfSDavid du Colombier 		qcopycnt += n;
7817dd7cddfSDavid du Colombier 		sofar += n;
7827dd7cddfSDavid du Colombier 		nb->wp += n;
7837dd7cddfSDavid du Colombier 		b = b->next;
7847dd7cddfSDavid du Colombier 		if(b == nil)
7857dd7cddfSDavid du Colombier 			break;
7867dd7cddfSDavid du Colombier 		n = BLEN(b);
7877dd7cddfSDavid du Colombier 		p = b->rp;
7887dd7cddfSDavid du Colombier 	}
7897dd7cddfSDavid du Colombier 	iunlock(q);
7907dd7cddfSDavid du Colombier 
7917dd7cddfSDavid du Colombier 	return nb;
7927dd7cddfSDavid du Colombier }
7937dd7cddfSDavid du Colombier 
7947dd7cddfSDavid du Colombier /*
7957dd7cddfSDavid du Colombier  *  called by non-interrupt code
7967dd7cddfSDavid du Colombier  */
7977dd7cddfSDavid du Colombier Queue*
qopen(int limit,int msg,void (* kick)(void *),void * arg)7987dd7cddfSDavid du Colombier qopen(int limit, int msg, void (*kick)(void*), void *arg)
7997dd7cddfSDavid du Colombier {
8007dd7cddfSDavid du Colombier 	Queue *q;
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier 	q = malloc(sizeof(Queue));
8037dd7cddfSDavid du Colombier 	if(q == 0)
8047dd7cddfSDavid du Colombier 		return 0;
8057dd7cddfSDavid du Colombier 
8067dd7cddfSDavid du Colombier 	q->limit = q->inilim = limit;
8077dd7cddfSDavid du Colombier 	q->kick = kick;
8087dd7cddfSDavid du Colombier 	q->arg = arg;
8093ff48bf5SDavid du Colombier 	q->state = msg;
81080ee5cbfSDavid du Colombier 
8117dd7cddfSDavid du Colombier 	q->state |= Qstarve;
8127dd7cddfSDavid du Colombier 	q->eof = 0;
8137dd7cddfSDavid du Colombier 	q->noblock = 0;
814e6c6b7f8SDavid du Colombier 
815e6c6b7f8SDavid du Colombier 	return q;
816e6c6b7f8SDavid du Colombier }
817e6c6b7f8SDavid du Colombier 
818e6c6b7f8SDavid du Colombier /* open a queue to be bypassed */
819e6c6b7f8SDavid du Colombier Queue*
qbypass(void (* bypass)(void *,Block *),void * arg)820e6c6b7f8SDavid du Colombier qbypass(void (*bypass)(void*, Block*), void *arg)
821e6c6b7f8SDavid du Colombier {
822e6c6b7f8SDavid du Colombier 	Queue *q;
823e6c6b7f8SDavid du Colombier 
824e6c6b7f8SDavid du Colombier 	q = malloc(sizeof(Queue));
825e6c6b7f8SDavid du Colombier 	if(q == 0)
826e6c6b7f8SDavid du Colombier 		return 0;
827e6c6b7f8SDavid du Colombier 
828e6c6b7f8SDavid du Colombier 	q->limit = 0;
829e6c6b7f8SDavid du Colombier 	q->arg = arg;
830e6c6b7f8SDavid du Colombier 	q->bypass = bypass;
831e6c6b7f8SDavid du Colombier 	q->state = 0;
8327dd7cddfSDavid du Colombier 
8337dd7cddfSDavid du Colombier 	return q;
8347dd7cddfSDavid du Colombier }
8357dd7cddfSDavid du Colombier 
8367dd7cddfSDavid du Colombier static int
notempty(void * a)8377dd7cddfSDavid du Colombier notempty(void *a)
8387dd7cddfSDavid du Colombier {
8397dd7cddfSDavid du Colombier 	Queue *q = a;
8407dd7cddfSDavid du Colombier 
8417dd7cddfSDavid du Colombier 	return (q->state & Qclosed) || q->bfirst != 0;
8427dd7cddfSDavid du Colombier }
8437dd7cddfSDavid du Colombier 
8447dd7cddfSDavid du Colombier /*
84580ee5cbfSDavid du Colombier  *  wait for the queue to be non-empty or closed.
84680ee5cbfSDavid du Colombier  *  called with q ilocked.
8477dd7cddfSDavid du Colombier  */
84880ee5cbfSDavid du Colombier static int
qwait(Queue * q)84980ee5cbfSDavid du Colombier qwait(Queue *q)
8507dd7cddfSDavid du Colombier {
8517dd7cddfSDavid du Colombier 	/* wait for data */
8527dd7cddfSDavid du Colombier 	for(;;){
85380ee5cbfSDavid du Colombier 		if(q->bfirst != nil)
8547dd7cddfSDavid du Colombier 			break;
8557dd7cddfSDavid du Colombier 
8567dd7cddfSDavid du Colombier 		if(q->state & Qclosed){
8577dd7cddfSDavid du Colombier 			if(++q->eof > 3)
85880ee5cbfSDavid du Colombier 				return -1;
8599a747e4fSDavid du Colombier 			if(*q->err && strcmp(q->err, Ehungup) != 0)
8609a747e4fSDavid du Colombier 				return -1;
8617dd7cddfSDavid du Colombier 			return 0;
8627dd7cddfSDavid du Colombier 		}
8637dd7cddfSDavid du Colombier 
8647dd7cddfSDavid du Colombier 		q->state |= Qstarve;	/* flag requesting producer to wake me */
8657dd7cddfSDavid du Colombier 		iunlock(q);
8667dd7cddfSDavid du Colombier 		sleep(&q->rr, notempty, q);
86780ee5cbfSDavid du Colombier 		ilock(q);
86880ee5cbfSDavid du Colombier 	}
86980ee5cbfSDavid du Colombier 	return 1;
8707dd7cddfSDavid du Colombier }
8717dd7cddfSDavid du Colombier 
87280ee5cbfSDavid du Colombier /*
8739a747e4fSDavid du Colombier  * add a block list to a queue
8749a747e4fSDavid du Colombier  */
8759a747e4fSDavid du Colombier void
qaddlist(Queue * q,Block * b)8769a747e4fSDavid du Colombier qaddlist(Queue *q, Block *b)
8779a747e4fSDavid du Colombier {
8789a747e4fSDavid du Colombier 	/* queue the block */
8799a747e4fSDavid du Colombier 	if(q->bfirst)
8809a747e4fSDavid du Colombier 		q->blast->next = b;
8819a747e4fSDavid du Colombier 	else
8829a747e4fSDavid du Colombier 		q->bfirst = b;
8839a747e4fSDavid du Colombier 	q->len += blockalloclen(b);
8849a747e4fSDavid du Colombier 	q->dlen += blocklen(b);
8859a747e4fSDavid du Colombier 	while(b->next)
8869a747e4fSDavid du Colombier 		b = b->next;
8879a747e4fSDavid du Colombier 	q->blast = b;
8889a747e4fSDavid du Colombier }
8899a747e4fSDavid du Colombier 
8909a747e4fSDavid du Colombier /*
89180ee5cbfSDavid du Colombier  *  called with q ilocked
89280ee5cbfSDavid du Colombier  */
8939a747e4fSDavid du Colombier Block*
qremove(Queue * q)89480ee5cbfSDavid du Colombier qremove(Queue *q)
89580ee5cbfSDavid du Colombier {
89680ee5cbfSDavid du Colombier 	Block *b;
89780ee5cbfSDavid du Colombier 
89880ee5cbfSDavid du Colombier 	b = q->bfirst;
89980ee5cbfSDavid du Colombier 	if(b == nil)
90080ee5cbfSDavid du Colombier 		return nil;
9017dd7cddfSDavid du Colombier 	q->bfirst = b->next;
90280ee5cbfSDavid du Colombier 	b->next = nil;
90380ee5cbfSDavid du Colombier 	q->dlen -= BLEN(b);
9047dd7cddfSDavid du Colombier 	q->len -= BALLOC(b);
90580ee5cbfSDavid du Colombier 	QDEBUG checkb(b, "qremove");
90680ee5cbfSDavid du Colombier 	return b;
90780ee5cbfSDavid du Colombier }
9087dd7cddfSDavid du Colombier 
90980ee5cbfSDavid du Colombier /*
9109a747e4fSDavid du Colombier  *  copy the contents of a string of blocks into
9119a747e4fSDavid du Colombier  *  memory.  emptied blocks are freed.  return
9129a747e4fSDavid du Colombier  *  pointer to first unconsumed block.
9139a747e4fSDavid du Colombier  */
9149a747e4fSDavid du Colombier Block*
bl2mem(uchar * p,Block * b,int n)9159a747e4fSDavid du Colombier bl2mem(uchar *p, Block *b, int n)
9169a747e4fSDavid du Colombier {
9179a747e4fSDavid du Colombier 	int i;
9189a747e4fSDavid du Colombier 	Block *next;
9199a747e4fSDavid du Colombier 
9209a747e4fSDavid du Colombier 	for(; b != nil; b = next){
9219a747e4fSDavid du Colombier 		i = BLEN(b);
9229a747e4fSDavid du Colombier 		if(i > n){
9239a747e4fSDavid du Colombier 			memmove(p, b->rp, n);
9249a747e4fSDavid du Colombier 			b->rp += n;
9259a747e4fSDavid du Colombier 			return b;
9269a747e4fSDavid du Colombier 		}
9279a747e4fSDavid du Colombier 		memmove(p, b->rp, i);
9289a747e4fSDavid du Colombier 		n -= i;
9299a747e4fSDavid du Colombier 		p += i;
9309a747e4fSDavid du Colombier 		b->rp += i;
9319a747e4fSDavid du Colombier 		next = b->next;
9329a747e4fSDavid du Colombier 		freeb(b);
9339a747e4fSDavid du Colombier 	}
9349a747e4fSDavid du Colombier 	return nil;
9359a747e4fSDavid du Colombier }
9369a747e4fSDavid du Colombier 
9379a747e4fSDavid du Colombier /*
9389a747e4fSDavid du Colombier  *  copy the contents of memory into a string of blocks.
9399a747e4fSDavid du Colombier  *  return nil on error.
9409a747e4fSDavid du Colombier  */
9419a747e4fSDavid du Colombier Block*
mem2bl(uchar * p,int len)9429a747e4fSDavid du Colombier mem2bl(uchar *p, int len)
9439a747e4fSDavid du Colombier {
9449a747e4fSDavid du Colombier 	int n;
9459a747e4fSDavid du Colombier 	Block *b, *first, **l;
9469a747e4fSDavid du Colombier 
9479a747e4fSDavid du Colombier 	first = nil;
9489a747e4fSDavid du Colombier 	l = &first;
9499a747e4fSDavid du Colombier 	if(waserror()){
9509a747e4fSDavid du Colombier 		freeblist(first);
9519a747e4fSDavid du Colombier 		nexterror();
9529a747e4fSDavid du Colombier 	}
9539a747e4fSDavid du Colombier 	do {
9549a747e4fSDavid du Colombier 		n = len;
9559a747e4fSDavid du Colombier 		if(n > Maxatomic)
9569a747e4fSDavid du Colombier 			n = Maxatomic;
9579a747e4fSDavid du Colombier 
9589a747e4fSDavid du Colombier 		*l = b = allocb(n);
9599a747e4fSDavid du Colombier 		setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]);
9609a747e4fSDavid du Colombier 		memmove(b->wp, p, n);
9619a747e4fSDavid du Colombier 		b->wp += n;
9629a747e4fSDavid du Colombier 		p += n;
9639a747e4fSDavid du Colombier 		len -= n;
9649a747e4fSDavid du Colombier 		l = &b->next;
9659a747e4fSDavid du Colombier 	} while(len > 0);
9669a747e4fSDavid du Colombier 	poperror();
9679a747e4fSDavid du Colombier 
9689a747e4fSDavid du Colombier 	return first;
9699a747e4fSDavid du Colombier }
9709a747e4fSDavid du Colombier 
9719a747e4fSDavid du Colombier /*
97280ee5cbfSDavid du Colombier  *  put a block back to the front of the queue
97380ee5cbfSDavid du Colombier  *  called with q ilocked
97480ee5cbfSDavid du Colombier  */
9759a747e4fSDavid du Colombier void
qputback(Queue * q,Block * b)97680ee5cbfSDavid du Colombier qputback(Queue *q, Block *b)
97780ee5cbfSDavid du Colombier {
9787dd7cddfSDavid du Colombier 	b->next = q->bfirst;
97980ee5cbfSDavid du Colombier 	if(q->bfirst == nil)
9807dd7cddfSDavid du Colombier 		q->blast = b;
9817dd7cddfSDavid du Colombier 	q->bfirst = b;
9827dd7cddfSDavid du Colombier 	q->len += BALLOC(b);
98380ee5cbfSDavid du Colombier 	q->dlen += BLEN(b);
9847dd7cddfSDavid du Colombier }
98580ee5cbfSDavid du Colombier 
98680ee5cbfSDavid du Colombier /*
98780ee5cbfSDavid du Colombier  *  flow control, get producer going again
98880ee5cbfSDavid du Colombier  *  called with q ilocked
98980ee5cbfSDavid du Colombier  */
99080ee5cbfSDavid du Colombier static void
qwakeup_iunlock(Queue * q)99180ee5cbfSDavid du Colombier qwakeup_iunlock(Queue *q)
99280ee5cbfSDavid du Colombier {
99380ee5cbfSDavid du Colombier 	int dowakeup = 0;
9947dd7cddfSDavid du Colombier 
9957dd7cddfSDavid du Colombier 	/* if writer flow controlled, restart */
9967dd7cddfSDavid du Colombier 	if((q->state & Qflow) && q->len < q->limit/2){
9977dd7cddfSDavid du Colombier 		q->state &= ~Qflow;
9987dd7cddfSDavid du Colombier 		dowakeup = 1;
99980ee5cbfSDavid du Colombier 	}
10007dd7cddfSDavid du Colombier 
10017dd7cddfSDavid du Colombier 	iunlock(q);
10027dd7cddfSDavid du Colombier 
10037dd7cddfSDavid du Colombier 	/* wakeup flow controlled writers */
10047dd7cddfSDavid du Colombier 	if(dowakeup){
10057dd7cddfSDavid du Colombier 		if(q->kick)
10067dd7cddfSDavid du Colombier 			q->kick(q->arg);
10077dd7cddfSDavid du Colombier 		wakeup(&q->wr);
10087dd7cddfSDavid du Colombier 	}
100980ee5cbfSDavid du Colombier }
101080ee5cbfSDavid du Colombier 
101180ee5cbfSDavid du Colombier /*
101280ee5cbfSDavid du Colombier  *  get next block from a queue (up to a limit)
101380ee5cbfSDavid du Colombier  */
101480ee5cbfSDavid du Colombier Block*
qbread(Queue * q,int len)101580ee5cbfSDavid du Colombier qbread(Queue *q, int len)
101680ee5cbfSDavid du Colombier {
101780ee5cbfSDavid du Colombier 	Block *b, *nb;
101880ee5cbfSDavid du Colombier 	int n;
101980ee5cbfSDavid du Colombier 
102080ee5cbfSDavid du Colombier 	qlock(&q->rlock);
102180ee5cbfSDavid du Colombier 	if(waserror()){
102280ee5cbfSDavid du Colombier 		qunlock(&q->rlock);
102380ee5cbfSDavid du Colombier 		nexterror();
102480ee5cbfSDavid du Colombier 	}
102580ee5cbfSDavid du Colombier 
102680ee5cbfSDavid du Colombier 	ilock(q);
102780ee5cbfSDavid du Colombier 	switch(qwait(q)){
102880ee5cbfSDavid du Colombier 	case 0:
102980ee5cbfSDavid du Colombier 		/* queue closed */
103080ee5cbfSDavid du Colombier 		iunlock(q);
103180ee5cbfSDavid du Colombier 		qunlock(&q->rlock);
103280ee5cbfSDavid du Colombier 		poperror();
103380ee5cbfSDavid du Colombier 		return nil;
103480ee5cbfSDavid du Colombier 	case -1:
103580ee5cbfSDavid du Colombier 		/* multiple reads on a closed queue */
103680ee5cbfSDavid du Colombier 		iunlock(q);
103780ee5cbfSDavid du Colombier 		error(q->err);
103880ee5cbfSDavid du Colombier 	}
103980ee5cbfSDavid du Colombier 
104080ee5cbfSDavid du Colombier 	/* if we get here, there's at least one block in the queue */
104180ee5cbfSDavid du Colombier 	b = qremove(q);
104280ee5cbfSDavid du Colombier 	n = BLEN(b);
104380ee5cbfSDavid du Colombier 
104480ee5cbfSDavid du Colombier 	/* split block if it's too big and this is not a message queue */
104580ee5cbfSDavid du Colombier 	nb = b;
104680ee5cbfSDavid du Colombier 	if(n > len){
104780ee5cbfSDavid du Colombier 		if((q->state&Qmsg) == 0){
104880ee5cbfSDavid du Colombier 			n -= len;
104980ee5cbfSDavid du Colombier 			b = allocb(n);
105080ee5cbfSDavid du Colombier 			memmove(b->wp, nb->rp+len, n);
105180ee5cbfSDavid du Colombier 			b->wp += n;
105280ee5cbfSDavid du Colombier 			qputback(q, b);
105380ee5cbfSDavid du Colombier 		}
105480ee5cbfSDavid du Colombier 		nb->wp = nb->rp + len;
105580ee5cbfSDavid du Colombier 	}
105680ee5cbfSDavid du Colombier 
105780ee5cbfSDavid du Colombier 	/* restart producer */
105880ee5cbfSDavid du Colombier 	qwakeup_iunlock(q);
10597dd7cddfSDavid du Colombier 
10607dd7cddfSDavid du Colombier 	poperror();
10617dd7cddfSDavid du Colombier 	qunlock(&q->rlock);
10627dd7cddfSDavid du Colombier 	return nb;
10637dd7cddfSDavid du Colombier }
10647dd7cddfSDavid du Colombier 
10657dd7cddfSDavid du Colombier /*
10667dd7cddfSDavid du Colombier  *  read a queue.  if no data is queued, post a Block
10677dd7cddfSDavid du Colombier  *  and wait on its Rendez.
10687dd7cddfSDavid du Colombier  */
10697dd7cddfSDavid du Colombier long
qread(Queue * q,void * vp,int len)10707dd7cddfSDavid du Colombier qread(Queue *q, void *vp, int len)
10717dd7cddfSDavid du Colombier {
10729a747e4fSDavid du Colombier 	Block *b, *first, **l;
10739a747e4fSDavid du Colombier 	int m, n;
107480ee5cbfSDavid du Colombier 
107580ee5cbfSDavid du Colombier 	qlock(&q->rlock);
107680ee5cbfSDavid du Colombier 	if(waserror()){
107780ee5cbfSDavid du Colombier 		qunlock(&q->rlock);
107880ee5cbfSDavid du Colombier 		nexterror();
107980ee5cbfSDavid du Colombier 	}
108080ee5cbfSDavid du Colombier 
108180ee5cbfSDavid du Colombier 	ilock(q);
108280ee5cbfSDavid du Colombier again:
108380ee5cbfSDavid du Colombier 	switch(qwait(q)){
108480ee5cbfSDavid du Colombier 	case 0:
108580ee5cbfSDavid du Colombier 		/* queue closed */
108680ee5cbfSDavid du Colombier 		iunlock(q);
108780ee5cbfSDavid du Colombier 		qunlock(&q->rlock);
108880ee5cbfSDavid du Colombier 		poperror();
10897dd7cddfSDavid du Colombier 		return 0;
109080ee5cbfSDavid du Colombier 	case -1:
109180ee5cbfSDavid du Colombier 		/* multiple reads on a closed queue */
109280ee5cbfSDavid du Colombier 		iunlock(q);
109380ee5cbfSDavid du Colombier 		error(q->err);
109480ee5cbfSDavid du Colombier 	}
10957dd7cddfSDavid du Colombier 
109680ee5cbfSDavid du Colombier 	/* if we get here, there's at least one block in the queue */
109780ee5cbfSDavid du Colombier 	if(q->state & Qcoalesce){
109880ee5cbfSDavid du Colombier 		/* when coalescing, 0 length blocks just go away */
109980ee5cbfSDavid du Colombier 		b = q->bfirst;
110080ee5cbfSDavid du Colombier 		if(BLEN(b) <= 0){
110180ee5cbfSDavid du Colombier 			freeb(qremove(q));
110280ee5cbfSDavid du Colombier 			goto again;
110380ee5cbfSDavid du Colombier 		}
110480ee5cbfSDavid du Colombier 
110580ee5cbfSDavid du Colombier 		/*  grab the first block plus as many
110680ee5cbfSDavid du Colombier 		 *  following blocks as will completely
110780ee5cbfSDavid du Colombier 		 *  fit in the read.
110880ee5cbfSDavid du Colombier 		 */
11099a747e4fSDavid du Colombier 		n = 0;
111080ee5cbfSDavid du Colombier 		l = &first;
111180ee5cbfSDavid du Colombier 		m = BLEN(b);
111280ee5cbfSDavid du Colombier 		for(;;) {
111380ee5cbfSDavid du Colombier 			*l = qremove(q);
111480ee5cbfSDavid du Colombier 			l = &b->next;
11159a747e4fSDavid du Colombier 			n += m;
111680ee5cbfSDavid du Colombier 
111780ee5cbfSDavid du Colombier 			b = q->bfirst;
111880ee5cbfSDavid du Colombier 			if(b == nil)
111980ee5cbfSDavid du Colombier 				break;
112080ee5cbfSDavid du Colombier 			m = BLEN(b);
11219a747e4fSDavid du Colombier 			if(n+m > len)
112280ee5cbfSDavid du Colombier 				break;
112380ee5cbfSDavid du Colombier 		}
112480ee5cbfSDavid du Colombier 	} else {
112580ee5cbfSDavid du Colombier 		first = qremove(q);
11269a747e4fSDavid du Colombier 		n = BLEN(first);
112780ee5cbfSDavid du Colombier 	}
112880ee5cbfSDavid du Colombier 
112980ee5cbfSDavid du Colombier 	/* copy to user space outside of the ilock */
113080ee5cbfSDavid du Colombier 	iunlock(q);
11319a747e4fSDavid du Colombier 	b = bl2mem(vp, first, len);
113280ee5cbfSDavid du Colombier 	ilock(q);
113380ee5cbfSDavid du Colombier 
113480ee5cbfSDavid du Colombier 	/* take care of any left over partial block */
113580ee5cbfSDavid du Colombier 	if(b != nil){
11369a747e4fSDavid du Colombier 		n -= BLEN(b);
113780ee5cbfSDavid du Colombier 		if(q->state & Qmsg)
113880ee5cbfSDavid du Colombier 			freeb(b);
113980ee5cbfSDavid du Colombier 		else
114080ee5cbfSDavid du Colombier 			qputback(q, b);
114180ee5cbfSDavid du Colombier 	}
114280ee5cbfSDavid du Colombier 
114380ee5cbfSDavid du Colombier 	/* restart producer */
114480ee5cbfSDavid du Colombier 	qwakeup_iunlock(q);
114580ee5cbfSDavid du Colombier 
114680ee5cbfSDavid du Colombier 	poperror();
114780ee5cbfSDavid du Colombier 	qunlock(&q->rlock);
11489a747e4fSDavid du Colombier 	return n;
11497dd7cddfSDavid du Colombier }
11507dd7cddfSDavid du Colombier 
11517dd7cddfSDavid du Colombier static int
qnotfull(void * a)11527dd7cddfSDavid du Colombier qnotfull(void *a)
11537dd7cddfSDavid du Colombier {
11547dd7cddfSDavid du Colombier 	Queue *q = a;
11557dd7cddfSDavid du Colombier 
11567dd7cddfSDavid du Colombier 	return q->len < q->limit || (q->state & Qclosed);
11577dd7cddfSDavid du Colombier }
11587dd7cddfSDavid du Colombier 
11593ff48bf5SDavid du Colombier ulong noblockcnt;
11603ff48bf5SDavid du Colombier 
11617dd7cddfSDavid du Colombier /*
11627dd7cddfSDavid du Colombier  *  add a block to a queue obeying flow control
11637dd7cddfSDavid du Colombier  */
11647dd7cddfSDavid du Colombier long
qbwrite(Queue * q,Block * b)11657dd7cddfSDavid du Colombier qbwrite(Queue *q, Block *b)
11667dd7cddfSDavid du Colombier {
11677dd7cddfSDavid du Colombier 	int n, dowakeup;
116880ee5cbfSDavid du Colombier 	Proc *p;
11697dd7cddfSDavid du Colombier 
11707dd7cddfSDavid du Colombier 	n = BLEN(b);
1171e6c6b7f8SDavid du Colombier 
1172e6c6b7f8SDavid du Colombier 	if(q->bypass){
1173e6c6b7f8SDavid du Colombier 		(*q->bypass)(q->arg, b);
1174e6c6b7f8SDavid du Colombier 		return n;
1175e6c6b7f8SDavid du Colombier 	}
1176e6c6b7f8SDavid du Colombier 
1177e6c6b7f8SDavid du Colombier 	dowakeup = 0;
11787dd7cddfSDavid du Colombier 	qlock(&q->wlock);
11797dd7cddfSDavid du Colombier 	if(waserror()){
118059cc4ca5SDavid du Colombier 		if(b != nil)
118159cc4ca5SDavid du Colombier 			freeb(b);
11827dd7cddfSDavid du Colombier 		qunlock(&q->wlock);
11837dd7cddfSDavid du Colombier 		nexterror();
11847dd7cddfSDavid du Colombier 	}
11857dd7cddfSDavid du Colombier 
11867dd7cddfSDavid du Colombier 	ilock(q);
11877dd7cddfSDavid du Colombier 
118859cc4ca5SDavid du Colombier 	/* give up if the queue is closed */
11897dd7cddfSDavid du Colombier 	if(q->state & Qclosed){
11907dd7cddfSDavid du Colombier 		iunlock(q);
11917dd7cddfSDavid du Colombier 		error(q->err);
11927dd7cddfSDavid du Colombier 	}
11937dd7cddfSDavid du Colombier 
119459cc4ca5SDavid du Colombier 	/* if nonblocking, don't queue over the limit */
119559cc4ca5SDavid du Colombier 	if(q->len >= q->limit){
11967dd7cddfSDavid du Colombier 		if(q->noblock){
11977dd7cddfSDavid du Colombier 			iunlock(q);
11987dd7cddfSDavid du Colombier 			freeb(b);
11993ff48bf5SDavid du Colombier 			noblockcnt += n;
12007dd7cddfSDavid du Colombier 			qunlock(&q->wlock);
12017dd7cddfSDavid du Colombier 			poperror();
12027dd7cddfSDavid du Colombier 			return n;
12037dd7cddfSDavid du Colombier 		}
12047dd7cddfSDavid du Colombier 	}
12057dd7cddfSDavid du Colombier 
120659cc4ca5SDavid du Colombier 	/* queue the block */
12077dd7cddfSDavid du Colombier 	if(q->bfirst)
12087dd7cddfSDavid du Colombier 		q->blast->next = b;
12097dd7cddfSDavid du Colombier 	else
12107dd7cddfSDavid du Colombier 		q->bfirst = b;
12117dd7cddfSDavid du Colombier 	q->blast = b;
12127dd7cddfSDavid du Colombier 	b->next = 0;
12137dd7cddfSDavid du Colombier 	q->len += BALLOC(b);
12147dd7cddfSDavid du Colombier 	q->dlen += n;
12157dd7cddfSDavid du Colombier 	QDEBUG checkb(b, "qbwrite");
121659cc4ca5SDavid du Colombier 	b = nil;
12177dd7cddfSDavid du Colombier 
121859cc4ca5SDavid du Colombier 	/* make sure other end gets awakened */
12197dd7cddfSDavid du Colombier 	if(q->state & Qstarve){
12207dd7cddfSDavid du Colombier 		q->state &= ~Qstarve;
12217dd7cddfSDavid du Colombier 		dowakeup = 1;
12227dd7cddfSDavid du Colombier 	}
12237dd7cddfSDavid du Colombier 	iunlock(q);
12247dd7cddfSDavid du Colombier 
12253ff48bf5SDavid du Colombier 	/*  get output going again */
12263ff48bf5SDavid du Colombier 	if(q->kick && (dowakeup || (q->state&Qkick)))
12277dd7cddfSDavid du Colombier 		q->kick(q->arg);
12283ff48bf5SDavid du Colombier 
12293ff48bf5SDavid du Colombier 	/* wakeup anyone consuming at the other end */
12303ff48bf5SDavid du Colombier 	if(dowakeup){
123180ee5cbfSDavid du Colombier 		p = wakeup(&q->rr);
123280ee5cbfSDavid du Colombier 
123380ee5cbfSDavid du Colombier 		/* if we just wokeup a higher priority process, let it run */
123480ee5cbfSDavid du Colombier 		if(p != nil && p->priority > up->priority)
123580ee5cbfSDavid du Colombier 			sched();
12367dd7cddfSDavid du Colombier 	}
12377dd7cddfSDavid du Colombier 
123859cc4ca5SDavid du Colombier 	/*
123959cc4ca5SDavid du Colombier 	 *  flow control, wait for queue to get below the limit
124059cc4ca5SDavid du Colombier 	 *  before allowing the process to continue and queue
124159cc4ca5SDavid du Colombier 	 *  more.  We do this here so that postnote can only
12429a747e4fSDavid du Colombier 	 *  interrupt us after the data has been queued.  This
124359cc4ca5SDavid du Colombier 	 *  means that things like 9p flushes and ssl messages
124459cc4ca5SDavid du Colombier 	 *  will not be disrupted by software interrupts.
124559cc4ca5SDavid du Colombier 	 *
124659cc4ca5SDavid du Colombier 	 *  Note - this is moderately dangerous since a process
124759cc4ca5SDavid du Colombier 	 *  that keeps getting interrupted and rewriting will
124859cc4ca5SDavid du Colombier 	 *  queue infinite crud.
124959cc4ca5SDavid du Colombier 	 */
125059cc4ca5SDavid du Colombier 	for(;;){
125159cc4ca5SDavid du Colombier 		if(q->noblock || qnotfull(q))
125259cc4ca5SDavid du Colombier 			break;
125359cc4ca5SDavid du Colombier 
125459cc4ca5SDavid du Colombier 		ilock(q);
125559cc4ca5SDavid du Colombier 		q->state |= Qflow;
125659cc4ca5SDavid du Colombier 		iunlock(q);
125759cc4ca5SDavid du Colombier 		sleep(&q->wr, qnotfull, q);
125859cc4ca5SDavid du Colombier 	}
125959cc4ca5SDavid du Colombier 	USED(b);
126059cc4ca5SDavid du Colombier 
12617dd7cddfSDavid du Colombier 	qunlock(&q->wlock);
12627dd7cddfSDavid du Colombier 	poperror();
12637dd7cddfSDavid du Colombier 	return n;
12647dd7cddfSDavid du Colombier }
12657dd7cddfSDavid du Colombier 
12667dd7cddfSDavid du Colombier /*
12677dd7cddfSDavid du Colombier  *  write to a queue.  only Maxatomic bytes at a time is atomic.
12687dd7cddfSDavid du Colombier  */
12697dd7cddfSDavid du Colombier int
qwrite(Queue * q,void * vp,int len)12707dd7cddfSDavid du Colombier qwrite(Queue *q, void *vp, int len)
12717dd7cddfSDavid du Colombier {
12727dd7cddfSDavid du Colombier 	int n, sofar;
12737dd7cddfSDavid du Colombier 	Block *b;
12747dd7cddfSDavid du Colombier 	uchar *p = vp;
12757dd7cddfSDavid du Colombier 
12769a747e4fSDavid du Colombier 	QDEBUG if(!islo())
1277*b8a11165SDavid du Colombier 		print("qwrite hi %#p\n", getcallerpc(&q));
12787dd7cddfSDavid du Colombier 
12797dd7cddfSDavid du Colombier 	sofar = 0;
12807dd7cddfSDavid du Colombier 	do {
12817dd7cddfSDavid du Colombier 		n = len-sofar;
12827dd7cddfSDavid du Colombier 		if(n > Maxatomic)
12837dd7cddfSDavid du Colombier 			n = Maxatomic;
12847dd7cddfSDavid du Colombier 
12857dd7cddfSDavid du Colombier 		b = allocb(n);
12867dd7cddfSDavid du Colombier 		setmalloctag(b, (up->text[0]<<24)|(up->text[1]<<16)|(up->text[2]<<8)|up->text[3]);
12877dd7cddfSDavid du Colombier 		if(waserror()){
12887dd7cddfSDavid du Colombier 			freeb(b);
12897dd7cddfSDavid du Colombier 			nexterror();
12907dd7cddfSDavid du Colombier 		}
12917dd7cddfSDavid du Colombier 		memmove(b->wp, p+sofar, n);
12927dd7cddfSDavid du Colombier 		poperror();
12937dd7cddfSDavid du Colombier 		b->wp += n;
12947dd7cddfSDavid du Colombier 
12957dd7cddfSDavid du Colombier 		qbwrite(q, b);
12967dd7cddfSDavid du Colombier 
12977dd7cddfSDavid du Colombier 		sofar += n;
12987dd7cddfSDavid du Colombier 	} while(sofar < len && (q->state & Qmsg) == 0);
12997dd7cddfSDavid du Colombier 
13007dd7cddfSDavid du Colombier 	return len;
13017dd7cddfSDavid du Colombier }
13027dd7cddfSDavid du Colombier 
13037dd7cddfSDavid du Colombier /*
13047dd7cddfSDavid du Colombier  *  used by print() to write to a queue.  Since we may be splhi or not in
13057dd7cddfSDavid du Colombier  *  a process, don't qlock.
130640ef9009SDavid du Colombier  *
130740ef9009SDavid du Colombier  *  this routine merges adjacent blocks if block n+1 will fit into
130840ef9009SDavid du Colombier  *  the free space of block n.
13097dd7cddfSDavid du Colombier  */
13107dd7cddfSDavid du Colombier int
qiwrite(Queue * q,void * vp,int len)13117dd7cddfSDavid du Colombier qiwrite(Queue *q, void *vp, int len)
13127dd7cddfSDavid du Colombier {
13137dd7cddfSDavid du Colombier 	int n, sofar, dowakeup;
13147dd7cddfSDavid du Colombier 	Block *b;
13157dd7cddfSDavid du Colombier 	uchar *p = vp;
13167dd7cddfSDavid du Colombier 
13177dd7cddfSDavid du Colombier 	dowakeup = 0;
13187dd7cddfSDavid du Colombier 
13197dd7cddfSDavid du Colombier 	sofar = 0;
13207dd7cddfSDavid du Colombier 	do {
13217dd7cddfSDavid du Colombier 		n = len-sofar;
13227dd7cddfSDavid du Colombier 		if(n > Maxatomic)
13237dd7cddfSDavid du Colombier 			n = Maxatomic;
13247dd7cddfSDavid du Colombier 
13259a747e4fSDavid du Colombier 		b = iallocb(n);
13269a747e4fSDavid du Colombier 		if(b == nil)
13279a747e4fSDavid du Colombier 			break;
13287dd7cddfSDavid du Colombier 		memmove(b->wp, p+sofar, n);
13297dd7cddfSDavid du Colombier 		b->wp += n;
13307dd7cddfSDavid du Colombier 
13317dd7cddfSDavid du Colombier 		ilock(q);
13327dd7cddfSDavid du Colombier 
133340ef9009SDavid du Colombier 		/* we use an artificially high limit for kernel prints since anything
133440ef9009SDavid du Colombier 		 * over the limit gets dropped
133540ef9009SDavid du Colombier 		 */
133640ef9009SDavid du Colombier 		if(q->dlen >= 16*1024){
133739734e7eSDavid du Colombier 			iunlock(q);
133839734e7eSDavid du Colombier 			freeb(b);
133939734e7eSDavid du Colombier 			break;
134039734e7eSDavid du Colombier 		}
134139734e7eSDavid du Colombier 
13427dd7cddfSDavid du Colombier 		QDEBUG checkb(b, "qiwrite");
13437dd7cddfSDavid du Colombier 		if(q->bfirst)
13447dd7cddfSDavid du Colombier 			q->blast->next = b;
13457dd7cddfSDavid du Colombier 		else
13467dd7cddfSDavid du Colombier 			q->bfirst = b;
13477dd7cddfSDavid du Colombier 		q->blast = b;
13487dd7cddfSDavid du Colombier 		q->len += BALLOC(b);
13497dd7cddfSDavid du Colombier 		q->dlen += n;
13507dd7cddfSDavid du Colombier 
13517dd7cddfSDavid du Colombier 		if(q->state & Qstarve){
13527dd7cddfSDavid du Colombier 			q->state &= ~Qstarve;
13537dd7cddfSDavid du Colombier 			dowakeup = 1;
13547dd7cddfSDavid du Colombier 		}
13557dd7cddfSDavid du Colombier 
13567dd7cddfSDavid du Colombier 		iunlock(q);
13577dd7cddfSDavid du Colombier 
13587dd7cddfSDavid du Colombier 		if(dowakeup){
13597dd7cddfSDavid du Colombier 			if(q->kick)
13607dd7cddfSDavid du Colombier 				q->kick(q->arg);
13617dd7cddfSDavid du Colombier 			wakeup(&q->rr);
13627dd7cddfSDavid du Colombier 		}
13637dd7cddfSDavid du Colombier 
13647dd7cddfSDavid du Colombier 		sofar += n;
13657dd7cddfSDavid du Colombier 	} while(sofar < len && (q->state & Qmsg) == 0);
13667dd7cddfSDavid du Colombier 
13679a747e4fSDavid du Colombier 	return sofar;
13687dd7cddfSDavid du Colombier }
13697dd7cddfSDavid du Colombier 
13707dd7cddfSDavid du Colombier /*
13717dd7cddfSDavid du Colombier  *  be extremely careful when calling this,
13727dd7cddfSDavid du Colombier  *  as there is no reference accounting
13737dd7cddfSDavid du Colombier  */
13747dd7cddfSDavid du Colombier void
qfree(Queue * q)13757dd7cddfSDavid du Colombier qfree(Queue *q)
13767dd7cddfSDavid du Colombier {
13777dd7cddfSDavid du Colombier 	qclose(q);
13787dd7cddfSDavid du Colombier 	free(q);
13797dd7cddfSDavid du Colombier }
13807dd7cddfSDavid du Colombier 
13817dd7cddfSDavid du Colombier /*
13827dd7cddfSDavid du Colombier  *  Mark a queue as closed.  No further IO is permitted.
13837dd7cddfSDavid du Colombier  *  All blocks are released.
13847dd7cddfSDavid du Colombier  */
13857dd7cddfSDavid du Colombier void
qclose(Queue * q)13867dd7cddfSDavid du Colombier qclose(Queue *q)
13877dd7cddfSDavid du Colombier {
13887dd7cddfSDavid du Colombier 	Block *bfirst;
13897dd7cddfSDavid du Colombier 
13907dd7cddfSDavid du Colombier 	if(q == nil)
13917dd7cddfSDavid du Colombier 		return;
13927dd7cddfSDavid du Colombier 
13937dd7cddfSDavid du Colombier 	/* mark it */
13947dd7cddfSDavid du Colombier 	ilock(q);
13957dd7cddfSDavid du Colombier 	q->state |= Qclosed;
13967dd7cddfSDavid du Colombier 	q->state &= ~(Qflow|Qstarve);
13977dd7cddfSDavid du Colombier 	strcpy(q->err, Ehungup);
13987dd7cddfSDavid du Colombier 	bfirst = q->bfirst;
13997dd7cddfSDavid du Colombier 	q->bfirst = 0;
14007dd7cddfSDavid du Colombier 	q->len = 0;
14017dd7cddfSDavid du Colombier 	q->dlen = 0;
14027dd7cddfSDavid du Colombier 	q->noblock = 0;
14037dd7cddfSDavid du Colombier 	iunlock(q);
14047dd7cddfSDavid du Colombier 
14057dd7cddfSDavid du Colombier 	/* free queued blocks */
14067dd7cddfSDavid du Colombier 	freeblist(bfirst);
14077dd7cddfSDavid du Colombier 
14087dd7cddfSDavid du Colombier 	/* wake up readers/writers */
14097dd7cddfSDavid du Colombier 	wakeup(&q->rr);
14107dd7cddfSDavid du Colombier 	wakeup(&q->wr);
14117dd7cddfSDavid du Colombier }
14127dd7cddfSDavid du Colombier 
14137dd7cddfSDavid du Colombier /*
14147dd7cddfSDavid du Colombier  *  Mark a queue as closed.  Wakeup any readers.  Don't remove queued
14157dd7cddfSDavid du Colombier  *  blocks.
14167dd7cddfSDavid du Colombier  */
14177dd7cddfSDavid du Colombier void
qhangup(Queue * q,char * msg)14187dd7cddfSDavid du Colombier qhangup(Queue *q, char *msg)
14197dd7cddfSDavid du Colombier {
14207dd7cddfSDavid du Colombier 	/* mark it */
14217dd7cddfSDavid du Colombier 	ilock(q);
14227dd7cddfSDavid du Colombier 	q->state |= Qclosed;
14237dd7cddfSDavid du Colombier 	if(msg == 0 || *msg == 0)
14247dd7cddfSDavid du Colombier 		strcpy(q->err, Ehungup);
14257dd7cddfSDavid du Colombier 	else
14269a747e4fSDavid du Colombier 		strncpy(q->err, msg, ERRMAX-1);
14277dd7cddfSDavid du Colombier 	iunlock(q);
14287dd7cddfSDavid du Colombier 
14297dd7cddfSDavid du Colombier 	/* wake up readers/writers */
14307dd7cddfSDavid du Colombier 	wakeup(&q->rr);
14317dd7cddfSDavid du Colombier 	wakeup(&q->wr);
14327dd7cddfSDavid du Colombier }
14337dd7cddfSDavid du Colombier 
14347dd7cddfSDavid du Colombier /*
14357dd7cddfSDavid du Colombier  *  return non-zero if the q is hungup
14367dd7cddfSDavid du Colombier  */
14377dd7cddfSDavid du Colombier int
qisclosed(Queue * q)14387dd7cddfSDavid du Colombier qisclosed(Queue *q)
14397dd7cddfSDavid du Colombier {
14407dd7cddfSDavid du Colombier 	return q->state & Qclosed;
14417dd7cddfSDavid du Colombier }
14427dd7cddfSDavid du Colombier 
14437dd7cddfSDavid du Colombier /*
14447dd7cddfSDavid du Colombier  *  mark a queue as no longer hung up
14457dd7cddfSDavid du Colombier  */
14467dd7cddfSDavid du Colombier void
qreopen(Queue * q)14477dd7cddfSDavid du Colombier qreopen(Queue *q)
14487dd7cddfSDavid du Colombier {
14497dd7cddfSDavid du Colombier 	ilock(q);
14507dd7cddfSDavid du Colombier 	q->state &= ~Qclosed;
14517dd7cddfSDavid du Colombier 	q->state |= Qstarve;
14527dd7cddfSDavid du Colombier 	q->eof = 0;
14537dd7cddfSDavid du Colombier 	q->limit = q->inilim;
14547dd7cddfSDavid du Colombier 	iunlock(q);
14557dd7cddfSDavid du Colombier }
14567dd7cddfSDavid du Colombier 
14577dd7cddfSDavid du Colombier /*
14587dd7cddfSDavid du Colombier  *  return bytes queued
14597dd7cddfSDavid du Colombier  */
14607dd7cddfSDavid du Colombier int
qlen(Queue * q)14617dd7cddfSDavid du Colombier qlen(Queue *q)
14627dd7cddfSDavid du Colombier {
14637dd7cddfSDavid du Colombier 	return q->dlen;
14647dd7cddfSDavid du Colombier }
14657dd7cddfSDavid du Colombier 
14667dd7cddfSDavid du Colombier /*
14677dd7cddfSDavid du Colombier  * return space remaining before flow control
14687dd7cddfSDavid du Colombier  */
14697dd7cddfSDavid du Colombier int
qwindow(Queue * q)14707dd7cddfSDavid du Colombier qwindow(Queue *q)
14717dd7cddfSDavid du Colombier {
14727dd7cddfSDavid du Colombier 	int l;
14737dd7cddfSDavid du Colombier 
14747dd7cddfSDavid du Colombier 	l = q->limit - q->len;
14757dd7cddfSDavid du Colombier 	if(l < 0)
14767dd7cddfSDavid du Colombier 		l = 0;
14777dd7cddfSDavid du Colombier 	return l;
14787dd7cddfSDavid du Colombier }
14797dd7cddfSDavid du Colombier 
14807dd7cddfSDavid du Colombier /*
14817dd7cddfSDavid du Colombier  *  return true if we can read without blocking
14827dd7cddfSDavid du Colombier  */
14837dd7cddfSDavid du Colombier int
qcanread(Queue * q)14847dd7cddfSDavid du Colombier qcanread(Queue *q)
14857dd7cddfSDavid du Colombier {
14867dd7cddfSDavid du Colombier 	return q->bfirst!=0;
14877dd7cddfSDavid du Colombier }
14887dd7cddfSDavid du Colombier 
14897dd7cddfSDavid du Colombier /*
14907dd7cddfSDavid du Colombier  *  change queue limit
14917dd7cddfSDavid du Colombier  */
14927dd7cddfSDavid du Colombier void
qsetlimit(Queue * q,int limit)14937dd7cddfSDavid du Colombier qsetlimit(Queue *q, int limit)
14947dd7cddfSDavid du Colombier {
14957dd7cddfSDavid du Colombier 	q->limit = limit;
14967dd7cddfSDavid du Colombier }
14977dd7cddfSDavid du Colombier 
14987dd7cddfSDavid du Colombier /*
14997dd7cddfSDavid du Colombier  *  set blocking/nonblocking
15007dd7cddfSDavid du Colombier  */
15017dd7cddfSDavid du Colombier void
qnoblock(Queue * q,int onoff)15027dd7cddfSDavid du Colombier qnoblock(Queue *q, int onoff)
15037dd7cddfSDavid du Colombier {
15047dd7cddfSDavid du Colombier 	q->noblock = onoff;
15057dd7cddfSDavid du Colombier }
15067dd7cddfSDavid du Colombier 
15077dd7cddfSDavid du Colombier /*
15087dd7cddfSDavid du Colombier  *  flush the output queue
15097dd7cddfSDavid du Colombier  */
15107dd7cddfSDavid du Colombier void
qflush(Queue * q)15117dd7cddfSDavid du Colombier qflush(Queue *q)
15127dd7cddfSDavid du Colombier {
15137dd7cddfSDavid du Colombier 	Block *bfirst;
15147dd7cddfSDavid du Colombier 
15157dd7cddfSDavid du Colombier 	/* mark it */
15167dd7cddfSDavid du Colombier 	ilock(q);
15177dd7cddfSDavid du Colombier 	bfirst = q->bfirst;
15187dd7cddfSDavid du Colombier 	q->bfirst = 0;
15197dd7cddfSDavid du Colombier 	q->len = 0;
15207dd7cddfSDavid du Colombier 	q->dlen = 0;
15217dd7cddfSDavid du Colombier 	iunlock(q);
15227dd7cddfSDavid du Colombier 
15237dd7cddfSDavid du Colombier 	/* free queued blocks */
15247dd7cddfSDavid du Colombier 	freeblist(bfirst);
15257dd7cddfSDavid du Colombier 
15267dd7cddfSDavid du Colombier 	/* wake up readers/writers */
15277dd7cddfSDavid du Colombier 	wakeup(&q->wr);
15287dd7cddfSDavid du Colombier }
15297dd7cddfSDavid du Colombier 
15307dd7cddfSDavid du Colombier int
qfull(Queue * q)15317dd7cddfSDavid du Colombier qfull(Queue *q)
15327dd7cddfSDavid du Colombier {
15337dd7cddfSDavid du Colombier 	return q->state & Qflow;
15347dd7cddfSDavid du Colombier }
15357dd7cddfSDavid du Colombier 
15367dd7cddfSDavid du Colombier int
qstate(Queue * q)15377dd7cddfSDavid du Colombier qstate(Queue *q)
15387dd7cddfSDavid du Colombier {
15397dd7cddfSDavid du Colombier 	return q->state;
15407dd7cddfSDavid du Colombier }
1541