All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling
@ 2015-08-22 14:52 Jeff Layton
  2015-08-22 14:52 ` [PATCH 1/2] nfsd: ensure that the ol stateid hash reference is only put once Jeff Layton
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Jeff Layton @ 2015-08-22 14:52 UTC (permalink / raw)
  To: bfields; +Cc: Anna Schumaker, Andrew W Elble, linux-nfs

I hadn't heard from Andrew in a while so I'm not 100% clear on whether
his patches had fixed the race that he had spotted in the NFSv4
unhashing code.

Still, once he had done the analysis, the problem was clear. I think
this patchset should fix the problem in a relatively clean way. I've
run some tests against this and it seems to not have broken anything,
but I haven't been able to reproduce the actual race so I can't
verify that this fixes it.

Andrew, Anna, if you are able to do so could you test these patches
and let us know whether this fixes those races?

Jeff Layton (2):
  nfsd: ensure that the ol stateid hash reference is only put once
  nfsd: ensure that delegation stateid hash references are only put once

 fs/nfsd/nfs4state.c | 76 ++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 49 insertions(+), 27 deletions(-)

-- 
2.4.3


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] nfsd: ensure that the ol stateid hash reference is only put once
  2015-08-22 14:52 [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Jeff Layton
@ 2015-08-22 14:52 ` Jeff Layton
  2015-08-22 14:52 ` [PATCH 2/2] nfsd: ensure that delegation stateid hash references are " Jeff Layton
  2015-09-18 19:05 ` [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Benjamin Coddington
  2 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2015-08-22 14:52 UTC (permalink / raw)
  To: bfields; +Cc: Anna Schumaker, Andrew W Elble, linux-nfs

When an open or lock stateid is hashed, we take an extra reference to
it. When we unhash it, we drop that reference. The code however does
not properly account for the case where we have two callers concurrently
trying to unhash the stateid. This can lead to list corruption and the
hash reference being put more than once.

Fix this by having unhash_ol_stateid use list_del_init on the st_perfile
list_head, and then testing to see if that list_head is empty before
releasing the hash reference. This means that some of the unhashing
wrappers now become bool return functions so we can test to see whether
the stateid was unhashed before we put the reference.

Reported-by: Andrew W Elble <aweits@rit.edu>
Reported-by: Anna Schumaker <Anna.Schumaker@netapp.com>
Signed-off-by: Jeff Layton <jeff.layton@primarydata.com>
---
 fs/nfsd/nfs4state.c | 50 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 18 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ef9cb8f5f758..c2448ffd6b8e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1011,16 +1011,20 @@ static void nfs4_put_stateowner(struct nfs4_stateowner *sop)
 	nfs4_free_stateowner(sop);
 }
 
-static void unhash_ol_stateid(struct nfs4_ol_stateid *stp)
+static bool unhash_ol_stateid(struct nfs4_ol_stateid *stp)
 {
 	struct nfs4_file *fp = stp->st_stid.sc_file;
 
 	lockdep_assert_held(&stp->st_stateowner->so_client->cl_lock);
 
+	if (list_empty(&stp->st_perfile))
+		return false;
+
 	spin_lock(&fp->fi_lock);
-	list_del(&stp->st_perfile);
+	list_del_init(&stp->st_perfile);
 	spin_unlock(&fp->fi_lock);
 	list_del(&stp->st_perstateowner);
+	return true;
 }
 
 static void nfs4_free_ol_stateid(struct nfs4_stid *stid)
@@ -1073,25 +1077,27 @@ static void put_ol_stateid_locked(struct nfs4_ol_stateid *stp,
 	list_add(&stp->st_locks, reaplist);
 }
 
-static void unhash_lock_stateid(struct nfs4_ol_stateid *stp)
+static bool unhash_lock_stateid(struct nfs4_ol_stateid *stp)
 {
 	struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
 
 	lockdep_assert_held(&oo->oo_owner.so_client->cl_lock);
 
 	list_del_init(&stp->st_locks);
-	unhash_ol_stateid(stp);
 	nfs4_unhash_stid(&stp->st_stid);
+	return unhash_ol_stateid(stp);
 }
 
 static void release_lock_stateid(struct nfs4_ol_stateid *stp)
 {
 	struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner);
+	bool unhashed;
 
 	spin_lock(&oo->oo_owner.so_client->cl_lock);
-	unhash_lock_stateid(stp);
+	unhashed = unhash_lock_stateid(stp);
 	spin_unlock(&oo->oo_owner.so_client->cl_lock);
-	nfs4_put_stid(&stp->st_stid);
+	if (unhashed)
+		nfs4_put_stid(&stp->st_stid);
 }
 
 static void unhash_lockowner_locked(struct nfs4_lockowner *lo)
@@ -1139,7 +1145,7 @@ static void release_lockowner(struct nfs4_lockowner *lo)
 	while (!list_empty(&lo->lo_owner.so_stateids)) {
 		stp = list_first_entry(&lo->lo_owner.so_stateids,
 				struct nfs4_ol_stateid, st_perstateowner);
-		unhash_lock_stateid(stp);
+		WARN_ON(!unhash_lock_stateid(stp));
 		put_ol_stateid_locked(stp, &reaplist);
 	}
 	spin_unlock(&clp->cl_lock);
@@ -1152,21 +1158,26 @@ static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp,
 {
 	struct nfs4_ol_stateid *stp;
 
+	lockdep_assert_held(&open_stp->st_stid.sc_client->cl_lock);
+
 	while (!list_empty(&open_stp->st_locks)) {
 		stp = list_entry(open_stp->st_locks.next,
 				struct nfs4_ol_stateid, st_locks);
-		unhash_lock_stateid(stp);
+		WARN_ON(!unhash_lock_stateid(stp));
 		put_ol_stateid_locked(stp, reaplist);
 	}
 }
 
-static void unhash_open_stateid(struct nfs4_ol_stateid *stp,
+static bool unhash_open_stateid(struct nfs4_ol_stateid *stp,
 				struct list_head *reaplist)
 {
+	bool unhashed;
+
 	lockdep_assert_held(&stp->st_stid.sc_client->cl_lock);
 
-	unhash_ol_stateid(stp);
+	unhashed = unhash_ol_stateid(stp);
 	release_open_stateid_locks(stp, reaplist);
+	return unhashed;
 }
 
 static void release_open_stateid(struct nfs4_ol_stateid *stp)
@@ -1174,8 +1185,8 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)
 	LIST_HEAD(reaplist);
 
 	spin_lock(&stp->st_stid.sc_client->cl_lock);
-	unhash_open_stateid(stp, &reaplist);
-	put_ol_stateid_locked(stp, &reaplist);
+	if (unhash_open_stateid(stp, &reaplist))
+		put_ol_stateid_locked(stp, &reaplist);
 	spin_unlock(&stp->st_stid.sc_client->cl_lock);
 	free_ol_stateid_reaplist(&reaplist);
 }
@@ -1220,8 +1231,8 @@ static void release_openowner(struct nfs4_openowner *oo)
 	while (!list_empty(&oo->oo_owner.so_stateids)) {
 		stp = list_first_entry(&oo->oo_owner.so_stateids,
 				struct nfs4_ol_stateid, st_perstateowner);
-		unhash_open_stateid(stp, &reaplist);
-		put_ol_stateid_locked(stp, &reaplist);
+		if (unhash_open_stateid(stp, &reaplist))
+			put_ol_stateid_locked(stp, &reaplist);
 	}
 	spin_unlock(&clp->cl_lock);
 	free_ol_stateid_reaplist(&reaplist);
@@ -4752,7 +4763,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 		if (check_for_locks(stp->st_stid.sc_file,
 				    lockowner(stp->st_stateowner)))
 			break;
-		unhash_lock_stateid(stp);
+		WARN_ON(!unhash_lock_stateid(stp));
 		spin_unlock(&cl->cl_lock);
 		nfs4_put_stid(s);
 		ret = nfs_ok;
@@ -4968,20 +4979,23 @@ out:
 static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
 {
 	struct nfs4_client *clp = s->st_stid.sc_client;
+	bool unhashed;
 	LIST_HEAD(reaplist);
 
 	s->st_stid.sc_type = NFS4_CLOSED_STID;
 	spin_lock(&clp->cl_lock);
-	unhash_open_stateid(s, &reaplist);
+	unhashed = unhash_open_stateid(s, &reaplist);
 
 	if (clp->cl_minorversion) {
-		put_ol_stateid_locked(s, &reaplist);
+		if (unhashed)
+			put_ol_stateid_locked(s, &reaplist);
 		spin_unlock(&clp->cl_lock);
 		free_ol_stateid_reaplist(&reaplist);
 	} else {
 		spin_unlock(&clp->cl_lock);
 		free_ol_stateid_reaplist(&reaplist);
-		move_to_close_lru(s, clp->net);
+		if (unhashed)
+			move_to_close_lru(s, clp->net);
 	}
 }
 
-- 
2.4.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] nfsd: ensure that delegation stateid hash references are only put once
  2015-08-22 14:52 [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Jeff Layton
  2015-08-22 14:52 ` [PATCH 1/2] nfsd: ensure that the ol stateid hash reference is only put once Jeff Layton
@ 2015-08-22 14:52 ` Jeff Layton
  2015-09-18 19:05 ` [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Benjamin Coddington
  2 siblings, 0 replies; 5+ messages in thread
From: Jeff Layton @ 2015-08-22 14:52 UTC (permalink / raw)
  To: bfields; +Cc: Anna Schumaker, Andrew W Elble, linux-nfs

It's possible that a DELEGRETURN could race with (e.g.) client expiry,
in which case we could end up putting the delegation hash reference more
than once.

Have unhash_delegation_locked return a bool that indicates whether it
was already unhashed. In the case of destroy_delegation we only
conditionally put the hash reference if that returns true.

The other callers of unhash_delegation_locked call it while walking
list_heads that shouldn't yet be detached. If we find that it doesn't
return true in those cases, then throw a WARN_ON as that indicates that
we have a partially hashed delegation, and that something is likely very
wrong.

Signed-off-by: Jeff Layton <jeff.layton@primarydata.com>
---
 fs/nfsd/nfs4state.c | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c2448ffd6b8e..22bc632a65e4 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -779,13 +779,16 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
 	list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
 }
 
-static void
+static bool
 unhash_delegation_locked(struct nfs4_delegation *dp)
 {
 	struct nfs4_file *fp = dp->dl_stid.sc_file;
 
 	lockdep_assert_held(&state_lock);
 
+	if (list_empty(&dp->dl_perfile))
+		return false;
+
 	dp->dl_stid.sc_type = NFS4_CLOSED_DELEG_STID;
 	/* Ensure that deleg break won't try to requeue it */
 	++dp->dl_time;
@@ -794,16 +797,21 @@ unhash_delegation_locked(struct nfs4_delegation *dp)
 	list_del_init(&dp->dl_recall_lru);
 	list_del_init(&dp->dl_perfile);
 	spin_unlock(&fp->fi_lock);
+	return true;
 }
 
 static void destroy_delegation(struct nfs4_delegation *dp)
 {
+	bool unhashed;
+
 	spin_lock(&state_lock);
-	unhash_delegation_locked(dp);
+	unhashed = unhash_delegation_locked(dp);
 	spin_unlock(&state_lock);
-	put_clnt_odstate(dp->dl_clnt_odstate);
-	nfs4_put_deleg_lease(dp->dl_stid.sc_file);
-	nfs4_put_stid(&dp->dl_stid);
+	if (unhashed) {
+		put_clnt_odstate(dp->dl_clnt_odstate);
+		nfs4_put_deleg_lease(dp->dl_stid.sc_file);
+		nfs4_put_stid(&dp->dl_stid);
+	}
 }
 
 static void revoke_delegation(struct nfs4_delegation *dp)
@@ -1735,7 +1743,7 @@ __destroy_client(struct nfs4_client *clp)
 	spin_lock(&state_lock);
 	while (!list_empty(&clp->cl_delegations)) {
 		dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
-		unhash_delegation_locked(dp);
+		WARN_ON(!unhash_delegation_locked(dp));
 		list_add(&dp->dl_recall_lru, &reaplist);
 	}
 	spin_unlock(&state_lock);
@@ -4362,7 +4370,7 @@ nfs4_laundromat(struct nfsd_net *nn)
 			new_timeo = min(new_timeo, t);
 			break;
 		}
-		unhash_delegation_locked(dp);
+		WARN_ON(!unhash_delegation_locked(dp));
 		list_add(&dp->dl_recall_lru, &reaplist);
 	}
 	spin_unlock(&state_lock);
@@ -6314,7 +6322,7 @@ static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max,
 				continue;
 
 			atomic_inc(&clp->cl_refcount);
-			unhash_delegation_locked(dp);
+			WARN_ON(!unhash_delegation_locked(dp));
 			list_add(&dp->dl_recall_lru, victims);
 		}
 		++count;
@@ -6645,7 +6653,7 @@ nfs4_state_shutdown_net(struct net *net)
 	spin_lock(&state_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);
+		WARN_ON(!unhash_delegation_locked(dp));
 		list_add(&dp->dl_recall_lru, &reaplist);
 	}
 	spin_unlock(&state_lock);
-- 
2.4.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling
  2015-08-22 14:52 [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Jeff Layton
  2015-08-22 14:52 ` [PATCH 1/2] nfsd: ensure that the ol stateid hash reference is only put once Jeff Layton
  2015-08-22 14:52 ` [PATCH 2/2] nfsd: ensure that delegation stateid hash references are " Jeff Layton
@ 2015-09-18 19:05 ` Benjamin Coddington
  2015-09-18 19:07   ` Benjamin Coddington
  2 siblings, 1 reply; 5+ messages in thread
From: Benjamin Coddington @ 2015-09-18 19:05 UTC (permalink / raw)
  To: Jeff Layton; +Cc: bfields, Anna Schumaker, Andrew W Elble, linux-nfs

ACK to these two.  Sorry for the delay..

Ben

On Sat, 22 Aug 2015, Jeff Layton wrote:

> I hadn't heard from Andrew in a while so I'm not 100% clear on whether
> his patches had fixed the race that he had spotted in the NFSv4
> unhashing code.
>
> Still, once he had done the analysis, the problem was clear. I think
> this patchset should fix the problem in a relatively clean way. I've
> run some tests against this and it seems to not have broken anything,
> but I haven't been able to reproduce the actual race so I can't
> verify that this fixes it.
>
> Andrew, Anna, if you are able to do so could you test these patches
> and let us know whether this fixes those races?
>
> Jeff Layton (2):
>   nfsd: ensure that the ol stateid hash reference is only put once
>   nfsd: ensure that delegation stateid hash references are only put once
>
>  fs/nfsd/nfs4state.c | 76 ++++++++++++++++++++++++++++++++++-------------------
>  1 file changed, 49 insertions(+), 27 deletions(-)
>
> --
> 2.4.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling
  2015-09-18 19:05 ` [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Benjamin Coddington
@ 2015-09-18 19:07   ` Benjamin Coddington
  0 siblings, 0 replies; 5+ messages in thread
From: Benjamin Coddington @ 2015-09-18 19:07 UTC (permalink / raw)
  To: Jeff Layton; +Cc: bfields, Anna Schumaker, Andrew W Elble, linux-nfs

On Fri, 18 Sep 2015, Benjamin Coddington wrote:

> ACK to these two.  Sorry for the delay..
>
> Ben

Whoops!  Wrong posting, please disregard.  Sorry about the noise.

Not that this work is un-ackable.  Excellent work, this.  Thanks for fixing
this Jeff.

Ben

>
> On Sat, 22 Aug 2015, Jeff Layton wrote:
>
> > I hadn't heard from Andrew in a while so I'm not 100% clear on whether
> > his patches had fixed the race that he had spotted in the NFSv4
> > unhashing code.
> >
> > Still, once he had done the analysis, the problem was clear. I think
> > this patchset should fix the problem in a relatively clean way. I've
> > run some tests against this and it seems to not have broken anything,
> > but I haven't been able to reproduce the actual race so I can't
> > verify that this fixes it.
> >
> > Andrew, Anna, if you are able to do so could you test these patches
> > and let us know whether this fixes those races?
> >
> > Jeff Layton (2):
> >   nfsd: ensure that the ol stateid hash reference is only put once
> >   nfsd: ensure that delegation stateid hash references are only put once
> >
> >  fs/nfsd/nfs4state.c | 76 ++++++++++++++++++++++++++++++++++-------------------
> >  1 file changed, 49 insertions(+), 27 deletions(-)
> >
> > --
> > 2.4.3
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2015-09-18 19:07 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-22 14:52 [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Jeff Layton
2015-08-22 14:52 ` [PATCH 1/2] nfsd: ensure that the ol stateid hash reference is only put once Jeff Layton
2015-08-22 14:52 ` [PATCH 2/2] nfsd: ensure that delegation stateid hash references are " Jeff Layton
2015-09-18 19:05 ` [PATCH 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Benjamin Coddington
2015-09-18 19:07   ` Benjamin Coddington

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.