From 899fb781e05a1ff24cacf7abb2807277d4f06e2d Mon Sep 17 00:00:00 2001 From: fvdl Date: Tue, 19 Sep 2000 22:11:47 +0000 Subject: [PATCH] Move handling of B_NEEDCOMMIT buffers to nfs_doio, so that bawrite() calls for them are actually done asynchronously. Idea taken from FreeBSD. Do away with nfs_writebp completely, it's not needed anymore. Keep an eye on the range of a file that needs to be committed, and do it in heaps. --- sys/nfs/nfs_bio.c | 106 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/sys/nfs/nfs_bio.c b/sys/nfs/nfs_bio.c index 230b9a092dce..863657e41c44 100644 --- a/sys/nfs/nfs_bio.c +++ b/sys/nfs/nfs_bio.c @@ -1,4 +1,4 @@ -/* $NetBSD: nfs_bio.c,v 1.51 2000/09/19 17:04:50 bjh21 Exp $ */ +/* $NetBSD: nfs_bio.c,v 1.52 2000/09/19 22:11:47 fvdl Exp $ */ /* * Copyright (c) 1989, 1993 @@ -731,7 +731,15 @@ again: * Since this block is being modified, it must be written * again and not just committed. */ - bp->b_flags &= ~B_NEEDCOMMIT; + if (NFS_ISV3(vp)) { + lockmgr(&np->n_commitlock, LK_EXCLUSIVE, NULL); + if (bp->b_flags & B_NEEDCOMMIT) { + bp->b_flags &= ~B_NEEDCOMMIT; + nfs_del_tobecommitted_range(vp, bp); + } + nfs_del_committed_range(vp, bp); + lockmgr(&np->n_commitlock, LK_RELEASE, NULL); + } /* * If the lease is non-cachable or IO_SYNC do bwrite(). @@ -749,8 +757,7 @@ again: } else if ((n + on) == biosize && (nmp->nm_flag & NFSMNT_NQNFS) == 0) { bp->b_proc = (struct proc *)0; - bp->b_flags |= B_ASYNC; - (void)nfs_writebp(bp, 0); + bawrite(bp); } else { bdwrite(bp); } @@ -962,9 +969,12 @@ nfs_doio(bp, cr, p) struct vnode *vp; struct nfsnode *np; struct nfsmount *nmp; - int error = 0, diff, len, iomode, must_commit = 0, s; + int error = 0, diff, len, iomode, must_commit = 0, s, retv = 0; + int pushedrange; + unsigned cnt; struct uio uio; struct iovec io; + off_t off; vp = bp->b_vp; np = VTONFS(vp); @@ -1071,6 +1081,61 @@ nfs_doio(bp, cr, p) bp->b_error = error; } } else { + /* + * If B_NEEDCOMMIT is set, a commit rpc may do the trick. If not + * an actual write will have to be scheduled. + */ + if (bp->b_flags & B_NEEDCOMMIT) { + /* + * If the buffer is in the range that we already committed, + * there's nothing to do. + * + * If it's in the range that we need to commit, push the + * whole range at once. Else only push the buffer. In + * both these cases, acquire the commit lock to avoid + * other processes modifying the range. Normally the + * vnode lock should have handled this, but there are + * no proper vnode locks for NFS yet (XXX). + */ + lockmgr(&np->n_commitlock, LK_EXCLUSIVE, NULL); + if (!(bp->b_flags & B_NEEDCOMMIT)) { + lockmgr(&np->n_commitlock, LK_RELEASE, NULL); + goto dowrite; + } + if (!nfs_in_committed_range(vp, bp)) { + if (nfs_in_tobecommitted_range(vp, bp)) { + pushedrange = 1; + off = np->n_pushlo; + /* XXX will be too big if > 2G buffer cache */ + cnt = np->n_pushhi - np->n_pushlo; + } else { + pushedrange = 0; + off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE; + cnt = bp->b_dirtyend; + } + bp->b_flags |= B_WRITEINPROG; + retv = nfs_commit(bp->b_vp, off, cnt, + bp->b_wcred, bp->b_proc); + + bp->b_flags &= ~B_WRITEINPROG; + if (retv == 0) { + if (pushedrange) { + nfs_merge_commit_ranges(vp); + } + else + nfs_add_committed_range(vp, bp); + } + } + lockmgr(&np->n_commitlock, LK_RELEASE, NULL); + if (!retv) { + bp->b_resid = bp->b_dirtyoff = bp->b_dirtyend = 0; + bp->b_flags &= ~B_NEEDCOMMIT; + biodone(bp); + return (0); + } else if (retv == NFSERR_STALEWRITEVERF) + nfs_clearcommit(bp->b_vp->v_mount); + } +dowrite: io.iov_len = uiop->uio_resid = bp->b_dirtyend - bp->b_dirtyoff; uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE @@ -1082,18 +1147,23 @@ nfs_doio(bp, cr, p) iomode = NFSV3WRITE_UNSTABLE; else iomode = NFSV3WRITE_FILESYNC; + bp->b_flags |= B_WRITEINPROG; -#ifdef fvdl_debug - printf("nfs_doio(%p): bp %p doff %d dend %d\n", - vp, bp, bp->b_dirtyoff, bp->b_dirtyend); -#endif error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit); s = splbio(); - if (!error && iomode == NFSV3WRITE_UNSTABLE) + if (!error && iomode == NFSV3WRITE_UNSTABLE) { bp->b_flags |= B_NEEDCOMMIT; - else + lockmgr(&np->n_commitlock, LK_EXCLUSIVE, NULL); + nfs_add_tobecommitted_range(vp, bp); + lockmgr(&np->n_commitlock, LK_RELEASE, NULL); + } else if (!error && bp->b_flags & B_NEEDCOMMIT) { bp->b_flags &= ~B_NEEDCOMMIT; - bp->b_flags &= ~B_WRITEINPROG; + lockmgr(&np->n_commitlock, LK_EXCLUSIVE, NULL); + nfs_del_committed_range(vp, bp); + lockmgr(&np->n_commitlock, LK_RELEASE, NULL); + } + /* XXX the use of NOCACHE is a hack */ + bp->b_flags &= ~(B_WRITEINPROG|B_NOCACHE); /* * For an interrupted write, the buffer is still valid and the @@ -1109,15 +1179,13 @@ nfs_doio(bp, cr, p) */ if (error == EINTR || (!error && (bp->b_flags & B_NEEDCOMMIT))) { bp->b_flags |= B_DELWRI; - /* - * Since for the B_ASYNC case, nfs_bwrite() has reassigned the - * buffer to the clean list, we have to reassign it back to the - * dirty one. Ugh. + * A B_ASYNC block still needs to be committed, so put + * it back on the dirty list. */ - if (bp->b_flags & B_ASYNC) { - reassignbuf(bp, vp); - } else if (error) + if (bp->b_flags & B_ASYNC) + reassignbuf(bp, vp); + else if (error) bp->b_flags |= B_EINTR; } else { if (error) {