nfscl: Use hash lists to improve expected search performance for opens

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.  When searching for an open,
usually for a match by file handle, a linear search of all opens
is done.

Commit 3f7e14ad93 added a hash table of lists hashed on file handle
for the opens.  This patch uses the hash lists for searching for
a matching open based of file handle instead of an exhaustive
linear search of all opens.
This change appears to be performance neutral for a small number
of opens, but should improve expected performance for a large
number of opens.

This commit should not affect the high level semantics of open
handling.

(cherry picked from commit 96b40b8967)
This commit is contained in:
Rick Macklem 2021-05-27 19:08:36 -07:00
parent 624a723a95
commit 972883b9e0

View file

@ -1229,7 +1229,6 @@ nfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
struct nfscllockowner **lpp, int *dorpcp)
{
struct nfscllockowner *lp;
struct nfsclowner *owp;
struct nfsclopen *op;
struct nfscllock *nlop, *other_lop = NULL;
struct nfscldeleg *dp;
@ -1291,24 +1290,21 @@ nfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
*/
lp = NULL;
fnd = 0;
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
LIST_FOREACH(op, NFSCLOPENHASH(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len), nfso_hash) {
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (lp->nfsl_inprog == NULL &&
!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN)) {
fnd = 1;
break;
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (lp->nfsl_inprog == NULL &&
!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN)) {
fnd = 1;
break;
}
}
}
if (fnd)
break;
}
}
if (fnd)
break;
if (fnd)
break;
}
if (lp != NULL) {
@ -1338,7 +1334,6 @@ void
nfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p,
void *id, int flags)
{
struct nfsclowner *owp;
struct nfsclopen *op;
struct nfscllockowner *lp;
struct nfsnode *np;
@ -1347,20 +1342,19 @@ nfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p,
np = VTONFS(vp);
nfscl_filllockowner(id, own, flags);
NFSLOCKCLSTATE();
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
LIST_FOREACH(op, NFSCLOPENHASH(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len), nfso_hash) {
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (lp->nfsl_inprog == p &&
!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN)) {
lp->nfsl_inprog = NULL;
nfscl_lockunlock(&lp->nfsl_rwlock);
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (lp->nfsl_inprog == p &&
!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN)) {
lp->nfsl_inprog = NULL;
nfscl_lockunlock(&lp->nfsl_rwlock);
}
}
}
}
}
}
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
@ -1376,7 +1370,6 @@ int
nfscl_checkwritelocked(vnode_t vp, struct flock *fl,
struct ucred *cred, NFSPROC_T *p, void *id, int flags)
{
struct nfsclowner *owp;
struct nfscllockowner *lp;
struct nfsclopen *op;
struct nfsclclient *clp;
@ -1445,30 +1438,29 @@ nfscl_checkwritelocked(vnode_t vp, struct flock *fl,
/*
* Now, check state against the server.
*/
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
LIST_FOREACH(op, NFSCLOPENHASH(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len), nfso_hash) {
if (op->nfso_fhlen == np->n_fhp->nfh_len &&
!NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN))
break;
}
if (lp != NULL) {
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
if (lop->nfslo_first >= end)
break;
if (lop->nfslo_end <= off)
continue;
if (lop->nfslo_type == F_WRLCK) {
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
return (1);
}
LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
if (!NFSBCMP(lp->nfsl_owner, own,
NFSV4CL_LOCKNAMELEN))
break;
}
if (lp != NULL) {
LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
if (lop->nfslo_first >= end)
break;
if (lop->nfslo_end <= off)
continue;
if (lop->nfslo_type == F_WRLCK) {
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
return (1);
}
}
}
}
}
}
}
nfscl_clrelease(clp);
NFSUNLOCKCLSTATE();
@ -3243,23 +3235,22 @@ nfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
}
/* Now process the opens against the server. */
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == nfhp->nfh_len &&
!NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
nfhp->nfh_len)) {
/* Found an open, decrement cnt if possible */
if (notdecr && op->nfso_opencnt > 0) {
notdecr = 0;
op->nfso_opencnt--;
}
/*
* There are more opens, so just return.
*/
if (op->nfso_opencnt > 0) {
NFSUNLOCKCLSTATE();
return (0);
}
LIST_FOREACH(op, NFSCLOPENHASH(clp, nfhp->nfh_fh, nfhp->nfh_len),
nfso_hash) {
if (op->nfso_fhlen == nfhp->nfh_len &&
!NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
nfhp->nfh_len)) {
/* Found an open, decrement cnt if possible */
if (notdecr && op->nfso_opencnt > 0) {
notdecr = 0;
op->nfso_opencnt--;
}
/*
* There are more opens, so just return.
*/
if (op->nfso_opencnt > 0) {
NFSUNLOCKCLSTATE();
return (0);
}
}
}
@ -3310,24 +3301,21 @@ nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
/* Now process the opens against the server. */
lookformore:
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
op = LIST_FIRST(&owp->nfsow_open);
while (op != NULL) {
if (op->nfso_fhlen == nfhp->nfh_len &&
!NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
nfhp->nfh_len)) {
/* Found an open, close it. */
LIST_FOREACH(op, NFSCLOPENHASH(clp, nfhp->nfh_fh, nfhp->nfh_len),
nfso_hash) {
if (op->nfso_fhlen == nfhp->nfh_len &&
!NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
nfhp->nfh_len)) {
/* Found an open, close it. */
#ifdef DIAGNOSTIC
KASSERT((op->nfso_opencnt == 0),
("nfscl: bad open cnt on server (%d)",
op->nfso_opencnt));
KASSERT((op->nfso_opencnt == 0),
("nfscl: bad open cnt on server (%d)",
op->nfso_opencnt));
#endif
NFSUNLOCKCLSTATE();
nfsrpc_doclose(VFSTONFS(vp->v_mount), op, p);
NFSLOCKCLSTATE();
goto lookformore;
}
op = LIST_NEXT(op, nfso_list);
NFSUNLOCKCLSTATE();
nfsrpc_doclose(VFSTONFS(vp->v_mount), op, p);
NFSLOCKCLSTATE();
goto lookformore;
}
}
NFSUNLOCKCLSTATE();
@ -3956,7 +3944,6 @@ nfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
struct nfscllock **lopp)
{
struct nfsclowner *owp;
struct nfsclopen *op;
int ret;
@ -3965,15 +3952,13 @@ nfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
if (ret)
return (ret);
}
LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
if (op->nfso_fhlen == fhlen &&
!NFSBCMP(op->nfso_fh, fhp, fhlen)) {
ret = nfscl_checkconflict(&op->nfso_lock, nlop,
own, lopp);
if (ret)
return (ret);
}
LIST_FOREACH(op, NFSCLOPENHASH(clp, fhp, fhlen), nfso_hash) {
if (op->nfso_fhlen == fhlen &&
!NFSBCMP(op->nfso_fh, fhp, fhlen)) {
ret = nfscl_checkconflict(&op->nfso_lock, nlop,
own, lopp);
if (ret)
return (ret);
}
}
return (0);