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