Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 10 Nov 1998 02:02:11 -0800
From:      Mike Smith <mike@smith.net.au>
To:        current@FreeBSD.ORG
Subject:   Simple NFS ACCESS caching, call for testers
Message-ID:  <199811101002.CAA01721@dingo.cdrom.com>

next in thread | raw e-mail | index | archive | help
This is a multipart MIME message.

--==_Exmh_-13891298860
Content-Type: text/plain; charset=us-ascii


The attached patch adds a trivial cache for NFS ACCESS operations, 
which may provide a moderate to substantial performance improvement in 
some cases.

The issues surrounding caching these requests are actually quite 
subtle, and it's not immediately clear that a more sophisticated 
approach would actually yield great results in many more cases than 
this trivial one does.  The trivial implementation has the advantage of 
simplicity. 8)

If you have an NFS v3 server that you beat heavily on, I'd love to know 
whether these changes make any difference to you.

Apply the patches to a -current kernel (they should apply fairly 
cleanly to a -stable kernel as well, but I haven't tried this).

The new kernel has three new sysctl options:

 vfs.nfs.access_cache_timeout

	The time (in seconds) for which an ACCESS result is cached.
	Try values from 2 to 10 or so.  A value of 0 (the default)
	disables caching.

 vfs.nfs.access_cache_hits

	The number of access calls that have been satisfied from 
	cached entries rather than wire calls.

 vfs.nfs.access_cache_fills

	The number of access calls that have had to go to the wire
	to be satisfied.

Trivial testing tends to indicate that operations involving a single 
UID and a large directory hierarchy may benefit substantially from 
this, but I really need more results before I can commit.



--==_Exmh_-13891298860
Content-Type: text/plain ; name="nfsdiff"; charset=us-ascii
Content-Description: nfsdiff
Content-Disposition: attachment; filename="nfsdiff"

Index: nfs_vnops.c
===================================================================
RCS file: /home/ncvs/src/sys/nfs/nfs_vnops.c,v
retrieving revision 1.111
diff -u -r1.111 nfs_vnops.c
--- nfs_vnops.c	1998/11/09 07:00:14	1.111
+++ nfs_vnops.c	1998/11/10 08:48:40
@@ -60,6 +60,7 @@
 #include <sys/fcntl.h>
 #include <sys/lockf.h>
 #include <sys/stat.h>
+#include <sys/sysctl.h>
 
 #include <vm/vm.h>
 #include <vm/vm_extern.h>
@@ -247,6 +248,35 @@
 int nfs_numasync = 0;
 #define	DIRHDSIZ	(sizeof (struct dirent) - (MAXNAMLEN + 1))
 
+static int	nfsaccess_cache_timeout;
+SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_timeout, CTLFLAG_RW, 
+	   &nfsaccess_cache_timeout, 0, "NFS ACCESS cache timeout");
+
+static int	nfsaccess_cache_hits;
+SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_hits, CTLFLAG_RD, 
+	   &nfsaccess_cache_hits, 0, "NFS ACCESS cache hit count");
+
+static int	nfsaccess_cache_fills;
+SYSCTL_INT(_vfs_nfs, OID_AUTO, access_cache_fills, CTLFLAG_RD, 
+	   &nfsaccess_cache_fills, 0, "NFS ACCESS cache fill count");
+
+/*
+ * Compare two ucred structures, returns zero on equality, nonzero
+ * otherwise.
+ */
+static int
+nfsa_ucredcmp(struct ucred *c1, struct ucred *c2)
+{
+    int		i;
+    
+    if ((c1->cr_uid != c2->cr_uid) || (c1->cr_ngroups != c2->cr_ngroups))
+	return(1);
+    for (i = 0; i < c1->cr_ngroups; i++)
+	if (c1->cr_groups[i] != c2->cr_groups[i])
+	    return(1);
+    return(0);
+}
+
 /*
  * nfs access vnode op.
  * For nfs version 2, just return ok. File accesses may fail later.
@@ -269,8 +299,9 @@
 	caddr_t bpos, dpos, cp2;
 	int error = 0, attrflag;
 	struct mbuf *mreq, *mrep, *md, *mb, *mb2;
-	u_int32_t mode, rmode;
+	u_int32_t mode, rmode, wmode;
 	int v3 = NFS_ISV3(vp);
+	struct nfsnode *np = VTONFS(vp);
 
 	/*
 	 * Disallow write attempts on filesystems mounted read-only;
@@ -288,18 +319,14 @@
 		}
 	}
 	/*
-	 * For nfs v3, do an access rpc, otherwise you are stuck emulating
+	 * For nfs v3, check to see if we have done this recently, and if
+	 * so return our cached result instead of making an ACCESS call.
+	 * If not, do an access rpc, otherwise you are stuck emulating
 	 * ufs_access() locally using the vattr. This may not be correct,
 	 * since the server may apply other access criteria such as
-	 * client uid-->server uid mapping that we do not know about, but
-	 * this is better than just returning anything that is lying about
-	 * in the cache.
+	 * client uid-->server uid mapping that we do not know about.
 	 */
 	if (v3) {
-		nfsstats.rpccnt[NFSPROC_ACCESS]++;
-		nfsm_reqhead(vp, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED);
-		nfsm_fhtom(vp, v3);
-		nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
 		if (ap->a_mode & VREAD)
 			mode = NFSV3ACCESS_READ;
 		else
@@ -315,22 +342,50 @@
 					 NFSV3ACCESS_DELETE);
 			if (ap->a_mode & VEXEC)
 				mode |= NFSV3ACCESS_LOOKUP;
+		}
+		/* XXX safety belt, only make blanket request if caching */
+		if (nfsaccess_cache_timeout > 0) {
+			wmode = NFSV3ACCESS_READ | NFSV3ACCESS_MODIFY | 
+				NFSV3ACCESS_EXTEND | NFSV3ACCESS_EXECUTE | 
+				NFSV3ACCESS_DELETE | NFSV3ACCESS_LOOKUP;
+		} else {
+			wmode = mode;
 		}
-		*tl = txdr_unsigned(mode);
-		nfsm_request(vp, NFSPROC_ACCESS, ap->a_p, ap->a_cred);
-		nfsm_postop_attr(vp, attrflag);
-		if (!error) {
-			nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
-			rmode = fxdr_unsigned(u_int32_t, *tl);
-			/*
-			 * The NFS V3 spec does not clarify whether or not
-			 * the returned access bits can be a superset of
-			 * the ones requested, so...
-			 */
-			if ((rmode & mode) != mode)
+
+		/* do we have a cached result? */
+		if ((time_second < (np->n_modestamp + nfsaccess_cache_timeout)) &&
+		    !nfsa_ucredcmp(ap->a_cred, &np->n_modecred)) {
+			nfsaccess_cache_hits++;
+			if ((np->n_mode & mode) != mode)
 				error = EACCES;
+		} else {
+			nfsstats.rpccnt[NFSPROC_ACCESS]++;
+			nfsm_reqhead(vp, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED);
+			nfsm_fhtom(vp, v3);
+			nfsm_build(tl, u_int32_t *, NFSX_UNSIGNED);
+			*tl = txdr_unsigned(wmode); 
+			nfsm_request(vp, NFSPROC_ACCESS, ap->a_p, ap->a_cred);
+			nfsm_postop_attr(vp, attrflag);
+			if (!error) {
+				nfsm_dissect(tl, u_int32_t *, NFSX_UNSIGNED);
+				rmode = fxdr_unsigned(u_int32_t, *tl);
+				/*
+				 * The NFS V3 spec does not clarify whether or not
+				 * the returned access bits can be a superset of
+				 * the ones requested, so...
+				 */
+				if ((rmode & mode) != mode) {
+					error = EACCES;
+				} else if (nfsaccess_cache_timeout > 0) {
+					/* cache the result */
+					nfsaccess_cache_fills++;
+					np->n_mode = rmode;
+					np->n_modecred = *ap->a_cred;
+					np->n_modestamp = time_second;
+				}
+			}
+			nfsm_reqdone;
 		}
-		nfsm_reqdone;
 		return (error);
 	} else {
 		if (error = nfsspec_access(ap))
Index: nfsnode.h
===================================================================
RCS file: /home/ncvs/src/sys/nfs/nfsnode.h,v
retrieving revision 1.26
diff -u -r1.26 nfsnode.h
--- nfsnode.h	1998/05/31 18:32:23	1.26
+++ nfsnode.h	1998/11/10 08:41:02
@@ -93,6 +93,9 @@
 	u_quad_t		n_lrev;		/* Modify rev for lease */
 	struct vattr		n_vattr;	/* Vnode attribute cache */
 	time_t			n_attrstamp;	/* Attr. cache timestamp */
+	u_int32_t		n_mode;		/* ACCESS mode cache */
+	struct ucred		n_modecred;	/* credentials having mode */
+	time_t			n_modestamp;	/* mode cache timestamp */
 	time_t			n_mtime;	/* Prev modify time. */
 	time_t			n_ctime;	/* Prev create time. */
 	time_t			n_expiry;	/* Lease expiry time */

--==_Exmh_-13891298860
Content-Type: text/plain; charset=us-ascii

\\  Sometimes you're ahead,       \\  Mike Smith
\\  sometimes you're behind.      \\  mike@smith.net.au
\\  The race is long, and in the  \\  msmith@freebsd.org
\\  end it's only with yourself.  \\  msmith@cdrom.com

--==_Exmh_-13891298860--



To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199811101002.CAA01721>