summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarius eriksen <marius@cvs.openbsd.org>2004-08-03 17:11:49 +0000
committermarius eriksen <marius@cvs.openbsd.org>2004-08-03 17:11:49 +0000
commitb8ed71e1dd36fa802eca4f3384c7c7ab410abea5 (patch)
tree36f516e146da6232ec374cbd7c288e1d9758bc6a
parent819ed0960a343745b18452727e63b4c4dae1e1c7 (diff)
NFS commit coalescion: instead of sending a commit for each block, coalesce
these into larger ranges wherever possible. this should speed up NFS writes quite a bit. ok art@ millert@ pedro@ tedu@
-rw-r--r--sys/kern/vfs_conf.c3
-rw-r--r--sys/nfs/nfs_bio.c28
-rw-r--r--sys/nfs/nfs_node.c4
-rw-r--r--sys/nfs/nfs_subs.c168
-rw-r--r--sys/nfs/nfs_var.h9
-rw-r--r--sys/nfs/nfs_vnops.c51
-rw-r--r--sys/nfs/nfsnode.h17
7 files changed, 264 insertions, 16 deletions
diff --git a/sys/kern/vfs_conf.c b/sys/kern/vfs_conf.c
index 7ed2a32012b..895452bbde8 100644
--- a/sys/kern/vfs_conf.c
+++ b/sys/kern/vfs_conf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vfs_conf.c,v 1.23 2004/04/13 00:15:28 tedu Exp $ */
+/* $OpenBSD: vfs_conf.c,v 1.24 2004/08/03 17:11:48 marius Exp $ */
/* $NetBSD: vfs_conf.c,v 1.21.4.1 1995/11/01 00:06:26 jtc Exp $ */
/*
@@ -60,6 +60,7 @@
#endif
#ifdef NFSCLIENT
+#include <sys/rwlock.h> /* XXX*/
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfsnode.h>
diff --git a/sys/nfs/nfs_bio.c b/sys/nfs/nfs_bio.c
index 53caf23abcf..3630b55ab33 100644
--- a/sys/nfs/nfs_bio.c
+++ b/sys/nfs/nfs_bio.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: nfs_bio.c,v 1.39 2004/07/21 17:30:55 marius Exp $ */
+/* $OpenBSD: nfs_bio.c,v 1.40 2004/08/03 17:11:48 marius Exp $ */
/* $NetBSD: nfs_bio.c,v 1.25.4.2 1996/07/08 20:47:04 jtc Exp $ */
/*
@@ -35,7 +35,6 @@
* @(#)nfs_bio.c 8.9 (Berkeley) 3/30/95
*/
-
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/resourcevar.h>
@@ -415,7 +414,17 @@ 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)) {
+ rw_enter_write(&np->n_commitlock);
+ if (bp->b_flags & B_NEEDCOMMIT) {
+ bp->b_flags &= ~B_NEEDCOMMIT;
+ nfs_del_tobecommitted_range(vp, bp);
+ }
+ nfs_del_committed_range(vp, bp);
+ rw_exit_write(&np->n_commitlock);
+ } else
+ bp->b_flags &= ~B_NEEDCOMMIT;
/*
* If the lease is non-cachable or IO_SYNC do bwrite().
@@ -670,7 +679,7 @@ nfs_doio(bp, p)
error = nfs_readlinkrpc(vp, uiop, curproc->p_ucred);
break;
default:
- printf("nfs_doio: type %x unexpected\n",vp->v_type);
+ printf("nfs_doio: type %x unexpected\n", vp->v_type);
break;
};
if (error) {
@@ -695,10 +704,17 @@ nfs_doio(bp, p)
vp, bp, bp->b_dirtyoff, bp->b_dirtyend);
#endif
error = nfs_writerpc(vp, uiop, &iomode, &must_commit);
- if (!error && iomode == NFSV3WRITE_UNSTABLE)
+
+ rw_enter_write(&np->n_commitlock);
+ if (!error && iomode == NFSV3WRITE_UNSTABLE) {
bp->b_flags |= B_NEEDCOMMIT;
- else
+ nfs_add_tobecommitted_range(vp, bp);
+ } else {
bp->b_flags &= ~B_NEEDCOMMIT;
+ nfs_del_committed_range(vp, bp);
+ }
+ rw_exit_write(&np->n_commitlock);
+
bp->b_flags &= ~B_WRITEINPROG;
/*
diff --git a/sys/nfs/nfs_node.c b/sys/nfs/nfs_node.c
index 4e8ded5cb6e..026ec6149c1 100644
--- a/sys/nfs/nfs_node.c
+++ b/sys/nfs/nfs_node.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: nfs_node.c,v 1.26 2003/06/02 23:28:19 millert Exp $ */
+/* $OpenBSD: nfs_node.c,v 1.27 2004/08/03 17:11:48 marius Exp $ */
/* $NetBSD: nfs_node.c,v 1.16 1996/02/18 11:53:42 fvdl Exp $ */
/*
@@ -128,6 +128,8 @@ loop:
vp->v_data = np;
np->n_vnode = vp;
+ rw_init(&np->n_commitlock);
+
/*
* Are we getting the root? If so, make sure the vnode flags
* are correct
diff --git a/sys/nfs/nfs_subs.c b/sys/nfs/nfs_subs.c
index 6cd3d3290c7..a93418d73fa 100644
--- a/sys/nfs/nfs_subs.c
+++ b/sys/nfs/nfs_subs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: nfs_subs.c,v 1.49 2004/08/03 06:58:40 miod Exp $ */
+/* $OpenBSD: nfs_subs.c,v 1.50 2004/08/03 17:11:48 marius Exp $ */
/* $NetBSD: nfs_subs.c,v 1.27.4.3 1996/07/08 20:34:24 jtc Exp $ */
/*
@@ -1744,6 +1744,172 @@ loop:
splx(s);
}
+void
+nfs_merge_commit_ranges(vp)
+ struct vnode *vp;
+{
+ struct nfsnode *np = VTONFS(vp);
+
+ if (!(np->n_commitflags & NFS_COMMIT_PUSHED_VALID)) {
+ np->n_pushedlo = np->n_pushlo;
+ np->n_pushedhi = np->n_pushhi;
+ np->n_commitflags |= NFS_COMMIT_PUSHED_VALID;
+ } else {
+ if (np->n_pushlo < np->n_pushedlo)
+ np->n_pushedlo = np->n_pushlo;
+ if (np->n_pushhi > np->n_pushedhi)
+ np->n_pushedhi = np->n_pushhi;
+ }
+
+ np->n_pushlo = np->n_pushhi = 0;
+ np->n_commitflags &= ~NFS_COMMIT_PUSH_VALID;
+}
+
+int
+nfs_in_committed_range(vp, bp)
+ struct vnode *vp;
+ struct buf *bp;
+{
+ struct nfsnode *np = VTONFS(vp);
+ off_t lo, hi;
+
+ if (!(np->n_commitflags & NFS_COMMIT_PUSHED_VALID))
+ return 0;
+ lo = (off_t)bp->b_blkno * DEV_BSIZE;
+ hi = lo + bp->b_dirtyend;
+
+ return (lo >= np->n_pushedlo && hi <= np->n_pushedhi);
+}
+
+int
+nfs_in_tobecommitted_range(vp, bp)
+ struct vnode *vp;
+ struct buf *bp;
+{
+ struct nfsnode *np = VTONFS(vp);
+ off_t lo, hi;
+
+ if (!(np->n_commitflags & NFS_COMMIT_PUSH_VALID))
+ return 0;
+ lo = (off_t)bp->b_blkno * DEV_BSIZE;
+ hi = lo + bp->b_dirtyend;
+
+ return (lo >= np->n_pushlo && hi <= np->n_pushhi);
+}
+
+void
+nfs_add_committed_range(vp, bp)
+ struct vnode *vp;
+ struct buf *bp;
+{
+ struct nfsnode *np = VTONFS(vp);
+ off_t lo, hi;
+
+ lo = (off_t)bp->b_blkno * DEV_BSIZE;
+ hi = lo + bp->b_dirtyend;
+
+ if (!(np->n_commitflags & NFS_COMMIT_PUSHED_VALID)) {
+ np->n_pushedlo = lo;
+ np->n_pushedhi = hi;
+ np->n_commitflags |= NFS_COMMIT_PUSHED_VALID;
+ } else {
+ if (hi > np->n_pushedhi)
+ np->n_pushedhi = hi;
+ if (lo < np->n_pushedlo)
+ np->n_pushedlo = lo;
+ }
+}
+
+void
+nfs_del_committed_range(vp, bp)
+ struct vnode *vp;
+ struct buf *bp;
+{
+ struct nfsnode *np = VTONFS(vp);
+ off_t lo, hi;
+
+ if (!(np->n_commitflags & NFS_COMMIT_PUSHED_VALID))
+ return;
+
+ lo = (off_t)bp->b_blkno * DEV_BSIZE;
+ hi = lo + bp->b_dirtyend;
+
+ if (lo > np->n_pushedhi || hi < np->n_pushedlo)
+ return;
+ if (lo <= np->n_pushedlo)
+ np->n_pushedlo = hi;
+ else if (hi >= np->n_pushedhi)
+ np->n_pushedhi = lo;
+ else {
+ /*
+ * XXX There's only one range. If the deleted range
+ * is in the middle, pick the largest of the
+ * contiguous ranges that it leaves.
+ */
+ if ((np->n_pushedlo - lo) > (hi - np->n_pushedhi))
+ np->n_pushedhi = lo;
+ else
+ np->n_pushedlo = hi;
+ }
+}
+
+void
+nfs_add_tobecommitted_range(vp, bp)
+ struct vnode *vp;
+ struct buf *bp;
+{
+ struct nfsnode *np = VTONFS(vp);
+ off_t lo, hi;
+
+ lo = (off_t)bp->b_blkno * DEV_BSIZE;
+ hi = lo + bp->b_dirtyend;
+
+ if (!(np->n_commitflags & NFS_COMMIT_PUSH_VALID)) {
+ np->n_pushlo = lo;
+ np->n_pushhi = hi;
+ np->n_commitflags |= NFS_COMMIT_PUSH_VALID;
+ } else {
+ if (lo < np->n_pushlo)
+ np->n_pushlo = lo;
+ if (hi > np->n_pushhi)
+ np->n_pushhi = hi;
+ }
+}
+
+void
+nfs_del_tobecommitted_range(vp, bp)
+ struct vnode *vp;
+ struct buf *bp;
+{
+ struct nfsnode *np = VTONFS(vp);
+ off_t lo, hi;
+
+ if (!(np->n_commitflags & NFS_COMMIT_PUSH_VALID))
+ return;
+
+ lo = (off_t)bp->b_blkno * DEV_BSIZE;
+ hi = lo + bp->b_dirtyend;
+
+ if (lo > np->n_pushhi || hi < np->n_pushlo)
+ return;
+
+ if (lo <= np->n_pushlo)
+ np->n_pushlo = hi;
+ else if (hi >= np->n_pushhi)
+ np->n_pushhi = lo;
+ else {
+ /*
+ * XXX There's only one range. If the deleted range
+ * is in the middle, pick the largest of the
+ * contiguous ranges that it leaves.
+ */
+ if ((np->n_pushlo - lo) > (hi - np->n_pushhi))
+ np->n_pushhi = lo;
+ else
+ np->n_pushlo = hi;
+ }
+}
+
/*
* Map errnos to NFS error numbers. For Version 3 also filter out error
* numbers not specified for the associated procedure.
diff --git a/sys/nfs/nfs_var.h b/sys/nfs/nfs_var.h
index e5a6a45d3b2..fc7bb9d506a 100644
--- a/sys/nfs/nfs_var.h
+++ b/sys/nfs/nfs_var.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: nfs_var.h,v 1.22 2004/07/21 17:30:56 marius Exp $ */
+/* $OpenBSD: nfs_var.h,v 1.23 2004/08/03 17:11:48 marius Exp $ */
/* $NetBSD: nfs_var.h,v 1.3 1996/02/18 11:53:54 fvdl Exp $ */
/*
@@ -257,6 +257,13 @@ int nfsrv_fhtovp(fhandle_t *, int, struct vnode **, struct ucred *,
struct nfssvc_sock *, struct mbuf *, int *, int);
int netaddr_match(int, union nethostaddr *, struct mbuf *);
void nfs_clearcommit(struct mount *);
+int nfs_in_committed_range(struct vnode *, struct buf *);
+int nfs_in_tobecommitted_range(struct vnode *, struct buf *);
+void nfs_add_committed_range(struct vnode *, struct buf *);
+void nfs_del_committed_range(struct vnode *, struct buf *);
+void nfs_add_tobecommitted_range(struct vnode *, struct buf *);
+void nfs_del_tobecommitted_range(struct vnode *, struct buf *);
+void nfs_merge_commit_ranges(struct vnode *);
int nfsrv_errmap(struct nfsrv_descript *, int);
void nfsrvw_sort(gid_t *, int);
void nfsrv_setcred(struct ucred *, struct ucred *);
diff --git a/sys/nfs/nfs_vnops.c b/sys/nfs/nfs_vnops.c
index f1a28db9e48..01ae894082b 100644
--- a/sys/nfs/nfs_vnops.c
+++ b/sys/nfs/nfs_vnops.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: nfs_vnops.c,v 1.62 2004/07/21 17:30:56 marius Exp $ */
+/* $OpenBSD: nfs_vnops.c,v 1.63 2004/08/03 17:11:48 marius Exp $ */
/* $NetBSD: nfs_vnops.c,v 1.62.4.1 1996/07/08 20:26:52 jtc Exp $ */
/*
@@ -2989,11 +2989,17 @@ nfs_writebp(bp, force)
int oldflags = bp->b_flags, retv = 1;
struct proc *p = curproc; /* XXX */
off_t off;
+ size_t cnt;
int s;
+ struct vnode *vp;
+ struct nfsnode *np;
if(!(bp->b_flags & B_BUSY))
panic("bwrite: buffer is not busy???");
+ vp = bp->b_vp;
+ np = VTONFS(vp);
+
#ifdef fvdl_debug
printf("nfs_writebp(%x): vp %x voff %d vend %d doff %d dend %d\n",
bp, bp->b_vp, bp->b_validoff, bp->b_validend, bp->b_dirtyoff,
@@ -3017,10 +3023,45 @@ nfs_writebp(bp, force)
*/
if ((oldflags & (B_NEEDCOMMIT | B_WRITEINPROG)) == B_NEEDCOMMIT) {
off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff;
- bp->b_flags |= B_WRITEINPROG;
- retv = nfs_commit(bp->b_vp, off, bp->b_dirtyend-bp->b_dirtyoff,
- bp->b_proc);
- bp->b_flags &= ~B_WRITEINPROG;
+ cnt = bp->b_dirtyend - bp->b_dirtyoff;
+
+ rw_enter_write(&np->n_commitlock);
+ if (!(bp->b_flags & B_NEEDCOMMIT)) {
+ rw_exit_write(&np->n_commitlock);
+ return (0);
+ }
+
+ /*
+ * If it's already been commited by somebody else,
+ * bail.
+ */
+ if (!nfs_in_committed_range(vp, bp)) {
+ int pushedrange = 0;
+ /*
+ * Since we're going to do this, push as much
+ * as we can.
+ */
+
+ if (nfs_in_tobecommitted_range(vp, bp)) {
+ pushedrange = 1;
+ off = np->n_pushlo;
+ cnt = np->n_pushhi - np->n_pushlo;
+ }
+
+ bp->b_flags |= B_WRITEINPROG;
+ retv = nfs_commit(bp->b_vp, off, cnt, 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);
+ }
+ } else
+ retv = 0; /* It has already been commited. */
+
+ rw_exit_write(&np->n_commitlock);
if (!retv) {
bp->b_dirtyoff = bp->b_dirtyend = 0;
bp->b_flags &= ~B_NEEDCOMMIT;
diff --git a/sys/nfs/nfsnode.h b/sys/nfs/nfsnode.h
index a0d49d6c3cd..ef09b6ff6a1 100644
--- a/sys/nfs/nfsnode.h
+++ b/sys/nfs/nfsnode.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: nfsnode.h,v 1.21 2004/04/26 18:57:36 millert Exp $ */
+/* $OpenBSD: nfsnode.h,v 1.22 2004/08/03 17:11:48 marius Exp $ */
/* $NetBSD: nfsnode.h,v 1.16 1996/02/18 11:54:04 fvdl Exp $ */
/*
@@ -43,6 +43,8 @@
#include <nfs/nfs.h>
#endif
+#include <sys/rwlock.h>
+
/*
* Silly rename structure that hangs off the nfsnode until the name
* can be removed by nfs_inactive()
@@ -115,8 +117,21 @@ struct nfsnode {
nfsfh_t n_fh; /* Small File Handle */
struct ucred *n_rcred;
struct ucred *n_wcred;
+
+ off_t n_pushedlo; /* 1st blk in commited range */
+ off_t n_pushedhi; /* Last block in range */
+ off_t n_pushlo; /* 1st block in commit range */
+ off_t n_pushhi; /* Last block in range */
+ struct rwlock n_commitlock; /* Serialize commits */
+ int n_commitflags;
};
+/*
+ * Values for n_commitflags
+ */
+#define NFS_COMMIT_PUSH_VALID 0x0001 /* push range valid */
+#define NFS_COMMIT_PUSHED_VALID 0x0002 /* pushed range valid */
+
#define n_atim n_un1.nf_atim
#define n_mtim n_un2.nf_mtim
#define n_sillyrename n_un3.nf_silly