xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 41897)
1 /*
2  * Copyright (c) 1989 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  *	@(#)nfs_bio.c	7.13 (Berkeley) 05/14/90
21  */
22 
23 #include "param.h"
24 #include "user.h"
25 #include "buf.h"
26 #include "vnode.h"
27 #include "trace.h"
28 #include "mount.h"
29 #include "nfsnode.h"
30 #include "nfsv2.h"
31 #include "nfs.h"
32 #include "nfsiom.h"
33 #include "nfsmount.h"
34 
35 /* True and false, how exciting */
36 #define	TRUE	1
37 #define	FALSE	0
38 
39 /*
40  * Vnode op for read using bio
41  * Any similarity to readip() is purely coincidental
42  */
43 nfs_bioread(vp, uio, ioflag, cred)
44 	register struct vnode *vp;
45 	struct uio *uio;
46 	int ioflag;
47 	struct ucred *cred;
48 {
49 	register struct nfsnode *np = VTONFS(vp);
50 	struct nfsmount *nmp;
51 	struct buf *bp;
52 	struct vattr vattr;
53 	daddr_t lbn, bn, rablock;
54 	int diff, error = 0;
55 	long n, on;
56 
57 	if (uio->uio_rw != UIO_READ)
58 		panic("nfs_read mode");
59 	if (uio->uio_resid == 0)
60 		return (0);
61 	if (uio->uio_offset < 0 && vp->v_type != VDIR)
62 		return (EINVAL);
63 	/*
64 	 * If the file's modify time on the server has changed since the
65 	 * last read rpc or you have written to the file,
66 	 * you may have lost data cache consistency with the
67 	 * server, so flush all of the file's data out of the cache.
68 	 * Then force a getattr rpc to ensure that you have up to date
69 	 * attributes.
70 	 * NB: This implies that cache data can be read when up to
71 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
72 	 * attributes this could be forced by setting n_attrstamp to 0 before
73 	 * the nfs_getattr() call.
74 	 */
75 	if (vp->v_type != VLNK) {
76 		if (np->n_flag & NMODIFIED) {
77 			np->n_flag &= ~NMODIFIED;
78 			vinvalbuf(vp, TRUE);
79 			np->n_attrstamp = 0;
80 			np->n_direofoffset = 0;
81 			if (error = nfs_getattr(vp, &vattr, cred))
82 				return (error);
83 			np->n_mtime = vattr.va_mtime.tv_sec;
84 		} else {
85 			if (error = nfs_getattr(vp, &vattr, cred))
86 				return (error);
87 			if (np->n_mtime != vattr.va_mtime.tv_sec) {
88 				np->n_direofoffset = 0;
89 				vinvalbuf(vp, TRUE);
90 				np->n_mtime = vattr.va_mtime.tv_sec;
91 			}
92 		}
93 	}
94 	nmp = VFSTONFS(vp->v_mount);
95 	do {
96 	    switch (vp->v_type) {
97 	    case VREG:
98 		nfsstats.biocache_reads++;
99 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
100 		on = uio->uio_offset & (NFS_BIOSIZE-1);
101 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
102 		diff = np->n_size - uio->uio_offset;
103 		if (diff <= 0)
104 			return (error);
105 		if (diff < n)
106 			n = diff;
107 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
108 		rablock = (lbn+1)*(NFS_BIOSIZE/DEV_BSIZE);
109 		if (vp->v_lastr + 1 == lbn &&
110 		    np->n_size > (rablock * DEV_BSIZE))
111 			error = breada(vp, bn, NFS_BIOSIZE, rablock, NFS_BIOSIZE,
112 				cred, &bp);
113 		else
114 			error = bread(vp, bn, NFS_BIOSIZE, cred, &bp);
115 		vp->v_lastr = lbn;
116 		if (bp->b_resid) {
117 		   diff = (on >= (NFS_BIOSIZE-bp->b_resid)) ? 0 :
118 			(NFS_BIOSIZE-bp->b_resid-on);
119 		   n = MIN(n, diff);
120 		}
121 		break;
122 	    case VLNK:
123 		nfsstats.biocache_readlinks++;
124 		on = 0;
125 		error = bread(vp, (daddr_t)0, NFS_MAXPATHLEN, cred, &bp);
126 		n = MIN(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
127 		break;
128 	    case VDIR:
129 		nfsstats.biocache_readdirs++;
130 		on = 0;
131 		error = bread(vp, uio->uio_offset, DIRBLKSIZ, cred, &bp);
132 		n = MIN(uio->uio_resid, DIRBLKSIZ - bp->b_resid);
133 		break;
134 	    };
135 	    if (error) {
136 		brelse(bp);
137 		return (error);
138 	    }
139 	    if (n > 0)
140 		error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
141 	    switch (vp->v_type) {
142 	    case VREG:
143 		if (n+on == NFS_BIOSIZE || uio->uio_offset == np->n_size)
144 			bp->b_flags |= B_AGE;
145 		break;
146 	    case VLNK:
147 		n = 0;
148 		break;
149 	    case VDIR:
150 		uio->uio_offset = bp->b_blkno;
151 		break;
152 	    };
153 	    brelse(bp);
154 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
155 	return (error);
156 }
157 
158 /*
159  * Vnode op for write using bio
160  */
161 nfs_write(vp, uio, ioflag, cred)
162 	register struct vnode *vp;
163 	register struct uio *uio;
164 	int ioflag;
165 	struct ucred *cred;
166 {
167 	struct buf *bp;
168 	struct nfsnode *np = VTONFS(vp);
169 	struct vattr vattr;
170 	daddr_t lbn, bn;
171 	int n, on, error = 0;
172 
173 	if (uio->uio_rw != UIO_WRITE)
174 		panic("nfs_write mode");
175 	if (vp->v_type != VREG)
176 		return (EIO);
177 	/* Should we try and do this ?? */
178 	if (ioflag & IO_APPEND) {
179 		if (np->n_flag & NMODIFIED) {
180 			np->n_flag &= ~NMODIFIED;
181 			vinvalbuf(vp, TRUE);
182 		}
183 		np->n_attrstamp = 0;
184 		if (error = nfs_getattr(vp, &vattr, cred))
185 			return (error);
186 		uio->uio_offset = np->n_size;
187 		return (nfs_writerpc(vp, uio, cred, u.u_procp));
188 	}
189 #ifdef notdef
190 	cnt = uio->uio_resid;
191 	osize = np->n_size;
192 #endif
193 	if (uio->uio_offset < 0)
194 		return (EINVAL);
195 	if (uio->uio_resid == 0)
196 		return (0);
197 	/*
198 	 * Maybe this should be above the vnode op call, but so long as
199 	 * file servers have no limits, i don't think it matters
200 	 */
201 	if (uio->uio_offset + uio->uio_resid >
202 	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
203 		psignal(u.u_procp, SIGXFSZ);
204 		return (EFBIG);
205 	}
206 	np->n_flag |= NMODIFIED;
207 	do {
208 		nfsstats.biocache_writes++;
209 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
210 		on = uio->uio_offset & (NFS_BIOSIZE-1);
211 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
212 		if (uio->uio_offset+n > np->n_size)
213 			np->n_size = uio->uio_offset+n;
214 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
215 again:
216 		bp = getblk(vp, bn, NFS_BIOSIZE);
217 		if (bp->b_wcred == NOCRED) {
218 			crhold(cred);
219 			bp->b_wcred = cred;
220 		}
221 		if (bp->b_dirtyend > 0) {
222 			/*
223 			 * If the new write will leave a contiguous dirty
224 			 * area, just update the b_dirtyoff and b_dirtyend,
225 			 * otherwise force a write rpc of the old dirty area.
226 			 */
227 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
228 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
229 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
230 			} else {
231 				bp->b_proc = u.u_procp;
232 				if (error = bwrite(bp))
233 					return (error);
234 				goto again;
235 			}
236 		} else {
237 			bp->b_dirtyoff = on;
238 			bp->b_dirtyend = on+n;
239 		}
240 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
241 			brelse(bp);
242 			return (error);
243 		}
244 		if ((n+on) == NFS_BIOSIZE) {
245 			bp->b_flags |= B_AGE;
246 			bp->b_proc = (struct proc *)0;
247 			bawrite(bp);
248 		} else {
249 			bp->b_proc = (struct proc *)0;
250 			bdwrite(bp);
251 		}
252 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
253 #ifdef notdef
254 	/* Should we try and do this for nfs ?? */
255 	if (error && (ioflag & IO_UNIT)) {
256 		np->n_size = osize;
257 		uio->uio_offset -= cnt - uio->uio_resid;
258 		uio->uio_resid = cnt;
259 	}
260 #endif
261 	return (error);
262 }
263