xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 39487)
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*39487Smckusick  *	@(#)nfs_bio.c	7.4 (Berkeley) 11/03/89
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"
3038882Smacklem #include "nfsiom.h"
3138882Smacklem 
3238882Smacklem /* True and false, how exciting */
3338882Smacklem #define	TRUE	1
3438882Smacklem #define	FALSE	0
3538882Smacklem 
3638882Smacklem /*
3738882Smacklem  * Vnode op for read using bio
3838882Smacklem  * Any similarity to readip() is purely coincidental
3938882Smacklem  */
4038882Smacklem nfs_read(vp, uio, offp, ioflag, cred)
4138882Smacklem 	register struct vnode *vp;
4238882Smacklem 	struct uio *uio;
4338882Smacklem 	off_t *offp;
4438882Smacklem 	int ioflag;
4538882Smacklem 	struct ucred *cred;
4638882Smacklem {
4738882Smacklem 	register struct nfsnode *np = VTONFS(vp);
4838882Smacklem 	struct buf *bp;
4938882Smacklem 	struct vattr vattr;
5038882Smacklem 	daddr_t lbn, bn, rablock;
51*39487Smckusick 	int diff, error = 0;
5238882Smacklem 	long n, on;
5338882Smacklem 
5438882Smacklem 	if (!(ioflag & IO_NODELOCKED))
5538882Smacklem 		nfs_lock(vp);
5638882Smacklem 	/*
5738882Smacklem 	 * Avoid caching directories. Once everything is using getdirentries()
5838882Smacklem 	 * this will never happen anyhow.
5938882Smacklem 	 */
6038882Smacklem 	if (vp->v_type == VDIR) {
6138882Smacklem 		error = nfs_readrpc(vp, uio, offp, cred);
6239340Smckusick 		if (!(ioflag & IO_NODELOCKED))
6339340Smckusick 			nfs_unlock(vp);
6439340Smckusick 		return (error);
6538882Smacklem 	}
6639340Smckusick 	uio->uio_offset = *offp;
6738882Smacklem 	if (uio->uio_rw != UIO_READ)
6838882Smacklem 		panic("nfs_read mode");
6938882Smacklem 	if (vp->v_type != VREG)
7038882Smacklem 		panic("nfs_read type");
7138882Smacklem 	if (uio->uio_resid == 0)
7238882Smacklem 		goto out;
7338882Smacklem 	if (uio->uio_offset < 0) {
7438882Smacklem 		error = EINVAL;
7538882Smacklem 		goto out;
7638882Smacklem 	}
7738882Smacklem 	/*
7838882Smacklem 	 * If the file's modify time on the server has changed since the
7938882Smacklem 	 * last read rpc or you have written to the file,
8038882Smacklem 	 * you may have lost data cache consistency with the
8138882Smacklem 	 * server, so flush all of the file's data out of the cache.
8238882Smacklem 	 * This will implicitly bring the modify time up to date, since
8338882Smacklem 	 * up to date attributes are returned in the reply to any write rpc's
8438882Smacklem 	 * NB: This implies that cache data can be read when up to
8538882Smacklem 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
8638882Smacklem 	 * attributes this could be forced by setting n_attrstamp to 0 before
8738882Smacklem 	 * the nfs_getattr() call.
8838882Smacklem 	 */
8938882Smacklem 	if (np->n_flag & NMODIFIED) {
9038882Smacklem 		np->n_flag &= ~NMODIFIED;
9139340Smckusick 		if (error = nfs_blkflush(vp, (daddr_t)0, np->n_size, TRUE))
9238882Smacklem 			goto out;
9338882Smacklem 		if (error = nfs_getattr(vp, &vattr, cred))
9438882Smacklem 			goto out;
9538882Smacklem 		np->n_mtime = vattr.va_mtime.tv_sec;
9638882Smacklem 	} else {
9738882Smacklem 		if (error = nfs_getattr(vp, &vattr, cred))
9838882Smacklem 			goto out;
9938882Smacklem 		if (np->n_mtime != vattr.va_mtime.tv_sec) {
10039358Smckusick 			if (error = nfs_blkflush(vp, (daddr_t)0,
10139358Smckusick 				np->n_size, TRUE))
10238882Smacklem 				goto out;
10338882Smacklem 			np->n_mtime = vattr.va_mtime.tv_sec;
10438882Smacklem 		}
10538882Smacklem 	}
10639340Smckusick 	np->n_flag |= NBUFFERED;
10738882Smacklem 	do {
10838882Smacklem 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
10938882Smacklem 		on = uio->uio_offset & (NFS_BIOSIZE-1);
11038882Smacklem 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
11138882Smacklem 		diff = np->n_size - uio->uio_offset;
11238882Smacklem 		if (diff <= 0)
11338882Smacklem 			goto out;
11438882Smacklem 		if (diff < n)
11538882Smacklem 			n = diff;
11638882Smacklem 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
11738882Smacklem 		rablock = (lbn+1)*(NFS_BIOSIZE/DEV_BSIZE);
11839358Smckusick 		if (np->n_lastr+1 == lbn && np->n_size > (rablock*DEV_BSIZE))
11938882Smacklem 			error = breada(vp, bn, NFS_BIOSIZE, rablock, NFS_BIOSIZE,
12038882Smacklem 				cred, &bp);
12138882Smacklem 		else
12238882Smacklem 			error = bread(vp, bn, NFS_BIOSIZE, cred, &bp);
12338882Smacklem 		np->n_lastr = lbn;
12438882Smacklem 		if (bp->b_resid) {
12538882Smacklem 			diff = (on >= (NFS_BIOSIZE-bp->b_resid)) ? 0 :
12638882Smacklem 				(NFS_BIOSIZE-bp->b_resid-on);
12738882Smacklem 			n = MIN(n, diff);
12838882Smacklem 		}
12938882Smacklem 		if (error) {
13038882Smacklem 			brelse(bp);
13138882Smacklem 			goto out;
13238882Smacklem 		}
13338882Smacklem 		if (n > 0)
13438882Smacklem 			error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
13538882Smacklem 		if (n+on == NFS_BIOSIZE || uio->uio_offset == np->n_size)
13638882Smacklem 			bp->b_flags |= B_AGE;
13738882Smacklem 		brelse(bp);
13838882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
13938882Smacklem out:
14038882Smacklem 	*offp = uio->uio_offset;
14139340Smckusick 	if (!(ioflag & IO_NODELOCKED))
14238882Smacklem 		nfs_unlock(vp);
14338882Smacklem 	return (error);
14438882Smacklem }
14538882Smacklem 
14638882Smacklem /*
14738882Smacklem  * Vnode op for write using bio
14838882Smacklem  */
14938882Smacklem nfs_write(vp, uio, offp, ioflag, cred)
15038882Smacklem 	register struct vnode *vp;
15138882Smacklem 	register struct uio *uio;
15238882Smacklem 	off_t *offp;
15338882Smacklem 	int ioflag;
15438882Smacklem 	struct ucred *cred;
15538882Smacklem {
15638882Smacklem 	struct buf *bp;
15738882Smacklem 	struct nfsnode *np = VTONFS(vp);
15838882Smacklem 	daddr_t lbn, bn;
159*39487Smckusick 	int i, n, on, cnt, count, error = 0;
16038882Smacklem 
16138882Smacklem 	if ((ioflag & IO_NODELOCKED) == 0)
16238882Smacklem 		nfs_lock(vp);
16338882Smacklem 	/* Should we try and do this ?? */
16438882Smacklem 	if (vp->v_type == VREG && (ioflag & IO_APPEND))
16538882Smacklem 		*offp = np->n_size;
16638882Smacklem 	uio->uio_offset = *offp;
16738882Smacklem 	cnt = uio->uio_resid;
16838882Smacklem #ifdef notdef
16938882Smacklem 	osize = np->n_size;
17038882Smacklem #endif
17138882Smacklem 	if (uio->uio_rw != UIO_WRITE)
17238882Smacklem 		panic("nfs_write mode");
17338882Smacklem 	if (vp->v_type != VREG)
17438882Smacklem 		panic("nfs_write type");
17538882Smacklem 	if (uio->uio_offset < 0) {
17638882Smacklem 		error = EINVAL;
17738882Smacklem 		goto out;
17838882Smacklem 	}
17938882Smacklem 	if (uio->uio_resid == 0)
18038882Smacklem 		goto out;
18138882Smacklem 	/*
18238882Smacklem 	 * Maybe this should be above the vnode op call, but so long as
18338882Smacklem 	 * file servers have no limits, i don't think it matters
18438882Smacklem 	 */
18538882Smacklem 	if (vp->v_type == VREG &&
18638882Smacklem 	    uio->uio_offset + uio->uio_resid >
18738882Smacklem 	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
18838882Smacklem 		psignal(u.u_procp, SIGXFSZ);
18938882Smacklem 		error = EFBIG;
19038882Smacklem 		goto out;
19138882Smacklem 	}
19239340Smckusick 	np->n_flag |= (NMODIFIED|NBUFFERED);
19338882Smacklem 	do {
19438882Smacklem 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
19538882Smacklem 		on = uio->uio_offset & (NFS_BIOSIZE-1);
19638882Smacklem 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
19738882Smacklem 		if (uio->uio_offset+n > np->n_size)
19838882Smacklem 			np->n_size = uio->uio_offset+n;
19938882Smacklem 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
20038882Smacklem 		count = howmany(NFS_BIOSIZE, CLBYTES);
20138882Smacklem 		for (i = 0; i < count; i++)
20238882Smacklem 			munhash(vp, bn + i * CLBYTES / DEV_BSIZE);
20338882Smacklem 		bp = getblk(vp, bn, NFS_BIOSIZE);
20438882Smacklem 		if (bp->b_wcred == NOCRED) {
20538882Smacklem 			crhold(cred);
20638882Smacklem 			bp->b_wcred = cred;
20738882Smacklem 		}
20838882Smacklem 		if (bp->b_dirtyend > 0) {
20938882Smacklem 			/*
21038882Smacklem 			 * Iff the new write will leave a contiguous
21138882Smacklem 			 * dirty area, just update the b_dirtyoff and
21238882Smacklem 			 * b_dirtyend
21338882Smacklem 			 * otherwise force a write rpc of the old dirty
21438882Smacklem 			 * area
21538882Smacklem 			 */
21638882Smacklem 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
21738882Smacklem 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
21838882Smacklem 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
21938882Smacklem 			} else {
22038882Smacklem 				/*
22138882Smacklem 				 * Like bwrite() but without the brelse
22238882Smacklem 				 */
22338882Smacklem 				bp->b_flags &= ~(B_READ | B_DONE |
22438882Smacklem 				    B_ERROR | B_DELWRI | B_ASYNC);
22538882Smacklem 				u.u_ru.ru_oublock++;
22638882Smacklem 				VOP_STRATEGY(bp);
22738882Smacklem 				error = biowait(bp);
22838882Smacklem 				if (bp->b_flags & B_ERROR) {
22938882Smacklem 					brelse(bp);
23038882Smacklem 					if (bp->b_error)
23138882Smacklem 						error = bp->b_error;
23238882Smacklem 					else
23338882Smacklem 						error = EIO;
23438882Smacklem 					goto out;
23538882Smacklem 				}
23638882Smacklem 				bp->b_dirtyoff = on;
23738882Smacklem 				bp->b_dirtyend = on+n;
23838882Smacklem 			}
23938882Smacklem 		} else {
24038882Smacklem 			bp->b_dirtyoff = on;
24138882Smacklem 			bp->b_dirtyend = on+n;
24238882Smacklem 		}
24338882Smacklem 		if (error = uiomove(bp->b_un.b_addr + on, n, uio))
24438882Smacklem 			goto out;
24538882Smacklem 		if ((n+on) == NFS_BIOSIZE) {
24638882Smacklem 			bp->b_flags |= B_AGE;
24738882Smacklem 			bawrite(bp);
24838882Smacklem 		} else {
24938882Smacklem 			bdwrite(bp);
25038882Smacklem 		}
25138882Smacklem 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
25238882Smacklem #ifdef notdef
25338882Smacklem 	/* Should we try and do this for nfs ?? */
25438882Smacklem 	if (error && (ioflag & IO_UNIT))
25538882Smacklem 		np->n_size = osize;
25638882Smacklem 	else
25738882Smacklem #endif
25838882Smacklem 		*offp += cnt - uio->uio_resid;
25938882Smacklem out:
26038882Smacklem 	if ((ioflag & IO_NODELOCKED) == 0)
26138882Smacklem 		nfs_unlock(vp);
26238882Smacklem 	return (error);
26338882Smacklem }
26438882Smacklem 
26538882Smacklem /*
26638882Smacklem  * Flush and invalidate all of the buffers associated with the blocks of vp
26738882Smacklem  */
26838882Smacklem nfs_blkflush(vp, blkno, size, invalidate)
26938882Smacklem 	struct vnode *vp;
27038882Smacklem 	daddr_t blkno;
27138882Smacklem 	long size;
27238882Smacklem 	int invalidate;
27338882Smacklem {
27438882Smacklem 	register struct buf *ep;
27538882Smacklem 	struct buf *dp;
27639358Smckusick 	daddr_t curblk, nextblk, ecurblk, lastblk;
27738882Smacklem 	int s, error, allerrors = 0;
27839358Smckusick 
27939358Smckusick 	/*
28039358Smckusick 	 * Iterate through each possible hash chain.
28139358Smckusick 	 */
28239358Smckusick 	lastblk = blkno + btodb(size+DEV_BSIZE-1) - 1;
28339358Smckusick 	for (curblk = blkno; curblk <= lastblk; curblk = nextblk) {
28439358Smckusick #if RND & (RND-1)
28539358Smckusick 	        nextblk = ((curblk / RND) + 1) * RND;
28639358Smckusick #else
28739358Smckusick 	        nextblk = ((curblk & ~(RND-1)) + RND);
28839358Smckusick #endif
28939358Smckusick 	        ecurblk = nextblk > lastblk ? lastblk : nextblk - 1;
29039358Smckusick 	        dp = BUFHASH(vp, curblk);
29138882Smacklem loop:
29239358Smckusick 	        for (ep = dp->b_forw; ep != dp; ep = ep->b_forw) {
29339358Smckusick 	                if (ep->b_vp != vp || (ep->b_flags & B_INVAL))
29439358Smckusick 	                        continue;
29539358Smckusick 	                /* look for overlap */
29639358Smckusick 	                if (ep->b_bcount == 0 || ep->b_blkno > ecurblk ||
29739358Smckusick 	                    ep->b_blkno + btodb(ep->b_bcount) <= curblk)
29839358Smckusick 	                        continue;
29939358Smckusick 	                s = splbio();
30039358Smckusick 	                if (ep->b_flags&B_BUSY) {
30139358Smckusick 	                        ep->b_flags |= B_WANTED;
30239358Smckusick 	                        sleep((caddr_t)ep, PRIBIO+1);
30339358Smckusick 	                        splx(s);
30439358Smckusick 	                        goto loop;
30539358Smckusick 	                }
30639358Smckusick 	                if (ep->b_flags & B_DELWRI) {
30739358Smckusick 	                        splx(s);
30839358Smckusick 	                        notavail(ep);
30939358Smckusick 	                        if (error = bwrite(ep))
31039358Smckusick 	                                allerrors = error;
31139358Smckusick 	                        goto loop;
31239358Smckusick 	                }
31339358Smckusick 	                splx(s);
31438882Smacklem 			if (invalidate) {
31539358Smckusick 				notavail(ep);
31638882Smacklem 				ep->b_flags |= B_INVAL;
31738882Smacklem 				brelvp(ep);
31839358Smckusick 				brelse(ep);
31938882Smacklem 			}
32039358Smckusick 	        }
32138882Smacklem 	}
32238882Smacklem 	return (allerrors);
32338882Smacklem }
324