diff options
author | David Greenman <dg@FreeBSD.org> | 1995-05-21 21:39:31 +0000 |
---|---|---|
committer | David Greenman <dg@FreeBSD.org> | 1995-05-21 21:39:31 +0000 |
commit | 61f5d510623c7fd6aa5335cf8c5b785915365c8d (patch) | |
tree | 07e1ad65c2ce70cb5c077eecec53a0a1544d9f49 /sys/nfs/nfs_bio.c | |
parent | d6f9f66840bc9b9f0007c1f89139a2d653ef767a (diff) | |
download | src-61f5d510623c7fd6aa5335cf8c5b785915365c8d.tar.gz src-61f5d510623c7fd6aa5335cf8c5b785915365c8d.zip |
Changes to fix the following bugs:
1) Files weren't properly synced on filesystems other than UFS. In some
cases, this lead to lost data. Most likely would be noticed on NFS.
The fix is to make the VM page sync/object_clean general rather than
in each filesystem.
2) Mixing regular and mmaped file I/O on NFS was very broken. It caused
chunks of files to end up as zeroes rather than the intended contents.
The fix was to fix several race conditions and to kludge up the
"b_dirtyoff" and "b_dirtyend" that NFS relies upon - paying attention
to page modifications that occurred via the mmapping.
Reviewed by: David Greenman
Submitted by: John Dyson
Notes
Notes:
svn path=/head/; revision=8692
Diffstat (limited to 'sys/nfs/nfs_bio.c')
-rw-r--r-- | sys/nfs/nfs_bio.c | 98 |
1 files changed, 64 insertions, 34 deletions
diff --git a/sys/nfs/nfs_bio.c b/sys/nfs/nfs_bio.c index ebbab310fe9f..057ffb6d598f 100644 --- a/sys/nfs/nfs_bio.c +++ b/sys/nfs/nfs_bio.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_bio.c 8.5 (Berkeley) 1/4/94 - * $Id: nfs_bio.c,v 1.11 1995/03/04 03:24:34 davidg Exp $ + * $Id: nfs_bio.c,v 1.12 1995/04/16 05:05:25 davidg Exp $ */ #include <sys/param.h> @@ -78,6 +78,7 @@ nfs_bioread(vp, uio, ioflag, cred) struct proc *p; struct nfsmount *nmp; daddr_t lbn, rabn; + int bufsize; int nra, error = 0, n = 0, on = 0, not_readin; #ifdef lint @@ -209,7 +210,7 @@ nfs_bioread(vp, uio, ioflag, cred) rabp = nfs_getcacheblk(vp, rabn, biosize, p); if (!rabp) return (EINTR); - if ((rabp->b_flags & B_DELWRI) == 0) { + if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) { rabp->b_flags |= (B_READ | B_ASYNC); vfs_busy_pages(rabp, 0); if (nfs_asyncio(rabp, cred)) { @@ -231,7 +232,12 @@ nfs_bioread(vp, uio, ioflag, cred) * as required. */ again: - bp = nfs_getcacheblk(vp, lbn, biosize, p); + bufsize = biosize; + if ((lbn + 1) * biosize > np->n_size) { + bufsize = np->n_size - lbn * biosize; + bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1); + } + bp = nfs_getcacheblk(vp, lbn, bufsize, p); if (!bp) return (EINTR); if ((bp->b_flags & B_CACHE) == 0) { @@ -244,7 +250,11 @@ again: return (error); } } - n = min((unsigned)(biosize - on), uio->uio_resid); + if (bufsize > on) { + n = min((unsigned)(bufsize - on), uio->uio_resid); + } else { + n = 0; + } diff = np->n_size - uio->uio_offset; if (diff < n) n = diff; @@ -313,7 +323,7 @@ again: !incore(vp, rabn)) { rabp = nfs_getcacheblk(vp, rabn, NFS_DIRBLKSIZ, p); if (rabp) { - if ((rabp->b_flags & B_CACHE) == 0) { + if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) { rabp->b_flags |= (B_READ | B_ASYNC); vfs_busy_pages(rabp, 0); if (nfs_asyncio(rabp, cred)) { @@ -378,6 +388,7 @@ nfs_write(ap) struct vattr vattr; struct nfsmount *nmp; daddr_t lbn; + int bufsize; int n, on, error = 0; #ifdef DIAGNOSTIC @@ -458,7 +469,16 @@ nfs_write(ap) on = uio->uio_offset & (biosize-1); n = min((unsigned)(biosize - on), uio->uio_resid); again: - bp = nfs_getcacheblk(vp, lbn, biosize, p); + if (uio->uio_offset + n > np->n_size) { + np->n_size = uio->uio_offset + n; + vnode_pager_setsize(vp, (u_long)np->n_size); + } + bufsize = biosize; + if ((lbn + 1) * biosize > np->n_size) { + bufsize = np->n_size - lbn * biosize; + bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1); + } + bp = nfs_getcacheblk(vp, lbn, bufsize, p); if (!bp) return (EINTR); if (bp->b_wcred == NOCRED) { @@ -466,9 +486,9 @@ again: bp->b_wcred = cred; } np->n_flag |= NMODIFIED; - if (uio->uio_offset + n > np->n_size) { - np->n_size = uio->uio_offset + n; - vnode_pager_setsize(vp, (u_long)np->n_size); + + if ((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend > np->n_size) { + bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE); } /* @@ -801,18 +821,23 @@ nfs_doio(bp, cr, p) bp->b_error = error; } } else { - io.iov_len = uiop->uio_resid = bp->b_dirtyend - - bp->b_dirtyoff; - uiop->uio_offset = (bp->b_blkno * DEV_BSIZE) - + bp->b_dirtyoff; - io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; - uiop->uio_rw = UIO_WRITE; - nfsstats.write_bios++; - if (bp->b_flags & B_APPENDWRITE) - error = nfs_writerpc(vp, uiop, cr, IO_APPEND); - else - error = nfs_writerpc(vp, uiop, cr, 0); - bp->b_flags &= ~(B_WRITEINPROG | B_APPENDWRITE); + + if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size) + bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE); + + if (bp->b_dirtyend > bp->b_dirtyoff) { + io.iov_len = uiop->uio_resid = bp->b_dirtyend + - bp->b_dirtyoff; + uiop->uio_offset = (bp->b_blkno * DEV_BSIZE) + + bp->b_dirtyoff; + io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; + uiop->uio_rw = UIO_WRITE; + nfsstats.write_bios++; + if (bp->b_flags & B_APPENDWRITE) + error = nfs_writerpc(vp, uiop, cr, IO_APPEND); + else + error = nfs_writerpc(vp, uiop, cr, 0); + bp->b_flags &= ~(B_WRITEINPROG | B_APPENDWRITE); /* * For an interrupted write, the buffer is still valid and the @@ -821,26 +846,31 @@ nfs_doio(bp, cr, p) * the B_ASYNC case, B_EINTR is not relevant, so the rpc attempt * is essentially a noop. */ - if (error == EINTR) { - bp->b_flags &= ~B_INVAL; - bp->b_flags |= B_DELWRI; + if (error == EINTR) { + bp->b_flags &= ~(B_INVAL|B_NOCACHE); + bp->b_flags |= B_DELWRI; /* * Since for the B_ASYNC case, nfs_bwrite() has reassigned the * buffer to the clean list, we have to reassign it back to the * dirty one. Ugh. */ - if (bp->b_flags & B_ASYNC) - reassignbuf(bp, vp); - else - bp->b_flags |= B_EINTR; - } else { - if (error) { - bp->b_flags |= B_ERROR; - bp->b_error = np->n_error = error; - np->n_flag |= NWRITEERR; + if (bp->b_flags & B_ASYNC) + reassignbuf(bp, vp); + else + bp->b_flags |= B_EINTR; + } else { + if (error) { + bp->b_flags |= B_ERROR; + bp->b_error = np->n_error = error; + np->n_flag |= NWRITEERR; + } + bp->b_dirtyoff = bp->b_dirtyend = 0; } - bp->b_dirtyoff = bp->b_dirtyend = 0; + } else { + bp->b_resid = 0; + biodone(bp); + return (0); } } bp->b_resid = uiop->uio_resid; |