xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 45714)
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*45714Smckusick  *	@(#)nfs_bio.c	7.17 (Berkeley) 12/05/90
1138882Smacklem  */
1238882Smacklem 
1338882Smacklem #include "param.h"
1438882Smacklem #include "user.h"
1538882Smacklem #include "buf.h"
1638882Smacklem #include "vnode.h"
1738882Smacklem #include "trace.h"
1838882Smacklem #include "mount.h"
1938882Smacklem #include "nfsnode.h"
2039750Smckusick #include "nfsv2.h"
2139750Smckusick #include "nfs.h"
2238882Smacklem #include "nfsiom.h"
2341897Smckusick #include "nfsmount.h"
2438882Smacklem 
2538882Smacklem /* True and false, how exciting */
2638882Smacklem #define	TRUE	1
2738882Smacklem #define	FALSE	0
2838882Smacklem 
2938882Smacklem /*
3038882Smacklem  * Vnode op for read using bio
3138882Smacklem  * Any similarity to readip() is purely coincidental
3238882Smacklem  */
3341897Smckusick nfs_bioread(vp, uio, ioflag, cred)
3438882Smacklem 	register struct vnode *vp;
3543348Smckusick 	register struct uio *uio;
3638882Smacklem 	int ioflag;
3738882Smacklem 	struct ucred *cred;
3838882Smacklem {
3938882Smacklem 	register struct nfsnode *np = VTONFS(vp);
4043348Smckusick 	register int biosize;
4138882Smacklem 	struct buf *bp;
4238882Smacklem 	struct vattr vattr;
4338882Smacklem 	daddr_t lbn, bn, rablock;
4439487Smckusick 	int diff, error = 0;
4538882Smacklem 	long n, on;
4638882Smacklem 
4742241Smckusick #ifdef lint
4842241Smckusick 	ioflag = ioflag;
4942241Smckusick #endif /* lint */
5038882Smacklem 	if (uio->uio_rw != UIO_READ)
5138882Smacklem 		panic("nfs_read mode");
5238882Smacklem 	if (uio->uio_resid == 0)
5339584Smckusick 		return (0);
5441897Smckusick 	if (uio->uio_offset < 0 && vp->v_type != VDIR)
5539584Smckusick 		return (EINVAL);
5643348Smckusick 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
5738882Smacklem 	/*
5838882Smacklem 	 * If the file's modify time on the server has changed since the
5938882Smacklem 	 * last read rpc or you have written to the file,
6038882Smacklem 	 * you may have lost data cache consistency with the
6138882Smacklem 	 * server, so flush all of the file's data out of the cache.
6241897Smckusick 	 * Then force a getattr rpc to ensure that you have up to date
6341897Smckusick 	 * attributes.
6438882Smacklem 	 * NB: This implies that cache data can be read when up to
6538882Smacklem 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
6638882Smacklem 	 * attributes this could be forced by setting n_attrstamp to 0 before
6743348Smckusick 	 * the nfs_dogetattr() call.
6838882Smacklem 	 */
6941897Smckusick 	if (vp->v_type != VLNK) {
7041897Smckusick 		if (np->n_flag & NMODIFIED) {
7141897Smckusick 			np->n_flag &= ~NMODIFIED;
7241897Smckusick 			vinvalbuf(vp, TRUE);
7341897Smckusick 			np->n_attrstamp = 0;
7441897Smckusick 			np->n_direofoffset = 0;
7543348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
7639750Smckusick 				return (error);
7739750Smckusick 			np->n_mtime = vattr.va_mtime.tv_sec;
7841897Smckusick 		} else {
7943348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
8041897Smckusick 				return (error);
8141897Smckusick 			if (np->n_mtime != vattr.va_mtime.tv_sec) {
8241897Smckusick 				np->n_direofoffset = 0;
8341897Smckusick 				vinvalbuf(vp, TRUE);
8441897Smckusick 				np->n_mtime = vattr.va_mtime.tv_sec;
8541897Smckusick 			}
8639750Smckusick 		}
8738882Smacklem 	}
8838882Smacklem 	do {
8941897Smckusick 	    switch (vp->v_type) {
9041897Smckusick 	    case VREG:
9139750Smckusick 		nfsstats.biocache_reads++;
9243348Smckusick 		lbn = uio->uio_offset / biosize;
9343348Smckusick 		on = uio->uio_offset & (biosize-1);
9443348Smckusick 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
9538882Smacklem 		diff = np->n_size - uio->uio_offset;
9638882Smacklem 		if (diff <= 0)
9739584Smckusick 			return (error);
9838882Smacklem 		if (diff < n)
9938882Smacklem 			n = diff;
10043348Smckusick 		bn = lbn*(biosize/DEV_BSIZE);
10143348Smckusick 		rablock = (lbn+1)*(biosize/DEV_BSIZE);
10239901Smckusick 		if (vp->v_lastr + 1 == lbn &&
10339901Smckusick 		    np->n_size > (rablock * DEV_BSIZE))
10443348Smckusick 			error = breada(vp, bn, biosize, rablock, biosize,
10538882Smacklem 				cred, &bp);
10638882Smacklem 		else
10743348Smckusick 			error = bread(vp, bn, biosize, cred, &bp);
10839901Smckusick 		vp->v_lastr = lbn;
10938882Smacklem 		if (bp->b_resid) {
11043348Smckusick 		   diff = (on >= (biosize-bp->b_resid)) ? 0 :
11143348Smckusick 			(biosize-bp->b_resid-on);
11241897Smckusick 		   n = MIN(n, diff);
11338882Smacklem 		}
11441897Smckusick 		break;
11541897Smckusick 	    case VLNK:
11641897Smckusick 		nfsstats.biocache_readlinks++;
11741897Smckusick 		on = 0;
11841897Smckusick 		error = bread(vp, (daddr_t)0, NFS_MAXPATHLEN, cred, &bp);
11941897Smckusick 		n = MIN(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
12041897Smckusick 		break;
12141897Smckusick 	    case VDIR:
12241897Smckusick 		nfsstats.biocache_readdirs++;
12341897Smckusick 		on = 0;
12441897Smckusick 		error = bread(vp, uio->uio_offset, DIRBLKSIZ, cred, &bp);
12541897Smckusick 		n = MIN(uio->uio_resid, DIRBLKSIZ - bp->b_resid);
12641897Smckusick 		break;
12741897Smckusick 	    };
12841897Smckusick 	    if (error) {
12941897Smckusick 		brelse(bp);
13041897Smckusick 		return (error);
13141897Smckusick 	    }
13241897Smckusick 	    if (n > 0)
13341897Smckusick 		error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
13441897Smckusick 	    switch (vp->v_type) {
13541897Smckusick 	    case VREG:
13643348Smckusick 		if (n+on == biosize || uio->uio_offset == np->n_size)
13738882Smacklem 			bp->b_flags |= B_AGE;
13841897Smckusick 		break;
13941897Smckusick 	    case VLNK:
14041897Smckusick 		n = 0;
14141897Smckusick 		break;
14241897Smckusick 	    case VDIR:
14341897Smckusick 		uio->uio_offset = bp->b_blkno;
14441897Smckusick 		break;
14541897Smckusick 	    };
14641897Smckusick 	    brelse(bp);
14738882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
14838882Smacklem 	return (error);
14938882Smacklem }
15038882Smacklem 
15138882Smacklem /*
15238882Smacklem  * Vnode op for write using bio
15338882Smacklem  */
15439584Smckusick nfs_write(vp, uio, ioflag, cred)
15538882Smacklem 	register struct vnode *vp;
15638882Smacklem 	register struct uio *uio;
15738882Smacklem 	int ioflag;
15838882Smacklem 	struct ucred *cred;
15938882Smacklem {
16043348Smckusick 	register int biosize;
16138882Smacklem 	struct buf *bp;
16238882Smacklem 	struct nfsnode *np = VTONFS(vp);
16341897Smckusick 	struct vattr vattr;
16438882Smacklem 	daddr_t lbn, bn;
16540220Smckusick 	int n, on, error = 0;
16638882Smacklem 
16741897Smckusick 	if (uio->uio_rw != UIO_WRITE)
16841897Smckusick 		panic("nfs_write mode");
16941897Smckusick 	if (vp->v_type != VREG)
17041897Smckusick 		return (EIO);
17138882Smacklem 	/* Should we try and do this ?? */
17243348Smckusick 	if (ioflag & (IO_APPEND | IO_SYNC)) {
17341897Smckusick 		if (np->n_flag & NMODIFIED) {
17441897Smckusick 			np->n_flag &= ~NMODIFIED;
17541897Smckusick 			vinvalbuf(vp, TRUE);
17641897Smckusick 		}
17743348Smckusick 		if (ioflag & IO_APPEND) {
17843348Smckusick 			np->n_attrstamp = 0;
17943348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
18043348Smckusick 				return (error);
18143348Smckusick 			uio->uio_offset = np->n_size;
18243348Smckusick 		}
18341897Smckusick 		return (nfs_writerpc(vp, uio, cred, u.u_procp));
18441897Smckusick 	}
18539584Smckusick #ifdef notdef
18638882Smacklem 	cnt = uio->uio_resid;
18738882Smacklem 	osize = np->n_size;
18838882Smacklem #endif
18939584Smckusick 	if (uio->uio_offset < 0)
19039584Smckusick 		return (EINVAL);
19138882Smacklem 	if (uio->uio_resid == 0)
19239584Smckusick 		return (0);
19338882Smacklem 	/*
19438882Smacklem 	 * Maybe this should be above the vnode op call, but so long as
19538882Smacklem 	 * file servers have no limits, i don't think it matters
19638882Smacklem 	 */
19741897Smckusick 	if (uio->uio_offset + uio->uio_resid >
19838882Smacklem 	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
19938882Smacklem 		psignal(u.u_procp, SIGXFSZ);
20039584Smckusick 		return (EFBIG);
20138882Smacklem 	}
20243348Smckusick 	/*
20343348Smckusick 	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
20443348Smckusick 	 * will be the same size within a filesystem. nfs_writerpc will
20543348Smckusick 	 * still use nm_wsize when sizing the rpc's.
20643348Smckusick 	 */
20743348Smckusick 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
20841897Smckusick 	np->n_flag |= NMODIFIED;
20938882Smacklem 	do {
21039750Smckusick 		nfsstats.biocache_writes++;
21143348Smckusick 		lbn = uio->uio_offset / biosize;
21243348Smckusick 		on = uio->uio_offset & (biosize-1);
21343348Smckusick 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
214*45714Smckusick 		if (uio->uio_offset+n > np->n_size) {
21538882Smacklem 			np->n_size = uio->uio_offset+n;
216*45714Smckusick 			vnode_pager_setsize(vp, np->n_size);
217*45714Smckusick 		}
21843348Smckusick 		bn = lbn*(biosize/DEV_BSIZE);
21940037Smckusick again:
22043348Smckusick 		bp = getblk(vp, bn, biosize);
22138882Smacklem 		if (bp->b_wcred == NOCRED) {
22238882Smacklem 			crhold(cred);
22338882Smacklem 			bp->b_wcred = cred;
22438882Smacklem 		}
22538882Smacklem 		if (bp->b_dirtyend > 0) {
22638882Smacklem 			/*
22740037Smckusick 			 * If the new write will leave a contiguous dirty
22840037Smckusick 			 * area, just update the b_dirtyoff and b_dirtyend,
22940037Smckusick 			 * otherwise force a write rpc of the old dirty area.
23038882Smacklem 			 */
23138882Smacklem 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
23238882Smacklem 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
23338882Smacklem 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
23438882Smacklem 			} else {
23541897Smckusick 				bp->b_proc = u.u_procp;
23640037Smckusick 				if (error = bwrite(bp))
23739584Smckusick 					return (error);
23840037Smckusick 				goto again;
23938882Smacklem 			}
24038882Smacklem 		} else {
24138882Smacklem 			bp->b_dirtyoff = on;
24238882Smacklem 			bp->b_dirtyend = on+n;
24338882Smacklem 		}
24440037Smckusick 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
24540037Smckusick 			brelse(bp);
24639584Smckusick 			return (error);
24740037Smckusick 		}
24843348Smckusick 		if ((n+on) == biosize) {
24938882Smacklem 			bp->b_flags |= B_AGE;
25041897Smckusick 			bp->b_proc = (struct proc *)0;
25138882Smacklem 			bawrite(bp);
25238882Smacklem 		} else {
25341897Smckusick 			bp->b_proc = (struct proc *)0;
25438882Smacklem 			bdwrite(bp);
25538882Smacklem 		}
25638882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
25738882Smacklem #ifdef notdef
25838882Smacklem 	/* Should we try and do this for nfs ?? */
25939584Smckusick 	if (error && (ioflag & IO_UNIT)) {
26038882Smacklem 		np->n_size = osize;
26139584Smckusick 		uio->uio_offset -= cnt - uio->uio_resid;
26239584Smckusick 		uio->uio_resid = cnt;
26339584Smckusick 	}
26438882Smacklem #endif
26538882Smacklem 	return (error);
26638882Smacklem }
267