nfs_bio.c revision 1.9 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, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * from: @(#)nfs_bio.c 7.19 (Berkeley) 4/16/91
37 * $Id: nfs_bio.c,v 1.9 1994/04/21 07:49:07 cgd Exp $
38 */
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/proc.h>
43 #include <sys/buf.h>
44 #include <sys/uio.h>
45 #include <sys/namei.h>
46 #include <sys/vnode.h>
47 #include <sys/trace.h>
48 #include <sys/mount.h>
49 #include <sys/resourcevar.h>
50
51 #include <nfs/nfsnode.h>
52 #include <nfs/nfsv2.h>
53 #include <nfs/nfs.h>
54 #include <nfs/nfsiom.h>
55 #include <nfs/nfsmount.h>
56
57 /* True and false, how exciting */
58 #define TRUE 1
59 #define FALSE 0
60
61 /*
62 * Vnode op for read using bio
63 * Any similarity to readip() is purely coincidental
64 */
65 nfs_bioread(vp, uio, ioflag, cred)
66 register struct vnode *vp;
67 register struct uio *uio;
68 int ioflag;
69 struct ucred *cred;
70 {
71 register struct nfsnode *np = VTONFS(vp);
72 register int biosize;
73 struct buf *bp;
74 struct vattr vattr;
75 daddr_t lbn, bn, rablock;
76 int diff, error = 0;
77 long n, on;
78
79 #ifdef lint
80 ioflag = ioflag;
81 #endif /* lint */
82 #ifdef DIAGNOSTIC
83 if (uio->uio_rw != UIO_READ)
84 panic("nfs_read mode");
85 #endif
86 if (uio->uio_resid == 0)
87 return (0);
88 if (uio->uio_offset < 0 && vp->v_type != VDIR)
89 return (EINVAL);
90 biosize = VFSTONFS(vp->v_mount)->nm_rsize;
91 /*
92 * If the file's modify time on the server has changed since the
93 * last read rpc or you have written to the file,
94 * you may have lost data cache consistency with the
95 * server, so flush all of the file's data out of the cache.
96 * Then force a getattr rpc to ensure that you have up to date
97 * attributes.
98 * NB: This implies that cache data can be read when up to
99 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
100 * attributes this could be forced by setting n_attrstamp to 0 before
101 * the nfs_dogetattr() call.
102 */
103 if (vp->v_type != VLNK) {
104 if (np->n_flag & NMODIFIED) {
105 np->n_flag &= ~NMODIFIED;
106 vinvalbuf(vp, TRUE);
107 np->n_attrstamp = 0;
108 np->n_direofoffset = 0;
109 if (error = nfs_dogetattr(vp, &vattr, cred, 1,
110 uio->uio_procp))
111 return (error);
112 np->n_mtime = vattr.va_mtime.tv_sec;
113 } else {
114 if (error = nfs_dogetattr(vp, &vattr, cred, 1,
115 uio->uio_procp))
116 return (error);
117 if (np->n_mtime != vattr.va_mtime.tv_sec) {
118 np->n_direofoffset = 0;
119 vinvalbuf(vp, TRUE);
120 np->n_mtime = vattr.va_mtime.tv_sec;
121 }
122 }
123 }
124 do {
125 switch (vp->v_type) {
126 case VREG:
127 nfsstats.biocache_reads++;
128 lbn = uio->uio_offset / biosize;
129 on = uio->uio_offset & (biosize-1);
130 n = MIN((unsigned)(biosize - on), uio->uio_resid);
131 diff = np->n_size - uio->uio_offset;
132 if (diff <= 0)
133 return (error);
134 if (diff < n)
135 n = diff;
136 bn = lbn*(biosize/DEV_BSIZE);
137 rablock = (lbn+1)*(biosize/DEV_BSIZE);
138 if (vp->v_lastr + 1 == lbn &&
139 np->n_size > (rablock * DEV_BSIZE))
140 error = breada(vp, bn, biosize, rablock, biosize,
141 cred, &bp);
142 else
143 error = bread(vp, bn, biosize, cred, &bp);
144 vp->v_lastr = lbn;
145 if (bp->b_resid) {
146 diff = (on >= (biosize-bp->b_resid)) ? 0 :
147 (biosize-bp->b_resid-on);
148 n = MIN(n, diff);
149 }
150 break;
151 case VLNK:
152 nfsstats.biocache_readlinks++;
153 on = 0;
154 error = bread(vp, (daddr_t)0, NFS_MAXPATHLEN, cred, &bp);
155 n = MIN(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
156 break;
157 case VDIR:
158 nfsstats.biocache_readdirs++;
159 on = 0;
160 error = bread(vp, uio->uio_offset, NFS_DIRBLKSIZ, cred, &bp);
161 n = MIN(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid);
162 break;
163 };
164 if (error) {
165 brelse(bp);
166 return (error);
167 }
168 if (n > 0)
169 error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
170 switch (vp->v_type) {
171 case VREG:
172 if (n+on == biosize || uio->uio_offset == np->n_size)
173 bp->b_flags |= B_AGE;
174 break;
175 case VLNK:
176 n = 0;
177 break;
178 case VDIR:
179 uio->uio_offset = bp->b_blkno;
180 break;
181 };
182 brelse(bp);
183 } while (error == 0 && uio->uio_resid > 0 && n != 0);
184 return (error);
185 }
186
187 /*
188 * Vnode op for write using bio
189 */
190 nfs_write(vp, uio, ioflag, cred)
191 register struct vnode *vp;
192 register struct uio *uio;
193 int ioflag;
194 struct ucred *cred;
195 {
196 struct proc *p = uio->uio_procp;
197 register int biosize;
198 struct buf *bp;
199 struct nfsnode *np = VTONFS(vp);
200 struct vattr vattr;
201 daddr_t lbn, bn;
202 int n, on, error = 0;
203
204 #ifdef DIAGNOSTIC
205 if (uio->uio_rw != UIO_WRITE)
206 panic("nfs_write mode");
207 if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
208 panic("nfs_write proc");
209 #endif
210 if (vp->v_type != VREG)
211 return (EIO);
212 /* Should we try and do this ?? */
213 if (ioflag & (IO_APPEND | IO_SYNC)) {
214 if (np->n_flag & NMODIFIED) {
215 np->n_flag &= ~NMODIFIED;
216 vinvalbuf(vp, TRUE);
217 }
218 if (ioflag & IO_APPEND) {
219 np->n_attrstamp = 0;
220 if (error = nfs_dogetattr(vp, &vattr, cred, 1, p))
221 return (error);
222 uio->uio_offset = np->n_size;
223 }
224 return (nfs_writerpc(vp, uio, cred));
225 }
226 #ifdef notdef
227 cnt = uio->uio_resid;
228 osize = np->n_size;
229 #endif
230 if (uio->uio_offset < 0)
231 return (EINVAL);
232 if (uio->uio_resid == 0)
233 return (0);
234 /*
235 * Maybe this should be above the vnode op call, but so long as
236 * file servers have no limits, i don't think it matters
237 */
238 if (p &&
239 uio->uio_offset + uio->uio_resid >
240 p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
241 psignal(p, SIGXFSZ);
242 return (EFBIG);
243 }
244 /*
245 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
246 * will be the same size within a filesystem. nfs_writerpc will
247 * still use nm_wsize when sizing the rpc's.
248 */
249 biosize = VFSTONFS(vp->v_mount)->nm_rsize;
250 np->n_flag |= NMODIFIED;
251 vnode_pager_uncache(vp);
252 do {
253 nfsstats.biocache_writes++;
254 lbn = uio->uio_offset / biosize;
255 on = uio->uio_offset & (biosize-1);
256 n = MIN((unsigned)(biosize - on), uio->uio_resid);
257 if (uio->uio_offset+n > np->n_size) {
258 np->n_size = uio->uio_offset+n;
259 vnode_pager_setsize(vp, np->n_size);
260 }
261 bn = lbn*(biosize/DEV_BSIZE);
262 again:
263 bp = getblk(vp, bn, biosize, 0, 0);
264 if (bp->b_wcred == NOCRED && cred != NOCRED) {
265 crhold(cred);
266 bp->b_wcred = cred;
267 }
268 if (bp->b_dirtyend > 0) {
269 /*
270 * If the new write will leave a contiguous dirty
271 * area, just update the b_dirtyoff and b_dirtyend,
272 * otherwise force a write rpc of the old dirty area.
273 */
274 if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
275 bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
276 bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
277 } else {
278 bp->b_proc = p;
279 if (error = bwrite(bp))
280 return (error);
281 goto again;
282 }
283 } else {
284 bp->b_dirtyoff = on;
285 bp->b_dirtyend = on+n;
286 }
287 if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
288 brelse(bp);
289 return (error);
290 }
291 if ((n+on) == biosize) {
292 bp->b_flags |= B_AGE;
293 bp->b_proc = (struct proc *)0;
294 bawrite(bp);
295 } else {
296 bp->b_proc = (struct proc *)0;
297 bdwrite(bp);
298 }
299 } while (error == 0 && uio->uio_resid > 0 && n != 0);
300 #ifdef notdef
301 /* Should we try and do this for nfs ?? */
302 if (error && (ioflag & IO_UNIT)) {
303 np->n_size = osize;
304 uio->uio_offset -= cnt - uio->uio_resid;
305 uio->uio_resid = cnt;
306 }
307 #endif
308 return (error);
309 }
310