From: "J. Bruce Fields" <bfields@fieldses.org>
To: Jeff Layton <jlayton@poochiereds.net>
Cc: Anna Schumaker <Anna.Schumaker@netapp.com>,
Andrew W Elble <aweits@rit.edu>,
linux-nfs@vger.kernel.org
Subject: Re: [PATCH v2 1/2] nfsd: ensure that the ol stateid hash reference is only put once
Date: Mon, 24 Aug 2015 16:34:56 -0400 [thread overview]
Message-ID: <20150824203456.GA708@fieldses.org> (raw)
In-Reply-To: <1440434508-16046-2-git-send-email-jeff.layton@primarydata.com>
On Mon, Aug 24, 2015 at 12:41:47PM -0400, Jeff Layton wrote:
> 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.
Can we make this any simpler if we make unhash_ol_stateid do the put
itself instead of making every caller do it?
Also, while I'm looking at that.... The stateid-putting code is
partially duplicated between nfs4_put_stid and put_ol_stateid_locked,
with the difference just being that the latter moves stuff to a list so
we can put a bunch at once under one cl_lock. It'd be nice if we could
remove that bit of duplication.
Haven't tried yet, though.
--b.
>
> 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 | 58 +++++++++++++++++++++++++++++++++--------------------
> 1 file changed, 36 insertions(+), 22 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index c0c47a878cc6..4b4faf5e4bc7 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -1009,16 +1009,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)
> @@ -1068,25 +1072,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)
> @@ -1134,7 +1140,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);
> @@ -1147,21 +1153,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)
> @@ -1169,8 +1180,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);
> }
> @@ -1215,8 +1226,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);
> }
> }
>
> @@ -6014,7 +6028,7 @@ nfsd_inject_add_lock_to_list(struct nfs4_ol_stateid *lst,
>
> static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max,
> struct list_head *collect,
> - void (*func)(struct nfs4_ol_stateid *))
> + bool (*func)(struct nfs4_ol_stateid *))
> {
> struct nfs4_openowner *oop;
> struct nfs4_ol_stateid *stp, *st_next;
> @@ -6028,9 +6042,9 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max,
> list_for_each_entry_safe(lst, lst_next,
> &stp->st_locks, st_locks) {
> if (func) {
> - func(lst);
> - nfsd_inject_add_lock_to_list(lst,
> - collect);
> + if (func(lst))
> + nfsd_inject_add_lock_to_list(lst,
> + collect);
> }
> ++count;
> /*
> --
> 2.4.3
next prev parent reply other threads:[~2015-08-24 20:34 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-08-24 16:41 [PATCH v2 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Jeff Layton
2015-08-24 16:41 ` [PATCH v2 1/2] nfsd: ensure that the ol stateid hash reference is only put once Jeff Layton
2015-08-24 20:34 ` J. Bruce Fields [this message]
2015-08-24 20:40 ` Jeff Layton
2015-08-25 19:21 ` J. Bruce Fields
2015-08-24 16:41 ` [PATCH v2 2/2] nfsd: ensure that delegation stateid hash references are " Jeff Layton
2015-08-24 19:54 ` [PATCH v2 0/2] nfsd: fix unhashing races in nfsv4 stateid handling Andrew W Elble
2015-08-25 18:32 ` Andrew W Elble
2015-08-25 19:17 ` J. Bruce Fields
2015-08-24 21:25 ` Anna Schumaker
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150824203456.GA708@fieldses.org \
--to=bfields@fieldses.org \
--cc=Anna.Schumaker@netapp.com \
--cc=aweits@rit.edu \
--cc=jlayton@poochiereds.net \
--cc=linux-nfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).