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