nfsd: Protect session creation and client confirm using client_lock
authorJeff Layton <jlayton@primarydata.com>
Wed, 30 Jul 2014 12:27:07 +0000 (08:27 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 1 Aug 2014 20:28:21 +0000 (16:28 -0400)
In particular, we want to ensure that the move_to_confirmed() is
protected by the nn->client_lock spin lock, so that we can use that when
looking up the clientid etc. instead of relying on the client_mutex.

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

index f149e30..52a4677 100644 (file)
@@ -137,17 +137,6 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp)
        return nfs_ok;
 }
 
-static __be32 mark_client_expired(struct nfs4_client *clp)
-{
-       struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
-       __be32 ret;
-
-       spin_lock(&nn->client_lock);
-       ret = mark_client_expired_locked(clp);
-       spin_unlock(&nn->client_lock);
-       return ret;
-}
-
 static __be32 get_client_locked(struct nfs4_client *clp)
 {
        if (is_client_expired(clp))
@@ -1437,12 +1426,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
        new->se_cb_sec = cses->cb_sec;
        atomic_set(&new->se_ref, 0);
        idx = hash_sessionid(&new->se_sessionid);
-       spin_lock(&nn->client_lock);
        list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
        spin_lock(&clp->cl_lock);
        list_add(&new->se_perclnt, &clp->cl_sessions);
        spin_unlock(&clp->cl_lock);
-       spin_unlock(&nn->client_lock);
 
        if (cses->flags & SESSION4_BACK_CHAN) {
                struct sockaddr *sa = svc_addr(rqstp);
@@ -2411,6 +2398,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
 {
        struct sockaddr *sa = svc_addr(rqstp);
        struct nfs4_client *conf, *unconf;
+       struct nfs4_client *old = NULL;
        struct nfsd4_session *new;
        struct nfsd4_conn *conn;
        struct nfsd4_clid_slot *cs_slot = NULL;
@@ -2437,6 +2425,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                goto out_free_session;
 
        nfs4_lock_state();
+       spin_lock(&nn->client_lock);
        unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn);
        conf = find_confirmed_client(&cr_ses->clientid, true, nn);
        WARN_ON_ONCE(conf && unconf);
@@ -2455,7 +2444,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                        goto out_free_conn;
                }
        } else if (unconf) {
-               struct nfs4_client *old;
                if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
                    !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
                        status = nfserr_clid_inuse;
@@ -2473,10 +2461,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                }
                old = find_confirmed_client_by_name(&unconf->cl_name, nn);
                if (old) {
-                       status = mark_client_expired(old);
+                       status = mark_client_expired_locked(old);
                        if (status)
                                goto out_free_conn;
-                       expire_client(old);
+                       unhash_client_locked(old);
                }
                move_to_confirmed(unconf);
                conf = unconf;
@@ -2492,20 +2480,29 @@ nfsd4_create_session(struct svc_rqst *rqstp,
        cr_ses->flags &= ~SESSION4_RDMA;
 
        init_session(rqstp, new, conf, cr_ses);
-       nfsd4_init_conn(rqstp, conn, new);
+       nfsd4_get_session_locked(new);
 
        memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
               NFS4_MAX_SESSIONID_LEN);
        cs_slot->sl_seqid++;
        cr_ses->seqid = cs_slot->sl_seqid;
 
-       /* cache solo and embedded create sessions under the state lock */
+       /* cache solo and embedded create sessions under the client_lock */
        nfsd4_cache_create_session(cr_ses, cs_slot, status);
+       spin_unlock(&nn->client_lock);
+       /* init connection and backchannel */
+       nfsd4_init_conn(rqstp, conn, new);
+       nfsd4_put_session(new);
        nfs4_unlock_state();
+       if (old)
+               expire_client(old);
        return status;
 out_free_conn:
+       spin_unlock(&nn->client_lock);
        nfs4_unlock_state();
        free_conn(conn);
+       if (old)
+               expire_client(old);
 out_free_session:
        __free_session(new);
 out_release_drc_mem:
@@ -2965,6 +2962,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                         struct nfsd4_setclientid_confirm *setclientid_confirm)
 {
        struct nfs4_client *conf, *unconf;
+       struct nfs4_client *old = NULL;
        nfs4_verifier confirm = setclientid_confirm->sc_confirm; 
        clientid_t * clid = &setclientid_confirm->sc_clientid;
        __be32 status;
@@ -2974,6 +2972,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                return nfserr_stale_clientid;
        nfs4_lock_state();
 
+       spin_lock(&nn->client_lock);
        conf = find_confirmed_client(clid, false, nn);
        unconf = find_unconfirmed_client(clid, false, nn);
        /*
@@ -2997,21 +2996,29 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
        }
        status = nfs_ok;
        if (conf) { /* case 1: callback update */
+               old = unconf;
+               unhash_client_locked(old);
                nfsd4_change_callback(conf, &unconf->cl_cb_conn);
-               nfsd4_probe_callback(conf);
-               expire_client(unconf);
        } else { /* case 3: normal case; new or rebooted client */
-               conf = find_confirmed_client_by_name(&unconf->cl_name, nn);
-               if (conf) {
-                       status = mark_client_expired(conf);
+               old = find_confirmed_client_by_name(&unconf->cl_name, nn);
+               if (old) {
+                       status = mark_client_expired_locked(old);
                        if (status)
                                goto out;
-                       expire_client(conf);
+                       unhash_client_locked(old);
                }
                move_to_confirmed(unconf);
-               nfsd4_probe_callback(unconf);
+               conf = unconf;
        }
+       get_client_locked(conf);
+       spin_unlock(&nn->client_lock);
+       nfsd4_probe_callback(conf);
+       spin_lock(&nn->client_lock);
+       put_client_renew_locked(conf);
 out:
+       spin_unlock(&nn->client_lock);
+       if (old)
+               expire_client(old);
        nfs4_unlock_state();
        return status;
 }
@@ -5648,7 +5655,13 @@ nfs4_check_open_reclaim(clientid_t *clid,
 
 u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)
 {
-       if (mark_client_expired(clp))
+       __be32 ret;
+       struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+       spin_lock(&nn->client_lock);
+       ret = mark_client_expired_locked(clp);
+       spin_unlock(&nn->client_lock);
+       if (ret != nfs_ok)
                return 0;
        expire_client(clp);
        return 1;