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>