Commit 8be12e0c authored by Jeff Layton's avatar Jeff Layton Committed by Chuck Lever
Browse files

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: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent facc4e3c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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 */
+32 −30
Original line number Diff line number Diff line
@@ -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, &lt);

	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(&lt, 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);
+1 −1
Original line number Diff line number Diff line
@@ -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.