Merge commit 'linux-pnfs/nfs41-for-2.6.31' into nfsv41-for-2.6.31
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Thu, 18 Jun 2009 00:59:58 +0000 (17:59 -0700)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Thu, 18 Jun 2009 00:59:58 +0000 (17:59 -0700)
38 files changed:
fs/nfs/Kconfig
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/callback_xdr.c
fs/nfs/client.c
fs/nfs/direct.c
fs/nfs/internal.h
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4renewd.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/read.c
fs/nfs/super.c
fs/nfs/unlink.c
fs/nfs/write.c
include/linux/nfs4.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h
include/linux/nfsd/state.h
include/linux/sunrpc/bc_xprt.h [new file with mode: 0644]
include/linux/sunrpc/clnt.h
include/linux/sunrpc/sched.h
include/linux/sunrpc/svc.h
include/linux/sunrpc/svcsock.h
include/linux/sunrpc/xprt.h
net/sunrpc/Makefile
net/sunrpc/backchannel_rqst.c [new file with mode: 0644]
net/sunrpc/bc_svc.c [new file with mode: 0644]
net/sunrpc/clnt.c
net/sunrpc/sched.c
net/sunrpc/stats.c
net/sunrpc/sunrpc.h [new file with mode: 0644]
net/sunrpc/svc.c
net/sunrpc/svcsock.c
net/sunrpc/xprt.c
net/sunrpc/xprtsock.c

index e67f3ec0773681c17dcf906e0cddfbd12d9ea1bc..5d6d6f4159357c1f11dd29d93343daa43e0c8135 100644 (file)
@@ -74,6 +74,15 @@ config NFS_V4
 
          If unsure, say N.
 
+config NFS_V4_1
+       bool "NFS client support for NFSv4.1 (DEVELOPER ONLY)"
+       depends on NFS_V4 && EXPERIMENTAL
+       help
+         This option enables support for minor version 1 of the NFSv4 protocol
+         (draft-ietf-nfsv4-minorversion1) in the kernel's NFS client.
+
+         Unless you're an NFS developer, say N.
+
 config ROOT_NFS
        bool "Root file system on NFS"
        depends on NFS_FS=y && IP_PNP
index a886e692ddd006359aa8b021f053e3b7f9904567..e69b8f61189ec1e625632526b3ffd4c8a097ca6f 100644 (file)
@@ -17,6 +17,9 @@
 #include <linux/freezer.h>
 #include <linux/kthread.h>
 #include <linux/sunrpc/svcauth_gss.h>
+#if defined(CONFIG_NFS_V4_1)
+#include <linux/sunrpc/bc_xprt.h>
+#endif
 
 #include <net/inet_sock.h>
 
 
 struct nfs_callback_data {
        unsigned int users;
+       struct svc_serv *serv;
        struct svc_rqst *rqst;
        struct task_struct *task;
 };
 
-static struct nfs_callback_data nfs_callback_info;
+static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
 static DEFINE_MUTEX(nfs_callback_mutex);
 static struct svc_program nfs4_callback_program;
 
@@ -56,10 +60,10 @@ module_param_call(callback_tcpport, param_set_port, param_get_int,
                 &nfs_callback_set_tcpport, 0644);
 
 /*
- * This is the callback kernel thread.
+ * This is the NFSv4 callback kernel thread.
  */
 static int
-nfs_callback_svc(void *vrqstp)
+nfs4_callback_svc(void *vrqstp)
 {
        int err, preverr = 0;
        struct svc_rqst *rqstp = vrqstp;
@@ -97,20 +101,12 @@ nfs_callback_svc(void *vrqstp)
 }
 
 /*
- * Bring up the callback thread if it is not already up.
+ * Prepare to bring up the NFSv4 callback service
  */
-int nfs_callback_up(void)
+struct svc_rqst *
+nfs4_callback_up(struct svc_serv *serv)
 {
-       struct svc_serv *serv = NULL;
-       int ret = 0;
-
-       mutex_lock(&nfs_callback_mutex);
-       if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
-               goto out;
-       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
-       ret = -ENOMEM;
-       if (!serv)
-               goto out_err;
+       int ret;
 
        ret = svc_create_xprt(serv, "tcp", PF_INET,
                                nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
@@ -131,23 +127,168 @@ int nfs_callback_up(void)
                goto out_err;
 #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
 
-       nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]);
-       if (IS_ERR(nfs_callback_info.rqst)) {
-               ret = PTR_ERR(nfs_callback_info.rqst);
-               nfs_callback_info.rqst = NULL;
+       return svc_prepare_thread(serv, &serv->sv_pools[0]);
+
+out_err:
+       if (ret == 0)
+               ret = -ENOMEM;
+       return ERR_PTR(ret);
+}
+
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * The callback service for NFSv4.1 callbacks
+ */
+static int
+nfs41_callback_svc(void *vrqstp)
+{
+       struct svc_rqst *rqstp = vrqstp;
+       struct svc_serv *serv = rqstp->rq_server;
+       struct rpc_rqst *req;
+       int error;
+       DEFINE_WAIT(wq);
+
+       set_freezable();
+
+       /*
+        * FIXME: do we really need to run this under the BKL? If so, please
+        * add a comment about what it's intended to protect.
+        */
+       lock_kernel();
+       while (!kthread_should_stop()) {
+               prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
+               spin_lock_bh(&serv->sv_cb_lock);
+               if (!list_empty(&serv->sv_cb_list)) {
+                       req = list_first_entry(&serv->sv_cb_list,
+                                       struct rpc_rqst, rq_bc_list);
+                       list_del(&req->rq_bc_list);
+                       spin_unlock_bh(&serv->sv_cb_lock);
+                       dprintk("Invoking bc_svc_process()\n");
+                       error = bc_svc_process(serv, req, rqstp);
+                       dprintk("bc_svc_process() returned w/ error code= %d\n",
+                               error);
+               } else {
+                       spin_unlock_bh(&serv->sv_cb_lock);
+                       schedule();
+               }
+               finish_wait(&serv->sv_cb_waitq, &wq);
+       }
+       unlock_kernel();
+       return 0;
+}
+
+/*
+ * Bring up the NFSv4.1 callback service
+ */
+struct svc_rqst *
+nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
+{
+       struct svc_xprt *bc_xprt;
+       struct svc_rqst *rqstp = ERR_PTR(-ENOMEM);
+
+       dprintk("--> %s\n", __func__);
+       /* Create a svc_sock for the service */
+       bc_xprt = svc_sock_create(serv, xprt->prot);
+       if (!bc_xprt)
+               goto out;
+
+       /*
+        * Save the svc_serv in the transport so that it can
+        * be referenced when the session backchannel is initialized
+        */
+       serv->bc_xprt = bc_xprt;
+       xprt->bc_serv = serv;
+
+       INIT_LIST_HEAD(&serv->sv_cb_list);
+       spin_lock_init(&serv->sv_cb_lock);
+       init_waitqueue_head(&serv->sv_cb_waitq);
+       rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]);
+       if (IS_ERR(rqstp))
+               svc_sock_destroy(bc_xprt);
+out:
+       dprintk("--> %s return %p\n", __func__, rqstp);
+       return rqstp;
+}
+
+static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
+               struct svc_serv *serv, struct rpc_xprt *xprt,
+               struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
+{
+       if (minorversion) {
+               *rqstpp = nfs41_callback_up(serv, xprt);
+               *callback_svc = nfs41_callback_svc;
+       }
+       return minorversion;
+}
+
+static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
+               struct nfs_callback_data *cb_info)
+{
+       if (minorversion)
+               xprt->bc_serv = cb_info->serv;
+}
+#else
+static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
+               struct svc_serv *serv, struct rpc_xprt *xprt,
+               struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
+{
+       return 0;
+}
+
+static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
+               struct nfs_callback_data *cb_info)
+{
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+/*
+ * Bring up the callback thread if it is not already up.
+ */
+int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
+{
+       struct svc_serv *serv = NULL;
+       struct svc_rqst *rqstp;
+       int (*callback_svc)(void *vrqstp);
+       struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
+       char svc_name[12];
+       int ret = 0;
+       int minorversion_setup;
+
+       mutex_lock(&nfs_callback_mutex);
+       if (cb_info->users++ || cb_info->task != NULL) {
+               nfs_callback_bc_serv(minorversion, xprt, cb_info);
+               goto out;
+       }
+       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
+       if (!serv) {
+               ret = -ENOMEM;
+               goto out_err;
+       }
+
+       minorversion_setup =  nfs_minorversion_callback_svc_setup(minorversion,
+                                       serv, xprt, &rqstp, &callback_svc);
+       if (!minorversion_setup) {
+               /* v4.0 callback setup */
+               rqstp = nfs4_callback_up(serv);
+               callback_svc = nfs4_callback_svc;
+       }
+
+       if (IS_ERR(rqstp)) {
+               ret = PTR_ERR(rqstp);
                goto out_err;
        }
 
        svc_sock_update_bufs(serv);
 
-       nfs_callback_info.task = kthread_run(nfs_callback_svc,
-                                            nfs_callback_info.rqst,
-                                            "nfsv4-svc");
-       if (IS_ERR(nfs_callback_info.task)) {
-               ret = PTR_ERR(nfs_callback_info.task);
-               svc_exit_thread(nfs_callback_info.rqst);
-               nfs_callback_info.rqst = NULL;
-               nfs_callback_info.task = NULL;
+       sprintf(svc_name, "nfsv4.%u-svc", minorversion);
+       cb_info->serv = serv;
+       cb_info->rqst = rqstp;
+       cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
+       if (IS_ERR(cb_info->task)) {
+               ret = PTR_ERR(cb_info->task);
+               svc_exit_thread(cb_info->rqst);
+               cb_info->rqst = NULL;
+               cb_info->task = NULL;
                goto out_err;
        }
 out:
@@ -164,22 +305,25 @@ out:
 out_err:
        dprintk("NFS: Couldn't create callback socket or server thread; "
                "err = %d\n", ret);
-       nfs_callback_info.users--;
+       cb_info->users--;
        goto out;
 }
 
 /*
  * Kill the callback thread if it's no longer being used.
  */
-void nfs_callback_down(void)
+void nfs_callback_down(int minorversion)
 {
+       struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
+
        mutex_lock(&nfs_callback_mutex);
-       nfs_callback_info.users--;
-       if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
-               kthread_stop(nfs_callback_info.task);
-               svc_exit_thread(nfs_callback_info.rqst);
-               nfs_callback_info.rqst = NULL;
-               nfs_callback_info.task = NULL;
+       cb_info->users--;
+       if (cb_info->users == 0 && cb_info->task != NULL) {
+               kthread_stop(cb_info->task);
+               svc_exit_thread(cb_info->rqst);
+               cb_info->serv = NULL;
+               cb_info->rqst = NULL;
+               cb_info->task = NULL;
        }
        mutex_unlock(&nfs_callback_mutex);
 }
index e110e286a26217d681b8a26e4165d5fff3a7b75d..07baa8254ca1407a6342e80bc3e545c34868161e 100644 (file)
@@ -20,13 +20,24 @@ enum nfs4_callback_procnum {
 enum nfs4_callback_opnum {
        OP_CB_GETATTR = 3,
        OP_CB_RECALL  = 4,
+/* Callback operations new to NFSv4.1 */
+       OP_CB_LAYOUTRECALL  = 5,
+       OP_CB_NOTIFY        = 6,
+       OP_CB_PUSH_DELEG    = 7,
+       OP_CB_RECALL_ANY    = 8,
+       OP_CB_RECALLABLE_OBJ_AVAIL = 9,
+       OP_CB_RECALL_SLOT   = 10,
+       OP_CB_SEQUENCE      = 11,
+       OP_CB_WANTS_CANCELLED = 12,
+       OP_CB_NOTIFY_LOCK   = 13,
+       OP_CB_NOTIFY_DEVICEID = 14,
        OP_CB_ILLEGAL = 10044,
 };
 
 struct cb_compound_hdr_arg {
        unsigned int taglen;
        const char *tag;
-       unsigned int callback_ident;
+       unsigned int minorversion;
        unsigned nops;
 };
 
@@ -59,16 +70,59 @@ struct cb_recallargs {
        uint32_t truncate;
 };
 
+#if defined(CONFIG_NFS_V4_1)
+
+struct referring_call {
+       uint32_t                        rc_sequenceid;
+       uint32_t                        rc_slotid;
+};
+
+struct referring_call_list {
+       struct nfs4_sessionid           rcl_sessionid;
+       uint32_t                        rcl_nrefcalls;
+       struct referring_call           *rcl_refcalls;
+};
+
+struct cb_sequenceargs {
+       struct sockaddr                 *csa_addr;
+       struct nfs4_sessionid           csa_sessionid;
+       uint32_t                        csa_sequenceid;
+       uint32_t                        csa_slotid;
+       uint32_t                        csa_highestslotid;
+       uint32_t                        csa_cachethis;
+       uint32_t                        csa_nrclists;
+       struct referring_call_list      *csa_rclists;
+};
+
+struct cb_sequenceres {
+       __be32                          csr_status;
+       struct nfs4_sessionid           csr_sessionid;
+       uint32_t                        csr_sequenceid;
+       uint32_t                        csr_slotid;
+       uint32_t                        csr_highestslotid;
+       uint32_t                        csr_target_highestslotid;
+};
+
+extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
+                                      struct cb_sequenceres *res);
+
+#endif /* CONFIG_NFS_V4_1 */
+
 extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
 extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
 
 #ifdef CONFIG_NFS_V4
-extern int nfs_callback_up(void);
-extern void nfs_callback_down(void);
-#else
-#define nfs_callback_up()      (0)
-#define nfs_callback_down()    do {} while(0)
-#endif
+extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
+extern void nfs_callback_down(int minorversion);
+#endif /* CONFIG_NFS_V4 */
+
+/*
+ * nfs41: Callbacks are expected to not cause substantial latency,
+ * so we limit their concurrency to 1 by setting up the maximum number
+ * of slots for the backchannel.
+ */
+#define NFS41_BC_MIN_CALLBACKS 1
+#define NFS41_BC_MAX_CALLBACKS 1
 
 extern unsigned int nfs_callback_set_tcpport;
 extern unsigned short nfs_callback_tcpport;
index f7e83e23cf9f47a991ee6d303eee2227009f2712..b7da1f54da68e3334aded5549ef3811ff5253f7a 100644 (file)
@@ -101,3 +101,130 @@ out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
        return res;
 }
+
+#if defined(CONFIG_NFS_V4_1)
+
+/*
+ * Validate the sequenceID sent by the server.
+ * Return success if the sequenceID is one more than what we last saw on
+ * this slot, accounting for wraparound.  Increments the slot's sequence.
+ *
+ * We don't yet implement a duplicate request cache, so at this time
+ * we will log replays, and process them as if we had not seen them before,
+ * but we don't bump the sequence in the slot.  Not too worried about it,
+ * since we only currently implement idempotent callbacks anyway.
+ *
+ * We have a single slot backchannel at this time, so we don't bother
+ * checking the used_slots bit array on the table.  The lower layer guarantees
+ * a single outstanding callback request at a time.
+ */
+static int
+validate_seqid(struct nfs4_slot_table *tbl, u32 slotid, u32 seqid)
+{
+       struct nfs4_slot *slot;
+
+       dprintk("%s enter. slotid %d seqid %d\n",
+               __func__, slotid, seqid);
+
+       if (slotid > NFS41_BC_MAX_CALLBACKS)
+               return htonl(NFS4ERR_BADSLOT);
+
+       slot = tbl->slots + slotid;
+       dprintk("%s slot table seqid: %d\n", __func__, slot->seq_nr);
+
+       /* Normal */
+       if (likely(seqid == slot->seq_nr + 1)) {
+               slot->seq_nr++;
+               return htonl(NFS4_OK);
+       }
+
+       /* Replay */
+       if (seqid == slot->seq_nr) {
+               dprintk("%s seqid %d is a replay - no DRC available\n",
+                       __func__, seqid);
+               return htonl(NFS4_OK);
+       }
+
+       /* Wraparound */
+       if (seqid == 1 && (slot->seq_nr + 1) == 0) {
+               slot->seq_nr = 1;
+               return htonl(NFS4_OK);
+       }
+
+       /* Misordered request */
+       return htonl(NFS4ERR_SEQ_MISORDERED);
+}
+
+/*
+ * Returns a pointer to a held 'struct nfs_client' that matches the server's
+ * address, major version number, and session ID.  It is the caller's
+ * responsibility to release the returned reference.
+ *
+ * Returns NULL if there are no connections with sessions, or if no session
+ * matches the one of interest.
+ */
+ static struct nfs_client *find_client_with_session(
+       const struct sockaddr *addr, u32 nfsversion,
+       struct nfs4_sessionid *sessionid)
+{
+       struct nfs_client *clp;
+
+       clp = nfs_find_client(addr, 4);
+       if (clp == NULL)
+               return NULL;
+
+       do {
+               struct nfs_client *prev = clp;
+
+               if (clp->cl_session != NULL) {
+                       if (memcmp(clp->cl_session->sess_id.data,
+                                       sessionid->data,
+                                       NFS4_MAX_SESSIONID_LEN) == 0) {
+                               /* Returns a held reference to clp */
+                               return clp;
+                       }
+               }
+               clp = nfs_find_client_next(prev);
+               nfs_put_client(prev);
+       } while (clp != NULL);
+
+       return NULL;
+}
+
+/* FIXME: referring calls should be processed */
+unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
+                               struct cb_sequenceres *res)
+{
+       struct nfs_client *clp;
+       int i, status;
+
+       for (i = 0; i < args->csa_nrclists; i++)
+               kfree(args->csa_rclists[i].rcl_refcalls);
+       kfree(args->csa_rclists);
+
+       status = htonl(NFS4ERR_BADSESSION);
+       clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid);
+       if (clp == NULL)
+               goto out;
+
+       status = validate_seqid(&clp->cl_session->bc_slot_table,
+                               args->csa_slotid, args->csa_sequenceid);
+       if (status)
+               goto out_putclient;
+
+       memcpy(&res->csr_sessionid, &args->csa_sessionid,
+              sizeof(res->csr_sessionid));
+       res->csr_sequenceid = args->csa_sequenceid;
+       res->csr_slotid = args->csa_slotid;
+       res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
+       res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
+
+out_putclient:
+       nfs_put_client(clp);
+out:
+       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+       res->csr_status = status;
+       return res->csr_status;
+}
+
+#endif /* CONFIG_NFS_V4_1 */
index dd0ef34b58450409b0ca959dca6f8e90c00568d7..e5a2dac5f715fe3a98c8c42a6059a29e3914dba3 100644 (file)
                                2 + 2 + 3 + 3)
 #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
 
+#if defined(CONFIG_NFS_V4_1)
+#define CB_OP_SEQUENCE_RES_MAXSZ       (CB_OP_HDR_RES_MAXSZ + \
+                                       4 + 1 + 3)
+#endif /* CONFIG_NFS_V4_1 */
+
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
 
 typedef __be32 (*callback_process_op_t)(void *, void *);
@@ -132,7 +137,6 @@ static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
 static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr)
 {
        __be32 *p;
-       unsigned int minor_version;
        __be32 status;
 
        status = decode_string(xdr, &hdr->taglen, &hdr->tag);
@@ -147,15 +151,19 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
        p = read_buf(xdr, 12);
        if (unlikely(p == NULL))
                return htonl(NFS4ERR_RESOURCE);
-       minor_version = ntohl(*p++);
-       /* Check minor version is zero. */
-       if (minor_version != 0) {
-               printk(KERN_WARNING "%s: NFSv4 server callback with illegal minor version %u!\n",
-                               __func__, minor_version);
+       hdr->minorversion = ntohl(*p++);
+       /* Check minor version is zero or one. */
+       if (hdr->minorversion <= 1) {
+               p++;    /* skip callback_ident */
+       } else {
+               printk(KERN_WARNING "%s: NFSv4 server callback with "
+                       "illegal minor version %u!\n",
+                       __func__, hdr->minorversion);
                return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
        }
-       hdr->callback_ident = ntohl(*p++);
        hdr->nops = ntohl(*p);
+       dprintk("%s: minorversion %d nops %d\n", __func__,
+               hdr->minorversion, hdr->nops);
        return 0;
 }
 
@@ -204,6 +212,122 @@ out:
        return status;
 }
 
+#if defined(CONFIG_NFS_V4_1)
+
+static unsigned decode_sessionid(struct xdr_stream *xdr,
+                                struct nfs4_sessionid *sid)
+{
+       uint32_t *p;
+       int len = NFS4_MAX_SESSIONID_LEN;
+
+       p = read_buf(xdr, len);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);;
+
+       memcpy(sid->data, p, len);
+       return 0;
+}
+
+static unsigned decode_rc_list(struct xdr_stream *xdr,
+                              struct referring_call_list *rc_list)
+{
+       uint32_t *p;
+       int i;
+       unsigned status;
+
+       status = decode_sessionid(xdr, &rc_list->rcl_sessionid);
+       if (status)
+               goto out;
+
+       status = htonl(NFS4ERR_RESOURCE);
+       p = read_buf(xdr, sizeof(uint32_t));
+       if (unlikely(p == NULL))
+               goto out;
+
+       rc_list->rcl_nrefcalls = ntohl(*p++);
+       if (rc_list->rcl_nrefcalls) {
+               p = read_buf(xdr,
+                            rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t));
+               if (unlikely(p == NULL))
+                       goto out;
+               rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls *
+                                               sizeof(*rc_list->rcl_refcalls),
+                                               GFP_KERNEL);
+               if (unlikely(rc_list->rcl_refcalls == NULL))
+                       goto out;
+               for (i = 0; i < rc_list->rcl_nrefcalls; i++) {
+                       rc_list->rcl_refcalls[i].rc_sequenceid = ntohl(*p++);
+                       rc_list->rcl_refcalls[i].rc_slotid = ntohl(*p++);
+               }
+       }
+       status = 0;
+
+out:
+       return status;
+}
+
+static unsigned decode_cb_sequence_args(struct svc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       struct cb_sequenceargs *args)
+{
+       uint32_t *p;
+       int i;
+       unsigned status;
+
+       status = decode_sessionid(xdr, &args->csa_sessionid);
+       if (status)
+               goto out;
+
+       status = htonl(NFS4ERR_RESOURCE);
+       p = read_buf(xdr, 5 * sizeof(uint32_t));
+       if (unlikely(p == NULL))
+               goto out;
+
+       args->csa_addr = svc_addr(rqstp);
+       args->csa_sequenceid = ntohl(*p++);
+       args->csa_slotid = ntohl(*p++);
+       args->csa_highestslotid = ntohl(*p++);
+       args->csa_cachethis = ntohl(*p++);
+       args->csa_nrclists = ntohl(*p++);
+       args->csa_rclists = NULL;
+       if (args->csa_nrclists) {
+               args->csa_rclists = kmalloc(args->csa_nrclists *
+                                           sizeof(*args->csa_rclists),
+                                           GFP_KERNEL);
+               if (unlikely(args->csa_rclists == NULL))
+                       goto out;
+
+               for (i = 0; i < args->csa_nrclists; i++) {
+                       status = decode_rc_list(xdr, &args->csa_rclists[i]);
+                       if (status)
+                               goto out_free;
+               }
+       }
+       status = 0;
+
+       dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u slotid %u "
+               "highestslotid %u cachethis %d nrclists %u\n",
+               __func__,
+               ((u32 *)&args->csa_sessionid)[0],
+               ((u32 *)&args->csa_sessionid)[1],
+               ((u32 *)&args->csa_sessionid)[2],
+               ((u32 *)&args->csa_sessionid)[3],
+               args->csa_sequenceid, args->csa_slotid,
+               args->csa_highestslotid, args->csa_cachethis,
+               args->csa_nrclists);
+out:
+       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+       return status;
+
+out_free:
+       for (i = 0; i < args->csa_nrclists; i++)
+               kfree(args->csa_rclists[i].rcl_refcalls);
+       kfree(args->csa_rclists);
+       goto out;
+}
+
+#endif /* CONFIG_NFS_V4_1 */
+
 static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
 {
        __be32 *p;
@@ -353,31 +477,134 @@ out:
        return status;
 }
 
-static __be32 process_op(struct svc_rqst *rqstp,
+#if defined(CONFIG_NFS_V4_1)
+
+static unsigned encode_sessionid(struct xdr_stream *xdr,
+                                const struct nfs4_sessionid *sid)
+{
+       uint32_t *p;
+       int len = NFS4_MAX_SESSIONID_LEN;
+
+       p = xdr_reserve_space(xdr, len);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+
+       memcpy(p, sid, len);
+       return 0;
+}
+
+static unsigned encode_cb_sequence_res(struct svc_rqst *rqstp,
+                                      struct xdr_stream *xdr,
+                                      const struct cb_sequenceres *res)
+{
+       uint32_t *p;
+       unsigned status = res->csr_status;
+
+       if (unlikely(status != 0))
+               goto out;
+
+       encode_sessionid(xdr, &res->csr_sessionid);
+
+       p = xdr_reserve_space(xdr, 4 * sizeof(uint32_t));
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_RESOURCE);
+
+       *p++ = htonl(res->csr_sequenceid);
+       *p++ = htonl(res->csr_slotid);
+       *p++ = htonl(res->csr_highestslotid);
+       *p++ = htonl(res->csr_target_highestslotid);
+out:
+       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+       return status;
+}
+
+static __be32
+preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
+{
+       if (op_nr == OP_CB_SEQUENCE) {
+               if (nop != 0)
+                       return htonl(NFS4ERR_SEQUENCE_POS);
+       } else {
+               if (nop == 0)
+                       return htonl(NFS4ERR_OP_NOT_IN_SESSION);
+       }
+
+       switch (op_nr) {
+       case OP_CB_GETATTR:
+       case OP_CB_RECALL:
+       case OP_CB_SEQUENCE:
+               *op = &callback_ops[op_nr];
+               break;
+
+       case OP_CB_LAYOUTRECALL:
+       case OP_CB_NOTIFY_DEVICEID:
+       case OP_CB_NOTIFY:
+       case OP_CB_PUSH_DELEG:
+       case OP_CB_RECALL_ANY:
+       case OP_CB_RECALLABLE_OBJ_AVAIL:
+       case OP_CB_RECALL_SLOT:
+       case OP_CB_WANTS_CANCELLED:
+       case OP_CB_NOTIFY_LOCK:
+               return htonl(NFS4ERR_NOTSUPP);
+
+       default:
+               return htonl(NFS4ERR_OP_ILLEGAL);
+       }
+
+       return htonl(NFS_OK);
+}
+
+#else /* CONFIG_NFS_V4_1 */
+
+static __be32
+preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
+{
+       return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
+}
+
+#endif /* CONFIG_NFS_V4_1 */
+
+static __be32
+preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
+{
+       switch (op_nr) {
+       case OP_CB_GETATTR:
+       case OP_CB_RECALL:
+               *op = &callback_ops[op_nr];
+               break;
+       default:
+               return htonl(NFS4ERR_OP_ILLEGAL);
+       }
+
+       return htonl(NFS_OK);
+}
+
+static __be32 process_op(uint32_t minorversion, int nop,
+               struct svc_rqst *rqstp,
                struct xdr_stream *xdr_in, void *argp,
                struct xdr_stream *xdr_out, void *resp)
 {
        struct callback_op *op = &callback_ops[0];
        unsigned int op_nr = OP_CB_ILLEGAL;
-       __be32 status = 0;
+       __be32 status;
        long maxlen;
        __be32 res;
 
        dprintk("%s: start\n", __func__);
        status = decode_op_hdr(xdr_in, &op_nr);
-       if (likely(status == 0)) {
-               switch (op_nr) {
-                       case OP_CB_GETATTR:
-                       case OP_CB_RECALL:
-                               op = &callback_ops[op_nr];
-                               break;
-                       default:
-                               op_nr = OP_CB_ILLEGAL;
-                               op = &callback_ops[0];
-                               status = htonl(NFS4ERR_OP_ILLEGAL);
-               }
+       if (unlikely(status)) {
+               status = htonl(NFS4ERR_OP_ILLEGAL);
+               goto out;
        }
 
+       dprintk("%s: minorversion=%d nop=%d op_nr=%u\n",
+               __func__, minorversion, nop, op_nr);
+
+       status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) :
+                               preprocess_nfs4_op(op_nr, &op);
+       if (status == htonl(NFS4ERR_OP_ILLEGAL))
+               op_nr = OP_CB_ILLEGAL;
+out:
        maxlen = xdr_out->end - xdr_out->p;
        if (maxlen > 0 && maxlen < PAGE_SIZE) {
                if (likely(status == 0 && op->decode_args != NULL))
@@ -425,7 +652,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
                return rpc_system_err;
 
        while (status == 0 && nops != hdr_arg.nops) {
-               status = process_op(rqstp, &xdr_in, argp, &xdr_out, resp);
+               status = process_op(hdr_arg.minorversion, nops,
+                                   rqstp, &xdr_in, argp, &xdr_out, resp);
                nops++;
        }
 
@@ -452,7 +680,15 @@ static struct callback_op callback_ops[] = {
                .process_op = (callback_process_op_t)nfs4_callback_recall,
                .decode_args = (callback_decode_arg_t)decode_recall_args,
                .res_maxsize = CB_OP_RECALL_RES_MAXSZ,
-       }
+       },
+#if defined(CONFIG_NFS_V4_1)
+       [OP_CB_SEQUENCE] = {
+               .process_op = (callback_process_op_t)nfs4_callback_sequence,
+               .decode_args = (callback_decode_arg_t)decode_cb_sequence_args,
+               .encode_res = (callback_encode_res_t)encode_cb_sequence_res,
+               .res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ,
+       },
+#endif /* CONFIG_NFS_V4_1 */
 };
 
 /*
index 75c9cd2aa1194e0f0c4ac754ae2b85e722ce5809..4f75ec593be8b8395899b44fa63fafa1742e8eb7 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/in6.h>
 #include <net/ipv6.h>
 #include <linux/nfs_xdr.h>
+#include <linux/sunrpc/bc_xprt.h>
 
 #include <asm/system.h>
 
@@ -102,6 +103,7 @@ struct nfs_client_initdata {
        size_t addrlen;
        const struct nfs_rpc_ops *rpc_ops;
        int proto;
+       u32 minorversion;
 };
 
 /*
@@ -120,12 +122,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 
        clp->rpc_ops = cl_init->rpc_ops;
 
-       if (cl_init->rpc_ops->version == 4) {
-               if (nfs_callback_up() < 0)
-                       goto error_2;
-               __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
-       }
-
        atomic_set(&clp->cl_count, 1);
        clp->cl_cons_state = NFS_CS_INITING;
 
@@ -135,7 +131,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        if (cl_init->hostname) {
                clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
                if (!clp->cl_hostname)
-                       goto error_3;
+                       goto error_cleanup;
        }
 
        INIT_LIST_HEAD(&clp->cl_superblocks);
@@ -150,6 +146,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
        clp->cl_boot_time = CURRENT_TIME;
        clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
+       clp->cl_minorversion = cl_init->minorversion;
 #endif
        cred = rpc_lookup_machine_cred();
        if (!IS_ERR(cred))
@@ -159,10 +156,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
 
        return clp;
 
-error_3:
-       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
-               nfs_callback_down();
-error_2:
+error_cleanup:
        kfree(clp);
 error_0:
        return NULL;
@@ -181,6 +175,35 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
 #endif
 }
 
+/*
+ * Destroy the NFS4 callback service
+ */
+static void nfs4_destroy_callback(struct nfs_client *clp)
+{
+#ifdef CONFIG_NFS_V4
+       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
+               nfs_callback_down(clp->cl_minorversion);
+#endif /* CONFIG_NFS_V4 */
+}
+
+/*
+ * Clears/puts all minor version specific parts from an nfs_client struct
+ * reverting it to minorversion 0.
+ */
+static void nfs4_clear_client_minor_version(struct nfs_client *clp)
+{
+#ifdef CONFIG_NFS_V4_1
+       if (nfs4_has_session(clp)) {
+               nfs4_destroy_session(clp->cl_session);
+               clp->cl_session = NULL;
+       }
+
+       clp->cl_call_sync = _nfs4_call_sync;
+#endif /* CONFIG_NFS_V4_1 */
+
+       nfs4_destroy_callback(clp);
+}
+
 /*
  * Destroy a shared client record
  */
@@ -188,6 +211,7 @@ static void nfs_free_client(struct nfs_client *clp)
 {
        dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version);
 
+       nfs4_clear_client_minor_version(clp);
        nfs4_shutdown_client(clp);
 
        nfs_fscache_release_client_cookie(clp);
@@ -196,9 +220,6 @@ static void nfs_free_client(struct nfs_client *clp)
        if (!IS_ERR(clp->cl_rpcclient))
                rpc_shutdown_client(clp->cl_rpcclient);
 
-       if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
-               nfs_callback_down();
-
        if (clp->cl_machine_cred != NULL)
                put_rpccred(clp->cl_machine_cred);
 
@@ -347,7 +368,8 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
                struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
 
                /* Don't match clients that failed to initialise properly */
-               if (clp->cl_cons_state != NFS_CS_READY)
+               if (!(clp->cl_cons_state == NFS_CS_READY ||
+                     clp->cl_cons_state == NFS_CS_SESSION_INITING))
                        continue;
 
                /* Different NFS versions cannot share the same nfs_client */
@@ -420,7 +442,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
 
                if (clp->cl_proto != data->proto)
                        continue;
-
+               /* Match nfsv4 minorversion */
+               if (clp->cl_minorversion != data->minorversion)
+                       continue;
                /* Match the full socket address */
                if (!nfs_sockaddr_cmp(sap, clap))
                        continue;
@@ -478,7 +502,7 @@ found_client:
                nfs_free_client(new);
 
        error = wait_event_killable(nfs_client_active_wq,
-                               clp->cl_cons_state != NFS_CS_INITING);
+                               clp->cl_cons_state < NFS_CS_INITING);
        if (error < 0) {
                nfs_put_client(clp);
                return ERR_PTR(-ERESTARTSYS);
@@ -499,12 +523,28 @@ found_client:
 /*
  * Mark a server as ready or failed
  */
-static void nfs_mark_client_ready(struct nfs_client *clp, int state)
+void nfs_mark_client_ready(struct nfs_client *clp, int state)
 {
        clp->cl_cons_state = state;
        wake_up_all(&nfs_client_active_wq);
 }
 
+/*
+ * With sessions, the client is not marked ready until after a
+ * successful EXCHANGE_ID and CREATE_SESSION.
+ *
+ * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
+ * other versions of NFS can be tried.
+ */
+int nfs4_check_client_ready(struct nfs_client *clp)
+{
+       if (!nfs4_has_session(clp))
+               return 0;
+       if (clp->cl_cons_state < NFS_CS_READY)
+               return -EPROTONOSUPPORT;
+       return 0;
+}
+
 /*
  * Initialise the timeout values for a connection
  */
@@ -1049,6 +1089,61 @@ error:
 }
 
 #ifdef CONFIG_NFS_V4
+/*
+ * Initialize the NFS4 callback service
+ */
+static int nfs4_init_callback(struct nfs_client *clp)
+{
+       int error;
+
+       if (clp->rpc_ops->version == 4) {
+               if (nfs4_has_session(clp)) {
+                       error = xprt_setup_backchannel(
+                                               clp->cl_rpcclient->cl_xprt,
+                                               NFS41_BC_MIN_CALLBACKS);
+                       if (error < 0)
+                               return error;
+               }
+
+               error = nfs_callback_up(clp->cl_minorversion,
+                                       clp->cl_rpcclient->cl_xprt);
+               if (error < 0) {
+                       dprintk("%s: failed to start callback. Error = %d\n",
+                               __func__, error);
+                       return error;
+               }
+               __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
+       }
+       return 0;
+}
+
+/*
+ * Initialize the minor version specific parts of an NFS4 client record
+ */
+static int nfs4_init_client_minor_version(struct nfs_client *clp)
+{
+       clp->cl_call_sync = _nfs4_call_sync;
+
+#if defined(CONFIG_NFS_V4_1)
+       if (clp->cl_minorversion) {
+               struct nfs4_session *session = NULL;
+               /*
+                * Create the session and mark it expired.
+                * When a SEQUENCE operation encounters the expired session
+                * it will do session recovery to initialize it.
+                */
+               session = nfs4_alloc_session(clp);
+               if (!session)
+                       return -ENOMEM;
+
+               clp->cl_session = session;
+               clp->cl_call_sync = _nfs4_call_sync_session;
+       }
+#endif /* CONFIG_NFS_V4_1 */
+
+       return nfs4_init_callback(clp);
+}
+
 /*
  * Initialise an NFS4 client record
  */
@@ -1083,7 +1178,12 @@ static int nfs4_init_client(struct nfs_client *clp,
        }
        __set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
 
-       nfs_mark_client_ready(clp, NFS_CS_READY);
+       error = nfs4_init_client_minor_version(clp);
+       if (error < 0)
+               goto error;
+
+       if (!nfs4_has_session(clp))
+               nfs_mark_client_ready(clp, NFS_CS_READY);
        return 0;
 
 error:
@@ -1101,7 +1201,8 @@ static int nfs4_set_client(struct nfs_server *server,
                const size_t addrlen,
                const char *ip_addr,
                rpc_authflavor_t authflavour,
-               int proto, const struct rpc_timeout *timeparms)
+               int proto, const struct rpc_timeout *timeparms,
+               u32 minorversion)
 {
        struct nfs_client_initdata cl_init = {
                .hostname = hostname,
@@ -1109,6 +1210,7 @@ static int nfs4_set_client(struct nfs_server *server,
                .addrlen = addrlen,
                .rpc_ops = &nfs_v4_clientops,
                .proto = proto,
+               .minorversion = minorversion,
        };
        struct nfs_client *clp;
        int error;
@@ -1137,6 +1239,36 @@ error:
        return error;
 }
 
+/*
+ * Initialize a session.
+ * Note: save the mount rsize and wsize for create_server negotiation.
+ */
+static void nfs4_init_session(struct nfs_client *clp,
+                             unsigned int wsize, unsigned int rsize)
+{
+#if defined(CONFIG_NFS_V4_1)
+       if (nfs4_has_session(clp)) {
+               clp->cl_session->fc_attrs.max_rqst_sz = wsize;
+               clp->cl_session->fc_attrs.max_resp_sz = rsize;
+       }
+#endif /* CONFIG_NFS_V4_1 */
+}
+
+/*
+ * Session has been established, and the client marked ready.
+ * Set the mount rsize and wsize with negotiated fore channel
+ * attributes which will be bound checked in nfs_server_set_fsinfo.
+ */
+static void nfs4_session_set_rwsize(struct nfs_server *server)
+{
+#ifdef CONFIG_NFS_V4_1
+       if (!nfs4_has_session(server->nfs_client))
+               return;
+       server->rsize = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
+       server->wsize = server->nfs_client->cl_session->fc_attrs.max_rqst_sz;
+#endif /* CONFIG_NFS_V4_1 */
+}
+
 /*
  * Create a version 4 volume record
  */
@@ -1164,7 +1296,8 @@ static int nfs4_init_server(struct nfs_server *server,
                        data->client_address,
                        data->auth_flavors[0],
                        data->nfs_server.protocol,
-                       &timeparms);
+                       &timeparms,
+                       data->minorversion);
        if (error < 0)
                goto error;
 
@@ -1214,6 +1347,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
        BUG_ON(!server->nfs_client->rpc_ops);
        BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
 
+       nfs4_init_session(server->nfs_client, server->wsize, server->rsize);
+
        /* Probe the root fh to retrieve its FSID */
        error = nfs4_path_walk(server, mntfh, data->nfs_server.export_path);
        if (error < 0)
@@ -1224,6 +1359,8 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
                (unsigned long long) server->fsid.minor);
        dprintk("Mount FH: %d\n", mntfh->size);
 
+       nfs4_session_set_rwsize(server);
+
        error = nfs_probe_fsinfo(server, mntfh, &fattr);
        if (error < 0)
                goto error;
@@ -1282,7 +1419,8 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
                                parent_client->cl_ipaddr,
                                data->authflavor,
                                parent_server->client->cl_xprt->prot,
-                               parent_server->client->cl_timeout);
+                               parent_server->client->cl_timeout,
+                               parent_client->cl_minorversion);
        if (error < 0)
                goto error;
 
index 08f6b040d289d9ecb26979342bdf478db3c5a0b8..489fc01a3204e9d7b21b99bee47dcdded6fb38d0 100644 (file)
@@ -259,6 +259,9 @@ static void nfs_direct_read_release(void *calldata)
 }
 
 static const struct rpc_call_ops nfs_read_direct_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_read_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs_direct_read_result,
        .rpc_release = nfs_direct_read_release,
 };
@@ -535,6 +538,9 @@ static void nfs_direct_commit_release(void *calldata)
 }
 
 static const struct rpc_call_ops nfs_commit_direct_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_write_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs_direct_commit_result,
        .rpc_release = nfs_direct_commit_release,
 };
@@ -673,6 +679,9 @@ out_unlock:
 }
 
 static const struct rpc_call_ops nfs_write_direct_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_write_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs_direct_write_result,
        .rpc_release = nfs_direct_write_release,
 };
index e4d6a8348adf5178a8d7d63b85a96fe22e0d129b..acee3274d275731ac136a1f4833ebe35288e1f8f 100644 (file)
@@ -2,6 +2,7 @@
  * NFS internal definitions
  */
 
+#include "nfs4_fs.h"
 #include <linux/mount.h>
 #include <linux/security.h>
 
@@ -17,6 +18,18 @@ struct nfs_string;
  */
 #define NFS_MAX_READAHEAD      (RPC_DEF_SLOT_TABLE - 1)
 
+/*
+ * Determine if sessions are in use.
+ */
+static inline int nfs4_has_session(const struct nfs_client *clp)
+{
+#ifdef CONFIG_NFS_V4_1
+       if (clp->cl_session)
+               return 1;
+#endif /* CONFIG_NFS_V4_1 */
+       return 0;
+}
+
 struct nfs_clone_mount {
        const struct super_block *sb;
        const struct dentry *dentry;
@@ -44,6 +57,7 @@ struct nfs_parsed_mount_data {
        unsigned int            auth_flavor_len;
        rpc_authflavor_t        auth_flavors[1];
        char                    *client_address;
+       unsigned int            minorversion;
        char                    *fscache_uniq;
 
        struct {
@@ -99,6 +113,8 @@ extern void nfs_free_server(struct nfs_server *server);
 extern struct nfs_server *nfs_clone_server(struct nfs_server *,
                                           struct nfs_fh *,
                                           struct nfs_fattr *);
+extern void nfs_mark_client_ready(struct nfs_client *clp, int state);
+extern int nfs4_check_client_ready(struct nfs_client *clp);
 #ifdef CONFIG_PROC_FS
 extern int __init nfs_fs_proc_init(void);
 extern void nfs_fs_proc_exit(void);
@@ -146,6 +162,20 @@ extern __be32 * nfs_decode_dirent(__be32 *, struct nfs_entry *, int);
 extern struct rpc_procinfo nfs3_procedures[];
 extern __be32 *nfs3_decode_dirent(__be32 *, struct nfs_entry *, int);
 
+/* nfs4proc.c */
+static inline void nfs4_restart_rpc(struct rpc_task *task,
+                                   const struct nfs_client *clp)
+{
+#ifdef CONFIG_NFS_V4_1
+       if (nfs4_has_session(clp) &&
+           test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) {
+               rpc_restart_call_prepare(task);
+               return;
+       }
+#endif /* CONFIG_NFS_V4_1 */
+       rpc_restart_call(task);
+}
+
 /* nfs4xdr.c */
 #ifdef CONFIG_NFS_V4
 extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus);
@@ -205,6 +235,38 @@ extern int nfs4_path_walk(struct nfs_server *server,
                          const char *path);
 #endif
 
+/* read.c */
+extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
+
+/* write.c */
+extern void nfs_write_prepare(struct rpc_task *task, void *calldata);
+
+/* nfs4proc.c */
+extern int _nfs4_call_sync(struct nfs_server *server,
+                          struct rpc_message *msg,
+                          struct nfs4_sequence_args *args,
+                          struct nfs4_sequence_res *res,
+                          int cache_reply);
+extern int _nfs4_call_sync_session(struct nfs_server *server,
+                                  struct rpc_message *msg,
+                                  struct nfs4_sequence_args *args,
+                                  struct nfs4_sequence_res *res,
+                                  int cache_reply);
+
+#ifdef CONFIG_NFS_V4_1
+extern void nfs41_sequence_free_slot(const struct nfs_client *,
+                                    struct nfs4_sequence_res *res);
+#endif /* CONFIG_NFS_V4_1 */
+
+static inline void nfs4_sequence_free_slot(const struct nfs_client *clp,
+                                          struct nfs4_sequence_res *res)
+{
+#ifdef CONFIG_NFS_V4_1
+       if (nfs4_has_session(clp))
+               nfs41_sequence_free_slot(clp, res);
+#endif /* CONFIG_NFS_V4_1 */
+}
+
 /*
  * Determine the device name as a string
  */
index 84345deab26ff06a506dff7e4578586166437fe2..61bc3a32e1e25780c4b9aa896814e22945fc2842 100644 (file)
@@ -44,6 +44,7 @@ enum nfs4_client_state {
        NFS4CLNT_RECLAIM_REBOOT,
        NFS4CLNT_RECLAIM_NOGRACE,
        NFS4CLNT_DELEGRETURN,
+       NFS4CLNT_SESSION_SETUP,
 };
 
 /*
@@ -177,6 +178,14 @@ struct nfs4_state_recovery_ops {
        int state_flag_bit;
        int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
        int (*recover_lock)(struct nfs4_state *, struct file_lock *);
+       int (*establish_clid)(struct nfs_client *, struct rpc_cred *);
+       struct rpc_cred * (*get_clid_cred)(struct nfs_client *);
+};
+
+struct nfs4_state_maintenance_ops {
+       int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *);
+       struct rpc_cred * (*get_state_renewal_cred_locked)(struct nfs_client *);
+       int (*renew_lease)(struct nfs_client *, struct rpc_cred *);
 };
 
 extern const struct dentry_operations nfs4_dentry_operations;
@@ -193,6 +202,7 @@ extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struc
 extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
 extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
 extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
+extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *);
 extern int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait);
 extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *);
 extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *);
@@ -200,8 +210,26 @@ extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fh
 extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
                struct nfs4_fs_locations *fs_locations, struct page *page);
 
-extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
-extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops;
+extern struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[];
+extern struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[];
+#if defined(CONFIG_NFS_V4_1)
+extern int nfs4_setup_sequence(struct nfs_client *clp,
+               struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
+               int cache_reply, struct rpc_task *task);
+extern void nfs4_destroy_session(struct nfs4_session *session);
+extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp);
+extern int nfs4_proc_create_session(struct nfs_client *, int reset);
+extern int nfs4_proc_destroy_session(struct nfs4_session *);
+#else /* CONFIG_NFS_v4_1 */
+static inline int nfs4_setup_sequence(struct nfs_client *clp,
+               struct nfs4_sequence_args *args, struct nfs4_sequence_res *res,
+               int cache_reply, struct rpc_task *task)
+{
+       return 0;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+extern struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[];
 
 extern const u32 nfs4_fattr_bitmap[2];
 extern const u32 nfs4_statfs_bitmap[2];
@@ -216,7 +244,12 @@ extern void nfs4_kill_renewd(struct nfs_client *);
 extern void nfs4_renew_state(struct work_struct *);
 
 /* nfs4state.c */
+struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp);
 struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
+#if defined(CONFIG_NFS_V4_1)
+struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp);
+struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp);
+#endif /* CONFIG_NFS_V4_1 */
 
 extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
 extern void nfs4_put_state_owner(struct nfs4_state_owner *);
index 4674f8092da8aaa52ae4af6835c6b150341efe66..57dabb8a048e494d1df269dec09d5e8769a3d997 100644 (file)
 #include <linux/smp_lock.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
+#include <linux/module.h>
+#include <linux/sunrpc/bc_xprt.h>
 
 #include "nfs4_fs.h"
 #include "delegation.h"
 #include "internal.h"
 #include "iostat.h"
+#include "callback.h"
 
 #define NFSDBG_FACILITY                NFSDBG_PROC
 
@@ -247,7 +250,25 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode,
                        ret = nfs4_wait_clnt_recover(clp);
                        if (ret == 0)
                                exception->retry = 1;
+#if !defined(CONFIG_NFS_V4_1)
                        break;
+#else /* !defined(CONFIG_NFS_V4_1) */
+                       if (!nfs4_has_session(server->nfs_client))
+                               break;
+                       /* FALLTHROUGH */
+               case -NFS4ERR_BADSESSION:
+               case -NFS4ERR_BADSLOT:
+               case -NFS4ERR_BAD_HIGH_SLOT:
+               case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+               case -NFS4ERR_DEADSESSION:
+               case -NFS4ERR_SEQ_FALSE_RETRY:
+               case -NFS4ERR_SEQ_MISORDERED:
+                       dprintk("%s ERROR: %d Reset session\n", __func__,
+                               errorcode);
+                       set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
+                       exception->retry = 1;
+                       /* FALLTHROUGH */
+#endif /* !defined(CONFIG_NFS_V4_1) */
                case -NFS4ERR_FILE_OPEN:
                case -NFS4ERR_GRACE:
                case -NFS4ERR_DELAY:
@@ -271,6 +292,353 @@ static void renew_lease(const struct nfs_server *server, unsigned long timestamp
        spin_unlock(&clp->cl_lock);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+
+/*
+ * nfs4_free_slot - free a slot and efficiently update slot table.
+ *
+ * freeing a slot is trivially done by clearing its respective bit
+ * in the bitmap.
+ * If the freed slotid equals highest_used_slotid we want to update it
+ * so that the server would be able to size down the slot table if needed,
+ * otherwise we know that the highest_used_slotid is still in use.
+ * When updating highest_used_slotid there may be "holes" in the bitmap
+ * so we need to scan down from highest_used_slotid to 0 looking for the now
+ * highest slotid in use.
+ * If none found, highest_used_slotid is set to -1.
+ */
+static void
+nfs4_free_slot(struct nfs4_slot_table *tbl, u8 free_slotid)
+{
+       int slotid = free_slotid;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       /* clear used bit in bitmap */
+       __clear_bit(slotid, tbl->used_slots);
+
+       /* update highest_used_slotid when it is freed */
+       if (slotid == tbl->highest_used_slotid) {
+               slotid = find_last_bit(tbl->used_slots, tbl->max_slots);
+               if (slotid >= 0 && slotid < tbl->max_slots)
+                       tbl->highest_used_slotid = slotid;
+               else
+                       tbl->highest_used_slotid = -1;
+       }
+       rpc_wake_up_next(&tbl->slot_tbl_waitq);
+       spin_unlock(&tbl->slot_tbl_lock);
+       dprintk("%s: free_slotid %u highest_used_slotid %d\n", __func__,
+               free_slotid, tbl->highest_used_slotid);
+}
+
+void nfs41_sequence_free_slot(const struct nfs_client *clp,
+                             struct nfs4_sequence_res *res)
+{
+       struct nfs4_slot_table *tbl;
+
+       if (!nfs4_has_session(clp)) {
+               dprintk("%s: No session\n", __func__);
+               return;
+       }
+       tbl = &clp->cl_session->fc_slot_table;
+       if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) {
+               dprintk("%s: No slot\n", __func__);
+               /* just wake up the next guy waiting since
+                * we may have not consumed a slot after all */
+               rpc_wake_up_next(&tbl->slot_tbl_waitq);
+               return;
+       }
+       nfs4_free_slot(tbl, res->sr_slotid);
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+}
+
+static void nfs41_sequence_done(struct nfs_client *clp,
+                               struct nfs4_sequence_res *res,
+                               int rpc_status)
+{
+       unsigned long timestamp;
+       struct nfs4_slot_table *tbl;
+       struct nfs4_slot *slot;
+
+       /*
+        * sr_status remains 1 if an RPC level error occurred. The server
+        * may or may not have processed the sequence operation..
+        * Proceed as if the server received and processed the sequence
+        * operation.
+        */
+       if (res->sr_status == 1)
+               res->sr_status = NFS_OK;
+
+       /* -ERESTARTSYS can result in skipping nfs41_sequence_setup */
+       if (res->sr_slotid == NFS4_MAX_SLOT_TABLE)
+               goto out;
+
+       tbl = &clp->cl_session->fc_slot_table;
+       slot = tbl->slots + res->sr_slotid;
+
+       if (res->sr_status == 0) {
+               /* Update the slot's sequence and clientid lease timer */
+               ++slot->seq_nr;
+               timestamp = res->sr_renewal_time;
+               spin_lock(&clp->cl_lock);
+               if (time_before(clp->cl_last_renewal, timestamp))
+                       clp->cl_last_renewal = timestamp;
+               spin_unlock(&clp->cl_lock);
+               return;
+       }
+out:
+       /* The session may be reset by one of the error handlers. */
+       dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);
+       nfs41_sequence_free_slot(clp, res);
+}
+
+/*
+ * nfs4_find_slot - efficiently look for a free slot
+ *
+ * nfs4_find_slot looks for an unset bit in the used_slots bitmap.
+ * If found, we mark the slot as used, update the highest_used_slotid,
+ * and respectively set up the sequence operation args.
+ * The slot number is returned if found, or NFS4_MAX_SLOT_TABLE otherwise.
+ *
+ * Note: must be called with under the slot_tbl_lock.
+ */
+static u8
+nfs4_find_slot(struct nfs4_slot_table *tbl, struct rpc_task *task)
+{
+       int slotid;
+       u8 ret_id = NFS4_MAX_SLOT_TABLE;
+       BUILD_BUG_ON((u8)NFS4_MAX_SLOT_TABLE != (int)NFS4_MAX_SLOT_TABLE);
+
+       dprintk("--> %s used_slots=%04lx highest_used=%d max_slots=%d\n",
+               __func__, tbl->used_slots[0], tbl->highest_used_slotid,
+               tbl->max_slots);
+       slotid = find_first_zero_bit(tbl->used_slots, tbl->max_slots);
+       if (slotid >= tbl->max_slots)
+               goto out;
+       __set_bit(slotid, tbl->used_slots);
+       if (slotid > tbl->highest_used_slotid)
+               tbl->highest_used_slotid = slotid;
+       ret_id = slotid;
+out:
+       dprintk("<-- %s used_slots=%04lx highest_used=%d slotid=%d \n",
+               __func__, tbl->used_slots[0], tbl->highest_used_slotid, ret_id);
+       return ret_id;
+}
+
+static int nfs4_recover_session(struct nfs4_session *session)
+{
+       struct nfs_client *clp = session->clp;
+       int ret;
+
+       for (;;) {
+               ret = nfs4_wait_clnt_recover(clp);
+               if (ret != 0)
+                               return ret;
+               if (!test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state))
+                       break;
+               nfs4_schedule_state_manager(clp);
+       }
+       return 0;
+}
+
+static int nfs41_setup_sequence(struct nfs4_session *session,
+                               struct nfs4_sequence_args *args,
+                               struct nfs4_sequence_res *res,
+                               int cache_reply,
+                               struct rpc_task *task)
+{
+       struct nfs4_slot *slot;
+       struct nfs4_slot_table *tbl;
+       int status = 0;
+       u8 slotid;
+
+       dprintk("--> %s\n", __func__);
+       /* slot already allocated? */
+       if (res->sr_slotid != NFS4_MAX_SLOT_TABLE)
+               return 0;
+
+       memset(res, 0, sizeof(*res));
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+       tbl = &session->fc_slot_table;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       if (test_bit(NFS4CLNT_SESSION_SETUP, &session->clp->cl_state)) {
+               if (tbl->highest_used_slotid != -1) {
+                       rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
+                       spin_unlock(&tbl->slot_tbl_lock);
+                       dprintk("<-- %s: Session reset: draining\n", __func__);
+                       return -EAGAIN;
+               }
+
+               /* The slot table is empty; start the reset thread */
+               dprintk("%s Session Reset\n", __func__);
+               spin_unlock(&tbl->slot_tbl_lock);
+               status = nfs4_recover_session(session);
+               if (status)
+                       return status;
+               spin_lock(&tbl->slot_tbl_lock);
+       }
+
+       slotid = nfs4_find_slot(tbl, task);
+       if (slotid == NFS4_MAX_SLOT_TABLE) {
+               rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
+               spin_unlock(&tbl->slot_tbl_lock);
+               dprintk("<-- %s: no free slots\n", __func__);
+               return -EAGAIN;
+       }
+       spin_unlock(&tbl->slot_tbl_lock);
+
+       slot = tbl->slots + slotid;
+       args->sa_session = session;
+       args->sa_slotid = slotid;
+       args->sa_cache_this = cache_reply;
+
+       dprintk("<-- %s slotid=%d seqid=%d\n", __func__, slotid, slot->seq_nr);
+
+       res->sr_session = session;
+       res->sr_slotid = slotid;
+       res->sr_renewal_time = jiffies;
+       /*
+        * sr_status is only set in decode_sequence, and so will remain
+        * set to 1 if an rpc level failure occurs.
+        */
+       res->sr_status = 1;
+       return 0;
+}
+
+int nfs4_setup_sequence(struct nfs_client *clp,
+                       struct nfs4_sequence_args *args,
+                       struct nfs4_sequence_res *res,
+                       int cache_reply,
+                       struct rpc_task *task)
+{
+       int ret = 0;
+
+       dprintk("--> %s clp %p session %p sr_slotid %d\n",
+               __func__, clp, clp->cl_session, res->sr_slotid);
+
+       if (!nfs4_has_session(clp))
+               goto out;
+       ret = nfs41_setup_sequence(clp->cl_session, args, res, cache_reply,
+                                  task);
+       if (ret != -EAGAIN) {
+               /* terminate rpc task */
+               task->tk_status = ret;
+               task->tk_action = NULL;
+       }
+out:
+       dprintk("<-- %s status=%d\n", __func__, ret);
+       return ret;
+}
+
+struct nfs41_call_sync_data {
+       struct nfs_client *clp;
+       struct nfs4_sequence_args *seq_args;
+       struct nfs4_sequence_res *seq_res;
+       int cache_reply;
+};
+
+static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata)
+{
+       struct nfs41_call_sync_data *data = calldata;
+
+       dprintk("--> %s data->clp->cl_session %p\n", __func__,
+               data->clp->cl_session);
+       if (nfs4_setup_sequence(data->clp, data->seq_args,
+                               data->seq_res, data->cache_reply, task))
+               return;
+       rpc_call_start(task);
+}
+
+static void nfs41_call_sync_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs41_call_sync_data *data = calldata;
+
+       nfs41_sequence_done(data->clp, data->seq_res, task->tk_status);
+       nfs41_sequence_free_slot(data->clp, data->seq_res);
+}
+
+struct rpc_call_ops nfs41_call_sync_ops = {
+       .rpc_call_prepare = nfs41_call_sync_prepare,
+       .rpc_call_done = nfs41_call_sync_done,
+};
+
+static int nfs4_call_sync_sequence(struct nfs_client *clp,
+                                  struct rpc_clnt *clnt,
+                                  struct rpc_message *msg,
+                                  struct nfs4_sequence_args *args,
+                                  struct nfs4_sequence_res *res,
+                                  int cache_reply)
+{
+       int ret;
+       struct rpc_task *task;
+       struct nfs41_call_sync_data data = {
+               .clp = clp,
+               .seq_args = args,
+               .seq_res = res,
+               .cache_reply = cache_reply,
+       };
+       struct rpc_task_setup task_setup = {
+               .rpc_client = clnt,
+               .rpc_message = msg,
+               .callback_ops = &nfs41_call_sync_ops,
+               .callback_data = &data
+       };
+
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+       task = rpc_run_task(&task_setup);
+       if (IS_ERR(task))
+               ret = PTR_ERR(task);
+       else {
+               ret = task->tk_status;
+               rpc_put_task(task);
+       }
+       return ret;
+}
+
+int _nfs4_call_sync_session(struct nfs_server *server,
+                           struct rpc_message *msg,
+                           struct nfs4_sequence_args *args,
+                           struct nfs4_sequence_res *res,
+                           int cache_reply)
+{
+       return nfs4_call_sync_sequence(server->nfs_client, server->client,
+                                      msg, args, res, cache_reply);
+}
+
+#endif /* CONFIG_NFS_V4_1 */
+
+int _nfs4_call_sync(struct nfs_server *server,
+                   struct rpc_message *msg,
+                   struct nfs4_sequence_args *args,
+                   struct nfs4_sequence_res *res,
+                   int cache_reply)
+{
+       args->sa_session = res->sr_session = NULL;
+       return rpc_call_sync(server->client, msg, 0);
+}
+
+#define nfs4_call_sync(server, msg, args, res, cache_reply) \
+       (server)->nfs_client->cl_call_sync((server), (msg), &(args)->seq_args, \
+                       &(res)->seq_res, (cache_reply))
+
+static void nfs4_sequence_done(const struct nfs_server *server,
+                              struct nfs4_sequence_res *res, int rpc_status)
+{
+#ifdef CONFIG_NFS_V4_1
+       if (nfs4_has_session(server->nfs_client))
+               nfs41_sequence_done(server->nfs_client, res, rpc_status);
+#endif /* CONFIG_NFS_V4_1 */
+}
+
+/* no restart, therefore free slot here */
+static void nfs4_sequence_done_free_slot(const struct nfs_server *server,
+                                        struct nfs4_sequence_res *res,
+                                        int rpc_status)
+{
+       nfs4_sequence_done(server, res, rpc_status);
+       nfs4_sequence_free_slot(server->nfs_client, res);
+}
+
 static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
 {
        struct nfs_inode *nfsi = NFS_I(dir);
@@ -343,6 +711,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
        p->o_arg.server = server;
        p->o_arg.bitmask = server->attr_bitmask;
        p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
+       p->o_res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        if (flags & O_EXCL) {
                u32 *s = (u32 *) p->o_arg.u.verifier.data;
                s[0] = jiffies;
@@ -929,6 +1298,10 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
                nfs_copy_fh(&data->o_res.fh, data->o_arg.fh);
        }
        data->timestamp = jiffies;
+       if (nfs4_setup_sequence(data->o_arg.server->nfs_client,
+                               &data->o_arg.seq_args,
+                               &data->o_res.seq_res, 1, task))
+               return;
        rpc_call_start(task);
        return;
 out_no_action:
@@ -941,6 +1314,10 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata)
        struct nfs4_opendata *data = calldata;
 
        data->rpc_status = task->tk_status;
+
+       nfs4_sequence_done_free_slot(data->o_arg.server, &data->o_res.seq_res,
+                                    task->tk_status);
+
        if (RPC_ASSASSINATED(task))
                return;
        if (task->tk_status == 0) {
@@ -1269,7 +1646,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        } else
                memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
 
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &arg, &res, 1);
        if (status == 0 && state != NULL)
                renew_lease(server, timestamp);
        return status;
@@ -1318,6 +1695,7 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
        struct nfs4_state *state = calldata->state;
        struct nfs_server *server = NFS_SERVER(calldata->inode);
 
+       nfs4_sequence_done(server, &calldata->res.seq_res, task->tk_status);
        if (RPC_ASSASSINATED(task))
                return;
         /* hmm. we are done with the inode, and in the process of freeing
@@ -1336,10 +1714,11 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                                break;
                default:
                        if (nfs4_async_handle_error(task, server, state) == -EAGAIN) {
-                               rpc_restart_call(task);
+                               nfs4_restart_rpc(task, server->nfs_client);
                                return;
                        }
        }
+       nfs4_sequence_free_slot(server->nfs_client, &calldata->res.seq_res);
        nfs_refresh_inode(calldata->inode, calldata->res.fattr);
 }
 
@@ -1380,6 +1759,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
                calldata->arg.fmode = FMODE_WRITE;
        }
        calldata->timestamp = jiffies;
+       if (nfs4_setup_sequence((NFS_SERVER(calldata->inode))->nfs_client,
+                               &calldata->arg.seq_args, &calldata->res.seq_res,
+                               1, task))
+               return;
        rpc_call_start(task);
 }
 
@@ -1419,13 +1802,15 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
        };
        int status = -ENOMEM;
 
-       calldata = kmalloc(sizeof(*calldata), GFP_KERNEL);
+       calldata = kzalloc(sizeof(*calldata), GFP_KERNEL);
        if (calldata == NULL)
                goto out;
        calldata->inode = state->inode;
        calldata->state = state;
        calldata->arg.fh = NFS_FH(state->inode);
        calldata->arg.stateid = &state->open_stateid;
+       if (nfs4_has_session(server->nfs_client))
+               memset(calldata->arg.stateid->data, 0, 4);    /* clear seqid */
        /* Serialization for the sequence id */
        calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
        if (calldata->arg.seqid == NULL)
@@ -1435,6 +1820,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
        calldata->res.fattr = &calldata->fattr;
        calldata->res.seqid = calldata->arg.seqid;
        calldata->res.server = server;
+       calldata->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        calldata->path.mnt = mntget(path->mnt);
        calldata->path.dentry = dget(path->dentry);
 
@@ -1584,15 +1970,18 @@ void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
 
 static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
 {
+       struct nfs4_server_caps_arg args = {
+               .fhandle = fhandle,
+       };
        struct nfs4_server_caps_res res = {};
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SERVER_CAPS],
-               .rpc_argp = fhandle,
+               .rpc_argp = &args,
                .rpc_resp = &res,
        };
        int status;
 
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
        if (status == 0) {
                memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
                if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
@@ -1606,6 +1995,7 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
                server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
                server->acl_bitmask = res.acl_bitmask;
        }
+
        return status;
 }
 
@@ -1637,8 +2027,15 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
+       int status;
+
        nfs_fattr_init(info->fattr);
-       return rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_recover_expired_lease(server);
+       if (!status)
+               status = nfs4_check_client_ready(server->nfs_client);
+       if (!status)
+               status = nfs4_call_sync(server, &msg, &args, &res, 0);
+       return status;
 }
 
 static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -1728,7 +2125,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
        };
        
        nfs_fattr_init(fattr);
-       return rpc_call_sync(server->client, &msg, 0);
+       return nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
@@ -1812,7 +2209,7 @@ static int _nfs4_proc_lookupfh(struct nfs_server *server, const struct nfs_fh *d
        nfs_fattr_init(fattr);
 
        dprintk("NFS call  lookupfh %s\n", name->name);
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
        dprintk("NFS reply lookupfh: %d\n", status);
        return status;
 }
@@ -1898,7 +2295,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
                        args.access |= NFS4_ACCESS_EXECUTE;
        }
        nfs_fattr_init(&fattr);
-       status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
        if (!status) {
                entry->mask = 0;
                if (res.access & NFS4_ACCESS_READ)
@@ -1957,13 +2354,14 @@ static int _nfs4_proc_readlink(struct inode *inode, struct page *page,
                .pglen    = pglen,
                .pages    = &page,
        };
+       struct nfs4_readlink_res res;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK],
                .rpc_argp = &args,
-               .rpc_resp = NULL,
+               .rpc_resp = &res,
        };
 
-       return rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       return nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0);
 }
 
 static int nfs4_proc_readlink(struct inode *inode, struct page *page,
@@ -2057,7 +2455,7 @@ static int _nfs4_proc_remove(struct inode *dir, struct qstr *name)
        int                     status;
 
        nfs_fattr_init(&res.dir_attr);
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 1);
        if (status == 0) {
                update_changeattr(dir, &res.cinfo);
                nfs_post_op_update_inode(dir, &res.dir_attr);
@@ -2092,8 +2490,10 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
 {
        struct nfs_removeres *res = task->tk_msg.rpc_resp;
 
+       nfs4_sequence_done(res->server, &res->seq_res, task->tk_status);
        if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
                return 0;
+       nfs4_sequence_free_slot(res->server->nfs_client, &res->seq_res);
        update_changeattr(dir, &res->cinfo);
        nfs_post_op_update_inode(dir, &res->dir_attr);
        return 1;
@@ -2125,7 +2525,7 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
        
        nfs_fattr_init(res.old_fattr);
        nfs_fattr_init(res.new_fattr);
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &arg, &res, 1);
 
        if (!status) {
                update_changeattr(old_dir, &res.old_cinfo);
@@ -2174,7 +2574,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
 
        nfs_fattr_init(res.fattr);
        nfs_fattr_init(res.dir_attr);
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &arg, &res, 1);
        if (!status) {
                update_changeattr(dir, &res.cinfo);
                nfs_post_op_update_inode(dir, res.dir_attr);
@@ -2235,7 +2635,8 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
 
 static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
 {
-       int status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
+       int status = nfs4_call_sync(NFS_SERVER(dir), &data->msg,
+                                   &data->arg, &data->res, 1);
        if (status == 0) {
                update_changeattr(dir, &data->res.dir_cinfo);
                nfs_post_op_update_inode(dir, data->res.dir_fattr);
@@ -2344,7 +2745,7 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
                        (unsigned long long)cookie);
        nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
        res.pgbase = args.pgbase;
-       status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+       status = nfs4_call_sync(NFS_SERVER(dir), &msg, &args, &res, 0);
        if (status == 0)
                memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
 
@@ -2422,14 +2823,17 @@ static int _nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
                .fh = fhandle,
                .bitmask = server->attr_bitmask,
        };
+       struct nfs4_statfs_res res = {
+               .fsstat = fsstat,
+       };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS],
                .rpc_argp = &args,
-               .rpc_resp = fsstat,
+               .rpc_resp = &res,
        };
 
        nfs_fattr_init(fsstat->fattr);
-       return rpc_call_sync(server->client, &msg, 0);
+       return  nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat)
@@ -2451,13 +2855,16 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
                .fh = fhandle,
                .bitmask = server->attr_bitmask,
        };
+       struct nfs4_fsinfo_res res = {
+               .fsinfo = fsinfo,
+       };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO],
                .rpc_argp = &args,
-               .rpc_resp = fsinfo,
+               .rpc_resp = &res,
        };
 
-       return rpc_call_sync(server->client, &msg, 0);
+       return nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
@@ -2486,10 +2893,13 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle
                .fh = fhandle,
                .bitmask = server->attr_bitmask,
        };
+       struct nfs4_pathconf_res res = {
+               .pathconf = pathconf,
+       };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF],
                .rpc_argp = &args,
-               .rpc_resp = pathconf,
+               .rpc_resp = &res,
        };
 
        /* None of the pathconf attributes are mandatory to implement */
@@ -2499,7 +2909,7 @@ static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle
        }
 
        nfs_fattr_init(pathconf->fattr);
-       return rpc_call_sync(server->client, &msg, 0);
+       return nfs4_call_sync(server, &msg, &args, &res, 0);
 }
 
 static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -2520,8 +2930,13 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
 {
        struct nfs_server *server = NFS_SERVER(data->inode);
 
+       dprintk("--> %s\n", __func__);
+
+       /* nfs4_sequence_free_slot called in the read rpc_call_done */
+       nfs4_sequence_done(server, &data->res.seq_res, task->tk_status);
+
        if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, server->nfs_client);
                return -EAGAIN;
        }
 
@@ -2541,8 +2956,12 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        struct inode *inode = data->inode;
        
+       /* slot is freed in nfs_writeback_done */
+       nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res,
+                          task->tk_status);
+
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
                return -EAGAIN;
        }
        if (task->tk_status >= 0) {
@@ -2567,10 +2986,14 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        struct inode *inode = data->inode;
        
+       nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res,
+                          task->tk_status);
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
                return -EAGAIN;
        }
+       nfs4_sequence_free_slot(NFS_SERVER(inode)->nfs_client,
+                               &data->res.seq_res);
        nfs_refresh_inode(inode, data->res.fattr);
        return 0;
 }
@@ -2603,6 +3026,9 @@ static void nfs4_renew_done(struct rpc_task *task, void *data)
        if (time_before(clp->cl_last_renewal,timestamp))
                clp->cl_last_renewal = timestamp;
        spin_unlock(&clp->cl_lock);
+       dprintk("%s calling put_rpccred on rpc_cred %p\n", __func__,
+                               task->tk_msg.rpc_cred);
+       put_rpccred(task->tk_msg.rpc_cred);
 }
 
 static const struct rpc_call_ops nfs4_renew_ops = {
@@ -2742,12 +3168,14 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
                .acl_pages = pages,
                .acl_len = buflen,
        };
-       size_t resp_len = buflen;
+       struct nfs_getaclres res = {
+               .acl_len = buflen,
+       };
        void *resp_buf;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
                .rpc_argp = &args,
-               .rpc_resp = &resp_len,
+               .rpc_resp = &res,
        };
        struct page *localpage = NULL;
        int ret;
@@ -2761,26 +3189,26 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
                        return -ENOMEM;
                args.acl_pages[0] = localpage;
                args.acl_pgbase = 0;
-               resp_len = args.acl_len = PAGE_SIZE;
+               args.acl_len = PAGE_SIZE;
        } else {
                resp_buf = buf;
                buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
        }
-       ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       ret = nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0);
        if (ret)
                goto out_free;
-       if (resp_len > args.acl_len)
-               nfs4_write_cached_acl(inode, NULL, resp_len);
+       if (res.acl_len > args.acl_len)
+               nfs4_write_cached_acl(inode, NULL, res.acl_len);
        else
-               nfs4_write_cached_acl(inode, resp_buf, resp_len);
+               nfs4_write_cached_acl(inode, resp_buf, res.acl_len);
        if (buf) {
                ret = -ERANGE;
-               if (resp_len > buflen)
+               if (res.acl_len > buflen)
                        goto out_free;
                if (localpage)
-                       memcpy(buf, resp_buf, resp_len);
+                       memcpy(buf, resp_buf, res.acl_len);
        }
-       ret = resp_len;
+       ret = res.acl_len;
 out_free:
        if (localpage)
                __free_page(localpage);
@@ -2827,10 +3255,11 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
                .acl_pages      = pages,
                .acl_len        = buflen,
        };
+       struct nfs_setaclres res;
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETACL],
                .rpc_argp       = &arg,
-               .rpc_resp       = NULL,
+               .rpc_resp       = &res,
        };
        int ret;
 
@@ -2838,7 +3267,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
                return -EOPNOTSUPP;
        nfs_inode_return_delegation(inode);
        buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
-       ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+       ret = nfs4_call_sync(server, &msg, &arg, &res, 1);
        nfs_access_zap_cache(inode);
        nfs_zap_acl_cache(inode);
        return ret;
@@ -2857,10 +3286,8 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
 }
 
 static int
-nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
+_nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs_client *clp, struct nfs4_state *state)
 {
-       struct nfs_client *clp = server->nfs_client;
-
        if (!clp || task->tk_status >= 0)
                return 0;
        switch(task->tk_status) {
@@ -2879,8 +3306,23 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                                rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
                        task->tk_status = 0;
                        return -EAGAIN;
+#if defined(CONFIG_NFS_V4_1)
+               case -NFS4ERR_BADSESSION:
+               case -NFS4ERR_BADSLOT:
+               case -NFS4ERR_BAD_HIGH_SLOT:
+               case -NFS4ERR_DEADSESSION:
+               case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+               case -NFS4ERR_SEQ_FALSE_RETRY:
+               case -NFS4ERR_SEQ_MISORDERED:
+                       dprintk("%s ERROR %d, Reset session\n", __func__,
+                               task->tk_status);
+                       set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
+                       task->tk_status = 0;
+                       return -EAGAIN;
+#endif /* CONFIG_NFS_V4_1 */
                case -NFS4ERR_DELAY:
-                       nfs_inc_server_stats(server, NFSIOS_DELAY);
+                       if (server)
+                               nfs_inc_server_stats(server, NFSIOS_DELAY);
                case -NFS4ERR_GRACE:
                        rpc_delay(task, NFS4_POLL_RETRY_MAX);
                        task->tk_status = 0;
@@ -2893,6 +3335,12 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
        return 0;
 }
 
+static int
+nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
+{
+       return _nfs4_async_handle_error(task, server, server->nfs_client, state);
+}
+
 int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
 {
        nfs4_verifier sc_verifier;
@@ -3000,6 +3448,10 @@ struct nfs4_delegreturndata {
 static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
 {
        struct nfs4_delegreturndata *data = calldata;
+
+       nfs4_sequence_done_free_slot(data->res.server, &data->res.seq_res,
+                                    task->tk_status);
+
        data->rpc_status = task->tk_status;
        if (data->rpc_status == 0)
                renew_lease(data->res.server, data->timestamp);
@@ -3010,7 +3462,25 @@ static void nfs4_delegreturn_release(void *calldata)
        kfree(calldata);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data)
+{
+       struct nfs4_delegreturndata *d_data;
+
+       d_data = (struct nfs4_delegreturndata *)data;
+
+       if (nfs4_setup_sequence(d_data->res.server->nfs_client,
+                               &d_data->args.seq_args,
+                               &d_data->res.seq_res, 1, task))
+               return;
+       rpc_call_start(task);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static const struct rpc_call_ops nfs4_delegreturn_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs4_delegreturn_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs4_delegreturn_done,
        .rpc_release = nfs4_delegreturn_release,
 };
@@ -3032,7 +3502,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
        };
        int status = 0;
 
-       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (data == NULL)
                return -ENOMEM;
        data->args.fhandle = &data->fh;
@@ -3042,6 +3512,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
        memcpy(&data->stateid, stateid, sizeof(data->stateid));
        data->res.fattr = &data->fattr;
        data->res.server = server;
+       data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        nfs_fattr_init(data->res.fattr);
        data->timestamp = jiffies;
        data->rpc_status = 0;
@@ -3127,7 +3598,7 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
                goto out;
        lsp = request->fl_u.nfs4_fl.owner;
        arg.lock_owner.id = lsp->ls_id.id;
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &arg, &res, 1);
        switch (status) {
                case 0:
                        request->fl_type = F_UNLCK;
@@ -3187,13 +3658,14 @@ static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl,
        struct nfs4_unlockdata *p;
        struct inode *inode = lsp->ls_state->inode;
 
-       p = kmalloc(sizeof(*p), GFP_KERNEL);
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
        if (p == NULL)
                return NULL;
        p->arg.fh = NFS_FH(inode);
        p->arg.fl = &p->fl;
        p->arg.seqid = seqid;
        p->res.seqid = seqid;
+       p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        p->arg.stateid = &lsp->ls_stateid;
        p->lsp = lsp;
        atomic_inc(&lsp->ls_count);
@@ -3217,6 +3689,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
 {
        struct nfs4_unlockdata *calldata = data;
 
+       nfs4_sequence_done(calldata->server, &calldata->res.seq_res,
+                          task->tk_status);
        if (RPC_ASSASSINATED(task))
                return;
        switch (task->tk_status) {
@@ -3233,8 +3707,11 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                        break;
                default:
                        if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
-                               rpc_restart_call(task);
+                               nfs4_restart_rpc(task,
+                                               calldata->server->nfs_client);
        }
+       nfs4_sequence_free_slot(calldata->server->nfs_client,
+                               &calldata->res.seq_res);
 }
 
 static void nfs4_locku_prepare(struct rpc_task *task, void *data)
@@ -3249,6 +3726,10 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data)
                return;
        }
        calldata->timestamp = jiffies;
+       if (nfs4_setup_sequence(calldata->server->nfs_client,
+                               &calldata->arg.seq_args,
+                               &calldata->res.seq_res, 1, task))
+               return;
        rpc_call_start(task);
 }
 
@@ -3341,6 +3822,7 @@ struct nfs4_lockdata {
        unsigned long timestamp;
        int rpc_status;
        int cancelled;
+       struct nfs_server *server;
 };
 
 static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
@@ -3366,7 +3848,9 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
        p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
        p->arg.lock_owner.id = lsp->ls_id.id;
        p->res.lock_seqid = p->arg.lock_seqid;
+       p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        p->lsp = lsp;
+       p->server = server;
        atomic_inc(&lsp->ls_count);
        p->ctx = get_nfs_open_context(ctx);
        memcpy(&p->fl, fl, sizeof(p->fl));
@@ -3396,6 +3880,9 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
        } else
                data->arg.new_lock_owner = 0;
        data->timestamp = jiffies;
+       if (nfs4_setup_sequence(data->server->nfs_client, &data->arg.seq_args,
+                               &data->res.seq_res, 1, task))
+               return;
        rpc_call_start(task);
        dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
 }
@@ -3406,6 +3893,9 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
 
        dprintk("%s: begin!\n", __func__);
 
+       nfs4_sequence_done_free_slot(data->server, &data->res.seq_res,
+                                    task->tk_status);
+
        data->rpc_status = task->tk_status;
        if (RPC_ASSASSINATED(task))
                goto out;
@@ -3706,10 +4196,13 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
                .page = page,
                .bitmask = bitmask,
        };
+       struct nfs4_fs_locations_res res = {
+               .fs_locations = fs_locations,
+       };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
                .rpc_argp = &args,
-               .rpc_resp = fs_locations,
+               .rpc_resp = &res,
        };
        int status;
 
@@ -3717,24 +4210,720 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
        nfs_fattr_init(&fs_locations->fattr);
        fs_locations->server = server;
        fs_locations->nlocations = 0;
-       status = rpc_call_sync(server->client, &msg, 0);
+       status = nfs4_call_sync(server, &msg, &args, &res, 0);
        nfs_fixup_referral_attributes(&fs_locations->fattr);
        dprintk("%s: returned status = %d\n", __func__, status);
        return status;
 }
 
-struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = {
+#ifdef CONFIG_NFS_V4_1
+/*
+ * nfs4_proc_exchange_id()
+ *
+ * Since the clientid has expired, all compounds using sessions
+ * associated with the stale clientid will be returning
+ * NFS4ERR_BADSESSION in the sequence operation, and will therefore
+ * be in some phase of session reset.
+ */
+static int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
+{
+       nfs4_verifier verifier;
+       struct nfs41_exchange_id_args args = {
+               .client = clp,
+               .flags = clp->cl_exchange_flags,
+       };
+       struct nfs41_exchange_id_res res = {
+               .client = clp,
+       };
+       int status;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+               .rpc_cred = cred,
+       };
+       __be32 *p;
+
+       dprintk("--> %s\n", __func__);
+       BUG_ON(clp == NULL);
+
+       p = (u32 *)verifier.data;
+       *p++ = htonl((u32)clp->cl_boot_time.tv_sec);
+       *p = htonl((u32)clp->cl_boot_time.tv_nsec);
+       args.verifier = &verifier;
+
+       while (1) {
+               args.id_len = scnprintf(args.id, sizeof(args.id),
+                                       "%s/%s %u",
+                                       clp->cl_ipaddr,
+                                       rpc_peeraddr2str(clp->cl_rpcclient,
+                                                        RPC_DISPLAY_ADDR),
+                                       clp->cl_id_uniquifier);
+
+               status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
+
+               if (status != NFS4ERR_CLID_INUSE)
+                       break;
+
+               if (signalled())
+                       break;
+
+               if (++clp->cl_id_uniquifier == 0)
+                       break;
+       }
+
+       dprintk("<-- %s status= %d\n", __func__, status);
+       return status;
+}
+
+struct nfs4_get_lease_time_data {
+       struct nfs4_get_lease_time_args *args;
+       struct nfs4_get_lease_time_res *res;
+       struct nfs_client *clp;
+};
+
+static void nfs4_get_lease_time_prepare(struct rpc_task *task,
+                                       void *calldata)
+{
+       int ret;
+       struct nfs4_get_lease_time_data *data =
+                       (struct nfs4_get_lease_time_data *)calldata;
+
+       dprintk("--> %s\n", __func__);
+       /* just setup sequence, do not trigger session recovery
+          since we're invoked within one */
+       ret = nfs41_setup_sequence(data->clp->cl_session,
+                                       &data->args->la_seq_args,
+                                       &data->res->lr_seq_res, 0, task);
+
+       BUG_ON(ret == -EAGAIN);
+       rpc_call_start(task);
+       dprintk("<-- %s\n", __func__);
+}
+
+/*
+ * Called from nfs4_state_manager thread for session setup, so don't recover
+ * from sequence operation or clientid errors.
+ */
+static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs4_get_lease_time_data *data =
+                       (struct nfs4_get_lease_time_data *)calldata;
+
+       dprintk("--> %s\n", __func__);
+       nfs41_sequence_done(data->clp, &data->res->lr_seq_res, task->tk_status);
+       switch (task->tk_status) {
+       case -NFS4ERR_DELAY:
+       case -NFS4ERR_GRACE:
+               dprintk("%s Retry: tk_status %d\n", __func__, task->tk_status);
+               rpc_delay(task, NFS4_POLL_RETRY_MIN);
+               task->tk_status = 0;
+               nfs4_restart_rpc(task, data->clp);
+               return;
+       }
+       nfs41_sequence_free_slot(data->clp, &data->res->lr_seq_res);
+       dprintk("<-- %s\n", __func__);
+}
+
+struct rpc_call_ops nfs4_get_lease_time_ops = {
+       .rpc_call_prepare = nfs4_get_lease_time_prepare,
+       .rpc_call_done = nfs4_get_lease_time_done,
+};
+
+int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
+{
+       struct rpc_task *task;
+       struct nfs4_get_lease_time_args args;
+       struct nfs4_get_lease_time_res res = {
+               .lr_fsinfo = fsinfo,
+       };
+       struct nfs4_get_lease_time_data data = {
+               .args = &args,
+               .res = &res,
+               .clp = clp,
+       };
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GET_LEASE_TIME],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+       struct rpc_task_setup task_setup = {
+               .rpc_client = clp->cl_rpcclient,
+               .rpc_message = &msg,
+               .callback_ops = &nfs4_get_lease_time_ops,
+               .callback_data = &data
+       };
+       int status;
+
+       res.lr_seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
+       dprintk("--> %s\n", __func__);
+       task = rpc_run_task(&task_setup);
+
+       if (IS_ERR(task))
+               status = PTR_ERR(task);
+       else {
+               status = task->tk_status;
+               rpc_put_task(task);
+       }
+       dprintk("<-- %s return %d\n", __func__, status);
+
+       return status;
+}
+
+/*
+ * Reset a slot table
+ */
+static int nfs4_reset_slot_table(struct nfs4_slot_table *tbl, int max_slots,
+               int old_max_slots, int ivalue)
+{
+       int i;
+       int ret = 0;
+
+       dprintk("--> %s: max_reqs=%u, tbl %p\n", __func__, max_slots, tbl);
+
+       /*
+        * Until we have dynamic slot table adjustment, insist
+        * upon the same slot table size
+        */
+       if (max_slots != old_max_slots) {
+               dprintk("%s reset slot table does't match old\n",
+                       __func__);
+               ret = -EINVAL; /*XXX NFS4ERR_REQ_TOO_BIG ? */
+               goto out;
+       }
+       spin_lock(&tbl->slot_tbl_lock);
+       for (i = 0; i < max_slots; ++i)
+               tbl->slots[i].seq_nr = ivalue;
+       tbl->highest_used_slotid = -1;
+       spin_unlock(&tbl->slot_tbl_lock);
+       dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
+               tbl, tbl->slots, tbl->max_slots);
+out:
+       dprintk("<-- %s: return %d\n", __func__, ret);
+       return ret;
+}
+
+/*
+ * Reset the forechannel and backchannel slot tables
+ */
+static int nfs4_reset_slot_tables(struct nfs4_session *session)
+{
+       int status;
+
+       status = nfs4_reset_slot_table(&session->fc_slot_table,
+                       session->fc_attrs.max_reqs,
+                       session->fc_slot_table.max_slots,
+                       1);
+       if (status)
+               return status;
+
+       status = nfs4_reset_slot_table(&session->bc_slot_table,
+                       session->bc_attrs.max_reqs,
+                       session->bc_slot_table.max_slots,
+                       0);
+       return status;
+}
+
+/* Destroy the slot table */
+static void nfs4_destroy_slot_tables(struct nfs4_session *session)
+{
+       if (session->fc_slot_table.slots != NULL) {
+               kfree(session->fc_slot_table.slots);
+               session->fc_slot_table.slots = NULL;
+       }
+       if (session->bc_slot_table.slots != NULL) {
+               kfree(session->bc_slot_table.slots);
+               session->bc_slot_table.slots = NULL;
+       }
+       return;
+}
+
+/*
+ * Initialize slot table
+ */
+static int nfs4_init_slot_table(struct nfs4_slot_table *tbl,
+               int max_slots, int ivalue)
+{
+       int i;
+       struct nfs4_slot *slot;
+       int ret = -ENOMEM;
+
+       BUG_ON(max_slots > NFS4_MAX_SLOT_TABLE);
+
+       dprintk("--> %s: max_reqs=%u\n", __func__, max_slots);
+
+       slot = kcalloc(max_slots, sizeof(struct nfs4_slot), GFP_KERNEL);
+       if (!slot)
+               goto out;
+       for (i = 0; i < max_slots; ++i)
+               slot[i].seq_nr = ivalue;
+       ret = 0;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       if (tbl->slots != NULL) {
+               spin_unlock(&tbl->slot_tbl_lock);
+               dprintk("%s: slot table already initialized. tbl=%p slots=%p\n",
+                       __func__, tbl, tbl->slots);
+               WARN_ON(1);
+               goto out_free;
+       }
+       tbl->max_slots = max_slots;
+       tbl->slots = slot;
+       tbl->highest_used_slotid = -1;  /* no slot is currently used */
+       spin_unlock(&tbl->slot_tbl_lock);
+       dprintk("%s: tbl=%p slots=%p max_slots=%d\n", __func__,
+               tbl, tbl->slots, tbl->max_slots);
+out:
+       dprintk("<-- %s: return %d\n", __func__, ret);
+       return ret;
+
+out_free:
+       kfree(slot);
+       goto out;
+}
+
+/*
+ * Initialize the forechannel and backchannel tables
+ */
+static int nfs4_init_slot_tables(struct nfs4_session *session)
+{
+       int status;
+
+       status = nfs4_init_slot_table(&session->fc_slot_table,
+                       session->fc_attrs.max_reqs, 1);
+       if (status)
+               return status;
+
+       status = nfs4_init_slot_table(&session->bc_slot_table,
+                       session->bc_attrs.max_reqs, 0);
+       if (status)
+               nfs4_destroy_slot_tables(session);
+
+       return status;
+}
+
+struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
+{
+       struct nfs4_session *session;
+       struct nfs4_slot_table *tbl;
+
+       session = kzalloc(sizeof(struct nfs4_session), GFP_KERNEL);
+       if (!session)
+               return NULL;
+
+       set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
+       /*
+        * The create session reply races with the server back
+        * channel probe. Mark the client NFS_CS_SESSION_INITING
+        * so that the client back channel can find the
+        * nfs_client struct
+        */
+       clp->cl_cons_state = NFS_CS_SESSION_INITING;
+
+       tbl = &session->fc_slot_table;
+       spin_lock_init(&tbl->slot_tbl_lock);
+       rpc_init_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table");
+
+       tbl = &session->bc_slot_table;
+       spin_lock_init(&tbl->slot_tbl_lock);
+       rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table");
+
+       session->clp = clp;
+       return session;
+}
+
+void nfs4_destroy_session(struct nfs4_session *session)
+{
+       nfs4_proc_destroy_session(session);
+       dprintk("%s Destroy backchannel for xprt %p\n",
+               __func__, session->clp->cl_rpcclient->cl_xprt);
+       xprt_destroy_backchannel(session->clp->cl_rpcclient->cl_xprt,
+                               NFS41_BC_MIN_CALLBACKS);
+       nfs4_destroy_slot_tables(session);
+       kfree(session);
+}
+
+/*
+ * Initialize the values to be used by the client in CREATE_SESSION
+ * If nfs4_init_session set the fore channel request and response sizes,
+ * use them.
+ *
+ * Set the back channel max_resp_sz_cached to zero to force the client to
+ * always set csa_cachethis to FALSE because the current implementation
+ * of the back channel DRC only supports caching the CB_SEQUENCE operation.
+ */
+static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
+{
+       struct nfs4_session *session = args->client->cl_session;
+       unsigned int mxrqst_sz = session->fc_attrs.max_rqst_sz,
+                    mxresp_sz = session->fc_attrs.max_resp_sz;
+
+       if (mxrqst_sz == 0)
+               mxrqst_sz = NFS_MAX_FILE_IO_SIZE;
+       if (mxresp_sz == 0)
+               mxresp_sz = NFS_MAX_FILE_IO_SIZE;
+       /* Fore channel attributes */
+       args->fc_attrs.headerpadsz = 0;
+       args->fc_attrs.max_rqst_sz = mxrqst_sz;
+       args->fc_attrs.max_resp_sz = mxresp_sz;
+       args->fc_attrs.max_resp_sz_cached = mxresp_sz;
+       args->fc_attrs.max_ops = NFS4_MAX_OPS;
+       args->fc_attrs.max_reqs = session->clp->cl_rpcclient->cl_xprt->max_reqs;
+
+       dprintk("%s: Fore Channel : max_rqst_sz=%u max_resp_sz=%u "
+               "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
+               __func__,
+               args->fc_attrs.max_rqst_sz, args->fc_attrs.max_resp_sz,
+               args->fc_attrs.max_resp_sz_cached, args->fc_attrs.max_ops,
+               args->fc_attrs.max_reqs);
+
+       /* Back channel attributes */
+       args->bc_attrs.headerpadsz = 0;
+       args->bc_attrs.max_rqst_sz = PAGE_SIZE;
+       args->bc_attrs.max_resp_sz = PAGE_SIZE;
+       args->bc_attrs.max_resp_sz_cached = 0;
+       args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS;
+       args->bc_attrs.max_reqs = 1;
+
+       dprintk("%s: Back Channel : max_rqst_sz=%u max_resp_sz=%u "
+               "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
+               __func__,
+               args->bc_attrs.max_rqst_sz, args->bc_attrs.max_resp_sz,
+               args->bc_attrs.max_resp_sz_cached, args->bc_attrs.max_ops,
+               args->bc_attrs.max_reqs);
+}
+
+static int _verify_channel_attr(char *chan, char *attr_name, u32 sent, u32 rcvd)
+{
+       if (rcvd <= sent)
+               return 0;
+       printk(KERN_WARNING "%s: Session INVALID: %s channel %s increased. "
+               "sent=%u rcvd=%u\n", __func__, chan, attr_name, sent, rcvd);
+       return -EINVAL;
+}
+
+#define _verify_fore_channel_attr(_name_) \
+       _verify_channel_attr("fore", #_name_, \
+                            args->fc_attrs._name_, \
+                            session->fc_attrs._name_)
+
+#define _verify_back_channel_attr(_name_) \
+       _verify_channel_attr("back", #_name_, \
+                            args->bc_attrs._name_, \
+                            session->bc_attrs._name_)
+
+/*
+ * The server is not allowed to increase the fore channel header pad size,
+ * maximum response size, or maximum number of operations.
+ *
+ * The back channel attributes are only negotiatied down: We send what the
+ * (back channel) server insists upon.
+ */
+static int nfs4_verify_channel_attrs(struct nfs41_create_session_args *args,
+                                    struct nfs4_session *session)
+{
+       int ret = 0;
+
+       ret |= _verify_fore_channel_attr(headerpadsz);
+       ret |= _verify_fore_channel_attr(max_resp_sz);
+       ret |= _verify_fore_channel_attr(max_ops);
+
+       ret |= _verify_back_channel_attr(headerpadsz);
+       ret |= _verify_back_channel_attr(max_rqst_sz);
+       ret |= _verify_back_channel_attr(max_resp_sz);
+       ret |= _verify_back_channel_attr(max_resp_sz_cached);
+       ret |= _verify_back_channel_attr(max_ops);
+       ret |= _verify_back_channel_attr(max_reqs);
+
+       return ret;
+}
+
+static int _nfs4_proc_create_session(struct nfs_client *clp)
+{
+       struct nfs4_session *session = clp->cl_session;
+       struct nfs41_create_session_args args = {
+               .client = clp,
+               .cb_program = NFS4_CALLBACK,
+       };
+       struct nfs41_create_session_res res = {
+               .client = clp,
+       };
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+       };
+       int status;
+
+       nfs4_init_channel_attrs(&args);
+       args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);
+
+       status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0);
+
+       if (!status)
+               /* Verify the session's negotiated channel_attrs values */
+               status = nfs4_verify_channel_attrs(&args, session);
+       if (!status) {
+               /* Increment the clientid slot sequence id */
+               clp->cl_seqid++;
+       }
+
+       return status;
+}
+
+/*
+ * Issues a CREATE_SESSION operation to the server.
+ * It is the responsibility of the caller to verify the session is
+ * expired before calling this routine.
+ */
+int nfs4_proc_create_session(struct nfs_client *clp, int reset)
+{
+       int status;
+       unsigned *ptr;
+       struct nfs_fsinfo fsinfo;
+       struct nfs4_session *session = clp->cl_session;
+
+       dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
+
+       status = _nfs4_proc_create_session(clp);
+       if (status)
+               goto out;
+
+       /* Init or reset the fore channel */
+       if (reset)
+               status = nfs4_reset_slot_tables(session);
+       else
+               status = nfs4_init_slot_tables(session);
+       dprintk("fore channel slot table initialization returned %d\n", status);
+       if (status)
+               goto out;
+
+       ptr = (unsigned *)&session->sess_id.data[0];
+       dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__,
+               clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]);
+
+       if (reset)
+               /* Lease time is aleady set */
+               goto out;
+
+       /* Get the lease time */
+       status = nfs4_proc_get_lease_time(clp, &fsinfo);
+       if (status == 0) {
+               /* Update lease time and schedule renewal */
+               spin_lock(&clp->cl_lock);
+               clp->cl_lease_time = fsinfo.lease_time * HZ;
+               clp->cl_last_renewal = jiffies;
+               clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+               spin_unlock(&clp->cl_lock);
+
+               nfs4_schedule_state_renewal(clp);
+       }
+out:
+       dprintk("<-- %s\n", __func__);
+       return status;
+}
+
+/*
+ * Issue the over-the-wire RPC DESTROY_SESSION.
+ * The caller must serialize access to this routine.
+ */
+int nfs4_proc_destroy_session(struct nfs4_session *session)
+{
+       int status = 0;
+       struct rpc_message msg;
+
+       dprintk("--> nfs4_proc_destroy_session\n");
+
+       /* session is still being setup */
+       if (session->clp->cl_cons_state != NFS_CS_READY)
+               return status;
+
+       msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION];
+       msg.rpc_argp = session;
+       msg.rpc_resp = NULL;
+       msg.rpc_cred = NULL;
+       status = rpc_call_sync(session->clp->cl_rpcclient, &msg, 0);
+
+       if (status)
+               printk(KERN_WARNING
+                       "Got error %d from the server on DESTROY_SESSION. "
+                       "Session has been destroyed regardless...\n", status);
+
+       dprintk("<-- nfs4_proc_destroy_session\n");
+       return status;
+}
+
+/*
+ * Renew the cl_session lease.
+ */
+static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred)
+{
+       struct nfs4_sequence_args args;
+       struct nfs4_sequence_res res;
+
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE],
+               .rpc_argp = &args,
+               .rpc_resp = &res,
+               .rpc_cred = cred,
+       };
+
+       args.sa_cache_this = 0;
+
+       return nfs4_call_sync_sequence(clp, clp->cl_rpcclient, &msg, &args,
+                                      &res, 0);
+}
+
+void nfs41_sequence_call_done(struct rpc_task *task, void *data)
+{
+       struct nfs_client *clp = (struct nfs_client *)data;
+
+       nfs41_sequence_done(clp, task->tk_msg.rpc_resp, task->tk_status);
+
+       if (task->tk_status < 0) {
+               dprintk("%s ERROR %d\n", __func__, task->tk_status);
+
+               if (_nfs4_async_handle_error(task, NULL, clp, NULL)
+                                                               == -EAGAIN) {
+                       nfs4_restart_rpc(task, clp);
+                       return;
+               }
+       }
+       nfs41_sequence_free_slot(clp, task->tk_msg.rpc_resp);
+       dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred);
+
+       put_rpccred(task->tk_msg.rpc_cred);
+       kfree(task->tk_msg.rpc_argp);
+       kfree(task->tk_msg.rpc_resp);
+
+       dprintk("<-- %s\n", __func__);
+}
+
+static void nfs41_sequence_prepare(struct rpc_task *task, void *data)
+{
+       struct nfs_client *clp;
+       struct nfs4_sequence_args *args;
+       struct nfs4_sequence_res *res;
+
+       clp = (struct nfs_client *)data;
+       args = task->tk_msg.rpc_argp;
+       res = task->tk_msg.rpc_resp;
+
+       if (nfs4_setup_sequence(clp, args, res, 0, task))
+               return;
+       rpc_call_start(task);
+}
+
+static const struct rpc_call_ops nfs41_sequence_ops = {
+       .rpc_call_done = nfs41_sequence_call_done,
+       .rpc_call_prepare = nfs41_sequence_prepare,
+};
+
+static int nfs41_proc_async_sequence(struct nfs_client *clp,
+                                    struct rpc_cred *cred)
+{
+       struct nfs4_sequence_args *args;
+       struct nfs4_sequence_res *res;
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE],
+               .rpc_cred = cred,
+       };
+
+       args = kzalloc(sizeof(*args), GFP_KERNEL);
+       if (!args)
+               return -ENOMEM;
+       res = kzalloc(sizeof(*res), GFP_KERNEL);
+       if (!res) {
+               kfree(args);
+               return -ENOMEM;
+       }
+       res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+       msg.rpc_argp = args;
+       msg.rpc_resp = res;
+
+       return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT,
+                             &nfs41_sequence_ops, (void *)clp);
+}
+
+#endif /* CONFIG_NFS_V4_1 */
+
+struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
        .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
        .state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
        .recover_open   = nfs4_open_reclaim,
        .recover_lock   = nfs4_lock_reclaim,
+       .establish_clid = nfs4_init_clientid,
+       .get_clid_cred  = nfs4_get_setclientid_cred,
 };
 
-struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = {
+#if defined(CONFIG_NFS_V4_1)
+struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
+       .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
+       .state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
+       .recover_open   = nfs4_open_reclaim,
+       .recover_lock   = nfs4_lock_reclaim,
+       .establish_clid = nfs4_proc_exchange_id,
+       .get_clid_cred  = nfs4_get_exchange_id_cred,
+};
+#endif /* CONFIG_NFS_V4_1 */
+
+struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
        .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
        .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
        .recover_open   = nfs4_open_expired,
        .recover_lock   = nfs4_lock_expired,
+       .establish_clid = nfs4_init_clientid,
+       .get_clid_cred  = nfs4_get_setclientid_cred,
+};
+
+#if defined(CONFIG_NFS_V4_1)
+struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = {
+       .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
+       .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
+       .recover_open   = nfs4_open_expired,
+       .recover_lock   = nfs4_lock_expired,
+       .establish_clid = nfs4_proc_exchange_id,
+       .get_clid_cred  = nfs4_get_exchange_id_cred,
+};
+#endif /* CONFIG_NFS_V4_1 */
+
+struct nfs4_state_maintenance_ops nfs40_state_renewal_ops = {
+       .sched_state_renewal = nfs4_proc_async_renew,
+       .get_state_renewal_cred_locked = nfs4_get_renew_cred_locked,
+       .renew_lease = nfs4_proc_renew,
+};
+
+#if defined(CONFIG_NFS_V4_1)
+struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = {
+       .sched_state_renewal = nfs41_proc_async_sequence,
+       .get_state_renewal_cred_locked = nfs4_get_machine_cred_locked,
+       .renew_lease = nfs4_proc_sequence,
+};
+#endif
+
+/*
+ * Per minor version reboot and network partition recovery ops
+ */
+
+struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[] = {
+       &nfs40_reboot_recovery_ops,
+#if defined(CONFIG_NFS_V4_1)
+       &nfs41_reboot_recovery_ops,
+#endif
+};
+
+struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[] = {
+       &nfs40_nograce_recovery_ops,
+#if defined(CONFIG_NFS_V4_1)
+       &nfs41_nograce_recovery_ops,
+#endif
+};
+
+struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[] = {
+       &nfs40_state_renewal_ops,
+#if defined(CONFIG_NFS_V4_1)
+       &nfs41_state_renewal_ops,
+#endif
 };
 
 static const struct inode_operations nfs4_file_inode_operations = {
index f524e932ff7b295f8bf0adce947f058f2d8ede73..e27c6cef18f291abb642141e762055bcaf85f4f6 100644 (file)
 void
 nfs4_renew_state(struct work_struct *work)
 {
+       struct nfs4_state_maintenance_ops *ops;
        struct nfs_client *clp =
                container_of(work, struct nfs_client, cl_renewd.work);
        struct rpc_cred *cred;
        long lease, timeout;
        unsigned long last, now;
 
+       ops = nfs4_state_renewal_ops[clp->cl_minorversion];
        dprintk("%s: start\n", __func__);
        /* Are there any active superblocks? */
        if (list_empty(&clp->cl_superblocks))
@@ -76,7 +78,7 @@ nfs4_renew_state(struct work_struct *work)
        timeout = (2 * lease) / 3 + (long)last - (long)now;
        /* Are we close to a lease timeout? */
        if (time_after(now, last + lease/3)) {
-               cred = nfs4_get_renew_cred_locked(clp);
+               cred = ops->get_state_renewal_cred_locked(clp);
                spin_unlock(&clp->cl_lock);
                if (cred == NULL) {
                        if (list_empty(&clp->cl_delegations)) {
@@ -86,7 +88,7 @@ nfs4_renew_state(struct work_struct *work)
                        nfs_expire_all_delegations(clp);
                } else {
                        /* Queue an asynchronous RENEW. */
-                       nfs4_proc_async_renew(clp, cred);
+                       ops->sched_state_renewal(clp, cred);
                        put_rpccred(cred);
                }
                timeout = (2 * lease) / 3;
index 0298e909559fdc67f0bc5e7bab885595743e90a1..2cfca9929c9a34cf6794eba185bf8892eb11267e 100644 (file)
@@ -60,7 +60,7 @@ const nfs4_stateid zero_stateid;
 
 static LIST_HEAD(nfs4_clientid_list);
 
-static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
+int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
 {
        unsigned short port;
        int status;
@@ -77,7 +77,7 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
        return status;
 }
 
-static struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
+struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
 {
        struct rpc_cred *cred = NULL;
 
@@ -114,17 +114,21 @@ struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
        return cred;
 }
 
-static struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
+#if defined(CONFIG_NFS_V4_1)
+
+struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp)
 {
        struct rpc_cred *cred;
 
        spin_lock(&clp->cl_lock);
-       cred = nfs4_get_renew_cred_locked(clp);
+       cred = nfs4_get_machine_cred_locked(clp);
        spin_unlock(&clp->cl_lock);
        return cred;
 }
 
-static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
+#endif /* CONFIG_NFS_V4_1 */
+
+struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
 {
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
@@ -738,12 +742,14 @@ static void nfs_increment_seqid(int status, struct nfs_seqid *seqid)
 
 void nfs_increment_open_seqid(int status, struct nfs_seqid *seqid)
 {
-       if (status == -NFS4ERR_BAD_SEQID) {
-               struct nfs4_state_owner *sp = container_of(seqid->sequence,
-                               struct nfs4_state_owner, so_seqid);
+       struct nfs4_state_owner *sp = container_of(seqid->sequence,
+                                       struct nfs4_state_owner, so_seqid);
+       struct nfs_server *server = sp->so_server;
+
+       if (status == -NFS4ERR_BAD_SEQID)
                nfs4_drop_state_owner(sp);
-       }
-       nfs_increment_seqid(status, seqid);
+       if (!nfs4_has_session(server->nfs_client))
+               nfs_increment_seqid(status, seqid);
 }
 
 /*
@@ -1042,6 +1048,14 @@ static void nfs4_recovery_handle_error(struct nfs_client *clp, int error)
                case -NFS4ERR_EXPIRED:
                        set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
                        nfs4_state_start_reclaim_nograce(clp);
+               case -NFS4ERR_BADSESSION:
+               case -NFS4ERR_BADSLOT:
+               case -NFS4ERR_BAD_HIGH_SLOT:
+               case -NFS4ERR_DEADSESSION:
+               case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+               case -NFS4ERR_SEQ_FALSE_RETRY:
+               case -NFS4ERR_SEQ_MISORDERED:
+                       set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
        }
 }
 
@@ -1075,18 +1089,22 @@ restart:
 static int nfs4_check_lease(struct nfs_client *clp)
 {
        struct rpc_cred *cred;
+       struct nfs4_state_maintenance_ops *ops =
+               nfs4_state_renewal_ops[clp->cl_minorversion];
        int status = -NFS4ERR_EXPIRED;
 
        /* Is the client already known to have an expired lease? */
        if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
                return 0;
-       cred = nfs4_get_renew_cred(clp);
+       spin_lock(&clp->cl_lock);
+       cred = ops->get_state_renewal_cred_locked(clp);
+       spin_unlock(&clp->cl_lock);
        if (cred == NULL) {
                cred = nfs4_get_setclientid_cred(clp);
                if (cred == NULL)
                        goto out;
        }
-       status = nfs4_proc_renew(clp, cred);
+       status = ops->renew_lease(clp, cred);
        put_rpccred(cred);
 out:
        nfs4_recovery_handle_error(clp, status);
@@ -1096,21 +1114,98 @@ out:
 static int nfs4_reclaim_lease(struct nfs_client *clp)
 {
        struct rpc_cred *cred;
+       struct nfs4_state_recovery_ops *ops =
+               nfs4_reboot_recovery_ops[clp->cl_minorversion];
        int status = -ENOENT;
 
-       cred = nfs4_get_setclientid_cred(clp);
+       cred = ops->get_clid_cred(clp);
        if (cred != NULL) {
-               status = nfs4_init_client(clp, cred);
+               status = ops->establish_clid(clp, cred);
                put_rpccred(cred);
                /* Handle case where the user hasn't set up machine creds */
                if (status == -EACCES && cred == clp->cl_machine_cred) {
                        nfs4_clear_machine_cred(clp);
                        status = -EAGAIN;
                }
+               if (status == -NFS4ERR_MINOR_VERS_MISMATCH)
+                       status = -EPROTONOSUPPORT;
        }
        return status;
 }
 
+#ifdef CONFIG_NFS_V4_1
+static void nfs4_session_recovery_handle_error(struct nfs_client *clp, int err)
+{
+       switch (err) {
+       case -NFS4ERR_STALE_CLIENTID:
+               set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+               set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
+       }
+}
+
+static int nfs4_reset_session(struct nfs_client *clp)
+{
+       int status;
+
+       status = nfs4_proc_destroy_session(clp->cl_session);
+       if (status && status != -NFS4ERR_BADSESSION &&
+           status != -NFS4ERR_DEADSESSION) {
+               nfs4_session_recovery_handle_error(clp, status);
+               goto out;
+       }
+
+       memset(clp->cl_session->sess_id.data, 0, NFS4_MAX_SESSIONID_LEN);
+       status = nfs4_proc_create_session(clp, 1);
+       if (status)
+               nfs4_session_recovery_handle_error(clp, status);
+               /* fall through*/
+out:
+       /* Wake up the next rpc task even on error */
+       rpc_wake_up_next(&clp->cl_session->fc_slot_table.slot_tbl_waitq);
+       return status;
+}
+
+static int nfs4_initialize_session(struct nfs_client *clp)
+{
+       int status;
+
+       status = nfs4_proc_create_session(clp, 0);
+       if (!status) {
+               nfs_mark_client_ready(clp, NFS_CS_READY);
+       } else if (status == -NFS4ERR_STALE_CLIENTID) {
+               set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+               set_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state);
+       } else {
+               nfs_mark_client_ready(clp, status);
+       }
+       return status;
+}
+#else /* CONFIG_NFS_V4_1 */
+static int nfs4_reset_session(struct nfs_client *clp) { return 0; }
+static int nfs4_initialize_session(struct nfs_client *clp) { return 0; }
+#endif /* CONFIG_NFS_V4_1 */
+
+/* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors
+ * on EXCHANGE_ID for v4.1
+ */
+static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
+{
+       if (nfs4_has_session(clp)) {
+               switch (status) {
+               case -NFS4ERR_DELAY:
+               case -NFS4ERR_CLID_INUSE:
+               case -EAGAIN:
+                       break;
+
+               case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
+                                        * in nfs4_exchange_id */
+               default:
+                       return;
+               }
+       }
+       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+}
+
 static void nfs4_state_manager(struct nfs_client *clp)
 {
        int status = 0;
@@ -1121,9 +1216,12 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        /* We're going to have to re-establish a clientid */
                        status = nfs4_reclaim_lease(clp);
                        if (status) {
-                               set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+                               nfs4_set_lease_expired(clp, status);
                                if (status == -EAGAIN)
                                        continue;
+                               if (clp->cl_cons_state ==
+                                                       NFS_CS_SESSION_INITING)
+                                       nfs_mark_client_ready(clp, status);
                                goto out_error;
                        }
                        clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
@@ -1134,25 +1232,44 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        if (status != 0)
                                continue;
                }
-
+               /* Initialize or reset the session */
+               if (nfs4_has_session(clp) &&
+                  test_and_clear_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state)) {
+                       if (clp->cl_cons_state == NFS_CS_SESSION_INITING)
+                               status = nfs4_initialize_session(clp);
+                       else
+                               status = nfs4_reset_session(clp);
+                       if (status) {
+                               if (status == -NFS4ERR_STALE_CLIENTID)
+                                       continue;
+                               goto out_error;
+                       }
+               }
                /* First recover reboot state... */
                if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
-                       status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops);
+                       status = nfs4_do_reclaim(clp,
+                               nfs4_reboot_recovery_ops[clp->cl_minorversion]);
                        if (status == -NFS4ERR_STALE_CLIENTID)
                                continue;
+                       if (test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state))
+                               continue;
                        nfs4_state_end_reclaim_reboot(clp);
                        continue;
                }
 
                /* Now recover expired state... */
                if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
-                       status = nfs4_do_reclaim(clp, &nfs4_nograce_recovery_ops);
+                       status = nfs4_do_reclaim(clp,
+                               nfs4_nograce_recovery_ops[clp->cl_minorversion]);
                        if (status < 0) {
                                set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
                                if (status == -NFS4ERR_STALE_CLIENTID)
                                        continue;
                                if (status == -NFS4ERR_EXPIRED)
                                        continue;
+                               if (test_bit(NFS4CLNT_SESSION_SETUP,
+                                                               &clp->cl_state))
+                                       continue;
                                goto out_error;
                        } else
                                nfs4_state_end_reclaim_nograce(clp);
index 1690f0e44b9169bf0d0277b98f1a43776b0bd18a..617273e7d47f56df133c44bc93449b369f97b70f 100644 (file)
@@ -192,12 +192,16 @@ static int nfs4_stat_to_errno(int);
                                 decode_verifier_maxsz)
 #define encode_remove_maxsz    (op_encode_hdr_maxsz + \
                                nfs4_name_maxsz)
+#define decode_remove_maxsz    (op_decode_hdr_maxsz + \
+                                decode_change_info_maxsz)
 #define encode_rename_maxsz    (op_encode_hdr_maxsz + \
                                2 * nfs4_name_maxsz)
-#define decode_rename_maxsz    (op_decode_hdr_maxsz + 5 + 5)
+#define decode_rename_maxsz    (op_decode_hdr_maxsz + \
+                                decode_change_info_maxsz + \
+                                decode_change_info_maxsz)
 #define encode_link_maxsz      (op_encode_hdr_maxsz + \
                                nfs4_name_maxsz)
-#define decode_link_maxsz      (op_decode_hdr_maxsz + 5)
+#define decode_link_maxsz      (op_decode_hdr_maxsz + decode_change_info_maxsz)
 #define encode_lock_maxsz      (op_encode_hdr_maxsz + \
                                 7 + \
                                 1 + encode_stateid_maxsz + 8)
@@ -240,43 +244,115 @@ static int nfs4_stat_to_errno(int);
                                (encode_getattr_maxsz)
 #define decode_fs_locations_maxsz \
                                (0)
+
+#if defined(CONFIG_NFS_V4_1)
+#define NFS4_MAX_MACHINE_NAME_LEN (64)
+
+#define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \
+                               encode_verifier_maxsz + \
+                               1 /* co_ownerid.len */ + \
+                               XDR_QUADLEN(NFS4_EXCHANGE_ID_LEN) + \
+                               1 /* flags */ + \
+                               1 /* spa_how */ + \
+                               0 /* SP4_NONE (for now) */ + \
+                               1 /* zero implemetation id array */)
+#define decode_exchange_id_maxsz (op_decode_hdr_maxsz + \
+                               2 /* eir_clientid */ + \
+                               1 /* eir_sequenceid */ + \
+                               1 /* eir_flags */ + \
+                               1 /* spr_how */ + \
+                               0 /* SP4_NONE (for now) */ + \
+                               2 /* eir_server_owner.so_minor_id */ + \
+                               /* eir_server_owner.so_major_id<> */ \
+                               XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \
+                               /* eir_server_scope<> */ \
+                               XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + 1 + \
+                               1 /* eir_server_impl_id array length */ + \
+                               0 /* ignored eir_server_impl_id contents */)
+#define encode_channel_attrs_maxsz  (6 + 1 /* ca_rdma_ird.len (0) */)
+#define decode_channel_attrs_maxsz  (6 + \
+                                    1 /* ca_rdma_ird.len */ + \
+                                    1 /* ca_rdma_ird */)
+#define encode_create_session_maxsz  (op_encode_hdr_maxsz + \
+                                    2 /* csa_clientid */ + \
+                                    1 /* csa_sequence */ + \
+                                    1 /* csa_flags */ + \
+                                    encode_channel_attrs_maxsz + \
+                                    encode_channel_attrs_maxsz + \
+                                    1 /* csa_cb_program */ + \
+                                    1 /* csa_sec_parms.len (1) */ + \
+                                    1 /* cb_secflavor (AUTH_SYS) */ + \
+                                    1 /* stamp */ + \
+                                    1 /* machinename.len */ + \
+                                    XDR_QUADLEN(NFS4_MAX_MACHINE_NAME_LEN) + \
+                                    1 /* uid */ + \
+                                    1 /* gid */ + \
+                                    1 /* gids.len (0) */)
+#define decode_create_session_maxsz  (op_decode_hdr_maxsz +    \
+                                    XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + \
+                                    1 /* csr_sequence */ + \
+                                    1 /* csr_flags */ + \
+                                    decode_channel_attrs_maxsz + \
+                                    decode_channel_attrs_maxsz)
+#define encode_destroy_session_maxsz    (op_encode_hdr_maxsz + 4)
+#define decode_destroy_session_maxsz    (op_decode_hdr_maxsz)
+#define encode_sequence_maxsz  (op_encode_hdr_maxsz + \
+                               XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 4)
+#define decode_sequence_maxsz  (op_decode_hdr_maxsz + \
+                               XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5)
+#else /* CONFIG_NFS_V4_1 */
+#define encode_sequence_maxsz  0
+#define decode_sequence_maxsz  0
+#endif /* CONFIG_NFS_V4_1 */
+
 #define NFS4_enc_compound_sz   (1024)  /* XXX: large enough? */
 #define NFS4_dec_compound_sz   (1024)  /* XXX: large enough? */
 #define NFS4_enc_read_sz       (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_read_maxsz)
 #define NFS4_dec_read_sz       (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_read_maxsz)
 #define NFS4_enc_readlink_sz   (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_readlink_maxsz)
 #define NFS4_dec_readlink_sz   (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_readlink_maxsz)
 #define NFS4_enc_readdir_sz    (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_readdir_maxsz)
 #define NFS4_dec_readdir_sz    (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_readdir_maxsz)
 #define NFS4_enc_write_sz      (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_write_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_write_sz      (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_write_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_commit_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_commit_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_commit_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_commit_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_open_sz        (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_savefh_maxsz + \
                                encode_open_maxsz + \
@@ -285,6 +361,7 @@ static int nfs4_stat_to_errno(int);
                                encode_restorefh_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_open_sz        (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_savefh_maxsz + \
                                decode_open_maxsz + \
@@ -301,43 +378,53 @@ static int nfs4_stat_to_errno(int);
                                 decode_putfh_maxsz + \
                                 decode_open_confirm_maxsz)
 #define NFS4_enc_open_noattr_sz        (compound_encode_hdr_maxsz + \
+                                       encode_sequence_maxsz + \
                                        encode_putfh_maxsz + \
                                        encode_open_maxsz + \
                                        encode_getattr_maxsz)
 #define NFS4_dec_open_noattr_sz        (compound_decode_hdr_maxsz + \
+                                       decode_sequence_maxsz + \
                                        decode_putfh_maxsz + \
                                        decode_open_maxsz + \
                                        decode_getattr_maxsz)
 #define NFS4_enc_open_downgrade_sz \
                                (compound_encode_hdr_maxsz + \
+                                encode_sequence_maxsz + \
                                 encode_putfh_maxsz + \
                                 encode_open_downgrade_maxsz + \
                                 encode_getattr_maxsz)
 #define NFS4_dec_open_downgrade_sz \
                                (compound_decode_hdr_maxsz + \
+                                decode_sequence_maxsz + \
                                 decode_putfh_maxsz + \
                                 decode_open_downgrade_maxsz + \
                                 decode_getattr_maxsz)
 #define NFS4_enc_close_sz      (compound_encode_hdr_maxsz + \
+                                encode_sequence_maxsz + \
                                 encode_putfh_maxsz + \
                                 encode_close_maxsz + \
                                 encode_getattr_maxsz)
 #define NFS4_dec_close_sz      (compound_decode_hdr_maxsz + \
+                                decode_sequence_maxsz + \
                                 decode_putfh_maxsz + \
                                 decode_close_maxsz + \
                                 decode_getattr_maxsz)
 #define NFS4_enc_setattr_sz    (compound_encode_hdr_maxsz + \
+                                encode_sequence_maxsz + \
                                 encode_putfh_maxsz + \
                                 encode_setattr_maxsz + \
                                 encode_getattr_maxsz)
 #define NFS4_dec_setattr_sz    (compound_decode_hdr_maxsz + \
+                                decode_sequence_maxsz + \
                                 decode_putfh_maxsz + \
                                 decode_setattr_maxsz + \
                                 decode_getattr_maxsz)
 #define NFS4_enc_fsinfo_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_fsinfo_maxsz)
 #define NFS4_dec_fsinfo_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_fsinfo_maxsz)
 #define NFS4_enc_renew_sz      (compound_encode_hdr_maxsz + \
@@ -359,64 +446,81 @@ static int nfs4_stat_to_errno(int);
                                decode_putrootfh_maxsz + \
                                decode_fsinfo_maxsz)
 #define NFS4_enc_lock_sz        (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_lock_maxsz)
 #define NFS4_dec_lock_sz        (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_lock_maxsz)
 #define NFS4_enc_lockt_sz       (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_lockt_maxsz)
 #define NFS4_dec_lockt_sz       (compound_decode_hdr_maxsz + \
+                                decode_sequence_maxsz + \
                                 decode_putfh_maxsz + \
                                 decode_lockt_maxsz)
 #define NFS4_enc_locku_sz       (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_locku_maxsz)
 #define NFS4_dec_locku_sz       (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_locku_maxsz)
 #define NFS4_enc_access_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_access_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_access_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_access_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_getattr_sz    (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_getattr_sz    (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_lookup_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_lookup_maxsz + \
                                encode_getattr_maxsz + \
                                encode_getfh_maxsz)
 #define NFS4_dec_lookup_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_lookup_maxsz + \
                                decode_getattr_maxsz + \
                                decode_getfh_maxsz)
 #define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putrootfh_maxsz + \
                                encode_getattr_maxsz + \
                                encode_getfh_maxsz)
 #define NFS4_dec_lookup_root_sz (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putrootfh_maxsz + \
                                decode_getattr_maxsz + \
                                decode_getfh_maxsz)
 #define NFS4_enc_remove_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_remove_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_remove_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
-                               op_decode_hdr_maxsz + 5 + \
+                               decode_remove_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_rename_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_savefh_maxsz + \
                                encode_putfh_maxsz + \
@@ -425,6 +529,7 @@ static int nfs4_stat_to_errno(int);
                                encode_restorefh_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_rename_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_savefh_maxsz + \
                                decode_putfh_maxsz + \
@@ -433,6 +538,7 @@ static int nfs4_stat_to_errno(int);
                                decode_restorefh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_link_sz       (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_savefh_maxsz + \
                                encode_putfh_maxsz + \
@@ -441,6 +547,7 @@ static int nfs4_stat_to_errno(int);
                                encode_restorefh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_dec_link_sz       (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_savefh_maxsz + \
                                decode_putfh_maxsz + \
@@ -449,16 +556,19 @@ static int nfs4_stat_to_errno(int);
                                decode_restorefh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_symlink_sz    (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_symlink_maxsz + \
                                encode_getattr_maxsz + \
                                encode_getfh_maxsz)
 #define NFS4_dec_symlink_sz    (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_symlink_maxsz + \
                                decode_getattr_maxsz + \
                                decode_getfh_maxsz)
 #define NFS4_enc_create_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_savefh_maxsz + \
                                encode_create_maxsz + \
@@ -467,6 +577,7 @@ static int nfs4_stat_to_errno(int);
                                encode_restorefh_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_create_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_savefh_maxsz + \
                                decode_create_maxsz + \
@@ -475,52 +586,98 @@ static int nfs4_stat_to_errno(int);
                                decode_restorefh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_pathconf_sz   (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_pathconf_sz   (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_statfs_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_statfs_maxsz)
 #define NFS4_dec_statfs_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_statfs_maxsz)
 #define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_delegreturn_sz        (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_delegreturn_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_delegreturn_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_getacl_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_getacl_maxsz)
 #define NFS4_dec_getacl_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_getacl_maxsz)
 #define NFS4_enc_setacl_sz     (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
                                encode_setacl_maxsz)
 #define NFS4_dec_setacl_sz     (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
                                decode_putfh_maxsz + \
                                decode_setacl_maxsz)
 #define NFS4_enc_fs_locations_sz \
                                (compound_encode_hdr_maxsz + \
+                                encode_sequence_maxsz + \
                                 encode_putfh_maxsz + \
                                 encode_lookup_maxsz + \
                                 encode_fs_locations_maxsz)
 #define NFS4_dec_fs_locations_sz \
                                (compound_decode_hdr_maxsz + \
+                                decode_sequence_maxsz + \
                                 decode_putfh_maxsz + \
                                 decode_lookup_maxsz + \
                                 decode_fs_locations_maxsz)
+#if defined(CONFIG_NFS_V4_1)
+#define NFS4_enc_exchange_id_sz \
+                               (compound_encode_hdr_maxsz + \
+                                encode_exchange_id_maxsz)
+#define NFS4_dec_exchange_id_sz \
+                               (compound_decode_hdr_maxsz + \
+                                decode_exchange_id_maxsz)
+#define NFS4_enc_create_session_sz \
+                               (compound_encode_hdr_maxsz + \
+                                encode_create_session_maxsz)
+#define NFS4_dec_create_session_sz \
+                               (compound_decode_hdr_maxsz + \
+                                decode_create_session_maxsz)
+#define NFS4_enc_destroy_session_sz    (compound_encode_hdr_maxsz + \
+                                        encode_destroy_session_maxsz)
+#define NFS4_dec_destroy_session_sz    (compound_decode_hdr_maxsz + \
+                                        decode_destroy_session_maxsz)
+#define NFS4_enc_sequence_sz \
+                               (compound_decode_hdr_maxsz + \
+                                encode_sequence_maxsz)
+#define NFS4_dec_sequence_sz \
+                               (compound_decode_hdr_maxsz + \
+                                decode_sequence_maxsz)
+#define NFS4_enc_get_lease_time_sz     (compound_encode_hdr_maxsz + \
+                                        encode_sequence_maxsz + \
+                                        encode_putrootfh_maxsz + \
+                                        encode_fsinfo_maxsz)
+#define NFS4_dec_get_lease_time_sz     (compound_decode_hdr_maxsz + \
+                                        decode_sequence_maxsz + \
+                                        decode_putrootfh_maxsz + \
+                                        decode_fsinfo_maxsz)
+#endif /* CONFIG_NFS_V4_1 */
 
 static const umode_t nfs_type2fmt[] = {
        [NF4BAD] = 0,
@@ -541,6 +698,8 @@ struct compound_hdr {
        __be32 *        nops_p;
        uint32_t        taglen;
        char *          tag;
+       uint32_t        replen;         /* expected reply words */
+       u32             minorversion;
 };
 
 /*
@@ -576,22 +735,31 @@ static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *
        xdr_encode_opaque(p, str, len);
 }
 
-static void encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
+static void encode_compound_hdr(struct xdr_stream *xdr,
+                               struct rpc_rqst *req,
+                               struct compound_hdr *hdr)
 {
        __be32 *p;
+       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
+
+       /* initialize running count of expected bytes in reply.
+        * NOTE: the replied tag SHOULD be the same is the one sent,
+        * but this is not required as a MUST for the server to do so. */
+       hdr->replen = RPC_REPHDRSIZE + auth->au_rslack + 3 + hdr->taglen;
 
        dprintk("encode_compound: tag=%.*s\n", (int)hdr->taglen, hdr->tag);
        BUG_ON(hdr->taglen > NFS4_MAXTAGLEN);
        RESERVE_SPACE(12+(XDR_QUADLEN(hdr->taglen)<<2));
        WRITE32(hdr->taglen);
        WRITEMEM(hdr->tag, hdr->taglen);
-       WRITE32(NFS4_MINOR_VERSION);
+       WRITE32(hdr->minorversion);
        hdr->nops_p = p;
        WRITE32(hdr->nops);
 }
 
 static void encode_nops(struct compound_hdr *hdr)
 {
+       BUG_ON(hdr->nops > NFS4_MAX_OPS);
        *hdr->nops_p = htonl(hdr->nops);
 }
 
@@ -736,6 +904,7 @@ static void encode_access(struct xdr_stream *xdr, u32 access, struct compound_hd
        WRITE32(OP_ACCESS);
        WRITE32(access);
        hdr->nops++;
+       hdr->replen += decode_access_maxsz;
 }
 
 static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
@@ -747,6 +916,7 @@ static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg
        WRITE32(arg->seqid->sequence->counter);
        WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
        hdr->nops++;
+       hdr->replen += decode_close_maxsz;
 }
 
 static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
@@ -758,6 +928,7 @@ static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *ar
        WRITE64(args->offset);
        WRITE32(args->count);
        hdr->nops++;
+       hdr->replen += decode_commit_maxsz;
 }
 
 static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create, struct compound_hdr *hdr)
@@ -789,6 +960,7 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
        WRITE32(create->name->len);
        WRITEMEM(create->name->name, create->name->len);
        hdr->nops++;
+       hdr->replen += decode_create_maxsz;
 
        encode_attrs(xdr, create->attrs, create->server);
 }
@@ -802,6 +974,7 @@ static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct c
        WRITE32(1);
        WRITE32(bitmap);
        hdr->nops++;
+       hdr->replen += decode_getattr_maxsz;
 }
 
 static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1, struct compound_hdr *hdr)
@@ -814,6 +987,7 @@ static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm
        WRITE32(bm0);
        WRITE32(bm1);
        hdr->nops++;
+       hdr->replen += decode_getattr_maxsz;
 }
 
 static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
@@ -841,6 +1015,7 @@ static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
        RESERVE_SPACE(4);
        WRITE32(OP_GETFH);
        hdr->nops++;
+       hdr->replen += decode_getfh_maxsz;
 }
 
 static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
@@ -852,6 +1027,7 @@ static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct
        WRITE32(name->len);
        WRITEMEM(name->name, name->len);
        hdr->nops++;
+       hdr->replen += decode_link_maxsz;
 }
 
 static inline int nfs4_lock_type(struct file_lock *fl, int block)
@@ -899,6 +1075,7 @@ static void encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args
                WRITE32(args->lock_seqid->sequence->counter);
        }
        hdr->nops++;
+       hdr->replen += decode_lock_maxsz;
 }
 
 static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args, struct compound_hdr *hdr)
@@ -915,6 +1092,7 @@ static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *ar
        WRITEMEM("lock id:", 8);
        WRITE64(args->lock_owner.id);
        hdr->nops++;
+       hdr->replen += decode_lockt_maxsz;
 }
 
 static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args, struct compound_hdr *hdr)
@@ -929,6 +1107,7 @@ static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *ar
        WRITE64(args->fl->fl_start);
        WRITE64(nfs4_lock_length(args->fl));
        hdr->nops++;
+       hdr->replen += decode_locku_maxsz;
 }
 
 static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
@@ -941,6 +1120,7 @@ static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struc
        WRITE32(len);
        WRITEMEM(name->name, len);
        hdr->nops++;
+       hdr->replen += decode_lookup_maxsz;
 }
 
 static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode)
@@ -1080,6 +1260,7 @@ static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg,
                BUG();
        }
        hdr->nops++;
+       hdr->replen += decode_open_maxsz;
 }
 
 static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg, struct compound_hdr *hdr)
@@ -1091,6 +1272,7 @@ static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_co
        WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
        WRITE32(arg->seqid->sequence->counter);
        hdr->nops++;
+       hdr->replen += decode_open_confirm_maxsz;
 }
 
 static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
@@ -1103,6 +1285,7 @@ static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_close
        WRITE32(arg->seqid->sequence->counter);
        encode_share_access(xdr, arg->fmode);
        hdr->nops++;
+       hdr->replen += decode_open_downgrade_maxsz;
 }
 
 static void
@@ -1116,6 +1299,7 @@ encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh, struct compound_hd
        WRITE32(len);
        WRITEMEM(fh->data, len);
        hdr->nops++;
+       hdr->replen += decode_putfh_maxsz;
 }
 
 static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
@@ -1125,6 +1309,7 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
        RESERVE_SPACE(4);
        WRITE32(OP_PUTROOTFH);
        hdr->nops++;
+       hdr->replen += decode_putrootfh_maxsz;
 }
 
 static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
@@ -1153,6 +1338,7 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args,
        WRITE64(args->offset);
        WRITE32(args->count);
        hdr->nops++;
+       hdr->replen += decode_read_maxsz;
 }
 
 static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr)
@@ -1178,6 +1364,7 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
        WRITE32(attrs[0] & readdir->bitmask[0]);
        WRITE32(attrs[1] & readdir->bitmask[1]);
        hdr->nops++;
+       hdr->replen += decode_readdir_maxsz;
        dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n",
                        __func__,
                        (unsigned long long)readdir->cookie,
@@ -1194,6 +1381,7 @@ static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *
        RESERVE_SPACE(4);
        WRITE32(OP_READLINK);
        hdr->nops++;
+       hdr->replen += decode_readlink_maxsz;
 }
 
 static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
@@ -1205,6 +1393,7 @@ static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struc
        WRITE32(name->len);
        WRITEMEM(name->name, name->len);
        hdr->nops++;
+       hdr->replen += decode_remove_maxsz;
 }
 
 static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname, struct compound_hdr *hdr)
@@ -1220,6 +1409,7 @@ static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, co
        WRITE32(newname->len);
        WRITEMEM(newname->name, newname->len);
        hdr->nops++;
+       hdr->replen += decode_rename_maxsz;
 }
 
 static void encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid, struct compound_hdr *hdr)
@@ -1230,6 +1420,7 @@ static void encode_renew(struct xdr_stream *xdr, const struct nfs_client *client
        WRITE32(OP_RENEW);
        WRITE64(client_stateid->cl_clientid);
        hdr->nops++;
+       hdr->replen += decode_renew_maxsz;
 }
 
 static void
@@ -1240,6 +1431,7 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
        RESERVE_SPACE(4);
        WRITE32(OP_RESTOREFH);
        hdr->nops++;
+       hdr->replen += decode_restorefh_maxsz;
 }
 
 static int
@@ -1259,6 +1451,7 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
        WRITE32(arg->acl_len);
        xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
        hdr->nops++;
+       hdr->replen += decode_setacl_maxsz;
        return 0;
 }
 
@@ -1270,6 +1463,7 @@ encode_savefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
        RESERVE_SPACE(4);
        WRITE32(OP_SAVEFH);
        hdr->nops++;
+       hdr->replen += decode_savefh_maxsz;
 }
 
 static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server, struct compound_hdr *hdr)
@@ -1280,6 +1474,7 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs
        WRITE32(OP_SETATTR);
        WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE);
        hdr->nops++;
+       hdr->replen += decode_setattr_maxsz;
        encode_attrs(xdr, arg->iap, server);
 }
 
@@ -1299,6 +1494,7 @@ static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclie
        RESERVE_SPACE(4);
        WRITE32(setclientid->sc_cb_ident);
        hdr->nops++;
+       hdr->replen += decode_setclientid_maxsz;
 }
 
 static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state, struct compound_hdr *hdr)
@@ -1310,6 +1506,7 @@ static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_
        WRITE64(client_state->cl_clientid);
        WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE);
        hdr->nops++;
+       hdr->replen += decode_setclientid_confirm_maxsz;
 }
 
 static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
@@ -1328,6 +1525,7 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg
 
        xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
        hdr->nops++;
+       hdr->replen += decode_write_maxsz;
 }
 
 static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid, struct compound_hdr *hdr)
@@ -1339,11 +1537,163 @@ static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *state
        WRITE32(OP_DELEGRETURN);
        WRITEMEM(stateid->data, NFS4_STATEID_SIZE);
        hdr->nops++;
+       hdr->replen += decode_delegreturn_maxsz;
+}
+
+#if defined(CONFIG_NFS_V4_1)
+/* NFSv4.1 operations */
+static void encode_exchange_id(struct xdr_stream *xdr,
+                              struct nfs41_exchange_id_args *args,
+                              struct compound_hdr *hdr)
+{
+       __be32 *p;
+
+       RESERVE_SPACE(4 + sizeof(args->verifier->data));
+       WRITE32(OP_EXCHANGE_ID);
+       WRITEMEM(args->verifier->data, sizeof(args->verifier->data));
+
+       encode_string(xdr, args->id_len, args->id);
+
+       RESERVE_SPACE(12);
+       WRITE32(args->flags);
+       WRITE32(0);     /* zero length state_protect4_a */
+       WRITE32(0);     /* zero length implementation id array */
+       hdr->nops++;
+       hdr->replen += decode_exchange_id_maxsz;
+}
+
+static void encode_create_session(struct xdr_stream *xdr,
+                                 struct nfs41_create_session_args *args,
+                                 struct compound_hdr *hdr)
+{
+       __be32 *p;
+       char machine_name[NFS4_MAX_MACHINE_NAME_LEN];
+       uint32_t len;
+       struct nfs_client *clp = args->client;
+
+       RESERVE_SPACE(4);
+       WRITE32(OP_CREATE_SESSION);
+
+       RESERVE_SPACE(8);
+       WRITE64(clp->cl_ex_clid);
+
+       RESERVE_SPACE(8);
+       WRITE32(clp->cl_seqid);                 /*Sequence id */
+       WRITE32(args->flags);                   /*flags */
+
+       RESERVE_SPACE(2*28);                    /* 2 channel_attrs */
+       /* Fore Channel */
+       WRITE32(args->fc_attrs.headerpadsz);    /* header padding size */
+       WRITE32(args->fc_attrs.max_rqst_sz);    /* max req size */
+       WRITE32(args->fc_attrs.max_resp_sz);    /* max resp size */
+       WRITE32(args->fc_attrs.max_resp_sz_cached);     /* Max resp sz cached */
+       WRITE32(args->fc_attrs.max_ops);        /* max operations */
+       WRITE32(args->fc_attrs.max_reqs);       /* max requests */
+       WRITE32(0);                             /* rdmachannel_attrs */
+
+       /* Back Channel */
+       WRITE32(args->fc_attrs.headerpadsz);    /* header padding size */
+       WRITE32(args->bc_attrs.max_rqst_sz);    /* max req size */
+       WRITE32(args->bc_attrs.max_resp_sz);    /* max resp size */
+       WRITE32(args->bc_attrs.max_resp_sz_cached);     /* Max resp sz cached */
+       WRITE32(args->bc_attrs.max_ops);        /* max operations */
+       WRITE32(args->bc_attrs.max_reqs);       /* max requests */
+       WRITE32(0);                             /* rdmachannel_attrs */
+
+       RESERVE_SPACE(4);
+       WRITE32(args->cb_program);              /* cb_program */
+
+       RESERVE_SPACE(4);                       /* # of security flavors */
+       WRITE32(1);
+
+       RESERVE_SPACE(4);
+       WRITE32(RPC_AUTH_UNIX);                 /* auth_sys */
+
+       /* authsys_parms rfc1831 */
+       RESERVE_SPACE(4);
+       WRITE32((u32)clp->cl_boot_time.tv_nsec);        /* stamp */
+       len = scnprintf(machine_name, sizeof(machine_name), "%s",
+                       clp->cl_ipaddr);
+       RESERVE_SPACE(16 + len);
+       WRITE32(len);
+       WRITEMEM(machine_name, len);
+       WRITE32(0);                             /* UID */
+       WRITE32(0);                             /* GID */
+       WRITE32(0);                             /* No more gids */
+       hdr->nops++;
+       hdr->replen += decode_create_session_maxsz;
+}
+
+static void encode_destroy_session(struct xdr_stream *xdr,
+                                  struct nfs4_session *session,
+                                  struct compound_hdr *hdr)
+{
+       __be32 *p;
+       RESERVE_SPACE(4 + NFS4_MAX_SESSIONID_LEN);
+       WRITE32(OP_DESTROY_SESSION);
+       WRITEMEM(session->sess_id.data, NFS4_MAX_SESSIONID_LEN);
+       hdr->nops++;
+       hdr->replen += decode_destroy_session_maxsz;
 }
+#endif /* CONFIG_NFS_V4_1 */
+
+static void encode_sequence(struct xdr_stream *xdr,
+                           const struct nfs4_sequence_args *args,
+                           struct compound_hdr *hdr)
+{
+#if defined(CONFIG_NFS_V4_1)
+       struct nfs4_session *session = args->sa_session;
+       struct nfs4_slot_table *tp;
+       struct nfs4_slot *slot;
+       __be32 *p;
+
+       if (!session)
+               return;
+
+       tp = &session->fc_slot_table;
+
+       WARN_ON(args->sa_slotid == NFS4_MAX_SLOT_TABLE);
+       slot = tp->slots + args->sa_slotid;
+
+       RESERVE_SPACE(4);
+       WRITE32(OP_SEQUENCE);
+
+       /*
+        * Sessionid + seqid + slotid + max slotid + cache_this
+        */
+       dprintk("%s: sessionid=%u:%u:%u:%u seqid=%d slotid=%d "
+               "max_slotid=%d cache_this=%d\n",
+               __func__,
+               ((u32 *)session->sess_id.data)[0],
+               ((u32 *)session->sess_id.data)[1],
+               ((u32 *)session->sess_id.data)[2],
+               ((u32 *)session->sess_id.data)[3],
+               slot->seq_nr, args->sa_slotid,
+               tp->highest_used_slotid, args->sa_cache_this);
+       RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 16);
+       WRITEMEM(session->sess_id.data, NFS4_MAX_SESSIONID_LEN);
+       WRITE32(slot->seq_nr);
+       WRITE32(args->sa_slotid);
+       WRITE32(tp->highest_used_slotid);
+       WRITE32(args->sa_cache_this);
+       hdr->nops++;
+       hdr->replen += decode_sequence_maxsz;
+#endif /* CONFIG_NFS_V4_1 */
+}
+
 /*
  * END OF "GENERIC" ENCODE ROUTINES.
  */
 
+static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args)
+{
+#if defined(CONFIG_NFS_V4_1)
+       if (args->sa_session)
+               return args->sa_session->clp->cl_minorversion;
+#endif /* CONFIG_NFS_V4_1 */
+       return 0;
+}
+
 /*
  * Encode an ACCESS request
  */
@@ -1351,11 +1701,12 @@ static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_access(&xdr, args->access, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1370,11 +1721,12 @@ static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->dir_fh, &hdr);
        encode_lookup(&xdr, args->name, &hdr);
        encode_getfh(&xdr, &hdr);
@@ -1390,11 +1742,12 @@ static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struc
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putrootfh(&xdr, &hdr);
        encode_getfh(&xdr, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1409,11 +1762,12 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_remove(&xdr, &args->name, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1428,11 +1782,12 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->old_dir, &hdr);
        encode_savefh(&xdr, &hdr);
        encode_putfh(&xdr, args->new_dir, &hdr);
@@ -1451,11 +1806,12 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_savefh(&xdr, &hdr);
        encode_putfh(&xdr, args->dir_fh, &hdr);
@@ -1474,11 +1830,12 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->dir_fh, &hdr);
        encode_savefh(&xdr, &hdr);
        encode_create(&xdr, args, &hdr);
@@ -1505,11 +1862,12 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
@@ -1523,11 +1881,12 @@ static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closea
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_close(&xdr, args, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1542,11 +1901,12 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openarg
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_savefh(&xdr, &hdr);
        encode_open(&xdr, args, &hdr);
@@ -1569,7 +1929,7 @@ static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, __be32 *p, struct nfs
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_open_confirm(&xdr, args, &hdr);
        encode_nops(&hdr);
@@ -1583,11 +1943,12 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_open(&xdr, args, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1602,11 +1963,12 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct n
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_open_downgrade(&xdr, args, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1621,11 +1983,12 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_ar
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_lock(&xdr, args, &hdr);
        encode_nops(&hdr);
@@ -1639,11 +2002,12 @@ static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_lockt(&xdr, args, &hdr);
        encode_nops(&hdr);
@@ -1657,11 +2021,12 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_locku(&xdr, args, &hdr);
        encode_nops(&hdr);
@@ -1675,22 +2040,16 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
-       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
-       unsigned int replen;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_readlink(&xdr, args, req, &hdr);
 
-       /* set up reply kvec
-        *    toplevel_status + taglen + rescount + OP_PUTFH + status
-        *      + OP_READLINK + status + string length = 8
-        */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readlink_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages,
+       xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages,
                        args->pgbase, args->pglen);
        encode_nops(&hdr);
        return 0;
@@ -1703,25 +2062,19 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
-       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
-       int replen;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_readdir(&xdr, args, req, &hdr);
 
-       /* set up reply kvec
-        *    toplevel_status + taglen + rescount + OP_PUTFH + status
-        *      + OP_READDIR + status + verifer(2)  = 9
-        */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readdir_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages,
+       xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages,
                         args->pgbase, args->count);
        dprintk("%s: inlined page args = (%u, %p, %u, %u)\n",
-                       __func__, replen, args->pages,
+                       __func__, hdr.replen << 2, args->pages,
                        args->pgbase, args->count);
        encode_nops(&hdr);
        return 0;
@@ -1732,24 +2085,18 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
  */
 static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
 {
-       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
-       int replen;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_read(&xdr, args, &hdr);
 
-       /* set up reply kvec
-        *    toplevel status + taglen=0 + rescount + OP_PUTFH + status
-        *       + OP_READ + status + eof + datalen = 9
-        */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_read_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen,
+       xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2,
                         args->pages, args->pgbase, args->count);
        req->rq_rcv_buf.flags |= XDRBUF_READ;
        encode_nops(&hdr);
@@ -1763,11 +2110,12 @@ static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_seta
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_setattr(&xdr, args, args->server, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1783,20 +2131,19 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p,
                struct nfs_getaclargs *args)
 {
        struct xdr_stream xdr;
-       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
-       int replen;
+       uint32_t replen;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
+       replen = hdr.replen + nfs4_fattr_bitmap_maxsz + 1;
        encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr);
 
-       /* set up reply buffer: */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_getacl_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen,
+       xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
                args->acl_pages, args->acl_pgbase, args->acl_len);
        encode_nops(&hdr);
        return 0;
@@ -1809,11 +2156,12 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writea
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_write(&xdr, args, &hdr);
        req->rq_snd_buf.flags |= XDRBUF_WRITE;
@@ -1829,11 +2177,12 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_write
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_commit(&xdr, args, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1848,11 +2197,12 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsin
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_fsinfo(&xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
@@ -1866,11 +2216,12 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct n
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_getattr_one(&xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0],
                           &hdr);
@@ -1885,11 +2236,12 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        encode_getattr_two(&xdr, args->bitmask[0] & nfs4_statfs_bitmap[0],
                           args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr);
@@ -1900,16 +2252,18 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs
 /*
  * GETATTR_BITMAP request
  */
-static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, const struct nfs_fh *fhandle)
+static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p,
+                                   struct nfs4_server_caps_arg *args)
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
-       encode_putfh(&xdr, fhandle, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
+       encode_putfh(&xdr, args->fhandle, &hdr);
        encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
                           FATTR4_WORD0_LINK_SUPPORT|
                           FATTR4_WORD0_SYMLINK_SUPPORT|
@@ -1929,7 +2283,7 @@ static int nfs4_xdr_enc_renew(struct rpc_rqst *req, __be32 *p, struct nfs_client
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
        encode_renew(&xdr, clp, &hdr);
        encode_nops(&hdr);
        return 0;
@@ -1946,7 +2300,7 @@ static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
        encode_setclientid(&xdr, sc, &hdr);
        encode_nops(&hdr);
        return 0;
@@ -1964,7 +2318,7 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str
        const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
        encode_setclientid_confirm(&xdr, clp, &hdr);
        encode_putrootfh(&xdr, &hdr);
        encode_fsinfo(&xdr, lease_bitmap, &hdr);
@@ -1979,11 +2333,12 @@ static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struc
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fhandle, &hdr);
        encode_delegreturn(&xdr, args->stateid, &hdr);
        encode_getfattr(&xdr, args->bitmask, &hdr);
@@ -1998,28 +2353,119 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
-       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
-       int replen;
+       uint32_t replen;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->dir_fh, &hdr);
        encode_lookup(&xdr, args->name, &hdr);
+       replen = hdr.replen;    /* get the attribute into args->page */
        encode_fs_locations(&xdr, args->bitmask, &hdr);
 
-       /* set up reply
-        *   toplevel_status + OP_PUTFH + status
-        *   + OP_LOOKUP + status + OP_GETATTR + status = 7
-        */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen, &args->page,
+       xdr_inline_pages(&req->rq_rcv_buf, replen << 2, &args->page,
                        0, PAGE_SIZE);
        encode_nops(&hdr);
        return 0;
 }
 
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * EXCHANGE_ID request
+ */
+static int nfs4_xdr_enc_exchange_id(struct rpc_rqst *req, uint32_t *p,
+                                   struct nfs41_exchange_id_args *args)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+               .minorversion = args->client->cl_minorversion,
+       };
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_exchange_id(&xdr, args, &hdr);
+       encode_nops(&hdr);
+       return 0;
+}
+
+/*
+ * a CREATE_SESSION request
+ */
+static int nfs4_xdr_enc_create_session(struct rpc_rqst *req, uint32_t *p,
+                                      struct nfs41_create_session_args *args)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+               .minorversion = args->client->cl_minorversion,
+       };
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_create_session(&xdr, args, &hdr);
+       encode_nops(&hdr);
+       return 0;
+}
+
+/*
+ * a DESTROY_SESSION request
+ */
+static int nfs4_xdr_enc_destroy_session(struct rpc_rqst *req, uint32_t *p,
+                                       struct nfs4_session *session)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+               .minorversion = session->clp->cl_minorversion,
+       };
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_destroy_session(&xdr, session, &hdr);
+       encode_nops(&hdr);
+       return 0;
+}
+
+/*
+ * a SEQUENCE request
+ */
+static int nfs4_xdr_enc_sequence(struct rpc_rqst *req, uint32_t *p,
+                                struct nfs4_sequence_args *args)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+               .minorversion = nfs4_xdr_minorversion(args),
+       };
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, args, &hdr);
+       encode_nops(&hdr);
+       return 0;
+}
+
+/*
+ * a GET_LEASE_TIME request
+ */
+static int nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req, uint32_t *p,
+                                      struct nfs4_get_lease_time_args *args)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+               .minorversion = nfs4_xdr_minorversion(&args->la_seq_args),
+       };
+       const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 };
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->la_seq_args, &hdr);
+       encode_putrootfh(&xdr, &hdr);
+       encode_fsinfo(&xdr, lease_bitmap, &hdr);
+       encode_nops(&hdr);
+       return 0;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 /*
  * START OF "GENERIC" DECODE ROUTINES.
  *   These may look a little ugly since they are imported from a "generic"
@@ -3657,7 +4103,7 @@ decode_savefh(struct xdr_stream *xdr)
        return decode_op_hdr(xdr, OP_SAVEFH);
 }
 
-static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res)
+static int decode_setattr(struct xdr_stream *xdr)
 {
        __be32 *p;
        uint32_t bmlen;
@@ -3735,6 +4181,169 @@ static int decode_delegreturn(struct xdr_stream *xdr)
        return decode_op_hdr(xdr, OP_DELEGRETURN);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+static int decode_exchange_id(struct xdr_stream *xdr,
+                             struct nfs41_exchange_id_res *res)
+{
+       __be32 *p;
+       uint32_t dummy;
+       int status;
+       struct nfs_client *clp = res->client;
+
+       status = decode_op_hdr(xdr, OP_EXCHANGE_ID);
+       if (status)
+               return status;
+
+       READ_BUF(8);
+       READ64(clp->cl_ex_clid);
+       READ_BUF(12);
+       READ32(clp->cl_seqid);
+       READ32(clp->cl_exchange_flags);
+
+       /* We ask for SP4_NONE */
+       READ32(dummy);
+       if (dummy != SP4_NONE)
+               return -EIO;
+
+       /* Throw away minor_id */
+       READ_BUF(8);
+
+       /* Throw away Major id */
+       READ_BUF(4);
+       READ32(dummy);
+       READ_BUF(dummy);
+
+       /* Throw away server_scope */
+       READ_BUF(4);
+       READ32(dummy);
+       READ_BUF(dummy);
+
+       /* Throw away Implementation id array */
+       READ_BUF(4);
+       READ32(dummy);
+       READ_BUF(dummy);
+
+       return 0;
+}
+
+static int decode_chan_attrs(struct xdr_stream *xdr,
+                            struct nfs4_channel_attrs *attrs)
+{
+       __be32 *p;
+       u32 nr_attrs;
+
+       READ_BUF(28);
+       READ32(attrs->headerpadsz);
+       READ32(attrs->max_rqst_sz);
+       READ32(attrs->max_resp_sz);
+       READ32(attrs->max_resp_sz_cached);
+       READ32(attrs->max_ops);
+       READ32(attrs->max_reqs);
+       READ32(nr_attrs);
+       if (unlikely(nr_attrs > 1)) {
+               printk(KERN_WARNING "%s: Invalid rdma channel attrs count %u\n",
+                       __func__, nr_attrs);
+               return -EINVAL;
+       }
+       if (nr_attrs == 1)
+               READ_BUF(4); /* skip rdma_attrs */
+       return 0;
+}
+
+static int decode_create_session(struct xdr_stream *xdr,
+                                struct nfs41_create_session_res *res)
+{
+       __be32 *p;
+       int status;
+       struct nfs_client *clp = res->client;
+       struct nfs4_session *session = clp->cl_session;
+
+       status = decode_op_hdr(xdr, OP_CREATE_SESSION);
+
+       if (status)
+               return status;
+
+       /* sessionid */
+       READ_BUF(NFS4_MAX_SESSIONID_LEN);
+       COPYMEM(&session->sess_id, NFS4_MAX_SESSIONID_LEN);
+
+       /* seqid, flags */
+       READ_BUF(8);
+       READ32(clp->cl_seqid);
+       READ32(session->flags);
+
+       /* Channel attributes */
+       status = decode_chan_attrs(xdr, &session->fc_attrs);
+       if (!status)
+               status = decode_chan_attrs(xdr, &session->bc_attrs);
+       return status;
+}
+
+static int decode_destroy_session(struct xdr_stream *xdr, void *dummy)
+{
+       return decode_op_hdr(xdr, OP_DESTROY_SESSION);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+static int decode_sequence(struct xdr_stream *xdr,
+                          struct nfs4_sequence_res *res,
+                          struct rpc_rqst *rqstp)
+{
+#if defined(CONFIG_NFS_V4_1)
+       struct nfs4_slot *slot;
+       struct nfs4_sessionid id;
+       u32 dummy;
+       int status;
+       __be32 *p;
+
+       if (!res->sr_session)
+               return 0;
+
+       status = decode_op_hdr(xdr, OP_SEQUENCE);
+       if (status)
+               goto out_err;
+
+       /*
+        * If the server returns different values for sessionID, slotID or
+        * sequence number, the server is looney tunes.
+        */
+       status = -ESERVERFAULT;
+
+       slot = &res->sr_session->fc_slot_table.slots[res->sr_slotid];
+       READ_BUF(NFS4_MAX_SESSIONID_LEN + 20);
+       COPYMEM(id.data, NFS4_MAX_SESSIONID_LEN);
+       if (memcmp(id.data, res->sr_session->sess_id.data,
+                  NFS4_MAX_SESSIONID_LEN)) {
+               dprintk("%s Invalid session id\n", __func__);
+               goto out_err;
+       }
+       /* seqid */
+       READ32(dummy);
+       if (dummy != slot->seq_nr) {
+               dprintk("%s Invalid sequence number\n", __func__);
+               goto out_err;
+       }
+       /* slot id */
+       READ32(dummy);
+       if (dummy != res->sr_slotid) {
+               dprintk("%s Invalid slot id\n", __func__);
+               goto out_err;
+       }
+       /* highest slot id - currently not processed */
+       READ32(dummy);
+       /* target highest slot id - currently not processed */
+       READ32(dummy);
+       /* result flags - currently not processed */
+       READ32(dummy);
+       status = 0;
+out_err:
+       res->sr_status = status;
+       return status;
+#else  /* CONFIG_NFS_V4_1 */
+       return 0;
+#endif /* CONFIG_NFS_V4_1 */
+}
+
 /*
  * END OF "GENERIC" DECODE ROUTINES.
  */
@@ -3750,6 +4359,9 @@ static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, __be32 *p, struct
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -3773,7 +4385,11 @@ static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_ac
        int status;
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       if (status)
                goto out;
        status = decode_putfh(&xdr);
        if (status != 0)
@@ -3796,7 +4412,11 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_lo
        int status;
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       if (status)
                goto out;
        if ((status = decode_putfh(&xdr)) != 0)
                goto out;
@@ -3819,7 +4439,11 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, __be32 *p, struct nf
        int status;
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       if (status)
                goto out;
        if ((status = decode_putrootfh(&xdr)) != 0)
                goto out;
@@ -3839,7 +4463,11 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_rem
        int status;
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       if (status)
                goto out;
        if ((status = decode_putfh(&xdr)) != 0)
                goto out;
@@ -3860,7 +4488,11 @@ static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_re
        int status;
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       if (status)
                goto out;
        if ((status = decode_putfh(&xdr)) != 0)
                goto out;
@@ -3890,7 +4522,11 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_link
        int status;
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       if (status)
                goto out;
        if ((status = decode_putfh(&xdr)) != 0)
                goto out;
@@ -3923,7 +4559,11 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_cr
        int status;
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       if (status)
                goto out;
        if ((status = decode_putfh(&xdr)) != 0)
                goto out;
@@ -3961,6 +4601,9 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -3979,12 +4622,13 @@ nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 0,
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
        int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, &hdr);
+       encode_compound_hdr(&xdr, req, &hdr);
+       encode_sequence(&xdr, &args->seq_args, &hdr);
        encode_putfh(&xdr, args->fh, &hdr);
        status = encode_setacl(&xdr, args, &hdr);
        encode_nops(&hdr);
@@ -3995,7 +4639,8 @@ nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args
  * Decode SETACL response
  */
 static int
-nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, __be32 *p, void *res)
+nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, __be32 *p,
+                   struct nfs_setaclres *res)
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr;
@@ -4003,12 +4648,15 @@ nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, __be32 *p, void *res)
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
        if (status)
                goto out;
-       status = decode_setattr(&xdr, res);
+       status = decode_setattr(&xdr);
 out:
        return status;
 }
@@ -4017,7 +4665,8 @@ out:
  * Decode GETACL response
  */
 static int
-nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, __be32 *p, size_t *acl_len)
+nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, __be32 *p,
+                   struct nfs_getaclres *res)
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr;
@@ -4025,12 +4674,15 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, __be32 *p, size_t *acl_len)
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
        if (status)
                goto out;
-       status = decode_getacl(&xdr, rqstp, acl_len);
+       status = decode_getacl(&xdr, rqstp, &res->acl_len);
 
 out:
        return status;
@@ -4047,6 +4699,9 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_clos
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4077,6 +4732,9 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openr
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4131,6 +4789,9 @@ static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp, __be32 *p, struct nf
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4155,12 +4816,15 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_se
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
        if (status)
                goto out;
-       status = decode_setattr(&xdr, res);
+       status = decode_setattr(&xdr);
        if (status)
                goto out;
        decode_getfattr(&xdr, res->fattr, res->server);
@@ -4179,6 +4843,9 @@ static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, __be32 *p, struct nfs_lock_
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4200,6 +4867,9 @@ static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, __be32 *p, struct nfs_lock
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4221,6 +4891,9 @@ static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, __be32 *p, struct nfs_lock
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4234,7 +4907,8 @@ out:
 /*
  * Decode READLINK response
  */
-static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, __be32 *p, void *res)
+static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, __be32 *p,
+                                struct nfs4_readlink_res *res)
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr;
@@ -4242,6 +4916,9 @@ static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, __be32 *p, void *res)
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4263,6 +4940,9 @@ static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_r
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4284,6 +4964,9 @@ static int nfs4_xdr_dec_read(struct rpc_rqst *rqstp, __be32 *p, struct nfs_readr
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4307,6 +4990,9 @@ static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, __be32 *p, struct nfs_writ
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4333,6 +5019,9 @@ static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, __be32 *p, struct nfs_wri
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
        status = decode_putfh(&xdr);
@@ -4349,7 +5038,8 @@ out:
 /*
  * FSINFO request
  */
-static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *fsinfo)
+static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p,
+                              struct nfs4_fsinfo_res *res)
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr;
@@ -4357,17 +5047,20 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinf
 
        xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (!status)
+               status = decode_sequence(&xdr, &res->seq_res, req);
        if (!status)
                status = decode_putfh(&xdr);
        if (!status)
-               status = decode_fsinfo(&xdr, fsinfo);
+               status = decode_fsinfo(&xdr, res->fsinfo);
        return status;
 }
 
 /*
  * PATHCONF request
  */
-static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *pathconf)
+static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p,
+                                struct nfs4_pathconf_res *res)
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr;
@@ -4375,17 +5068,20 @@ static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p, struct nfs_pat
 
        xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (!status)
+               status = decode_sequence(&xdr, &res->seq_res, req);
        if (!status)
                status = decode_putfh(&xdr);
        if (!status)
-               status = decode_pathconf(&xdr, pathconf);
+               status = decode_pathconf(&xdr, res->pathconf);
        return status;
 }
 
 /*
  * STATFS request
  */
-static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *fsstat)
+static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, __be32 *p,
+                              struct nfs4_statfs_res *res)
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr;
@@ -4393,10 +5089,12 @@ static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, __be32 *p, struct nfs_fssta
 
        xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
+       if (!status)
+               status = decode_sequence(&xdr, &res->seq_res, req);
        if (!status)
                status = decode_putfh(&xdr);
        if (!status)
-               status = decode_statfs(&xdr, fsstat);
+               status = decode_statfs(&xdr, res->fsstat);
        return status;
 }
 
@@ -4410,7 +5108,11 @@ static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req, __be32 *p, struct nfs4
        int status;
 
        xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, req);
+       if (status)
                goto out;
        if ((status = decode_putfh(&xdr)) != 0)
                goto out;
@@ -4483,7 +5185,10 @@ static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp, __be32 *p, struct nf
 
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
-       if (status != 0)
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       if (status)
                goto out;
        status = decode_putfh(&xdr);
        if (status != 0)
@@ -4497,7 +5202,8 @@ out:
 /*
  * FS_LOCATIONS request
  */
-static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs4_fs_locations *res)
+static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p,
+                                    struct nfs4_fs_locations_res *res)
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr;
@@ -4505,18 +5211,113 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
 
        xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
-       if (status != 0)
+       if (status)
+               goto out;
+       status = decode_sequence(&xdr, &res->seq_res, req);
+       if (status)
                goto out;
        if ((status = decode_putfh(&xdr)) != 0)
                goto out;
        if ((status = decode_lookup(&xdr)) != 0)
                goto out;
        xdr_enter_page(&xdr, PAGE_SIZE);
-       status = decode_getfattr(&xdr, &res->fattr, res->server);
+       status = decode_getfattr(&xdr, &res->fs_locations->fattr,
+                                res->fs_locations->server);
 out:
        return status;
 }
 
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * EXCHANGE_ID request
+ */
+static int nfs4_xdr_dec_exchange_id(struct rpc_rqst *rqstp, uint32_t *p,
+                                   void *res)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (!status)
+               status = decode_exchange_id(&xdr, res);
+       return status;
+}
+
+/*
+ * a CREATE_SESSION request
+ */
+static int nfs4_xdr_dec_create_session(struct rpc_rqst *rqstp, uint32_t *p,
+                                      struct nfs41_create_session_res *res)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (!status)
+               status = decode_create_session(&xdr, res);
+       return status;
+}
+
+/*
+ * a DESTROY_SESSION request
+ */
+static int nfs4_xdr_dec_destroy_session(struct rpc_rqst *rqstp, uint32_t *p,
+                                       void *dummy)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (!status)
+               status = decode_destroy_session(&xdr, dummy);
+       return status;
+}
+
+/*
+ * a SEQUENCE request
+ */
+static int nfs4_xdr_dec_sequence(struct rpc_rqst *rqstp, uint32_t *p,
+                                struct nfs4_sequence_res *res)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (!status)
+               status = decode_sequence(&xdr, res, rqstp);
+       return status;
+}
+
+/*
+ * a GET_LEASE_TIME request
+ */
+static int nfs4_xdr_dec_get_lease_time(struct rpc_rqst *rqstp, uint32_t *p,
+                                      struct nfs4_get_lease_time_res *res)
+{
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (!status)
+               status = decode_sequence(&xdr, &res->lr_seq_res, rqstp);
+       if (!status)
+               status = decode_putrootfh(&xdr);
+       if (!status)
+               status = decode_fsinfo(&xdr, res->lr_fsinfo);
+       return status;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
 {
        uint32_t bitmap[2] = {0};
@@ -4686,6 +5487,13 @@ struct rpc_procinfo      nfs4_procedures[] = {
   PROC(GETACL,         enc_getacl,     dec_getacl),
   PROC(SETACL,         enc_setacl,     dec_setacl),
   PROC(FS_LOCATIONS,   enc_fs_locations, dec_fs_locations),
+#if defined(CONFIG_NFS_V4_1)
+  PROC(EXCHANGE_ID,    enc_exchange_id,        dec_exchange_id),
+  PROC(CREATE_SESSION, enc_create_session,     dec_create_session),
+  PROC(DESTROY_SESSION,        enc_destroy_session,    dec_destroy_session),
+  PROC(SEQUENCE,       enc_sequence,   dec_sequence),
+  PROC(GET_LEASE_TIME, enc_get_lease_time,     dec_get_lease_time),
+#endif /* CONFIG_NFS_V4_1 */
 };
 
 struct rpc_version             nfs_version4 = {
index 4ace3c50a8ebae5dc010084b7dc479cc8664e834..96c4ebfa46f4f8892b125fcf59bdb300cc2f01ad 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <asm/system.h>
 
+#include "nfs4_fs.h"
 #include "internal.h"
 #include "iostat.h"
 #include "fscache.h"
@@ -46,6 +47,7 @@ struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
                memset(p, 0, sizeof(*p));
                INIT_LIST_HEAD(&p->pages);
                p->npages = pagecount;
+               p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
                if (pagecount <= ARRAY_SIZE(p->page_array))
                        p->pagevec = p->page_array;
                else {
@@ -357,19 +359,25 @@ static void nfs_readpage_retry(struct rpc_task *task, struct nfs_read_data *data
        struct nfs_readres *resp = &data->res;
 
        if (resp->eof || resp->count == argp->count)
-               return;
+               goto out;
 
        /* This is a short read! */
        nfs_inc_stats(data->inode, NFSIOS_SHORTREAD);
        /* Has the server at least made some progress? */
        if (resp->count == 0)
-               return;
+               goto out;
 
        /* Yes, so retry the read at the end of the data */
        argp->offset += resp->count;
        argp->pgbase += resp->count;
        argp->count -= resp->count;
-       rpc_restart_call(task);
+       nfs4_restart_rpc(task, NFS_SERVER(data->inode)->nfs_client);
+       return;
+out:
+       nfs4_sequence_free_slot(NFS_SERVER(data->inode)->nfs_client,
+                               &data->res.seq_res);
+       return;
+
 }
 
 /*
@@ -406,7 +414,23 @@ static void nfs_readpage_release_partial(void *calldata)
        nfs_readdata_release(calldata);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+void nfs_read_prepare(struct rpc_task *task, void *calldata)
+{
+       struct nfs_read_data *data = calldata;
+
+       if (nfs4_setup_sequence(NFS_SERVER(data->inode)->nfs_client,
+                               &data->args.seq_args, &data->res.seq_res,
+                               0, task))
+               return;
+       rpc_call_start(task);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static const struct rpc_call_ops nfs_read_partial_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_read_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs_readpage_result_partial,
        .rpc_release = nfs_readpage_release_partial,
 };
@@ -470,6 +494,9 @@ static void nfs_readpage_release_full(void *calldata)
 }
 
 static const struct rpc_call_ops nfs_read_full_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_read_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs_readpage_result_full,
        .rpc_release = nfs_readpage_release_full,
 };
index 26127b69a2755c66e3713a119259051f018745bc..6063054455f8587785cc3e65c31b1c68420f73e8 100644 (file)
@@ -90,6 +90,7 @@ enum {
        Opt_mountport,
        Opt_mountvers,
        Opt_nfsvers,
+       Opt_minorversion,
 
        /* Mount options that take string arguments */
        Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
@@ -155,6 +156,7 @@ static const match_table_t nfs_mount_option_tokens = {
        { Opt_mountvers, "mountvers=%u" },
        { Opt_nfsvers, "nfsvers=%u" },
        { Opt_nfsvers, "vers=%u" },
+       { Opt_minorversion, "minorversion=%u" },
 
        { Opt_sec, "sec=%s" },
        { Opt_proto, "proto=%s" },
@@ -1211,6 +1213,13 @@ static int nfs_parse_mount_options(char *raw,
                                nfs_parse_invalid_value("nfsvers");
                        }
                        break;
+               case Opt_minorversion:
+                       if (match_int(args, &option))
+                               return 0;
+                       if (option < 0 || option > NFS4_MAX_MINOR_VERSION)
+                               return 0;
+                       mnt->minorversion = option;
+                       break;
 
                /*
                 * options that take text values
@@ -2263,6 +2272,7 @@ static int nfs4_validate_mount_data(void *options,
        args->nfs_server.port   = NFS_PORT; /* 2049 unless user set port= */
        args->auth_flavors[0]   = RPC_AUTH_UNIX;
        args->auth_flavor_len   = 0;
+       args->minorversion      = 0;
 
        switch (data->version) {
        case 1:
@@ -2477,12 +2487,13 @@ static void nfs4_kill_super(struct super_block *sb)
 {
        struct nfs_server *server = NFS_SB(sb);
 
+       dprintk("--> %s\n", __func__);
        nfs_super_return_all_delegations(sb);
        kill_anon_super(sb);
-
        nfs4_renewd_prepare_shutdown(server);
        nfs_fscache_release_super_cookie(sb);
        nfs_free_server(server);
+       dprintk("<-- %s\n", __func__);
 }
 
 /*
index ecc295347775e2b2f31a508da1172e307898d234..1064c91ae810c3496f621729cbc754653be69a8e 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/wait.h>
 
 #include "internal.h"
+#include "nfs4_fs.h"
 
 struct nfs_unlinkdata {
        struct hlist_node list;
@@ -82,7 +83,7 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata)
        struct inode *dir = data->dir;
 
        if (!NFS_PROTO(dir)->unlink_done(task, dir))
-               rpc_restart_call(task);
+               nfs4_restart_rpc(task, NFS_SERVER(dir)->nfs_client);
 }
 
 /**
@@ -102,9 +103,25 @@ static void nfs_async_unlink_release(void *calldata)
        nfs_sb_deactive(sb);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+void nfs_unlink_prepare(struct rpc_task *task, void *calldata)
+{
+       struct nfs_unlinkdata *data = calldata;
+       struct nfs_server *server = NFS_SERVER(data->dir);
+
+       if (nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
+                               &data->res.seq_res, 1, task))
+               return;
+       rpc_call_start(task);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static const struct rpc_call_ops nfs_unlink_ops = {
        .rpc_call_done = nfs_async_unlink_done,
        .rpc_release = nfs_async_unlink_release,
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_unlink_prepare,
+#endif /* CONFIG_NFS_V4_1 */
 };
 
 static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
@@ -241,6 +258,7 @@ nfs_async_unlink(struct inode *dir, struct dentry *dentry)
                status = PTR_ERR(data->cred);
                goto out_free;
        }
+       data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
 
        status = -EBUSY;
        spin_lock(&dentry->d_lock);
index e560a78995a3cf5027a01df5168060183ef49547..ce728829f79a5dce2b238ccbe763631adec71343 100644 (file)
@@ -25,6 +25,7 @@
 #include "delegation.h"
 #include "internal.h"
 #include "iostat.h"
+#include "nfs4_fs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_PAGECACHE
 
@@ -52,6 +53,7 @@ struct nfs_write_data *nfs_commitdata_alloc(void)
        if (p) {
                memset(p, 0, sizeof(*p));
                INIT_LIST_HEAD(&p->pages);
+               p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
        }
        return p;
 }
@@ -71,6 +73,7 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
                memset(p, 0, sizeof(*p));
                INIT_LIST_HEAD(&p->pages);
                p->npages = pagecount;
+               p->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
                if (pagecount <= ARRAY_SIZE(p->page_array))
                        p->pagevec = p->page_array;
                else {
@@ -1048,7 +1051,23 @@ out:
        nfs_writedata_release(calldata);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+void nfs_write_prepare(struct rpc_task *task, void *calldata)
+{
+       struct nfs_write_data *data = calldata;
+       struct nfs_client *clp = (NFS_SERVER(data->inode))->nfs_client;
+
+       if (nfs4_setup_sequence(clp, &data->args.seq_args,
+                               &data->res.seq_res, 1, task))
+               return;
+       rpc_call_start(task);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static const struct rpc_call_ops nfs_write_partial_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_write_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs_writeback_done_partial,
        .rpc_release = nfs_writeback_release_partial,
 };
@@ -1111,6 +1130,9 @@ remove_request:
 }
 
 static const struct rpc_call_ops nfs_write_full_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_write_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs_writeback_done_full,
        .rpc_release = nfs_writeback_release_full,
 };
@@ -1123,6 +1145,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        struct nfs_writeargs    *argp = &data->args;
        struct nfs_writeres     *resp = &data->res;
+       struct nfs_server       *server = NFS_SERVER(data->inode);
        int status;
 
        dprintk("NFS: %5u nfs_writeback_done (status %d)\n",
@@ -1155,7 +1178,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
                if (time_before(complain, jiffies)) {
                        dprintk("NFS:       faulty NFS server %s:"
                                " (committed = %d) != (stable = %d)\n",
-                               NFS_SERVER(data->inode)->nfs_client->cl_hostname,
+                               server->nfs_client->cl_hostname,
                                resp->verf->committed, argp->stable);
                        complain = jiffies + 300 * HZ;
                }
@@ -1181,7 +1204,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
                                 */
                                argp->stable = NFS_FILE_SYNC;
                        }
-                       rpc_restart_call(task);
+                       nfs4_restart_rpc(task, server->nfs_client);
                        return -EAGAIN;
                }
                if (time_before(complain, jiffies)) {
@@ -1193,6 +1216,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
                /* Can't do anything about it except throw an error. */
                task->tk_status = -EIO;
        }
+       nfs4_sequence_free_slot(server->nfs_client, &data->res.seq_res);
        return 0;
 }
 
@@ -1349,6 +1373,9 @@ static void nfs_commit_release(void *calldata)
 }
 
 static const struct rpc_call_ops nfs_commit_ops = {
+#if defined(CONFIG_NFS_V4_1)
+       .rpc_call_prepare = nfs_write_prepare,
+#endif /* CONFIG_NFS_V4_1 */
        .rpc_call_done = nfs_commit_done,
        .rpc_release = nfs_commit_release,
 };
index e3f0cbcbd0db8179e6055b59411c2d64550d5db2..bd2eba5306677d059b47a4ab7b63c7a2926bb08e 100644 (file)
@@ -21,6 +21,7 @@
 #define NFS4_FHSIZE            128
 #define NFS4_MAXPATHLEN                PATH_MAX
 #define NFS4_MAXNAMLEN         NAME_MAX
+#define NFS4_OPAQUE_LIMIT      1024
 #define NFS4_MAX_SESSIONID_LEN 16
 
 #define NFS4_ACCESS_READ        0x0001
 
 #define NFS4_MAX_UINT64        (~(u64)0)
 
+/* An NFS4 sessions server must support at least NFS4_MAX_OPS operations.
+ * If a compound requires more operations, adjust NFS4_MAX_OPS accordingly.
+ */
+#define NFS4_MAX_OPS   8
+
+/* Our NFS4 client back channel server only wants the cb_sequene and the
+ * actual operation per compound
+ */
+#define NFS4_MAX_BACK_CHANNEL_OPS 2
+
 enum nfs4_acl_whotype {
        NFS4_ACL_WHO_NAMED = 0,
        NFS4_ACL_WHO_OWNER,
@@ -462,6 +473,13 @@ enum lock_type4 {
 #define NFSPROC4_NULL 0
 #define NFSPROC4_COMPOUND 1
 #define NFS4_MINOR_VERSION 0
+
+#if defined(CONFIG_NFS_V4_1)
+#define NFS4_MAX_MINOR_VERSION 1
+#else
+#define NFS4_MAX_MINOR_VERSION 0
+#endif /* CONFIG_NFS_V4_1 */
+
 #define NFS4_DEBUG 1
 
 /* Index of predefined Linux client operations */
index 6ad75948cbf76613fb18c69069c0ccc8efdf580a..19fe15d120429c25b0f36d3abdb9e3d359114f83 100644 (file)
@@ -4,11 +4,17 @@
 #include <linux/list.h>
 #include <linux/backing-dev.h>
 #include <linux/wait.h>
+#include <linux/nfs_xdr.h>
+#include <linux/sunrpc/xprt.h>
 
 #include <asm/atomic.h>
 
+struct nfs4_session;
 struct nfs_iostats;
 struct nlm_host;
+struct nfs4_sequence_args;
+struct nfs4_sequence_res;
+struct nfs_server;
 
 /*
  * The nfs_client identifies our client state to the server.
@@ -18,6 +24,7 @@ struct nfs_client {
        int                     cl_cons_state;  /* current construction state (-ve: init error) */
 #define NFS_CS_READY           0               /* ready to be used */
 #define NFS_CS_INITING         1               /* busy initialising */
+#define NFS_CS_SESSION_INITING 2               /* busy initialising  session */
        unsigned long           cl_res_state;   /* NFS resources state */
 #define NFS_CS_CALLBACK                1               /* - callback started */
 #define NFS_CS_IDMAP           2               /* - idmap started */
@@ -32,6 +39,7 @@ struct nfs_client {
        const struct nfs_rpc_ops *rpc_ops;      /* NFS protocol vector */
        int                     cl_proto;       /* Network transport protocol */
 
+       u32                     cl_minorversion;/* NFSv4 minorversion */
        struct rpc_cred         *cl_machine_cred;
 
 #ifdef CONFIG_NFS_V4
@@ -63,7 +71,22 @@ struct nfs_client {
         */
        char                    cl_ipaddr[48];
        unsigned char           cl_id_uniquifier;
-#endif
+       int                  (* cl_call_sync)(struct nfs_server *server,
+                                             struct rpc_message *msg,
+                                             struct nfs4_sequence_args *args,
+                                             struct nfs4_sequence_res *res,
+                                             int cache_reply);
+#endif /* CONFIG_NFS_V4 */
+
+#ifdef CONFIG_NFS_V4_1
+       /* clientid returned from EXCHANGE_ID, used by session operations */
+       u64                     cl_ex_clid;
+       /* The sequence id to use for the next CREATE_SESSION */
+       u32                     cl_seqid;
+       /* The flags used for obtaining the clientid during EXCHANGE_ID */
+       u32                     cl_exchange_flags;
+       struct nfs4_session     *cl_session;    /* sharred session */
+#endif /* CONFIG_NFS_V4_1 */
 
 #ifdef CONFIG_NFS_FSCACHE
        struct fscache_cookie   *fscache;       /* client index cache cookie */
@@ -145,4 +168,46 @@ struct nfs_server {
 #define NFS_CAP_ACLS           (1U << 3)
 #define NFS_CAP_ATOMIC_OPEN    (1U << 4)
 
+
+/* maximum number of slots to use */
+#define NFS4_MAX_SLOT_TABLE RPC_MAX_SLOT_TABLE
+
+#if defined(CONFIG_NFS_V4_1)
+
+/* Sessions */
+#define SLOT_TABLE_SZ (NFS4_MAX_SLOT_TABLE/(8*sizeof(long)))
+struct nfs4_slot_table {
+       struct nfs4_slot *slots;                /* seqid per slot */
+       unsigned long   used_slots[SLOT_TABLE_SZ]; /* used/unused bitmap */
+       spinlock_t      slot_tbl_lock;
+       struct rpc_wait_queue   slot_tbl_waitq; /* allocators may wait here */
+       int             max_slots;              /* # slots in table */
+       int             highest_used_slotid;    /* sent to server on each SEQ.
+                                                * op for dynamic resizing */
+};
+
+static inline int slot_idx(struct nfs4_slot_table *tbl, struct nfs4_slot *sp)
+{
+       return sp - tbl->slots;
+}
+
+/*
+ * Session related parameters
+ */
+struct nfs4_session {
+       struct nfs4_sessionid           sess_id;
+       u32                             flags;
+       unsigned long                   session_state;
+       u32                             hash_alg;
+       u32                             ssv_len;
+
+       /* The fore and back channel */
+       struct nfs4_channel_attrs       fc_attrs;
+       struct nfs4_slot_table          fc_slot_table;
+       struct nfs4_channel_attrs       bc_attrs;
+       struct nfs4_slot_table          bc_slot_table;
+       struct nfs_client               *clp;
+};
+
+#endif /* CONFIG_NFS_V4_1 */
 #endif
index b89c34e40bc2b23e5d34485f8640a28197b7a3e5..62f63fb0c4c8d6cd54c8c798e5d7239408d45d34 100644 (file)
@@ -145,6 +145,44 @@ struct nfs4_change_info {
 };
 
 struct nfs_seqid;
+
+/* nfs41 sessions channel attributes */
+struct nfs4_channel_attrs {
+       u32                     headerpadsz;
+       u32                     max_rqst_sz;
+       u32                     max_resp_sz;
+       u32                     max_resp_sz_cached;
+       u32                     max_ops;
+       u32                     max_reqs;
+};
+
+/* nfs41 sessions slot seqid */
+struct nfs4_slot {
+       u32                     seq_nr;
+};
+
+struct nfs4_sequence_args {
+       struct nfs4_session     *sa_session;
+       u8                      sa_slotid;
+       u8                      sa_cache_this;
+};
+
+struct nfs4_sequence_res {
+       struct nfs4_session     *sr_session;
+       u8                      sr_slotid;      /* slot used to send request */
+       unsigned long           sr_renewal_time;
+       int                     sr_status;      /* sequence operation status */
+};
+
+struct nfs4_get_lease_time_args {
+       struct nfs4_sequence_args       la_seq_args;
+};
+
+struct nfs4_get_lease_time_res {
+       struct nfs_fsinfo              *lr_fsinfo;
+       struct nfs4_sequence_res        lr_seq_res;
+};
+
 /*
  * Arguments to the open call.
  */
@@ -165,6 +203,7 @@ struct nfs_openargs {
        const struct nfs_server *server;         /* Needed for ID mapping */
        const u32 *             bitmask;
        __u32                   claim;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_openres {
@@ -181,6 +220,7 @@ struct nfs_openres {
        __u32                   do_recall;
        __u64                   maxsize;
        __u32                   attrset[NFS4_BITMAP_SIZE];
+       struct nfs4_sequence_res        seq_res;
 };
 
 /*
@@ -206,6 +246,7 @@ struct nfs_closeargs {
        struct nfs_seqid *      seqid;
        fmode_t                 fmode;
        const u32 *             bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_closeres {
@@ -213,6 +254,7 @@ struct nfs_closeres {
        struct nfs_fattr *      fattr;
        struct nfs_seqid *      seqid;
        const struct nfs_server *server;
+       struct nfs4_sequence_res        seq_res;
 };
 /*
  *  * Arguments to the lock,lockt, and locku call.
@@ -233,12 +275,14 @@ struct nfs_lock_args {
        unsigned char           block : 1;
        unsigned char           reclaim : 1;
        unsigned char           new_lock_owner : 1;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_lock_res {
        nfs4_stateid            stateid;
        struct nfs_seqid *      lock_seqid;
        struct nfs_seqid *      open_seqid;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs_locku_args {
@@ -246,32 +290,38 @@ struct nfs_locku_args {
        struct file_lock *      fl;
        struct nfs_seqid *      seqid;
        nfs4_stateid *          stateid;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_locku_res {
        nfs4_stateid            stateid;
        struct nfs_seqid *      seqid;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs_lockt_args {
        struct nfs_fh *         fh;
        struct file_lock *      fl;
        struct nfs_lowner       lock_owner;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_lockt_res {
        struct file_lock *      denied; /* LOCK, LOCKT failed */
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_delegreturnargs {
        const struct nfs_fh *fhandle;
        const nfs4_stateid *stateid;
        const u32 * bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_delegreturnres {
        struct nfs_fattr * fattr;
        const struct nfs_server *server;
+       struct nfs4_sequence_res        seq_res;
 };
 
 /*
@@ -284,12 +334,14 @@ struct nfs_readargs {
        __u32                   count;
        unsigned int            pgbase;
        struct page **          pages;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_readres {
        struct nfs_fattr *      fattr;
        __u32                   count;
        int                     eof;
+       struct nfs4_sequence_res        seq_res;
 };
 
 /*
@@ -304,6 +356,7 @@ struct nfs_writeargs {
        unsigned int            pgbase;
        struct page **          pages;
        const u32 *             bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_writeverf {
@@ -316,6 +369,7 @@ struct nfs_writeres {
        struct nfs_writeverf *  verf;
        __u32                   count;
        const struct nfs_server *server;
+       struct nfs4_sequence_res        seq_res;
 };
 
 /*
@@ -325,12 +379,14 @@ struct nfs_removeargs {
        const struct nfs_fh     *fh;
        struct qstr             name;
        const u32 *             bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_removeres {
        const struct nfs_server *server;
        struct nfs4_change_info cinfo;
        struct nfs_fattr        dir_attr;
+       struct nfs4_sequence_res        seq_res;
 };
 
 /*
@@ -383,6 +439,7 @@ struct nfs_setattrargs {
        struct iattr *                  iap;
        const struct nfs_server *       server; /* Needed for name mapping */
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs_setaclargs {
@@ -390,6 +447,11 @@ struct nfs_setaclargs {
        size_t                          acl_len;
        unsigned int                    acl_pgbase;
        struct page **                  acl_pages;
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs_setaclres {
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs_getaclargs {
@@ -397,11 +459,18 @@ struct nfs_getaclargs {
        size_t                          acl_len;
        unsigned int                    acl_pgbase;
        struct page **                  acl_pages;
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs_getaclres {
+       size_t                          acl_len;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs_setattrres {
        struct nfs_fattr *              fattr;
        const struct nfs_server *       server;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs_linkargs {
@@ -583,6 +652,7 @@ struct nfs4_accessargs {
        const struct nfs_fh *           fh;
        const u32 *                     bitmask;
        u32                             access;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_accessres {
@@ -590,6 +660,7 @@ struct nfs4_accessres {
        struct nfs_fattr *              fattr;
        u32                             supported;
        u32                             access;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_create_arg {
@@ -609,6 +680,7 @@ struct nfs4_create_arg {
        const struct iattr *            attrs;
        const struct nfs_fh *           dir_fh;
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_create_res {
@@ -617,21 +689,30 @@ struct nfs4_create_res {
        struct nfs_fattr *              fattr;
        struct nfs4_change_info         dir_cinfo;
        struct nfs_fattr *              dir_fattr;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_fsinfo_arg {
        const struct nfs_fh *           fh;
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs4_fsinfo_res {
+       struct nfs_fsinfo              *fsinfo;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_getattr_arg {
        const struct nfs_fh *           fh;
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_getattr_res {
        const struct nfs_server *       server;
        struct nfs_fattr *              fattr;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_link_arg {
@@ -639,6 +720,7 @@ struct nfs4_link_arg {
        const struct nfs_fh *           dir_fh;
        const struct qstr *             name;
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_link_res {
@@ -646,6 +728,7 @@ struct nfs4_link_res {
        struct nfs_fattr *              fattr;
        struct nfs4_change_info         cinfo;
        struct nfs_fattr *              dir_attr;
+       struct nfs4_sequence_res        seq_res;
 };
 
 
@@ -653,21 +736,30 @@ struct nfs4_lookup_arg {
        const struct nfs_fh *           dir_fh;
        const struct qstr *             name;
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_lookup_res {
        const struct nfs_server *       server;
        struct nfs_fattr *              fattr;
        struct nfs_fh *                 fh;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_lookup_root_arg {
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_pathconf_arg {
        const struct nfs_fh *           fh;
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs4_pathconf_res {
+       struct nfs_pathconf            *pathconf;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_readdir_arg {
@@ -678,11 +770,13 @@ struct nfs4_readdir_arg {
        struct page **                  pages;  /* zero-copy data */
        unsigned int                    pgbase; /* zero-copy data */
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_readdir_res {
        nfs4_verifier                   verifier;
        unsigned int                    pgbase;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_readlink {
@@ -690,6 +784,11 @@ struct nfs4_readlink {
        unsigned int                    pgbase;
        unsigned int                    pglen;   /* zero-copy data */
        struct page **                  pages;   /* zero-copy data */
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs4_readlink_res {
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_rename_arg {
@@ -698,6 +797,7 @@ struct nfs4_rename_arg {
        const struct qstr *             old_name;
        const struct qstr *             new_name;
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_rename_res {
@@ -706,6 +806,7 @@ struct nfs4_rename_res {
        struct nfs_fattr *              old_fattr;
        struct nfs4_change_info         new_cinfo;
        struct nfs_fattr *              new_fattr;
+       struct nfs4_sequence_res        seq_res;
 };
 
 #define NFS4_SETCLIENTID_NAMELEN       (127)
@@ -724,6 +825,17 @@ struct nfs4_setclientid {
 struct nfs4_statfs_arg {
        const struct nfs_fh *           fh;
        const u32 *                     bitmask;
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs4_statfs_res {
+       struct nfs_fsstat              *fsstat;
+       struct nfs4_sequence_res        seq_res;
+};
+
+struct nfs4_server_caps_arg {
+       struct nfs_fh                  *fhandle;
+       struct nfs4_sequence_args       seq_args;
 };
 
 struct nfs4_server_caps_res {
@@ -731,6 +843,7 @@ struct nfs4_server_caps_res {
        u32                             acl_bitmask;
        u32                             has_links;
        u32                             has_symlinks;
+       struct nfs4_sequence_res        seq_res;
 };
 
 struct nfs4_string {
@@ -765,10 +878,68 @@ struct nfs4_fs_locations_arg {
        const struct qstr *name;
        struct page *page;
        const u32 *bitmask;
+       struct nfs4_sequence_args       seq_args;
+};
+
+struct nfs4_fs_locations_res {
+       struct nfs4_fs_locations       *fs_locations;
+       struct nfs4_sequence_res        seq_res;
 };
 
 #endif /* CONFIG_NFS_V4 */
 
+struct nfstime4 {
+       u64     seconds;
+       u32     nseconds;
+};
+
+#ifdef CONFIG_NFS_V4_1
+struct nfs_impl_id4 {
+       u32             domain_len;
+       char            *domain;
+       u32             name_len;
+       char            *name;
+       struct nfstime4 date;
+};
+
+#define NFS4_EXCHANGE_ID_LEN   (48)
+struct nfs41_exchange_id_args {
+       struct nfs_client               *client;
+       nfs4_verifier                   *verifier;
+       unsigned int                    id_len;
+       char                            id[NFS4_EXCHANGE_ID_LEN];
+       u32                             flags;
+};
+
+struct server_owner {
+       uint64_t                        minor_id;
+       uint32_t                        major_id_sz;
+       char                            major_id[NFS4_OPAQUE_LIMIT];
+};
+
+struct server_scope {
+       uint32_t                        server_scope_sz;
+       char                            server_scope[NFS4_OPAQUE_LIMIT];
+};
+
+struct nfs41_exchange_id_res {
+       struct nfs_client               *client;
+       u32                             flags;
+};
+
+struct nfs41_create_session_args {
+       struct nfs_client              *client;
+       uint32_t                        flags;
+       uint32_t                        cb_program;
+       struct nfs4_channel_attrs       fc_attrs;       /* Fore Channel */
+       struct nfs4_channel_attrs       bc_attrs;       /* Back Channel */
+};
+
+struct nfs41_create_session_res {
+       struct nfs_client              *client;
+};
+#endif /* CONFIG_NFS_V4_1 */
+
 struct nfs_page;
 
 #define NFS_PAGEVEC_SIZE       (8U)
index 4d61c873feed3d107d5dfcc3a64564fae252706e..7ef4b7ad1214e503a86035cf6073aa632f0315ee 100644 (file)
@@ -41,7 +41,6 @@
 #include <linux/kref.h>
 #include <linux/sunrpc/clnt.h>
 
-#define NFS4_OPAQUE_LIMIT 1024
 typedef struct {
        u32             cl_boot;
        u32             cl_id;
diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h
new file mode 100644 (file)
index 0000000..6508f0d
--- /dev/null
@@ -0,0 +1,49 @@
+/******************************************************************************
+
+(c) 2008 NetApp.  All Rights Reserved.
+
+NetApp provides this source code under the GPL v2 License.
+The GPL v2 license is available at
+http://opensource.org/licenses/gpl-license.php.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*
+ * Functions to create and manage the backchannel
+ */
+
+#ifndef _LINUX_SUNRPC_BC_XPRT_H
+#define _LINUX_SUNRPC_BC_XPRT_H
+
+#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/sched.h>
+
+#ifdef CONFIG_NFS_V4_1
+struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt);
+void xprt_free_bc_request(struct rpc_rqst *req);
+int xprt_setup_backchannel(struct rpc_xprt *, unsigned int min_reqs);
+void xprt_destroy_backchannel(struct rpc_xprt *, int max_reqs);
+void bc_release_request(struct rpc_task *);
+int bc_send(struct rpc_rqst *req);
+#else /* CONFIG_NFS_V4_1 */
+static inline int xprt_setup_backchannel(struct rpc_xprt *xprt,
+                                        unsigned int min_reqs)
+{
+       return 0;
+}
+#endif /* CONFIG_NFS_V4_1 */
+#endif /* _LINUX_SUNRPC_BC_XPRT_H */
+
index c39a21040dcb64aabce55d51db9a56d52f62c289..37881f1a0bd70af99e72302f408adfaf7b4cb30d 100644 (file)
@@ -143,6 +143,7 @@ int         rpc_call_sync(struct rpc_clnt *clnt,
                              const struct rpc_message *msg, int flags);
 struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred,
                               int flags);
+void           rpc_restart_call_prepare(struct rpc_task *);
 void           rpc_restart_call(struct rpc_task *);
 void           rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
 size_t         rpc_max_payload(struct rpc_clnt *);
index 64981a2f1cae12e1b9f384b758cbc1718ee09721..401097781fc0601e5b7359ea15ec76ad2a3f5f03 100644 (file)
@@ -210,6 +210,8 @@ struct rpc_wait_queue {
  */
 struct rpc_task *rpc_new_task(const struct rpc_task_setup *);
 struct rpc_task *rpc_run_task(const struct rpc_task_setup *);
+struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req,
+                               const struct rpc_call_ops *ops);
 void           rpc_put_task(struct rpc_task *);
 void           rpc_exit_task(struct rpc_task *);
 void           rpc_release_calldata(const struct rpc_call_ops *, void *);
@@ -237,6 +239,7 @@ void                rpc_show_tasks(void);
 int            rpc_init_mempool(void);
 void           rpc_destroy_mempool(void);
 extern struct workqueue_struct *rpciod_workqueue;
+void           rpc_prepare_task(struct rpc_task *task);
 
 static inline void rpc_exit(struct rpc_task *task, int status)
 {
index 2a30775959e9343e187118016cf2ff9bd8214bc9..ea8009695c69b1ea437b255fd0a141ebf21feaa7 100644 (file)
@@ -96,6 +96,15 @@ struct svc_serv {
        svc_thread_fn           sv_function;    /* main function for threads */
        unsigned int            sv_drc_max_pages; /* Total pages for DRC */
        unsigned int            sv_drc_pages_used;/* DRC pages used */
+#if defined(CONFIG_NFS_V4_1)
+       struct list_head        sv_cb_list;     /* queue for callback requests
+                                                * that arrive over the same
+                                                * connection */
+       spinlock_t              sv_cb_lock;     /* protects the svc_cb_list */
+       wait_queue_head_t       sv_cb_waitq;    /* sleep here if there are no
+                                                * entries in the svc_cb_list */
+       struct svc_xprt         *bc_xprt;
+#endif /* CONFIG_NFS_V4_1 */
 };
 
 /*
@@ -411,6 +420,8 @@ int            svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
 int               svc_pool_stats_open(struct svc_serv *serv, struct file *file);
 void              svc_destroy(struct svc_serv *);
 int               svc_process(struct svc_rqst *);
+int               bc_svc_process(struct svc_serv *, struct rpc_rqst *,
+                       struct svc_rqst *);
 int               svc_register(const struct svc_serv *, const int,
                                const unsigned short, const unsigned short);
 
index 483e10380aae45429b8ad7aba5a930aa8e4f3695..6bb1ec4ae3108dc963d597658b7332f0c0854aa9 100644 (file)
@@ -42,6 +42,8 @@ int           svc_sock_names(char *buf, struct svc_serv *serv, char *toclose);
 int            svc_addsock(struct svc_serv *serv, int fd, char *name_return);
 void           svc_init_xprt_sock(void);
 void           svc_cleanup_xprt_sock(void);
+struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot);
+void           svc_sock_destroy(struct svc_xprt *);
 
 /*
  * svc_makesock socket characteristics
index 08afe43118f4a3621e7f53dd6004e489d7b9ec00..1175d58efc2e8498ea52c236c759ae0116ef23db 100644 (file)
@@ -67,7 +67,8 @@ struct rpc_rqst {
        struct rpc_task *       rq_task;        /* RPC task data */
        __be32                  rq_xid;         /* request XID */
        int                     rq_cong;        /* has incremented xprt->cong */
-       int                     rq_received;    /* receive completed */
+       int                     rq_reply_bytes_recvd;   /* number of reply */
+                                                       /* bytes received */
        u32                     rq_seqno;       /* gss seq no. used on req. */
        int                     rq_enc_pages_num;
        struct page             **rq_enc_pages; /* scratch pages for use by
@@ -97,6 +98,12 @@ struct rpc_rqst {
 
        unsigned long           rq_xtime;       /* when transmitted */
        int                     rq_ntrans;
+
+#if defined(CONFIG_NFS_V4_1)
+       struct list_head        rq_bc_list;     /* Callback service list */
+       unsigned long           rq_bc_pa_state; /* Backchannel prealloc state */
+       struct list_head        rq_bc_pa_list;  /* Backchannel prealloc list */
+#endif /* CONFIG_NFS_V4_1 */
 };
 #define rq_svec                        rq_snd_buf.head
 #define rq_slen                        rq_snd_buf.len
@@ -174,6 +181,15 @@ struct rpc_xprt {
        spinlock_t              reserve_lock;   /* lock slot table */
        u32                     xid;            /* Next XID value to use */
        struct rpc_task *       snd_task;       /* Task blocked in send */
+#if defined(CONFIG_NFS_V4_1)
+       struct svc_serv         *bc_serv;       /* The RPC service which will */
+                                               /* process the callback */
+       unsigned int            bc_alloc_count; /* Total number of preallocs */
+       spinlock_t              bc_pa_lock;     /* Protects the preallocated
+                                                * items */
+       struct list_head        bc_pa_list;     /* List of preallocated
+                                                * backchannel rpc_rqst's */
+#endif /* CONFIG_NFS_V4_1 */
        struct list_head        recv;
 
        struct {
@@ -192,6 +208,26 @@ struct rpc_xprt {
        const char              *address_strings[RPC_DISPLAY_MAX];
 };
 
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * Backchannel flags
+ */
+#define        RPC_BC_PA_IN_USE        0x0001          /* Preallocated backchannel */
+                                               /* buffer in use */
+#endif /* CONFIG_NFS_V4_1 */
+
+#if defined(CONFIG_NFS_V4_1)
+static inline int bc_prealloc(struct rpc_rqst *req)
+{
+       return test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
+}
+#else
+static inline int bc_prealloc(struct rpc_rqst *req)
+{
+       return 0;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 struct xprt_create {
        int                     ident;          /* XPRT_TRANSPORT identifier */
        struct sockaddr *       srcaddr;        /* optional local address */
index 5369aa369b353f9c32f10fcd0836b344353686d4..db73fd2a3f0e4110dff6dd9f879ef6060efc7a4c 100644 (file)
@@ -13,5 +13,6 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
            rpcb_clnt.o timer.o xdr.o \
            sunrpc_syms.o cache.o rpc_pipe.o \
            svc_xprt.o
+sunrpc-$(CONFIG_NFS_V4_1) += backchannel_rqst.o bc_svc.o
 sunrpc-$(CONFIG_PROC_FS) += stats.o
 sunrpc-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c
new file mode 100644 (file)
index 0000000..5a7d342
--- /dev/null
@@ -0,0 +1,278 @@
+/******************************************************************************
+
+(c) 2007 Network Appliance, Inc.  All Rights Reserved.
+(c) 2009 NetApp.  All Rights Reserved.
+
+NetApp provides this source code under the GPL v2 License.
+The GPL v2 license is available at
+http://opensource.org/licenses/gpl-license.php.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <linux/tcp.h>
+#include <linux/sunrpc/xprt.h>
+
+#ifdef RPC_DEBUG
+#define RPCDBG_FACILITY        RPCDBG_TRANS
+#endif
+
+#if defined(CONFIG_NFS_V4_1)
+
+/*
+ * Helper routines that track the number of preallocation elements
+ * on the transport.
+ */
+static inline int xprt_need_to_requeue(struct rpc_xprt *xprt)
+{
+       return xprt->bc_alloc_count > 0;
+}
+
+static inline void xprt_inc_alloc_count(struct rpc_xprt *xprt, unsigned int n)
+{
+       xprt->bc_alloc_count += n;
+}
+
+static inline int xprt_dec_alloc_count(struct rpc_xprt *xprt, unsigned int n)
+{
+       return xprt->bc_alloc_count -= n;
+}
+
+/*
+ * Free the preallocated rpc_rqst structure and the memory
+ * buffers hanging off of it.
+ */
+static void xprt_free_allocation(struct rpc_rqst *req)
+{
+       struct xdr_buf *xbufp;
+
+       dprintk("RPC:        free allocations for req= %p\n", req);
+       BUG_ON(test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state));
+       xbufp = &req->rq_private_buf;
+       free_page((unsigned long)xbufp->head[0].iov_base);
+       xbufp = &req->rq_snd_buf;
+       free_page((unsigned long)xbufp->head[0].iov_base);
+       list_del(&req->rq_bc_pa_list);
+       kfree(req);
+}
+
+/*
+ * Preallocate up to min_reqs structures and related buffers for use
+ * by the backchannel.  This function can be called multiple times
+ * when creating new sessions that use the same rpc_xprt.  The
+ * preallocated buffers are added to the pool of resources used by
+ * the rpc_xprt.  Anyone of these resources may be used used by an
+ * incoming callback request.  It's up to the higher levels in the
+ * stack to enforce that the maximum number of session slots is not
+ * being exceeded.
+ *
+ * Some callback arguments can be large.  For example, a pNFS server
+ * using multiple deviceids.  The list can be unbound, but the client
+ * has the ability to tell the server the maximum size of the callback
+ * requests.  Each deviceID is 16 bytes, so allocate one page
+ * for the arguments to have enough room to receive a number of these
+ * deviceIDs.  The NFS client indicates to the pNFS server that its
+ * callback requests can be up to 4096 bytes in size.
+ */
+int xprt_setup_backchannel(struct rpc_xprt *xprt, unsigned int min_reqs)
+{
+       struct page *page_rcv = NULL, *page_snd = NULL;
+       struct xdr_buf *xbufp = NULL;
+       struct rpc_rqst *req, *tmp;
+       struct list_head tmp_list;
+       int i;
+
+       dprintk("RPC:       setup backchannel transport\n");
+
+       /*
+        * We use a temporary list to keep track of the preallocated
+        * buffers.  Once we're done building the list we splice it
+        * into the backchannel preallocation list off of the rpc_xprt
+        * struct.  This helps minimize the amount of time the list
+        * lock is held on the rpc_xprt struct.  It also makes cleanup
+        * easier in case of memory allocation errors.
+        */
+       INIT_LIST_HEAD(&tmp_list);
+       for (i = 0; i < min_reqs; i++) {
+               /* Pre-allocate one backchannel rpc_rqst */
+               req = kzalloc(sizeof(struct rpc_rqst), GFP_KERNEL);
+               if (req == NULL) {
+                       printk(KERN_ERR "Failed to create bc rpc_rqst\n");
+                       goto out_free;
+               }
+
+               /* Add the allocated buffer to the tmp list */
+               dprintk("RPC:       adding req= %p\n", req);
+               list_add(&req->rq_bc_pa_list, &tmp_list);
+
+               req->rq_xprt = xprt;
+               INIT_LIST_HEAD(&req->rq_list);
+               INIT_LIST_HEAD(&req->rq_bc_list);
+
+               /* Preallocate one XDR receive buffer */
+               page_rcv = alloc_page(GFP_KERNEL);
+               if (page_rcv == NULL) {
+                       printk(KERN_ERR "Failed to create bc receive xbuf\n");
+                       goto out_free;
+               }
+               xbufp = &req->rq_rcv_buf;
+               xbufp->head[0].iov_base = page_address(page_rcv);
+               xbufp->head[0].iov_len = PAGE_SIZE;
+               xbufp->tail[0].iov_base = NULL;
+               xbufp->tail[0].iov_len = 0;
+               xbufp->page_len = 0;
+               xbufp->len = PAGE_SIZE;
+               xbufp->buflen = PAGE_SIZE;
+
+               /* Preallocate one XDR send buffer */
+               page_snd = alloc_page(GFP_KERNEL);
+               if (page_snd == NULL) {
+                       printk(KERN_ERR "Failed to create bc snd xbuf\n");
+                       goto out_free;
+               }
+
+               xbufp = &req->rq_snd_buf;
+               xbufp->head[0].iov_base = page_address(page_snd);
+               xbufp->head[0].iov_len = 0;
+               xbufp->tail[0].iov_base = NULL;
+               xbufp->tail[0].iov_len = 0;
+               xbufp->page_len = 0;
+               xbufp->len = 0;
+               xbufp->buflen = PAGE_SIZE;
+       }
+
+       /*
+        * Add the temporary list to the backchannel preallocation list
+        */
+       spin_lock_bh(&xprt->bc_pa_lock);
+       list_splice(&tmp_list, &xprt->bc_pa_list);
+       xprt_inc_alloc_count(xprt, min_reqs);
+       spin_unlock_bh(&xprt->bc_pa_lock);
+
+       dprintk("RPC:       setup backchannel transport done\n");
+       return 0;
+
+out_free:
+       /*
+        * Memory allocation failed, free the temporary list
+        */
+       list_for_each_entry_safe(req, tmp, &tmp_list, rq_bc_pa_list)
+               xprt_free_allocation(req);
+
+       dprintk("RPC:       setup backchannel transport failed\n");
+       return -1;
+}
+EXPORT_SYMBOL(xprt_setup_backchannel);
+
+/*
+ * Destroys the backchannel preallocated structures.
+ * Since these structures may have been allocated by multiple calls
+ * to xprt_setup_backchannel, we only destroy up to the maximum number
+ * of reqs specified by the caller.
+ * @xprt:      the transport holding the preallocated strucures
+ * @max_reqs   the maximum number of preallocated structures to destroy
+ */
+void xprt_destroy_backchannel(struct rpc_xprt *xprt, unsigned int max_reqs)
+{
+       struct rpc_rqst *req = NULL, *tmp = NULL;
+
+       dprintk("RPC:        destroy backchannel transport\n");
+
+       BUG_ON(max_reqs == 0);
+       spin_lock_bh(&xprt->bc_pa_lock);
+       xprt_dec_alloc_count(xprt, max_reqs);
+       list_for_each_entry_safe(req, tmp, &xprt->bc_pa_list, rq_bc_pa_list) {
+               dprintk("RPC:        req=%p\n", req);
+               xprt_free_allocation(req);
+               if (--max_reqs == 0)
+                       break;
+       }
+       spin_unlock_bh(&xprt->bc_pa_lock);
+
+       dprintk("RPC:        backchannel list empty= %s\n",
+               list_empty(&xprt->bc_pa_list) ? "true" : "false");
+}
+EXPORT_SYMBOL(xprt_destroy_backchannel);
+
+/*
+ * One or more rpc_rqst structure have been preallocated during the
+ * backchannel setup.  Buffer space for the send and private XDR buffers
+ * has been preallocated as well.  Use xprt_alloc_bc_request to allocate
+ * to this request.  Use xprt_free_bc_request to return it.
+ *
+ * Return an available rpc_rqst, otherwise NULL if non are available.
+ */
+struct rpc_rqst *xprt_alloc_bc_request(struct rpc_xprt *xprt)
+{
+       struct rpc_rqst *req;
+
+       dprintk("RPC:       allocate a backchannel request\n");
+       spin_lock_bh(&xprt->bc_pa_lock);
+       if (!list_empty(&xprt->bc_pa_list)) {
+               req = list_first_entry(&xprt->bc_pa_list, struct rpc_rqst,
+                               rq_bc_pa_list);
+               list_del(&req->rq_bc_pa_list);
+       } else {
+               req = NULL;
+       }
+       spin_unlock_bh(&xprt->bc_pa_lock);
+
+       if (req != NULL) {
+               set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
+               req->rq_reply_bytes_recvd = 0;
+               req->rq_bytes_sent = 0;
+               memcpy(&req->rq_private_buf, &req->rq_rcv_buf,
+                       sizeof(req->rq_private_buf));
+       }
+       dprintk("RPC:       backchannel req=%p\n", req);
+       return req;
+}
+
+/*
+ * Return the preallocated rpc_rqst structure and XDR buffers
+ * associated with this rpc_task.
+ */
+void xprt_free_bc_request(struct rpc_rqst *req)
+{
+       struct rpc_xprt *xprt = req->rq_xprt;
+
+       dprintk("RPC:       free backchannel req=%p\n", req);
+
+       smp_mb__before_clear_bit();
+       BUG_ON(!test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state));
+       clear_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
+       smp_mb__after_clear_bit();
+
+       if (!xprt_need_to_requeue(xprt)) {
+               /*
+                * The last remaining session was destroyed while this
+                * entry was in use.  Free the entry and don't attempt
+                * to add back to the list because there is no need to
+                * have anymore preallocated entries.
+                */
+               dprintk("RPC:       Last session removed req=%p\n", req);
+               xprt_free_allocation(req);
+               return;
+       }
+
+       /*
+        * Return it to the list of preallocations so that it
+        * may be reused by a new callback request.
+        */
+       spin_lock_bh(&xprt->bc_pa_lock);
+       list_add(&req->rq_bc_pa_list, &xprt->bc_pa_list);
+       spin_unlock_bh(&xprt->bc_pa_lock);
+}
+
+#endif /* CONFIG_NFS_V4_1 */
diff --git a/net/sunrpc/bc_svc.c b/net/sunrpc/bc_svc.c
new file mode 100644 (file)
index 0000000..13f214f
--- /dev/null
@@ -0,0 +1,81 @@
+/******************************************************************************
+
+(c) 2007 Network Appliance, Inc.  All Rights Reserved.
+(c) 2009 NetApp.  All Rights Reserved.
+
+NetApp provides this source code under the GPL v2 License.
+The GPL v2 license is available at
+http://opensource.org/licenses/gpl-license.php.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*
+ * The NFSv4.1 callback service helper routines.
+ * They implement the transport level processing required to send the
+ * reply over an existing open connection previously established by the client.
+ */
+
+#if defined(CONFIG_NFS_V4_1)
+
+#include <linux/module.h>
+
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/bc_xprt.h>
+
+#define RPCDBG_FACILITY        RPCDBG_SVCDSP
+
+void bc_release_request(struct rpc_task *task)
+{
+       struct rpc_rqst *req = task->tk_rqstp;
+
+       dprintk("RPC:       bc_release_request: task= %p\n", task);
+
+       /*
+        * Release this request only if it's a backchannel
+        * preallocated request
+        */
+       if (!bc_prealloc(req))
+               return;
+       xprt_free_bc_request(req);
+}
+
+/* Empty callback ops */
+static const struct rpc_call_ops nfs41_callback_ops = {
+};
+
+
+/*
+ * Send the callback reply
+ */
+int bc_send(struct rpc_rqst *req)
+{
+       struct rpc_task *task;
+       int ret;
+
+       dprintk("RPC:       bc_send req= %p\n", req);
+       task = rpc_run_bc_task(req, &nfs41_callback_ops);
+       if (IS_ERR(task))
+               ret = PTR_ERR(task);
+       else {
+               BUG_ON(atomic_read(&task->tk_count) != 1);
+               ret = task->tk_status;
+               rpc_put_task(task);
+       }
+       return ret;
+       dprintk("RPC:       bc_send ret= %d \n", ret);
+}
+
+#endif /* CONFIG_NFS_V4_1 */
index 5abab094441f97cbe8dd7f91dcd554579030b6be..5bc2f45bddf0051a90f73274707b2697cc53b5d0 100644 (file)
@@ -36,7 +36,9 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/sunrpc/metrics.h>
+#include <linux/sunrpc/bc_xprt.h>
 
+#include "sunrpc.h"
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY       RPCDBG_CALL
@@ -63,6 +65,9 @@ static void   call_decode(struct rpc_task *task);
 static void    call_bind(struct rpc_task *task);
 static void    call_bind_status(struct rpc_task *task);
 static void    call_transmit(struct rpc_task *task);
+#if defined(CONFIG_NFS_V4_1)
+static void    call_bc_transmit(struct rpc_task *task);
+#endif /* CONFIG_NFS_V4_1 */
 static void    call_status(struct rpc_task *task);
 static void    call_transmit_status(struct rpc_task *task);
 static void    call_refresh(struct rpc_task *task);
@@ -613,6 +618,50 @@ rpc_call_async(struct rpc_clnt *clnt, const struct rpc_message *msg, int flags,
 }
 EXPORT_SYMBOL_GPL(rpc_call_async);
 
+#if defined(CONFIG_NFS_V4_1)
+/**
+ * rpc_run_bc_task - Allocate a new RPC task for backchannel use, then run
+ * rpc_execute against it
+ * @ops: RPC call ops
+ */
+struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req,
+                                       const struct rpc_call_ops *tk_ops)
+{
+       struct rpc_task *task;
+       struct xdr_buf *xbufp = &req->rq_snd_buf;
+       struct rpc_task_setup task_setup_data = {
+               .callback_ops = tk_ops,
+       };
+
+       dprintk("RPC: rpc_run_bc_task req= %p\n", req);
+       /*
+        * Create an rpc_task to send the data
+        */
+       task = rpc_new_task(&task_setup_data);
+       if (!task) {
+               xprt_free_bc_request(req);
+               goto out;
+       }
+       task->tk_rqstp = req;
+
+       /*
+        * Set up the xdr_buf length.
+        * This also indicates that the buffer is XDR encoded already.
+        */
+       xbufp->len = xbufp->head[0].iov_len + xbufp->page_len +
+                       xbufp->tail[0].iov_len;
+
+       task->tk_action = call_bc_transmit;
+       atomic_inc(&task->tk_count);
+       BUG_ON(atomic_read(&task->tk_count) != 2);
+       rpc_execute(task);
+
+out:
+       dprintk("RPC: rpc_run_bc_task: task= %p\n", task);
+       return task;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 void
 rpc_call_start(struct rpc_task *task)
 {
@@ -694,6 +743,19 @@ void rpc_force_rebind(struct rpc_clnt *clnt)
 }
 EXPORT_SYMBOL_GPL(rpc_force_rebind);
 
+/*
+ * Restart an (async) RPC call from the call_prepare state.
+ * Usually called from within the exit handler.
+ */
+void
+rpc_restart_call_prepare(struct rpc_task *task)
+{
+       if (RPC_ASSASSINATED(task))
+               return;
+       task->tk_action = rpc_prepare_task;
+}
+EXPORT_SYMBOL_GPL(rpc_restart_call_prepare);
+
 /*
  * Restart an (async) RPC call. Usually called from within the
  * exit handler.
@@ -1085,7 +1147,7 @@ call_transmit(struct rpc_task *task)
         * in order to allow access to the socket to other RPC requests.
         */
        call_transmit_status(task);
-       if (task->tk_msg.rpc_proc->p_decode != NULL)
+       if (rpc_reply_expected(task))
                return;
        task->tk_action = rpc_exit_task;
        rpc_wake_up_queued_task(&task->tk_xprt->pending, task);
@@ -1120,6 +1182,72 @@ call_transmit_status(struct rpc_task *task)
        }
 }
 
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * 5b. Send the backchannel RPC reply.  On error, drop the reply.  In
+ * addition, disconnect on connectivity errors.
+ */
+static void
+call_bc_transmit(struct rpc_task *task)
+{
+       struct rpc_rqst *req = task->tk_rqstp;
+
+       BUG_ON(task->tk_status != 0);
+       task->tk_status = xprt_prepare_transmit(task);
+       if (task->tk_status == -EAGAIN) {
+               /*
+                * Could not reserve the transport. Try again after the
+                * transport is released.
+                */
+               task->tk_status = 0;
+               task->tk_action = call_bc_transmit;
+               return;
+       }
+
+       task->tk_action = rpc_exit_task;
+       if (task->tk_status < 0) {
+               printk(KERN_NOTICE "RPC: Could not send backchannel reply "
+                       "error: %d\n", task->tk_status);
+               return;
+       }
+
+       xprt_transmit(task);
+       xprt_end_transmit(task);
+       dprint_status(task);
+       switch (task->tk_status) {
+       case 0:
+               /* Success */
+               break;
+       case -EHOSTDOWN:
+       case -EHOSTUNREACH:
+       case -ENETUNREACH:
+       case -ETIMEDOUT:
+               /*
+                * Problem reaching the server.  Disconnect and let the
+                * forechannel reestablish the connection.  The server will
+                * have to retransmit the backchannel request and we'll
+                * reprocess it.  Since these ops are idempotent, there's no
+                * need to cache our reply at this time.
+                */
+               printk(KERN_NOTICE "RPC: Could not send backchannel reply "
+                       "error: %d\n", task->tk_status);
+               xprt_conditional_disconnect(task->tk_xprt,
+                       req->rq_connect_cookie);
+               break;
+       default:
+               /*
+                * We were unable to reply and will have to drop the
+                * request.  The server should reconnect and retransmit.
+                */
+               BUG_ON(task->tk_status == -EAGAIN);
+               printk(KERN_NOTICE "RPC: Could not send backchannel reply "
+                       "error: %d\n", task->tk_status);
+               break;
+       }
+       rpc_wake_up_queued_task(&req->rq_xprt->pending, task);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 /*
  * 6.  Sort out the RPC call status
  */
@@ -1130,8 +1258,8 @@ call_status(struct rpc_task *task)
        struct rpc_rqst *req = task->tk_rqstp;
        int             status;
 
-       if (req->rq_received > 0 && !req->rq_bytes_sent)
-               task->tk_status = req->rq_received;
+       if (req->rq_reply_bytes_recvd > 0 && !req->rq_bytes_sent)
+               task->tk_status = req->rq_reply_bytes_recvd;
 
        dprint_status(task);
 
@@ -1248,7 +1376,7 @@ call_decode(struct rpc_task *task)
 
        /*
         * Ensure that we see all writes made by xprt_complete_rqst()
-        * before it changed req->rq_received.
+        * before it changed req->rq_reply_bytes_recvd.
         */
        smp_rmb();
        req->rq_rcv_buf.len = req->rq_private_buf.len;
@@ -1289,7 +1417,7 @@ out_retry:
        task->tk_status = 0;
        /* Note: rpc_verify_header() may have freed the RPC slot */
        if (task->tk_rqstp == req) {
-               req->rq_received = req->rq_rcv_buf.len = 0;
+               req->rq_reply_bytes_recvd = req->rq_rcv_buf.len = 0;
                if (task->tk_client->cl_discrtry)
                        xprt_conditional_disconnect(task->tk_xprt,
                                        req->rq_connect_cookie);
@@ -1377,13 +1505,14 @@ rpc_verify_header(struct rpc_task *task)
        }
        if ((len -= 3) < 0)
                goto out_overflow;
-       p += 1; /* skip XID */
 
+       p += 1; /* skip XID */
        if ((n = ntohl(*p++)) != RPC_REPLY) {
                dprintk("RPC: %5u %s: not an RPC reply: %x\n",
-                               task->tk_pid, __func__, n);
+                       task->tk_pid, __func__, n);
                goto out_garbage;
        }
+
        if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
                if (--len < 0)
                        goto out_overflow;
index ff50a054686593b55a203d28ba8a8aa68c191260..1102ce1251f7b61ad7d6a70a7df8826ce2a420c2 100644 (file)
@@ -569,7 +569,7 @@ EXPORT_SYMBOL_GPL(rpc_delay);
 /*
  * Helper to call task->tk_ops->rpc_call_prepare
  */
-static void rpc_prepare_task(struct rpc_task *task)
+void rpc_prepare_task(struct rpc_task *task)
 {
        task->tk_ops->rpc_call_prepare(task, task->tk_calldata);
 }
index 1ef6e46d9da273b276625e765e6d6bd1c880457a..1b4e6791ecf3d3a036347150708fb0e345c26198 100644 (file)
@@ -141,12 +141,14 @@ EXPORT_SYMBOL_GPL(rpc_free_iostats);
 void rpc_count_iostats(struct rpc_task *task)
 {
        struct rpc_rqst *req = task->tk_rqstp;
-       struct rpc_iostats *stats = task->tk_client->cl_metrics;
+       struct rpc_iostats *stats;
        struct rpc_iostats *op_metrics;
        long rtt, execute, queue;
 
-       if (!stats || !req)
+       if (!task->tk_client || !task->tk_client->cl_metrics || !req)
                return;
+
+       stats = task->tk_client->cl_metrics;
        op_metrics = &stats[task->tk_msg.rpc_proc->p_statidx];
 
        op_metrics->om_ops++;
@@ -154,7 +156,7 @@ void rpc_count_iostats(struct rpc_task *task)
        op_metrics->om_timeouts += task->tk_timeouts;
 
        op_metrics->om_bytes_sent += task->tk_bytes_sent;
-       op_metrics->om_bytes_recv += req->rq_received;
+       op_metrics->om_bytes_recv += req->rq_reply_bytes_recvd;
 
        queue = (long)req->rq_xtime - task->tk_start;
        if (queue < 0)
diff --git a/net/sunrpc/sunrpc.h b/net/sunrpc/sunrpc.h
new file mode 100644 (file)
index 0000000..5d9dd74
--- /dev/null
@@ -0,0 +1,37 @@
+/******************************************************************************
+
+(c) 2008 NetApp.  All Rights Reserved.
+
+NetApp provides this source code under the GPL v2 License.
+The GPL v2 license is available at
+http://opensource.org/licenses/gpl-license.php.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+/*
+ * Functions and macros used internally by RPC
+ */
+
+#ifndef _NET_SUNRPC_SUNRPC_H
+#define _NET_SUNRPC_SUNRPC_H
+
+static inline int rpc_reply_expected(struct rpc_task *task)
+{
+       return (task->tk_msg.rpc_proc != NULL) &&
+               (task->tk_msg.rpc_proc->p_decode != NULL);
+}
+
+#endif /* _NET_SUNRPC_SUNRPC_H */
+
index 5ed8931dfe98f8af810a28a046f9c6729f07e6a3..952f206ff307a42b8a7bb20066dcf6ed5e3ab2c9 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/sunrpc/stats.h>
 #include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/bc_xprt.h>
 
 #define RPCDBG_FACILITY        RPCDBG_SVCDSP
 
@@ -486,6 +487,10 @@ svc_destroy(struct svc_serv *serv)
        if (svc_serv_is_pooled(serv))
                svc_pool_map_put();
 
+#if defined(CONFIG_NFS_V4_1)
+       svc_sock_destroy(serv->bc_xprt);
+#endif /* CONFIG_NFS_V4_1 */
+
        svc_unregister(serv);
        kfree(serv->sv_pools);
        kfree(serv);
@@ -970,20 +975,18 @@ svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)
 }
 
 /*
- * Process the RPC request.
+ * Common routine for processing the RPC request.
  */
-int
-svc_process(struct svc_rqst *rqstp)
+static int
+svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
 {
        struct svc_program      *progp;
        struct svc_version      *versp = NULL;  /* compiler food */
        struct svc_procedure    *procp = NULL;
-       struct kvec *           argv = &rqstp->rq_arg.head[0];
-       struct kvec *           resv = &rqstp->rq_res.head[0];
        struct svc_serv         *serv = rqstp->rq_server;
        kxdrproc_t              xdr;
        __be32                  *statp;
-       u32                     dir, prog, vers, proc;
+       u32                     prog, vers, proc;
        __be32                  auth_stat, rpc_stat;
        int                     auth_res;
        __be32                  *reply_statp;
@@ -993,19 +996,6 @@ svc_process(struct svc_rqst *rqstp)
        if (argv->iov_len < 6*4)
                goto err_short_len;
 
-       /* setup response xdr_buf.
-        * Initially it has just one page
-        */
-       rqstp->rq_resused = 1;
-       resv->iov_base = page_address(rqstp->rq_respages[0]);
-       resv->iov_len = 0;
-       rqstp->rq_res.pages = rqstp->rq_respages + 1;
-       rqstp->rq_res.len = 0;
-       rqstp->rq_res.page_base = 0;
-       rqstp->rq_res.page_len = 0;
-       rqstp->rq_res.buflen = PAGE_SIZE;
-       rqstp->rq_res.tail[0].iov_base = NULL;
-       rqstp->rq_res.tail[0].iov_len = 0;
        /* Will be turned off only in gss privacy case: */
        rqstp->rq_splice_ok = 1;
        /* Will be turned off only when NFSv4 Sessions are used */
@@ -1014,17 +1004,13 @@ svc_process(struct svc_rqst *rqstp)
        /* Setup reply header */
        rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp);
 
-       rqstp->rq_xid = svc_getu32(argv);
        svc_putu32(resv, rqstp->rq_xid);
 
-       dir  = svc_getnl(argv);
        vers = svc_getnl(argv);
 
        /* First words of reply: */
        svc_putnl(resv, 1);             /* REPLY */
 
-       if (dir != 0)           /* direction != CALL */
-               goto err_bad_dir;
        if (vers != 2)          /* RPC version number */
                goto err_bad_rpc;
 
@@ -1147,7 +1133,7 @@ svc_process(struct svc_rqst *rqstp)
  sendit:
        if (svc_authorise(rqstp))
                goto dropit;
-       return svc_send(rqstp);
+       return 1;               /* Caller can now send it */
 
  dropit:
        svc_authorise(rqstp);   /* doesn't hurt to call this twice */
@@ -1161,12 +1147,6 @@ err_short_len:
 
        goto dropit;                    /* drop request */
 
-err_bad_dir:
-       svc_printk(rqstp, "bad direction %d, dropping request\n", dir);
-
-       serv->sv_stats->rpcbadfmt++;
-       goto dropit;                    /* drop request */
-
 err_bad_rpc:
        serv->sv_stats->rpcbadfmt++;
        svc_putnl(resv, 1);     /* REJECT */
@@ -1219,6 +1199,100 @@ err_bad:
 }
 EXPORT_SYMBOL_GPL(svc_process);
 
+/*
+ * Process the RPC request.
+ */
+int
+svc_process(struct svc_rqst *rqstp)
+{
+       struct kvec             *argv = &rqstp->rq_arg.head[0];
+       struct kvec             *resv = &rqstp->rq_res.head[0];
+       struct svc_serv         *serv = rqstp->rq_server;
+       u32                     dir;
+       int                     error;
+
+       /*
+        * Setup response xdr_buf.
+        * Initially it has just one page
+        */
+       rqstp->rq_resused = 1;
+       resv->iov_base = page_address(rqstp->rq_respages[0]);
+       resv->iov_len = 0;
+       rqstp->rq_res.pages = rqstp->rq_respages + 1;
+       rqstp->rq_res.len = 0;
+       rqstp->rq_res.page_base = 0;
+       rqstp->rq_res.page_len = 0;
+       rqstp->rq_res.buflen = PAGE_SIZE;
+       rqstp->rq_res.tail[0].iov_base = NULL;
+       rqstp->rq_res.tail[0].iov_len = 0;
+
+       rqstp->rq_xid = svc_getu32(argv);
+
+       dir  = svc_getnl(argv);
+       if (dir != 0) {
+               /* direction != CALL */
+               svc_printk(rqstp, "bad direction %d, dropping request\n", dir);
+               serv->sv_stats->rpcbadfmt++;
+               svc_drop(rqstp);
+               return 0;
+       }
+
+       error = svc_process_common(rqstp, argv, resv);
+       if (error <= 0)
+               return error;
+
+       return svc_send(rqstp);
+}
+
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * Process a backchannel RPC request that arrived over an existing
+ * outbound connection
+ */
+int
+bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
+              struct svc_rqst *rqstp)
+{
+       struct kvec     *argv = &rqstp->rq_arg.head[0];
+       struct kvec     *resv = &rqstp->rq_res.head[0];
+       int             error;
+
+       /* Build the svc_rqst used by the common processing routine */
+       rqstp->rq_xprt = serv->bc_xprt;
+       rqstp->rq_xid = req->rq_xid;
+       rqstp->rq_prot = req->rq_xprt->prot;
+       rqstp->rq_server = serv;
+
+       rqstp->rq_addrlen = sizeof(req->rq_xprt->addr);
+       memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen);
+       memcpy(&rqstp->rq_arg, &req->rq_rcv_buf, sizeof(rqstp->rq_arg));
+       memcpy(&rqstp->rq_res, &req->rq_snd_buf, sizeof(rqstp->rq_res));
+
+       /* reset result send buffer "put" position */
+       resv->iov_len = 0;
+
+       if (rqstp->rq_prot != IPPROTO_TCP) {
+               printk(KERN_ERR "No support for Non-TCP transports!\n");
+               BUG();
+       }
+
+       /*
+        * Skip the next two words because they've already been
+        * processed in the trasport
+        */
+       svc_getu32(argv);       /* XID */
+       svc_getnl(argv);        /* CALLDIR */
+
+       error = svc_process_common(rqstp, argv, resv);
+       if (error <= 0)
+               return error;
+
+       memcpy(&req->rq_snd_buf, &rqstp->rq_res, sizeof(req->rq_snd_buf));
+       return bc_send(req);
+}
+EXPORT_SYMBOL(bc_svc_process);
+#endif /* CONFIG_NFS_V4_1 */
+
 /*
  * Return (transport-specific) limit on the rpc payload.
  */
index 9d504234af4a664f5f116171b9d6e02825d1ac4f..a2a03e500533a80f31a2c457bc198feb7448935c 100644 (file)
@@ -1327,3 +1327,42 @@ static void svc_sock_free(struct svc_xprt *xprt)
                sock_release(svsk->sk_sock);
        kfree(svsk);
 }
+
+/*
+ * Create a svc_xprt.
+ *
+ * For internal use only (e.g. nfsv4.1 backchannel).
+ * Callers should typically use the xpo_create() method.
+ */
+struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot)
+{
+       struct svc_sock *svsk;
+       struct svc_xprt *xprt = NULL;
+
+       dprintk("svc: %s\n", __func__);
+       svsk = kzalloc(sizeof(*svsk), GFP_KERNEL);
+       if (!svsk)
+               goto out;
+
+       xprt = &svsk->sk_xprt;
+       if (prot == IPPROTO_TCP)
+               svc_xprt_init(&svc_tcp_class, xprt, serv);
+       else if (prot == IPPROTO_UDP)
+               svc_xprt_init(&svc_udp_class, xprt, serv);
+       else
+               BUG();
+out:
+       dprintk("svc: %s return %p\n", __func__, xprt);
+       return xprt;
+}
+EXPORT_SYMBOL_GPL(svc_sock_create);
+
+/*
+ * Destroy a svc_sock.
+ */
+void svc_sock_destroy(struct svc_xprt *xprt)
+{
+       if (xprt)
+               kfree(container_of(xprt, struct svc_sock, sk_xprt));
+}
+EXPORT_SYMBOL_GPL(svc_sock_destroy);
index 06ca058572f236bb6fd322ff1686c0aaaa088abd..f412a852bc73d7f1eb59af470e2b87d25ca9fa58 100644 (file)
@@ -12,8 +12,9 @@
  *  -  Next, the caller puts together the RPC message, stuffs it into
  *     the request struct, and calls xprt_transmit().
  *  -  xprt_transmit sends the message and installs the caller on the
- *     transport's wait list. At the same time, it installs a timer that
- *     is run after the packet's timeout has expired.
+ *     transport's wait list. At the same time, if a reply is expected,
+ *     it installs a timer that is run after the packet's timeout has
+ *     expired.
  *  -  When a packet arrives, the data_ready handler walks the list of
  *     pending requests for that transport. If a matching XID is found, the
  *     caller is woken up, and the timer removed.
@@ -46,6 +47,8 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/metrics.h>
 
+#include "sunrpc.h"
+
 /*
  * Local variables
  */
@@ -192,8 +195,8 @@ EXPORT_SYMBOL_GPL(xprt_load_transport);
  */
 int xprt_reserve_xprt(struct rpc_task *task)
 {
-       struct rpc_xprt *xprt = task->tk_xprt;
        struct rpc_rqst *req = task->tk_rqstp;
+       struct rpc_xprt *xprt = req->rq_xprt;
 
        if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
                if (task == xprt->snd_task)
@@ -803,9 +806,10 @@ void xprt_complete_rqst(struct rpc_task *task, int copied)
 
        list_del_init(&req->rq_list);
        req->rq_private_buf.len = copied;
-       /* Ensure all writes are done before we update req->rq_received */
+       /* Ensure all writes are done before we update */
+       /* req->rq_reply_bytes_recvd */
        smp_wmb();
-       req->rq_received = copied;
+       req->rq_reply_bytes_recvd = copied;
        rpc_wake_up_queued_task(&xprt->pending, task);
 }
 EXPORT_SYMBOL_GPL(xprt_complete_rqst);
@@ -820,7 +824,7 @@ static void xprt_timer(struct rpc_task *task)
        dprintk("RPC: %5u xprt_timer\n", task->tk_pid);
 
        spin_lock_bh(&xprt->transport_lock);
-       if (!req->rq_received) {
+       if (!req->rq_reply_bytes_recvd) {
                if (xprt->ops->timer)
                        xprt->ops->timer(task);
        } else
@@ -842,8 +846,8 @@ int xprt_prepare_transmit(struct rpc_task *task)
        dprintk("RPC: %5u xprt_prepare_transmit\n", task->tk_pid);
 
        spin_lock_bh(&xprt->transport_lock);
-       if (req->rq_received && !req->rq_bytes_sent) {
-               err = req->rq_received;
+       if (req->rq_reply_bytes_recvd && !req->rq_bytes_sent) {
+               err = req->rq_reply_bytes_recvd;
                goto out_unlock;
        }
        if (!xprt->ops->reserve_xprt(task))
@@ -855,7 +859,7 @@ out_unlock:
 
 void xprt_end_transmit(struct rpc_task *task)
 {
-       xprt_release_write(task->tk_xprt, task);
+       xprt_release_write(task->tk_rqstp->rq_xprt, task);
 }
 
 /**
@@ -872,8 +876,11 @@ void xprt_transmit(struct rpc_task *task)
 
        dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen);
 
-       if (!req->rq_received) {
-               if (list_empty(&req->rq_list)) {
+       if (!req->rq_reply_bytes_recvd) {
+               if (list_empty(&req->rq_list) && rpc_reply_expected(task)) {
+                       /*
+                        * Add to the list only if we're expecting a reply
+                        */
                        spin_lock_bh(&xprt->transport_lock);
                        /* Update the softirq receive buffer */
                        memcpy(&req->rq_private_buf, &req->rq_rcv_buf,
@@ -908,8 +915,13 @@ void xprt_transmit(struct rpc_task *task)
        /* Don't race with disconnect */
        if (!xprt_connected(xprt))
                task->tk_status = -ENOTCONN;
-       else if (!req->rq_received)
+       else if (!req->rq_reply_bytes_recvd && rpc_reply_expected(task)) {
+               /*
+                * Sleep on the pending queue since
+                * we're expecting a reply.
+                */
                rpc_sleep_on(&xprt->pending, task, xprt_timer);
+       }
        spin_unlock_bh(&xprt->transport_lock);
 }
 
@@ -982,11 +994,17 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
  */
 void xprt_release(struct rpc_task *task)
 {
-       struct rpc_xprt *xprt = task->tk_xprt;
+       struct rpc_xprt *xprt;
        struct rpc_rqst *req;
+       int is_bc_request;
 
        if (!(req = task->tk_rqstp))
                return;
+
+       /* Preallocated backchannel request? */
+       is_bc_request = bc_prealloc(req);
+
+       xprt = req->rq_xprt;
        rpc_count_iostats(task);
        spin_lock_bh(&xprt->transport_lock);
        xprt->ops->release_xprt(xprt, task);
@@ -999,10 +1017,19 @@ void xprt_release(struct rpc_task *task)
                mod_timer(&xprt->timer,
                                xprt->last_used + xprt->idle_timeout);
        spin_unlock_bh(&xprt->transport_lock);
-       xprt->ops->buf_free(req->rq_buffer);
+       if (!bc_prealloc(req))
+               xprt->ops->buf_free(req->rq_buffer);
        task->tk_rqstp = NULL;
        if (req->rq_release_snd_buf)
                req->rq_release_snd_buf(req);
+
+       /*
+        * Early exit if this is a backchannel preallocated request.
+        * There is no need to have it added to the RPC slot list.
+        */
+       if (is_bc_request)
+               return;
+
        memset(req, 0, sizeof(*req));   /* mark unused */
 
        dprintk("RPC: %5u release request %p\n", task->tk_pid, req);
@@ -1049,6 +1076,11 @@ found:
 
        INIT_LIST_HEAD(&xprt->free);
        INIT_LIST_HEAD(&xprt->recv);
+#if defined(CONFIG_NFS_V4_1)
+       spin_lock_init(&xprt->bc_pa_lock);
+       INIT_LIST_HEAD(&xprt->bc_pa_list);
+#endif /* CONFIG_NFS_V4_1 */
+
        INIT_WORK(&xprt->task_cleanup, xprt_autoclose);
        setup_timer(&xprt->timer, xprt_init_autodisconnect,
                        (unsigned long)xprt);
index 6c2d61586551511ff303760923bf6a887e0ae7b1..9111d11c09fd5955c44b7ee2a9831a61597defb5 100644 (file)
@@ -34,6 +34,9 @@
 #include <linux/sunrpc/sched.h>
 #include <linux/sunrpc/xprtsock.h>
 #include <linux/file.h>
+#ifdef CONFIG_NFS_V4_1
+#include <linux/sunrpc/bc_xprt.h>
+#endif
 
 #include <net/sock.h>
 #include <net/checksum.h>
@@ -270,6 +273,13 @@ struct sock_xprt {
 #define TCP_RCV_COPY_FRAGHDR   (1UL << 1)
 #define TCP_RCV_COPY_XID       (1UL << 2)
 #define TCP_RCV_COPY_DATA      (1UL << 3)
+#define TCP_RCV_READ_CALLDIR   (1UL << 4)
+#define TCP_RCV_COPY_CALLDIR   (1UL << 5)
+
+/*
+ * TCP RPC flags
+ */
+#define TCP_RPC_REPLY          (1UL << 6)
 
 static inline struct sockaddr *xs_addr(struct rpc_xprt *xprt)
 {
@@ -956,7 +966,7 @@ static inline void xs_tcp_read_fraghdr(struct rpc_xprt *xprt, struct xdr_skb_rea
        transport->tcp_offset = 0;
 
        /* Sanity check of the record length */
-       if (unlikely(transport->tcp_reclen < 4)) {
+       if (unlikely(transport->tcp_reclen < 8)) {
                dprintk("RPC:       invalid TCP record fragment length\n");
                xprt_force_disconnect(xprt);
                return;
@@ -991,33 +1001,77 @@ static inline void xs_tcp_read_xid(struct sock_xprt *transport, struct xdr_skb_r
        if (used != len)
                return;
        transport->tcp_flags &= ~TCP_RCV_COPY_XID;
-       transport->tcp_flags |= TCP_RCV_COPY_DATA;
+       transport->tcp_flags |= TCP_RCV_READ_CALLDIR;
        transport->tcp_copied = 4;
-       dprintk("RPC:       reading reply for XID %08x\n",
+       dprintk("RPC:       reading %s XID %08x\n",
+                       (transport->tcp_flags & TCP_RPC_REPLY) ? "reply for"
+                                                             : "request with",
                        ntohl(transport->tcp_xid));
        xs_tcp_check_fraghdr(transport);
 }
 
-static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_reader *desc)
+static inline void xs_tcp_read_calldir(struct sock_xprt *transport,
+                                      struct xdr_skb_reader *desc)
 {
-       struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
-       struct rpc_rqst *req;
+       size_t len, used;
+       u32 offset;
+       __be32  calldir;
+
+       /*
+        * We want transport->tcp_offset to be 8 at the end of this routine
+        * (4 bytes for the xid and 4 bytes for the call/reply flag).
+        * When this function is called for the first time,
+        * transport->tcp_offset is 4 (after having already read the xid).
+        */
+       offset = transport->tcp_offset - sizeof(transport->tcp_xid);
+       len = sizeof(calldir) - offset;
+       dprintk("RPC:       reading CALL/REPLY flag (%Zu bytes)\n", len);
+       used = xdr_skb_read_bits(desc, &calldir, len);
+       transport->tcp_offset += used;
+       if (used != len)
+               return;
+       transport->tcp_flags &= ~TCP_RCV_READ_CALLDIR;
+       transport->tcp_flags |= TCP_RCV_COPY_CALLDIR;
+       transport->tcp_flags |= TCP_RCV_COPY_DATA;
+       /*
+        * We don't yet have the XDR buffer, so we will write the calldir
+        * out after we get the buffer from the 'struct rpc_rqst'
+        */
+       if (ntohl(calldir) == RPC_REPLY)
+               transport->tcp_flags |= TCP_RPC_REPLY;
+       else
+               transport->tcp_flags &= ~TCP_RPC_REPLY;
+       dprintk("RPC:       reading %s CALL/REPLY flag %08x\n",
+                       (transport->tcp_flags & TCP_RPC_REPLY) ?
+                               "reply for" : "request with", calldir);
+       xs_tcp_check_fraghdr(transport);
+}
+
+static inline void xs_tcp_read_common(struct rpc_xprt *xprt,
+                                    struct xdr_skb_reader *desc,
+                                    struct rpc_rqst *req)
+{
+       struct sock_xprt *transport =
+                               container_of(xprt, struct sock_xprt, xprt);
        struct xdr_buf *rcvbuf;
        size_t len;
        ssize_t r;
 
-       /* Find and lock the request corresponding to this xid */
-       spin_lock(&xprt->transport_lock);
-       req = xprt_lookup_rqst(xprt, transport->tcp_xid);
-       if (!req) {
-               transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
-               dprintk("RPC:       XID %08x request not found!\n",
-                               ntohl(transport->tcp_xid));
-               spin_unlock(&xprt->transport_lock);
-               return;
+       rcvbuf = &req->rq_private_buf;
+
+       if (transport->tcp_flags & TCP_RCV_COPY_CALLDIR) {
+               /*
+                * Save the RPC direction in the XDR buffer
+                */
+               __be32  calldir = transport->tcp_flags & TCP_RPC_REPLY ?
+                                       htonl(RPC_REPLY) : 0;
+
+               memcpy(rcvbuf->head[0].iov_base + transport->tcp_copied,
+                       &calldir, sizeof(calldir));
+               transport->tcp_copied += sizeof(calldir);
+               transport->tcp_flags &= ~TCP_RCV_COPY_CALLDIR;
        }
 
-       rcvbuf = &req->rq_private_buf;
        len = desc->count;
        if (len > transport->tcp_reclen - transport->tcp_offset) {
                struct xdr_skb_reader my_desc;
@@ -1054,7 +1108,7 @@ static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_rea
                                "tcp_offset = %u, tcp_reclen = %u\n",
                                xprt, transport->tcp_copied,
                                transport->tcp_offset, transport->tcp_reclen);
-               goto out;
+               return;
        }
 
        dprintk("RPC:       XID %08x read %Zd bytes\n",
@@ -1070,11 +1124,125 @@ static inline void xs_tcp_read_request(struct rpc_xprt *xprt, struct xdr_skb_rea
                        transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
        }
 
-out:
+       return;
+}
+
+/*
+ * Finds the request corresponding to the RPC xid and invokes the common
+ * tcp read code to read the data.
+ */
+static inline int xs_tcp_read_reply(struct rpc_xprt *xprt,
+                                   struct xdr_skb_reader *desc)
+{
+       struct sock_xprt *transport =
+                               container_of(xprt, struct sock_xprt, xprt);
+       struct rpc_rqst *req;
+
+       dprintk("RPC:       read reply XID %08x\n", ntohl(transport->tcp_xid));
+
+       /* Find and lock the request corresponding to this xid */
+       spin_lock(&xprt->transport_lock);
+       req = xprt_lookup_rqst(xprt, transport->tcp_xid);
+       if (!req) {
+               dprintk("RPC:       XID %08x request not found!\n",
+                               ntohl(transport->tcp_xid));
+               spin_unlock(&xprt->transport_lock);
+               return -1;
+       }
+
+       xs_tcp_read_common(xprt, desc, req);
+
        if (!(transport->tcp_flags & TCP_RCV_COPY_DATA))
                xprt_complete_rqst(req->rq_task, transport->tcp_copied);
+
        spin_unlock(&xprt->transport_lock);
-       xs_tcp_check_fraghdr(transport);
+       return 0;
+}
+
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * Obtains an rpc_rqst previously allocated and invokes the common
+ * tcp read code to read the data.  The result is placed in the callback
+ * queue.
+ * If we're unable to obtain the rpc_rqst we schedule the closing of the
+ * connection and return -1.
+ */
+static inline int xs_tcp_read_callback(struct rpc_xprt *xprt,
+                                      struct xdr_skb_reader *desc)
+{
+       struct sock_xprt *transport =
+                               container_of(xprt, struct sock_xprt, xprt);
+       struct rpc_rqst *req;
+
+       req = xprt_alloc_bc_request(xprt);
+       if (req == NULL) {
+               printk(KERN_WARNING "Callback slot table overflowed\n");
+               xprt_force_disconnect(xprt);
+               return -1;
+       }
+
+       req->rq_xid = transport->tcp_xid;
+       dprintk("RPC:       read callback  XID %08x\n", ntohl(req->rq_xid));
+       xs_tcp_read_common(xprt, desc, req);
+
+       if (!(transport->tcp_flags & TCP_RCV_COPY_DATA)) {
+               struct svc_serv *bc_serv = xprt->bc_serv;
+
+               /*
+                * Add callback request to callback list.  The callback
+                * service sleeps on the sv_cb_waitq waiting for new
+                * requests.  Wake it up after adding enqueing the
+                * request.
+                */
+               dprintk("RPC:       add callback request to list\n");
+               spin_lock(&bc_serv->sv_cb_lock);
+               list_add(&req->rq_bc_list, &bc_serv->sv_cb_list);
+               spin_unlock(&bc_serv->sv_cb_lock);
+               wake_up(&bc_serv->sv_cb_waitq);
+       }
+
+       req->rq_private_buf.len = transport->tcp_copied;
+
+       return 0;
+}
+
+static inline int _xs_tcp_read_data(struct rpc_xprt *xprt,
+                                       struct xdr_skb_reader *desc)
+{
+       struct sock_xprt *transport =
+                               container_of(xprt, struct sock_xprt, xprt);
+
+       return (transport->tcp_flags & TCP_RPC_REPLY) ?
+               xs_tcp_read_reply(xprt, desc) :
+               xs_tcp_read_callback(xprt, desc);
+}
+#else
+static inline int _xs_tcp_read_data(struct rpc_xprt *xprt,
+                                       struct xdr_skb_reader *desc)
+{
+       return xs_tcp_read_reply(xprt, desc);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+/*
+ * Read data off the transport.  This can be either an RPC_CALL or an
+ * RPC_REPLY.  Relay the processing to helper functions.
+ */
+static void xs_tcp_read_data(struct rpc_xprt *xprt,
+                                   struct xdr_skb_reader *desc)
+{
+       struct sock_xprt *transport =
+                               container_of(xprt, struct sock_xprt, xprt);
+
+       if (_xs_tcp_read_data(xprt, desc) == 0)
+               xs_tcp_check_fraghdr(transport);
+       else {
+               /*
+                * The transport_lock protects the request handling.
+                * There's no need to hold it to update the tcp_flags.
+                */
+               transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
+       }
 }
 
 static inline void xs_tcp_read_discard(struct sock_xprt *transport, struct xdr_skb_reader *desc)
@@ -1114,9 +1282,14 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns
                        xs_tcp_read_xid(transport, &desc);
                        continue;
                }
+               /* Read in the call/reply flag */
+               if (transport->tcp_flags & TCP_RCV_READ_CALLDIR) {
+                       xs_tcp_read_calldir(transport, &desc);
+                       continue;
+               }
                /* Read in the request data */
                if (transport->tcp_flags & TCP_RCV_COPY_DATA) {
-                       xs_tcp_read_request(xprt, &desc);
+                       xs_tcp_read_data(xprt, &desc);
                        continue;
                }
                /* Skip over any trailing bytes on short reads */
@@ -2010,6 +2183,9 @@ static struct rpc_xprt_ops xs_tcp_ops = {
        .buf_free               = rpc_free,
        .send_request           = xs_tcp_send_request,
        .set_retrans_timeout    = xprt_set_retrans_timeout_def,
+#if defined(CONFIG_NFS_V4_1)
+       .release_request        = bc_release_request,
+#endif /* CONFIG_NFS_V4_1 */
        .close                  = xs_tcp_close,
        .destroy                = xs_destroy,
        .print_stats            = xs_tcp_print_stats,