nfsd4.1: fix session memory use calculation
[safe/jmp/linux-2.6] / fs / nfsd / nfs4state.c
index 96a7423..c171371 100644 (file)
@@ -414,46 +414,77 @@ gen_sessionid(struct nfsd4_session *ses)
 }
 
 /*
- * Give the client the number of slots it requests bound by
- * NFSD_MAX_SLOTS_PER_SESSION and by nfsd_drc_max_mem.
+ * The protocol defines ca_maxresponssize_cached to include the size of
+ * the rpc header, but all we need to cache is the data starting after
+ * the end of the initial SEQUENCE operation--the rest we regenerate
+ * each time.  Therefore we can advertise a ca_maxresponssize_cached
+ * value that is the number of bytes in our cache plus a few additional
+ * bytes.  In order to stay on the safe side, and not promise more than
+ * we can cache, those additional bytes must be the minimum possible: 24
+ * bytes of rpc header (xid through accept state, with AUTH_NULL
+ * verifier), 12 for the compound header (with zero-length tag), and 44
+ * for the SEQUENCE op response:
+ */
+#define NFSD_MIN_HDR_SEQ_SZ  (24 + 12 + 44)
+
+/*
+ * Give the client the number of ca_maxresponsesize_cached slots it
+ * requests, of size bounded by NFSD_SLOT_CACHE_SIZE,
+ * NFSD_MAX_MEM_PER_SESSION, and nfsd_drc_max_mem. Do not allow more
+ * than NFSD_MAX_SLOTS_PER_SESSION.
  *
- * If we run out of reserved DRC memory we should (up to a point) re-negotiate
- * active sessions and reduce their slot usage to make rooom for new
- * connections. For now we just fail the create session.
+ * If we run out of reserved DRC memory we should (up to a point)
+ * re-negotiate active sessions and reduce their slot usage to make
+ * rooom for new connections. For now we just fail the create session.
  */
-static int set_forechannel_maxreqs(struct nfsd4_channel_attrs *fchan)
+static int set_forechannel_drc_size(struct nfsd4_channel_attrs *fchan)
 {
-       int mem;
+       int mem, size = fchan->maxresp_cached;
 
        if (fchan->maxreqs < 1)
                return nfserr_inval;
-       else if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
-               fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
 
-       mem = fchan->maxreqs * NFSD_SLOT_CACHE_SIZE;
+       if (size < NFSD_MIN_HDR_SEQ_SZ)
+               size = NFSD_MIN_HDR_SEQ_SZ;
+       size -= NFSD_MIN_HDR_SEQ_SZ;
+       if (size > NFSD_SLOT_CACHE_SIZE)
+               size = NFSD_SLOT_CACHE_SIZE;
+
+       /* bound the maxreqs by NFSD_MAX_MEM_PER_SESSION */
+       mem = fchan->maxreqs * size;
+       if (mem > NFSD_MAX_MEM_PER_SESSION) {
+               fchan->maxreqs = NFSD_MAX_MEM_PER_SESSION / size;
+               if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
+                       fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
+               mem = fchan->maxreqs * size;
+       }
 
        spin_lock(&nfsd_drc_lock);
-       if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem)
-               mem = ((nfsd_drc_max_mem - nfsd_drc_mem_used) /
-                               NFSD_SLOT_CACHE_SIZE) * NFSD_SLOT_CACHE_SIZE;
+       /* bound the total session drc memory ussage */
+       if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem) {
+               fchan->maxreqs = (nfsd_drc_max_mem - nfsd_drc_mem_used) / size;
+               mem = fchan->maxreqs * size;
+       }
        nfsd_drc_mem_used += mem;
        spin_unlock(&nfsd_drc_lock);
 
-       fchan->maxreqs = mem / NFSD_SLOT_CACHE_SIZE;
        if (fchan->maxreqs == 0)
-               return nfserr_resource;
+               return nfserr_serverfault;
+
+       fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ;
        return 0;
 }
 
 /*
  * fchan holds the client values on input, and the server values on output
+ * sv_max_mesg is the maximum payload plus one page for overhead.
  */
 static int init_forechannel_attrs(struct svc_rqst *rqstp,
                                  struct nfsd4_channel_attrs *session_fchan,
                                  struct nfsd4_channel_attrs *fchan)
 {
        int status = 0;
-       __u32   maxcount = svc_max_payload(rqstp);
+       __u32   maxcount = nfsd_serv->sv_max_mesg;
 
        /* headerpadsz set to zero in encode routine */
 
@@ -466,9 +497,6 @@ static int init_forechannel_attrs(struct svc_rqst *rqstp,
                fchan->maxresp_sz = maxcount;
        session_fchan->maxresp_sz = fchan->maxresp_sz;
 
-       session_fchan->maxresp_cached = NFSD_SLOT_CACHE_SIZE;
-       fchan->maxresp_cached = session_fchan->maxresp_cached;
-
        /* Use the client's maxops if possible */
        if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND)
                fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND;
@@ -478,18 +506,32 @@ static int init_forechannel_attrs(struct svc_rqst *rqstp,
         * recover pages from existing sessions. For now fail session
         * creation.
         */
-       status = set_forechannel_maxreqs(fchan);
+       status = set_forechannel_drc_size(fchan);
 
+       session_fchan->maxresp_cached = fchan->maxresp_cached;
        session_fchan->maxreqs = fchan->maxreqs;
+
+       dprintk("%s status %d\n", __func__, status);
        return status;
 }
 
+static void
+free_session_slots(struct nfsd4_session *ses)
+{
+       int i;
+
+       for (i = 0; i < ses->se_fchannel.maxreqs; i++)
+               kfree(ses->se_slots[i]);
+}
+
 static int
 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
                   struct nfsd4_create_session *cses)
 {
        struct nfsd4_session *new, tmp;
-       int idx, status = nfserr_resource, slotsize;
+       struct nfsd4_slot *sp;
+       int idx, slotsize, cachesize, i;
+       int status;
 
        memset(&tmp, 0, sizeof(tmp));
 
@@ -500,14 +542,27 @@ alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
        if (status)
                goto out;
 
-       /* allocate struct nfsd4_session and slot table in one piece */
-       slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot);
+       BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot)
+                    + sizeof(struct nfsd4_session) > PAGE_SIZE);
+
+       status = nfserr_serverfault;
+       /* allocate struct nfsd4_session and slot table pointers in one piece */
+       slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot *);
        new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL);
        if (!new)
                goto out;
 
        memcpy(new, &tmp, sizeof(*new));
 
+       /* allocate each struct nfsd4_slot and data cache in one piece */
+       cachesize = new->se_fchannel.maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
+       for (i = 0; i < new->se_fchannel.maxreqs; i++) {
+               sp = kzalloc(sizeof(*sp) + cachesize, GFP_KERNEL);
+               if (!sp)
+                       goto out_free;
+               new->se_slots[i] = sp;
+       }
+
        new->se_client = clp;
        gen_sessionid(new);
        idx = hash_sessionid(&new->se_sessionid);
@@ -524,6 +579,10 @@ alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
        status = nfs_ok;
 out:
        return status;
+out_free:
+       free_session_slots(new);
+       kfree(new);
+       goto out;
 }
 
 /* caller must hold sessionid_lock */
@@ -566,22 +625,19 @@ release_session(struct nfsd4_session *ses)
        nfsd4_put_session(ses);
 }
 
-static void nfsd4_release_respages(struct page **respages, short resused);
-
 void
 free_session(struct kref *kref)
 {
        struct nfsd4_session *ses;
-       int i;
+       int mem;
 
        ses = container_of(kref, struct nfsd4_session, se_ref);
-       for (i = 0; i < ses->se_fchannel.maxreqs; i++) {
-               struct nfsd4_cache_entry *e = &ses->se_slots[i].sl_cache_entry;
-               nfsd4_release_respages(e->ce_respages, e->ce_resused);
-       }
        spin_lock(&nfsd_drc_lock);
-       nfsd_drc_mem_used -= ses->se_fchannel.maxreqs * NFSD_SLOT_CACHE_SIZE;
+       mem = ses->se_fchannel.maxreqs
+               * (ses->se_fchannel.maxresp_cached - NFSD_MIN_HDR_SEQ_SZ);
+       nfsd_drc_mem_used -= mem;
        spin_unlock(&nfsd_drc_lock);
+       free_session_slots(ses);
        kfree(ses);
 }
 
@@ -644,16 +700,14 @@ shutdown_callback_client(struct nfs4_client *clp)
                clp->cl_cb_conn.cb_client = NULL;
                rpc_shutdown_client(clnt);
        }
-       if (clp->cl_cb_conn.cb_cred) {
-               put_rpccred(clp->cl_cb_conn.cb_cred);
-               clp->cl_cb_conn.cb_cred = NULL;
-       }
 }
 
 static inline void
 free_client(struct nfs4_client *clp)
 {
        shutdown_callback_client(clp);
+       if (clp->cl_cb_xprt)
+               svc_xprt_put(clp->cl_cb_xprt);
        if (clp->cl_cred.cr_group_info)
                put_group_info(clp->cl_cred.cr_group_info);
        kfree(clp->cl_principal);
@@ -709,25 +763,6 @@ expire_client(struct nfs4_client *clp)
        put_nfs4_client(clp);
 }
 
-static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir)
-{
-       struct nfs4_client *clp;
-
-       clp = alloc_client(name);
-       if (clp == NULL)
-               return NULL;
-       memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
-       atomic_set(&clp->cl_count, 1);
-       atomic_set(&clp->cl_cb_conn.cb_set, 0);
-       INIT_LIST_HEAD(&clp->cl_idhash);
-       INIT_LIST_HEAD(&clp->cl_strhash);
-       INIT_LIST_HEAD(&clp->cl_openowners);
-       INIT_LIST_HEAD(&clp->cl_delegations);
-       INIT_LIST_HEAD(&clp->cl_sessions);
-       INIT_LIST_HEAD(&clp->cl_lru);
-       return clp;
-}
-
 static void copy_verf(struct nfs4_client *target, nfs4_verifier *source)
 {
        memcpy(target->cl_verifier.data, source->data,
@@ -790,6 +825,46 @@ static void gen_confirm(struct nfs4_client *clp)
        *p++ = i++;
 }
 
+static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
+               struct svc_rqst *rqstp, nfs4_verifier *verf)
+{
+       struct nfs4_client *clp;
+       struct sockaddr *sa = svc_addr(rqstp);
+       char *princ;
+
+       clp = alloc_client(name);
+       if (clp == NULL)
+               return NULL;
+
+       princ = svc_gss_principal(rqstp);
+       if (princ) {
+               clp->cl_principal = kstrdup(princ, GFP_KERNEL);
+               if (clp->cl_principal == NULL) {
+                       free_client(clp);
+                       return NULL;
+               }
+       }
+
+       memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
+       atomic_set(&clp->cl_count, 1);
+       atomic_set(&clp->cl_cb_conn.cb_set, 0);
+       INIT_LIST_HEAD(&clp->cl_idhash);
+       INIT_LIST_HEAD(&clp->cl_strhash);
+       INIT_LIST_HEAD(&clp->cl_openowners);
+       INIT_LIST_HEAD(&clp->cl_delegations);
+       INIT_LIST_HEAD(&clp->cl_sessions);
+       INIT_LIST_HEAD(&clp->cl_lru);
+       clear_bit(0, &clp->cl_cb_slot_busy);
+       rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
+       copy_verf(clp, verf);
+       rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa);
+       clp->cl_flavor = rqstp->rq_flavor;
+       copy_cred(&clp->cl_cred, &rqstp->rq_cred);
+       gen_confirm(clp);
+
+       return clp;
+}
+
 static int check_name(struct xdr_netobj name)
 {
        if (name.len == 0) 
@@ -898,12 +973,19 @@ find_unconfirmed_client_by_str(const char *dname, unsigned int hashval,
 }
 
 static void
-gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
+gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
 {
        struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
-
-       /* Currently, we only support tcp for the callback channel */
-       if ((se->se_callback_netid_len != 3) || memcmp((char *)se->se_callback_netid_val, "tcp", 3))
+       unsigned short expected_family;
+
+       /* Currently, we only support tcp and tcp6 for the callback channel */
+       if (se->se_callback_netid_len == 3 &&
+           !memcmp(se->se_callback_netid_val, "tcp", 3))
+               expected_family = AF_INET;
+       else if (se->se_callback_netid_len == 4 &&
+                !memcmp(se->se_callback_netid_val, "tcp6", 4))
+               expected_family = AF_INET6;
+       else
                goto out_err;
 
        cb->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
@@ -911,9 +993,12 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
                                            (struct sockaddr *) &cb->cb_addr,
                                            sizeof(cb->cb_addr));
 
-       if (!cb->cb_addrlen || cb->cb_addr.ss_family != AF_INET)
+       if (!cb->cb_addrlen || cb->cb_addr.ss_family != expected_family)
                goto out_err;
 
+       if (cb->cb_addr.ss_family == AF_INET6)
+               ((struct sockaddr_in6 *) &cb->cb_addr)->sin6_scope_id = scopeid;
+
        cb->cb_minorversion = 0;
        cb->cb_prog = se->se_callback_prog;
        cb->cb_ident = se->se_callback_ident;
@@ -928,122 +1013,31 @@ out_err:
        return;
 }
 
-void
-nfsd4_set_statp(struct svc_rqst *rqstp, __be32 *statp)
-{
-       struct nfsd4_compoundres *resp = rqstp->rq_resp;
-
-       resp->cstate.statp = statp;
-}
-
-/*
- * Dereference the result pages.
- */
-static void
-nfsd4_release_respages(struct page **respages, short resused)
-{
-       int i;
-
-       dprintk("--> %s\n", __func__);
-       for (i = 0; i < resused; i++) {
-               if (!respages[i])
-                       continue;
-               put_page(respages[i]);
-               respages[i] = NULL;
-       }
-}
-
-static void
-nfsd4_copy_pages(struct page **topages, struct page **frompages, short count)
-{
-       int i;
-
-       for (i = 0; i < count; i++) {
-               topages[i] = frompages[i];
-               if (!topages[i])
-                       continue;
-               get_page(topages[i]);
-       }
-}
-
 /*
- * Cache the reply pages up to NFSD_PAGES_PER_SLOT + 1, clearing the previous
- * pages. We add a page to NFSD_PAGES_PER_SLOT for the case where the total
- * length of the XDR response is less than se_fmaxresp_cached
- * (NFSD_PAGES_PER_SLOT * PAGE_SIZE) but the xdr_buf pages is used for a
- * of the reply (e.g. readdir).
- *
- * Store the base and length of the rq_req.head[0] page
- * of the NFSv4.1 data, just past the rpc header.
+ * Cache a reply. nfsd4_check_drc_limit() has bounded the cache size.
  */
 void
 nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
 {
-       struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry;
-       struct svc_rqst *rqstp = resp->rqstp;
-       struct nfsd4_compoundargs *args = rqstp->rq_argp;
-       struct nfsd4_op *op = &args->ops[resp->opcnt];
-       struct kvec *resv = &rqstp->rq_res.head[0];
-
-       dprintk("--> %s entry %p\n", __func__, entry);
+       struct nfsd4_slot *slot = resp->cstate.slot;
+       unsigned int base;
 
-       /* Don't cache a failed OP_SEQUENCE. */
-       if (resp->opcnt == 1 && op->opnum == OP_SEQUENCE && resp->cstate.status)
-               return;
+       dprintk("--> %s slot %p\n", __func__, slot);
 
-       nfsd4_release_respages(entry->ce_respages, entry->ce_resused);
-       entry->ce_opcnt = resp->opcnt;
-       entry->ce_status = resp->cstate.status;
-
-       /*
-        * Don't need a page to cache just the sequence operation - the slot
-        * does this for us!
-        */
+       slot->sl_opcnt = resp->opcnt;
+       slot->sl_status = resp->cstate.status;
 
        if (nfsd4_not_cached(resp)) {
-               entry->ce_resused = 0;
-               entry->ce_rpchdrlen = 0;
-               dprintk("%s Just cache SEQUENCE. ce_cachethis %d\n", __func__,
-                       resp->cstate.slot->sl_cache_entry.ce_cachethis);
+               slot->sl_datalen = 0;
                return;
        }
-       entry->ce_resused = rqstp->rq_resused;
-       if (entry->ce_resused > NFSD_PAGES_PER_SLOT + 1)
-               entry->ce_resused = NFSD_PAGES_PER_SLOT + 1;
-       nfsd4_copy_pages(entry->ce_respages, rqstp->rq_respages,
-                        entry->ce_resused);
-       entry->ce_datav.iov_base = resp->cstate.statp;
-       entry->ce_datav.iov_len = resv->iov_len - ((char *)resp->cstate.statp -
-                               (char *)page_address(rqstp->rq_respages[0]));
-       /* Current request rpc header length*/
-       entry->ce_rpchdrlen = (char *)resp->cstate.statp -
-                               (char *)page_address(rqstp->rq_respages[0]);
-}
-
-/*
- * We keep the rpc header, but take the nfs reply from the replycache.
- */
-static int
-nfsd41_copy_replay_data(struct nfsd4_compoundres *resp,
-                       struct nfsd4_cache_entry *entry)
-{
-       struct svc_rqst *rqstp = resp->rqstp;
-       struct kvec *resv = &resp->rqstp->rq_res.head[0];
-       int len;
-
-       /* Current request rpc header length*/
-       len = (char *)resp->cstate.statp -
-                       (char *)page_address(rqstp->rq_respages[0]);
-       if (entry->ce_datav.iov_len + len > PAGE_SIZE) {
-               dprintk("%s v41 cached reply too large (%Zd).\n", __func__,
-                       entry->ce_datav.iov_len);
-               return 0;
-       }
-       /* copy the cached reply nfsd data past the current rpc header */
-       memcpy((char *)resv->iov_base + len, entry->ce_datav.iov_base,
-               entry->ce_datav.iov_len);
-       resv->iov_len = len + entry->ce_datav.iov_len;
-       return 1;
+       slot->sl_datalen = (char *)resp->p - (char *)resp->cstate.datap;
+       base = (char *)resp->cstate.datap -
+                                       (char *)resp->xbuf->head[0].iov_base;
+       if (read_bytes_from_xdr_buf(resp->xbuf, base, slot->sl_data,
+                                   slot->sl_datalen))
+               WARN("%s: sessions DRC could not cache compound\n", __func__);
+       return;
 }
 
 /*
@@ -1061,14 +1055,14 @@ nfsd4_enc_sequence_replay(struct nfsd4_compoundargs *args,
        struct nfsd4_slot *slot = resp->cstate.slot;
 
        dprintk("--> %s resp->opcnt %d cachethis %u \n", __func__,
-               resp->opcnt, resp->cstate.slot->sl_cache_entry.ce_cachethis);
+               resp->opcnt, resp->cstate.slot->sl_cachethis);
 
        /* Encode the replayed sequence operation */
        op = &args->ops[resp->opcnt - 1];
        nfsd4_encode_operation(resp, op);
 
        /* Return nfserr_retry_uncached_rep in next operation. */
-       if (args->opcnt > 1 && slot->sl_cache_entry.ce_cachethis == 0) {
+       if (args->opcnt > 1 && slot->sl_cachethis == 0) {
                op = &args->ops[resp->opcnt++];
                op->status = nfserr_retry_uncached_rep;
                nfsd4_encode_operation(resp, op);
@@ -1077,58 +1071,29 @@ nfsd4_enc_sequence_replay(struct nfsd4_compoundargs *args,
 }
 
 /*
- * Keep the first page of the replay. Copy the NFSv4.1 data from the first
- * cached page.  Replace any futher replay pages from the cache.
+ * The sequence operation is not cached because we can use the slot and
+ * session values.
  */
 __be32
 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp,
                         struct nfsd4_sequence *seq)
 {
-       struct nfsd4_cache_entry *entry = &resp->cstate.slot->sl_cache_entry;
+       struct nfsd4_slot *slot = resp->cstate.slot;
        __be32 status;
 
-       dprintk("--> %s entry %p\n", __func__, entry);
-
-       /*
-        * If this is just the sequence operation, we did not keep
-        * a page in the cache entry because we can just use the
-        * slot info stored in struct nfsd4_sequence that was checked
-        * against the slot in nfsd4_sequence().
-        *
-        * This occurs when seq->cachethis is FALSE, or when the client
-        * session inactivity timer fires and a solo sequence operation
-        * is sent (lease renewal).
-        */
-       seq->maxslots = resp->cstate.session->se_fchannel.maxreqs;
+       dprintk("--> %s slot %p\n", __func__, slot);
 
        /* Either returns 0 or nfserr_retry_uncached */
        status = nfsd4_enc_sequence_replay(resp->rqstp->rq_argp, resp);
        if (status == nfserr_retry_uncached_rep)
                return status;
 
-       if (!nfsd41_copy_replay_data(resp, entry)) {
-               /*
-                * Not enough room to use the replay rpc header, send the
-                * cached header. Release all the allocated result pages.
-                */
-               svc_free_res_pages(resp->rqstp);
-               nfsd4_copy_pages(resp->rqstp->rq_respages, entry->ce_respages,
-                       entry->ce_resused);
-       } else {
-               /* Release all but the first allocated result page */
+       /* The sequence operation has been encoded, cstate->datap set. */
+       memcpy(resp->cstate.datap, slot->sl_data, slot->sl_datalen);
 
-               resp->rqstp->rq_resused--;
-               svc_free_res_pages(resp->rqstp);
-
-               nfsd4_copy_pages(&resp->rqstp->rq_respages[1],
-                                &entry->ce_respages[1],
-                                entry->ce_resused - 1);
-       }
-
-       resp->rqstp->rq_resused = entry->ce_resused;
-       resp->opcnt = entry->ce_opcnt;
-       resp->cstate.iovlen = entry->ce_datav.iov_len + entry->ce_rpchdrlen;
-       status = entry->ce_status;
+       resp->opcnt = slot->sl_opcnt;
+       resp->p = resp->cstate.datap + XDR_QUADLEN(slot->sl_datalen);
+       status = slot->sl_status;
 
        return status;
 }
@@ -1247,17 +1212,13 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
 
 out_new:
        /* Normal case */
-       new = create_client(exid->clname, dname);
+       new = create_client(exid->clname, dname, rqstp, &verf);
        if (new == NULL) {
-               status = nfserr_resource;
+               status = nfserr_serverfault;
                goto out;
        }
 
-       copy_verf(new, &verf);
-       copy_cred(&new->cl_cred, &rqstp->rq_cred);
-       rpc_copy_addr((struct sockaddr *) &new->cl_addr, sa);
        gen_clid(new);
-       gen_confirm(new);
        add_to_unconfirmed(new, strhashval);
 out_copy:
        exid->clientid.cl_boot = new->cl_clientid.cl_boot;
@@ -1379,6 +1340,19 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                cr_ses->flags &= ~SESSION4_PERSIST;
                cr_ses->flags &= ~SESSION4_RDMA;
 
+               if (cr_ses->flags & SESSION4_BACK_CHAN) {
+                       unconf->cl_cb_xprt = rqstp->rq_xprt;
+                       svc_xprt_get(unconf->cl_cb_xprt);
+                       rpc_copy_addr(
+                               (struct sockaddr *)&unconf->cl_cb_conn.cb_addr,
+                               sa);
+                       unconf->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
+                       unconf->cl_cb_conn.cb_minorversion =
+                               cstate->minorversion;
+                       unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog;
+                       unconf->cl_cb_seq_nr = 1;
+                       nfsd4_probe_callback(unconf);
+               }
                conf = unconf;
        } else {
                status = nfserr_stale_clientid;
@@ -1460,18 +1434,23 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        if (seq->slotid >= session->se_fchannel.maxreqs)
                goto out;
 
-       slot = &session->se_slots[seq->slotid];
+       slot = session->se_slots[seq->slotid];
        dprintk("%s: slotid %d\n", __func__, seq->slotid);
 
+       /* We do not negotiate the number of slots yet, so set the
+        * maxslots to the session maxreqs which is used to encode
+        * sr_highest_slotid and the sr_target_slot id to maxslots */
+       seq->maxslots = session->se_fchannel.maxreqs;
+
        status = check_slot_seqid(seq->seqid, slot->sl_seqid, slot->sl_inuse);
        if (status == nfserr_replay_cache) {
                cstate->slot = slot;
                cstate->session = session;
                /* Return the cached reply status and set cstate->status
-                * for nfsd4_svc_encode_compoundres processing */
+                * for nfsd4_proc_compound processing */
                status = nfsd4_replay_cache_entry(resp, seq);
                cstate->status = nfserr_replay_cache;
-               goto replay_cache;
+               goto out;
        }
        if (status)
                goto out;
@@ -1479,23 +1458,23 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        /* Success! bump slot seqid */
        slot->sl_inuse = true;
        slot->sl_seqid = seq->seqid;
-       slot->sl_cache_entry.ce_cachethis = seq->cachethis;
-       /* Always set the cache entry cachethis for solo sequence */
-       if (nfsd4_is_solo_sequence(resp))
-               slot->sl_cache_entry.ce_cachethis = 1;
+       slot->sl_cachethis = seq->cachethis;
 
        cstate->slot = slot;
        cstate->session = session;
 
-replay_cache:
-       /* Renew the clientid on success and on replay.
-        * Hold a session reference until done processing the compound:
+       /* Hold a session reference until done processing the compound:
         * nfsd4_put_session called only if the cstate slot is set.
         */
-       renew_client(session->se_client);
        nfsd4_get_session(session);
 out:
        spin_unlock(&sessionid_lock);
+       /* Renew the clientid on success and on replay */
+       if (cstate->session) {
+               nfs4_lock_state();
+               renew_client(session->se_client);
+               nfs4_unlock_state();
+       }
        dprintk("%s: return %d\n", __func__, ntohl(status));
        return status;
 }
@@ -1513,7 +1492,6 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        unsigned int            strhashval;
        struct nfs4_client      *conf, *unconf, *new;
        __be32                  status;
-       char                    *princ;
        char                    dname[HEXDIR_LEN];
        
        if (!check_name(clname))
@@ -1558,7 +1536,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                 */
                if (unconf)
                        expire_client(unconf);
-               new = create_client(clname, dname);
+               new = create_client(clname, dname, rqstp, &clverifier);
                if (new == NULL)
                        goto out;
                gen_clid(new);
@@ -1575,7 +1553,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                         */
                        expire_client(unconf);
                }
-               new = create_client(clname, dname);
+               new = create_client(clname, dname, rqstp, &clverifier);
                if (new == NULL)
                        goto out;
                copy_clid(new, conf);
@@ -1585,7 +1563,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                 * probable client reboot; state will be removed if
                 * confirmed.
                 */
-               new = create_client(clname, dname);
+               new = create_client(clname, dname, rqstp, &clverifier);
                if (new == NULL)
                        goto out;
                gen_clid(new);
@@ -1596,25 +1574,12 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                 * confirmed.
                 */
                expire_client(unconf);
-               new = create_client(clname, dname);
+               new = create_client(clname, dname, rqstp, &clverifier);
                if (new == NULL)
                        goto out;
                gen_clid(new);
        }
-       copy_verf(new, &clverifier);
-       rpc_copy_addr((struct sockaddr *) &new->cl_addr, sa);
-       new->cl_flavor = rqstp->rq_flavor;
-       princ = svc_gss_principal(rqstp);
-       if (princ) {
-               new->cl_principal = kstrdup(princ, GFP_KERNEL);
-               if (new->cl_principal == NULL) {
-                       free_client(new);
-                       goto out;
-               }
-       }
-       copy_cred(&new->cl_cred, &rqstp->rq_cred);
-       gen_confirm(new);
-       gen_callback(new, setclid);
+       gen_callback(new, setclid, rpc_get_scope_id(sa));
        add_to_unconfirmed(new, strhashval);
        setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot;
        setclid->se_clientid.cl_id = new->cl_clientid.cl_id;
@@ -2148,7 +2113,7 @@ int nfsd_change_deleg_cb(struct file_lock **onlist, int arg)
                return -EAGAIN;
 }
 
-static struct lock_manager_operations nfsd_lease_mng_ops = {
+static const struct lock_manager_operations nfsd_lease_mng_ops = {
        .fl_break = nfsd_break_deleg_cb,
        .fl_release_private = nfsd_release_deleg_cb,
        .fl_copy_lock = nfsd_copy_lock_deleg_cb,
@@ -3353,7 +3318,7 @@ nfs4_transform_lock_offset(struct file_lock *lock)
 
 /* Hack!: For now, we're defining this just so we can use a pointer to it
  * as a unique cookie to identify our (NFSv4's) posix locks. */
-static struct lock_manager_operations nfsd_posix_mng_ops  = {
+static const struct lock_manager_operations nfsd_posix_mng_ops  = {
 };
 
 static inline void
@@ -4057,7 +4022,7 @@ set_max_delegations(void)
 
 /* initialization to perform when the nfsd service is started: */
 
-static void
+static int
 __nfs4_state_start(void)
 {
        unsigned long grace_time;
@@ -4069,19 +4034,26 @@ __nfs4_state_start(void)
        printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
               grace_time/HZ);
        laundry_wq = create_singlethread_workqueue("nfsd4");
+       if (laundry_wq == NULL)
+               return -ENOMEM;
        queue_delayed_work(laundry_wq, &laundromat_work, grace_time);
        set_max_delegations();
+       return set_callback_cred();
 }
 
-void
+int
 nfs4_state_start(void)
 {
+       int ret;
+
        if (nfs4_init)
-               return;
+               return 0;
        nfsd4_load_reboot_recovery_data();
-       __nfs4_state_start();
+       ret = __nfs4_state_start();
+       if (ret)
+               return ret;
        nfs4_init = 1;
-       return;
+       return 0;
 }
 
 time_t