nfsd: Ensure struct nfs4_client is unhashed before we try to destroy it
authorTrond Myklebust <trond.myklebust@primarydata.com>
Wed, 30 Jul 2014 12:27:02 +0000 (08:27 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 1 Aug 2014 20:28:17 +0000 (16:28 -0400)
When we remove the client_mutex protection, we will need to ensure
that it can't be found by other threads while we're destroying it.

Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs4state.c

index 52ec47d..cb630db 100644 (file)
@@ -1588,12 +1588,23 @@ free_client(struct nfs4_client *clp)
 }
 
 /* must be called under the client_lock */
-static inline void
+static void
 unhash_client_locked(struct nfs4_client *clp)
 {
+       struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
        struct nfsd4_session *ses;
 
-       list_del(&clp->cl_lru);
+       /* Mark the client as expired! */
+       clp->cl_time = 0;
+       /* Make it invisible */
+       if (!list_empty(&clp->cl_idhash)) {
+               list_del_init(&clp->cl_idhash);
+               if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags))
+                       rb_erase(&clp->cl_namenode, &nn->conf_name_tree);
+               else
+                       rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
+       }
+       list_del_init(&clp->cl_lru);
        spin_lock(&clp->cl_lock);
        list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
                list_del_init(&ses->se_hash);
@@ -1601,7 +1612,17 @@ unhash_client_locked(struct nfs4_client *clp)
 }
 
 static void
-destroy_client(struct nfs4_client *clp)
+unhash_client(struct nfs4_client *clp)
+{
+       struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+       spin_lock(&nn->client_lock);
+       unhash_client_locked(clp);
+       spin_unlock(&nn->client_lock);
+}
+
+static void
+__destroy_client(struct nfs4_client *clp)
 {
        struct nfs4_openowner *oo;
        struct nfs4_delegation *dp;
@@ -1634,22 +1655,24 @@ destroy_client(struct nfs4_client *clp)
        nfsd4_shutdown_callback(clp);
        if (clp->cl_cb_conn.cb_xprt)
                svc_xprt_put(clp->cl_cb_conn.cb_xprt);
-       list_del(&clp->cl_idhash);
-       if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags))
-               rb_erase(&clp->cl_namenode, &nn->conf_name_tree);
-       else
-               rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
        spin_lock(&nn->client_lock);
-       unhash_client_locked(clp);
        WARN_ON_ONCE(atomic_read(&clp->cl_refcount));
        free_client(clp);
        spin_unlock(&nn->client_lock);
 }
 
+static void
+destroy_client(struct nfs4_client *clp)
+{
+       unhash_client(clp);
+       __destroy_client(clp);
+}
+
 static void expire_client(struct nfs4_client *clp)
 {
+       unhash_client(clp);
        nfsd4_client_record_remove(clp);
-       destroy_client(clp);
+       __destroy_client(clp);
 }
 
 static void copy_verf(struct nfs4_client *target, nfs4_verifier *source)