nfs_bio.c revision 24577
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
3724577Sdfr * $Id: nfs_bio.c,v 1.33 1997/03/09 10:21:26 bde 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;
5058692Sdg			vnode_pager_setsize(vp, (u_long)np->n_size);
5068692Sdg		}
5078692Sdg		bufsize = biosize;
5088692Sdg		if ((lbn + 1) * biosize > np->n_size) {
5098692Sdg			bufsize = np->n_size - lbn * biosize;
5108692Sdg			bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
5118692Sdg		}
5128692Sdg		bp = nfs_getcacheblk(vp, lbn, bufsize, p);
5131541Srgrimes		if (!bp)
5141541Srgrimes			return (EINTR);
5151541Srgrimes		if (bp->b_wcred == NOCRED) {
5161541Srgrimes			crhold(cred);
5171541Srgrimes			bp->b_wcred = cred;
5181541Srgrimes		}
5191541Srgrimes		np->n_flag |= NMODIFIED;
5208692Sdg
5218692Sdg		if ((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend > np->n_size) {
5228692Sdg			bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
5231541Srgrimes		}
5241541Srgrimes
5251541Srgrimes		/*
5261541Srgrimes		 * If the new write will leave a contiguous dirty
5271541Srgrimes		 * area, just update the b_dirtyoff and b_dirtyend,
5281541Srgrimes		 * otherwise force a write rpc of the old dirty area.
5291541Srgrimes		 */
5301541Srgrimes		if (bp->b_dirtyend > 0 &&
5311541Srgrimes		    (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {
5321541Srgrimes			bp->b_proc = p;
5331541Srgrimes			if (VOP_BWRITE(bp) == EINTR)
5341541Srgrimes				return (EINTR);
5351541Srgrimes			goto again;
5361541Srgrimes		}
5371541Srgrimes
5381541Srgrimes		/*
5391541Srgrimes		 * Check for valid write lease and get one as required.
5401541Srgrimes		 * In case getblk() and/or bwrite() delayed us.
5411541Srgrimes		 */
5421541Srgrimes		if ((nmp->nm_flag & NFSMNT_NQNFS) &&
5439336Sdfr		    NQNFS_CKINVALID(vp, np, ND_WRITE)) {
5441541Srgrimes			do {
5459336Sdfr				error = nqnfs_getlease(vp, ND_WRITE, cred, p);
5461541Srgrimes			} while (error == NQNFS_EXPIRED);
5471541Srgrimes			if (error) {
5481541Srgrimes				brelse(bp);
5491541Srgrimes				return (error);
5501541Srgrimes			}
5511541Srgrimes			if (np->n_lrev != np->n_brev ||
5521541Srgrimes			    (np->n_flag & NQNFSNONCACHE)) {
5531541Srgrimes				brelse(bp);
5543305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
5553305Sphk				if (error)
5561541Srgrimes					return (error);
5571541Srgrimes				np->n_brev = np->n_lrev;
5581541Srgrimes				goto again;
5591541Srgrimes			}
5601541Srgrimes		}
5613305Sphk		error = uiomove((char *)bp->b_data + on, n, uio);
5623305Sphk		if (error) {
5631541Srgrimes			bp->b_flags |= B_ERROR;
5641541Srgrimes			brelse(bp);
5651541Srgrimes			return (error);
5661541Srgrimes		}
5671541Srgrimes		if (bp->b_dirtyend > 0) {
5681541Srgrimes			bp->b_dirtyoff = min(on, bp->b_dirtyoff);
5691541Srgrimes			bp->b_dirtyend = max((on + n), bp->b_dirtyend);
5701541Srgrimes		} else {
5711541Srgrimes			bp->b_dirtyoff = on;
5721541Srgrimes			bp->b_dirtyend = on + n;
5731541Srgrimes		}
5741541Srgrimes		if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||
5751541Srgrimes		    bp->b_validoff > bp->b_dirtyend) {
5761541Srgrimes			bp->b_validoff = bp->b_dirtyoff;
5771541Srgrimes			bp->b_validend = bp->b_dirtyend;
5781541Srgrimes		} else {
5791541Srgrimes			bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);
5801541Srgrimes			bp->b_validend = max(bp->b_validend, bp->b_dirtyend);
5811541Srgrimes		}
58217186Sdfr
5831541Srgrimes		/*
58417186Sdfr		 * Since this block is being modified, it must be written
58517186Sdfr		 * again and not just committed.
58617186Sdfr		 */
58717186Sdfr		bp->b_flags &= ~B_NEEDCOMMIT;
58817186Sdfr
58917186Sdfr		/*
5901541Srgrimes		 * If the lease is non-cachable or IO_SYNC do bwrite().
5911541Srgrimes		 */
5921541Srgrimes		if ((np->n_flag & NQNFSNONCACHE) || (ioflag & IO_SYNC)) {
5931541Srgrimes			bp->b_proc = p;
5943305Sphk			error = VOP_BWRITE(bp);
5953305Sphk			if (error)
5961541Srgrimes				return (error);
5979336Sdfr			if (np->n_flag & NQNFSNONCACHE) {
5989336Sdfr				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
5999336Sdfr				if (error)
6009336Sdfr					return (error);
6019336Sdfr			}
6021541Srgrimes		} else if ((n + on) == biosize &&
6031541Srgrimes			(nmp->nm_flag & NFSMNT_NQNFS) == 0) {
6041541Srgrimes			bp->b_proc = (struct proc *)0;
6059336Sdfr			bp->b_flags |= B_ASYNC;
6069336Sdfr			(void)nfs_writebp(bp, 0);
6071541Srgrimes		} else
6081541Srgrimes			bdwrite(bp);
6091541Srgrimes	} while (uio->uio_resid > 0 && n > 0);
6101541Srgrimes	return (0);
6111541Srgrimes}
6121541Srgrimes
6131541Srgrimes/*
6141541Srgrimes * Get an nfs cache block.
6151541Srgrimes * Allocate a new one if the block isn't currently in the cache
6161541Srgrimes * and return the block marked busy. If the calling process is
6171541Srgrimes * interrupted by a signal for an interruptible mount point, return
6181541Srgrimes * NULL.
6191541Srgrimes */
62012911Sphkstatic struct buf *
6211541Srgrimesnfs_getcacheblk(vp, bn, size, p)
6221541Srgrimes	struct vnode *vp;
6231541Srgrimes	daddr_t bn;
6241541Srgrimes	int size;
6251541Srgrimes	struct proc *p;
6261541Srgrimes{
6271541Srgrimes	register struct buf *bp;
6281541Srgrimes	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
6299428Sdfr	int biosize = vp->v_mount->mnt_stat.f_iosize;
6301541Srgrimes
6311541Srgrimes	if (nmp->nm_flag & NFSMNT_INT) {
6321541Srgrimes		bp = getblk(vp, bn, size, PCATCH, 0);
6331541Srgrimes		while (bp == (struct buf *)0) {
6341541Srgrimes			if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
6351541Srgrimes				return ((struct buf *)0);
6361541Srgrimes			bp = getblk(vp, bn, size, 0, 2 * hz);
6371541Srgrimes		}
6381541Srgrimes	} else
6391541Srgrimes		bp = getblk(vp, bn, size, 0, 0);
6405455Sdg
6415455Sdg	if( vp->v_type == VREG)
6429336Sdfr		bp->b_blkno = (bn * biosize) / DEV_BSIZE;
6435455Sdg
6441541Srgrimes	return (bp);
6451541Srgrimes}
6461541Srgrimes
6471541Srgrimes/*
6481541Srgrimes * Flush and invalidate all dirty buffers. If another process is already
6491541Srgrimes * doing the flush, just wait for completion.
6501541Srgrimes */
6511549Srgrimesint
6521541Srgrimesnfs_vinvalbuf(vp, flags, cred, p, intrflg)
6531541Srgrimes	struct vnode *vp;
6541541Srgrimes	int flags;
6551541Srgrimes	struct ucred *cred;
6561541Srgrimes	struct proc *p;
6571541Srgrimes	int intrflg;
6581541Srgrimes{
6591541Srgrimes	register struct nfsnode *np = VTONFS(vp);
6601541Srgrimes	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
6611541Srgrimes	int error = 0, slpflag, slptimeo;
6621541Srgrimes
6631541Srgrimes	if ((nmp->nm_flag & NFSMNT_INT) == 0)
6641541Srgrimes		intrflg = 0;
6651541Srgrimes	if (intrflg) {
6661541Srgrimes		slpflag = PCATCH;
6671541Srgrimes		slptimeo = 2 * hz;
6681541Srgrimes	} else {
6691541Srgrimes		slpflag = 0;
6701541Srgrimes		slptimeo = 0;
6711541Srgrimes	}
6721541Srgrimes	/*
6731541Srgrimes	 * First wait for any other process doing a flush to complete.
6741541Srgrimes	 */
6751541Srgrimes	while (np->n_flag & NFLUSHINPROG) {
6761541Srgrimes		np->n_flag |= NFLUSHWANT;
6771541Srgrimes		error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "nfsvinval",
6781541Srgrimes			slptimeo);
6791541Srgrimes		if (error && intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p))
6801541Srgrimes			return (EINTR);
6811541Srgrimes	}
6821541Srgrimes
6831541Srgrimes	/*
6841541Srgrimes	 * Now, flush as required.
6851541Srgrimes	 */
6861541Srgrimes	np->n_flag |= NFLUSHINPROG;
6871541Srgrimes	error = vinvalbuf(vp, flags, cred, p, slpflag, 0);
6881541Srgrimes	while (error) {
6891541Srgrimes		if (intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p)) {
6901541Srgrimes			np->n_flag &= ~NFLUSHINPROG;
6911541Srgrimes			if (np->n_flag & NFLUSHWANT) {
6921541Srgrimes				np->n_flag &= ~NFLUSHWANT;
6931541Srgrimes				wakeup((caddr_t)&np->n_flag);
6941541Srgrimes			}
6951541Srgrimes			return (EINTR);
6961541Srgrimes		}
6971541Srgrimes		error = vinvalbuf(vp, flags, cred, p, 0, slptimeo);
6981541Srgrimes	}
6991541Srgrimes	np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
7001541Srgrimes	if (np->n_flag & NFLUSHWANT) {
7011541Srgrimes		np->n_flag &= ~NFLUSHWANT;
7021541Srgrimes		wakeup((caddr_t)&np->n_flag);
7031541Srgrimes	}
7041541Srgrimes	return (0);
7051541Srgrimes}
7061541Srgrimes
7071541Srgrimes/*
7081541Srgrimes * Initiate asynchronous I/O. Return an error if no nfsiods are available.
7091541Srgrimes * This is mainly to avoid queueing async I/O requests when the nfsiods
7101541Srgrimes * are all hung on a dead server.
7111541Srgrimes */
7121549Srgrimesint
7131541Srgrimesnfs_asyncio(bp, cred)
7141541Srgrimes	register struct buf *bp;
7151541Srgrimes	struct ucred *cred;
7161541Srgrimes{
71719449Sdfr	struct nfsmount *nmp;
71819449Sdfr	int i;
71919449Sdfr	int gotiod;
72019449Sdfr	int slpflag = 0;
72119449Sdfr	int slptimeo = 0;
72219449Sdfr	int error;
7231541Srgrimes
7241541Srgrimes	if (nfs_numasync == 0)
7251541Srgrimes		return (EIO);
72619449Sdfr
72719449Sdfr	nmp = VFSTONFS(bp->b_vp->v_mount);
72819449Sdfragain:
72919449Sdfr	if (nmp->nm_flag & NFSMNT_INT)
73019449Sdfr		slpflag = PCATCH;
73119449Sdfr	gotiod = FALSE;
73219449Sdfr
73319449Sdfr	/*
73419449Sdfr	 * Find a free iod to process this request.
73519449Sdfr	 */
7361541Srgrimes	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
73719449Sdfr		if (nfs_iodwant[i]) {
73819449Sdfr			/*
73919449Sdfr			 * Found one, so wake it up and tell it which
74019449Sdfr			 * mount to process.
74119449Sdfr			 */
74219449Sdfr			NFS_DPF(ASYNCIO,
74319449Sdfr				("nfs_asyncio: waking iod %d for mount %p\n",
74419449Sdfr				 i, nmp));
74519449Sdfr			nfs_iodwant[i] = (struct proc *)0;
74619449Sdfr			nfs_iodmount[i] = nmp;
74719449Sdfr			nmp->nm_bufqiods++;
74819449Sdfr			wakeup((caddr_t)&nfs_iodwant[i]);
74919449Sdfr			gotiod = TRUE;
75019449Sdfr		}
75119449Sdfr
75219449Sdfr	/*
75319449Sdfr	 * If none are free, we may already have an iod working on this mount
75419449Sdfr	 * point.  If so, it will process our request.
75519449Sdfr	 */
75619449Sdfr	if (!gotiod) {
75719449Sdfr		if (nmp->nm_bufqiods > 0) {
75819449Sdfr			NFS_DPF(ASYNCIO,
75919449Sdfr				("nfs_asyncio: %d iods are already processing mount %p\n",
76019449Sdfr				 nmp->nm_bufqiods, nmp));
76119449Sdfr			gotiod = TRUE;
76219449Sdfr		}
76319449Sdfr	}
76419449Sdfr
76519449Sdfr	/*
76619449Sdfr	 * If we have an iod which can process the request, then queue
76719449Sdfr	 * the buffer.
76819449Sdfr	 */
76919449Sdfr	if (gotiod) {
77019449Sdfr		/*
77119449Sdfr		 * Ensure that the queue never grows too large.
77219449Sdfr		 */
77319449Sdfr		while (nmp->nm_bufqlen >= 2*nfs_numasync) {
77419449Sdfr			NFS_DPF(ASYNCIO,
77519449Sdfr				("nfs_asyncio: waiting for mount %p queue to drain\n", nmp));
77619449Sdfr			nmp->nm_bufqwant = TRUE;
77719449Sdfr			error = tsleep(&nmp->nm_bufq, slpflag | PRIBIO,
77819449Sdfr				       "nfsaio", slptimeo);
77919449Sdfr			if (error) {
78019449Sdfr				if (nfs_sigintr(nmp, NULL, bp->b_proc))
78119449Sdfr					return (EINTR);
78219449Sdfr				if (slpflag == PCATCH) {
78319449Sdfr					slpflag = 0;
78419449Sdfr					slptimeo = 2 * hz;
78519449Sdfr				}
78619449Sdfr			}
78719449Sdfr			/*
78819449Sdfr			 * We might have lost our iod while sleeping,
78919449Sdfr			 * so check and loop if nescessary.
79019449Sdfr			 */
79119449Sdfr			if (nmp->nm_bufqiods == 0) {
79219449Sdfr				NFS_DPF(ASYNCIO,
79319449Sdfr					("nfs_asyncio: no iods after mount %p queue was drained, looping\n", nmp));
79419449Sdfr				goto again;
79519449Sdfr			}
79619449Sdfr		}
79719449Sdfr
7981541Srgrimes		if (bp->b_flags & B_READ) {
7991541Srgrimes			if (bp->b_rcred == NOCRED && cred != NOCRED) {
8001541Srgrimes				crhold(cred);
8011541Srgrimes				bp->b_rcred = cred;
8021541Srgrimes			}
8031541Srgrimes		} else {
8049336Sdfr			bp->b_flags |= B_WRITEINPROG;
8051541Srgrimes			if (bp->b_wcred == NOCRED && cred != NOCRED) {
8061541Srgrimes				crhold(cred);
8071541Srgrimes				bp->b_wcred = cred;
8081541Srgrimes			}
8091541Srgrimes		}
8108876Srgrimes
81119449Sdfr		TAILQ_INSERT_TAIL(&nmp->nm_bufq, bp, b_freelist);
81219449Sdfr		nmp->nm_bufqlen++;
8131541Srgrimes		return (0);
81419449Sdfr	}
8159336Sdfr
8169336Sdfr	/*
81719449Sdfr	 * All the iods are busy on other mounts, so return EIO to
81819449Sdfr	 * force the caller to process the i/o synchronously.
8199336Sdfr	 */
82019449Sdfr	NFS_DPF(ASYNCIO, ("nfs_asyncio: no iods available, i/o is synchronous\n"));
82119449Sdfr	return (EIO);
8221541Srgrimes}
8231541Srgrimes
8241541Srgrimes/*
8251541Srgrimes * Do an I/O operation to/from a cache block. This may be called
8261541Srgrimes * synchronously or from an nfsiod.
8271541Srgrimes */
8281541Srgrimesint
8291541Srgrimesnfs_doio(bp, cr, p)
8301541Srgrimes	register struct buf *bp;
8313305Sphk	struct ucred *cr;
8321541Srgrimes	struct proc *p;
8331541Srgrimes{
8341541Srgrimes	register struct uio *uiop;
8351541Srgrimes	register struct vnode *vp;
8361541Srgrimes	struct nfsnode *np;
8371541Srgrimes	struct nfsmount *nmp;
8389336Sdfr	int error = 0, diff, len, iomode, must_commit = 0;
8391541Srgrimes	struct uio uio;
8401541Srgrimes	struct iovec io;
8411541Srgrimes
8421541Srgrimes	vp = bp->b_vp;
8431541Srgrimes	np = VTONFS(vp);
8441541Srgrimes	nmp = VFSTONFS(vp->v_mount);
8451541Srgrimes	uiop = &uio;
8461541Srgrimes	uiop->uio_iov = &io;
8471541Srgrimes	uiop->uio_iovcnt = 1;
8481541Srgrimes	uiop->uio_segflg = UIO_SYSSPACE;
8491541Srgrimes	uiop->uio_procp = p;
8501541Srgrimes
8511541Srgrimes	/*
8521541Srgrimes	 * Historically, paging was done with physio, but no more.
8531541Srgrimes	 */
8543664Sphk	if (bp->b_flags & B_PHYS) {
8553664Sphk	    /*
8563664Sphk	     * ...though reading /dev/drum still gets us here.
8573664Sphk	     */
8581541Srgrimes	    io.iov_len = uiop->uio_resid = bp->b_bcount;
8593664Sphk	    /* mapping was done by vmapbuf() */
8601541Srgrimes	    io.iov_base = bp->b_data;
8619336Sdfr	    uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
8623664Sphk	    if (bp->b_flags & B_READ) {
8633664Sphk		uiop->uio_rw = UIO_READ;
8643664Sphk		nfsstats.read_physios++;
8653664Sphk		error = nfs_readrpc(vp, uiop, cr);
8663664Sphk	    } else {
8679336Sdfr		int com;
8689336Sdfr
8699336Sdfr		iomode = NFSV3WRITE_DATASYNC;
8703664Sphk		uiop->uio_rw = UIO_WRITE;
8713664Sphk		nfsstats.write_physios++;
8729336Sdfr		error = nfs_writerpc(vp, uiop, cr, &iomode, &com);
8733664Sphk	    }
8743664Sphk	    if (error) {
8753664Sphk		bp->b_flags |= B_ERROR;
8763664Sphk		bp->b_error = error;
8773664Sphk	    }
8783664Sphk	} else if (bp->b_flags & B_READ) {
8793664Sphk	    io.iov_len = uiop->uio_resid = bp->b_bcount;
8803664Sphk	    io.iov_base = bp->b_data;
8811541Srgrimes	    uiop->uio_rw = UIO_READ;
8821541Srgrimes	    switch (vp->v_type) {
8831541Srgrimes	    case VREG:
8849336Sdfr		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
8851541Srgrimes		nfsstats.read_bios++;
8861541Srgrimes		error = nfs_readrpc(vp, uiop, cr);
8871541Srgrimes		if (!error) {
8881541Srgrimes		    bp->b_validoff = 0;
8891541Srgrimes		    if (uiop->uio_resid) {
8901541Srgrimes			/*
8911541Srgrimes			 * If len > 0, there is a hole in the file and
8921541Srgrimes			 * no writes after the hole have been pushed to
8931541Srgrimes			 * the server yet.
8941541Srgrimes			 * Just zero fill the rest of the valid area.
8951541Srgrimes			 */
8961541Srgrimes			diff = bp->b_bcount - uiop->uio_resid;
8979336Sdfr			len = np->n_size - (((u_quad_t)bp->b_blkno) * DEV_BSIZE
8981541Srgrimes				+ diff);
8991541Srgrimes			if (len > 0) {
9001541Srgrimes			    len = min(len, uiop->uio_resid);
9011541Srgrimes			    bzero((char *)bp->b_data + diff, len);
9021541Srgrimes			    bp->b_validend = diff + len;
9031541Srgrimes			} else
9041541Srgrimes			    bp->b_validend = diff;
9051541Srgrimes		    } else
9061541Srgrimes			bp->b_validend = bp->b_bcount;
9071541Srgrimes		}
9081541Srgrimes		if (p && (vp->v_flag & VTEXT) &&
9091541Srgrimes			(((nmp->nm_flag & NFSMNT_NQNFS) &&
9109336Sdfr			  NQNFS_CKINVALID(vp, np, ND_READ) &&
9111541Srgrimes			  np->n_lrev != np->n_brev) ||
9121541Srgrimes			 (!(nmp->nm_flag & NFSMNT_NQNFS) &&
91318397Snate			  np->n_mtime != np->n_vattr.va_mtime.tv_sec))) {
9141541Srgrimes			uprintf("Process killed due to text file modification\n");
9151541Srgrimes			psignal(p, SIGKILL);
9169336Sdfr#ifdef __NetBSD__
9179336Sdfr			p->p_holdcnt++;
9189336Sdfr#else
9191541Srgrimes			p->p_flag |= P_NOSWAP;
9209336Sdfr#endif
9211541Srgrimes		}
9221541Srgrimes		break;
9231541Srgrimes	    case VLNK:
9249336Sdfr		uiop->uio_offset = (off_t)0;
9251541Srgrimes		nfsstats.readlink_bios++;
9261541Srgrimes		error = nfs_readlinkrpc(vp, uiop, cr);
9271541Srgrimes		break;
9281541Srgrimes	    case VDIR:
9291541Srgrimes		nfsstats.readdir_bios++;
9309336Sdfr		uiop->uio_offset = ((u_quad_t)bp->b_lblkno) * NFS_DIRBLKSIZ;
9319336Sdfr		if (nmp->nm_flag & NFSMNT_RDIRPLUS) {
9329336Sdfr			error = nfs_readdirplusrpc(vp, uiop, cr);
9339336Sdfr			if (error == NFSERR_NOTSUPP)
9349336Sdfr				nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
9359336Sdfr		}
9369336Sdfr		if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0)
9379336Sdfr			error = nfs_readdirrpc(vp, uiop, cr);
9381541Srgrimes		break;
9393305Sphk	    default:
9403305Sphk		printf("nfs_doio:  type %x unexpected\n",vp->v_type);
9413305Sphk		break;
9421541Srgrimes	    };
9431541Srgrimes	    if (error) {
9441541Srgrimes		bp->b_flags |= B_ERROR;
9451541Srgrimes		bp->b_error = error;
9461541Srgrimes	    }
9471541Srgrimes	} else {
9488692Sdg	    if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size)
9498692Sdg		bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
9508692Sdg
9518692Sdg	    if (bp->b_dirtyend > bp->b_dirtyoff) {
9528692Sdg		io.iov_len = uiop->uio_resid = bp->b_dirtyend
9539336Sdfr		    - bp->b_dirtyoff;
9549336Sdfr		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE
9559336Sdfr		    + bp->b_dirtyoff;
9568692Sdg		io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
9578692Sdg		uiop->uio_rw = UIO_WRITE;
9588692Sdg		nfsstats.write_bios++;
9599336Sdfr		if ((bp->b_flags & (B_ASYNC | B_NEEDCOMMIT | B_NOCACHE)) == B_ASYNC)
9609336Sdfr		    iomode = NFSV3WRITE_UNSTABLE;
9618692Sdg		else
9629336Sdfr		    iomode = NFSV3WRITE_FILESYNC;
9639336Sdfr		bp->b_flags |= B_WRITEINPROG;
9649336Sdfr		error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit);
9659336Sdfr		if (!error && iomode == NFSV3WRITE_UNSTABLE)
96623570Sbde		    bp->b_flags |= B_NEEDCOMMIT | B_CLUSTEROK;
9679336Sdfr		else
9689336Sdfr		    bp->b_flags &= ~B_NEEDCOMMIT;
9699336Sdfr		bp->b_flags &= ~B_WRITEINPROG;
9708692Sdg
9719336Sdfr		/*
9729336Sdfr		 * For an interrupted write, the buffer is still valid
9739336Sdfr		 * and the write hasn't been pushed to the server yet,
9749336Sdfr		 * so we can't set B_ERROR and report the interruption
9759336Sdfr		 * by setting B_EINTR. For the B_ASYNC case, B_EINTR
9769336Sdfr		 * is not relevant, so the rpc attempt is essentially
9779336Sdfr		 * a noop.  For the case of a V3 write rpc not being
9789336Sdfr		 * committed to stable storage, the block is still
9799336Sdfr		 * dirty and requires either a commit rpc or another
9809336Sdfr		 * write rpc with iomode == NFSV3WRITE_FILESYNC before
9819336Sdfr		 * the block is reused. This is indicated by setting
9829336Sdfr		 * the B_DELWRI and B_NEEDCOMMIT flags.
9839336Sdfr		 */
9849336Sdfr    		if (error == EINTR
9859336Sdfr		    || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
9868692Sdg			bp->b_flags &= ~(B_INVAL|B_NOCACHE);
9878692Sdg			bp->b_flags |= B_DELWRI;
9881541Srgrimes
9891541Srgrimes		/*
9901541Srgrimes		 * Since for the B_ASYNC case, nfs_bwrite() has reassigned the
9911541Srgrimes		 * buffer to the clean list, we have to reassign it back to the
9921541Srgrimes		 * dirty one. Ugh.
9931541Srgrimes		 */
9948692Sdg			if (bp->b_flags & B_ASYNC)
9958692Sdg				reassignbuf(bp, vp);
9968692Sdg			else
9978692Sdg				bp->b_flags |= B_EINTR;
9988692Sdg	    	} else {
9998692Sdg			if (error) {
10008692Sdg				bp->b_flags |= B_ERROR;
10018692Sdg				bp->b_error = np->n_error = error;
10028692Sdg				np->n_flag |= NWRITEERR;
10038692Sdg			}
10048692Sdg			bp->b_dirtyoff = bp->b_dirtyend = 0;
10058692Sdg		}
10061541Srgrimes	    } else {
10078692Sdg		bp->b_resid = 0;
10088692Sdg		biodone(bp);
10098692Sdg		return (0);
10101541Srgrimes	    }
10111541Srgrimes	}
10121541Srgrimes	bp->b_resid = uiop->uio_resid;
10139336Sdfr	if (must_commit)
10149336Sdfr		nfs_clearcommit(vp->v_mount);
10151541Srgrimes	biodone(bp);
10161541Srgrimes	return (error);
10171541Srgrimes}
1018