Merge tag 'nfs-for-3.5-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 May 2012 17:43:51 +0000 (10:43 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 May 2012 17:43:51 +0000 (10:43 -0700)
Pull NFS client updates from Trond Myklebust:
 "New features include:
   - Rewrite the O_DIRECT code so that it can share the same coalescing
     and pNFS functionality as the page cache code.
   - Allow the server to provide hints as to when we should use pNFS,
     and when it is more efficient to read and write through the
     metadata server.
   - NFS cache consistency updates:
     * Use the ctime to emulate a change attribute for NFSv2/v3 so that
       all NFS versions can share the same cache management code.
     * New cache management code will only look at the change attribute
       and size attribute when deciding whether or not our cached data
       is still valid or not.
     * Don't request NFSv4 post-op attributes on writes in cases such as
       O_DIRECT, where we don't care about data cache consistency, or
       when we have a write delegation, and know that our cache is still
       consistent.
     * Don't request NFSv4 post-op attributes on operations such as
       COMMIT, where there are no expected metadata updates.
     * Don't request NFSv4 directory post-op attributes in cases where
       the operations themselves already return change attribute
       updates: i.e. operations such as OPEN, CREATE, REMOVE, LINK and
       RENAME.
   - Speed up 'ls' and friends by using READDIR rather than READDIRPLUS
     if we detect no attempts to lookup filenames.
   - Improve the code sharing between NFSv2/v3 and v4 mounts
   - NFSv4.1 state management efficiency improvements
   - More patches in preparation for NFSv4/v4.1 migration functionality."

Fix trivial conflict in fs/nfs/nfs4proc.c that was due to the dcache
qstr name initialization changes (that made the length/hash a 64-bit
union)

* tag 'nfs-for-3.5-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (146 commits)
  NFSv4: Add debugging printks to state manager
  NFSv4: Map NFS4ERR_SHARE_DENIED into an EACCES error instead of EIO
  NFSv4: update_changeattr does not need to set NFS_INO_REVAL_PAGECACHE
  NFSv4.1: nfs4_reset_session should use nfs4_handle_reclaim_lease_error
  NFSv4.1: Handle other occurrences of NFS4ERR_CONN_NOT_BOUND_TO_SESSION
  NFSv4.1: Handle NFS4ERR_CONN_NOT_BOUND_TO_SESSION in the state manager
  NFSv4.1: Handle errors in nfs4_bind_conn_to_session
  NFSv4.1: nfs4_bind_conn_to_session should drain the session
  NFSv4.1: Don't clobber the seqid if exchange_id returns a confirmed clientid
  NFSv4.1: Add DESTROY_CLIENTID
  NFSv4.1: Ensure we use the correct credentials for bind_conn_to_session
  NFSv4.1: Ensure we use the correct credentials for session create/destroy
  NFSv4.1: Move NFSPROC4_CLNT_BIND_CONN_TO_SESSION to the end of the operations
  NFSv4.1: Handle NFS4ERR_SEQ_MISORDERED when confirming the lease
  NFSv4: When purging the lease, we must clear NFS4CLNT_LEASE_CONFIRM
  NFSv4: Clean up the error handling for nfs4_reclaim_lease
  NFSv4.1: Exchange ID must use GFP_NOFS allocation mode
  nfs41: Use BIND_CONN_TO_SESSION for CB_PATH_DOWN*
  nfs4.1: add BIND_CONN_TO_SESSION operation
  NFSv4.1 test the mdsthreshold hint parameters
  ...

1  2 
fs/nfs/dir.c
fs/nfs/inode.c
fs/nfs/nfs3proc.c
fs/nfs/nfs4proc.c
fs/nfs/proc.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/xprt.c

diff --combined fs/nfs/dir.c
index eedd24d0ad2efc6a02e7ff7b552e2e707adadfbd,d0884c0d946452ef04c2f9cc8d2d54319b34842b..0989a2099688a377279d76f4f8c56dbc91070027
@@@ -474,10 -474,36 +474,33 @@@ different
        return 0;
  }
  
+ static
+ bool nfs_use_readdirplus(struct inode *dir, struct file *filp)
+ {
+       if (!nfs_server_capable(dir, NFS_CAP_READDIRPLUS))
+               return false;
+       if (test_and_clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags))
+               return true;
+       if (filp->f_pos == 0)
+               return true;
+       return false;
+ }
+ /*
+  * This function is called by the lookup code to request the use of
+  * readdirplus to accelerate any future lookups in the same
+  * directory.
+  */
+ static
+ void nfs_advise_use_readdirplus(struct inode *dir)
+ {
+       set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags);
+ }
  static
  void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
  {
 -      struct qstr filename = {
 -              .len = entry->len,
 -              .name = entry->name,
 -      };
 +      struct qstr filename = QSTR_INIT(entry->name, entry->len);
        struct dentry *dentry;
        struct dentry *alias;
        struct inode *dir = parent->d_inode;
@@@ -871,7 -897,7 +894,7 @@@ static int nfs_readdir(struct file *fil
        desc->file = filp;
        desc->dir_cookie = &dir_ctx->dir_cookie;
        desc->decode = NFS_PROTO(inode)->decode_dirent;
-       desc->plus = NFS_USE_READDIRPLUS(inode);
+       desc->plus = nfs_use_readdirplus(inode, filp) ? 1 : 0;
  
        nfs_block_sillyrename(dentry);
        res = nfs_revalidate_mapping(inode, filp->f_mapping);
@@@ -1111,7 -1137,7 +1134,7 @@@ static int nfs_lookup_revalidate(struc
        if (!inode) {
                if (nfs_neg_need_reval(dir, dentry, nd))
                        goto out_bad;
-               goto out_valid;
+               goto out_valid_noent;
        }
  
        if (is_bad_inode(inode)) {
        if (fhandle == NULL || fattr == NULL)
                goto out_error;
  
-       error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
        if (error)
                goto out_bad;
        if (nfs_compare_fh(NFS_FH(inode), fhandle))
  out_set_verifier:
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
   out_valid:
+       /* Success: notify readdir to use READDIRPLUS */
+       nfs_advise_use_readdirplus(dir);
+  out_valid_noent:
        dput(parent);
        dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
                        __func__, dentry->d_parent->d_name.name,
@@@ -1296,7 -1325,7 +1322,7 @@@ static struct dentry *nfs_lookup(struc
        parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        nfs_block_sillyrename(parent);
-       error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
        if (error == -ENOENT)
                goto no_entry;
        if (error < 0) {
        if (IS_ERR(res))
                goto out_unblock_sillyrename;
  
+       /* Success: notify readdir to use READDIRPLUS */
+       nfs_advise_use_readdirplus(dir);
  no_entry:
        res = d_materialise_unique(dentry, inode);
        if (res != NULL) {
@@@ -1643,7 -1675,7 +1672,7 @@@ int nfs_instantiate(struct dentry *dent
        if (dentry->d_inode)
                goto out;
        if (fhandle->size == 0) {
-               error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
+               error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
                if (error)
                        goto out_error;
        }
diff --combined fs/nfs/inode.c
index c6073139b402f1250634dfa892ca6ae4a4885cc3,a6f5fbbe9b9c3c0e3a6d4d6851e32004d6b224e7..2f6f78c4b42d7f263419f7cf75153d5766f06c71
@@@ -121,7 -121,7 +121,7 @@@ static void nfs_clear_inode(struct inod
  void nfs_evict_inode(struct inode *inode)
  {
        truncate_inode_pages(&inode->i_data, 0);
 -      end_writeback(inode);
 +      clear_inode(inode);
        nfs_clear_inode(inode);
  }
  
@@@ -285,9 -285,7 +285,7 @@@ nfs_fhget(struct super_block *sb, struc
                inode->i_mode = fattr->mode;
                if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
                                && nfs_server_capable(inode, NFS_CAP_MODE))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                /* Why so? Because we want revalidate for devices/FIFOs, and
                 * that's precisely what we have in nfs_file_inode_operations.
                 */
                        inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
                        inode->i_fop = &nfs_dir_operations;
                        inode->i_data.a_ops = &nfs_dir_aops;
-                       if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
-                               set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
                        /* Deal with crossing mountpoints */
                        if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
                                        fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
                inode->i_gid = -2;
                inode->i_blocks = 0;
                memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
+               nfsi->write_io = 0;
+               nfsi->read_io = 0;
  
                nfsi->read_cache_jiffies = fattr->time_start;
                nfsi->attr_gencount = fattr->gencount;
                if (fattr->valid & NFS_ATTR_FATTR_MTIME)
                        inode->i_mtime = fattr->mtime;
                else if (nfs_server_capable(inode, NFS_CAP_MTIME))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_DATA;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_CTIME)
                        inode->i_ctime = fattr->ctime;
                else if (nfs_server_capable(inode, NFS_CAP_CTIME))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
                        inode->i_version = fattr->change_attr;
                else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_DATA;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_SIZE)
                        inode->i_size = nfs_size_to_loff_t(fattr->size);
                else
                        nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_DATA
                                | NFS_INO_REVAL_PAGECACHE;
                if (fattr->valid & NFS_ATTR_FATTR_NLINK)
                        set_nlink(inode, fattr->nlink);
                if (fattr->valid & NFS_ATTR_FATTR_OWNER)
                        inode->i_uid = fattr->uid;
                else if (nfs_server_capable(inode, NFS_CAP_OWNER))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_GROUP)
                        inode->i_gid = fattr->gid;
                else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP))
-                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL;
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
                        inode->i_blocks = fattr->du.nfs2.blocks;
                if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
@@@ -654,6 -643,7 +643,7 @@@ struct nfs_open_context *alloc_nfs_open
        nfs_init_lock_context(&ctx->lock_context);
        ctx->lock_context.open_context = ctx;
        INIT_LIST_HEAD(&ctx->list);
+       ctx->mdsthreshold = NULL;
        return ctx;
  }
  
@@@ -682,6 -672,7 +672,7 @@@ static void __put_nfs_open_context(stru
                put_rpccred(ctx->cred);
        dput(ctx->dentry);
        nfs_sb_deactive(sb);
+       kfree(ctx->mdsthreshold);
        kfree(ctx);
  }
  
@@@ -870,6 -861,15 +861,15 @@@ static int nfs_invalidate_mapping(struc
        return 0;
  }
  
+ static bool nfs_mapping_need_revalidate_inode(struct inode *inode)
+ {
+       if (nfs_have_delegated_attributes(inode))
+               return false;
+       return (NFS_I(inode)->cache_validity & NFS_INO_REVAL_PAGECACHE)
+               || nfs_attribute_timeout(inode)
+               || NFS_STALE(inode);
+ }
  /**
   * nfs_revalidate_mapping - Revalidate the pagecache
   * @inode - pointer to host inode
@@@ -880,9 -880,7 +880,7 @@@ int nfs_revalidate_mapping(struct inod
        struct nfs_inode *nfsi = NFS_I(inode);
        int ret = 0;
  
-       if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE)
-                       || nfs_attribute_cache_expired(inode)
-                       || NFS_STALE(inode)) {
+       if (nfs_mapping_need_revalidate_inode(inode)) {
                ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
                if (ret < 0)
                        goto out;
@@@ -948,6 -946,8 +946,8 @@@ static int nfs_check_inode_attributes(s
        unsigned long invalid = 0;
  
  
+       if (nfs_have_delegated_attributes(inode))
+               return 0;
        /* Has the inode gone and changed behind our back? */
        if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid)
                return -EIO;
  
        /* Verify a few of the more important attributes */
        if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime))
-               invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+               invalid |= NFS_INO_INVALID_ATTR;
  
        if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
                cur_size = i_size_read(inode);
@@@ -1279,14 -1279,26 +1279,26 @@@ static int nfs_update_inode(struct inod
                        nfs_display_fhandle_hash(NFS_FH(inode)),
                        atomic_read(&inode->i_count), fattr->valid);
  
-       if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid)
-               goto out_fileid;
+       if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid) {
+               printk(KERN_ERR "NFS: server %s error: fileid changed\n"
+                       "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
+                       NFS_SERVER(inode)->nfs_client->cl_hostname,
+                       inode->i_sb->s_id, (long long)nfsi->fileid,
+                       (long long)fattr->fileid);
+               goto out_err;
+       }
  
        /*
         * Make sure the inode's type hasn't changed.
         */
-       if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
-               goto out_changed;
+       if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
+               /*
+               * Big trouble! The inode has become a different object.
+               */
+               printk(KERN_DEBUG "NFS: %s: inode %ld mode changed, %07o to %07o\n",
+                               __func__, inode->i_ino, inode->i_mode, fattr->mode);
+               goto out_err;
+       }
  
        server = NFS_SERVER(inode);
        /* Update the fsid? */
                if (inode->i_version != fattr->change_attr) {
                        dprintk("NFS: change_attr change on server for file %s/%ld\n",
                                        inode->i_sb->s_id, inode->i_ino);
-                       invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+                       invalid |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_PAGECACHE;
                        if (S_ISDIR(inode->i_mode))
                                nfs_force_lookup_revalidate(inode);
                        inode->i_version = fattr->change_attr;
                invalid |= save_cache_validity;
  
        if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
-               /* NFSv2/v3: Check if the mtime agrees */
-               if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
-                       dprintk("NFS: mtime change on server for file %s/%ld\n",
-                                       inode->i_sb->s_id, inode->i_ino);
-                       invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
-                       if (S_ISDIR(inode->i_mode))
-                               nfs_force_lookup_revalidate(inode);
-                       memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
-               }
+               memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
        } else if (server->caps & NFS_CAP_MTIME)
                invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_DATA
-                               | NFS_INO_REVAL_PAGECACHE
                                | NFS_INO_REVAL_FORCED);
  
        if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
-               /* If ctime has changed we should definitely clear access+acl caches */
-               if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) {
-                       invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-                       /* and probably clear data for a directory too as utimes can cause
-                        * havoc with our cache.
-                        */
-                       if (S_ISDIR(inode->i_mode)) {
-                               invalid |= NFS_INO_INVALID_DATA;
-                               nfs_force_lookup_revalidate(inode);
-                       }
-                       memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
-               }
+               memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
        } else if (server->caps & NFS_CAP_CTIME)
                invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
-                               | NFS_INO_INVALID_ACCESS
-                               | NFS_INO_INVALID_ACL
                                | NFS_INO_REVAL_FORCED);
  
        /* Check if our cached file size is stale */
                nfsi->cache_validity |= invalid;
  
        return 0;
-  out_changed:
-       /*
-        * Big trouble! The inode has become a different object.
-        */
-       printk(KERN_DEBUG "NFS: %s: inode %ld mode changed, %07o to %07o\n",
-                       __func__, inode->i_ino, inode->i_mode, fattr->mode);
   out_err:
        /*
         * No need to worry about unhashing the dentry, as the
         */
        nfs_invalidate_inode(inode);
        return -ESTALE;
-  out_fileid:
-       printk(KERN_ERR "NFS: server %s error: fileid changed\n"
-               "fsid %s: expected fileid 0x%Lx, got 0x%Lx\n",
-               NFS_SERVER(inode)->nfs_client->cl_hostname, inode->i_sb->s_id,
-               (long long)nfsi->fileid, (long long)fattr->fileid);
-       goto out_err;
  }
  
  
  void nfs4_evict_inode(struct inode *inode)
  {
        truncate_inode_pages(&inode->i_data, 0);
 -      end_writeback(inode);
 +      clear_inode(inode);
        pnfs_return_layout(inode);
        pnfs_destroy_layout(NFS_I(inode));
        /* If we are holding a delegation, return it! */
@@@ -1547,7 -1527,7 +1527,7 @@@ static inline void nfs4_init_once(struc
        nfsi->delegation_state = 0;
        init_rwsem(&nfsi->rwsem);
        nfsi->layout = NULL;
-       atomic_set(&nfsi->commits_outstanding, 0);
+       atomic_set(&nfsi->commit_info.rpcs_out, 0);
  #endif
  }
  
@@@ -1559,9 -1539,9 +1539,9 @@@ static void init_once(void *foo
        INIT_LIST_HEAD(&nfsi->open_files);
        INIT_LIST_HEAD(&nfsi->access_cache_entry_lru);
        INIT_LIST_HEAD(&nfsi->access_cache_inode_lru);
-       INIT_LIST_HEAD(&nfsi->commit_list);
+       INIT_LIST_HEAD(&nfsi->commit_info.list);
        nfsi->npages = 0;
-       nfsi->ncommit = 0;
+       nfsi->commit_info.ncommit = 0;
        atomic_set(&nfsi->silly_count, 1);
        INIT_HLIST_HEAD(&nfsi->silly_list);
        init_waitqueue_head(&nfsi->waitqueue);
diff --combined fs/nfs/nfs3proc.c
index 75c68299358e226e805e861e4ac8ffc81d96ab4f,48bcad294161e2d37588d63d9c332c5e905856cc..2292a0fd2bffd3b042b43e9dc4b607acbd0eb6ad
@@@ -142,7 -142,7 +142,7 @@@ nfs3_proc_setattr(struct dentry *dentry
  }
  
  static int
- nfs3_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+ nfs3_proc_lookup(struct inode *dir, struct qstr *name,
                 struct nfs_fh *fhandle, struct nfs_fattr *fattr)
  {
        struct nfs3_diropargs   arg = {
@@@ -398,7 -398,8 +398,7 @@@ nfs3_proc_remove(struct inode *dir, str
  {
        struct nfs_removeargs arg = {
                .fh = NFS_FH(dir),
 -              .name.len = name->len,
 -              .name.name = name->name,
 +              .name = *name,
        };
        struct nfs_removeres res;
        struct rpc_message msg = {
@@@ -810,11 -811,13 +810,13 @@@ nfs3_proc_pathconf(struct nfs_server *s
  
  static int nfs3_read_done(struct rpc_task *task, struct nfs_read_data *data)
  {
-       if (nfs3_async_handle_jukebox(task, data->inode))
+       struct inode *inode = data->header->inode;
+       if (nfs3_async_handle_jukebox(task, inode))
                return -EAGAIN;
  
-       nfs_invalidate_atime(data->inode);
-       nfs_refresh_inode(data->inode, &data->fattr);
+       nfs_invalidate_atime(inode);
+       nfs_refresh_inode(inode, &data->fattr);
        return 0;
  }
  
@@@ -830,10 -833,12 +832,12 @@@ static void nfs3_proc_read_rpc_prepare(
  
  static int nfs3_write_done(struct rpc_task *task, struct nfs_write_data *data)
  {
-       if (nfs3_async_handle_jukebox(task, data->inode))
+       struct inode *inode = data->header->inode;
+       if (nfs3_async_handle_jukebox(task, inode))
                return -EAGAIN;
        if (task->tk_status >= 0)
-               nfs_post_op_update_inode_force_wcc(data->inode, data->res.fattr);
+               nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
        return 0;
  }
  
@@@ -847,7 -852,12 +851,12 @@@ static void nfs3_proc_write_rpc_prepare
        rpc_call_start(task);
  }
  
- static int nfs3_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+ static void nfs3_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
+ {
+       rpc_call_start(task);
+ }
+ static int nfs3_commit_done(struct rpc_task *task, struct nfs_commit_data *data)
  {
        if (nfs3_async_handle_jukebox(task, data->inode))
                return -EAGAIN;
        return 0;
  }
  
- static void nfs3_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
+ static void nfs3_proc_commit_setup(struct nfs_commit_data *data, struct rpc_message *msg)
  {
        msg->rpc_proc = &nfs3_procedures[NFS3PROC_COMMIT];
  }
@@@ -875,6 -885,7 +884,7 @@@ const struct nfs_rpc_ops nfs_v3_cliento
        .file_inode_ops = &nfs3_file_inode_operations,
        .file_ops       = &nfs_file_operations,
        .getroot        = nfs3_proc_get_root,
+       .submount       = nfs_submount,
        .getattr        = nfs3_proc_getattr,
        .setattr        = nfs3_proc_setattr,
        .lookup         = nfs3_proc_lookup,
        .write_rpc_prepare = nfs3_proc_write_rpc_prepare,
        .write_done     = nfs3_write_done,
        .commit_setup   = nfs3_proc_commit_setup,
+       .commit_rpc_prepare = nfs3_proc_commit_rpc_prepare,
        .commit_done    = nfs3_commit_done,
        .lock           = nfs3_proc_lock,
        .clear_acl_cache = nfs3_forget_cached_acls,
diff --combined fs/nfs/nfs4proc.c
index ab985f6f0da8d93da67f625ba8027ff851678c55,42d9e9ca7d2fbe22af780544eb86b4d40ce3f31a..d48dbefa0e71ebf6d9ac90edbb893364afd2d0a3
@@@ -64,6 -64,7 +64,7 @@@
  #include "iostat.h"
  #include "callback.h"
  #include "pnfs.h"
+ #include "netns.h"
  
  #define NFSDBG_FACILITY               NFSDBG_PROC
  
@@@ -80,6 -81,7 +81,7 @@@ static int _nfs4_recover_proc_open(stru
  static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
  static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
  static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
+ static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *);
  static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
  static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
@@@ -101,6 -103,8 +103,8 @@@ static int nfs4_map_errors(int err
        case -NFS4ERR_BADOWNER:
        case -NFS4ERR_BADNAME:
                return -EINVAL;
+       case -NFS4ERR_SHARE_DENIED:
+               return -EACCES;
        default:
                dprintk("%s could not handle NFSv4 error %d\n",
                                __func__, -err);
@@@ -304,7 -308,7 +308,7 @@@ static int nfs4_handle_exception(struc
                case -NFS4ERR_SEQ_MISORDERED:
                        dprintk("%s ERROR: %d Reset session\n", __func__,
                                errorcode);
-                       nfs4_schedule_session_recovery(clp->cl_session);
+                       nfs4_schedule_session_recovery(clp->cl_session, errorcode);
                        exception->retry = 1;
                        break;
  #endif /* defined(CONFIG_NFS_V4_1) */
@@@ -772,7 -776,7 +776,7 @@@ static void update_changeattr(struct in
        struct nfs_inode *nfsi = NFS_I(dir);
  
        spin_lock(&dir->i_lock);
-       nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
+       nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
        if (!cinfo->atomic || cinfo->before != dir->i_version)
                nfs_force_lookup_revalidate(dir);
        dir->i_version = cinfo->after;
@@@ -788,7 -792,6 +792,6 @@@ struct nfs4_opendata 
        struct nfs4_string owner_name;
        struct nfs4_string group_name;
        struct nfs_fattr f_attr;
-       struct nfs_fattr dir_attr;
        struct dentry *dir;
        struct dentry *dentry;
        struct nfs4_state_owner *owner;
  static void nfs4_init_opendata_res(struct nfs4_opendata *p)
  {
        p->o_res.f_attr = &p->f_attr;
-       p->o_res.dir_attr = &p->dir_attr;
        p->o_res.seqid = p->o_arg.seqid;
        p->c_res.seqid = p->c_arg.seqid;
        p->o_res.server = p->o_arg.server;
        nfs_fattr_init(&p->f_attr);
-       nfs_fattr_init(&p->dir_attr);
        nfs_fattr_init_names(&p->f_attr, &p->owner_name, &p->group_name);
  }
  
@@@ -843,7 -844,6 +844,6 @@@ static struct nfs4_opendata *nfs4_opend
        p->o_arg.name = &dentry->d_name;
        p->o_arg.server = server;
        p->o_arg.bitmask = server->attr_bitmask;
-       p->o_arg.dir_bitmask = server->cache_consistency_bitmask;
        p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
        if (attrs != NULL && attrs->ia_valid != 0) {
                __be32 verf[2];
@@@ -1332,7 -1332,7 +1332,7 @@@ int nfs4_open_delegation_recall(struct 
                        case -NFS4ERR_BAD_HIGH_SLOT:
                        case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
                        case -NFS4ERR_DEADSESSION:
-                               nfs4_schedule_session_recovery(server->nfs_client->cl_session);
+                               nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
                                goto out;
                        case -NFS4ERR_STALE_CLIENTID:
                        case -NFS4ERR_STALE_STATEID:
@@@ -1611,8 -1611,6 +1611,6 @@@ static int _nfs4_recover_proc_open(stru
  
        nfs_fattr_map_and_free_names(NFS_SERVER(dir), &data->f_attr);
  
-       nfs_refresh_inode(dir, o_res->dir_attr);
        if (o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
                status = _nfs4_proc_open_confirm(data);
                if (status != 0)
@@@ -1645,11 -1643,8 +1643,8 @@@ static int _nfs4_proc_open(struct nfs4_
  
        nfs_fattr_map_and_free_names(server, &data->f_attr);
  
-       if (o_arg->open_flags & O_CREAT) {
+       if (o_arg->open_flags & O_CREAT)
                update_changeattr(dir, &o_res->cinfo);
-               nfs_post_op_update_inode(dir, o_res->dir_attr);
-       } else
-               nfs_refresh_inode(dir, o_res->dir_attr);
        if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0)
                server->caps &= ~NFS_CAP_POSIX_LOCK;
        if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
@@@ -1789,7 -1784,14 +1784,14 @@@ static inline void nfs4_exclusive_attrs
  /*
   * Returns a referenced nfs4_state
   */
- static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+ static int _nfs4_do_open(struct inode *dir,
+                       struct dentry *dentry,
+                       fmode_t fmode,
+                       int flags,
+                       struct iattr *sattr,
+                       struct rpc_cred *cred,
+                       struct nfs4_state **res,
+                       struct nfs4_threshold **ctx_th)
  {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
        if (opendata == NULL)
                goto err_put_state_owner;
  
+       if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
+               opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
+               if (!opendata->f_attr.mdsthreshold)
+                       goto err_opendata_put;
+       }
        if (dentry->d_inode != NULL)
                opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
  
                        nfs_setattr_update_inode(state->inode, sattr);
                nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
        }
+       if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server))
+               *ctx_th = opendata->f_attr.mdsthreshold;
+       else
+               kfree(opendata->f_attr.mdsthreshold);
+       opendata->f_attr.mdsthreshold = NULL;
        nfs4_opendata_put(opendata);
        nfs4_put_state_owner(sp);
        *res = state;
        return 0;
  err_opendata_put:
+       kfree(opendata->f_attr.mdsthreshold);
        nfs4_opendata_put(opendata);
  err_put_state_owner:
        nfs4_put_state_owner(sp);
@@@ -1853,14 -1868,21 +1868,21 @@@ out_err
  }
  
  
- static struct nfs4_state *nfs4_do_open(struct inode *dir, struct dentry *dentry, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
+ static struct nfs4_state *nfs4_do_open(struct inode *dir,
+                                       struct dentry *dentry,
+                                       fmode_t fmode,
+                                       int flags,
+                                       struct iattr *sattr,
+                                       struct rpc_cred *cred,
+                                       struct nfs4_threshold **ctx_th)
  {
        struct nfs4_exception exception = { };
        struct nfs4_state *res;
        int status;
  
        do {
-               status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, &res);
+               status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred,
+                                      &res, ctx_th);
                if (status == 0)
                        break;
                /* NOTE: BAD_SEQID means the server and client disagree about the
@@@ -2184,7 -2206,8 +2206,8 @@@ nfs4_atomic_open(struct inode *dir, str
        struct nfs4_state *state;
  
        /* Protect against concurrent sillydeletes */
-       state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, ctx->cred);
+       state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr,
+                            ctx->cred, &ctx->mdsthreshold);
        if (IS_ERR(state))
                return ERR_CAST(state);
        ctx->state = state;
@@@ -2354,8 -2377,8 +2377,8 @@@ static int nfs4_find_root_sec(struct nf
  /*
   * get the file handle for the "/" directory on the server
   */
static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
-                             struct nfs_fsinfo *info)
int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,
+                        struct nfs_fsinfo *info)
  {
        int minor_version = server->nfs_client->cl_minorversion;
        int status = nfs4_lookup_root(server, fhandle, info);
        return nfs4_map_errors(status);
  }
  
+ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
+                             struct nfs_fsinfo *info)
+ {
+       int error;
+       struct nfs_fattr *fattr = info->fattr;
+       error = nfs4_server_capabilities(server, mntfh);
+       if (error < 0) {
+               dprintk("nfs4_get_root: getcaps error = %d\n", -error);
+               return error;
+       }
+       error = nfs4_proc_getattr(server, mntfh, fattr);
+       if (error < 0) {
+               dprintk("nfs4_get_root: getattr error = %d\n", -error);
+               return error;
+       }
+       if (fattr->valid & NFS_ATTR_FATTR_FSID &&
+           !nfs_fsid_equal(&server->fsid, &fattr->fsid))
+               memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
+       return error;
+ }
  /*
   * Get locations and (maybe) other attributes of a referral.
   * Note that we'll actually follow the referral later when
@@@ -2578,7 -2626,7 +2626,7 @@@ out
        return err;
  }
  
- static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+ static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
                            struct nfs_fh *fhandle, struct nfs_fattr *fattr)
  {
        int status;
@@@ -2761,7 -2809,7 +2809,7 @@@ nfs4_proc_create(struct inode *dir, str
                fmode = ctx->mode;
        }
        sattr->ia_mode &= ~current_umask();
-       state = nfs4_do_open(dir, de, fmode, flags, sattr, cred);
+       state = nfs4_do_open(dir, de, fmode, flags, sattr, cred, NULL);
        d_drop(dentry);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
@@@ -2782,8 -2830,8 +2830,7 @@@ static int _nfs4_proc_remove(struct ino
        struct nfs_server *server = NFS_SERVER(dir);
        struct nfs_removeargs args = {
                .fh = NFS_FH(dir),
 -              .name.len = name->len,
 -              .name.name = name->name,
 +              .name = *name,
-               .bitmask = server->attr_bitmask,
        };
        struct nfs_removeres res = {
                .server = server,
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       int status = -ENOMEM;
-       res.dir_attr = nfs_alloc_fattr();
-       if (res.dir_attr == NULL)
-               goto out;
+       int status;
  
        status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
-       if (status == 0) {
+       if (status == 0)
                update_changeattr(dir, &res.cinfo);
-               nfs_post_op_update_inode(dir, res.dir_attr);
-       }
-       nfs_free_fattr(res.dir_attr);
- out:
        return status;
  }
  
@@@ -2827,7 -2867,6 +2866,6 @@@ static void nfs4_proc_unlink_setup(stru
        struct nfs_removeargs *args = msg->rpc_argp;
        struct nfs_removeres *res = msg->rpc_resp;
  
-       args->bitmask = server->cache_consistency_bitmask;
        res->server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
        nfs41_init_sequence(&args->seq_args, &res->seq_res, 1);
@@@ -2852,7 -2891,6 +2890,6 @@@ static int nfs4_proc_unlink_done(struc
        if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
                return 0;
        update_changeattr(dir, &res->cinfo);
-       nfs_post_op_update_inode(dir, res->dir_attr);
        return 1;
  }
  
@@@ -2863,7 -2901,6 +2900,6 @@@ static void nfs4_proc_rename_setup(stru
        struct nfs_renameres *res = msg->rpc_resp;
  
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
-       arg->bitmask = server->attr_bitmask;
        res->server = server;
        nfs41_init_sequence(&arg->seq_args, &res->seq_res, 1);
  }
@@@ -2889,9 -2926,7 +2925,7 @@@ static int nfs4_proc_rename_done(struc
                return 0;
  
        update_changeattr(old_dir, &res->old_cinfo);
-       nfs_post_op_update_inode(old_dir, res->old_fattr);
        update_changeattr(new_dir, &res->new_cinfo);
-       nfs_post_op_update_inode(new_dir, res->new_fattr);
        return 1;
  }
  
@@@ -2904,7 -2939,6 +2938,6 @@@ static int _nfs4_proc_rename(struct ino
                .new_dir = NFS_FH(new_dir),
                .old_name = old_name,
                .new_name = new_name,
-               .bitmask = server->attr_bitmask,
        };
        struct nfs_renameres res = {
                .server = server,
        };
        int status = -ENOMEM;
        
-       res.old_fattr = nfs_alloc_fattr();
-       res.new_fattr = nfs_alloc_fattr();
-       if (res.old_fattr == NULL || res.new_fattr == NULL)
-               goto out;
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(old_dir, &res.old_cinfo);
-               nfs_post_op_update_inode(old_dir, res.old_fattr);
                update_changeattr(new_dir, &res.new_cinfo);
-               nfs_post_op_update_inode(new_dir, res.new_fattr);
        }
- out:
-       nfs_free_fattr(res.new_fattr);
-       nfs_free_fattr(res.old_fattr);
        return status;
  }
  
@@@ -2968,18 -2992,15 +2991,15 @@@ static int _nfs4_proc_link(struct inod
        int status = -ENOMEM;
  
        res.fattr = nfs_alloc_fattr();
-       res.dir_attr = nfs_alloc_fattr();
-       if (res.fattr == NULL || res.dir_attr == NULL)
+       if (res.fattr == NULL)
                goto out;
  
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(dir, &res.cinfo);
-               nfs_post_op_update_inode(dir, res.dir_attr);
                nfs_post_op_update_inode(inode, res.fattr);
        }
  out:
-       nfs_free_fattr(res.dir_attr);
        nfs_free_fattr(res.fattr);
        return status;
  }
@@@ -3002,7 -3023,6 +3022,6 @@@ struct nfs4_createdata 
        struct nfs4_create_res res;
        struct nfs_fh fh;
        struct nfs_fattr fattr;
-       struct nfs_fattr dir_fattr;
  };
  
  static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
                data->res.server = server;
                data->res.fh = &data->fh;
                data->res.fattr = &data->fattr;
-               data->res.dir_fattr = &data->dir_fattr;
                nfs_fattr_init(data->res.fattr);
-               nfs_fattr_init(data->res.dir_fattr);
        }
        return data;
  }
@@@ -3039,7 -3057,6 +3056,6 @@@ static int nfs4_do_create(struct inode 
                                    &data->arg.seq_args, &data->res.seq_res, 1);
        if (status == 0) {
                update_changeattr(dir, &data->res.dir_cinfo);
-               nfs_post_op_update_inode(dir, data->res.dir_fattr);
                status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
        }
        return status;
@@@ -3335,12 -3352,12 +3351,12 @@@ static int nfs4_proc_pathconf(struct nf
  
  void __nfs4_read_done_cb(struct nfs_read_data *data)
  {
-       nfs_invalidate_atime(data->inode);
+       nfs_invalidate_atime(data->header->inode);
  }
  
  static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_read_data *data)
  {
-       struct nfs_server *server = NFS_SERVER(data->inode);
+       struct nfs_server *server = NFS_SERVER(data->header->inode);
  
        if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
                rpc_restart_call_prepare(task);
@@@ -3375,7 -3392,7 +3391,7 @@@ static void nfs4_proc_read_setup(struc
  
  static void nfs4_proc_read_rpc_prepare(struct rpc_task *task, struct nfs_read_data *data)
  {
-       if (nfs4_setup_sequence(NFS_SERVER(data->inode),
+       if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
                                &data->args.seq_args,
                                &data->res.seq_res,
                                task))
        rpc_call_start(task);
  }
  
- /* Reset the the nfs_read_data to send the read to the MDS. */
- void nfs4_reset_read(struct rpc_task *task, struct nfs_read_data *data)
- {
-       dprintk("%s Reset task for i/o through\n", __func__);
-       put_lseg(data->lseg);
-       data->lseg = NULL;
-       /* offsets will differ in the dense stripe case */
-       data->args.offset = data->mds_offset;
-       data->ds_clp = NULL;
-       data->args.fh     = NFS_FH(data->inode);
-       data->read_done_cb = nfs4_read_done_cb;
-       task->tk_ops = data->mds_ops;
-       rpc_task_reset_client(task, NFS_CLIENT(data->inode));
- }
- EXPORT_SYMBOL_GPL(nfs4_reset_read);
  static int nfs4_write_done_cb(struct rpc_task *task, struct nfs_write_data *data)
  {
-       struct inode *inode = data->inode;
+       struct inode *inode = data->header->inode;
        
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
                rpc_restart_call_prepare(task);
        }
        if (task->tk_status >= 0) {
                renew_lease(NFS_SERVER(inode), data->timestamp);
-               nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
+               nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
        }
        return 0;
  }
@@@ -3422,32 -3423,30 +3422,30 @@@ static int nfs4_write_done(struct rpc_t
                nfs4_write_done_cb(task, data);
  }
  
- /* Reset the the nfs_write_data to send the write to the MDS. */
void nfs4_reset_write(struct rpc_task *task, struct nfs_write_data *data)
+ static
bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data)
  {
-       dprintk("%s Reset task for i/o through\n", __func__);
-       put_lseg(data->lseg);
-       data->lseg          = NULL;
-       data->ds_clp        = NULL;
-       data->write_done_cb = nfs4_write_done_cb;
-       data->args.fh       = NFS_FH(data->inode);
-       data->args.bitmask  = data->res.server->cache_consistency_bitmask;
-       data->args.offset   = data->mds_offset;
-       data->res.fattr     = &data->fattr;
-       task->tk_ops        = data->mds_ops;
-       rpc_task_reset_client(task, NFS_CLIENT(data->inode));
+       const struct nfs_pgio_header *hdr = data->header;
+       /* Don't request attributes for pNFS or O_DIRECT writes */
+       if (data->ds_clp != NULL || hdr->dreq != NULL)
+               return false;
+       /* Otherwise, request attributes if and only if we don't hold
+        * a delegation
+        */
+       return nfs_have_delegation(hdr->inode, FMODE_READ) == 0;
  }
- EXPORT_SYMBOL_GPL(nfs4_reset_write);
  
  static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg)
  {
-       struct nfs_server *server = NFS_SERVER(data->inode);
+       struct nfs_server *server = NFS_SERVER(data->header->inode);
  
-       if (data->lseg) {
+       if (!nfs4_write_need_cache_consistency_data(data)) {
                data->args.bitmask = NULL;
                data->res.fattr = NULL;
        } else
                data->args.bitmask = server->cache_consistency_bitmask;
        if (!data->write_done_cb)
                data->write_done_cb = nfs4_write_done_cb;
        data->res.server = server;
  }
  
  static void nfs4_proc_write_rpc_prepare(struct rpc_task *task, struct nfs_write_data *data)
+ {
+       if (nfs4_setup_sequence(NFS_SERVER(data->header->inode),
+                               &data->args.seq_args,
+                               &data->res.seq_res,
+                               task))
+               return;
+       rpc_call_start(task);
+ }
+ static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
  {
        if (nfs4_setup_sequence(NFS_SERVER(data->inode),
                                &data->args.seq_args,
        rpc_call_start(task);
  }
  
- static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_write_data *data)
+ static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_commit_data *data)
  {
        struct inode *inode = data->inode;
  
                rpc_restart_call_prepare(task);
                return -EAGAIN;
        }
-       nfs_refresh_inode(inode, data->res.fattr);
        return 0;
  }
  
- static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+ static int nfs4_commit_done(struct rpc_task *task, struct nfs_commit_data *data)
  {
        if (!nfs4_sequence_done(task, &data->res.seq_res))
                return -EAGAIN;
-       return data->write_done_cb(task, data);
+       return data->commit_done_cb(task, data);
  }
  
- static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
+ static void nfs4_proc_commit_setup(struct nfs_commit_data *data, struct rpc_message *msg)
  {
        struct nfs_server *server = NFS_SERVER(data->inode);
  
-       if (data->lseg) {
-               data->args.bitmask = NULL;
-               data->res.fattr = NULL;
-       } else
-               data->args.bitmask = server->cache_consistency_bitmask;
-       if (!data->write_done_cb)
-               data->write_done_cb = nfs4_commit_done_cb;
+       if (data->commit_done_cb == NULL)
+               data->commit_done_cb = nfs4_commit_done_cb;
        data->res.server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
        nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
@@@ -3905,7 -3908,7 +3907,7 @@@ nfs4_async_handle_error(struct rpc_tas
                case -NFS4ERR_SEQ_MISORDERED:
                        dprintk("%s ERROR %d, Reset session\n", __func__,
                                task->tk_status);
-                       nfs4_schedule_session_recovery(clp->cl_session);
+                       nfs4_schedule_session_recovery(clp->cl_session, task->tk_status);
                        task->tk_status = 0;
                        return -EAGAIN;
  #endif /* CONFIG_NFS_V4_1 */
@@@ -3931,13 -3934,21 +3933,21 @@@ wait_on_recovery
        return -EAGAIN;
  }
  
- static void nfs4_construct_boot_verifier(struct nfs_client *clp,
-                                        nfs4_verifier *bootverf)
+ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
+                                   nfs4_verifier *bootverf)
  {
        __be32 verf[2];
  
-       verf[0] = htonl((u32)clp->cl_boot_time.tv_sec);
-       verf[1] = htonl((u32)clp->cl_boot_time.tv_nsec);
+       if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
+               /* An impossible timestamp guarantees this value
+                * will never match a generated boot time. */
+               verf[0] = 0;
+               verf[1] = (__be32)(NSEC_PER_SEC + 1);
+       } else {
+               struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
+               verf[0] = (__be32)nn->boot_time.tv_sec;
+               verf[1] = (__be32)nn->boot_time.tv_nsec;
+       }
        memcpy(bootverf->data, verf, sizeof(bootverf->data));
  }
  
@@@ -3960,7 -3971,7 +3970,7 @@@ int nfs4_proc_setclientid(struct nfs_cl
        int loop = 0;
        int status;
  
-       nfs4_construct_boot_verifier(clp, &sc_verifier);
+       nfs4_init_boot_verifier(clp, &sc_verifier);
  
        for(;;) {
                rcu_read_lock();
@@@ -4104,7 -4115,7 +4114,7 @@@ static int _nfs4_proc_delegreturn(struc
        nfs41_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
        data->args.fhandle = &data->fh;
        data->args.stateid = &data->stateid;
-       data->args.bitmask = server->attr_bitmask;
+       data->args.bitmask = server->cache_consistency_bitmask;
        nfs_copy_fh(&data->fh, NFS_FH(inode));
        nfs4_stateid_copy(&data->stateid, stateid);
        data->res.fattr = &data->fattr;
        if (status != 0)
                goto out;
        status = data->rpc_status;
-       if (status != 0)
-               goto out;
-       nfs_refresh_inode(inode, &data->fattr);
+       if (status == 0)
+               nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
+       else
+               nfs_refresh_inode(inode, &data->fattr);
  out:
        rpc_put_task(task);
        return status;
@@@ -4837,7 -4849,7 +4848,7 @@@ int nfs4_lock_delegation_recall(struct 
                        case -NFS4ERR_BAD_HIGH_SLOT:
                        case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
                        case -NFS4ERR_DEADSESSION:
-                               nfs4_schedule_session_recovery(server->nfs_client->cl_session);
+                               nfs4_schedule_session_recovery(server->nfs_client->cl_session, err);
                                goto out;
                        case -ERESTARTSYS:
                                /*
@@@ -5079,7 -5091,8 +5090,8 @@@ out_inval
  }
  
  static bool
- nfs41_same_server_scope(struct server_scope *a, struct server_scope *b)
+ nfs41_same_server_scope(struct nfs41_server_scope *a,
+                       struct nfs41_server_scope *b)
  {
        if (a->server_scope_sz == b->server_scope_sz &&
            memcmp(a->server_scope, b->server_scope, a->server_scope_sz) == 0)
        return false;
  }
  
+ /*
+  * nfs4_proc_bind_conn_to_session()
+  *
+  * The 4.1 client currently uses the same TCP connection for the
+  * fore and backchannel.
+  */
+ int nfs4_proc_bind_conn_to_session(struct nfs_client *clp, struct rpc_cred *cred)
+ {
+       int status;
+       struct nfs41_bind_conn_to_session_res res;
+       struct rpc_message msg = {
+               .rpc_proc =
+                       &nfs4_procedures[NFSPROC4_CLNT_BIND_CONN_TO_SESSION],
+               .rpc_argp = clp,
+               .rpc_resp = &res,
+               .rpc_cred = cred,
+       };
+       dprintk("--> %s\n", __func__);
+       BUG_ON(clp == NULL);
+       res.session = kzalloc(sizeof(struct nfs4_session), GFP_NOFS);
+       if (unlikely(res.session == NULL)) {
+               status = -ENOMEM;
+               goto out;
+       }
+       status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
+       if (status == 0) {
+               if (memcmp(res.session->sess_id.data,
+                   clp->cl_session->sess_id.data, NFS4_MAX_SESSIONID_LEN)) {
+                       dprintk("NFS: %s: Session ID mismatch\n", __func__);
+                       status = -EIO;
+                       goto out_session;
+               }
+               if (res.dir != NFS4_CDFS4_BOTH) {
+                       dprintk("NFS: %s: Unexpected direction from server\n",
+                               __func__);
+                       status = -EIO;
+                       goto out_session;
+               }
+               if (res.use_conn_in_rdma_mode) {
+                       dprintk("NFS: %s: Server returned RDMA mode = true\n",
+                               __func__);
+                       status = -EIO;
+                       goto out_session;
+               }
+       }
+ out_session:
+       kfree(res.session);
+ out:
+       dprintk("<-- %s status= %d\n", __func__, status);
+       return status;
+ }
  /*
   * nfs4_proc_exchange_id()
   *
@@@ -5105,7 -5173,7 +5172,7 @@@ int nfs4_proc_exchange_id(struct nfs_cl
                .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER,
        };
        struct nfs41_exchange_id_res res = {
-               .client = clp,
+               0
        };
        int status;
        struct rpc_message msg = {
        dprintk("--> %s\n", __func__);
        BUG_ON(clp == NULL);
  
-       nfs4_construct_boot_verifier(clp, &verifier);
+       nfs4_init_boot_verifier(clp, &verifier);
  
        args.id_len = scnprintf(args.id, sizeof(args.id),
                                "%s/%s/%u",
                                clp->cl_rpcclient->cl_nodename,
                                clp->cl_rpcclient->cl_auth->au_flavor);
  
-       res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL);
-       if (unlikely(!res.server_scope)) {
+       res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
+                                       GFP_NOFS);
+       if (unlikely(res.server_owner == NULL)) {
                status = -ENOMEM;
                goto out;
        }
  
-       res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_KERNEL);
-       if (unlikely(!res.impl_id)) {
+       res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
+                                       GFP_NOFS);
+       if (unlikely(res.server_scope == NULL)) {
+               status = -ENOMEM;
+               goto out_server_owner;
+       }
+       res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
+       if (unlikely(res.impl_id == NULL)) {
                status = -ENOMEM;
                goto out_server_scope;
        }
  
        status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
-       if (!status)
-               status = nfs4_check_cl_exchange_flags(clp->cl_exchange_flags);
+       if (status == 0)
+               status = nfs4_check_cl_exchange_flags(res.flags);
+       if (status == 0) {
+               clp->cl_clientid = res.clientid;
+               clp->cl_exchange_flags = (res.flags & ~EXCHGID4_FLAG_CONFIRMED_R);
+               if (!(res.flags & EXCHGID4_FLAG_CONFIRMED_R))
+                       clp->cl_seqid = res.seqid;
+               kfree(clp->cl_serverowner);
+               clp->cl_serverowner = res.server_owner;
+               res.server_owner = NULL;
  
-       if (!status) {
                /* use the most recent implementation id */
-               kfree(clp->impl_id);
-               clp->impl_id = res.impl_id;
-       } else
-               kfree(res.impl_id);
+               kfree(clp->cl_implid);
+               clp->cl_implid = res.impl_id;
  
-       if (!status) {
-               if (clp->server_scope &&
-                   !nfs41_same_server_scope(clp->server_scope,
+               if (clp->cl_serverscope != NULL &&
+                   !nfs41_same_server_scope(clp->cl_serverscope,
                                             res.server_scope)) {
                        dprintk("%s: server_scope mismatch detected\n",
                                __func__);
                        set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
-                       kfree(clp->server_scope);
-                       clp->server_scope = NULL;
+                       kfree(clp->cl_serverscope);
+                       clp->cl_serverscope = NULL;
                }
  
-               if (!clp->server_scope) {
-                       clp->server_scope = res.server_scope;
+               if (clp->cl_serverscope == NULL) {
+                       clp->cl_serverscope = res.server_scope;
                        goto out;
                }
-       }
+       } else
+               kfree(res.impl_id);
  
+ out_server_owner:
+       kfree(res.server_owner);
  out_server_scope:
        kfree(res.server_scope);
  out:
-       if (clp->impl_id)
+       if (clp->cl_implid != NULL)
                dprintk("%s: Server Implementation ID: "
                        "domain: %s, name: %s, date: %llu,%u\n",
-                       __func__, clp->impl_id->domain, clp->impl_id->name,
-                       clp->impl_id->date.seconds,
-                       clp->impl_id->date.nseconds);
+                       __func__, clp->cl_implid->domain, clp->cl_implid->name,
+                       clp->cl_implid->date.seconds,
+                       clp->cl_implid->date.nseconds);
        dprintk("<-- %s status= %d\n", __func__, status);
        return status;
  }
  
+ static int _nfs4_proc_destroy_clientid(struct nfs_client *clp,
+               struct rpc_cred *cred)
+ {
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_CLIENTID],
+               .rpc_argp = clp,
+               .rpc_cred = cred,
+       };
+       int status;
+       status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
+       if (status)
+               pr_warn("NFS: Got error %d from the server %s on "
+                       "DESTROY_CLIENTID.", status, clp->cl_hostname);
+       return status;
+ }
+ static int nfs4_proc_destroy_clientid(struct nfs_client *clp,
+               struct rpc_cred *cred)
+ {
+       unsigned int loop;
+       int ret;
+       for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
+               ret = _nfs4_proc_destroy_clientid(clp, cred);
+               switch (ret) {
+               case -NFS4ERR_DELAY:
+               case -NFS4ERR_CLIENTID_BUSY:
+                       ssleep(1);
+                       break;
+               default:
+                       return ret;
+               }
+       }
+       return 0;
+ }
+ int nfs4_destroy_clientid(struct nfs_client *clp)
+ {
+       struct rpc_cred *cred;
+       int ret = 0;
+       if (clp->cl_mvops->minor_version < 1)
+               goto out;
+       if (clp->cl_exchange_flags == 0)
+               goto out;
+       cred = nfs4_get_exchange_id_cred(clp);
+       ret = nfs4_proc_destroy_clientid(clp, cred);
+       if (cred)
+               put_rpccred(cred);
+       switch (ret) {
+       case 0:
+       case -NFS4ERR_STALE_CLIENTID:
+               clp->cl_exchange_flags = 0;
+       }
+ out:
+       return ret;
+ }
  struct nfs4_get_lease_time_data {
        struct nfs4_get_lease_time_args *args;
        struct nfs4_get_lease_time_res *res;
@@@ -5399,8 -5543,12 +5542,12 @@@ struct nfs4_session *nfs4_alloc_session
  void nfs4_destroy_session(struct nfs4_session *session)
  {
        struct rpc_xprt *xprt;
+       struct rpc_cred *cred;
  
-       nfs4_proc_destroy_session(session);
+       cred = nfs4_get_exchange_id_cred(session->clp);
+       nfs4_proc_destroy_session(session, cred);
+       if (cred)
+               put_rpccred(cred);
  
        rcu_read_lock();
        xprt = rcu_dereference(session->clp->cl_rpcclient->cl_xprt);
@@@ -5510,7 -5658,8 +5657,8 @@@ static int nfs4_verify_channel_attrs(st
        return nfs4_verify_back_channel_attrs(args, session);
  }
  
- static int _nfs4_proc_create_session(struct nfs_client *clp)
+ static int _nfs4_proc_create_session(struct nfs_client *clp,
+               struct rpc_cred *cred)
  {
        struct nfs4_session *session = clp->cl_session;
        struct nfs41_create_session_args args = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION],
                .rpc_argp = &args,
                .rpc_resp = &res,
+               .rpc_cred = cred,
        };
        int status;
  
   * It is the responsibility of the caller to verify the session is
   * expired before calling this routine.
   */
- int nfs4_proc_create_session(struct nfs_client *clp)
+ int nfs4_proc_create_session(struct nfs_client *clp, struct rpc_cred *cred)
  {
        int status;
        unsigned *ptr;
  
        dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
  
-       status = _nfs4_proc_create_session(clp);
+       status = _nfs4_proc_create_session(clp, cred);
        if (status)
                goto out;
  
   * Issue the over-the-wire RPC DESTROY_SESSION.
   * The caller must serialize access to this routine.
   */
- int nfs4_proc_destroy_session(struct nfs4_session *session)
+ int nfs4_proc_destroy_session(struct nfs4_session *session,
+               struct rpc_cred *cred)
  {
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION],
+               .rpc_argp = session,
+               .rpc_cred = cred,
+       };
        int status = 0;
-       struct rpc_message msg;
  
        dprintk("--> nfs4_proc_destroy_session\n");
  
        if (session->clp->cl_cons_state != NFS_CS_READY)
                return status;
  
-       msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DESTROY_SESSION];
-       msg.rpc_argp = session;
-       msg.rpc_resp = NULL;
-       msg.rpc_cred = NULL;
        status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
  
        if (status)
        return status;
  }
  
+ /*
+  * With sessions, the client is not marked ready until after a
+  * successful EXCHANGE_ID and CREATE_SESSION.
+  *
+  * Map errors cl_cons_state errors to EPROTONOSUPPORT to indicate
+  * other versions of NFS can be tried.
+  */
+ static int nfs41_check_session_ready(struct nfs_client *clp)
+ {
+       int ret;
+       
+       if (clp->cl_cons_state == NFS_CS_SESSION_INITING) {
+               ret = nfs4_client_recover_expired_lease(clp);
+               if (ret)
+                       return ret;
+       }
+       if (clp->cl_cons_state < NFS_CS_READY)
+               return -EPROTONOSUPPORT;
+       smp_rmb();
+       return 0;
+ }
  int nfs4_init_session(struct nfs_server *server)
  {
        struct nfs_client *clp = server->nfs_client;
        struct nfs4_session *session;
        unsigned int rsize, wsize;
-       int ret;
  
        if (!nfs4_has_session(clp))
                return 0;
  
        session = clp->cl_session;
-       if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
-               return 0;
+       spin_lock(&clp->cl_lock);
+       if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {
  
-       rsize = server->rsize;
-       if (rsize == 0)
-               rsize = NFS_MAX_FILE_IO_SIZE;
-       wsize = server->wsize;
-       if (wsize == 0)
-               wsize = NFS_MAX_FILE_IO_SIZE;
+               rsize = server->rsize;
+               if (rsize == 0)
+                       rsize = NFS_MAX_FILE_IO_SIZE;
+               wsize = server->wsize;
+               if (wsize == 0)
+                       wsize = NFS_MAX_FILE_IO_SIZE;
  
-       session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
-       session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
+               session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead;
+               session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead;
+       }
+       spin_unlock(&clp->cl_lock);
  
-       ret = nfs4_recover_expired_lease(server);
-       if (!ret)
-               ret = nfs4_check_client_ready(clp);
-       return ret;
+       return nfs41_check_session_ready(clp);
  }
  
- int nfs4_init_ds_session(struct nfs_client *clp)
+ int nfs4_init_ds_session(struct nfs_client *clp, unsigned long lease_time)
  {
        struct nfs4_session *session = clp->cl_session;
        int ret;
  
-       if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state))
-               return 0;
-       ret = nfs4_client_recover_expired_lease(clp);
-       if (!ret)
-               /* Test for the DS role */
-               if (!is_ds_client(clp))
-                       ret = -ENODEV;
-       if (!ret)
-               ret = nfs4_check_client_ready(clp);
-       return ret;
+       spin_lock(&clp->cl_lock);
+       if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) {
+               /*
+                * Do not set NFS_CS_CHECK_LEASE_TIME instead set the
+                * DS lease to be equal to the MDS lease.
+                */
+               clp->cl_lease_time = lease_time;
+               clp->cl_last_renewal = jiffies;
+       }
+       spin_unlock(&clp->cl_lock);
  
+       ret = nfs41_check_session_ready(clp);
+       if (ret)
+               return ret;
+       /* Test for the DS role */
+       if (!is_ds_client(clp))
+               return -ENODEV;
+       return 0;
  }
  EXPORT_SYMBOL_GPL(nfs4_init_ds_session);
  
@@@ -6557,6 -6734,7 +6733,7 @@@ const struct nfs_rpc_ops nfs_v4_cliento
        .file_inode_ops = &nfs4_file_inode_operations,
        .file_ops       = &nfs4_file_operations,
        .getroot        = nfs4_proc_get_root,
+       .submount       = nfs4_submount,
        .getattr        = nfs4_proc_getattr,
        .setattr        = nfs4_proc_setattr,
        .lookup         = nfs4_proc_lookup,
        .write_rpc_prepare = nfs4_proc_write_rpc_prepare,
        .write_done     = nfs4_write_done,
        .commit_setup   = nfs4_proc_commit_setup,
+       .commit_rpc_prepare = nfs4_proc_commit_rpc_prepare,
        .commit_done    = nfs4_commit_done,
        .lock           = nfs4_proc_lock,
        .clear_acl_cache = nfs4_zap_acl_attr,
        .close_context  = nfs4_close_context,
        .open_context   = nfs4_atomic_open,
        .init_client    = nfs4_init_client,
-       .secinfo        = nfs4_proc_secinfo,
  };
  
  static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
diff --combined fs/nfs/proc.c
index d6408b6437de4f9f0c55f9f211f61df129976b7c,fea9163d6f8efdf1933aec41b47c870f08f7799f..a706b6bcc286a5a401318e868b0d1fbab2a206a4
@@@ -178,7 -178,7 +178,7 @@@ nfs_proc_setattr(struct dentry *dentry
  }
  
  static int
- nfs_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
+ nfs_proc_lookup(struct inode *dir, struct qstr *name,
                struct nfs_fh *fhandle, struct nfs_fattr *fattr)
  {
        struct nfs_diropargs    arg = {
@@@ -335,7 -335,8 +335,7 @@@ nfs_proc_remove(struct inode *dir, stru
  {
        struct nfs_removeargs arg = {
                .fh = NFS_FH(dir),
 -              .name.len = name->len,
 -              .name.name = name->name,
 +              .name = *name,
        };
        struct rpc_message msg = { 
                .rpc_proc = &nfs_procedures[NFSPROC_REMOVE],
@@@ -640,12 -641,14 +640,14 @@@ nfs_proc_pathconf(struct nfs_server *se
  
  static int nfs_read_done(struct rpc_task *task, struct nfs_read_data *data)
  {
+       struct inode *inode = data->header->inode;
        if (nfs_async_handle_expired_key(task))
                return -EAGAIN;
  
-       nfs_invalidate_atime(data->inode);
+       nfs_invalidate_atime(inode);
        if (task->tk_status >= 0) {
-               nfs_refresh_inode(data->inode, data->res.fattr);
+               nfs_refresh_inode(inode, data->res.fattr);
                /* Emulate the eof flag, which isn't normally needed in NFSv2
                 * as it is guaranteed to always return the file attributes
                 */
@@@ -667,11 -670,13 +669,13 @@@ static void nfs_proc_read_rpc_prepare(s
  
  static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data)
  {
+       struct inode *inode = data->header->inode;
        if (nfs_async_handle_expired_key(task))
                return -EAGAIN;
  
        if (task->tk_status >= 0)
-               nfs_post_op_update_inode_force_wcc(data->inode, data->res.fattr);
+               nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
        return 0;
  }
  
@@@ -687,8 -692,13 +691,13 @@@ static void nfs_proc_write_rpc_prepare(
        rpc_call_start(task);
  }
  
+ static void nfs_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
+ {
+       BUG();
+ }
  static void
- nfs_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
+ nfs_proc_commit_setup(struct nfs_commit_data *data, struct rpc_message *msg)
  {
        BUG();
  }
@@@ -732,6 -742,7 +741,7 @@@ const struct nfs_rpc_ops nfs_v2_cliento
        .file_inode_ops = &nfs_file_inode_operations,
        .file_ops       = &nfs_file_operations,
        .getroot        = nfs_proc_get_root,
+       .submount       = nfs_submount,
        .getattr        = nfs_proc_getattr,
        .setattr        = nfs_proc_setattr,
        .lookup         = nfs_proc_lookup,
        .write_rpc_prepare = nfs_proc_write_rpc_prepare,
        .write_done     = nfs_write_done,
        .commit_setup   = nfs_proc_commit_setup,
+       .commit_rpc_prepare = nfs_proc_commit_rpc_prepare,
        .lock           = nfs_proc_lock,
        .lock_check_bounds = nfs_lock_check_bounds,
        .close_context  = nfs_close_context,
diff --combined net/sunrpc/clnt.c
index 7fee13b331d193e1a46831c257ffd6fcf6a4fde8,25302c80246069f887fe8203e1811c92958f43a1..f56f045778aedf4a0da1fcf2566eacf69c7c6c8a
@@@ -127,7 -127,9 +127,7 @@@ static struct dentry *rpc_setup_pipedir
  {
        static uint32_t clntid;
        char name[15];
 -      struct qstr q = {
 -              .name = name,
 -      };
 +      struct qstr q = { .name = name };
        struct dentry *dir, *dentry;
        int error;
  
@@@ -1286,6 -1288,8 +1286,8 @@@ call_reserveresult(struct rpc_task *tas
        }
  
        switch (status) {
+       case -ENOMEM:
+               rpc_delay(task, HZ >> 2);
        case -EAGAIN:   /* woken up; retry */
                task->tk_action = call_reserve;
                return;
diff --combined net/sunrpc/rpc_pipe.c
index fd2423991c2d4dc473223b128d9a761b3da5beea,88945d0f7594d9fa11a7c10a7d0ca302a9b1848a..04040476082e6efd5ef08f9c7e6444c0fec77929
@@@ -120,7 -120,7 +120,7 @@@ EXPORT_SYMBOL_GPL(rpc_pipe_generic_upca
  
  /**
   * rpc_queue_upcall - queue an upcall message to userspace
-  * @inode: inode of upcall pipe on which to queue given message
+  * @pipe: upcall pipe on which to queue given message
   * @msg: message to queue
   *
   * Call with an @inode created by rpc_mkpipe() to queue an upcall.
@@@ -819,9 -819,7 +819,7 @@@ static int rpc_rmdir_depopulate(struct 
   * @parent: dentry of directory to create new "pipe" in
   * @name: name of pipe
   * @private: private data to associate with the pipe, for the caller's use
-  * @ops: operations defining the behavior of the pipe: upcall, downcall,
-  *    release_pipe, open_pipe, and destroy_msg.
-  * @flags: rpc_pipe flags
+  * @pipe: &rpc_pipe containing input parameters
   *
   * Data is made available for userspace to read by calls to
   * rpc_queue_upcall().  The actual reads will result in calls to
@@@ -943,7 -941,7 +941,7 @@@ struct dentry *rpc_create_client_dir(st
  
  /**
   * rpc_remove_client_dir - Remove a directory created with rpc_create_client_dir()
-  * @clnt: rpc client
+  * @dentry: dentry for the pipe
   */
  int rpc_remove_client_dir(struct dentry *dentry)
  {
@@@ -1059,9 -1057,12 +1057,9 @@@ static const struct rpc_filelist files[
  struct dentry *rpc_d_lookup_sb(const struct super_block *sb,
                               const unsigned char *dir_name)
  {
 -      struct qstr dir = {
 -              .name = dir_name,
 -              .len = strlen(dir_name),
 -              .hash = full_name_hash(dir_name, strlen(dir_name)),
 -      };
 +      struct qstr dir = QSTR_INIT(dir_name, strlen(dir_name));
  
 +      dir.hash = full_name_hash(dir.name, dir.len);
        return d_lookup(sb->s_root, &dir);
  }
  EXPORT_SYMBOL_GPL(rpc_d_lookup_sb);
@@@ -1115,7 -1116,7 +1113,7 @@@ rpc_fill_super(struct super_block *sb, 
        sb->s_op = &s_ops;
        sb->s_time_gran = 1;
  
-       inode = rpc_get_inode(sb, S_IFDIR | 0755);
+       inode = rpc_get_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
        sb->s_root = root = d_make_root(inode);
        if (!root)
                return -ENOMEM;
diff --combined net/sunrpc/xprt.c
index 6fe2dcead15027e6a5d4b7086121b319d8e05363,d7ccd7923eab9d9e6e9706d038df345316338ec6..3c83035cdaa9940849fbfb1c729a6a2720b32702
@@@ -783,7 -783,7 +783,7 @@@ static void xprt_update_rtt(struct rpc_
  {
        struct rpc_rqst *req = task->tk_rqstp;
        struct rpc_rtt *rtt = task->tk_client->cl_rtt;
 -      unsigned timer = task->tk_msg.rpc_proc->p_timer;
 +      unsigned int timer = task->tk_msg.rpc_proc->p_timer;
        long m = usecs_to_jiffies(ktime_to_us(req->rq_rtt));
  
        if (timer) {
@@@ -979,20 -979,21 +979,21 @@@ static void xprt_alloc_slot(struct rpc_
                list_del(&req->rq_list);
                goto out_init_req;
        }
-       req = xprt_dynamic_alloc_slot(xprt, GFP_NOWAIT);
+       req = xprt_dynamic_alloc_slot(xprt, GFP_NOWAIT|__GFP_NOWARN);
        if (!IS_ERR(req))
                goto out_init_req;
        switch (PTR_ERR(req)) {
        case -ENOMEM:
-               rpc_delay(task, HZ >> 2);
                dprintk("RPC:       dynamic allocation of request slot "
                                "failed! Retrying\n");
+               task->tk_status = -ENOMEM;
                break;
        case -EAGAIN:
                rpc_sleep_on(&xprt->backlog, task, NULL);
                dprintk("RPC:       waiting for request slot\n");
+       default:
+               task->tk_status = -EAGAIN;
        }
-       task->tk_status = -EAGAIN;
        return;
  out_init_req:
        task->tk_status = 0;