nfsd4: keep a reference count on client while in use
authorBenny Halevy <bhalevy@panasas.com>
Tue, 11 May 2010 21:13:54 +0000 (00:13 +0300)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Thu, 13 May 2010 15:58:54 +0000 (11:58 -0400)
Get a refcount on the client on SEQUENCE,
Release the refcount and renew the client when all respective compounds completed.
Do not expire the client by the laundromat while in use.
If the client was expired via another path, free it when the compounds
complete and the refcount reaches 0.

Note that unhash_client_locked must call list_del_init on cl_lru as
it may be called twice for the same client (once from nfs4_laundromat
and then from expire_client)

Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/state.h

index 98aa7e8827b954d01829c6e8b090375b2156e248..cc0e9117dd16398895fc545b95b70ee8764fa6ac 100644 (file)
@@ -701,6 +701,22 @@ free_client(struct nfs4_client *clp)
        kfree(clp);
 }
 
+void
+release_session_client(struct nfsd4_session *session)
+{
+       struct nfs4_client *clp = session->se_client;
+
+       if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock))
+               return;
+       if (is_client_expired(clp)) {
+               free_client(clp);
+               session->se_client = NULL;
+       } else
+               renew_client_locked(clp);
+       spin_unlock(&client_lock);
+       nfsd4_put_session(session);
+}
+
 /* must be called under the client_lock */
 static inline void
 unhash_client_locked(struct nfs4_client *clp)
@@ -1476,8 +1492,7 @@ out:
        /* Hold a session reference until done processing the compound. */
        if (cstate->session) {
                nfsd4_get_session(cstate->session);
-               /* Renew the clientid on success and on replay */
-               renew_client_locked(session->se_client);
+               atomic_inc(&session->se_client->cl_refcount);
        }
        spin_unlock(&client_lock);
        dprintk("%s: return %d\n", __func__, ntohl(status));
@@ -2598,7 +2613,13 @@ nfs4_laundromat(void)
                                clientid_val = t;
                        break;
                }
-               list_move(&clp->cl_lru, &reaplist);
+               if (atomic_read(&clp->cl_refcount)) {
+                       dprintk("NFSD: client in use (clientid %08x)\n",
+                               clp->cl_clientid.cl_id);
+                       continue;
+               }
+               unhash_client_locked(clp);
+               list_add(&clp->cl_lru, &reaplist);
        }
        spin_unlock(&client_lock);
        list_for_each_safe(pos, next, &reaplist) {
index 5c2de471329a6cacd3a6d4c13f07136245f45e10..126d0caabb3cd7e8f390f0a839c0c8c3e9c9f2b3 100644 (file)
@@ -3313,7 +3313,8 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
                        dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
                        cs->slot->sl_inuse = false;
                }
-               nfsd4_put_session(cs->session);
+               /* Renew the clientid on success and on replay */
+               release_session_client(cs->session);
        }
        return 1;
 }
index cfd743ea4b7966e2237a85fa7ddaa312143e13ab..006c84230c7c5f71ab38d6f43e6a9329736a39eb 100644 (file)
@@ -420,6 +420,7 @@ extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id);
 extern void nfsd4_recdir_purge_old(void);
 extern int nfsd4_create_clid_dir(struct nfs4_client *clp);
 extern void nfsd4_remove_clid_dir(struct nfs4_client *clp);
+extern void release_session_client(struct nfsd4_session *);
 
 static inline void
 nfs4_put_stateowner(struct nfs4_stateowner *so)