xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 48047)
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*48047Smckusick  *	@(#)nfs_bio.c	7.19 (Berkeley) 04/16/91
1138882Smacklem  */
1238882Smacklem 
1338882Smacklem #include "param.h"
1447572Skarels #include "proc.h"
1538882Smacklem #include "buf.h"
16*48047Smckusick #include "uio.h"
17*48047Smckusick #include "namei.h"
1838882Smacklem #include "vnode.h"
1938882Smacklem #include "trace.h"
2038882Smacklem #include "mount.h"
2147572Skarels #include "resourcevar.h"
2247572Skarels 
2338882Smacklem #include "nfsnode.h"
2439750Smckusick #include "nfsv2.h"
2539750Smckusick #include "nfs.h"
2638882Smacklem #include "nfsiom.h"
2741897Smckusick #include "nfsmount.h"
2838882Smacklem 
2938882Smacklem /* True and false, how exciting */
3038882Smacklem #define	TRUE	1
3138882Smacklem #define	FALSE	0
3238882Smacklem 
3338882Smacklem /*
3438882Smacklem  * Vnode op for read using bio
3538882Smacklem  * Any similarity to readip() is purely coincidental
3638882Smacklem  */
3741897Smckusick nfs_bioread(vp, uio, ioflag, cred)
3838882Smacklem 	register struct vnode *vp;
3943348Smckusick 	register struct uio *uio;
4038882Smacklem 	int ioflag;
4138882Smacklem 	struct ucred *cred;
4238882Smacklem {
4338882Smacklem 	register struct nfsnode *np = VTONFS(vp);
4443348Smckusick 	register int biosize;
4538882Smacklem 	struct buf *bp;
4638882Smacklem 	struct vattr vattr;
4738882Smacklem 	daddr_t lbn, bn, rablock;
4839487Smckusick 	int diff, error = 0;
4938882Smacklem 	long n, on;
5038882Smacklem 
5142241Smckusick #ifdef lint
5242241Smckusick 	ioflag = ioflag;
5342241Smckusick #endif /* lint */
54*48047Smckusick #ifdef DIAGNOSTIC
5538882Smacklem 	if (uio->uio_rw != UIO_READ)
5638882Smacklem 		panic("nfs_read mode");
57*48047Smckusick #endif
5838882Smacklem 	if (uio->uio_resid == 0)
5939584Smckusick 		return (0);
6041897Smckusick 	if (uio->uio_offset < 0 && vp->v_type != VDIR)
6139584Smckusick 		return (EINVAL);
6243348Smckusick 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
6338882Smacklem 	/*
6438882Smacklem 	 * If the file's modify time on the server has changed since the
6538882Smacklem 	 * last read rpc or you have written to the file,
6638882Smacklem 	 * you may have lost data cache consistency with the
6738882Smacklem 	 * server, so flush all of the file's data out of the cache.
6841897Smckusick 	 * Then force a getattr rpc to ensure that you have up to date
6941897Smckusick 	 * attributes.
7038882Smacklem 	 * NB: This implies that cache data can be read when up to
7138882Smacklem 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
7238882Smacklem 	 * attributes this could be forced by setting n_attrstamp to 0 before
7343348Smckusick 	 * the nfs_dogetattr() call.
7438882Smacklem 	 */
7541897Smckusick 	if (vp->v_type != VLNK) {
7641897Smckusick 		if (np->n_flag & NMODIFIED) {
7741897Smckusick 			np->n_flag &= ~NMODIFIED;
7841897Smckusick 			vinvalbuf(vp, TRUE);
7941897Smckusick 			np->n_attrstamp = 0;
8041897Smckusick 			np->n_direofoffset = 0;
81*48047Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1,
82*48047Smckusick 			    uio->uio_procp))
8339750Smckusick 				return (error);
8439750Smckusick 			np->n_mtime = vattr.va_mtime.tv_sec;
8541897Smckusick 		} else {
86*48047Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1,
87*48047Smckusick 			    uio->uio_procp))
8841897Smckusick 				return (error);
8941897Smckusick 			if (np->n_mtime != vattr.va_mtime.tv_sec) {
9041897Smckusick 				np->n_direofoffset = 0;
9141897Smckusick 				vinvalbuf(vp, TRUE);
9241897Smckusick 				np->n_mtime = vattr.va_mtime.tv_sec;
9341897Smckusick 			}
9439750Smckusick 		}
9538882Smacklem 	}
9638882Smacklem 	do {
9741897Smckusick 	    switch (vp->v_type) {
9841897Smckusick 	    case VREG:
9939750Smckusick 		nfsstats.biocache_reads++;
10043348Smckusick 		lbn = uio->uio_offset / biosize;
10143348Smckusick 		on = uio->uio_offset & (biosize-1);
10243348Smckusick 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
10338882Smacklem 		diff = np->n_size - uio->uio_offset;
10438882Smacklem 		if (diff <= 0)
10539584Smckusick 			return (error);
10638882Smacklem 		if (diff < n)
10738882Smacklem 			n = diff;
10843348Smckusick 		bn = lbn*(biosize/DEV_BSIZE);
10943348Smckusick 		rablock = (lbn+1)*(biosize/DEV_BSIZE);
11039901Smckusick 		if (vp->v_lastr + 1 == lbn &&
11139901Smckusick 		    np->n_size > (rablock * DEV_BSIZE))
11243348Smckusick 			error = breada(vp, bn, biosize, rablock, biosize,
11338882Smacklem 				cred, &bp);
11438882Smacklem 		else
11543348Smckusick 			error = bread(vp, bn, biosize, cred, &bp);
11639901Smckusick 		vp->v_lastr = lbn;
11738882Smacklem 		if (bp->b_resid) {
11843348Smckusick 		   diff = (on >= (biosize-bp->b_resid)) ? 0 :
11943348Smckusick 			(biosize-bp->b_resid-on);
12041897Smckusick 		   n = MIN(n, diff);
12138882Smacklem 		}
12241897Smckusick 		break;
12341897Smckusick 	    case VLNK:
12441897Smckusick 		nfsstats.biocache_readlinks++;
12541897Smckusick 		on = 0;
12641897Smckusick 		error = bread(vp, (daddr_t)0, NFS_MAXPATHLEN, cred, &bp);
12741897Smckusick 		n = MIN(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
12841897Smckusick 		break;
12941897Smckusick 	    case VDIR:
13041897Smckusick 		nfsstats.biocache_readdirs++;
13141897Smckusick 		on = 0;
132*48047Smckusick 		error = bread(vp, uio->uio_offset, NFS_DIRBLKSIZ, cred, &bp);
133*48047Smckusick 		n = MIN(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid);
13441897Smckusick 		break;
13541897Smckusick 	    };
13641897Smckusick 	    if (error) {
13741897Smckusick 		brelse(bp);
13841897Smckusick 		return (error);
13941897Smckusick 	    }
14041897Smckusick 	    if (n > 0)
14141897Smckusick 		error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
14241897Smckusick 	    switch (vp->v_type) {
14341897Smckusick 	    case VREG:
14443348Smckusick 		if (n+on == biosize || uio->uio_offset == np->n_size)
14538882Smacklem 			bp->b_flags |= B_AGE;
14641897Smckusick 		break;
14741897Smckusick 	    case VLNK:
14841897Smckusick 		n = 0;
14941897Smckusick 		break;
15041897Smckusick 	    case VDIR:
15141897Smckusick 		uio->uio_offset = bp->b_blkno;
15241897Smckusick 		break;
15341897Smckusick 	    };
15441897Smckusick 	    brelse(bp);
15538882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
15638882Smacklem 	return (error);
15738882Smacklem }
15838882Smacklem 
15938882Smacklem /*
16038882Smacklem  * Vnode op for write using bio
16138882Smacklem  */
16239584Smckusick nfs_write(vp, uio, ioflag, cred)
16338882Smacklem 	register struct vnode *vp;
16438882Smacklem 	register struct uio *uio;
16538882Smacklem 	int ioflag;
16638882Smacklem 	struct ucred *cred;
16738882Smacklem {
168*48047Smckusick 	struct proc *p = uio->uio_procp;
16943348Smckusick 	register int biosize;
17038882Smacklem 	struct buf *bp;
17138882Smacklem 	struct nfsnode *np = VTONFS(vp);
17241897Smckusick 	struct vattr vattr;
17338882Smacklem 	daddr_t lbn, bn;
17440220Smckusick 	int n, on, error = 0;
17538882Smacklem 
176*48047Smckusick #ifdef DIAGNOSTIC
17741897Smckusick 	if (uio->uio_rw != UIO_WRITE)
17841897Smckusick 		panic("nfs_write mode");
179*48047Smckusick 	if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
180*48047Smckusick 		panic("nfs_write proc");
181*48047Smckusick #endif
18241897Smckusick 	if (vp->v_type != VREG)
18341897Smckusick 		return (EIO);
18438882Smacklem 	/* Should we try and do this ?? */
18543348Smckusick 	if (ioflag & (IO_APPEND | IO_SYNC)) {
18641897Smckusick 		if (np->n_flag & NMODIFIED) {
18741897Smckusick 			np->n_flag &= ~NMODIFIED;
18841897Smckusick 			vinvalbuf(vp, TRUE);
18941897Smckusick 		}
19043348Smckusick 		if (ioflag & IO_APPEND) {
19143348Smckusick 			np->n_attrstamp = 0;
192*48047Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1, p))
19343348Smckusick 				return (error);
19443348Smckusick 			uio->uio_offset = np->n_size;
19543348Smckusick 		}
196*48047Smckusick 		return (nfs_writerpc(vp, uio, cred));
19741897Smckusick 	}
19839584Smckusick #ifdef notdef
19938882Smacklem 	cnt = uio->uio_resid;
20038882Smacklem 	osize = np->n_size;
20138882Smacklem #endif
20239584Smckusick 	if (uio->uio_offset < 0)
20339584Smckusick 		return (EINVAL);
20438882Smacklem 	if (uio->uio_resid == 0)
20539584Smckusick 		return (0);
20638882Smacklem 	/*
20738882Smacklem 	 * Maybe this should be above the vnode op call, but so long as
20838882Smacklem 	 * file servers have no limits, i don't think it matters
20938882Smacklem 	 */
21041897Smckusick 	if (uio->uio_offset + uio->uio_resid >
21147572Skarels 	      p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
21247572Skarels 		psignal(p, SIGXFSZ);
21339584Smckusick 		return (EFBIG);
21438882Smacklem 	}
21543348Smckusick 	/*
21643348Smckusick 	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
21743348Smckusick 	 * will be the same size within a filesystem. nfs_writerpc will
21843348Smckusick 	 * still use nm_wsize when sizing the rpc's.
21943348Smckusick 	 */
22043348Smckusick 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
22141897Smckusick 	np->n_flag |= NMODIFIED;
22238882Smacklem 	do {
22339750Smckusick 		nfsstats.biocache_writes++;
22443348Smckusick 		lbn = uio->uio_offset / biosize;
22543348Smckusick 		on = uio->uio_offset & (biosize-1);
22643348Smckusick 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
22745714Smckusick 		if (uio->uio_offset+n > np->n_size) {
22838882Smacklem 			np->n_size = uio->uio_offset+n;
22945714Smckusick 			vnode_pager_setsize(vp, np->n_size);
23045714Smckusick 		}
23143348Smckusick 		bn = lbn*(biosize/DEV_BSIZE);
23240037Smckusick again:
23343348Smckusick 		bp = getblk(vp, bn, biosize);
23438882Smacklem 		if (bp->b_wcred == NOCRED) {
23538882Smacklem 			crhold(cred);
23638882Smacklem 			bp->b_wcred = cred;
23738882Smacklem 		}
23838882Smacklem 		if (bp->b_dirtyend > 0) {
23938882Smacklem 			/*
24040037Smckusick 			 * If the new write will leave a contiguous dirty
24140037Smckusick 			 * area, just update the b_dirtyoff and b_dirtyend,
24240037Smckusick 			 * otherwise force a write rpc of the old dirty area.
24338882Smacklem 			 */
24438882Smacklem 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
24538882Smacklem 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
24638882Smacklem 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
24738882Smacklem 			} else {
24847572Skarels 				bp->b_proc = p;
24940037Smckusick 				if (error = bwrite(bp))
25039584Smckusick 					return (error);
25140037Smckusick 				goto again;
25238882Smacklem 			}
25338882Smacklem 		} else {
25438882Smacklem 			bp->b_dirtyoff = on;
25538882Smacklem 			bp->b_dirtyend = on+n;
25638882Smacklem 		}
25740037Smckusick 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
25840037Smckusick 			brelse(bp);
25939584Smckusick 			return (error);
26040037Smckusick 		}
26143348Smckusick 		if ((n+on) == biosize) {
26238882Smacklem 			bp->b_flags |= B_AGE;
26341897Smckusick 			bp->b_proc = (struct proc *)0;
26438882Smacklem 			bawrite(bp);
26538882Smacklem 		} else {
26641897Smckusick 			bp->b_proc = (struct proc *)0;
26738882Smacklem 			bdwrite(bp);
26838882Smacklem 		}
26938882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
27038882Smacklem #ifdef notdef
27138882Smacklem 	/* Should we try and do this for nfs ?? */
27239584Smckusick 	if (error && (ioflag & IO_UNIT)) {
27338882Smacklem 		np->n_size = osize;
27439584Smckusick 		uio->uio_offset -= cnt - uio->uio_resid;
27539584Smckusick 		uio->uio_resid = cnt;
27639584Smckusick 	}
27738882Smacklem #endif
27838882Smacklem 	return (error);
27938882Smacklem }
280