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.1 (Berkeley) 08/30/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 "nfsiom.h" 31 32 /* True and false, how exciting */ 33 #define TRUE 1 34 #define FALSE 0 35 36 /* 37 * Vnode op for read using bio 38 * Any similarity to readip() is purely coincidental 39 */ 40 nfs_read(vp, uio, offp, ioflag, cred) 41 register struct vnode *vp; 42 struct uio *uio; 43 off_t *offp; 44 int ioflag; 45 struct ucred *cred; 46 { 47 register struct nfsnode *np = VTONFS(vp); 48 struct buf *bp; 49 struct vattr vattr; 50 daddr_t lbn, bn, rablock; 51 int error = 0; 52 int diff; 53 long n, on; 54 int count; 55 56 if (!(ioflag & IO_NODELOCKED)) 57 nfs_lock(vp); 58 uio->uio_offset = *offp; 59 /* 60 * Avoid caching directories. Once everything is using getdirentries() 61 * this will never happen anyhow. 62 */ 63 if (vp->v_type == VDIR) { 64 error = nfs_readrpc(vp, uio, offp, cred); 65 goto out; 66 } 67 count = uio->uio_resid; 68 if (uio->uio_rw != UIO_READ) 69 panic("nfs_read mode"); 70 if (vp->v_type != VREG) 71 panic("nfs_read type"); 72 if (uio->uio_resid == 0) 73 goto out; 74 if (uio->uio_offset < 0) { 75 error = EINVAL; 76 goto out; 77 } 78 /* 79 * If the file's modify time on the server has changed since the 80 * last read rpc or you have written to the file, 81 * you may have lost data cache consistency with the 82 * server, so flush all of the file's data out of the cache. 83 * This will implicitly bring the modify time up to date, since 84 * up to date attributes are returned in the reply to any write rpc's 85 * NB: This implies that cache data can be read when up to 86 * NFS_ATTRTIMEO seconds out of date. If you find that you need current 87 * attributes this could be forced by setting n_attrstamp to 0 before 88 * the nfs_getattr() call. 89 */ 90 if (np->n_flag & NMODIFIED) { 91 np->n_flag &= ~NMODIFIED; 92 if (error = nfs_blkflush(vp, (daddr_t)0, np->n_size, FALSE)) 93 goto out; 94 if (error = nfs_getattr(vp, &vattr, cred)) 95 goto out; 96 np->n_size = vattr.va_size; 97 np->n_mtime = vattr.va_mtime.tv_sec; 98 } else { 99 if (error = nfs_getattr(vp, &vattr, cred)) 100 goto out; 101 if (np->n_mtime != vattr.va_mtime.tv_sec) { 102 if (error = nfs_blkflush(vp, (daddr_t)0, np->n_size, FALSE)) 103 goto out; 104 np->n_size = vattr.va_size; 105 np->n_mtime = vattr.va_mtime.tv_sec; 106 } 107 } 108 do { 109 lbn = uio->uio_offset >> NFS_BIOSHIFT; 110 on = uio->uio_offset & (NFS_BIOSIZE-1); 111 n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid); 112 diff = np->n_size - uio->uio_offset; 113 if (diff <= 0) 114 goto out; 115 if (diff < n) 116 n = diff; 117 bn = lbn*(NFS_BIOSIZE/DEV_BSIZE); 118 rablock = (lbn+1)*(NFS_BIOSIZE/DEV_BSIZE); 119 if (np->n_lastr+1 == lbn) 120 error = breada(vp, bn, NFS_BIOSIZE, rablock, NFS_BIOSIZE, 121 cred, &bp); 122 else 123 error = bread(vp, bn, NFS_BIOSIZE, cred, &bp); 124 np->n_lastr = lbn; 125 if (bp->b_resid) { 126 diff = (on >= (NFS_BIOSIZE-bp->b_resid)) ? 0 : 127 (NFS_BIOSIZE-bp->b_resid-on); 128 n = MIN(n, diff); 129 } 130 if (error) { 131 brelse(bp); 132 goto out; 133 } 134 if (n > 0) 135 error = uiomove(bp->b_un.b_addr + on, (int)n, uio); 136 if (n+on == NFS_BIOSIZE || uio->uio_offset == np->n_size) 137 bp->b_flags |= B_AGE; 138 brelse(bp); 139 } while (error == 0 && uio->uio_resid > 0 && n != 0); 140 out: 141 *offp = uio->uio_offset; 142 if ((ioflag & IO_NODELOCKED) == 0) 143 nfs_unlock(vp); 144 return (error); 145 } 146 147 /* 148 * Vnode op for write using bio 149 */ 150 nfs_write(vp, uio, offp, ioflag, cred) 151 register struct vnode *vp; 152 register struct uio *uio; 153 off_t *offp; 154 int ioflag; 155 struct ucred *cred; 156 { 157 struct buf *bp; 158 struct nfsnode *np = VTONFS(vp); 159 daddr_t lbn, bn; 160 int i, n, on; 161 int flags, count, size; 162 int error = 0; 163 int cnt; 164 u_long osize; 165 166 if ((ioflag & IO_NODELOCKED) == 0) 167 nfs_lock(vp); 168 /* Should we try and do this ?? */ 169 if (vp->v_type == VREG && (ioflag & IO_APPEND)) 170 *offp = np->n_size; 171 uio->uio_offset = *offp; 172 cnt = uio->uio_resid; 173 #ifdef notdef 174 osize = np->n_size; 175 #endif 176 if (uio->uio_rw != UIO_WRITE) 177 panic("nfs_write mode"); 178 if (vp->v_type != VREG) 179 panic("nfs_write type"); 180 if (uio->uio_offset < 0) { 181 error = EINVAL; 182 goto out; 183 } 184 if (uio->uio_resid == 0) 185 goto out; 186 /* 187 * Maybe this should be above the vnode op call, but so long as 188 * file servers have no limits, i don't think it matters 189 */ 190 if (vp->v_type == VREG && 191 uio->uio_offset + uio->uio_resid > 192 u.u_rlimit[RLIMIT_FSIZE].rlim_cur) { 193 psignal(u.u_procp, SIGXFSZ); 194 error = EFBIG; 195 goto out; 196 } 197 np->n_flag |= NMODIFIED; 198 do { 199 lbn = uio->uio_offset >> NFS_BIOSHIFT; 200 on = uio->uio_offset & (NFS_BIOSIZE-1); 201 n = MIN((unsigned)(NFS_BIOSIZE - on), uio->uio_resid); 202 if (uio->uio_offset+n > np->n_size) 203 np->n_size = uio->uio_offset+n; 204 bn = lbn*(NFS_BIOSIZE/DEV_BSIZE); 205 count = howmany(NFS_BIOSIZE, CLBYTES); 206 for (i = 0; i < count; i++) 207 munhash(vp, bn + i * CLBYTES / DEV_BSIZE); 208 bp = getblk(vp, bn, NFS_BIOSIZE); 209 if (bp->b_wcred == NOCRED) { 210 crhold(cred); 211 bp->b_wcred = cred; 212 } 213 if (bp->b_dirtyend > 0) { 214 /* 215 * Iff the new write will leave a contiguous 216 * dirty area, just update the b_dirtyoff and 217 * b_dirtyend 218 * otherwise force a write rpc of the old dirty 219 * area 220 */ 221 if (on <= bp->b_dirtyend && (on+n) >= bp->b_dirtyoff) { 222 bp->b_dirtyoff = MIN(on, bp->b_dirtyoff); 223 bp->b_dirtyend = MAX((on+n), bp->b_dirtyend); 224 } else { 225 /* 226 * Like bwrite() but without the brelse 227 */ 228 bp->b_flags &= ~(B_READ | B_DONE | 229 B_ERROR | B_DELWRI | B_ASYNC); 230 u.u_ru.ru_oublock++; 231 VOP_STRATEGY(bp); 232 error = biowait(bp); 233 if (bp->b_flags & B_ERROR) { 234 brelse(bp); 235 if (bp->b_error) 236 error = bp->b_error; 237 else 238 error = EIO; 239 goto out; 240 } 241 bp->b_dirtyoff = on; 242 bp->b_dirtyend = on+n; 243 } 244 } else { 245 bp->b_dirtyoff = on; 246 bp->b_dirtyend = on+n; 247 } 248 if (error = uiomove(bp->b_un.b_addr + on, n, uio)) 249 goto out; 250 if ((n+on) == NFS_BIOSIZE) { 251 bp->b_flags |= B_AGE; 252 bawrite(bp); 253 } else { 254 bdwrite(bp); 255 } 256 } while (error == 0 && uio->uio_resid > 0 && n != 0); 257 #ifdef notdef 258 /* Should we try and do this for nfs ?? */ 259 if (error && (ioflag & IO_UNIT)) 260 np->n_size = osize; 261 else 262 #endif 263 *offp += cnt - uio->uio_resid; 264 out: 265 if ((ioflag & IO_NODELOCKED) == 0) 266 nfs_unlock(vp); 267 return (error); 268 } 269 270 /* 271 * Flush and invalidate all of the buffers associated with the blocks of vp 272 */ 273 nfs_blkflush(vp, blkno, size, invalidate) 274 struct vnode *vp; 275 daddr_t blkno; 276 long size; 277 int invalidate; 278 { 279 register struct buf *ep; 280 struct buf *dp; 281 daddr_t curblkno, last; 282 int s, error, allerrors = 0; 283 284 last = blkno + btodb(size); 285 for (curblkno = blkno; curblkno <= last; 286 curblkno += (NFS_BIOSIZE / DEV_BSIZE)) { 287 dp = BUFHASH(vp, curblkno); 288 loop: 289 for (ep = dp->b_forw; ep != dp; ep = ep->b_forw) { 290 if (ep->b_vp != vp || (ep->b_flags & B_INVAL)) 291 continue; 292 if (curblkno != ep->b_blkno) 293 continue; 294 s = splbio(); 295 if (ep->b_flags & B_BUSY) { 296 ep->b_flags |= B_WANTED; 297 sleep((caddr_t)ep, PRIBIO+1); 298 splx(s); 299 goto loop; 300 } 301 splx(s); 302 notavail(ep); 303 if (ep->b_flags & B_DELWRI) { 304 ep->b_flags &= ~B_ASYNC; 305 if (error = bwrite(ep)) 306 allerrors = error; 307 goto loop; 308 } 309 if (invalidate) { 310 ep->b_flags |= B_INVAL; 311 brelvp(ep); 312 } 313 brelse(ep); 314 } 315 } 316 return (allerrors); 317 } 318