xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 42241)
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.14 (Berkeley) 05/18/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 	struct uio *uio;
46 	int ioflag;
47 	struct ucred *cred;
48 {
49 	register struct nfsnode *np = VTONFS(vp);
50 	struct buf *bp;
51 	struct vattr vattr;
52 	daddr_t lbn, bn, rablock;
53 	int diff, error = 0;
54 	long n, on;
55 
56 #ifdef lint
57 	ioflag = ioflag;
58 #endif /* lint */
59 	if (uio->uio_rw != UIO_READ)
60 		panic("nfs_read mode");
61 	if (uio->uio_resid == 0)
62 		return (0);
63 	if (uio->uio_offset < 0 && vp->v_type != VDIR)
64 		return (EINVAL);
65 	/*
66 	 * If the file's modify time on the server has changed since the
67 	 * last read rpc or you have written to the file,
68 	 * you may have lost data cache consistency with the
69 	 * server, so flush all of the file's data out of the cache.
70 	 * Then force a getattr rpc to ensure that you have up to date
71 	 * attributes.
72 	 * NB: This implies that cache data can be read when up to
73 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
74 	 * attributes this could be forced by setting n_attrstamp to 0 before
75 	 * the nfs_getattr() call.
76 	 */
77 	if (vp->v_type != VLNK) {
78 		if (np->n_flag & NMODIFIED) {
79 			np->n_flag &= ~NMODIFIED;
80 			vinvalbuf(vp, TRUE);
81 			np->n_attrstamp = 0;
82 			np->n_direofoffset = 0;
83 			if (error = nfs_getattr(vp, &vattr, cred))
84 				return (error);
85 			np->n_mtime = vattr.va_mtime.tv_sec;
86 		} else {
87 			if (error = nfs_getattr(vp, &vattr, cred))
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 >> NFS_BIOSHIFT;
101 		on = uio->uio_offset & (NFS_BIOSIZE-1);
102 		n = MIN((unsigned)(NFS_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*(NFS_BIOSIZE/DEV_BSIZE);
109 		rablock = (lbn+1)*(NFS_BIOSIZE/DEV_BSIZE);
110 		if (vp->v_lastr + 1 == lbn &&
111 		    np->n_size > (rablock * DEV_BSIZE))
112 			error = breada(vp, bn, NFS_BIOSIZE, rablock, NFS_BIOSIZE,
113 				cred, &bp);
114 		else
115 			error = bread(vp, bn, NFS_BIOSIZE, cred, &bp);
116 		vp->v_lastr = lbn;
117 		if (bp->b_resid) {
118 		   diff = (on >= (NFS_BIOSIZE-bp->b_resid)) ? 0 :
119 			(NFS_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, DIRBLKSIZ, cred, &bp);
133 		n = MIN(uio->uio_resid, 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 == NFS_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 buf *bp;
169 	struct nfsnode *np = VTONFS(vp);
170 	struct vattr vattr;
171 	daddr_t lbn, bn;
172 	int n, on, error = 0;
173 
174 	if (uio->uio_rw != UIO_WRITE)
175 		panic("nfs_write mode");
176 	if (vp->v_type != VREG)
177 		return (EIO);
178 	/* Should we try and do this ?? */
179 	if (ioflag & IO_APPEND) {
180 		if (np->n_flag & NMODIFIED) {
181 			np->n_flag &= ~NMODIFIED;
182 			vinvalbuf(vp, TRUE);
183 		}
184 		np->n_attrstamp = 0;
185 		if (error = nfs_getattr(vp, &vattr, cred))
186 			return (error);
187 		uio->uio_offset = np->n_size;
188 		return (nfs_writerpc(vp, uio, cred, u.u_procp));
189 	}
190 #ifdef notdef
191 	cnt = uio->uio_resid;
192 	osize = np->n_size;
193 #endif
194 	if (uio->uio_offset < 0)
195 		return (EINVAL);
196 	if (uio->uio_resid == 0)
197 		return (0);
198 	/*
199 	 * Maybe this should be above the vnode op call, but so long as
200 	 * file servers have no limits, i don't think it matters
201 	 */
202 	if (uio->uio_offset + uio->uio_resid >
203 	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
204 		psignal(u.u_procp, SIGXFSZ);
205 		return (EFBIG);
206 	}
207 	np->n_flag |= NMODIFIED;
208 	do {
209 		nfsstats.biocache_writes++;
210 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
211 		on = uio->uio_offset & (NFS_BIOSIZE-1);
212 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
213 		if (uio->uio_offset+n > np->n_size)
214 			np->n_size = uio->uio_offset+n;
215 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
216 again:
217 		bp = getblk(vp, bn, NFS_BIOSIZE);
218 		if (bp->b_wcred == NOCRED) {
219 			crhold(cred);
220 			bp->b_wcred = cred;
221 		}
222 		if (bp->b_dirtyend > 0) {
223 			/*
224 			 * If the new write will leave a contiguous dirty
225 			 * area, just update the b_dirtyoff and b_dirtyend,
226 			 * otherwise force a write rpc of the old dirty area.
227 			 */
228 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
229 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
230 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
231 			} else {
232 				bp->b_proc = u.u_procp;
233 				if (error = bwrite(bp))
234 					return (error);
235 				goto again;
236 			}
237 		} else {
238 			bp->b_dirtyoff = on;
239 			bp->b_dirtyend = on+n;
240 		}
241 		if (error = uiomove(bp->b_un.b_addr + on, n, uio)) {
242 			brelse(bp);
243 			return (error);
244 		}
245 		if ((n+on) == NFS_BIOSIZE) {
246 			bp->b_flags |= B_AGE;
247 			bp->b_proc = (struct proc *)0;
248 			bawrite(bp);
249 		} else {
250 			bp->b_proc = (struct proc *)0;
251 			bdwrite(bp);
252 		}
253 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
254 #ifdef notdef
255 	/* Should we try and do this for nfs ?? */
256 	if (error && (ioflag & IO_UNIT)) {
257 		np->n_size = osize;
258 		uio->uio_offset -= cnt - uio->uio_resid;
259 		uio->uio_resid = cnt;
260 	}
261 #endif
262 	return (error);
263 }
264