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