xref: /386bsd/usr/src/kernel/nfs/nfs_bio.c (revision a2142627)
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  *	$Id: nfs_bio.c,v 1.1 94/10/20 10:57:29 root Exp $
37  */
38 
39 #include "sys/param.h"
40 #include "sys/file.h"
41 #include "sys/mount.h"
42 #include "uio.h"
43 #include "ucred.h"
44 #include "sys/time.h"
45 #include "sys/errno.h"
46 #include "buf.h"
47 
48 #include "namei.h"
49 #include "vnode.h"
50 #include "nfs_node.h"
51 #include "nfs_v2.h"
52 #include "nfs.h"
53 #include "nfs_iom.h"
54 #include "nfs_mount.h"
55 #include "prototypes.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 DIAGNOSTICx
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 	/*
236 	 * I use nm_rsize, not nm_wsize so that all buffer cache blocks
237 	 * will be the same size within a filesystem. nfs_writerpc will
238 	 * still use nm_wsize when sizing the rpc's.
239 	 */
240 	biosize = VFSTONFS(vp->v_mount)->nm_rsize;
241 	np->n_flag |= NMODIFIED;
242 	do {
243 		nfsstats.biocache_writes++;
244 		lbn = uio->uio_offset / biosize;
245 		on = uio->uio_offset & (biosize-1);
246 		n = min((unsigned)(biosize - on), uio->uio_resid);
247 		if (uio->uio_offset+n > np->n_size) {
248 			np->n_size = uio->uio_offset+n;
249 			vnode_pager_setsize(vp, np->n_size);
250 		}
251 		bn = lbn*(biosize/DEV_BSIZE);
252 again:
253 		bp = getblk(vp, bn, biosize);
254 		if (bp->b_wcred == NOCRED) {
255 			crhold(cred);
256 			bp->b_wcred = cred;
257 		}
258 		if (bp->b_dirtyend > 0) {
259 			/*
260 			 * If the new write will leave a contiguous dirty
261 			 * area, just update the b_dirtyoff and b_dirtyend,
262 			 * otherwise force a write rpc of the old dirty area.
263 			 */
264 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
265 				bp->b_dirtyoff = min(on, bp->b_dirtyoff);
266 				bp->b_dirtyend = max((on+n), bp->b_dirtyend);
267 			} else {
268 				bp->b_proc = p;
269 				if (error = bwrite(bp))
270 					return (error);
271 				goto again;
272 			}
273 		} else {
274 			bp->b_dirtyoff = on;
275 			bp->b_dirtyend = on+n;
276 		}
277 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
278 			brelse(bp);
279 			return (error);
280 		}
281 		if ((n+on) == biosize) {
282 			bp->b_flags |= B_AGE;
283 			bp->b_proc = (struct proc *)0;
284 			bawrite(bp);
285 		} else {
286 			bp->b_proc = (struct proc *)0;
287 			bdwrite(bp, vp);
288 		}
289 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
290 #ifdef notdef
291 	/* Should we try and do this for nfs ?? */
292 	if (error && (ioflag & IO_UNIT)) {
293 		np->n_size = osize;
294 		uio->uio_offset -= cnt - uio->uio_resid;
295 		uio->uio_resid = cnt;
296 	}
297 #endif
298 	return (error);
299 }
300