xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 38882)
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.1 (Berkeley) 08/30/89
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 "nfsiom.h"
31 
32 /* True and false, how exciting */
33 #define	TRUE	1
34 #define	FALSE	0
35 
36 /*
37  * Vnode op for read using bio
38  * Any similarity to readip() is purely coincidental
39  */
40 nfs_read(vp, uio, offp, ioflag, cred)
41 	register struct vnode *vp;
42 	struct uio *uio;
43 	off_t *offp;
44 	int ioflag;
45 	struct ucred *cred;
46 {
47 	register struct nfsnode *np = VTONFS(vp);
48 	struct buf *bp;
49 	struct vattr vattr;
50 	daddr_t lbn, bn, rablock;
51 	int error = 0;
52 	int diff;
53 	long n, on;
54 	int count;
55 
56 	if (!(ioflag & IO_NODELOCKED))
57 		nfs_lock(vp);
58 	uio->uio_offset = *offp;
59 	/*
60 	 * Avoid caching directories. Once everything is using getdirentries()
61 	 * this will never happen anyhow.
62 	 */
63 	if (vp->v_type == VDIR) {
64 		error = nfs_readrpc(vp, uio, offp, cred);
65 		goto out;
66 	}
67 	count = uio->uio_resid;
68 	if (uio->uio_rw != UIO_READ)
69 		panic("nfs_read mode");
70 	if (vp->v_type != VREG)
71 		panic("nfs_read type");
72 	if (uio->uio_resid == 0)
73 		goto out;
74 	if (uio->uio_offset < 0) {
75 		error = EINVAL;
76 		goto out;
77 	}
78 	/*
79 	 * If the file's modify time on the server has changed since the
80 	 * last read rpc or you have written to the file,
81 	 * you may have lost data cache consistency with the
82 	 * server, so flush all of the file's data out of the cache.
83 	 * This will implicitly bring the modify time up to date, since
84 	 * up to date attributes are returned in the reply to any write rpc's
85 	 * NB: This implies that cache data can be read when up to
86 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
87 	 * attributes this could be forced by setting n_attrstamp to 0 before
88 	 * the nfs_getattr() call.
89 	 */
90 	if (np->n_flag & NMODIFIED) {
91 		np->n_flag &= ~NMODIFIED;
92 		if (error = nfs_blkflush(vp, (daddr_t)0, np->n_size, FALSE))
93 			goto out;
94 		if (error = nfs_getattr(vp, &vattr, cred))
95 			goto out;
96 		np->n_size = vattr.va_size;
97 		np->n_mtime = vattr.va_mtime.tv_sec;
98 	} else {
99 		if (error = nfs_getattr(vp, &vattr, cred))
100 			goto out;
101 		if (np->n_mtime != vattr.va_mtime.tv_sec) {
102 			if (error = nfs_blkflush(vp, (daddr_t)0, np->n_size, FALSE))
103 				goto out;
104 			np->n_size = vattr.va_size;
105 			np->n_mtime = vattr.va_mtime.tv_sec;
106 		}
107 	}
108 	do {
109 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
110 		on = uio->uio_offset & (NFS_BIOSIZE-1);
111 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
112 		diff = np->n_size - uio->uio_offset;
113 		if (diff <= 0)
114 			goto out;
115 		if (diff < n)
116 			n = diff;
117 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
118 		rablock = (lbn+1)*(NFS_BIOSIZE/DEV_BSIZE);
119 		if (np->n_lastr+1 == lbn)
120 			error = breada(vp, bn, NFS_BIOSIZE, rablock, NFS_BIOSIZE,
121 				cred, &bp);
122 		else
123 			error = bread(vp, bn, NFS_BIOSIZE, cred, &bp);
124 		np->n_lastr = lbn;
125 		if (bp->b_resid) {
126 			diff = (on >= (NFS_BIOSIZE-bp->b_resid)) ? 0 :
127 				(NFS_BIOSIZE-bp->b_resid-on);
128 			n = MIN(n, diff);
129 		}
130 		if (error) {
131 			brelse(bp);
132 			goto out;
133 		}
134 		if (n > 0)
135 			error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
136 		if (n+on == NFS_BIOSIZE || uio->uio_offset == np->n_size)
137 			bp->b_flags |= B_AGE;
138 		brelse(bp);
139 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
140 out:
141 	*offp = uio->uio_offset;
142 	if ((ioflag & IO_NODELOCKED) == 0)
143 		nfs_unlock(vp);
144 	return (error);
145 }
146 
147 /*
148  * Vnode op for write using bio
149  */
150 nfs_write(vp, uio, offp, ioflag, cred)
151 	register struct vnode *vp;
152 	register struct uio *uio;
153 	off_t *offp;
154 	int ioflag;
155 	struct ucred *cred;
156 {
157 	struct buf *bp;
158 	struct nfsnode *np = VTONFS(vp);
159 	daddr_t lbn, bn;
160 	int i, n, on;
161 	int flags, count, size;
162 	int error = 0;
163 	int cnt;
164 	u_long osize;
165 
166 	if ((ioflag & IO_NODELOCKED) == 0)
167 		nfs_lock(vp);
168 	/* Should we try and do this ?? */
169 	if (vp->v_type == VREG && (ioflag & IO_APPEND))
170 		*offp = np->n_size;
171 	uio->uio_offset = *offp;
172 	cnt = uio->uio_resid;
173 #ifdef notdef
174 	osize = np->n_size;
175 #endif
176 	if (uio->uio_rw != UIO_WRITE)
177 		panic("nfs_write mode");
178 	if (vp->v_type != VREG)
179 		panic("nfs_write type");
180 	if (uio->uio_offset < 0) {
181 		error = EINVAL;
182 		goto out;
183 	}
184 	if (uio->uio_resid == 0)
185 		goto out;
186 	/*
187 	 * Maybe this should be above the vnode op call, but so long as
188 	 * file servers have no limits, i don't think it matters
189 	 */
190 	if (vp->v_type == VREG &&
191 	    uio->uio_offset + uio->uio_resid >
192 	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
193 		psignal(u.u_procp, SIGXFSZ);
194 		error = EFBIG;
195 		goto out;
196 	}
197 	np->n_flag |= NMODIFIED;
198 	do {
199 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
200 		on = uio->uio_offset & (NFS_BIOSIZE-1);
201 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
202 		if (uio->uio_offset+n > np->n_size)
203 			np->n_size = uio->uio_offset+n;
204 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
205 		count = howmany(NFS_BIOSIZE, CLBYTES);
206 		for (i = 0; i < count; i++)
207 			munhash(vp, bn + i * CLBYTES / DEV_BSIZE);
208 		bp = getblk(vp, bn, NFS_BIOSIZE);
209 		if (bp->b_wcred == NOCRED) {
210 			crhold(cred);
211 			bp->b_wcred = cred;
212 		}
213 		if (bp->b_dirtyend > 0) {
214 			/*
215 			 * Iff the new write will leave a contiguous
216 			 * dirty area, just update the b_dirtyoff and
217 			 * b_dirtyend
218 			 * otherwise force a write rpc of the old dirty
219 			 * area
220 			 */
221 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
222 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
223 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
224 			} else {
225 				/*
226 				 * Like bwrite() but without the brelse
227 				 */
228 				bp->b_flags &= ~(B_READ | B_DONE |
229 				    B_ERROR | B_DELWRI | B_ASYNC);
230 				u.u_ru.ru_oublock++;
231 				VOP_STRATEGY(bp);
232 				error = biowait(bp);
233 				if (bp->b_flags & B_ERROR) {
234 					brelse(bp);
235 					if (bp->b_error)
236 						error = bp->b_error;
237 					else
238 						error = EIO;
239 					goto out;
240 				}
241 				bp->b_dirtyoff = on;
242 				bp->b_dirtyend = on+n;
243 			}
244 		} else {
245 			bp->b_dirtyoff = on;
246 			bp->b_dirtyend = on+n;
247 		}
248 		if (error = uiomove(bp->b_un.b_addr + on, n, uio))
249 			goto out;
250 		if ((n+on) == NFS_BIOSIZE) {
251 			bp->b_flags |= B_AGE;
252 			bawrite(bp);
253 		} else {
254 			bdwrite(bp);
255 		}
256 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
257 #ifdef notdef
258 	/* Should we try and do this for nfs ?? */
259 	if (error && (ioflag & IO_UNIT))
260 		np->n_size = osize;
261 	else
262 #endif
263 		*offp += cnt - uio->uio_resid;
264 out:
265 	if ((ioflag & IO_NODELOCKED) == 0)
266 		nfs_unlock(vp);
267 	return (error);
268 }
269 
270 /*
271  * Flush and invalidate all of the buffers associated with the blocks of vp
272  */
273 nfs_blkflush(vp, blkno, size, invalidate)
274 	struct vnode *vp;
275 	daddr_t blkno;
276 	long size;
277 	int invalidate;
278 {
279 	register struct buf *ep;
280 	struct buf *dp;
281 	daddr_t curblkno, last;
282 	int s, error, allerrors = 0;
283 
284 	last = blkno + btodb(size);
285 	for (curblkno = blkno; curblkno <= last;
286 	     curblkno += (NFS_BIOSIZE / DEV_BSIZE)) {
287 		dp = BUFHASH(vp, curblkno);
288 loop:
289 		for (ep = dp->b_forw; ep != dp; ep = ep->b_forw) {
290 			if (ep->b_vp != vp || (ep->b_flags & B_INVAL))
291 				continue;
292 			if (curblkno != ep->b_blkno)
293 				continue;
294 			s = splbio();
295 			if (ep->b_flags & B_BUSY) {
296 				ep->b_flags |= B_WANTED;
297 				sleep((caddr_t)ep, PRIBIO+1);
298 				splx(s);
299 				goto loop;
300 			}
301 			splx(s);
302 			notavail(ep);
303 			if (ep->b_flags & B_DELWRI) {
304 				ep->b_flags &= ~B_ASYNC;
305 				if (error = bwrite(ep))
306 					allerrors = error;
307 				goto loop;
308 			}
309 			if (invalidate) {
310 				ep->b_flags |= B_INVAL;
311 				brelvp(ep);
312 			}
313 			brelse(ep);
314 		}
315 	}
316 	return (allerrors);
317 }
318