X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/c66bde989c72461b5ca17917a47f91441a3f3347..ed554bc5e4201344d7eaad78263566e79428759c:/usr/src/sys/kern/vfs_bio.c diff --git a/usr/src/sys/kern/vfs_bio.c b/usr/src/sys/kern/vfs_bio.c index 2b9568bfc2..ec5c962f7d 100644 --- a/usr/src/sys/kern/vfs_bio.c +++ b/usr/src/sys/kern/vfs_bio.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1986, 1989, 1993 + * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed @@ -7,12 +7,35 @@ * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * - * This code is derived from software contributed to Berkeley by - * Berkeley Software Design Inc. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * - * %sccs.include.redist.c% + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. * - * @(#)vfs_bio.c 8.10 (Berkeley) %G% + * from: @(#)vfs_bio.c 8.6 (Berkeley) 1/11/94 */ #include @@ -24,8 +47,6 @@ #include #include #include -#include -#include /* * Definitions for the buffer hash lists. @@ -118,648 +139,169 @@ bufinit() } } -/* - * Find the block in the buffer pool. - * If the buffer is not present, allocate a new buffer and load - * its contents according to the filesystem fill routine. - */ -bread(vp, blkno, size, cred, bpp) - struct vnode *vp; - daddr_t blkno; - int size; - struct ucred *cred; - struct buf **bpp; -#ifdef SECSIZE - long secsize; -#endif SECSIZE -{ - struct proc *p = curproc; /* XXX */ - register struct buf *bp; - - if (size == 0) - panic("bread: size 0"); -#ifdef SECSIZE - bp = getblk(dev, blkno, size, secsize); -#else SECSIZE - *bpp = bp = getblk(vp, blkno, size, 0, 0); -#endif SECSIZE - if (bp->b_flags & (B_DONE | B_DELWRI)) { - trace(TR_BREADHIT, pack(vp, size), blkno); - return (0); - } - bp->b_flags |= B_READ; - if (bp->b_bcount > bp->b_bufsize) - panic("bread"); - if (bp->b_rcred == NOCRED && cred != NOCRED) { - crhold(cred); - bp->b_rcred = cred; - } - VOP_STRATEGY(bp); - trace(TR_BREADMISS, pack(vp, size), blkno); - p->p_stats->p_ru.ru_inblock++; /* pay for read */ - return (biowait(bp)); -} - -/* - * Operates like bread, but also starts I/O on the N specified - * read-ahead blocks. - */ -breadn(vp, blkno, size, rablkno, rabsize, num, cred, bpp) - struct vnode *vp; - daddr_t blkno; int size; -#ifdef SECSIZE - long secsize; -#endif SECSIZE - daddr_t rablkno[]; int rabsize[]; - int num; - struct ucred *cred; - struct buf **bpp; +bread(a1, a2, a3, a4, a5) + struct vnode *a1; + daddr_t a2; + int a3; + struct ucred *a4; + struct buf **a5; { - struct proc *p = curproc; /* XXX */ - register struct buf *bp, *rabp; - register int i; - bp = NULL; /* - * If the block is not memory resident, - * allocate a buffer and start I/O. + * Body deleted. */ - if (!incore(vp, blkno)) { - *bpp = bp = getblk(vp, blkno, size, 0, 0); -#endif SECSIZE - if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) { - bp->b_flags |= B_READ; - if (bp->b_bcount > bp->b_bufsize) - panic("breadn"); - if (bp->b_rcred == NOCRED && cred != NOCRED) { - crhold(cred); - bp->b_rcred = cred; - } - VOP_STRATEGY(bp); - trace(TR_BREADMISS, pack(vp, size), blkno); - p->p_stats->p_ru.ru_inblock++; /* pay for read */ - } else { - trace(TR_BREADHIT, pack(vp, size), blkno); - } - } + return (EIO); +} - /* - * If there's read-ahead block(s), start I/O - * on them also (as above). - */ - for (i = 0; i < num; i++) { - if (incore(vp, rablkno[i])) - continue; - rabp = getblk(vp, rablkno[i], rabsize[i], 0, 0); -#endif SECSIZE - if (rabp->b_flags & (B_DONE | B_DELWRI)) { - brelse(rabp); - trace(TR_BREADHITRA, pack(vp, rabsize[i]), rablkno[i]); - } else { - rabp->b_flags |= B_ASYNC | B_READ; - if (rabp->b_bcount > rabp->b_bufsize) - panic("breadrabp"); - if (rabp->b_rcred == NOCRED && cred != NOCRED) { - crhold(cred); - rabp->b_rcred = cred; - } - VOP_STRATEGY(rabp); - trace(TR_BREADMISSRA, pack(vp, rabsize[i]), rablkno[i]); - p->p_stats->p_ru.ru_inblock++; /* pay in advance */ - } - } +breadn(a1, a2, a3, a4, a5, a6, a7, a8) + struct vnode *a1; + daddr_t a2; int a3; + daddr_t a4[]; int a5[]; + int a6; + struct ucred *a7; + struct buf **a8; +{ /* - * If block was memory resident, let bread get it. - * If block was not memory resident, the read was - * started above, so just wait for the read to complete. + * Body deleted. */ - if (bp == NULL) -#ifdef SECSIZE - return (bread(dev, blkno, size, secsize)); -#else SECSIZE - return (bread(vp, blkno, size, cred, bpp)); - return (biowait(bp)); + return (EIO); } -/* - * Synchronous write. - * Release buffer on completion. - */ -bwrite(bp) - register struct buf *bp; +bwrite(a1) + struct buf *a1; { - struct proc *p = curproc; /* XXX */ - register int flag; - int s, error = 0; - - if ((bp->b_flags & B_ASYNC) == 0 && - bp->b_vp && bp->b_vp->v_mount && - (bp->b_vp->v_mount->mnt_flag & MNT_ASYNC)) { - bdwrite(bp); - return (0); - } - flag = bp->b_flags; - bp->b_flags &= ~(B_READ | B_DONE | B_ERROR | B_DELWRI); - if (flag & B_ASYNC) { - if ((flag & B_DELWRI) == 0) - p->p_stats->p_ru.ru_oublock++; /* no one paid yet */ - else - reassignbuf(bp, bp->b_vp); - } - trace(TR_BWRITE, pack(bp->b_vp, bp->b_bcount), bp->b_lblkno); - if (bp->b_bcount > bp->b_bufsize) - panic("bwrite"); - s = splbio(); - bp->b_vp->v_numoutput++; - bp->b_flags |= B_WRITEINPROG; - splx(s); - VOP_STRATEGY(bp); /* - * If the write was synchronous, then await I/O completion. - * If the write was "delayed", then we put the buffer on - * the queue of blocks awaiting I/O completion status. + * Body deleted. */ - if ((flag & B_ASYNC) == 0) { - error = biowait(bp); - if ((flag&B_DELWRI) == 0) - p->p_stats->p_ru.ru_oublock++; /* no one paid yet */ - else - reassignbuf(bp, bp->b_vp); - if (bp->b_flags & B_EINTR) { - bp->b_flags &= ~B_EINTR; - error = EINTR; - } - brelse(bp); - } else if (flag & B_DELWRI) { - s = splbio(); - bp->b_flags |= B_AGE; - splx(s); - } - return (error); + return (EIO); } int vn_bwrite(ap) struct vop_bwrite_args *ap; { - return (bwrite(ap->a_bp)); } - -/* - * Delayed write. - * - * The buffer is marked dirty, but is not queued for I/O. - * This routine should be used when the buffer is expected - * to be modified again soon, typically a small write that - * partially fills a buffer. - * - * NB: magnetic tapes cannot be delayed; they must be - * written in the order that the writes are requested. - */ -bdwrite(bp) - register struct buf *bp; +bdwrite(a1) + struct buf *a1; { - struct proc *p = curproc; /* XXX */ - if ((bp->b_flags & B_DELWRI) == 0) { - bp->b_flags |= B_DELWRI; - reassignbuf(bp, bp->b_vp); - p->p_stats->p_ru.ru_oublock++; /* no one paid yet */ - } /* - * If this is a tape drive, the write must be initiated. + * Body deleted. */ - if (bdevsw[major(bp->b_dev)].d_flags & B_TAPE) - bawrite(bp); - } else { - bp->b_flags |= (B_DONE | B_DELWRI); - brelse(bp); - } + return; } -/* - * Asynchronous write. - * Start I/O on a buffer, but do not wait for it to complete. - * The buffer is released when the I/O completes. - */ -bawrite(bp) - register struct buf *bp; +bawrite(a1) + struct buf *a1; { /* - * Setting the ASYNC flag causes bwrite to return - * after starting the I/O. + * Body deleted. */ - bp->b_flags |= B_ASYNC; - (void) VOP_BWRITE(bp); + return; } -/* - * Release a buffer. - * Even if the buffer is dirty, no I/O is started. - */ -brelse(bp) - register struct buf *bp; +brelse(a1) + struct buf *a1; { - register struct bqueues *flist; - int s; - trace(TR_BRELSE, pack(bp->b_vp, bp->b_bufsize), bp->b_lblkno); /* - * If a process is waiting for the buffer, or - * is waiting for a free buffer, awaken it. + * Body deleted. */ - if (bp->b_flags & B_WANTED) - wakeup((caddr_t)bp); - if (needbuffer) { - needbuffer = 0; - wakeup((caddr_t)&needbuffer); - } - /* - * Retry I/O for locked buffers rather than invalidating them. - */ - s = splbio(); - if ((bp->b_flags & B_ERROR) && (bp->b_flags & B_LOCKED)) - bp->b_flags &= ~B_ERROR; - /* - * Disassociate buffers that are no longer valid. - */ - if (bp->b_flags & (B_NOCACHE | B_ERROR)) - bp->b_flags |= B_INVAL; - if ((bp->b_bufsize <= 0) || (bp->b_flags & (B_ERROR | B_INVAL))) { - if (bp->b_vp) - brelvp(bp); - bp->b_flags &= ~B_DELWRI; - } - /* - * Stick the buffer back on a free list. - */ - if (bp->b_bufsize <= 0) { - /* block has no buffer ... put at front of unused buffer list */ - flist = &bufqueues[BQ_EMPTY]; - binsheadfree(bp, flist); - } else if (bp->b_flags & (B_ERROR | B_INVAL)) { - /* block has no info ... put at front of most free list */ - flist = &bufqueues[BQ_AGE]; - binsheadfree(bp, flist); - } else { - if (bp->b_flags & B_LOCKED) - flist = &bufqueues[BQ_LOCKED]; - else if (bp->b_flags & B_AGE) - flist = &bufqueues[BQ_AGE]; - else - flist = &bufqueues[BQ_LRU]; - binstailfree(bp, flist); - } - bp->b_flags &= ~(B_WANTED | B_BUSY | B_ASYNC | B_AGE | B_NOCACHE); - splx(s); + return; } -/* - * Check to see if a block is currently memory resident. - */ struct buf * -incore(vp, blkno) - struct vnode *vp; - daddr_t blkno; +incore(a1, a2) + struct vnode *a1; + daddr_t a2; { - register struct buf *bp; - for (bp = BUFHASH(vp, blkno)->lh_first; bp; bp = bp->b_hash.le_next) - if (bp->b_lblkno == blkno && bp->b_vp == vp && - (bp->b_flags & B_INVAL) == 0) - return (bp); - return (NULL); + /* + * Body deleted. + */ + return (0); } -/* - * Check to see if a block is currently memory resident. - * If it is resident, return it. If it is not resident, - * allocate a new buffer and assign it to the block. - */ struct buf * -#ifdef SECSIZE -getblk(dev, blkno, size, secsize) -#else SECSIZE -getblk(vp, blkno, size, slpflag, slptimeo) - register struct vnode *vp; - daddr_t blkno; - int size, slpflag, slptimeo; -#ifdef SECSIZE - long secsize; -#endif SECSIZE +getblk(a1, a2, a3, a4, a5) + struct vnode *a1; + daddr_t a2; + int a3, a4, a5; { - register struct buf *bp; - struct bufhashhdr *dp; - int s, error; - if (size > MAXBSIZE) - panic("getblk: size too big"); - /* - * Search the cache for the block. If the buffer is found, - * but it is currently locked, the we must wait for it to - * become available. - */ - dp = BUFHASH(vp, blkno); -loop: - for (bp = dp->lh_first; bp; bp = bp->b_hash.le_next) { - if (bp->b_lblkno != blkno || bp->b_vp != vp) - continue; - s = splbio(); - if (bp->b_flags & B_BUSY) { - bp->b_flags |= B_WANTED; - error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1), - "getblk", slptimeo); - splx(s); - if (error) - return (NULL); - goto loop; - } - /* - * The test for B_INVAL is moved down here, since there - * are cases where B_INVAL is set before VOP_BWRITE() is - * called and for NFS, the process cannot be allowed to - * allocate a new buffer for the same block until the write - * back to the server has been completed. (ie. B_BUSY clears) - */ - if (bp->b_flags & B_INVAL) { - splx(s); - continue; - } - bremfree(bp); - bp->b_flags |= B_BUSY; - splx(s); - if (bp->b_bcount != size) { - printf("getblk: stray size\n"); - bp->b_flags |= B_INVAL; - VOP_BWRITE(bp); - goto loop; - } - bp->b_flags |= B_CACHE; - return (bp); - } /* - * The loop back to the top when getnewbuf() fails is because - * stateless filesystems like NFS have no node locks. Thus, - * there is a slight chance that more than one process will - * try and getnewbuf() for the same block concurrently when - * the first sleeps in getnewbuf(). So after a sleep, go back - * up to the top to check the hash lists again. + * Body deleted. */ - if ((bp = getnewbuf(slpflag, slptimeo)) == 0) - goto loop; - bremhash(bp); - bgetvp(vp, bp); - bp->b_bcount = 0; - bp->b_lblkno = blkno; -#ifdef SECSIZE - bp->b_blksize = secsize; -#endif SECSIZE - bp->b_blkno = blkno; - bp->b_error = 0; - bp->b_resid = 0; - binshash(bp, dp); - allocbuf(bp, size); - return (bp); + return ((struct buf *)0); } -/* - * Allocate a buffer. - * The caller will assign it to a block. - */ struct buf * -geteblk(size) - int size; +geteblk(a1) + int a1; { - register struct buf *bp; - if (size > MAXBSIZE) - panic("geteblk: size too big"); - while ((bp = getnewbuf(0, 0)) == NULL) - /* void */; - bp->b_flags |= B_INVAL; - bremhash(bp); - binshash(bp, &invalhash); - bp->b_bcount = 0; -#ifdef SECSIZE - bp->b_blksize = DEV_BSIZE; -#endif SECSIZE - bp->b_error = 0; - bp->b_resid = 0; - allocbuf(bp, size); - return (bp); + /* + * Body deleted. + */ + return ((struct buf *)0); } -/* - * Expand or contract the actual memory allocated to a buffer. - * If no memory is available, release buffer and take error exit. - */ -allocbuf(tp, size) - register struct buf *tp; - int size; +allocbuf(a1, a2) + struct buf *a1; + int a2; { - register struct buf *bp, *ep; - int sizealloc, take, s; - sizealloc = roundup(size, CLBYTES); - /* - * Buffer size does not change - */ - if (sizealloc == tp->b_bufsize) - goto out; /* - * Buffer size is shrinking. - * Place excess space in a buffer header taken from the - * BQ_EMPTY buffer list and placed on the "most free" list. - * If no extra buffer headers are available, leave the - * extra space in the present buffer. + * Body deleted. */ - if (sizealloc < tp->b_bufsize) { - if ((ep = bufqueues[BQ_EMPTY].tqh_first) == NULL) - goto out; - s = splbio(); - bremfree(ep); - ep->b_flags |= B_BUSY; - splx(s); - pagemove((char *)tp->b_data + sizealloc, ep->b_data, - (int)tp->b_bufsize - sizealloc); - ep->b_bufsize = tp->b_bufsize - sizealloc; - tp->b_bufsize = sizealloc; - ep->b_flags |= B_INVAL; - ep->b_bcount = 0; - brelse(ep); - goto out; - } - /* - * More buffer space is needed. Get it out of buffers on - * the "most free" list, placing the empty headers on the - * BQ_EMPTY buffer header list. - */ - while (tp->b_bufsize < sizealloc) { - take = sizealloc - tp->b_bufsize; - while ((bp = getnewbuf(0, 0)) == NULL) - /* void */; - if (take >= bp->b_bufsize) - take = bp->b_bufsize; - pagemove(&((char *)bp->b_data)[bp->b_bufsize - take], - &((char *)tp->b_data)[tp->b_bufsize], take); - tp->b_bufsize += take; - bp->b_bufsize = bp->b_bufsize - take; - if (bp->b_bcount > bp->b_bufsize) - bp->b_bcount = bp->b_bufsize; - if (bp->b_bufsize <= 0) { - bremhash(bp); - binshash(bp, &invalhash); - bp->b_dev = NODEV; - bp->b_error = 0; - bp->b_flags |= B_INVAL; - } - brelse(bp); - } -out: - tp->b_bcount = size; - return (1); + return (0); } -/* - * Find a buffer which is available for use. - * Select something from a free list. - * Preference is to AGE list, then LRU list. - */ struct buf * -getnewbuf(slpflag, slptimeo) - int slpflag, slptimeo; +getnewbuf(a1, a2) + int a1, a2; { - register struct buf *bp; - register struct bqueues *dp; - register struct ucred *cred; - int s; - struct buf *abp; - static int losecnt = 0; - -loop: - s = splbio(); - abp = NULL; - for (dp = &bufqueues[BQ_AGE]; dp > bufqueues; dp--) { - for (bp = dp->qe_next; bp; bp = bp->b_freelist.qe_next) { - if (abp == NULL) - abp = bp; - if ((bp->b_flags & B_DELWRI) && - bp->b_vp && VOP_ISLOCKED(bp->b_vp)) - continue; - goto found; - } - } - if (dp == bufqueues) { /* no free blocks */ - if (abp) { - bp = abp; - bp->b_flags |= B_XXX; - if (losecnt++ < 20) { - vprint("skipping blkno check", bp->b_vp); - printf("\tlblkno %d, blkno %d\n", - bp->b_lblkno, bp->b_blkno); - } - goto found; - } - needbuffer = 1; - (void) tsleep((caddr_t)&needbuffer, slpflag | (PRIBIO + 1), - "getnewbuf", slptimeo); - splx(s); - return (NULL); - } -found: - bremfree(bp); - bp->b_flags |= B_BUSY; - splx(s); - if (bp->b_flags & B_DELWRI) { - (void) bawrite(bp); - goto loop; - } - trace(TR_BRELSE, pack(bp->b_vp, bp->b_bufsize), bp->b_lblkno); - if (bp->b_vp) - brelvp(bp); - if (bp->b_rcred != NOCRED) { - cred = bp->b_rcred; - bp->b_rcred = NOCRED; - crfree(cred); - } - if (bp->b_wcred != NOCRED) { - cred = bp->b_wcred; - bp->b_wcred = NOCRED; - crfree(cred); - } - bp->b_flags = B_BUSY; - bp->b_dirtyoff = bp->b_dirtyend = 0; - bp->b_validoff = bp->b_validend = 0; - return (bp); + + /* + * Body deleted. + */ + return ((struct buf *)0); } -/* - * Wait for I/O to complete. - * - * Extract and return any errors associated with the I/O. - * If the error flag is set, but no specific error is - * given, return EIO. - */ -biowait(bp) - register struct buf *bp; +biowait(a1) + struct buf *a1; { - int s; - - s = splbio(); - while ((bp->b_flags & B_DONE) == 0) - sleep((caddr_t)bp, PRIBIO); - splx(s); - if ((bp->b_flags & B_ERROR) == 0) - return (0); - if (bp->b_error) - return (bp->b_error); + + /* + * Body deleted. + */ return (EIO); } -/* - * Mark I/O complete on a buffer. - * - * If a callback has been requested, e.g. the pageout - * daemon, do so. Otherwise, awaken waiting processes. - */ void -biodone(bp) - register struct buf *bp; +biodone(a1) + struct buf *a1; { - if (bp->b_flags & B_DONE) - panic("dup biodone"); - bp->b_flags |= B_DONE; - if ((bp->b_flags & B_READ) == 0) - vwakeup(bp); - if (bp->b_flags & B_CALL) { - bp->b_flags &= ~B_CALL; - (*bp->b_iodone)(bp); - return; - } - if (bp->b_flags & B_ASYNC) - brelse(bp); - else { - bp->b_flags &= ~B_WANTED; - wakeup((caddr_t)bp); - } + /* + * Body deleted. + */ + return; } int count_lock_queue() { - register struct buf *bp; - register int ret; - for (ret = 0, bp = (struct buf *)bufqueues[BQ_LOCKED].tqh_first; - bp; bp = (struct buf *)bp->b_freelist.tqe_next) - ++ret; - return(ret); + /* + * Body deleted. + */ + return (0); } #ifdef DIAGNOSTIC