nfsd: Allow struct nfsd4_compound_state to cache the nfs4_client
[linux-2.6-block.git] / fs / nfsd / nfs4state.c
index 1f8aab8f67bab3f53899a495cee568a39acf62c8..c01d81e216024cd21c374f69d0fb3e34b9fcc744 100644 (file)
@@ -189,6 +189,15 @@ static void put_client_renew_locked(struct nfs4_client *clp)
                renew_client_locked(clp);
 }
 
+static void put_client_renew(struct nfs4_client *clp)
+{
+       struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
+
+       spin_lock(&nn->client_lock);
+       put_client_renew_locked(clp);
+       spin_unlock(&nn->client_lock);
+}
+
 static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses)
 {
        __be32 status;
@@ -2391,6 +2400,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
                        goto out_put_session;
                cstate->slot = slot;
                cstate->session = session;
+               cstate->clp = clp;
                /* Return the cached reply status and set cstate->status
                 * for nfsd4_proc_compound processing */
                status = nfsd4_replay_cache_entry(resp, seq);
@@ -2425,6 +2435,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
 
        cstate->slot = slot;
        cstate->session = session;
+       cstate->clp = clp;
 
 out:
        switch (clp->cl_cb_state) {
@@ -2461,7 +2472,8 @@ nfsd4_sequence_done(struct nfsd4_compoundres *resp)
                }
                /* Drop session reference that was taken in nfsd4_sequence() */
                nfsd4_put_session(cs->session);
-       }
+       } else if (cs->clp)
+               put_client_renew(cs->clp);
 }
 
 __be32
@@ -2986,6 +2998,38 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4
        return nfserr_bad_seqid;
 }
 
+static __be32 lookup_clientid(clientid_t *clid,
+               struct nfsd4_compound_state *cstate,
+               struct nfsd_net *nn)
+{
+       struct nfs4_client *found;
+
+       if (cstate->clp) {
+               found = cstate->clp;
+               if (!same_clid(&found->cl_clientid, clid))
+                       return nfserr_stale_clientid;
+               return nfs_ok;
+       }
+
+       if (STALE_CLIENTID(clid, nn))
+               return nfserr_stale_clientid;
+
+       /*
+        * For v4.1+ we get the client in the SEQUENCE op. If we don't have one
+        * cached already then we know this is for is for v4.0 and "sessions"
+        * will be false.
+        */
+       WARN_ON_ONCE(cstate->session);
+       found = find_confirmed_client(clid, false, nn);
+       if (!found)
+               return nfserr_expired;
+
+       /* Cache the nfs4_client in cstate! */
+       cstate->clp = found;
+       atomic_inc(&found->cl_refcount);
+       return nfs_ok;
+}
+
 __be32
 nfsd4_process_open1(struct nfsd4_compound_state *cstate,
                    struct nfsd4_open *open, struct nfsd_net *nn)
@@ -3498,18 +3542,6 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status)
                free_generic_stateid(open->op_stp);
 }
 
-static __be32 lookup_clientid(clientid_t *clid, bool session, struct nfsd_net *nn, struct nfs4_client **clp)
-{
-       struct nfs4_client *found;
-
-       if (STALE_CLIENTID(clid, nn))
-               return nfserr_stale_clientid;
-       found = find_confirmed_client(clid, session, nn);
-       if (clp)
-               *clp = found;
-       return found ? nfs_ok : nfserr_expired;
-}
-
 __be32
 nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
            clientid_t *clid)
@@ -3521,9 +3553,10 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        nfs4_lock_state();
        dprintk("process_renew(%08x/%08x): starting\n", 
                        clid->cl_boot, clid->cl_id);
-       status = lookup_clientid(clid, cstate->minorversion, nn, &clp);
+       status = lookup_clientid(clid, cstate, nn);
        if (status)
                goto out;
+       clp = cstate->clp;
        status = nfserr_cb_path_down;
        if (!list_empty(&clp->cl_delegations)
                        && clp->cl_cb_state != NFSD4_CB_UP)
@@ -3786,22 +3819,19 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
                     stateid_t *stateid, unsigned char typemask,
                     struct nfs4_stid **s, struct nfsd_net *nn)
 {
-       struct nfs4_client *cl;
        __be32 status;
-       bool sessions = cstate->minorversion != 0;
 
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
                return nfserr_bad_stateid;
-       status = lookup_clientid(&stateid->si_opaque.so_clid, sessions,
-                                                       nn, &cl);
+       status = lookup_clientid(&stateid->si_opaque.so_clid, cstate, nn);
        if (status == nfserr_stale_clientid) {
-               if (sessions)
+               if (cstate->session)
                        return nfserr_bad_stateid;
                return nfserr_stale_stateid;
        }
        if (status)
                return status;
-       *s = find_stateid_by_type(cl, stateid, typemask);
+       *s = find_stateid_by_type(cstate->clp, stateid, typemask);
        if (!*s)
                return nfserr_bad_stateid;
        return nfs_ok;
@@ -4651,7 +4681,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        nfs4_lock_state();
 
        if (!nfsd4_has_session(cstate)) {
-               status = lookup_clientid(&lockt->lt_clientid, false, nn, NULL);
+               status = lookup_clientid(&lockt->lt_clientid, cstate, nn);
                if (status)
                        goto out;
        }
@@ -4820,7 +4850,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp,
 
        nfs4_lock_state();
 
-       status = lookup_clientid(clid, cstate->minorversion, nn, NULL);
+       status = lookup_clientid(clid, cstate, nn);
        if (status)
                goto out;