lkml.org 
[lkml]   [2015]   [Sep]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.1 056/159] nfsd: ensure that delegation stateid hash references are only put once
    Date
    4.1-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Jeff Layton <jlayton@poochiereds.net>

    commit 3fcbbd244ed1d20dc0eb7d48d729503992fa9b7d upstream.

    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.

    Tested-by: Andrew W Elble <aweits@rit.edu>
    Tested-by: Anna Schumaker <Anna.Schumaker@netapp.com>
    Signed-off-by: Jeff Layton <jeff.layton@primarydata.com>
    Signed-off-by: J. Bruce Fields <bfields@redhat.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/nfsd/nfs4state.c | 26 +++++++++++++++++---------
    1 file changed, 17 insertions(+), 9 deletions(-)

    --- a/fs/nfsd/nfs4state.c
    +++ b/fs/nfsd/nfs4state.c
    @@ -777,13 +777,16 @@ hash_delegation_locked(struct nfs4_deleg
    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;
    @@ -792,16 +795,21 @@ unhash_delegation_locked(struct nfs4_del
    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)
    @@ -1725,7 +1733,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);
    @@ -4357,7 +4365,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);
    @@ -6282,7 +6290,7 @@ static u64 nfsd_find_all_delegations(str
    continue;

    atomic_inc(&clp->cl_refcount);
    - unhash_delegation_locked(dp);
    + WARN_ON(!unhash_delegation_locked(dp));
    list_add(&dp->dl_recall_lru, victims);
    }
    ++count;
    @@ -6612,7 +6620,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);



    \
     
     \ /
      Last update: 2015-09-26 23:41    [W:2.963 / U:0.152 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site