xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 43348)
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*43348Smckusick  *	@(#)nfs_bio.c	7.15 (Berkeley) 06/21/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;
45*43348Smckusick 	register struct uio *uio;
4638882Smacklem 	int ioflag;
4738882Smacklem 	struct ucred *cred;
4838882Smacklem {
4938882Smacklem 	register struct nfsnode *np = VTONFS(vp);
50*43348Smckusick 	register int biosize;
5138882Smacklem 	struct buf *bp;
5238882Smacklem 	struct vattr vattr;
5338882Smacklem 	daddr_t lbn, bn, rablock;
5439487Smckusick 	int diff, error = 0;
5538882Smacklem 	long n, on;
5638882Smacklem 
5742241Smckusick #ifdef lint
5842241Smckusick 	ioflag = ioflag;
5942241Smckusick #endif /* lint */
6038882Smacklem 	if (uio->uio_rw != UIO_READ)
6138882Smacklem 		panic("nfs_read mode");
6238882Smacklem 	if (uio->uio_resid == 0)
6339584Smckusick 		return (0);
6441897Smckusick 	if (uio->uio_offset < 0 && vp->v_type != VDIR)
6539584Smckusick 		return (EINVAL);
66*43348Smckusick 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
6738882Smacklem 	/*
6838882Smacklem 	 * If the file's modify time on the server has changed since the
6938882Smacklem 	 * last read rpc or you have written to the file,
7038882Smacklem 	 * you may have lost data cache consistency with the
7138882Smacklem 	 * server, so flush all of the file's data out of the cache.
7241897Smckusick 	 * Then force a getattr rpc to ensure that you have up to date
7341897Smckusick 	 * attributes.
7438882Smacklem 	 * NB: This implies that cache data can be read when up to
7538882Smacklem 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
7638882Smacklem 	 * attributes this could be forced by setting n_attrstamp to 0 before
77*43348Smckusick 	 * the nfs_dogetattr() call.
7838882Smacklem 	 */
7941897Smckusick 	if (vp->v_type != VLNK) {
8041897Smckusick 		if (np->n_flag & NMODIFIED) {
8141897Smckusick 			np->n_flag &= ~NMODIFIED;
8241897Smckusick 			vinvalbuf(vp, TRUE);
8341897Smckusick 			np->n_attrstamp = 0;
8441897Smckusick 			np->n_direofoffset = 0;
85*43348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
8639750Smckusick 				return (error);
8739750Smckusick 			np->n_mtime = vattr.va_mtime.tv_sec;
8841897Smckusick 		} else {
89*43348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
9041897Smckusick 				return (error);
9141897Smckusick 			if (np->n_mtime != vattr.va_mtime.tv_sec) {
9241897Smckusick 				np->n_direofoffset = 0;
9341897Smckusick 				vinvalbuf(vp, TRUE);
9441897Smckusick 				np->n_mtime = vattr.va_mtime.tv_sec;
9541897Smckusick 			}
9639750Smckusick 		}
9738882Smacklem 	}
9838882Smacklem 	do {
9941897Smckusick 	    switch (vp->v_type) {
10041897Smckusick 	    case VREG:
10139750Smckusick 		nfsstats.biocache_reads++;
102*43348Smckusick 		lbn = uio->uio_offset / biosize;
103*43348Smckusick 		on = uio->uio_offset & (biosize-1);
104*43348Smckusick 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
10538882Smacklem 		diff = np->n_size - uio->uio_offset;
10638882Smacklem 		if (diff <= 0)
10739584Smckusick 			return (error);
10838882Smacklem 		if (diff < n)
10938882Smacklem 			n = diff;
110*43348Smckusick 		bn = lbn*(biosize/DEV_BSIZE);
111*43348Smckusick 		rablock = (lbn+1)*(biosize/DEV_BSIZE);
11239901Smckusick 		if (vp->v_lastr + 1 == lbn &&
11339901Smckusick 		    np->n_size > (rablock * DEV_BSIZE))
114*43348Smckusick 			error = breada(vp, bn, biosize, rablock, biosize,
11538882Smacklem 				cred, &bp);
11638882Smacklem 		else
117*43348Smckusick 			error = bread(vp, bn, biosize, cred, &bp);
11839901Smckusick 		vp->v_lastr = lbn;
11938882Smacklem 		if (bp->b_resid) {
120*43348Smckusick 		   diff = (on >= (biosize-bp->b_resid)) ? 0 :
121*43348Smckusick 			(biosize-bp->b_resid-on);
12241897Smckusick 		   n = MIN(n, diff);
12338882Smacklem 		}
12441897Smckusick 		break;
12541897Smckusick 	    case VLNK:
12641897Smckusick 		nfsstats.biocache_readlinks++;
12741897Smckusick 		on = 0;
12841897Smckusick 		error = bread(vp, (daddr_t)0, NFS_MAXPATHLEN, cred, &bp);
12941897Smckusick 		n = MIN(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
13041897Smckusick 		break;
13141897Smckusick 	    case VDIR:
13241897Smckusick 		nfsstats.biocache_readdirs++;
13341897Smckusick 		on = 0;
13441897Smckusick 		error = bread(vp, uio->uio_offset, DIRBLKSIZ, cred, &bp);
13541897Smckusick 		n = MIN(uio->uio_resid, DIRBLKSIZ - bp->b_resid);
13641897Smckusick 		break;
13741897Smckusick 	    };
13841897Smckusick 	    if (error) {
13941897Smckusick 		brelse(bp);
14041897Smckusick 		return (error);
14141897Smckusick 	    }
14241897Smckusick 	    if (n > 0)
14341897Smckusick 		error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
14441897Smckusick 	    switch (vp->v_type) {
14541897Smckusick 	    case VREG:
146*43348Smckusick 		if (n+on == biosize || uio->uio_offset == np->n_size)
14738882Smacklem 			bp->b_flags |= B_AGE;
14841897Smckusick 		break;
14941897Smckusick 	    case VLNK:
15041897Smckusick 		n = 0;
15141897Smckusick 		break;
15241897Smckusick 	    case VDIR:
15341897Smckusick 		uio->uio_offset = bp->b_blkno;
15441897Smckusick 		break;
15541897Smckusick 	    };
15641897Smckusick 	    brelse(bp);
15738882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
15838882Smacklem 	return (error);
15938882Smacklem }
16038882Smacklem 
16138882Smacklem /*
16238882Smacklem  * Vnode op for write using bio
16338882Smacklem  */
16439584Smckusick nfs_write(vp, uio, ioflag, cred)
16538882Smacklem 	register struct vnode *vp;
16638882Smacklem 	register struct uio *uio;
16738882Smacklem 	int ioflag;
16838882Smacklem 	struct ucred *cred;
16938882Smacklem {
170*43348Smckusick 	register int biosize;
17138882Smacklem 	struct buf *bp;
17238882Smacklem 	struct nfsnode *np = VTONFS(vp);
17341897Smckusick 	struct vattr vattr;
17438882Smacklem 	daddr_t lbn, bn;
17540220Smckusick 	int n, on, error = 0;
17638882Smacklem 
17741897Smckusick 	if (uio->uio_rw != UIO_WRITE)
17841897Smckusick 		panic("nfs_write mode");
17941897Smckusick 	if (vp->v_type != VREG)
18041897Smckusick 		return (EIO);
18138882Smacklem 	/* Should we try and do this ?? */
182*43348Smckusick 	if (ioflag & (IO_APPEND | IO_SYNC)) {
18341897Smckusick 		if (np->n_flag & NMODIFIED) {
18441897Smckusick 			np->n_flag &= ~NMODIFIED;
18541897Smckusick 			vinvalbuf(vp, TRUE);
18641897Smckusick 		}
187*43348Smckusick 		if (ioflag & IO_APPEND) {
188*43348Smckusick 			np->n_attrstamp = 0;
189*43348Smckusick 			if (error = nfs_dogetattr(vp, &vattr, cred, 1))
190*43348Smckusick 				return (error);
191*43348Smckusick 			uio->uio_offset = np->n_size;
192*43348Smckusick 		}
19341897Smckusick 		return (nfs_writerpc(vp, uio, cred, u.u_procp));
19441897Smckusick 	}
19539584Smckusick #ifdef notdef
19638882Smacklem 	cnt = uio->uio_resid;
19738882Smacklem 	osize = np->n_size;
19838882Smacklem #endif
19939584Smckusick 	if (uio->uio_offset < 0)
20039584Smckusick 		return (EINVAL);
20138882Smacklem 	if (uio->uio_resid == 0)
20239584Smckusick 		return (0);
20338882Smacklem 	/*
20438882Smacklem 	 * Maybe this should be above the vnode op call, but so long as
20538882Smacklem 	 * file servers have no limits, i don't think it matters
20638882Smacklem 	 */
20741897Smckusick 	if (uio->uio_offset + uio->uio_resid >
20838882Smacklem 	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
20938882Smacklem 		psignal(u.u_procp, SIGXFSZ);
21039584Smckusick 		return (EFBIG);
21138882Smacklem 	}
212*43348Smckusick 	/*
213*43348Smckusick 	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
214*43348Smckusick 	 * will be the same size within a filesystem. nfs_writerpc will
215*43348Smckusick 	 * still use nm_wsize when sizing the rpc's.
216*43348Smckusick 	 */
217*43348Smckusick 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
21841897Smckusick 	np->n_flag |= NMODIFIED;
21938882Smacklem 	do {
22039750Smckusick 		nfsstats.biocache_writes++;
221*43348Smckusick 		lbn = uio->uio_offset / biosize;
222*43348Smckusick 		on = uio->uio_offset & (biosize-1);
223*43348Smckusick 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
22438882Smacklem 		if (uio->uio_offset+n > np->n_size)
22538882Smacklem 			np->n_size = uio->uio_offset+n;
226*43348Smckusick 		bn = lbn*(biosize/DEV_BSIZE);
22740037Smckusick again:
228*43348Smckusick 		bp = getblk(vp, bn, biosize);
22938882Smacklem 		if (bp->b_wcred == NOCRED) {
23038882Smacklem 			crhold(cred);
23138882Smacklem 			bp->b_wcred = cred;
23238882Smacklem 		}
23338882Smacklem 		if (bp->b_dirtyend > 0) {
23438882Smacklem 			/*
23540037Smckusick 			 * If the new write will leave a contiguous dirty
23640037Smckusick 			 * area, just update the b_dirtyoff and b_dirtyend,
23740037Smckusick 			 * otherwise force a write rpc of the old dirty area.
23838882Smacklem 			 */
23938882Smacklem 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
24038882Smacklem 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
24138882Smacklem 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
24238882Smacklem 			} else {
24341897Smckusick 				bp->b_proc = u.u_procp;
24440037Smckusick 				if (error = bwrite(bp))
24539584Smckusick 					return (error);
24640037Smckusick 				goto again;
24738882Smacklem 			}
24838882Smacklem 		} else {
24938882Smacklem 			bp->b_dirtyoff = on;
25038882Smacklem 			bp->b_dirtyend = on+n;
25138882Smacklem 		}
25240037Smckusick 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
25340037Smckusick 			brelse(bp);
25439584Smckusick 			return (error);
25540037Smckusick 		}
256*43348Smckusick 		if ((n+on) == biosize) {
25738882Smacklem 			bp->b_flags |= B_AGE;
25841897Smckusick 			bp->b_proc = (struct proc *)0;
25938882Smacklem 			bawrite(bp);
26038882Smacklem 		} else {
26141897Smckusick 			bp->b_proc = (struct proc *)0;
26238882Smacklem 			bdwrite(bp);
26338882Smacklem 		}
26438882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
26538882Smacklem #ifdef notdef
26638882Smacklem 	/* Should we try and do this for nfs ?? */
26739584Smckusick 	if (error && (ioflag & IO_UNIT)) {
26838882Smacklem 		np->n_size = osize;
26939584Smckusick 		uio->uio_offset -= cnt - uio->uio_resid;
27039584Smckusick 		uio->uio_resid = cnt;
27139584Smckusick 	}
27238882Smacklem #endif
27338882Smacklem 	return (error);
27438882Smacklem }
275