nfs_bio.c revision 12662
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 *
361541Srgrimes *	@(#)nfs_bio.c	8.5 (Berkeley) 1/4/94
3712662Sdg * $Id: nfs_bio.c,v 1.19 1995/12/03 10:02:52 bde Exp $
381541Srgrimes */
391541Srgrimes
401541Srgrimes#include <sys/param.h>
411541Srgrimes#include <sys/systm.h>
421541Srgrimes#include <sys/resourcevar.h>
433305Sphk#include <sys/signalvar.h>
441541Srgrimes#include <sys/proc.h>
451541Srgrimes#include <sys/buf.h>
461541Srgrimes#include <sys/vnode.h>
471541Srgrimes#include <sys/mount.h>
481541Srgrimes#include <sys/kernel.h>
491541Srgrimes
501541Srgrimes#include <vm/vm.h>
5112662Sdg#include <vm/vm_param.h>
5212662Sdg#include <vm/vm_extern.h>
531541Srgrimes
541541Srgrimes#include <nfs/rpcv2.h>
559336Sdfr#include <nfs/nfsproto.h>
561541Srgrimes#include <nfs/nfs.h>
571541Srgrimes#include <nfs/nfsmount.h>
581541Srgrimes#include <nfs/nqnfs.h>
599336Sdfr#include <nfs/nfsnode.h>
601541Srgrimes
6112588Sbdeextern struct buf *nfs_getcacheblk __P((struct vnode *vp, daddr_t bn, int size,
6212588Sbde					struct proc *p));
6312588Sbde
641541Srgrimesextern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
651541Srgrimesextern int nfs_numasync;
669336Sdfrextern struct nfsstats nfsstats;
671541Srgrimes
681541Srgrimes/*
699336Sdfr * Ifdefs for FreeBSD-current's merged VM/buffer cache. It is unfortunate
709336Sdfr * that this isn't done inside getblk() and brelse() so these calls
719336Sdfr * wouldn't need to be here.
729336Sdfr */
739336Sdfr#ifdef B_VMIO
749336Sdfr#define vnode_pager_uncache(vp)
759336Sdfr#else
769336Sdfr#define vfs_busy_pages(bp, f)
779336Sdfr#define vfs_unbusy_pages(bp)
789336Sdfr#define vfs_dirty_pages(bp)
799336Sdfr#endif
809336Sdfr
819336Sdfr/*
821541Srgrimes * Vnode op for read using bio
831541Srgrimes * Any similarity to readip() is purely coincidental
841541Srgrimes */
851549Srgrimesint
861541Srgrimesnfs_bioread(vp, uio, ioflag, cred)
871541Srgrimes	register struct vnode *vp;
881541Srgrimes	register struct uio *uio;
891541Srgrimes	int ioflag;
901541Srgrimes	struct ucred *cred;
911541Srgrimes{
921541Srgrimes	register struct nfsnode *np = VTONFS(vp);
939336Sdfr	register int biosize, diff, i;
941549Srgrimes	struct buf *bp = 0, *rabp;
951541Srgrimes	struct vattr vattr;
961541Srgrimes	struct proc *p;
979336Sdfr	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
985455Sdg	daddr_t lbn, rabn;
998692Sdg	int bufsize;
1007871Sdg	int nra, error = 0, n = 0, on = 0, not_readin;
1011541Srgrimes
1021541Srgrimes#ifdef DIAGNOSTIC
1031541Srgrimes	if (uio->uio_rw != UIO_READ)
1041541Srgrimes		panic("nfs_read mode");
1051541Srgrimes#endif
1061541Srgrimes	if (uio->uio_resid == 0)
1071541Srgrimes		return (0);
1089336Sdfr	if (uio->uio_offset < 0)
1091541Srgrimes		return (EINVAL);
1101541Srgrimes	p = uio->uio_procp;
1119336Sdfr	if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
1129336Sdfr		(void)nfs_fsinfo(nmp, vp, cred, p);
1139428Sdfr	biosize = vp->v_mount->mnt_stat.f_iosize;
1141541Srgrimes	/*
1151541Srgrimes	 * For nfs, cache consistency can only be maintained approximately.
1161541Srgrimes	 * Although RFC1094 does not specify the criteria, the following is
1171541Srgrimes	 * believed to be compatible with the reference port.
1181541Srgrimes	 * For nqnfs, full cache consistency is maintained within the loop.
1191541Srgrimes	 * For nfs:
1201541Srgrimes	 * If the file's modify time on the server has changed since the
1211541Srgrimes	 * last read rpc or you have written to the file,
1221541Srgrimes	 * you may have lost data cache consistency with the
1231541Srgrimes	 * server, so flush all of the file's data out of the cache.
1241541Srgrimes	 * Then force a getattr rpc to ensure that you have up to date
1251541Srgrimes	 * attributes.
1261541Srgrimes	 * NB: This implies that cache data can be read when up to
1271541Srgrimes	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
1281541Srgrimes	 * attributes this could be forced by setting n_attrstamp to 0 before
1291541Srgrimes	 * the VOP_GETATTR() call.
1301541Srgrimes	 */
13110219Sdfr	if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) {
1321541Srgrimes		if (np->n_flag & NMODIFIED) {
1339336Sdfr			if (vp->v_type != VREG) {
1349336Sdfr				if (vp->v_type != VDIR)
1359336Sdfr					panic("nfs: bioread, not dir");
1369336Sdfr				nfs_invaldir(vp);
1373305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1383305Sphk				if (error)
1391541Srgrimes					return (error);
1401541Srgrimes			}
1411541Srgrimes			np->n_attrstamp = 0;
1423305Sphk			error = VOP_GETATTR(vp, &vattr, cred, p);
1433305Sphk			if (error)
1441541Srgrimes				return (error);
1451541Srgrimes			np->n_mtime = vattr.va_mtime.ts_sec;
1461541Srgrimes		} else {
1473305Sphk			error = VOP_GETATTR(vp, &vattr, cred, p);
1483305Sphk			if (error)
1491541Srgrimes				return (error);
1501541Srgrimes			if (np->n_mtime != vattr.va_mtime.ts_sec) {
1519336Sdfr				if (vp->v_type == VDIR)
1529336Sdfr					nfs_invaldir(vp);
1533305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1543305Sphk				if (error)
1551541Srgrimes					return (error);
1561541Srgrimes				np->n_mtime = vattr.va_mtime.ts_sec;
1571541Srgrimes			}
1581541Srgrimes		}
1591541Srgrimes	}
1601541Srgrimes	do {
1611541Srgrimes
1621541Srgrimes	    /*
1631541Srgrimes	     * Get a valid lease. If cached data is stale, flush it.
1641541Srgrimes	     */
1651541Srgrimes	    if (nmp->nm_flag & NFSMNT_NQNFS) {
1669336Sdfr		if (NQNFS_CKINVALID(vp, np, ND_READ)) {
1671541Srgrimes		    do {
1689336Sdfr			error = nqnfs_getlease(vp, ND_READ, cred, p);
1691541Srgrimes		    } while (error == NQNFS_EXPIRED);
1701541Srgrimes		    if (error)
1711541Srgrimes			return (error);
1721541Srgrimes		    if (np->n_lrev != np->n_brev ||
1731541Srgrimes			(np->n_flag & NQNFSNONCACHE) ||
1741541Srgrimes			((np->n_flag & NMODIFIED) && vp->v_type == VDIR)) {
1759336Sdfr			if (vp->v_type == VDIR)
1769336Sdfr			    nfs_invaldir(vp);
1773305Sphk			error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1783305Sphk			if (error)
1791541Srgrimes			    return (error);
1801541Srgrimes			np->n_brev = np->n_lrev;
1811541Srgrimes		    }
1821541Srgrimes		} else if (vp->v_type == VDIR && (np->n_flag & NMODIFIED)) {
1839336Sdfr		    nfs_invaldir(vp);
1843305Sphk		    error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
1853305Sphk		    if (error)
1861541Srgrimes			return (error);
1871541Srgrimes		}
1881541Srgrimes	    }
1891541Srgrimes	    if (np->n_flag & NQNFSNONCACHE) {
1901541Srgrimes		switch (vp->v_type) {
1911541Srgrimes		case VREG:
1929336Sdfr			return (nfs_readrpc(vp, uio, cred));
1931541Srgrimes		case VLNK:
1949336Sdfr			return (nfs_readlinkrpc(vp, uio, cred));
1951541Srgrimes		case VDIR:
1961541Srgrimes			break;
1973305Sphk		default:
1988876Srgrimes			printf(" NQNFSNONCACHE: type %x unexpected\n",
1993305Sphk				vp->v_type);
2001541Srgrimes		};
2011541Srgrimes	    }
2021541Srgrimes	    switch (vp->v_type) {
2031541Srgrimes	    case VREG:
2041541Srgrimes		nfsstats.biocache_reads++;
2051541Srgrimes		lbn = uio->uio_offset / biosize;
2069336Sdfr		on = uio->uio_offset & (biosize - 1);
2071541Srgrimes		not_readin = 1;
2081541Srgrimes
2091541Srgrimes		/*
2101541Srgrimes		 * Start the read ahead(s), as required.
2111541Srgrimes		 */
2129336Sdfr		if (nfs_numasync > 0 && nmp->nm_readahead > 0) {
2131541Srgrimes		    for (nra = 0; nra < nmp->nm_readahead &&
2141541Srgrimes			(lbn + 1 + nra) * biosize < np->n_size; nra++) {
2155455Sdg			rabn = lbn + 1 + nra;
2161541Srgrimes			if (!incore(vp, rabn)) {
2171541Srgrimes			    rabp = nfs_getcacheblk(vp, rabn, biosize, p);
2181541Srgrimes			    if (!rabp)
2191541Srgrimes				return (EINTR);
2208692Sdg			    if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) {
2211541Srgrimes				rabp->b_flags |= (B_READ | B_ASYNC);
2225455Sdg				vfs_busy_pages(rabp, 0);
2231541Srgrimes				if (nfs_asyncio(rabp, cred)) {
2245455Sdg				    rabp->b_flags |= B_INVAL|B_ERROR;
2255455Sdg				    vfs_unbusy_pages(rabp);
2261541Srgrimes				    brelse(rabp);
2271541Srgrimes				}
2285471Sdg			    } else {
2295471Sdg				brelse(rabp);
2301541Srgrimes			    }
2311541Srgrimes			}
2321541Srgrimes		    }
2331541Srgrimes		}
2341541Srgrimes
2351541Srgrimes		/*
2361541Srgrimes		 * If the block is in the cache and has the required data
2371541Srgrimes		 * in a valid region, just copy it out.
2381541Srgrimes		 * Otherwise, get the block and write back/read in,
2391541Srgrimes		 * as required.
2401541Srgrimes		 */
2411541Srgrimesagain:
2428692Sdg		bufsize = biosize;
2438692Sdg		if ((lbn + 1) * biosize > np->n_size) {
2448692Sdg			bufsize = np->n_size - lbn * biosize;
2458692Sdg			bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
2468692Sdg		}
2478692Sdg		bp = nfs_getcacheblk(vp, lbn, bufsize, p);
2487871Sdg		if (!bp)
2497871Sdg			return (EINTR);
2507871Sdg		if ((bp->b_flags & B_CACHE) == 0) {
2517871Sdg			bp->b_flags |= B_READ;
2527871Sdg			not_readin = 0;
2537871Sdg			vfs_busy_pages(bp, 0);
2547871Sdg			error = nfs_doio(bp, cred, p);
2557871Sdg			if (error) {
2567871Sdg			    brelse(bp);
2577871Sdg			    return (error);
2581541Srgrimes			}
2591541Srgrimes		}
2608692Sdg		if (bufsize > on) {
2618692Sdg			n = min((unsigned)(bufsize - on), uio->uio_resid);
2628692Sdg		} else {
2638692Sdg			n = 0;
2648692Sdg		}
2651541Srgrimes		diff = np->n_size - uio->uio_offset;
2661541Srgrimes		if (diff < n)
2671541Srgrimes			n = diff;
2681541Srgrimes		if (not_readin && n > 0) {
2691541Srgrimes			if (on < bp->b_validoff || (on + n) > bp->b_validend) {
2706148Sdg				bp->b_flags |= B_NOCACHE;
2711541Srgrimes				if (bp->b_dirtyend > 0) {
2721541Srgrimes				    if ((bp->b_flags & B_DELWRI) == 0)
2731541Srgrimes					panic("nfsbioread");
2741541Srgrimes				    if (VOP_BWRITE(bp) == EINTR)
2751541Srgrimes					return (EINTR);
2761541Srgrimes				} else
2771541Srgrimes				    brelse(bp);
2781541Srgrimes				goto again;
2791541Srgrimes			}
2801541Srgrimes		}
2811541Srgrimes		vp->v_lastr = lbn;
2821541Srgrimes		diff = (on >= bp->b_validend) ? 0 : (bp->b_validend - on);
2831541Srgrimes		if (diff < n)
2841541Srgrimes			n = diff;
2851541Srgrimes		break;
2861541Srgrimes	    case VLNK:
2871541Srgrimes		nfsstats.biocache_readlinks++;
2881541Srgrimes		bp = nfs_getcacheblk(vp, (daddr_t)0, NFS_MAXPATHLEN, p);
2891541Srgrimes		if (!bp)
2901541Srgrimes			return (EINTR);
2917871Sdg		if ((bp->b_flags & B_CACHE) == 0) {
2921541Srgrimes			bp->b_flags |= B_READ;
2935455Sdg			vfs_busy_pages(bp, 0);
2943305Sphk			error = nfs_doio(bp, cred, p);
2953305Sphk			if (error) {
2965455Sdg				bp->b_flags |= B_ERROR;
2971541Srgrimes				brelse(bp);
2981541Srgrimes				return (error);
2991541Srgrimes			}
3001541Srgrimes		}
3011541Srgrimes		n = min(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
3021541Srgrimes		on = 0;
3031541Srgrimes		break;
3041541Srgrimes	    case VDIR:
3051541Srgrimes		nfsstats.biocache_readdirs++;
3069336Sdfr		lbn = uio->uio_offset / NFS_DIRBLKSIZ;
3079336Sdfr		on = uio->uio_offset & (NFS_DIRBLKSIZ - 1);
3085455Sdg		bp = nfs_getcacheblk(vp, lbn, NFS_DIRBLKSIZ, p);
3091541Srgrimes		if (!bp)
3109336Sdfr		    return (EINTR);
3117871Sdg		if ((bp->b_flags & B_CACHE) == 0) {
3129336Sdfr		    bp->b_flags |= B_READ;
3139336Sdfr		    vfs_busy_pages(bp, 0);
3149336Sdfr		    error = nfs_doio(bp, cred, p);
3159336Sdfr		    if (error) {
3169336Sdfr			brelse(bp);
3179336Sdfr			while (error == NFSERR_BAD_COOKIE) {
3189336Sdfr			    nfs_invaldir(vp);
3199336Sdfr			    error = nfs_vinvalbuf(vp, 0, cred, p, 1);
3209336Sdfr			    /*
3219336Sdfr			     * Yuck! The directory has been modified on the
3229336Sdfr			     * server. The only way to get the block is by
3239336Sdfr			     * reading from the beginning to get all the
3249336Sdfr			     * offset cookies.
3259336Sdfr			     */
3269336Sdfr			    for (i = 0; i <= lbn && !error; i++) {
3279336Sdfr				bp = nfs_getcacheblk(vp, i, NFS_DIRBLKSIZ, p);
3289336Sdfr				if (!bp)
3299336Sdfr				    return (EINTR);
3309336Sdfr				if ((bp->b_flags & B_DONE) == 0) {
3319336Sdfr				    bp->b_flags |= B_READ;
3329336Sdfr				    vfs_busy_pages(bp, 0);
3339336Sdfr				    error = nfs_doio(bp, cred, p);
3349336Sdfr				    if (error)
3359336Sdfr					brelse(bp);
3369336Sdfr				}
3379336Sdfr			    }
3381541Srgrimes			}
3399336Sdfr			if (error)
3409336Sdfr			    return (error);
3419336Sdfr		    }
3421541Srgrimes		}
3431541Srgrimes
3441541Srgrimes		/*
3451541Srgrimes		 * If not eof and read aheads are enabled, start one.
3461541Srgrimes		 * (You need the current block first, so that you have the
3479336Sdfr		 *  directory offset cookie of the next block.)
3481541Srgrimes		 */
3491541Srgrimes		if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
3509336Sdfr		    (np->n_direofoffset == 0 ||
3519336Sdfr		    (lbn + 1) * NFS_DIRBLKSIZ < np->n_direofoffset) &&
3529336Sdfr		    !(np->n_flag & NQNFSNONCACHE) &&
3539336Sdfr		    !incore(vp, lbn + 1)) {
3549336Sdfr			rabp = nfs_getcacheblk(vp, lbn + 1, NFS_DIRBLKSIZ, p);
3551541Srgrimes			if (rabp) {
3568692Sdg			    if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) {
3571541Srgrimes				rabp->b_flags |= (B_READ | B_ASYNC);
3585455Sdg				vfs_busy_pages(rabp, 0);
3591541Srgrimes				if (nfs_asyncio(rabp, cred)) {
3606148Sdg				    rabp->b_flags |= B_INVAL|B_ERROR;
3615455Sdg				    vfs_unbusy_pages(rabp);
3621541Srgrimes				    brelse(rabp);
3631541Srgrimes				}
3645471Sdg			    } else {
3655471Sdg				brelse(rabp);
3661541Srgrimes			    }
3671541Srgrimes			}
3681541Srgrimes		}
3699336Sdfr		n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid - on);
3701541Srgrimes		break;
3713305Sphk	    default:
3729336Sdfr		printf(" nfs_bioread: type %x unexpected\n",vp->v_type);
3733305Sphk		break;
3741541Srgrimes	    };
3751541Srgrimes
3761541Srgrimes	    if (n > 0) {
3777871Sdg		error = uiomove(bp->b_data + on, (int)n, uio);
3781541Srgrimes	    }
3791541Srgrimes	    switch (vp->v_type) {
3801541Srgrimes	    case VREG:
3811541Srgrimes		break;
3821541Srgrimes	    case VLNK:
3831541Srgrimes		n = 0;
3841541Srgrimes		break;
3851541Srgrimes	    case VDIR:
3869336Sdfr		if (np->n_flag & NQNFSNONCACHE)
3879336Sdfr			bp->b_flags |= B_INVAL;
3881541Srgrimes		break;
3893305Sphk	    default:
3909336Sdfr		printf(" nfs_bioread: type %x unexpected\n",vp->v_type);
3913305Sphk	    }
3927871Sdg 	    brelse(bp);
3931541Srgrimes	} while (error == 0 && uio->uio_resid > 0 && n > 0);
3941541Srgrimes	return (error);
3951541Srgrimes}
3961541Srgrimes
3971541Srgrimes/*
3981541Srgrimes * Vnode op for write using bio
3991541Srgrimes */
4001549Srgrimesint
4011541Srgrimesnfs_write(ap)
4021541Srgrimes	struct vop_write_args /* {
4031541Srgrimes		struct vnode *a_vp;
4041541Srgrimes		struct uio *a_uio;
4051541Srgrimes		int  a_ioflag;
4061541Srgrimes		struct ucred *a_cred;
4071541Srgrimes	} */ *ap;
4081541Srgrimes{
4091541Srgrimes	register int biosize;
4101541Srgrimes	register struct uio *uio = ap->a_uio;
4111541Srgrimes	struct proc *p = uio->uio_procp;
4121541Srgrimes	register struct vnode *vp = ap->a_vp;
4131541Srgrimes	struct nfsnode *np = VTONFS(vp);
4141541Srgrimes	register struct ucred *cred = ap->a_cred;
4151541Srgrimes	int ioflag = ap->a_ioflag;
4161541Srgrimes	struct buf *bp;
4171541Srgrimes	struct vattr vattr;
4189336Sdfr	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
41911921Sphk	daddr_t lbn;
4208692Sdg	int bufsize;
4219336Sdfr	int n, on, error = 0, iomode, must_commit;
4221541Srgrimes
4231541Srgrimes#ifdef DIAGNOSTIC
4241541Srgrimes	if (uio->uio_rw != UIO_WRITE)
4251541Srgrimes		panic("nfs_write mode");
4261541Srgrimes	if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
4271541Srgrimes		panic("nfs_write proc");
4281541Srgrimes#endif
4291541Srgrimes	if (vp->v_type != VREG)
4301541Srgrimes		return (EIO);
4311541Srgrimes	if (np->n_flag & NWRITEERR) {
4321541Srgrimes		np->n_flag &= ~NWRITEERR;
4331541Srgrimes		return (np->n_error);
4341541Srgrimes	}
4359336Sdfr	if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
4369336Sdfr		(void)nfs_fsinfo(nmp, vp, cred, p);
4371541Srgrimes	if (ioflag & (IO_APPEND | IO_SYNC)) {
4381541Srgrimes		if (np->n_flag & NMODIFIED) {
4391541Srgrimes			np->n_attrstamp = 0;
4403305Sphk			error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
4413305Sphk			if (error)
4421541Srgrimes				return (error);
4431541Srgrimes		}
4441541Srgrimes		if (ioflag & IO_APPEND) {
4451541Srgrimes			np->n_attrstamp = 0;
4463305Sphk			error = VOP_GETATTR(vp, &vattr, cred, p);
4473305Sphk			if (error)
4481541Srgrimes				return (error);
4491541Srgrimes			uio->uio_offset = np->n_size;
4501541Srgrimes		}
4511541Srgrimes	}
4521541Srgrimes	if (uio->uio_offset < 0)
4531541Srgrimes		return (EINVAL);
4541541Srgrimes	if (uio->uio_resid == 0)
4551541Srgrimes		return (0);
4561541Srgrimes	/*
4571541Srgrimes	 * Maybe this should be above the vnode op call, but so long as
4581541Srgrimes	 * file servers have no limits, i don't think it matters
4591541Srgrimes	 */
4601541Srgrimes	if (p && uio->uio_offset + uio->uio_resid >
4611541Srgrimes	      p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
4621541Srgrimes		psignal(p, SIGXFSZ);
4631541Srgrimes		return (EFBIG);
4641541Srgrimes	}
4651541Srgrimes	/*
4661541Srgrimes	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
4671541Srgrimes	 * will be the same size within a filesystem. nfs_writerpc will
4681541Srgrimes	 * still use nm_wsize when sizing the rpc's.
4691541Srgrimes	 */
4709428Sdfr	biosize = vp->v_mount->mnt_stat.f_iosize;
4711541Srgrimes	do {
4721541Srgrimes
4731541Srgrimes		/*
4743664Sphk		 * XXX make sure we aren't cached in the VM page cache
4753664Sphk		 */
4763664Sphk		/*
4771541Srgrimes		 * Check for a valid write lease.
4781541Srgrimes		 */
4791541Srgrimes		if ((nmp->nm_flag & NFSMNT_NQNFS) &&
4809336Sdfr		    NQNFS_CKINVALID(vp, np, ND_WRITE)) {
4811541Srgrimes			do {
4829336Sdfr				error = nqnfs_getlease(vp, ND_WRITE, cred, p);
4831541Srgrimes			} while (error == NQNFS_EXPIRED);
4841541Srgrimes			if (error)
4851541Srgrimes				return (error);
4861541Srgrimes			if (np->n_lrev != np->n_brev ||
4871541Srgrimes			    (np->n_flag & NQNFSNONCACHE)) {
4883305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
4893305Sphk				if (error)
4901541Srgrimes					return (error);
4911541Srgrimes				np->n_brev = np->n_lrev;
4921541Srgrimes			}
4931541Srgrimes		}
4949336Sdfr		if ((np->n_flag & NQNFSNONCACHE) && uio->uio_iovcnt == 1) {
4959336Sdfr		    iomode = NFSV3WRITE_FILESYNC;
4969336Sdfr		    error = nfs_writerpc(vp, uio, cred, &iomode, &must_commit);
4979336Sdfr		    if (must_commit)
4989336Sdfr			nfs_clearcommit(vp->v_mount);
4999336Sdfr		    return (error);
5009336Sdfr		}
5011541Srgrimes		nfsstats.biocache_writes++;
5021541Srgrimes		lbn = uio->uio_offset / biosize;
5031541Srgrimes		on = uio->uio_offset & (biosize-1);
5041541Srgrimes		n = min((unsigned)(biosize - on), uio->uio_resid);
5051541Srgrimesagain:
5068692Sdg		if (uio->uio_offset + n > np->n_size) {
5078692Sdg			np->n_size = uio->uio_offset + n;
5088692Sdg			vnode_pager_setsize(vp, (u_long)np->n_size);
5098692Sdg		}
5108692Sdg		bufsize = biosize;
5118692Sdg		if ((lbn + 1) * biosize > np->n_size) {
5128692Sdg			bufsize = np->n_size - lbn * biosize;
5138692Sdg			bufsize = (bufsize + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1);
5148692Sdg		}
5158692Sdg		bp = nfs_getcacheblk(vp, lbn, bufsize, p);
5161541Srgrimes		if (!bp)
5171541Srgrimes			return (EINTR);
5181541Srgrimes		if (bp->b_wcred == NOCRED) {
5191541Srgrimes			crhold(cred);
5201541Srgrimes			bp->b_wcred = cred;
5211541Srgrimes		}
5221541Srgrimes		np->n_flag |= NMODIFIED;
5238692Sdg
5248692Sdg		if ((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend > np->n_size) {
5258692Sdg			bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
5261541Srgrimes		}
5271541Srgrimes
5281541Srgrimes		/*
5291541Srgrimes		 * If the new write will leave a contiguous dirty
5301541Srgrimes		 * area, just update the b_dirtyoff and b_dirtyend,
5311541Srgrimes		 * otherwise force a write rpc of the old dirty area.
5321541Srgrimes		 */
5331541Srgrimes		if (bp->b_dirtyend > 0 &&
5341541Srgrimes		    (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {
5351541Srgrimes			bp->b_proc = p;
5361541Srgrimes			if (VOP_BWRITE(bp) == EINTR)
5371541Srgrimes				return (EINTR);
5381541Srgrimes			goto again;
5391541Srgrimes		}
5401541Srgrimes
5411541Srgrimes		/*
5421541Srgrimes		 * Check for valid write lease and get one as required.
5431541Srgrimes		 * In case getblk() and/or bwrite() delayed us.
5441541Srgrimes		 */
5451541Srgrimes		if ((nmp->nm_flag & NFSMNT_NQNFS) &&
5469336Sdfr		    NQNFS_CKINVALID(vp, np, ND_WRITE)) {
5471541Srgrimes			do {
5489336Sdfr				error = nqnfs_getlease(vp, ND_WRITE, cred, p);
5491541Srgrimes			} while (error == NQNFS_EXPIRED);
5501541Srgrimes			if (error) {
5511541Srgrimes				brelse(bp);
5521541Srgrimes				return (error);
5531541Srgrimes			}
5541541Srgrimes			if (np->n_lrev != np->n_brev ||
5551541Srgrimes			    (np->n_flag & NQNFSNONCACHE)) {
5561541Srgrimes				brelse(bp);
5573305Sphk				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
5583305Sphk				if (error)
5591541Srgrimes					return (error);
5601541Srgrimes				np->n_brev = np->n_lrev;
5611541Srgrimes				goto again;
5621541Srgrimes			}
5631541Srgrimes		}
5643305Sphk		error = uiomove((char *)bp->b_data + on, n, uio);
5653305Sphk		if (error) {
5661541Srgrimes			bp->b_flags |= B_ERROR;
5671541Srgrimes			brelse(bp);
5681541Srgrimes			return (error);
5691541Srgrimes		}
5701541Srgrimes		if (bp->b_dirtyend > 0) {
5711541Srgrimes			bp->b_dirtyoff = min(on, bp->b_dirtyoff);
5721541Srgrimes			bp->b_dirtyend = max((on + n), bp->b_dirtyend);
5731541Srgrimes		} else {
5741541Srgrimes			bp->b_dirtyoff = on;
5751541Srgrimes			bp->b_dirtyend = on + n;
5761541Srgrimes		}
5771541Srgrimes		if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||
5781541Srgrimes		    bp->b_validoff > bp->b_dirtyend) {
5791541Srgrimes			bp->b_validoff = bp->b_dirtyoff;
5801541Srgrimes			bp->b_validend = bp->b_dirtyend;
5811541Srgrimes		} else {
5821541Srgrimes			bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);
5831541Srgrimes			bp->b_validend = max(bp->b_validend, bp->b_dirtyend);
5841541Srgrimes		}
5851541Srgrimes		/*
5861541Srgrimes		 * If the lease is non-cachable or IO_SYNC do bwrite().
5871541Srgrimes		 */
5881541Srgrimes		if ((np->n_flag & NQNFSNONCACHE) || (ioflag & IO_SYNC)) {
5891541Srgrimes			bp->b_proc = p;
5903305Sphk			error = VOP_BWRITE(bp);
5913305Sphk			if (error)
5921541Srgrimes				return (error);
5939336Sdfr			if (np->n_flag & NQNFSNONCACHE) {
5949336Sdfr				error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
5959336Sdfr				if (error)
5969336Sdfr					return (error);
5979336Sdfr			}
5981541Srgrimes		} else if ((n + on) == biosize &&
5991541Srgrimes			(nmp->nm_flag & NFSMNT_NQNFS) == 0) {
6001541Srgrimes			bp->b_proc = (struct proc *)0;
6019336Sdfr			bp->b_flags |= B_ASYNC;
6029336Sdfr			(void)nfs_writebp(bp, 0);
6031541Srgrimes		} else
6041541Srgrimes			bdwrite(bp);
6051541Srgrimes	} while (uio->uio_resid > 0 && n > 0);
6061541Srgrimes	return (0);
6071541Srgrimes}
6081541Srgrimes
6091541Srgrimes/*
6101541Srgrimes * Get an nfs cache block.
6111541Srgrimes * Allocate a new one if the block isn't currently in the cache
6121541Srgrimes * and return the block marked busy. If the calling process is
6131541Srgrimes * interrupted by a signal for an interruptible mount point, return
6141541Srgrimes * NULL.
6151541Srgrimes */
6161541Srgrimesstruct buf *
6171541Srgrimesnfs_getcacheblk(vp, bn, size, p)
6181541Srgrimes	struct vnode *vp;
6191541Srgrimes	daddr_t bn;
6201541Srgrimes	int size;
6211541Srgrimes	struct proc *p;
6221541Srgrimes{
6231541Srgrimes	register struct buf *bp;
6241541Srgrimes	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
6259428Sdfr	int biosize = vp->v_mount->mnt_stat.f_iosize;
6261541Srgrimes
6271541Srgrimes	if (nmp->nm_flag & NFSMNT_INT) {
6281541Srgrimes		bp = getblk(vp, bn, size, PCATCH, 0);
6291541Srgrimes		while (bp == (struct buf *)0) {
6301541Srgrimes			if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
6311541Srgrimes				return ((struct buf *)0);
6321541Srgrimes			bp = getblk(vp, bn, size, 0, 2 * hz);
6331541Srgrimes		}
6341541Srgrimes	} else
6351541Srgrimes		bp = getblk(vp, bn, size, 0, 0);
6365455Sdg
6375455Sdg	if( vp->v_type == VREG)
6389336Sdfr		bp->b_blkno = (bn * biosize) / DEV_BSIZE;
6395455Sdg
6401541Srgrimes	return (bp);
6411541Srgrimes}
6421541Srgrimes
6431541Srgrimes/*
6441541Srgrimes * Flush and invalidate all dirty buffers. If another process is already
6451541Srgrimes * doing the flush, just wait for completion.
6461541Srgrimes */
6471549Srgrimesint
6481541Srgrimesnfs_vinvalbuf(vp, flags, cred, p, intrflg)
6491541Srgrimes	struct vnode *vp;
6501541Srgrimes	int flags;
6511541Srgrimes	struct ucred *cred;
6521541Srgrimes	struct proc *p;
6531541Srgrimes	int intrflg;
6541541Srgrimes{
6551541Srgrimes	register struct nfsnode *np = VTONFS(vp);
6561541Srgrimes	struct nfsmount *nmp = VFSTONFS(vp->v_mount);
6571541Srgrimes	int error = 0, slpflag, slptimeo;
6581541Srgrimes
6591541Srgrimes	if ((nmp->nm_flag & NFSMNT_INT) == 0)
6601541Srgrimes		intrflg = 0;
6611541Srgrimes	if (intrflg) {
6621541Srgrimes		slpflag = PCATCH;
6631541Srgrimes		slptimeo = 2 * hz;
6641541Srgrimes	} else {
6651541Srgrimes		slpflag = 0;
6661541Srgrimes		slptimeo = 0;
6671541Srgrimes	}
6681541Srgrimes	/*
6691541Srgrimes	 * First wait for any other process doing a flush to complete.
6701541Srgrimes	 */
6711541Srgrimes	while (np->n_flag & NFLUSHINPROG) {
6721541Srgrimes		np->n_flag |= NFLUSHWANT;
6731541Srgrimes		error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "nfsvinval",
6741541Srgrimes			slptimeo);
6751541Srgrimes		if (error && intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p))
6761541Srgrimes			return (EINTR);
6771541Srgrimes	}
6781541Srgrimes
6791541Srgrimes	/*
6801541Srgrimes	 * Now, flush as required.
6811541Srgrimes	 */
6821541Srgrimes	np->n_flag |= NFLUSHINPROG;
6831541Srgrimes	error = vinvalbuf(vp, flags, cred, p, slpflag, 0);
6841541Srgrimes	while (error) {
6851541Srgrimes		if (intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p)) {
6861541Srgrimes			np->n_flag &= ~NFLUSHINPROG;
6871541Srgrimes			if (np->n_flag & NFLUSHWANT) {
6881541Srgrimes				np->n_flag &= ~NFLUSHWANT;
6891541Srgrimes				wakeup((caddr_t)&np->n_flag);
6901541Srgrimes			}
6911541Srgrimes			return (EINTR);
6921541Srgrimes		}
6931541Srgrimes		error = vinvalbuf(vp, flags, cred, p, 0, slptimeo);
6941541Srgrimes	}
6951541Srgrimes	np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
6961541Srgrimes	if (np->n_flag & NFLUSHWANT) {
6971541Srgrimes		np->n_flag &= ~NFLUSHWANT;
6981541Srgrimes		wakeup((caddr_t)&np->n_flag);
6991541Srgrimes	}
7001541Srgrimes	return (0);
7011541Srgrimes}
7021541Srgrimes
7031541Srgrimes/*
7041541Srgrimes * Initiate asynchronous I/O. Return an error if no nfsiods are available.
7051541Srgrimes * This is mainly to avoid queueing async I/O requests when the nfsiods
7061541Srgrimes * are all hung on a dead server.
7071541Srgrimes */
7081549Srgrimesint
7091541Srgrimesnfs_asyncio(bp, cred)
7101541Srgrimes	register struct buf *bp;
7111541Srgrimes	struct ucred *cred;
7121541Srgrimes{
7131541Srgrimes	register int i;
7141541Srgrimes
7151541Srgrimes	if (nfs_numasync == 0)
7161541Srgrimes		return (EIO);
7171541Srgrimes	for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
7181541Srgrimes	    if (nfs_iodwant[i]) {
7191541Srgrimes		if (bp->b_flags & B_READ) {
7201541Srgrimes			if (bp->b_rcred == NOCRED && cred != NOCRED) {
7211541Srgrimes				crhold(cred);
7221541Srgrimes				bp->b_rcred = cred;
7231541Srgrimes			}
7241541Srgrimes		} else {
7259336Sdfr			bp->b_flags |= B_WRITEINPROG;
7261541Srgrimes			if (bp->b_wcred == NOCRED && cred != NOCRED) {
7271541Srgrimes				crhold(cred);
7281541Srgrimes				bp->b_wcred = cred;
7291541Srgrimes			}
7301541Srgrimes		}
7318876Srgrimes
7321541Srgrimes		TAILQ_INSERT_TAIL(&nfs_bufq, bp, b_freelist);
7331541Srgrimes		nfs_iodwant[i] = (struct proc *)0;
7341541Srgrimes		wakeup((caddr_t)&nfs_iodwant[i]);
7351541Srgrimes		return (0);
7361541Srgrimes	    }
7379336Sdfr
7389336Sdfr	/*
7399336Sdfr	 * If it is a read or a write already marked B_WRITEINPROG or B_NOCACHE
7409336Sdfr	 * return EIO so the process will call nfs_doio() and do it
7419336Sdfr	 * synchronously.
7429336Sdfr	 */
7439336Sdfr	if (bp->b_flags & (B_READ | B_WRITEINPROG | B_NOCACHE))
7449336Sdfr		return (EIO);
7459336Sdfr
7469336Sdfr	/*
7479336Sdfr	 * Just turn the async write into a delayed write, instead of
7489336Sdfr	 * doing in synchronously. Hopefully, at least one of the nfsiods
7499336Sdfr	 * is currently doing a write for this file and will pick up the
7509336Sdfr	 * delayed writes before going back to sleep.
7519336Sdfr	 */
7529336Sdfr	bp->b_flags |= B_DELWRI;
7539336Sdfr	reassignbuf(bp, bp->b_vp);
7549336Sdfr	biodone(bp);
7559336Sdfr	return (0);
7561541Srgrimes}
7571541Srgrimes
7581541Srgrimes/*
7591541Srgrimes * Do an I/O operation to/from a cache block. This may be called
7601541Srgrimes * synchronously or from an nfsiod.
7611541Srgrimes */
7621541Srgrimesint
7631541Srgrimesnfs_doio(bp, cr, p)
7641541Srgrimes	register struct buf *bp;
7653305Sphk	struct ucred *cr;
7661541Srgrimes	struct proc *p;
7671541Srgrimes{
7681541Srgrimes	register struct uio *uiop;
7691541Srgrimes	register struct vnode *vp;
7701541Srgrimes	struct nfsnode *np;
7711541Srgrimes	struct nfsmount *nmp;
7729336Sdfr	int error = 0, diff, len, iomode, must_commit = 0;
7731541Srgrimes	struct uio uio;
7741541Srgrimes	struct iovec io;
7751541Srgrimes
7761541Srgrimes	vp = bp->b_vp;
7771541Srgrimes	np = VTONFS(vp);
7781541Srgrimes	nmp = VFSTONFS(vp->v_mount);
7791541Srgrimes	uiop = &uio;
7801541Srgrimes	uiop->uio_iov = &io;
7811541Srgrimes	uiop->uio_iovcnt = 1;
7821541Srgrimes	uiop->uio_segflg = UIO_SYSSPACE;
7831541Srgrimes	uiop->uio_procp = p;
7841541Srgrimes
7851541Srgrimes	/*
7861541Srgrimes	 * Historically, paging was done with physio, but no more.
7871541Srgrimes	 */
7883664Sphk	if (bp->b_flags & B_PHYS) {
7893664Sphk	    /*
7903664Sphk	     * ...though reading /dev/drum still gets us here.
7913664Sphk	     */
7921541Srgrimes	    io.iov_len = uiop->uio_resid = bp->b_bcount;
7933664Sphk	    /* mapping was done by vmapbuf() */
7941541Srgrimes	    io.iov_base = bp->b_data;
7959336Sdfr	    uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
7963664Sphk	    if (bp->b_flags & B_READ) {
7973664Sphk		uiop->uio_rw = UIO_READ;
7983664Sphk		nfsstats.read_physios++;
7993664Sphk		error = nfs_readrpc(vp, uiop, cr);
8003664Sphk	    } else {
8019336Sdfr		int com;
8029336Sdfr
8039336Sdfr		iomode = NFSV3WRITE_DATASYNC;
8043664Sphk		uiop->uio_rw = UIO_WRITE;
8053664Sphk		nfsstats.write_physios++;
8069336Sdfr		error = nfs_writerpc(vp, uiop, cr, &iomode, &com);
8073664Sphk	    }
8083664Sphk	    if (error) {
8093664Sphk		bp->b_flags |= B_ERROR;
8103664Sphk		bp->b_error = error;
8113664Sphk	    }
8123664Sphk	} else if (bp->b_flags & B_READ) {
8133664Sphk	    io.iov_len = uiop->uio_resid = bp->b_bcount;
8143664Sphk	    io.iov_base = bp->b_data;
8151541Srgrimes	    uiop->uio_rw = UIO_READ;
8161541Srgrimes	    switch (vp->v_type) {
8171541Srgrimes	    case VREG:
8189336Sdfr		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE;
8191541Srgrimes		nfsstats.read_bios++;
8201541Srgrimes		error = nfs_readrpc(vp, uiop, cr);
8211541Srgrimes		if (!error) {
8221541Srgrimes		    bp->b_validoff = 0;
8231541Srgrimes		    if (uiop->uio_resid) {
8241541Srgrimes			/*
8251541Srgrimes			 * If len > 0, there is a hole in the file and
8261541Srgrimes			 * no writes after the hole have been pushed to
8271541Srgrimes			 * the server yet.
8281541Srgrimes			 * Just zero fill the rest of the valid area.
8291541Srgrimes			 */
8301541Srgrimes			diff = bp->b_bcount - uiop->uio_resid;
8319336Sdfr			len = np->n_size - (((u_quad_t)bp->b_blkno) * DEV_BSIZE
8321541Srgrimes				+ diff);
8331541Srgrimes			if (len > 0) {
8341541Srgrimes			    len = min(len, uiop->uio_resid);
8351541Srgrimes			    bzero((char *)bp->b_data + diff, len);
8361541Srgrimes			    bp->b_validend = diff + len;
8371541Srgrimes			} else
8381541Srgrimes			    bp->b_validend = diff;
8391541Srgrimes		    } else
8401541Srgrimes			bp->b_validend = bp->b_bcount;
8411541Srgrimes		}
8421541Srgrimes		if (p && (vp->v_flag & VTEXT) &&
8431541Srgrimes			(((nmp->nm_flag & NFSMNT_NQNFS) &&
8449336Sdfr			  NQNFS_CKINVALID(vp, np, ND_READ) &&
8451541Srgrimes			  np->n_lrev != np->n_brev) ||
8461541Srgrimes			 (!(nmp->nm_flag & NFSMNT_NQNFS) &&
8471541Srgrimes			  np->n_mtime != np->n_vattr.va_mtime.ts_sec))) {
8481541Srgrimes			uprintf("Process killed due to text file modification\n");
8491541Srgrimes			psignal(p, SIGKILL);
8509336Sdfr#ifdef __NetBSD__
8519336Sdfr			p->p_holdcnt++;
8529336Sdfr#else
8531541Srgrimes			p->p_flag |= P_NOSWAP;
8549336Sdfr#endif
8551541Srgrimes		}
8561541Srgrimes		break;
8571541Srgrimes	    case VLNK:
8589336Sdfr		uiop->uio_offset = (off_t)0;
8591541Srgrimes		nfsstats.readlink_bios++;
8601541Srgrimes		error = nfs_readlinkrpc(vp, uiop, cr);
8611541Srgrimes		break;
8621541Srgrimes	    case VDIR:
8631541Srgrimes		nfsstats.readdir_bios++;
8649336Sdfr		uiop->uio_offset = ((u_quad_t)bp->b_lblkno) * NFS_DIRBLKSIZ;
8659336Sdfr		if (nmp->nm_flag & NFSMNT_RDIRPLUS) {
8669336Sdfr			error = nfs_readdirplusrpc(vp, uiop, cr);
8679336Sdfr			if (error == NFSERR_NOTSUPP)
8689336Sdfr				nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
8699336Sdfr		}
8709336Sdfr		if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0)
8719336Sdfr			error = nfs_readdirrpc(vp, uiop, cr);
8721541Srgrimes		break;
8733305Sphk	    default:
8743305Sphk		printf("nfs_doio:  type %x unexpected\n",vp->v_type);
8753305Sphk		break;
8761541Srgrimes	    };
8771541Srgrimes	    if (error) {
8781541Srgrimes		bp->b_flags |= B_ERROR;
8791541Srgrimes		bp->b_error = error;
8801541Srgrimes	    }
8811541Srgrimes	} else {
8828692Sdg	    if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size)
8838692Sdg		bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE);
8848692Sdg
8858692Sdg	    if (bp->b_dirtyend > bp->b_dirtyoff) {
8868692Sdg		io.iov_len = uiop->uio_resid = bp->b_dirtyend
8879336Sdfr		    - bp->b_dirtyoff;
8889336Sdfr		uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE
8899336Sdfr		    + bp->b_dirtyoff;
8908692Sdg		io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
8918692Sdg		uiop->uio_rw = UIO_WRITE;
8928692Sdg		nfsstats.write_bios++;
8939336Sdfr		if ((bp->b_flags & (B_ASYNC | B_NEEDCOMMIT | B_NOCACHE)) == B_ASYNC)
8949336Sdfr		    iomode = NFSV3WRITE_UNSTABLE;
8958692Sdg		else
8969336Sdfr		    iomode = NFSV3WRITE_FILESYNC;
8979336Sdfr		bp->b_flags |= B_WRITEINPROG;
8989336Sdfr		error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit);
8999336Sdfr		if (!error && iomode == NFSV3WRITE_UNSTABLE)
9009336Sdfr		    bp->b_flags |= B_NEEDCOMMIT;
9019336Sdfr		else
9029336Sdfr		    bp->b_flags &= ~B_NEEDCOMMIT;
9039336Sdfr		bp->b_flags &= ~B_WRITEINPROG;
9048692Sdg
9059336Sdfr		/*
9069336Sdfr		 * For an interrupted write, the buffer is still valid
9079336Sdfr		 * and the write hasn't been pushed to the server yet,
9089336Sdfr		 * so we can't set B_ERROR and report the interruption
9099336Sdfr		 * by setting B_EINTR. For the B_ASYNC case, B_EINTR
9109336Sdfr		 * is not relevant, so the rpc attempt is essentially
9119336Sdfr		 * a noop.  For the case of a V3 write rpc not being
9129336Sdfr		 * committed to stable storage, the block is still
9139336Sdfr		 * dirty and requires either a commit rpc or another
9149336Sdfr		 * write rpc with iomode == NFSV3WRITE_FILESYNC before
9159336Sdfr		 * the block is reused. This is indicated by setting
9169336Sdfr		 * the B_DELWRI and B_NEEDCOMMIT flags.
9179336Sdfr		 */
9189336Sdfr    		if (error == EINTR
9199336Sdfr		    || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
9208692Sdg			bp->b_flags &= ~(B_INVAL|B_NOCACHE);
9218692Sdg			bp->b_flags |= B_DELWRI;
9221541Srgrimes
9231541Srgrimes		/*
9241541Srgrimes		 * Since for the B_ASYNC case, nfs_bwrite() has reassigned the
9251541Srgrimes		 * buffer to the clean list, we have to reassign it back to the
9261541Srgrimes		 * dirty one. Ugh.
9271541Srgrimes		 */
9288692Sdg			if (bp->b_flags & B_ASYNC)
9298692Sdg				reassignbuf(bp, vp);
9308692Sdg			else
9318692Sdg				bp->b_flags |= B_EINTR;
9328692Sdg	    	} else {
9338692Sdg			if (error) {
9348692Sdg				bp->b_flags |= B_ERROR;
9358692Sdg				bp->b_error = np->n_error = error;
9368692Sdg				np->n_flag |= NWRITEERR;
9378692Sdg			}
9388692Sdg			bp->b_dirtyoff = bp->b_dirtyend = 0;
9398692Sdg		}
9401541Srgrimes	    } else {
9418692Sdg		bp->b_resid = 0;
9428692Sdg		biodone(bp);
9438692Sdg		return (0);
9441541Srgrimes	    }
9451541Srgrimes	}
9461541Srgrimes	bp->b_resid = uiop->uio_resid;
9479336Sdfr	if (must_commit)
9489336Sdfr		nfs_clearcommit(vp->v_mount);
9491541Srgrimes	biodone(bp);
9501541Srgrimes	return (error);
9511541Srgrimes}
952