Annotation of sys/nfs/nfs_bio.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: nfs_bio.c,v 1.46 2007/06/01 23:47:57 deraadt Exp $ */
! 2: /* $NetBSD: nfs_bio.c,v 1.25.4.2 1996/07/08 20:47:04 jtc Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1989, 1993
! 6: * The Regents of the University of California. All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to Berkeley by
! 9: * Rick Macklem at The University of Guelph.
! 10: *
! 11: * Redistribution and use in source and binary forms, with or without
! 12: * modification, are permitted provided that the following conditions
! 13: * are met:
! 14: * 1. Redistributions of source code must retain the above copyright
! 15: * notice, this list of conditions and the following disclaimer.
! 16: * 2. Redistributions in binary form must reproduce the above copyright
! 17: * notice, this list of conditions and the following disclaimer in the
! 18: * documentation and/or other materials provided with the distribution.
! 19: * 3. Neither the name of the University nor the names of its contributors
! 20: * may be used to endorse or promote products derived from this software
! 21: * without specific prior written permission.
! 22: *
! 23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 33: * SUCH DAMAGE.
! 34: *
! 35: * @(#)nfs_bio.c 8.9 (Berkeley) 3/30/95
! 36: */
! 37:
! 38: #include <sys/param.h>
! 39: #include <sys/systm.h>
! 40: #include <sys/resourcevar.h>
! 41: #include <sys/signalvar.h>
! 42: #include <sys/proc.h>
! 43: #include <sys/buf.h>
! 44: #include <sys/vnode.h>
! 45: #include <sys/mount.h>
! 46: #include <sys/kernel.h>
! 47: #include <sys/namei.h>
! 48:
! 49: #include <uvm/uvm_extern.h>
! 50:
! 51: #include <nfs/rpcv2.h>
! 52: #include <nfs/nfsproto.h>
! 53: #include <nfs/nfs.h>
! 54: #include <nfs/nfsmount.h>
! 55: #include <nfs/nfsnode.h>
! 56: #include <nfs/nfs_var.h>
! 57:
! 58: extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
! 59: extern int nfs_numasync;
! 60: extern struct nfsstats nfsstats;
! 61: struct nfs_bufqhead nfs_bufq;
! 62:
! 63: /*
! 64: * Vnode op for read using bio
! 65: * Any similarity to readip() is purely coincidental
! 66: */
! 67: int
! 68: nfs_bioread(vp, uio, ioflag, cred)
! 69: struct vnode *vp;
! 70: struct uio *uio;
! 71: int ioflag;
! 72: struct ucred *cred;
! 73: {
! 74: struct nfsnode *np = VTONFS(vp);
! 75: int biosize, diff;
! 76: struct buf *bp = NULL, *rabp;
! 77: struct vattr vattr;
! 78: struct proc *p;
! 79: struct nfsmount *nmp = VFSTONFS(vp->v_mount);
! 80: daddr64_t lbn, bn, rabn;
! 81: caddr_t baddr;
! 82: int got_buf = 0, nra, error = 0, n = 0, on = 0, not_readin;
! 83: off_t offdiff;
! 84:
! 85: #ifdef DIAGNOSTIC
! 86: if (uio->uio_rw != UIO_READ)
! 87: panic("nfs_read mode");
! 88: #endif
! 89: if (uio->uio_resid == 0)
! 90: return (0);
! 91: if (uio->uio_offset < 0)
! 92: return (EINVAL);
! 93: p = uio->uio_procp;
! 94: if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
! 95: (void)nfs_fsinfo(nmp, vp, cred, p);
! 96: biosize = nmp->nm_rsize;
! 97: /*
! 98: * For nfs, cache consistency can only be maintained approximately.
! 99: * Although RFC1094 does not specify the criteria, the following is
! 100: * believed to be compatible with the reference port.
! 101: * For nfs:
! 102: * If the file's modify time on the server has changed since the
! 103: * last read rpc or you have written to the file,
! 104: * you may have lost data cache consistency with the
! 105: * server, so flush all of the file's data out of the cache.
! 106: * Then force a getattr rpc to ensure that you have up to date
! 107: * attributes.
! 108: * NB: This implies that cache data can be read when up to
! 109: * NFS_ATTRTIMEO seconds out of date. If you find that you need current
! 110: * attributes this could be forced by setting n_attrstamp to 0 before
! 111: * the VOP_GETATTR() call.
! 112: */
! 113: if (np->n_flag & NMODIFIED) {
! 114: np->n_attrstamp = 0;
! 115: error = VOP_GETATTR(vp, &vattr, cred, p);
! 116: if (error)
! 117: return (error);
! 118: np->n_mtime = vattr.va_mtime.tv_sec;
! 119: } else {
! 120: error = VOP_GETATTR(vp, &vattr, cred, p);
! 121: if (error)
! 122: return (error);
! 123: if (np->n_mtime != vattr.va_mtime.tv_sec) {
! 124: error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
! 125: if (error)
! 126: return (error);
! 127: np->n_mtime = vattr.va_mtime.tv_sec;
! 128: }
! 129: }
! 130:
! 131: /*
! 132: * update the cache read creds for this vnode
! 133: */
! 134: if (np->n_rcred)
! 135: crfree(np->n_rcred);
! 136: np->n_rcred = cred;
! 137: crhold(cred);
! 138:
! 139: do {
! 140: if ((vp->v_flag & VROOT) && vp->v_type == VLNK) {
! 141: return (nfs_readlinkrpc(vp, uio, cred));
! 142: }
! 143: baddr = (caddr_t)0;
! 144: switch (vp->v_type) {
! 145: case VREG:
! 146: nfsstats.biocache_reads++;
! 147: lbn = uio->uio_offset / biosize;
! 148: on = uio->uio_offset & (biosize - 1);
! 149: bn = lbn * (biosize / DEV_BSIZE);
! 150: not_readin = 1;
! 151:
! 152: /*
! 153: * Start the read ahead(s), as required.
! 154: */
! 155: if (nfs_numasync > 0 && nmp->nm_readahead > 0) {
! 156: for (nra = 0; nra < nmp->nm_readahead &&
! 157: (lbn + 1 + nra) * biosize < np->n_size; nra++) {
! 158: rabn = (lbn + 1 + nra) * (biosize / DEV_BSIZE);
! 159: if (!incore(vp, rabn)) {
! 160: rabp = nfs_getcacheblk(vp, rabn, biosize, p);
! 161: if (!rabp)
! 162: return (EINTR);
! 163: if ((rabp->b_flags & (B_DELWRI | B_DONE)) == 0) {
! 164: rabp->b_flags |= (B_READ | B_ASYNC);
! 165: if (nfs_asyncio(rabp)) {
! 166: rabp->b_flags |= B_INVAL;
! 167: brelse(rabp);
! 168: }
! 169: } else
! 170: brelse(rabp);
! 171: }
! 172: }
! 173: }
! 174:
! 175: /*
! 176: * If the block is in the cache and has the required data
! 177: * in a valid region, just copy it out.
! 178: * Otherwise, get the block and write back/read in,
! 179: * as required.
! 180: */
! 181: if ((bp = incore(vp, bn)) &&
! 182: (bp->b_flags & (B_BUSY | B_WRITEINPROG)) ==
! 183: (B_BUSY | B_WRITEINPROG))
! 184: got_buf = 0;
! 185: else {
! 186: again:
! 187: bp = nfs_getcacheblk(vp, bn, biosize, p);
! 188: if (!bp)
! 189: return (EINTR);
! 190: got_buf = 1;
! 191: if ((bp->b_flags & (B_DONE | B_DELWRI)) == 0) {
! 192: bp->b_flags |= B_READ;
! 193: not_readin = 0;
! 194: error = nfs_doio(bp, p);
! 195: if (error) {
! 196: brelse(bp);
! 197: return (error);
! 198: }
! 199: }
! 200: }
! 201: n = min((unsigned)(biosize - on), uio->uio_resid);
! 202: offdiff = np->n_size - uio->uio_offset;
! 203: if (offdiff < (off_t)n)
! 204: n = (int)offdiff;
! 205: if (not_readin && n > 0) {
! 206: if (on < bp->b_validoff || (on + n) > bp->b_validend) {
! 207: if (!got_buf) {
! 208: bp = nfs_getcacheblk(vp, bn, biosize, p);
! 209: if (!bp)
! 210: return (EINTR);
! 211: got_buf = 1;
! 212: }
! 213: bp->b_flags |= B_INVAFTERWRITE;
! 214: if (bp->b_dirtyend > 0) {
! 215: if ((bp->b_flags & B_DELWRI) == 0)
! 216: panic("nfsbioread");
! 217: if (VOP_BWRITE(bp) == EINTR)
! 218: return (EINTR);
! 219: } else
! 220: brelse(bp);
! 221: goto again;
! 222: }
! 223: }
! 224: diff = (on >= bp->b_validend) ? 0 : (bp->b_validend - on);
! 225: if (diff < n)
! 226: n = diff;
! 227: break;
! 228: case VLNK:
! 229: nfsstats.biocache_readlinks++;
! 230: bp = nfs_getcacheblk(vp, 0, NFS_MAXPATHLEN, p);
! 231: if (!bp)
! 232: return (EINTR);
! 233: if ((bp->b_flags & B_DONE) == 0) {
! 234: bp->b_flags |= B_READ;
! 235: error = nfs_doio(bp, p);
! 236: if (error) {
! 237: brelse(bp);
! 238: return (error);
! 239: }
! 240: }
! 241: n = min(uio->uio_resid, NFS_MAXPATHLEN - bp->b_resid);
! 242: got_buf = 1;
! 243: on = 0;
! 244: break;
! 245: default:
! 246: printf(" nfsbioread: type %x unexpected\n",vp->v_type);
! 247: break;
! 248: }
! 249:
! 250: if (n > 0) {
! 251: if (!baddr)
! 252: baddr = bp->b_data;
! 253: error = uiomove(baddr + on, (int)n, uio);
! 254: }
! 255: switch (vp->v_type) {
! 256: case VREG:
! 257: break;
! 258: case VLNK:
! 259: n = 0;
! 260: break;
! 261: default:
! 262: printf(" nfsbioread: type %x unexpected\n",vp->v_type);
! 263: }
! 264: if (got_buf)
! 265: brelse(bp);
! 266: } while (error == 0 && uio->uio_resid > 0 && n > 0);
! 267: return (error);
! 268: }
! 269:
! 270: /*
! 271: * Vnode op for write using bio
! 272: */
! 273: int
! 274: nfs_write(v)
! 275: void *v;
! 276: {
! 277: struct vop_write_args *ap = v;
! 278: int biosize;
! 279: struct uio *uio = ap->a_uio;
! 280: struct proc *p = uio->uio_procp;
! 281: struct vnode *vp = ap->a_vp;
! 282: struct nfsnode *np = VTONFS(vp);
! 283: struct ucred *cred = ap->a_cred;
! 284: int ioflag = ap->a_ioflag;
! 285: struct buf *bp;
! 286: struct vattr vattr;
! 287: struct nfsmount *nmp = VFSTONFS(vp->v_mount);
! 288: daddr64_t lbn, bn;
! 289: int n, on, error = 0, extended = 0, wrotedta = 0, truncated = 0;
! 290:
! 291: #ifdef DIAGNOSTIC
! 292: if (uio->uio_rw != UIO_WRITE)
! 293: panic("nfs_write mode");
! 294: if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
! 295: panic("nfs_write proc");
! 296: #endif
! 297: if (vp->v_type != VREG)
! 298: return (EIO);
! 299: if (np->n_flag & NWRITEERR) {
! 300: np->n_flag &= ~NWRITEERR;
! 301: return (np->n_error);
! 302: }
! 303: if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
! 304: (void)nfs_fsinfo(nmp, vp, cred, p);
! 305: if (ioflag & (IO_APPEND | IO_SYNC)) {
! 306: if (np->n_flag & NMODIFIED) {
! 307: np->n_attrstamp = 0;
! 308: error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
! 309: if (error)
! 310: return (error);
! 311: }
! 312: if (ioflag & IO_APPEND) {
! 313: np->n_attrstamp = 0;
! 314: error = VOP_GETATTR(vp, &vattr, cred, p);
! 315: if (error)
! 316: return (error);
! 317: uio->uio_offset = np->n_size;
! 318: }
! 319: }
! 320: if (uio->uio_offset < 0)
! 321: return (EINVAL);
! 322: if (uio->uio_resid == 0)
! 323: return (0);
! 324: /*
! 325: * Maybe this should be above the vnode op call, but so long as
! 326: * file servers have no limits, i don't think it matters
! 327: */
! 328: if (p && uio->uio_offset + uio->uio_resid >
! 329: p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
! 330: psignal(p, SIGXFSZ);
! 331: return (EFBIG);
! 332: }
! 333:
! 334: /*
! 335: * update the cache write creds for this node.
! 336: */
! 337: if (np->n_wcred)
! 338: crfree(np->n_wcred);
! 339: np->n_wcred = cred;
! 340: crhold(cred);
! 341:
! 342: /*
! 343: * I use nm_rsize, not nm_wsize so that all buffer cache blocks
! 344: * will be the same size within a filesystem. nfs_writerpc will
! 345: * still use nm_wsize when sizing the rpc's.
! 346: */
! 347: biosize = nmp->nm_rsize;
! 348: do {
! 349:
! 350: /*
! 351: * XXX make sure we aren't cached in the VM page cache
! 352: */
! 353: uvm_vnp_uncache(vp);
! 354:
! 355: nfsstats.biocache_writes++;
! 356: lbn = uio->uio_offset / biosize;
! 357: on = uio->uio_offset & (biosize-1);
! 358: n = min((unsigned)(biosize - on), uio->uio_resid);
! 359: bn = lbn * (biosize / DEV_BSIZE);
! 360: again:
! 361: bp = nfs_getcacheblk(vp, bn, biosize, p);
! 362: if (!bp)
! 363: return (EINTR);
! 364: np->n_flag |= NMODIFIED;
! 365: if (uio->uio_offset + n > np->n_size) {
! 366: np->n_size = uio->uio_offset + n;
! 367: uvm_vnp_setsize(vp, (u_long)np->n_size);
! 368: extended = 1;
! 369: } else if (uio->uio_offset + n < np->n_size)
! 370: truncated = 1;
! 371:
! 372: /*
! 373: * If the new write will leave a contiguous dirty
! 374: * area, just update the b_dirtyoff and b_dirtyend,
! 375: * otherwise force a write rpc of the old dirty area.
! 376: */
! 377: if (bp->b_dirtyend > 0 &&
! 378: (on > bp->b_dirtyend || (on + n) < bp->b_dirtyoff)) {
! 379: bp->b_proc = p;
! 380: if (VOP_BWRITE(bp) == EINTR)
! 381: return (EINTR);
! 382: goto again;
! 383: }
! 384:
! 385: error = uiomove((char *)bp->b_data + on, n, uio);
! 386: if (error) {
! 387: bp->b_flags |= B_ERROR;
! 388: brelse(bp);
! 389: return (error);
! 390: }
! 391: if (bp->b_dirtyend > 0) {
! 392: bp->b_dirtyoff = min(on, bp->b_dirtyoff);
! 393: bp->b_dirtyend = max((on + n), bp->b_dirtyend);
! 394: } else {
! 395: bp->b_dirtyoff = on;
! 396: bp->b_dirtyend = on + n;
! 397: }
! 398: if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff ||
! 399: bp->b_validoff > bp->b_dirtyend) {
! 400: bp->b_validoff = bp->b_dirtyoff;
! 401: bp->b_validend = bp->b_dirtyend;
! 402: } else {
! 403: bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff);
! 404: bp->b_validend = max(bp->b_validend, bp->b_dirtyend);
! 405: }
! 406:
! 407: wrotedta = 1;
! 408:
! 409: /*
! 410: * Since this block is being modified, it must be written
! 411: * again and not just committed.
! 412: */
! 413:
! 414: if (NFS_ISV3(vp)) {
! 415: rw_enter_write(&np->n_commitlock);
! 416: if (bp->b_flags & B_NEEDCOMMIT) {
! 417: bp->b_flags &= ~B_NEEDCOMMIT;
! 418: nfs_del_tobecommitted_range(vp, bp);
! 419: }
! 420: nfs_del_committed_range(vp, bp);
! 421: rw_exit_write(&np->n_commitlock);
! 422: } else
! 423: bp->b_flags &= ~B_NEEDCOMMIT;
! 424:
! 425: /*
! 426: * If the lease is non-cachable or IO_SYNC do bwrite().
! 427: */
! 428: if (ioflag & IO_SYNC) {
! 429: bp->b_proc = p;
! 430: error = VOP_BWRITE(bp);
! 431: if (error)
! 432: return (error);
! 433: } else if ((n + on) == biosize) {
! 434: bp->b_proc = (struct proc *)0;
! 435: bp->b_flags |= B_ASYNC;
! 436: (void)nfs_writebp(bp, 0);
! 437: } else {
! 438: bdwrite(bp);
! 439: }
! 440: } while (uio->uio_resid > 0 && n > 0);
! 441:
! 442: if (wrotedta)
! 443: VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0) |
! 444: (truncated ? NOTE_TRUNCATE : 0));
! 445:
! 446: return (0);
! 447: }
! 448:
! 449: /*
! 450: * Get an nfs cache block.
! 451: * Allocate a new one if the block isn't currently in the cache
! 452: * and return the block marked busy. If the calling process is
! 453: * interrupted by a signal for an interruptible mount point, return
! 454: * NULL.
! 455: */
! 456: struct buf *
! 457: nfs_getcacheblk(vp, bn, size, p)
! 458: struct vnode *vp;
! 459: daddr64_t bn;
! 460: int size;
! 461: struct proc *p;
! 462: {
! 463: struct buf *bp;
! 464: struct nfsmount *nmp = VFSTONFS(vp->v_mount);
! 465:
! 466: if (nmp->nm_flag & NFSMNT_INT) {
! 467: bp = getblk(vp, bn, size, PCATCH, 0);
! 468: while (bp == (struct buf *)0) {
! 469: if (nfs_sigintr(nmp, (struct nfsreq *)0, p))
! 470: return ((struct buf *)0);
! 471: bp = getblk(vp, bn, size, 0, 2 * hz);
! 472: }
! 473: } else
! 474: bp = getblk(vp, bn, size, 0, 0);
! 475: return (bp);
! 476: }
! 477:
! 478: /*
! 479: * Flush and invalidate all dirty buffers. If another process is already
! 480: * doing the flush, just wait for completion.
! 481: */
! 482: int
! 483: nfs_vinvalbuf(vp, flags, cred, p, intrflg)
! 484: struct vnode *vp;
! 485: int flags;
! 486: struct ucred *cred;
! 487: struct proc *p;
! 488: int intrflg;
! 489: {
! 490: struct nfsnode *np = VTONFS(vp);
! 491: struct nfsmount *nmp = VFSTONFS(vp->v_mount);
! 492: int error = 0, slpflag, slptimeo;
! 493:
! 494: if ((nmp->nm_flag & NFSMNT_INT) == 0)
! 495: intrflg = 0;
! 496: if (intrflg) {
! 497: slpflag = PCATCH;
! 498: slptimeo = 2 * hz;
! 499: } else {
! 500: slpflag = 0;
! 501: slptimeo = 0;
! 502: }
! 503: /*
! 504: * First wait for any other process doing a flush to complete.
! 505: */
! 506: while (np->n_flag & NFLUSHINPROG) {
! 507: np->n_flag |= NFLUSHWANT;
! 508: error = tsleep((caddr_t)&np->n_flag, PRIBIO + 2, "nfsvinval",
! 509: slptimeo);
! 510: if (error && intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p))
! 511: return (EINTR);
! 512: }
! 513:
! 514: /*
! 515: * Now, flush as required.
! 516: */
! 517: np->n_flag |= NFLUSHINPROG;
! 518: error = vinvalbuf(vp, flags, cred, p, slpflag, 0);
! 519: while (error) {
! 520: if (intrflg && nfs_sigintr(nmp, (struct nfsreq *)0, p)) {
! 521: np->n_flag &= ~NFLUSHINPROG;
! 522: if (np->n_flag & NFLUSHWANT) {
! 523: np->n_flag &= ~NFLUSHWANT;
! 524: wakeup((caddr_t)&np->n_flag);
! 525: }
! 526: return (EINTR);
! 527: }
! 528: error = vinvalbuf(vp, flags, cred, p, 0, slptimeo);
! 529: }
! 530: np->n_flag &= ~(NMODIFIED | NFLUSHINPROG);
! 531: if (np->n_flag & NFLUSHWANT) {
! 532: np->n_flag &= ~NFLUSHWANT;
! 533: wakeup((caddr_t)&np->n_flag);
! 534: }
! 535: return (0);
! 536: }
! 537:
! 538: /*
! 539: * Initiate asynchronous I/O. Return an error if no nfsiods are available.
! 540: * This is mainly to avoid queueing async I/O requests when the nfsiods
! 541: * are all hung on a dead server.
! 542: */
! 543: int
! 544: nfs_asyncio(bp)
! 545: struct buf *bp;
! 546: {
! 547: int i,s;
! 548:
! 549: if (nfs_numasync == 0)
! 550: return (EIO);
! 551: for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
! 552: if (nfs_iodwant[i]) {
! 553: if ((bp->b_flags & B_READ) == 0) {
! 554: bp->b_flags |= B_WRITEINPROG;
! 555: }
! 556:
! 557: TAILQ_INSERT_TAIL(&nfs_bufq, bp, b_freelist);
! 558: nfs_iodwant[i] = (struct proc *)0;
! 559: wakeup((caddr_t)&nfs_iodwant[i]);
! 560: return (0);
! 561: }
! 562:
! 563: /*
! 564: * If it is a read or a write already marked B_WRITEINPROG or B_NOCACHE
! 565: * return EIO so the process will call nfs_doio() and do it
! 566: * synchronously.
! 567: */
! 568: if (bp->b_flags & (B_READ | B_WRITEINPROG | B_NOCACHE))
! 569: return (EIO);
! 570:
! 571: /*
! 572: * Just turn the async write into a delayed write, instead of
! 573: * doing in synchronously. Hopefully, at least one of the nfsiods
! 574: * is currently doing a write for this file and will pick up the
! 575: * delayed writes before going back to sleep.
! 576: */
! 577: s = splbio();
! 578: buf_dirty(bp);
! 579: biodone(bp);
! 580: splx(s);
! 581: return (0);
! 582: }
! 583:
! 584: /*
! 585: * Do an I/O operation to/from a cache block. This may be called
! 586: * synchronously or from an nfsiod.
! 587: */
! 588: int
! 589: nfs_doio(bp, p)
! 590: struct buf *bp;
! 591: struct proc *p;
! 592: {
! 593: struct uio *uiop;
! 594: struct vnode *vp;
! 595: struct nfsnode *np;
! 596: struct nfsmount *nmp;
! 597: int s, error = 0, diff, len, iomode, must_commit = 0;
! 598: struct uio uio;
! 599: struct iovec io;
! 600:
! 601: vp = bp->b_vp;
! 602: np = VTONFS(vp);
! 603: nmp = VFSTONFS(vp->v_mount);
! 604: uiop = &uio;
! 605: uiop->uio_iov = &io;
! 606: uiop->uio_iovcnt = 1;
! 607: uiop->uio_segflg = UIO_SYSSPACE;
! 608: uiop->uio_procp = p;
! 609:
! 610: /*
! 611: * Historically, paging was done with physio, but no more...
! 612: */
! 613: if (bp->b_flags & B_PHYS) {
! 614: /*
! 615: * ...though reading /dev/drum still gets us here.
! 616: */
! 617: io.iov_len = uiop->uio_resid = bp->b_bcount;
! 618: /* mapping was done by vmapbuf() */
! 619: io.iov_base = bp->b_data;
! 620: uiop->uio_offset = ((off_t)bp->b_blkno) << DEV_BSHIFT;
! 621: if (bp->b_flags & B_READ) {
! 622: uiop->uio_rw = UIO_READ;
! 623: nfsstats.read_physios++;
! 624: error = nfs_readrpc(vp, uiop);
! 625: } else {
! 626: iomode = NFSV3WRITE_DATASYNC;
! 627: uiop->uio_rw = UIO_WRITE;
! 628: nfsstats.write_physios++;
! 629: error = nfs_writerpc(vp, uiop, &iomode, &must_commit);
! 630: }
! 631: if (error) {
! 632: bp->b_flags |= B_ERROR;
! 633: bp->b_error = error;
! 634: }
! 635: } else if (bp->b_flags & B_READ) {
! 636: io.iov_len = uiop->uio_resid = bp->b_bcount;
! 637: io.iov_base = bp->b_data;
! 638: uiop->uio_rw = UIO_READ;
! 639: switch (vp->v_type) {
! 640: case VREG:
! 641: uiop->uio_offset = ((off_t)bp->b_blkno) << DEV_BSHIFT;
! 642: nfsstats.read_bios++;
! 643: error = nfs_readrpc(vp, uiop);
! 644: if (!error) {
! 645: bp->b_validoff = 0;
! 646: if (uiop->uio_resid) {
! 647: /*
! 648: * If len > 0, there is a hole in the file and
! 649: * no writes after the hole have been pushed to
! 650: * the server yet.
! 651: * Just zero fill the rest of the valid area.
! 652: */
! 653: diff = bp->b_bcount - uiop->uio_resid;
! 654: len = np->n_size - ((((off_t)bp->b_blkno) << DEV_BSHIFT)
! 655: + diff);
! 656: if (len > 0) {
! 657: len = min(len, uiop->uio_resid);
! 658: bzero((char *)bp->b_data + diff, len);
! 659: bp->b_validend = diff + len;
! 660: } else
! 661: bp->b_validend = diff;
! 662: } else
! 663: bp->b_validend = bp->b_bcount;
! 664: }
! 665: if (p && (vp->v_flag & VTEXT) &&
! 666: (np->n_mtime != np->n_vattr.va_mtime.tv_sec)) {
! 667: uprintf("Process killed due to text file modification\n");
! 668: psignal(p, SIGKILL);
! 669: }
! 670: break;
! 671: case VLNK:
! 672: uiop->uio_offset = (off_t)0;
! 673: nfsstats.readlink_bios++;
! 674: error = nfs_readlinkrpc(vp, uiop, curproc->p_ucred);
! 675: break;
! 676: default:
! 677: printf("nfs_doio: type %x unexpected\n", vp->v_type);
! 678: break;
! 679: };
! 680: if (error) {
! 681: bp->b_flags |= B_ERROR;
! 682: bp->b_error = error;
! 683: }
! 684: } else {
! 685: io.iov_len = uiop->uio_resid = bp->b_dirtyend
! 686: - bp->b_dirtyoff;
! 687: uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE
! 688: + bp->b_dirtyoff;
! 689: io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
! 690: uiop->uio_rw = UIO_WRITE;
! 691: nfsstats.write_bios++;
! 692: if ((bp->b_flags & (B_ASYNC | B_NEEDCOMMIT | B_NOCACHE)) == B_ASYNC)
! 693: iomode = NFSV3WRITE_UNSTABLE;
! 694: else
! 695: iomode = NFSV3WRITE_FILESYNC;
! 696: bp->b_flags |= B_WRITEINPROG;
! 697: error = nfs_writerpc(vp, uiop, &iomode, &must_commit);
! 698:
! 699: rw_enter_write(&np->n_commitlock);
! 700: if (!error && iomode == NFSV3WRITE_UNSTABLE) {
! 701: bp->b_flags |= B_NEEDCOMMIT;
! 702: nfs_add_tobecommitted_range(vp, bp);
! 703: } else {
! 704: bp->b_flags &= ~B_NEEDCOMMIT;
! 705: nfs_del_committed_range(vp, bp);
! 706: }
! 707: rw_exit_write(&np->n_commitlock);
! 708:
! 709: bp->b_flags &= ~B_WRITEINPROG;
! 710:
! 711: /*
! 712: * For an interrupted write, the buffer is still valid and the
! 713: * write hasn't been pushed to the server yet, so we can't set
! 714: * B_ERROR and report the interruption by setting B_EINTR. For
! 715: * the B_ASYNC case, B_EINTR is not relevant, so the rpc attempt
! 716: * is essentially a noop.
! 717: * For the case of a V3 write rpc not being committed to stable
! 718: * storage, the block is still dirty and requires either a commit
! 719: * rpc or another write rpc with iomode == NFSV3WRITE_FILESYNC
! 720: * before the block is reused. This is indicated by setting the
! 721: * B_DELWRI and B_NEEDCOMMIT flags.
! 722: */
! 723: if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) {
! 724: s = splbio();
! 725: buf_dirty(bp);
! 726: splx(s);
! 727:
! 728: if (!(bp->b_flags & B_ASYNC) && error)
! 729: bp->b_flags |= B_EINTR;
! 730: } else {
! 731: if (error) {
! 732: bp->b_flags |= B_ERROR;
! 733: bp->b_error = np->n_error = error;
! 734: np->n_flag |= NWRITEERR;
! 735: }
! 736: bp->b_dirtyoff = bp->b_dirtyend = 0;
! 737: }
! 738: }
! 739: bp->b_resid = uiop->uio_resid;
! 740: if (must_commit)
! 741: nfs_clearcommit(vp->v_mount);
! 742: s = splbio();
! 743: biodone(bp);
! 744: splx(s);
! 745: return (error);
! 746: }
CVSweb