]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/port/qio.c
kernel: massive pci code rewrite
[plan9front.git] / sys / src / 9 / port / qio.c
index 8fb10851103659aa43044e3fe40a15ed517b7818..08b489b6787af82fb5b59d20a64dc8ccc3ea297f 100644 (file)
@@ -11,9 +11,6 @@ static ulong pullupblockcnt;
 static ulong copyblockcnt;
 static ulong consumecnt;
 static ulong producecnt;
-static ulong qcopycnt;
-
-static int debugging;
 
 #define QDEBUG if(0)
 
@@ -56,17 +53,6 @@ enum
 
 uint   qiomaxatomic = Maxatomic;
 
-void
-ixsummary(void)
-{
-       debugging ^= 1;
-       iallocsummary();
-       print("pad %lud, concat %lud, pullup %lud, copy %lud\n",
-               padblockcnt, concatblockcnt, pullupblockcnt, copyblockcnt);
-       print("consume %lud, produce %lud, qcopy %lud\n",
-               consumecnt, producecnt, qcopycnt);
-}
-
 /*
  *  free a list of blocks
  */
@@ -75,10 +61,9 @@ freeblist(Block *b)
 {
        Block *next;
 
-       for(; b != 0; b = next){
+       for(; b != nil; b = next){
                next = b->next;
-               if(b->ref == 1)
-                       b->next = nil;
+               b->next = nil;
                freeb(b);
        }
 }
@@ -92,40 +77,31 @@ padblock(Block *bp, int size)
        int n;
        Block *nbp;
 
-       QDEBUG checkb(bp, "padblock 1");
+       QDEBUG checkb(bp, "padblock 0");
        if(size >= 0){
                if(bp->rp - bp->base >= size){
                        bp->rp -= size;
                        return bp;
                }
-
-               if(bp->next)
-                       panic("padblock %#p", getcallerpc(&bp));
                n = BLEN(bp);
-               padblockcnt++;
                nbp = allocb(size+n);
                nbp->rp += size;
                nbp->wp = nbp->rp;
                memmove(nbp->wp, bp->rp, n);
                nbp->wp += n;
-               freeb(bp);
                nbp->rp -= size;
        } else {
                size = -size;
-
-               if(bp->next)
-                       panic("padblock %#p", getcallerpc(&bp));
-
                if(bp->lim - bp->wp >= size)
                        return bp;
-
                n = BLEN(bp);
-               padblockcnt++;
-               nbp = allocb(size+n);
+               nbp = allocb(n+size);
                memmove(nbp->wp, bp->rp, n);
                nbp->wp += n;
-               freeb(bp);
        }
+       nbp->next = bp->next;
+       freeb(bp);
+       padblockcnt++;
        QDEBUG checkb(nbp, "padblock 1");
        return nbp;
 }
@@ -139,7 +115,7 @@ blocklen(Block *bp)
        int len;
 
        len = 0;
-       while(bp) {
+       while(bp != nil) {
                len += BLEN(bp);
                bp = bp->next;
        }
@@ -155,7 +131,7 @@ blockalloclen(Block *bp)
        int len;
 
        len = 0;
-       while(bp) {
+       while(bp != nil) {
                len += BALLOC(bp);
                bp = bp->next;
        }
@@ -163,28 +139,19 @@ blockalloclen(Block *bp)
 }
 
 /*
- *  copy the  string of blocks into
+ *  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)
+       if(bp->next == nil)
                return bp;
-
-       nb = allocb(blocklen(bp));
-       for(f = bp; f; f = f->next) {
-               len = BLEN(f);
-               memmove(nb->wp, f->rp, len);
-               nb->wp += len;
-       }
-       concatblockcnt += BLEN(nb);
-       freeblist(bp);
-       QDEBUG checkb(nb, "concatblock 1");
-       return nb;
+       len = blocklen(bp);
+       concatblockcnt += len;
+       return pullupblock(bp, len);
 }
 
 /*
@@ -193,8 +160,8 @@ concatblock(Block *bp)
 Block*
 pullupblock(Block *bp, int n)
 {
-       int i;
        Block *nbp;
+       int i;
 
        /*
         *  this should almost always be true, it's
@@ -217,11 +184,11 @@ pullupblock(Block *bp, int n)
         *  copy bytes from the trailing blocks into the first
         */
        n -= BLEN(bp);
-       while(nbp = bp->next){
+       while((nbp = bp->next) != nil){
+               pullupblockcnt++;
                i = BLEN(nbp);
                if(i > n) {
                        memmove(bp->wp, nbp->rp, n);
-                       pullupblockcnt++;
                        bp->wp += n;
                        nbp->rp += n;
                        QDEBUG checkb(bp, "pullupblock 1");
@@ -234,10 +201,9 @@ pullupblock(Block *bp, int n)
                                i = 0;
                        }
                        memmove(bp->wp, nbp->rp, i);
-                       pullupblockcnt++;
                        bp->wp += i;
                        bp->next = nbp->next;
-                       nbp->next = 0;
+                       nbp->next = nil;
                        freeb(nbp);
                        n -= i;
                        if(n == 0){
@@ -247,7 +213,7 @@ pullupblock(Block *bp, int n)
                }
        }
        freeb(bp);
-       return 0;
+       return nil;
 }
 
 /*
@@ -277,7 +243,10 @@ trimblock(Block *bp, int offset, int len)
        Block *nb, *startb;
 
        QDEBUG checkb(bp, "trimblock 1");
-       if(blocklen(bp) < offset+len) {
+       l = blocklen(bp);
+       if(offset == 0 && len == l)
+               return bp;
+       if(l < offset+len) {
                freeblist(bp);
                return nil;
        }
@@ -300,7 +269,7 @@ trimblock(Block *bp, int offset, int len)
 
        bp->wp -= (BLEN(bp) - len);
 
-       if(bp->next) {
+       if(bp->next != nil) {
                freeblist(bp->next);
                bp->next = nil;
        }
@@ -319,7 +288,7 @@ copyblock(Block *bp, int count)
 
        QDEBUG checkb(bp, "copyblock 0");
        nbp = allocb(count);
-       for(; count > 0 && bp != 0; bp = bp->next){
+       for(; count > 0 && bp != nil; bp = bp->next){
                l = BLEN(bp);
                if(l > count)
                        l = count;
@@ -417,11 +386,11 @@ qget(Queue *q)
                iunlock(q);
                return nil;
        }
+       QDEBUG checkb(b, "qget");
        q->bfirst = b->next;
-       b->next = 0;
+       b->next = nil;
        q->len -= BALLOC(b);
        q->dlen -= BLEN(b);
-       QDEBUG checkb(b, "qget");
 
        /* if writer flow controlled, restart */
        if((q->state & Qflow) && q->len < q->limit/2){
@@ -444,7 +413,7 @@ qget(Queue *q)
 int
 qdiscard(Queue *q, int len)
 {
-       Block *b;
+       Block *b, *tofree = nil;
        int dowakeup, n, sofar;
 
        ilock(q);
@@ -456,10 +425,12 @@ qdiscard(Queue *q, int len)
                n = BLEN(b);
                if(n <= len - sofar){
                        q->bfirst = b->next;
-                       b->next = 0;
                        q->len -= BALLOC(b);
                        q->dlen -= BLEN(b);
-                       freeb(b);
+
+                       /* remember to free this */
+                       b->next = tofree;
+                       tofree = b;
                } else {
                        n = len - sofar;
                        b->rp += n;
@@ -488,6 +459,9 @@ qdiscard(Queue *q, int len)
        if(dowakeup)
                wakeup(&q->wr);
 
+       if(tofree != nil)
+               freeblist(tofree);
+
        return sofar;
 }
 
@@ -497,20 +471,19 @@ qdiscard(Queue *q, int len)
 int
 qconsume(Queue *q, void *vp, int len)
 {
-       Block *b;
+       Block *b, *tofree = nil;
        int n, dowakeup;
        uchar *p = vp;
-       Block *tofree = nil;
 
        /* sync with qwrite */
        ilock(q);
 
        for(;;) {
                b = q->bfirst;
-               if(b == 0){
+               if(b == nil){
                        q->state |= Qstarve;
-                       iunlock(q);
-                       return -1;
+                       len = -1;
+                       goto out;
                }
                QDEBUG checkb(b, "qconsume 1");
 
@@ -525,17 +498,16 @@ qconsume(Queue *q, void *vp, int len)
                tofree = b;
        };
 
+       consumecnt += n;
        if(n < len)
                len = n;
        memmove(p, b->rp, len);
-       consumecnt += n;
        b->rp += len;
        q->dlen -= len;
 
        /* discard the block if we're done with it */
        if((q->state & Qmsg) || len == n){
                q->bfirst = b->next;
-               b->next = 0;
                q->len -= BALLOC(b);
                q->dlen -= BLEN(b);
 
@@ -544,6 +516,7 @@ qconsume(Queue *q, void *vp, int len)
                tofree = b;
        }
 
+out:
        /* if writer flow controlled, restart */
        if((q->state & Qflow) && q->len < q->limit/2){
                q->state &= ~Qflow;
@@ -565,40 +538,23 @@ qconsume(Queue *q, void *vp, int len)
 int
 qpass(Queue *q, Block *b)
 {
-       int dlen, len, dowakeup;
+       int len, dowakeup;
 
        /* sync with qread */
        dowakeup = 0;
        ilock(q);
        if(q->len >= q->limit){
-               freeblist(b);
                iunlock(q);
+               freeblist(b);
                return -1;
        }
        if(q->state & Qclosed){
-               len = BALLOC(b);
-               freeblist(b);
                iunlock(q);
-               return len;
+               freeblist(b);
+               return 0;
        }
 
-       /* add buffer to queue */
-       if(q->bfirst)
-               q->blast->next = b;
-       else
-               q->bfirst = b;
-       len = BALLOC(b);
-       dlen = BLEN(b);
-       QDEBUG checkb(b, "qpass");
-       while(b->next){
-               b = b->next;
-               QDEBUG checkb(b, "qpass");
-               len += BALLOC(b);
-               dlen += BLEN(b);
-       }
-       q->blast = b;
-       q->len += len;
-       q->dlen += dlen;
+       len = qaddlist(q, b);
 
        if(q->len >= q->limit/2)
                q->state |= Qflow;
@@ -618,35 +574,19 @@ qpass(Queue *q, Block *b)
 int
 qpassnolim(Queue *q, Block *b)
 {
-       int dlen, len, dowakeup;
+       int len, dowakeup;
 
        /* sync with qread */
        dowakeup = 0;
        ilock(q);
 
        if(q->state & Qclosed){
-               freeblist(b);
                iunlock(q);
-               return BALLOC(b);
+               freeblist(b);
+               return 0;
        }
 
-       /* add buffer to queue */
-       if(q->bfirst)
-               q->blast->next = b;
-       else
-               q->bfirst = b;
-       len = BALLOC(b);
-       dlen = BLEN(b);
-       QDEBUG checkb(b, "qpass");
-       while(b->next){
-               b = b->next;
-               QDEBUG checkb(b, "qpass");
-               len += BALLOC(b);
-               dlen += BLEN(b);
-       }
-       q->blast = b;
-       q->len += len;
-       q->dlen += dlen;
+       len = qaddlist(q, b);
 
        if(q->len >= q->limit/2)
                q->state |= Qflow;
@@ -673,8 +613,7 @@ packblock(Block *bp)
        Block **l, *nbp;
        int n;
 
-       for(l = &bp; *l; l = &(*l)->next){
-               nbp = *l;
+       for(l = &bp; (nbp = *l) != nil; l = &(*l)->next){
                n = BLEN(nbp);
                if((n<<2) < BALLOC(nbp)){
                        *l = allocb(n);
@@ -695,6 +634,10 @@ qproduce(Queue *q, void *vp, int len)
        int dowakeup;
        uchar *p = vp;
 
+       b = iallocb(len);
+       if(b == nil)
+               return 0;
+
        /* sync with qread */
        dowakeup = 0;
        ilock(q);
@@ -705,25 +648,12 @@ qproduce(Queue *q, void *vp, int len)
                iunlock(q);
                return -1;
        }
+       producecnt += len;
 
        /* save in buffer */
-       b = iallocb(len);
-       if(b == 0){
-               iunlock(q);
-               return 0;
-       }
        memmove(b->wp, p, len);
-       producecnt += len;
        b->wp += len;
-       if(q->bfirst)
-               q->blast->next = b;
-       else
-               q->bfirst = b;
-       q->blast = b;
-       /* b->next = 0; done by iallocb() */
-       q->len += BALLOC(b);
-       q->dlen += BLEN(b);
-       QDEBUG checkb(b, "qproduce");
+       qaddlist(q, b);
 
        if(q->state & Qstarve){
                q->state &= ~Qstarve;
@@ -746,49 +676,13 @@ qproduce(Queue *q, void *vp, int len)
 Block*
 qcopy(Queue *q, int len, ulong offset)
 {
-       int sofar;
-       int n;
-       Block *b, *nb;
-       uchar *p;
-
-       nb = allocb(len);
+       Block *b;
 
+       b = allocb(len);
        ilock(q);
-
-       /* go to offset */
-       b = q->bfirst;
-       for(sofar = 0; ; sofar += n){
-               if(b == nil){
-                       iunlock(q);
-                       return nb;
-               }
-               n = BLEN(b);
-               if(sofar + n > offset){
-                       p = b->rp + offset - sofar;
-                       n -= offset - sofar;
-                       break;
-               }
-               QDEBUG checkb(b, "qcopy");
-               b = b->next;
-       }
-
-       /* copy bytes from there */
-       for(sofar = 0; sofar < len;){
-               if(n > len - sofar)
-                       n = len - sofar;
-               memmove(nb->wp, p, n);
-               qcopycnt += n;
-               sofar += n;
-               nb->wp += n;
-               b = b->next;
-               if(b == nil)
-                       break;
-               n = BLEN(b);
-               p = b->rp;
-       }
+       b->wp += readblist(q->bfirst, b->wp, len, offset);
        iunlock(q);
-
-       return nb;
+       return b;
 }
 
 /*
@@ -800,8 +694,8 @@ qopen(int limit, int msg, void (*kick)(void*), void *arg)
        Queue *q;
 
        q = malloc(sizeof(Queue));
-       if(q == 0)
-               return 0;
+       if(q == nil)
+               return nil;
 
        q->limit = q->inilim = limit;
        q->kick = kick;
@@ -822,8 +716,8 @@ qbypass(void (*bypass)(void*, Block*), void *arg)
        Queue *q;
 
        q = malloc(sizeof(Queue));
-       if(q == 0)
-               return 0;
+       if(q == nil)
+               return nil;
 
        q->limit = 0;
        q->arg = arg;
@@ -838,7 +732,7 @@ notempty(void *a)
 {
        Queue *q = a;
 
-       return (q->state & Qclosed) || q->bfirst != 0;
+       return (q->state & Qclosed) || q->bfirst != nil;
 }
 
 /*
@@ -870,21 +764,34 @@ qwait(Queue *q)
 }
 
 /*
- * add a block list to a queue
+ * add a block list to a queue, return bytes added
  */
-void
+int
 qaddlist(Queue *q, Block *b)
 {
+       int len, dlen;
+
+       QDEBUG checkb(b, "qaddlist 1");
+
        /* queue the block */
-       if(q->bfirst)
+       if(q->bfirst != nil)
                q->blast->next = b;
        else
                q->bfirst = b;
-       q->len += blockalloclen(b);
-       q->dlen += blocklen(b);
-       while(b->next)
+
+       len = BALLOC(b);
+       dlen = BLEN(b);
+       while(b->next != nil){
                b = b->next;
+               QDEBUG checkb(b, "qaddlist 2");
+
+               len += BALLOC(b);
+               dlen += BLEN(b);
+       }
        q->blast = b;
+       q->len += len;
+       q->dlen += dlen;
+       return dlen;
 }
 
 /*
@@ -898,74 +805,42 @@ qremove(Queue *q)
        b = q->bfirst;
        if(b == nil)
                return nil;
+       QDEBUG checkb(b, "qremove");
        q->bfirst = b->next;
        b->next = nil;
        q->dlen -= BLEN(b);
        q->len -= BALLOC(b);
-       QDEBUG checkb(b, "qremove");
        return b;
 }
 
 /*
  *  copy the contents of a string of blocks into
- *  memory.  emptied blocks are freed.  return
- *  pointer to first unconsumed block.
+ *  memory from an offset. blocklist kept unchanged.
+ *  return number of copied bytes.
  */
-Block*
-bl2mem(uchar *p, Block *b, int n)
+long
+readblist(Block *b, uchar *p, long n, ulong o)
 {
-       int i;
-       Block *next;
+       ulong m, r;
 
-       for(; b != nil; b = next){
-               i = BLEN(b);
-               if(i > n){
-                       memmove(p, b->rp, n);
-                       b->rp += n;
-                       return b;
+       r = 0;
+       while(n > 0 && b != nil){
+               m = BLEN(b);
+               if(o >= m)
+                       o -= m;
+               else {
+                       m -= o;
+                       if(n < m)
+                               m = n;
+                       memmove(p, b->rp + o, m);
+                       p += m;
+                       r += m;
+                       n -= m;
+                       o = 0;
                }
-               memmove(p, b->rp, i);
-               n -= i;
-               p += i;
-               b->rp += i;
-               next = b->next;
-               freeb(b);
-       }
-       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();
+               b = b->next;
        }
-       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;
+       return r;
 }
 
 /*
@@ -983,6 +858,35 @@ qputback(Queue *q, Block *b)
        q->dlen += BLEN(b);
 }
 
+/*
+ *  cut off n bytes from the end of *h. return a new
+ *  block with the tail and change *h to refer to the
+ *  head.
+ */
+static Block*
+splitblock(Block **h, int n)
+{
+       Block *a, *b;
+       int m;
+
+       a = *h;
+       m = BLEN(a) - n;
+       if(m < n){
+               b = allocb(m);
+               memmove(b->wp, a->rp, m);
+               b->wp += m;
+               a->rp += m;
+               *h = b;
+               return a;
+       } else {
+               b = allocb(n);
+               a->wp -= n;
+               memmove(b->wp, a->wp, n);
+               b->wp += n;
+               return b;
+       }
+}
+
 /*
  *  flow control, get producer going again
  *  called with q ilocked
@@ -1002,7 +906,7 @@ qwakeup_iunlock(Queue *q)
 
        /* wakeup flow controlled writers */
        if(dowakeup){
-               if(q->kick)
+               if(q->kick != nil)
                        q->kick(q->arg);
                wakeup(&q->wr);
        }
@@ -1014,7 +918,7 @@ qwakeup_iunlock(Queue *q)
 Block*
 qbread(Queue *q, int len)
 {
-       Block *b, *nb;
+       Block *b;
        int n;
 
        eqlock(&q->rlock);
@@ -1042,24 +946,21 @@ qbread(Queue *q, int len)
        n = BLEN(b);
 
        /* split block if it's too big and this is not a message queue */
-       nb = b;
        if(n > len){
-               if((q->state&Qmsg) == 0){
-                       n -= len;
-                       b = allocb(n);
-                       memmove(b->wp, nb->rp+len, n);
-                       b->wp += n;
-                       qputback(q, b);
-               }
-               nb->wp = nb->rp + len;
+               n -= len;
+               if((q->state & Qmsg) == 0)
+                       qputback(q, splitblock(&b, n));
+               else
+                       b->wp -= n;
        }
 
        /* restart producer */
        qwakeup_iunlock(q);
 
-       poperror();
        qunlock(&q->rlock);
-       return nb;
+       poperror();
+
+       return b;
 }
 
 /*
@@ -1069,7 +970,7 @@ qbread(Queue *q, int len)
 long
 qread(Queue *q, void *vp, int len)
 {
-       Block *b, *first, **l;
+       Block *b, *first, **last;
        int m, n;
 
        eqlock(&q->rlock);
@@ -1094,57 +995,53 @@ again:
        }
 
        /* if we get here, there's at least one block in the queue */
+       last = &first;
        if(q->state & Qcoalesce){
                /* when coalescing, 0 length blocks just go away */
                b = q->bfirst;
-               if(BLEN(b) <= 0){
+               m = BLEN(b);
+               if(m <= 0){
                        freeb(qremove(q));
                        goto again;
                }
 
                /*  grab the first block plus as many
-                *  following blocks as will completely
+                *  following blocks as will partially
                 *  fit in the read.
                 */
                n = 0;
-               l = &first;
-               m = BLEN(b);
                for(;;) {
-                       *l = qremove(q);
-                       l = &b->next;
+                       *last = qremove(q);
                        n += m;
-
-                       b = q->bfirst;
-                       if(b == nil)
+                       if(n >= len || q->bfirst == nil)
                                break;
+                       last = &b->next;
+                       b = q->bfirst;
                        m = BLEN(b);
-                       if(n+m > len)
-                               break;
                }
        } else {
                first = qremove(q);
                n = BLEN(first);
        }
 
-       /* copy to user space outside of the ilock */
-       iunlock(q);
-       b = bl2mem(vp, first, len);
-       ilock(q);
-
-       /* take care of any left over partial block */
-       if(b != nil){
-               n -= BLEN(b);
-               if(q->state & Qmsg)
-                       freeb(b);
-               else
-                       qputback(q, b);
-       }
+       /* split last block if it's too big and this is not a message queue */
+       if(n > len && (q->state & Qmsg) == 0)
+               qputback(q, splitblock(last, n - len));
 
        /* restart producer */
        qwakeup_iunlock(q);
 
-       poperror();
        qunlock(&q->rlock);
+       poperror();
+
+       if(waserror()){
+               freeblist(first);
+               nexterror();
+       }
+       n = readblist(first, vp, len, 0);
+       freeblist(first);
+       poperror();
+
        return n;
 }
 
@@ -1156,25 +1053,49 @@ qnotfull(void *a)
        return q->len < q->limit || (q->state & Qclosed);
 }
 
+/*
+ *  flow control, wait for queue to get below the limit
+ */
+static void
+qflow(Queue *q)
+{
+       for(;;){
+               if(q->noblock || qnotfull(q))
+                       break;
+
+               ilock(q);
+               q->state |= Qflow;
+               iunlock(q);
+
+               eqlock(&q->wlock);
+               if(waserror()){
+                       qunlock(&q->wlock);
+                       nexterror();
+               }
+               sleep(&q->wr, qnotfull, q);
+               qunlock(&q->wlock);
+               poperror();
+       }
+}
+
 /*
  *  add a block to a queue obeying flow control
  */
 long
 qbwrite(Queue *q, Block *b)
 {
-       int n, dowakeup;
+       int len, dowakeup;
        Proc *p;
 
-       n = BLEN(b);
-
-       if(q->bypass){
+       if(q->bypass != nil){
+               len = blocklen(b);
                (*q->bypass)(q->arg, b);
-               return n;
+               return len;
        }
 
        dowakeup = 0;
        if(waserror()){
-               freeb(b);
+               freeblist(b);
                nexterror();
        }
        ilock(q);
@@ -1186,29 +1107,15 @@ qbwrite(Queue *q, Block *b)
        }
 
        /* don't queue over the limit */
-       if(q->len >= q->limit){
-               if(q->noblock){
-                       iunlock(q);
-                       freeb(b);
-                       poperror();
-                       return n;
-               }
-               if(q->len >= q->limit*2){
-                       iunlock(q);
-                       error(Egreg);
-               }
+       if(q->len >= q->limit && q->noblock){
+               iunlock(q);
+               poperror();
+               len = blocklen(b);
+               freeblist(b);
+               return len;
        }
 
-       /* queue the block */
-       if(q->bfirst)
-               q->blast->next = b;
-       else
-               q->bfirst = b;
-       q->blast = b;
-       b->next = 0;
-       q->len += BALLOC(b);
-       q->dlen += n;
-       QDEBUG checkb(b, "qbwrite");
+       len = qaddlist(q, b);
 
        /* make sure other end gets awakened */
        if(q->state & Qstarve){
@@ -1219,7 +1126,7 @@ qbwrite(Queue *q, Block *b)
        poperror();
 
        /*  get output going again */
-       if(q->kick && (dowakeup || (q->state&Qkick)))
+       if(q->kick != nil && (dowakeup || (q->state&Qkick)))
                q->kick(q->arg);
 
        /* wakeup anyone consuming at the other end */
@@ -1232,36 +1139,15 @@ qbwrite(Queue *q, Block *b)
        }
 
        /*
-        *  flow control, wait for queue to get below the limit
-        *  before allowing the process to continue and queue
-        *  more.  We do this here so that postnote can only
-        *  interrupt us after the data has been queued.  This
-        *  means that things like 9p flushes and ssl messages
-        *  will not be disrupted by software interrupts.
-        *
-        *  Note - this is moderately dangerous since a process
-        *  that keeps getting interrupted and rewriting will
-        *  queue infinite crud.
+        *  flow control, before allowing the process to continue and
+        *  queue more. We do this here so that postnote can only
+        *  interrupt us after the data has been queued.  This means that
+        *  things like 9p flushes and ssl messages will not be disrupted
+        *  by software interrupts.
         */
-       for(;;){
-               if(q->noblock || qnotfull(q))
-                       break;
-
-               ilock(q);
-               q->state |= Qflow;
-               iunlock(q);
-
-               eqlock(&q->wlock);
-               if(waserror()){
-                       qunlock(&q->wlock);
-                       nexterror();
-               }
-               sleep(&q->wr, qnotfull, q);
-               qunlock(&q->wlock);
-               poperror();
-       }
+       qflow(q);
 
-       return n;
+       return len;
 }
 
 /*
@@ -1277,6 +1163,16 @@ qwrite(Queue *q, void *vp, int len)
        QDEBUG if(!islo())
                print("qwrite hi %#p\n", getcallerpc(&q));
 
+       /* stop queue bloat before allocating blocks */
+       if(q->len/2 >= q->limit && q->noblock == 0 && q->bypass == nil){
+               while(waserror()){
+                       if(up->procctl == Proc_exitme || up->procctl == Proc_exitbig)
+                               error(Egreg);
+               }
+               qflow(q);
+               poperror();
+       }
+
        sofar = 0;
        do {
                n = len-sofar;
@@ -1284,7 +1180,6 @@ qwrite(Queue *q, void *vp, int len)
                        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();
@@ -1293,9 +1188,7 @@ qwrite(Queue *q, void *vp, int len)
                poperror();
                b->wp += n;
 
-               qbwrite(q, b);
-
-               sofar += n;
+               sofar += qbwrite(q, b);
        } while(sofar < len && (q->state & Qmsg) == 0);
 
        return len;
@@ -1304,9 +1197,6 @@ qwrite(Queue *q, void *vp, int len)
 /*
  *  used by print() to write to a queue.  Since we may be splhi or not in
  *  a process, don't qlock.
- *
- *  this routine merges adjacent blocks if block n+1 will fit into
- *  the free space of block n.
  */
 int
 qiwrite(Queue *q, void *vp, int len)
@@ -1334,20 +1224,13 @@ qiwrite(Queue *q, void *vp, int len)
                /* we use an artificially high limit for kernel prints since anything
                 * over the limit gets dropped
                 */
-               if(q->dlen >= 16*1024){
+               if((q->state & Qclosed) != 0 || q->len/2 >= q->limit){
                        iunlock(q);
                        freeb(b);
                        break;
                }
 
-               QDEBUG checkb(b, "qiwrite");
-               if(q->bfirst)
-                       q->blast->next = b;
-               else
-                       q->bfirst = b;
-               q->blast = b;
-               q->len += BALLOC(b);
-               q->dlen += n;
+               qaddlist(q, b);
 
                if(q->state & Qstarve){
                        q->state &= ~Qstarve;
@@ -1357,7 +1240,7 @@ qiwrite(Queue *q, void *vp, int len)
                iunlock(q);
 
                if(dowakeup){
-                       if(q->kick)
+                       if(q->kick != nil)
                                q->kick(q->arg);
                        wakeup(&q->rr);
                }
@@ -1395,9 +1278,9 @@ qclose(Queue *q)
        ilock(q);
        q->state |= Qclosed;
        q->state &= ~(Qflow|Qstarve);
-       strcpy(q->err, Ehungup);
+       kstrcpy(q->err, Ehungup, ERRMAX);
        bfirst = q->bfirst;
-       q->bfirst = 0;
+       q->bfirst = nil;
        q->len = 0;
        q->dlen = 0;
        q->noblock = 0;
@@ -1421,10 +1304,9 @@ qhangup(Queue *q, char *msg)
        /* mark it */
        ilock(q);
        q->state |= Qclosed;
-       if(msg == 0 || *msg == 0)
-               strcpy(q->err, Ehungup);
-       else
-               strncpy(q->err, msg, ERRMAX-1);
+       if(msg == nil || *msg == '\0')
+               msg = Ehungup;
+       kstrcpy(q->err, msg, ERRMAX);
        iunlock(q);
 
        /* wake up readers/writers */
@@ -1484,7 +1366,7 @@ qwindow(Queue *q)
 int
 qcanread(Queue *q)
 {
-       return q->bfirst!=0;
+       return q->bfirst != nil;
 }
 
 /*
@@ -1516,7 +1398,7 @@ qflush(Queue *q)
        /* mark it */
        ilock(q);
        bfirst = q->bfirst;
-       q->bfirst = 0;
+       q->bfirst = nil;
        q->len = 0;
        q->dlen = 0;
        iunlock(q);
@@ -1524,7 +1406,7 @@ qflush(Queue *q)
        /* free queued blocks */
        freeblist(bfirst);
 
-       /* wake up readers/writers */
+       /* wake up writers */
        wakeup(&q->wr);
 }
 
@@ -1533,9 +1415,3 @@ qfull(Queue *q)
 {
        return q->state & Qflow;
 }
-
-int
-qstate(Queue *q)
-{
-       return q->state;
-}