xref: /netbsd-src/sys/nfs/nfs_bio.c (revision 61f282557f0bc41c0b762c629a2f4c14be8b7591)
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  *	@(#)nfs_bio.c	7.19 (Berkeley) 4/16/91
37  */
38 
39 #include "param.h"
40 #include "proc.h"
41 #include "buf.h"
42 #include "uio.h"
43 #include "namei.h"
44 #include "vnode.h"
45 #include "trace.h"
46 #include "mount.h"
47 #include "resourcevar.h"
48 
49 #include "nfsnode.h"
50 #include "nfsv2.h"
51 #include "nfs.h"
52 #include "nfsiom.h"
53 #include "nfsmount.h"
54 
55 /* True and false, how exciting */
56 #define	TRUE	1
57 #define	FALSE	0
58 
59 /*
60  * Vnode op for read using bio
61  * Any similarity to readip() is purely coincidental
62  */
63 nfs_bioread(vp, uio, ioflag, cred)
64 	register struct vnode *vp;
65 	register struct uio *uio;
66 	int ioflag;
67 	struct ucred *cred;
68 {
69 	register struct nfsnode *np = VTONFS(vp);
70 	register int biosize;
71 	struct buf *bp;
72 	struct vattr vattr;
73 	daddr_t lbn, bn, rablock;
74 	int diff, error = 0;
75 	long n, on;
76 
77 #ifdef lint
78 	ioflag = ioflag;
79 #endif /* lint */
80 #ifdef DIAGNOSTIC
81 	if (uio->uio_rw != UIO_READ)
82 		panic("nfs_read mode");
83 #endif
84 	if (uio->uio_resid == 0)
85 		return (0);
86 	if (uio->uio_offset < 0 && vp->v_type != VDIR)
87 		return (EINVAL);
88 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
89 	/*
90 	 * If the file's modify time on the server has changed since the
91 	 * last read rpc or you have written to the file,
92 	 * you may have lost data cache consistency with the
93 	 * server, so flush all of the file's data out of the cache.
94 	 * Then force a getattr rpc to ensure that you have up to date
95 	 * attributes.
96 	 * NB: This implies that cache data can be read when up to
97 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
98 	 * attributes this could be forced by setting n_attrstamp to 0 before
99 	 * the nfs_dogetattr() call.
100 	 */
101 	if (vp->v_type != VLNK) {
102 		if (np->n_flag & NMODIFIED) {
103 			np->n_flag &= ~NMODIFIED;
104 			vinvalbuf(vp, TRUE);
105 			np->n_attrstamp = 0;
106 			np->n_direofoffset = 0;
107 			if (error = nfs_dogetattr(vp, &vattr, cred, 1,
108 			    uio->uio_procp))
109 				return (error);
110 			np->n_mtime = vattr.va_mtime.tv_sec;
111 		} else {
112 			if (error = nfs_dogetattr(vp, &vattr, cred, 1,
113 			    uio->uio_procp))
114 				return (error);
115 			if (np->n_mtime != vattr.va_mtime.tv_sec) {
116 				np->n_direofoffset = 0;
117 				vinvalbuf(vp, TRUE);
118 				np->n_mtime = vattr.va_mtime.tv_sec;
119 			}
120 		}
121 	}
122 	do {
123 	    switch (vp->v_type) {
124 	    case VREG:
125 		nfsstats.biocache_reads++;
126 		lbn = uio->uio_offset / biosize;
127 		on = uio->uio_offset & (biosize-1);
128 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
129 		diff = np->n_size - uio->uio_offset;
130 		if (diff <= 0)
131 			return (error);
132 		if (diff < n)
133 			n = diff;
134 		bn = lbn*(biosize/DEV_BSIZE);
135 		rablock = (lbn+1)*(biosize/DEV_BSIZE);
136 		if (vp->v_lastr + 1 == lbn &&
137 		    np->n_size > (rablock * DEV_BSIZE))
138 			error = breada(vp, bn, biosize, rablock, biosize,
139 				cred, &bp);
140 		else
141 			error = bread(vp, bn, biosize, cred, &bp);
142 		vp->v_lastr = lbn;
143 		if (bp->b_resid) {
144 		   diff = (on >= (biosize-bp->b_resid)) ? 0 :
145 			(biosize-bp->b_resid-on);
146 		   n = MIN(n, diff);
147 		}
148 		break;
149 	    case VLNK:
150 		nfsstats.biocache_readlinks++;
151 		on = 0;
152 		error = bread(vp, (daddr_t)0, NFS_MAXPATHLEN, cred, &bp);
153 		n = MIN(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
154 		break;
155 	    case VDIR:
156 		nfsstats.biocache_readdirs++;
157 		on = 0;
158 		error = bread(vp, uio->uio_offset, NFS_DIRBLKSIZ, cred, &bp);
159 		n = MIN(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid);
160 		break;
161 	    };
162 	    if (error) {
163 		brelse(bp);
164 		return (error);
165 	    }
166 	    if (n > 0)
167 		error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
168 	    switch (vp->v_type) {
169 	    case VREG:
170 		if (n+on == biosize || uio->uio_offset == np->n_size)
171 			bp->b_flags |= B_AGE;
172 		break;
173 	    case VLNK:
174 		n = 0;
175 		break;
176 	    case VDIR:
177 		uio->uio_offset = bp->b_blkno;
178 		break;
179 	    };
180 	    brelse(bp);
181 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
182 	return (error);
183 }
184 
185 /*
186  * Vnode op for write using bio
187  */
188 nfs_write(vp, uio, ioflag, cred)
189 	register struct vnode *vp;
190 	register struct uio *uio;
191 	int ioflag;
192 	struct ucred *cred;
193 {
194 	struct proc *p = uio->uio_procp;
195 	register int biosize;
196 	struct buf *bp;
197 	struct nfsnode *np = VTONFS(vp);
198 	struct vattr vattr;
199 	daddr_t lbn, bn;
200 	int n, on, error = 0;
201 
202 #ifdef DIAGNOSTIC
203 	if (uio->uio_rw != UIO_WRITE)
204 		panic("nfs_write mode");
205 	if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
206 		panic("nfs_write proc");
207 #endif
208 	if (vp->v_type != VREG)
209 		return (EIO);
210 	/* Should we try and do this ?? */
211 	if (ioflag & (IO_APPEND | IO_SYNC)) {
212 		if (np->n_flag & NMODIFIED) {
213 			np->n_flag &= ~NMODIFIED;
214 			vinvalbuf(vp, TRUE);
215 		}
216 		if (ioflag & IO_APPEND) {
217 			np->n_attrstamp = 0;
218 			if (error = nfs_dogetattr(vp, &vattr, cred, 1, p))
219 				return (error);
220 			uio->uio_offset = np->n_size;
221 		}
222 		return (nfs_writerpc(vp, uio, cred));
223 	}
224 #ifdef notdef
225 	cnt = uio->uio_resid;
226 	osize = np->n_size;
227 #endif
228 	if (uio->uio_offset < 0)
229 		return (EINVAL);
230 	if (uio->uio_resid == 0)
231 		return (0);
232 	/*
233 	 * Maybe this should be above the vnode op call, but so long as
234 	 * file servers have no limits, i don't think it matters
235 	 */
236 	if (uio->uio_offset + uio->uio_resid >
237 	      p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
238 		psignal(p, SIGXFSZ);
239 		return (EFBIG);
240 	}
241 	/*
242 	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
243 	 * will be the same size within a filesystem. nfs_writerpc will
244 	 * still use nm_wsize when sizing the rpc's.
245 	 */
246 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
247 	np->n_flag |= NMODIFIED;
248 	do {
249 		nfsstats.biocache_writes++;
250 		lbn = uio->uio_offset / biosize;
251 		on = uio->uio_offset & (biosize-1);
252 		n = MIN((unsigned)(biosize - on), uio->uio_resid);
253 		if (uio->uio_offset+n > np->n_size) {
254 			np->n_size = uio->uio_offset+n;
255 			vnode_pager_setsize(vp, np->n_size);
256 		}
257 		bn = lbn*(biosize/DEV_BSIZE);
258 again:
259 		bp = getblk(vp, bn, biosize);
260 		if (bp->b_wcred == NOCRED) {
261 			crhold(cred);
262 			bp->b_wcred = cred;
263 		}
264 		if (bp->b_dirtyend > 0) {
265 			/*
266 			 * If the new write will leave a contiguous dirty
267 			 * area, just update the b_dirtyoff and b_dirtyend,
268 			 * otherwise force a write rpc of the old dirty area.
269 			 */
270 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
271 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
272 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
273 			} else {
274 				bp->b_proc = p;
275 				if (error = bwrite(bp))
276 					return (error);
277 				goto again;
278 			}
279 		} else {
280 			bp->b_dirtyoff = on;
281 			bp->b_dirtyend = on+n;
282 		}
283 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
284 			brelse(bp);
285 			return (error);
286 		}
287 		if ((n+on) == biosize) {
288 			bp->b_flags |= B_AGE;
289 			bp->b_proc = (struct proc *)0;
290 			bawrite(bp);
291 		} else {
292 			bp->b_proc = (struct proc *)0;
293 			bdwrite(bp);
294 		}
295 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
296 #ifdef notdef
297 	/* Should we try and do this for nfs ?? */
298 	if (error && (ioflag & IO_UNIT)) {
299 		np->n_size = osize;
300 		uio->uio_offset -= cnt - uio->uio_resid;
301 		uio->uio_resid = cnt;
302 	}
303 #endif
304 	return (error);
305 }
306