xref: /csrg-svn/sys/nfs/nfs_bio.c (revision 39750)
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.8 (Berkeley) 12/20/89
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 
34 /* True and false, how exciting */
35 #define	TRUE	1
36 #define	FALSE	0
37 
38 /*
39  * Vnode op for read using bio
40  * Any similarity to readip() is purely coincidental
41  */
42 nfs_read(vp, uio, ioflag, cred)
43 	register struct vnode *vp;
44 	struct uio *uio;
45 	int ioflag;
46 	struct ucred *cred;
47 {
48 	register struct nfsnode *np = VTONFS(vp);
49 	struct buf *bp;
50 	struct vattr vattr;
51 	daddr_t lbn, bn, rablock;
52 	int diff, error = 0;
53 	long n, on;
54 
55 	/*
56 	 * Avoid caching directories. Once everything is using getdirentries()
57 	 * this will never happen anyhow.
58 	 */
59 	if (vp->v_type == VDIR)
60 		return (nfs_readrpc(vp, uio, cred));
61 	if (uio->uio_rw != UIO_READ)
62 		panic("nfs_read mode");
63 	if (vp->v_type != VREG)
64 		panic("nfs_read type");
65 	if (uio->uio_resid == 0)
66 		return (0);
67 	if (uio->uio_offset < 0)
68 		return (EINVAL);
69 	/*
70 	 * If the file's modify time on the server has changed since the
71 	 * last read rpc or you have written to the file,
72 	 * you may have lost data cache consistency with the
73 	 * server, so flush all of the file's data out of the cache.
74 	 * This will implicitly bring the modify time up to date, since
75 	 * up to date attributes are returned in the reply to any write rpc's
76 	 * NB: This implies that cache data can be read when up to
77 	 * NFS_ATTRTIMEO seconds out of date. If you find that you need current
78 	 * attributes this could be forced by setting n_attrstamp to 0 before
79 	 * the nfs_getattr() call.
80 	 */
81 	if (np->n_flag & NMODIFIED) {
82 		np->n_flag &= ~NMODIFIED;
83 		if (vp->v_blockh && vinvalbuf(vp, TRUE)) {
84 			if (error = nfs_getattr(vp, &vattr, cred))
85 				return (error);
86 			np->n_mtime = vattr.va_mtime.tv_sec;
87 		}
88 	} else if (vp->v_blockh) {
89 		if (error = nfs_getattr(vp, &vattr, cred))
90 			return (error);
91 		if (np->n_mtime != vattr.va_mtime.tv_sec) {
92 			vinvalbuf(vp, TRUE);
93 			np->n_mtime = vattr.va_mtime.tv_sec;
94 		}
95 	}
96 	np->n_flag |= NBUFFERED;
97 	do {
98 		nfsstats.biocache_reads++;
99 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
100 		on = uio->uio_offset & (NFS_BIOSIZE-1);
101 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
102 		diff = np->n_size - uio->uio_offset;
103 		if (diff <= 0)
104 			return (error);
105 		if (diff < n)
106 			n = diff;
107 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
108 		rablock = (lbn+1)*(NFS_BIOSIZE/DEV_BSIZE);
109 		if (np->n_lastr+1 == lbn && np->n_size > (rablock*DEV_BSIZE))
110 			error = breada(vp, bn, NFS_BIOSIZE, rablock, NFS_BIOSIZE,
111 				cred, &bp);
112 		else
113 			error = bread(vp, bn, NFS_BIOSIZE, cred, &bp);
114 		np->n_lastr = lbn;
115 		if (bp->b_resid) {
116 			diff = (on >= (NFS_BIOSIZE-bp->b_resid)) ? 0 :
117 				(NFS_BIOSIZE-bp->b_resid-on);
118 			n = MIN(n, diff);
119 		}
120 		if (error) {
121 			brelse(bp);
122 			return (error);
123 		}
124 		if (n > 0)
125 			error = uiomove(bp->b_un.b_addr + on, (int)n, uio);
126 		if (n+on == NFS_BIOSIZE || uio->uio_offset == np->n_size)
127 			bp->b_flags |= B_AGE;
128 		brelse(bp);
129 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
130 	return (error);
131 }
132 
133 /*
134  * Vnode op for write using bio
135  */
136 nfs_write(vp, uio, ioflag, cred)
137 	register struct vnode *vp;
138 	register struct uio *uio;
139 	int ioflag;
140 	struct ucred *cred;
141 {
142 	struct buf *bp;
143 	struct nfsnode *np = VTONFS(vp);
144 	daddr_t lbn, bn;
145 	int i, n, on, count, error = 0;
146 
147 	/* Should we try and do this ?? */
148 	if (vp->v_type == VREG && (ioflag & IO_APPEND))
149 		uio->uio_offset = np->n_size;
150 #ifdef notdef
151 	cnt = uio->uio_resid;
152 	osize = np->n_size;
153 #endif
154 	if (uio->uio_rw != UIO_WRITE)
155 		panic("nfs_write mode");
156 	if (vp->v_type != VREG)
157 		panic("nfs_write type");
158 	if (uio->uio_offset < 0)
159 		return (EINVAL);
160 	if (uio->uio_resid == 0)
161 		return (0);
162 	/*
163 	 * Maybe this should be above the vnode op call, but so long as
164 	 * file servers have no limits, i don't think it matters
165 	 */
166 	if (vp->v_type == VREG &&
167 	    uio->uio_offset + uio->uio_resid >
168 	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
169 		psignal(u.u_procp, SIGXFSZ);
170 		return (EFBIG);
171 	}
172 	np->n_flag |= (NMODIFIED|NBUFFERED);
173 	do {
174 		nfsstats.biocache_writes++;
175 		lbn = uio->uio_offset >> NFS_BIOSHIFT;
176 		on = uio->uio_offset & (NFS_BIOSIZE-1);
177 		n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid);
178 		if (uio->uio_offset+n > np->n_size)
179 			np->n_size = uio->uio_offset+n;
180 		bn = lbn*(NFS_BIOSIZE/DEV_BSIZE);
181 		count = howmany(NFS_BIOSIZE, CLBYTES);
182 		for (i = 0; i < count; i++)
183 			munhash(vp, bn + i * CLBYTES / DEV_BSIZE);
184 		bp = getblk(vp, bn, NFS_BIOSIZE);
185 		if (bp->b_wcred == NOCRED) {
186 			crhold(cred);
187 			bp->b_wcred = cred;
188 		}
189 		if (bp->b_dirtyend > 0) {
190 			/*
191 			 * If the new write will leave a contiguous
192 			 * dirty area, just update the b_dirtyoff and
193 			 * b_dirtyend
194 			 * otherwise force a write rpc of the old dirty
195 			 * area
196 			 */
197 			if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) {
198 				bp->b_dirtyoff = MIN(on, bp->b_dirtyoff);
199 				bp->b_dirtyend = MAX((on+n), bp->b_dirtyend);
200 			} else {
201 				/*
202 				 * Like bwrite() but without the brelse
203 				 */
204 				bp->b_flags &= ~(B_READ | B_DONE |
205 				    B_ERROR | B_DELWRI | B_ASYNC);
206 				u.u_ru.ru_oublock++;
207 				VOP_STRATEGY(bp);
208 				error = biowait(bp);
209 				if (bp->b_flags & B_ERROR) {
210 					brelse(bp);
211 					if (bp->b_error)
212 						error = bp->b_error;
213 					else
214 						error = EIO;
215 					return (error);
216 				}
217 				bp->b_dirtyoff = on;
218 				bp->b_dirtyend = on+n;
219 			}
220 		} else {
221 			bp->b_dirtyoff = on;
222 			bp->b_dirtyend = on+n;
223 		}
224 		if (error = uiomove(bp->b_un.b_addr + on, n, uio))
225 			return (error);
226 		if ((n+on) == NFS_BIOSIZE) {
227 			bp->b_flags |= B_AGE;
228 			bawrite(bp);
229 		} else {
230 			bdwrite(bp);
231 		}
232 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
233 #ifdef notdef
234 	/* Should we try and do this for nfs ?? */
235 	if (error && (ioflag & IO_UNIT)) {
236 		np->n_size = osize;
237 		uio->uio_offset -= cnt - uio->uio_resid;
238 		uio->uio_resid = cnt;
239 	}
240 #endif
241 	return (error);
242 }
243