nfs_bio.c revision 25023
11541Srgrimes/*
21541Srgrimes * Copyright (c) 1989, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This code is derived from software contributed to Berkeley by
61541Srgrimes * Rick Macklem at The University of Guelph.
71541Srgrimes *
81541Srgrimes * Redistribution and use in source and binary forms, with or without
91541Srgrimes * modification, are permitted provided that the following conditions
101541Srgrimes * are met:
111541Srgrimes * 1. Redistributions of source code must retain the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer.
131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer in the
151541Srgrimes *    documentation and/or other materials provided with the distribution.
161541Srgrimes * 3. All advertising materials mentioning features or use of this software
171541Srgrimes *    must display the following acknowledgement:
181541Srgrimes *	This product includes software developed by the University of
191541Srgrimes *	California, Berkeley and its contributors.
201541Srgrimes * 4. Neither the name of the University nor the names of its contributors
211541Srgrimes *    may be used to endorse or promote products derived from this software
221541Srgrimes *    without specific prior written permission.
231541Srgrimes *
241541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341541Srgrimes * SUCH DAMAGE.
351541Srgrimes *
3622521Sdyson *	@(#)nfs_bio.c	8.9 (Berkeley) 3/30/95
3725023Sdfr * $Id: nfs_bio.c,v 1.35 1997/04/18 14:11:59 dfr Exp $
381541Srgrimes */
391541Srgrimes
4022521Sdyson
411541Srgrimes#include <sys/param.h>
421541Srgrimes#include <sys/systm.h>
431541Srgrimes#include <sys/resourcevar.h>
443305Sphk#include <sys/signalvar.h>
451541Srgrimes#include <sys/proc.h>
461541Srgrimes#include <sys/buf.h>
471541Srgrimes#include <sys/vnode.h>
481541Srgrimes#include <sys/mount.h>
491541Srgrimes#include <sys/kernel.h>
5018866Sdfr#include <sys/sysctl.h>
511541Srgrimes
521541Srgrimes#include <vm/vm.h>
5312662Sdg#include <vm/vm_param.h>
5412662Sdg#include <vm/vm_extern.h>
551541Srgrimes
561541Srgrimes#include <nfs/rpcv2.h>
579336Sdfr#include <nfs/nfsproto.h>
581541Srgrimes#include <nfs/nfs.h>
591541Srgrimes#include <nfs/nfsmount.h>
601541Srgrimes#include <nfs/nqnfs.h>
619336Sdfr#include <nfs/nfsnode.h>
621541Srgrimes
6312911Sphkstatic struct buf *nfs_getcacheblk __P((struct vnode *vp, daddr_t bn, int size,
6412588Sbde					struct proc *p));
6512588Sbde
661541Srgrimesextern int nfs_numasync;
679336Sdfrextern struct nfsstats nfsstats;
681541Srgrimes
691541Srgrimes/*
701541Srgrimes * Vnode op for read using bio
711541Srgrimes * Any similarity to readip() is purely coincidental
721541Srgrimes */
731549Srgrimesint
741541Srgrimesnfs_bioread(vp, uio, ioflag, cred)
751541Srgrimes	register struct vnode *vp;
761541Srgrimes	register struct uio *uio;
771541Srgrimes	int ioflag;
781541Srgrimes	struct ucred *cred;
791541Srgrimes{
801541Srgrimes	register struct nfsnode *np = VTONFS(vp);
819336Sdfr	register int biosize, diff, i;
821549Srgrimes	struct buf *bp = 0, *rabp;
831541Srgrimes	struct vattr vattr;
841541Srgrimes	struct proc *p;
859336Sdfr	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
865455Sdg	daddr_t lbn, rabn;
878692Sdg	int bufsize;
887871Sdg	int nra, error = 0, n = 0, on = 0, not_readin;
891541Srgrimes
901541Srgrimes#ifdef DIAGNOSTIC
911541Srgrimes	if (uio->uio_rw != UIO_READ)
921541Srgrimes		panic("nfs_read mode");
931541Srgrimes#endif
941541Srgrimes	if (uio->uio_resid == 0)
951541Srgrimes		return (0);
969336Sdfr	if (uio->uio_offset < 0)
971541Srgrimes		return (EINVAL);
981541Srgrimes	p = uio->uio_procp;
999336Sdfr	if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
1009336Sdfr		(void)nfs_fsinfo(nmp, vp, cred, p);
1019428Sdfr	biosize = vp->v_mount->mnt_stat.f_iosize;
1021541Srgrimes	/*
1031541Srgrimes	 * For nfs, cache consistency can only be maintained approximately.
1041541Srgrimes	 * Although RFC1094 does not specify the criteria, the following is
1051541Srgrimes	 * believed to be compatible with the reference port.
1061541Srgrimes	 * For nqnfs, full cache consistency is maintained within the loop.
1071541Srgrimes	 * For nfs:
1081541Srgrimes	 * If the file's modify time on the server has changed since the
1091541Srgrimes	 * last read rpc or you have written to the file,
1101541Srgrimes	 * you may have lost data cache consistency with the
1111541Srgrimes	 * server, so flush all of the file's data out of the cache.
1121541Srgrimes	 * Then force a getattr rpc to ensure that you have up to date
1131541Srgrimes	 * attributes.
1141541Srgrimes	 * NB: This implies that cache data can be read when up to
1151541Srgrimes	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
1161541Srgrimes	 * attributes this could be forced by setting n_attrstamp to 0 before
1171541Srgrimes	 * the VOP_GETATTR() call.
1181541Srgrimes	 */
11910219Sdfr	if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) {
1201541Srgrimes		if (np->n_flag & NMODIFIED) {
1219336Sdfr			if (vp->v_type != VREG) {
1229336Sdfr				if (vp->v_type != VDIR)
1239336Sdfr					panic("nfs: bioread, not dir");
1249336Sdfr				nfs_invaldir(vp);
1253305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1263305Sphk				if (error)
1271541Srgrimes					return (error);
1281541Srgrimes			}
1291541Srgrimes			np->n_attrstamp = 0;
1303305Sphk			error = VOP_GETATTR(vp, &vattr, cred, p);
1313305Sphk			if (error)
1321541Srgrimes				return (error);
13318397Snate			np->n_mtime = vattr.va_mtime.tv_sec;
1341541Srgrimes		} else {
1353305Sphk			error = VOP_GETATTR(vp, &vattr, cred, p);
1363305Sphk			if (error)
1371541Srgrimes				return (error);
13818397Snate			if (np->n_mtime != vattr.va_mtime.tv_sec) {
1399336Sdfr				if (vp->v_type == VDIR)
1409336Sdfr					nfs_invaldir(vp);
1413305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1423305Sphk				if (error)
1431541Srgrimes					return (error);
14418397Snate				np->n_mtime = vattr.va_mtime.tv_sec;
1451541Srgrimes			}
1461541Srgrimes		}
1471541Srgrimes	}
1481541Srgrimes	do {
1491541Srgrimes
1501541Srgrimes	    /*
1511541Srgrimes	     * Get a valid lease. If cached data is stale, flush it.
1521541Srgrimes	     */
1531541Srgrimes	    if (nmp->nm_flag & NFSMNT_NQNFS) {
1549336Sdfr		if (NQNFS_CKINVALID(vp, np, ND_READ)) {
1551541Srgrimes		    do {
1569336Sdfr			error = nqnfs_getlease(vp, ND_READ, cred, p);
1571541Srgrimes		    } while (error == NQNFS_EXPIRED);
1581541Srgrimes		    if (error)
1591541Srgrimes			return (error);
1601541Srgrimes		    if (np->n_lrev != np->n_brev ||
1611541Srgrimes			(np->n_flag & NQNFSNONCACHE) ||
1621541Srgrimes			((np->n_flag & NMODIFIED) && vp->v_type == VDIR)) {
1639336Sdfr			if (vp->v_type == VDIR)
1649336Sdfr			    nfs_invaldir(vp);
1653305Sphk			error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1663305Sphk			if (error)
1671541Srgrimes			    return (error);
1681541Srgrimes			np->n_brev = np->n_lrev;
1691541Srgrimes		    }
1701541Srgrimes		} else if (vp->v_type == VDIR && (np->n_flag & NMODIFIED)) {
1719336Sdfr		    nfs_invaldir(vp);
1723305Sphk		    error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1733305Sphk		    if (error)
1741541Srgrimes			return (error);
1751541Srgrimes		}
1761541Srgrimes	    }
1771541Srgrimes	    if (np->n_flag & NQNFSNONCACHE) {
1781541Srgrimes		switch (vp->v_type) {
1791541Srgrimes		case VREG:
1809336Sdfr			return (nfs_readrpc(vp, uio, cred));
1811541Srgrimes		case VLNK:
1829336Sdfr			return (nfs_readlinkrpc(vp, uio, cred));
1831541Srgrimes		case VDIR:
1841541Srgrimes			break;
1853305Sphk		default:
18622521Sdyson			printf(" NQNFSNONCACHE: type %x unexpected\n",
1873305Sphk				vp->v_type);
1881541Srgrimes		};
1891541Srgrimes	    }
1901541Srgrimes	    switch (vp->v_type) {
1911541Srgrimes	    case VREG:
1921541Srgrimes		nfsstats.biocache_reads++;
1931541Srgrimes		lbn = uio->uio_offset / biosize;
1949336Sdfr		on = uio->uio_offset & (biosize - 1);
1951541Srgrimes		not_readin = 1;
1961541Srgrimes
1971541Srgrimes		/*
1981541Srgrimes		 * Start the read ahead(s), as required.
1991541Srgrimes		 */
2009336Sdfr		if (nfs_numasync > 0 && nmp->nm_readahead > 0) {
2011541Srgrimes		    for (nra = 0; nra < nmp->nm_readahead &&
20213612Smpp			(off_t)(lbn + 1 + nra) * biosize < np->n_size; nra++) {
2035455Sdg			rabn = lbn + 1 + nra;
2041541Srgrimes			if (!incore(vp, rabn)) {
2051541Srgrimes			    rabp = nfs_getcacheblk(vp, rabn, biosize, p);
2061541Srgrimes			    if (!rabp)
2071541Srgrimes				return (EINTR);
2088692Sdg			    if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) {
2091541Srgrimes				rabp->b_flags |= (B_READ | B_ASYNC);
2105455Sdg				vfs_busy_pages(rabp, 0);
2111541Srgrimes				if (nfs_asyncio(rabp, cred)) {
2125455Sdg				    rabp->b_flags |= B_INVAL|B_ERROR;
2135455Sdg				    vfs_unbusy_pages(rabp);
2141541Srgrimes				    brelse(rabp);
2151541Srgrimes				}
21622521Sdyson			    } else
2175471Sdg				brelse(rabp);
2181541Srgrimes			}
2191541Srgrimes		    }
2201541Srgrimes		}
2211541Srgrimes
2221541Srgrimes		/*
2231541Srgrimes		 * If the block is in the cache and has the required data
2241541Srgrimes		 * in a valid region, just copy it out.
2251541Srgrimes		 * Otherwise, get the block and write back/read in,
2261541Srgrimes		 * as required.
2271541Srgrimes		 */
2281541Srgrimesagain:
2298692Sdg		bufsize = biosize;
23013612Smpp		if ((off_t)(lbn + 1) * biosize > np->n_size &&
23113612Smpp		    (off_t)(lbn + 1) * biosize - np->n_size < biosize) {
2328692Sdg			bufsize = np->n_size - lbn * biosize;
2338692Sdg			bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
2348692Sdg		}
2358692Sdg		bp = nfs_getcacheblk(vp, lbn, bufsize, p);
2367871Sdg		if (!bp)
2377871Sdg			return (EINTR);
2387871Sdg		if ((bp->b_flags & B_CACHE) == 0) {
2397871Sdg			bp->b_flags |= B_READ;
24016192Spst			bp->b_flags &= ~(B_DONE | B_ERROR | B_INVAL);
2417871Sdg			not_readin = 0;
2427871Sdg			vfs_busy_pages(bp, 0);
2437871Sdg			error = nfs_doio(bp, cred, p);
2447871Sdg			if (error) {
2457871Sdg			    brelse(bp);
2467871Sdg			    return (error);
2471541Srgrimes			}
2481541Srgrimes		}
2498692Sdg		if (bufsize > on) {
2508692Sdg			n = min((unsigned)(bufsize - on), uio->uio_resid);
2518692Sdg		} else {
2528692Sdg			n = 0;
2538692Sdg		}
2541541Srgrimes		diff = np->n_size - uio->uio_offset;
2551541Srgrimes		if (diff < n)
2561541Srgrimes			n = diff;
2571541Srgrimes		if (not_readin && n > 0) {
2581541Srgrimes			if (on < bp->b_validoff || (on + n) > bp->b_validend) {
2596148Sdg				bp->b_flags |= B_NOCACHE;
26022521Sdyson				bp->b_flags |= B_INVAFTERWRITE;
2611541Srgrimes				if (bp->b_dirtyend > 0) {
2621541Srgrimes				    if ((bp->b_flags & B_DELWRI) == 0)
2631541Srgrimes					panic("nfsbioread");
2641541Srgrimes				    if (VOP_BWRITE(bp) == EINTR)
2651541Srgrimes					return (EINTR);
2661541Srgrimes				} else
2671541Srgrimes				    brelse(bp);
2681541Srgrimes				goto again;
2691541Srgrimes			}
2701541Srgrimes		}
2711541Srgrimes		vp->v_lastr = lbn;
2721541Srgrimes		diff = (on >= bp->b_validend) ? 0 : (bp->b_validend - on);
2731541Srgrimes		if (diff < n)
2741541Srgrimes			n = diff;
2751541Srgrimes		break;
2761541Srgrimes	    case VLNK:
2771541Srgrimes		nfsstats.biocache_readlinks++;
2781541Srgrimes		bp = nfs_getcacheblk(vp, (daddr_t)0, NFS_MAXPATHLEN, p);
2791541Srgrimes		if (!bp)
2801541Srgrimes			return (EINTR);
2817871Sdg		if ((bp->b_flags & B_CACHE) == 0) {
2821541Srgrimes			bp->b_flags |= B_READ;
2835455Sdg			vfs_busy_pages(bp, 0);
2843305Sphk			error = nfs_doio(bp, cred, p);
2853305Sphk			if (error) {
2865455Sdg				bp->b_flags |= B_ERROR;
2871541Srgrimes				brelse(bp);
2881541Srgrimes				return (error);
2891541Srgrimes			}
2901541Srgrimes		}
2911541Srgrimes		n = min(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
2921541Srgrimes		on = 0;
2931541Srgrimes		break;
2941541Srgrimes	    case VDIR:
2951541Srgrimes		nfsstats.biocache_readdirs++;
29624577Sdfr		if (np->n_direofoffset
29724577Sdfr		    && uio->uio_offset >= np->n_direofoffset) {
29824577Sdfr		    return (0);
29924577Sdfr		}
3009336Sdfr		lbn = uio->uio_offset / NFS_DIRBLKSIZ;
3019336Sdfr		on = uio->uio_offset & (NFS_DIRBLKSIZ - 1);
3025455Sdg		bp = nfs_getcacheblk(vp, lbn, NFS_DIRBLKSIZ, p);
3031541Srgrimes		if (!bp)
3049336Sdfr		    return (EINTR);
3057871Sdg		if ((bp->b_flags & B_CACHE) == 0) {
3069336Sdfr		    bp->b_flags |= B_READ;
3079336Sdfr		    vfs_busy_pages(bp, 0);
3089336Sdfr		    error = nfs_doio(bp, cred, p);
3099336Sdfr		    if (error) {
31019070Sdfr		        vfs_unbusy_pages(bp);
3119336Sdfr			brelse(bp);
3129336Sdfr			while (error == NFSERR_BAD_COOKIE) {
3139336Sdfr			    nfs_invaldir(vp);
3149336Sdfr			    error = nfs_vinvalbuf(vp, 0, cred, p, 1);
3159336Sdfr			    /*
3169336Sdfr			     * Yuck! The directory has been modified on the
3179336Sdfr			     * server. The only way to get the block is by
3189336Sdfr			     * reading from the beginning to get all the
3199336Sdfr			     * offset cookies.
3209336Sdfr			     */
3219336Sdfr			    for (i = 0; i <= lbn && !error; i++) {
32224577Sdfr				if (np->n_direofoffset
32324577Sdfr				    && (i * NFS_DIRBLKSIZ) >= np->n_direofoffset)
32424577Sdfr				    return (0);
3259336Sdfr				bp = nfs_getcacheblk(vp, i, NFS_DIRBLKSIZ, p);
3269336Sdfr				if (!bp)
3279336Sdfr				    return (EINTR);
3289336Sdfr				if ((bp->b_flags & B_DONE) == 0) {
3299336Sdfr				    bp->b_flags |= B_READ;
3309336Sdfr				    vfs_busy_pages(bp, 0);
3319336Sdfr				    error = nfs_doio(bp, cred, p);
33219070Sdfr				    if (error) {
33319070Sdfr					vfs_unbusy_pages(bp);
3349336Sdfr					brelse(bp);
33519070Sdfr				    } else if (i < lbn)
33619070Sdfr					brelse(bp);
3379336Sdfr				}
3389336Sdfr			    }
3391541Srgrimes			}
3409336Sdfr			if (error)
3419336Sdfr			    return (error);
3429336Sdfr		    }
3431541Srgrimes		}
3441541Srgrimes
3451541Srgrimes		/*
3461541Srgrimes		 * If not eof and read aheads are enabled, start one.
3471541Srgrimes		 * (You need the current block first, so that you have the
3489336Sdfr		 *  directory offset cookie of the next block.)
3491541Srgrimes		 */
3501541Srgrimes		if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
3519336Sdfr		    (np->n_direofoffset == 0 ||
3529336Sdfr		    (lbn + 1) * NFS_DIRBLKSIZ < np->n_direofoffset) &&
3539336Sdfr		    !(np->n_flag & NQNFSNONCACHE) &&
3549336Sdfr		    !incore(vp, lbn + 1)) {
3559336Sdfr			rabp = nfs_getcacheblk(vp, lbn + 1, NFS_DIRBLKSIZ, p);
3561541Srgrimes			if (rabp) {
3578692Sdg			    if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) {
3581541Srgrimes				rabp->b_flags |= (B_READ | B_ASYNC);
3595455Sdg				vfs_busy_pages(rabp, 0);
3601541Srgrimes				if (nfs_asyncio(rabp, cred)) {
3616148Sdg				    rabp->b_flags |= B_INVAL|B_ERROR;
3625455Sdg				    vfs_unbusy_pages(rabp);
3631541Srgrimes				    brelse(rabp);
3641541Srgrimes				}
3655471Sdg			    } else {
3665471Sdg				brelse(rabp);
3671541Srgrimes			    }
3681541Srgrimes			}
3691541Srgrimes		}
3709336Sdfr		n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid - on);
3711541Srgrimes		break;
3723305Sphk	    default:
3739336Sdfr		printf(" nfs_bioread: type %x unexpected\n",vp->v_type);
3743305Sphk		break;
3751541Srgrimes	    };
3761541Srgrimes
3771541Srgrimes	    if (n > 0) {
3787871Sdg		error = uiomove(bp->b_data + on, (int)n, uio);
3791541Srgrimes	    }
3801541Srgrimes	    switch (vp->v_type) {
3811541Srgrimes	    case VREG:
3821541Srgrimes		break;
3831541Srgrimes	    case VLNK:
3841541Srgrimes		n = 0;
3851541Srgrimes		break;
3861541Srgrimes	    case VDIR:
3879336Sdfr		if (np->n_flag & NQNFSNONCACHE)
3889336Sdfr			bp->b_flags |= B_INVAL;
3891541Srgrimes		break;
3903305Sphk	    default:
3919336Sdfr		printf(" nfs_bioread: type %x unexpected\n",vp->v_type);
3923305Sphk	    }
3937871Sdg 	    brelse(bp);
3941541Srgrimes	} while (error == 0 && uio->uio_resid > 0 && n > 0);
3951541Srgrimes	return (error);
3961541Srgrimes}
3971541Srgrimes
3981541Srgrimes/*
3991541Srgrimes * Vnode op for write using bio
4001541Srgrimes */
4011549Srgrimesint
4021541Srgrimesnfs_write(ap)
4031541Srgrimes	struct vop_write_args /* {
4041541Srgrimes		struct vnode *a_vp;
4051541Srgrimes		struct uio *a_uio;
4061541Srgrimes		int  a_ioflag;
4071541Srgrimes		struct ucred *a_cred;
4081541Srgrimes	} */ *ap;
4091541Srgrimes{
4101541Srgrimes	register int biosize;
4111541Srgrimes	register struct uio *uio = ap->a_uio;
4121541Srgrimes	struct proc *p = uio->uio_procp;
4131541Srgrimes	register struct vnode *vp = ap->a_vp;
4141541Srgrimes	struct nfsnode *np = VTONFS(vp);
4151541Srgrimes	register struct ucred *cred = ap->a_cred;
4161541Srgrimes	int ioflag = ap->a_ioflag;
4171541Srgrimes	struct buf *bp;
4181541Srgrimes	struct vattr vattr;
4199336Sdfr	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
42011921Sphk	daddr_t lbn;
4218692Sdg	int bufsize;
4229336Sdfr	int n, on, error = 0, iomode, must_commit;
4231541Srgrimes
4241541Srgrimes#ifdef DIAGNOSTIC
4251541Srgrimes	if (uio->uio_rw != UIO_WRITE)
4261541Srgrimes		panic("nfs_write mode");
4271541Srgrimes	if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
4281541Srgrimes		panic("nfs_write proc");
4291541Srgrimes#endif
4301541Srgrimes	if (vp->v_type != VREG)
4311541Srgrimes		return (EIO);
4321541Srgrimes	if (np->n_flag & NWRITEERR) {
4331541Srgrimes		np->n_flag &= ~NWRITEERR;
4341541Srgrimes		return (np->n_error);
4351541Srgrimes	}
4369336Sdfr	if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
4379336Sdfr		(void)nfs_fsinfo(nmp, vp, cred, p);
4381541Srgrimes	if (ioflag & (IO_APPEND | IO_SYNC)) {
4391541Srgrimes		if (np->n_flag & NMODIFIED) {
4401541Srgrimes			np->n_attrstamp = 0;
4413305Sphk			error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
4423305Sphk			if (error)
4431541Srgrimes				return (error);
4441541Srgrimes		}
4451541Srgrimes		if (ioflag & IO_APPEND) {
4461541Srgrimes			np->n_attrstamp = 0;
4473305Sphk			error = VOP_GETATTR(vp, &vattr, cred, p);
4483305Sphk			if (error)
4491541Srgrimes				return (error);
4501541Srgrimes			uio->uio_offset = np->n_size;
4511541Srgrimes		}
4521541Srgrimes	}
4531541Srgrimes	if (uio->uio_offset < 0)
4541541Srgrimes		return (EINVAL);
4551541Srgrimes	if (uio->uio_resid == 0)
4561541Srgrimes		return (0);
4571541Srgrimes	/*
4581541Srgrimes	 * Maybe this should be above the vnode op call, but so long as
4591541Srgrimes	 * file servers have no limits, i don't think it matters
4601541Srgrimes	 */
4611541Srgrimes	if (p && uio->uio_offset + uio->uio_resid >
4621541Srgrimes	      p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
4631541Srgrimes		psignal(p, SIGXFSZ);
4641541Srgrimes		return (EFBIG);
4651541Srgrimes	}
4661541Srgrimes	/*
4671541Srgrimes	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
4681541Srgrimes	 * will be the same size within a filesystem. nfs_writerpc will
4691541Srgrimes	 * still use nm_wsize when sizing the rpc's.
4701541Srgrimes	 */
4719428Sdfr	biosize = vp->v_mount->mnt_stat.f_iosize;
4721541Srgrimes	do {
4731541Srgrimes		/*
4741541Srgrimes		 * Check for a valid write lease.
4751541Srgrimes		 */
4761541Srgrimes		if ((nmp->nm_flag & NFSMNT_NQNFS) &&
4779336Sdfr		    NQNFS_CKINVALID(vp, np, ND_WRITE)) {
4781541Srgrimes			do {
4799336Sdfr				error = nqnfs_getlease(vp, ND_WRITE, cred, p);
4801541Srgrimes			} while (error == NQNFS_EXPIRED);
4811541Srgrimes			if (error)
4821541Srgrimes				return (error);
4831541Srgrimes			if (np->n_lrev != np->n_brev ||
4841541Srgrimes			    (np->n_flag & NQNFSNONCACHE)) {
4853305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
4863305Sphk				if (error)
4871541Srgrimes					return (error);
4881541Srgrimes				np->n_brev = np->n_lrev;
4891541Srgrimes			}
4901541Srgrimes		}
4919336Sdfr		if ((np->n_flag & NQNFSNONCACHE) && uio->uio_iovcnt == 1) {
4929336Sdfr		    iomode = NFSV3WRITE_FILESYNC;
4939336Sdfr		    error = nfs_writerpc(vp, uio, cred, &iomode, &must_commit);
4949336Sdfr		    if (must_commit)
4959336Sdfr			nfs_clearcommit(vp->v_mount);
4969336Sdfr		    return (error);
4979336Sdfr		}
4981541Srgrimes		nfsstats.biocache_writes++;
4991541Srgrimes		lbn = uio->uio_offset / biosize;
5001541Srgrimes		on = uio->uio_offset & (biosize-1);
5011541Srgrimes		n = min((unsigned)(biosize - on), uio->uio_resid);
5021541Srgrimesagain:
5038692Sdg		if (uio->uio_offset + n > np->n_size) {
5048692Sdg			np->n_size = uio->uio_offset + n;
50525023Sdfr			np->n_flag |= NMODIFIED;
5068692Sdg			vnode_pager_setsize(vp, (u_long)np->n_size);
5078692Sdg		}
5088692Sdg		bufsize = biosize;
5098692Sdg		if ((lbn + 1) * biosize > np->n_size) {
5108692Sdg			bufsize = np->n_size - lbn * biosize;
5118692Sdg			bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
5128692Sdg		}
5138692Sdg		bp = nfs_getcacheblk(vp, lbn, bufsize, p);
5141541Srgrimes		if (!bp)
5151541Srgrimes			return (EINTR);
5161541Srgrimes		if (bp->b_wcred == NOCRED) {
5171541Srgrimes			crhold(cred);
5181541Srgrimes			bp->b_wcred = cred;
5191541Srgrimes		}
5201541Srgrimes		np->n_flag |= NMODIFIED;
5218692Sdg
5228692Sdg		if ((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend > np->n_size) {
5238692Sdg			bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
5241541Srgrimes		}
5251541Srgrimes
5261541Srgrimes		/*
5271541Srgrimes		 * If the new write will leave a contiguous dirty
5281541Srgrimes		 * area, just update the b_dirtyoff and b_dirtyend,
5291541Srgrimes		 * otherwise force a write rpc of the old dirty area.
5301541Srgrimes		 */
5311541Srgrimes		if (bp->b_dirtyend > 0 &&
5321541Srgrimes		    (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {
5331541Srgrimes			bp->b_proc = p;
5341541Srgrimes			if (VOP_BWRITE(bp) == EINTR)
5351541Srgrimes				return (EINTR);
5361541Srgrimes			goto again;
5371541Srgrimes		}
5381541Srgrimes
5391541Srgrimes		/*
5401541Srgrimes		 * Check for valid write lease and get one as required.
5411541Srgrimes		 * In case getblk() and/or bwrite() delayed us.
5421541Srgrimes		 */
5431541Srgrimes		if ((nmp->nm_flag & NFSMNT_NQNFS) &&
5449336Sdfr		    NQNFS_CKINVALID(vp, np, ND_WRITE)) {
5451541Srgrimes			do {
5469336Sdfr				error = nqnfs_getlease(vp, ND_WRITE, cred, p);
5471541Srgrimes			} while (error == NQNFS_EXPIRED);
5481541Srgrimes			if (error) {
5491541Srgrimes				brelse(bp);
5501541Srgrimes				return (error);
5511541Srgrimes			}
5521541Srgrimes			if (np->n_lrev != np->n_brev ||
5531541Srgrimes			    (np->n_flag & NQNFSNONCACHE)) {
5541541Srgrimes				brelse(bp);
5553305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
5563305Sphk				if (error)
5571541Srgrimes					return (error);
5581541Srgrimes				np->n_brev = np->n_lrev;
5591541Srgrimes				goto again;
5601541Srgrimes			}
5611541Srgrimes		}
5623305Sphk		error = uiomove((char *)bp->b_data + on, n, uio);
5633305Sphk		if (error) {
5641541Srgrimes			bp->b_flags |= B_ERROR;
5651541Srgrimes			brelse(bp);
5661541Srgrimes			return (error);
5671541Srgrimes		}
5681541Srgrimes		if (bp->b_dirtyend > 0) {
5691541Srgrimes			bp->b_dirtyoff = min(on, bp->b_dirtyoff);
5701541Srgrimes			bp->b_dirtyend = max((on + n), bp->b_dirtyend);
5711541Srgrimes		} else {
5721541Srgrimes			bp->b_dirtyoff = on;
5731541Srgrimes			bp->b_dirtyend = on + n;
5741541Srgrimes		}
5751541Srgrimes		if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||
5761541Srgrimes		    bp->b_validoff > bp->b_dirtyend) {
5771541Srgrimes			bp->b_validoff = bp->b_dirtyoff;
5781541Srgrimes			bp->b_validend = bp->b_dirtyend;
5791541Srgrimes		} else {
5801541Srgrimes			bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);
5811541Srgrimes			bp->b_validend = max(bp->b_validend, bp->b_dirtyend);
5821541Srgrimes		}
58317186Sdfr
5841541Srgrimes		/*
58517186Sdfr		 * Since this block is being modified, it must be written
58617186Sdfr		 * again and not just committed.
58717186Sdfr		 */
58817186Sdfr		bp->b_flags &= ~B_NEEDCOMMIT;
58917186Sdfr
59017186Sdfr		/*
5911541Srgrimes		 * If the lease is non-cachable or IO_SYNC do bwrite().
5921541Srgrimes		 */
5931541Srgrimes		if ((np->n_flag & NQNFSNONCACHE) || (ioflag & IO_SYNC)) {
5941541Srgrimes			bp->b_proc = p;
5953305Sphk			error = VOP_BWRITE(bp);
5963305Sphk			if (error)
5971541Srgrimes				return (error);
5989336Sdfr			if (np->n_flag & NQNFSNONCACHE) {
5999336Sdfr				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
6009336Sdfr				if (error)
6019336Sdfr					return (error);
6029336Sdfr			}
6031541Srgrimes		} else if ((n + on) == biosize &&
6041541Srgrimes			(nmp->nm_flag & NFSMNT_NQNFS) == 0) {
6051541Srgrimes			bp->b_proc = (struct proc *)0;
6069336Sdfr			bp->b_flags |= B_ASYNC;
6079336Sdfr			(void)nfs_writebp(bp, 0);
6081541Srgrimes		} else
6091541Srgrimes			bdwrite(bp);
6101541Srgrimes	} while (uio->uio_resid > 0 && n > 0);
6111541Srgrimes	return (0);
6121541Srgrimes}
6131541Srgrimes
6141541Srgrimes/*
6151541Srgrimes * Get an nfs cache block.
6161541Srgrimes * Allocate a new one if the block isn't currently in the cache
6171541Srgrimes * and return the block marked busy. If the calling process is
6181541Srgrimes * interrupted by a signal for an interruptible mount point, return
6191541Srgrimes * NULL.
6201541Srgrimes */
62112911Sphkstatic struct buf *
6221541Srgrimesnfs_getcacheblk(vp, bn, size, p)
6231541Srgrimes	struct vnode *vp;
6241541Srgrimes	daddr_t bn;
6251541Srgrimes	int size;
6261541Srgrimes	struct proc *p;
6271541Srgrimes{
6281541Srgrimes	register struct buf *bp;
6291541Srgrimes	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
6309428Sdfr	int biosize = vp->v_mount->mnt_stat.f_iosize;
6311541Srgrimes
6321541Srgrimes	if (nmp->nm_flag & NFSMNT_INT) {
6331541Srgrimes		bp = getblk(vp, bn, size, PCATCH, 0);
6341541Srgrimes		while (bp == (struct buf *)0) {
6351541Srgrimes			if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
6361541Srgrimes				return ((struct buf *)0);
6371541Srgrimes			bp = getblk(vp, bn, size, 0, 2 * hz);
6381541Srgrimes		}
6391541Srgrimes	} else
6401541Srgrimes		bp = getblk(vp, bn, size, 0, 0);
6415455Sdg
6425455Sdg	if( vp->v_type == VREG)
6439336Sdfr		bp->b_blkno = (bn * biosize) / DEV_BSIZE;
6445455Sdg
6451541Srgrimes	return (bp);
6461541Srgrimes}
6471541Srgrimes
6481541Srgrimes/*
6491541Srgrimes * Flush and invalidate all dirty buffers. If another process is already
6501541Srgrimes * doing the flush, just wait for completion.
6511541Srgrimes */
6521549Srgrimesint
6531541Srgrimesnfs_vinvalbuf(vp, flags, cred, p, intrflg)
6541541Srgrimes	struct vnode *vp;
6551541Srgrimes	int flags;
6561541Srgrimes	struct ucred *cred;
6571541Srgrimes	struct proc *p;
6581541Srgrimes	int intrflg;
6591541Srgrimes{
6601541Srgrimes	register struct nfsnode *np = VTONFS(vp);
6611541Srgrimes	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
6621541Srgrimes	int error = 0, slpflag, slptimeo;
6631541Srgrimes
6641541Srgrimes	if ((nmp->nm_flag & NFSMNT_INT) == 0)
6651541Srgrimes		intrflg = 0;
6661541Srgrimes	if (intrflg) {
6671541Srgrimes		slpflag = PCATCH;
6681541Srgrimes		slptimeo = 2 * hz;
6691541Srgrimes	} else {
6701541Srgrimes		slpflag = 0;
6711541Srgrimes		slptimeo = 0;
6721541Srgrimes	}
6731541Srgrimes	/*
6741541Srgrimes	 * First wait for any other process doing a flush to complete.
6751541Srgrimes	 */
6761541Srgrimes	while (np->n_flag & NFLUSHINPROG) {
6771541Srgrimes		np->n_flag |= NFLUSHWANT;
6781541Srgrimes		error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "nfsvinval",
6791541Srgrimes			slptimeo);
6801541Srgrimes		if (error && intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p))
6811541Srgrimes			return (EINTR);
6821541Srgrimes	}
6831541Srgrimes
6841541Srgrimes	/*
6851541Srgrimes	 * Now, flush as required.
6861541Srgrimes	 */
6871541Srgrimes	np->n_flag |= NFLUSHINPROG;
6881541Srgrimes	error = vinvalbuf(vp, flags, cred, p, slpflag, 0);
6891541Srgrimes	while (error) {
6901541Srgrimes		if (intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p)) {
6911541Srgrimes			np->n_flag &= ~NFLUSHINPROG;
6921541Srgrimes			if (np->n_flag & NFLUSHWANT) {
6931541Srgrimes				np->n_flag &= ~NFLUSHWANT;
6941541Srgrimes				wakeup((caddr_t)&np->n_flag);
6951541Srgrimes			}
6961541Srgrimes			return (EINTR);
6971541Srgrimes		}
6981541Srgrimes		error = vinvalbuf(vp, flags, cred, p, 0, slptimeo);
6991541Srgrimes	}
7001541Srgrimes	np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
7011541Srgrimes	if (np->n_flag & NFLUSHWANT) {
7021541Srgrimes		np->n_flag &= ~NFLUSHWANT;
7031541Srgrimes		wakeup((caddr_t)&np->n_flag);
7041541Srgrimes	}
7051541Srgrimes	return (0);
7061541Srgrimes}
7071541Srgrimes
7081541Srgrimes/*
7091541Srgrimes * Initiate asynchronous I/O. Return an error if no nfsiods are available.
7101541Srgrimes * This is mainly to avoid queueing async I/O requests when the nfsiods
7111541Srgrimes * are all hung on a dead server.
7121541Srgrimes */
7131549Srgrimesint
7141541Srgrimesnfs_asyncio(bp, cred)
7151541Srgrimes	register struct buf *bp;
7161541Srgrimes	struct ucred *cred;
7171541Srgrimes{
71819449Sdfr	struct nfsmount *nmp;
71919449Sdfr	int i;
72019449Sdfr	int gotiod;
72119449Sdfr	int slpflag = 0;
72219449Sdfr	int slptimeo = 0;
72319449Sdfr	int error;
7241541Srgrimes
7251541Srgrimes	if (nfs_numasync == 0)
7261541Srgrimes		return (EIO);
72719449Sdfr
72819449Sdfr	nmp = VFSTONFS(bp->b_vp->v_mount);
72919449Sdfragain:
73019449Sdfr	if (nmp->nm_flag & NFSMNT_INT)
73119449Sdfr		slpflag = PCATCH;
73219449Sdfr	gotiod = FALSE;
73319449Sdfr
73419449Sdfr	/*
73519449Sdfr	 * Find a free iod to process this request.
73619449Sdfr	 */
7371541Srgrimes	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
73819449Sdfr		if (nfs_iodwant[i]) {
73919449Sdfr			/*
74019449Sdfr			 * Found one, so wake it up and tell it which
74119449Sdfr			 * mount to process.
74219449Sdfr			 */
74319449Sdfr			NFS_DPF(ASYNCIO,
74419449Sdfr				("nfs_asyncio: waking iod %d for mount %p\n",
74519449Sdfr				 i, nmp));
74619449Sdfr			nfs_iodwant[i] = (struct proc *)0;
74719449Sdfr			nfs_iodmount[i] = nmp;
74819449Sdfr			nmp->nm_bufqiods++;
74919449Sdfr			wakeup((caddr_t)&nfs_iodwant[i]);
75019449Sdfr			gotiod = TRUE;
75125023Sdfr			break;
75219449Sdfr		}
75319449Sdfr
75419449Sdfr	/*
75519449Sdfr	 * If none are free, we may already have an iod working on this mount
75619449Sdfr	 * point.  If so, it will process our request.
75719449Sdfr	 */
75819449Sdfr	if (!gotiod) {
75919449Sdfr		if (nmp->nm_bufqiods > 0) {
76019449Sdfr			NFS_DPF(ASYNCIO,
76119449Sdfr				("nfs_asyncio: %d iods are already processing mount %p\n",
76219449Sdfr				 nmp->nm_bufqiods, nmp));
76319449Sdfr			gotiod = TRUE;
76419449Sdfr		}
76519449Sdfr	}
76619449Sdfr
76719449Sdfr	/*
76819449Sdfr	 * If we have an iod which can process the request, then queue
76919449Sdfr	 * the buffer.
77019449Sdfr	 */
77119449Sdfr	if (gotiod) {
77219449Sdfr		/*
77319449Sdfr		 * Ensure that the queue never grows too large.
77419449Sdfr		 */
77519449Sdfr		while (nmp->nm_bufqlen >= 2*nfs_numasync) {
77619449Sdfr			NFS_DPF(ASYNCIO,
77719449Sdfr				("nfs_asyncio: waiting for mount %p queue to drain\n", nmp));
77819449Sdfr			nmp->nm_bufqwant = TRUE;
77919449Sdfr			error = tsleep(&nmp->nm_bufq, slpflag | PRIBIO,
78019449Sdfr				       "nfsaio", slptimeo);
78119449Sdfr			if (error) {
78219449Sdfr				if (nfs_sigintr(nmp, NULL, bp->b_proc))
78319449Sdfr					return (EINTR);
78419449Sdfr				if (slpflag == PCATCH) {
78519449Sdfr					slpflag = 0;
78619449Sdfr					slptimeo = 2 * hz;
78719449Sdfr				}
78819449Sdfr			}
78919449Sdfr			/*
79019449Sdfr			 * We might have lost our iod while sleeping,
79119449Sdfr			 * so check and loop if nescessary.
79219449Sdfr			 */
79319449Sdfr			if (nmp->nm_bufqiods == 0) {
79419449Sdfr				NFS_DPF(ASYNCIO,
79519449Sdfr					("nfs_asyncio: no iods after mount %p queue was drained, looping\n", nmp));
79619449Sdfr				goto again;
79719449Sdfr			}
79819449Sdfr		}
79919449Sdfr
8001541Srgrimes		if (bp->b_flags & B_READ) {
8011541Srgrimes			if (bp->b_rcred == NOCRED && cred != NOCRED) {
8021541Srgrimes				crhold(cred);
8031541Srgrimes				bp->b_rcred = cred;
8041541Srgrimes			}
8051541Srgrimes		} else {
8069336Sdfr			bp->b_flags |= B_WRITEINPROG;
8071541Srgrimes			if (bp->b_wcred == NOCRED && cred != NOCRED) {
8081541Srgrimes				crhold(cred);
8091541Srgrimes				bp->b_wcred = cred;
8101541Srgrimes			}
8111541Srgrimes		}
8128876Srgrimes
81319449Sdfr		TAILQ_INSERT_TAIL(&nmp->nm_bufq, bp, b_freelist);
81419449Sdfr		nmp->nm_bufqlen++;
8151541Srgrimes		return (0);
81619449Sdfr	}
8179336Sdfr
8189336Sdfr	/*
81919449Sdfr	 * All the iods are busy on other mounts, so return EIO to
82019449Sdfr	 * force the caller to process the i/o synchronously.
8219336Sdfr	 */
82219449Sdfr	NFS_DPF(ASYNCIO, ("nfs_asyncio: no iods available, i/o is synchronous\n"));
82319449Sdfr	return (EIO);
8241541Srgrimes}
8251541Srgrimes
8261541Srgrimes/*
8271541Srgrimes * Do an I/O operation to/from a cache block. This may be called
8281541Srgrimes * synchronously or from an nfsiod.
8291541Srgrimes */
8301541Srgrimesint
8311541Srgrimesnfs_doio(bp, cr, p)
8321541Srgrimes	register struct buf *bp;
8333305Sphk	struct ucred *cr;
8341541Srgrimes	struct proc *p;
8351541Srgrimes{
8361541Srgrimes	register struct uio *uiop;
8371541Srgrimes	register struct vnode *vp;
8381541Srgrimes	struct nfsnode *np;
8391541Srgrimes	struct nfsmount *nmp;
8409336Sdfr	int error = 0, diff, len, iomode, must_commit = 0;
8411541Srgrimes	struct uio uio;
8421541Srgrimes	struct iovec io;
8431541Srgrimes
8441541Srgrimes	vp = bp->b_vp;
8451541Srgrimes	np = VTONFS(vp);
8461541Srgrimes	nmp = VFSTONFS(vp->v_mount);
8471541Srgrimes	uiop = &uio;
8481541Srgrimes	uiop->uio_iov = &io;
8491541Srgrimes	uiop->uio_iovcnt = 1;
8501541Srgrimes	uiop->uio_segflg = UIO_SYSSPACE;
8511541Srgrimes	uiop->uio_procp = p;
8521541Srgrimes
8531541Srgrimes	/*
8541541Srgrimes	 * Historically, paging was done with physio, but no more.
8551541Srgrimes	 */
8563664Sphk	if (bp->b_flags & B_PHYS) {
8573664Sphk	    /*
8583664Sphk	     * ...though reading /dev/drum still gets us here.
8593664Sphk	     */
8601541Srgrimes	    io.iov_len = uiop->uio_resid = bp->b_bcount;
8613664Sphk	    /* mapping was done by vmapbuf() */
8621541Srgrimes	    io.iov_base = bp->b_data;
8639336Sdfr	    uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
8643664Sphk	    if (bp->b_flags & B_READ) {
8653664Sphk		uiop->uio_rw = UIO_READ;
8663664Sphk		nfsstats.read_physios++;
8673664Sphk		error = nfs_readrpc(vp, uiop, cr);
8683664Sphk	    } else {
8699336Sdfr		int com;
8709336Sdfr
8719336Sdfr		iomode = NFSV3WRITE_DATASYNC;
8723664Sphk		uiop->uio_rw = UIO_WRITE;
8733664Sphk		nfsstats.write_physios++;
8749336Sdfr		error = nfs_writerpc(vp, uiop, cr, &iomode, &com);
8753664Sphk	    }
8763664Sphk	    if (error) {
8773664Sphk		bp->b_flags |= B_ERROR;
8783664Sphk		bp->b_error = error;
8793664Sphk	    }
8803664Sphk	} else if (bp->b_flags & B_READ) {
8813664Sphk	    io.iov_len = uiop->uio_resid = bp->b_bcount;
8823664Sphk	    io.iov_base = bp->b_data;
8831541Srgrimes	    uiop->uio_rw = UIO_READ;
8841541Srgrimes	    switch (vp->v_type) {
8851541Srgrimes	    case VREG:
8869336Sdfr		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
8871541Srgrimes		nfsstats.read_bios++;
8881541Srgrimes		error = nfs_readrpc(vp, uiop, cr);
8891541Srgrimes		if (!error) {
8901541Srgrimes		    bp->b_validoff = 0;
8911541Srgrimes		    if (uiop->uio_resid) {
8921541Srgrimes			/*
8931541Srgrimes			 * If len > 0, there is a hole in the file and
8941541Srgrimes			 * no writes after the hole have been pushed to
8951541Srgrimes			 * the server yet.
8961541Srgrimes			 * Just zero fill the rest of the valid area.
8971541Srgrimes			 */
8981541Srgrimes			diff = bp->b_bcount - uiop->uio_resid;
8999336Sdfr			len = np->n_size - (((u_quad_t)bp->b_blkno) * DEV_BSIZE
9001541Srgrimes				+ diff);
9011541Srgrimes			if (len > 0) {
9021541Srgrimes			    len = min(len, uiop->uio_resid);
9031541Srgrimes			    bzero((char *)bp->b_data + diff, len);
9041541Srgrimes			    bp->b_validend = diff + len;
9051541Srgrimes			} else
9061541Srgrimes			    bp->b_validend = diff;
9071541Srgrimes		    } else
9081541Srgrimes			bp->b_validend = bp->b_bcount;
9091541Srgrimes		}
9101541Srgrimes		if (p && (vp->v_flag & VTEXT) &&
9111541Srgrimes			(((nmp->nm_flag & NFSMNT_NQNFS) &&
9129336Sdfr			  NQNFS_CKINVALID(vp, np, ND_READ) &&
9131541Srgrimes			  np->n_lrev != np->n_brev) ||
9141541Srgrimes			 (!(nmp->nm_flag & NFSMNT_NQNFS) &&
91518397Snate			  np->n_mtime != np->n_vattr.va_mtime.tv_sec))) {
9161541Srgrimes			uprintf("Process killed due to text file modification\n");
9171541Srgrimes			psignal(p, SIGKILL);
9189336Sdfr#ifdef __NetBSD__
9199336Sdfr			p->p_holdcnt++;
9209336Sdfr#else
9211541Srgrimes			p->p_flag |= P_NOSWAP;
9229336Sdfr#endif
9231541Srgrimes		}
9241541Srgrimes		break;
9251541Srgrimes	    case VLNK:
9269336Sdfr		uiop->uio_offset = (off_t)0;
9271541Srgrimes		nfsstats.readlink_bios++;
9281541Srgrimes		error = nfs_readlinkrpc(vp, uiop, cr);
9291541Srgrimes		break;
9301541Srgrimes	    case VDIR:
9311541Srgrimes		nfsstats.readdir_bios++;
9329336Sdfr		uiop->uio_offset = ((u_quad_t)bp->b_lblkno) * NFS_DIRBLKSIZ;
9339336Sdfr		if (nmp->nm_flag & NFSMNT_RDIRPLUS) {
9349336Sdfr			error = nfs_readdirplusrpc(vp, uiop, cr);
9359336Sdfr			if (error == NFSERR_NOTSUPP)
9369336Sdfr				nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
9379336Sdfr		}
9389336Sdfr		if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0)
9399336Sdfr			error = nfs_readdirrpc(vp, uiop, cr);
9401541Srgrimes		break;
9413305Sphk	    default:
9423305Sphk		printf("nfs_doio:  type %x unexpected\n",vp->v_type);
9433305Sphk		break;
9441541Srgrimes	    };
9451541Srgrimes	    if (error) {
9461541Srgrimes		bp->b_flags |= B_ERROR;
9471541Srgrimes		bp->b_error = error;
9481541Srgrimes	    }
9491541Srgrimes	} else {
9508692Sdg	    if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size)
9518692Sdg		bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
9528692Sdg
9538692Sdg	    if (bp->b_dirtyend > bp->b_dirtyoff) {
9548692Sdg		io.iov_len = uiop->uio_resid = bp->b_dirtyend
9559336Sdfr		    - bp->b_dirtyoff;
9569336Sdfr		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE
9579336Sdfr		    + bp->b_dirtyoff;
9588692Sdg		io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
9598692Sdg		uiop->uio_rw = UIO_WRITE;
9608692Sdg		nfsstats.write_bios++;
9619336Sdfr		if ((bp->b_flags & (B_ASYNC | B_NEEDCOMMIT | B_NOCACHE)) == B_ASYNC)
9629336Sdfr		    iomode = NFSV3WRITE_UNSTABLE;
9638692Sdg		else
9649336Sdfr		    iomode = NFSV3WRITE_FILESYNC;
9659336Sdfr		bp->b_flags |= B_WRITEINPROG;
9669336Sdfr		error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit);
96725003Sdfr		if (!error && iomode == NFSV3WRITE_UNSTABLE) {
96825003Sdfr		    bp->b_flags |= B_NEEDCOMMIT;
96925003Sdfr		    if (bp->b_dirtyoff == 0
97025003Sdfr			&& bp->b_dirtyend == bp->b_bufsize)
97125003Sdfr			bp->b_flags |= B_CLUSTEROK;
97225003Sdfr		} else
9739336Sdfr		    bp->b_flags &= ~B_NEEDCOMMIT;
9749336Sdfr		bp->b_flags &= ~B_WRITEINPROG;
9758692Sdg
9769336Sdfr		/*
9779336Sdfr		 * For an interrupted write, the buffer is still valid
9789336Sdfr		 * and the write hasn't been pushed to the server yet,
9799336Sdfr		 * so we can't set B_ERROR and report the interruption
9809336Sdfr		 * by setting B_EINTR. For the B_ASYNC case, B_EINTR
9819336Sdfr		 * is not relevant, so the rpc attempt is essentially
9829336Sdfr		 * a noop.  For the case of a V3 write rpc not being
9839336Sdfr		 * committed to stable storage, the block is still
9849336Sdfr		 * dirty and requires either a commit rpc or another
9859336Sdfr		 * write rpc with iomode == NFSV3WRITE_FILESYNC before
9869336Sdfr		 * the block is reused. This is indicated by setting
9879336Sdfr		 * the B_DELWRI and B_NEEDCOMMIT flags.
9889336Sdfr		 */
9899336Sdfr    		if (error == EINTR
9909336Sdfr		    || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
9918692Sdg			bp->b_flags &= ~(B_INVAL|B_NOCACHE);
9928692Sdg			bp->b_flags |= B_DELWRI;
9931541Srgrimes
9941541Srgrimes		/*
9951541Srgrimes		 * Since for the B_ASYNC case, nfs_bwrite() has reassigned the
9961541Srgrimes		 * buffer to the clean list, we have to reassign it back to the
9971541Srgrimes		 * dirty one. Ugh.
9981541Srgrimes		 */
9998692Sdg			if (bp->b_flags & B_ASYNC)
10008692Sdg				reassignbuf(bp, vp);
10018692Sdg			else
10028692Sdg				bp->b_flags |= B_EINTR;
10038692Sdg	    	} else {
10048692Sdg			if (error) {
10058692Sdg				bp->b_flags |= B_ERROR;
10068692Sdg				bp->b_error = np->n_error = error;
10078692Sdg				np->n_flag |= NWRITEERR;
10088692Sdg			}
10098692Sdg			bp->b_dirtyoff = bp->b_dirtyend = 0;
10108692Sdg		}
10111541Srgrimes	    } else {
10128692Sdg		bp->b_resid = 0;
10138692Sdg		biodone(bp);
10148692Sdg		return (0);
10151541Srgrimes	    }
10161541Srgrimes	}
10171541Srgrimes	bp->b_resid = uiop->uio_resid;
10189336Sdfr	if (must_commit)
10199336Sdfr		nfs_clearcommit(vp->v_mount);
10201541Srgrimes	biodone(bp);
10211541Srgrimes	return (error);
10221541Srgrimes}
1023