Merge branch 'bugfixes' into nfs-for-2.6.38
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Mon, 10 Jan 2011 19:48:02 +0000 (14:48 -0500)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Mon, 10 Jan 2011 19:48:02 +0000 (14:48 -0500)
Conflicts:
fs/nfs/nfs2xdr.c
fs/nfs/nfs3xdr.c
fs/nfs/nfs4xdr.c

58 files changed:
fs/lockd/Makefile
fs/lockd/clnt4xdr.c [new file with mode: 0644]
fs/lockd/clntlock.c
fs/lockd/clntproc.c
fs/lockd/clntxdr.c [new file with mode: 0644]
fs/lockd/host.c
fs/lockd/mon.c
fs/lockd/svc4proc.c
fs/lockd/svclock.c
fs/lockd/svcproc.c
fs/lockd/xdr.c
fs/lockd/xdr4.c
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/callback_xdr.c
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/idmap.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/mount_clnt.c
fs/nfs/nfs2xdr.c
fs/nfs/nfs3xdr.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4filelayout.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4renewd.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/pagelist.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h
fs/nfs/proc.c
fs/nfs/super.c
fs/nfs/unlink.c
fs/nfsd/nfs4callback.c
include/linux/lockd/debug.h
include/linux/lockd/lockd.h
include/linux/nfs3.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h
include/linux/sunrpc/auth.h
include/linux/sunrpc/bc_xprt.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/svc.h
include/linux/sunrpc/svc_xprt.h
include/linux/sunrpc/xdr.h
net/sunrpc/auth.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/bc_svc.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/rpcb_clnt.c
net/sunrpc/svc.c
net/sunrpc/svcsock.c

index 97f6073..ca58d64 100644 (file)
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_LOCKD) += lockd.o
 
-lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
-               svcproc.o svcsubs.o mon.o xdr.o grace.o
-lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o
+lockd-objs-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
+               svcshare.o svcproc.o svcsubs.o mon.o xdr.o grace.o
+lockd-objs-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o
 lockd-objs                   := $(lockd-objs-y)
diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c
new file mode 100644 (file)
index 0000000..f848b52
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * linux/fs/lockd/clnt4xdr.c
+ *
+ * XDR functions to encode/decode NLM version 4 RPC arguments and results.
+ *
+ * NLM client-side only.
+ *
+ * Copyright (C) 2010, Oracle.  All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+
+#define NLMDBG_FACILITY                NLMDBG_XDR
+
+#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
+#  error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
+#endif
+
+#if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN)
+#  error "NLM host name cannot be larger than NLM's maximum string length!"
+#endif
+
+/*
+ * Declare the space requirements for NLM arguments and replies as
+ * number of 32bit-words
+ */
+#define NLM4_void_sz           (0)
+#define NLM4_cookie_sz         (1+(NLM_MAXCOOKIELEN>>2))
+#define NLM4_caller_sz         (1+(NLMCLNT_OHSIZE>>2))
+#define NLM4_owner_sz          (1+(NLMCLNT_OHSIZE>>2))
+#define NLM4_fhandle_sz                (1+(NFS3_FHSIZE>>2))
+#define NLM4_lock_sz           (5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz)
+#define NLM4_holder_sz         (6+NLM4_owner_sz)
+
+#define NLM4_testargs_sz       (NLM4_cookie_sz+1+NLM4_lock_sz)
+#define NLM4_lockargs_sz       (NLM4_cookie_sz+4+NLM4_lock_sz)
+#define NLM4_cancargs_sz       (NLM4_cookie_sz+2+NLM4_lock_sz)
+#define NLM4_unlockargs_sz     (NLM4_cookie_sz+NLM4_lock_sz)
+
+#define NLM4_testres_sz                (NLM4_cookie_sz+1+NLM4_holder_sz)
+#define NLM4_res_sz            (NLM4_cookie_sz+1)
+#define NLM4_norep_sz          (0)
+
+
+static s64 loff_t_to_s64(loff_t offset)
+{
+       s64 res;
+
+       if (offset >= NLM4_OFFSET_MAX)
+               res = NLM4_OFFSET_MAX;
+       else if (offset <= -NLM4_OFFSET_MAX)
+               res = -NLM4_OFFSET_MAX;
+       else
+               res = offset;
+       return res;
+}
+
+static void nlm4_compute_offsets(const struct nlm_lock *lock,
+                                u64 *l_offset, u64 *l_len)
+{
+       const struct file_lock *fl = &lock->fl;
+
+       BUG_ON(fl->fl_start > NLM4_OFFSET_MAX);
+       BUG_ON(fl->fl_end > NLM4_OFFSET_MAX &&
+                               fl->fl_end != OFFSET_MAX);
+
+       *l_offset = loff_t_to_s64(fl->fl_start);
+       if (fl->fl_end == OFFSET_MAX)
+               *l_len = 0;
+       else
+               *l_len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
+}
+
+/*
+ * Handle decode buffer overflows out-of-line.
+ */
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
+{
+       dprintk("lockd: %s prematurely hit the end of our receive buffer. "
+               "Remaining buffer length is %tu words.\n",
+               func, xdr->end - xdr->p);
+}
+
+
+/*
+ * Encode/decode NLMv4 basic data types
+ *
+ * Basic NLMv4 data types are defined in Appendix II, section 6.1.4
+ * of RFC 1813: "NFS Version 3 Protocol Specification" and in Chapter
+ * 10 of X/Open's "Protocols for Interworking: XNFS, Version 3W".
+ *
+ * Not all basic data types have their own encoding and decoding
+ * functions.  For run-time efficiency, some data types are encoded
+ * or decoded inline.
+ */
+
+static void encode_bool(struct xdr_stream *xdr, const int value)
+{
+       __be32 *p;
+
+       p = xdr_reserve_space(xdr, 4);
+       *p = value ? xdr_one : xdr_zero;
+}
+
+static void encode_int32(struct xdr_stream *xdr, const s32 value)
+{
+       __be32 *p;
+
+       p = xdr_reserve_space(xdr, 4);
+       *p = cpu_to_be32(value);
+}
+
+/*
+ *     typedef opaque netobj<MAXNETOBJ_SZ>
+ */
+static void encode_netobj(struct xdr_stream *xdr,
+                         const u8 *data, const unsigned int length)
+{
+       __be32 *p;
+
+       BUG_ON(length > XDR_MAX_NETOBJ);
+       p = xdr_reserve_space(xdr, 4 + length);
+       xdr_encode_opaque(p, data, length);
+}
+
+static int decode_netobj(struct xdr_stream *xdr,
+                        struct xdr_netobj *obj)
+{
+       u32 length;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       length = be32_to_cpup(p++);
+       if (unlikely(length > XDR_MAX_NETOBJ))
+               goto out_size;
+       obj->len = length;
+       obj->data = (u8 *)p;
+       return 0;
+out_size:
+       dprintk("NFS: returned netobj was too long: %u\n", length);
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ *     netobj cookie;
+ */
+static void encode_cookie(struct xdr_stream *xdr,
+                         const struct nlm_cookie *cookie)
+{
+       BUG_ON(cookie->len > NLM_MAXCOOKIELEN);
+       encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
+}
+
+static int decode_cookie(struct xdr_stream *xdr,
+                            struct nlm_cookie *cookie)
+{
+       u32 length;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       length = be32_to_cpup(p++);
+       /* apparently HPUX can return empty cookies */
+       if (length == 0)
+               goto out_hpux;
+       if (length > NLM_MAXCOOKIELEN)
+               goto out_size;
+       p = xdr_inline_decode(xdr, length);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       cookie->len = length;
+       memcpy(cookie->data, p, length);
+       return 0;
+out_hpux:
+       cookie->len = 4;
+       memset(cookie->data, 0, 4);
+       return 0;
+out_size:
+       dprintk("NFS: returned cookie was too long: %u\n", length);
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ *     netobj fh;
+ */
+static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh)
+{
+       BUG_ON(fh->size > NFS3_FHSIZE);
+       encode_netobj(xdr, (u8 *)&fh->data, fh->size);
+}
+
+/*
+ *     enum nlm4_stats {
+ *             NLM4_GRANTED = 0,
+ *             NLM4_DENIED = 1,
+ *             NLM4_DENIED_NOLOCKS = 2,
+ *             NLM4_BLOCKED = 3,
+ *             NLM4_DENIED_GRACE_PERIOD = 4,
+ *             NLM4_DEADLCK = 5,
+ *             NLM4_ROFS = 6,
+ *             NLM4_STALE_FH = 7,
+ *             NLM4_FBIG = 8,
+ *             NLM4_FAILED = 9
+ *     };
+ *
+ *     struct nlm4_stat {
+ *             nlm4_stats stat;
+ *     };
+ *
+ * NB: we don't swap bytes for the NLM status values.  The upper
+ * layers deal directly with the status value in network byte
+ * order.
+ */
+static void encode_nlm4_stat(struct xdr_stream *xdr,
+                            const __be32 stat)
+{
+       __be32 *p;
+
+       BUG_ON(be32_to_cpu(stat) > NLM_FAILED);
+       p = xdr_reserve_space(xdr, 4);
+       *p = stat;
+}
+
+static int decode_nlm4_stat(struct xdr_stream *xdr, __be32 *stat)
+{
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       if (unlikely(*p > nlm4_failed))
+               goto out_bad_xdr;
+       *stat = *p;
+       return 0;
+out_bad_xdr:
+       dprintk("%s: server returned invalid nlm4_stats value: %u\n",
+                       __func__, be32_to_cpup(p));
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ *     struct nlm4_holder {
+ *             bool    exclusive;
+ *             int32   svid;
+ *             netobj  oh;
+ *             uint64  l_offset;
+ *             uint64  l_len;
+ *     };
+ */
+static void encode_nlm4_holder(struct xdr_stream *xdr,
+                              const struct nlm_res *result)
+{
+       const struct nlm_lock *lock = &result->lock;
+       u64 l_offset, l_len;
+       __be32 *p;
+
+       encode_bool(xdr, lock->fl.fl_type == F_RDLCK);
+       encode_int32(xdr, lock->svid);
+       encode_netobj(xdr, lock->oh.data, lock->oh.len);
+
+       p = xdr_reserve_space(xdr, 4 + 4);
+       nlm4_compute_offsets(lock, &l_offset, &l_len);
+       p = xdr_encode_hyper(p, l_offset);
+       xdr_encode_hyper(p, l_len);
+}
+
+static int decode_nlm4_holder(struct xdr_stream *xdr, struct nlm_res *result)
+{
+       struct nlm_lock *lock = &result->lock;
+       struct file_lock *fl = &lock->fl;
+       u64 l_offset, l_len;
+       u32 exclusive;
+       int error;
+       __be32 *p;
+       s32 end;
+
+       memset(lock, 0, sizeof(*lock));
+       locks_init_lock(fl);
+
+       p = xdr_inline_decode(xdr, 4 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       exclusive = be32_to_cpup(p++);
+       lock->svid = be32_to_cpup(p);
+       fl->fl_pid = (pid_t)lock->svid;
+
+       error = decode_netobj(xdr, &lock->oh);
+       if (unlikely(error))
+               goto out;
+
+       p = xdr_inline_decode(xdr, 8 + 8);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+
+       fl->fl_flags = FL_POSIX;
+       fl->fl_type  = exclusive != 0 ? F_WRLCK : F_RDLCK;
+       p = xdr_decode_hyper(p, &l_offset);
+       xdr_decode_hyper(p, &l_len);
+       end = l_offset + l_len - 1;
+
+       fl->fl_start = (loff_t)l_offset;
+       if (l_len == 0 || end < 0)
+               fl->fl_end = OFFSET_MAX;
+       else
+               fl->fl_end = (loff_t)end;
+       error = 0;
+out:
+       return error;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ *     string caller_name<LM_MAXSTRLEN>;
+ */
+static void encode_caller_name(struct xdr_stream *xdr, const char *name)
+{
+       /* NB: client-side does not set lock->len */
+       u32 length = strlen(name);
+       __be32 *p;
+
+       BUG_ON(length > NLM_MAXSTRLEN);
+       p = xdr_reserve_space(xdr, 4 + length);
+       xdr_encode_opaque(p, name, length);
+}
+
+/*
+ *     struct nlm4_lock {
+ *             string  caller_name<LM_MAXSTRLEN>;
+ *             netobj  fh;
+ *             netobj  oh;
+ *             int32   svid;
+ *             uint64  l_offset;
+ *             uint64  l_len;
+ *     };
+ */
+static void encode_nlm4_lock(struct xdr_stream *xdr,
+                            const struct nlm_lock *lock)
+{
+       u64 l_offset, l_len;
+       __be32 *p;
+
+       encode_caller_name(xdr, lock->caller);
+       encode_fh(xdr, &lock->fh);
+       encode_netobj(xdr, lock->oh.data, lock->oh.len);
+
+       p = xdr_reserve_space(xdr, 4 + 8 + 8);
+       *p++ = cpu_to_be32(lock->svid);
+
+       nlm4_compute_offsets(lock, &l_offset, &l_len);
+       p = xdr_encode_hyper(p, l_offset);
+       xdr_encode_hyper(p, l_len);
+}
+
+
+/*
+ * NLMv4 XDR encode functions
+ *
+ * NLMv4 argument types are defined in Appendix II of RFC 1813:
+ * "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's
+ * "Protocols for Interworking: XNFS, Version 3W".
+ */
+
+/*
+ *     struct nlm4_testargs {
+ *             netobj cookie;
+ *             bool exclusive;
+ *             struct nlm4_lock alock;
+ *     };
+ */
+static void nlm4_xdr_enc_testargs(struct rpc_rqst *req,
+                                 struct xdr_stream *xdr,
+                                 const struct nlm_args *args)
+{
+       const struct nlm_lock *lock = &args->lock;
+
+       encode_cookie(xdr, &args->cookie);
+       encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+       encode_nlm4_lock(xdr, lock);
+}
+
+/*
+ *     struct nlm4_lockargs {
+ *             netobj cookie;
+ *             bool block;
+ *             bool exclusive;
+ *             struct nlm4_lock alock;
+ *             bool reclaim;
+ *             int state;
+ *     };
+ */
+static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req,
+                                 struct xdr_stream *xdr,
+                                 const struct nlm_args *args)
+{
+       const struct nlm_lock *lock = &args->lock;
+
+       encode_cookie(xdr, &args->cookie);
+       encode_bool(xdr, args->block);
+       encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+       encode_nlm4_lock(xdr, lock);
+       encode_bool(xdr, args->reclaim);
+       encode_int32(xdr, args->state);
+}
+
+/*
+ *     struct nlm4_cancargs {
+ *             netobj cookie;
+ *             bool block;
+ *             bool exclusive;
+ *             struct nlm4_lock alock;
+ *     };
+ */
+static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req,
+                                 struct xdr_stream *xdr,
+                                 const struct nlm_args *args)
+{
+       const struct nlm_lock *lock = &args->lock;
+
+       encode_cookie(xdr, &args->cookie);
+       encode_bool(xdr, args->block);
+       encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+       encode_nlm4_lock(xdr, lock);
+}
+
+/*
+ *     struct nlm4_unlockargs {
+ *             netobj cookie;
+ *             struct nlm4_lock alock;
+ *     };
+ */
+static void nlm4_xdr_enc_unlockargs(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   const struct nlm_args *args)
+{
+       const struct nlm_lock *lock = &args->lock;
+
+       encode_cookie(xdr, &args->cookie);
+       encode_nlm4_lock(xdr, lock);
+}
+
+/*
+ *     struct nlm4_res {
+ *             netobj cookie;
+ *             nlm4_stat stat;
+ *     };
+ */
+static void nlm4_xdr_enc_res(struct rpc_rqst *req,
+                            struct xdr_stream *xdr,
+                            const struct nlm_res *result)
+{
+       encode_cookie(xdr, &result->cookie);
+       encode_nlm4_stat(xdr, result->status);
+}
+
+/*
+ *     union nlm4_testrply switch (nlm4_stats stat) {
+ *     case NLM4_DENIED:
+ *             struct nlm4_holder holder;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     struct nlm4_testres {
+ *             netobj cookie;
+ *             nlm4_testrply test_stat;
+ *     };
+ */
+static void nlm4_xdr_enc_testres(struct rpc_rqst *req,
+                                struct xdr_stream *xdr,
+                                const struct nlm_res *result)
+{
+       encode_cookie(xdr, &result->cookie);
+       encode_nlm4_stat(xdr, result->status);
+       if (result->status == nlm_lck_denied)
+               encode_nlm4_holder(xdr, result);
+}
+
+
+/*
+ * NLMv4 XDR decode functions
+ *
+ * NLMv4 argument types are defined in Appendix II of RFC 1813:
+ * "NFS Version 3 Protocol Specification" and Chapter 10 of X/Open's
+ * "Protocols for Interworking: XNFS, Version 3W".
+ */
+
+/*
+ *     union nlm4_testrply switch (nlm4_stats stat) {
+ *     case NLM4_DENIED:
+ *             struct nlm4_holder holder;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     struct nlm4_testres {
+ *             netobj cookie;
+ *             nlm4_testrply test_stat;
+ *     };
+ */
+static int decode_nlm4_testrply(struct xdr_stream *xdr,
+                               struct nlm_res *result)
+{
+       int error;
+
+       error = decode_nlm4_stat(xdr, &result->status);
+       if (unlikely(error))
+               goto out;
+       if (result->status == nlm_lck_denied)
+               error = decode_nlm4_holder(xdr, result);
+out:
+       return error;
+}
+
+static int nlm4_xdr_dec_testres(struct rpc_rqst *req,
+                               struct xdr_stream *xdr,
+                               struct nlm_res *result)
+{
+       int error;
+
+       error = decode_cookie(xdr, &result->cookie);
+       if (unlikely(error))
+               goto out;
+       error = decode_nlm4_testrply(xdr, result);
+out:
+       return error;
+}
+
+/*
+ *     struct nlm4_res {
+ *             netobj cookie;
+ *             nlm4_stat stat;
+ *     };
+ */
+static int nlm4_xdr_dec_res(struct rpc_rqst *req,
+                           struct xdr_stream *xdr,
+                           struct nlm_res *result)
+{
+       int error;
+
+       error = decode_cookie(xdr, &result->cookie);
+       if (unlikely(error))
+               goto out;
+       error = decode_nlm4_stat(xdr, &result->status);
+out:
+       return error;
+}
+
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlm4_xdr_dec_norep     NULL
+
+#define PROC(proc, argtype, restype)                                   \
+[NLMPROC_##proc] = {                                                   \
+       .p_proc      = NLMPROC_##proc,                                  \
+       .p_encode    = (kxdreproc_t)nlm4_xdr_enc_##argtype,             \
+       .p_decode    = (kxdrdproc_t)nlm4_xdr_dec_##restype,             \
+       .p_arglen    = NLM4_##argtype##_sz,                             \
+       .p_replen    = NLM4_##restype##_sz,                             \
+       .p_statidx   = NLMPROC_##proc,                                  \
+       .p_name      = #proc,                                           \
+       }
+
+static struct rpc_procinfo     nlm4_procedures[] = {
+       PROC(TEST,              testargs,       testres),
+       PROC(LOCK,              lockargs,       res),
+       PROC(CANCEL,            cancargs,       res),
+       PROC(UNLOCK,            unlockargs,     res),
+       PROC(GRANTED,           testargs,       res),
+       PROC(TEST_MSG,          testargs,       norep),
+       PROC(LOCK_MSG,          lockargs,       norep),
+       PROC(CANCEL_MSG,        cancargs,       norep),
+       PROC(UNLOCK_MSG,        unlockargs,     norep),
+       PROC(GRANTED_MSG,       testargs,       norep),
+       PROC(TEST_RES,          testres,        norep),
+       PROC(LOCK_RES,          res,            norep),
+       PROC(CANCEL_RES,        res,            norep),
+       PROC(UNLOCK_RES,        res,            norep),
+       PROC(GRANTED_RES,       res,            norep),
+};
+
+struct rpc_version     nlm_version4 = {
+       .number         = 4,
+       .nrprocs        = ARRAY_SIZE(nlm4_procedures),
+       .procs          = nlm4_procedures,
+};
index 25509eb..8d4ea83 100644 (file)
@@ -79,7 +79,7 @@ EXPORT_SYMBOL_GPL(nlmclnt_init);
  */
 void nlmclnt_done(struct nlm_host *host)
 {
-       nlm_release_host(host);
+       nlmclnt_release_host(host);
        lockd_down();
 }
 EXPORT_SYMBOL_GPL(nlmclnt_done);
@@ -273,7 +273,7 @@ restart:
        spin_unlock(&nlm_blocked_lock);
 
        /* Release host handle after use */
-       nlm_release_host(host);
+       nlmclnt_release_host(host);
        lockd_down();
        return 0;
 }
index 332c54c..adb45ec 100644 (file)
@@ -58,7 +58,7 @@ static void nlm_put_lockowner(struct nlm_lockowner *lockowner)
                return;
        list_del(&lockowner->list);
        spin_unlock(&lockowner->host->h_lock);
-       nlm_release_host(lockowner->host);
+       nlmclnt_release_host(lockowner->host);
        kfree(lockowner);
 }
 
@@ -207,22 +207,22 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
                printk("nlm_alloc_call: failed, waiting for memory\n");
                schedule_timeout_interruptible(5*HZ);
        }
-       nlm_release_host(host);
+       nlmclnt_release_host(host);
        return NULL;
 }
 
-void nlm_release_call(struct nlm_rqst *call)
+void nlmclnt_release_call(struct nlm_rqst *call)
 {
        if (!atomic_dec_and_test(&call->a_count))
                return;
-       nlm_release_host(call->a_host);
+       nlmclnt_release_host(call->a_host);
        nlmclnt_release_lockargs(call);
        kfree(call);
 }
 
 static void nlmclnt_rpc_release(void *data)
 {
-       nlm_release_call(data);
+       nlmclnt_release_call(data);
 }
 
 static int nlm_wait_on_grace(wait_queue_head_t *queue)
@@ -436,7 +436,7 @@ nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
                        status = nlm_stat_to_errno(req->a_res.status);
        }
 out:
-       nlm_release_call(req);
+       nlmclnt_release_call(req);
        return status;
 }
 
@@ -593,7 +593,7 @@ again:
 out_unblock:
        nlmclnt_finish_block(block);
 out:
-       nlm_release_call(req);
+       nlmclnt_release_call(req);
        return status;
 out_unlock:
        /* Fatal error: ensure that we remove the lock altogether */
@@ -694,7 +694,7 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
        /* What to do now? I'm out of my depth... */
        status = -ENOLCK;
 out:
-       nlm_release_call(req);
+       nlmclnt_release_call(req);
        return status;
 }
 
@@ -755,7 +755,7 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
                        NLMPROC_CANCEL, &nlmclnt_cancel_ops);
        if (status == 0 && req->a_res.status == nlm_lck_denied)
                status = -ENOLCK;
-       nlm_release_call(req);
+       nlmclnt_release_call(req);
        return status;
 }
 
diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c
new file mode 100644 (file)
index 0000000..180ac34
--- /dev/null
@@ -0,0 +1,627 @@
+/*
+ * linux/fs/lockd/clntxdr.c
+ *
+ * XDR functions to encode/decode NLM version 3 RPC arguments and results.
+ * NLM version 3 is backwards compatible with NLM versions 1 and 2.
+ *
+ * NLM client-side only.
+ *
+ * Copyright (C) 2010, Oracle.  All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+
+#define NLMDBG_FACILITY                NLMDBG_XDR
+
+#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
+#  error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
+#endif
+
+/*
+ * Declare the space requirements for NLM arguments and replies as
+ * number of 32bit-words
+ */
+#define NLM_cookie_sz          (1+(NLM_MAXCOOKIELEN>>2))
+#define NLM_caller_sz          (1+(NLMCLNT_OHSIZE>>2))
+#define NLM_owner_sz           (1+(NLMCLNT_OHSIZE>>2))
+#define NLM_fhandle_sz         (1+(NFS2_FHSIZE>>2))
+#define NLM_lock_sz            (3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz)
+#define NLM_holder_sz          (4+NLM_owner_sz)
+
+#define NLM_testargs_sz                (NLM_cookie_sz+1+NLM_lock_sz)
+#define NLM_lockargs_sz                (NLM_cookie_sz+4+NLM_lock_sz)
+#define NLM_cancargs_sz                (NLM_cookie_sz+2+NLM_lock_sz)
+#define NLM_unlockargs_sz      (NLM_cookie_sz+NLM_lock_sz)
+
+#define NLM_testres_sz         (NLM_cookie_sz+1+NLM_holder_sz)
+#define NLM_res_sz             (NLM_cookie_sz+1)
+#define NLM_norep_sz           (0)
+
+
+static s32 loff_t_to_s32(loff_t offset)
+{
+       s32 res;
+
+       if (offset >= NLM_OFFSET_MAX)
+               res = NLM_OFFSET_MAX;
+       else if (offset <= -NLM_OFFSET_MAX)
+               res = -NLM_OFFSET_MAX;
+       else
+               res = offset;
+       return res;
+}
+
+static void nlm_compute_offsets(const struct nlm_lock *lock,
+                               u32 *l_offset, u32 *l_len)
+{
+       const struct file_lock *fl = &lock->fl;
+
+       BUG_ON(fl->fl_start > NLM_OFFSET_MAX);
+       BUG_ON(fl->fl_end > NLM_OFFSET_MAX &&
+                               fl->fl_end != OFFSET_MAX);
+
+       *l_offset = loff_t_to_s32(fl->fl_start);
+       if (fl->fl_end == OFFSET_MAX)
+               *l_len = 0;
+       else
+               *l_len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
+}
+
+/*
+ * Handle decode buffer overflows out-of-line.
+ */
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
+{
+       dprintk("lockd: %s prematurely hit the end of our receive buffer. "
+               "Remaining buffer length is %tu words.\n",
+               func, xdr->end - xdr->p);
+}
+
+
+/*
+ * Encode/decode NLMv3 basic data types
+ *
+ * Basic NLMv3 data types are not defined in an IETF standards
+ * document.  X/Open has a description of these data types that
+ * is useful.  See Chapter 10 of "Protocols for Interworking:
+ * XNFS, Version 3W".
+ *
+ * Not all basic data types have their own encoding and decoding
+ * functions.  For run-time efficiency, some data types are encoded
+ * or decoded inline.
+ */
+
+static void encode_bool(struct xdr_stream *xdr, const int value)
+{
+       __be32 *p;
+
+       p = xdr_reserve_space(xdr, 4);
+       *p = value ? xdr_one : xdr_zero;
+}
+
+static void encode_int32(struct xdr_stream *xdr, const s32 value)
+{
+       __be32 *p;
+
+       p = xdr_reserve_space(xdr, 4);
+       *p = cpu_to_be32(value);
+}
+
+/*
+ *     typedef opaque netobj<MAXNETOBJ_SZ>
+ */
+static void encode_netobj(struct xdr_stream *xdr,
+                         const u8 *data, const unsigned int length)
+{
+       __be32 *p;
+
+       BUG_ON(length > XDR_MAX_NETOBJ);
+       p = xdr_reserve_space(xdr, 4 + length);
+       xdr_encode_opaque(p, data, length);
+}
+
+static int decode_netobj(struct xdr_stream *xdr,
+                        struct xdr_netobj *obj)
+{
+       u32 length;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       length = be32_to_cpup(p++);
+       if (unlikely(length > XDR_MAX_NETOBJ))
+               goto out_size;
+       obj->len = length;
+       obj->data = (u8 *)p;
+       return 0;
+out_size:
+       dprintk("NFS: returned netobj was too long: %u\n", length);
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ *     netobj cookie;
+ */
+static void encode_cookie(struct xdr_stream *xdr,
+                         const struct nlm_cookie *cookie)
+{
+       BUG_ON(cookie->len > NLM_MAXCOOKIELEN);
+       encode_netobj(xdr, (u8 *)&cookie->data, cookie->len);
+}
+
+static int decode_cookie(struct xdr_stream *xdr,
+                        struct nlm_cookie *cookie)
+{
+       u32 length;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       length = be32_to_cpup(p++);
+       /* apparently HPUX can return empty cookies */
+       if (length == 0)
+               goto out_hpux;
+       if (length > NLM_MAXCOOKIELEN)
+               goto out_size;
+       p = xdr_inline_decode(xdr, length);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       cookie->len = length;
+       memcpy(cookie->data, p, length);
+       return 0;
+out_hpux:
+       cookie->len = 4;
+       memset(cookie->data, 0, 4);
+       return 0;
+out_size:
+       dprintk("NFS: returned cookie was too long: %u\n", length);
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ *     netobj fh;
+ */
+static void encode_fh(struct xdr_stream *xdr, const struct nfs_fh *fh)
+{
+       BUG_ON(fh->size != NFS2_FHSIZE);
+       encode_netobj(xdr, (u8 *)&fh->data, NFS2_FHSIZE);
+}
+
+/*
+ *     enum nlm_stats {
+ *             LCK_GRANTED = 0,
+ *             LCK_DENIED = 1,
+ *             LCK_DENIED_NOLOCKS = 2,
+ *             LCK_BLOCKED = 3,
+ *             LCK_DENIED_GRACE_PERIOD = 4
+ *     };
+ *
+ *
+ *     struct nlm_stat {
+ *             nlm_stats stat;
+ *     };
+ *
+ * NB: we don't swap bytes for the NLM status values.  The upper
+ * layers deal directly with the status value in network byte
+ * order.
+ */
+
+static void encode_nlm_stat(struct xdr_stream *xdr,
+                           const __be32 stat)
+{
+       __be32 *p;
+
+       BUG_ON(be32_to_cpu(stat) > NLM_LCK_DENIED_GRACE_PERIOD);
+       p = xdr_reserve_space(xdr, 4);
+       *p = stat;
+}
+
+static int decode_nlm_stat(struct xdr_stream *xdr,
+                          __be32 *stat)
+{
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       if (unlikely(*p > nlm_lck_denied_grace_period))
+               goto out_enum;
+       *stat = *p;
+       return 0;
+out_enum:
+       dprintk("%s: server returned invalid nlm_stats value: %u\n",
+               __func__, be32_to_cpup(p));
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ *     struct nlm_holder {
+ *             bool exclusive;
+ *             int uppid;
+ *             netobj oh;
+ *             unsigned l_offset;
+ *             unsigned l_len;
+ *     };
+ */
+static void encode_nlm_holder(struct xdr_stream *xdr,
+                             const struct nlm_res *result)
+{
+       const struct nlm_lock *lock = &result->lock;
+       u32 l_offset, l_len;
+       __be32 *p;
+
+       encode_bool(xdr, lock->fl.fl_type == F_RDLCK);
+       encode_int32(xdr, lock->svid);
+       encode_netobj(xdr, lock->oh.data, lock->oh.len);
+
+       p = xdr_reserve_space(xdr, 4 + 4);
+       nlm_compute_offsets(lock, &l_offset, &l_len);
+       *p++ = cpu_to_be32(l_offset);
+       *p   = cpu_to_be32(l_len);
+}
+
+static int decode_nlm_holder(struct xdr_stream *xdr, struct nlm_res *result)
+{
+       struct nlm_lock *lock = &result->lock;
+       struct file_lock *fl = &lock->fl;
+       u32 exclusive, l_offset, l_len;
+       int error;
+       __be32 *p;
+       s32 end;
+
+       memset(lock, 0, sizeof(*lock));
+       locks_init_lock(fl);
+
+       p = xdr_inline_decode(xdr, 4 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       exclusive = be32_to_cpup(p++);
+       lock->svid = be32_to_cpup(p);
+       fl->fl_pid = (pid_t)lock->svid;
+
+       error = decode_netobj(xdr, &lock->oh);
+       if (unlikely(error))
+               goto out;
+
+       p = xdr_inline_decode(xdr, 4 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+
+       fl->fl_flags = FL_POSIX;
+       fl->fl_type  = exclusive != 0 ? F_WRLCK : F_RDLCK;
+       l_offset = be32_to_cpup(p++);
+       l_len = be32_to_cpup(p);
+       end = l_offset + l_len - 1;
+
+       fl->fl_start = (loff_t)l_offset;
+       if (l_len == 0 || end < 0)
+               fl->fl_end = OFFSET_MAX;
+       else
+               fl->fl_end = (loff_t)end;
+       error = 0;
+out:
+       return error;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ *     string caller_name<LM_MAXSTRLEN>;
+ */
+static void encode_caller_name(struct xdr_stream *xdr, const char *name)
+{
+       /* NB: client-side does not set lock->len */
+       u32 length = strlen(name);
+       __be32 *p;
+
+       BUG_ON(length > NLM_MAXSTRLEN);
+       p = xdr_reserve_space(xdr, 4 + length);
+       xdr_encode_opaque(p, name, length);
+}
+
+/*
+ *     struct nlm_lock {
+ *             string caller_name<LM_MAXSTRLEN>;
+ *             netobj fh;
+ *             netobj oh;
+ *             int uppid;
+ *             unsigned l_offset;
+ *             unsigned l_len;
+ *     };
+ */
+static void encode_nlm_lock(struct xdr_stream *xdr,
+                           const struct nlm_lock *lock)
+{
+       u32 l_offset, l_len;
+       __be32 *p;
+
+       encode_caller_name(xdr, lock->caller);
+       encode_fh(xdr, &lock->fh);
+       encode_netobj(xdr, lock->oh.data, lock->oh.len);
+
+       p = xdr_reserve_space(xdr, 4 + 4 + 4);
+       *p++ = cpu_to_be32(lock->svid);
+
+       nlm_compute_offsets(lock, &l_offset, &l_len);
+       *p++ = cpu_to_be32(l_offset);
+       *p   = cpu_to_be32(l_len);
+}
+
+
+/*
+ * NLMv3 XDR encode functions
+ *
+ * NLMv3 argument types are defined in Chapter 10 of The Open Group's
+ * "Protocols for Interworking: XNFS, Version 3W".
+ */
+
+/*
+ *     struct nlm_testargs {
+ *             netobj cookie;
+ *             bool exclusive;
+ *             struct nlm_lock alock;
+ *     };
+ */
+static void nlm_xdr_enc_testargs(struct rpc_rqst *req,
+                                struct xdr_stream *xdr,
+                                const struct nlm_args *args)
+{
+       const struct nlm_lock *lock = &args->lock;
+
+       encode_cookie(xdr, &args->cookie);
+       encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+       encode_nlm_lock(xdr, lock);
+}
+
+/*
+ *     struct nlm_lockargs {
+ *             netobj cookie;
+ *             bool block;
+ *             bool exclusive;
+ *             struct nlm_lock alock;
+ *             bool reclaim;
+ *             int state;
+ *     };
+ */
+static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,
+                                struct xdr_stream *xdr,
+                                const struct nlm_args *args)
+{
+       const struct nlm_lock *lock = &args->lock;
+
+       encode_cookie(xdr, &args->cookie);
+       encode_bool(xdr, args->block);
+       encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+       encode_nlm_lock(xdr, lock);
+       encode_bool(xdr, args->reclaim);
+       encode_int32(xdr, args->state);
+}
+
+/*
+ *     struct nlm_cancargs {
+ *             netobj cookie;
+ *             bool block;
+ *             bool exclusive;
+ *             struct nlm_lock alock;
+ *     };
+ */
+static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,
+                                struct xdr_stream *xdr,
+                                const struct nlm_args *args)
+{
+       const struct nlm_lock *lock = &args->lock;
+
+       encode_cookie(xdr, &args->cookie);
+       encode_bool(xdr, args->block);
+       encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
+       encode_nlm_lock(xdr, lock);
+}
+
+/*
+ *     struct nlm_unlockargs {
+ *             netobj cookie;
+ *             struct nlm_lock alock;
+ *     };
+ */
+static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  const struct nlm_args *args)
+{
+       const struct nlm_lock *lock = &args->lock;
+
+       encode_cookie(xdr, &args->cookie);
+       encode_nlm_lock(xdr, lock);
+}
+
+/*
+ *     struct nlm_res {
+ *             netobj cookie;
+ *             nlm_stat stat;
+ *     };
+ */
+static void nlm_xdr_enc_res(struct rpc_rqst *req,
+                           struct xdr_stream *xdr,
+                           const struct nlm_res *result)
+{
+       encode_cookie(xdr, &result->cookie);
+       encode_nlm_stat(xdr, result->status);
+}
+
+/*
+ *     union nlm_testrply switch (nlm_stats stat) {
+ *     case LCK_DENIED:
+ *             struct nlm_holder holder;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     struct nlm_testres {
+ *             netobj cookie;
+ *             nlm_testrply test_stat;
+ *     };
+ */
+static void encode_nlm_testrply(struct xdr_stream *xdr,
+                               const struct nlm_res *result)
+{
+       if (result->status == nlm_lck_denied)
+               encode_nlm_holder(xdr, result);
+}
+
+static void nlm_xdr_enc_testres(struct rpc_rqst *req,
+                               struct xdr_stream *xdr,
+                               const struct nlm_res *result)
+{
+       encode_cookie(xdr, &result->cookie);
+       encode_nlm_stat(xdr, result->status);
+       encode_nlm_testrply(xdr, result);
+}
+
+
+/*
+ * NLMv3 XDR decode functions
+ *
+ * NLMv3 result types are defined in Chapter 10 of The Open Group's
+ * "Protocols for Interworking: XNFS, Version 3W".
+ */
+
+/*
+ *     union nlm_testrply switch (nlm_stats stat) {
+ *     case LCK_DENIED:
+ *             struct nlm_holder holder;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     struct nlm_testres {
+ *             netobj cookie;
+ *             nlm_testrply test_stat;
+ *     };
+ */
+static int decode_nlm_testrply(struct xdr_stream *xdr,
+                              struct nlm_res *result)
+{
+       int error;
+
+       error = decode_nlm_stat(xdr, &result->status);
+       if (unlikely(error))
+               goto out;
+       if (result->status == nlm_lck_denied)
+               error = decode_nlm_holder(xdr, result);
+out:
+       return error;
+}
+
+static int nlm_xdr_dec_testres(struct rpc_rqst *req,
+                              struct xdr_stream *xdr,
+                              struct nlm_res *result)
+{
+       int error;
+
+       error = decode_cookie(xdr, &result->cookie);
+       if (unlikely(error))
+               goto out;
+       error = decode_nlm_testrply(xdr, result);
+out:
+       return error;
+}
+
+/*
+ *     struct nlm_res {
+ *             netobj cookie;
+ *             nlm_stat stat;
+ *     };
+ */
+static int nlm_xdr_dec_res(struct rpc_rqst *req,
+                          struct xdr_stream *xdr,
+                          struct nlm_res *result)
+{
+       int error;
+
+       error = decode_cookie(xdr, &result->cookie);
+       if (unlikely(error))
+               goto out;
+       error = decode_nlm_stat(xdr, &result->status);
+out:
+       return error;
+}
+
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlm_xdr_dec_norep      NULL
+
+#define PROC(proc, argtype, restype)   \
+[NLMPROC_##proc] = {                                                   \
+       .p_proc      = NLMPROC_##proc,                                  \
+       .p_encode    = (kxdreproc_t)nlm_xdr_enc_##argtype,              \
+       .p_decode    = (kxdrdproc_t)nlm_xdr_dec_##restype,              \
+       .p_arglen    = NLM_##argtype##_sz,                              \
+       .p_replen    = NLM_##restype##_sz,                              \
+       .p_statidx   = NLMPROC_##proc,                                  \
+       .p_name      = #proc,                                           \
+       }
+
+static struct rpc_procinfo     nlm_procedures[] = {
+       PROC(TEST,              testargs,       testres),
+       PROC(LOCK,              lockargs,       res),
+       PROC(CANCEL,            cancargs,       res),
+       PROC(UNLOCK,            unlockargs,     res),
+       PROC(GRANTED,           testargs,       res),
+       PROC(TEST_MSG,          testargs,       norep),
+       PROC(LOCK_MSG,          lockargs,       norep),
+       PROC(CANCEL_MSG,        cancargs,       norep),
+       PROC(UNLOCK_MSG,        unlockargs,     norep),
+       PROC(GRANTED_MSG,       testargs,       norep),
+       PROC(TEST_RES,          testres,        norep),
+       PROC(LOCK_RES,          res,            norep),
+       PROC(CANCEL_RES,        res,            norep),
+       PROC(UNLOCK_RES,        res,            norep),
+       PROC(GRANTED_RES,       res,            norep),
+};
+
+static struct rpc_version      nlm_version1 = {
+               .number         = 1,
+               .nrprocs        = ARRAY_SIZE(nlm_procedures),
+               .procs          = nlm_procedures,
+};
+
+static struct rpc_version      nlm_version3 = {
+               .number         = 3,
+               .nrprocs        = ARRAY_SIZE(nlm_procedures),
+               .procs          = nlm_procedures,
+};
+
+static struct rpc_version      *nlm_versions[] = {
+       [1] = &nlm_version1,
+       [3] = &nlm_version3,
+#ifdef CONFIG_LOCKD_V4
+       [4] = &nlm_version4,
+#endif
+};
+
+static struct rpc_stat         nlm_rpc_stats;
+
+struct rpc_program             nlm_program = {
+               .name           = "lockd",
+               .number         = NLM_PROGRAM,
+               .nrvers         = ARRAY_SIZE(nlm_versions),
+               .version        = nlm_versions,
+               .stats          = &nlm_rpc_stats,
+};
index ed0c59f..5f1bcb2 100644 (file)
 #define NLM_HOST_EXPIRE                (300 * HZ)
 #define NLM_HOST_COLLECT       (120 * HZ)
 
-static struct hlist_head       nlm_hosts[NLM_HOST_NRHASH];
+static struct hlist_head       nlm_server_hosts[NLM_HOST_NRHASH];
+static struct hlist_head       nlm_client_hosts[NLM_HOST_NRHASH];
+
+#define for_each_host(host, pos, chain, table) \
+       for ((chain) = (table); \
+            (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
+               hlist_for_each_entry((host), (pos), (chain), h_hash)
+
+#define for_each_host_safe(host, pos, next, chain, table) \
+       for ((chain) = (table); \
+            (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
+               hlist_for_each_entry_safe((host), (pos), (next), \
+                                               (chain), h_hash)
+
 static unsigned long           next_gc;
-static int                     nrhosts;
+static unsigned long           nrhosts;
 static DEFINE_MUTEX(nlm_host_mutex);
 
 static void                    nlm_gc_hosts(void);
@@ -40,8 +53,6 @@ struct nlm_lookup_host_info {
        const u32               version;        /* NLM version to search for */
        const char              *hostname;      /* remote's hostname */
        const size_t            hostname_len;   /* it's length */
-       const struct sockaddr   *src_sap;       /* our address (optional) */
-       const size_t            src_len;        /* it's length */
        const int               noresvport;     /* use non-priv port */
 };
 
@@ -88,127 +99,83 @@ static unsigned int nlm_hash_address(const struct sockaddr *sap)
 }
 
 /*
- * Common host lookup routine for server & client
+ * Allocate and initialize an nlm_host.  Common to both client and server.
  */
-static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
+static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
+                                      struct nsm_handle *nsm)
 {
-       struct hlist_head *chain;
-       struct hlist_node *pos;
-       struct nlm_host *host;
-       struct nsm_handle *nsm = NULL;
-
-       mutex_lock(&nlm_host_mutex);
+       struct nlm_host *host = NULL;
+       unsigned long now = jiffies;
 
-       if (time_after_eq(jiffies, next_gc))
-               nlm_gc_hosts();
-
-       /* We may keep several nlm_host objects for a peer, because each
-        * nlm_host is identified by
-        * (address, protocol, version, server/client)
-        * We could probably simplify this a little by putting all those
-        * different NLM rpc_clients into one single nlm_host object.
-        * This would allow us to have one nlm_host per address.
-        */
-       chain = &nlm_hosts[nlm_hash_address(ni->sap)];
-       hlist_for_each_entry(host, pos, chain, h_hash) {
-               if (!rpc_cmp_addr(nlm_addr(host), ni->sap))
-                       continue;
-
-               /* See if we have an NSM handle for this client */
-               if (!nsm)
-                       nsm = host->h_nsmhandle;
-
-               if (host->h_proto != ni->protocol)
-                       continue;
-               if (host->h_version != ni->version)
-                       continue;
-               if (host->h_server != ni->server)
-                       continue;
-               if (ni->server && ni->src_len != 0 &&
-                   !rpc_cmp_addr(nlm_srcaddr(host), ni->src_sap))
-                       continue;
-
-               /* Move to head of hash chain. */
-               hlist_del(&host->h_hash);
-               hlist_add_head(&host->h_hash, chain);
-
-               nlm_get_host(host);
-               dprintk("lockd: nlm_lookup_host found host %s (%s)\n",
-                               host->h_name, host->h_addrbuf);
-               goto out;
-       }
-
-       /*
-        * The host wasn't in our hash table.  If we don't
-        * have an NSM handle for it yet, create one.
-        */
-       if (nsm)
+       if (nsm != NULL)
                atomic_inc(&nsm->sm_count);
        else {
                host = NULL;
                nsm = nsm_get_handle(ni->sap, ni->salen,
                                        ni->hostname, ni->hostname_len);
-               if (!nsm) {
-                       dprintk("lockd: nlm_lookup_host failed; "
-                               "no nsm handle\n");
+               if (unlikely(nsm == NULL)) {
+                       dprintk("lockd: %s failed; no nsm handle\n",
+                               __func__);
                        goto out;
                }
        }
 
-       host = kzalloc(sizeof(*host), GFP_KERNEL);
-       if (!host) {
+       host = kmalloc(sizeof(*host), GFP_KERNEL);
+       if (unlikely(host == NULL)) {
+               dprintk("lockd: %s failed; no memory\n", __func__);
                nsm_release(nsm);
-               dprintk("lockd: nlm_lookup_host failed; no memory\n");
                goto out;
        }
-       host->h_name       = nsm->sm_name;
-       host->h_addrbuf    = nsm->sm_addrbuf;
+
        memcpy(nlm_addr(host), ni->sap, ni->salen);
-       host->h_addrlen = ni->salen;
+       host->h_addrlen    = ni->salen;
        rpc_set_port(nlm_addr(host), 0);
-       memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
-       host->h_srcaddrlen = ni->src_len;
+       host->h_srcaddrlen = 0;
+
+       host->h_rpcclnt    = NULL;
+       host->h_name       = nsm->sm_name;
        host->h_version    = ni->version;
        host->h_proto      = ni->protocol;
-       host->h_rpcclnt    = NULL;
-       mutex_init(&host->h_mutex);
-       host->h_nextrebind = jiffies + NLM_HOST_REBIND;
-       host->h_expires    = jiffies + NLM_HOST_EXPIRE;
-       atomic_set(&host->h_count, 1);
+       host->h_reclaiming = 0;
+       host->h_server     = ni->server;
+       host->h_noresvport = ni->noresvport;
+       host->h_inuse      = 0;
        init_waitqueue_head(&host->h_gracewait);
        init_rwsem(&host->h_rwsem);
-       host->h_state      = 0;                 /* pseudo NSM state */
-       host->h_nsmstate   = 0;                 /* real NSM state */
-       host->h_nsmhandle  = nsm;
-       host->h_server     = ni->server;
-       host->h_noresvport = ni->noresvport;
-       hlist_add_head(&host->h_hash, chain);
+       host->h_state      = 0;
+       host->h_nsmstate   = 0;
+       host->h_pidcount   = 0;
+       atomic_set(&host->h_count, 1);
+       mutex_init(&host->h_mutex);
+       host->h_nextrebind = now + NLM_HOST_REBIND;
+       host->h_expires    = now + NLM_HOST_EXPIRE;
        INIT_LIST_HEAD(&host->h_lockowners);
        spin_lock_init(&host->h_lock);
        INIT_LIST_HEAD(&host->h_granted);
        INIT_LIST_HEAD(&host->h_reclaim);
-
-       nrhosts++;
-
-       dprintk("lockd: nlm_lookup_host created host %s\n",
-                       host->h_name);
+       host->h_nsmhandle  = nsm;
+       host->h_addrbuf    = nsm->sm_addrbuf;
 
 out:
-       mutex_unlock(&nlm_host_mutex);
        return host;
 }
 
 /*
- * Destroy a host
+ * Destroy an nlm_host and free associated resources
+ *
+ * Caller must hold nlm_host_mutex.
  */
-static void
-nlm_destroy_host(struct nlm_host *host)
+static void nlm_destroy_host_locked(struct nlm_host *host)
 {
        struct rpc_clnt *clnt;
 
+       dprintk("lockd: destroy host %s\n", host->h_name);
+
        BUG_ON(!list_empty(&host->h_lockowners));
        BUG_ON(atomic_read(&host->h_count));
 
+       hlist_del_init(&host->h_hash);
+
        nsm_unmonitor(host);
        nsm_release(host->h_nsmhandle);
 
@@ -216,6 +183,8 @@ nlm_destroy_host(struct nlm_host *host)
        if (clnt != NULL)
                rpc_shutdown_client(clnt);
        kfree(host);
+
+       nrhosts--;
 }
 
 /**
@@ -249,12 +218,76 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
                .hostname_len   = strlen(hostname),
                .noresvport     = noresvport,
        };
+       struct hlist_head *chain;
+       struct hlist_node *pos;
+       struct nlm_host *host;
+       struct nsm_handle *nsm = NULL;
 
        dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
                        (hostname ? hostname : "<none>"), version,
                        (protocol == IPPROTO_UDP ? "udp" : "tcp"));
 
-       return nlm_lookup_host(&ni);
+       mutex_lock(&nlm_host_mutex);
+
+       chain = &nlm_client_hosts[nlm_hash_address(sap)];
+       hlist_for_each_entry(host, pos, chain, h_hash) {
+               if (!rpc_cmp_addr(nlm_addr(host), sap))
+                       continue;
+
+               /* Same address. Share an NSM handle if we already have one */
+               if (nsm == NULL)
+                       nsm = host->h_nsmhandle;
+
+               if (host->h_proto != protocol)
+                       continue;
+               if (host->h_version != version)
+                       continue;
+
+               nlm_get_host(host);
+               dprintk("lockd: %s found host %s (%s)\n", __func__,
+                       host->h_name, host->h_addrbuf);
+               goto out;
+       }
+
+       host = nlm_alloc_host(&ni, nsm);
+       if (unlikely(host == NULL))
+               goto out;
+
+       hlist_add_head(&host->h_hash, chain);
+       nrhosts++;
+
+       dprintk("lockd: %s created host %s (%s)\n", __func__,
+               host->h_name, host->h_addrbuf);
+
+out:
+       mutex_unlock(&nlm_host_mutex);
+       return host;
+}
+
+/**
+ * nlmclnt_release_host - release client nlm_host
+ * @host: nlm_host to release
+ *
+ */
+void nlmclnt_release_host(struct nlm_host *host)
+{
+       if (host == NULL)
+               return;
+
+       dprintk("lockd: release client host %s\n", host->h_name);
+
+       BUG_ON(atomic_read(&host->h_count) < 0);
+       BUG_ON(host->h_server);
+
+       if (atomic_dec_and_test(&host->h_count)) {
+               BUG_ON(!list_empty(&host->h_lockowners));
+               BUG_ON(!list_empty(&host->h_granted));
+               BUG_ON(!list_empty(&host->h_reclaim));
+
+               mutex_lock(&nlm_host_mutex);
+               nlm_destroy_host_locked(host);
+               mutex_unlock(&nlm_host_mutex);
+       }
 }
 
 /**
@@ -279,12 +312,18 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                                    const char *hostname,
                                    const size_t hostname_len)
 {
+       struct hlist_head *chain;
+       struct hlist_node *pos;
+       struct nlm_host *host = NULL;
+       struct nsm_handle *nsm = NULL;
        struct sockaddr_in sin = {
                .sin_family     = AF_INET,
        };
        struct sockaddr_in6 sin6 = {
                .sin6_family    = AF_INET6,
        };
+       struct sockaddr *src_sap;
+       size_t src_len = rqstp->rq_addrlen;
        struct nlm_lookup_host_info ni = {
                .server         = 1,
                .sap            = svc_addr(rqstp),
@@ -293,27 +332,91 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                .version        = rqstp->rq_vers,
                .hostname       = hostname,
                .hostname_len   = hostname_len,
-               .src_len        = rqstp->rq_addrlen,
        };
 
        dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__,
                        (int)hostname_len, hostname, rqstp->rq_vers,
                        (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
 
+       mutex_lock(&nlm_host_mutex);
+
        switch (ni.sap->sa_family) {
        case AF_INET:
                sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
-               ni.src_sap = (struct sockaddr *)&sin;
+               src_sap = (struct sockaddr *)&sin;
                break;
        case AF_INET6:
                ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6);
-               ni.src_sap = (struct sockaddr *)&sin6;
+               src_sap = (struct sockaddr *)&sin6;
                break;
        default:
-               return NULL;
+               dprintk("lockd: %s failed; unrecognized address family\n",
+                       __func__);
+               goto out;
+       }
+
+       if (time_after_eq(jiffies, next_gc))
+               nlm_gc_hosts();
+
+       chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
+       hlist_for_each_entry(host, pos, chain, h_hash) {
+               if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
+                       continue;
+
+               /* Same address. Share an NSM handle if we already have one */
+               if (nsm == NULL)
+                       nsm = host->h_nsmhandle;
+
+               if (host->h_proto != ni.protocol)
+                       continue;
+               if (host->h_version != ni.version)
+                       continue;
+               if (!rpc_cmp_addr(nlm_srcaddr(host), src_sap))
+                       continue;
+
+               /* Move to head of hash chain. */
+               hlist_del(&host->h_hash);
+               hlist_add_head(&host->h_hash, chain);
+
+               nlm_get_host(host);
+               dprintk("lockd: %s found host %s (%s)\n",
+                       __func__, host->h_name, host->h_addrbuf);
+               goto out;
        }
 
-       return nlm_lookup_host(&ni);
+       host = nlm_alloc_host(&ni, nsm);
+       if (unlikely(host == NULL))
+               goto out;
+
+       memcpy(nlm_srcaddr(host), src_sap, src_len);
+       host->h_srcaddrlen = src_len;
+       hlist_add_head(&host->h_hash, chain);
+       nrhosts++;
+
+       dprintk("lockd: %s created host %s (%s)\n",
+               __func__, host->h_name, host->h_addrbuf);
+
+out:
+       mutex_unlock(&nlm_host_mutex);
+       return host;
+}
+
+/**
+ * nlmsvc_release_host - release server nlm_host
+ * @host: nlm_host to release
+ *
+ * Host is destroyed later in nlm_gc_host().
+ */
+void nlmsvc_release_host(struct nlm_host *host)
+{
+       if (host == NULL)
+               return;
+
+       dprintk("lockd: release server host %s\n", host->h_name);
+
+       BUG_ON(atomic_read(&host->h_count) < 0);
+       BUG_ON(!host->h_server);
+       atomic_dec(&host->h_count);
 }
 
 /*
@@ -413,20 +516,28 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
        return host;
 }
 
-/*
- * Release NLM host after use
- */
-void nlm_release_host(struct nlm_host *host)
+static struct nlm_host *next_host_state(struct hlist_head *cache,
+                                       struct nsm_handle *nsm,
+                                       const struct nlm_reboot *info)
 {
-       if (host != NULL) {
-               dprintk("lockd: release host %s\n", host->h_name);
-               BUG_ON(atomic_read(&host->h_count) < 0);
-               if (atomic_dec_and_test(&host->h_count)) {
-                       BUG_ON(!list_empty(&host->h_lockowners));
-                       BUG_ON(!list_empty(&host->h_granted));
-                       BUG_ON(!list_empty(&host->h_reclaim));
+       struct nlm_host *host = NULL;
+       struct hlist_head *chain;
+       struct hlist_node *pos;
+
+       mutex_lock(&nlm_host_mutex);
+       for_each_host(host, pos, chain, cache) {
+               if (host->h_nsmhandle == nsm
+                   && host->h_nsmstate != info->state) {
+                       host->h_nsmstate = info->state;
+                       host->h_state++;
+
+                       nlm_get_host(host);
+                       goto out;
                }
        }
+out:
+       mutex_unlock(&nlm_host_mutex);
+       return host;
 }
 
 /**
@@ -438,8 +549,6 @@ void nlm_release_host(struct nlm_host *host)
  */
 void nlm_host_rebooted(const struct nlm_reboot *info)
 {
-       struct hlist_head *chain;
-       struct hlist_node *pos;
        struct nsm_handle *nsm;
        struct nlm_host *host;
 
@@ -452,32 +561,15 @@ void nlm_host_rebooted(const struct nlm_reboot *info)
         * lock for this.
         * To avoid processing a host several times, we match the nsmstate.
         */
-again: mutex_lock(&nlm_host_mutex);
-       for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-               hlist_for_each_entry(host, pos, chain, h_hash) {
-                       if (host->h_nsmhandle == nsm
-                        && host->h_nsmstate != info->state) {
-                               host->h_nsmstate = info->state;
-                               host->h_state++;
-
-                               nlm_get_host(host);
-                               mutex_unlock(&nlm_host_mutex);
-
-                               if (host->h_server) {
-                                       /* We're server for this guy, just ditch
-                                        * all the locks he held. */
-                                       nlmsvc_free_host_resources(host);
-                               } else {
-                                       /* He's the server, initiate lock recovery. */
-                                       nlmclnt_recovery(host);
-                               }
-
-                               nlm_release_host(host);
-                               goto again;
-                       }
-               }
+       while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
+               nlmsvc_free_host_resources(host);
+               nlmsvc_release_host(host);
        }
-       mutex_unlock(&nlm_host_mutex);
+       while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
+               nlmclnt_recovery(host);
+               nlmclnt_release_host(host);
+       }
+
        nsm_release(nsm);
 }
 
@@ -497,13 +589,11 @@ nlm_shutdown_hosts(void)
 
        /* First, make all hosts eligible for gc */
        dprintk("lockd: nuking all hosts...\n");
-       for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-               hlist_for_each_entry(host, pos, chain, h_hash) {
-                       host->h_expires = jiffies - 1;
-                       if (host->h_rpcclnt) {
-                               rpc_shutdown_client(host->h_rpcclnt);
-                               host->h_rpcclnt = NULL;
-                       }
+       for_each_host(host, pos, chain, nlm_server_hosts) {
+               host->h_expires = jiffies - 1;
+               if (host->h_rpcclnt) {
+                       rpc_shutdown_client(host->h_rpcclnt);
+                       host->h_rpcclnt = NULL;
                }
        }
 
@@ -512,15 +602,13 @@ nlm_shutdown_hosts(void)
        mutex_unlock(&nlm_host_mutex);
 
        /* complain if any hosts are left */
-       if (nrhosts) {
+       if (nrhosts != 0) {
                printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
-               dprintk("lockd: %d hosts left:\n", nrhosts);
-               for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-                       hlist_for_each_entry(host, pos, chain, h_hash) {
-                               dprintk("       %s (cnt %d use %d exp %ld)\n",
-                                       host->h_name, atomic_read(&host->h_count),
-                                       host->h_inuse, host->h_expires);
-                       }
+               dprintk("lockd: %lu hosts left:\n", nrhosts);
+               for_each_host(host, pos, chain, nlm_server_hosts) {
+                       dprintk("       %s (cnt %d use %d exp %ld)\n",
+                               host->h_name, atomic_read(&host->h_count),
+                               host->h_inuse, host->h_expires);
                }
        }
 }
@@ -538,29 +626,22 @@ nlm_gc_hosts(void)
        struct nlm_host *host;
 
        dprintk("lockd: host garbage collection\n");
-       for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-               hlist_for_each_entry(host, pos, chain, h_hash)
-                       host->h_inuse = 0;
-       }
+       for_each_host(host, pos, chain, nlm_server_hosts)
+               host->h_inuse = 0;
 
        /* Mark all hosts that hold locks, blocks or shares */
        nlmsvc_mark_resources();
 
-       for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-               hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
-                       if (atomic_read(&host->h_count) || host->h_inuse
-                        || time_before(jiffies, host->h_expires)) {
-                               dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
-                                       host->h_name, atomic_read(&host->h_count),
-                                       host->h_inuse, host->h_expires);
-                               continue;
-                       }
-                       dprintk("lockd: delete host %s\n", host->h_name);
-                       hlist_del_init(&host->h_hash);
-
-                       nlm_destroy_host(host);
-                       nrhosts--;
+       for_each_host_safe(host, pos, next, chain, nlm_server_hosts) {
+               if (atomic_read(&host->h_count) || host->h_inuse
+                || time_before(jiffies, host->h_expires)) {
+                       dprintk("nlm_gc_hosts skipping %s "
+                               "(cnt %d use %d exp %ld)\n",
+                               host->h_name, atomic_read(&host->h_count),
+                               host->h_inuse, host->h_expires);
+                       continue;
                }
+               nlm_destroy_host_locked(host);
        }
 
        next_gc = jiffies + NLM_HOST_COLLECT;
index e0c9189..23d7451 100644 (file)
@@ -401,26 +401,22 @@ void nsm_release(struct nsm_handle *nsm)
  * Status Monitor wire protocol.
  */
 
-static int encode_nsm_string(struct xdr_stream *xdr, const char *string)
+static void encode_nsm_string(struct xdr_stream *xdr, const char *string)
 {
        const u32 len = strlen(string);
        __be32 *p;
 
-       if (unlikely(len > SM_MAXSTRLEN))
-               return -EIO;
-       p = xdr_reserve_space(xdr, sizeof(u32) + len);
-       if (unlikely(p == NULL))
-               return -EIO;
+       BUG_ON(len > SM_MAXSTRLEN);
+       p = xdr_reserve_space(xdr, 4 + len);
        xdr_encode_opaque(p, string, len);
-       return 0;
 }
 
 /*
  * "mon_name" specifies the host to be monitored.
  */
-static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
+static void encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
 {
-       return encode_nsm_string(xdr, argp->mon_name);
+       encode_nsm_string(xdr, argp->mon_name);
 }
 
 /*
@@ -429,35 +425,25 @@ static int encode_mon_name(struct xdr_stream *xdr, const struct nsm_args *argp)
  * (via the NLMPROC_SM_NOTIFY call) that the state of host "mon_name"
  * has changed.
  */
-static int encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
+static void encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
 {
-       int status;
        __be32 *p;
 
-       status = encode_nsm_string(xdr, utsname()->nodename);
-       if (unlikely(status != 0))
-               return status;
-       p = xdr_reserve_space(xdr, 3 * sizeof(u32));
-       if (unlikely(p == NULL))
-               return -EIO;
-       *p++ = htonl(argp->prog);
-       *p++ = htonl(argp->vers);
-       *p++ = htonl(argp->proc);
-       return 0;
+       encode_nsm_string(xdr, utsname()->nodename);
+       p = xdr_reserve_space(xdr, 4 + 4 + 4);
+       *p++ = cpu_to_be32(argp->prog);
+       *p++ = cpu_to_be32(argp->vers);
+       *p = cpu_to_be32(argp->proc);
 }
 
 /*
  * The "mon_id" argument specifies the non-private arguments
  * of an NSMPROC_MON or NSMPROC_UNMON call.
  */
-static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
+static void encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
 {
-       int status;
-
-       status = encode_mon_name(xdr, argp);
-       if (unlikely(status != 0))
-               return status;
-       return encode_my_id(xdr, argp);
+       encode_mon_name(xdr, argp);
+       encode_my_id(xdr, argp);
 }
 
 /*
@@ -465,68 +451,56 @@ static int encode_mon_id(struct xdr_stream *xdr, const struct nsm_args *argp)
  * by the NSMPROC_MON call. This information will be supplied in the
  * NLMPROC_SM_NOTIFY call.
  */
-static int encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
+static void encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)
 {
        __be32 *p;
 
        p = xdr_reserve_space(xdr, SM_PRIV_SIZE);
-       if (unlikely(p == NULL))
-               return -EIO;
        xdr_encode_opaque_fixed(p, argp->priv->data, SM_PRIV_SIZE);
-       return 0;
 }
 
-static int xdr_enc_mon(struct rpc_rqst *req, __be32 *p,
-                      const struct nsm_args *argp)
+static void nsm_xdr_enc_mon(struct rpc_rqst *req, struct xdr_stream *xdr,
+                           const struct nsm_args *argp)
 {
-       struct xdr_stream xdr;
-       int status;
-
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       status = encode_mon_id(&xdr, argp);
-       if (unlikely(status))
-               return status;
-       return encode_priv(&xdr, argp);
+       encode_mon_id(xdr, argp);
+       encode_priv(xdr, argp);
 }
 
-static int xdr_enc_unmon(struct rpc_rqst *req, __be32 *p,
-                        const struct nsm_args *argp)
+static void nsm_xdr_enc_unmon(struct rpc_rqst *req, struct xdr_stream *xdr,
+                             const struct nsm_args *argp)
 {
-       struct xdr_stream xdr;
-
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       return encode_mon_id(&xdr, argp);
+       encode_mon_id(xdr, argp);
 }
 
-static int xdr_dec_stat_res(struct rpc_rqst *rqstp, __be32 *p,
-                           struct nsm_res *resp)
+static int nsm_xdr_dec_stat_res(struct rpc_rqst *rqstp,
+                               struct xdr_stream *xdr,
+                               struct nsm_res *resp)
 {
-       struct xdr_stream xdr;
+       __be32 *p;
 
-       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       p = xdr_inline_decode(&xdr, 2 * sizeof(u32));
+       p = xdr_inline_decode(xdr, 4 + 4);
        if (unlikely(p == NULL))
                return -EIO;
-       resp->status = ntohl(*p++);
-       resp->state = ntohl(*p);
+       resp->status = be32_to_cpup(p++);
+       resp->state = be32_to_cpup(p);
 
-       dprintk("lockd: xdr_dec_stat_res status %d state %d\n",
-                       resp->status, resp->state);
+       dprintk("lockd: %s status %d state %d\n",
+               __func__, resp->status, resp->state);
        return 0;
 }
 
-static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p,
-                       struct nsm_res *resp)
+static int nsm_xdr_dec_stat(struct rpc_rqst *rqstp,
+                           struct xdr_stream *xdr,
+                           struct nsm_res *resp)
 {
-       struct xdr_stream xdr;
+       __be32 *p;
 
-       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       p = xdr_inline_decode(&xdr, sizeof(u32));
+       p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                return -EIO;
-       resp->state = ntohl(*p);
+       resp->state = be32_to_cpup(p);
 
-       dprintk("lockd: xdr_dec_stat state %d\n", resp->state);
+       dprintk("lockd: %s state %d\n", __func__, resp->state);
        return 0;
 }
 
@@ -542,8 +516,8 @@ static int xdr_dec_stat(struct rpc_rqst *rqstp, __be32 *p,
 static struct rpc_procinfo     nsm_procedures[] = {
 [NSMPROC_MON] = {
                .p_proc         = NSMPROC_MON,
-               .p_encode       = (kxdrproc_t)xdr_enc_mon,
-               .p_decode       = (kxdrproc_t)xdr_dec_stat_res,
+               .p_encode       = (kxdreproc_t)nsm_xdr_enc_mon,
+               .p_decode       = (kxdrdproc_t)nsm_xdr_dec_stat_res,
                .p_arglen       = SM_mon_sz,
                .p_replen       = SM_monres_sz,
                .p_statidx      = NSMPROC_MON,
@@ -551,8 +525,8 @@ static struct rpc_procinfo  nsm_procedures[] = {
        },
 [NSMPROC_UNMON] = {
                .p_proc         = NSMPROC_UNMON,
-               .p_encode       = (kxdrproc_t)xdr_enc_unmon,
-               .p_decode       = (kxdrproc_t)xdr_dec_stat,
+               .p_encode       = (kxdreproc_t)nsm_xdr_enc_unmon,
+               .p_decode       = (kxdrdproc_t)nsm_xdr_dec_stat,
                .p_arglen       = SM_mon_id_sz,
                .p_replen       = SM_unmonres_sz,
                .p_statidx      = NSMPROC_UNMON,
index 38d2611..9a41fdc 100644 (file)
@@ -51,7 +51,7 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
        return 0;
 
 no_locks:
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        if (error)
                return error;   
        return nlm_lck_denied_nolocks;
@@ -92,7 +92,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
        else
                dprintk("lockd: TEST4        status %d\n", ntohl(resp->status));
 
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rc;
 }
@@ -134,7 +134,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
        else
                dprintk("lockd: LOCK         status %d\n", ntohl(resp->status));
 
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rc;
 }
@@ -164,7 +164,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
 
        dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rpc_success;
 }
@@ -197,7 +197,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->status = nlmsvc_unlock(file, &argp->lock);
 
        dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rpc_success;
 }
@@ -229,7 +229,7 @@ static void nlm4svc_callback_exit(struct rpc_task *task, void *data)
 
 static void nlm4svc_callback_release(void *data)
 {
-       nlm_release_call(data);
+       nlmsvc_release_call(data);
 }
 
 static const struct rpc_call_ops nlm4svc_callback_ops = {
@@ -261,7 +261,7 @@ static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
 
        stat = func(rqstp, argp, &call->a_res);
        if (stat != 0) {
-               nlm_release_call(call);
+               nlmsvc_release_call(call);
                return stat;
        }
 
@@ -334,7 +334,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->status = nlmsvc_share_file(host, file, argp);
 
        dprintk("lockd: SHARE         status %d\n", ntohl(resp->status));
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rpc_success;
 }
@@ -367,7 +367,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->status = nlmsvc_unshare_file(host, file, argp);
 
        dprintk("lockd: UNSHARE       status %d\n", ntohl(resp->status));
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rpc_success;
 }
@@ -399,7 +399,7 @@ nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
                return rpc_success;
 
        nlmsvc_free_host_resources(host);
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        return rpc_success;
 }
 
index ef5659b..6e31695 100644 (file)
@@ -46,6 +46,7 @@ static void   nlmsvc_remove_block(struct nlm_block *block);
 static int nlmsvc_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock);
 static void nlmsvc_freegrantargs(struct nlm_rqst *call);
 static const struct rpc_call_ops nlmsvc_grant_ops;
+static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie);
 
 /*
  * The list of blocked locks to retry
@@ -233,7 +234,7 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
 failed_free:
        kfree(block);
 failed:
-       nlm_release_call(call);
+       nlmsvc_release_call(call);
        return NULL;
 }
 
@@ -266,7 +267,7 @@ static void nlmsvc_free_block(struct kref *kref)
        mutex_unlock(&file->f_mutex);
 
        nlmsvc_freegrantargs(block->b_call);
-       nlm_release_call(block->b_call);
+       nlmsvc_release_call(block->b_call);
        nlm_release_file(block->b_file);
        kfree(block->b_fl);
        kfree(block);
@@ -934,3 +935,32 @@ nlmsvc_retry_blocked(void)
 
        return timeout;
 }
+
+#ifdef RPC_DEBUG
+static const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
+{
+       /*
+        * We can get away with a static buffer because we're only
+        * called with BKL held.
+        */
+       static char buf[2*NLM_MAXCOOKIELEN+1];
+       unsigned int i, len = sizeof(buf);
+       char *p = buf;
+
+       len--;  /* allow for trailing \0 */
+       if (len < 3)
+               return "???";
+       for (i = 0 ; i < cookie->len ; i++) {
+               if (len < 2) {
+                       strcpy(p-3, "...");
+                       break;
+               }
+               sprintf(p, "%02x", cookie->data[i]);
+               p += 2;
+               len -= 2;
+       }
+       *p = '\0';
+
+       return buf;
+}
+#endif
index 0caea53..d27aab1 100644 (file)
@@ -80,7 +80,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
        return 0;
 
 no_locks:
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        if (error)
                return error;
        return nlm_lck_denied_nolocks;
@@ -122,7 +122,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
                dprintk("lockd: TEST          status %d vers %d\n",
                        ntohl(resp->status), rqstp->rq_vers);
 
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rc;
 }
@@ -164,7 +164,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
        else
                dprintk("lockd: LOCK         status %d\n", ntohl(resp->status));
 
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rc;
 }
@@ -194,7 +194,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock));
 
        dprintk("lockd: CANCEL        status %d\n", ntohl(resp->status));
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rpc_success;
 }
@@ -227,7 +227,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->status = cast_status(nlmsvc_unlock(file, &argp->lock));
 
        dprintk("lockd: UNLOCK        status %d\n", ntohl(resp->status));
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rpc_success;
 }
@@ -257,9 +257,17 @@ static void nlmsvc_callback_exit(struct rpc_task *task, void *data)
                        -task->tk_status);
 }
 
+void nlmsvc_release_call(struct nlm_rqst *call)
+{
+       if (!atomic_dec_and_test(&call->a_count))
+               return;
+       nlmsvc_release_host(call->a_host);
+       kfree(call);
+}
+
 static void nlmsvc_callback_release(void *data)
 {
-       nlm_release_call(data);
+       nlmsvc_release_call(data);
 }
 
 static const struct rpc_call_ops nlmsvc_callback_ops = {
@@ -291,7 +299,7 @@ static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
 
        stat = func(rqstp, argp, &call->a_res);
        if (stat != 0) {
-               nlm_release_call(call);
+               nlmsvc_release_call(call);
                return stat;
        }
 
@@ -366,7 +374,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->status = cast_status(nlmsvc_share_file(host, file, argp));
 
        dprintk("lockd: SHARE         status %d\n", ntohl(resp->status));
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rpc_success;
 }
@@ -399,7 +407,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
        resp->status = cast_status(nlmsvc_unshare_file(host, file, argp));
 
        dprintk("lockd: UNSHARE       status %d\n", ntohl(resp->status));
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        nlm_release_file(file);
        return rpc_success;
 }
@@ -431,7 +439,7 @@ nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
                return rpc_success;
 
        nlmsvc_free_host_resources(host);
-       nlm_release_host(host);
+       nlmsvc_release_host(host);
        return rpc_success;
 }
 
index b583ab0..964666c 100644 (file)
@@ -149,37 +149,6 @@ nlm_decode_lock(__be32 *p, struct nlm_lock *lock)
 }
 
 /*
- * Encode a lock as part of an NLM call
- */
-static __be32 *
-nlm_encode_lock(__be32 *p, struct nlm_lock *lock)
-{
-       struct file_lock        *fl = &lock->fl;
-       __s32                   start, len;
-
-       if (!(p = xdr_encode_string(p, lock->caller))
-        || !(p = nlm_encode_fh(p, &lock->fh))
-        || !(p = nlm_encode_oh(p, &lock->oh)))
-               return NULL;
-
-       if (fl->fl_start > NLM_OFFSET_MAX
-        || (fl->fl_end > NLM_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
-               return NULL;
-
-       start = loff_t_to_s32(fl->fl_start);
-       if (fl->fl_end == OFFSET_MAX)
-               len = 0;
-       else
-               len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
-
-       *p++ = htonl(lock->svid);
-       *p++ = htonl(start);
-       *p++ = htonl(len);
-
-       return p;
-}
-
-/*
  * Encode result of a TEST/TEST_MSG call
  */
 static __be32 *
@@ -372,259 +341,3 @@ nlmsvc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
 {
        return xdr_ressize_check(rqstp, p);
 }
-
-/*
- * Now, the client side XDR functions
- */
-#ifdef NLMCLNT_SUPPORT_SHARES
-static int
-nlmclt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
-{
-       return 0;
-}
-#endif
-
-static int
-nlmclt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
-       struct nlm_lock *lock = &argp->lock;
-
-       if (!(p = nlm_encode_cookie(p, &argp->cookie)))
-               return -EIO;
-       *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
-       if (!(p = nlm_encode_lock(p, lock)))
-               return -EIO;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlmclt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
-       if (!(p = nlm_decode_cookie(p, &resp->cookie)))
-               return -EIO;
-       resp->status = *p++;
-       if (resp->status == nlm_lck_denied) {
-               struct file_lock        *fl = &resp->lock.fl;
-               u32                     excl;
-               s32                     start, len, end;
-
-               memset(&resp->lock, 0, sizeof(resp->lock));
-               locks_init_lock(fl);
-               excl = ntohl(*p++);
-               resp->lock.svid = ntohl(*p++);
-               fl->fl_pid = (pid_t)resp->lock.svid;
-               if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
-                       return -EIO;
-
-               fl->fl_flags = FL_POSIX;
-               fl->fl_type  = excl? F_WRLCK : F_RDLCK;
-               start = ntohl(*p++);
-               len = ntohl(*p++);
-               end = start + len - 1;
-
-               fl->fl_start = s32_to_loff_t(start);
-               if (len == 0 || end < 0)
-                       fl->fl_end = OFFSET_MAX;
-               else
-                       fl->fl_end = s32_to_loff_t(end);
-       }
-       return 0;
-}
-
-
-static int
-nlmclt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
-       struct nlm_lock *lock = &argp->lock;
-
-       if (!(p = nlm_encode_cookie(p, &argp->cookie)))
-               return -EIO;
-       *p++ = argp->block? xdr_one : xdr_zero;
-       *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
-       if (!(p = nlm_encode_lock(p, lock)))
-               return -EIO;
-       *p++ = argp->reclaim? xdr_one : xdr_zero;
-       *p++ = htonl(argp->state);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlmclt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
-       struct nlm_lock *lock = &argp->lock;
-
-       if (!(p = nlm_encode_cookie(p, &argp->cookie)))
-               return -EIO;
-       *p++ = argp->block? xdr_one : xdr_zero;
-       *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
-       if (!(p = nlm_encode_lock(p, lock)))
-               return -EIO;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlmclt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
-       struct nlm_lock *lock = &argp->lock;
-
-       if (!(p = nlm_encode_cookie(p, &argp->cookie)))
-               return -EIO;
-       if (!(p = nlm_encode_lock(p, lock)))
-               return -EIO;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlmclt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
-       if (!(p = nlm_encode_cookie(p, &resp->cookie)))
-               return -EIO;
-       *p++ = resp->status;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlmclt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
-       if (!(p = nlm_encode_testres(p, resp)))
-               return -EIO;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
-       if (!(p = nlm_decode_cookie(p, &resp->cookie)))
-               return -EIO;
-       resp->status = *p++;
-       return 0;
-}
-
-#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
-#  error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
-#endif
-
-/*
- * Buffer requirements for NLM
- */
-#define NLM_void_sz            0
-#define NLM_cookie_sz          1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
-#define NLM_caller_sz          1+XDR_QUADLEN(NLMCLNT_OHSIZE)
-#define NLM_owner_sz           1+XDR_QUADLEN(NLMCLNT_OHSIZE)
-#define NLM_fhandle_sz         1+XDR_QUADLEN(NFS2_FHSIZE)
-#define NLM_lock_sz            3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz
-#define NLM_holder_sz          4+NLM_owner_sz
-
-#define NLM_testargs_sz                NLM_cookie_sz+1+NLM_lock_sz
-#define NLM_lockargs_sz                NLM_cookie_sz+4+NLM_lock_sz
-#define NLM_cancargs_sz                NLM_cookie_sz+2+NLM_lock_sz
-#define NLM_unlockargs_sz      NLM_cookie_sz+NLM_lock_sz
-
-#define NLM_testres_sz         NLM_cookie_sz+1+NLM_holder_sz
-#define NLM_res_sz             NLM_cookie_sz+1
-#define NLM_norep_sz           0
-
-/*
- * For NLM, a void procedure really returns nothing
- */
-#define nlmclt_decode_norep    NULL
-
-#define PROC(proc, argtype, restype)   \
-[NLMPROC_##proc] = {                                                   \
-       .p_proc      = NLMPROC_##proc,                                  \
-       .p_encode    = (kxdrproc_t) nlmclt_encode_##argtype,            \
-       .p_decode    = (kxdrproc_t) nlmclt_decode_##restype,            \
-       .p_arglen    = NLM_##argtype##_sz,                              \
-       .p_replen    = NLM_##restype##_sz,                              \
-       .p_statidx   = NLMPROC_##proc,                                  \
-       .p_name      = #proc,                                           \
-       }
-
-static struct rpc_procinfo     nlm_procedures[] = {
-    PROC(TEST,         testargs,       testres),
-    PROC(LOCK,         lockargs,       res),
-    PROC(CANCEL,       cancargs,       res),
-    PROC(UNLOCK,       unlockargs,     res),
-    PROC(GRANTED,      testargs,       res),
-    PROC(TEST_MSG,     testargs,       norep),
-    PROC(LOCK_MSG,     lockargs,       norep),
-    PROC(CANCEL_MSG,   cancargs,       norep),
-    PROC(UNLOCK_MSG,   unlockargs,     norep),
-    PROC(GRANTED_MSG,  testargs,       norep),
-    PROC(TEST_RES,     testres,        norep),
-    PROC(LOCK_RES,     res,            norep),
-    PROC(CANCEL_RES,   res,            norep),
-    PROC(UNLOCK_RES,   res,            norep),
-    PROC(GRANTED_RES,  res,            norep),
-#ifdef NLMCLNT_SUPPORT_SHARES
-    PROC(SHARE,                shareargs,      shareres),
-    PROC(UNSHARE,      shareargs,      shareres),
-    PROC(NM_LOCK,      lockargs,       res),
-    PROC(FREE_ALL,     notify,         void),
-#endif
-};
-
-static struct rpc_version      nlm_version1 = {
-               .number         = 1,
-               .nrprocs        = 16,
-               .procs          = nlm_procedures,
-};
-
-static struct rpc_version      nlm_version3 = {
-               .number         = 3,
-               .nrprocs        = 24,
-               .procs          = nlm_procedures,
-};
-
-static struct rpc_version *    nlm_versions[] = {
-       [1] = &nlm_version1,
-       [3] = &nlm_version3,
-#ifdef         CONFIG_LOCKD_V4
-       [4] = &nlm_version4,
-#endif
-};
-
-static struct rpc_stat         nlm_stats;
-
-struct rpc_program             nlm_program = {
-               .name           = "lockd",
-               .number         = NLM_PROGRAM,
-               .nrvers         = ARRAY_SIZE(nlm_versions),
-               .version        = nlm_versions,
-               .stats          = &nlm_stats,
-};
-
-#ifdef RPC_DEBUG
-const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
-{
-       /*
-        * We can get away with a static buffer because we're only
-        * called with BKL held.
-        */
-       static char buf[2*NLM_MAXCOOKIELEN+1];
-       unsigned int i, len = sizeof(buf);
-       char *p = buf;
-
-       len--;  /* allow for trailing \0 */
-       if (len < 3)
-               return "???";
-       for (i = 0 ; i < cookie->len ; i++) {
-               if (len < 2) {
-                       strcpy(p-3, "...");
-                       break;
-               }
-               sprintf(p, "%02x", cookie->data[i]);
-               p += 2;
-               len -= 2;
-       }
-       *p = '\0';
-
-       return buf;
-}
-#endif
index ad9dbbc..dfa4789 100644 (file)
@@ -93,15 +93,6 @@ nlm4_decode_fh(__be32 *p, struct nfs_fh *f)
        return p + XDR_QUADLEN(f->size);
 }
 
-static __be32 *
-nlm4_encode_fh(__be32 *p, struct nfs_fh *f)
-{
-       *p++ = htonl(f->size);
-       if (f->size) p[XDR_QUADLEN(f->size)-1] = 0; /* don't leak anything */
-       memcpy(p, f->data, f->size);
-       return p + XDR_QUADLEN(f->size);
-}
-
 /*
  * Encode and decode owner handle
  */
@@ -112,12 +103,6 @@ nlm4_decode_oh(__be32 *p, struct xdr_netobj *oh)
 }
 
 static __be32 *
-nlm4_encode_oh(__be32 *p, struct xdr_netobj *oh)
-{
-       return xdr_encode_netobj(p, oh);
-}
-
-static __be32 *
 nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
 {
        struct file_lock        *fl = &lock->fl;
@@ -150,38 +135,6 @@ nlm4_decode_lock(__be32 *p, struct nlm_lock *lock)
 }
 
 /*
- * Encode a lock as part of an NLM call
- */
-static __be32 *
-nlm4_encode_lock(__be32 *p, struct nlm_lock *lock)
-{
-       struct file_lock        *fl = &lock->fl;
-       __s64                   start, len;
-
-       if (!(p = xdr_encode_string(p, lock->caller))
-        || !(p = nlm4_encode_fh(p, &lock->fh))
-        || !(p = nlm4_encode_oh(p, &lock->oh)))
-               return NULL;
-
-       if (fl->fl_start > NLM4_OFFSET_MAX
-        || (fl->fl_end > NLM4_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
-               return NULL;
-
-       *p++ = htonl(lock->svid);
-
-       start = loff_t_to_s64(fl->fl_start);
-       if (fl->fl_end == OFFSET_MAX)
-               len = 0;
-       else
-               len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
-
-       p = xdr_encode_hyper(p, start);
-       p = xdr_encode_hyper(p, len);
-
-       return p;
-}
-
-/*
  * Encode result of a TEST/TEST_MSG call
  */
 static __be32 *
@@ -379,211 +332,3 @@ nlm4svc_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
 {
        return xdr_ressize_check(rqstp, p);
 }
-
-/*
- * Now, the client side XDR functions
- */
-#ifdef NLMCLNT_SUPPORT_SHARES
-static int
-nlm4clt_decode_void(struct rpc_rqst *req, __be32 *p, void *ptr)
-{
-       return 0;
-}
-#endif
-
-static int
-nlm4clt_encode_testargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
-       struct nlm_lock *lock = &argp->lock;
-
-       if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
-               return -EIO;
-       *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
-       if (!(p = nlm4_encode_lock(p, lock)))
-               return -EIO;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlm4clt_decode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
-       if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
-               return -EIO;
-       resp->status = *p++;
-       if (resp->status == nlm_lck_denied) {
-               struct file_lock        *fl = &resp->lock.fl;
-               u32                     excl;
-               __u64                   start, len;
-               __s64                   end;
-
-               memset(&resp->lock, 0, sizeof(resp->lock));
-               locks_init_lock(fl);
-               excl = ntohl(*p++);
-               resp->lock.svid = ntohl(*p++);
-               fl->fl_pid = (pid_t)resp->lock.svid;
-               if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
-                       return -EIO;
-
-               fl->fl_flags = FL_POSIX;
-               fl->fl_type  = excl? F_WRLCK : F_RDLCK;
-               p = xdr_decode_hyper(p, &start);
-               p = xdr_decode_hyper(p, &len);
-               end = start + len - 1;
-
-               fl->fl_start = s64_to_loff_t(start);
-               if (len == 0 || end < 0)
-                       fl->fl_end = OFFSET_MAX;
-               else
-                       fl->fl_end = s64_to_loff_t(end);
-       }
-       return 0;
-}
-
-
-static int
-nlm4clt_encode_lockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
-       struct nlm_lock *lock = &argp->lock;
-
-       if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
-               return -EIO;
-       *p++ = argp->block? xdr_one : xdr_zero;
-       *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
-       if (!(p = nlm4_encode_lock(p, lock)))
-               return -EIO;
-       *p++ = argp->reclaim? xdr_one : xdr_zero;
-       *p++ = htonl(argp->state);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlm4clt_encode_cancargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
-       struct nlm_lock *lock = &argp->lock;
-
-       if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
-               return -EIO;
-       *p++ = argp->block? xdr_one : xdr_zero;
-       *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
-       if (!(p = nlm4_encode_lock(p, lock)))
-               return -EIO;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlm4clt_encode_unlockargs(struct rpc_rqst *req, __be32 *p, nlm_args *argp)
-{
-       struct nlm_lock *lock = &argp->lock;
-
-       if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
-               return -EIO;
-       if (!(p = nlm4_encode_lock(p, lock)))
-               return -EIO;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlm4clt_encode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
-       if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
-               return -EIO;
-       *p++ = resp->status;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlm4clt_encode_testres(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
-       if (!(p = nlm4_encode_testres(p, resp)))
-               return -EIO;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
-}
-
-static int
-nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
-{
-       if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
-               return -EIO;
-       resp->status = *p++;
-       return 0;
-}
-
-#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
-#  error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
-#endif
-
-#if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN)
-#  error "NLM host name cannot be larger than NLM's maximum string length!"
-#endif
-
-/*
- * Buffer requirements for NLM
- */
-#define NLM4_void_sz           0
-#define NLM4_cookie_sz         1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
-#define NLM4_caller_sz         1+XDR_QUADLEN(NLMCLNT_OHSIZE)
-#define NLM4_owner_sz          1+XDR_QUADLEN(NLMCLNT_OHSIZE)
-#define NLM4_fhandle_sz                1+XDR_QUADLEN(NFS3_FHSIZE)
-#define NLM4_lock_sz           5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz
-#define NLM4_holder_sz         6+NLM4_owner_sz
-
-#define NLM4_testargs_sz       NLM4_cookie_sz+1+NLM4_lock_sz
-#define NLM4_lockargs_sz       NLM4_cookie_sz+4+NLM4_lock_sz
-#define NLM4_cancargs_sz       NLM4_cookie_sz+2+NLM4_lock_sz
-#define NLM4_unlockargs_sz     NLM4_cookie_sz+NLM4_lock_sz
-
-#define NLM4_testres_sz                NLM4_cookie_sz+1+NLM4_holder_sz
-#define NLM4_res_sz            NLM4_cookie_sz+1
-#define NLM4_norep_sz          0
-
-/*
- * For NLM, a void procedure really returns nothing
- */
-#define nlm4clt_decode_norep   NULL
-
-#define PROC(proc, argtype, restype)                                   \
-[NLMPROC_##proc] = {                                                   \
-       .p_proc      = NLMPROC_##proc,                                  \
-       .p_encode    = (kxdrproc_t) nlm4clt_encode_##argtype,           \
-       .p_decode    = (kxdrproc_t) nlm4clt_decode_##restype,           \
-       .p_arglen    = NLM4_##argtype##_sz,                             \
-       .p_replen    = NLM4_##restype##_sz,                             \
-       .p_statidx   = NLMPROC_##proc,                                  \
-       .p_name      = #proc,                                           \
-       }
-
-static struct rpc_procinfo     nlm4_procedures[] = {
-    PROC(TEST,         testargs,       testres),
-    PROC(LOCK,         lockargs,       res),
-    PROC(CANCEL,       cancargs,       res),
-    PROC(UNLOCK,       unlockargs,     res),
-    PROC(GRANTED,      testargs,       res),
-    PROC(TEST_MSG,     testargs,       norep),
-    PROC(LOCK_MSG,     lockargs,       norep),
-    PROC(CANCEL_MSG,   cancargs,       norep),
-    PROC(UNLOCK_MSG,   unlockargs,     norep),
-    PROC(GRANTED_MSG,  testargs,       norep),
-    PROC(TEST_RES,     testres,        norep),
-    PROC(LOCK_RES,     res,            norep),
-    PROC(CANCEL_RES,   res,            norep),
-    PROC(UNLOCK_RES,   res,            norep),
-    PROC(GRANTED_RES,  res,            norep),
-#ifdef NLMCLNT_SUPPORT_SHARES
-    PROC(SHARE,                shareargs,      shareres),
-    PROC(UNSHARE,      shareargs,      shareres),
-    PROC(NM_LOCK,      lockargs,       res),
-    PROC(FREE_ALL,     notify,         void),
-#endif
-};
-
-struct rpc_version     nlm_version4 = {
-       .number         = 4,
-       .nrprocs        = 24,
-       .procs          = nlm4_procedures,
-};
index 93a8b3b..1990165 100644 (file)
@@ -16,9 +16,7 @@
 #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>
 
@@ -137,6 +135,33 @@ out_err:
 
 #if defined(CONFIG_NFS_V4_1)
 /*
+ *  * CB_SEQUENCE operations will fail until the callback sessionid is set.
+ *   */
+int nfs4_set_callback_sessionid(struct nfs_client *clp)
+{
+       struct svc_serv *serv = clp->cl_rpcclient->cl_xprt->bc_serv;
+       struct nfs4_sessionid *bc_sid;
+
+       if (!serv->sv_bc_xprt)
+               return -EINVAL;
+
+       /* on success freed in xprt_free */
+       bc_sid = kmalloc(sizeof(struct nfs4_sessionid), GFP_KERNEL);
+       if (!bc_sid)
+               return -ENOMEM;
+       memcpy(bc_sid->data, &clp->cl_session->sess_id.data,
+               NFS4_MAX_SESSIONID_LEN);
+       spin_lock_bh(&serv->sv_cb_lock);
+       serv->sv_bc_xprt->xpt_bc_sid = bc_sid;
+       spin_unlock_bh(&serv->sv_cb_lock);
+       dprintk("%s set xpt_bc_sid=%u:%u:%u:%u for sv_bc_xprt %p\n", __func__,
+               ((u32 *)bc_sid->data)[0], ((u32 *)bc_sid->data)[1],
+               ((u32 *)bc_sid->data)[2], ((u32 *)bc_sid->data)[3],
+               serv->sv_bc_xprt);
+       return 0;
+}
+
+/*
  * The callback service for NFSv4.1 callbacks
  */
 static int
@@ -177,30 +202,38 @@ nfs41_callback_svc(void *vrqstp)
 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);
+       struct svc_rqst *rqstp;
+       int ret;
 
-       dprintk("--> %s\n", __func__);
-       /* Create a svc_sock for the service */
-       bc_xprt = svc_sock_create(serv, xprt->prot);
-       if (!bc_xprt)
+       /*
+        * Create an svc_sock for the back channel service that shares the
+        * fore channel connection.
+        * Returns the input port (0) and sets the svc_serv bc_xprt on success
+        */
+       ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0,
+                             SVC_SOCK_ANONYMOUS);
+       if (ret < 0) {
+               rqstp = ERR_PTR(ret);
                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);
+       if (IS_ERR(rqstp)) {
+               svc_xprt_put(serv->sv_bc_xprt);
+               serv->sv_bc_xprt = NULL;
+       }
 out:
-       dprintk("--> %s return %p\n", __func__, rqstp);
+       dprintk("--> %s return %ld\n", __func__,
+               IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0);
        return rqstp;
 }
 
@@ -233,6 +266,10 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
                struct nfs_callback_data *cb_info)
 {
 }
+int nfs4_set_callback_sessionid(struct nfs_client *clp)
+{
+       return 0;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -328,6 +365,9 @@ static int check_gss_callback_principal(struct nfs_client *clp,
        struct rpc_clnt *r = clp->cl_rpcclient;
        char *p = svc_gss_principal(rqstp);
 
+       /* No RPC_AUTH_GSS on NFSv4.1 back channel yet */
+       if (clp->cl_minorversion != 0)
+               return SVC_DROP;
        /*
         * It might just be a normal user principal, in which case
         * userspace won't bother to tell us the name at all.
@@ -345,6 +385,23 @@ static int check_gss_callback_principal(struct nfs_client *clp,
        return SVC_OK;
 }
 
+/* pg_authenticate method helper */
+static struct nfs_client *nfs_cb_find_client(struct svc_rqst *rqstp)
+{
+       struct nfs4_sessionid *sessionid = bc_xprt_sid(rqstp);
+       int is_cb_compound = rqstp->rq_proc == CB_COMPOUND ? 1 : 0;
+
+       dprintk("--> %s rq_proc %d\n", __func__, rqstp->rq_proc);
+       if (svc_is_backchannel(rqstp))
+               /* Sessionid (usually) set after CB_NULL ping */
+               return nfs4_find_client_sessionid(svc_addr(rqstp), sessionid,
+                                                 is_cb_compound);
+       else
+               /* No callback identifier in pg_authenticate */
+               return nfs4_find_client_no_ident(svc_addr(rqstp));
+}
+
+/* pg_authenticate method for nfsv4 callback threads. */
 static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 {
        struct nfs_client *clp;
@@ -352,7 +409,7 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
        int ret = SVC_OK;
 
        /* Don't talk to strangers */
-       clp = nfs_find_client(svc_addr(rqstp), 4);
+       clp = nfs_cb_find_client(rqstp);
        if (clp == NULL)
                return SVC_DROP;
 
index 85a7cfd..d3b44f9 100644 (file)
@@ -34,10 +34,17 @@ enum nfs4_callback_opnum {
        OP_CB_ILLEGAL = 10044,
 };
 
+struct cb_process_state {
+       __be32                  drc_status;
+       struct nfs_client       *clp;
+       struct nfs4_sessionid   *svc_sid; /* v4.1 callback service sessionid */
+};
+
 struct cb_compound_hdr_arg {
        unsigned int taglen;
        const char *tag;
        unsigned int minorversion;
+       unsigned int cb_ident; /* v4.0 callback identifier */
        unsigned nops;
 };
 
@@ -103,14 +110,23 @@ struct cb_sequenceres {
        uint32_t                        csr_target_highestslotid;
 };
 
-extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args,
-                                      struct cb_sequenceres *res);
+extern __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
+                                      struct cb_sequenceres *res,
+                                      struct cb_process_state *cps);
 
 extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation,
                                             const nfs4_stateid *stateid);
 
 #define RCA4_TYPE_MASK_RDATA_DLG       0
 #define RCA4_TYPE_MASK_WDATA_DLG       1
+#define RCA4_TYPE_MASK_DIR_DLG         2
+#define RCA4_TYPE_MASK_FILE_LAYOUT     3
+#define RCA4_TYPE_MASK_BLK_LAYOUT      4
+#define RCA4_TYPE_MASK_OBJ_LAYOUT_MIN  8
+#define RCA4_TYPE_MASK_OBJ_LAYOUT_MAX  9
+#define RCA4_TYPE_MASK_OTHER_LAYOUT_MIN 12
+#define RCA4_TYPE_MASK_OTHER_LAYOUT_MAX 15
+#define RCA4_TYPE_MASK_ALL 0xf31f
 
 struct cb_recallanyargs {
        struct sockaddr *craa_addr;
@@ -118,25 +134,52 @@ struct cb_recallanyargs {
        uint32_t        craa_type_mask;
 };
 
-extern unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy);
+extern __be32 nfs4_callback_recallany(struct cb_recallanyargs *args,
+                                       void *dummy,
+                                       struct cb_process_state *cps);
 
 struct cb_recallslotargs {
        struct sockaddr *crsa_addr;
        uint32_t        crsa_target_max_slots;
 };
-extern unsigned nfs4_callback_recallslot(struct cb_recallslotargs *args,
-                                         void *dummy);
+extern __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args,
+                                        void *dummy,
+                                        struct cb_process_state *cps);
+
+struct cb_layoutrecallargs {
+       struct sockaddr         *cbl_addr;
+       uint32_t                cbl_recall_type;
+       uint32_t                cbl_layout_type;
+       uint32_t                cbl_layoutchanged;
+       union {
+               struct {
+                       struct nfs_fh           cbl_fh;
+                       struct pnfs_layout_range cbl_range;
+                       nfs4_stateid            cbl_stateid;
+               };
+               struct nfs_fsid         cbl_fsid;
+       };
+};
 
-#endif /* CONFIG_NFS_V4_1 */
+extern unsigned nfs4_callback_layoutrecall(
+       struct cb_layoutrecallargs *args,
+       void *dummy, struct cb_process_state *cps);
 
-extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res);
-extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy);
+extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses);
+extern void nfs4_cb_take_slot(struct nfs_client *clp);
+#endif /* CONFIG_NFS_V4_1 */
 
+extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args,
+                                   struct cb_getattrres *res,
+                                   struct cb_process_state *cps);
+extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
+                                  struct cb_process_state *cps);
 #ifdef CONFIG_NFS_V4
 extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
 extern void nfs_callback_down(int minorversion);
 extern int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation,
                                            const nfs4_stateid *stateid);
+extern int nfs4_set_callback_sessionid(struct nfs_client *clp);
 #endif /* CONFIG_NFS_V4 */
 /*
  * nfs41: Callbacks are expected to not cause substantial latency,
index 2950fca..4bb91cb 100644 (file)
 #include "callback.h"
 #include "delegation.h"
 #include "internal.h"
+#include "pnfs.h"
 
 #ifdef NFS_DEBUG
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
 #endif
-__be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res)
+
+__be32 nfs4_callback_getattr(struct cb_getattrargs *args,
+                            struct cb_getattrres *res,
+                            struct cb_process_state *cps)
 {
-       struct nfs_client *clp;
        struct nfs_delegation *delegation;
        struct nfs_inode *nfsi;
        struct inode *inode;
 
+       res->status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
+       if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
+               goto out;
+
        res->bitmap[0] = res->bitmap[1] = 0;
        res->status = htonl(NFS4ERR_BADHANDLE);
-       clp = nfs_find_client(args->addr, 4);
-       if (clp == NULL)
-               goto out;
 
        dprintk("NFS: GETATTR callback request from %s\n",
-               rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
+               rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
 
-       inode = nfs_delegation_find_inode(clp, &args->fh);
+       inode = nfs_delegation_find_inode(cps->clp, &args->fh);
        if (inode == NULL)
-               goto out_putclient;
+               goto out;
        nfsi = NFS_I(inode);
        rcu_read_lock();
        delegation = rcu_dereference(nfsi->delegation);
@@ -55,49 +58,41 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *
 out_iput:
        rcu_read_unlock();
        iput(inode);
-out_putclient:
-       nfs_put_client(clp);
 out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status));
        return res->status;
 }
 
-__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy)
+__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
+                           struct cb_process_state *cps)
 {
-       struct nfs_client *clp;
        struct inode *inode;
        __be32 res;
        
-       res = htonl(NFS4ERR_BADHANDLE);
-       clp = nfs_find_client(args->addr, 4);
-       if (clp == NULL)
+       res = htonl(NFS4ERR_OP_NOT_IN_SESSION);
+       if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
                goto out;
 
        dprintk("NFS: RECALL callback request from %s\n",
-               rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
-
-       do {
-               struct nfs_client *prev = clp;
-
-               inode = nfs_delegation_find_inode(clp, &args->fh);
-               if (inode != NULL) {
-                       /* Set up a helper thread to actually return the delegation */
-                       switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
-                               case 0:
-                                       res = 0;
-                                       break;
-                               case -ENOENT:
-                                       if (res != 0)
-                                               res = htonl(NFS4ERR_BAD_STATEID);
-                                       break;
-                               default:
-                                       res = htonl(NFS4ERR_RESOURCE);
-                       }
-                       iput(inode);
-               }
-               clp = nfs_find_client_next(prev);
-               nfs_put_client(prev);
-       } while (clp != NULL);
+               rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
+
+       res = htonl(NFS4ERR_BADHANDLE);
+       inode = nfs_delegation_find_inode(cps->clp, &args->fh);
+       if (inode == NULL)
+               goto out;
+       /* Set up a helper thread to actually return the delegation */
+       switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
+       case 0:
+               res = 0;
+               break;
+       case -ENOENT:
+               if (res != 0)
+                       res = htonl(NFS4ERR_BAD_STATEID);
+               break;
+       default:
+               res = htonl(NFS4ERR_RESOURCE);
+       }
+       iput(inode);
 out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
        return res;
@@ -113,6 +108,139 @@ int nfs4_validate_delegation_stateid(struct nfs_delegation *delegation, const nf
 
 #if defined(CONFIG_NFS_V4_1)
 
+static u32 initiate_file_draining(struct nfs_client *clp,
+                                 struct cb_layoutrecallargs *args)
+{
+       struct pnfs_layout_hdr *lo;
+       struct inode *ino;
+       bool found = false;
+       u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
+       LIST_HEAD(free_me_list);
+
+       spin_lock(&clp->cl_lock);
+       list_for_each_entry(lo, &clp->cl_layouts, plh_layouts) {
+               if (nfs_compare_fh(&args->cbl_fh,
+                                  &NFS_I(lo->plh_inode)->fh))
+                       continue;
+               ino = igrab(lo->plh_inode);
+               if (!ino)
+                       continue;
+               found = true;
+               /* Without this, layout can be freed as soon
+                * as we release cl_lock.
+                */
+               get_layout_hdr(lo);
+               break;
+       }
+       spin_unlock(&clp->cl_lock);
+       if (!found)
+               return NFS4ERR_NOMATCHING_LAYOUT;
+
+       spin_lock(&ino->i_lock);
+       if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) ||
+           mark_matching_lsegs_invalid(lo, &free_me_list,
+                                       args->cbl_range.iomode))
+               rv = NFS4ERR_DELAY;
+       else
+               rv = NFS4ERR_NOMATCHING_LAYOUT;
+       pnfs_set_layout_stateid(lo, &args->cbl_stateid, true);
+       spin_unlock(&ino->i_lock);
+       pnfs_free_lseg_list(&free_me_list);
+       put_layout_hdr(lo);
+       iput(ino);
+       return rv;
+}
+
+static u32 initiate_bulk_draining(struct nfs_client *clp,
+                                 struct cb_layoutrecallargs *args)
+{
+       struct pnfs_layout_hdr *lo;
+       struct inode *ino;
+       u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
+       struct pnfs_layout_hdr *tmp;
+       LIST_HEAD(recall_list);
+       LIST_HEAD(free_me_list);
+       struct pnfs_layout_range range = {
+               .iomode = IOMODE_ANY,
+               .offset = 0,
+               .length = NFS4_MAX_UINT64,
+       };
+
+       spin_lock(&clp->cl_lock);
+       list_for_each_entry(lo, &clp->cl_layouts, plh_layouts) {
+               if ((args->cbl_recall_type == RETURN_FSID) &&
+                   memcmp(&NFS_SERVER(lo->plh_inode)->fsid,
+                          &args->cbl_fsid, sizeof(struct nfs_fsid)))
+                       continue;
+               if (!igrab(lo->plh_inode))
+                       continue;
+               get_layout_hdr(lo);
+               BUG_ON(!list_empty(&lo->plh_bulk_recall));
+               list_add(&lo->plh_bulk_recall, &recall_list);
+       }
+       spin_unlock(&clp->cl_lock);
+       list_for_each_entry_safe(lo, tmp,
+                                &recall_list, plh_bulk_recall) {
+               ino = lo->plh_inode;
+               spin_lock(&ino->i_lock);
+               set_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
+               if (mark_matching_lsegs_invalid(lo, &free_me_list, range.iomode))
+                       rv = NFS4ERR_DELAY;
+               list_del_init(&lo->plh_bulk_recall);
+               spin_unlock(&ino->i_lock);
+               put_layout_hdr(lo);
+               iput(ino);
+       }
+       pnfs_free_lseg_list(&free_me_list);
+       return rv;
+}
+
+static u32 do_callback_layoutrecall(struct nfs_client *clp,
+                                   struct cb_layoutrecallargs *args)
+{
+       u32 res = NFS4ERR_DELAY;
+
+       dprintk("%s enter, type=%i\n", __func__, args->cbl_recall_type);
+       if (test_and_set_bit(NFS4CLNT_LAYOUTRECALL, &clp->cl_state))
+               goto out;
+       if (args->cbl_recall_type == RETURN_FILE)
+               res = initiate_file_draining(clp, args);
+       else
+               res = initiate_bulk_draining(clp, args);
+       clear_bit(NFS4CLNT_LAYOUTRECALL, &clp->cl_state);
+out:
+       dprintk("%s returning %i\n", __func__, res);
+       return res;
+
+}
+
+__be32 nfs4_callback_layoutrecall(struct cb_layoutrecallargs *args,
+                                 void *dummy, struct cb_process_state *cps)
+{
+       u32 res;
+
+       dprintk("%s: -->\n", __func__);
+
+       if (cps->clp)
+               res = do_callback_layoutrecall(cps->clp, args);
+       else
+               res = NFS4ERR_OP_NOT_IN_SESSION;
+
+       dprintk("%s: exit with status = %d\n", __func__, res);
+       return cpu_to_be32(res);
+}
+
+static void pnfs_recall_all_layouts(struct nfs_client *clp)
+{
+       struct cb_layoutrecallargs args;
+
+       /* Pretend we got a CB_LAYOUTRECALL(ALL) */
+       memset(&args, 0, sizeof(args));
+       args.cbl_recall_type = RETURN_ALL;
+       /* FIXME we ignore errors, what should we do? */
+       do_callback_layoutrecall(clp, &args);
+}
+
 int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, const nfs4_stateid *stateid)
 {
        if (delegation == NULL)
@@ -185,42 +313,6 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
 }
 
 /*
- * 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;
-}
-
-/*
  * For each referring call triple, check the session's slot table for
  * a match.  If the slot is in use and the sequence numbers match, the
  * client is still waiting for a response to the original request.
@@ -276,20 +368,34 @@ out:
 }
 
 __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
-                               struct cb_sequenceres *res)
+                             struct cb_sequenceres *res,
+                             struct cb_process_state *cps)
 {
        struct nfs_client *clp;
        int i;
        __be32 status;
 
+       cps->clp = NULL;
+
        status = htonl(NFS4ERR_BADSESSION);
-       clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid);
+       /* Incoming session must match the callback session */
+       if (memcmp(&args->csa_sessionid, cps->svc_sid, NFS4_MAX_SESSIONID_LEN))
+               goto out;
+
+       clp = nfs4_find_client_sessionid(args->csa_addr,
+                                        &args->csa_sessionid, 1);
        if (clp == NULL)
                goto out;
 
+       /* state manager is resetting the session */
+       if (test_bit(NFS4_SESSION_DRAINING, &clp->cl_session->session_state)) {
+               status = NFS4ERR_DELAY;
+               goto out;
+       }
+
        status = validate_seqid(&clp->cl_session->bc_slot_table, args);
        if (status)
-               goto out_putclient;
+               goto out;
 
        /*
         * Check for pending referring calls.  If a match is found, a
@@ -298,7 +404,7 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
         */
        if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
                status = htonl(NFS4ERR_DELAY);
-               goto out_putclient;
+               goto out;
        }
 
        memcpy(&res->csr_sessionid, &args->csa_sessionid,
@@ -307,83 +413,93 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
        res->csr_slotid = args->csa_slotid;
        res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
        res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
+       nfs4_cb_take_slot(clp);
+       cps->clp = clp; /* put in nfs4_callback_compound */
 
-out_putclient:
-       nfs_put_client(clp);
 out:
        for (i = 0; i < args->csa_nrclists; i++)
                kfree(args->csa_rclists[i].rcl_refcalls);
        kfree(args->csa_rclists);
 
-       if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP))
-               res->csr_status = 0;
-       else
+       if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
+               cps->drc_status = status;
+               status = 0;
+       } else
                res->csr_status = status;
+
        dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
                ntohl(status), ntohl(res->csr_status));
        return status;
 }
 
-__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy)
+static bool
+validate_bitmap_values(unsigned long mask)
+{
+       return (mask & ~RCA4_TYPE_MASK_ALL) == 0;
+}
+
+__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy,
+                              struct cb_process_state *cps)
 {
-       struct nfs_client *clp;
        __be32 status;
        fmode_t flags = 0;
 
-       status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
-       clp = nfs_find_client(args->craa_addr, 4);
-       if (clp == NULL)
+       status = cpu_to_be32(NFS4ERR_OP_NOT_IN_SESSION);
+       if (!cps->clp) /* set in cb_sequence */
                goto out;
 
        dprintk("NFS: RECALL_ANY callback request from %s\n",
-               rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
+               rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
+
+       status = cpu_to_be32(NFS4ERR_INVAL);
+       if (!validate_bitmap_values(args->craa_type_mask))
+               goto out;
 
+       status = cpu_to_be32(NFS4_OK);
        if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *)
                     &args->craa_type_mask))
                flags = FMODE_READ;
        if (test_bit(RCA4_TYPE_MASK_WDATA_DLG, (const unsigned long *)
                     &args->craa_type_mask))
                flags |= FMODE_WRITE;
-
+       if (test_bit(RCA4_TYPE_MASK_FILE_LAYOUT, (const unsigned long *)
+                    &args->craa_type_mask))
+               pnfs_recall_all_layouts(cps->clp);
        if (flags)
-               nfs_expire_all_delegation_types(clp, flags);
-       status = htonl(NFS4_OK);
+               nfs_expire_all_delegation_types(cps->clp, flags);
 out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
        return status;
 }
 
 /* Reduce the fore channel's max_slots to the target value */
-__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy)
+__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy,
+                               struct cb_process_state *cps)
 {
-       struct nfs_client *clp;
        struct nfs4_slot_table *fc_tbl;
        __be32 status;
 
        status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
-       clp = nfs_find_client(args->crsa_addr, 4);
-       if (clp == NULL)
+       if (!cps->clp) /* set in cb_sequence */
                goto out;
 
        dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n",
-               rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR),
+               rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR),
                args->crsa_target_max_slots);
 
-       fc_tbl = &clp->cl_session->fc_slot_table;
+       fc_tbl = &cps->clp->cl_session->fc_slot_table;
 
        status = htonl(NFS4ERR_BAD_HIGH_SLOT);
        if (args->crsa_target_max_slots > fc_tbl->max_slots ||
            args->crsa_target_max_slots < 1)
-               goto out_putclient;
+               goto out;
 
        status = htonl(NFS4_OK);
        if (args->crsa_target_max_slots == fc_tbl->max_slots)
-               goto out_putclient;
+               goto out;
 
        fc_tbl->target_max_slots = args->crsa_target_max_slots;
-       nfs41_handle_recall_slot(clp);
-out_putclient:
-       nfs_put_client(clp);    /* balance nfs_find_client */
+       nfs41_handle_recall_slot(cps->clp);
 out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
        return status;
index 05af212..23112c2 100644 (file)
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
 #include <linux/slab.h>
+#include <linux/sunrpc/bc_xprt.h>
 #include "nfs4_fs.h"
 #include "callback.h"
+#include "internal.h"
 
 #define CB_OP_TAGLEN_MAXSZ     (512)
 #define CB_OP_HDR_RES_MAXSZ    (2 + CB_OP_TAGLEN_MAXSZ)
@@ -22,6 +24,7 @@
 #define CB_OP_RECALL_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ)
 
 #if defined(CONFIG_NFS_V4_1)
+#define CB_OP_LAYOUTRECALL_RES_MAXSZ   (CB_OP_HDR_RES_MAXSZ)
 #define CB_OP_SEQUENCE_RES_MAXSZ       (CB_OP_HDR_RES_MAXSZ + \
                                        4 + 1 + 3)
 #define CB_OP_RECALLANY_RES_MAXSZ      (CB_OP_HDR_RES_MAXSZ)
@@ -33,7 +36,8 @@
 /* Internal error code */
 #define NFS4ERR_RESOURCE_HDR   11050
 
-typedef __be32 (*callback_process_op_t)(void *, void *);
+typedef __be32 (*callback_process_op_t)(void *, void *,
+                                       struct cb_process_state *);
 typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
 typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
 
@@ -160,7 +164,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
        hdr->minorversion = ntohl(*p++);
        /* Check minor version is zero or one. */
        if (hdr->minorversion <= 1) {
-               p++;    /* skip callback_ident */
+               hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
        } else {
                printk(KERN_WARNING "%s: NFSv4 server callback with "
                        "illegal minor version %u!\n",
@@ -220,6 +224,66 @@ out:
 
 #if defined(CONFIG_NFS_V4_1)
 
+static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
+                                      struct xdr_stream *xdr,
+                                      struct cb_layoutrecallargs *args)
+{
+       __be32 *p;
+       __be32 status = 0;
+       uint32_t iomode;
+
+       args->cbl_addr = svc_addr(rqstp);
+       p = read_buf(xdr, 4 * sizeof(uint32_t));
+       if (unlikely(p == NULL)) {
+               status = htonl(NFS4ERR_BADXDR);
+               goto out;
+       }
+
+       args->cbl_layout_type = ntohl(*p++);
+       /* Depite the spec's xdr, iomode really belongs in the FILE switch,
+        * as it is unuseable and ignored with the other types.
+        */
+       iomode = ntohl(*p++);
+       args->cbl_layoutchanged = ntohl(*p++);
+       args->cbl_recall_type = ntohl(*p++);
+
+       if (args->cbl_recall_type == RETURN_FILE) {
+               args->cbl_range.iomode = iomode;
+               status = decode_fh(xdr, &args->cbl_fh);
+               if (unlikely(status != 0))
+                       goto out;
+
+               p = read_buf(xdr, 2 * sizeof(uint64_t));
+               if (unlikely(p == NULL)) {
+                       status = htonl(NFS4ERR_BADXDR);
+                       goto out;
+               }
+               p = xdr_decode_hyper(p, &args->cbl_range.offset);
+               p = xdr_decode_hyper(p, &args->cbl_range.length);
+               status = decode_stateid(xdr, &args->cbl_stateid);
+               if (unlikely(status != 0))
+                       goto out;
+       } else if (args->cbl_recall_type == RETURN_FSID) {
+               p = read_buf(xdr, 2 * sizeof(uint64_t));
+               if (unlikely(p == NULL)) {
+                       status = htonl(NFS4ERR_BADXDR);
+                       goto out;
+               }
+               p = xdr_decode_hyper(p, &args->cbl_fsid.major);
+               p = xdr_decode_hyper(p, &args->cbl_fsid.minor);
+       } else if (args->cbl_recall_type != RETURN_ALL) {
+               status = htonl(NFS4ERR_BADXDR);
+               goto out;
+       }
+       dprintk("%s: ltype 0x%x iomode %d changed %d recall_type %d\n",
+               __func__,
+               args->cbl_layout_type, iomode,
+               args->cbl_layoutchanged, args->cbl_recall_type);
+out:
+       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+       return status;
+}
+
 static __be32 decode_sessionid(struct xdr_stream *xdr,
                                 struct nfs4_sessionid *sid)
 {
@@ -574,10 +638,10 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_SEQUENCE:
        case OP_CB_RECALL_ANY:
        case OP_CB_RECALL_SLOT:
+       case OP_CB_LAYOUTRECALL:
                *op = &callback_ops[op_nr];
                break;
 
-       case OP_CB_LAYOUTRECALL:
        case OP_CB_NOTIFY_DEVICEID:
        case OP_CB_NOTIFY:
        case OP_CB_PUSH_DELEG:
@@ -593,6 +657,37 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        return htonl(NFS_OK);
 }
 
+static void nfs4_callback_free_slot(struct nfs4_session *session)
+{
+       struct nfs4_slot_table *tbl = &session->bc_slot_table;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       /*
+        * Let the state manager know callback processing done.
+        * A single slot, so highest used slotid is either 0 or -1
+        */
+       tbl->highest_used_slotid--;
+       nfs4_check_drain_bc_complete(session);
+       spin_unlock(&tbl->slot_tbl_lock);
+}
+
+static void nfs4_cb_free_slot(struct nfs_client *clp)
+{
+       if (clp && clp->cl_session)
+               nfs4_callback_free_slot(clp->cl_session);
+}
+
+/* A single slot, so highest used slotid is either 0 or -1 */
+void nfs4_cb_take_slot(struct nfs_client *clp)
+{
+       struct nfs4_slot_table *tbl = &clp->cl_session->bc_slot_table;
+
+       spin_lock(&tbl->slot_tbl_lock);
+       tbl->highest_used_slotid++;
+       BUG_ON(tbl->highest_used_slotid != 0);
+       spin_unlock(&tbl->slot_tbl_lock);
+}
+
 #else /* CONFIG_NFS_V4_1 */
 
 static __be32
@@ -601,6 +696,9 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
 }
 
+static void nfs4_cb_free_slot(struct nfs_client *clp)
+{
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 static __be32
@@ -621,7 +719,8 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
 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, int* drc_status)
+               struct xdr_stream *xdr_out, void *resp,
+               struct cb_process_state *cps)
 {
        struct callback_op *op = &callback_ops[0];
        unsigned int op_nr;
@@ -644,8 +743,8 @@ static __be32 process_op(uint32_t minorversion, int nop,
        if (status)
                goto encode_hdr;
 
-       if (*drc_status) {
-               status = *drc_status;
+       if (cps->drc_status) {
+               status = cps->drc_status;
                goto encode_hdr;
        }
 
@@ -653,16 +752,10 @@ static __be32 process_op(uint32_t minorversion, int nop,
        if (maxlen > 0 && maxlen < PAGE_SIZE) {
                status = op->decode_args(rqstp, xdr_in, argp);
                if (likely(status == 0))
-                       status = op->process_op(argp, resp);
+                       status = op->process_op(argp, resp, cps);
        } else
                status = htonl(NFS4ERR_RESOURCE);
 
-       /* Only set by OP_CB_SEQUENCE processing */
-       if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
-               *drc_status = status;
-               status = 0;
-       }
-
 encode_hdr:
        res = encode_op_hdr(xdr_out, op_nr, status);
        if (unlikely(res))
@@ -681,8 +774,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
        struct cb_compound_hdr_arg hdr_arg = { 0 };
        struct cb_compound_hdr_res hdr_res = { NULL };
        struct xdr_stream xdr_in, xdr_out;
-       __be32 *p;
-       __be32 status, drc_status = 0;
+       __be32 *p, status;
+       struct cb_process_state cps = {
+               .drc_status = 0,
+               .clp = NULL,
+       };
        unsigned int nops = 0;
 
        dprintk("%s: start\n", __func__);
@@ -696,6 +792,13 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
        if (status == __constant_htonl(NFS4ERR_RESOURCE))
                return rpc_garbage_args;
 
+       if (hdr_arg.minorversion == 0) {
+               cps.clp = nfs4_find_client_ident(hdr_arg.cb_ident);
+               if (!cps.clp)
+                       return rpc_drop_reply;
+       } else
+               cps.svc_sid = bc_xprt_sid(rqstp);
+
        hdr_res.taglen = hdr_arg.taglen;
        hdr_res.tag = hdr_arg.tag;
        if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
@@ -703,7 +806,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
 
        while (status == 0 && nops != hdr_arg.nops) {
                status = process_op(hdr_arg.minorversion, nops, rqstp,
-                                   &xdr_in, argp, &xdr_out, resp, &drc_status);
+                                   &xdr_in, argp, &xdr_out, resp, &cps);
                nops++;
        }
 
@@ -716,6 +819,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
 
        *hdr_res.status = status;
        *hdr_res.nops = htonl(nops);
+       nfs4_cb_free_slot(cps.clp);
+       nfs_put_client(cps.clp);
        dprintk("%s: done, status = %u\n", __func__, ntohl(status));
        return rpc_success;
 }
@@ -739,6 +844,12 @@ static struct callback_op callback_ops[] = {
                .res_maxsize = CB_OP_RECALL_RES_MAXSZ,
        },
 #if defined(CONFIG_NFS_V4_1)
+       [OP_CB_LAYOUTRECALL] = {
+               .process_op = (callback_process_op_t)nfs4_callback_layoutrecall,
+               .decode_args =
+                       (callback_decode_arg_t)decode_layoutrecall_args,
+               .res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ,
+       },
        [OP_CB_SEQUENCE] = {
                .process_op = (callback_process_op_t)nfs4_callback_sequence,
                .decode_args = (callback_decode_arg_t)decode_cb_sequence_args,
index 0870d0d..192f2f8 100644 (file)
@@ -56,6 +56,30 @@ static DEFINE_SPINLOCK(nfs_client_lock);
 static LIST_HEAD(nfs_client_list);
 static LIST_HEAD(nfs_volume_list);
 static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
+#ifdef CONFIG_NFS_V4
+static DEFINE_IDR(cb_ident_idr); /* Protected by nfs_client_lock */
+
+/*
+ * Get a unique NFSv4.0 callback identifier which will be used
+ * by the V4.0 callback service to lookup the nfs_client struct
+ */
+static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
+{
+       int ret = 0;
+
+       if (clp->rpc_ops->version != 4 || minorversion != 0)
+               return ret;
+retry:
+       if (!idr_pre_get(&cb_ident_idr, GFP_KERNEL))
+               return -ENOMEM;
+       spin_lock(&nfs_client_lock);
+       ret = idr_get_new(&cb_ident_idr, clp, &clp->cl_cb_ident);
+       spin_unlock(&nfs_client_lock);
+       if (ret == -EAGAIN)
+               goto retry;
+       return ret;
+}
+#endif /* CONFIG_NFS_V4 */
 
 /*
  * RPC cruft for NFS
@@ -144,7 +168,10 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        clp->cl_proto = cl_init->proto;
 
 #ifdef CONFIG_NFS_V4
-       INIT_LIST_HEAD(&clp->cl_delegations);
+       err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
+       if (err)
+               goto error_cleanup;
+
        spin_lock_init(&clp->cl_lock);
        INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
        rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
@@ -170,21 +197,17 @@ error_0:
 }
 
 #ifdef 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)) {
+static void nfs4_shutdown_session(struct nfs_client *clp)
+{
+       if (nfs4_has_session(clp))
                nfs4_destroy_session(clp->cl_session);
-               clp->cl_session = NULL;
-       }
-
-       clp->cl_mvops = nfs_v4_minor_ops[0];
-#endif /* CONFIG_NFS_V4_1 */
 }
+#else /* CONFIG_NFS_V4_1 */
+static void nfs4_shutdown_session(struct nfs_client *clp)
+{
+}
+#endif /* CONFIG_NFS_V4_1 */
 
 /*
  * Destroy the NFS4 callback service
@@ -199,17 +222,49 @@ static void nfs4_shutdown_client(struct nfs_client *clp)
 {
        if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
                nfs4_kill_renewd(clp);
-       nfs4_clear_client_minor_version(clp);
+       nfs4_shutdown_session(clp);
        nfs4_destroy_callback(clp);
        if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
                nfs_idmap_delete(clp);
 
        rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
 }
+
+/* idr_remove_all is not needed as all id's are removed by nfs_put_client */
+void nfs_cleanup_cb_ident_idr(void)
+{
+       idr_destroy(&cb_ident_idr);
+}
+
+/* nfs_client_lock held */
+static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
+{
+       if (clp->cl_cb_ident)
+               idr_remove(&cb_ident_idr, clp->cl_cb_ident);
+}
+
+static void pnfs_init_server(struct nfs_server *server)
+{
+       rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");
+}
+
 #else
 static void nfs4_shutdown_client(struct nfs_client *clp)
 {
 }
+
+void nfs_cleanup_cb_ident_idr(void)
+{
+}
+
+static void nfs_cb_idr_remove_locked(struct nfs_client *clp)
+{
+}
+
+static void pnfs_init_server(struct nfs_server *server)
+{
+}
+
 #endif /* CONFIG_NFS_V4 */
 
 /*
@@ -248,6 +303,7 @@ void nfs_put_client(struct nfs_client *clp)
 
        if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) {
                list_del(&clp->cl_share_link);
+               nfs_cb_idr_remove_locked(clp);
                spin_unlock(&nfs_client_lock);
 
                BUG_ON(!list_empty(&clp->cl_superblocks));
@@ -363,70 +419,28 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
        return 0;
 }
 
-/*
- * Find a client by IP address and protocol version
- * - returns NULL if no such client
- */
-struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
-{
-       struct nfs_client *clp;
-
-       spin_lock(&nfs_client_lock);
-       list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
-               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 ||
-                     clp->cl_cons_state == NFS_CS_SESSION_INITING))
-                       continue;
-
-               /* Different NFS versions cannot share the same nfs_client */
-               if (clp->rpc_ops->version != nfsversion)
-                       continue;
-
-               /* Match only the IP address, not the port number */
-               if (!nfs_sockaddr_match_ipaddr(addr, clap))
-                       continue;
-
-               atomic_inc(&clp->cl_count);
-               spin_unlock(&nfs_client_lock);
-               return clp;
-       }
-       spin_unlock(&nfs_client_lock);
-       return NULL;
-}
-
-/*
- * Find a client by IP address and protocol version
- * - returns NULL if no such client
- */
-struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
+/* Common match routine for v4.0 and v4.1 callback services */
+bool
+nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp,
+                    u32 minorversion)
 {
-       struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr;
-       u32 nfsvers = clp->rpc_ops->version;
+       struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
 
-       spin_lock(&nfs_client_lock);
-       list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) {
-               struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
+       /* Don't match clients that failed to initialise */
+       if (!(clp->cl_cons_state == NFS_CS_READY ||
+           clp->cl_cons_state == NFS_CS_SESSION_INITING))
+               return false;
 
-               /* Don't match clients that failed to initialise properly */
-               if (clp->cl_cons_state != NFS_CS_READY)
-                       continue;
+       /* Match the version and minorversion */
+       if (clp->rpc_ops->version != 4 ||
+           clp->cl_minorversion != minorversion)
+               return false;
 
-               /* Different NFS versions cannot share the same nfs_client */
-               if (clp->rpc_ops->version != nfsvers)
-                       continue;
+       /* Match only the IP address, not the port number */
+       if (!nfs_sockaddr_match_ipaddr(addr, clap))
+               return false;
 
-               /* Match only the IP address, not the port number */
-               if (!nfs_sockaddr_match_ipaddr(sap, clap))
-                       continue;
-
-               atomic_inc(&clp->cl_count);
-               spin_unlock(&nfs_client_lock);
-               return clp;
-       }
-       spin_unlock(&nfs_client_lock);
-       return NULL;
+       return true;
 }
 
 /*
@@ -988,6 +1002,27 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve
        target->options = source->options;
 }
 
+static void nfs_server_insert_lists(struct nfs_server *server)
+{
+       struct nfs_client *clp = server->nfs_client;
+
+       spin_lock(&nfs_client_lock);
+       list_add_tail_rcu(&server->client_link, &clp->cl_superblocks);
+       list_add_tail(&server->master_link, &nfs_volume_list);
+       spin_unlock(&nfs_client_lock);
+
+}
+
+static void nfs_server_remove_lists(struct nfs_server *server)
+{
+       spin_lock(&nfs_client_lock);
+       list_del_rcu(&server->client_link);
+       list_del(&server->master_link);
+       spin_unlock(&nfs_client_lock);
+
+       synchronize_rcu();
+}
+
 /*
  * Allocate and initialise a server record
  */
@@ -1004,6 +1039,7 @@ static struct nfs_server *nfs_alloc_server(void)
        /* Zero out the NFS state stuff */
        INIT_LIST_HEAD(&server->client_link);
        INIT_LIST_HEAD(&server->master_link);
+       INIT_LIST_HEAD(&server->delegations);
 
        atomic_set(&server->active, 0);
 
@@ -1019,6 +1055,8 @@ static struct nfs_server *nfs_alloc_server(void)
                return NULL;
        }
 
+       pnfs_init_server(server);
+
        return server;
 }
 
@@ -1029,11 +1067,8 @@ void nfs_free_server(struct nfs_server *server)
 {
        dprintk("--> nfs_free_server()\n");
 
+       nfs_server_remove_lists(server);
        unset_pnfs_layoutdriver(server);
-       spin_lock(&nfs_client_lock);
-       list_del(&server->client_link);
-       list_del(&server->master_link);
-       spin_unlock(&nfs_client_lock);
 
        if (server->destroy != NULL)
                server->destroy(server);
@@ -1108,11 +1143,7 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
                (unsigned long long) server->fsid.major,
                (unsigned long long) server->fsid.minor);
 
-       spin_lock(&nfs_client_lock);
-       list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
-       list_add_tail(&server->master_link, &nfs_volume_list);
-       spin_unlock(&nfs_client_lock);
-
+       nfs_server_insert_lists(server);
        server->mount_time = jiffies;
        nfs_free_fattr(fattr);
        return server;
@@ -1125,6 +1156,101 @@ error:
 
 #ifdef CONFIG_NFS_V4
 /*
+ * NFSv4.0 callback thread helper
+ *
+ * Find a client by IP address, protocol version, and minorversion
+ *
+ * Called from the pg_authenticate method. The callback identifier
+ * is not used as it has not been decoded.
+ *
+ * Returns NULL if no such client
+ */
+struct nfs_client *
+nfs4_find_client_no_ident(const struct sockaddr *addr)
+{
+       struct nfs_client *clp;
+
+       spin_lock(&nfs_client_lock);
+       list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
+               if (nfs4_cb_match_client(addr, clp, 0) == false)
+                       continue;
+               atomic_inc(&clp->cl_count);
+               spin_unlock(&nfs_client_lock);
+               return clp;
+       }
+       spin_unlock(&nfs_client_lock);
+       return NULL;
+}
+
+/*
+ * NFSv4.0 callback thread helper
+ *
+ * Find a client by callback identifier
+ */
+struct nfs_client *
+nfs4_find_client_ident(int cb_ident)
+{
+       struct nfs_client *clp;
+
+       spin_lock(&nfs_client_lock);
+       clp = idr_find(&cb_ident_idr, cb_ident);
+       if (clp)
+               atomic_inc(&clp->cl_count);
+       spin_unlock(&nfs_client_lock);
+       return clp;
+}
+
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * NFSv4.1 callback thread helper
+ * For CB_COMPOUND calls, find a client by IP address, protocol version,
+ * minorversion, and sessionID
+ *
+ * CREATE_SESSION triggers a CB_NULL ping from servers. The callback service
+ * sessionid can only be set after the CREATE_SESSION return, so a CB_NULL
+ * can arrive before the callback sessionid is set. For CB_NULL calls,
+ * find a client by IP address protocol version, and minorversion.
+ *
+ * Returns NULL if no such client
+ */
+struct nfs_client *
+nfs4_find_client_sessionid(const struct sockaddr *addr,
+                          struct nfs4_sessionid *sid, int is_cb_compound)
+{
+       struct nfs_client *clp;
+
+       spin_lock(&nfs_client_lock);
+       list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
+               if (nfs4_cb_match_client(addr, clp, 1) == false)
+                       continue;
+
+               if (!nfs4_has_session(clp))
+                       continue;
+
+               /* Match sessionid unless cb_null call*/
+               if (is_cb_compound && (memcmp(clp->cl_session->sess_id.data,
+                   sid->data, NFS4_MAX_SESSIONID_LEN) != 0))
+                       continue;
+
+               atomic_inc(&clp->cl_count);
+               spin_unlock(&nfs_client_lock);
+               return clp;
+       }
+       spin_unlock(&nfs_client_lock);
+       return NULL;
+}
+
+#else /* CONFIG_NFS_V4_1 */
+
+struct nfs_client *
+nfs4_find_client_sessionid(const struct sockaddr *addr,
+                          struct nfs4_sessionid *sid, int is_cb_compound)
+{
+       return NULL;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+/*
  * Initialize the NFS4 callback service
  */
 static int nfs4_init_callback(struct nfs_client *clp)
@@ -1342,11 +1468,7 @@ static int nfs4_server_common_setup(struct nfs_server *server,
        if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
                server->namelen = NFS4_MAXNAMLEN;
 
-       spin_lock(&nfs_client_lock);
-       list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
-       list_add_tail(&server->master_link, &nfs_volume_list);
-       spin_unlock(&nfs_client_lock);
-
+       nfs_server_insert_lists(server);
        server->mount_time = jiffies;
 out:
        nfs_free_fattr(fattr);
@@ -1551,11 +1673,7 @@ struct nfs_server *nfs_clone_server(struct nfs_server *source,
        if (error < 0)
                goto out_free_server;
 
-       spin_lock(&nfs_client_lock);
-       list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
-       list_add_tail(&server->master_link, &nfs_volume_list);
-       spin_unlock(&nfs_client_lock);
-
+       nfs_server_insert_lists(server);
        server->mount_time = jiffies;
 
        nfs_free_fattr(fattr_fsinfo);
index 1fd62fc..364e432 100644 (file)
@@ -40,11 +40,23 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
        call_rcu(&delegation->rcu, nfs_free_delegation_callback);
 }
 
+/**
+ * nfs_mark_delegation_referenced - set delegation's REFERENCED flag
+ * @delegation: delegation to process
+ *
+ */
 void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
 {
        set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
 }
 
+/**
+ * nfs_have_delegation - check if inode has a delegation
+ * @inode: inode to check
+ * @flags: delegation types to check for
+ *
+ * Returns one if inode has the indicated delegation, otherwise zero.
+ */
 int nfs_have_delegation(struct inode *inode, fmode_t flags)
 {
        struct nfs_delegation *delegation;
@@ -119,10 +131,15 @@ again:
        return 0;
 }
 
-/*
- * Set up a delegation on an inode
+/**
+ * nfs_inode_reclaim_delegation - process a delegation reclaim request
+ * @inode: inode to process
+ * @cred: credential to use for request
+ * @res: new delegation state from server
+ *
  */
-void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
+void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
+                                 struct nfs_openres *res)
 {
        struct nfs_delegation *delegation;
        struct rpc_cred *oldcred = NULL;
@@ -175,38 +192,52 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
        return inode;
 }
 
-static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi,
-                                                          const nfs4_stateid *stateid,
-                                                          struct nfs_client *clp)
+static struct nfs_delegation *
+nfs_detach_delegation_locked(struct nfs_inode *nfsi,
+                            struct nfs_server *server)
 {
        struct nfs_delegation *delegation =
                rcu_dereference_protected(nfsi->delegation,
-                                         lockdep_is_held(&clp->cl_lock));
+                               lockdep_is_held(&server->nfs_client->cl_lock));
 
        if (delegation == NULL)
                goto nomatch;
+
        spin_lock(&delegation->lock);
-       if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
-                               sizeof(delegation->stateid.data)) != 0)
-               goto nomatch_unlock;
        list_del_rcu(&delegation->super_list);
        delegation->inode = NULL;
        nfsi->delegation_state = 0;
        rcu_assign_pointer(nfsi->delegation, NULL);
        spin_unlock(&delegation->lock);
        return delegation;
-nomatch_unlock:
-       spin_unlock(&delegation->lock);
 nomatch:
        return NULL;
 }
 
-/*
- * Set up a delegation on an inode
+static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi,
+                                                   struct nfs_server *server)
+{
+       struct nfs_client *clp = server->nfs_client;
+       struct nfs_delegation *delegation;
+
+       spin_lock(&clp->cl_lock);
+       delegation = nfs_detach_delegation_locked(nfsi, server);
+       spin_unlock(&clp->cl_lock);
+       return delegation;
+}
+
+/**
+ * nfs_inode_set_delegation - set up a delegation on an inode
+ * @inode: inode to which delegation applies
+ * @cred: cred to use for subsequent delegation processing
+ * @res: new delegation state from server
+ *
+ * Returns zero on success, or a negative errno value.
  */
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
 {
-       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs_client *clp = server->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_delegation *delegation, *old_delegation;
        struct nfs_delegation *freeme = NULL;
@@ -227,7 +258,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
 
        spin_lock(&clp->cl_lock);
        old_delegation = rcu_dereference_protected(nfsi->delegation,
-                                                  lockdep_is_held(&clp->cl_lock));
+                                       lockdep_is_held(&clp->cl_lock));
        if (old_delegation != NULL) {
                if (memcmp(&delegation->stateid, &old_delegation->stateid,
                                        sizeof(old_delegation->stateid)) == 0 &&
@@ -246,9 +277,9 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
                        delegation = NULL;
                        goto out;
                }
-               freeme = nfs_detach_delegation_locked(nfsi, NULL, clp);
+               freeme = nfs_detach_delegation_locked(nfsi, server);
        }
-       list_add_rcu(&delegation->super_list, &clp->cl_delegations);
+       list_add_rcu(&delegation->super_list, &server->delegations);
        nfsi->delegation_state = delegation->type;
        rcu_assign_pointer(nfsi->delegation, delegation);
        delegation = NULL;
@@ -290,73 +321,85 @@ out:
        return err;
 }
 
-/*
- * Return all delegations that have been marked for return
+/**
+ * nfs_client_return_marked_delegations - return previously marked delegations
+ * @clp: nfs_client to process
+ *
+ * Returns zero on success, or a negative errno value.
  */
 int nfs_client_return_marked_delegations(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation;
+       struct nfs_server *server;
        struct inode *inode;
        int err = 0;
 
 restart:
        rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
-               if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
-                       continue;
-               inode = nfs_delegation_grab_inode(delegation);
-               if (inode == NULL)
-                       continue;
-               spin_lock(&clp->cl_lock);
-               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);
-               spin_unlock(&clp->cl_lock);
-               rcu_read_unlock();
-               if (delegation != NULL) {
-                       filemap_flush(inode->i_mapping);
-                       err = __nfs_inode_return_delegation(inode, delegation, 0);
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               list_for_each_entry_rcu(delegation, &server->delegations,
+                                                               super_list) {
+                       if (!test_and_clear_bit(NFS_DELEGATION_RETURN,
+                                                       &delegation->flags))
+                               continue;
+                       inode = nfs_delegation_grab_inode(delegation);
+                       if (inode == NULL)
+                               continue;
+                       delegation = nfs_detach_delegation(NFS_I(inode),
+                                                               server);
+                       rcu_read_unlock();
+
+                       if (delegation != NULL) {
+                               filemap_flush(inode->i_mapping);
+                               err = __nfs_inode_return_delegation(inode,
+                                                               delegation, 0);
+                       }
+                       iput(inode);
+                       if (!err)
+                               goto restart;
+                       set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+                       return err;
                }
-               iput(inode);
-               if (!err)
-                       goto restart;
-               set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
-               return err;
        }
        rcu_read_unlock();
        return 0;
 }
 
-/*
- * This function returns the delegation without reclaiming opens
- * or protecting against delegation reclaims.
- * It is therefore really only safe to be called from
- * nfs4_clear_inode()
+/**
+ * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens
+ * @inode: inode to process
+ *
+ * Does not protect against delegation reclaims, therefore really only safe
+ * to be called from nfs4_clear_inode().
  */
 void nfs_inode_return_delegation_noreclaim(struct inode *inode)
 {
-       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_server *server = NFS_SERVER(inode);
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_delegation *delegation;
 
        if (rcu_access_pointer(nfsi->delegation) != NULL) {
-               spin_lock(&clp->cl_lock);
-               delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
-               spin_unlock(&clp->cl_lock);
+               delegation = nfs_detach_delegation(nfsi, server);
                if (delegation != NULL)
                        nfs_do_return_delegation(inode, delegation, 0);
        }
 }
 
+/**
+ * nfs_inode_return_delegation - synchronously return a delegation
+ * @inode: inode to process
+ *
+ * Returns zero on success, or a negative errno value.
+ */
 int nfs_inode_return_delegation(struct inode *inode)
 {
-       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_server *server = NFS_SERVER(inode);
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_delegation *delegation;
        int err = 0;
 
        if (rcu_access_pointer(nfsi->delegation) != NULL) {
-               spin_lock(&clp->cl_lock);
-               delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
-               spin_unlock(&clp->cl_lock);
+               delegation = nfs_detach_delegation(nfsi, server);
                if (delegation != NULL) {
                        nfs_wb_all(inode);
                        err = __nfs_inode_return_delegation(inode, delegation, 1);
@@ -365,46 +408,61 @@ int nfs_inode_return_delegation(struct inode *inode)
        return err;
 }
 
-static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation)
+static void nfs_mark_return_delegation(struct nfs_delegation *delegation)
 {
+       struct nfs_client *clp = NFS_SERVER(delegation->inode)->nfs_client;
+
        set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
        set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
 }
 
-/*
- * Return all delegations associated to a super block
+/**
+ * nfs_super_return_all_delegations - return delegations for one superblock
+ * @sb: sb to process
+ *
  */
 void nfs_super_return_all_delegations(struct super_block *sb)
 {
-       struct nfs_client *clp = NFS_SB(sb)->nfs_client;
+       struct nfs_server *server = NFS_SB(sb);
+       struct nfs_client *clp = server->nfs_client;
        struct nfs_delegation *delegation;
 
        if (clp == NULL)
                return;
+
        rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                spin_lock(&delegation->lock);
-               if (delegation->inode != NULL && delegation->inode->i_sb == sb)
-                       set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+               set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
                spin_unlock(&delegation->lock);
        }
        rcu_read_unlock();
+
        if (nfs_client_return_marked_delegations(clp) != 0)
                nfs4_schedule_state_manager(clp);
 }
 
-static
-void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, fmode_t flags)
+static void nfs_mark_return_all_delegation_types(struct nfs_server *server,
+                                                fmode_t flags)
 {
        struct nfs_delegation *delegation;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE))
                        continue;
                if (delegation->type & flags)
-                       nfs_mark_return_delegation(clp, delegation);
+                       nfs_mark_return_delegation(delegation);
        }
+}
+
+static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp,
+                                                       fmode_t flags)
+{
+       struct nfs_server *server;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs_mark_return_all_delegation_types(server, flags);
        rcu_read_unlock();
 }
 
@@ -419,19 +477,32 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp)
                nfs4_schedule_state_manager(clp);
 }
 
+/**
+ * nfs_expire_all_delegation_types
+ * @clp: client to process
+ * @flags: delegation types to expire
+ *
+ */
 void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags)
 {
        nfs_client_mark_return_all_delegation_types(clp, flags);
        nfs_delegation_run_state_manager(clp);
 }
 
+/**
+ * nfs_expire_all_delegations
+ * @clp: client to process
+ *
+ */
 void nfs_expire_all_delegations(struct nfs_client *clp)
 {
        nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE);
 }
 
-/*
- * Return all delegations following an NFS4ERR_CB_PATH_DOWN error.
+/**
+ * nfs_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN
+ * @clp: client to process
+ *
  */
 void nfs_handle_cb_pathdown(struct nfs_client *clp)
 {
@@ -440,29 +511,43 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp)
        nfs_client_mark_return_all_delegations(clp);
 }
 
-static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp)
+static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server)
 {
        struct nfs_delegation *delegation;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
                        continue;
-               nfs_mark_return_delegation(clp, delegation);
+               nfs_mark_return_delegation(delegation);
        }
-       rcu_read_unlock();
 }
 
+/**
+ * nfs_expire_unreferenced_delegations - Eliminate unused delegations
+ * @clp: nfs_client to process
+ *
+ */
 void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
 {
-       nfs_client_mark_return_unreferenced_delegations(clp);
+       struct nfs_server *server;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs_mark_return_unreferenced_delegations(server);
+       rcu_read_unlock();
+
        nfs_delegation_run_state_manager(clp);
 }
 
-/*
- * Asynchronous delegation recall!
+/**
+ * nfs_async_inode_return_delegation - asynchronously return a delegation
+ * @inode: inode to process
+ * @stateid: state ID information from CB_RECALL arguments
+ *
+ * Returns zero on success, or a negative errno value.
  */
-int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
+int nfs_async_inode_return_delegation(struct inode *inode,
+                                     const nfs4_stateid *stateid)
 {
        struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_delegation *delegation;
@@ -474,22 +559,21 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s
                rcu_read_unlock();
                return -ENOENT;
        }
-
-       nfs_mark_return_delegation(clp, delegation);
+       nfs_mark_return_delegation(delegation);
        rcu_read_unlock();
+
        nfs_delegation_run_state_manager(clp);
        return 0;
 }
 
-/*
- * Retrieve the inode associated with a delegation
- */
-struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle)
+static struct inode *
+nfs_delegation_find_inode_server(struct nfs_server *server,
+                                const struct nfs_fh *fhandle)
 {
        struct nfs_delegation *delegation;
        struct inode *res = NULL;
-       rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
+
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                spin_lock(&delegation->lock);
                if (delegation->inode != NULL &&
                    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
@@ -499,49 +583,121 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
                if (res != NULL)
                        break;
        }
+       return res;
+}
+
+/**
+ * nfs_delegation_find_inode - retrieve the inode associated with a delegation
+ * @clp: client state handle
+ * @fhandle: filehandle from a delegation recall
+ *
+ * Returns pointer to inode matching "fhandle," or NULL if a matching inode
+ * cannot be found.
+ */
+struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
+                                       const struct nfs_fh *fhandle)
+{
+       struct nfs_server *server;
+       struct inode *res = NULL;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               res = nfs_delegation_find_inode_server(server, fhandle);
+               if (res != NULL)
+                       break;
+       }
        rcu_read_unlock();
        return res;
 }
 
-/*
- * Mark all delegations as needing to be reclaimed
+static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
+{
+       struct nfs_delegation *delegation;
+
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list)
+               set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
+}
+
+/**
+ * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed
+ * @clp: nfs_client to process
+ *
  */
 void nfs_delegation_mark_reclaim(struct nfs_client *clp)
 {
-       struct nfs_delegation *delegation;
+       struct nfs_server *server;
+
        rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
-               set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs_delegation_mark_reclaim_server(server);
        rcu_read_unlock();
 }
 
-/*
- * Reap all unclaimed delegations after reboot recovery is done
+/**
+ * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done
+ * @clp: nfs_client to process
+ *
  */
 void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation;
+       struct nfs_server *server;
        struct inode *inode;
+
 restart:
        rcu_read_lock();
-       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
-               if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0)
-                       continue;
-               inode = nfs_delegation_grab_inode(delegation);
-               if (inode == NULL)
-                       continue;
-               spin_lock(&clp->cl_lock);
-               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);
-               spin_unlock(&clp->cl_lock);
-               rcu_read_unlock();
-               if (delegation != NULL)
-                       nfs_free_delegation(delegation);
-               iput(inode);
-               goto restart;
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               list_for_each_entry_rcu(delegation, &server->delegations,
+                                                               super_list) {
+                       if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
+                                               &delegation->flags) == 0)
+                               continue;
+                       inode = nfs_delegation_grab_inode(delegation);
+                       if (inode == NULL)
+                               continue;
+                       delegation = nfs_detach_delegation(NFS_I(inode),
+                                                               server);
+                       rcu_read_unlock();
+
+                       if (delegation != NULL)
+                               nfs_free_delegation(delegation);
+                       iput(inode);
+                       goto restart;
+               }
        }
        rcu_read_unlock();
 }
 
+/**
+ * nfs_delegations_present - check for existence of delegations
+ * @clp: client state handle
+ *
+ * Returns one if there are any nfs_delegation structures attached
+ * to this nfs_client.
+ */
+int nfs_delegations_present(struct nfs_client *clp)
+{
+       struct nfs_server *server;
+       int ret = 0;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               if (!list_empty(&server->delegations)) {
+                       ret = 1;
+                       break;
+               }
+       rcu_read_unlock();
+       return ret;
+}
+
+/**
+ * nfs4_copy_delegation_stateid - Copy inode's state ID information
+ * @dst: stateid data structure to fill in
+ * @inode: inode to check
+ *
+ * Returns one and fills in "dst->data" * if inode had a delegation,
+ * otherwise zero is returned.
+ */
 int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
index 2026304..d9322e4 100644 (file)
@@ -44,6 +44,7 @@ void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags);
 void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
 void nfs_handle_cb_pathdown(struct nfs_client *clp);
 int nfs_client_return_marked_delegations(struct nfs_client *clp);
+int nfs_delegations_present(struct nfs_client *clp);
 
 void nfs_delegation_mark_reclaim(struct nfs_client *clp);
 void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
index 0108cf4..16ec096 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/mount.h>
 #include <linux/sched.h>
 #include <linux/kmemleak.h>
+#include <linux/xattr.h>
 
 #include "delegation.h"
 #include "iostat.h"
@@ -124,9 +125,10 @@ const struct inode_operations nfs4_dir_inode_operations = {
        .permission     = nfs_permission,
        .getattr        = nfs_getattr,
        .setattr        = nfs_setattr,
-       .getxattr       = nfs4_getxattr,
-       .setxattr       = nfs4_setxattr,
-       .listxattr      = nfs4_listxattr,
+       .getxattr       = generic_getxattr,
+       .setxattr       = generic_setxattr,
+       .listxattr      = generic_listxattr,
+       .removexattr    = generic_removexattr,
 };
 
 #endif /* CONFIG_NFS_V4 */
@@ -171,7 +173,7 @@ struct nfs_cache_array {
        struct nfs_cache_array_entry array[0];
 };
 
-typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
+typedef int (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int);
 typedef struct {
        struct file     *file;
        struct page     *page;
@@ -377,14 +379,14 @@ error:
        return error;
 }
 
-/* Fill in an entry based on the xdr code stored in desc->page */
-static
-int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream)
+static int xdr_decode(nfs_readdir_descriptor_t *desc,
+                     struct nfs_entry *entry, struct xdr_stream *xdr)
 {
-       __be32 *p = desc->decode(stream, entry, NFS_SERVER(desc->file->f_path.dentry->d_inode), desc->plus);
-       if (IS_ERR(p))
-               return PTR_ERR(p);
+       int error;
 
+       error = desc->decode(xdr, entry, desc->plus);
+       if (error)
+               return error;
        entry->fattr->time_start = desc->timestamp;
        entry->fattr->gencount = desc->gencount;
        return 0;
@@ -564,6 +566,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
        entry.eof = 0;
        entry.fh = nfs_alloc_fhandle();
        entry.fattr = nfs_alloc_fattr();
+       entry.server = NFS_SERVER(inode);
        if (entry.fh == NULL || entry.fattr == NULL)
                goto out;
 
@@ -1215,7 +1218,7 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
                goto out_unblock_sillyrename;
        }
        inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
-       res = (struct dentry *)inode;
+       res = ERR_CAST(inode);
        if (IS_ERR(res))
                goto out_unblock_sillyrename;
 
@@ -1349,8 +1352,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
        if (nd->flags & LOOKUP_CREATE) {
                attr.ia_mode = nd->intent.open.create_mode;
                attr.ia_valid = ATTR_MODE;
-               if (!IS_POSIXACL(dir))
-                       attr.ia_mode &= ~current_umask();
+               attr.ia_mode &= ~current_umask();
        } else {
                open_flags &= ~(O_EXCL | O_CREAT);
                attr.ia_valid = 0;
index 4e2d9b6..1869688 100644 (file)
@@ -238,7 +238,7 @@ int nfs_map_gid_to_group(struct nfs_client *clp, __u32 gid, char *buf, size_t bu
        return nfs_idmap_lookup_name(gid, "group", buf, buflen);
 }
 
-#else  /* CONFIG_NFS_USE_IDMAPPER not defined */
+#else  /* CONFIG_NFS_USE_NEW_IDMAPPER not defined */
 
 #include <linux/module.h>
 #include <linux/mutex.h>
index e67e31c..790b786 100644 (file)
@@ -1410,9 +1410,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
  */
 void nfs4_evict_inode(struct inode *inode)
 {
+       pnfs_destroy_layout(NFS_I(inode));
        truncate_inode_pages(&inode->i_data, 0);
        end_writeback(inode);
-       pnfs_destroy_layout(NFS_I(inode));
        /* If we are holding a delegation, return it! */
        nfs_inode_return_delegation_noreclaim(inode);
        /* First call standard NFS clear_inode() code */
@@ -1612,6 +1612,7 @@ static void __exit exit_nfs_fs(void)
 #ifdef CONFIG_PROC_FS
        rpc_proc_unregister("nfs");
 #endif
+       nfs_cleanup_cb_ident_idr();
        unregister_nfs_fs();
        nfs_fs_proc_exit();
        nfsiod_stop();
index e6356b7..bfa3a34 100644 (file)
@@ -128,9 +128,13 @@ extern void nfs_umount(const struct nfs_mount_request *info);
 /* client.c */
 extern struct rpc_program nfs_program;
 
+extern void nfs_cleanup_cb_ident_idr(void);
 extern void nfs_put_client(struct nfs_client *);
-extern struct nfs_client *nfs_find_client(const struct sockaddr *, u32);
-extern struct nfs_client *nfs_find_client_next(struct nfs_client *);
+extern struct nfs_client *nfs4_find_client_no_ident(const struct sockaddr *);
+extern struct nfs_client *nfs4_find_client_ident(int);
+extern struct nfs_client *
+nfs4_find_client_sessionid(const struct sockaddr *, struct nfs4_sessionid *,
+                          int);
 extern struct nfs_server *nfs_create_server(
                                        const struct nfs_parsed_mount_data *,
                                        struct nfs_fh *);
@@ -185,17 +189,20 @@ extern int __init nfs_init_directcache(void);
 extern void nfs_destroy_directcache(void);
 
 /* nfs2xdr.c */
-extern int nfs_stat_to_errno(int);
+extern int nfs_stat_to_errno(enum nfs_stat);
 extern struct rpc_procinfo nfs_procedures[];
-extern __be32 *nfs_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
+extern int nfs2_decode_dirent(struct xdr_stream *,
+                               struct nfs_entry *, int);
 
 /* nfs3xdr.c */
 extern struct rpc_procinfo nfs3_procedures[];
-extern __be32 *nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
+extern int nfs3_decode_dirent(struct xdr_stream *,
+                               struct nfs_entry *, int);
 
 /* nfs4xdr.c */
 #ifdef CONFIG_NFS_V4
-extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
+extern int nfs4_decode_dirent(struct xdr_stream *,
+                               struct nfs_entry *, int);
 #endif
 #ifdef CONFIG_NFS_V4_1
 extern const u32 nfs41_maxread_overhead;
index 4f981f1..d4c2d6b 100644 (file)
@@ -236,10 +236,8 @@ void nfs_umount(const struct nfs_mount_request *info)
                .authflavor     = RPC_AUTH_UNIX,
                .flags          = RPC_CLNT_CREATE_NOPING,
        };
-       struct mountres result;
        struct rpc_message msg  = {
                .rpc_argp       = info->dirpath,
-               .rpc_resp       = &result,
        };
        struct rpc_clnt *clnt;
        int status;
@@ -248,7 +246,7 @@ void nfs_umount(const struct nfs_mount_request *info)
                args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
 
        clnt = rpc_create(&args);
-       if (unlikely(IS_ERR(clnt)))
+       if (IS_ERR(clnt))
                goto out_clnt_err;
 
        dprintk("NFS: sending UMNT request for %s:%s\n",
@@ -280,29 +278,20 @@ out_call_err:
  * XDR encode/decode functions for MOUNT
  */
 
-static int encode_mntdirpath(struct xdr_stream *xdr, const char *pathname)
+static void encode_mntdirpath(struct xdr_stream *xdr, const char *pathname)
 {
        const u32 pathname_len = strlen(pathname);
        __be32 *p;
 
-       if (unlikely(pathname_len > MNTPATHLEN))
-               return -EIO;
-
-       p = xdr_reserve_space(xdr, sizeof(u32) + pathname_len);
-       if (unlikely(p == NULL))
-               return -EIO;
+       BUG_ON(pathname_len > MNTPATHLEN);
+       p = xdr_reserve_space(xdr, 4 + pathname_len);
        xdr_encode_opaque(p, pathname, pathname_len);
-
-       return 0;
 }
 
-static int mnt_enc_dirpath(struct rpc_rqst *req, __be32 *p,
-                          const char *dirpath)
+static void mnt_xdr_enc_dirpath(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               const char *dirpath)
 {
-       struct xdr_stream xdr;
-
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       return encode_mntdirpath(&xdr, dirpath);
+       encode_mntdirpath(xdr, dirpath);
 }
 
 /*
@@ -320,10 +309,10 @@ static int decode_status(struct xdr_stream *xdr, struct mountres *res)
        u32 status;
        __be32 *p;
 
-       p = xdr_inline_decode(xdr, sizeof(status));
+       p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                return -EIO;
-       status = ntohl(*p);
+       status = be32_to_cpup(p);
 
        for (i = 0; i < ARRAY_SIZE(mnt_errtbl); i++) {
                if (mnt_errtbl[i].status == status) {
@@ -351,18 +340,16 @@ static int decode_fhandle(struct xdr_stream *xdr, struct mountres *res)
        return 0;
 }
 
-static int mnt_dec_mountres(struct rpc_rqst *req, __be32 *p,
-                           struct mountres *res)
+static int mnt_xdr_dec_mountres(struct rpc_rqst *req,
+                               struct xdr_stream *xdr,
+                               struct mountres *res)
 {
-       struct xdr_stream xdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-
-       status = decode_status(&xdr, res);
+       status = decode_status(xdr, res);
        if (unlikely(status != 0 || res->errno != 0))
                return status;
-       return decode_fhandle(&xdr, res);
+       return decode_fhandle(xdr, res);
 }
 
 static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res)
@@ -371,10 +358,10 @@ static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res)
        u32 status;
        __be32 *p;
 
-       p = xdr_inline_decode(xdr, sizeof(status));
+       p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                return -EIO;
-       status = ntohl(*p);
+       status = be32_to_cpup(p);
 
        for (i = 0; i < ARRAY_SIZE(mnt3_errtbl); i++) {
                if (mnt3_errtbl[i].status == status) {
@@ -394,11 +381,11 @@ static int decode_fhandle3(struct xdr_stream *xdr, struct mountres *res)
        u32 size;
        __be32 *p;
 
-       p = xdr_inline_decode(xdr, sizeof(size));
+       p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                return -EIO;
 
-       size = ntohl(*p++);
+       size = be32_to_cpup(p);
        if (size > NFS3_FHSIZE || size == 0)
                return -EIO;
 
@@ -421,15 +408,15 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res)
        if (*count == 0)
                return 0;
 
-       p = xdr_inline_decode(xdr, sizeof(entries));
+       p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                return -EIO;
-       entries = ntohl(*p);
+       entries = be32_to_cpup(p);
        dprintk("NFS: received %u auth flavors\n", entries);
        if (entries > NFS_MAX_SECFLAVORS)
                entries = NFS_MAX_SECFLAVORS;
 
-       p = xdr_inline_decode(xdr, sizeof(u32) * entries);
+       p = xdr_inline_decode(xdr, 4 * entries);
        if (unlikely(p == NULL))
                return -EIO;
 
@@ -437,7 +424,7 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res)
                entries = *count;
 
        for (i = 0; i < entries; i++) {
-               flavors[i] = ntohl(*p++);
+               flavors[i] = be32_to_cpup(p++);
                dprintk("NFS:   auth flavor[%u]: %d\n", i, flavors[i]);
        }
        *count = i;
@@ -445,30 +432,28 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res)
        return 0;
 }
 
-static int mnt_dec_mountres3(struct rpc_rqst *req, __be32 *p,
-                            struct mountres *res)
+static int mnt_xdr_dec_mountres3(struct rpc_rqst *req,
+                                struct xdr_stream *xdr,
+                                struct mountres *res)
 {
-       struct xdr_stream xdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-
-       status = decode_fhs_status(&xdr, res);
+       status = decode_fhs_status(xdr, res);
        if (unlikely(status != 0 || res->errno != 0))
                return status;
-       status = decode_fhandle3(&xdr, res);
+       status = decode_fhandle3(xdr, res);
        if (unlikely(status != 0)) {
                res->errno = -EBADHANDLE;
                return 0;
        }
-       return decode_auth_flavors(&xdr, res);
+       return decode_auth_flavors(xdr, res);
 }
 
 static struct rpc_procinfo mnt_procedures[] = {
        [MOUNTPROC_MNT] = {
                .p_proc         = MOUNTPROC_MNT,
-               .p_encode       = (kxdrproc_t)mnt_enc_dirpath,
-               .p_decode       = (kxdrproc_t)mnt_dec_mountres,
+               .p_encode       = (kxdreproc_t)mnt_xdr_enc_dirpath,
+               .p_decode       = (kxdrdproc_t)mnt_xdr_dec_mountres,
                .p_arglen       = MNT_enc_dirpath_sz,
                .p_replen       = MNT_dec_mountres_sz,
                .p_statidx      = MOUNTPROC_MNT,
@@ -476,7 +461,7 @@ static struct rpc_procinfo mnt_procedures[] = {
        },
        [MOUNTPROC_UMNT] = {
                .p_proc         = MOUNTPROC_UMNT,
-               .p_encode       = (kxdrproc_t)mnt_enc_dirpath,
+               .p_encode       = (kxdreproc_t)mnt_xdr_enc_dirpath,
                .p_arglen       = MNT_enc_dirpath_sz,
                .p_statidx      = MOUNTPROC_UMNT,
                .p_name         = "UMOUNT",
@@ -486,8 +471,8 @@ static struct rpc_procinfo mnt_procedures[] = {
 static struct rpc_procinfo mnt3_procedures[] = {
        [MOUNTPROC3_MNT] = {
                .p_proc         = MOUNTPROC3_MNT,
-               .p_encode       = (kxdrproc_t)mnt_enc_dirpath,
-               .p_decode       = (kxdrproc_t)mnt_dec_mountres3,
+               .p_encode       = (kxdreproc_t)mnt_xdr_enc_dirpath,
+               .p_decode       = (kxdrdproc_t)mnt_xdr_dec_mountres3,
                .p_arglen       = MNT_enc_dirpath_sz,
                .p_replen       = MNT_dec_mountres3_sz,
                .p_statidx      = MOUNTPROC3_MNT,
@@ -495,7 +480,7 @@ static struct rpc_procinfo mnt3_procedures[] = {
        },
        [MOUNTPROC3_UMNT] = {
                .p_proc         = MOUNTPROC3_UMNT,
-               .p_encode       = (kxdrproc_t)mnt_enc_dirpath,
+               .p_encode       = (kxdreproc_t)mnt_xdr_enc_dirpath,
                .p_arglen       = MNT_enc_dirpath_sz,
                .p_statidx      = MOUNTPROC3_UMNT,
                .p_name         = "UMOUNT",
index b382a1b..792cb13 100644 (file)
 #define NFS_readdirres_sz      (1)
 #define NFS_statfsres_sz       (1+NFS_info_sz)
 
+
+/*
+ * While encoding arguments, set up the reply buffer in advance to
+ * receive reply data directly into the page cache.
+ */
+static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
+                                unsigned int base, unsigned int len,
+                                unsigned int bufsize)
+{
+       struct rpc_auth *auth = req->rq_cred->cr_auth;
+       unsigned int replen;
+
+       replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
+       xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
+}
+
 /*
- * Common NFS XDR functions as inlines
+ * Handle decode buffer overflows out-of-line.
  */
-static inline __be32 *
-xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fhandle)
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
 {
-       memcpy(p, fhandle->data, NFS2_FHSIZE);
-       return p + XDR_QUADLEN(NFS2_FHSIZE);
+       dprintk("NFS: %s prematurely hit the end of our receive buffer. "
+               "Remaining buffer length is %tu words.\n",
+               func, xdr->end - xdr->p);
 }
 
-static inline __be32 *
-xdr_decode_fhandle(__be32 *p, struct nfs_fh *fhandle)
+
+/*
+ * Encode/decode NFSv2 basic data types
+ *
+ * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
+ * "NFS: Network File System Protocol Specification".
+ *
+ * Not all basic data types have their own encoding and decoding
+ * functions.  For run-time efficiency, some data types are encoded
+ * or decoded inline.
+ */
+
+/*
+ *     typedef opaque  nfsdata<>;
+ */
+static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result)
 {
-       /* NFSv2 handles have a fixed length */
-       fhandle->size = NFS2_FHSIZE;
-       memcpy(fhandle->data, p, NFS2_FHSIZE);
-       return p + XDR_QUADLEN(NFS2_FHSIZE);
+       u32 recvd, count;
+       size_t hdrlen;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       count = be32_to_cpup(p);
+       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+       recvd = xdr->buf->len - hdrlen;
+       if (unlikely(count > recvd))
+               goto out_cheating;
+out:
+       xdr_read_pages(xdr, count);
+       result->eof = 0;        /* NFSv2 does not pass EOF flag on the wire. */
+       result->count = count;
+       return count;
+out_cheating:
+       dprintk("NFS: server cheating in read result: "
+               "count %u > recvd %u\n", count, recvd);
+       count = recvd;
+       goto out;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
-static inline __be32*
-xdr_encode_time(__be32 *p, struct timespec *timep)
+/*
+ *     enum stat {
+ *             NFS_OK = 0,
+ *             NFSERR_PERM = 1,
+ *             NFSERR_NOENT = 2,
+ *             NFSERR_IO = 5,
+ *             NFSERR_NXIO = 6,
+ *             NFSERR_ACCES = 13,
+ *             NFSERR_EXIST = 17,
+ *             NFSERR_NODEV = 19,
+ *             NFSERR_NOTDIR = 20,
+ *             NFSERR_ISDIR = 21,
+ *             NFSERR_FBIG = 27,
+ *             NFSERR_NOSPC = 28,
+ *             NFSERR_ROFS = 30,
+ *             NFSERR_NAMETOOLONG = 63,
+ *             NFSERR_NOTEMPTY = 66,
+ *             NFSERR_DQUOT = 69,
+ *             NFSERR_STALE = 70,
+ *             NFSERR_WFLUSH = 99
+ *     };
+ */
+static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
 {
-       *p++ = htonl(timep->tv_sec);
-       /* Convert nanoseconds into microseconds */
-       *p++ = htonl(timep->tv_nsec ? timep->tv_nsec / 1000 : 0);
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       *status = be32_to_cpup(p);
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ * 2.3.2.  ftype
+ *
+ *     enum ftype {
+ *             NFNON = 0,
+ *             NFREG = 1,
+ *             NFDIR = 2,
+ *             NFBLK = 3,
+ *             NFCHR = 4,
+ *             NFLNK = 5
+ *     };
+ *
+ */
+static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
+{
+       *type = be32_to_cpup(p++);
+       if (unlikely(*type > NF2FIFO))
+               *type = NFBAD;
        return p;
 }
 
-static inline __be32*
-xdr_encode_current_server_time(__be32 *p, struct timespec *timep)
+/*
+ * 2.3.3.  fhandle
+ *
+ *     typedef opaque fhandle[FHSIZE];
+ */
+static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
+{
+       __be32 *p;
+
+       BUG_ON(fh->size != NFS2_FHSIZE);
+       p = xdr_reserve_space(xdr, NFS2_FHSIZE);
+       memcpy(p, fh->data, NFS2_FHSIZE);
+}
+
+static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
 {
-       /*
-        * Passing the invalid value useconds=1000000 is a
-        * Sun convention for "set to current server time".
-        * It's needed to make permissions checks for the
-        * "touch" program across v2 mounts to Solaris and
-        * Irix boxes work correctly. See description of
-        * sattr in section 6.1 of "NFS Illustrated" by
-        * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
-        */
-       *p++ = htonl(timep->tv_sec);
-       *p++ = htonl(1000000);
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, NFS2_FHSIZE);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       fh->size = NFS2_FHSIZE;
+       memcpy(fh->data, p, NFS2_FHSIZE);
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ * 2.3.4.  timeval
+ *
+ *     struct timeval {
+ *             unsigned int seconds;
+ *             unsigned int useconds;
+ *     };
+ */
+static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
+{
+       *p++ = cpu_to_be32(timep->tv_sec);
+       if (timep->tv_nsec != 0)
+               *p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
+       else
+               *p++ = cpu_to_be32(0);
+       return p;
+}
+
+/*
+ * Passing the invalid value useconds=1000000 is a Sun convention for
+ * "set to current server time".  It's needed to make permissions checks
+ * for the "touch" program across v2 mounts to Solaris and Irix servers
+ * work correctly.  See description of sattr in section 6.1 of "NFS
+ * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
+ */
+static __be32 *xdr_encode_current_server_time(__be32 *p,
+                                             const struct timespec *timep)
+{
+       *p++ = cpu_to_be32(timep->tv_sec);
+       *p++ = cpu_to_be32(1000000);
        return p;
 }
 
-static inline __be32*
-xdr_decode_time(__be32 *p, struct timespec *timep)
+static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep)
 {
-       timep->tv_sec = ntohl(*p++);
-       /* Convert microseconds into nanoseconds */
-       timep->tv_nsec = ntohl(*p++) * 1000;
+       timep->tv_sec = be32_to_cpup(p++);
+       timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC;
        return p;
 }
 
-static __be32 *
-xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
+/*
+ * 2.3.5.  fattr
+ *
+ *     struct fattr {
+ *             ftype           type;
+ *             unsigned int    mode;
+ *             unsigned int    nlink;
+ *             unsigned int    uid;
+ *             unsigned int    gid;
+ *             unsigned int    size;
+ *             unsigned int    blocksize;
+ *             unsigned int    rdev;
+ *             unsigned int    blocks;
+ *             unsigned int    fsid;
+ *             unsigned int    fileid;
+ *             timeval         atime;
+ *             timeval         mtime;
+ *             timeval         ctime;
+ *     };
+ *
+ */
+static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
 {
        u32 rdev, type;
-       type = ntohl(*p++);
-       fattr->mode = ntohl(*p++);
-       fattr->nlink = ntohl(*p++);
-       fattr->uid = ntohl(*p++);
-       fattr->gid = ntohl(*p++);
-       fattr->size = ntohl(*p++);
-       fattr->du.nfs2.blocksize = ntohl(*p++);
-       rdev = ntohl(*p++);
-       fattr->du.nfs2.blocks = ntohl(*p++);
-       fattr->fsid.major = ntohl(*p++);
-       fattr->fsid.minor = 0;
-       fattr->fileid = ntohl(*p++);
-       p = xdr_decode_time(p, &fattr->atime);
-       p = xdr_decode_time(p, &fattr->mtime);
-       p = xdr_decode_time(p, &fattr->ctime);
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+
        fattr->valid |= NFS_ATTR_FATTR_V2;
+
+       p = xdr_decode_ftype(p, &type);
+
+       fattr->mode = be32_to_cpup(p++);
+       fattr->nlink = be32_to_cpup(p++);
+       fattr->uid = be32_to_cpup(p++);
+       fattr->gid = be32_to_cpup(p++);
+       fattr->size = be32_to_cpup(p++);
+       fattr->du.nfs2.blocksize = be32_to_cpup(p++);
+
+       rdev = be32_to_cpup(p++);
        fattr->rdev = new_decode_dev(rdev);
-       if (type == NFCHR && rdev == NFS2_FIFO_DEV) {
+       if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) {
                fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
                fattr->rdev = 0;
        }
-       return p;
+
+       fattr->du.nfs2.blocks = be32_to_cpup(p++);
+       fattr->fsid.major = be32_to_cpup(p++);
+       fattr->fsid.minor = 0;
+       fattr->fileid = be32_to_cpup(p++);
+
+       p = xdr_decode_time(p, &fattr->atime);
+       p = xdr_decode_time(p, &fattr->mtime);
+       xdr_decode_time(p, &fattr->ctime);
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
-static inline __be32 *
-xdr_encode_sattr(__be32 *p, struct iattr *attr)
+/*
+ * 2.3.6.  sattr
+ *
+ *     struct sattr {
+ *             unsigned int    mode;
+ *             unsigned int    uid;
+ *             unsigned int    gid;
+ *             unsigned int    size;
+ *             timeval         atime;
+ *             timeval         mtime;
+ *     };
+ */
+
+#define NFS2_SATTR_NOT_SET     (0xffffffff)
+
+static __be32 *xdr_time_not_set(__be32 *p)
 {
-       const __be32 not_set = __constant_htonl(0xFFFFFFFF);
+       *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
+       *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
+       return p;
+}
 
-       *p++ = (attr->ia_valid & ATTR_MODE) ? htonl(attr->ia_mode) : not_set;
-       *p++ = (attr->ia_valid & ATTR_UID) ? htonl(attr->ia_uid) : not_set;
-       *p++ = (attr->ia_valid & ATTR_GID) ? htonl(attr->ia_gid) : not_set;
-       *p++ = (attr->ia_valid & ATTR_SIZE) ? htonl(attr->ia_size) : not_set;
+static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
+{
+       __be32 *p;
 
-       if (attr->ia_valid & ATTR_ATIME_SET) {
+       p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
+
+       if (attr->ia_valid & ATTR_MODE)
+               *p++ = cpu_to_be32(attr->ia_mode);
+       else
+               *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
+       if (attr->ia_valid & ATTR_UID)
+               *p++ = cpu_to_be32(attr->ia_uid);
+       else
+               *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
+       if (attr->ia_valid & ATTR_GID)
+               *p++ = cpu_to_be32(attr->ia_gid);
+       else
+               *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
+       if (attr->ia_valid & ATTR_SIZE)
+               *p++ = cpu_to_be32((u32)attr->ia_size);
+       else
+               *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
+
+       if (attr->ia_valid & ATTR_ATIME_SET)
                p = xdr_encode_time(p, &attr->ia_atime);
-       } else if (attr->ia_valid & ATTR_ATIME) {
+       else if (attr->ia_valid & ATTR_ATIME)
                p = xdr_encode_current_server_time(p, &attr->ia_atime);
-       } else {
-               *p++ = not_set;
-               *p++ = not_set;
-       }
-
-       if (attr->ia_valid & ATTR_MTIME_SET) {
-               p = xdr_encode_time(p, &attr->ia_mtime);
-       } else if (attr->ia_valid & ATTR_MTIME) {
-               p = xdr_encode_current_server_time(p, &attr->ia_mtime);
-       } else {
-               *p++ = not_set; 
-               *p++ = not_set;
-       }
-       return p;
+       else
+               p = xdr_time_not_set(p);
+       if (attr->ia_valid & ATTR_MTIME_SET)
+               xdr_encode_time(p, &attr->ia_mtime);
+       else if (attr->ia_valid & ATTR_MTIME)
+               xdr_encode_current_server_time(p, &attr->ia_mtime);
+       else
+               xdr_time_not_set(p);
 }
 
 /*
- * NFS encode functions
+ * 2.3.7.  filename
+ *
+ *     typedef string filename<MAXNAMLEN>;
  */
+static void encode_filename(struct xdr_stream *xdr,
+                           const char *name, u32 length)
+{
+       __be32 *p;
+
+       BUG_ON(length > NFS2_MAXNAMLEN);
+       p = xdr_reserve_space(xdr, 4 + length);
+       xdr_encode_opaque(p, name, length);
+}
+
+static int decode_filename_inline(struct xdr_stream *xdr,
+                                 const char **name, u32 *length)
+{
+       __be32 *p;
+       u32 count;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       count = be32_to_cpup(p);
+       if (count > NFS3_MAXNAMLEN)
+               goto out_nametoolong;
+       p = xdr_inline_decode(xdr, count);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       *name = (const char *)p;
+       *length = count;
+       return 0;
+out_nametoolong:
+       dprintk("NFS: returned filename too long: %u\n", count);
+       return -ENAMETOOLONG;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
 /*
- * Encode file handle argument
- * GETATTR, READLINK, STATFS
+ * 2.3.8.  path
+ *
+ *     typedef string path<MAXPATHLEN>;
  */
-static int
-nfs_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
+static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
 {
-       p = xdr_encode_fhandle(p, fh);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       __be32 *p;
+
+       BUG_ON(length > NFS2_MAXPATHLEN);
+       p = xdr_reserve_space(xdr, 4);
+       *p = cpu_to_be32(length);
+       xdr_write_pages(xdr, pages, 0, length);
+}
+
+static int decode_path(struct xdr_stream *xdr)
+{
+       u32 length, recvd;
+       size_t hdrlen;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       length = be32_to_cpup(p);
+       if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
+               goto out_size;
+       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+       recvd = xdr->buf->len - hdrlen;
+       if (unlikely(length > recvd))
+               goto out_cheating;
+
+       xdr_read_pages(xdr, length);
+       xdr_terminate_string(xdr->buf, length);
        return 0;
+out_size:
+       dprintk("NFS: returned pathname too long: %u\n", length);
+       return -ENAMETOOLONG;
+out_cheating:
+       dprintk("NFS: server cheating in pathname result: "
+               "length %u > received %u\n", length, recvd);
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 /*
- * Encode SETATTR arguments
+ * 2.3.9.  attrstat
+ *
+ *     union attrstat switch (stat status) {
+ *     case NFS_OK:
+ *             fattr attributes;
+ *     default:
+ *             void;
+ *     };
  */
-static int
-nfs_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs_sattrargs *args)
+static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_sattr(p, args->sattr);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       enum nfs_stat status;
+       int error;
+
+       error = decode_stat(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS_OK)
+               goto out_default;
+       error = decode_fattr(xdr, result);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
 }
 
 /*
- * Encode directory ops argument
- * LOOKUP, RMDIR
+ * 2.3.10.  diropargs
+ *
+ *     struct diropargs {
+ *             fhandle  dir;
+ *             filename name;
+ *     };
  */
-static int
-nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args)
+static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
+                            const char *name, u32 length)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_array(p, args->name, args->len);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       encode_fhandle(xdr, fh);
+       encode_filename(xdr, name, length);
+}
+
+/*
+ * 2.3.11.  diropres
+ *
+ *     union diropres switch (stat status) {
+ *     case NFS_OK:
+ *             struct {
+ *                     fhandle file;
+ *                     fattr   attributes;
+ *             } diropok;
+ *     default:
+ *             void;
+ *     };
+ */
+static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
+{
+       int error;
+
+       error = decode_fhandle(xdr, result->fh);
+       if (unlikely(error))
+               goto out;
+       error = decode_fattr(xdr, result->fattr);
+out:
+       return error;
 }
 
+static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_stat(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS_OK)
+               goto out_default;
+       error = decode_diropok(xdr, result);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
+}
+
+
 /*
- * Encode REMOVE argument
+ * NFSv2 XDR encode functions
+ *
+ * NFSv2 argument types are defined in section 2.2 of RFC 1094:
+ * "NFS: Network File System Protocol Specification".
  */
-static int
-nfs_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
+
+static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
+                                struct xdr_stream *xdr,
+                                const struct nfs_fh *fh)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_array(p, args->name.name, args->name.len);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       encode_fhandle(xdr, fh);
 }
 
 /*
- * Arguments to a READ call. Since we read data directly into the page
- * cache, we also set up the reply iovec here so that iov[1] points
- * exactly to the page we want to fetch.
+ * 2.2.3.  sattrargs
+ *
+ *     struct sattrargs {
+ *             fhandle file;
+ *             sattr attributes;
+ *     };
  */
-static int
-nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
+static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  const struct nfs_sattrargs *args)
 {
-       struct rpc_auth *auth = req->rq_cred->cr_auth;
-       unsigned int replen;
-       u32 offset = (u32)args->offset;
+       encode_fhandle(xdr, args->fh);
+       encode_sattr(xdr, args->sattr);
+}
+
+static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  const struct nfs_diropargs *args)
+{
+       encode_diropargs(xdr, args->fh, args->name, args->len);
+}
+
+static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
+                                     struct xdr_stream *xdr,
+                                     const struct nfs_readlinkargs *args)
+{
+       encode_fhandle(xdr, args->fh);
+       prepare_reply_buffer(req, args->pages, args->pgbase,
+                                       args->pglen, NFS_readlinkres_sz);
+}
+
+/*
+ * 2.2.7.  readargs
+ *
+ *     struct readargs {
+ *             fhandle file;
+ *             unsigned offset;
+ *             unsigned count;
+ *             unsigned totalcount;
+ *     };
+ */
+static void encode_readargs(struct xdr_stream *xdr,
+                           const struct nfs_readargs *args)
+{
+       u32 offset = args->offset;
        u32 count = args->count;
+       __be32 *p;
+
+       encode_fhandle(xdr, args->fh);
 
-       p = xdr_encode_fhandle(p, args->fh);
-       *p++ = htonl(offset);
-       *p++ = htonl(count);
-       *p++ = htonl(count);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       p = xdr_reserve_space(xdr, 4 + 4 + 4);
+       *p++ = cpu_to_be32(offset);
+       *p++ = cpu_to_be32(count);
+       *p = cpu_to_be32(count);
+}
 
-       /* Inline the page array */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen,
-                        args->pages, args->pgbase, count);
+static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
+                                 struct xdr_stream *xdr,
+                                 const struct nfs_readargs *args)
+{
+       encode_readargs(xdr, args);
+       prepare_reply_buffer(req, args->pages, args->pgbase,
+                                       args->count, NFS_readres_sz);
        req->rq_rcv_buf.flags |= XDRBUF_READ;
-       return 0;
 }
 
 /*
- * Decode READ reply
+ * 2.2.9.  writeargs
+ *
+ *     struct writeargs {
+ *             fhandle file;
+ *             unsigned beginoffset;
+ *             unsigned offset;
+ *             unsigned totalcount;
+ *             nfsdata data;
+ *     };
  */
-static int
-nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
+static void encode_writeargs(struct xdr_stream *xdr,
+                            const struct nfs_writeargs *args)
 {
-       struct kvec *iov = req->rq_rcv_buf.head;
-       size_t hdrlen;
-       u32 count, recvd;
-       int status;
-
-       if ((status = ntohl(*p++)))
-               return nfs_stat_to_errno(status);
-       p = xdr_decode_fattr(p, res->fattr);
-
-       count = ntohl(*p++);
-       res->eof = 0;
-       hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len < hdrlen) {
-               dprintk("NFS: READ reply header overflowed:"
-                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
-               return -errno_NFSERR_IO;
-       } else if (iov->iov_len != hdrlen) {
-               dprintk("NFS: READ header is short. iovec will be shifted.\n");
-               xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
-       }
+       u32 offset = args->offset;
+       u32 count = args->count;
+       __be32 *p;
 
-       recvd = req->rq_rcv_buf.len - hdrlen;
-       if (count > recvd) {
-               dprintk("NFS: server cheating in read reply: "
-                       "count %u > recvd %u\n", count, recvd);
-               count = recvd;
-       }
+       encode_fhandle(xdr, args->fh);
 
-       dprintk("RPC:      readres OK count %u\n", count);
-       if (count < res->count)
-               res->count = count;
+       p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
+       *p++ = cpu_to_be32(offset);
+       *p++ = cpu_to_be32(offset);
+       *p++ = cpu_to_be32(count);
 
-       return count;
+       /* nfsdata */
+       *p = cpu_to_be32(count);
+       xdr_write_pages(xdr, args->pages, args->pgbase, count);
 }
 
+static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  const struct nfs_writeargs *args)
+{
+       encode_writeargs(xdr, args);
+       xdr->buf->flags |= XDRBUF_WRITE;
+}
 
 /*
- * Write arguments. Splice the buffer to be written into the iovec.
+ * 2.2.10.  createargs
+ *
+ *     struct createargs {
+ *             diropargs where;
+ *             sattr attributes;
+ *     };
  */
-static int
-nfs_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
+static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   const struct nfs_createargs *args)
 {
-       struct xdr_buf *sndbuf = &req->rq_snd_buf;
-       u32 offset = (u32)args->offset;
-       u32 count = args->count;
-
-       p = xdr_encode_fhandle(p, args->fh);
-       *p++ = htonl(offset);
-       *p++ = htonl(offset);
-       *p++ = htonl(count);
-       *p++ = htonl(count);
-       sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
+       encode_diropargs(xdr, args->fh, args->name, args->len);
+       encode_sattr(xdr, args->sattr);
+}
 
-       /* Copy the page array */
-       xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
-       sndbuf->flags |= XDRBUF_WRITE;
-       return 0;
+static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   const struct nfs_removeargs *args)
+{
+       encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
 }
 
 /*
- * Encode create arguments
- * CREATE, MKDIR
+ * 2.2.12.  renameargs
+ *
+ *     struct renameargs {
+ *             diropargs from;
+ *             diropargs to;
+ *     };
  */
-static int
-nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args)
+static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   const struct nfs_renameargs *args)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_array(p, args->name, args->len);
-       p = xdr_encode_sattr(p, args->sattr);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       const struct qstr *old = args->old_name;
+       const struct qstr *new = args->new_name;
+
+       encode_diropargs(xdr, args->old_dir, old->name, old->len);
+       encode_diropargs(xdr, args->new_dir, new->name, new->len);
 }
 
 /*
- * Encode RENAME arguments
+ * 2.2.13.  linkargs
+ *
+ *     struct linkargs {
+ *             fhandle from;
+ *             diropargs to;
+ *     };
  */
-static int
-nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
+static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
+                                 struct xdr_stream *xdr,
+                                 const struct nfs_linkargs *args)
 {
-       p = xdr_encode_fhandle(p, args->old_dir);
-       p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
-       p = xdr_encode_fhandle(p, args->new_dir);
-       p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       encode_fhandle(xdr, args->fromfh);
+       encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
 }
 
 /*
- * Encode LINK arguments
+ * 2.2.14.  symlinkargs
+ *
+ *     struct symlinkargs {
+ *             diropargs from;
+ *             path to;
+ *             sattr attributes;
+ *     };
  */
-static int
-nfs_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs_linkargs *args)
+static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs_symlinkargs *args)
 {
-       p = xdr_encode_fhandle(p, args->fromfh);
-       p = xdr_encode_fhandle(p, args->tofh);
-       p = xdr_encode_array(p, args->toname, args->tolen);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
+       encode_path(xdr, args->pages, args->pathlen);
+       encode_sattr(xdr, args->sattr);
 }
 
 /*
- * Encode SYMLINK arguments
+ * 2.2.17.  readdirargs
+ *
+ *     struct readdirargs {
+ *             fhandle dir;
+ *             nfscookie cookie;
+ *             unsigned count;
+ *     };
  */
-static int
-nfs_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_symlinkargs *args)
+static void encode_readdirargs(struct xdr_stream *xdr,
+                              const struct nfs_readdirargs *args)
 {
-       struct xdr_buf *sndbuf = &req->rq_snd_buf;
-       size_t pad;
+       __be32 *p;
 
-       p = xdr_encode_fhandle(p, args->fromfh);
-       p = xdr_encode_array(p, args->fromname, args->fromlen);
-       *p++ = htonl(args->pathlen);
-       sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
+       encode_fhandle(xdr, args->fh);
 
-       xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen);
+       p = xdr_reserve_space(xdr, 4 + 4);
+       *p++ = cpu_to_be32(args->cookie);
+       *p = cpu_to_be32(args->count);
+}
 
-       /*
-        * xdr_encode_pages may have added a few bytes to ensure the
-        * pathname ends on a 4-byte boundary.  Start encoding the
-        * attributes after the pad bytes.
-        */
-       pad = sndbuf->tail->iov_len;
-       if (pad > 0)
-               p++;
-       p = xdr_encode_sattr(p, args->sattr);
-       sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad;
-       return 0;
+static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs_readdirargs *args)
+{
+       encode_readdirargs(xdr, args);
+       prepare_reply_buffer(req, args->pages, 0,
+                                       args->count, NFS_readdirres_sz);
 }
 
 /*
- * Encode arguments to readdir call
+ * NFSv2 XDR decode functions
+ *
+ * NFSv2 result types are defined in section 2.2 of RFC 1094:
+ * "NFS: Network File System Protocol Specification".
  */
-static int
-nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args)
+
+static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr,
+                            void *__unused)
 {
-       struct rpc_auth *auth = req->rq_cred->cr_auth;
-       unsigned int replen;
-       u32 count = args->count;
+       enum nfs_stat status;
+       int error;
+
+       error = decode_stat(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS_OK)
+               goto out_default;
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
+}
 
-       p = xdr_encode_fhandle(p, args->fh);
-       *p++ = htonl(args->cookie);
-       *p++ = htonl(count); /* see above */
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                struct nfs_fattr *result)
+{
+       return decode_attrstat(xdr, result);
+}
 
-       /* Inline the page array */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
-       return 0;
+static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                struct nfs_diropok *result)
+{
+       return decode_diropres(xdr, result);
 }
 
 /*
- * Decode the result of a readdir call.
- * We're not really decoding anymore, we just leave the buffer untouched
- * and only check that it is syntactically correct.
- * The real decoding happens in nfs_decode_entry below, called directly
- * from nfs_readdir for each entry.
+ * 2.2.6.  readlinkres
+ *
+ *     union readlinkres switch (stat status) {
+ *     case NFS_OK:
+ *             path data;
+ *     default:
+ *             void;
+ *     };
  */
-static int
-nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
+static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr, void *__unused)
 {
-       struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
-       struct kvec *iov = rcvbuf->head;
-       struct page **page;
-       size_t hdrlen;
-       unsigned int pglen, recvd;
-       int status;
-
-       if ((status = ntohl(*p++)))
-               return nfs_stat_to_errno(status);
-
-       hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len < hdrlen) {
-               dprintk("NFS: READDIR reply header overflowed:"
-                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
-               return -errno_NFSERR_IO;
-       } else if (iov->iov_len != hdrlen) {
-               dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
-               xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
-       }
+       enum nfs_stat status;
+       int error;
+
+       error = decode_stat(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS_OK)
+               goto out_default;
+       error = decode_path(xdr);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
+}
 
-       pglen = rcvbuf->page_len;
-       recvd = rcvbuf->len - hdrlen;
-       if (pglen > recvd)
-               pglen = recvd;
-       page = rcvbuf->pages;
-       return pglen;
+/*
+ * 2.2.7.  readres
+ *
+ *     union readres switch (stat status) {
+ *     case NFS_OK:
+ *             fattr attributes;
+ *             nfsdata data;
+ *     default:
+ *             void;
+ *     };
+ */
+static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               struct nfs_readres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_stat(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS_OK)
+               goto out_default;
+       error = decode_fattr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       error = decode_nfsdata(xdr, result);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
 }
 
-static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
+static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                struct nfs_writeres *result)
 {
-       dprintk("nfs: %s: prematurely hit end of receive buffer. "
-               "Remaining buffer length is %tu words.\n",
-               func, xdr->end - xdr->p);
+       /* All NFSv2 writes are "file sync" writes */
+       result->verf->committed = NFS_FILE_SYNC;
+       return decode_attrstat(xdr, result->fattr);
 }
 
-__be32 *
-nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
+/**
+ * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
+ *                      the local page cache.
+ * @xdr: XDR stream where entry resides
+ * @entry: buffer to fill in with entry data
+ * @plus: boolean indicating whether this should be a readdirplus entry
+ *
+ * Returns zero if successful, otherwise a negative errno value is
+ * returned.
+ *
+ * This function is not invoked during READDIR reply decoding, but
+ * rather whenever an application invokes the getdents(2) system call
+ * on a directory already in our cache.
+ *
+ * 2.2.17.  entry
+ *
+ *     struct entry {
+ *             unsigned        fileid;
+ *             filename        name;
+ *             nfscookie       cookie;
+ *             entry           *nextentry;
+ *     };
+ */
+int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
+                      int plus)
 {
        __be32 *p;
+       int error;
+
        p = xdr_inline_decode(xdr, 4);
-       if (unlikely(!p))
+       if (unlikely(p == NULL))
                goto out_overflow;
-       if (!ntohl(*p++)) {
+       if (*p++ == xdr_zero) {
                p = xdr_inline_decode(xdr, 4);
-               if (unlikely(!p))
+               if (unlikely(p == NULL))
                        goto out_overflow;
-               if (!ntohl(*p++))
-                       return ERR_PTR(-EAGAIN);
+               if (*p++ == xdr_zero)
+                       return -EAGAIN;
                entry->eof = 1;
-               return ERR_PTR(-EBADCOOKIE);
+               return -EBADCOOKIE;
        }
 
-       p = xdr_inline_decode(xdr, 8);
-       if (unlikely(!p))
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
                goto out_overflow;
+       entry->ino = be32_to_cpup(p);
 
-       entry->ino        = ntohl(*p++);
-       entry->len        = ntohl(*p++);
+       error = decode_filename_inline(xdr, &entry->name, &entry->len);
+       if (unlikely(error))
+               return error;
 
-       p = xdr_inline_decode(xdr, entry->len + 4);
-       if (unlikely(!p))
+       /*
+        * The type (size and byte order) of nfscookie isn't defined in
+        * RFC 1094.  This implementation assumes that it's an XDR uint32.
+        */
+       entry->prev_cookie = entry->cookie;
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
                goto out_overflow;
-       entry->name       = (const char *) p;
-       p                += XDR_QUADLEN(entry->len);
-       entry->prev_cookie        = entry->cookie;
-       entry->cookie     = ntohl(*p++);
+       entry->cookie = be32_to_cpup(p);
 
        entry->d_type = DT_UNKNOWN;
 
-       return p;
+       return 0;
 
 out_overflow:
        print_overflow_msg(__func__, xdr);
-       return ERR_PTR(-EAGAIN);
-}
-
-/*
- * NFS XDR decode functions
- */
-/*
- * Decode simple status reply
- */
-static int
-nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
-{
-       int     status;
-
-       if ((status = ntohl(*p++)) != 0)
-               status = nfs_stat_to_errno(status);
-       return status;
+       return -EAGAIN;
 }
 
 /*
- * Decode attrstat reply
- * GETATTR, SETATTR, WRITE
- */
-static int
-nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
-{
-       int     status;
-
-       if ((status = ntohl(*p++)))
-               return nfs_stat_to_errno(status);
-       xdr_decode_fattr(p, fattr);
-       return 0;
-}
-
-/*
- * Decode diropres reply
- * LOOKUP, CREATE, MKDIR
+ * 2.2.17.  readdirres
+ *
+ *     union readdirres switch (stat status) {
+ *     case NFS_OK:
+ *             struct {
+ *                     entry *entries;
+ *                     bool eof;
+ *             } readdirok;
+ *     default:
+ *             void;
+ *     };
+ *
+ * Read the directory contents into the page cache, but don't
+ * touch them.  The actual decoding is done by nfs2_decode_dirent()
+ * during subsequent nfs_readdir() calls.
  */
-static int
-nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
+static int decode_readdirok(struct xdr_stream *xdr)
 {
-       int     status;
+       u32 recvd, pglen;
+       size_t hdrlen;
 
-       if ((status = ntohl(*p++)))
-               return nfs_stat_to_errno(status);
-       p = xdr_decode_fhandle(p, res->fh);
-       xdr_decode_fattr(p, res->fattr);
-       return 0;
+       pglen = xdr->buf->page_len;
+       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+       recvd = xdr->buf->len - hdrlen;
+       if (unlikely(pglen > recvd))
+               goto out_cheating;
+out:
+       xdr_read_pages(xdr, pglen);
+       return pglen;
+out_cheating:
+       dprintk("NFS: server cheating in readdir result: "
+               "pglen %u > recvd %u\n", pglen, recvd);
+       pglen = recvd;
+       goto out;
 }
 
-/*
- * Encode READLINK args
- */
-static int
-nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args)
+static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr, void *__unused)
 {
-       struct rpc_auth *auth = req->rq_cred->cr_auth;
-       unsigned int replen;
-
-       p = xdr_encode_fhandle(p, args->fh);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-
-       /* Inline the page array */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
-       return 0;
+       enum nfs_stat status;
+       int error;
+
+       error = decode_stat(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS_OK)
+               goto out_default;
+       error = decode_readdirok(xdr);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
 }
 
 /*
- * Decode READLINK reply
+ * 2.2.18.  statfsres
+ *
+ *     union statfsres (stat status) {
+ *     case NFS_OK:
+ *             struct {
+ *                     unsigned tsize;
+ *                     unsigned bsize;
+ *                     unsigned blocks;
+ *                     unsigned bfree;
+ *                     unsigned bavail;
+ *             } info;
+ *     default:
+ *             void;
+ *     };
  */
-static int
-nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
+static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
 {
-       struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
-       struct kvec *iov = rcvbuf->head;
-       size_t hdrlen;
-       u32 len, recvd;
-       int     status;
-
-       if ((status = ntohl(*p++)))
-               return nfs_stat_to_errno(status);
-       /* Convert length of symlink */
-       len = ntohl(*p++);
-       if (len >= rcvbuf->page_len) {
-               dprintk("nfs: server returned giant symlink!\n");
-               return -ENAMETOOLONG;
-       }
-       hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len < hdrlen) {
-               dprintk("NFS: READLINK reply header overflowed:"
-                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
-               return -errno_NFSERR_IO;
-       } else if (iov->iov_len != hdrlen) {
-               dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
-               xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
-       }
-       recvd = req->rq_rcv_buf.len - hdrlen;
-       if (recvd < len) {
-               dprintk("NFS: server cheating in readlink reply: "
-                               "count %u > recvd %u\n", len, recvd);
-               return -EIO;
-       }
+       __be32 *p;
 
-       xdr_terminate_string(rcvbuf, len);
+       p = xdr_inline_decode(xdr, NFS_info_sz << 2);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       result->tsize  = be32_to_cpup(p++);
+       result->bsize  = be32_to_cpup(p++);
+       result->blocks = be32_to_cpup(p++);
+       result->bfree  = be32_to_cpup(p++);
+       result->bavail = be32_to_cpup(p);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
-/*
- * Decode WRITE reply
- */
-static int
-nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
+static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                 struct nfs2_fsstat *result)
 {
-       res->verf->committed = NFS_FILE_SYNC;
-       return nfs_xdr_attrstat(req, p, res->fattr);
+       enum nfs_stat status;
+       int error;
+
+       error = decode_stat(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS_OK)
+               goto out_default;
+       error = decode_info(xdr, result);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
 }
 
-/*
- * Decode STATFS reply
- */
-static int
-nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
-{
-       int     status;
-
-       if ((status = ntohl(*p++)))
-               return nfs_stat_to_errno(status);
-
-       res->tsize  = ntohl(*p++);
-       res->bsize  = ntohl(*p++);
-       res->blocks = ntohl(*p++);
-       res->bfree  = ntohl(*p++);
-       res->bavail = ntohl(*p++);
-       return 0;
-}
 
 /*
  * We need to translate between nfs status return values and
  * the local errno values which may not be the same.
  */
-static struct {
+static const struct {
        int stat;
        int errno;
 } nfs_errtbl[] = {
@@ -672,28 +1102,30 @@ static struct {
        { -1,                   -EIO            }
 };
 
-/*
- * Convert an NFS error code to a local one.
- * This one is used jointly by NFSv2 and NFSv3.
+/**
+ * nfs_stat_to_errno - convert an NFS status code to a local errno
+ * @status: NFS status code to convert
+ *
+ * Returns a local errno value, or -EIO if the NFS status code is
+ * not recognized.  This function is used jointly by NFSv2 and NFSv3.
  */
-int
-nfs_stat_to_errno(int stat)
+int nfs_stat_to_errno(enum nfs_stat status)
 {
        int i;
 
        for (i = 0; nfs_errtbl[i].stat != -1; i++) {
-               if (nfs_errtbl[i].stat == stat)
+               if (nfs_errtbl[i].stat == (int)status)
                        return nfs_errtbl[i].errno;
        }
-       dprintk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
+       dprintk("NFS: Unrecognized nfs status value: %u\n", status);
        return nfs_errtbl[i].errno;
 }
 
 #define PROC(proc, argtype, restype, timer)                            \
 [NFSPROC_##proc] = {                                                   \
        .p_proc     =  NFSPROC_##proc,                                  \
-       .p_encode   =  (kxdrproc_t) nfs_xdr_##argtype,                  \
-       .p_decode   =  (kxdrproc_t) nfs_xdr_##restype,                  \
+       .p_encode   =  (kxdreproc_t)nfs2_xdr_enc_##argtype,             \
+       .p_decode   =  (kxdrdproc_t)nfs2_xdr_dec_##restype,             \
        .p_arglen   =  NFS_##argtype##_sz,                              \
        .p_replen   =  NFS_##restype##_sz,                              \
        .p_timer    =  timer,                                           \
@@ -701,21 +1133,21 @@ nfs_stat_to_errno(int stat)
        .p_name     =  #proc,                                           \
        }
 struct rpc_procinfo    nfs_procedures[] = {
-    PROC(GETATTR,      fhandle,        attrstat, 1),
-    PROC(SETATTR,      sattrargs,      attrstat, 0),
-    PROC(LOOKUP,       diropargs,      diropres, 2),
-    PROC(READLINK,     readlinkargs,   readlinkres, 3),
-    PROC(READ,         readargs,       readres, 3),
-    PROC(WRITE,                writeargs,      writeres, 4),
-    PROC(CREATE,       createargs,     diropres, 0),
-    PROC(REMOVE,       removeargs,     stat, 0),
-    PROC(RENAME,       renameargs,     stat, 0),
-    PROC(LINK,         linkargs,       stat, 0),
-    PROC(SYMLINK,      symlinkargs,    stat, 0),
-    PROC(MKDIR,                createargs,     diropres, 0),
-    PROC(RMDIR,                diropargs,      stat, 0),
-    PROC(READDIR,      readdirargs,    readdirres, 3),
-    PROC(STATFS,       fhandle,        statfsres, 0),
+       PROC(GETATTR,   fhandle,        attrstat,       1),
+       PROC(SETATTR,   sattrargs,      attrstat,       0),
+       PROC(LOOKUP,    diropargs,      diropres,       2),
+       PROC(READLINK,  readlinkargs,   readlinkres,    3),
+       PROC(READ,      readargs,       readres,        3),
+       PROC(WRITE,     writeargs,      writeres,       4),
+       PROC(CREATE,    createargs,     diropres,       0),
+       PROC(REMOVE,    removeargs,     stat,           0),
+       PROC(RENAME,    renameargs,     stat,           0),
+       PROC(LINK,      linkargs,       stat,           0),
+       PROC(SYMLINK,   symlinkargs,    stat,           0),
+       PROC(MKDIR,     createargs,     diropres,       0),
+       PROC(RMDIR,     diropargs,      stat,           0),
+       PROC(READDIR,   readdirargs,    readdirres,     3),
+       PROC(STATFS,    fhandle,        statfsres,      0),
 };
 
 struct rpc_version             nfs_version2 = {
index ba91236..01c5e8b 100644 (file)
 #define NFS3_filename_sz       (1+(NFS3_MAXNAMLEN>>2))
 #define NFS3_path_sz           (1+(NFS3_MAXPATHLEN>>2))
 #define NFS3_fattr_sz          (21)
-#define NFS3_wcc_attr_sz               (6)
+#define NFS3_cookieverf_sz     (NFS3_COOKIEVERFSIZE>>2)
+#define NFS3_wcc_attr_sz       (6)
 #define NFS3_pre_op_attr_sz    (1+NFS3_wcc_attr_sz)
 #define NFS3_post_op_attr_sz   (1+NFS3_fattr_sz)
-#define NFS3_wcc_data_sz               (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
-#define NFS3_fsstat_sz         
-#define NFS3_fsinfo_sz         
-#define NFS3_pathconf_sz               
-#define NFS3_entry_sz          (NFS3_filename_sz+3)
-
-#define NFS3_sattrargs_sz      (NFS3_fh_sz+NFS3_sattr_sz+3)
+#define NFS3_wcc_data_sz       (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz)
 #define NFS3_diropargs_sz      (NFS3_fh_sz+NFS3_filename_sz)
-#define NFS3_removeargs_sz     (NFS3_fh_sz+NFS3_filename_sz)
+
+#define NFS3_getattrargs_sz    (NFS3_fh_sz)
+#define NFS3_setattrargs_sz    (NFS3_fh_sz+NFS3_sattr_sz+3)
+#define NFS3_lookupargs_sz     (NFS3_fh_sz+NFS3_filename_sz)
 #define NFS3_accessargs_sz     (NFS3_fh_sz+1)
 #define NFS3_readlinkargs_sz   (NFS3_fh_sz)
 #define NFS3_readargs_sz       (NFS3_fh_sz+3)
 #define NFS3_mkdirargs_sz      (NFS3_diropargs_sz+NFS3_sattr_sz)
 #define NFS3_symlinkargs_sz    (NFS3_diropargs_sz+1+NFS3_sattr_sz)
 #define NFS3_mknodargs_sz      (NFS3_diropargs_sz+2+NFS3_sattr_sz)
+#define NFS3_removeargs_sz     (NFS3_fh_sz+NFS3_filename_sz)
 #define NFS3_renameargs_sz     (NFS3_diropargs_sz+NFS3_diropargs_sz)
 #define NFS3_linkargs_sz               (NFS3_fh_sz+NFS3_diropargs_sz)
-#define NFS3_readdirargs_sz    (NFS3_fh_sz+2)
+#define NFS3_readdirargs_sz    (NFS3_fh_sz+NFS3_cookieverf_sz+3)
+#define NFS3_readdirplusargs_sz        (NFS3_fh_sz+NFS3_cookieverf_sz+4)
 #define NFS3_commitargs_sz     (NFS3_fh_sz+3)
 
-#define NFS3_attrstat_sz       (1+NFS3_fattr_sz)
-#define NFS3_wccstat_sz                (1+NFS3_wcc_data_sz)
-#define NFS3_removeres_sz      (NFS3_wccstat_sz)
+#define NFS3_getattrres_sz     (1+NFS3_fattr_sz)
+#define NFS3_setattrres_sz     (1+NFS3_wcc_data_sz)
+#define NFS3_removeres_sz      (NFS3_setattrres_sz)
 #define NFS3_lookupres_sz      (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
 #define NFS3_accessres_sz      (1+NFS3_post_op_attr_sz+1)
 #define NFS3_readlinkres_sz    (1+NFS3_post_op_attr_sz+1)
@@ -100,1073 +100,2362 @@ static const umode_t nfs_type2fmt[] = {
        [NF3FIFO] = S_IFIFO,
 };
 
+/*
+ * While encoding arguments, set up the reply buffer in advance to
+ * receive reply data directly into the page cache.
+ */
+static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
+                                unsigned int base, unsigned int len,
+                                unsigned int bufsize)
+{
+       struct rpc_auth *auth = req->rq_cred->cr_auth;
+       unsigned int replen;
+
+       replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
+       xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
+}
+
+/*
+ * Handle decode buffer overflows out-of-line.
+ */
 static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
 {
-       dprintk("nfs: %s: prematurely hit end of receive buffer. "
+       dprintk("NFS: %s prematurely hit the end of our receive buffer. "
                "Remaining buffer length is %tu words.\n",
                func, xdr->end - xdr->p);
 }
 
+
 /*
- * Common NFS XDR functions as inlines
+ * Encode/decode NFSv3 basic data types
+ *
+ * Basic NFSv3 data types are defined in section 2.5 of RFC 1813:
+ * "NFS Version 3 Protocol Specification".
+ *
+ * Not all basic data types have their own encoding and decoding
+ * functions.  For run-time efficiency, some data types are encoded
+ * or decoded inline.
  */
-static inline __be32 *
-xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
-{
-       return xdr_encode_array(p, fh->data, fh->size);
-}
 
-static inline __be32 *
-xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
+static void encode_uint32(struct xdr_stream *xdr, u32 value)
 {
-       if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
-               memcpy(fh->data, p, fh->size);
-               return p + XDR_QUADLEN(fh->size);
-       }
-       return NULL;
+       __be32 *p = xdr_reserve_space(xdr, 4);
+       *p = cpu_to_be32(value);
 }
 
-static inline __be32 *
-xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh)
+static int decode_uint32(struct xdr_stream *xdr, u32 *value)
 {
        __be32 *p;
+
        p = xdr_inline_decode(xdr, 4);
-       if (unlikely(!p))
+       if (unlikely(p == NULL))
                goto out_overflow;
-       fh->size = ntohl(*p++);
-
-       if (fh->size <= NFS3_FHSIZE) {
-               p = xdr_inline_decode(xdr, fh->size);
-               if (unlikely(!p))
-                       goto out_overflow;
-               memcpy(fh->data, p, fh->size);
-               return p + XDR_QUADLEN(fh->size);
-       }
-       return NULL;
-
+       *value = be32_to_cpup(p);
+       return 0;
 out_overflow:
        print_overflow_msg(__func__, xdr);
-       return ERR_PTR(-EIO);
+       return -EIO;
 }
 
-/*
- * Encode/decode time.
- */
-static inline __be32 *
-xdr_encode_time3(__be32 *p, struct timespec *timep)
+static int decode_uint64(struct xdr_stream *xdr, u64 *value)
 {
-       *p++ = htonl(timep->tv_sec);
-       *p++ = htonl(timep->tv_nsec);
-       return p;
-}
+       __be32 *p;
 
-static inline __be32 *
-xdr_decode_time3(__be32 *p, struct timespec *timep)
-{
-       timep->tv_sec = ntohl(*p++);
-       timep->tv_nsec = ntohl(*p++);
-       return p;
+       p = xdr_inline_decode(xdr, 8);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       xdr_decode_hyper(p, value);
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
-static __be32 *
-xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
+/*
+ * fileid3
+ *
+ *     typedef uint64 fileid3;
+ */
+static __be32 *xdr_decode_fileid3(__be32 *p, u64 *fileid)
 {
-       unsigned int    type, major, minor;
-       umode_t         fmode;
-
-       type = ntohl(*p++);
-       if (type > NF3FIFO)
-               type = NF3NON;
-       fmode = nfs_type2fmt[type];
-       fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
-       fattr->nlink = ntohl(*p++);
-       fattr->uid = ntohl(*p++);
-       fattr->gid = ntohl(*p++);
-       p = xdr_decode_hyper(p, &fattr->size);
-       p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
-
-       /* Turn remote device info into Linux-specific dev_t */
-       major = ntohl(*p++);
-       minor = ntohl(*p++);
-       fattr->rdev = MKDEV(major, minor);
-       if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor)
-               fattr->rdev = 0;
-
-       p = xdr_decode_hyper(p, &fattr->fsid.major);
-       fattr->fsid.minor = 0;
-       p = xdr_decode_hyper(p, &fattr->fileid);
-       p = xdr_decode_time3(p, &fattr->atime);
-       p = xdr_decode_time3(p, &fattr->mtime);
-       p = xdr_decode_time3(p, &fattr->ctime);
-
-       /* Update the mode bits */
-       fattr->valid |= NFS_ATTR_FATTR_V3;
-       return p;
+       return xdr_decode_hyper(p, fileid);
 }
 
-static inline __be32 *
-xdr_encode_sattr(__be32 *p, struct iattr *attr)
+static int decode_fileid3(struct xdr_stream *xdr, u64 *fileid)
 {
-       if (attr->ia_valid & ATTR_MODE) {
-               *p++ = xdr_one;
-               *p++ = htonl(attr->ia_mode & S_IALLUGO);
-       } else {
-               *p++ = xdr_zero;
-       }
-       if (attr->ia_valid & ATTR_UID) {
-               *p++ = xdr_one;
-               *p++ = htonl(attr->ia_uid);
-       } else {
-               *p++ = xdr_zero;
-       }
-       if (attr->ia_valid & ATTR_GID) {
-               *p++ = xdr_one;
-               *p++ = htonl(attr->ia_gid);
-       } else {
-               *p++ = xdr_zero;
-       }
-       if (attr->ia_valid & ATTR_SIZE) {
-               *p++ = xdr_one;
-               p = xdr_encode_hyper(p, (__u64) attr->ia_size);
-       } else {
-               *p++ = xdr_zero;
-       }
-       if (attr->ia_valid & ATTR_ATIME_SET) {
-               *p++ = xdr_two;
-               p = xdr_encode_time3(p, &attr->ia_atime);
-       } else if (attr->ia_valid & ATTR_ATIME) {
-               *p++ = xdr_one;
-       } else {
-               *p++ = xdr_zero;
-       }
-       if (attr->ia_valid & ATTR_MTIME_SET) {
-               *p++ = xdr_two;
-               p = xdr_encode_time3(p, &attr->ia_mtime);
-       } else if (attr->ia_valid & ATTR_MTIME) {
-               *p++ = xdr_one;
-       } else {
-               *p++ = xdr_zero;
-       }
-       return p;
+       return decode_uint64(xdr, fileid);
 }
 
-static inline __be32 *
-xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
+/*
+ * filename3
+ *
+ *     typedef string filename3<>;
+ */
+static void encode_filename3(struct xdr_stream *xdr,
+                            const char *name, u32 length)
 {
-       p = xdr_decode_hyper(p, &fattr->pre_size);
-       p = xdr_decode_time3(p, &fattr->pre_mtime);
-       p = xdr_decode_time3(p, &fattr->pre_ctime);
-       fattr->valid |= NFS_ATTR_FATTR_PRESIZE
-               | NFS_ATTR_FATTR_PREMTIME
-               | NFS_ATTR_FATTR_PRECTIME;
-       return p;
-}
+       __be32 *p;
 
-static inline __be32 *
-xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
-{
-       if (*p++)
-               p = xdr_decode_fattr(p, fattr);
-       return p;
+       BUG_ON(length > NFS3_MAXNAMLEN);
+       p = xdr_reserve_space(xdr, 4 + length);
+       xdr_encode_opaque(p, name, length);
 }
 
-static inline __be32 *
-xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr)
+static int decode_inline_filename3(struct xdr_stream *xdr,
+                                  const char **name, u32 *length)
 {
        __be32 *p;
+       u32 count;
 
        p = xdr_inline_decode(xdr, 4);
-       if (unlikely(!p))
+       if (unlikely(p == NULL))
                goto out_overflow;
-       if (ntohl(*p++)) {
-               p = xdr_inline_decode(xdr, 84);
-               if (unlikely(!p))
-                       goto out_overflow;
-               p = xdr_decode_fattr(p, fattr);
-       }
-       return p;
+       count = be32_to_cpup(p);
+       if (count > NFS3_MAXNAMLEN)
+               goto out_nametoolong;
+       p = xdr_inline_decode(xdr, count);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       *name = (const char *)p;
+       *length = count;
+       return 0;
+
+out_nametoolong:
+       dprintk("NFS: returned filename too long: %u\n", count);
+       return -ENAMETOOLONG;
 out_overflow:
        print_overflow_msg(__func__, xdr);
-       return ERR_PTR(-EIO);
+       return -EIO;
 }
 
-static inline __be32 *
-xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
+/*
+ * nfspath3
+ *
+ *     typedef string nfspath3<>;
+ */
+static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
+                           const u32 length)
 {
-       if (*p++)
-               return xdr_decode_wcc_attr(p, fattr);
-       return p;
+       BUG_ON(length > NFS3_MAXPATHLEN);
+       encode_uint32(xdr, length);
+       xdr_write_pages(xdr, pages, 0, length);
 }
 
-
-static inline __be32 *
-xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
+static int decode_nfspath3(struct xdr_stream *xdr)
 {
-       p = xdr_decode_pre_op_attr(p, fattr);
-       return xdr_decode_post_op_attr(p, fattr);
-}
-
-/*
- * NFS encode functions
- */
+       u32 recvd, count;
+       size_t hdrlen;
+       __be32 *p;
 
-/*
- * Encode file handle argument
- */
-static int
-nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
-{
-       p = xdr_encode_fhandle(p, fh);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       count = be32_to_cpup(p);
+       if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
+               goto out_nametoolong;
+       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+       recvd = xdr->buf->len - hdrlen;
+       if (unlikely(count > recvd))
+               goto out_cheating;
+
+       xdr_read_pages(xdr, count);
+       xdr_terminate_string(xdr->buf, count);
        return 0;
-}
 
-/*
- * Encode SETATTR arguments
- */
-static int
-nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
-{
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_sattr(p, args->sattr);
-       *p++ = htonl(args->guard);
-       if (args->guard)
-               p = xdr_encode_time3(p, &args->guardtime);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+out_nametoolong:
+       dprintk("NFS: returned pathname too long: %u\n", count);
+       return -ENAMETOOLONG;
+out_cheating:
+       dprintk("NFS: server cheating in pathname result: "
+               "count %u > recvd %u\n", count, recvd);
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 /*
- * Encode directory ops argument
+ * cookie3
+ *
+ *     typedef uint64 cookie3
  */
-static int
-nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
+static __be32 *xdr_encode_cookie3(__be32 *p, u64 cookie)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_array(p, args->name, args->len);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       return xdr_encode_hyper(p, cookie);
 }
 
-/*
- * Encode REMOVE argument
- */
-static int
-nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
+static int decode_cookie3(struct xdr_stream *xdr, u64 *cookie)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_array(p, args->name.name, args->name.len);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       return decode_uint64(xdr, cookie);
 }
 
 /*
- * Encode access() argument
+ * cookieverf3
+ *
+ *     typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE];
  */
-static int
-nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
+static __be32 *xdr_encode_cookieverf3(__be32 *p, const __be32 *verifier)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       *p++ = htonl(args->access);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       memcpy(p, verifier, NFS3_COOKIEVERFSIZE);
+       return p + XDR_QUADLEN(NFS3_COOKIEVERFSIZE);
 }
 
-/*
- * Arguments to a READ call. Since we read data directly into the page
- * cache, we also set up the reply iovec here so that iov[1] points
- * exactly to the page we want to fetch.
- */
-static int
-nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
+static int decode_cookieverf3(struct xdr_stream *xdr, __be32 *verifier)
 {
-       struct rpc_auth *auth = req->rq_cred->cr_auth;
-       unsigned int replen;
-       u32 count = args->count;
-
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_hyper(p, args->offset);
-       *p++ = htonl(count);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       __be32 *p;
 
-       /* Inline the page array */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen,
-                        args->pages, args->pgbase, count);
-       req->rq_rcv_buf.flags |= XDRBUF_READ;
+       p = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       memcpy(verifier, p, NFS3_COOKIEVERFSIZE);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 /*
- * Write arguments. Splice the buffer to be written into the iovec.
+ * createverf3
+ *
+ *     typedef opaque createverf3[NFS3_CREATEVERFSIZE];
  */
-static int
-nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
+static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
 {
-       struct xdr_buf *sndbuf = &req->rq_snd_buf;
-       u32 count = args->count;
+       __be32 *p;
 
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_hyper(p, args->offset);
-       *p++ = htonl(count);
-       *p++ = htonl(args->stable);
-       *p++ = htonl(count);
-       sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
-
-       /* Copy the page array */
-       xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
-       sndbuf->flags |= XDRBUF_WRITE;
-       return 0;
+       p = xdr_reserve_space(xdr, NFS3_CREATEVERFSIZE);
+       memcpy(p, verifier, NFS3_CREATEVERFSIZE);
 }
 
-/*
- * Encode CREATE arguments
- */
-static int
-nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
+static int decode_writeverf3(struct xdr_stream *xdr, __be32 *verifier)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_array(p, args->name, args->len);
-
-       *p++ = htonl(args->createmode);
-       if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
-               *p++ = args->verifier[0];
-               *p++ = args->verifier[1];
-       } else
-               p = xdr_encode_sattr(p, args->sattr);
+       __be32 *p;
 
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       memcpy(verifier, p, NFS3_WRITEVERFSIZE);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 /*
- * Encode MKDIR arguments
+ * size3
+ *
+ *     typedef uint64 size3;
  */
-static int
-nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
+static __be32 *xdr_decode_size3(__be32 *p, u64 *size)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_array(p, args->name, args->len);
-       p = xdr_encode_sattr(p, args->sattr);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       return xdr_decode_hyper(p, size);
 }
 
 /*
- * Encode SYMLINK arguments
+ * nfsstat3
+ *
+ *     enum nfsstat3 {
+ *             NFS3_OK = 0,
+ *             ...
+ *     }
  */
-static int
-nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
+#define NFS3_OK                NFS_OK
+
+static int decode_nfsstat3(struct xdr_stream *xdr, enum nfs_stat *status)
 {
-       p = xdr_encode_fhandle(p, args->fromfh);
-       p = xdr_encode_array(p, args->fromname, args->fromlen);
-       p = xdr_encode_sattr(p, args->sattr);
-       *p++ = htonl(args->pathlen);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       __be32 *p;
 
-       /* Copy the page */
-       xdr_encode_pages(&req->rq_snd_buf, args->pages, 0, args->pathlen);
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       *status = be32_to_cpup(p);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 /*
- * Encode MKNOD arguments
+ * ftype3
+ *
+ *     enum ftype3 {
+ *             NF3REG  = 1,
+ *             NF3DIR  = 2,
+ *             NF3BLK  = 3,
+ *             NF3CHR  = 4,
+ *             NF3LNK  = 5,
+ *             NF3SOCK = 6,
+ *             NF3FIFO = 7
+ *     };
  */
-static int
-nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
-{
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_array(p, args->name, args->len);
-       *p++ = htonl(args->type);
-       p = xdr_encode_sattr(p, args->sattr);
-       if (args->type == NF3CHR || args->type == NF3BLK) {
-               *p++ = htonl(MAJOR(args->rdev));
-               *p++ = htonl(MINOR(args->rdev));
-       }
-
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+static void encode_ftype3(struct xdr_stream *xdr, const u32 type)
+{
+       BUG_ON(type > NF3FIFO);
+       encode_uint32(xdr, type);
 }
 
-/*
- * Encode RENAME arguments
- */
-static int
-nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
-{
-       p = xdr_encode_fhandle(p, args->old_dir);
-       p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
-       p = xdr_encode_fhandle(p, args->new_dir);
-       p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+static __be32 *xdr_decode_ftype3(__be32 *p, umode_t *mode)
+{
+       u32 type;
+
+       type = be32_to_cpup(p++);
+       if (type > NF3FIFO)
+               type = NF3NON;
+       *mode = nfs_type2fmt[type];
+       return p;
 }
 
 /*
- * Encode LINK arguments
+ * specdata3
+ *
+ *     struct specdata3 {
+ *             uint32  specdata1;
+ *             uint32  specdata2;
+ *     };
  */
-static int
-nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
+static void encode_specdata3(struct xdr_stream *xdr, const dev_t rdev)
 {
-       p = xdr_encode_fhandle(p, args->fromfh);
-       p = xdr_encode_fhandle(p, args->tofh);
-       p = xdr_encode_array(p, args->toname, args->tolen);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       __be32 *p;
+
+       p = xdr_reserve_space(xdr, 8);
+       *p++ = cpu_to_be32(MAJOR(rdev));
+       *p = cpu_to_be32(MINOR(rdev));
 }
 
-/*
- * Encode arguments to readdir call
- */
-static int
-nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
+static __be32 *xdr_decode_specdata3(__be32 *p, dev_t *rdev)
 {
-       struct rpc_auth *auth = req->rq_cred->cr_auth;
-       unsigned int replen;
-       u32 count = args->count;
-
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_hyper(p, args->cookie);
-       *p++ = args->verf[0];
-       *p++ = args->verf[1];
-       if (args->plus) {
-               /* readdirplus: need dircount + buffer size.
-                * We just make sure we make dircount big enough */
-               *p++ = htonl(count >> 3);
-       }
-       *p++ = htonl(count);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       unsigned int major, minor;
 
-       /* Inline the page array */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
-       return 0;
+       major = be32_to_cpup(p++);
+       minor = be32_to_cpup(p++);
+       *rdev = MKDEV(major, minor);
+       if (MAJOR(*rdev) != major || MINOR(*rdev) != minor)
+               *rdev = 0;
+       return p;
 }
 
 /*
- * Decode the result of a readdir call.
- * We just check for syntactical correctness.
+ * nfs_fh3
+ *
+ *     struct nfs_fh3 {
+ *             opaque       data<NFS3_FHSIZE>;
+ *     };
  */
-static int
-nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
+static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh)
 {
-       struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
-       struct kvec *iov = rcvbuf->head;
-       struct page **page;
-       size_t hdrlen;
-       u32 recvd, pglen;
-       int status;
-
-       status = ntohl(*p++);
-       /* Decode post_op_attrs */
-       p = xdr_decode_post_op_attr(p, res->dir_attr);
-       if (status)
-               return nfs_stat_to_errno(status);
-       /* Decode verifier cookie */
-       if (res->verf) {
-               res->verf[0] = *p++;
-               res->verf[1] = *p++;
-       } else {
-               p += 2;
-       }
-
-       hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len < hdrlen) {
-               dprintk("NFS: READDIR reply header overflowed:"
-                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
-               return -errno_NFSERR_IO;
-       } else if (iov->iov_len != hdrlen) {
-               dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
-               xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
-       }
-
-       pglen = rcvbuf->page_len;
-       recvd = rcvbuf->len - hdrlen;
-       if (pglen > recvd)
-               pglen = recvd;
-       page = rcvbuf->pages;
+       __be32 *p;
 
-       return pglen;
+       BUG_ON(fh->size > NFS3_FHSIZE);
+       p = xdr_reserve_space(xdr, 4 + fh->size);
+       xdr_encode_opaque(p, fh->data, fh->size);
 }
 
-__be32 *
-nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
+static int decode_nfs_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
 {
+       u32 length;
        __be32 *p;
-       struct nfs_entry old = *entry;
 
        p = xdr_inline_decode(xdr, 4);
-       if (unlikely(!p))
-               goto out_overflow;
-       if (!ntohl(*p++)) {
-               p = xdr_inline_decode(xdr, 4);
-               if (unlikely(!p))
-                       goto out_overflow;
-               if (!ntohl(*p++))
-                       return ERR_PTR(-EAGAIN);
-               entry->eof = 1;
-               return ERR_PTR(-EBADCOOKIE);
-       }
-
-       p = xdr_inline_decode(xdr, 12);
-       if (unlikely(!p))
+       if (unlikely(p == NULL))
                goto out_overflow;
-       p = xdr_decode_hyper(p, &entry->ino);
-       entry->len  = ntohl(*p++);
-
-       p = xdr_inline_decode(xdr, entry->len + 8);
-       if (unlikely(!p))
+       length = be32_to_cpup(p++);
+       if (unlikely(length > NFS3_FHSIZE))
+               goto out_toobig;
+       p = xdr_inline_decode(xdr, length);
+       if (unlikely(p == NULL))
                goto out_overflow;
-       entry->name = (const char *) p;
-       p += XDR_QUADLEN(entry->len);
-       entry->prev_cookie = entry->cookie;
-       p = xdr_decode_hyper(p, &entry->cookie);
-
-       entry->d_type = DT_UNKNOWN;
-       if (plus) {
-               entry->fattr->valid = 0;
-               p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
-               if (IS_ERR(p))
-                       goto out_overflow_exit;
-               entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
-               /* In fact, a post_op_fh3: */
-               p = xdr_inline_decode(xdr, 4);
-               if (unlikely(!p))
-                       goto out_overflow;
-               if (*p++) {
-                       p = xdr_decode_fhandle_stream(xdr, entry->fh);
-                       if (IS_ERR(p))
-                               goto out_overflow_exit;
-                       /* Ugh -- server reply was truncated */
-                       if (p == NULL) {
-                               dprintk("NFS: FH truncated\n");
-                               *entry = old;
-                               return ERR_PTR(-EAGAIN);
-                       }
-               } else
-                       memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
-       }
-
-       return p;
-
+       fh->size = length;
+       memcpy(fh->data, p, length);
+       return 0;
+out_toobig:
+       dprintk("NFS: file handle size (%u) too big\n", length);
+       return -E2BIG;
 out_overflow:
        print_overflow_msg(__func__, xdr);
-out_overflow_exit:
-       return ERR_PTR(-EAGAIN);
+       return -EIO;
 }
 
-/*
- * Encode COMMIT arguments
- */
-static int
-nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
+static void zero_nfs_fh3(struct nfs_fh *fh)
 {
-       p = xdr_encode_fhandle(p, args->fh);
-       p = xdr_encode_hyper(p, args->offset);
-       *p++ = htonl(args->count);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       return 0;
+       memset(fh, 0, sizeof(*fh));
 }
 
-#ifdef CONFIG_NFS_V3_ACL
 /*
- * Encode GETACL arguments
+ * nfstime3
+ *
+ *     struct nfstime3 {
+ *             uint32  seconds;
+ *             uint32  nseconds;
+ *     };
  */
-static int
-nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
-                   struct nfs3_getaclargs *args)
+static __be32 *xdr_encode_nfstime3(__be32 *p, const struct timespec *timep)
 {
-       struct rpc_auth *auth = req->rq_cred->cr_auth;
-       unsigned int replen;
-
-       p = xdr_encode_fhandle(p, args->fh);
-       *p++ = htonl(args->mask);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-
-       if (args->mask & (NFS_ACL | NFS_DFACL)) {
-               /* Inline the page array */
-               replen = (RPC_REPHDRSIZE + auth->au_rslack +
-                         ACL3_getaclres_sz) << 2;
-               xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0,
-                                NFSACL_MAXPAGES << PAGE_SHIFT);
-       }
-       return 0;
+       *p++ = cpu_to_be32(timep->tv_sec);
+       *p++ = cpu_to_be32(timep->tv_nsec);
+       return p;
 }
 
-/*
- * Encode SETACL arguments
- */
-static int
-nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
-                   struct nfs3_setaclargs *args)
+static __be32 *xdr_decode_nfstime3(__be32 *p, struct timespec *timep)
 {
-       struct xdr_buf *buf = &req->rq_snd_buf;
-       unsigned int base;
-       int err;
-
-       p = xdr_encode_fhandle(p, NFS_FH(args->inode));
-       *p++ = htonl(args->mask);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
-       base = req->rq_slen;
-
-       if (args->npages != 0)
-               xdr_encode_pages(buf, args->pages, 0, args->len);
-       else
-               req->rq_slen = xdr_adjust_iovec(req->rq_svec,
-                               p + XDR_QUADLEN(args->len));
-
-       err = nfsacl_encode(buf, base, args->inode,
-                           (args->mask & NFS_ACL) ?
-                           args->acl_access : NULL, 1, 0);
-       if (err > 0)
-               err = nfsacl_encode(buf, base + err, args->inode,
-                                   (args->mask & NFS_DFACL) ?
-                                   args->acl_default : NULL, 1,
-                                   NFS_ACL_DEFAULT);
-       return (err > 0) ? 0 : err;
+       timep->tv_sec = be32_to_cpup(p++);
+       timep->tv_nsec = be32_to_cpup(p++);
+       return p;
 }
-#endif  /* CONFIG_NFS_V3_ACL */
 
 /*
- * NFS XDR decode functions
- */
-
-/*
- * Decode attrstat reply.
+ * sattr3
+ *
+ *     enum time_how {
+ *             DONT_CHANGE             = 0,
+ *             SET_TO_SERVER_TIME      = 1,
+ *             SET_TO_CLIENT_TIME      = 2
+ *     };
+ *
+ *     union set_mode3 switch (bool set_it) {
+ *     case TRUE:
+ *             mode3   mode;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     union set_uid3 switch (bool set_it) {
+ *     case TRUE:
+ *             uid3    uid;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     union set_gid3 switch (bool set_it) {
+ *     case TRUE:
+ *             gid3    gid;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     union set_size3 switch (bool set_it) {
+ *     case TRUE:
+ *             size3   size;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     union set_atime switch (time_how set_it) {
+ *     case SET_TO_CLIENT_TIME:
+ *             nfstime3        atime;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     union set_mtime switch (time_how set_it) {
+ *     case SET_TO_CLIENT_TIME:
+ *             nfstime3  mtime;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     struct sattr3 {
+ *             set_mode3       mode;
+ *             set_uid3        uid;
+ *             set_gid3        gid;
+ *             set_size3       size;
+ *             set_atime       atime;
+ *             set_mtime       mtime;
+ *     };
  */
-static int
-nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
+static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)
 {
-       int     status;
+       u32 nbytes;
+       __be32 *p;
 
-       if ((status = ntohl(*p++)))
-               return nfs_stat_to_errno(status);
-       xdr_decode_fattr(p, fattr);
+       /*
+        * In order to make only a single xdr_reserve_space() call,
+        * pre-compute the total number of bytes to be reserved.
+        * Six boolean values, one for each set_foo field, are always
+        * present in the encoded result, so start there.
+        */
+       nbytes = 6 * 4;
+       if (attr->ia_valid & ATTR_MODE)
+               nbytes += 4;
+       if (attr->ia_valid & ATTR_UID)
+               nbytes += 4;
+       if (attr->ia_valid & ATTR_GID)
+               nbytes += 4;
+       if (attr->ia_valid & ATTR_SIZE)
+               nbytes += 8;
+       if (attr->ia_valid & ATTR_ATIME_SET)
+               nbytes += 8;
+       if (attr->ia_valid & ATTR_MTIME_SET)
+               nbytes += 8;
+       p = xdr_reserve_space(xdr, nbytes);
+
+       if (attr->ia_valid & ATTR_MODE) {
+               *p++ = xdr_one;
+               *p++ = cpu_to_be32(attr->ia_mode & S_IALLUGO);
+       } else
+               *p++ = xdr_zero;
+
+       if (attr->ia_valid & ATTR_UID) {
+               *p++ = xdr_one;
+               *p++ = cpu_to_be32(attr->ia_uid);
+       } else
+               *p++ = xdr_zero;
+
+       if (attr->ia_valid & ATTR_GID) {
+               *p++ = xdr_one;
+               *p++ = cpu_to_be32(attr->ia_gid);
+       } else
+               *p++ = xdr_zero;
+
+       if (attr->ia_valid & ATTR_SIZE) {
+               *p++ = xdr_one;
+               p = xdr_encode_hyper(p, (u64)attr->ia_size);
+       } else
+               *p++ = xdr_zero;
+
+       if (attr->ia_valid & ATTR_ATIME_SET) {
+               *p++ = xdr_two;
+               p = xdr_encode_nfstime3(p, &attr->ia_atime);
+       } else if (attr->ia_valid & ATTR_ATIME) {
+               *p++ = xdr_one;
+       } else
+               *p++ = xdr_zero;
+
+       if (attr->ia_valid & ATTR_MTIME_SET) {
+               *p++ = xdr_two;
+               xdr_encode_nfstime3(p, &attr->ia_mtime);
+       } else if (attr->ia_valid & ATTR_MTIME) {
+               *p = xdr_one;
+       } else
+               *p = xdr_zero;
+}
+
+/*
+ * fattr3
+ *
+ *     struct fattr3 {
+ *             ftype3          type;
+ *             mode3           mode;
+ *             uint32          nlink;
+ *             uid3            uid;
+ *             gid3            gid;
+ *             size3           size;
+ *             size3           used;
+ *             specdata3       rdev;
+ *             uint64          fsid;
+ *             fileid3         fileid;
+ *             nfstime3        atime;
+ *             nfstime3        mtime;
+ *             nfstime3        ctime;
+ *     };
+ */
+static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr)
+{
+       umode_t fmode;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, NFS3_fattr_sz << 2);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+
+       p = xdr_decode_ftype3(p, &fmode);
+
+       fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode;
+       fattr->nlink = be32_to_cpup(p++);
+       fattr->uid = be32_to_cpup(p++);
+       fattr->gid = be32_to_cpup(p++);
+
+       p = xdr_decode_size3(p, &fattr->size);
+       p = xdr_decode_size3(p, &fattr->du.nfs3.used);
+       p = xdr_decode_specdata3(p, &fattr->rdev);
+
+       p = xdr_decode_hyper(p, &fattr->fsid.major);
+       fattr->fsid.minor = 0;
+
+       p = xdr_decode_fileid3(p, &fattr->fileid);
+       p = xdr_decode_nfstime3(p, &fattr->atime);
+       p = xdr_decode_nfstime3(p, &fattr->mtime);
+       xdr_decode_nfstime3(p, &fattr->ctime);
+
+       fattr->valid |= NFS_ATTR_FATTR_V3;
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
 /*
- * Decode status+wcc_data reply
- * SATTR, REMOVE, RMDIR
+ * post_op_attr
+ *
+ *     union post_op_attr switch (bool attributes_follow) {
+ *     case TRUE:
+ *             fattr3  attributes;
+ *     case FALSE:
+ *             void;
+ *     };
  */
-static int
-nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
+static int decode_post_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
 {
-       int     status;
+       __be32 *p;
 
-       if ((status = ntohl(*p++)))
-               status = nfs_stat_to_errno(status);
-       xdr_decode_wcc_data(p, fattr);
-       return status;
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       if (*p != xdr_zero)
+               return decode_fattr3(xdr, fattr);
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ * wcc_attr
+ *     struct wcc_attr {
+ *             size3           size;
+ *             nfstime3        mtime;
+ *             nfstime3        ctime;
+ *     };
+ */
+static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
+{
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, NFS3_wcc_attr_sz << 2);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+
+       fattr->valid |= NFS_ATTR_FATTR_PRESIZE
+               | NFS_ATTR_FATTR_PREMTIME
+               | NFS_ATTR_FATTR_PRECTIME;
+
+       p = xdr_decode_size3(p, &fattr->pre_size);
+       p = xdr_decode_nfstime3(p, &fattr->pre_mtime);
+       xdr_decode_nfstime3(p, &fattr->pre_ctime);
+
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ * pre_op_attr
+ *     union pre_op_attr switch (bool attributes_follow) {
+ *     case TRUE:
+ *             wcc_attr        attributes;
+ *     case FALSE:
+ *             void;
+ *     };
+ *
+ * wcc_data
+ *
+ *     struct wcc_data {
+ *             pre_op_attr     before;
+ *             post_op_attr    after;
+ *     };
+ */
+static int decode_pre_op_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
+{
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       if (*p != xdr_zero)
+               return decode_wcc_attr(xdr, fattr);
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+static int decode_wcc_data(struct xdr_stream *xdr, struct nfs_fattr *fattr)
+{
+       int error;
+
+       error = decode_pre_op_attr(xdr, fattr);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, fattr);
+out:
+       return error;
+}
+
+/*
+ * post_op_fh3
+ *
+ *     union post_op_fh3 switch (bool handle_follows) {
+ *     case TRUE:
+ *             nfs_fh3  handle;
+ *     case FALSE:
+ *             void;
+ *     };
+ */
+static int decode_post_op_fh3(struct xdr_stream *xdr, struct nfs_fh *fh)
+{
+       __be32 *p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       if (*p != xdr_zero)
+               return decode_nfs_fh3(xdr, fh);
+       zero_nfs_fh3(fh);
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+/*
+ * diropargs3
+ *
+ *     struct diropargs3 {
+ *             nfs_fh3         dir;
+ *             filename3       name;
+ *     };
+ */
+static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
+                             const char *name, u32 length)
+{
+       encode_nfs_fh3(xdr, fh);
+       encode_filename3(xdr, name, length);
 }
 
-static int
-nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
+
+/*
+ * NFSv3 XDR encode functions
+ *
+ * NFSv3 argument types are defined in section 3.3 of RFC 1813:
+ * "NFS Version 3 Protocol Specification".
+ */
+
+/*
+ * 3.3.1  GETATTR3args
+ *
+ *     struct GETATTR3args {
+ *             nfs_fh3  object;
+ *     };
+ */
+static void nfs3_xdr_enc_getattr3args(struct rpc_rqst *req,
+                                     struct xdr_stream *xdr,
+                                     const struct nfs_fh *fh)
 {
-       return nfs3_xdr_wccstat(req, p, res->dir_attr);
+       encode_nfs_fh3(xdr, fh);
 }
 
 /*
- * Decode LOOKUP reply
+ * 3.3.2  SETATTR3args
+ *
+ *     union sattrguard3 switch (bool check) {
+ *     case TRUE:
+ *             nfstime3  obj_ctime;
+ *     case FALSE:
+ *             void;
+ *     };
+ *
+ *     struct SETATTR3args {
+ *             nfs_fh3         object;
+ *             sattr3          new_attributes;
+ *             sattrguard3     guard;
+ *     };
  */
-static int
-nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
+static void encode_sattrguard3(struct xdr_stream *xdr,
+                              const struct nfs3_sattrargs *args)
 {
-       int     status;
+       __be32 *p;
 
-       if ((status = ntohl(*p++))) {
-               status = nfs_stat_to_errno(status);
+       if (args->guard) {
+               p = xdr_reserve_space(xdr, 4 + 8);
+               *p++ = xdr_one;
+               xdr_encode_nfstime3(p, &args->guardtime);
        } else {
-               if (!(p = xdr_decode_fhandle(p, res->fh)))
-                       return -errno_NFSERR_IO;
-               p = xdr_decode_post_op_attr(p, res->fattr);
+               p = xdr_reserve_space(xdr, 4);
+               *p = xdr_zero;
        }
-       xdr_decode_post_op_attr(p, res->dir_attr);
-       return status;
+}
+
+static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
+                                     struct xdr_stream *xdr,
+                                     const struct nfs3_sattrargs *args)
+{
+       encode_nfs_fh3(xdr, args->fh);
+       encode_sattr3(xdr, args->sattr);
+       encode_sattrguard3(xdr, args);
 }
 
 /*
- * Decode ACCESS reply
+ * 3.3.3  LOOKUP3args
+ *
+ *     struct LOOKUP3args {
+ *             diropargs3  what;
+ *     };
  */
-static int
-nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
+static void nfs3_xdr_enc_lookup3args(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs3_diropargs *args)
 {
-       int     status = ntohl(*p++);
+       encode_diropargs3(xdr, args->fh, args->name, args->len);
+}
 
-       p = xdr_decode_post_op_attr(p, res->fattr);
-       if (status)
-               return nfs_stat_to_errno(status);
-       res->access = ntohl(*p++);
-       return 0;
+/*
+ * 3.3.4  ACCESS3args
+ *
+ *     struct ACCESS3args {
+ *             nfs_fh3         object;
+ *             uint32          access;
+ *     };
+ */
+static void encode_access3args(struct xdr_stream *xdr,
+                              const struct nfs3_accessargs *args)
+{
+       encode_nfs_fh3(xdr, args->fh);
+       encode_uint32(xdr, args->access);
 }
 
-static int
-nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
+static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs3_accessargs *args)
 {
-       struct rpc_auth *auth = req->rq_cred->cr_auth;
-       unsigned int replen;
+       encode_access3args(xdr, args);
+}
 
-       p = xdr_encode_fhandle(p, args->fh);
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+/*
+ * 3.3.5  READLINK3args
+ *
+ *     struct READLINK3args {
+ *             nfs_fh3 symlink;
+ *     };
+ */
+static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req,
+                                      struct xdr_stream *xdr,
+                                      const struct nfs3_readlinkargs *args)
+{
+       encode_nfs_fh3(xdr, args->fh);
+       prepare_reply_buffer(req, args->pages, args->pgbase,
+                                       args->pglen, NFS3_readlinkres_sz);
+}
 
-       /* Inline the page array */
-       replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
-       xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
-       return 0;
+/*
+ * 3.3.6  READ3args
+ *
+ *     struct READ3args {
+ *             nfs_fh3         file;
+ *             offset3         offset;
+ *             count3          count;
+ *     };
+ */
+static void encode_read3args(struct xdr_stream *xdr,
+                            const struct nfs_readargs *args)
+{
+       __be32 *p;
+
+       encode_nfs_fh3(xdr, args->fh);
+
+       p = xdr_reserve_space(xdr, 8 + 4);
+       p = xdr_encode_hyper(p, args->offset);
+       *p = cpu_to_be32(args->count);
+}
+
+static void nfs3_xdr_enc_read3args(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  const struct nfs_readargs *args)
+{
+       encode_read3args(xdr, args);
+       prepare_reply_buffer(req, args->pages, args->pgbase,
+                                       args->count, NFS3_readres_sz);
+       req->rq_rcv_buf.flags |= XDRBUF_READ;
 }
 
 /*
- * Decode READLINK reply
+ * 3.3.7  WRITE3args
+ *
+ *     enum stable_how {
+ *             UNSTABLE  = 0,
+ *             DATA_SYNC = 1,
+ *             FILE_SYNC = 2
+ *     };
+ *
+ *     struct WRITE3args {
+ *             nfs_fh3         file;
+ *             offset3         offset;
+ *             count3          count;
+ *             stable_how      stable;
+ *             opaque          data<>;
+ *     };
  */
-static int
-nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
+static void encode_write3args(struct xdr_stream *xdr,
+                             const struct nfs_writeargs *args)
 {
-       struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
-       struct kvec *iov = rcvbuf->head;
-       size_t hdrlen;
-       u32 len, recvd;
-       int     status;
+       __be32 *p;
 
-       status = ntohl(*p++);
-       p = xdr_decode_post_op_attr(p, fattr);
+       encode_nfs_fh3(xdr, args->fh);
 
-       if (status != 0)
-               return nfs_stat_to_errno(status);
+       p = xdr_reserve_space(xdr, 8 + 4 + 4 + 4);
+       p = xdr_encode_hyper(p, args->offset);
+       *p++ = cpu_to_be32(args->count);
+       *p++ = cpu_to_be32(args->stable);
+       *p = cpu_to_be32(args->count);
+       xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
+}
 
-       /* Convert length of symlink */
-       len = ntohl(*p++);
-       if (len >= rcvbuf->page_len) {
-               dprintk("nfs: server returned giant symlink!\n");
-               return -ENAMETOOLONG;
-       }
+static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   const struct nfs_writeargs *args)
+{
+       encode_write3args(xdr, args);
+       xdr->buf->flags |= XDRBUF_WRITE;
+}
 
-       hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len < hdrlen) {
-               dprintk("NFS: READLINK reply header overflowed:"
-                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
-               return -errno_NFSERR_IO;
-       } else if (iov->iov_len != hdrlen) {
-               dprintk("NFS: READLINK header is short. "
-                       "iovec will be shifted.\n");
-               xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
-       }
-       recvd = req->rq_rcv_buf.len - hdrlen;
-       if (recvd < len) {
-               dprintk("NFS: server cheating in readlink reply: "
-                               "count %u > recvd %u\n", len, recvd);
-               return -EIO;
+/*
+ * 3.3.8  CREATE3args
+ *
+ *     enum createmode3 {
+ *             UNCHECKED = 0,
+ *             GUARDED   = 1,
+ *             EXCLUSIVE = 2
+ *     };
+ *
+ *     union createhow3 switch (createmode3 mode) {
+ *     case UNCHECKED:
+ *     case GUARDED:
+ *             sattr3       obj_attributes;
+ *     case EXCLUSIVE:
+ *             createverf3  verf;
+ *     };
+ *
+ *     struct CREATE3args {
+ *             diropargs3      where;
+ *             createhow3      how;
+ *     };
+ */
+static void encode_createhow3(struct xdr_stream *xdr,
+                             const struct nfs3_createargs *args)
+{
+       encode_uint32(xdr, args->createmode);
+       switch (args->createmode) {
+       case NFS3_CREATE_UNCHECKED:
+       case NFS3_CREATE_GUARDED:
+               encode_sattr3(xdr, args->sattr);
+               break;
+       case NFS3_CREATE_EXCLUSIVE:
+               encode_createverf3(xdr, args->verifier);
+               break;
+       default:
+               BUG();
        }
+}
 
-       xdr_terminate_string(rcvbuf, len);
-       return 0;
+static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs3_createargs *args)
+{
+       encode_diropargs3(xdr, args->fh, args->name, args->len);
+       encode_createhow3(xdr, args);
 }
 
 /*
- * Decode READ reply
+ * 3.3.9  MKDIR3args
+ *
+ *     struct MKDIR3args {
+ *             diropargs3      where;
+ *             sattr3          attributes;
+ *     };
  */
-static int
-nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
+static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   const struct nfs3_mkdirargs *args)
 {
-       struct kvec *iov = req->rq_rcv_buf.head;
-       size_t hdrlen;
-       u32 count, ocount, recvd;
-       int status;
+       encode_diropargs3(xdr, args->fh, args->name, args->len);
+       encode_sattr3(xdr, args->sattr);
+}
 
-       status = ntohl(*p++);
-       p = xdr_decode_post_op_attr(p, res->fattr);
+/*
+ * 3.3.10  SYMLINK3args
+ *
+ *     struct symlinkdata3 {
+ *             sattr3          symlink_attributes;
+ *             nfspath3        symlink_data;
+ *     };
+ *
+ *     struct SYMLINK3args {
+ *             diropargs3      where;
+ *             symlinkdata3    symlink;
+ *     };
+ */
+static void encode_symlinkdata3(struct xdr_stream *xdr,
+                               const struct nfs3_symlinkargs *args)
+{
+       encode_sattr3(xdr, args->sattr);
+       encode_nfspath3(xdr, args->pages, args->pathlen);
+}
 
-       if (status != 0)
-               return nfs_stat_to_errno(status);
+static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,
+                                     struct xdr_stream *xdr,
+                                     const struct nfs3_symlinkargs *args)
+{
+       encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen);
+       encode_symlinkdata3(xdr, args);
+}
 
-       /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
-        * in that it puts the count both in the res struct and in the
-        * opaque data count. */
-       count    = ntohl(*p++);
-       res->eof = ntohl(*p++);
-       ocount   = ntohl(*p++);
+/*
+ * 3.3.11  MKNOD3args
+ *
+ *     struct devicedata3 {
+ *             sattr3          dev_attributes;
+ *             specdata3       spec;
+ *     };
+ *
+ *     union mknoddata3 switch (ftype3 type) {
+ *     case NF3CHR:
+ *     case NF3BLK:
+ *             devicedata3     device;
+ *     case NF3SOCK:
+ *     case NF3FIFO:
+ *             sattr3          pipe_attributes;
+ *     default:
+ *             void;
+ *     };
+ *
+ *     struct MKNOD3args {
+ *             diropargs3      where;
+ *             mknoddata3      what;
+ *     };
+ */
+static void encode_devicedata3(struct xdr_stream *xdr,
+                              const struct nfs3_mknodargs *args)
+{
+       encode_sattr3(xdr, args->sattr);
+       encode_specdata3(xdr, args->rdev);
+}
 
-       if (ocount != count) {
-               dprintk("NFS: READ count doesn't match RPC opaque count.\n");
-               return -errno_NFSERR_IO;
+static void encode_mknoddata3(struct xdr_stream *xdr,
+                             const struct nfs3_mknodargs *args)
+{
+       encode_ftype3(xdr, args->type);
+       switch (args->type) {
+       case NF3CHR:
+       case NF3BLK:
+               encode_devicedata3(xdr, args);
+               break;
+       case NF3SOCK:
+       case NF3FIFO:
+               encode_sattr3(xdr, args->sattr);
+               break;
+       case NF3REG:
+       case NF3DIR:
+               break;
+       default:
+               BUG();
        }
+}
 
-       hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len < hdrlen) {
-               dprintk("NFS: READ reply header overflowed:"
-                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
-                       return -errno_NFSERR_IO;
-       } else if (iov->iov_len != hdrlen) {
-               dprintk("NFS: READ header is short. iovec will be shifted.\n");
-               xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
-       }
+static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   const struct nfs3_mknodargs *args)
+{
+       encode_diropargs3(xdr, args->fh, args->name, args->len);
+       encode_mknoddata3(xdr, args);
+}
 
-       recvd = req->rq_rcv_buf.len - hdrlen;
-       if (count > recvd) {
-               dprintk("NFS: server cheating in read reply: "
-                       "count %u > recvd %u\n", count, recvd);
-               count = recvd;
-               res->eof = 0;
-       }
+/*
+ * 3.3.12  REMOVE3args
+ *
+ *     struct REMOVE3args {
+ *             diropargs3  object;
+ *     };
+ */
+static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs_removeargs *args)
+{
+       encode_diropargs3(xdr, args->fh, args->name.name, args->name.len);
+}
 
-       if (count < res->count)
-               res->count = count;
+/*
+ * 3.3.14  RENAME3args
+ *
+ *     struct RENAME3args {
+ *             diropargs3      from;
+ *             diropargs3      to;
+ *     };
+ */
+static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs_renameargs *args)
+{
+       const struct qstr *old = args->old_name;
+       const struct qstr *new = args->new_name;
 
-       return count;
+       encode_diropargs3(xdr, args->old_dir, old->name, old->len);
+       encode_diropargs3(xdr, args->new_dir, new->name, new->len);
 }
 
 /*
- * Decode WRITE response
+ * 3.3.15  LINK3args
+ *
+ *     struct LINK3args {
+ *             nfs_fh3         file;
+ *             diropargs3      link;
+ *     };
  */
-static int
-nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
+static void nfs3_xdr_enc_link3args(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  const struct nfs3_linkargs *args)
 {
-       int     status;
+       encode_nfs_fh3(xdr, args->fromfh);
+       encode_diropargs3(xdr, args->tofh, args->toname, args->tolen);
+}
 
-       status = ntohl(*p++);
-       p = xdr_decode_wcc_data(p, res->fattr);
+/*
+ * 3.3.16  READDIR3args
+ *
+ *     struct READDIR3args {
+ *             nfs_fh3         dir;
+ *             cookie3         cookie;
+ *             cookieverf3     cookieverf;
+ *             count3          count;
+ *     };
+ */
+static void encode_readdir3args(struct xdr_stream *xdr,
+                               const struct nfs3_readdirargs *args)
+{
+       __be32 *p;
 
-       if (status != 0)
-               return nfs_stat_to_errno(status);
+       encode_nfs_fh3(xdr, args->fh);
 
-       res->count = ntohl(*p++);
-       res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
-       res->verf->verifier[0] = *p++;
-       res->verf->verifier[1] = *p++;
+       p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4);
+       p = xdr_encode_cookie3(p, args->cookie);
+       p = xdr_encode_cookieverf3(p, args->verf);
+       *p = cpu_to_be32(args->count);
+}
 
-       return res->count;
+static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req,
+                                     struct xdr_stream *xdr,
+                                     const struct nfs3_readdirargs *args)
+{
+       encode_readdir3args(xdr, args);
+       prepare_reply_buffer(req, args->pages, 0,
+                               args->count, NFS3_readdirres_sz);
 }
 
 /*
- * Decode a CREATE response
+ * 3.3.17  READDIRPLUS3args
+ *
+ *     struct READDIRPLUS3args {
+ *             nfs_fh3         dir;
+ *             cookie3         cookie;
+ *             cookieverf3     cookieverf;
+ *             count3          dircount;
+ *             count3          maxcount;
+ *     };
  */
-static int
-nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
-{
-       int     status;
-
-       status = ntohl(*p++);
-       if (status == 0) {
-               if (*p++) {
-                       if (!(p = xdr_decode_fhandle(p, res->fh)))
-                               return -errno_NFSERR_IO;
-                       p = xdr_decode_post_op_attr(p, res->fattr);
-               } else {
-                       memset(res->fh, 0, sizeof(*res->fh));
-                       /* Do decode post_op_attr but set it to NULL */
-                       p = xdr_decode_post_op_attr(p, res->fattr);
-                       res->fattr->valid = 0;
-               }
-       } else {
-               status = nfs_stat_to_errno(status);
-       }
-       p = xdr_decode_wcc_data(p, res->dir_attr);
-       return status;
+static void encode_readdirplus3args(struct xdr_stream *xdr,
+                                   const struct nfs3_readdirargs *args)
+{
+       __be32 *p;
+
+       encode_nfs_fh3(xdr, args->fh);
+
+       p = xdr_reserve_space(xdr, 8 + NFS3_COOKIEVERFSIZE + 4 + 4);
+       p = xdr_encode_cookie3(p, args->cookie);
+       p = xdr_encode_cookieverf3(p, args->verf);
+
+       /*
+        * readdirplus: need dircount + buffer size.
+        * We just make sure we make dircount big enough
+        */
+       *p++ = cpu_to_be32(args->count >> 3);
+
+       *p = cpu_to_be32(args->count);
+}
+
+static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
+                                         struct xdr_stream *xdr,
+                                         const struct nfs3_readdirargs *args)
+{
+       encode_readdirplus3args(xdr, args);
+       prepare_reply_buffer(req, args->pages, 0,
+                               args->count, NFS3_readdirres_sz);
 }
 
 /*
- * Decode RENAME reply
+ * 3.3.21  COMMIT3args
+ *
+ *     struct COMMIT3args {
+ *             nfs_fh3         file;
+ *             offset3         offset;
+ *             count3          count;
+ *     };
  */
-static int
-nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs_renameres *res)
+static void encode_commit3args(struct xdr_stream *xdr,
+                              const struct nfs_writeargs *args)
 {
-       int     status;
+       __be32 *p;
+
+       encode_nfs_fh3(xdr, args->fh);
+
+       p = xdr_reserve_space(xdr, 8 + 4);
+       p = xdr_encode_hyper(p, args->offset);
+       *p = cpu_to_be32(args->count);
+}
 
-       if ((status = ntohl(*p++)) != 0)
-               status = nfs_stat_to_errno(status);
-       p = xdr_decode_wcc_data(p, res->old_fattr);
-       p = xdr_decode_wcc_data(p, res->new_fattr);
-       return status;
+static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs_writeargs *args)
+{
+       encode_commit3args(xdr, args);
 }
 
+#ifdef CONFIG_NFS_V3_ACL
+
+static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs3_getaclargs *args)
+{
+       encode_nfs_fh3(xdr, args->fh);
+       encode_uint32(xdr, args->mask);
+       if (args->mask & (NFS_ACL | NFS_DFACL))
+               prepare_reply_buffer(req, args->pages, 0,
+                                       NFSACL_MAXPAGES << PAGE_SHIFT,
+                                       ACL3_getaclres_sz);
+}
+
+static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs3_setaclargs *args)
+{
+       unsigned int base;
+       int error;
+
+       encode_nfs_fh3(xdr, NFS_FH(args->inode));
+       encode_uint32(xdr, args->mask);
+       if (args->npages != 0)
+               xdr_write_pages(xdr, args->pages, 0, args->len);
+
+       base = req->rq_slen;
+       error = nfsacl_encode(xdr->buf, base, args->inode,
+                           (args->mask & NFS_ACL) ?
+                           args->acl_access : NULL, 1, 0);
+       BUG_ON(error < 0);
+       error = nfsacl_encode(xdr->buf, base + error, args->inode,
+                           (args->mask & NFS_DFACL) ?
+                           args->acl_default : NULL, 1,
+                           NFS_ACL_DEFAULT);
+       BUG_ON(error < 0);
+}
+
+#endif  /* CONFIG_NFS_V3_ACL */
+
 /*
- * Decode LINK reply
+ * NFSv3 XDR decode functions
+ *
+ * NFSv3 result types are defined in section 3.3 of RFC 1813:
+ * "NFS Version 3 Protocol Specification".
  */
-static int
-nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
+
+/*
+ * 3.3.1  GETATTR3res
+ *
+ *     struct GETATTR3resok {
+ *             fattr3          obj_attributes;
+ *     };
+ *
+ *     union GETATTR3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             GETATTR3resok  resok;
+ *     default:
+ *             void;
+ *     };
+ */
+static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   struct nfs_fattr *result)
 {
-       int     status;
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_default;
+       error = decode_fattr3(xdr, result);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
+}
 
-       if ((status = ntohl(*p++)) != 0)
-               status = nfs_stat_to_errno(status);
-       p = xdr_decode_post_op_attr(p, res->fattr);
-       p = xdr_decode_wcc_data(p, res->dir_attr);
-       return status;
+/*
+ * 3.3.2  SETATTR3res
+ *
+ *     struct SETATTR3resok {
+ *             wcc_data  obj_wcc;
+ *     };
+ *
+ *     struct SETATTR3resfail {
+ *             wcc_data  obj_wcc;
+ *     };
+ *
+ *     union SETATTR3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             SETATTR3resok   resok;
+ *     default:
+ *             SETATTR3resfail resfail;
+ *     };
+ */
+static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   struct nfs_fattr *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_wcc_data(xdr, result);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
 }
 
 /*
- * Decode FSSTAT reply
+ * 3.3.3  LOOKUP3res
+ *
+ *     struct LOOKUP3resok {
+ *             nfs_fh3         object;
+ *             post_op_attr    obj_attributes;
+ *             post_op_attr    dir_attributes;
+ *     };
+ *
+ *     struct LOOKUP3resfail {
+ *             post_op_attr    dir_attributes;
+ *     };
+ *
+ *     union LOOKUP3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             LOOKUP3resok    resok;
+ *     default:
+ *             LOOKUP3resfail  resfail;
+ *     };
  */
-static int
-nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
+static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs3_diropres *result)
 {
-       int             status;
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_default;
+       error = decode_nfs_fh3(xdr, result->fh);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->dir_attr);
+out:
+       return error;
+out_default:
+       error = decode_post_op_attr(xdr, result->dir_attr);
+       if (unlikely(error))
+               goto out;
+       return nfs_stat_to_errno(status);
+}
 
-       status = ntohl(*p++);
+/*
+ * 3.3.4  ACCESS3res
+ *
+ *     struct ACCESS3resok {
+ *             post_op_attr    obj_attributes;
+ *             uint32          access;
+ *     };
+ *
+ *     struct ACCESS3resfail {
+ *             post_op_attr    obj_attributes;
+ *     };
+ *
+ *     union ACCESS3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             ACCESS3resok    resok;
+ *     default:
+ *             ACCESS3resfail  resfail;
+ *     };
+ */
+static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs3_accessres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_default;
+       error = decode_uint32(xdr, &result->access);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
+}
 
-       p = xdr_decode_post_op_attr(p, res->fattr);
-       if (status != 0)
-               return nfs_stat_to_errno(status);
+/*
+ * 3.3.5  READLINK3res
+ *
+ *     struct READLINK3resok {
+ *             post_op_attr    symlink_attributes;
+ *             nfspath3        data;
+ *     };
+ *
+ *     struct READLINK3resfail {
+ *             post_op_attr    symlink_attributes;
+ *     };
+ *
+ *     union READLINK3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             READLINK3resok  resok;
+ *     default:
+ *             READLINK3resfail resfail;
+ *     };
+ */
+static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    struct nfs_fattr *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_default;
+       error = decode_nfspath3(xdr);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
+}
 
-       p = xdr_decode_hyper(p, &res->tbytes);
-       p = xdr_decode_hyper(p, &res->fbytes);
-       p = xdr_decode_hyper(p, &res->abytes);
-       p = xdr_decode_hyper(p, &res->tfiles);
-       p = xdr_decode_hyper(p, &res->ffiles);
-       p = xdr_decode_hyper(p, &res->afiles);
+/*
+ * 3.3.6  READ3res
+ *
+ *     struct READ3resok {
+ *             post_op_attr    file_attributes;
+ *             count3          count;
+ *             bool            eof;
+ *             opaque          data<>;
+ *     };
+ *
+ *     struct READ3resfail {
+ *             post_op_attr    file_attributes;
+ *     };
+ *
+ *     union READ3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             READ3resok      resok;
+ *     default:
+ *             READ3resfail    resfail;
+ *     };
+ */
+static int decode_read3resok(struct xdr_stream *xdr,
+                            struct nfs_readres *result)
+{
+       u32 eof, count, ocount, recvd;
+       size_t hdrlen;
+       __be32 *p;
 
-       /* ignore invarsec */
-       return 0;
+       p = xdr_inline_decode(xdr, 4 + 4 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       count = be32_to_cpup(p++);
+       eof = be32_to_cpup(p++);
+       ocount = be32_to_cpup(p++);
+       if (unlikely(ocount != count))
+               goto out_mismatch;
+       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+       recvd = xdr->buf->len - hdrlen;
+       if (unlikely(count > recvd))
+               goto out_cheating;
+
+out:
+       xdr_read_pages(xdr, count);
+       result->eof = eof;
+       result->count = count;
+       return count;
+out_mismatch:
+       dprintk("NFS: READ count doesn't match length of opaque: "
+               "count %u != ocount %u\n", count, ocount);
+       return -EIO;
+out_cheating:
+       dprintk("NFS: server cheating in read result: "
+               "count %u > recvd %u\n", count, recvd);
+       count = recvd;
+       eof = 0;
+       goto out;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                struct nfs_readres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+       error = decode_read3resok(xdr, result);
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
 }
 
 /*
- * Decode FSINFO reply
+ * 3.3.7  WRITE3res
+ *
+ *     enum stable_how {
+ *             UNSTABLE  = 0,
+ *             DATA_SYNC = 1,
+ *             FILE_SYNC = 2
+ *     };
+ *
+ *     struct WRITE3resok {
+ *             wcc_data        file_wcc;
+ *             count3          count;
+ *             stable_how      committed;
+ *             writeverf3      verf;
+ *     };
+ *
+ *     struct WRITE3resfail {
+ *             wcc_data        file_wcc;
+ *     };
+ *
+ *     union WRITE3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             WRITE3resok     resok;
+ *     default:
+ *             WRITE3resfail   resfail;
+ *     };
  */
-static int
-nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
+static int decode_write3resok(struct xdr_stream *xdr,
+                             struct nfs_writeres *result)
 {
-       int             status;
+       __be32 *p;
 
-       status = ntohl(*p++);
+       p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       result->count = be32_to_cpup(p++);
+       result->verf->committed = be32_to_cpup(p++);
+       if (unlikely(result->verf->committed > NFS_FILE_SYNC))
+               goto out_badvalue;
+       memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE);
+       return result->count;
+out_badvalue:
+       dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
+       return -EIO;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
 
-       p = xdr_decode_post_op_attr(p, res->fattr);
-       if (status != 0)
-               return nfs_stat_to_errno(status);
+static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                 struct nfs_writeres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_wcc_data(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+       error = decode_write3resok(xdr, result);
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
+}
 
-       res->rtmax  = ntohl(*p++);
-       res->rtpref = ntohl(*p++);
-       res->rtmult = ntohl(*p++);
-       res->wtmax  = ntohl(*p++);
-       res->wtpref = ntohl(*p++);
-       res->wtmult = ntohl(*p++);
-       res->dtpref = ntohl(*p++);
-       p = xdr_decode_hyper(p, &res->maxfilesize);
-       p = xdr_decode_time3(p, &res->time_delta);
+/*
+ * 3.3.8  CREATE3res
+ *
+ *     struct CREATE3resok {
+ *             post_op_fh3     obj;
+ *             post_op_attr    obj_attributes;
+ *             wcc_data        dir_wcc;
+ *     };
+ *
+ *     struct CREATE3resfail {
+ *             wcc_data        dir_wcc;
+ *     };
+ *
+ *     union CREATE3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             CREATE3resok    resok;
+ *     default:
+ *             CREATE3resfail  resfail;
+ *     };
+ */
+static int decode_create3resok(struct xdr_stream *xdr,
+                              struct nfs3_diropres *result)
+{
+       int error;
+
+       error = decode_post_op_fh3(xdr, result->fh);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       /* The server isn't required to return a file handle.
+        * If it didn't, force the client to perform a LOOKUP
+        * to determine the correct file handle and attribute
+        * values for the new object. */
+       if (result->fh->size == 0)
+               result->fattr->valid = 0;
+       error = decode_wcc_data(xdr, result->dir_attr);
+out:
+       return error;
+}
 
-       /* ignore properties */
-       res->lease_time = 0;
-       return 0;
+static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs3_diropres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_default;
+       error = decode_create3resok(xdr, result);
+out:
+       return error;
+out_default:
+       error = decode_wcc_data(xdr, result->dir_attr);
+       if (unlikely(error))
+               goto out;
+       return nfs_stat_to_errno(status);
 }
 
 /*
- * Decode PATHCONF reply
+ * 3.3.12  REMOVE3res
+ *
+ *     struct REMOVE3resok {
+ *             wcc_data    dir_wcc;
+ *     };
+ *
+ *     struct REMOVE3resfail {
+ *             wcc_data    dir_wcc;
+ *     };
+ *
+ *     union REMOVE3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             REMOVE3resok   resok;
+ *     default:
+ *             REMOVE3resfail resfail;
+ *     };
  */
-static int
-nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
+static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs_removeres *result)
 {
-       int             status;
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_wcc_data(xdr, result->dir_attr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
+}
 
-       status = ntohl(*p++);
+/*
+ * 3.3.14  RENAME3res
+ *
+ *     struct RENAME3resok {
+ *             wcc_data        fromdir_wcc;
+ *             wcc_data        todir_wcc;
+ *     };
+ *
+ *     struct RENAME3resfail {
+ *             wcc_data        fromdir_wcc;
+ *             wcc_data        todir_wcc;
+ *     };
+ *
+ *     union RENAME3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             RENAME3resok   resok;
+ *     default:
+ *             RENAME3resfail resfail;
+ *     };
+ */
+static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs_renameres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_wcc_data(xdr, result->old_fattr);
+       if (unlikely(error))
+               goto out;
+       error = decode_wcc_data(xdr, result->new_fattr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
+}
 
-       p = xdr_decode_post_op_attr(p, res->fattr);
-       if (status != 0)
-               return nfs_stat_to_errno(status);
-       res->max_link = ntohl(*p++);
-       res->max_namelen = ntohl(*p++);
+/*
+ * 3.3.15  LINK3res
+ *
+ *     struct LINK3resok {
+ *             post_op_attr    file_attributes;
+ *             wcc_data        linkdir_wcc;
+ *     };
+ *
+ *     struct LINK3resfail {
+ *             post_op_attr    file_attributes;
+ *             wcc_data        linkdir_wcc;
+ *     };
+ *
+ *     union LINK3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             LINK3resok      resok;
+ *     default:
+ *             LINK3resfail    resfail;
+ *     };
+ */
+static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                struct nfs3_linkres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       error = decode_wcc_data(xdr, result->dir_attr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
+}
+
+/**
+ * nfs3_decode_dirent - Decode a single NFSv3 directory entry stored in
+ *                     the local page cache
+ * @xdr: XDR stream where entry resides
+ * @entry: buffer to fill in with entry data
+ * @plus: boolean indicating whether this should be a readdirplus entry
+ *
+ * Returns zero if successful, otherwise a negative errno value is
+ * returned.
+ *
+ * This function is not invoked during READDIR reply decoding, but
+ * rather whenever an application invokes the getdents(2) system call
+ * on a directory already in our cache.
+ *
+ * 3.3.16  entry3
+ *
+ *     struct entry3 {
+ *             fileid3         fileid;
+ *             filename3       name;
+ *             cookie3         cookie;
+ *             fhandle3        filehandle;
+ *             post_op_attr3   attributes;
+ *             entry3          *nextentry;
+ *     };
+ *
+ * 3.3.17  entryplus3
+ *     struct entryplus3 {
+ *             fileid3         fileid;
+ *             filename3       name;
+ *             cookie3         cookie;
+ *             post_op_attr    name_attributes;
+ *             post_op_fh3     name_handle;
+ *             entryplus3      *nextentry;
+ *     };
+ */
+int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
+                      int plus)
+{
+       struct nfs_entry old = *entry;
+       __be32 *p;
+       int error;
+
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       if (*p == xdr_zero) {
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(p == NULL))
+                       goto out_overflow;
+               if (*p == xdr_zero)
+                       return -EAGAIN;
+               entry->eof = 1;
+               return -EBADCOOKIE;
+       }
+
+       error = decode_fileid3(xdr, &entry->ino);
+       if (unlikely(error))
+               return error;
+
+       error = decode_inline_filename3(xdr, &entry->name, &entry->len);
+       if (unlikely(error))
+               return error;
+
+       entry->prev_cookie = entry->cookie;
+       error = decode_cookie3(xdr, &entry->cookie);
+       if (unlikely(error))
+               return error;
+
+       entry->d_type = DT_UNKNOWN;
+
+       if (plus) {
+               entry->fattr->valid = 0;
+               error = decode_post_op_attr(xdr, entry->fattr);
+               if (unlikely(error))
+                       return error;
+               if (entry->fattr->valid & NFS_ATTR_FATTR_V3)
+                       entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
+
+               /* In fact, a post_op_fh3: */
+               p = xdr_inline_decode(xdr, 4);
+               if (unlikely(p == NULL))
+                       goto out_overflow;
+               if (*p != xdr_zero) {
+                       error = decode_nfs_fh3(xdr, entry->fh);
+                       if (unlikely(error)) {
+                               if (error == -E2BIG)
+                                       goto out_truncated;
+                               return error;
+                       }
+               } else
+                       zero_nfs_fh3(entry->fh);
+       }
 
-       /* ignore remaining fields */
        return 0;
+
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EAGAIN;
+out_truncated:
+       dprintk("NFS: directory entry contains invalid file handle\n");
+       *entry = old;
+       return -EAGAIN;
 }
 
 /*
- * Decode COMMIT reply
+ * 3.3.16  READDIR3res
+ *
+ *     struct dirlist3 {
+ *             entry3          *entries;
+ *             bool            eof;
+ *     };
+ *
+ *     struct READDIR3resok {
+ *             post_op_attr    dir_attributes;
+ *             cookieverf3     cookieverf;
+ *             dirlist3        reply;
+ *     };
+ *
+ *     struct READDIR3resfail {
+ *             post_op_attr    dir_attributes;
+ *     };
+ *
+ *     union READDIR3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             READDIR3resok   resok;
+ *     default:
+ *             READDIR3resfail resfail;
+ *     };
+ *
+ * Read the directory contents into the page cache, but otherwise
+ * don't touch them.  The actual decoding is done by nfs3_decode_entry()
+ * during subsequent nfs_readdir() calls.
  */
-static int
-nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
+static int decode_dirlist3(struct xdr_stream *xdr)
+{
+       u32 recvd, pglen;
+       size_t hdrlen;
+
+       pglen = xdr->buf->page_len;
+       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+       recvd = xdr->buf->len - hdrlen;
+       if (unlikely(pglen > recvd))
+               goto out_cheating;
+out:
+       xdr_read_pages(xdr, pglen);
+       return pglen;
+out_cheating:
+       dprintk("NFS: server cheating in readdir result: "
+               "pglen %u > recvd %u\n", pglen, recvd);
+       pglen = recvd;
+       goto out;
+}
+
+static int decode_readdir3resok(struct xdr_stream *xdr,
+                               struct nfs3_readdirres *result)
 {
-       int             status;
+       int error;
+
+       error = decode_post_op_attr(xdr, result->dir_attr);
+       if (unlikely(error))
+               goto out;
+       /* XXX: do we need to check if result->verf != NULL ? */
+       error = decode_cookieverf3(xdr, result->verf);
+       if (unlikely(error))
+               goto out;
+       error = decode_dirlist3(xdr);
+out:
+       return error;
+}
 
-       status = ntohl(*p++);
-       p = xdr_decode_wcc_data(p, res->fattr);
-       if (status != 0)
-               return nfs_stat_to_errno(status);
+static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   struct nfs3_readdirres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_default;
+       error = decode_readdir3resok(xdr, result);
+out:
+       return error;
+out_default:
+       error = decode_post_op_attr(xdr, result->dir_attr);
+       if (unlikely(error))
+               goto out;
+       return nfs_stat_to_errno(status);
+}
 
-       res->verf->verifier[0] = *p++;
-       res->verf->verifier[1] = *p++;
+/*
+ * 3.3.18  FSSTAT3res
+ *
+ *     struct FSSTAT3resok {
+ *             post_op_attr    obj_attributes;
+ *             size3           tbytes;
+ *             size3           fbytes;
+ *             size3           abytes;
+ *             size3           tfiles;
+ *             size3           ffiles;
+ *             size3           afiles;
+ *             uint32          invarsec;
+ *     };
+ *
+ *     struct FSSTAT3resfail {
+ *             post_op_attr    obj_attributes;
+ *     };
+ *
+ *     union FSSTAT3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             FSSTAT3resok    resok;
+ *     default:
+ *             FSSTAT3resfail  resfail;
+ *     };
+ */
+static int decode_fsstat3resok(struct xdr_stream *xdr,
+                              struct nfs_fsstat *result)
+{
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 8 * 6 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       p = xdr_decode_size3(p, &result->tbytes);
+       p = xdr_decode_size3(p, &result->fbytes);
+       p = xdr_decode_size3(p, &result->abytes);
+       p = xdr_decode_size3(p, &result->tfiles);
+       p = xdr_decode_size3(p, &result->ffiles);
+       xdr_decode_size3(p, &result->afiles);
+       /* ignore invarsec */
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs_fsstat *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+       error = decode_fsstat3resok(xdr, result);
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
 }
 
-#ifdef CONFIG_NFS_V3_ACL
 /*
- * Decode GETACL reply
+ * 3.3.19  FSINFO3res
+ *
+ *     struct FSINFO3resok {
+ *             post_op_attr    obj_attributes;
+ *             uint32          rtmax;
+ *             uint32          rtpref;
+ *             uint32          rtmult;
+ *             uint32          wtmax;
+ *             uint32          wtpref;
+ *             uint32          wtmult;
+ *             uint32          dtpref;
+ *             size3           maxfilesize;
+ *             nfstime3        time_delta;
+ *             uint32          properties;
+ *     };
+ *
+ *     struct FSINFO3resfail {
+ *             post_op_attr    obj_attributes;
+ *     };
+ *
+ *     union FSINFO3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             FSINFO3resok    resok;
+ *     default:
+ *             FSINFO3resfail  resfail;
+ *     };
  */
-static int
-nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
-                  struct nfs3_getaclres *res)
+static int decode_fsinfo3resok(struct xdr_stream *xdr,
+                              struct nfs_fsinfo *result)
 {
-       struct xdr_buf *buf = &req->rq_rcv_buf;
-       int status = ntohl(*p++);
-       struct posix_acl **acl;
-       unsigned int *aclcnt;
-       int err, base;
+       __be32 *p;
 
-       if (status != 0)
-               return nfs_stat_to_errno(status);
-       p = xdr_decode_post_op_attr(p, res->fattr);
-       res->mask = ntohl(*p++);
-       if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
-               return -EINVAL;
-       base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base;
+       p = xdr_inline_decode(xdr, 4 * 7 + 8 + 8 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       result->rtmax  = be32_to_cpup(p++);
+       result->rtpref = be32_to_cpup(p++);
+       result->rtmult = be32_to_cpup(p++);
+       result->wtmax  = be32_to_cpup(p++);
+       result->wtpref = be32_to_cpup(p++);
+       result->wtmult = be32_to_cpup(p++);
+       result->dtpref = be32_to_cpup(p++);
+       p = xdr_decode_size3(p, &result->maxfilesize);
+       xdr_decode_nfstime3(p, &result->time_delta);
 
-       acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL;
-       aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL;
-       err = nfsacl_decode(buf, base, aclcnt, acl);
+       /* ignore properties */
+       result->lease_time = 0;
+       return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
 
-       acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL;
-       aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL;
-       if (err > 0)
-               err = nfsacl_decode(buf, base + err, aclcnt, acl);
-       return (err > 0) ? 0 : err;
+static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs_fsinfo *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+       error = decode_fsinfo3resok(xdr, result);
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
 }
 
 /*
- * Decode setacl reply.
+ * 3.3.20  PATHCONF3res
+ *
+ *     struct PATHCONF3resok {
+ *             post_op_attr    obj_attributes;
+ *             uint32          linkmax;
+ *             uint32          name_max;
+ *             bool            no_trunc;
+ *             bool            chown_restricted;
+ *             bool            case_insensitive;
+ *             bool            case_preserving;
+ *     };
+ *
+ *     struct PATHCONF3resfail {
+ *             post_op_attr    obj_attributes;
+ *     };
+ *
+ *     union PATHCONF3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             PATHCONF3resok  resok;
+ *     default:
+ *             PATHCONF3resfail resfail;
+ *     };
  */
-static int
-nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
+static int decode_pathconf3resok(struct xdr_stream *xdr,
+                                struct nfs_pathconf *result)
 {
-       int status = ntohl(*p++);
+       __be32 *p;
 
-       if (status)
-               return nfs_stat_to_errno(status);
-       xdr_decode_post_op_attr(p, fattr);
+       p = xdr_inline_decode(xdr, 4 * 6);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       result->max_link = be32_to_cpup(p++);
+       result->max_namelen = be32_to_cpup(p);
+       /* ignore remaining fields */
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+}
+
+static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    struct nfs_pathconf *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+       error = decode_pathconf3resok(xdr, result);
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
+}
+
+/*
+ * 3.3.21  COMMIT3res
+ *
+ *     struct COMMIT3resok {
+ *             wcc_data        file_wcc;
+ *             writeverf3      verf;
+ *     };
+ *
+ *     struct COMMIT3resfail {
+ *             wcc_data        file_wcc;
+ *     };
+ *
+ *     union COMMIT3res switch (nfsstat3 status) {
+ *     case NFS3_OK:
+ *             COMMIT3resok    resok;
+ *     default:
+ *             COMMIT3resfail  resfail;
+ *     };
+ */
+static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs_writeres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       error = decode_wcc_data(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_status;
+       error = decode_writeverf3(xdr, result->verf->verifier);
+out:
+       return error;
+out_status:
+       return nfs_stat_to_errno(status);
+}
+
+#ifdef CONFIG_NFS_V3_ACL
+
+static inline int decode_getacl3resok(struct xdr_stream *xdr,
+                                     struct nfs3_getaclres *result)
+{
+       struct posix_acl **acl;
+       unsigned int *aclcnt;
+       size_t hdrlen;
+       int error;
+
+       error = decode_post_op_attr(xdr, result->fattr);
+       if (unlikely(error))
+               goto out;
+       error = decode_uint32(xdr, &result->mask);
+       if (unlikely(error))
+               goto out;
+       error = -EINVAL;
+       if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
+               goto out;
+
+       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+
+       acl = NULL;
+       if (result->mask & NFS_ACL)
+               acl = &result->acl_access;
+       aclcnt = NULL;
+       if (result->mask & NFS_ACLCNT)
+               aclcnt = &result->acl_access_count;
+       error = nfsacl_decode(xdr->buf, hdrlen, aclcnt, acl);
+       if (unlikely(error <= 0))
+               goto out;
+
+       acl = NULL;
+       if (result->mask & NFS_DFACL)
+               acl = &result->acl_default;
+       aclcnt = NULL;
+       if (result->mask & NFS_DFACLCNT)
+               aclcnt = &result->acl_default_count;
+       error = nfsacl_decode(xdr->buf, hdrlen + error, aclcnt, acl);
+       if (unlikely(error <= 0))
+               return error;
+       error = 0;
+out:
+       return error;
 }
+
+static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs3_getaclres *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_default;
+       error = decode_getacl3resok(xdr, result);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
+}
+
+static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs_fattr *result)
+{
+       enum nfs_stat status;
+       int error;
+
+       error = decode_nfsstat3(xdr, &status);
+       if (unlikely(error))
+               goto out;
+       if (status != NFS3_OK)
+               goto out_default;
+       error = decode_post_op_attr(xdr, result);
+out:
+       return error;
+out_default:
+       return nfs_stat_to_errno(status);
+}
+
 #endif  /* CONFIG_NFS_V3_ACL */
 
 #define PROC(proc, argtype, restype, timer)                            \
 [NFS3PROC_##proc] = {                                                  \
        .p_proc      = NFS3PROC_##proc,                                 \
-       .p_encode    = (kxdrproc_t) nfs3_xdr_##argtype,                 \
-       .p_decode    = (kxdrproc_t) nfs3_xdr_##restype,                 \
-       .p_arglen    = NFS3_##argtype##_sz,                             \
-       .p_replen    = NFS3_##restype##_sz,                             \
+       .p_encode    = (kxdreproc_t)nfs3_xdr_enc_##argtype##3args,      \
+       .p_decode    = (kxdrdproc_t)nfs3_xdr_dec_##restype##3res,       \
+       .p_arglen    = NFS3_##argtype##args_sz,                         \
+       .p_replen    = NFS3_##restype##res_sz,                          \
        .p_timer     = timer,                                           \
        .p_statidx   = NFS3PROC_##proc,                                 \
        .p_name      = #proc,                                           \
        }
 
 struct rpc_procinfo    nfs3_procedures[] = {
-  PROC(GETATTR,                fhandle,        attrstat, 1),
-  PROC(SETATTR,        sattrargs,      wccstat, 0),
-  PROC(LOOKUP,         diropargs,      lookupres, 2),
-  PROC(ACCESS,         accessargs,     accessres, 1),
-  PROC(READLINK,       readlinkargs,   readlinkres, 3),
-  PROC(READ,           readargs,       readres, 3),
-  PROC(WRITE,          writeargs,      writeres, 4),
-  PROC(CREATE,         createargs,     createres, 0),
-  PROC(MKDIR,          mkdirargs,      createres, 0),
-  PROC(SYMLINK,                symlinkargs,    createres, 0),
-  PROC(MKNOD,          mknodargs,      createres, 0),
-  PROC(REMOVE,         removeargs,     removeres, 0),
-  PROC(RMDIR,          diropargs,      wccstat, 0),
-  PROC(RENAME,         renameargs,     renameres, 0),
-  PROC(LINK,           linkargs,       linkres, 0),
-  PROC(READDIR,                readdirargs,    readdirres, 3),
-  PROC(READDIRPLUS,    readdirargs,    readdirres, 3),
-  PROC(FSSTAT,         fhandle,        fsstatres, 0),
-  PROC(FSINFO,         fhandle,        fsinfores, 0),
-  PROC(PATHCONF,       fhandle,        pathconfres, 0),
-  PROC(COMMIT,         commitargs,     commitres, 5),
+       PROC(GETATTR,           getattr,        getattr,        1),
+       PROC(SETATTR,           setattr,        setattr,        0),
+       PROC(LOOKUP,            lookup,         lookup,         2),
+       PROC(ACCESS,            access,         access,         1),
+       PROC(READLINK,          readlink,       readlink,       3),
+       PROC(READ,              read,           read,           3),
+       PROC(WRITE,             write,          write,          4),
+       PROC(CREATE,            create,         create,         0),
+       PROC(MKDIR,             mkdir,          create,         0),
+       PROC(SYMLINK,           symlink,        create,         0),
+       PROC(MKNOD,             mknod,          create,         0),
+       PROC(REMOVE,            remove,         remove,         0),
+       PROC(RMDIR,             lookup,         setattr,        0),
+       PROC(RENAME,            rename,         rename,         0),
+       PROC(LINK,              link,           link,           0),
+       PROC(READDIR,           readdir,        readdir,        3),
+       PROC(READDIRPLUS,       readdirplus,    readdir,        3),
+       PROC(FSSTAT,            getattr,        fsstat,         0),
+       PROC(FSINFO,            getattr,        fsinfo,         0),
+       PROC(PATHCONF,          getattr,        pathconf,       0),
+       PROC(COMMIT,            commit,         commit,         5),
 };
 
 struct rpc_version             nfs_version3 = {
@@ -1179,8 +2468,8 @@ struct rpc_version                nfs_version3 = {
 static struct rpc_procinfo     nfs3_acl_procedures[] = {
        [ACLPROC3_GETACL] = {
                .p_proc = ACLPROC3_GETACL,
-               .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
-               .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
+               .p_encode = (kxdreproc_t)nfs3_xdr_enc_getacl3args,
+               .p_decode = (kxdrdproc_t)nfs3_xdr_dec_getacl3res,
                .p_arglen = ACL3_getaclargs_sz,
                .p_replen = ACL3_getaclres_sz,
                .p_timer = 1,
@@ -1188,8 +2477,8 @@ static struct rpc_procinfo        nfs3_acl_procedures[] = {
        },
        [ACLPROC3_SETACL] = {
                .p_proc = ACLPROC3_SETACL,
-               .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
-               .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
+               .p_encode = (kxdreproc_t)nfs3_xdr_enc_setacl3args,
+               .p_decode = (kxdrdproc_t)nfs3_xdr_dec_setacl3res,
                .p_arglen = ACL3_setaclargs_sz,
                .p_replen = ACL3_setaclres_sz,
                .p_timer = 0,
index 9fa4963..7a74740 100644 (file)
@@ -44,6 +44,7 @@ enum nfs4_client_state {
        NFS4CLNT_RECLAIM_REBOOT,
        NFS4CLNT_RECLAIM_NOGRACE,
        NFS4CLNT_DELEGRETURN,
+       NFS4CLNT_LAYOUTRECALL,
        NFS4CLNT_SESSION_RESET,
        NFS4CLNT_RECALL_SLOT,
 };
@@ -109,7 +110,7 @@ struct nfs_unique_id {
 struct nfs4_state_owner {
        struct nfs_unique_id so_owner_id;
        struct nfs_server    *so_server;
-       struct rb_node       so_client_node;
+       struct rb_node       so_server_node;
 
        struct rpc_cred      *so_cred;   /* Associated cred */
 
@@ -227,12 +228,6 @@ struct nfs4_state_maintenance_ops {
 extern const struct dentry_operations nfs4_dentry_operations;
 extern const struct inode_operations nfs4_dir_inode_operations;
 
-/* inode.c */
-extern ssize_t nfs4_getxattr(struct dentry *, const char *, void *, size_t);
-extern int nfs4_setxattr(struct dentry *, const char *, const void *, size_t, int);
-extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t);
-
-
 /* nfs4proc.c */
 extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *, struct nfs4_setclientid_res *);
 extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct nfs4_setclientid_res *arg, struct rpc_cred *);
@@ -241,11 +236,12 @@ 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 nfs41_init_clientid(struct nfs_client *, struct rpc_cred *);
-extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait);
+extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc);
 extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
 extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
                struct nfs4_fs_locations *fs_locations, struct page *page);
 extern void nfs4_release_lockowner(const struct nfs4_lock_state *);
+extern const struct xattr_handler *nfs4_xattr_handlers[];
 
 #if defined(CONFIG_NFS_V4_1)
 static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server)
@@ -331,7 +327,6 @@ extern void nfs_free_seqid(struct nfs_seqid *seqid);
 extern const nfs4_stateid zero_stateid;
 
 /* nfs4xdr.c */
-extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int);
 extern struct rpc_procinfo nfs4_procedures[];
 
 struct nfs4_mount_data;
index 2e92f0d..23f930c 100644 (file)
@@ -82,7 +82,7 @@ filelayout_check_layout(struct pnfs_layout_hdr *lo,
 {
        struct nfs4_file_layout_dsaddr *dsaddr;
        int status = -EINVAL;
-       struct nfs_server *nfss = NFS_SERVER(lo->inode);
+       struct nfs_server *nfss = NFS_SERVER(lo->plh_inode);
 
        dprintk("--> %s\n", __func__);
 
@@ -101,7 +101,7 @@ filelayout_check_layout(struct pnfs_layout_hdr *lo,
        /* find and reference the deviceid */
        dsaddr = nfs4_fl_find_get_deviceid(nfss->nfs_client, id);
        if (dsaddr == NULL) {
-               dsaddr = get_device_info(lo->inode, id);
+               dsaddr = get_device_info(lo->plh_inode, id);
                if (dsaddr == NULL)
                        goto out;
        }
@@ -243,7 +243,7 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid,
 static void
 filelayout_free_lseg(struct pnfs_layout_segment *lseg)
 {
-       struct nfs_server *nfss = NFS_SERVER(lseg->layout->inode);
+       struct nfs_server *nfss = NFS_SERVER(lseg->pls_layout->plh_inode);
        struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
 
        dprintk("--> %s\n", __func__);
index 4435e5e..f2b92f6 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/mount.h>
 #include <linux/module.h>
 #include <linux/sunrpc/bc_xprt.h>
+#include <linux/xattr.h>
 
 #include "nfs4_fs.h"
 #include "delegation.h"
@@ -355,9 +356,9 @@ nfs4_free_slot(struct nfs4_slot_table *tbl, struct nfs4_slot *free_slot)
 }
 
 /*
- * Signal state manager thread if session is drained
+ * Signal state manager thread if session fore channel is drained
  */
-static void nfs41_check_drain_session_complete(struct nfs4_session *ses)
+static void nfs4_check_drain_fc_complete(struct nfs4_session *ses)
 {
        struct rpc_task *task;
 
@@ -371,8 +372,20 @@ static void nfs41_check_drain_session_complete(struct nfs4_session *ses)
        if (ses->fc_slot_table.highest_used_slotid != -1)
                return;
 
-       dprintk("%s COMPLETE: Session Drained\n", __func__);
-       complete(&ses->complete);
+       dprintk("%s COMPLETE: Session Fore Channel Drained\n", __func__);
+       complete(&ses->fc_slot_table.complete);
+}
+
+/*
+ * Signal state manager thread if session back channel is drained
+ */
+void nfs4_check_drain_bc_complete(struct nfs4_session *ses)
+{
+       if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state) ||
+           ses->bc_slot_table.highest_used_slotid != -1)
+               return;
+       dprintk("%s COMPLETE: Session Back Channel Drained\n", __func__);
+       complete(&ses->bc_slot_table.complete);
 }
 
 static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
@@ -389,7 +402,7 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
 
        spin_lock(&tbl->slot_tbl_lock);
        nfs4_free_slot(tbl, res->sr_slot);
-       nfs41_check_drain_session_complete(res->sr_session);
+       nfs4_check_drain_fc_complete(res->sr_session);
        spin_unlock(&tbl->slot_tbl_lock);
        res->sr_slot = NULL;
 }
@@ -1826,6 +1839,8 @@ struct nfs4_closedata {
        struct nfs_closeres res;
        struct nfs_fattr fattr;
        unsigned long timestamp;
+       bool roc;
+       u32 roc_barrier;
 };
 
 static void nfs4_free_closedata(void *data)
@@ -1833,6 +1848,8 @@ static void nfs4_free_closedata(void *data)
        struct nfs4_closedata *calldata = data;
        struct nfs4_state_owner *sp = calldata->state->owner;
 
+       if (calldata->roc)
+               pnfs_roc_release(calldata->state->inode);
        nfs4_put_open_state(calldata->state);
        nfs_free_seqid(calldata->arg.seqid);
        nfs4_put_state_owner(sp);
@@ -1865,6 +1882,9 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
         */
        switch (task->tk_status) {
                case 0:
+                       if (calldata->roc)
+                               pnfs_roc_set_barrier(state->inode,
+                                                    calldata->roc_barrier);
                        nfs_set_open_stateid(state, &calldata->res.stateid, 0);
                        renew_lease(server, calldata->timestamp);
                        nfs4_close_clear_stateid_flags(state,
@@ -1917,8 +1937,15 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
                return;
        }
 
-       if (calldata->arg.fmode == 0)
+       if (calldata->arg.fmode == 0) {
                task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
+               if (calldata->roc &&
+                   pnfs_roc_drain(calldata->inode, &calldata->roc_barrier)) {
+                       rpc_sleep_on(&NFS_SERVER(calldata->inode)->roc_rpcwaitq,
+                                    task, NULL);
+                       return;
+               }
+       }
 
        nfs_fattr_init(calldata->res.fattr);
        calldata->timestamp = jiffies;
@@ -1946,7 +1973,7 @@ static const struct rpc_call_ops nfs4_close_ops = {
  *
  * NOTE: Caller must be holding the sp->so_owner semaphore!
  */
-int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait)
+int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
        struct nfs4_closedata *calldata;
@@ -1981,11 +2008,12 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, i
        calldata->res.fattr = &calldata->fattr;
        calldata->res.seqid = calldata->arg.seqid;
        calldata->res.server = server;
+       calldata->roc = roc;
        path_get(path);
        calldata->path = *path;
 
-       msg.rpc_argp = &calldata->arg,
-       msg.rpc_resp = &calldata->res,
+       msg.rpc_argp = &calldata->arg;
+       msg.rpc_resp = &calldata->res;
        task_setup_data.callback_data = calldata;
        task = rpc_run_task(&task_setup_data);
        if (IS_ERR(task))
@@ -1998,6 +2026,8 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, i
 out_free_calldata:
        kfree(calldata);
 out:
+       if (roc)
+               pnfs_roc_release(state->inode);
        nfs4_put_open_state(state);
        nfs4_put_state_owner(sp);
        return status;
@@ -2486,6 +2516,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                path = &ctx->path;
                fmode = ctx->mode;
        }
+       sattr->ia_mode &= ~current_umask();
        state = nfs4_do_open(dir, path, fmode, flags, sattr, cred);
        d_drop(dentry);
        if (IS_ERR(state)) {
@@ -2816,6 +2847,8 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
 {
        struct nfs4_exception exception = { };
        int err;
+
+       sattr->ia_mode &= ~current_umask();
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
                                _nfs4_proc_mkdir(dir, dentry, sattr),
@@ -2916,6 +2949,8 @@ static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
 {
        struct nfs4_exception exception = { };
        int err;
+
+       sattr->ia_mode &= ~current_umask();
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
                                _nfs4_proc_mknod(dir, dentry, sattr, rdev),
@@ -3478,6 +3513,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
        struct nfs4_setclientid setclientid = {
                .sc_verifier = &sc_verifier,
                .sc_prog = program,
+               .sc_cb_ident = clp->cl_cb_ident,
        };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID],
@@ -3517,7 +3553,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
                if (signalled())
                        break;
                if (loop++ & 1)
-                       ssleep(clp->cl_lease_time + 1);
+                       ssleep(clp->cl_lease_time / HZ + 1);
                else
                        if (++clp->cl_id_uniquifier == 0)
                                break;
@@ -3663,8 +3699,8 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co
        data->rpc_status = 0;
 
        task_setup_data.callback_data = data;
-       msg.rpc_argp = &data->args,
-       msg.rpc_resp = &data->res,
+       msg.rpc_argp = &data->args;
+       msg.rpc_resp = &data->res;
        task = rpc_run_task(&task_setup_data);
        if (IS_ERR(task))
                return PTR_ERR(task);
@@ -3743,6 +3779,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;
+       arg.lock_owner.s_dev = server->s_dev;
        status = nfs4_call_sync(server, &msg, &arg, &res, 1);
        switch (status) {
                case 0:
@@ -3908,8 +3945,8 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
                return ERR_PTR(-ENOMEM);
        }
 
-       msg.rpc_argp = &data->arg,
-       msg.rpc_resp = &data->res,
+       msg.rpc_argp = &data->arg;
+       msg.rpc_resp = &data->res;
        task_setup_data.callback_data = data;
        return rpc_run_task(&task_setup_data);
 }
@@ -3988,6 +4025,7 @@ static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl,
        p->arg.lock_stateid = &lsp->ls_stateid;
        p->arg.lock_owner.clientid = server->nfs_client->cl_clientid;
        p->arg.lock_owner.id = lsp->ls_id.id;
+       p->arg.lock_owner.s_dev = server->s_dev;
        p->res.lock_seqid = p->arg.lock_seqid;
        p->lsp = lsp;
        p->server = server;
@@ -4145,8 +4183,8 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
                        data->arg.reclaim = NFS_LOCK_RECLAIM;
                task_setup_data.callback_ops = &nfs4_recover_lock_ops;
        }
-       msg.rpc_argp = &data->arg,
-       msg.rpc_resp = &data->res,
+       msg.rpc_argp = &data->arg;
+       msg.rpc_resp = &data->res;
        task_setup_data.callback_data = data;
        task = rpc_run_task(&task_setup_data);
        if (IS_ERR(task))
@@ -4392,48 +4430,43 @@ void nfs4_release_lockowner(const struct nfs4_lock_state *lsp)
                return;
        args->lock_owner.clientid = server->nfs_client->cl_clientid;
        args->lock_owner.id = lsp->ls_id.id;
+       args->lock_owner.s_dev = server->s_dev;
        msg.rpc_argp = args;
        rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, args);
 }
 
 #define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
 
-int nfs4_setxattr(struct dentry *dentry, const char *key, const void *buf,
-               size_t buflen, int flags)
+static int nfs4_xattr_set_nfs4_acl(struct dentry *dentry, const char *key,
+                                  const void *buf, size_t buflen,
+                                  int flags, int type)
 {
-       struct inode *inode = dentry->d_inode;
-
-       if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0)
-               return -EOPNOTSUPP;
+       if (strcmp(key, "") != 0)
+               return -EINVAL;
 
-       return nfs4_proc_set_acl(inode, buf, buflen);
+       return nfs4_proc_set_acl(dentry->d_inode, buf, buflen);
 }
 
-/* The getxattr man page suggests returning -ENODATA for unknown attributes,
- * and that's what we'll do for e.g. user attributes that haven't been set.
- * But we'll follow ext2/ext3's lead by returning -EOPNOTSUPP for unsupported
- * attributes in kernel-managed attribute namespaces. */
-ssize_t nfs4_getxattr(struct dentry *dentry, const char *key, void *buf,
-               size_t buflen)
+static int nfs4_xattr_get_nfs4_acl(struct dentry *dentry, const char *key,
+                                  void *buf, size_t buflen, int type)
 {
-       struct inode *inode = dentry->d_inode;
-
-       if (strcmp(key, XATTR_NAME_NFSV4_ACL) != 0)
-               return -EOPNOTSUPP;
+       if (strcmp(key, "") != 0)
+               return -EINVAL;
 
-       return nfs4_proc_get_acl(inode, buf, buflen);
+       return nfs4_proc_get_acl(dentry->d_inode, buf, buflen);
 }
 
-ssize_t nfs4_listxattr(struct dentry *dentry, char *buf, size_t buflen)
+static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
+                                      size_t list_len, const char *name,
+                                      size_t name_len, int type)
 {
-       size_t len = strlen(XATTR_NAME_NFSV4_ACL) + 1;
+       size_t len = sizeof(XATTR_NAME_NFSV4_ACL);
 
        if (!nfs4_server_supports_acls(NFS_SERVER(dentry->d_inode)))
                return 0;
-       if (buf && buflen < len)
-               return -ERANGE;
-       if (buf)
-               memcpy(buf, XATTR_NAME_NFSV4_ACL, len);
+
+       if (list && len <= list_len)
+               memcpy(list, XATTR_NAME_NFSV4_ACL, len);
        return len;
 }
 
@@ -4776,17 +4809,17 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
        if (!session)
                return NULL;
 
-       init_completion(&session->complete);
-
        tbl = &session->fc_slot_table;
        tbl->highest_used_slotid = -1;
        spin_lock_init(&tbl->slot_tbl_lock);
        rpc_init_priority_wait_queue(&tbl->slot_tbl_waitq, "ForeChannel Slot table");
+       init_completion(&tbl->complete);
 
        tbl = &session->bc_slot_table;
        tbl->highest_used_slotid = -1;
        spin_lock_init(&tbl->slot_tbl_lock);
        rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table");
+       init_completion(&tbl->complete);
 
        session->session_state = 1<<NFS4_SESSION_INITING;
 
@@ -5280,13 +5313,23 @@ static void
 nfs4_layoutget_prepare(struct rpc_task *task, void *calldata)
 {
        struct nfs4_layoutget *lgp = calldata;
-       struct inode *ino = lgp->args.inode;
-       struct nfs_server *server = NFS_SERVER(ino);
+       struct nfs_server *server = NFS_SERVER(lgp->args.inode);
 
        dprintk("--> %s\n", __func__);
+       /* Note the is a race here, where a CB_LAYOUTRECALL can come in
+        * right now covering the LAYOUTGET we are about to send.
+        * However, that is not so catastrophic, and there seems
+        * to be no way to prevent it completely.
+        */
        if (nfs4_setup_sequence(server, &lgp->args.seq_args,
                                &lgp->res.seq_res, 0, task))
                return;
+       if (pnfs_choose_layoutget_stateid(&lgp->args.stateid,
+                                         NFS_I(lgp->args.inode)->layout,
+                                         lgp->args.ctx->state)) {
+               rpc_exit(task, NFS4_OK);
+               return;
+       }
        rpc_call_start(task);
 }
 
@@ -5313,7 +5356,6 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
                        return;
                }
        }
-       lgp->status = task->tk_status;
        dprintk("<-- %s\n", __func__);
 }
 
@@ -5322,7 +5364,6 @@ static void nfs4_layoutget_release(void *calldata)
        struct nfs4_layoutget *lgp = calldata;
 
        dprintk("--> %s\n", __func__);
-       put_layout_hdr(lgp->args.inode);
        if (lgp->res.layout.buf != NULL)
                free_page((unsigned long) lgp->res.layout.buf);
        put_nfs_open_context(lgp->args.ctx);
@@ -5367,13 +5408,10 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
        if (IS_ERR(task))
                return PTR_ERR(task);
        status = nfs4_wait_for_completion_rpc_task(task);
-       if (status != 0)
-               goto out;
-       status = lgp->status;
-       if (status != 0)
-               goto out;
-       status = pnfs_layout_process(lgp);
-out:
+       if (status == 0)
+               status = task->tk_status;
+       if (status == 0)
+               status = pnfs_layout_process(lgp);
        rpc_put_task(task);
        dprintk("<-- %s status=%d\n", __func__, status);
        return status;
@@ -5504,9 +5542,10 @@ static const struct inode_operations nfs4_file_inode_operations = {
        .permission     = nfs_permission,
        .getattr        = nfs_getattr,
        .setattr        = nfs_setattr,
-       .getxattr       = nfs4_getxattr,
-       .setxattr       = nfs4_setxattr,
-       .listxattr      = nfs4_listxattr,
+       .getxattr       = generic_getxattr,
+       .setxattr       = generic_setxattr,
+       .listxattr      = generic_listxattr,
+       .removexattr    = generic_removexattr,
 };
 
 const struct nfs_rpc_ops nfs_v4_clientops = {
@@ -5551,6 +5590,18 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .open_context   = nfs4_atomic_open,
 };
 
+static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
+       .prefix = XATTR_NAME_NFSV4_ACL,
+       .list   = nfs4_xattr_list_nfs4_acl,
+       .get    = nfs4_xattr_get_nfs4_acl,
+       .set    = nfs4_xattr_set_nfs4_acl,
+};
+
+const struct xattr_handler *nfs4_xattr_handlers[] = {
+       &nfs4_xattr_nfs4_acl_handler,
+       NULL
+};
+
 /*
  * Local variables:
  *  c-basic-offset: 8
index 72b6c58..402143d 100644 (file)
@@ -63,9 +63,14 @@ nfs4_renew_state(struct work_struct *work)
 
        ops = clp->cl_mvops->state_renewal_ops;
        dprintk("%s: start\n", __func__);
-       /* Are there any active superblocks? */
-       if (list_empty(&clp->cl_superblocks))
+
+       rcu_read_lock();
+       if (list_empty(&clp->cl_superblocks)) {
+               rcu_read_unlock();
                goto out;
+       }
+       rcu_read_unlock();
+
        spin_lock(&clp->cl_lock);
        lease = clp->cl_lease_time;
        last = clp->cl_last_renewal;
@@ -75,7 +80,7 @@ nfs4_renew_state(struct work_struct *work)
                cred = ops->get_state_renewal_cred_locked(clp);
                spin_unlock(&clp->cl_lock);
                if (cred == NULL) {
-                       if (list_empty(&clp->cl_delegations)) {
+                       if (!nfs_delegations_present(clp)) {
                                set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
                                goto out;
                        }
index f575a31..2336d53 100644 (file)
@@ -105,14 +105,17 @@ static void nfs4_clear_machine_cred(struct nfs_client *clp)
                put_rpccred(cred);
 }
 
-struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
+static struct rpc_cred *
+nfs4_get_renew_cred_server_locked(struct nfs_server *server)
 {
+       struct rpc_cred *cred = NULL;
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
-       struct rpc_cred *cred = NULL;
 
-       for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
-               sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
+       for (pos = rb_first(&server->state_owners);
+            pos != NULL;
+            pos = rb_next(pos)) {
+               sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
                if (list_empty(&sp->so_states))
                        continue;
                cred = get_rpccred(sp->so_cred);
@@ -121,6 +124,28 @@ struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
        return cred;
 }
 
+/**
+ * nfs4_get_renew_cred_locked - Acquire credential for a renew operation
+ * @clp: client state handle
+ *
+ * Returns an rpc_cred with reference count bumped, or NULL.
+ * Caller must hold clp->cl_lock.
+ */
+struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
+{
+       struct rpc_cred *cred = NULL;
+       struct nfs_server *server;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               cred = nfs4_get_renew_cred_server_locked(server);
+               if (cred != NULL)
+                       break;
+       }
+       rcu_read_unlock();
+       return cred;
+}
+
 #if defined(CONFIG_NFS_V4_1)
 
 static int nfs41_setup_state_renewal(struct nfs_client *clp)
@@ -142,6 +167,11 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp)
        return status;
 }
 
+/*
+ * Back channel returns NFS4ERR_DELAY for new requests when
+ * NFS4_SESSION_DRAINING is set so there is no work to be done when draining
+ * is ended.
+ */
 static void nfs4_end_drain_session(struct nfs_client *clp)
 {
        struct nfs4_session *ses = clp->cl_session;
@@ -165,22 +195,32 @@ static void nfs4_end_drain_session(struct nfs_client *clp)
        }
 }
 
-static int nfs4_begin_drain_session(struct nfs_client *clp)
+static int nfs4_wait_on_slot_tbl(struct nfs4_slot_table *tbl)
 {
-       struct nfs4_session *ses = clp->cl_session;
-       struct nfs4_slot_table *tbl = &ses->fc_slot_table;
-
        spin_lock(&tbl->slot_tbl_lock);
-       set_bit(NFS4_SESSION_DRAINING, &ses->session_state);
        if (tbl->highest_used_slotid != -1) {
-               INIT_COMPLETION(ses->complete);
+               INIT_COMPLETION(tbl->complete);
                spin_unlock(&tbl->slot_tbl_lock);
-               return wait_for_completion_interruptible(&ses->complete);
+               return wait_for_completion_interruptible(&tbl->complete);
        }
        spin_unlock(&tbl->slot_tbl_lock);
        return 0;
 }
 
+static int nfs4_begin_drain_session(struct nfs_client *clp)
+{
+       struct nfs4_session *ses = clp->cl_session;
+       int ret = 0;
+
+       set_bit(NFS4_SESSION_DRAINING, &ses->session_state);
+       /* back channel */
+       ret = nfs4_wait_on_slot_tbl(&ses->bc_slot_table);
+       if (ret)
+               return ret;
+       /* fore channel */
+       return nfs4_wait_on_slot_tbl(&ses->fc_slot_table);
+}
+
 int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
 {
        int status;
@@ -192,6 +232,12 @@ int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
        status = nfs4_proc_create_session(clp);
        if (status != 0)
                goto out;
+       status = nfs4_set_callback_sessionid(clp);
+       if (status != 0) {
+               printk(KERN_WARNING "Sessionid not set. No callback service\n");
+               nfs_callback_down(1);
+               status = 0;
+       }
        nfs41_setup_state_renewal(clp);
        nfs_mark_client_ready(clp, NFS_CS_READY);
 out:
@@ -210,28 +256,56 @@ struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp)
 
 #endif /* CONFIG_NFS_V4_1 */
 
-struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
+static struct rpc_cred *
+nfs4_get_setclientid_cred_server(struct nfs_server *server)
 {
+       struct nfs_client *clp = server->nfs_client;
+       struct rpc_cred *cred = NULL;
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
+
+       spin_lock(&clp->cl_lock);
+       pos = rb_first(&server->state_owners);
+       if (pos != NULL) {
+               sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
+               cred = get_rpccred(sp->so_cred);
+       }
+       spin_unlock(&clp->cl_lock);
+       return cred;
+}
+
+/**
+ * nfs4_get_setclientid_cred - Acquire credential for a setclientid operation
+ * @clp: client state handle
+ *
+ * Returns an rpc_cred with reference count bumped, or NULL.
+ */
+struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
+{
+       struct nfs_server *server;
        struct rpc_cred *cred;
 
        spin_lock(&clp->cl_lock);
        cred = nfs4_get_machine_cred_locked(clp);
+       spin_unlock(&clp->cl_lock);
        if (cred != NULL)
                goto out;
-       pos = rb_first(&clp->cl_state_owners);
-       if (pos != NULL) {
-               sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
-               cred = get_rpccred(sp->so_cred);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               cred = nfs4_get_setclientid_cred_server(server);
+               if (cred != NULL)
+                       break;
        }
+       rcu_read_unlock();
+
 out:
-       spin_unlock(&clp->cl_lock);
        return cred;
 }
 
-static void nfs_alloc_unique_id(struct rb_root *root, struct nfs_unique_id *new,
-               __u64 minval, int maxbits)
+static void nfs_alloc_unique_id_locked(struct rb_root *root,
+                                      struct nfs_unique_id *new,
+                                      __u64 minval, int maxbits)
 {
        struct rb_node **p, *parent;
        struct nfs_unique_id *pos;
@@ -286,16 +360,15 @@ static void nfs_free_unique_id(struct rb_root *root, struct nfs_unique_id *id)
 }
 
 static struct nfs4_state_owner *
-nfs4_find_state_owner(struct nfs_server *server, struct rpc_cred *cred)
+nfs4_find_state_owner_locked(struct nfs_server *server, struct rpc_cred *cred)
 {
-       struct nfs_client *clp = server->nfs_client;
-       struct rb_node **p = &clp->cl_state_owners.rb_node,
+       struct rb_node **p = &server->state_owners.rb_node,
                       *parent = NULL;
        struct nfs4_state_owner *sp, *res = NULL;
 
        while (*p != NULL) {
                parent = *p;
-               sp = rb_entry(parent, struct nfs4_state_owner, so_client_node);
+               sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
 
                if (server < sp->so_server) {
                        p = &parent->rb_left;
@@ -319,24 +392,17 @@ nfs4_find_state_owner(struct nfs_server *server, struct rpc_cred *cred)
 }
 
 static struct nfs4_state_owner *
-nfs4_insert_state_owner(struct nfs_client *clp, struct nfs4_state_owner *new)
+nfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
 {
-       struct rb_node **p = &clp->cl_state_owners.rb_node,
+       struct nfs_server *server = new->so_server;
+       struct rb_node **p = &server->state_owners.rb_node,
                       *parent = NULL;
        struct nfs4_state_owner *sp;
 
        while (*p != NULL) {
                parent = *p;
-               sp = rb_entry(parent, struct nfs4_state_owner, so_client_node);
+               sp = rb_entry(parent, struct nfs4_state_owner, so_server_node);
 
-               if (new->so_server < sp->so_server) {
-                       p = &parent->rb_left;
-                       continue;
-               }
-               if (new->so_server > sp->so_server) {
-                       p = &parent->rb_right;
-                       continue;
-               }
                if (new->so_cred < sp->so_cred)
                        p = &parent->rb_left;
                else if (new->so_cred > sp->so_cred)
@@ -346,18 +412,21 @@ nfs4_insert_state_owner(struct nfs_client *clp, struct nfs4_state_owner *new)
                        return sp;
                }
        }
-       nfs_alloc_unique_id(&clp->cl_openowner_id, &new->so_owner_id, 1, 64);
-       rb_link_node(&new->so_client_node, parent, p);
-       rb_insert_color(&new->so_client_node, &clp->cl_state_owners);
+       nfs_alloc_unique_id_locked(&server->openowner_id,
+                                       &new->so_owner_id, 1, 64);
+       rb_link_node(&new->so_server_node, parent, p);
+       rb_insert_color(&new->so_server_node, &server->state_owners);
        return new;
 }
 
 static void
-nfs4_remove_state_owner(struct nfs_client *clp, struct nfs4_state_owner *sp)
+nfs4_remove_state_owner_locked(struct nfs4_state_owner *sp)
 {
-       if (!RB_EMPTY_NODE(&sp->so_client_node))
-               rb_erase(&sp->so_client_node, &clp->cl_state_owners);
-       nfs_free_unique_id(&clp->cl_openowner_id, &sp->so_owner_id);
+       struct nfs_server *server = sp->so_server;
+
+       if (!RB_EMPTY_NODE(&sp->so_server_node))
+               rb_erase(&sp->so_server_node, &server->state_owners);
+       nfs_free_unique_id(&server->openowner_id, &sp->so_owner_id);
 }
 
 /*
@@ -386,23 +455,32 @@ nfs4_alloc_state_owner(void)
 static void
 nfs4_drop_state_owner(struct nfs4_state_owner *sp)
 {
-       if (!RB_EMPTY_NODE(&sp->so_client_node)) {
-               struct nfs_client *clp = sp->so_server->nfs_client;
+       if (!RB_EMPTY_NODE(&sp->so_server_node)) {
+               struct nfs_server *server = sp->so_server;
+               struct nfs_client *clp = server->nfs_client;
 
                spin_lock(&clp->cl_lock);
-               rb_erase(&sp->so_client_node, &clp->cl_state_owners);
-               RB_CLEAR_NODE(&sp->so_client_node);
+               rb_erase(&sp->so_server_node, &server->state_owners);
+               RB_CLEAR_NODE(&sp->so_server_node);
                spin_unlock(&clp->cl_lock);
        }
 }
 
-struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
+/**
+ * nfs4_get_state_owner - Look up a state owner given a credential
+ * @server: nfs_server to search
+ * @cred: RPC credential to match
+ *
+ * Returns a pointer to an instantiated nfs4_state_owner struct, or NULL.
+ */
+struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
+                                             struct rpc_cred *cred)
 {
        struct nfs_client *clp = server->nfs_client;
        struct nfs4_state_owner *sp, *new;
 
        spin_lock(&clp->cl_lock);
-       sp = nfs4_find_state_owner(server, cred);
+       sp = nfs4_find_state_owner_locked(server, cred);
        spin_unlock(&clp->cl_lock);
        if (sp != NULL)
                return sp;
@@ -412,7 +490,7 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct
        new->so_server = server;
        new->so_cred = cred;
        spin_lock(&clp->cl_lock);
-       sp = nfs4_insert_state_owner(clp, new);
+       sp = nfs4_insert_state_owner_locked(new);
        spin_unlock(&clp->cl_lock);
        if (sp == new)
                get_rpccred(cred);
@@ -423,6 +501,11 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct
        return sp;
 }
 
+/**
+ * nfs4_put_state_owner - Release a nfs4_state_owner
+ * @sp: state owner data to release
+ *
+ */
 void nfs4_put_state_owner(struct nfs4_state_owner *sp)
 {
        struct nfs_client *clp = sp->so_server->nfs_client;
@@ -430,7 +513,7 @@ void nfs4_put_state_owner(struct nfs4_state_owner *sp)
 
        if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock))
                return;
-       nfs4_remove_state_owner(clp, sp);
+       nfs4_remove_state_owner_locked(sp);
        spin_unlock(&clp->cl_lock);
        rpc_destroy_wait_queue(&sp->so_sequence.wait);
        put_rpccred(cred);
@@ -585,8 +668,11 @@ static void __nfs4_close(struct path *path, struct nfs4_state *state,
        if (!call_close) {
                nfs4_put_open_state(state);
                nfs4_put_state_owner(owner);
-       } else
-               nfs4_do_close(path, state, gfp_mask, wait);
+       } else {
+               bool roc = pnfs_roc(state->inode);
+
+               nfs4_do_close(path, state, gfp_mask, wait, roc);
+       }
 }
 
 void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode)
@@ -633,7 +719,8 @@ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_p
 static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_pid, unsigned int type)
 {
        struct nfs4_lock_state *lsp;
-       struct nfs_client *clp = state->owner->so_server->nfs_client;
+       struct nfs_server *server = state->owner->so_server;
+       struct nfs_client *clp = server->nfs_client;
 
        lsp = kzalloc(sizeof(*lsp), GFP_NOFS);
        if (lsp == NULL)
@@ -657,7 +744,7 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
                return NULL;
        }
        spin_lock(&clp->cl_lock);
-       nfs_alloc_unique_id(&clp->cl_lockowner_id, &lsp->ls_id, 1, 64);
+       nfs_alloc_unique_id_locked(&server->lockowner_id, &lsp->ls_id, 1, 64);
        spin_unlock(&clp->cl_lock);
        INIT_LIST_HEAD(&lsp->ls_locks);
        return lsp;
@@ -665,10 +752,11 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f
 
 static void nfs4_free_lock_state(struct nfs4_lock_state *lsp)
 {
-       struct nfs_client *clp = lsp->ls_state->owner->so_server->nfs_client;
+       struct nfs_server *server = lsp->ls_state->owner->so_server;
+       struct nfs_client *clp = server->nfs_client;
 
        spin_lock(&clp->cl_lock);
-       nfs_free_unique_id(&clp->cl_lockowner_id, &lsp->ls_id);
+       nfs_free_unique_id(&server->lockowner_id, &lsp->ls_id);
        spin_unlock(&clp->cl_lock);
        rpc_destroy_wait_queue(&lsp->ls_sequence.wait);
        kfree(lsp);
@@ -1114,15 +1202,19 @@ static void nfs4_clear_open_state(struct nfs4_state *state)
        }
 }
 
-static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
+static void nfs4_reset_seqids(struct nfs_server *server,
+       int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
 {
+       struct nfs_client *clp = server->nfs_client;
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
        struct nfs4_state *state;
 
-       /* Reset all sequence ids to zero */
-       for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
-               sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
+       spin_lock(&clp->cl_lock);
+       for (pos = rb_first(&server->state_owners);
+            pos != NULL;
+            pos = rb_next(pos)) {
+               sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
                sp->so_seqid.flags = 0;
                spin_lock(&sp->so_lock);
                list_for_each_entry(state, &sp->so_states, open_states) {
@@ -1131,6 +1223,18 @@ static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, int (*mark_re
                }
                spin_unlock(&sp->so_lock);
        }
+       spin_unlock(&clp->cl_lock);
+}
+
+static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp,
+       int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
+{
+       struct nfs_server *server;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs4_reset_seqids(server, mark_reclaim);
+       rcu_read_unlock();
 }
 
 static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
@@ -1148,25 +1252,41 @@ static void nfs4_reclaim_complete(struct nfs_client *clp,
                (void)ops->reclaim_complete(clp);
 }
 
-static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp)
+static void nfs4_clear_reclaim_server(struct nfs_server *server)
 {
+       struct nfs_client *clp = server->nfs_client;
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
        struct nfs4_state *state;
 
-       if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
-               return 0;
-
-       for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
-               sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
+       spin_lock(&clp->cl_lock);
+       for (pos = rb_first(&server->state_owners);
+            pos != NULL;
+            pos = rb_next(pos)) {
+               sp = rb_entry(pos, struct nfs4_state_owner, so_server_node);
                spin_lock(&sp->so_lock);
                list_for_each_entry(state, &sp->so_states, open_states) {
-                       if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags))
+                       if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT,
+                                               &state->flags))
                                continue;
                        nfs4_state_mark_reclaim_nograce(clp, state);
                }
                spin_unlock(&sp->so_lock);
        }
+       spin_unlock(&clp->cl_lock);
+}
+
+static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp)
+{
+       struct nfs_server *server;
+
+       if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
+               return 0;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs4_clear_reclaim_server(server);
+       rcu_read_unlock();
 
        nfs_delegation_reap_unclaimed(clp);
        return 1;
@@ -1238,27 +1358,40 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
 
 static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
 {
+       struct nfs4_state_owner *sp;
+       struct nfs_server *server;
        struct rb_node *pos;
        int status = 0;
 
 restart:
-       spin_lock(&clp->cl_lock);
-       for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
-               struct nfs4_state_owner *sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
-               if (!test_and_clear_bit(ops->owner_flag_bit, &sp->so_flags))
-                       continue;
-               atomic_inc(&sp->so_count);
-               spin_unlock(&clp->cl_lock);
-               status = nfs4_reclaim_open_state(sp, ops);
-               if (status < 0) {
-                       set_bit(ops->owner_flag_bit, &sp->so_flags);
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               spin_lock(&clp->cl_lock);
+               for (pos = rb_first(&server->state_owners);
+                    pos != NULL;
+                    pos = rb_next(pos)) {
+                       sp = rb_entry(pos,
+                               struct nfs4_state_owner, so_server_node);
+                       if (!test_and_clear_bit(ops->owner_flag_bit,
+                                                       &sp->so_flags))
+                               continue;
+                       atomic_inc(&sp->so_count);
+                       spin_unlock(&clp->cl_lock);
+                       rcu_read_unlock();
+
+                       status = nfs4_reclaim_open_state(sp, ops);
+                       if (status < 0) {
+                               set_bit(ops->owner_flag_bit, &sp->so_flags);
+                               nfs4_put_state_owner(sp);
+                               return nfs4_recovery_handle_error(clp, status);
+                       }
+
                        nfs4_put_state_owner(sp);
-                       return nfs4_recovery_handle_error(clp, status);
+                       goto restart;
                }
-               nfs4_put_state_owner(sp);
-               goto restart;
+               spin_unlock(&clp->cl_lock);
        }
-       spin_unlock(&clp->cl_lock);
+       rcu_read_unlock();
        return status;
 }
 
index 0662a98..2ab8e5c 100644 (file)
@@ -71,8 +71,8 @@ static int nfs4_stat_to_errno(int);
 /* lock,open owner id:
  * we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT  >> 2)
  */
-#define open_owner_id_maxsz    (1 + 4)
-#define lock_owner_id_maxsz    (1 + 4)
+#define open_owner_id_maxsz    (1 + 1 + 4)
+#define lock_owner_id_maxsz    (1 + 1 + 4)
 #define decode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
 #define compound_encode_hdr_maxsz      (3 + (NFS4_MAXTAGLEN >> 2))
 #define compound_decode_hdr_maxsz      (3 + (NFS4_MAXTAGLEN >> 2))
@@ -1088,10 +1088,11 @@ static void encode_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lo
 {
        __be32 *p;
 
-       p = reserve_space(xdr, 28);
+       p = reserve_space(xdr, 32);
        p = xdr_encode_hyper(p, lowner->clientid);
-       *p++ = cpu_to_be32(16);
+       *p++ = cpu_to_be32(20);
        p = xdr_encode_opaque_fixed(p, "lock id:", 8);
+       *p++ = cpu_to_be32(lowner->s_dev);
        xdr_encode_hyper(p, lowner->id);
 }
 
@@ -1210,10 +1211,11 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
        *p++ = cpu_to_be32(OP_OPEN);
        *p = cpu_to_be32(arg->seqid->sequence->counter);
        encode_share_access(xdr, arg->fmode);
-       p = reserve_space(xdr, 28);
+       p = reserve_space(xdr, 32);
        p = xdr_encode_hyper(p, arg->clientid);
-       *p++ = cpu_to_be32(16);
+       *p++ = cpu_to_be32(20);
        p = xdr_encode_opaque_fixed(p, "open id:", 8);
+       *p++ = cpu_to_be32(arg->server->s_dev);
        xdr_encode_hyper(p, arg->id);
 }
 
@@ -1510,7 +1512,7 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
        hdr->replen += decode_restorefh_maxsz;
 }
 
-static int
+static void
 encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr)
 {
        __be32 *p;
@@ -1521,14 +1523,12 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
        p = reserve_space(xdr, 2*4);
        *p++ = cpu_to_be32(1);
        *p = cpu_to_be32(FATTR4_WORD0_ACL);
-       if (arg->acl_len % 4)
-               return -EINVAL;
+       BUG_ON(arg->acl_len % 4);
        p = reserve_space(xdr, 4);
        *p = cpu_to_be32(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;
 }
 
 static void
@@ -1789,7 +1789,6 @@ encode_layoutget(struct xdr_stream *xdr,
                      const struct nfs4_layoutget_args *args,
                      struct compound_hdr *hdr)
 {
-       nfs4_stateid stateid;
        __be32 *p;
 
        p = reserve_space(xdr, 44 + NFS4_STATEID_SIZE);
@@ -1800,9 +1799,7 @@ encode_layoutget(struct xdr_stream *xdr,
        p = xdr_encode_hyper(p, args->range.offset);
        p = xdr_encode_hyper(p, args->range.length);
        p = xdr_encode_hyper(p, args->minlength);
-       pnfs_get_layout_stateid(&stateid, NFS_I(args->inode)->layout,
-                               args->ctx->state);
-       p = xdr_encode_opaque_fixed(p, &stateid.data, NFS4_STATEID_SIZE);
+       p = xdr_encode_opaque_fixed(p, &args->stateid.data, NFS4_STATEID_SIZE);
        *p = cpu_to_be32(args->maxcount);
 
        dprintk("%s: 1st type:0x%x iomode:%d off:%lu len:%lu mc:%d\n",
@@ -1833,393 +1830,362 @@ static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args)
 /*
  * Encode an ACCESS request
  */
-static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs4_accessargs *args)
+static void nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               const struct nfs4_accessargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode LOOKUP request
  */
-static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs4_lookup_arg *args)
+static void nfs4_xdr_enc_lookup(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               const struct nfs4_lookup_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
-       encode_getfattr(&xdr, args->bitmask, &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);
+       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode LOOKUP_ROOT request
  */
-static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struct nfs4_lookup_root_arg *args)
+static void nfs4_xdr_enc_lookup_root(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs4_lookup_root_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode REMOVE request
  */
-static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
+static void nfs4_xdr_enc_remove(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               const struct nfs_removeargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode RENAME request
  */
-static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs_renameargs *args)
+static void nfs4_xdr_enc_rename(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               const struct nfs_renameargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
-       encode_rename(&xdr, args->old_name, args->new_name, &hdr);
-       encode_getfattr(&xdr, args->bitmask, &hdr);
-       encode_restorefh(&xdr, &hdr);
-       encode_getfattr(&xdr, args->bitmask, &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);
+       encode_rename(xdr, args->old_name, args->new_name, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
+       encode_restorefh(xdr, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode LINK request
  */
-static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_link_arg *args)
+static void nfs4_xdr_enc_link(struct rpc_rqst *req, struct xdr_stream *xdr,
+                            const struct nfs4_link_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
-       encode_link(&xdr, args->name, &hdr);
-       encode_getfattr(&xdr, args->bitmask, &hdr);
-       encode_restorefh(&xdr, &hdr);
-       encode_getfattr(&xdr, args->bitmask, &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);
+       encode_link(xdr, args->name, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
+       encode_restorefh(xdr, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode CREATE request
  */
-static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs4_create_arg *args)
+static void nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               const struct nfs4_create_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
-       encode_getfh(&xdr, &hdr);
-       encode_getfattr(&xdr, args->bitmask, &hdr);
-       encode_restorefh(&xdr, &hdr);
-       encode_getfattr(&xdr, args->bitmask, &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);
+       encode_getfh(xdr, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
+       encode_restorefh(xdr, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode SYMLINK request
  */
-static int nfs4_xdr_enc_symlink(struct rpc_rqst *req, __be32 *p, const struct nfs4_create_arg *args)
+static void nfs4_xdr_enc_symlink(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                const struct nfs4_create_arg *args)
 {
-       return nfs4_xdr_enc_create(req, p, args);
+       nfs4_xdr_enc_create(req, xdr, args);
 }
 
 /*
  * Encode GETATTR request
  */
-static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nfs4_getattr_arg *args)
+static void nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                const struct nfs4_getattr_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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_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);
-       return 0;
 }
 
 /*
  * Encode a CLOSE request
  */
-static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closeargs *args)
+static void nfs4_xdr_enc_close(struct rpc_rqst *req, struct xdr_stream *xdr,
+                              struct nfs_closeargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode an OPEN request
  */
-static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openargs *args)
+static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr,
+                             struct nfs_openargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
-       encode_getfh(&xdr, &hdr);
-       encode_getfattr(&xdr, args->bitmask, &hdr);
-       encode_restorefh(&xdr, &hdr);
-       encode_getfattr(&xdr, args->bitmask, &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);
+       encode_getfh(xdr, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
+       encode_restorefh(xdr, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode an OPEN_CONFIRM request
  */
-static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, __be32 *p, struct nfs_open_confirmargs *args)
+static void nfs4_xdr_enc_open_confirm(struct rpc_rqst *req,
+                                     struct xdr_stream *xdr,
+                                     struct nfs_open_confirmargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .nops   = 0,
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_putfh(&xdr, args->fh, &hdr);
-       encode_open_confirm(&xdr, args, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_putfh(xdr, args->fh, &hdr);
+       encode_open_confirm(xdr, args, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode an OPEN request with no attributes.
  */
-static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_openargs *args)
+static void nfs4_xdr_enc_open_noattr(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    struct nfs_openargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode an OPEN_DOWNGRADE request
  */
-static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct nfs_closeargs *args)
+static void nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req,
+                                       struct xdr_stream *xdr,
+                                       struct nfs_closeargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode a LOCK request
  */
-static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_args *args)
+static void nfs4_xdr_enc_lock(struct rpc_rqst *req, struct xdr_stream *xdr,
+                             struct nfs_lock_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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_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);
-       return 0;
 }
 
 /*
  * Encode a LOCKT request
  */
-static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_args *args)
+static void nfs4_xdr_enc_lockt(struct rpc_rqst *req, struct xdr_stream *xdr,
+                              struct nfs_lockt_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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_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);
-       return 0;
 }
 
 /*
  * Encode a LOCKU request
  */
-static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_args *args)
+static void nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr,
+                              struct nfs_locku_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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_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);
-       return 0;
 }
 
-static int nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req, __be32 *p, struct nfs_release_lockowner_args *args)
+static void nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req,
+                                          struct xdr_stream *xdr,
+                                       struct nfs_release_lockowner_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = 0,
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_release_lockowner(&xdr, &args->lock_owner, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_release_lockowner(xdr, &args->lock_owner, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode a READLINK request
  */
-static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct nfs4_readlink *args)
+static void nfs4_xdr_enc_readlink(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                 const struct nfs4_readlink *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
 
        xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages,
                        args->pgbase, args->pglen);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode a READDIR request
  */
-static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nfs4_readdir_arg *args)
+static void nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                const struct nfs4_readdir_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
 
        xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages,
                         args->pgbase, args->count);
@@ -2227,428 +2193,387 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
                        __func__, hdr.replen << 2, args->pages,
                        args->pgbase, args->count);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode a READ request
  */
-static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
+static void nfs4_xdr_enc_read(struct rpc_rqst *req, struct xdr_stream *xdr,
+                             struct nfs_readargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_sequence(&xdr, &args->seq_args, &hdr);
-       encode_putfh(&xdr, args->fh, &hdr);
-       encode_read(&xdr, args, &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);
 
        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);
-       return 0;
 }
 
 /*
  * Encode an SETATTR request
  */
-static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_setattrargs *args)
+static void nfs4_xdr_enc_setattr(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                struct nfs_setattrargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode a GETACL request
  */
-static int
-nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p,
-               struct nfs_getaclargs *args)
+static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               struct nfs_getaclargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
        uint32_t replen;
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_sequence(&xdr, &args->seq_args, &hdr);
-       encode_putfh(&xdr, args->fh, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_putfh(xdr, args->fh, &hdr);
        replen = hdr.replen + op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz + 1;
-       encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr);
+       encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr);
 
        xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
                args->acl_pages, args->acl_pgbase, args->acl_len);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode a WRITE request
  */
-static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
+static void nfs4_xdr_enc_write(struct rpc_rqst *req, struct xdr_stream *xdr,
+                              struct nfs_writeargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_sequence(&xdr, &args->seq_args, &hdr);
-       encode_putfh(&xdr, args->fh, &hdr);
-       encode_write(&xdr, args, &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;
-       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  *  a COMMIT request
  */
-static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
+static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               struct nfs_writeargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * FSINFO request
  */
-static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsinfo_arg *args)
+static void nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               struct nfs4_fsinfo_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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_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);
-       return 0;
 }
 
 /*
  * a PATHCONF request
  */
-static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct nfs4_pathconf_arg *args)
+static void nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                 const struct nfs4_pathconf_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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],
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * a STATFS request
  */
-static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs4_statfs_arg *args)
+static void nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               const struct nfs4_statfs_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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],
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * GETATTR_BITMAP request
  */
-static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p,
-                                   struct nfs4_server_caps_arg *args)
+static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    struct nfs4_server_caps_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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|
+       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|
                           FATTR4_WORD0_ACLSUPPORT, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * a RENEW request
  */
-static int nfs4_xdr_enc_renew(struct rpc_rqst *req, __be32 *p, struct nfs_client *clp)
+static void nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr,
+                              struct nfs_client *clp)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .nops   = 0,
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_renew(&xdr, clp, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_renew(xdr, clp, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * a SETCLIENTID request
  */
-static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4_setclientid *sc)
+static void nfs4_xdr_enc_setclientid(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    struct nfs4_setclientid *sc)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .nops   = 0,
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_setclientid(&xdr, sc, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_setclientid(xdr, sc, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * a SETCLIENTID_CONFIRM request
  */
-static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, struct nfs4_setclientid_res *arg)
+static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
+                                            struct xdr_stream *xdr,
+                                            struct nfs4_setclientid_res *arg)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .nops   = 0,
        };
        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_setclientid_confirm(&xdr, arg, &hdr);
-       encode_putrootfh(&xdr, &hdr);
-       encode_fsinfo(&xdr, lease_bitmap, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_setclientid_confirm(xdr, arg, &hdr);
+       encode_putrootfh(xdr, &hdr);
+       encode_fsinfo(xdr, lease_bitmap, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * DELEGRETURN request
  */
-static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struct nfs4_delegreturnargs *args)
+static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    const struct nfs4_delegreturnargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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);
+       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);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode FS_LOCATIONS request
  */
-static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs4_fs_locations_arg *args)
+static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
+                                     struct xdr_stream *xdr,
+                                     struct nfs4_fs_locations_arg *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
        uint32_t replen;
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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_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);
+       encode_fs_locations(xdr, args->bitmask, &hdr);
 
        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)
+static void nfs4_xdr_enc_exchange_id(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    struct nfs41_exchange_id_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = args->client->cl_mvops->minor_version,
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_exchange_id(&xdr, args, &hdr);
+       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)
+static void nfs4_xdr_enc_create_session(struct rpc_rqst *req,
+                                       struct xdr_stream *xdr,
+                                       struct nfs41_create_session_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = args->client->cl_mvops->minor_version,
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_create_session(&xdr, args, &hdr);
+       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)
+static void nfs4_xdr_enc_destroy_session(struct rpc_rqst *req,
+                                        struct xdr_stream *xdr,
+                                        struct nfs4_session *session)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = session->clp->cl_mvops->minor_version,
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_destroy_session(&xdr, session, &hdr);
+       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)
+static void nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                 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_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)
+static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req,
+                                       struct xdr_stream *xdr,
+                                       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_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;
 }
 
 /*
  * a RECLAIM_COMPLETE request
  */
-static int nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req, uint32_t *p,
-                                    struct nfs41_reclaim_complete_args *args)
+static void nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req,
+                                         struct xdr_stream *xdr,
+                               struct nfs41_reclaim_complete_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args)
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_sequence(&xdr, &args->seq_args, &hdr);
-       encode_reclaim_complete(&xdr, args, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_reclaim_complete(xdr, args, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  * Encode GETDEVICEINFO request
  */
-static int nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req, uint32_t *p,
-                                     struct nfs4_getdeviceinfo_args *args)
+static void nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req,
+                                      struct xdr_stream *xdr,
+                                      struct nfs4_getdeviceinfo_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_sequence(&xdr, &args->seq_args, &hdr);
-       encode_getdeviceinfo(&xdr, args, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_getdeviceinfo(xdr, args, &hdr);
 
        /* set up reply kvec. Subtract notification bitmap max size (2)
         * so that notification bitmap is put in xdr_buf tail */
@@ -2657,27 +2582,24 @@ static int nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req, uint32_t *p,
                         args->pdev->pglen);
 
        encode_nops(&hdr);
-       return 0;
 }
 
 /*
  *  Encode LAYOUTGET request
  */
-static int nfs4_xdr_enc_layoutget(struct rpc_rqst *req, uint32_t *p,
-                                 struct nfs4_layoutget_args *args)
+static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs4_layoutget_args *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_compound_hdr(&xdr, req, &hdr);
-       encode_sequence(&xdr, &args->seq_args, &hdr);
-       encode_putfh(&xdr, NFS_FH(args->inode), &hdr);
-       encode_layoutget(&xdr, args, &hdr);
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_putfh(xdr, NFS_FH(args->inode), &hdr);
+       encode_layoutget(xdr, args, &hdr);
        encode_nops(&hdr);
-       return 0;
 }
 #endif /* CONFIG_NFS_V4_1 */
 
@@ -4475,7 +4397,7 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_
                goto out_overflow;
        eof = be32_to_cpup(p++);
        count = be32_to_cpup(p);
-       hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+       hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base;
        recvd = req->rq_rcv_buf.len - hdrlen;
        if (count > recvd) {
                dprintk("NFS: server cheating in read reply: "
@@ -5000,7 +4922,7 @@ static int decode_getdeviceinfo(struct xdr_stream *xdr,
                goto out_overflow;
        len = be32_to_cpup(p);
        if (len) {
-               int i;
+               uint32_t i;
 
                p = xdr_inline_decode(xdr, 4 * len);
                if (unlikely(!p))
@@ -5090,26 +5012,26 @@ out_overflow:
 /*
  * Decode OPEN_DOWNGRADE response
  */
-static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, __be32 *p, struct nfs_closeres *res)
+static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp,
+                                      struct xdr_stream *xdr,
+                                      struct nfs_closeres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_open_downgrade(&xdr, res);
+       status = decode_open_downgrade(xdr, res);
        if (status != 0)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server,
+       decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5118,26 +5040,25 @@ out:
 /*
  * Decode ACCESS response
  */
-static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_accessres *res)
+static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                              struct nfs4_accessres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status != 0)
                goto out;
-       status = decode_access(&xdr, res);
+       status = decode_access(xdr, res);
        if (status != 0)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server,
+       decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5146,26 +5067,28 @@ out:
 /*
  * Decode LOOKUP response
  */
-static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_lookup_res *res)
+static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                              struct nfs4_lookup_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_lookup(&xdr)) != 0)
+       status = decode_lookup(xdr);
+       if (status)
                goto out;
-       if ((status = decode_getfh(&xdr, res->fh)) != 0)
+       status = decode_getfh(xdr, res->fh);
+       if (status)
                goto out;
-       status = decode_getfattr(&xdr, res->fattr, res->server
+       status = decode_getfattr(xdr, res->fattr, res->server
                        ,!RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5174,23 +5097,25 @@ out:
 /*
  * Decode LOOKUP_ROOT response
  */
-static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_lookup_res *res)
+static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp,
+                                   struct xdr_stream *xdr,
+                                   struct nfs4_lookup_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       if ((status = decode_putrootfh(&xdr)) != 0)
+       status = decode_putrootfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_getfh(&xdr, res->fh)) == 0)
-               status = decode_getfattr(&xdr, res->fattr, res->server,
+       status = decode_getfh(xdr, res->fh);
+       if (status == 0)
+               status = decode_getfattr(xdr, res->fattr, res->server,
                                !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5199,24 +5124,25 @@ out:
 /*
  * Decode REMOVE response
  */
-static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_removeres *res)
+static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                              struct nfs_removeres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_remove(&xdr, &res->cinfo)) != 0)
+       status = decode_remove(xdr, &res->cinfo);
+       if (status)
                goto out;
-       decode_getfattr(&xdr, res->dir_attr, res->server,
+       decode_getfattr(xdr, res->dir_attr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5225,34 +5151,38 @@ out:
 /*
  * Decode RENAME response
  */
-static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs_renameres *res)
+static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                              struct nfs_renameres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_savefh(&xdr)) != 0)
+       status = decode_savefh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_rename(&xdr, &res->old_cinfo, &res->new_cinfo)) != 0)
+       status = decode_rename(xdr, &res->old_cinfo, &res->new_cinfo);
+       if (status)
                goto out;
        /* Current FH is target directory */
-       if (decode_getfattr(&xdr, res->new_fattr, res->server,
+       if (decode_getfattr(xdr, res->new_fattr, res->server,
                                !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
                goto out;
-       if ((status = decode_restorefh(&xdr)) != 0)
+       status = decode_restorefh(xdr);
+       if (status)
                goto out;
-       decode_getfattr(&xdr, res->old_fattr, res->server,
+       decode_getfattr(xdr, res->old_fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5261,37 +5191,41 @@ out:
 /*
  * Decode LINK response
  */
-static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_link_res *res)
+static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                            struct nfs4_link_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_savefh(&xdr)) != 0)
+       status = decode_savefh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_link(&xdr, &res->cinfo)) != 0)
+       status = decode_link(xdr, &res->cinfo);
+       if (status)
                goto out;
        /*
         * Note order: OP_LINK leaves the directory as the current
         *             filehandle.
         */
-       if (decode_getfattr(&xdr, res->dir_attr, res->server,
+       if (decode_getfattr(xdr, res->dir_attr, res->server,
                                !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
                goto out;
-       if ((status = decode_restorefh(&xdr)) != 0)
+       status = decode_restorefh(xdr);
+       if (status)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server,
+       decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5300,33 +5234,37 @@ out:
 /*
  * Decode CREATE response
  */
-static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_create_res *res)
+static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                              struct nfs4_create_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_savefh(&xdr)) != 0)
+       status = decode_savefh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_create(&xdr,&res->dir_cinfo)) != 0)
+       status = decode_create(xdr, &res->dir_cinfo);
+       if (status)
                goto out;
-       if ((status = decode_getfh(&xdr, res->fh)) != 0)
+       status = decode_getfh(xdr, res->fh);
+       if (status)
                goto out;
-       if (decode_getfattr(&xdr, res->fattr, res->server,
+       if (decode_getfattr(xdr, res->fattr, res->server,
                                !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
                goto out;
-       if ((status = decode_restorefh(&xdr)) != 0)
+       status = decode_restorefh(xdr);
+       if (status)
                goto out;
-       decode_getfattr(&xdr, res->dir_fattr, res->server,
+       decode_getfattr(xdr, res->dir_fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5335,31 +5273,31 @@ out:
 /*
  * Decode SYMLINK response
  */
-static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_create_res *res)
+static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                               struct nfs4_create_res *res)
 {
-       return nfs4_xdr_dec_create(rqstp, p, res);
+       return nfs4_xdr_dec_create(rqstp, xdr, res);
 }
 
 /*
  * Decode GETATTR response
  */
-static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_getattr_res *res)
+static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                               struct nfs4_getattr_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_getfattr(&xdr, res->fattr, res->server,
+       status = decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5368,46 +5306,40 @@ out:
 /*
  * Encode an SETACL request
  */
-static int
-nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args)
+static void nfs4_xdr_enc_setacl(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               struct nfs_setaclargs *args)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr = {
                .minorversion = nfs4_xdr_minorversion(&args->seq_args),
        };
-       int status;
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       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_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_putfh(xdr, args->fh, &hdr);
+       encode_setacl(xdr, args, &hdr);
        encode_nops(&hdr);
-       return status;
 }
 
 /*
  * Decode SETACL response
  */
 static int
-nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, __be32 *p,
+nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
                    struct nfs_setaclres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_setattr(&xdr);
+       status = decode_setattr(xdr);
 out:
        return status;
 }
@@ -5416,24 +5348,22 @@ out:
  * Decode GETACL response
  */
 static int
-nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, __be32 *p,
+nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
                    struct nfs_getaclres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_getacl(&xdr, rqstp, &res->acl_len);
+       status = decode_getacl(xdr, rqstp, &res->acl_len);
 
 out:
        return status;
@@ -5442,23 +5372,22 @@ out:
 /*
  * Decode CLOSE response
  */
-static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_closeres *res)
+static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                             struct nfs_closeres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_close(&xdr, res);
+       status = decode_close(xdr, res);
        if (status != 0)
                goto out;
        /*
@@ -5467,7 +5396,7 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_clos
         *      an ESTALE error. Shouldn't be a problem,
         *      though, since fattr->valid will remain unset.
         */
-       decode_getfattr(&xdr, res->fattr, res->server,
+       decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5476,36 +5405,35 @@ out:
 /*
  * Decode OPEN response
  */
-static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openres *res)
+static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                            struct nfs_openres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_savefh(&xdr);
+       status = decode_savefh(xdr);
        if (status)
                goto out;
-       status = decode_open(&xdr, res);
+       status = decode_open(xdr, res);
        if (status)
                goto out;
-       if (decode_getfh(&xdr, &res->fh) != 0)
+       if (decode_getfh(xdr, &res->fh) != 0)
                goto out;
-       if (decode_getfattr(&xdr, res->f_attr, res->server,
+       if (decode_getfattr(xdr, res->f_attr, res->server,
                                !RPC_IS_ASYNC(rqstp->rq_task)) != 0)
                goto out;
-       if (decode_restorefh(&xdr) != 0)
+       if (decode_restorefh(xdr) != 0)
                goto out;
-       decode_getfattr(&xdr, res->dir_attr, res->server,
+       decode_getfattr(xdr, res->dir_attr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5514,20 +5442,20 @@ out:
 /*
  * Decode OPEN_CONFIRM response
  */
-static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, __be32 *p, struct nfs_open_confirmres *res)
+static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp,
+                                    struct xdr_stream *xdr,
+                                    struct nfs_open_confirmres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_open_confirm(&xdr, res);
+       status = decode_open_confirm(xdr, res);
 out:
        return status;
 }
@@ -5535,26 +5463,26 @@ out:
 /*
  * Decode OPEN response
  */
-static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openres *res)
+static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp,
+                                   struct xdr_stream *xdr,
+                                   struct nfs_openres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_open(&xdr, res);
+       status = decode_open(xdr, res);
        if (status)
                goto out;
-       decode_getfattr(&xdr, res->f_attr, res->server,
+       decode_getfattr(xdr, res->f_attr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5563,26 +5491,26 @@ out:
 /*
  * Decode SETATTR response
  */
-static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_setattrres *res)
+static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp,
+                               struct xdr_stream *xdr,
+                               struct nfs_setattrres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_setattr(&xdr);
+       status = decode_setattr(xdr);
        if (status)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server,
+       decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5591,23 +5519,22 @@ out:
 /*
  * Decode LOCK response
  */
-static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, __be32 *p, struct nfs_lock_res *res)
+static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                            struct nfs_lock_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_lock(&xdr, res);
+       status = decode_lock(xdr, res);
 out:
        return status;
 }
@@ -5615,23 +5542,22 @@ out:
 /*
  * Decode LOCKT response
  */
-static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, __be32 *p, struct nfs_lockt_res *res)
+static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                             struct nfs_lockt_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_lockt(&xdr, res);
+       status = decode_lockt(xdr, res);
 out:
        return status;
 }
@@ -5639,61 +5565,58 @@ out:
 /*
  * Decode LOCKU response
  */
-static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, __be32 *p, struct nfs_locku_res *res)
+static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                             struct nfs_locku_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_locku(&xdr, res);
+       status = decode_locku(xdr, res);
 out:
        return status;
 }
 
-static int nfs4_xdr_dec_release_lockowner(struct rpc_rqst *rqstp, __be32 *p, void *dummy)
+static int nfs4_xdr_dec_release_lockowner(struct rpc_rqst *rqstp,
+                                         struct xdr_stream *xdr, 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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_release_lockowner(&xdr);
+               status = decode_release_lockowner(xdr);
        return status;
 }
 
 /*
  * Decode READLINK response
  */
-static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp, __be32 *p,
+static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp,
+                                struct xdr_stream *xdr,
                                 struct nfs4_readlink_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_readlink(&xdr, rqstp);
+       status = decode_readlink(xdr, rqstp);
 out:
        return status;
 }
@@ -5701,23 +5624,22 @@ out:
 /*
  * Decode READDIR response
  */
-static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_readdir_res *res)
+static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                               struct nfs4_readdir_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_readdir(&xdr, rqstp, res);
+       status = decode_readdir(xdr, rqstp, res);
 out:
        return status;
 }
@@ -5725,23 +5647,22 @@ out:
 /*
  * Decode Read response
  */
-static int nfs4_xdr_dec_read(struct rpc_rqst *rqstp, __be32 *p, struct nfs_readres *res)
+static int nfs4_xdr_dec_read(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                            struct nfs_readres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_read(&xdr, rqstp, res);
+       status = decode_read(xdr, rqstp, res);
        if (!status)
                status = res->count;
 out:
@@ -5751,26 +5672,25 @@ out:
 /*
  * Decode WRITE response
  */
-static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, __be32 *p, struct nfs_writeres *res)
+static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                             struct nfs_writeres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_write(&xdr, res);
+       status = decode_write(xdr, res);
        if (status)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server,
+       decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
        if (!status)
                status = res->count;
@@ -5781,26 +5701,25 @@ out:
 /*
  * Decode COMMIT response
  */
-static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, __be32 *p, struct nfs_writeres *res)
+static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                              struct nfs_writeres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_commit(&xdr, res);
+       status = decode_commit(xdr, res);
        if (status)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server,
+       decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5809,85 +5728,80 @@ out:
 /*
  * Decode FSINFO response
  */
-static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p,
+static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
                               struct nfs4_fsinfo_res *res)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       status = decode_compound_hdr(&xdr, &hdr);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_sequence(&xdr, &res->seq_res, req);
+               status = decode_sequence(xdr, &res->seq_res, req);
        if (!status)
-               status = decode_putfh(&xdr);
+               status = decode_putfh(xdr);
        if (!status)
-               status = decode_fsinfo(&xdr, res->fsinfo);
+               status = decode_fsinfo(xdr, res->fsinfo);
        return status;
 }
 
 /*
  * Decode PATHCONF response
  */
-static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, __be32 *p,
+static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
                                 struct nfs4_pathconf_res *res)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       status = decode_compound_hdr(&xdr, &hdr);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_sequence(&xdr, &res->seq_res, req);
+               status = decode_sequence(xdr, &res->seq_res, req);
        if (!status)
-               status = decode_putfh(&xdr);
+               status = decode_putfh(xdr);
        if (!status)
-               status = decode_pathconf(&xdr, res->pathconf);
+               status = decode_pathconf(xdr, res->pathconf);
        return status;
 }
 
 /*
  * Decode STATFS response
  */
-static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, __be32 *p,
+static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
                               struct nfs4_statfs_res *res)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       status = decode_compound_hdr(&xdr, &hdr);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_sequence(&xdr, &res->seq_res, req);
+               status = decode_sequence(xdr, &res->seq_res, req);
        if (!status)
-               status = decode_putfh(&xdr);
+               status = decode_putfh(xdr);
        if (!status)
-               status = decode_statfs(&xdr, res->fsstat);
+               status = decode_statfs(xdr, res->fsstat);
        return status;
 }
 
 /*
  * Decode GETATTR_BITMAP response
  */
-static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req, __be32 *p, struct nfs4_server_caps_res *res)
+static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   struct nfs4_server_caps_res *res)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       status = decode_compound_hdr(&xdr, &hdr);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, req);
+       status = decode_sequence(xdr, &res->seq_res, req);
        if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       status = decode_server_caps(&xdr, res);
+       status = decode_server_caps(xdr, res);
 out:
        return status;
 }
@@ -5895,79 +5809,77 @@ out:
 /*
  * Decode RENEW response
  */
-static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, __be32 *p, void *dummy)
+static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
+                             void *__unused)
 {
-       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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_renew(&xdr);
+               status = decode_renew(xdr);
        return status;
 }
 
 /*
  * Decode SETCLIENTID response
  */
-static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, __be32 *p,
-               struct nfs4_setclientid_res *res)
+static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
+                                   struct nfs4_setclientid_res *res)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       status = decode_compound_hdr(&xdr, &hdr);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_setclientid(&xdr, res);
+               status = decode_setclientid(xdr, res);
        return status;
 }
 
 /*
  * Decode SETCLIENTID_CONFIRM response
  */
-static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *fsinfo)
+static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
+                                           struct xdr_stream *xdr,
+                                           struct nfs_fsinfo *fsinfo)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       status = decode_compound_hdr(&xdr, &hdr);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_setclientid_confirm(&xdr);
+               status = decode_setclientid_confirm(xdr);
        if (!status)
-               status = decode_putrootfh(&xdr);
+               status = decode_putrootfh(xdr);
        if (!status)
-               status = decode_fsinfo(&xdr, fsinfo);
+               status = decode_fsinfo(xdr, fsinfo);
        return status;
 }
 
 /*
  * Decode DELEGRETURN response
  */
-static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_delegreturnres *res)
+static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp,
+                                   struct xdr_stream *xdr,
+                                   struct nfs4_delegreturnres *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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status != 0)
                goto out;
-       status = decode_delegreturn(&xdr);
+       status = decode_delegreturn(xdr);
        if (status != 0)
                goto out;
-       decode_getfattr(&xdr, res->fattr, res->server,
+       decode_getfattr(xdr, res->fattr, res->server,
                        !RPC_IS_ASYNC(rqstp->rq_task));
 out:
        return status;
@@ -5976,26 +5888,27 @@ out:
 /*
  * Decode FS_LOCATIONS response
  */
-static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, __be32 *p,
+static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
                                     struct nfs4_fs_locations_res *res)
 {
-       struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       status = decode_compound_hdr(&xdr, &hdr);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, req);
+       status = decode_sequence(xdr, &res->seq_res, req);
        if (status)
                goto out;
-       if ((status = decode_putfh(&xdr)) != 0)
+       status = decode_putfh(xdr);
+       if (status)
                goto out;
-       if ((status = decode_lookup(&xdr)) != 0)
+       status = decode_lookup(xdr);
+       if (status)
                goto out;
-       xdr_enter_page(&xdr, PAGE_SIZE);
-       status = decode_getfattr(&xdr, &res->fs_locations->fattr,
+       xdr_enter_page(xdr, PAGE_SIZE);
+       status = decode_getfattr(xdr, &res->fs_locations->fattr,
                                 res->fs_locations->server,
                                 !RPC_IS_ASYNC(req->rq_task));
 out:
@@ -6006,129 +5919,122 @@ out:
 /*
  * Decode EXCHANGE_ID response
  */
-static int nfs4_xdr_dec_exchange_id(struct rpc_rqst *rqstp, uint32_t *p,
+static int nfs4_xdr_dec_exchange_id(struct rpc_rqst *rqstp,
+                                   struct xdr_stream *xdr,
                                    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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_exchange_id(&xdr, res);
+               status = decode_exchange_id(xdr, res);
        return status;
 }
 
 /*
  * Decode CREATE_SESSION response
  */
-static int nfs4_xdr_dec_create_session(struct rpc_rqst *rqstp, uint32_t *p,
+static int nfs4_xdr_dec_create_session(struct rpc_rqst *rqstp,
+                                      struct xdr_stream *xdr,
                                       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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_create_session(&xdr, res);
+               status = decode_create_session(xdr, res);
        return status;
 }
 
 /*
  * Decode DESTROY_SESSION response
  */
-static int nfs4_xdr_dec_destroy_session(struct rpc_rqst *rqstp, uint32_t *p,
-                                       void *dummy)
+static int nfs4_xdr_dec_destroy_session(struct rpc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_destroy_session(&xdr, dummy);
+               status = decode_destroy_session(xdr, res);
        return status;
 }
 
 /*
  * Decode SEQUENCE response
  */
-static int nfs4_xdr_dec_sequence(struct rpc_rqst *rqstp, uint32_t *p,
+static int nfs4_xdr_dec_sequence(struct rpc_rqst *rqstp,
+                                struct xdr_stream *xdr,
                                 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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_sequence(&xdr, res, rqstp);
+               status = decode_sequence(xdr, res, rqstp);
        return status;
 }
 
 /*
  * Decode GET_LEASE_TIME response
  */
-static int nfs4_xdr_dec_get_lease_time(struct rpc_rqst *rqstp, uint32_t *p,
+static int nfs4_xdr_dec_get_lease_time(struct rpc_rqst *rqstp,
+                                      struct xdr_stream *xdr,
                                       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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_sequence(&xdr, &res->lr_seq_res, rqstp);
+               status = decode_sequence(xdr, &res->lr_seq_res, rqstp);
        if (!status)
-               status = decode_putrootfh(&xdr);
+               status = decode_putrootfh(xdr);
        if (!status)
-               status = decode_fsinfo(&xdr, res->lr_fsinfo);
+               status = decode_fsinfo(xdr, res->lr_fsinfo);
        return status;
 }
 
 /*
  * Decode RECLAIM_COMPLETE response
  */
-static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p,
+static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp,
+                                        struct xdr_stream *xdr,
                                         struct nfs41_reclaim_complete_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (!status)
-               status = decode_sequence(&xdr, &res->seq_res, rqstp);
+               status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (!status)
-               status = decode_reclaim_complete(&xdr, (void *)NULL);
+               status = decode_reclaim_complete(xdr, (void *)NULL);
        return status;
 }
 
 /*
  * Decode GETDEVINFO response
  */
-static int nfs4_xdr_dec_getdeviceinfo(struct rpc_rqst *rqstp, uint32_t *p,
+static int nfs4_xdr_dec_getdeviceinfo(struct rpc_rqst *rqstp,
+                                     struct xdr_stream *xdr,
                                      struct nfs4_getdeviceinfo_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status != 0)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status != 0)
                goto out;
-       status = decode_getdeviceinfo(&xdr, res->pdev);
+       status = decode_getdeviceinfo(xdr, res->pdev);
 out:
        return status;
 }
@@ -6136,31 +6042,44 @@ out:
 /*
  * Decode LAYOUTGET response
  */
-static int nfs4_xdr_dec_layoutget(struct rpc_rqst *rqstp, uint32_t *p,
+static int nfs4_xdr_dec_layoutget(struct rpc_rqst *rqstp,
+                                 struct xdr_stream *xdr,
                                  struct nfs4_layoutget_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);
+       status = decode_compound_hdr(xdr, &hdr);
        if (status)
                goto out;
-       status = decode_sequence(&xdr, &res->seq_res, rqstp);
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
        if (status)
                goto out;
-       status = decode_putfh(&xdr);
+       status = decode_putfh(xdr);
        if (status)
                goto out;
-       status = decode_layoutget(&xdr, rqstp, res);
+       status = decode_layoutget(xdr, rqstp, res);
 out:
        return status;
 }
 #endif /* CONFIG_NFS_V4_1 */
 
-__be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
-                          struct nfs_server *server, int plus)
+/**
+ * nfs4_decode_dirent - Decode a single NFSv4 directory entry stored in
+ *                      the local page cache.
+ * @xdr: XDR stream where entry resides
+ * @entry: buffer to fill in with entry data
+ * @plus: boolean indicating whether this should be a readdirplus entry
+ *
+ * Returns zero if successful, otherwise a negative errno value is
+ * returned.
+ *
+ * This function is not invoked during READDIR reply decoding, but
+ * rather whenever an application invokes the getdents(2) system call
+ * on a directory already in our cache.
+ */
+int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
+                      int plus)
 {
        uint32_t bitmap[2] = {0};
        uint32_t len;
@@ -6172,9 +6091,9 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
                if (unlikely(!p))
                        goto out_overflow;
                if (!ntohl(*p++))
-                       return ERR_PTR(-EAGAIN);
+                       return -EAGAIN;
                entry->eof = 1;
-               return ERR_PTR(-EBADCOOKIE);
+               return -EBADCOOKIE;
        }
 
        p = xdr_inline_decode(xdr, 12);
@@ -6203,7 +6122,8 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
        if (decode_attr_length(xdr, &len, &p) < 0)
                goto out_overflow;
 
-       if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, server, 1) < 0)
+       if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
+                                       entry->server, 1) < 0)
                goto out_overflow;
        if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID)
                entry->ino = entry->fattr->fileid;
@@ -6215,11 +6135,11 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
        if (verify_attr_len(xdr, p, len) < 0)
                goto out_overflow;
 
-       return p;
+       return 0;
 
 out_overflow:
        print_overflow_msg(__func__, xdr);
-       return ERR_PTR(-EAGAIN);
+       return -EAGAIN;
 }
 
 /*
@@ -6295,8 +6215,8 @@ nfs4_stat_to_errno(int stat)
 #define PROC(proc, argtype, restype)                           \
 [NFSPROC4_CLNT_##proc] = {                                     \
        .p_proc   = NFSPROC4_COMPOUND,                          \
-       .p_encode = (kxdrproc_t) nfs4_xdr_##argtype,            \
-       .p_decode = (kxdrproc_t) nfs4_xdr_##restype,            \
+       .p_encode = (kxdreproc_t)nfs4_xdr_##argtype,            \
+       .p_decode = (kxdrdproc_t)nfs4_xdr_##restype,            \
        .p_arglen = NFS4_##argtype##_sz,                        \
        .p_replen = NFS4_##restype##_sz,                        \
        .p_statidx = NFSPROC4_CLNT_##proc,                      \
@@ -6304,50 +6224,50 @@ nfs4_stat_to_errno(int stat)
 }
 
 struct rpc_procinfo    nfs4_procedures[] = {
-  PROC(READ,           enc_read,       dec_read),
-  PROC(WRITE,          enc_write,      dec_write),
-  PROC(COMMIT,         enc_commit,     dec_commit),
-  PROC(OPEN,           enc_open,       dec_open),
-  PROC(OPEN_CONFIRM,   enc_open_confirm,       dec_open_confirm),
-  PROC(OPEN_NOATTR,    enc_open_noattr,        dec_open_noattr),
-  PROC(OPEN_DOWNGRADE, enc_open_downgrade,     dec_open_downgrade),
-  PROC(CLOSE,          enc_close,      dec_close),
-  PROC(SETATTR,                enc_setattr,    dec_setattr),
-  PROC(FSINFO,         enc_fsinfo,     dec_fsinfo),
-  PROC(RENEW,          enc_renew,      dec_renew),
-  PROC(SETCLIENTID,    enc_setclientid,        dec_setclientid),
-  PROC(SETCLIENTID_CONFIRM,    enc_setclientid_confirm,        dec_setclientid_confirm),
-  PROC(LOCK,            enc_lock,       dec_lock),
-  PROC(LOCKT,           enc_lockt,      dec_lockt),
-  PROC(LOCKU,           enc_locku,      dec_locku),
-  PROC(ACCESS,         enc_access,     dec_access),
-  PROC(GETATTR,                enc_getattr,    dec_getattr),
-  PROC(LOOKUP,         enc_lookup,     dec_lookup),
-  PROC(LOOKUP_ROOT,    enc_lookup_root,        dec_lookup_root),
-  PROC(REMOVE,         enc_remove,     dec_remove),
-  PROC(RENAME,         enc_rename,     dec_rename),
-  PROC(LINK,           enc_link,       dec_link),
-  PROC(SYMLINK,                enc_symlink,    dec_symlink),
-  PROC(CREATE,         enc_create,     dec_create),
-  PROC(PATHCONF,       enc_pathconf,   dec_pathconf),
-  PROC(STATFS,         enc_statfs,     dec_statfs),
-  PROC(READLINK,       enc_readlink,   dec_readlink),
-  PROC(READDIR,                enc_readdir,    dec_readdir),
-  PROC(SERVER_CAPS,    enc_server_caps, dec_server_caps),
-  PROC(DELEGRETURN,    enc_delegreturn, dec_delegreturn),
-  PROC(GETACL,         enc_getacl,     dec_getacl),
-  PROC(SETACL,         enc_setacl,     dec_setacl),
-  PROC(FS_LOCATIONS,   enc_fs_locations, dec_fs_locations),
-  PROC(RELEASE_LOCKOWNER, enc_release_lockowner, dec_release_lockowner),
+       PROC(READ,              enc_read,               dec_read),
+       PROC(WRITE,             enc_write,              dec_write),
+       PROC(COMMIT,            enc_commit,             dec_commit),
+       PROC(OPEN,              enc_open,               dec_open),
+       PROC(OPEN_CONFIRM,      enc_open_confirm,       dec_open_confirm),
+       PROC(OPEN_NOATTR,       enc_open_noattr,        dec_open_noattr),
+       PROC(OPEN_DOWNGRADE,    enc_open_downgrade,     dec_open_downgrade),
+       PROC(CLOSE,             enc_close,              dec_close),
+       PROC(SETATTR,           enc_setattr,            dec_setattr),
+       PROC(FSINFO,            enc_fsinfo,             dec_fsinfo),
+       PROC(RENEW,             enc_renew,              dec_renew),
+       PROC(SETCLIENTID,       enc_setclientid,        dec_setclientid),
+       PROC(SETCLIENTID_CONFIRM, enc_setclientid_confirm, dec_setclientid_confirm),
+       PROC(LOCK,              enc_lock,               dec_lock),
+       PROC(LOCKT,             enc_lockt,              dec_lockt),
+       PROC(LOCKU,             enc_locku,              dec_locku),
+       PROC(ACCESS,            enc_access,             dec_access),
+       PROC(GETATTR,           enc_getattr,            dec_getattr),
+       PROC(LOOKUP,            enc_lookup,             dec_lookup),
+       PROC(LOOKUP_ROOT,       enc_lookup_root,        dec_lookup_root),
+       PROC(REMOVE,            enc_remove,             dec_remove),
+       PROC(RENAME,            enc_rename,             dec_rename),
+       PROC(LINK,              enc_link,               dec_link),
+       PROC(SYMLINK,           enc_symlink,            dec_symlink),
+       PROC(CREATE,            enc_create,             dec_create),
+       PROC(PATHCONF,          enc_pathconf,           dec_pathconf),
+       PROC(STATFS,            enc_statfs,             dec_statfs),
+       PROC(READLINK,          enc_readlink,           dec_readlink),
+       PROC(READDIR,           enc_readdir,            dec_readdir),
+       PROC(SERVER_CAPS,       enc_server_caps,        dec_server_caps),
+       PROC(DELEGRETURN,       enc_delegreturn,        dec_delegreturn),
+       PROC(GETACL,            enc_getacl,             dec_getacl),
+       PROC(SETACL,            enc_setacl,             dec_setacl),
+       PROC(FS_LOCATIONS,      enc_fs_locations,       dec_fs_locations),
+       PROC(RELEASE_LOCKOWNER, enc_release_lockowner,  dec_release_lockowner),
 #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),
-  PROC(RECLAIM_COMPLETE, enc_reclaim_complete,  dec_reclaim_complete),
-  PROC(GETDEVICEINFO, enc_getdeviceinfo, dec_getdeviceinfo),
-  PROC(LAYOUTGET,  enc_layoutget,     dec_layoutget),
+       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),
+       PROC(RECLAIM_COMPLETE,  enc_reclaim_complete,   dec_reclaim_complete),
+       PROC(GETDEVICEINFO,     enc_getdeviceinfo,      dec_getdeviceinfo),
+       PROC(LAYOUTGET,         enc_layoutget,          dec_layoutget),
 #endif /* CONFIG_NFS_V4_1 */
 };
 
index b68536c..e1164e3 100644 (file)
@@ -26,12 +26,9 @@ static struct kmem_cache *nfs_page_cachep;
 static inline struct nfs_page *
 nfs_page_alloc(void)
 {
-       struct nfs_page *p;
-       p = kmem_cache_alloc(nfs_page_cachep, GFP_KERNEL);
-       if (p) {
-               memset(p, 0, sizeof(*p));
+       struct nfs_page *p = kmem_cache_zalloc(nfs_page_cachep, GFP_KERNEL);
+       if (p)
                INIT_LIST_HEAD(&p->wb_list);
-       }
        return p;
 }
 
index db77342..bc40897 100644 (file)
@@ -177,105 +177,149 @@ EXPORT_SYMBOL_GPL(pnfs_unregister_layoutdriver);
  * pNFS client layout cache
  */
 
+/* Need to hold i_lock if caller does not already hold reference */
+void
+get_layout_hdr(struct pnfs_layout_hdr *lo)
+{
+       atomic_inc(&lo->plh_refcount);
+}
+
 static void
-get_layout_hdr_locked(struct pnfs_layout_hdr *lo)
+destroy_layout_hdr(struct pnfs_layout_hdr *lo)
 {
-       assert_spin_locked(&lo->inode->i_lock);
-       lo->refcount++;
+       dprintk("%s: freeing layout cache %p\n", __func__, lo);
+       BUG_ON(!list_empty(&lo->plh_layouts));
+       NFS_I(lo->plh_inode)->layout = NULL;
+       kfree(lo);
 }
 
 static void
 put_layout_hdr_locked(struct pnfs_layout_hdr *lo)
 {
-       assert_spin_locked(&lo->inode->i_lock);
-       BUG_ON(lo->refcount == 0);
-
-       lo->refcount--;
-       if (!lo->refcount) {
-               dprintk("%s: freeing layout cache %p\n", __func__, lo);
-               BUG_ON(!list_empty(&lo->layouts));
-               NFS_I(lo->inode)->layout = NULL;
-               kfree(lo);
-       }
+       if (atomic_dec_and_test(&lo->plh_refcount))
+               destroy_layout_hdr(lo);
 }
 
 void
-put_layout_hdr(struct inode *inode)
+put_layout_hdr(struct pnfs_layout_hdr *lo)
 {
-       spin_lock(&inode->i_lock);
-       put_layout_hdr_locked(NFS_I(inode)->layout);
-       spin_unlock(&inode->i_lock);
+       struct inode *inode = lo->plh_inode;
+
+       if (atomic_dec_and_lock(&lo->plh_refcount, &inode->i_lock)) {
+               destroy_layout_hdr(lo);
+               spin_unlock(&inode->i_lock);
+       }
 }
 
 static void
 init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg)
 {
-       INIT_LIST_HEAD(&lseg->fi_list);
-       kref_init(&lseg->kref);
-       lseg->layout = lo;
+       INIT_LIST_HEAD(&lseg->pls_list);
+       atomic_set(&lseg->pls_refcount, 1);
+       smp_mb();
+       set_bit(NFS_LSEG_VALID, &lseg->pls_flags);
+       lseg->pls_layout = lo;
 }
 
-/* Called without i_lock held, as the free_lseg call may sleep */
-static void
-destroy_lseg(struct kref *kref)
+static void free_lseg(struct pnfs_layout_segment *lseg)
 {
-       struct pnfs_layout_segment *lseg =
-               container_of(kref, struct pnfs_layout_segment, kref);
-       struct inode *ino = lseg->layout->inode;
+       struct inode *ino = lseg->pls_layout->plh_inode;
 
-       dprintk("--> %s\n", __func__);
        NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg);
-       /* Matched by get_layout_hdr_locked in pnfs_insert_layout */
-       put_layout_hdr(ino);
+       /* Matched by get_layout_hdr in pnfs_insert_layout */
+       put_layout_hdr(NFS_I(ino)->layout);
 }
 
-static void
-put_lseg(struct pnfs_layout_segment *lseg)
+/* The use of tmp_list is necessary because pnfs_curr_ld->free_lseg
+ * could sleep, so must be called outside of the lock.
+ * Returns 1 if object was removed, otherwise return 0.
+ */
+static int
+put_lseg_locked(struct pnfs_layout_segment *lseg,
+               struct list_head *tmp_list)
+{
+       dprintk("%s: lseg %p ref %d valid %d\n", __func__, lseg,
+               atomic_read(&lseg->pls_refcount),
+               test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
+       if (atomic_dec_and_test(&lseg->pls_refcount)) {
+               struct inode *ino = lseg->pls_layout->plh_inode;
+
+               BUG_ON(test_bit(NFS_LSEG_VALID, &lseg->pls_flags));
+               list_del(&lseg->pls_list);
+               if (list_empty(&lseg->pls_layout->plh_segs)) {
+                       struct nfs_client *clp;
+
+                       clp = NFS_SERVER(ino)->nfs_client;
+                       spin_lock(&clp->cl_lock);
+                       /* List does not take a reference, so no need for put here */
+                       list_del_init(&lseg->pls_layout->plh_layouts);
+                       spin_unlock(&clp->cl_lock);
+                       clear_bit(NFS_LAYOUT_BULK_RECALL, &lseg->pls_layout->plh_flags);
+               }
+               rpc_wake_up(&NFS_SERVER(ino)->roc_rpcwaitq);
+               list_add(&lseg->pls_list, tmp_list);
+               return 1;
+       }
+       return 0;
+}
+
+static bool
+should_free_lseg(u32 lseg_iomode, u32 recall_iomode)
 {
-       if (!lseg)
-               return;
+       return (recall_iomode == IOMODE_ANY ||
+               lseg_iomode == recall_iomode);
+}
 
-       dprintk("%s: lseg %p ref %d\n", __func__, lseg,
-               atomic_read(&lseg->kref.refcount));
-       kref_put(&lseg->kref, destroy_lseg);
+/* Returns 1 if lseg is removed from list, 0 otherwise */
+static int mark_lseg_invalid(struct pnfs_layout_segment *lseg,
+                            struct list_head *tmp_list)
+{
+       int rv = 0;
+
+       if (test_and_clear_bit(NFS_LSEG_VALID, &lseg->pls_flags)) {
+               /* Remove the reference keeping the lseg in the
+                * list.  It will now be removed when all
+                * outstanding io is finished.
+                */
+               rv = put_lseg_locked(lseg, tmp_list);
+       }
+       return rv;
 }
 
-static void
-pnfs_clear_lseg_list(struct pnfs_layout_hdr *lo, struct list_head *tmp_list)
+/* Returns count of number of matching invalid lsegs remaining in list
+ * after call.
+ */
+int
+mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
+                           struct list_head *tmp_list,
+                           u32 iomode)
 {
        struct pnfs_layout_segment *lseg, *next;
-       struct nfs_client *clp;
+       int invalid = 0, removed = 0;
 
        dprintk("%s:Begin lo %p\n", __func__, lo);
 
-       assert_spin_locked(&lo->inode->i_lock);
-       list_for_each_entry_safe(lseg, next, &lo->segs, fi_list) {
-               dprintk("%s: freeing lseg %p\n", __func__, lseg);
-               list_move(&lseg->fi_list, tmp_list);
-       }
-       clp = NFS_SERVER(lo->inode)->nfs_client;
-       spin_lock(&clp->cl_lock);
-       /* List does not take a reference, so no need for put here */
-       list_del_init(&lo->layouts);
-       spin_unlock(&clp->cl_lock);
-       write_seqlock(&lo->seqlock);
-       clear_bit(NFS_LAYOUT_STATEID_SET, &lo->state);
-       write_sequnlock(&lo->seqlock);
-
-       dprintk("%s:Return\n", __func__);
+       list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list)
+               if (should_free_lseg(lseg->pls_range.iomode, iomode)) {
+                       dprintk("%s: freeing lseg %p iomode %d "
+                               "offset %llu length %llu\n", __func__,
+                               lseg, lseg->pls_range.iomode, lseg->pls_range.offset,
+                               lseg->pls_range.length);
+                       invalid++;
+                       removed += mark_lseg_invalid(lseg, tmp_list);
+               }
+       dprintk("%s:Return %i\n", __func__, invalid - removed);
+       return invalid - removed;
 }
 
-static void
-pnfs_free_lseg_list(struct list_head *tmp_list)
+void
+pnfs_free_lseg_list(struct list_head *free_me)
 {
-       struct pnfs_layout_segment *lseg;
+       struct pnfs_layout_segment *lseg, *tmp;
 
-       while (!list_empty(tmp_list)) {
-               lseg = list_entry(tmp_list->next, struct pnfs_layout_segment,
-                               fi_list);
-               dprintk("%s calling put_lseg on %p\n", __func__, lseg);
-               list_del(&lseg->fi_list);
-               put_lseg(lseg);
+       list_for_each_entry_safe(lseg, tmp, free_me, pls_list) {
+               list_del(&lseg->pls_list);
+               free_lseg(lseg);
        }
 }
 
@@ -288,7 +332,8 @@ pnfs_destroy_layout(struct nfs_inode *nfsi)
        spin_lock(&nfsi->vfs_inode.i_lock);
        lo = nfsi->layout;
        if (lo) {
-               pnfs_clear_lseg_list(lo, &tmp_list);
+               set_bit(NFS_LAYOUT_DESTROYED, &nfsi->layout->plh_flags);
+               mark_matching_lsegs_invalid(lo, &tmp_list, IOMODE_ANY);
                /* Matched by refcount set to 1 in alloc_init_layout_hdr */
                put_layout_hdr_locked(lo);
        }
@@ -312,76 +357,80 @@ pnfs_destroy_all_layouts(struct nfs_client *clp)
 
        while (!list_empty(&tmp_list)) {
                lo = list_entry(tmp_list.next, struct pnfs_layout_hdr,
-                               layouts);
+                               plh_layouts);
                dprintk("%s freeing layout for inode %lu\n", __func__,
-                       lo->inode->i_ino);
-               pnfs_destroy_layout(NFS_I(lo->inode));
+                       lo->plh_inode->i_ino);
+               pnfs_destroy_layout(NFS_I(lo->plh_inode));
        }
 }
 
-/* update lo->stateid with new if is more recent
- *
- * lo->stateid could be the open stateid, in which case we just use what given.
- */
-static void
-pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo,
-                       const nfs4_stateid *new)
-{
-       nfs4_stateid *old = &lo->stateid;
-       bool overwrite = false;
-
-       write_seqlock(&lo->seqlock);
-       if (!test_bit(NFS_LAYOUT_STATEID_SET, &lo->state) ||
-           memcmp(old->stateid.other, new->stateid.other, sizeof(new->stateid.other)))
-               overwrite = true;
-       else {
-               u32 oldseq, newseq;
-
-               oldseq = be32_to_cpu(old->stateid.seqid);
-               newseq = be32_to_cpu(new->stateid.seqid);
-               if ((int)(newseq - oldseq) > 0)
-                       overwrite = true;
+/* update lo->plh_stateid with new if is more recent */
+void
+pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo, const nfs4_stateid *new,
+                       bool update_barrier)
+{
+       u32 oldseq, newseq;
+
+       oldseq = be32_to_cpu(lo->plh_stateid.stateid.seqid);
+       newseq = be32_to_cpu(new->stateid.seqid);
+       if ((int)(newseq - oldseq) > 0) {
+               memcpy(&lo->plh_stateid, &new->stateid, sizeof(new->stateid));
+               if (update_barrier) {
+                       u32 new_barrier = be32_to_cpu(new->stateid.seqid);
+
+                       if ((int)(new_barrier - lo->plh_barrier))
+                               lo->plh_barrier = new_barrier;
+               } else {
+                       /* Because of wraparound, we want to keep the barrier
+                        * "close" to the current seqids.  It needs to be
+                        * within 2**31 to count as "behind", so if it
+                        * gets too near that limit, give us a litle leeway
+                        * and bring it to within 2**30.
+                        * NOTE - and yes, this is all unsigned arithmetic.
+                        */
+                       if (unlikely((newseq - lo->plh_barrier) > (3 << 29)))
+                               lo->plh_barrier = newseq - (1 << 30);
+               }
        }
-       if (overwrite)
-               memcpy(&old->stateid, &new->stateid, sizeof(new->stateid));
-       write_sequnlock(&lo->seqlock);
 }
 
-static void
-pnfs_layout_from_open_stateid(struct pnfs_layout_hdr *lo,
-                             struct nfs4_state *state)
+/* lget is set to 1 if called from inside send_layoutget call chain */
+static bool
+pnfs_layoutgets_blocked(struct pnfs_layout_hdr *lo, nfs4_stateid *stateid,
+                       int lget)
 {
-       int seq;
-
-       dprintk("--> %s\n", __func__);
-       write_seqlock(&lo->seqlock);
-       do {
-               seq = read_seqbegin(&state->seqlock);
-               memcpy(lo->stateid.data, state->stateid.data,
-                      sizeof(state->stateid.data));
-       } while (read_seqretry(&state->seqlock, seq));
-       set_bit(NFS_LAYOUT_STATEID_SET, &lo->state);
-       write_sequnlock(&lo->seqlock);
-       dprintk("<-- %s\n", __func__);
+       if ((stateid) &&
+           (int)(lo->plh_barrier - be32_to_cpu(stateid->stateid.seqid)) >= 0)
+               return true;
+       return lo->plh_block_lgets ||
+               test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags) ||
+               (list_empty(&lo->plh_segs) &&
+                (atomic_read(&lo->plh_outstanding) > lget));
 }
 
-void
-pnfs_get_layout_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo,
-                       struct nfs4_state *open_state)
+int
+pnfs_choose_layoutget_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo,
+                             struct nfs4_state *open_state)
 {
-       int seq;
+       int status = 0;
 
        dprintk("--> %s\n", __func__);
-       do {
-               seq = read_seqbegin(&lo->seqlock);
-               if (!test_bit(NFS_LAYOUT_STATEID_SET, &lo->state)) {
-                       /* This will trigger retry of the read */
-                       pnfs_layout_from_open_stateid(lo, open_state);
-               } else
-                       memcpy(dst->data, lo->stateid.data,
-                              sizeof(lo->stateid.data));
-       } while (read_seqretry(&lo->seqlock, seq));
+       spin_lock(&lo->plh_inode->i_lock);
+       if (pnfs_layoutgets_blocked(lo, NULL, 1)) {
+               status = -EAGAIN;
+       } else if (list_empty(&lo->plh_segs)) {
+               int seq;
+
+               do {
+                       seq = read_seqbegin(&open_state->seqlock);
+                       memcpy(dst->data, open_state->stateid.data,
+                              sizeof(open_state->stateid.data));
+               } while (read_seqretry(&open_state->seqlock, seq));
+       } else
+               memcpy(dst->data, lo->plh_stateid.data, sizeof(lo->plh_stateid.data));
+       spin_unlock(&lo->plh_inode->i_lock);
        dprintk("<-- %s\n", __func__);
+       return status;
 }
 
 /*
@@ -395,7 +444,7 @@ send_layoutget(struct pnfs_layout_hdr *lo,
           struct nfs_open_context *ctx,
           u32 iomode)
 {
-       struct inode *ino = lo->inode;
+       struct inode *ino = lo->plh_inode;
        struct nfs_server *server = NFS_SERVER(ino);
        struct nfs4_layoutget *lgp;
        struct pnfs_layout_segment *lseg = NULL;
@@ -404,10 +453,8 @@ send_layoutget(struct pnfs_layout_hdr *lo,
 
        BUG_ON(ctx == NULL);
        lgp = kzalloc(sizeof(*lgp), GFP_KERNEL);
-       if (lgp == NULL) {
-               put_layout_hdr(lo->inode);
+       if (lgp == NULL)
                return NULL;
-       }
        lgp->args.minlength = NFS4_MAX_UINT64;
        lgp->args.maxcount = PNFS_LAYOUT_MAXSIZE;
        lgp->args.range.iomode = iomode;
@@ -424,11 +471,88 @@ send_layoutget(struct pnfs_layout_hdr *lo,
        nfs4_proc_layoutget(lgp);
        if (!lseg) {
                /* remember that LAYOUTGET failed and suspend trying */
-               set_bit(lo_fail_bit(iomode), &lo->state);
+               set_bit(lo_fail_bit(iomode), &lo->plh_flags);
        }
        return lseg;
 }
 
+bool pnfs_roc(struct inode *ino)
+{
+       struct pnfs_layout_hdr *lo;
+       struct pnfs_layout_segment *lseg, *tmp;
+       LIST_HEAD(tmp_list);
+       bool found = false;
+
+       spin_lock(&ino->i_lock);
+       lo = NFS_I(ino)->layout;
+       if (!lo || !test_and_clear_bit(NFS_LAYOUT_ROC, &lo->plh_flags) ||
+           test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags))
+               goto out_nolayout;
+       list_for_each_entry_safe(lseg, tmp, &lo->plh_segs, pls_list)
+               if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) {
+                       mark_lseg_invalid(lseg, &tmp_list);
+                       found = true;
+               }
+       if (!found)
+               goto out_nolayout;
+       lo->plh_block_lgets++;
+       get_layout_hdr(lo); /* matched in pnfs_roc_release */
+       spin_unlock(&ino->i_lock);
+       pnfs_free_lseg_list(&tmp_list);
+       return true;
+
+out_nolayout:
+       spin_unlock(&ino->i_lock);
+       return false;
+}
+
+void pnfs_roc_release(struct inode *ino)
+{
+       struct pnfs_layout_hdr *lo;
+
+       spin_lock(&ino->i_lock);
+       lo = NFS_I(ino)->layout;
+       lo->plh_block_lgets--;
+       put_layout_hdr_locked(lo);
+       spin_unlock(&ino->i_lock);
+}
+
+void pnfs_roc_set_barrier(struct inode *ino, u32 barrier)
+{
+       struct pnfs_layout_hdr *lo;
+
+       spin_lock(&ino->i_lock);
+       lo = NFS_I(ino)->layout;
+       if ((int)(barrier - lo->plh_barrier) > 0)
+               lo->plh_barrier = barrier;
+       spin_unlock(&ino->i_lock);
+}
+
+bool pnfs_roc_drain(struct inode *ino, u32 *barrier)
+{
+       struct nfs_inode *nfsi = NFS_I(ino);
+       struct pnfs_layout_segment *lseg;
+       bool found = false;
+
+       spin_lock(&ino->i_lock);
+       list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list)
+               if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) {
+                       found = true;
+                       break;
+               }
+       if (!found) {
+               struct pnfs_layout_hdr *lo = nfsi->layout;
+               u32 current_seqid = be32_to_cpu(lo->plh_stateid.stateid.seqid);
+
+               /* Since close does not return a layout stateid for use as
+                * a barrier, we choose the worst-case barrier.
+                */
+               *barrier = current_seqid + atomic_read(&lo->plh_outstanding);
+       }
+       spin_unlock(&ino->i_lock);
+       return found;
+}
+
 /*
  * Compare two layout segments for sorting into layout cache.
  * We want to preferentially return RW over RO layouts, so ensure those
@@ -450,37 +574,29 @@ pnfs_insert_layout(struct pnfs_layout_hdr *lo,
 
        dprintk("%s:Begin\n", __func__);
 
-       assert_spin_locked(&lo->inode->i_lock);
-       if (list_empty(&lo->segs)) {
-               struct nfs_client *clp = NFS_SERVER(lo->inode)->nfs_client;
-
-               spin_lock(&clp->cl_lock);
-               BUG_ON(!list_empty(&lo->layouts));
-               list_add_tail(&lo->layouts, &clp->cl_layouts);
-               spin_unlock(&clp->cl_lock);
-       }
-       list_for_each_entry(lp, &lo->segs, fi_list) {
-               if (cmp_layout(lp->range.iomode, lseg->range.iomode) > 0)
+       assert_spin_locked(&lo->plh_inode->i_lock);
+       list_for_each_entry(lp, &lo->plh_segs, pls_list) {
+               if (cmp_layout(lp->pls_range.iomode, lseg->pls_range.iomode) > 0)
                        continue;
-               list_add_tail(&lseg->fi_list, &lp->fi_list);
+               list_add_tail(&lseg->pls_list, &lp->pls_list);
                dprintk("%s: inserted lseg %p "
                        "iomode %d offset %llu length %llu before "
                        "lp %p iomode %d offset %llu length %llu\n",
-                       __func__, lseg, lseg->range.iomode,
-                       lseg->range.offset, lseg->range.length,
-                       lp, lp->range.iomode, lp->range.offset,
-                       lp->range.length);
+                       __func__, lseg, lseg->pls_range.iomode,
+                       lseg->pls_range.offset, lseg->pls_range.length,
+                       lp, lp->pls_range.iomode, lp->pls_range.offset,
+                       lp->pls_range.length);
                found = 1;
                break;
        }
        if (!found) {
-               list_add_tail(&lseg->fi_list, &lo->segs);
+               list_add_tail(&lseg->pls_list, &lo->plh_segs);
                dprintk("%s: inserted lseg %p "
                        "iomode %d offset %llu length %llu at tail\n",
-                       __func__, lseg, lseg->range.iomode,
-                       lseg->range.offset, lseg->range.length);
+                       __func__, lseg, lseg->pls_range.iomode,
+                       lseg->pls_range.offset, lseg->pls_range.length);
        }
-       get_layout_hdr_locked(lo);
+       get_layout_hdr(lo);
 
        dprintk("%s:Return\n", __func__);
 }
@@ -493,11 +609,11 @@ alloc_init_layout_hdr(struct inode *ino)
        lo = kzalloc(sizeof(struct pnfs_layout_hdr), GFP_KERNEL);
        if (!lo)
                return NULL;
-       lo->refcount = 1;
-       INIT_LIST_HEAD(&lo->layouts);
-       INIT_LIST_HEAD(&lo->segs);
-       seqlock_init(&lo->seqlock);
-       lo->inode = ino;
+       atomic_set(&lo->plh_refcount, 1);
+       INIT_LIST_HEAD(&lo->plh_layouts);
+       INIT_LIST_HEAD(&lo->plh_segs);
+       INIT_LIST_HEAD(&lo->plh_bulk_recall);
+       lo->plh_inode = ino;
        return lo;
 }
 
@@ -510,9 +626,12 @@ pnfs_find_alloc_layout(struct inode *ino)
        dprintk("%s Begin ino=%p layout=%p\n", __func__, ino, nfsi->layout);
 
        assert_spin_locked(&ino->i_lock);
-       if (nfsi->layout)
-               return nfsi->layout;
-
+       if (nfsi->layout) {
+               if (test_bit(NFS_LAYOUT_DESTROYED, &nfsi->layout->plh_flags))
+                       return NULL;
+               else
+                       return nfsi->layout;
+       }
        spin_unlock(&ino->i_lock);
        new = alloc_init_layout_hdr(ino);
        spin_lock(&ino->i_lock);
@@ -538,31 +657,32 @@ pnfs_find_alloc_layout(struct inode *ino)
 static int
 is_matching_lseg(struct pnfs_layout_segment *lseg, u32 iomode)
 {
-       return (iomode != IOMODE_RW || lseg->range.iomode == IOMODE_RW);
+       return (iomode != IOMODE_RW || lseg->pls_range.iomode == IOMODE_RW);
 }
 
 /*
  * lookup range in layout
  */
 static struct pnfs_layout_segment *
-pnfs_has_layout(struct pnfs_layout_hdr *lo, u32 iomode)
+pnfs_find_lseg(struct pnfs_layout_hdr *lo, u32 iomode)
 {
        struct pnfs_layout_segment *lseg, *ret = NULL;
 
        dprintk("%s:Begin\n", __func__);
 
-       assert_spin_locked(&lo->inode->i_lock);
-       list_for_each_entry(lseg, &lo->segs, fi_list) {
-               if (is_matching_lseg(lseg, iomode)) {
+       assert_spin_locked(&lo->plh_inode->i_lock);
+       list_for_each_entry(lseg, &lo->plh_segs, pls_list) {
+               if (test_bit(NFS_LSEG_VALID, &lseg->pls_flags) &&
+                   is_matching_lseg(lseg, iomode)) {
                        ret = lseg;
                        break;
                }
-               if (cmp_layout(iomode, lseg->range.iomode) > 0)
+               if (cmp_layout(iomode, lseg->pls_range.iomode) > 0)
                        break;
        }
 
        dprintk("%s:Return lseg %p ref %d\n",
-               __func__, ret, ret ? atomic_read(&ret->kref.refcount) : 0);
+               __func__, ret, ret ? atomic_read(&ret->pls_refcount) : 0);
        return ret;
 }
 
@@ -576,6 +696,7 @@ pnfs_update_layout(struct inode *ino,
                   enum pnfs_iomode iomode)
 {
        struct nfs_inode *nfsi = NFS_I(ino);
+       struct nfs_client *clp = NFS_SERVER(ino)->nfs_client;
        struct pnfs_layout_hdr *lo;
        struct pnfs_layout_segment *lseg = NULL;
 
@@ -588,25 +709,53 @@ pnfs_update_layout(struct inode *ino,
                goto out_unlock;
        }
 
-       /* Check to see if the layout for the given range already exists */
-       lseg = pnfs_has_layout(lo, iomode);
-       if (lseg) {
-               dprintk("%s: Using cached lseg %p for iomode %d)\n",
-                       __func__, lseg, iomode);
+       /* Do we even need to bother with this? */
+       if (test_bit(NFS4CLNT_LAYOUTRECALL, &clp->cl_state) ||
+           test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) {
+               dprintk("%s matches recall, use MDS\n", __func__);
                goto out_unlock;
        }
+       /* Check to see if the layout for the given range already exists */
+       lseg = pnfs_find_lseg(lo, iomode);
+       if (lseg)
+               goto out_unlock;
 
        /* if LAYOUTGET already failed once we don't try again */
-       if (test_bit(lo_fail_bit(iomode), &nfsi->layout->state))
+       if (test_bit(lo_fail_bit(iomode), &nfsi->layout->plh_flags))
+               goto out_unlock;
+
+       if (pnfs_layoutgets_blocked(lo, NULL, 0))
                goto out_unlock;
+       atomic_inc(&lo->plh_outstanding);
 
-       get_layout_hdr_locked(lo); /* Matched in nfs4_layoutget_release */
+       get_layout_hdr(lo);
+       if (list_empty(&lo->plh_segs)) {
+               /* The lo must be on the clp list if there is any
+                * chance of a CB_LAYOUTRECALL(FILE) coming in.
+                */
+               spin_lock(&clp->cl_lock);
+               BUG_ON(!list_empty(&lo->plh_layouts));
+               list_add_tail(&lo->plh_layouts, &clp->cl_layouts);
+               spin_unlock(&clp->cl_lock);
+       }
        spin_unlock(&ino->i_lock);
 
        lseg = send_layoutget(lo, ctx, iomode);
+       if (!lseg) {
+               spin_lock(&ino->i_lock);
+               if (list_empty(&lo->plh_segs)) {
+                       spin_lock(&clp->cl_lock);
+                       list_del_init(&lo->plh_layouts);
+                       spin_unlock(&clp->cl_lock);
+                       clear_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
+               }
+               spin_unlock(&ino->i_lock);
+       }
+       atomic_dec(&lo->plh_outstanding);
+       put_layout_hdr(lo);
 out:
        dprintk("%s end, state 0x%lx lseg %p\n", __func__,
-               nfsi->layout->state, lseg);
+               nfsi->layout->plh_flags, lseg);
        return lseg;
 out_unlock:
        spin_unlock(&ino->i_lock);
@@ -619,9 +768,21 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
        struct pnfs_layout_hdr *lo = NFS_I(lgp->args.inode)->layout;
        struct nfs4_layoutget_res *res = &lgp->res;
        struct pnfs_layout_segment *lseg;
-       struct inode *ino = lo->inode;
+       struct inode *ino = lo->plh_inode;
+       struct nfs_client *clp = NFS_SERVER(ino)->nfs_client;
        int status = 0;
 
+       /* Verify we got what we asked for.
+        * Note that because the xdr parsing only accepts a single
+        * element array, this can fail even if the server is behaving
+        * correctly.
+        */
+       if (lgp->args.range.iomode > res->range.iomode ||
+           res->range.offset != 0 ||
+           res->range.length != NFS4_MAX_UINT64) {
+               status = -EINVAL;
+               goto out;
+       }
        /* Inject layout blob into I/O device driver */
        lseg = NFS_SERVER(ino)->pnfs_curr_ld->alloc_lseg(lo, res);
        if (!lseg || IS_ERR(lseg)) {
@@ -635,16 +796,37 @@ pnfs_layout_process(struct nfs4_layoutget *lgp)
        }
 
        spin_lock(&ino->i_lock);
+       if (test_bit(NFS4CLNT_LAYOUTRECALL, &clp->cl_state) ||
+           test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) {
+               dprintk("%s forget reply due to recall\n", __func__);
+               goto out_forget_reply;
+       }
+
+       if (pnfs_layoutgets_blocked(lo, &res->stateid, 1)) {
+               dprintk("%s forget reply due to state\n", __func__);
+               goto out_forget_reply;
+       }
        init_lseg(lo, lseg);
-       lseg->range = res->range;
+       lseg->pls_range = res->range;
        *lgp->lsegpp = lseg;
        pnfs_insert_layout(lo, lseg);
 
+       if (res->return_on_close) {
+               set_bit(NFS_LSEG_ROC, &lseg->pls_flags);
+               set_bit(NFS_LAYOUT_ROC, &lo->plh_flags);
+       }
+
        /* Done processing layoutget. Set the layout stateid */
-       pnfs_set_layout_stateid(lo, &res->stateid);
+       pnfs_set_layout_stateid(lo, &res->stateid, false);
        spin_unlock(&ino->i_lock);
 out:
        return status;
+
+out_forget_reply:
+       spin_unlock(&ino->i_lock);
+       lseg->pls_layout = lo;
+       NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg);
+       goto out;
 }
 
 /*
index e12367d..e2612ea 100644 (file)
 #ifndef FS_NFS_PNFS_H
 #define FS_NFS_PNFS_H
 
+enum {
+       NFS_LSEG_VALID = 0,     /* cleared when lseg is recalled/returned */
+       NFS_LSEG_ROC,           /* roc bit received from server */
+};
+
 struct pnfs_layout_segment {
-       struct list_head fi_list;
-       struct pnfs_layout_range range;
-       struct kref kref;
-       struct pnfs_layout_hdr *layout;
+       struct list_head pls_list;
+       struct pnfs_layout_range pls_range;
+       atomic_t pls_refcount;
+       unsigned long pls_flags;
+       struct pnfs_layout_hdr *pls_layout;
 };
 
 #ifdef CONFIG_NFS_V4_1
@@ -44,7 +50,9 @@ struct pnfs_layout_segment {
 enum {
        NFS_LAYOUT_RO_FAILED = 0,       /* get ro layout failed stop trying */
        NFS_LAYOUT_RW_FAILED,           /* get rw layout failed stop trying */
-       NFS_LAYOUT_STATEID_SET,         /* have a valid layout stateid */
+       NFS_LAYOUT_BULK_RECALL,         /* bulk recall affecting layout */
+       NFS_LAYOUT_ROC,                 /* some lseg had roc bit set */
+       NFS_LAYOUT_DESTROYED,           /* no new use of layout allowed */
 };
 
 /* Per-layout driver specific registration structure */
@@ -60,13 +68,16 @@ struct pnfs_layoutdriver_type {
 };
 
 struct pnfs_layout_hdr {
-       unsigned long           refcount;
-       struct list_head        layouts;   /* other client layouts */
-       struct list_head        segs;      /* layout segments list */
-       seqlock_t               seqlock;   /* Protects the stateid */
-       nfs4_stateid            stateid;
-       unsigned long           state;
-       struct inode            *inode;
+       atomic_t                plh_refcount;
+       struct list_head        plh_layouts;   /* other client layouts */
+       struct list_head        plh_bulk_recall; /* clnt list of bulk recalls */
+       struct list_head        plh_segs;      /* layout segments list */
+       nfs4_stateid            plh_stateid;
+       atomic_t                plh_outstanding; /* number of RPCs out */
+       unsigned long           plh_block_lgets; /* block LAYOUTGET if >0 */
+       u32                     plh_barrier; /* ignore lower seqids */
+       unsigned long           plh_flags;
+       struct inode            *plh_inode;
 };
 
 struct pnfs_device {
@@ -134,17 +145,30 @@ extern int nfs4_proc_getdeviceinfo(struct nfs_server *server,
 extern int nfs4_proc_layoutget(struct nfs4_layoutget *lgp);
 
 /* pnfs.c */
+void get_layout_hdr(struct pnfs_layout_hdr *lo);
 struct pnfs_layout_segment *
 pnfs_update_layout(struct inode *ino, struct nfs_open_context *ctx,
                   enum pnfs_iomode access_type);
 void set_pnfs_layoutdriver(struct nfs_server *, u32 id);
 void unset_pnfs_layoutdriver(struct nfs_server *);
 int pnfs_layout_process(struct nfs4_layoutget *lgp);
+void pnfs_free_lseg_list(struct list_head *tmp_list);
 void pnfs_destroy_layout(struct nfs_inode *);
 void pnfs_destroy_all_layouts(struct nfs_client *);
-void put_layout_hdr(struct inode *inode);
-void pnfs_get_layout_stateid(nfs4_stateid *dst, struct pnfs_layout_hdr *lo,
-                            struct nfs4_state *open_state);
+void put_layout_hdr(struct pnfs_layout_hdr *lo);
+void pnfs_set_layout_stateid(struct pnfs_layout_hdr *lo,
+                            const nfs4_stateid *new,
+                            bool update_barrier);
+int pnfs_choose_layoutget_stateid(nfs4_stateid *dst,
+                                 struct pnfs_layout_hdr *lo,
+                                 struct nfs4_state *open_state);
+int mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
+                               struct list_head *tmp_list,
+                               u32 iomode);
+bool pnfs_roc(struct inode *ino);
+void pnfs_roc_release(struct inode *ino);
+void pnfs_roc_set_barrier(struct inode *ino, u32 barrier);
+bool pnfs_roc_drain(struct inode *ino, u32 *barrier);
 
 
 static inline int lo_fail_bit(u32 iomode)
@@ -176,6 +200,28 @@ pnfs_update_layout(struct inode *ino, struct nfs_open_context *ctx,
        return NULL;
 }
 
+static inline bool
+pnfs_roc(struct inode *ino)
+{
+       return false;
+}
+
+static inline void
+pnfs_roc_release(struct inode *ino)
+{
+}
+
+static inline void
+pnfs_roc_set_barrier(struct inode *ino, u32 barrier)
+{
+}
+
+static inline bool
+pnfs_roc_drain(struct inode *ino, u32 *barrier)
+{
+       return false;
+}
+
 static inline void set_pnfs_layoutdriver(struct nfs_server *s, u32 id)
 {
 }
index 58e7f84..77d5e21 100644 (file)
@@ -458,7 +458,7 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
        fattr = nfs_alloc_fattr();
        status = -ENOMEM;
        if (fh == NULL || fattr == NULL)
-               goto out;
+               goto out_free;
 
        status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
        nfs_mark_for_revalidate(dir);
@@ -471,6 +471,7 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
        if (status == 0)
                status = nfs_instantiate(dentry, fh, fattr);
 
+out_free:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fh);
 out:
@@ -731,7 +732,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
        .statfs         = nfs_proc_statfs,
        .fsinfo         = nfs_proc_fsinfo,
        .pathconf       = nfs_proc_pathconf,
-       .decode_dirent  = nfs_decode_dirent,
+       .decode_dirent  = nfs2_decode_dirent,
        .read_setup     = nfs_proc_read_setup,
        .read_done      = nfs_read_done,
        .write_setup    = nfs_proc_write_setup,
index 4100630..0f9ea73 100644 (file)
@@ -598,7 +598,9 @@ static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss,
 
        if (nfss->mountd_version || showdefaults)
                seq_printf(m, ",mountvers=%u", nfss->mountd_version);
-       if (nfss->mountd_port || showdefaults)
+       if ((nfss->mountd_port &&
+               nfss->mountd_port != (unsigned short)NFS_UNSPEC_PORT) ||
+               showdefaults)
                seq_printf(m, ",mountport=%u", nfss->mountd_port);
 
        nfs_show_mountd_netid(m, nfss, showdefaults);
@@ -2494,7 +2496,13 @@ static void nfs4_clone_super(struct super_block *sb,
        sb->s_maxbytes = old_sb->s_maxbytes;
        sb->s_time_gran = 1;
        sb->s_op = old_sb->s_op;
-       nfs_initialise_sb(sb);
+       /*
+        * The VFS shouldn't apply the umask to mode bits. We will do
+        * so ourselves when necessary.
+        */
+       sb->s_flags  |= MS_POSIXACL;
+       sb->s_xattr  = old_sb->s_xattr;
+       nfs_initialise_sb(sb);
 }
 
 /*
@@ -2504,6 +2512,12 @@ static void nfs4_fill_super(struct super_block *sb)
 {
        sb->s_time_gran = 1;
        sb->s_op = &nfs4_sops;
+       /*
+        * The VFS shouldn't apply the umask to mode bits. We will do
+        * so ourselves when necessary.
+        */
+       sb->s_flags  |= MS_POSIXACL;
+       sb->s_xattr = nfs4_xattr_handlers;
        nfs_initialise_sb(sb);
 }
 
index 7bdec85..3bf1e53 100644 (file)
@@ -429,7 +429,7 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (data == NULL)
                return ERR_PTR(-ENOMEM);
-       task_setup_data.callback_data = data,
+       task_setup_data.callback_data = data;
 
        data->cred = rpc_lookup_cred();
        if (IS_ERR(data->cred)) {
index 143da2e..21a63da 100644 (file)
@@ -50,11 +50,6 @@ enum {
        NFSPROC4_CLNT_CB_SEQUENCE,
 };
 
-enum nfs_cb_opnum4 {
-       OP_CB_RECALL            = 4,
-       OP_CB_SEQUENCE          = 11,
-};
-
 #define NFS4_MAXTAGLEN         20
 
 #define NFS4_enc_cb_null_sz            0
@@ -79,61 +74,6 @@ enum nfs_cb_opnum4 {
                                        cb_sequence_dec_sz +            \
                                        op_dec_sz)
 
-/*
-* Generic encode routines from fs/nfs/nfs4xdr.c
-*/
-static inline __be32 *
-xdr_writemem(__be32 *p, const void *ptr, int nbytes)
-{
-       int tmp = XDR_QUADLEN(nbytes);
-       if (!tmp)
-               return p;
-       p[tmp-1] = 0;
-       memcpy(p, ptr, nbytes);
-       return p + tmp;
-}
-
-#define WRITE32(n)               *p++ = htonl(n)
-#define WRITEMEM(ptr,nbytes)     do {                           \
-       p = xdr_writemem(p, ptr, nbytes);                       \
-} while (0)
-#define RESERVE_SPACE(nbytes)   do {                            \
-       p = xdr_reserve_space(xdr, nbytes);                     \
-       if (!p) dprintk("NFSD: RESERVE_SPACE(%d) failed in function %s\n", (int) (nbytes), __func__); \
-       BUG_ON(!p);                                             \
-} while (0)
-
-/*
- * Generic decode routines from fs/nfs/nfs4xdr.c
- */
-#define DECODE_TAIL                             \
-       status = 0;                             \
-out:                                            \
-       return status;                          \
-xdr_error:                                      \
-       dprintk("NFSD: xdr error! (%s:%d)\n", __FILE__, __LINE__); \
-       status = -EIO;                          \
-       goto out
-
-#define READ32(x)         (x) = ntohl(*p++)
-#define READ64(x)         do {                  \
-       (x) = (u64)ntohl(*p++) << 32;           \
-       (x) |= ntohl(*p++);                     \
-} while (0)
-#define READTIME(x)       do {                  \
-       p++;                                    \
-       (x.tv_sec) = ntohl(*p++);               \
-       (x.tv_nsec) = ntohl(*p++);              \
-} while (0)
-#define READ_BUF(nbytes)  do { \
-       p = xdr_inline_decode(xdr, nbytes); \
-       if (!p) { \
-               dprintk("NFSD: %s: reply buffer overflowed in line %d.\n", \
-                       __func__, __LINE__); \
-               return -EIO; \
-       } \
-} while (0)
-
 struct nfs4_cb_compound_hdr {
        /* args */
        u32             ident;  /* minorversion 0 only */
@@ -144,295 +84,513 @@ struct nfs4_cb_compound_hdr {
        int             status;
 };
 
-static struct {
-int stat;
-int errno;
-} nfs_cb_errtbl[] = {
-       { NFS4_OK,              0               },
-       { NFS4ERR_PERM,         EPERM           },
-       { NFS4ERR_NOENT,        ENOENT          },
-       { NFS4ERR_IO,           EIO             },
-       { NFS4ERR_NXIO,         ENXIO           },
-       { NFS4ERR_ACCESS,       EACCES          },
-       { NFS4ERR_EXIST,        EEXIST          },
-       { NFS4ERR_XDEV,         EXDEV           },
-       { NFS4ERR_NOTDIR,       ENOTDIR         },
-       { NFS4ERR_ISDIR,        EISDIR          },
-       { NFS4ERR_INVAL,        EINVAL          },
-       { NFS4ERR_FBIG,         EFBIG           },
-       { NFS4ERR_NOSPC,        ENOSPC          },
-       { NFS4ERR_ROFS,         EROFS           },
-       { NFS4ERR_MLINK,        EMLINK          },
-       { NFS4ERR_NAMETOOLONG,  ENAMETOOLONG    },
-       { NFS4ERR_NOTEMPTY,     ENOTEMPTY       },
-       { NFS4ERR_DQUOT,        EDQUOT          },
-       { NFS4ERR_STALE,        ESTALE          },
-       { NFS4ERR_BADHANDLE,    EBADHANDLE      },
-       { NFS4ERR_BAD_COOKIE,   EBADCOOKIE      },
-       { NFS4ERR_NOTSUPP,      ENOTSUPP        },
-       { NFS4ERR_TOOSMALL,     ETOOSMALL       },
-       { NFS4ERR_SERVERFAULT,  ESERVERFAULT    },
-       { NFS4ERR_BADTYPE,      EBADTYPE        },
-       { NFS4ERR_LOCKED,       EAGAIN          },
-       { NFS4ERR_RESOURCE,     EREMOTEIO       },
-       { NFS4ERR_SYMLINK,      ELOOP           },
-       { NFS4ERR_OP_ILLEGAL,   EOPNOTSUPP      },
-       { NFS4ERR_DEADLOCK,     EDEADLK         },
-       { -1,                   EIO             }
-};
+/*
+ * Handle decode buffer overflows out-of-line.
+ */
+static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
+{
+       dprintk("NFS: %s prematurely hit the end of our receive buffer. "
+               "Remaining buffer length is %tu words.\n",
+               func, xdr->end - xdr->p);
+}
 
-static int
-nfs_cb_stat_to_errno(int stat)
+static __be32 *xdr_encode_empty_array(__be32 *p)
 {
-       int i;
-       for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
-               if (nfs_cb_errtbl[i].stat == stat)
-                       return nfs_cb_errtbl[i].errno;
-       }
-       /* If we cannot translate the error, the recovery routines should
-       * handle it.
-       * Note: remaining NFSv4 error codes have values > 10000, so should
-       * not conflict with native Linux error codes.
-       */
-       return stat;
+       *p++ = xdr_zero;
+       return p;
 }
 
 /*
- * XDR encode
+ * Encode/decode NFSv4 CB basic data types
+ *
+ * Basic NFSv4 callback data types are defined in section 15 of RFC
+ * 3530: "Network File System (NFS) version 4 Protocol" and section
+ * 20 of RFC 5661: "Network File System (NFS) Version 4 Minor Version
+ * 1 Protocol"
+ */
+
+/*
+ *     nfs_cb_opnum4
+ *
+ *     enum nfs_cb_opnum4 {
+ *             OP_CB_GETATTR           = 3,
+ *               ...
+ *     };
  */
+enum nfs_cb_opnum4 {
+       OP_CB_GETATTR                   = 3,
+       OP_CB_RECALL                    = 4,
+       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
+};
 
-static void
-encode_stateid(struct xdr_stream *xdr, stateid_t *sid)
+static void encode_nfs_cb_opnum4(struct xdr_stream *xdr, enum nfs_cb_opnum4 op)
 {
        __be32 *p;
 
-       RESERVE_SPACE(sizeof(stateid_t));
-       WRITE32(sid->si_generation);
-       WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
+       p = xdr_reserve_space(xdr, 4);
+       *p = cpu_to_be32(op);
 }
 
-static void
-encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
+/*
+ * nfs_fh4
+ *
+ *     typedef opaque nfs_fh4<NFS4_FHSIZE>;
+ */
+static void encode_nfs_fh4(struct xdr_stream *xdr, const struct knfsd_fh *fh)
 {
-       __be32 * p;
+       u32 length = fh->fh_size;
+       __be32 *p;
 
-       RESERVE_SPACE(16);
-       WRITE32(0);            /* tag length is always 0 */
-       WRITE32(hdr->minorversion);
-       WRITE32(hdr->ident);
-       hdr->nops_p = p;
-       WRITE32(hdr->nops);
+       BUG_ON(length > NFS4_FHSIZE);
+       p = xdr_reserve_space(xdr, 4 + length);
+       xdr_encode_opaque(p, &fh->fh_base, length);
 }
 
-static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr)
+/*
+ * stateid4
+ *
+ *     struct stateid4 {
+ *             uint32_t        seqid;
+ *             opaque          other[12];
+ *     };
+ */
+static void encode_stateid4(struct xdr_stream *xdr, const stateid_t *sid)
 {
-       *hdr->nops_p = htonl(hdr->nops);
+       __be32 *p;
+
+       p = xdr_reserve_space(xdr, NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(sid->si_generation);
+       xdr_encode_opaque_fixed(p, &sid->si_opaque, NFS4_STATEID_OTHER_SIZE);
 }
 
-static void
-encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
-               struct nfs4_cb_compound_hdr *hdr)
+/*
+ * sessionid4
+ *
+ *     typedef opaque sessionid4[NFS4_SESSIONID_SIZE];
+ */
+static void encode_sessionid4(struct xdr_stream *xdr,
+                             const struct nfsd4_session *session)
 {
        __be32 *p;
-       int len = dp->dl_fh.fh_size;
-
-       RESERVE_SPACE(4);
-       WRITE32(OP_CB_RECALL);
-       encode_stateid(xdr, &dp->dl_stateid);
-       RESERVE_SPACE(8 + (XDR_QUADLEN(len) << 2));
-       WRITE32(0); /* truncate optimization not implemented */
-       WRITE32(len);
-       WRITEMEM(&dp->dl_fh.fh_base, len);
-       hdr->nops++;
+
+       p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN);
+       xdr_encode_opaque_fixed(p, session->se_sessionid.data,
+                                       NFS4_MAX_SESSIONID_LEN);
 }
 
-static void
-encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
-                  struct nfs4_cb_compound_hdr *hdr)
-{
-       __be32 *p;
-       struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
+/*
+ * nfsstat4
+ */
+static const struct {
+       int stat;
+       int errno;
+} nfs_cb_errtbl[] = {
+       { NFS4_OK,              0               },
+       { NFS4ERR_PERM,         -EPERM          },
+       { NFS4ERR_NOENT,        -ENOENT         },
+       { NFS4ERR_IO,           -EIO            },
+       { NFS4ERR_NXIO,         -ENXIO          },
+       { NFS4ERR_ACCESS,       -EACCES         },
+       { NFS4ERR_EXIST,        -EEXIST         },
+       { NFS4ERR_XDEV,         -EXDEV          },
+       { NFS4ERR_NOTDIR,       -ENOTDIR        },
+       { NFS4ERR_ISDIR,        -EISDIR         },
+       { NFS4ERR_INVAL,        -EINVAL         },
+       { NFS4ERR_FBIG,         -EFBIG          },
+       { NFS4ERR_NOSPC,        -ENOSPC         },
+       { NFS4ERR_ROFS,         -EROFS          },
+       { NFS4ERR_MLINK,        -EMLINK         },
+       { NFS4ERR_NAMETOOLONG,  -ENAMETOOLONG   },
+       { NFS4ERR_NOTEMPTY,     -ENOTEMPTY      },
+       { NFS4ERR_DQUOT,        -EDQUOT         },
+       { NFS4ERR_STALE,        -ESTALE         },
+       { NFS4ERR_BADHANDLE,    -EBADHANDLE     },
+       { NFS4ERR_BAD_COOKIE,   -EBADCOOKIE     },
+       { NFS4ERR_NOTSUPP,      -ENOTSUPP       },
+       { NFS4ERR_TOOSMALL,     -ETOOSMALL      },
+       { NFS4ERR_SERVERFAULT,  -ESERVERFAULT   },
+       { NFS4ERR_BADTYPE,      -EBADTYPE       },
+       { NFS4ERR_LOCKED,       -EAGAIN         },
+       { NFS4ERR_RESOURCE,     -EREMOTEIO      },
+       { NFS4ERR_SYMLINK,      -ELOOP          },
+       { NFS4ERR_OP_ILLEGAL,   -EOPNOTSUPP     },
+       { NFS4ERR_DEADLOCK,     -EDEADLK        },
+       { -1,                   -EIO            }
+};
 
-       if (hdr->minorversion == 0)
-               return;
+/*
+ * If we cannot translate the error, the recovery routines should
+ * handle it.
+ *
+ * Note: remaining NFSv4 error codes have values > 10000, so should
+ * not conflict with native Linux error codes.
+ */
+static int nfs_cb_stat_to_errno(int status)
+{
+       int i;
 
-       RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20);
+       for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) {
+               if (nfs_cb_errtbl[i].stat == status)
+                       return nfs_cb_errtbl[i].errno;
+       }
 
-       WRITE32(OP_CB_SEQUENCE);
-       WRITEMEM(ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN);
-       WRITE32(ses->se_cb_seq_nr);
-       WRITE32(0);             /* slotid, always 0 */
-       WRITE32(0);             /* highest slotid always 0 */
-       WRITE32(0);             /* cachethis always 0 */
-       WRITE32(0); /* FIXME: support referring_call_lists */
-       hdr->nops++;
+       dprintk("NFSD: Unrecognized NFS CB status value: %u\n", status);
+       return -status;
 }
 
-static int
-nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
+static int decode_cb_op_status(struct xdr_stream *xdr, enum nfs_opnum4 expected,
+                              enum nfsstat4 *status)
 {
-       struct xdr_stream xdrs, *xdr = &xdrs;
+       __be32 *p;
+       u32 op;
 
-       xdr_init_encode(&xdrs, &req->rq_snd_buf, p);
-        RESERVE_SPACE(0);
+       p = xdr_inline_decode(xdr, 4 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       op = be32_to_cpup(p++);
+       if (unlikely(op != expected))
+               goto out_unexpected;
+       *status = be32_to_cpup(p);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+out_unexpected:
+       dprintk("NFSD: Callback server returned operation %d but "
+               "we issued a request for %d\n", op, expected);
+       return -EIO;
 }
 
-static int
-nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
-               struct nfsd4_callback *cb)
+/*
+ * CB_COMPOUND4args
+ *
+ *     struct CB_COMPOUND4args {
+ *             utf8str_cs      tag;
+ *             uint32_t        minorversion;
+ *             uint32_t        callback_ident;
+ *             nfs_cb_argop4   argarray<>;
+ *     };
+*/
+static void encode_cb_compound4args(struct xdr_stream *xdr,
+                                   struct nfs4_cb_compound_hdr *hdr)
 {
-       struct xdr_stream xdr;
-       struct nfs4_delegation *args = cb->cb_op;
-       struct nfs4_cb_compound_hdr hdr = {
-               .ident = cb->cb_clp->cl_cb_ident,
-               .minorversion = cb->cb_minorversion,
-       };
+       __be32 * p;
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-       encode_cb_compound_hdr(&xdr, &hdr);
-       encode_cb_sequence(&xdr, cb, &hdr);
-       encode_cb_recall(&xdr, args, &hdr);
-       encode_cb_nops(&hdr);
+       p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
+       p = xdr_encode_empty_array(p);          /* empty tag */
+       *p++ = cpu_to_be32(hdr->minorversion);
+       *p++ = cpu_to_be32(hdr->ident);
+
+       hdr->nops_p = p;
+       *p = cpu_to_be32(hdr->nops);            /* argarray element count */
+}
+
+/*
+ * Update argarray element count
+ */
+static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr)
+{
+       BUG_ON(hdr->nops > NFS4_MAX_BACK_CHANNEL_OPS);
+       *hdr->nops_p = cpu_to_be32(hdr->nops);
+}
+
+/*
+ * CB_COMPOUND4res
+ *
+ *     struct CB_COMPOUND4res {
+ *             nfsstat4        status;
+ *             utf8str_cs      tag;
+ *             nfs_cb_resop4   resarray<>;
+ *     };
+ */
+static int decode_cb_compound4res(struct xdr_stream *xdr,
+                                 struct nfs4_cb_compound_hdr *hdr)
+{
+       u32 length;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, 4 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       hdr->status = be32_to_cpup(p++);
+       /* Ignore the tag */
+       length = be32_to_cpup(p++);
+       p = xdr_inline_decode(xdr, length + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
+       hdr->nops = be32_to_cpup(p);
        return 0;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
+/*
+ * CB_RECALL4args
+ *
+ *     struct CB_RECALL4args {
+ *             stateid4        stateid;
+ *             bool            truncate;
+ *             nfs_fh4         fh;
+ *     };
+ */
+static void encode_cb_recall4args(struct xdr_stream *xdr,
+                                 const struct nfs4_delegation *dp,
+                                 struct nfs4_cb_compound_hdr *hdr)
+{
+       __be32 *p;
+
+       encode_nfs_cb_opnum4(xdr, OP_CB_RECALL);
+       encode_stateid4(xdr, &dp->dl_stateid);
+
+       p = xdr_reserve_space(xdr, 4);
+       *p++ = xdr_zero;                        /* truncate */
 
-static int
-decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){
-        __be32 *p;
-       u32 taglen;
+       encode_nfs_fh4(xdr, &dp->dl_fh);
 
-        READ_BUF(8);
-        READ32(hdr->status);
-       /* We've got no use for the tag; ignore it: */
-        READ32(taglen);
-        READ_BUF(taglen + 4);
-        p += XDR_QUADLEN(taglen);
-        READ32(hdr->nops);
-        return 0;
+       hdr->nops++;
 }
 
-static int
-decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
+/*
+ * CB_SEQUENCE4args
+ *
+ *     struct CB_SEQUENCE4args {
+ *             sessionid4              csa_sessionid;
+ *             sequenceid4             csa_sequenceid;
+ *             slotid4                 csa_slotid;
+ *             slotid4                 csa_highest_slotid;
+ *             bool                    csa_cachethis;
+ *             referring_call_list4    csa_referring_call_lists<>;
+ *     };
+ */
+static void encode_cb_sequence4args(struct xdr_stream *xdr,
+                                   const struct nfsd4_callback *cb,
+                                   struct nfs4_cb_compound_hdr *hdr)
 {
+       struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
        __be32 *p;
-       u32 op;
-       int32_t nfserr;
-
-       READ_BUF(8);
-       READ32(op);
-       if (op != expected) {
-               dprintk("NFSD: decode_cb_op_hdr: Callback server returned "
-                        " operation %d but we issued a request for %d\n",
-                        op, expected);
-               return -EIO;
-       }
-       READ32(nfserr);
-       if (nfserr != NFS_OK)
-               return -nfs_cb_stat_to_errno(nfserr);
-       return 0;
+
+       if (hdr->minorversion == 0)
+               return;
+
+       encode_nfs_cb_opnum4(xdr, OP_CB_SEQUENCE);
+       encode_sessionid4(xdr, session);
+
+       p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4 + 4);
+       *p++ = cpu_to_be32(session->se_cb_seq_nr);      /* csa_sequenceid */
+       *p++ = xdr_zero;                        /* csa_slotid */
+       *p++ = xdr_zero;                        /* csa_highest_slotid */
+       *p++ = xdr_zero;                        /* csa_cachethis */
+       xdr_encode_empty_array(p);              /* csa_referring_call_lists */
+
+       hdr->nops++;
 }
 
 /*
+ * CB_SEQUENCE4resok
+ *
+ *     struct CB_SEQUENCE4resok {
+ *             sessionid4      csr_sessionid;
+ *             sequenceid4     csr_sequenceid;
+ *             slotid4         csr_slotid;
+ *             slotid4         csr_highest_slotid;
+ *             slotid4         csr_target_highest_slotid;
+ *     };
+ *
+ *     union CB_SEQUENCE4res switch (nfsstat4 csr_status) {
+ *     case NFS4_OK:
+ *             CB_SEQUENCE4resok       csr_resok4;
+ *     default:
+ *             void;
+ *     };
+ *
  * Our current back channel implmentation supports a single backchannel
  * with a single slot.
  */
-static int
-decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
-                  struct rpc_rqst *rqstp)
+static int decode_cb_sequence4resok(struct xdr_stream *xdr,
+                                   struct nfsd4_callback *cb)
 {
-       struct nfsd4_session *ses = cb->cb_clp->cl_cb_session;
+       struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
        struct nfs4_sessionid id;
        int status;
-       u32 dummy;
        __be32 *p;
+       u32 dummy;
 
-       if (cb->cb_minorversion == 0)
-               return 0;
-
-       status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE);
-       if (status)
-               return status;
+       status = -ESERVERFAULT;
 
        /*
         * If the server returns different values for sessionID, slotID or
         * sequence number, the server is looney tunes.
         */
-       status = -ESERVERFAULT;
-
-       READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
+       p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4);
+       if (unlikely(p == NULL))
+               goto out_overflow;
        memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
-       p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
-       if (memcmp(id.data, ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) {
-               dprintk("%s Invalid session id\n", __func__);
+       if (memcmp(id.data, session->se_sessionid.data,
+                                       NFS4_MAX_SESSIONID_LEN) != 0) {
+               dprintk("NFS: %s Invalid session id\n", __func__);
                goto out;
        }
-       READ32(dummy);
-       if (dummy != ses->se_cb_seq_nr) {
-               dprintk("%s Invalid sequence number\n", __func__);
+       p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
+
+       dummy = be32_to_cpup(p++);
+       if (dummy != session->se_cb_seq_nr) {
+               dprintk("NFS: %s Invalid sequence number\n", __func__);
                goto out;
        }
-       READ32(dummy);  /* slotid must be 0 */
+
+       dummy = be32_to_cpup(p++);
        if (dummy != 0) {
-               dprintk("%s Invalid slotid\n", __func__);
+               dprintk("NFS: %s Invalid slotid\n", __func__);
                goto out;
        }
-       /* FIXME: process highest slotid and target highest slotid */
+
+       /*
+        * FIXME: process highest slotid and target highest slotid
+        */
        status = 0;
 out:
        return status;
+out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
 }
 
+static int decode_cb_sequence4res(struct xdr_stream *xdr,
+                                 struct nfsd4_callback *cb)
+{
+       enum nfsstat4 nfserr;
+       int status;
+
+       if (cb->cb_minorversion == 0)
+               return 0;
+
+       status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &nfserr);
+       if (unlikely(status))
+               goto out;
+       if (unlikely(nfserr != NFS4_OK))
+               goto out_default;
+       status = decode_cb_sequence4resok(xdr, cb);
+out:
+       return status;
+out_default:
+       return nfs_cb_stat_to_errno(status);
+}
 
-static int
-nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)
+/*
+ * NFSv4.0 and NFSv4.1 XDR encode functions
+ *
+ * NFSv4.0 callback argument types are defined in section 15 of RFC
+ * 3530: "Network File System (NFS) version 4 Protocol" and section 20
+ * of RFC 5661:  "Network File System (NFS) Version 4 Minor Version 1
+ * Protocol".
+ */
+
+/*
+ * NB: Without this zero space reservation, callbacks over krb5p fail
+ */
+static void nfs4_xdr_enc_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                void *__unused)
+{
+       xdr_reserve_space(xdr, 0);
+}
+
+/*
+ * 20.2. Operation 4: CB_RECALL - Recall a Delegation
+ */
+static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
+                                  const struct nfsd4_callback *cb)
+{
+       const struct nfs4_delegation *args = cb->cb_op;
+       struct nfs4_cb_compound_hdr hdr = {
+               .ident = cb->cb_clp->cl_cb_ident,
+               .minorversion = cb->cb_minorversion,
+       };
+
+       encode_cb_compound4args(xdr, &hdr);
+       encode_cb_sequence4args(xdr, cb, &hdr);
+       encode_cb_recall4args(xdr, args, &hdr);
+       encode_cb_nops(&hdr);
+}
+
+
+/*
+ * NFSv4.0 and NFSv4.1 XDR decode functions
+ *
+ * NFSv4.0 callback result types are defined in section 15 of RFC
+ * 3530: "Network File System (NFS) version 4 Protocol" and section 20
+ * of RFC 5661:  "Network File System (NFS) Version 4 Minor Version 1
+ * Protocol".
+ */
+
+static int nfs4_xdr_dec_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr,
+                               void *__unused)
 {
        return 0;
 }
 
-static int
-nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
-               struct nfsd4_callback *cb)
+/*
+ * 20.2. Operation 4: CB_RECALL - Recall a Delegation
+ */
+static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp,
+                                 struct xdr_stream *xdr,
+                                 struct nfsd4_callback *cb)
 {
-       struct xdr_stream xdr;
        struct nfs4_cb_compound_hdr hdr;
+       enum nfsstat4 nfserr;
        int status;
 
-       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-       status = decode_cb_compound_hdr(&xdr, &hdr);
-       if (status)
+       status = decode_cb_compound4res(xdr, &hdr);
+       if (unlikely(status))
                goto out;
-       if (cb) {
-               status = decode_cb_sequence(&xdr, cb, rqstp);
-               if (status)
+
+       if (cb != NULL) {
+               status = decode_cb_sequence4res(xdr, cb);
+               if (unlikely(status))
                        goto out;
        }
-       status = decode_cb_op_hdr(&xdr, OP_CB_RECALL);
+
+       status = decode_cb_op_status(xdr, OP_CB_RECALL, &nfserr);
+       if (unlikely(status))
+               goto out;
+       if (unlikely(nfserr != NFS4_OK))
+               goto out_default;
 out:
        return status;
+out_default:
+       return nfs_cb_stat_to_errno(status);
 }
 
 /*
  * RPC procedure tables
  */
-#define PROC(proc, call, argtype, restype)                              \
-[NFSPROC4_CLNT_##proc] = {                                             \
-        .p_proc   = NFSPROC4_CB_##call,                                        \
-        .p_encode = (kxdrproc_t) nfs4_xdr_##argtype,                    \
-        .p_decode = (kxdrproc_t) nfs4_xdr_##restype,                    \
-        .p_arglen = NFS4_##argtype##_sz,                                \
-        .p_replen = NFS4_##restype##_sz,                                \
-        .p_statidx = NFSPROC4_CB_##call,                               \
-       .p_name   = #proc,                                              \
-}
-
-static struct rpc_procinfo     nfs4_cb_procedures[] = {
-    PROC(CB_NULL,      NULL,     enc_cb_null,     dec_cb_null),
-    PROC(CB_RECALL,    COMPOUND,   enc_cb_recall,      dec_cb_recall),
+#define PROC(proc, call, argtype, restype)                             \
+[NFSPROC4_CLNT_##proc] = {                                             \
+       .p_proc    = NFSPROC4_CB_##call,                                \
+       .p_encode  = (kxdreproc_t)nfs4_xdr_enc_##argtype,               \
+       .p_decode  = (kxdrdproc_t)nfs4_xdr_dec_##restype,               \
+       .p_arglen  = NFS4_enc_##argtype##_sz,                           \
+       .p_replen  = NFS4_dec_##restype##_sz,                           \
+       .p_statidx = NFSPROC4_CB_##call,                                \
+       .p_name    = #proc,                                             \
+}
+
+static struct rpc_procinfo nfs4_cb_procedures[] = {
+       PROC(CB_NULL,   NULL,           cb_null,        cb_null),
+       PROC(CB_RECALL, COMPOUND,       cb_recall,      cb_recall),
 };
 
-static struct rpc_version       nfs_cb_version4 = {
+static struct rpc_version nfs_cb_version4 = {
 /*
  * Note on the callback rpc program version number: despite language in rfc
  * 5661 section 18.36.3 requiring servers to use 4 in this field, the
@@ -440,29 +598,29 @@ static struct rpc_version       nfs_cb_version4 = {
  * in practice that appears to be what implementations use.  The section
  * 18.36.3 language is expected to be fixed in an erratum.
  */
-        .number                 = 1,
-        .nrprocs                = ARRAY_SIZE(nfs4_cb_procedures),
-        .procs                  = nfs4_cb_procedures
+       .number                 = 1,
+       .nrprocs                = ARRAY_SIZE(nfs4_cb_procedures),
+       .procs                  = nfs4_cb_procedures
 };
 
-static struct rpc_version *    nfs_cb_version[] = {
+static struct rpc_version *nfs_cb_version[] = {
        &nfs_cb_version4,
 };
 
 static struct rpc_program cb_program;
 
 static struct rpc_stat cb_stats = {
-               .program        = &cb_program
+       .program                = &cb_program
 };
 
 #define NFS4_CALLBACK 0x40000000
 static struct rpc_program cb_program = {
-               .name           = "nfs4_cb",
-               .number         = NFS4_CALLBACK,
-               .nrvers         = ARRAY_SIZE(nfs_cb_version),
-               .version        = nfs_cb_version,
-               .stats          = &cb_stats,
-               .pipe_dir_name  = "/nfsd4_cb",
+       .name                   = "nfs4_cb",
+       .number                 = NFS4_CALLBACK,
+       .nrvers                 = ARRAY_SIZE(nfs_cb_version),
+       .version                = nfs_cb_version,
+       .stats                  = &cb_stats,
+       .pipe_dir_name          = "/nfsd4_cb",
 };
 
 static int max_cb_time(void)
index 34b2b7f..257d377 100644 (file)
 #define NLMDBG_XDR             0x0100
 #define NLMDBG_ALL             0x7fff
 
-
-/*
- * Support for printing NLM cookies in dprintk()
- */
-#ifdef RPC_DEBUG
-struct nlm_cookie;
-/* Call this function with the BKL held (it uses a static buffer) */
-extern const char *nlmdbg_cookie2a(const struct nlm_cookie *);
-#endif
-
 #endif /* LINUX_LOCKD_DEBUG_H */
index 2dee05e..ff9abff 100644 (file)
@@ -202,9 +202,9 @@ extern u32                  nsm_local_state;
  * Lockd client functions
  */
 struct nlm_rqst * nlm_alloc_call(struct nlm_host *host);
-void             nlm_release_call(struct nlm_rqst *);
 int              nlm_async_call(struct nlm_rqst *, u32, const struct rpc_call_ops *);
 int              nlm_async_reply(struct nlm_rqst *, u32, const struct rpc_call_ops *);
+void             nlmclnt_release_call(struct nlm_rqst *);
 struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl);
 void             nlmclnt_finish_block(struct nlm_wait *block);
 int              nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout);
@@ -223,13 +223,14 @@ struct nlm_host  *nlmclnt_lookup_host(const struct sockaddr *sap,
                                        const u32 version,
                                        const char *hostname,
                                        int noresvport);
+void             nlmclnt_release_host(struct nlm_host *);
 struct nlm_host  *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                                        const char *hostname,
                                        const size_t hostname_len);
+void             nlmsvc_release_host(struct nlm_host *);
 struct rpc_clnt * nlm_bind_host(struct nlm_host *);
 void             nlm_rebind_host(struct nlm_host *);
 struct nlm_host * nlm_get_host(struct nlm_host *);
-void             nlm_release_host(struct nlm_host *);
 void             nlm_shutdown_hosts(void);
 void             nlm_host_rebooted(const struct nlm_reboot *);
 
@@ -267,6 +268,7 @@ unsigned long         nlmsvc_retry_blocked(void);
 void             nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
                                        nlm_host_match_fn_t match);
 void             nlmsvc_grant_reply(struct nlm_cookie *, __be32);
+void             nlmsvc_release_call(struct nlm_rqst *);
 
 /*
  * File handling for the server personality
index ac33806..6ccfe3b 100644 (file)
@@ -11,6 +11,9 @@
 #define NFS3_MAXGROUPS         16
 #define NFS3_FHSIZE            64
 #define NFS3_COOKIESIZE                4
+#define NFS3_CREATEVERFSIZE    8
+#define NFS3_COOKIEVERFSIZE    8
+#define NFS3_WRITEVERFSIZE     8
 #define NFS3_FIFO_DEV          (-1)
 #define NFS3MODE_FMT           0170000
 #define NFS3MODE_DIR           0040000
index 452d964..b197563 100644 (file)
@@ -47,11 +47,6 @@ struct nfs_client {
        u64                     cl_clientid;    /* constant */
        unsigned long           cl_state;
 
-       struct rb_root          cl_openowner_id;
-       struct rb_root          cl_lockowner_id;
-
-       struct list_head        cl_delegations;
-       struct rb_root          cl_state_owners;
        spinlock_t              cl_lock;
 
        unsigned long           cl_lease_time;
@@ -71,6 +66,7 @@ struct nfs_client {
         */
        char                    cl_ipaddr[48];
        unsigned char           cl_id_uniquifier;
+       u32                     cl_cb_ident;    /* v4.0 callback identifier */
        const struct nfs4_minor_version_ops *cl_mvops;
 #endif /* CONFIG_NFS_V4 */
 
@@ -148,7 +144,14 @@ struct nfs_server {
                                                   that are supported on this
                                                   filesystem */
        struct pnfs_layoutdriver_type  *pnfs_curr_ld; /* Active layout driver */
+       struct rpc_wait_queue   roc_rpcwaitq;
+
+       /* the following fields are protected by nfs_client->cl_lock */
+       struct rb_root          state_owners;
+       struct rb_root          openowner_id;
+       struct rb_root          lockowner_id;
 #endif
+       struct list_head        delegations;
        void (*destroy)(struct nfs_server *);
 
        atomic_t active; /* Keep trace of any activity to this server */
@@ -196,6 +199,7 @@ struct nfs4_slot_table {
                                                 * op for dynamic resizing */
        int             target_max_slots;       /* Set by CB_RECALL_SLOT as
                                                 * the new max_slots */
+       struct completion complete;
 };
 
 static inline int slot_idx(struct nfs4_slot_table *tbl, struct nfs4_slot *sp)
@@ -212,7 +216,6 @@ struct nfs4_session {
        unsigned long                   session_state;
        u32                             hash_alg;
        u32                             ssv_len;
-       struct completion               complete;
 
        /* The fore and back channel */
        struct nfs4_channel_attrs       fc_attrs;
index 80f0719..b006857 100644 (file)
@@ -208,6 +208,7 @@ struct nfs4_layoutget_args {
        struct inode *inode;
        struct nfs_open_context *ctx;
        struct nfs4_sequence_args seq_args;
+       nfs4_stateid stateid;
 };
 
 struct nfs4_layoutget_res {
@@ -223,7 +224,6 @@ struct nfs4_layoutget {
        struct nfs4_layoutget_args args;
        struct nfs4_layoutget_res res;
        struct pnfs_layout_segment **lsegpp;
-       int status;
 };
 
 struct nfs4_getdeviceinfo_args {
@@ -317,6 +317,7 @@ struct nfs_closeres {
 struct nfs_lowner {
        __u64                   clientid;
        __u64                   id;
+       dev_t                   s_dev;
 };
 
 struct nfs_lock_args {
@@ -484,6 +485,7 @@ struct nfs_entry {
        struct nfs_fh *         fh;
        struct nfs_fattr *      fattr;
        unsigned char           d_type;
+       struct nfs_server *     server;
 };
 
 /*
@@ -1089,7 +1091,7 @@ struct nfs_rpc_ops {
        int     (*pathconf) (struct nfs_server *, struct nfs_fh *,
                             struct nfs_pathconf *);
        int     (*set_capabilities)(struct nfs_server *, struct nfs_fh *);
-       __be32 *(*decode_dirent)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int plus);
+       int     (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int);
        void    (*read_setup)   (struct nfs_read_data *, struct rpc_message *);
        int     (*read_done)  (struct rpc_task *, struct nfs_read_data *);
        void    (*write_setup)  (struct nfs_write_data *, struct rpc_message *);
index b202475..8521067 100644 (file)
@@ -110,9 +110,9 @@ struct rpc_credops {
        __be32 *                (*crmarshal)(struct rpc_task *, __be32 *);
        int                     (*crrefresh)(struct rpc_task *);
        __be32 *                (*crvalidate)(struct rpc_task *, __be32 *);
-       int                     (*crwrap_req)(struct rpc_task *, kxdrproc_t,
+       int                     (*crwrap_req)(struct rpc_task *, kxdreproc_t,
                                                void *, __be32 *, void *);
-       int                     (*crunwrap_resp)(struct rpc_task *, kxdrproc_t,
+       int                     (*crunwrap_resp)(struct rpc_task *, kxdrdproc_t,
                                                void *, __be32 *, void *);
 };
 
@@ -139,8 +139,8 @@ struct rpc_cred *   rpcauth_generic_bind_cred(struct rpc_task *, struct rpc_cred *
 void                   put_rpccred(struct rpc_cred *);
 __be32 *               rpcauth_marshcred(struct rpc_task *, __be32 *);
 __be32 *               rpcauth_checkverf(struct rpc_task *, __be32 *);
-int                    rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, __be32 *data, void *obj);
-int                    rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, __be32 *data, void *obj);
+int                    rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp, __be32 *data, void *obj);
+int                    rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, __be32 *data, void *obj);
 int                    rpcauth_refreshcred(struct rpc_task *);
 void                   rpcauth_invalcred(struct rpc_task *);
 int                    rpcauth_uptodatecred(struct rpc_task *);
index 7c91260..c50b458 100644 (file)
@@ -43,10 +43,18 @@ int bc_send(struct rpc_rqst *req);
  */
 static inline int svc_is_backchannel(const struct svc_rqst *rqstp)
 {
-       if (rqstp->rq_server->bc_xprt)
+       if (rqstp->rq_server->sv_bc_xprt)
                return 1;
        return 0;
 }
+static inline struct nfs4_sessionid *bc_xprt_sid(struct svc_rqst *rqstp)
+{
+       if (svc_is_backchannel(rqstp))
+               return (struct nfs4_sessionid *)
+                       rqstp->rq_server->sv_bc_xprt->xpt_bc_sid;
+       return NULL;
+}
+
 #else /* CONFIG_NFS_V4_1 */
 static inline int xprt_setup_backchannel(struct rpc_xprt *xprt,
                                         unsigned int min_reqs)
@@ -59,6 +67,11 @@ static inline int svc_is_backchannel(const struct svc_rqst *rqstp)
        return 0;
 }
 
+static inline struct nfs4_sessionid *bc_xprt_sid(struct svc_rqst *rqstp)
+{
+       return NULL;
+}
+
 static inline void xprt_free_bc_request(struct rpc_rqst *req)
 {
 }
index a5a55f2..ef9476a 100644 (file)
@@ -89,8 +89,8 @@ struct rpc_version {
  */
 struct rpc_procinfo {
        u32                     p_proc;         /* RPC procedure number */
-       kxdrproc_t              p_encode;       /* XDR encode function */
-       kxdrproc_t              p_decode;       /* XDR decode function */
+       kxdreproc_t             p_encode;       /* XDR encode function */
+       kxdrdproc_t             p_decode;       /* XDR decode function */
        unsigned int            p_arglen;       /* argument hdr length (u32) */
        unsigned int            p_replen;       /* reply hdr length (u32) */
        unsigned int            p_count;        /* call count */
index 5a3085b..c81d4d8 100644 (file)
@@ -99,7 +99,7 @@ struct svc_serv {
        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;
+       struct svc_xprt         *sv_bc_xprt;    /* callback on fore channel */
 #endif /* CONFIG_NFS_V4_1 */
 };
 
index aea0d43..357da5e 100644 (file)
@@ -78,6 +78,7 @@ struct svc_xprt {
        size_t                  xpt_remotelen;  /* length of address */
        struct rpc_wait_queue   xpt_bc_pending; /* backchannel wait queue */
        struct list_head        xpt_users;      /* callbacks on free */
+       void                    *xpt_bc_sid;    /* back channel session ID */
 
        struct net              *xpt_net;
 };
index 7783c68..fc84b7a 100644 (file)
@@ -33,8 +33,8 @@ struct xdr_netobj {
 };
 
 /*
- * This is the generic XDR function. rqstp is either a rpc_rqst (client
- * side) or svc_rqst pointer (server side).
+ * This is the legacy generic XDR function. rqstp is either a rpc_rqst
+ * (client side) or svc_rqst pointer (server side).
  * Encode functions always assume there's enough room in the buffer.
  */
 typedef int    (*kxdrproc_t)(void *rqstp, __be32 *data, void *obj);
@@ -205,6 +205,12 @@ struct xdr_stream {
        struct page **page_ptr; /* pointer to the current page */
 };
 
+/*
+ * These are the xdr_stream style generic XDR encode and decode functions.
+ */
+typedef void   (*kxdreproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
+typedef int    (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
+
 extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
 extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
 extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
index afe6784..67e3127 100644 (file)
@@ -563,8 +563,17 @@ rpcauth_checkverf(struct rpc_task *task, __be32 *p)
        return cred->cr_ops->crvalidate(task, p);
 }
 
+static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
+                                  __be32 *data, void *obj)
+{
+       struct xdr_stream xdr;
+
+       xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data);
+       encode(rqstp, &xdr, obj);
+}
+
 int
-rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
+rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp,
                __be32 *data, void *obj)
 {
        struct rpc_cred *cred = task->tk_rqstp->rq_cred;
@@ -574,11 +583,22 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
        if (cred->cr_ops->crwrap_req)
                return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
        /* By default, we encode the arguments normally. */
-       return encode(rqstp, data, obj);
+       rpcauth_wrap_req_encode(encode, rqstp, data, obj);
+       return 0;
+}
+
+static int
+rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp,
+                         __be32 *data, void *obj)
+{
+       struct xdr_stream xdr;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data);
+       return decode(rqstp, &xdr, obj);
 }
 
 int
-rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
+rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp,
                __be32 *data, void *obj)
 {
        struct rpc_cred *cred = task->tk_rqstp->rq_cred;
@@ -589,7 +609,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
                return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
                                                   data, obj);
        /* By default, we decode the arguments normally. */
-       return decode(rqstp, data, obj);
+       return rpcauth_unwrap_req_decode(decode, rqstp, data, obj);
 }
 
 int
index 3835ce3..45dbf15 100644 (file)
@@ -1231,9 +1231,19 @@ out_bad:
        return NULL;
 }
 
+static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
+                               __be32 *p, void *obj)
+{
+       struct xdr_stream xdr;
+
+       xdr_init_encode(&xdr, &rqstp->rq_snd_buf, p);
+       encode(rqstp, &xdr, obj);
+}
+
 static inline int
 gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
-               kxdrproc_t encode, struct rpc_rqst *rqstp, __be32 *p, void *obj)
+                  kxdreproc_t encode, struct rpc_rqst *rqstp,
+                  __be32 *p, void *obj)
 {
        struct xdr_buf  *snd_buf = &rqstp->rq_snd_buf;
        struct xdr_buf  integ_buf;
@@ -1249,9 +1259,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
        offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
        *p++ = htonl(rqstp->rq_seqno);
 
-       status = encode(rqstp, p, obj);
-       if (status)
-               return status;
+       gss_wrap_req_encode(encode, rqstp, p, obj);
 
        if (xdr_buf_subsegment(snd_buf, &integ_buf,
                                offset, snd_buf->len - offset))
@@ -1325,7 +1333,8 @@ out:
 
 static inline int
 gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
-               kxdrproc_t encode, struct rpc_rqst *rqstp, __be32 *p, void *obj)
+                 kxdreproc_t encode, struct rpc_rqst *rqstp,
+                 __be32 *p, void *obj)
 {
        struct xdr_buf  *snd_buf = &rqstp->rq_snd_buf;
        u32             offset;
@@ -1342,9 +1351,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
        offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
        *p++ = htonl(rqstp->rq_seqno);
 
-       status = encode(rqstp, p, obj);
-       if (status)
-               return status;
+       gss_wrap_req_encode(encode, rqstp, p, obj);
 
        status = alloc_enc_pages(rqstp);
        if (status)
@@ -1394,7 +1401,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
 
 static int
 gss_wrap_req(struct rpc_task *task,
-            kxdrproc_t encode, void *rqstp, __be32 *p, void *obj)
+            kxdreproc_t encode, void *rqstp, __be32 *p, void *obj)
 {
        struct rpc_cred *cred = task->tk_rqstp->rq_cred;
        struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
@@ -1407,12 +1414,14 @@ gss_wrap_req(struct rpc_task *task,
                /* The spec seems a little ambiguous here, but I think that not
                 * wrapping context destruction requests makes the most sense.
                 */
-               status = encode(rqstp, p, obj);
+               gss_wrap_req_encode(encode, rqstp, p, obj);
+               status = 0;
                goto out;
        }
        switch (gss_cred->gc_service) {
                case RPC_GSS_SVC_NONE:
-                       status = encode(rqstp, p, obj);
+                       gss_wrap_req_encode(encode, rqstp, p, obj);
+                       status = 0;
                        break;
                case RPC_GSS_SVC_INTEGRITY:
                        status = gss_wrap_req_integ(cred, ctx, encode,
@@ -1494,10 +1503,19 @@ gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
        return 0;
 }
 
+static int
+gss_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp,
+                     __be32 *p, void *obj)
+{
+       struct xdr_stream xdr;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       return decode(rqstp, &xdr, obj);
+}
 
 static int
 gss_unwrap_resp(struct rpc_task *task,
-               kxdrproc_t decode, void *rqstp, __be32 *p, void *obj)
+               kxdrdproc_t decode, void *rqstp, __be32 *p, void *obj)
 {
        struct rpc_cred *cred = task->tk_rqstp->rq_cred;
        struct gss_cred *gss_cred = container_of(cred, struct gss_cred,
@@ -1528,7 +1546,7 @@ gss_unwrap_resp(struct rpc_task *task,
        cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
                                                + (savedlen - head->iov_len);
 out_decode:
-       status = decode(rqstp, p, obj);
+       status = gss_unwrap_req_decode(decode, rqstp, p, obj);
 out:
        gss_put_ctx(ctx);
        dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid,
index 7dcfe0c..1dd1a68 100644 (file)
@@ -59,8 +59,8 @@ int bc_send(struct rpc_rqst *req)
                ret = task->tk_status;
                rpc_put_task(task);
        }
-       return ret;
        dprintk("RPC:       bc_send ret= %d\n", ret);
+       return ret;
 }
 
 #endif /* CONFIG_NFS_V4_1 */
index 92ce94f..57d344c 100644 (file)
@@ -1095,7 +1095,7 @@ static void
 rpc_xdr_encode(struct rpc_task *task)
 {
        struct rpc_rqst *req = task->tk_rqstp;
-       kxdrproc_t      encode;
+       kxdreproc_t     encode;
        __be32          *p;
 
        dprint_status(task);
@@ -1535,7 +1535,7 @@ call_decode(struct rpc_task *task)
 {
        struct rpc_clnt *clnt = task->tk_client;
        struct rpc_rqst *req = task->tk_rqstp;
-       kxdrproc_t      decode = task->tk_msg.rpc_proc->p_decode;
+       kxdrdproc_t     decode = task->tk_msg.rpc_proc->p_decode;
        __be32          *p;
 
        dprintk("RPC: %5u call_decode (status %d)\n",
@@ -1776,12 +1776,11 @@ out_overflow:
        goto out_garbage;
 }
 
-static int rpcproc_encode_null(void *rqstp, __be32 *data, void *obj)
+static void rpcproc_encode_null(void *rqstp, struct xdr_stream *xdr, void *obj)
 {
-       return 0;
 }
 
-static int rpcproc_decode_null(void *rqstp, __be32 *data, void *obj)
+static int rpcproc_decode_null(void *rqstp, struct xdr_stream *xdr, void *obj)
 {
        return 0;
 }
@@ -1830,23 +1829,15 @@ static void rpc_show_task(const struct rpc_clnt *clnt,
                          const struct rpc_task *task)
 {
        const char *rpc_waitq = "none";
-       char *p, action[KSYM_SYMBOL_LEN];
 
        if (RPC_IS_QUEUED(task))
                rpc_waitq = rpc_qname(task->tk_waitqueue);
 
-       /* map tk_action pointer to a function name; then trim off
-        * the "+0x0 [sunrpc]" */
-       sprint_symbol(action, (unsigned long)task->tk_action);
-       p = strchr(action, '+');
-       if (p)
-               *p = '\0';
-
-       printk(KERN_INFO "%5u %04x %6d %8p %8p %8ld %8p %sv%u %s a:%s q:%s\n",
+       printk(KERN_INFO "%5u %04x %6d %8p %8p %8ld %8p %sv%u %s a:%ps q:%s\n",
                task->tk_pid, task->tk_flags, task->tk_status,
                clnt, task->tk_rqstp, task->tk_timeout, task->tk_ops,
                clnt->cl_protname, clnt->cl_vers, rpc_proc_name(task),
-               action, rpc_waitq);
+               task->tk_action, rpc_waitq);
 }
 
 void rpc_show_tasks(void)
index 10a17a3..5356d95 100644 (file)
@@ -466,7 +466,7 @@ static int __rpc_create_common(struct inode *dir, struct dentry *dentry,
 {
        struct inode *inode;
 
-       BUG_ON(!d_unhashed(dentry));
+       d_drop(dentry);
        inode = rpc_get_inode(dir->i_sb, mode);
        if (!inode)
                goto out_err;
index fa6d7ca..c652e4c 100644 (file)
@@ -57,10 +57,6 @@ enum {
        RPCBPROC_GETSTAT,
 };
 
-#define RPCB_HIGHPROC_2                RPCBPROC_CALLIT
-#define RPCB_HIGHPROC_3                RPCBPROC_TADDR2UADDR
-#define RPCB_HIGHPROC_4                RPCBPROC_GETSTAT
-
 /*
  * r_owner
  *
@@ -693,46 +689,37 @@ static void rpcb_getport_done(struct rpc_task *child, void *data)
  * XDR functions for rpcbind
  */
 
-static int rpcb_enc_mapping(struct rpc_rqst *req, __be32 *p,
-                           const struct rpcbind_args *rpcb)
+static void rpcb_enc_mapping(struct rpc_rqst *req, struct xdr_stream *xdr,
+                            const struct rpcbind_args *rpcb)
 {
        struct rpc_task *task = req->rq_task;
-       struct xdr_stream xdr;
+       __be32 *p;
 
        dprintk("RPC: %5u encoding PMAP_%s call (%u, %u, %d, %u)\n",
                        task->tk_pid, task->tk_msg.rpc_proc->p_name,
                        rpcb->r_prog, rpcb->r_vers, rpcb->r_prot, rpcb->r_port);
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-
-       p = xdr_reserve_space(&xdr, sizeof(__be32) * RPCB_mappingargs_sz);
-       if (unlikely(p == NULL))
-               return -EIO;
-
-       *p++ = htonl(rpcb->r_prog);
-       *p++ = htonl(rpcb->r_vers);
-       *p++ = htonl(rpcb->r_prot);
-       *p   = htonl(rpcb->r_port);
-
-       return 0;
+       p = xdr_reserve_space(xdr, RPCB_mappingargs_sz << 2);
+       *p++ = cpu_to_be32(rpcb->r_prog);
+       *p++ = cpu_to_be32(rpcb->r_vers);
+       *p++ = cpu_to_be32(rpcb->r_prot);
+       *p   = cpu_to_be32(rpcb->r_port);
 }
 
-static int rpcb_dec_getport(struct rpc_rqst *req, __be32 *p,
+static int rpcb_dec_getport(struct rpc_rqst *req, struct xdr_stream *xdr,
                            struct rpcbind_args *rpcb)
 {
        struct rpc_task *task = req->rq_task;
-       struct xdr_stream xdr;
        unsigned long port;
-
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
+       __be32 *p;
 
        rpcb->r_port = 0;
 
-       p = xdr_inline_decode(&xdr, sizeof(__be32));
+       p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                return -EIO;
 
-       port = ntohl(*p);
+       port = be32_to_cpup(p);
        dprintk("RPC: %5u PMAP_%s result: %lu\n", task->tk_pid,
                        task->tk_msg.rpc_proc->p_name, port);
        if (unlikely(port > USHRT_MAX))
@@ -742,20 +729,18 @@ static int rpcb_dec_getport(struct rpc_rqst *req, __be32 *p,
        return 0;
 }
 
-static int rpcb_dec_set(struct rpc_rqst *req, __be32 *p,
+static int rpcb_dec_set(struct rpc_rqst *req, struct xdr_stream *xdr,
                        unsigned int *boolp)
 {
        struct rpc_task *task = req->rq_task;
-       struct xdr_stream xdr;
-
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
+       __be32 *p;
 
-       p = xdr_inline_decode(&xdr, sizeof(__be32));
+       p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                return -EIO;
 
        *boolp = 0;
-       if (*p)
+       if (*p != xdr_zero)
                *boolp = 1;
 
        dprintk("RPC: %5u RPCB_%s call %s\n",
@@ -764,73 +749,53 @@ static int rpcb_dec_set(struct rpc_rqst *req, __be32 *p,
        return 0;
 }
 
-static int encode_rpcb_string(struct xdr_stream *xdr, const char *string,
-                               const u32 maxstrlen)
+static void encode_rpcb_string(struct xdr_stream *xdr, const char *string,
+                              const u32 maxstrlen)
 {
-       u32 len;
        __be32 *p;
+       u32 len;
 
-       if (unlikely(string == NULL))
-               return -EIO;
        len = strlen(string);
-       if (unlikely(len > maxstrlen))
-               return -EIO;
-
-       p = xdr_reserve_space(xdr, sizeof(__be32) + len);
-       if (unlikely(p == NULL))
-               return -EIO;
+       BUG_ON(len > maxstrlen);
+       p = xdr_reserve_space(xdr, 4 + len);
        xdr_encode_opaque(p, string, len);
-
-       return 0;
 }
 
-static int rpcb_enc_getaddr(struct rpc_rqst *req, __be32 *p,
-                           const struct rpcbind_args *rpcb)
+static void rpcb_enc_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr,
+                            const struct rpcbind_args *rpcb)
 {
        struct rpc_task *task = req->rq_task;
-       struct xdr_stream xdr;
+       __be32 *p;
 
        dprintk("RPC: %5u encoding RPCB_%s call (%u, %u, '%s', '%s')\n",
                        task->tk_pid, task->tk_msg.rpc_proc->p_name,
                        rpcb->r_prog, rpcb->r_vers,
                        rpcb->r_netid, rpcb->r_addr);
 
-       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-
-       p = xdr_reserve_space(&xdr,
-                       sizeof(__be32) * (RPCB_program_sz + RPCB_version_sz));
-       if (unlikely(p == NULL))
-               return -EIO;
-       *p++ = htonl(rpcb->r_prog);
-       *p = htonl(rpcb->r_vers);
-
-       if (encode_rpcb_string(&xdr, rpcb->r_netid, RPCBIND_MAXNETIDLEN))
-               return -EIO;
-       if (encode_rpcb_string(&xdr, rpcb->r_addr, RPCBIND_MAXUADDRLEN))
-               return -EIO;
-       if (encode_rpcb_string(&xdr, rpcb->r_owner, RPCB_MAXOWNERLEN))
-               return -EIO;
+       p = xdr_reserve_space(xdr, (RPCB_program_sz + RPCB_version_sz) << 2);
+       *p++ = cpu_to_be32(rpcb->r_prog);
+       *p = cpu_to_be32(rpcb->r_vers);
 
-       return 0;
+       encode_rpcb_string(xdr, rpcb->r_netid, RPCBIND_MAXNETIDLEN);
+       encode_rpcb_string(xdr, rpcb->r_addr, RPCBIND_MAXUADDRLEN);
+       encode_rpcb_string(xdr, rpcb->r_owner, RPCB_MAXOWNERLEN);
 }
 
-static int rpcb_dec_getaddr(struct rpc_rqst *req, __be32 *p,
+static int rpcb_dec_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr,
                            struct rpcbind_args *rpcb)
 {
        struct sockaddr_storage address;
        struct sockaddr *sap = (struct sockaddr *)&address;
        struct rpc_task *task = req->rq_task;
-       struct xdr_stream xdr;
+       __be32 *p;
        u32 len;
 
        rpcb->r_port = 0;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-
-       p = xdr_inline_decode(&xdr, sizeof(__be32));
+       p = xdr_inline_decode(xdr, 4);
        if (unlikely(p == NULL))
                goto out_fail;
-       len = ntohl(*p);
+       len = be32_to_cpup(p);
 
        /*
         * If the returned universal address is a null string,
@@ -845,7 +810,7 @@ static int rpcb_dec_getaddr(struct rpc_rqst *req, __be32 *p,
        if (unlikely(len > RPCBIND_MAXUADDRLEN))
                goto out_fail;
 
-       p = xdr_inline_decode(&xdr, len);
+       p = xdr_inline_decode(xdr, len);
        if (unlikely(p == NULL))
                goto out_fail;
        dprintk("RPC: %5u RPCB_%s reply: %s\n", task->tk_pid,
@@ -871,8 +836,8 @@ out_fail:
 static struct rpc_procinfo rpcb_procedures2[] = {
        [RPCBPROC_SET] = {
                .p_proc         = RPCBPROC_SET,
-               .p_encode       = (kxdrproc_t)rpcb_enc_mapping,
-               .p_decode       = (kxdrproc_t)rpcb_dec_set,
+               .p_encode       = (kxdreproc_t)rpcb_enc_mapping,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_set,
                .p_arglen       = RPCB_mappingargs_sz,
                .p_replen       = RPCB_setres_sz,
                .p_statidx      = RPCBPROC_SET,
@@ -881,8 +846,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {
        },
        [RPCBPROC_UNSET] = {
                .p_proc         = RPCBPROC_UNSET,
-               .p_encode       = (kxdrproc_t)rpcb_enc_mapping,
-               .p_decode       = (kxdrproc_t)rpcb_dec_set,
+               .p_encode       = (kxdreproc_t)rpcb_enc_mapping,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_set,
                .p_arglen       = RPCB_mappingargs_sz,
                .p_replen       = RPCB_setres_sz,
                .p_statidx      = RPCBPROC_UNSET,
@@ -891,8 +856,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {
        },
        [RPCBPROC_GETPORT] = {
                .p_proc         = RPCBPROC_GETPORT,
-               .p_encode       = (kxdrproc_t)rpcb_enc_mapping,
-               .p_decode       = (kxdrproc_t)rpcb_dec_getport,
+               .p_encode       = (kxdreproc_t)rpcb_enc_mapping,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_getport,
                .p_arglen       = RPCB_mappingargs_sz,
                .p_replen       = RPCB_getportres_sz,
                .p_statidx      = RPCBPROC_GETPORT,
@@ -904,8 +869,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {
 static struct rpc_procinfo rpcb_procedures3[] = {
        [RPCBPROC_SET] = {
                .p_proc         = RPCBPROC_SET,
-               .p_encode       = (kxdrproc_t)rpcb_enc_getaddr,
-               .p_decode       = (kxdrproc_t)rpcb_dec_set,
+               .p_encode       = (kxdreproc_t)rpcb_enc_getaddr,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_set,
                .p_arglen       = RPCB_getaddrargs_sz,
                .p_replen       = RPCB_setres_sz,
                .p_statidx      = RPCBPROC_SET,
@@ -914,8 +879,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {
        },
        [RPCBPROC_UNSET] = {
                .p_proc         = RPCBPROC_UNSET,
-               .p_encode       = (kxdrproc_t)rpcb_enc_getaddr,
-               .p_decode       = (kxdrproc_t)rpcb_dec_set,
+               .p_encode       = (kxdreproc_t)rpcb_enc_getaddr,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_set,
                .p_arglen       = RPCB_getaddrargs_sz,
                .p_replen       = RPCB_setres_sz,
                .p_statidx      = RPCBPROC_UNSET,
@@ -924,8 +889,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {
        },
        [RPCBPROC_GETADDR] = {
                .p_proc         = RPCBPROC_GETADDR,
-               .p_encode       = (kxdrproc_t)rpcb_enc_getaddr,
-               .p_decode       = (kxdrproc_t)rpcb_dec_getaddr,
+               .p_encode       = (kxdreproc_t)rpcb_enc_getaddr,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_getaddr,
                .p_arglen       = RPCB_getaddrargs_sz,
                .p_replen       = RPCB_getaddrres_sz,
                .p_statidx      = RPCBPROC_GETADDR,
@@ -937,8 +902,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {
 static struct rpc_procinfo rpcb_procedures4[] = {
        [RPCBPROC_SET] = {
                .p_proc         = RPCBPROC_SET,
-               .p_encode       = (kxdrproc_t)rpcb_enc_getaddr,
-               .p_decode       = (kxdrproc_t)rpcb_dec_set,
+               .p_encode       = (kxdreproc_t)rpcb_enc_getaddr,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_set,
                .p_arglen       = RPCB_getaddrargs_sz,
                .p_replen       = RPCB_setres_sz,
                .p_statidx      = RPCBPROC_SET,
@@ -947,8 +912,8 @@ static struct rpc_procinfo rpcb_procedures4[] = {
        },
        [RPCBPROC_UNSET] = {
                .p_proc         = RPCBPROC_UNSET,
-               .p_encode       = (kxdrproc_t)rpcb_enc_getaddr,
-               .p_decode       = (kxdrproc_t)rpcb_dec_set,
+               .p_encode       = (kxdreproc_t)rpcb_enc_getaddr,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_set,
                .p_arglen       = RPCB_getaddrargs_sz,
                .p_replen       = RPCB_setres_sz,
                .p_statidx      = RPCBPROC_UNSET,
@@ -957,8 +922,8 @@ static struct rpc_procinfo rpcb_procedures4[] = {
        },
        [RPCBPROC_GETADDR] = {
                .p_proc         = RPCBPROC_GETADDR,
-               .p_encode       = (kxdrproc_t)rpcb_enc_getaddr,
-               .p_decode       = (kxdrproc_t)rpcb_dec_getaddr,
+               .p_encode       = (kxdreproc_t)rpcb_enc_getaddr,
+               .p_decode       = (kxdrdproc_t)rpcb_dec_getaddr,
                .p_arglen       = RPCB_getaddrargs_sz,
                .p_replen       = RPCB_getaddrres_sz,
                .p_statidx      = RPCBPROC_GETADDR,
@@ -993,19 +958,19 @@ static struct rpcb_info rpcb_next_version6[] = {
 
 static struct rpc_version rpcb_version2 = {
        .number         = RPCBVERS_2,
-       .nrprocs        = RPCB_HIGHPROC_2,
+       .nrprocs        = ARRAY_SIZE(rpcb_procedures2),
        .procs          = rpcb_procedures2
 };
 
 static struct rpc_version rpcb_version3 = {
        .number         = RPCBVERS_3,
-       .nrprocs        = RPCB_HIGHPROC_3,
+       .nrprocs        = ARRAY_SIZE(rpcb_procedures3),
        .procs          = rpcb_procedures3
 };
 
 static struct rpc_version rpcb_version4 = {
        .number         = RPCBVERS_4,
-       .nrprocs        = RPCB_HIGHPROC_4,
+       .nrprocs        = ARRAY_SIZE(rpcb_procedures4),
        .procs          = rpcb_procedures4
 };
 
index 6359c42..0e659c6 100644 (file)
@@ -488,10 +488,6 @@ 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);
@@ -1147,7 +1143,6 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
  dropit:
        svc_authorise(rqstp);   /* doesn't hurt to call this twice */
        dprintk("svc: svc_process dropit\n");
-       svc_drop(rqstp);
        return 0;
 
 err_short_len:
@@ -1218,7 +1213,6 @@ svc_process(struct svc_rqst *rqstp)
        struct kvec             *resv = &rqstp->rq_res.head[0];
        struct svc_serv         *serv = rqstp->rq_server;
        u32                     dir;
-       int                     error;
 
        /*
         * Setup response xdr_buf.
@@ -1246,11 +1240,13 @@ svc_process(struct svc_rqst *rqstp)
                return 0;
        }
 
-       error = svc_process_common(rqstp, argv, resv);
-       if (error <= 0)
-               return error;
-
-       return svc_send(rqstp);
+       /* Returns 1 for send, 0 for drop */
+       if (svc_process_common(rqstp, argv, resv))
+               return svc_send(rqstp);
+       else {
+               svc_drop(rqstp);
+               return 0;
+       }
 }
 
 #if defined(CONFIG_NFS_V4_1)
@@ -1264,10 +1260,9 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
 {
        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_xprt = serv->sv_bc_xprt;
        rqstp->rq_xid = req->rq_xid;
        rqstp->rq_prot = req->rq_xprt->prot;
        rqstp->rq_server = serv;
@@ -1292,12 +1287,15 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
        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);
+       /* Returns 1 for send, 0 for drop */
+       if (svc_process_common(rqstp, argv, resv)) {
+               memcpy(&req->rq_snd_buf, &rqstp->rq_res,
+                                               sizeof(req->rq_snd_buf));
+               return bc_send(req);
+       } else {
+               /* Nothing to do to drop request */
+               return 0;
+       }
 }
 EXPORT_SYMBOL(bc_svc_process);
 #endif /* CONFIG_NFS_V4_1 */
index 07919e1..d265aa7 100644 (file)
@@ -66,6 +66,13 @@ static void          svc_sock_free(struct svc_xprt *);
 static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
                                          struct net *, struct sockaddr *,
                                          int, int);
+#if defined(CONFIG_NFS_V4_1)
+static struct svc_xprt *svc_bc_create_socket(struct svc_serv *, int,
+                                            struct net *, struct sockaddr *,
+                                            int, int);
+static void svc_bc_sock_free(struct svc_xprt *xprt);
+#endif /* CONFIG_NFS_V4_1 */
+
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 static struct lock_class_key svc_key[2];
 static struct lock_class_key svc_slock_key[2];
@@ -1184,6 +1191,57 @@ static struct svc_xprt *svc_tcp_create(struct svc_serv *serv,
        return svc_create_socket(serv, IPPROTO_TCP, net, sa, salen, flags);
 }
 
+#if defined(CONFIG_NFS_V4_1)
+static struct svc_xprt *svc_bc_create_socket(struct svc_serv *, int,
+                                            struct net *, struct sockaddr *,
+                                            int, int);
+static void svc_bc_sock_free(struct svc_xprt *xprt);
+
+static struct svc_xprt *svc_bc_tcp_create(struct svc_serv *serv,
+                                      struct net *net,
+                                      struct sockaddr *sa, int salen,
+                                      int flags)
+{
+       return svc_bc_create_socket(serv, IPPROTO_TCP, net, sa, salen, flags);
+}
+
+static void svc_bc_tcp_sock_detach(struct svc_xprt *xprt)
+{
+}
+
+static struct svc_xprt_ops svc_tcp_bc_ops = {
+       .xpo_create = svc_bc_tcp_create,
+       .xpo_detach = svc_bc_tcp_sock_detach,
+       .xpo_free = svc_bc_sock_free,
+       .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr,
+};
+
+static struct svc_xprt_class svc_tcp_bc_class = {
+       .xcl_name = "tcp-bc",
+       .xcl_owner = THIS_MODULE,
+       .xcl_ops = &svc_tcp_bc_ops,
+       .xcl_max_payload = RPCSVC_MAXPAYLOAD_TCP,
+};
+
+static void svc_init_bc_xprt_sock(void)
+{
+       svc_reg_xprt_class(&svc_tcp_bc_class);
+}
+
+static void svc_cleanup_bc_xprt_sock(void)
+{
+       svc_unreg_xprt_class(&svc_tcp_bc_class);
+}
+#else /* CONFIG_NFS_V4_1 */
+static void svc_init_bc_xprt_sock(void)
+{
+}
+
+static void svc_cleanup_bc_xprt_sock(void)
+{
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static struct svc_xprt_ops svc_tcp_ops = {
        .xpo_create = svc_tcp_create,
        .xpo_recvfrom = svc_tcp_recvfrom,
@@ -1207,12 +1265,14 @@ void svc_init_xprt_sock(void)
 {
        svc_reg_xprt_class(&svc_tcp_class);
        svc_reg_xprt_class(&svc_udp_class);
+       svc_init_bc_xprt_sock();
 }
 
 void svc_cleanup_xprt_sock(void)
 {
        svc_unreg_xprt_class(&svc_tcp_class);
        svc_unreg_xprt_class(&svc_udp_class);
+       svc_cleanup_bc_xprt_sock();
 }
 
 static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv)
@@ -1509,41 +1569,45 @@ static void svc_sock_free(struct svc_xprt *xprt)
        kfree(svsk);
 }
 
+#if defined(CONFIG_NFS_V4_1)
 /*
- * Create a svc_xprt.
- *
- * For internal use only (e.g. nfsv4.1 backchannel).
- * Callers should typically use the xpo_create() method.
+ * Create a back channel svc_xprt which shares the fore channel socket.
  */
-struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot)
+static struct svc_xprt *svc_bc_create_socket(struct svc_serv *serv,
+                                            int protocol,
+                                            struct net *net,
+                                            struct sockaddr *sin, int len,
+                                            int flags)
 {
        struct svc_sock *svsk;
-       struct svc_xprt *xprt = NULL;
+       struct svc_xprt *xprt;
+
+       if (protocol != IPPROTO_TCP) {
+               printk(KERN_WARNING "svc: only TCP sockets"
+                       " supported on shared back channel\n");
+               return ERR_PTR(-EINVAL);
+       }
 
-       dprintk("svc: %s\n", __func__);
        svsk = kzalloc(sizeof(*svsk), GFP_KERNEL);
        if (!svsk)
-               goto out;
+               return ERR_PTR(-ENOMEM);
 
        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);
+       svc_xprt_init(&svc_tcp_bc_class, xprt, serv);
+
+       serv->sv_bc_xprt = xprt;
+
        return xprt;
 }
-EXPORT_SYMBOL_GPL(svc_sock_create);
 
 /*
- * Destroy a svc_sock.
+ * Free a back channel svc_sock.
  */
-void svc_sock_destroy(struct svc_xprt *xprt)
+static void svc_bc_sock_free(struct svc_xprt *xprt)
 {
-       if (xprt)
+       if (xprt) {
+               kfree(xprt->xpt_bc_sid);
                kfree(container_of(xprt, struct svc_sock, sk_xprt));
+       }
 }
-EXPORT_SYMBOL_GPL(svc_sock_destroy);
+#endif /* CONFIG_NFS_V4_1 */