Merge tag 'nfs-for-3.15-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 6 Apr 2014 17:09:38 +0000 (10:09 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 6 Apr 2014 17:09:38 +0000 (10:09 -0700)
Pull NFS client updates from Trond Myklebust:
 "Highlights include:

   - Stable fix for a use after free issue in the NFSv4.1 open code
   - Fix the SUNRPC bi-directional RPC code to account for TCP segmentation
   - Optimise usage of readdirplus when confronted with 'ls -l' situations
   - Soft mount bugfixes
   - NFS over RDMA bugfixes
   - NFSv4 close locking fixes
   - Various NFSv4.x client state management optimisations
   - Rename/unlink code cleanups"

* tag 'nfs-for-3.15-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (28 commits)
  nfs: pass string length to pr_notice message about readdir loops
  NFSv4: Fix a use-after-free problem in open()
  SUNRPC: rpc_restart_call/rpc_restart_call_prepare should clear task->tk_status
  SUNRPC: Don't let rpc_delay() clobber non-timeout errors
  SUNRPC: Ensure call_connect_status() deals correctly with SOFTCONN tasks
  SUNRPC: Ensure call_status() deals correctly with SOFTCONN tasks
  NFSv4: Ensure we respect soft mount timeouts during trunking discovery
  NFSv4: Schedule recovery if nfs40_walk_client_list() is interrupted
  NFS: advertise only supported callback netids
  SUNRPC: remove KERN_INFO from dprintk() call sites
  SUNRPC: Fix large reads on NFS/RDMA
  NFS: Clean up: revert increase in READDIR RPC buffer max size
  SUNRPC: Ensure that call_bind times out correctly
  SUNRPC: Ensure that call_connect times out correctly
  nfs: emit a fsnotify_nameremove call in sillyrename codepath
  nfs: remove synchronous rename code
  nfs: convert nfs_rename to use async_rename infrastructure
  nfs: make nfs_async_rename non-static
  nfs: abstract out code needed to complete a sillyrename
  NFSv4: Clear the open state flags if the new stateid does not match
  ...

1  2 
fs/nfs/dir.c
fs/nfs/inode.c
fs/nfs/nfs3proc.c

diff --combined fs/nfs/dir.c
index 4a48fe4b84b68c4e704aea84ea761e101a5ad0df,ef3fd090f59ac9c97141ddc21845e133278c0650..d9f3d067cd15635ffd0bb569ef76a156a2d84630
@@@ -69,21 -69,28 +69,28 @@@ const struct address_space_operations n
  
  static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)
  {
+       struct nfs_inode *nfsi = NFS_I(dir);
        struct nfs_open_dir_context *ctx;
        ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
        if (ctx != NULL) {
                ctx->duped = 0;
-               ctx->attr_gencount = NFS_I(dir)->attr_gencount;
+               ctx->attr_gencount = nfsi->attr_gencount;
                ctx->dir_cookie = 0;
                ctx->dup_cookie = 0;
                ctx->cred = get_rpccred(cred);
+               spin_lock(&dir->i_lock);
+               list_add(&ctx->list, &nfsi->open_files);
+               spin_unlock(&dir->i_lock);
                return ctx;
        }
        return  ERR_PTR(-ENOMEM);
  }
  
- static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx)
+ static void put_nfs_open_dir_context(struct inode *dir, struct nfs_open_dir_context *ctx)
  {
+       spin_lock(&dir->i_lock);
+       list_del(&ctx->list);
+       spin_unlock(&dir->i_lock);
        put_rpccred(ctx->cred);
        kfree(ctx);
  }
@@@ -126,7 -133,7 +133,7 @@@ out
  static int
  nfs_closedir(struct inode *inode, struct file *filp)
  {
-       put_nfs_open_dir_context(filp->private_data);
+       put_nfs_open_dir_context(filp->f_path.dentry->d_inode, filp->private_data);
        return 0;
  }
  
@@@ -306,10 -313,9 +313,9 @@@ int nfs_readdir_search_for_cookie(struc
                                        if (printk_ratelimit()) {
                                                pr_notice("NFS: directory %pD2 contains a readdir loop."
                                                                "Please contact your server vendor.  "
-                                                               "The file: %s has duplicate cookie %llu\n",
-                                                               desc->file,
-                                                               array->array[i].string.name,
-                                                               *desc->dir_cookie);
+                                                               "The file: %.*s has duplicate cookie %llu\n",
+                                                               desc->file, array->array[i].string.len,
+                                                               array->array[i].string.name, *desc->dir_cookie);
                                        }
                                        status = -ELOOP;
                                        goto out;
@@@ -437,6 -443,22 +443,22 @@@ void nfs_advise_use_readdirplus(struct 
        set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags);
  }
  
+ /*
+  * This function is mainly for use by nfs_getattr().
+  *
+  * If this is an 'ls -l', we want to force use of readdirplus.
+  * Do this by checking if there is an active file descriptor
+  * and calling nfs_advise_use_readdirplus, then forcing a
+  * cache flush.
+  */
+ void nfs_force_use_readdirplus(struct inode *dir)
+ {
+       if (!list_empty(&NFS_I(dir)->open_files)) {
+               nfs_advise_use_readdirplus(dir);
+               nfs_zap_mapping(dir, dir->i_mapping);
+       }
+ }
  static
  void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
  {
@@@ -815,6 -837,17 +837,17 @@@ int uncached_readdir(nfs_readdir_descri
        goto out;
  }
  
+ static bool nfs_dir_mapping_need_revalidate(struct inode *dir)
+ {
+       struct nfs_inode *nfsi = NFS_I(dir);
+       if (nfs_attribute_cache_expired(dir))
+               return true;
+       if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
+               return true;
+       return false;
+ }
  /* The file offset position represents the dirent entry number.  A
     last cookie cache takes care of the common case of reading the
     whole directory.
@@@ -847,7 -880,7 +880,7 @@@ static int nfs_readdir(struct file *fil
        desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0;
  
        nfs_block_sillyrename(dentry);
-       if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
+       if (ctx->pos == 0 || nfs_dir_mapping_need_revalidate(inode))
                res = nfs_revalidate_mapping(inode, file->f_mapping);
        if (res < 0)
                goto out;
@@@ -1846,11 -1879,6 +1879,11 @@@ int nfs_symlink(struct inode *dir, stru
                                                        GFP_KERNEL)) {
                SetPageUptodate(page);
                unlock_page(page);
 +              /*
 +               * add_to_page_cache_lru() grabs an extra page refcount.
 +               * Drop it here to avoid leaking this page later.
 +               */
 +              page_cache_release(page);
        } else
                __free_page(page);
  
@@@ -1911,6 -1939,7 +1944,7 @@@ int nfs_rename(struct inode *old_dir, s
        struct inode *old_inode = old_dentry->d_inode;
        struct inode *new_inode = new_dentry->d_inode;
        struct dentry *dentry = NULL, *rehash = NULL;
+       struct rpc_task *task;
        int error = -EBUSY;
  
        dfprintk(VFS, "NFS: rename(%pd2 -> %pd2, ct=%d)\n",
        if (new_inode != NULL)
                NFS_PROTO(new_inode)->return_delegation(new_inode);
  
-       error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
-                                          new_dir, &new_dentry->d_name);
+       task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL);
+       if (IS_ERR(task)) {
+               error = PTR_ERR(task);
+               goto out;
+       }
+       error = rpc_wait_for_completion_task(task);
+       if (error == 0)
+               error = task->tk_status;
+       rpc_put_task(task);
        nfs_mark_for_revalidate(old_inode);
  out:
        if (rehash)
diff --combined fs/nfs/inode.c
index c4702baa22b83355efdb327335bd1a6af512affc,9dbef878a2b207e89bf77a366c7ce55702094b1a..0c438973f3c8687836fc16bae3fb9aa89a767727
@@@ -128,7 -128,7 +128,7 @@@ EXPORT_SYMBOL_GPL(nfs_clear_inode)
  
  void nfs_evict_inode(struct inode *inode)
  {
 -      truncate_inode_pages(&inode->i_data, 0);
 +      truncate_inode_pages_final(&inode->i_data);
        clear_inode(inode);
        nfs_clear_inode(inode);
  }
@@@ -588,6 -588,25 +588,25 @@@ void nfs_setattr_update_inode(struct in
  }
  EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
  
+ static void nfs_request_parent_use_readdirplus(struct dentry *dentry)
+ {
+       struct dentry *parent;
+       parent = dget_parent(dentry);
+       nfs_force_use_readdirplus(parent->d_inode);
+       dput(parent);
+ }
+ static bool nfs_need_revalidate_inode(struct inode *inode)
+ {
+       if (NFS_I(inode)->cache_validity &
+                       (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
+               return true;
+       if (nfs_attribute_cache_expired(inode))
+               return true;
+       return false;
+ }
  int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
  {
        struct inode *inode = dentry->d_inode;
            ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
                need_atime = 0;
  
-       if (need_atime)
-               err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
-       else
-               err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       if (need_atime || nfs_need_revalidate_inode(inode)) {
+               struct nfs_server *server = NFS_SERVER(inode);
+               if (server->caps & NFS_CAP_READDIRPLUS)
+                       nfs_request_parent_use_readdirplus(dentry);
+               err = __nfs_revalidate_inode(server, inode);
+       }
        if (!err) {
                generic_fillattr(inode, stat);
                stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
@@@ -961,9 -983,7 +983,7 @@@ int nfs_attribute_cache_expired(struct 
   */
  int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
  {
-       if (!(NFS_I(inode)->cache_validity &
-                       (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
-                       && !nfs_attribute_cache_expired(inode))
+       if (!nfs_need_revalidate_inode(inode))
                return NFS_STALE(inode) ? -ESTALE : 0;
        return __nfs_revalidate_inode(server, inode);
  }
diff --combined fs/nfs/nfs3proc.c
index a462ef0fb5d6dcda8bab6f211e8122d7d972b070,251e6253fc362dda58fc1781d041b412ef810c52..db60149c4579a9cfd9409ec355da9eeb2f254359
@@@ -18,7 -18,6 +18,7 @@@
  #include <linux/lockd/bind.h>
  #include <linux/nfs_mount.h>
  #include <linux/freezer.h>
 +#include <linux/xattr.h>
  
  #include "iostat.h"
  #include "internal.h"
@@@ -478,41 -477,6 +478,6 @@@ nfs3_proc_rename_done(struct rpc_task *
        return 1;
  }
  
- static int
- nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name,
-                struct inode *new_dir, struct qstr *new_name)
- {
-       struct nfs_renameargs   arg = {
-               .old_dir        = NFS_FH(old_dir),
-               .old_name       = old_name,
-               .new_dir        = NFS_FH(new_dir),
-               .new_name       = new_name,
-       };
-       struct nfs_renameres res;
-       struct rpc_message msg = {
-               .rpc_proc       = &nfs3_procedures[NFS3PROC_RENAME],
-               .rpc_argp       = &arg,
-               .rpc_resp       = &res,
-       };
-       int status = -ENOMEM;
-       dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
-       res.old_fattr = nfs_alloc_fattr();
-       res.new_fattr = nfs_alloc_fattr();
-       if (res.old_fattr == NULL || res.new_fattr == NULL)
-               goto out;
-       status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
-       nfs_post_op_update_inode(old_dir, res.old_fattr);
-       nfs_post_op_update_inode(new_dir, res.new_fattr);
- out:
-       nfs_free_fattr(res.old_fattr);
-       nfs_free_fattr(res.new_fattr);
-       dprintk("NFS reply rename: %d\n", status);
-       return status;
- }
  static int
  nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
  {
@@@ -968,7 -932,6 +933,6 @@@ const struct nfs_rpc_ops nfs_v3_cliento
        .unlink_setup   = nfs3_proc_unlink_setup,
        .unlink_rpc_prepare = nfs3_proc_unlink_rpc_prepare,
        .unlink_done    = nfs3_proc_unlink_done,
-       .rename         = nfs3_proc_rename,
        .rename_setup   = nfs3_proc_rename_setup,
        .rename_rpc_prepare = nfs3_proc_rename_rpc_prepare,
        .rename_done    = nfs3_proc_rename_done,