]> pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'next'
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 15 Oct 2008 19:54:56 +0000 (15:54 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 15 Oct 2008 19:54:56 +0000 (15:54 -0400)
1  2 
fs/nfs/super.c
net/sunrpc/clnt.c
net/sunrpc/rpcb_clnt.c
net/sunrpc/xprtrdma/rpc_rdma.c

diff --combined fs/nfs/super.c
index ffb697416cb175aa19252d4e0ac0f48806d8733f,d496e4016224eb58e8bc8fcf3d5bc96e7795950d..8b28b95c9e44a6cafe221609111a05bb0c495361
@@@ -91,6 -91,7 +91,7 @@@ enum 
        /* Mount options that take string arguments */
        Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
        Opt_addr, Opt_mountaddr, Opt_clientaddr,
+       Opt_lookupcache,
  
        /* Special mount options */
        Opt_userspace, Opt_deprecated, Opt_sloppy,
@@@ -98,7 -99,7 +99,7 @@@
        Opt_err
  };
  
 -static match_table_t nfs_mount_option_tokens = {
 +static const match_table_t nfs_mount_option_tokens = {
        { Opt_userspace, "bg" },
        { Opt_userspace, "fg" },
        { Opt_userspace, "retry=%s" },
        { Opt_mounthost, "mounthost=%s" },
        { Opt_mountaddr, "mountaddr=%s" },
  
+       { Opt_lookupcache, "lookupcache=%s" },
        { Opt_err, NULL }
  };
  
@@@ -163,7 -166,7 +166,7 @@@ enum 
        Opt_xprt_err
  };
  
 -static match_table_t nfs_xprt_protocol_tokens = {
 +static const match_table_t nfs_xprt_protocol_tokens = {
        { Opt_xprt_udp, "udp" },
        { Opt_xprt_tcp, "tcp" },
        { Opt_xprt_rdma, "rdma" },
@@@ -180,7 -183,7 +183,7 @@@ enum 
        Opt_sec_err
  };
  
 -static match_table_t nfs_secflavor_tokens = {
 +static const match_table_t nfs_secflavor_tokens = {
        { Opt_sec_none, "none" },
        { Opt_sec_none, "null" },
        { Opt_sec_sys, "sys" },
        { Opt_sec_err, NULL }
  };
  
+ enum {
+       Opt_lookupcache_all, Opt_lookupcache_positive,
+       Opt_lookupcache_none,
+       Opt_lookupcache_err
+ };
+ static match_table_t nfs_lookupcache_tokens = {
+       { Opt_lookupcache_all, "all" },
+       { Opt_lookupcache_positive, "pos" },
+       { Opt_lookupcache_positive, "positive" },
+       { Opt_lookupcache_none, "none" },
+       { Opt_lookupcache_err, NULL }
+ };
  
  static void nfs_umount_begin(struct super_block *);
  static int  nfs_statfs(struct dentry *, struct kstatfs *);
@@@ -209,7 -228,6 +228,6 @@@ static int nfs_get_sb(struct file_syste
  static int nfs_xdev_get_sb(struct file_system_type *fs_type,
                int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
  static void nfs_kill_super(struct super_block *);
- static void nfs_put_super(struct super_block *);
  static int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
  
  static struct file_system_type nfs_fs_type = {
@@@ -232,7 -250,6 +250,6 @@@ static const struct super_operations nf
        .alloc_inode    = nfs_alloc_inode,
        .destroy_inode  = nfs_destroy_inode,
        .write_inode    = nfs_write_inode,
-       .put_super      = nfs_put_super,
        .statfs         = nfs_statfs,
        .clear_inode    = nfs_clear_inode,
        .umount_begin   = nfs_umount_begin,
@@@ -337,26 -354,20 +354,20 @@@ void __exit unregister_nfs_fs(void
        unregister_filesystem(&nfs_fs_type);
  }
  
- void nfs_sb_active(struct nfs_server *server)
+ void nfs_sb_active(struct super_block *sb)
  {
-       atomic_inc(&server->active);
- }
+       struct nfs_server *server = NFS_SB(sb);
  
- void nfs_sb_deactive(struct nfs_server *server)
- {
-       if (atomic_dec_and_test(&server->active))
-               wake_up(&server->active_wq);
+       if (atomic_inc_return(&server->active) == 1)
+               atomic_inc(&sb->s_active);
  }
  
static void nfs_put_super(struct super_block *sb)
void nfs_sb_deactive(struct super_block *sb)
  {
        struct nfs_server *server = NFS_SB(sb);
-       /*
-        * Make sure there are no outstanding ops to this server.
-        * If so, wait for them to finish before allowing the
-        * unmount to continue.
-        */
-       wait_event(server->active_wq, atomic_read(&server->active) == 0);
+       if (atomic_dec_and_test(&server->active))
+               deactivate_super(sb);
  }
  
  /*
@@@ -663,25 -674,6 +674,6 @@@ static void nfs_umount_begin(struct sup
                rpc_killall_tasks(rpc);
  }
  
- /*
-  * Set the port number in an address.  Be agnostic about the address family.
-  */
- static void nfs_set_port(struct sockaddr *sap, unsigned short port)
- {
-       switch (sap->sa_family) {
-       case AF_INET: {
-               struct sockaddr_in *ap = (struct sockaddr_in *)sap;
-               ap->sin_port = htons(port);
-               break;
-       }
-       case AF_INET6: {
-               struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap;
-               ap->sin6_port = htons(port);
-               break;
-       }
-       }
- }
  /*
   * Sanity-check a server address provided by the mount command.
   *
@@@ -724,20 -716,22 +716,22 @@@ static void nfs_parse_ipv4_address(cha
        *addr_len = 0;
  }
  
- #define IPV6_SCOPE_DELIMITER  '%'
  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- static void nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
-                                   const char *delim,
-                                   struct sockaddr_in6 *sin6)
+ static int nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
+                                  const char *delim,
+                                  struct sockaddr_in6 *sin6)
  {
        char *p;
        size_t len;
  
-       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
-               return ;
+       if ((string + str_len) == delim)
+               return 1;
        if (*delim != IPV6_SCOPE_DELIMITER)
-               return;
+               return 0;
+       if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
+               return 0;
  
        len = (string + str_len) - delim - 1;
        p = kstrndup(delim + 1, len, GFP_KERNEL);
                        scope_id = dev->ifindex;
                        dev_put(dev);
                } else {
-                       /* scope_id is set to zero on error */
-                       strict_strtoul(p, 10, &scope_id);
+                       if (strict_strtoul(p, 10, &scope_id) == 0) {
+                               kfree(p);
+                               return 0;
+                       }
                }
  
                kfree(p);
                sin6->sin6_scope_id = scope_id;
                dfprintk(MOUNT, "NFS: IPv6 scope ID = %lu\n", scope_id);
+               return 1;
        }
+       return 0;
  }
  
  static void nfs_parse_ipv6_address(char *string, size_t str_len,
  
                sin6->sin6_family = AF_INET6;
                *addr_len = sizeof(*sin6);
-               if (in6_pton(string, str_len, addr, IPV6_SCOPE_DELIMITER, &delim)) {
-                       nfs_parse_ipv6_scope_id(string, str_len, delim, sin6);
-                       return;
+               if (in6_pton(string, str_len, addr,
+                                       IPV6_SCOPE_DELIMITER, &delim) != 0) {
+                       if (nfs_parse_ipv6_scope_id(string, str_len,
+                                                       delim, sin6) != 0)
+                               return;
                }
        }
  
@@@ -798,7 -800,7 +800,7 @@@ static void nfs_parse_ipv6_address(cha
   * If there is a problem constructing the new sockaddr, set the address
   * family to AF_UNSPEC.
   */
static void nfs_parse_ip_address(char *string, size_t str_len,
+ void nfs_parse_ip_address(char *string, size_t str_len,
                                 struct sockaddr *sap, size_t *addr_len)
  {
        unsigned int i, colons;
@@@ -1258,6 -1260,30 +1260,30 @@@ static int nfs_parse_mount_options(cha
                                             &mnt->mount_server.addrlen);
                        kfree(string);
                        break;
+               case Opt_lookupcache:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+                       token = match_token(string,
+                                       nfs_lookupcache_tokens, args);
+                       kfree(string);
+                       switch (token) {
+                               case Opt_lookupcache_all:
+                                       mnt->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
+                                       break;
+                               case Opt_lookupcache_positive:
+                                       mnt->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE;
+                                       mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG;
+                                       break;
+                               case Opt_lookupcache_none:
+                                       mnt->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
+                                       break;
+                               default:
+                                       errors++;
+                                       dfprintk(MOUNT, "NFS:   invalid "
+                                                       "lookupcache argument\n");
+                       };
+                       break;
  
                /*
                 * Special options
@@@ -1558,7 -1584,7 +1584,7 @@@ static int nfs_validate_mount_data(voi
                 * Translate to nfs_parsed_mount_data, which nfs_fill_super
                 * can deal with.
                 */
-               args->flags             = data->flags;
+               args->flags             = data->flags & NFS_MOUNT_FLAGMASK;
                args->rsize             = data->rsize;
                args->wsize             = data->wsize;
                args->timeo             = data->timeo;
diff --combined net/sunrpc/clnt.c
index da0789fa1b88876e785e2f8c233fd8f2f3950dbd,a59cdf423c1cf907b40b2745f96c8c936676f55b..4895c341e46d03b2ad31b23c229e58876dce1c68
@@@ -174,7 -174,7 +174,7 @@@ static struct rpc_clnt * rpc_new_client
        clnt->cl_procinfo = version->procs;
        clnt->cl_maxproc  = version->nrprocs;
        clnt->cl_protname = program->name;
 -      clnt->cl_prog     = program->number;
 +      clnt->cl_prog     = args->prognumber ? : program->number;
        clnt->cl_vers     = version->number;
        clnt->cl_stats    = program->stats;
        clnt->cl_metrics  = rpc_alloc_iostats(clnt);
        }
  
        /* save the nodename */
-       clnt->cl_nodelen = strlen(utsname()->nodename);
+       clnt->cl_nodelen = strlen(init_utsname()->nodename);
        if (clnt->cl_nodelen > UNX_MAXNODENAME)
                clnt->cl_nodelen = UNX_MAXNODENAME;
-       memcpy(clnt->cl_nodename, utsname()->nodename, clnt->cl_nodelen);
+       memcpy(clnt->cl_nodename, init_utsname()->nodename, clnt->cl_nodelen);
        rpc_register_client(clnt);
        return clnt;
  
diff --combined net/sunrpc/rpcb_clnt.c
index 34abc91058d84ebe5dd89ed481d9542b548c24a7,0a22f00734a4ba1024cde9c5337e3e5b89168e76..41013dd66ac3f3e61e4823ef853911e179efc46b
@@@ -20,7 -20,6 +20,7 @@@
  #include <linux/in6.h>
  #include <linux/kernel.h>
  #include <linux/errno.h>
 +#include <net/ipv6.h>
  
  #include <linux/sunrpc/clnt.h>
  #include <linux/sunrpc/sched.h>
@@@ -177,12 -176,13 +177,12 @@@ static struct rpc_clnt *rpcb_create(cha
  }
  
  static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
 -                            u32 version, struct rpc_message *msg,
 -                            int *result)
 +                            u32 version, struct rpc_message *msg)
  {
        struct rpc_clnt *rpcb_clnt;
 -      int error = 0;
 +      int result, error = 0;
  
 -      *result = 0;
 +      msg->rpc_resp = &result;
  
        rpcb_clnt = rpcb_create_local(addr, addrlen, version);
        if (!IS_ERR(rpcb_clnt)) {
        } else
                error = PTR_ERR(rpcb_clnt);
  
 -      if (error < 0)
 +      if (error < 0) {
                printk(KERN_WARNING "RPC: failed to contact local rpcbind "
                                "server (errno %d).\n", -error);
 -      dprintk("RPC:       registration status %d/%d\n", error, *result);
 +              return error;
 +      }
  
 -      return error;
 +      if (!result)
 +              return -EACCES;
 +      return 0;
  }
  
  /**
   * @vers: RPC version number to bind
   * @prot: transport protocol to register
   * @port: port value to register
 - * @okay: OUT: result code
 + *
 + * Returns zero if the registration request was dispatched successfully
 + * and the rpcbind daemon returned success.  Otherwise, returns an errno
 + * value that reflects the nature of the error (request could not be
 + * dispatched, timed out, or rpcbind returned an error).
   *
   * RPC services invoke this function to advertise their contact
   * information via the system's rpcbind daemon.  RPC services
   * all registered transports for [program, version] from the local
   * rpcbind database.
   *
 - * Returns zero if the registration request was dispatched
 - * successfully and a reply was received.  The rpcbind daemon's
 - * boolean result code is stored in *okay.
 - *
 - * Returns an errno value and sets *result to zero if there was
 - * some problem that prevented the rpcbind request from being
 - * dispatched, or if the rpcbind daemon did not respond within
 - * the timeout.
 - *
   * This function uses rpcbind protocol version 2 to contact the
   * local rpcbind daemon.
   *
   * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6
   * addresses).
   */
 -int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
 +int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port)
  {
        struct rpcbind_args map = {
                .r_prog         = prog,
        };
        struct rpc_message msg = {
                .rpc_argp       = &map,
 -              .rpc_resp       = okay,
        };
  
        dprintk("RPC:       %sregistering (%u, %u, %d, %u) with local "
  
        return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
                                        sizeof(rpcb_inaddr_loopback),
 -                                      RPCBVERS_2, &msg, okay);
 +                                      RPCBVERS_2, &msg);
  }
  
  /*
@@@ -287,7 -290,7 +287,7 @@@ static int rpcb_register_netid4(struct 
  
        return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
                                        sizeof(rpcb_inaddr_loopback),
 -                                      RPCBVERS_4, msg, msg->rpc_resp);
 +                                      RPCBVERS_4, msg);
  }
  
  /*
@@@ -301,13 -304,10 +301,13 @@@ static int rpcb_register_netid6(struct 
        char buf[64];
  
        /* Construct AF_INET6 universal address */
 -      snprintf(buf, sizeof(buf),
 -                      NIP6_FMT".%u.%u",
 -                      NIP6(address_to_register->sin6_addr),
 -                      port >> 8, port & 0xff);
 +      if (ipv6_addr_any(&address_to_register->sin6_addr))
 +              snprintf(buf, sizeof(buf), "::.%u.%u",
 +                              port >> 8, port & 0xff);
 +      else
 +              snprintf(buf, sizeof(buf), NIP6_FMT".%u.%u",
 +                              NIP6(address_to_register->sin6_addr),
 +                              port >> 8, port & 0xff);
        map->r_addr = buf;
  
        dprintk("RPC:       %sregistering [%u, %u, %s, '%s'] with "
  
        return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback,
                                        sizeof(rpcb_in6addr_loopback),
 -                                      RPCBVERS_4, msg, msg->rpc_resp);
 +                                      RPCBVERS_4, msg);
  }
  
  /**
   * @version: RPC version number of service to (un)register
   * @address: address family, IP address, and port to (un)register
   * @netid: netid of transport protocol to (un)register
 - * @result: result code from rpcbind RPC call
 + *
 + * Returns zero if the registration request was dispatched successfully
 + * and the rpcbind daemon returned success.  Otherwise, returns an errno
 + * value that reflects the nature of the error (request could not be
 + * dispatched, timed out, or rpcbind returned an error).
   *
   * RPC services invoke this function to advertise their contact
   * information via the system's rpcbind daemon.  RPC services
   * to zero.  Callers pass a netid of "" to unregister all
   * transport netids associated with [program, version, address].
   *
 - * Returns zero if the registration request was dispatched
 - * successfully and a reply was received.  The rpcbind daemon's
 - * result code is stored in *result.
 - *
 - * Returns an errno value and sets *result to zero if there was
 - * some problem that prevented the rpcbind request from being
 - * dispatched, or if the rpcbind daemon did not respond within
 - * the timeout.
 - *
   * This function uses rpcbind protocol version 4 to contact the
   * local rpcbind daemon.  The local rpcbind daemon must support
   * version 4 of the rpcbind protocol in order for these functions
   * advertises the service on all IPv4 and IPv6 addresses.
   */
  int rpcb_v4_register(const u32 program, const u32 version,
 -                   const struct sockaddr *address, const char *netid,
 -                   int *result)
 +                   const struct sockaddr *address, const char *netid)
  {
        struct rpcbind_args map = {
                .r_prog         = program,
        };
        struct rpc_message msg = {
                .rpc_argp       = &map,
 -              .rpc_resp       = result,
        };
  
 -      *result = 0;
 -
        switch (address->sa_family) {
        case AF_INET:
                return rpcb_register_netid4((struct sockaddr_in *)address,
@@@ -460,6 -469,28 +460,28 @@@ static struct rpc_task *rpcb_call_async
        return rpc_run_task(&task_setup_data);
  }
  
+ /*
+  * In the case where rpc clients have been cloned, we want to make
+  * sure that we use the program number/version etc of the actual
+  * owner of the xprt. To do so, we walk back up the tree of parents
+  * to find whoever created the transport and/or whoever has the
+  * autobind flag set.
+  */
+ static struct rpc_clnt *rpcb_find_transport_owner(struct rpc_clnt *clnt)
+ {
+       struct rpc_clnt *parent = clnt->cl_parent;
+       while (parent != clnt) {
+               if (parent->cl_xprt != clnt->cl_xprt)
+                       break;
+               if (clnt->cl_autobind)
+                       break;
+               clnt = parent;
+               parent = parent->cl_parent;
+       }
+       return clnt;
+ }
  /**
   * rpcb_getport_async - obtain the port for a given RPC service on a given host
   * @task: task that is waiting for portmapper request
   */
  void rpcb_getport_async(struct rpc_task *task)
  {
-       struct rpc_clnt *clnt = task->tk_client;
+       struct rpc_clnt *clnt;
        struct rpc_procinfo *proc;
        u32 bind_version;
-       struct rpc_xprt *xprt = task->tk_xprt;
+       struct rpc_xprt *xprt;
        struct rpc_clnt *rpcb_clnt;
        static struct rpcbind_args *map;
        struct rpc_task *child;
        size_t salen;
        int status;
  
+       clnt = rpcb_find_transport_owner(task->tk_client);
+       xprt = clnt->cl_xprt;
        dprintk("RPC: %5u %s(%s, %u, %u, %d)\n",
                task->tk_pid, __func__,
                clnt->cl_server, clnt->cl_prog, clnt->cl_vers, xprt->prot);
  
-       /* Autobind on cloned rpc clients is discouraged */
-       BUG_ON(clnt->cl_parent != clnt);
        /* Put self on the wait queue to ensure we get notified if
         * some other task is already attempting to bind the port */
        rpc_sleep_on(&xprt->binding, task, NULL);
                status = -ENOMEM;
                dprintk("RPC: %5u %s: no memory available\n",
                        task->tk_pid, __func__);
-               goto bailout_nofree;
+               goto bailout_release_client;
        }
        map->r_prog = clnt->cl_prog;
        map->r_vers = clnt->cl_vers;
                        task->tk_pid, __func__);
                return;
        }
-       rpc_put_task(child);
  
-       task->tk_xprt->stat.bind_count++;
+       xprt->stat.bind_count++;
+       rpc_put_task(child);
        return;
  
+ bailout_release_client:
+       rpc_release_client(rpcb_clnt);
  bailout_nofree:
        rpcb_wake_rpcbind_waiters(xprt, status);
        task->tk_status = status;
@@@ -624,7 -657,7 +648,7 @@@ static void rpcb_getport_done(struct rp
  static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p,
                               struct rpcbind_args *rpcb)
  {
 -      dprintk("RPC:       rpcb_encode_mapping(%u, %u, %d, %u)\n",
 +      dprintk("RPC:       encoding rpcb request (%u, %u, %d, %u)\n",
                        rpcb->r_prog, rpcb->r_vers, rpcb->r_prot, rpcb->r_port);
        *p++ = htonl(rpcb->r_prog);
        *p++ = htonl(rpcb->r_vers);
@@@ -639,7 -672,7 +663,7 @@@ static int rpcb_decode_getport(struct r
                               unsigned short *portp)
  {
        *portp = (unsigned short) ntohl(*p++);
 -      dprintk("RPC:       rpcb_decode_getport result %u\n",
 +      dprintk("RPC:       rpcb getport result: %u\n",
                        *portp);
        return 0;
  }
@@@ -648,7 -681,7 +672,7 @@@ static int rpcb_decode_set(struct rpc_r
                           unsigned int *boolp)
  {
        *boolp = (unsigned int) ntohl(*p++);
 -      dprintk("RPC:       rpcb_decode_set: call %s\n",
 +      dprintk("RPC:       rpcb set/unset call %s\n",
                        (*boolp ? "succeeded" : "failed"));
        return 0;
  }
  static int rpcb_encode_getaddr(struct rpc_rqst *req, __be32 *p,
                               struct rpcbind_args *rpcb)
  {
 -      dprintk("RPC:       rpcb_encode_getaddr(%u, %u, %s)\n",
 +      dprintk("RPC:       encoding rpcb request (%u, %u, %s)\n",
                        rpcb->r_prog, rpcb->r_vers, rpcb->r_addr);
        *p++ = htonl(rpcb->r_prog);
        *p++ = htonl(rpcb->r_vers);
index 5c1954d28d0914578d744f94eddbd157f7da2e3a,15101f294e00b1f1ecb786ffa6b2caea74ce9dc6..14106d26bb95881b2f7e3c5496aa8b10625a298f
@@@ -118,6 -118,10 +118,10 @@@ rpcrdma_convert_iovs(struct xdr_buf *xd
        }
  
        if (xdrbuf->tail[0].iov_len) {
+               /* the rpcrdma protocol allows us to omit any trailing
+                * xdr pad bytes, saving the server an RDMA operation. */
+               if (xdrbuf->tail[0].iov_len < 4 && xprt_rdma_pad_optimize)
+                       return n;
                if (n == nsegs)
                        return 0;
                seg[n].mr_page = NULL;
@@@ -508,8 -512,8 +512,8 @@@ rpcrdma_marshal_req(struct rpc_rqst *rq
        if (hdrlen == 0)
                return -1;
  
-       dprintk("RPC:       %s: %s: hdrlen %zd rpclen %zd padlen %zd\n"
-               "                   headerp 0x%p base 0x%p lkey 0x%x\n",
+       dprintk("RPC:       %s: %s: hdrlen %zd rpclen %zd padlen %zd"
+               " headerp 0x%p base 0x%p lkey 0x%x\n",
                __func__, transfertypes[wtype], hdrlen, rpclen, padlen,
                headerp, base, req->rl_iov.lkey);
  
@@@ -594,7 -598,7 +598,7 @@@ rpcrdma_count_chunks(struct rpcrdma_re
   * Scatter inline received data back into provided iov's.
   */
  static void
- rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len)
+ rpcrdma_inline_fixup(struct rpc_rqst *rqst, char *srcp, int copy_len, int pad)
  {
        int i, npages, curlen, olen;
        char *destp;
        } else
                rqst->rq_rcv_buf.tail[0].iov_len = 0;
  
+       if (pad) {
+               /* implicit padding on terminal chunk */
+               unsigned char *p = rqst->rq_rcv_buf.tail[0].iov_base;
+               while (pad--)
+                       p[rqst->rq_rcv_buf.tail[0].iov_len++] = 0;
+       }
        if (copy_len)
                dprintk("RPC:       %s: %d bytes in"
                        " %d extra segments (%d lost)\n",
@@@ -681,12 -692,14 +692,14 @@@ rpcrdma_conn_func(struct rpcrdma_ep *ep
        struct rpc_xprt *xprt = ep->rep_xprt;
  
        spin_lock_bh(&xprt->transport_lock);
+       if (++xprt->connect_cookie == 0)        /* maintain a reserved value */
+               ++xprt->connect_cookie;
        if (ep->rep_connected > 0) {
                if (!xprt_test_and_set_connected(xprt))
                        xprt_wake_pending_tasks(xprt, 0);
        } else {
                if (xprt_test_and_clear_connected(xprt))
-                       xprt_wake_pending_tasks(xprt, ep->rep_connected);
+                       xprt_wake_pending_tasks(xprt, -ENOTCONN);
        }
        spin_unlock_bh(&xprt->transport_lock);
  }
@@@ -769,7 -782,7 +782,7 @@@ repost
        /* check for expected message types */
        /* The order of some of these tests is important. */
        switch (headerp->rm_type) {
 -      case __constant_htonl(RDMA_MSG):
 +      case htonl(RDMA_MSG):
                /* never expect read chunks */
                /* never expect reply chunks (two ways to check) */
                /* never expect write chunks without having offered RDMA */
                            ((unsigned char *)iptr - (unsigned char *)headerp);
                        status = rep->rr_len + rdmalen;
                        r_xprt->rx_stats.total_rdma_reply += rdmalen;
+                       /* special case - last chunk may omit padding */
+                       if (rdmalen &= 3) {
+                               rdmalen = 4 - rdmalen;
+                               status += rdmalen;
+                       }
                } else {
                        /* else ordinary inline */
+                       rdmalen = 0;
                        iptr = (__be32 *)((unsigned char *)headerp + 28);
                        rep->rr_len -= 28; /*sizeof *headerp;*/
                        status = rep->rr_len;
                }
                /* Fix up the rpc results for upper layer */
-               rpcrdma_inline_fixup(rqst, (char *)iptr, rep->rr_len);
+               rpcrdma_inline_fixup(rqst, (char *)iptr, rep->rr_len, rdmalen);
                break;
  
 -      case __constant_htonl(RDMA_NOMSG):
 +      case htonl(RDMA_NOMSG):
                /* never expect read or write chunks, always reply chunks */
                if (headerp->rm_body.rm_chunks[0] != xdr_zero ||
                    headerp->rm_body.rm_chunks[1] != xdr_zero ||