diff options
Diffstat (limited to 'sys/nfs/nfs_bio.c')
-rw-r--r-- | sys/nfs/nfs_bio.c | 292 |
1 files changed, 181 insertions, 111 deletions
diff --git a/sys/nfs/nfs_bio.c b/sys/nfs/nfs_bio.c index f929012..f9ae8b6 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.13 1995/05/21 21:39:21 davidg Exp $ + * $Id: nfs_bio.c,v 1.14 1995/05/30 08:12:35 rgrimes Exp $ */ #include <sys/param.h> @@ -49,16 +49,30 @@ #include <vm/vm.h> -#include <nfs/nfsnode.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfs.h> #include <nfs/nfsmount.h> #include <nfs/nqnfs.h> +#include <nfs/nfsnode.h> struct buf *nfs_getcacheblk(); extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; extern int nfs_numasync; +extern struct nfsstats nfsstats; + +/* + * Ifdefs for FreeBSD-current's merged VM/buffer cache. It is unfortunate + * that this isn't done inside getblk() and brelse() so these calls + * wouldn't need to be here. + */ +#ifdef B_VMIO +#define vnode_pager_uncache(vp) +#else +#define vfs_busy_pages(bp, f) +#define vfs_unbusy_pages(bp) +#define vfs_dirty_pages(bp) +#endif /* * Vnode op for read using bio @@ -72,29 +86,28 @@ nfs_bioread(vp, uio, ioflag, cred) struct ucred *cred; { register struct nfsnode *np = VTONFS(vp); - register int biosize, diff; + register int biosize, diff, i; struct buf *bp = 0, *rabp; struct vattr vattr; struct proc *p; - struct nfsmount *nmp; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); daddr_t lbn, rabn; int bufsize; int nra, error = 0, n = 0, on = 0, not_readin; + nfsquad_t tquad; -#ifdef lint - ioflag = ioflag; -#endif /* lint */ #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ) panic("nfs_read mode"); #endif if (uio->uio_resid == 0) return (0); - if (uio->uio_offset < 0 && vp->v_type != VDIR) + if (uio->uio_offset < 0) return (EINVAL); - nmp = VFSTONFS(vp->v_mount); - biosize = NFS_MAXDGRAMDATA; p = uio->uio_procp; + if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3) + (void)nfs_fsinfo(nmp, vp, cred, p); + biosize = nmp->nm_rsize; /* * For nfs, cache consistency can only be maintained approximately. * Although RFC1094 does not specify the criteria, the following is @@ -107,8 +120,6 @@ nfs_bioread(vp, uio, ioflag, cred) * server, so flush all of the file's data out of the cache. * Then force a getattr rpc to ensure that you have up to date * attributes. - * The mount flag NFSMNT_MYWRITE says "Assume that my writes are - * the ones changing the modify time. * NB: This implies that cache data can be read when up to * NFS_ATTRTIMEO seconds out of date. If you find that you need current * attributes this could be forced by setting n_attrstamp to 0 before @@ -116,14 +127,15 @@ nfs_bioread(vp, uio, ioflag, cred) */ if ((nmp->nm_flag & NFSMNT_NQNFS) == 0 && vp->v_type != VLNK) { if (np->n_flag & NMODIFIED) { - if ((nmp->nm_flag & NFSMNT_MYWRITE) == 0 || - vp->v_type != VREG) { + if (vp->v_type != VREG) { + if (vp->v_type != VDIR) + panic("nfs: bioread, not dir"); + nfs_invaldir(vp); error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); if (error) return (error); } np->n_attrstamp = 0; - np->n_direofoffset = 0; error = VOP_GETATTR(vp, &vattr, cred, p); if (error) return (error); @@ -133,7 +145,8 @@ nfs_bioread(vp, uio, ioflag, cred) if (error) return (error); if (np->n_mtime != vattr.va_mtime.ts_sec) { - np->n_direofoffset = 0; + if (vp->v_type == VDIR) + nfs_invaldir(vp); error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); if (error) return (error); @@ -147,27 +160,24 @@ nfs_bioread(vp, uio, ioflag, cred) * Get a valid lease. If cached data is stale, flush it. */ if (nmp->nm_flag & NFSMNT_NQNFS) { - if (NQNFS_CKINVALID(vp, np, NQL_READ)) { + if (NQNFS_CKINVALID(vp, np, ND_READ)) { do { - error = nqnfs_getlease(vp, NQL_READ, cred, p); + error = nqnfs_getlease(vp, ND_READ, cred, p); } while (error == NQNFS_EXPIRED); if (error) return (error); if (np->n_lrev != np->n_brev || (np->n_flag & NQNFSNONCACHE) || ((np->n_flag & NMODIFIED) && vp->v_type == VDIR)) { - if (vp->v_type == VDIR) { - np->n_direofoffset = 0; - cache_purge(vp); - } + if (vp->v_type == VDIR) + nfs_invaldir(vp); error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); if (error) return (error); np->n_brev = np->n_lrev; } } else if (vp->v_type == VDIR && (np->n_flag & NMODIFIED)) { - np->n_direofoffset = 0; - cache_purge(vp); + nfs_invaldir(vp); error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); if (error) return (error); @@ -176,33 +186,27 @@ nfs_bioread(vp, uio, ioflag, cred) if (np->n_flag & NQNFSNONCACHE) { switch (vp->v_type) { case VREG: - error = nfs_readrpc(vp, uio, cred); - break; + return (nfs_readrpc(vp, uio, cred)); case VLNK: - error = nfs_readlinkrpc(vp, uio, cred); - break; + return (nfs_readlinkrpc(vp, uio, cred)); case VDIR: - error = nfs_readdirrpc(vp, uio, cred); break; default: printf(" NQNFSNONCACHE: type %x unexpected\n", vp->v_type); - break; }; - return (error); } switch (vp->v_type) { case VREG: nfsstats.biocache_reads++; lbn = uio->uio_offset / biosize; - on = uio->uio_offset & (biosize-1); + on = uio->uio_offset & (biosize - 1); not_readin = 1; /* * Start the read ahead(s), as required. */ - if (nfs_numasync > 0 && nmp->nm_readahead > 0 && - lbn == vp->v_lastr + 1) { + if (nfs_numasync > 0 && nmp->nm_readahead > 0) { for (nra = 0; nra < nmp->nm_readahead && (lbn + 1 + nra) * biosize < np->n_size; nra++) { rabn = lbn + 1 + nra; @@ -296,32 +300,55 @@ again: break; case VDIR: nfsstats.biocache_readdirs++; - lbn = (daddr_t)uio->uio_offset; + lbn = uio->uio_offset / NFS_DIRBLKSIZ; + on = uio->uio_offset & (NFS_DIRBLKSIZ - 1); bp = nfs_getcacheblk(vp, lbn, NFS_DIRBLKSIZ, p); if (!bp) - return (EINTR); - + return (EINTR); if ((bp->b_flags & B_CACHE) == 0) { - bp->b_flags |= B_READ; - vfs_busy_pages(bp, 0); - error = nfs_doio(bp, cred, p); - if (error) { - bp->b_flags |= B_ERROR; - brelse(bp); - return (error); + bp->b_flags |= B_READ; + vfs_busy_pages(bp, 0); + error = nfs_doio(bp, cred, p); + if (error) { + brelse(bp); + while (error == NFSERR_BAD_COOKIE) { + nfs_invaldir(vp); + error = nfs_vinvalbuf(vp, 0, cred, p, 1); + /* + * Yuck! The directory has been modified on the + * server. The only way to get the block is by + * reading from the beginning to get all the + * offset cookies. + */ + for (i = 0; i <= lbn && !error; i++) { + bp = nfs_getcacheblk(vp, i, NFS_DIRBLKSIZ, p); + if (!bp) + return (EINTR); + if ((bp->b_flags & B_DONE) == 0) { + bp->b_flags |= B_READ; + vfs_busy_pages(bp, 0); + error = nfs_doio(bp, cred, p); + if (error) + brelse(bp); + } + } } + if (error) + return (error); + } } /* * If not eof and read aheads are enabled, start one. * (You need the current block first, so that you have the - * directory offset cookie of the next block. + * directory offset cookie of the next block.) */ - rabn = bp->b_blkno; if (nfs_numasync > 0 && nmp->nm_readahead > 0 && - rabn != 0 && rabn != np->n_direofoffset && - !incore(vp, rabn)) { - rabp = nfs_getcacheblk(vp, rabn, NFS_DIRBLKSIZ, p); + (np->n_direofoffset == 0 || + (lbn + 1) * NFS_DIRBLKSIZ < np->n_direofoffset) && + !(np->n_flag & NQNFSNONCACHE) && + !incore(vp, lbn + 1)) { + rabp = nfs_getcacheblk(vp, lbn + 1, NFS_DIRBLKSIZ, p); if (rabp) { if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) { rabp->b_flags |= (B_READ | B_ASYNC); @@ -336,11 +363,10 @@ again: } } } - on = 0; - n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid); + n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid - on); break; default: - printf(" nfsbioread: type %x unexpected\n",vp->v_type); + printf(" nfs_bioread: type %x unexpected\n",vp->v_type); break; }; @@ -354,11 +380,11 @@ again: n = 0; break; case VDIR: - uio->uio_offset = bp->b_blkno; + if (np->n_flag & NQNFSNONCACHE) + bp->b_flags |= B_INVAL; break; default: - printf(" nfsbioread: type %x unexpected\n",vp->v_type); - break; + printf(" nfs_bioread: type %x unexpected\n",vp->v_type); } brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n > 0); @@ -386,10 +412,10 @@ nfs_write(ap) int ioflag = ap->a_ioflag; struct buf *bp; struct vattr vattr; - struct nfsmount *nmp; - daddr_t lbn; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); + daddr_t lbn, bn; int bufsize; - int n, on, error = 0; + int n, on, error = 0, iomode, must_commit; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_WRITE) @@ -403,6 +429,8 @@ nfs_write(ap) np->n_flag &= ~NWRITEERR; return (np->n_error); } + if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3) + (void)nfs_fsinfo(nmp, vp, cred, p); if (ioflag & (IO_APPEND | IO_SYNC)) { if (np->n_flag & NMODIFIED) { np->n_attrstamp = 0; @@ -418,7 +446,6 @@ nfs_write(ap) uio->uio_offset = np->n_size; } } - nmp = VFSTONFS(vp->v_mount); if (uio->uio_offset < 0) return (EINVAL); if (uio->uio_resid == 0) @@ -437,7 +464,7 @@ nfs_write(ap) * will be the same size within a filesystem. nfs_writerpc will * still use nm_wsize when sizing the rpc's. */ - biosize = NFS_MAXDGRAMDATA; + biosize = nmp->nm_rsize; do { /* @@ -445,12 +472,11 @@ nfs_write(ap) */ /* * Check for a valid write lease. - * If non-cachable, just do the rpc */ if ((nmp->nm_flag & NFSMNT_NQNFS) && - NQNFS_CKINVALID(vp, np, NQL_WRITE)) { + NQNFS_CKINVALID(vp, np, ND_WRITE)) { do { - error = nqnfs_getlease(vp, NQL_WRITE, cred, p); + error = nqnfs_getlease(vp, ND_WRITE, cred, p); } while (error == NQNFS_EXPIRED); if (error) return (error); @@ -462,8 +488,13 @@ nfs_write(ap) np->n_brev = np->n_lrev; } } - if (np->n_flag & NQNFSNONCACHE) - return (nfs_writerpc(vp, uio, cred, ioflag)); + if ((np->n_flag & NQNFSNONCACHE) && uio->uio_iovcnt == 1) { + iomode = NFSV3WRITE_FILESYNC; + error = nfs_writerpc(vp, uio, cred, &iomode, &must_commit); + if (must_commit) + nfs_clearcommit(vp->v_mount); + return (error); + } nfsstats.biocache_writes++; lbn = uio->uio_offset / biosize; on = uio->uio_offset & (biosize-1); @@ -509,9 +540,9 @@ again: * In case getblk() and/or bwrite() delayed us. */ if ((nmp->nm_flag & NFSMNT_NQNFS) && - NQNFS_CKINVALID(vp, np, NQL_WRITE)) { + NQNFS_CKINVALID(vp, np, ND_WRITE)) { do { - error = nqnfs_getlease(vp, NQL_WRITE, cred, p); + error = nqnfs_getlease(vp, ND_WRITE, cred, p); } while (error == NQNFS_EXPIRED); if (error) { brelse(bp); @@ -540,7 +571,6 @@ again: bp->b_dirtyoff = on; bp->b_dirtyend = on + n; } -#ifndef notdef if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff || bp->b_validoff > bp->b_dirtyend) { bp->b_validoff = bp->b_dirtyoff; @@ -549,13 +579,6 @@ again: bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff); bp->b_validend = max(bp->b_validend, bp->b_dirtyend); } -#else - bp->b_validoff = bp->b_dirtyoff; - bp->b_validend = bp->b_dirtyend; -#endif - if (ioflag & IO_APPEND) - bp->b_flags |= B_APPENDWRITE; - /* * If the lease is non-cachable or IO_SYNC do bwrite(). */ @@ -564,10 +587,16 @@ again: error = VOP_BWRITE(bp); if (error) return (error); + if (np->n_flag & NQNFSNONCACHE) { + error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + if (error) + return (error); + } } else if ((n + on) == biosize && (nmp->nm_flag & NFSMNT_NQNFS) == 0) { bp->b_proc = (struct proc *)0; - bawrite(bp); + bp->b_flags |= B_ASYNC; + (void)nfs_writebp(bp, 0); } else bdwrite(bp); } while (uio->uio_resid > 0 && n > 0); @@ -590,6 +619,7 @@ nfs_getcacheblk(vp, bn, size, p) { register struct buf *bp; struct nfsmount *nmp = VFSTONFS(vp->v_mount); + int biosize = nmp->nm_rsize; if (nmp->nm_flag & NFSMNT_INT) { bp = getblk(vp, bn, size, PCATCH, 0); @@ -602,7 +632,7 @@ nfs_getcacheblk(vp, bn, size, p) bp = getblk(vp, bn, size, 0, 0); if( vp->v_type == VREG) - bp->b_blkno = (bn * NFS_MAXDGRAMDATA) / DEV_BSIZE; + bp->b_blkno = (bn * biosize) / DEV_BSIZE; return (bp); } @@ -689,6 +719,7 @@ nfs_asyncio(bp, cred) bp->b_rcred = cred; } } else { + bp->b_flags |= B_WRITEINPROG; if (bp->b_wcred == NOCRED && cred != NOCRED) { crhold(cred); bp->b_wcred = cred; @@ -700,7 +731,25 @@ nfs_asyncio(bp, cred) wakeup((caddr_t)&nfs_iodwant[i]); return (0); } - return (EIO); + + /* + * If it is a read or a write already marked B_WRITEINPROG or B_NOCACHE + * return EIO so the process will call nfs_doio() and do it + * synchronously. + */ + if (bp->b_flags & (B_READ | B_WRITEINPROG | B_NOCACHE)) + return (EIO); + + /* + * Just turn the async write into a delayed write, instead of + * doing in synchronously. Hopefully, at least one of the nfsiods + * is currently doing a write for this file and will pick up the + * delayed writes before going back to sleep. + */ + bp->b_flags |= B_DELWRI; + reassignbuf(bp, bp->b_vp); + biodone(bp); + return (0); } /* @@ -717,9 +766,10 @@ nfs_doio(bp, cr, p) register struct vnode *vp; struct nfsnode *np; struct nfsmount *nmp; - int error = 0, diff, len; + int error = 0, diff, len, iomode, must_commit = 0; struct uio uio; struct iovec io; + nfsquad_t tquad; vp = bp->b_vp; np = VTONFS(vp); @@ -740,15 +790,18 @@ nfs_doio(bp, cr, p) io.iov_len = uiop->uio_resid = bp->b_bcount; /* mapping was done by vmapbuf() */ io.iov_base = bp->b_data; - uiop->uio_offset = bp->b_blkno * DEV_BSIZE; + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; if (bp->b_flags & B_READ) { uiop->uio_rw = UIO_READ; nfsstats.read_physios++; error = nfs_readrpc(vp, uiop, cr); } else { + int com; + + iomode = NFSV3WRITE_DATASYNC; uiop->uio_rw = UIO_WRITE; nfsstats.write_physios++; - error = nfs_writerpc(vp, uiop, cr,0); + error = nfs_writerpc(vp, uiop, cr, &iomode, &com); } if (error) { bp->b_flags |= B_ERROR; @@ -760,7 +813,7 @@ nfs_doio(bp, cr, p) uiop->uio_rw = UIO_READ; switch (vp->v_type) { case VREG: - uiop->uio_offset = bp->b_blkno * DEV_BSIZE; + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; nfsstats.read_bios++; error = nfs_readrpc(vp, uiop, cr); if (!error) { @@ -773,7 +826,7 @@ nfs_doio(bp, cr, p) * Just zero fill the rest of the valid area. */ diff = bp->b_bcount - uiop->uio_resid; - len = np->n_size - (bp->b_blkno * DEV_BSIZE + len = np->n_size - (((u_quad_t)bp->b_blkno) * DEV_BSIZE + diff); if (len > 0) { len = min(len, uiop->uio_resid); @@ -786,31 +839,34 @@ nfs_doio(bp, cr, p) } if (p && (vp->v_flag & VTEXT) && (((nmp->nm_flag & NFSMNT_NQNFS) && - NQNFS_CKINVALID(vp, np, NQL_READ) && + NQNFS_CKINVALID(vp, np, ND_READ) && np->n_lrev != np->n_brev) || (!(nmp->nm_flag & NFSMNT_NQNFS) && np->n_mtime != np->n_vattr.va_mtime.ts_sec))) { uprintf("Process killed due to text file modification\n"); psignal(p, SIGKILL); +#ifdef __NetBSD__ + p->p_holdcnt++; +#else p->p_flag |= P_NOSWAP; +#endif } break; case VLNK: - uiop->uio_offset = 0; + uiop->uio_offset = (off_t)0; nfsstats.readlink_bios++; error = nfs_readlinkrpc(vp, uiop, cr); break; case VDIR: - uiop->uio_offset = bp->b_lblkno; nfsstats.readdir_bios++; - if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) - error = nfs_readdirlookrpc(vp, uiop, cr); - else - error = nfs_readdirrpc(vp, uiop, cr); - /* - * Save offset cookie in b_blkno. - */ - bp->b_blkno = uiop->uio_offset; + uiop->uio_offset = ((u_quad_t)bp->b_lblkno) * NFS_DIRBLKSIZ; + if (nmp->nm_flag & NFSMNT_RDIRPLUS) { + error = nfs_readdirplusrpc(vp, uiop, cr); + if (error == NFSERR_NOTSUPP) + nmp->nm_flag &= ~NFSMNT_RDIRPLUS; + } + if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0) + error = nfs_readdirrpc(vp, uiop, cr); break; default: printf("nfs_doio: type %x unexpected\n",vp->v_type); @@ -821,32 +877,44 @@ nfs_doio(bp, cr, p) bp->b_error = error; } } else { - 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; + - bp->b_dirtyoff; + uiop->uio_offset = ((off_t)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); + if ((bp->b_flags & (B_ASYNC | B_NEEDCOMMIT | B_NOCACHE)) == B_ASYNC) + iomode = NFSV3WRITE_UNSTABLE; + else + iomode = NFSV3WRITE_FILESYNC; + bp->b_flags |= B_WRITEINPROG; + error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit); + if (!error && iomode == NFSV3WRITE_UNSTABLE) + bp->b_flags |= B_NEEDCOMMIT; else - error = nfs_writerpc(vp, uiop, cr, 0); - bp->b_flags &= ~(B_WRITEINPROG | B_APPENDWRITE); + bp->b_flags &= ~B_NEEDCOMMIT; + bp->b_flags &= ~B_WRITEINPROG; - /* - * For an interrupted write, the buffer is still valid and the - * write hasn't been pushed to the server yet, so we can't set - * B_ERROR and report the interruption by setting B_EINTR. For - * the B_ASYNC case, B_EINTR is not relevant, so the rpc attempt - * is essentially a noop. - */ - if (error == EINTR) { + /* + * For an interrupted write, the buffer is still valid + * and the write hasn't been pushed to the server yet, + * so we can't set B_ERROR and report the interruption + * by setting B_EINTR. For the B_ASYNC case, B_EINTR + * is not relevant, so the rpc attempt is essentially + * a noop. For the case of a V3 write rpc not being + * committed to stable storage, the block is still + * dirty and requires either a commit rpc or another + * write rpc with iomode == NFSV3WRITE_FILESYNC before + * the block is reused. This is indicated by setting + * the B_DELWRI and B_NEEDCOMMIT flags. + */ + if (error == EINTR + || (!error && (bp->b_flags & B_NEEDCOMMIT))) { bp->b_flags &= ~(B_INVAL|B_NOCACHE); bp->b_flags |= B_DELWRI; @@ -874,6 +942,8 @@ nfs_doio(bp, cr, p) } } bp->b_resid = uiop->uio_resid; + if (must_commit) + nfs_clearcommit(vp->v_mount); biodone(bp); return (error); } |