xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 42241)
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  *
838882Smacklem  * Redistribution and use in source and binary forms are permitted
938882Smacklem  * provided that the above copyright notice and this paragraph are
1038882Smacklem  * duplicated in all such forms and that any documentation,
1138882Smacklem  * advertising materials, and other materials related to such
1238882Smacklem  * distribution and use acknowledge that the software was developed
1338882Smacklem  * by the University of California, Berkeley.  The name of the
1438882Smacklem  * University may not be used to endorse or promote products derived
1538882Smacklem  * from this software without specific prior written permission.
1638882Smacklem  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1738882Smacklem  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1838882Smacklem  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1938882Smacklem  *
20*42241Smckusick  *	@(#)nfs_bio.c	7.14 (Berkeley) 05/18/90
2138882Smacklem  */
2238882Smacklem 
2338882Smacklem #include "param.h"
2438882Smacklem #include "user.h"
2538882Smacklem #include "buf.h"
2638882Smacklem #include "vnode.h"
2738882Smacklem #include "trace.h"
2838882Smacklem #include "mount.h"
2938882Smacklem #include "nfsnode.h"
3039750Smckusick #include "nfsv2.h"
3139750Smckusick #include "nfs.h"
3238882Smacklem #include "nfsiom.h"
3341897Smckusick #include "nfsmount.h"
3438882Smacklem 
3538882Smacklem /* True and false, how exciting */
3638882Smacklem #define	TRUE	1
3738882Smacklem #define	FALSE	0
3838882Smacklem 
3938882Smacklem /*
4038882Smacklem  * Vnode op for read using bio
4138882Smacklem  * Any similarity to readip() is purely coincidental
4238882Smacklem  */
4341897Smckusick nfs_bioread(vp, uio, ioflag, cred)
4438882Smacklem 	register struct vnode *vp;
4538882Smacklem 	struct uio *uio;
4638882Smacklem 	int ioflag;
4738882Smacklem 	struct ucred *cred;
4838882Smacklem {
4938882Smacklem 	register struct nfsnode *np = VTONFS(vp);
5038882Smacklem 	struct buf *bp;
5138882Smacklem 	struct vattr vattr;
5238882Smacklem 	daddr_t lbn, bn, rablock;
5339487Smckusick 	int diff, error = 0;
5438882Smacklem 	long n, on;
5538882Smacklem 
56*42241Smckusick #ifdef lint
57*42241Smckusick 	ioflag = ioflag;
58*42241Smckusick #endif /* lint */
5938882Smacklem 	if (uio->uio_rw != UIO_READ)
6038882Smacklem 		panic("nfs_read mode");
6138882Smacklem 	if (uio->uio_resid == 0)
6239584Smckusick 		return (0);
6341897Smckusick 	if (uio->uio_offset < 0 && vp->v_type != VDIR)
6439584Smckusick 		return (EINVAL);
6538882Smacklem 	/*
6638882Smacklem 	 * If the file's modify time on the server has changed since the
6738882Smacklem 	 * last read rpc or you have written to the file,
6838882Smacklem 	 * you may have lost data cache consistency with the
6938882Smacklem 	 * server, so flush all of the file's data out of the cache.
7041897Smckusick 	 * Then force a getattr rpc to ensure that you have up to date
7141897Smckusick 	 * attributes.
7238882Smacklem 	 * NB: This implies that cache data can be read when up to
7338882Smacklem 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
7438882Smacklem 	 * attributes this could be forced by setting n_attrstamp to 0 before
7538882Smacklem 	 * the nfs_getattr() call.
7638882Smacklem 	 */
7741897Smckusick 	if (vp->v_type != VLNK) {
7841897Smckusick 		if (np->n_flag & NMODIFIED) {
7941897Smckusick 			np->n_flag &= ~NMODIFIED;
8041897Smckusick 			vinvalbuf(vp, TRUE);
8141897Smckusick 			np->n_attrstamp = 0;
8241897Smckusick 			np->n_direofoffset = 0;
8339750Smckusick 			if (error = nfs_getattr(vp, &vattr, cred))
8439750Smckusick 				return (error);
8539750Smckusick 			np->n_mtime = vattr.va_mtime.tv_sec;
8641897Smckusick 		} else {
8741897Smckusick 			if (error = nfs_getattr(vp, &vattr, cred))
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++;
10038882Smacklem 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
10138882Smacklem 		on = uio->uio_offset & (NFS_BIOSIZE-1);
10238882Smacklem 		n = MIN((unsigned)(NFS_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;
10838882Smacklem 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
10938882Smacklem 		rablock = (lbn+1)*(NFS_BIOSIZE/DEV_BSIZE);
11039901Smckusick 		if (vp->v_lastr + 1 == lbn &&
11139901Smckusick 		    np->n_size > (rablock * DEV_BSIZE))
11238882Smacklem 			error = breada(vp, bn, NFS_BIOSIZE, rablock, NFS_BIOSIZE,
11338882Smacklem 				cred, &bp);
11438882Smacklem 		else
11538882Smacklem 			error = bread(vp, bn, NFS_BIOSIZE, cred, &bp);
11639901Smckusick 		vp->v_lastr = lbn;
11738882Smacklem 		if (bp->b_resid) {
11841897Smckusick 		   diff = (on >= (NFS_BIOSIZE-bp->b_resid)) ? 0 :
11941897Smckusick 			(NFS_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;
13241897Smckusick 		error = bread(vp, uio->uio_offset, DIRBLKSIZ, cred, &bp);
13341897Smckusick 		n = MIN(uio->uio_resid, 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:
14438882Smacklem 		if (n+on == NFS_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 {
16838882Smacklem 	struct buf *bp;
16938882Smacklem 	struct nfsnode *np = VTONFS(vp);
17041897Smckusick 	struct vattr vattr;
17138882Smacklem 	daddr_t lbn, bn;
17240220Smckusick 	int n, on, error = 0;
17338882Smacklem 
17441897Smckusick 	if (uio->uio_rw != UIO_WRITE)
17541897Smckusick 		panic("nfs_write mode");
17641897Smckusick 	if (vp->v_type != VREG)
17741897Smckusick 		return (EIO);
17838882Smacklem 	/* Should we try and do this ?? */
17941897Smckusick 	if (ioflag & IO_APPEND) {
18041897Smckusick 		if (np->n_flag & NMODIFIED) {
18141897Smckusick 			np->n_flag &= ~NMODIFIED;
18241897Smckusick 			vinvalbuf(vp, TRUE);
18341897Smckusick 		}
18441897Smckusick 		np->n_attrstamp = 0;
18541897Smckusick 		if (error = nfs_getattr(vp, &vattr, cred))
18641897Smckusick 			return (error);
18739584Smckusick 		uio->uio_offset = np->n_size;
18841897Smckusick 		return (nfs_writerpc(vp, uio, cred, u.u_procp));
18941897Smckusick 	}
19039584Smckusick #ifdef notdef
19138882Smacklem 	cnt = uio->uio_resid;
19238882Smacklem 	osize = np->n_size;
19338882Smacklem #endif
19439584Smckusick 	if (uio->uio_offset < 0)
19539584Smckusick 		return (EINVAL);
19638882Smacklem 	if (uio->uio_resid == 0)
19739584Smckusick 		return (0);
19838882Smacklem 	/*
19938882Smacklem 	 * Maybe this should be above the vnode op call, but so long as
20038882Smacklem 	 * file servers have no limits, i don't think it matters
20138882Smacklem 	 */
20241897Smckusick 	if (uio->uio_offset + uio->uio_resid >
20338882Smacklem 	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
20438882Smacklem 		psignal(u.u_procp, SIGXFSZ);
20539584Smckusick 		return (EFBIG);
20638882Smacklem 	}
20741897Smckusick 	np->n_flag |= NMODIFIED;
20838882Smacklem 	do {
20939750Smckusick 		nfsstats.biocache_writes++;
21038882Smacklem 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
21138882Smacklem 		on = uio->uio_offset & (NFS_BIOSIZE-1);
21238882Smacklem 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
21338882Smacklem 		if (uio->uio_offset+n > np->n_size)
21438882Smacklem 			np->n_size = uio->uio_offset+n;
21538882Smacklem 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
21640037Smckusick again:
21738882Smacklem 		bp = getblk(vp, bn, NFS_BIOSIZE);
21838882Smacklem 		if (bp->b_wcred == NOCRED) {
21938882Smacklem 			crhold(cred);
22038882Smacklem 			bp->b_wcred = cred;
22138882Smacklem 		}
22238882Smacklem 		if (bp->b_dirtyend > 0) {
22338882Smacklem 			/*
22440037Smckusick 			 * If the new write will leave a contiguous dirty
22540037Smckusick 			 * area, just update the b_dirtyoff and b_dirtyend,
22640037Smckusick 			 * otherwise force a write rpc of the old dirty area.
22738882Smacklem 			 */
22838882Smacklem 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
22938882Smacklem 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
23038882Smacklem 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
23138882Smacklem 			} else {
23241897Smckusick 				bp->b_proc = u.u_procp;
23340037Smckusick 				if (error = bwrite(bp))
23439584Smckusick 					return (error);
23540037Smckusick 				goto again;
23638882Smacklem 			}
23738882Smacklem 		} else {
23838882Smacklem 			bp->b_dirtyoff = on;
23938882Smacklem 			bp->b_dirtyend = on+n;
24038882Smacklem 		}
24140037Smckusick 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
24240037Smckusick 			brelse(bp);
24339584Smckusick 			return (error);
24440037Smckusick 		}
24538882Smacklem 		if ((n+on) == NFS_BIOSIZE) {
24638882Smacklem 			bp->b_flags |= B_AGE;
24741897Smckusick 			bp->b_proc = (struct proc *)0;
24838882Smacklem 			bawrite(bp);
24938882Smacklem 		} else {
25041897Smckusick 			bp->b_proc = (struct proc *)0;
25138882Smacklem 			bdwrite(bp);
25238882Smacklem 		}
25338882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
25438882Smacklem #ifdef notdef
25538882Smacklem 	/* Should we try and do this for nfs ?? */
25639584Smckusick 	if (error && (ioflag & IO_UNIT)) {
25738882Smacklem 		np->n_size = osize;
25839584Smckusick 		uio->uio_offset -= cnt - uio->uio_resid;
25939584Smckusick 		uio->uio_resid = cnt;
26039584Smckusick 	}
26138882Smacklem #endif
26238882Smacklem 	return (error);
26338882Smacklem }
264