xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 47572)
138882Smacklem /*
238882Smacklem  * Copyright (c) 1989 The Regents of the University of California.
338882Smacklem  * All rights reserved.
438882Smacklem  *
538882Smacklem  * This code is derived from software contributed to Berkeley by
638882Smacklem  * Rick Macklem at The University of Guelph.
738882Smacklem  *
844509Sbostic  * %sccs.include.redist.c%
938882Smacklem  *
10*47572Skarels  *	@(#)nfs_bio.c	7.18 (Berkeley) 03/19/91
1138882Smacklem  */
1238882Smacklem 
1338882Smacklem #include "param.h"
14*47572Skarels #include "proc.h"
1538882Smacklem #include "buf.h"
1638882Smacklem #include "vnode.h"
1738882Smacklem #include "trace.h"
1838882Smacklem #include "mount.h"
19*47572Skarels #include "resourcevar.h"
20*47572Skarels 
2138882Smacklem #include "nfsnode.h"
2239750Smckusick #include "nfsv2.h"
2339750Smckusick #include "nfs.h"
2438882Smacklem #include "nfsiom.h"
2541897Smckusick #include "nfsmount.h"
2638882Smacklem 
2738882Smacklem /* True and false, how exciting */
2838882Smacklem #define	TRUE	1
2938882Smacklem #define	FALSE	0
3038882Smacklem 
3138882Smacklem /*
3238882Smacklem  * Vnode op for read using bio
3338882Smacklem  * Any similarity to readip() is purely coincidental
3438882Smacklem  */
3541897Smckusick nfs_bioread(vp, uio, ioflag, cred)
3638882Smacklem 	register struct vnode *vp;
3743348Smckusick 	register struct uio *uio;
3838882Smacklem 	int ioflag;
3938882Smacklem 	struct ucred *cred;
4038882Smacklem {
4138882Smacklem 	register struct nfsnode *np = VTONFS(vp);
4243348Smckusick 	register int biosize;
4338882Smacklem 	struct buf *bp;
4438882Smacklem 	struct vattr vattr;
4538882Smacklem 	daddr_t lbn, bn, rablock;
4639487Smckusick 	int diff, error = 0;
4738882Smacklem 	long n, on;
4838882Smacklem 
4942241Smckusick #ifdef lint
5042241Smckusick 	ioflag = ioflag;
5142241Smckusick #endif /* lint */
5238882Smacklem 	if (uio->uio_rw != UIO_READ)
5338882Smacklem 		panic("nfs_read mode");
5438882Smacklem 	if (uio->uio_resid == 0)
5539584Smckusick 		return (0);
5641897Smckusick 	if (uio->uio_offset < 0 && vp->v_type != VDIR)
5739584Smckusick 		return (EINVAL);
5843348Smckusick 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
5938882Smacklem 	/*
6038882Smacklem 	 * If the file's modify time on the server has changed since the
6138882Smacklem 	 * last read rpc or you have written to the file,
6238882Smacklem 	 * you may have lost data cache consistency with the
6338882Smacklem 	 * server, so flush all of the file's data out of the cache.
6441897Smckusick 	 * Then force a getattr rpc to ensure that you have up to date
6541897Smckusick 	 * attributes.
6638882Smacklem 	 * NB: This implies that cache data can be read when up to
6738882Smacklem 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
6838882Smacklem 	 * attributes this could be forced by setting n_attrstamp to 0 before
6943348Smckusick 	 * the nfs_dogetattr() call.
7038882Smacklem 	 */
7141897Smckusick 	if (vp->v_type != VLNK) {
7241897Smckusick 		if (np->n_flag & NMODIFIED) {
7341897Smckusick 			np->n_flag &= ~NMODIFIED;
7441897Smckusick 			vinvalbuf(vp, TRUE);
7541897Smckusick 			np->n_attrstamp = 0;
7641897Smckusick 			np->n_direofoffset = 0;
7743348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
7839750Smckusick 				return (error);
7939750Smckusick 			np->n_mtime = vattr.va_mtime.tv_sec;
8041897Smckusick 		} else {
8143348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
8241897Smckusick 				return (error);
8341897Smckusick 			if (np->n_mtime != vattr.va_mtime.tv_sec) {
8441897Smckusick 				np->n_direofoffset = 0;
8541897Smckusick 				vinvalbuf(vp, TRUE);
8641897Smckusick 				np->n_mtime = vattr.va_mtime.tv_sec;
8741897Smckusick 			}
8839750Smckusick 		}
8938882Smacklem 	}
9038882Smacklem 	do {
9141897Smckusick 	    switch (vp->v_type) {
9241897Smckusick 	    case VREG:
9339750Smckusick 		nfsstats.biocache_reads++;
9443348Smckusick 		lbn = uio->uio_offset / biosize;
9543348Smckusick 		on = uio->uio_offset & (biosize-1);
9643348Smckusick 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
9738882Smacklem 		diff = np->n_size - uio->uio_offset;
9838882Smacklem 		if (diff <= 0)
9939584Smckusick 			return (error);
10038882Smacklem 		if (diff < n)
10138882Smacklem 			n = diff;
10243348Smckusick 		bn = lbn*(biosize/DEV_BSIZE);
10343348Smckusick 		rablock = (lbn+1)*(biosize/DEV_BSIZE);
10439901Smckusick 		if (vp->v_lastr + 1 == lbn &&
10539901Smckusick 		    np->n_size > (rablock * DEV_BSIZE))
10643348Smckusick 			error = breada(vp, bn, biosize, rablock, biosize,
10738882Smacklem 				cred, &bp);
10838882Smacklem 		else
10943348Smckusick 			error = bread(vp, bn, biosize, cred, &bp);
11039901Smckusick 		vp->v_lastr = lbn;
11138882Smacklem 		if (bp->b_resid) {
11243348Smckusick 		   diff = (on >= (biosize-bp->b_resid)) ? 0 :
11343348Smckusick 			(biosize-bp->b_resid-on);
11441897Smckusick 		   n = MIN(n, diff);
11538882Smacklem 		}
11641897Smckusick 		break;
11741897Smckusick 	    case VLNK:
11841897Smckusick 		nfsstats.biocache_readlinks++;
11941897Smckusick 		on = 0;
12041897Smckusick 		error = bread(vp, (daddr_t)0, NFS_MAXPATHLEN, cred, &bp);
12141897Smckusick 		n = MIN(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
12241897Smckusick 		break;
12341897Smckusick 	    case VDIR:
12441897Smckusick 		nfsstats.biocache_readdirs++;
12541897Smckusick 		on = 0;
12641897Smckusick 		error = bread(vp, uio->uio_offset, DIRBLKSIZ, cred, &bp);
12741897Smckusick 		n = MIN(uio->uio_resid, DIRBLKSIZ - bp->b_resid);
12841897Smckusick 		break;
12941897Smckusick 	    };
13041897Smckusick 	    if (error) {
13141897Smckusick 		brelse(bp);
13241897Smckusick 		return (error);
13341897Smckusick 	    }
13441897Smckusick 	    if (n > 0)
13541897Smckusick 		error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
13641897Smckusick 	    switch (vp->v_type) {
13741897Smckusick 	    case VREG:
13843348Smckusick 		if (n+on == biosize || uio->uio_offset == np->n_size)
13938882Smacklem 			bp->b_flags |= B_AGE;
14041897Smckusick 		break;
14141897Smckusick 	    case VLNK:
14241897Smckusick 		n = 0;
14341897Smckusick 		break;
14441897Smckusick 	    case VDIR:
14541897Smckusick 		uio->uio_offset = bp->b_blkno;
14641897Smckusick 		break;
14741897Smckusick 	    };
14841897Smckusick 	    brelse(bp);
14938882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
15038882Smacklem 	return (error);
15138882Smacklem }
15238882Smacklem 
15338882Smacklem /*
15438882Smacklem  * Vnode op for write using bio
15538882Smacklem  */
15639584Smckusick nfs_write(vp, uio, ioflag, cred)
15738882Smacklem 	register struct vnode *vp;
15838882Smacklem 	register struct uio *uio;
15938882Smacklem 	int ioflag;
16038882Smacklem 	struct ucred *cred;
16138882Smacklem {
162*47572Skarels 	struct proc *p = curproc;		/* XXX */
16343348Smckusick 	register int biosize;
16438882Smacklem 	struct buf *bp;
16538882Smacklem 	struct nfsnode *np = VTONFS(vp);
16641897Smckusick 	struct vattr vattr;
16738882Smacklem 	daddr_t lbn, bn;
16840220Smckusick 	int n, on, error = 0;
16938882Smacklem 
17041897Smckusick 	if (uio->uio_rw != UIO_WRITE)
17141897Smckusick 		panic("nfs_write mode");
17241897Smckusick 	if (vp->v_type != VREG)
17341897Smckusick 		return (EIO);
17438882Smacklem 	/* Should we try and do this ?? */
17543348Smckusick 	if (ioflag & (IO_APPEND | IO_SYNC)) {
17641897Smckusick 		if (np->n_flag & NMODIFIED) {
17741897Smckusick 			np->n_flag &= ~NMODIFIED;
17841897Smckusick 			vinvalbuf(vp, TRUE);
17941897Smckusick 		}
18043348Smckusick 		if (ioflag & IO_APPEND) {
18143348Smckusick 			np->n_attrstamp = 0;
18243348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
18343348Smckusick 				return (error);
18443348Smckusick 			uio->uio_offset = np->n_size;
18543348Smckusick 		}
186*47572Skarels 		return (nfs_writerpc(vp, uio, cred, p));
18741897Smckusick 	}
18839584Smckusick #ifdef notdef
18938882Smacklem 	cnt = uio->uio_resid;
19038882Smacklem 	osize = np->n_size;
19138882Smacklem #endif
19239584Smckusick 	if (uio->uio_offset < 0)
19339584Smckusick 		return (EINVAL);
19438882Smacklem 	if (uio->uio_resid == 0)
19539584Smckusick 		return (0);
19638882Smacklem 	/*
19738882Smacklem 	 * Maybe this should be above the vnode op call, but so long as
19838882Smacklem 	 * file servers have no limits, i don't think it matters
19938882Smacklem 	 */
20041897Smckusick 	if (uio->uio_offset + uio->uio_resid >
201*47572Skarels 	      p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
202*47572Skarels 		psignal(p, SIGXFSZ);
20339584Smckusick 		return (EFBIG);
20438882Smacklem 	}
20543348Smckusick 	/*
20643348Smckusick 	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
20743348Smckusick 	 * will be the same size within a filesystem. nfs_writerpc will
20843348Smckusick 	 * still use nm_wsize when sizing the rpc's.
20943348Smckusick 	 */
21043348Smckusick 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
21141897Smckusick 	np->n_flag |= NMODIFIED;
21238882Smacklem 	do {
21339750Smckusick 		nfsstats.biocache_writes++;
21443348Smckusick 		lbn = uio->uio_offset / biosize;
21543348Smckusick 		on = uio->uio_offset & (biosize-1);
21643348Smckusick 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
21745714Smckusick 		if (uio->uio_offset+n > np->n_size) {
21838882Smacklem 			np->n_size = uio->uio_offset+n;
21945714Smckusick 			vnode_pager_setsize(vp, np->n_size);
22045714Smckusick 		}
22143348Smckusick 		bn = lbn*(biosize/DEV_BSIZE);
22240037Smckusick again:
22343348Smckusick 		bp = getblk(vp, bn, biosize);
22438882Smacklem 		if (bp->b_wcred == NOCRED) {
22538882Smacklem 			crhold(cred);
22638882Smacklem 			bp->b_wcred = cred;
22738882Smacklem 		}
22838882Smacklem 		if (bp->b_dirtyend > 0) {
22938882Smacklem 			/*
23040037Smckusick 			 * If the new write will leave a contiguous dirty
23140037Smckusick 			 * area, just update the b_dirtyoff and b_dirtyend,
23240037Smckusick 			 * otherwise force a write rpc of the old dirty area.
23338882Smacklem 			 */
23438882Smacklem 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
23538882Smacklem 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
23638882Smacklem 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
23738882Smacklem 			} else {
238*47572Skarels 				bp->b_proc = p;
23940037Smckusick 				if (error = bwrite(bp))
24039584Smckusick 					return (error);
24140037Smckusick 				goto again;
24238882Smacklem 			}
24338882Smacklem 		} else {
24438882Smacklem 			bp->b_dirtyoff = on;
24538882Smacklem 			bp->b_dirtyend = on+n;
24638882Smacklem 		}
24740037Smckusick 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
24840037Smckusick 			brelse(bp);
24939584Smckusick 			return (error);
25040037Smckusick 		}
25143348Smckusick 		if ((n+on) == biosize) {
25238882Smacklem 			bp->b_flags |= B_AGE;
25341897Smckusick 			bp->b_proc = (struct proc *)0;
25438882Smacklem 			bawrite(bp);
25538882Smacklem 		} else {
25641897Smckusick 			bp->b_proc = (struct proc *)0;
25738882Smacklem 			bdwrite(bp);
25838882Smacklem 		}
25938882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
26038882Smacklem #ifdef notdef
26138882Smacklem 	/* Should we try and do this for nfs ?? */
26239584Smckusick 	if (error && (ioflag & IO_UNIT)) {
26338882Smacklem 		np->n_size = osize;
26439584Smckusick 		uio->uio_offset -= cnt - uio->uio_resid;
26539584Smckusick 		uio->uio_resid = cnt;
26639584Smckusick 	}
26738882Smacklem #endif
26838882Smacklem 	return (error);
26938882Smacklem }
270