mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 14:51:51 -04:00
nfsd: convert global state_lock to per-net deleg_lock
Replace the global state_lock spinlock with a per-nfsd_net deleg_lock. The state_lock was only used to protect delegation lifecycle operations (the del_recall_lru list and delegation hash/unhash), all of which are scoped to a single network namespace. Making the lock per-net removes a source of unnecessary contention between containers. Signed-off-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
@@ -99,6 +99,9 @@ struct nfsd_net {
|
||||
*/
|
||||
struct list_head client_lru;
|
||||
struct list_head close_lru;
|
||||
|
||||
/* protects del_recall_lru and delegation hash/unhash */
|
||||
spinlock_t deleg_lock ____cacheline_aligned;
|
||||
struct list_head del_recall_lru;
|
||||
|
||||
/* protected by blocked_locks_lock */
|
||||
|
||||
@@ -93,13 +93,6 @@ static void deleg_reaper(struct nfsd_net *nn);
|
||||
|
||||
/* Locking: */
|
||||
|
||||
/*
|
||||
* Currently used for the del_recall_lru and file hash table. In an
|
||||
* effort to decrease the scope of the client_mutex, this spinlock may
|
||||
* eventually cover more:
|
||||
*/
|
||||
static DEFINE_SPINLOCK(state_lock);
|
||||
|
||||
enum nfsd4_st_mutex_lock_subclass {
|
||||
OPEN_STATEID_MUTEX = 0,
|
||||
LOCK_STATEID_MUTEX = 1,
|
||||
@@ -1295,8 +1288,9 @@ nfs4_delegation_exists(struct nfs4_client *clp, struct nfs4_file *fp)
|
||||
{
|
||||
struct nfs4_delegation *searchdp = NULL;
|
||||
struct nfs4_client *searchclp = NULL;
|
||||
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
|
||||
|
||||
lockdep_assert_held(&state_lock);
|
||||
lockdep_assert_held(&nn->deleg_lock);
|
||||
lockdep_assert_held(&fp->fi_lock);
|
||||
|
||||
list_for_each_entry(searchdp, &fp->fi_delegations, dl_perfile) {
|
||||
@@ -1325,8 +1319,9 @@ static int
|
||||
hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
|
||||
{
|
||||
struct nfs4_client *clp = dp->dl_stid.sc_client;
|
||||
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
|
||||
|
||||
lockdep_assert_held(&state_lock);
|
||||
lockdep_assert_held(&nn->deleg_lock);
|
||||
lockdep_assert_held(&fp->fi_lock);
|
||||
lockdep_assert_held(&clp->cl_lock);
|
||||
|
||||
@@ -1348,8 +1343,10 @@ static bool
|
||||
unhash_delegation_locked(struct nfs4_delegation *dp, unsigned short statusmask)
|
||||
{
|
||||
struct nfs4_file *fp = dp->dl_stid.sc_file;
|
||||
struct nfsd_net *nn = net_generic(dp->dl_stid.sc_client->net,
|
||||
nfsd_net_id);
|
||||
|
||||
lockdep_assert_held(&state_lock);
|
||||
lockdep_assert_held(&nn->deleg_lock);
|
||||
|
||||
if (!delegation_hashed(dp))
|
||||
return false;
|
||||
@@ -1374,10 +1371,12 @@ unhash_delegation_locked(struct nfs4_delegation *dp, unsigned short statusmask)
|
||||
static void destroy_delegation(struct nfs4_delegation *dp)
|
||||
{
|
||||
bool unhashed;
|
||||
struct nfsd_net *nn = net_generic(dp->dl_stid.sc_client->net,
|
||||
nfsd_net_id);
|
||||
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
unhashed = unhash_delegation_locked(dp, SC_STATUS_CLOSED);
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
if (unhashed)
|
||||
destroy_unhashed_deleg(dp);
|
||||
}
|
||||
@@ -1840,11 +1839,11 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb)
|
||||
case SC_TYPE_DELEG:
|
||||
refcount_inc(&stid->sc_count);
|
||||
dp = delegstateid(stid);
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
if (!unhash_delegation_locked(
|
||||
dp, SC_STATUS_ADMIN_REVOKED))
|
||||
dp = NULL;
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
if (dp)
|
||||
revoke_delegation(dp);
|
||||
break;
|
||||
@@ -2510,13 +2509,13 @@ __destroy_client(struct nfs4_client *clp)
|
||||
struct nfs4_delegation *dp;
|
||||
LIST_HEAD(reaplist);
|
||||
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
while (!list_empty(&clp->cl_delegations)) {
|
||||
dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
|
||||
unhash_delegation_locked(dp, SC_STATUS_CLOSED);
|
||||
list_add(&dp->dl_recall_lru, &reaplist);
|
||||
}
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
while (!list_empty(&reaplist)) {
|
||||
dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
|
||||
list_del_init(&dp->dl_recall_lru);
|
||||
@@ -5427,12 +5426,12 @@ static void nfsd4_cb_recall_prepare(struct nfsd4_callback *cb)
|
||||
* If the dl_time != 0, then we know that it has already been
|
||||
* queued for a lease break. Don't queue it again.
|
||||
*/
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
if (delegation_hashed(dp) && dp->dl_time == 0) {
|
||||
dp->dl_time = ktime_get_boottime_seconds();
|
||||
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru);
|
||||
}
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
}
|
||||
|
||||
static int nfsd4_cb_recall_done(struct nfsd4_callback *cb,
|
||||
@@ -6064,6 +6063,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
|
||||
{
|
||||
bool deleg_ts = nfsd4_want_deleg_timestamps(open);
|
||||
struct nfs4_client *clp = stp->st_stid.sc_client;
|
||||
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
|
||||
struct nfs4_file *fp = stp->st_stid.sc_file;
|
||||
struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate;
|
||||
struct nfs4_delegation *dp;
|
||||
@@ -6123,7 +6123,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
spin_lock(&fp->fi_lock);
|
||||
if (nfs4_delegation_exists(clp, fp))
|
||||
status = -EAGAIN;
|
||||
@@ -6138,7 +6138,7 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
|
||||
} else
|
||||
fp->fi_delegees++;
|
||||
spin_unlock(&fp->fi_lock);
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
if (nf)
|
||||
nfsd_file_put(nf);
|
||||
if (status)
|
||||
@@ -6182,13 +6182,13 @@ nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
|
||||
if (fp->fi_had_conflict)
|
||||
goto out_unlock;
|
||||
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
spin_lock(&clp->cl_lock);
|
||||
spin_lock(&fp->fi_lock);
|
||||
status = hash_delegation_locked(dp, fp);
|
||||
spin_unlock(&fp->fi_lock);
|
||||
spin_unlock(&clp->cl_lock);
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
|
||||
if (status)
|
||||
goto out_unlock;
|
||||
@@ -6964,7 +6964,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||
|
||||
nfs40_clean_admin_revoked(nn, <);
|
||||
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
list_for_each_safe(pos, next, &nn->del_recall_lru) {
|
||||
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
|
||||
if (!state_expired(<, dp->dl_time))
|
||||
@@ -6973,7 +6973,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||
unhash_delegation_locked(dp, SC_STATUS_REVOKED);
|
||||
list_add(&dp->dl_recall_lru, &reaplist);
|
||||
}
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
while (!list_empty(&reaplist)) {
|
||||
dp = list_first_entry(&reaplist, struct nfs4_delegation,
|
||||
dl_recall_lru);
|
||||
@@ -8996,6 +8996,7 @@ static int nfs4_state_create_net(struct net *net)
|
||||
INIT_LIST_HEAD(&nn->client_lru);
|
||||
INIT_LIST_HEAD(&nn->close_lru);
|
||||
INIT_LIST_HEAD(&nn->del_recall_lru);
|
||||
spin_lock_init(&nn->deleg_lock);
|
||||
spin_lock_init(&nn->client_lock);
|
||||
spin_lock_init(&nn->s2s_cp_lock);
|
||||
idr_init(&nn->s2s_cp_stateids);
|
||||
@@ -9127,13 +9128,13 @@ nfs4_state_shutdown_net(struct net *net)
|
||||
locks_end_grace(&nn->nfsd4_manager);
|
||||
|
||||
INIT_LIST_HEAD(&reaplist);
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
list_for_each_safe(pos, next, &nn->del_recall_lru) {
|
||||
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
|
||||
unhash_delegation_locked(dp, SC_STATUS_CLOSED);
|
||||
list_add(&dp->dl_recall_lru, &reaplist);
|
||||
}
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
list_for_each_safe(pos, next, &reaplist) {
|
||||
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
|
||||
list_del_init(&dp->dl_recall_lru);
|
||||
@@ -9456,6 +9457,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
|
||||
struct nfsd_file *nf)
|
||||
{
|
||||
struct nfs4_client *clp = cstate->clp;
|
||||
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
|
||||
struct nfs4_delegation *dp;
|
||||
struct file_lease *fl;
|
||||
struct nfs4_file *fp, *rfp;
|
||||
@@ -9479,7 +9481,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
|
||||
}
|
||||
|
||||
/* if this client already has one, return that it's unavailable */
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
spin_lock(&fp->fi_lock);
|
||||
/* existing delegation? */
|
||||
if (nfs4_delegation_exists(clp, fp)) {
|
||||
@@ -9491,7 +9493,7 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
|
||||
++fp->fi_delegees;
|
||||
}
|
||||
spin_unlock(&fp->fi_lock);
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
|
||||
if (status) {
|
||||
put_nfs4_file(fp);
|
||||
@@ -9520,13 +9522,13 @@ nfsd_get_dir_deleg(struct nfsd4_compound_state *cstate,
|
||||
* trying to set a delegation on the same file. If that happens,
|
||||
* then just say UNAVAIL.
|
||||
*/
|
||||
spin_lock(&state_lock);
|
||||
spin_lock(&nn->deleg_lock);
|
||||
spin_lock(&clp->cl_lock);
|
||||
spin_lock(&fp->fi_lock);
|
||||
status = hash_delegation_locked(dp, fp);
|
||||
spin_unlock(&fp->fi_lock);
|
||||
spin_unlock(&clp->cl_lock);
|
||||
spin_unlock(&state_lock);
|
||||
spin_unlock(&nn->deleg_lock);
|
||||
|
||||
if (!status) {
|
||||
put_nfs4_file(fp);
|
||||
|
||||
@@ -123,7 +123,7 @@ struct nfs4_stid {
|
||||
#define SC_TYPE_LAYOUT BIT(3)
|
||||
unsigned short sc_type;
|
||||
|
||||
/* state_lock protects sc_status for delegation stateids.
|
||||
/* nn->deleg_lock protects sc_status for delegation stateids.
|
||||
* ->cl_lock protects sc_status for open and lock stateids.
|
||||
* ->st_mutex also protect sc_status for open stateids.
|
||||
* ->ls_lock protects sc_status for layout stateids.
|
||||
|
||||
Reference in New Issue
Block a user