diff options
author | Rick Macklem <rmacklem@FreeBSD.org> | 2021-06-09 15:00:43 +0000 |
---|---|---|
committer | Rick Macklem <rmacklem@FreeBSD.org> | 2021-06-09 15:00:43 +0000 |
commit | 5e5ca4c8fc53d31bf71e182e08c4ccd8290428c7 (patch) | |
tree | a6af8871f2b021b89b237d459420f277b69b1e64 | |
parent | 334335cb14d4754d45265d19cd3c6e7708cca0c1 (diff) | |
download | src-5e5ca4c8fc53d31bf71e182e08c4ccd8290428c7.tar.gz src-5e5ca4c8fc53d31bf71e182e08c4ccd8290428c7.zip |
nfscl: Add a "has acquired a delegation" flag for delegations
A problem was reported via email, where a large (130000+) accumulation
of NFSv4 opens on an NFSv4 mount caused significant lock contention
on the mutex used to protect the client mount's open/lock state.
Although the root cause for the accumulation of opens was not
resolved, it is obvious that the NFSv4 client is not designed to
handle 100000+ opens efficiently.
For a common case where delegations are not being issued by the
NFSv4 server, the code acquires the mutex lock for open/lock state,
finds the delegation list empty and just unlocks the mutex and returns.
This patch adds an NFS mount point flag that is set when a delegation
is issued for the mount. Then the patched code checks for this flag
before acquiring the open/lock mutex, avoiding the need to acquire
the lock for the case where delegations are not being issued by the
NFSv4 server.
This change appears to be performance neutral for a small number
of opens, but should reduce lock contention for a large number of opens
for the common case where server is not issuing delegations.
This commit should not affect the high level semantics of delegation
handling.
MFC after: 2 weeks
-rw-r--r-- | sys/fs/nfsclient/nfs_clstate.c | 44 | ||||
-rw-r--r-- | sys/fs/nfsclient/nfsmount.h | 1 |
2 files changed, 42 insertions, 3 deletions
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index 5e4ac2ae9d4d..0659e77289e9 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -438,20 +438,32 @@ nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp, int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp) { struct nfscldeleg *dp = *dpp, *tdp; + struct nfsmount *nmp; + KASSERT(mp != NULL, ("nfscl_deleg: mp NULL")); + nmp = VFSTONFS(mp); /* * First, if we have received a Read delegation for a file on a * read/write file system, just return it, because they aren't * useful, imho. */ - if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) && + if (dp != NULL && !NFSMNT_RDONLY(mp) && (dp->nfsdl_flags & NFSCLDL_READ)) { - (void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p); + nfscl_trydelegreturn(dp, cred, nmp, p); free(dp, M_NFSCLDELEG); *dpp = NULL; return (0); } + /* + * Since a delegation might be added to the mount, + * set NFSMNTP_DELEGISSUED now. If a delegation already + * exagain ists, setting this flag is harmless. + */ + NFSLOCKMNT(nmp); + nmp->nm_privflag |= NFSMNTP_DELEGISSUED; + NFSUNLOCKMNT(nmp); + /* Look for the correct deleg, based upon FH */ NFSLOCKCLSTATE(); tdp = nfscl_finddeleg(clp, nfhp, fhlen); @@ -3356,12 +3368,20 @@ nfscl_delegreturnvp(vnode_t vp, NFSPROC_T *p) struct nfscldeleg *dp; struct ucred *cred; struct nfsnode *np; + struct nfsmount *nmp; + nmp = VFSTONFS(vp->v_mount); + NFSLOCKMNT(nmp); + if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) { + NFSUNLOCKMNT(nmp); + return; + } + NFSUNLOCKMNT(nmp); np = VTONFS(vp); cred = newnfs_getcred(); dp = NULL; NFSLOCKCLSTATE(); - clp = VFSTONFS(vp->v_mount)->nm_clp; + clp = nmp->nm_clp; if (clp != NULL) dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len); @@ -4500,6 +4520,12 @@ nfscl_nodeleg(vnode_t vp, int writedeleg) nmp = VFSTONFS(vp->v_mount); if (!NFSHASNFSV4(nmp)) return (1); + NFSLOCKMNT(nmp); + if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) { + NFSUNLOCKMNT(nmp); + return (1); + } + NFSUNLOCKMNT(nmp); NFSLOCKCLSTATE(); clp = nfscl_findcl(nmp); if (clp == NULL) { @@ -4856,6 +4882,12 @@ nfscl_delegmodtime(vnode_t vp) nmp = VFSTONFS(vp->v_mount); if (!NFSHASNFSV4(nmp)) return; + NFSLOCKMNT(nmp); + if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) { + NFSUNLOCKMNT(nmp); + return; + } + NFSUNLOCKMNT(nmp); NFSLOCKCLSTATE(); clp = nfscl_findcl(nmp); if (clp == NULL) { @@ -4885,6 +4917,12 @@ nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime) nmp = VFSTONFS(vp->v_mount); if (!NFSHASNFSV4(nmp)) return; + NFSLOCKMNT(nmp); + if ((nmp->nm_privflag & NFSMNTP_DELEGISSUED) == 0) { + NFSUNLOCKMNT(nmp); + return; + } + NFSUNLOCKMNT(nmp); NFSLOCKCLSTATE(); clp = nfscl_findcl(nmp); if (clp == NULL) { diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h index 57adcd8f2fca..f8ea8c9a1418 100644 --- a/sys/fs/nfsclient/nfsmount.h +++ b/sys/fs/nfsclient/nfsmount.h @@ -115,6 +115,7 @@ struct nfsmount { #define NFSMNTP_NOXATTR 0x00000080 #define NFSMNTP_NOADVISE 0x00000100 #define NFSMNTP_NOALLOCATE 0x00000200 +#define NFSMNTP_DELEGISSUED 0x00000400 /* New mount flags only used by the kernel via nmount(2). */ #define NFSMNT_TLS 0x00000001 |