/* * primitives dealing in stream blocks */ #include "sys/param.h" #include "sys/stream.h" #include "sys/mtpr.h" #include "sys/buf.h" #define M_HIPRI 127 /* for use by putbq */ extern struct block cblock[]; extern struct buf *cblkbuf[]; extern struct queue queue[]; extern int blkcnt, blkbcnt, queuecnt; struct queue *qhead; /* head of queues to run */ struct queue *qtail; /* last queue */ /* * blocks are kept in handful of free lists, * each containing blocks of a particular size. * there are MAXLEVEL sizes, each SPLITSIZE times bigger than the last. * the system starts out with blkbcnt blocks of the largest size * (which are in fact buffers from bio) * and blkcnt-blkbcnt blocks of the smallest size. * if there are no blocks of the requested size, * larger ones are split to make some. * the smallest blocks (level 0) are a special case: * the data lives in the block header, * and there's no point splitting larger blocks to make more * because there are no headers to use after splitting. */ #define MAXLEVEL 6 #define SPLITSIZE 4 int rbsize[MAXLEVEL] = { 4, 16, 64, 256, 1024, 4096 }; /* real block sizes */ int bsize[MAXLEVEL] = { 4, 16, 64, 256, 256, 256 }; /* size for q limits */ struct block *qfreelist[MAXLEVEL]; short blkfree[MAXLEVEL]; short blkall[MAXLEVEL]; short blkmax[MAXLEVEL]; /* * hooks for debugging * turn this on only if you need it; * it's quite slow */ #if STDEBUG static int qiniting; #define BADBP(bp) ((bp)!=&cblock[(bp)-cblock]) static badfreelist(lev) register lev; { register i = 0; register struct block *bp; /* look for block that is already on the free list */ for(bp=qfreelist[lev];bp!=0;bp=bp->next) i++; if(i!=blkfree[lev]){ printf("blkfree[%d]==%d i==%d\n", lev, blkfree[lev], i); panic("bad free list 1"); } } static duplicateblock(lev, bp) register lev; register struct block *bp; { register i = 0; register struct block *nbp; /* look for block that is already on the free list */ for(nbp=qfreelist[lev];nbp!=0;nbp=nbp->next) { if(nbp==bp) panic("duplicate free"); i++; } if(i!=blkfree[lev]){ printf("blkfree[%d]==%d i==%d\n", lev, blkfree[lev], i); panic("bad free list 2"); } } #endif /* * more debugging: trace the owner of a block * not so slow, but very VAX-dependent. */ #if BLKOWNER #define MAXOWNER 2000 /* this many blocks will be traced */ #define CALLER(x) *(&x+5) /* x is an int, and is first auto in function */ int blkowner[MAXOWNER]; #endif qinit() { register i; register struct buf *bp; #if STDEBUG qiniting=1; #endif for (i = 0; i<blkbcnt; i++) { if ((bp = geteblk()) == NULL) break; /* probably not best */ cblock[i].class = MAXLEVEL-1; cblock[i].base = (u_char *)&bp->b_un.b_addr[0]; cblock[i].lim = (u_char *)&bp->b_un.b_addr[bp->b_bcount]; cblock[i].rbufix = i; cblkbuf[i] = bp; freeb(&cblock[i]); } blkall[MAXLEVEL-1] = 0; for ( ; i<blkcnt; i++) { cblock[i].class = 0; cblock[i].base = &cblock[i].data[0]; cblock[i].lim = &cblock[i].data[sizeof(cblock[0].data)]; cblock[i].rbufix = NOBUFIX; freeb(&cblock[i]); } blkall[0] = 0; #if STDEBUG qiniting=0; #endif } static bsplit(); struct block * allocb(size) { #if BLKOWNER int x; #endif register struct block *bp; register lev; register s; for (lev = 0; lev < MAXLEVEL; lev++) { if (size <= rbsize[lev]) break; } if (lev >= MAXLEVEL) panic("allocb bad size"); s = spl6(); #if STDEBUG badfreelist(lev); #endif /* check for free block of right size */ if ((bp = qfreelist[lev]) == NULL) { bsplit(lev+1); /* else try to split larger block */ if ((bp = qfreelist[lev]) == NULL) { if (size != QBSIZE) { splx(s); return (allocb(QBSIZE)); /* last chance */ } panic("allocb no block"); } } qfreelist[lev] = bp->next; blkall[lev]++; if (blkall[lev] > blkmax[lev]) blkmax[lev] = blkall[lev]; blkfree[lev]--; #if BLKOWNER if ((x = bp-cblock) < MAXOWNER) blkowner[x] = CALLER(x); #endif splx(s); bp->type = M_DATA; bp->next = NULL; bp->rptr = bp->base; bp->wptr = bp->base; bp->bufix = bp->rbufix; bp->class &= ~S_DELIM; #if STDEBUG if (BADBP(bp)) panic("allocb"); #endif return(bp); } static bsplit(lev) register lev; { register struct block *bp, *bp1; register i, size; if (lev>=MAXLEVEL) return; if ((bp = qfreelist[lev]) == NULL) { bsplit(lev+1); if ((bp = qfreelist[lev]) == NULL) return; } qfreelist[lev] = bp->next; blkfree[lev]--; size = (bp->lim - bp->base)/SPLITSIZE; lev--; /* the blocks we're creating */ for (i=1; i<SPLITSIZE; i++) { if ((bp1 = qfreelist[0]) == NULL) return; qfreelist[0] = bp1->next; blkfree[0]--; bp1->base = bp->base; bp->base += size; bp1->lim = bp1->base+size; bp1->class = lev; bp1->rbufix = bp->rbufix; bp1->next = qfreelist[lev]; qfreelist[lev] = bp1; blkfree[lev]++; } bp->class = lev; bp->next = qfreelist[lev]; qfreelist[lev] = bp; blkfree[lev]++; } /* * if this is a large block and its data can be squeezed * into a smaller block, do it. */ struct block * cramb(bp) register struct block *bp; { register struct block *nbp; register int n; n = bp->wptr - bp->rptr; if((bp->class&CL_MASK) < 3 || n > rbsize[(bp->class&CL_MASK)-1]) return bp; nbp = allocb(n); if(nbp==NULL) return bp; if(nbp->lim-nbp->wptr < n) { freeb(nbp); return bp; } bcopy(bp->rptr, nbp->wptr, n); nbp->wptr += n; nbp->type = bp->type; if (bp->class & S_DELIM) nbp->class |= S_DELIM; freeb(bp); return nbp; } /* * make a block that points to the same data * as some existing block */ struct block * dupb(bp) register struct block *bp; { #if BLKOWNER int x; #endif register struct block *nbp; if ((nbp = allocb(0)) == NULL) return (NULL); nbp->type = bp->type; nbp->rptr = bp->rptr; nbp->wptr = bp->wptr; nbp->bufix = bp->bufix; nbp->class |= bp->class&S_DELIM; #if BLKOWNER if ((x = nbp-cblock) < MAXOWNER) blkowner[x] = CALLER(x); #endif return (nbp); } freeb(bp) register struct block *bp; { register s = spl6(); register lev; lev = bp->class&CL_MASK; #if STDEBUG if (BADBP(bp)) panic("freeb"); duplicateblock(lev, bp); if (blkall[lev] <= 0 && !qiniting) panic("freeb excess free"); #endif bp->next = qfreelist[lev]; qfreelist[lev] = bp; blkall[lev]--; blkfree[lev]++; splx(s); } struct block * getq(q) register struct queue *q; { #if BLKOWNER int x; #endif register struct block *bp; register s = spl6(); if ((bp = q->first) == NULL) { if ((q->flag&QENAB) == 0) q->flag |= QWANTR; } else { if ((q->first = bp->next) == NULL) q->last = NULL; q->count -= bsize[bp->class&CL_MASK]; if (q->count < q->qinfo->limit) q->flag &= ~QFULL; q->flag &= ~QWANTR; #if BLKOWNER if ((x = bp-cblock) < MAXOWNER) blkowner[x] = CALLER(x); #endif } if (q->count<=q->qinfo->lolimit && q->flag&QWANTW && OTHERQ(q)->next) { register struct queue *bq = backq(q); if (bq->qinfo->srvp) qenable(bq); q->flag &= ~QWANTW; } splx(s); #if STDEBUG if(bp && BADBP(bp)) panic("getq"); #endif return(bp); } putq(q, bp) register struct queue *q; register struct block *bp; { int s; #if STDEBUG if (BADBP(bp)) panic("putq"); #endif if (bp->type==M_FLUSH) flushq(q, 0); s = spl6(); if (q->first==NULL) { /* empty, just tack on */ q->first = bp; q->last = bp; bp->next = NULL; } else if (bp->type<QPCTL || q->last->type>=QPCTL) { /* put at end */ register struct block *lastp = q->last; register n = bp->wptr - bp->rptr; if (bp->type==M_DATA && lastp->type==M_DATA && (lastp->class&S_DELIM)==0 && n <= lastp->lim-lastp->wptr && lastp->wptr>=lastp->base) { bcopy(bp->rptr, lastp->wptr, n); lastp->wptr += n; if (bp->class&S_DELIM) lastp->class |= S_DELIM; freeb(bp); bp = NULL; } else { lastp->next = bp; q->last = bp; bp->next = NULL; } } else { /* pri, put after any others */ register struct block *nbp = q->first; if (nbp->type < QPCTL) { bp->next = q->first; q->first = bp; } else { while (nbp->next->type>=QPCTL) nbp = nbp->next; bp->next = nbp->next; nbp->next = bp; } } if (bp) { q->count += bsize[bp->class&CL_MASK]; if (bp->type >= QPCTL && bp->type!= M_HIPRI) q->flag |= QWANTR; } if (q->count >= q->qinfo->limit) q->flag |= QFULL|QWANTW; if ((q->flag & (QWANTR|QENAB|QNOENB)) == QWANTR && q->qinfo->srvp) qenable(q); splx(s); } /* * Put stuff back at beginning of Q * (but after any priority msgs) */ putbq(q, bp) register struct queue *q; register struct block *bp; { register savetype = bp->type; register s = spl6(); bp->type = M_HIPRI; /* fake priority, to force to start */ putq(q, bp); bp->type = savetype; splx(s); } /* * empty a queue. Leave any non-data messages, unless flag is 1. */ flushq(q, flag) register struct queue *q; { register struct block *bp, *nbp; register s = spl6(); bp = q->first; q->first = NULL; if (q->last) q->last->next = NULL; q->last = NULL; q->count = 0; q->flag &= ~QFULL; while (bp) { nbp = bp->next; if (bp->type != M_DATA && bp->type != M_DELIM && bp->type != M_CTL && bp->type != M_DELAY && bp->type != M_FLUSH && !flag) putq(q, bp); else { if (bp->type == M_PASS) printf("flushing PASS %x\n",*(int *)(bp->rptr)); freeb(bp); } bp = nbp; } if (q->flag&QWANTW && OTHERQ(q)->next) { q->flag &= ~QWANTW; qenable(backq(q)); } splx(s); } /* * allocate a pair of queues */ struct queue * allocq() { register struct queue *qp; static struct queue zeroR = { NULL,NULL,NULL,NULL,NULL,NULL,0,QUSE|QREADR}; static struct queue zeroW = { NULL,NULL,NULL,NULL,NULL,NULL,0,QUSE}; for (qp = queue; qp < &queue[queuecnt]; qp += 2) { if ((qp->flag & QUSE) == 0) { *qp = zeroR; *WR(qp) = zeroW; return(qp); } } return(NULL); } /* * put routine that shouldn't be called */ noput(q, c) struct queue *q; { panic("noput"); } /* * Put one data char on a queue, using f */ putd(f, q, c) int (*f)(); register struct queue *q; { register struct block *bp; register s = spl6(); if (f==putq && (bp = q->last) && bp->type==M_DATA && bp->wptr<bp->lim && (bp->class&S_DELIM)==0) { *bp->wptr++ = c; splx(s); } else { splx(s); if ((bp = allocb(16)) == NULL) return(0); *bp->wptr++ = c; (*f)(q, bp); } return(1); } /* * Put a block of type c on queue */ putctl(q, c) struct queue *q; { register struct block *bp; if ((bp = allocb(0)) == NULL) return(0); bp->type = c; (*q->qinfo->putp)(q, bp); return(1); } /* * Put a block of type c, delimited, on queue */ putctld(q, c) struct queue *q; { register struct block *bp; if ((bp = allocb(0)) == NULL) return(0); bp->type = c; bp->class |= S_DELIM; (*q->qinfo->putp)(q, bp); return(1); } /* * Block of type c, with one byte of data */ putctl1(q, c, p) struct queue *q; { register struct block *bp; if ((bp = allocb(1)) == NULL) return(0); bp->type = c; *bp->wptr++ = p; (*q->qinfo->putp)(q, bp); return(1); } /* * Block of type c, delimited, with one byte of data */ putctl1d(q, c, p) struct queue *q; { register struct block *bp; if ((bp = allocb(1)) == NULL) return(0); bp->type = c; bp->class |= S_DELIM; *bp->wptr++ = p; (*q->qinfo->putp)(q, bp); return(1); } /* * block with two parameters */ putctl2(q, c, p1, p2) struct queue *q; { register struct block *bp; if ((bp = allocb(2)) == NULL) return(0); bp->type = c; *bp->wptr++ = p1; *bp->wptr++ = p2; (*q->qinfo->putp)(q, bp); return(1); } /* * put control record, using putq instead of queue's putp */ qpctl(q, d) register struct queue *q; { register struct block *bp = allocb(1); if (bp) { bp->type = d; putq(q, bp); } } qpctl1(q, c, d) register struct queue *q; { register struct block *bp = allocb(1); if (bp) { bp->type = c; *bp->wptr++ = d; putq(q, bp); } } qpctld(q, d) register struct queue *q; { register struct block *bp = allocb(1); if (bp) { bp->type = d; bp->class |= S_DELIM; putq(q, bp); } } /* * Copy a literal record onto queue */ putcpy(q, cp, n) register struct queue *q; register char *cp; { register struct block *bp; register nm; while (n) { if ((bp = allocb(n)) == NULL) /* sorry */ return; bp->type = M_DATA; nm = bp->lim - bp->wptr; if (nm > n) nm = n; bcopy(cp, bp->wptr, nm); cp += nm; bp->wptr += nm; n -= nm; (*q->qinfo->putp)(q, bp); } } /* * return the queue upstream from this one */ struct queue * backq(q) register struct queue *q; { q = OTHERQ(q); if (q->next) { q = q->next; return(OTHERQ(q)); } q = OTHERQ(q); printf("backq called with no back (Q %x)\n", q); panic("backq"); return(NULL); } /* * Send a block back up the queue in reverse from this * one (e.g. to respond to ioctls) */ qreply(q, bp) register struct queue *q; struct block *bp; { q = OTHERQ(q); (*q->next->qinfo->putp)(q->next, bp); } /* * Enable a queue: put it on list of those whose srvp's are * ready to run. */ qenable(q) register struct queue *q; { register s; s = spl6(); if (q->flag & QENAB) { splx(s); return; } if (q->qinfo->srvp==NULL) { splx(s); return; } q->flag |= QENAB; q->link = NULL; if (qhead==NULL) qhead = q; else qtail->link = q; qtail = q; setqsched(); splx(s); } /* * Run the srvp's of each enabled queue * -- Should not be reentered */ queuerun() { register struct queue *q; register s; extern int queueflag; extern char *panicstr; if (panicstr) return; /* to minimize destruction */ s = spl6(); queueflag++; while (q = qhead) { if ((qhead = q->link) == NULL) qtail = NULL; q->flag &= ~QENAB; splx(s); if (q->qinfo->srvp != NULL) (*q->qinfo->srvp)(q); else printf("Q %x run with no srvp\n", q); spl6(); } queueflag--; splx(s); }