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