- FSDBG_TOP(256, np->n_size, NBOFF(bp), bp->nb_bufsize, bp->nb_flags);
- FSDBG(257, bp->nb_validoff, bp->nb_validend, bp->nb_dirtyoff,
- bp->nb_dirtyend);
-
- if (ISSET(bp->nb_flags, NB_READ)) {
- if (vtype == VREG)
- NFS_BUF_MAP(bp);
- io.iov_len = bp->nb_bufsize;
- uio_uio_resid_set(uiop, io.iov_len);
- io.iov_base = (uintptr_t) bp->nb_data;
- uiop->uio_rw = UIO_READ;
- switch (vtype) {
- case VREG:
- uiop->uio_offset = NBOFF(bp);
- OSAddAtomic(1, (SInt32*)&nfsstats.read_bios);
- error = nfs_readrpc(vp, uiop, cr, p);
- FSDBG(262, np->n_size, NBOFF(bp), uio_uio_resid(uiop), error);
- if (!error) {
- /* update valid range */
- bp->nb_validoff = 0;
- if (uio_uio_resid(uiop) != 0) {
- /*
- * If len > 0, there is a hole in the file and
- * no writes after the hole have been pushed to
- * the server yet.
- * Just zero fill the rest of the valid area.
- */
- // LP64todo - fix this
- diff = bp->nb_bufsize - uio_uio_resid(uiop);
- len = np->n_size - (NBOFF(bp) + diff);
- if (len > 0) {
- // LP64todo - fix this
- len = min(len, uio_uio_resid(uiop));
- bzero((char *)bp->nb_data + diff, len);
- bp->nb_validend = diff + len;
- FSDBG(258, diff, len, 0, 1);
- } else
- bp->nb_validend = diff;
- } else
- bp->nb_validend = bp->nb_bufsize;
- bp->nb_valid = (1 << (round_page_32(bp->nb_validend)/PAGE_SIZE)) - 1;
- if (bp->nb_validend & PAGE_MASK) {
- /* valid range ends in the middle of a page so we */
- /* need to zero-fill any invalid data at the end */
- /* of the last page */
- bzero((caddr_t)(bp->nb_data + bp->nb_validend),
- bp->nb_bufsize - bp->nb_validend);
- FSDBG(258, bp->nb_validend,
- bp->nb_bufsize - bp->nb_validend, 0, 2);
- }
- }
- break;
- case VLNK:
- uiop->uio_offset = (off_t)0;
- OSAddAtomic(1, (SInt32*)&nfsstats.readlink_bios);
- error = nfs_readlinkrpc(vp, uiop, cr, p);
- if (!error) {
- bp->nb_validoff = 0;
- bp->nb_validend = uiop->uio_offset;
- }
- break;
- case VDIR:
- OSAddAtomic(1, (SInt32*)&nfsstats.readdir_bios);
- uiop->uio_offset = NBOFF(bp);
- if (!(nmp->nm_flag & NFSMNT_NFSV3))
- nmp->nm_flag &= ~NFSMNT_RDIRPLUS; /* dk@farm.org */
- if (nmp->nm_flag & NFSMNT_RDIRPLUS) {
- error = nfs_readdirplusrpc(vp, uiop, cr, p);
- if (error == NFSERR_NOTSUPP)
- nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
- }
- if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0)
- error = nfs_readdirrpc(vp, uiop, cr, p);
- if (!error) {
- bp->nb_validoff = 0;
- bp->nb_validend = uiop->uio_offset - NBOFF(bp);
- bp->nb_valid = (1 << (round_page_32(bp->nb_validend)/PAGE_SIZE)) - 1;
- }
- break;
- default:
- printf("nfs_doio: type %x unexpected\n", vtype);
- break;
- };
- if (error) {
- SET(bp->nb_flags, NB_ERROR);
- bp->nb_error = error;
- }
-
- } else {
- /* we're doing a write */
- int doff, dend = 0;
-
- /* We need to make sure the pages are locked before doing I/O. */
- if (!ISSET(bp->nb_flags, NB_META) && UBCINFOEXISTS(vp)) {
- if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
- error = nfs_buf_upl_setup(bp);
- if (error) {
- printf("nfs_doio: upl create failed %d\n", error);
- SET(bp->nb_flags, NB_ERROR);
- bp->nb_error = EIO;
- return (EIO);
- }
- nfs_buf_upl_check(bp);
- }
- }
-
- if (ISSET(bp->nb_flags, NB_WASDIRTY)) {
- FSDBG(256, bp, NBOFF(bp), bp->nb_dirty, 0xd00dee);
- /*
- * There are pages marked dirty that need to be written out.
- *
- * We don't want to just combine the write range with the
- * range of pages that are dirty because that could cause us
- * to write data that wasn't actually written to.
- * We also don't want to write data more than once.
- *
- * If the dirty range just needs to be committed, we do that.
- * Otherwise, we write the dirty range and clear the dirty bits
- * for any COMPLETE pages covered by that range.
- * If there are dirty pages left after that, we write out the
- * parts that we haven't written yet.
- */
- }
-
- /*
- * If NB_NEEDCOMMIT is set, a commit rpc may do the trick. If not
- * an actual write will have to be done.
- * If NB_WRITEINPROG is already set, then push it with a write anyhow.
- */
- if (ISSET(bp->nb_flags, NB_NEEDCOMMIT))
- nfs_buf_check_write_verifier(np, bp);
- if ((bp->nb_flags & (NB_NEEDCOMMIT | NB_WRITEINPROG)) == NB_NEEDCOMMIT) {
- doff = NBOFF(bp) + bp->nb_dirtyoff;
- SET(bp->nb_flags, NB_WRITEINPROG);
- error = nfs_commit(vp, doff, bp->nb_dirtyend - bp->nb_dirtyoff,
- bp->nb_wcred, bp->nb_proc);
- CLR(bp->nb_flags, NB_WRITEINPROG);
- if (!error) {
- bp->nb_dirtyoff = bp->nb_dirtyend = 0;
- CLR(bp->nb_flags, NB_NEEDCOMMIT);
- np->n_needcommitcnt--;
- CHECK_NEEDCOMMITCNT(np);
- }
- }
-
- if (!error && bp->nb_dirtyend > 0) {
- /* there's a dirty range that needs to be written out */
- u_int32_t pagemask;
- int firstpg, lastpg;
-
- if (NBOFF(bp) + bp->nb_dirtyend > (off_t)np->n_size)
- bp->nb_dirtyend = np->n_size - NBOFF(bp);
-
- NFS_BUF_MAP(bp);
-
- doff = bp->nb_dirtyoff;
- dend = bp->nb_dirtyend;
-
- /* if doff page is dirty, move doff to start of page */
- if (NBPGDIRTY(bp,doff/PAGE_SIZE))
- doff -= doff & PAGE_MASK;
- /* try to expand write range to include preceding dirty pages */
- if (!(doff & PAGE_MASK))
- while (doff > 0 && NBPGDIRTY(bp,(doff-1)/PAGE_SIZE))
- doff -= PAGE_SIZE;
- /* if dend page is dirty, move dend to start of next page */
- if ((dend & PAGE_MASK) && NBPGDIRTY(bp,dend/PAGE_SIZE))
- dend = round_page_32(dend);
- /* try to expand write range to include trailing dirty pages */
- if (!(dend & PAGE_MASK))
- while (dend < bp->nb_bufsize && NBPGDIRTY(bp,dend/PAGE_SIZE))
- dend += PAGE_SIZE;
- /* make sure to keep dend clipped to EOF */
- if (NBOFF(bp) + dend > (off_t)np->n_size)
- dend = np->n_size - NBOFF(bp);
- /* calculate range of complete pages being written */
- firstpg = round_page_32(doff) / PAGE_SIZE;
- lastpg = (trunc_page_32(dend) - 1)/ PAGE_SIZE;
- /* calculate mask for that page range */
- pagemask = ((1 << (lastpg+1)) - 1) & ~((1 << firstpg) - 1);
-
- /* compare page mask to nb_dirty; if there are other dirty pages */
- /* then write FILESYNC; otherwise, write UNSTABLE if async and */
- /* not needcommit/nocache/call; otherwise write FILESYNC */
- if (bp->nb_dirty & ~pagemask)
- iomode = NFSV3WRITE_FILESYNC;
- else if ((bp->nb_flags & (NB_ASYNC | NB_NEEDCOMMIT | NB_NOCACHE | NB_STABLE)) == NB_ASYNC)
- iomode = NFSV3WRITE_UNSTABLE;
- else
- iomode = NFSV3WRITE_FILESYNC;
-
- /* write the dirty range */
- io.iov_len = dend - doff;
- uio_uio_resid_set(uiop, io.iov_len);
- uiop->uio_offset = NBOFF(bp) + doff;
- io.iov_base = (uintptr_t) bp->nb_data + doff;
- uiop->uio_rw = UIO_WRITE;
-
- OSAddAtomic(1, (SInt32*)&nfsstats.write_bios);
-
- SET(bp->nb_flags, NB_WRITEINPROG);
- error = nfs_writerpc(vp, uiop, cr, p, &iomode, &bp->nb_verf);
- /* clear dirty bits for pages we've written */
- if (!error)
- bp->nb_dirty &= ~pagemask;
- /* set/clear needcommit flag */
- if (!error && iomode == NFSV3WRITE_UNSTABLE) {
- if (!ISSET(bp->nb_flags, NB_NEEDCOMMIT))
- np->n_needcommitcnt++;
- SET(bp->nb_flags, NB_NEEDCOMMIT);
- /* make sure nb_dirtyoff/nb_dirtyend reflect actual range written */
- bp->nb_dirtyoff = doff;
- bp->nb_dirtyend = dend;
- } else {
- if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
- np->n_needcommitcnt--;
- CHECK_NEEDCOMMITCNT(np);
- }
- CLR(bp->nb_flags, NB_NEEDCOMMIT);
- }
- CLR(bp->nb_flags, NB_WRITEINPROG);
- /*
- * For an interrupted write, the buffer is still valid and the write
- * hasn't been pushed to the server yet, so we can't set NB_ERROR and
- * report the interruption by setting NB_EINTR. For the NB_ASYNC case,
- * NB_EINTR is not relevant.
- *
- * For the case of a V3 write rpc not being committed to stable
- * storage, the block is still dirty and requires either a commit rpc
- * or another write rpc with iomode == NFSV3WRITE_FILESYNC before the
- * block is reused. This is indicated by setting the NB_DELWRI and
- * NB_NEEDCOMMIT flags.
- */
- if (error == EINTR || (!error && bp->nb_flags & NB_NEEDCOMMIT)) {
- CLR(bp->nb_flags, NB_INVAL | NB_NOCACHE);
- if (!ISSET(bp->nb_flags, NB_DELWRI)) {
- SET(bp->nb_flags, NB_DELWRI);
- OSAddAtomic(1, (SInt32*)&nfs_nbdwrite);
- NFSBUFCNTCHK(0);
- }
- FSDBG(261, bp->nb_validoff, bp->nb_validend,
- bp->nb_bufsize, 0);
- /*
- * Since for the NB_ASYNC case, nfs_bwrite() has
- * reassigned the buffer to the clean list, we have to
- * reassign it back to the dirty one. Ugh.
- */
- if (ISSET(bp->nb_flags, NB_ASYNC)) {
- /* move to dirty list */
- lck_mtx_lock(nfs_buf_mutex);
- if (bp->nb_vnbufs.le_next != NFSNOLIST)
- LIST_REMOVE(bp, nb_vnbufs);
- LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
- lck_mtx_unlock(nfs_buf_mutex);
- } else {
- SET(bp->nb_flags, NB_EINTR);
- }
- } else {
- /* either there's an error or we don't need to commit */
- if (error) {
- SET(bp->nb_flags, NB_ERROR);
- bp->nb_error = np->n_error = error;
- np->n_flag |= NWRITEERR;
- /*
- * There was a write error and we need to
- * invalidate attrs and flush buffers in
- * order to sync up with the server.
- * (if this write was extending the file,
- * we may no longer know the correct size)
- *
- * But we can't call vinvalbuf while holding
- * this buffer busy. Set a flag to do it after
- * releasing the buffer.
- *
- * Note we can only invalidate in this function
- * if this is an async write and so the iodone
- * below will release the buffer. Also, we
- * shouldn't call vinvalbuf from nfsiod because
- * that may deadlock waiting for the completion
- * of writes that are queued up behind this one.
- */
- if (ISSET(bp->nb_flags, NB_ASYNC) &&
- !ISSET(bp->nb_flags, NB_IOD)) {
- invalidate = 1;
- } else {
- /* invalidate later */
- np->n_flag |= NNEEDINVALIDATE;
- }
- NATTRINVALIDATE(np);
- }
- /* clear the dirty range */
- bp->nb_dirtyoff = bp->nb_dirtyend = 0;
- }
- }
-
- if (!error && bp->nb_dirty) {
- /* there are pages marked dirty that need to be written out */
- int pg, count, npages, off;
-
- OSAddAtomic(1, (SInt32*)&nfsstats.write_bios);