]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'nfs-for-3.17-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 14 Aug 2014 00:13:19 +0000 (18:13 -0600)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 14 Aug 2014 00:13:19 +0000 (18:13 -0600)
Pull NFS client updates from Trond Myklebust:
 "Highlights include:

   - stable fix for a bug in nfs3_list_one_acl()
   - speed up NFS path walks by supporting LOOKUP_RCU
   - more read/write code cleanups
   - pNFS fixes for layout return on close
   - fixes for the RCU handling in the rpcsec_gss code
   - more NFS/RDMA fixes"

* tag 'nfs-for-3.17-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (79 commits)
  nfs: reject changes to resvport and sharecache during remount
  NFS: Avoid infinite loop when RELEASE_LOCKOWNER getting expired error
  SUNRPC: remove all refcounting of groupinfo from rpcauth_lookupcred
  NFS: fix two problems in lookup_revalidate in RCU-walk
  NFS: allow lockless access to access_cache
  NFS: teach nfs_lookup_verify_inode to handle LOOKUP_RCU
  NFS: teach nfs_neg_need_reval to understand LOOKUP_RCU
  NFS: support RCU_WALK in nfs_permission()
  sunrpc/auth: allow lockless (rcu) lookup of credential cache.
  NFS: prepare for RCU-walk support but pushing tests later in code.
  NFS: nfs4_lookup_revalidate: only evaluate parent if it will be used.
  NFS: add checks for returned value of try_module_get()
  nfs: clear_request_commit while holding i_lock
  pnfs: add pnfs_put_lseg_async
  pnfs: find swapped pages on pnfs commit lists too
  nfs: fix comment and add warn_on for PG_INODE_REF
  nfs: check wait_on_bit_lock err in page_group_lock
  sunrpc: remove "ec" argument from encrypt_v2 operation
  sunrpc: clean up sparse endianness warnings in gss_krb5_wrap.c
  sunrpc: clean up sparse endianness warnings in gss_krb5_seal.c
  ...

1  2 
fs/nfs/client.c
fs/nfs/filelayout/filelayoutdev.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/nfs4state.c
fs/nfs/pagelist.c
fs/nfs/pnfs.c
fs/nfs/write.c
net/sunrpc/xprt.c

diff --combined fs/nfs/client.c
index 180d1ec9c32ed511f3ce33f72088902ad050fffc,b7bfa27653709e440e1e28197d6f7eb2c6055b51..1c5ff6d5838585c4b6b0806aa64b579e58a64bc0
@@@ -110,8 -110,8 +110,8 @@@ struct nfs_subversion *get_nfs_version(
                mutex_unlock(&nfs_version_mutex);
        }
  
-       if (!IS_ERR(nfs))
-               try_module_get(nfs->owner);
+       if (!IS_ERR(nfs) && !try_module_get(nfs->owner))
+               return ERR_PTR(-EAGAIN);
        return nfs;
  }
  
@@@ -158,7 -158,8 +158,8 @@@ struct nfs_client *nfs_alloc_client(con
                goto error_0;
  
        clp->cl_nfs_mod = cl_init->nfs_mod;
-       try_module_get(clp->cl_nfs_mod->owner);
+       if (!try_module_get(clp->cl_nfs_mod->owner))
+               goto error_dealloc;
  
        clp->rpc_ops = clp->cl_nfs_mod->rpc_ops;
  
  
  error_cleanup:
        put_nfs_version(clp->cl_nfs_mod);
+ error_dealloc:
        kfree(clp);
  error_0:
        return ERR_PTR(err);
@@@ -252,6 -254,7 +254,7 @@@ void nfs_free_client(struct nfs_client 
        put_net(clp->cl_net);
        put_nfs_version(clp->cl_nfs_mod);
        kfree(clp->cl_hostname);
+       kfree(clp->cl_acceptor);
        kfree(clp);
  
        dprintk("<-- nfs_free_client()\n");
@@@ -482,8 -485,13 +485,13 @@@ nfs_get_client(const struct nfs_client_
        struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);
        const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops;
  
+       if (cl_init->hostname == NULL) {
+               WARN_ON(1);
+               return NULL;
+       }
        dprintk("--> nfs_get_client(%s,v%u)\n",
-               cl_init->hostname ?: "", rpc_ops->version);
+               cl_init->hostname, rpc_ops->version);
  
        /* see if the client already exists */
        do {
        } while (!IS_ERR(new));
  
        dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n",
-               cl_init->hostname ?: "", PTR_ERR(new));
+               cl_init->hostname, PTR_ERR(new));
        return new;
  }
  EXPORT_SYMBOL_GPL(nfs_get_client);
@@@ -1205,7 -1213,7 +1213,7 @@@ static const struct file_operations nfs
        .open           = nfs_server_list_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
 -      .release        = seq_release,
 +      .release        = seq_release_net,
        .owner          = THIS_MODULE,
  };
  
@@@ -1226,7 -1234,7 +1234,7 @@@ static const struct file_operations nfs
        .open           = nfs_volume_list_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
 -      .release        = seq_release,
 +      .release        = seq_release_net,
        .owner          = THIS_MODULE,
  };
  
   */
  static int nfs_server_list_open(struct inode *inode, struct file *file)
  {
 -      struct seq_file *m;
 -      int ret;
 -      struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info;
 -      struct net *net = pid_ns->child_reaper->nsproxy->net_ns;
 -
 -      ret = seq_open(file, &nfs_server_list_ops);
 -      if (ret < 0)
 -              return ret;
 -
 -      m = file->private_data;
 -      m->private = net;
 -
 -      return 0;
 +      return seq_open_net(inode, file, &nfs_server_list_ops,
 +                         sizeof(struct seq_net_private));
  }
  
  /*
   */
  static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
  {
 -      struct nfs_net *nn = net_generic(m->private, nfs_net_id);
 +      struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
  
        /* lock the list against modification */
        spin_lock(&nn->nfs_client_lock);
   */
  static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
  {
 -      struct nfs_net *nn = net_generic(p->private, nfs_net_id);
 +      struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
  
        return seq_list_next(v, &nn->nfs_client_list, pos);
  }
   */
  static void nfs_server_list_stop(struct seq_file *p, void *v)
  {
 -      struct nfs_net *nn = net_generic(p->private, nfs_net_id);
 +      struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
  
        spin_unlock(&nn->nfs_client_lock);
  }
  static int nfs_server_list_show(struct seq_file *m, void *v)
  {
        struct nfs_client *clp;
 -      struct nfs_net *nn = net_generic(m->private, nfs_net_id);
 +      struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
  
        /* display header on line 1 */
        if (v == &nn->nfs_client_list) {
   */
  static int nfs_volume_list_open(struct inode *inode, struct file *file)
  {
 -      struct seq_file *m;
 -      int ret;
 -      struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info;
 -      struct net *net = pid_ns->child_reaper->nsproxy->net_ns;
 -
 -      ret = seq_open(file, &nfs_volume_list_ops);
 -      if (ret < 0)
 -              return ret;
 -
 -      m = file->private_data;
 -      m->private = net;
 -
 -      return 0;
 +      return seq_open_net(inode, file, &nfs_server_list_ops,
 +                         sizeof(struct seq_net_private));
  }
  
  /*
   */
  static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
  {
 -      struct nfs_net *nn = net_generic(m->private, nfs_net_id);
 +      struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
  
        /* lock the list against modification */
        spin_lock(&nn->nfs_client_lock);
   */
  static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
  {
 -      struct nfs_net *nn = net_generic(p->private, nfs_net_id);
 +      struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
  
        return seq_list_next(v, &nn->nfs_volume_list, pos);
  }
   */
  static void nfs_volume_list_stop(struct seq_file *p, void *v)
  {
 -      struct nfs_net *nn = net_generic(p->private, nfs_net_id);
 +      struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
  
        spin_unlock(&nn->nfs_client_lock);
  }
@@@ -1354,7 -1384,7 +1362,7 @@@ static int nfs_volume_list_show(struct 
        struct nfs_server *server;
        struct nfs_client *clp;
        char dev[8], fsid[17];
 -      struct nfs_net *nn = net_generic(m->private, nfs_net_id);
 +      struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
  
        /* display header on line 1 */
        if (v == &nn->nfs_volume_list) {
        return 0;
  }
  
 +int nfs_fs_proc_net_init(struct net *net)
 +{
 +      struct nfs_net *nn = net_generic(net, nfs_net_id);
 +      struct proc_dir_entry *p;
 +
 +      nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net);
 +      if (!nn->proc_nfsfs)
 +              goto error_0;
 +
 +      /* a file of servers with which we're dealing */
 +      p = proc_create("servers", S_IFREG|S_IRUGO,
 +                      nn->proc_nfsfs, &nfs_server_list_fops);
 +      if (!p)
 +              goto error_1;
 +
 +      /* a file of volumes that we have mounted */
 +      p = proc_create("volumes", S_IFREG|S_IRUGO,
 +                      nn->proc_nfsfs, &nfs_volume_list_fops);
 +      if (!p)
 +              goto error_2;
 +      return 0;
 +
 +error_2:
 +      remove_proc_entry("servers", nn->proc_nfsfs);
 +error_1:
 +      remove_proc_entry("fs/nfsfs", NULL);
 +error_0:
 +      return -ENOMEM;
 +}
 +
 +void nfs_fs_proc_net_exit(struct net *net)
 +{
 +      struct nfs_net *nn = net_generic(net, nfs_net_id);
 +
 +      remove_proc_entry("volumes", nn->proc_nfsfs);
 +      remove_proc_entry("servers", nn->proc_nfsfs);
 +      remove_proc_entry("fs/nfsfs", NULL);
 +}
 +
  /*
   * initialise the /proc/fs/nfsfs/ directory
   */
@@@ -1436,12 -1427,14 +1444,12 @@@ int __init nfs_fs_proc_init(void
                goto error_0;
  
        /* a file of servers with which we're dealing */
 -      p = proc_create("servers", S_IFREG|S_IRUGO,
 -                      proc_fs_nfs, &nfs_server_list_fops);
 +      p = proc_symlink("servers", proc_fs_nfs, "../../net/nfsfs/servers");
        if (!p)
                goto error_1;
  
        /* a file of volumes that we have mounted */
 -      p = proc_create("volumes", S_IFREG|S_IRUGO,
 -                      proc_fs_nfs, &nfs_volume_list_fops);
 +      p = proc_symlink("volumes", proc_fs_nfs, "../../net/nfsfs/volumes");
        if (!p)
                goto error_2;
        return 0;
index e2a0361e24c680165a93597ca3226b0fde5fd373,48f8dcdb75db09d282f4fbf7f98d5a9cf7e11e64..8540516f4d719bff7d80c98068ee77a634485ec9
@@@ -695,7 -695,7 +695,7 @@@ filelayout_get_device_info(struct inod
        if (pdev == NULL)
                return NULL;
  
-       pages = kzalloc(max_pages * sizeof(struct page *), gfp_flags);
+       pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags);
        if (pages == NULL) {
                kfree(pdev);
                return NULL;
@@@ -783,8 -783,8 +783,8 @@@ nfs4_fl_select_ds_fh(struct pnfs_layout
  static void nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds)
  {
        might_sleep();
 -      wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING,
 -                      nfs_wait_bit_killable, TASK_KILLABLE);
 +      wait_on_bit_action(&ds->ds_state, NFS4DS_CONNECTING,
 +                         nfs_wait_bit_killable, TASK_KILLABLE);
  }
  
  static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds)
diff --combined fs/nfs/inode.c
index 68921b01b792634be15cb8c213319b0ae1ecd02d,147fd17e792073614991ae1d898b70fd743dd8a3..577a36f0a510b27cefe15429523750b6d21ca45a
@@@ -75,7 -75,7 +75,7 @@@ nfs_fattr_to_ino_t(struct nfs_fattr *fa
   * nfs_wait_bit_killable - helper for functions that are sleeping on bit locks
   * @word: long word containing the bit lock
   */
 -int nfs_wait_bit_killable(void *word)
 +int nfs_wait_bit_killable(struct wait_bit_key *key)
  {
        if (fatal_signal_pending(current))
                return -ERESTARTSYS;
@@@ -1002,6 -1002,15 +1002,15 @@@ int nfs_revalidate_inode(struct nfs_ser
  }
  EXPORT_SYMBOL_GPL(nfs_revalidate_inode);
  
+ int nfs_revalidate_inode_rcu(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))
+               return NFS_STALE(inode) ? -ESTALE : 0;
+       return -ECHILD;
+ }
  static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
  {
        struct nfs_inode *nfsi = NFS_I(inode);
@@@ -1074,8 -1083,8 +1083,8 @@@ int nfs_revalidate_mapping(struct inod
         * the bit lock here if it looks like we're going to be doing that.
         */
        for (;;) {
 -              ret = wait_on_bit(bitlock, NFS_INO_INVALIDATING,
 -                                nfs_wait_bit_killable, TASK_KILLABLE);
 +              ret = wait_on_bit_action(bitlock, NFS_INO_INVALIDATING,
 +                                       nfs_wait_bit_killable, TASK_KILLABLE);
                if (ret)
                        goto out;
                spin_lock(&inode->i_lock);
@@@ -1840,12 -1849,11 +1849,12 @@@ EXPORT_SYMBOL_GPL(nfs_net_id)
  static int nfs_net_init(struct net *net)
  {
        nfs_clients_init(net);
 -      return 0;
 +      return nfs_fs_proc_net_init(net);
  }
  
  static void nfs_net_exit(struct net *net)
  {
 +      nfs_fs_proc_net_exit(net);
        nfs_cleanup_cb_ident_idr(net);
  }
  
diff --combined fs/nfs/internal.h
index e2a45ae5014e0d4dc21c4bed16d1602f02fd88be,2f19e8392eb3464de12b6c9a7539db2d81845ced..9056622d223005c087caac590ed8139743665277
@@@ -195,16 -195,7 +195,16 @@@ extern struct rpc_clnt *nfs4_find_or_cr
  #ifdef CONFIG_PROC_FS
  extern int __init nfs_fs_proc_init(void);
  extern void nfs_fs_proc_exit(void);
 +extern int nfs_fs_proc_net_init(struct net *net);
 +extern void nfs_fs_proc_net_exit(struct net *net);
  #else
 +static inline int nfs_fs_proc_net_init(struct net *net)
 +{
 +      return 0;
 +}
 +static inline void nfs_fs_proc_net_exit(struct net *net)
 +{
 +}
  static inline int nfs_fs_proc_init(void)
  {
        return 0;
@@@ -247,11 -238,11 +247,11 @@@ void nfs_set_pgio_error(struct nfs_pgio
  int nfs_iocounter_wait(struct nfs_io_counter *c);
  
  extern const struct nfs_pageio_ops nfs_pgio_rw_ops;
- struct nfs_rw_header *nfs_rw_header_alloc(const struct nfs_rw_ops *);
- void nfs_rw_header_free(struct nfs_pgio_header *);
- void nfs_pgio_data_release(struct nfs_pgio_data *);
+ struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *);
+ void nfs_pgio_header_free(struct nfs_pgio_header *);
+ void nfs_pgio_data_destroy(struct nfs_pgio_header *);
  int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *);
- int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_data *,
+ int nfs_initiate_pgio(struct rpc_clnt *, struct nfs_pgio_header *,
                      const struct rpc_call_ops *, int, int);
  void nfs_free_request(struct nfs_page *req);
  
@@@ -357,7 -348,7 +357,7 @@@ extern int nfs_drop_inode(struct inode 
  extern void nfs_clear_inode(struct inode *);
  extern void nfs_evict_inode(struct inode *);
  void nfs_zap_acl_cache(struct inode *inode);
 -extern int nfs_wait_bit_killable(void *word);
 +extern int nfs_wait_bit_killable(struct wait_bit_key *key);
  
  /* super.c */
  extern const struct super_operations nfs_sops;
@@@ -451,6 -442,7 +451,7 @@@ int nfs_scan_commit(struct inode *inode
  void nfs_mark_request_commit(struct nfs_page *req,
                             struct pnfs_layout_segment *lseg,
                             struct nfs_commit_info *cinfo);
+ int nfs_write_need_commit(struct nfs_pgio_header *);
  int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
                            int how, struct nfs_commit_info *cinfo);
  void nfs_retry_commit(struct list_head *page_list,
@@@ -491,7 -483,7 +492,7 @@@ static inline void nfs_inode_dio_wait(s
  extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq);
  
  /* nfs4proc.c */
- extern void __nfs4_read_done_cb(struct nfs_pgio_data *);
+ extern void __nfs4_read_done_cb(struct nfs_pgio_header *);
  extern struct nfs_client *nfs4_init_client(struct nfs_client *clp,
                            const struct rpc_timeout *timeparms,
                            const char *ip_addr);
diff --combined fs/nfs/nfs4state.c
index 42f12118216700d2cde7c6093b8470b1f20ab674,a770c8e469a7e08fce67735bff68373dcd517f0f..a043f618cd5a30ef35a8ec63d54ff12034a2387f
@@@ -787,33 -787,36 +787,36 @@@ void nfs4_close_sync(struct nfs4_state 
   * that is compatible with current->files
   */
  static struct nfs4_lock_state *
- __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_pid, unsigned int type)
+ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
  {
        struct nfs4_lock_state *pos;
        list_for_each_entry(pos, &state->lock_states, ls_locks) {
-               if (type != NFS4_ANY_LOCK_TYPE && pos->ls_owner.lo_type != type)
+               if (pos->ls_owner != fl_owner)
                        continue;
-               switch (pos->ls_owner.lo_type) {
-               case NFS4_POSIX_LOCK_TYPE:
-                       if (pos->ls_owner.lo_u.posix_owner != fl_owner)
-                               continue;
-                       break;
-               case NFS4_FLOCK_LOCK_TYPE:
-                       if (pos->ls_owner.lo_u.flock_owner != fl_pid)
-                               continue;
-               }
                atomic_inc(&pos->ls_count);
                return pos;
        }
        return NULL;
  }
  
+ static void
+ free_lock_state_work(struct work_struct *work)
+ {
+       struct nfs4_lock_state *lsp = container_of(work,
+                                       struct nfs4_lock_state, ls_release);
+       struct nfs4_state *state = lsp->ls_state;
+       struct nfs_server *server = state->owner->so_server;
+       struct nfs_client *clp = server->nfs_client;
+       clp->cl_mvops->free_lock_state(server, lsp);
+ }
  /*
   * Return a compatible lock_state. If no initialized lock_state structure
   * exists, return an uninitialized one.
   *
   */
- static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_pid, unsigned int type)
+ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner)
  {
        struct nfs4_lock_state *lsp;
        struct nfs_server *server = state->owner->so_server;
        nfs4_init_seqid_counter(&lsp->ls_seqid);
        atomic_set(&lsp->ls_count, 1);
        lsp->ls_state = state;
-       lsp->ls_owner.lo_type = type;
-       switch (lsp->ls_owner.lo_type) {
-       case NFS4_FLOCK_LOCK_TYPE:
-               lsp->ls_owner.lo_u.flock_owner = fl_pid;
-               break;
-       case NFS4_POSIX_LOCK_TYPE:
-               lsp->ls_owner.lo_u.posix_owner = fl_owner;
-               break;
-       default:
-               goto out_free;
-       }
+       lsp->ls_owner = fl_owner;
        lsp->ls_seqid.owner_id = ida_simple_get(&server->lockowner_id, 0, 0, GFP_NOFS);
        if (lsp->ls_seqid.owner_id < 0)
                goto out_free;
        INIT_LIST_HEAD(&lsp->ls_locks);
+       INIT_WORK(&lsp->ls_release, free_lock_state_work);
        return lsp;
  out_free:
        kfree(lsp);
@@@ -857,13 -851,13 +851,13 @@@ void nfs4_free_lock_state(struct nfs_se
   * exists, return an uninitialized one.
   *
   */
- static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner, pid_t pid, unsigned int type)
+ static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
  {
        struct nfs4_lock_state *lsp, *new = NULL;
        
        for(;;) {
                spin_lock(&state->state_lock);
-               lsp = __nfs4_find_lock_state(state, owner, pid, type);
+               lsp = __nfs4_find_lock_state(state, owner);
                if (lsp != NULL)
                        break;
                if (new != NULL) {
                        break;
                }
                spin_unlock(&state->state_lock);
-               new = nfs4_alloc_lock_state(state, owner, pid, type);
+               new = nfs4_alloc_lock_state(state, owner);
                if (new == NULL)
                        return NULL;
        }
@@@ -902,13 -896,12 +896,12 @@@ void nfs4_put_lock_state(struct nfs4_lo
        if (list_empty(&state->lock_states))
                clear_bit(LK_STATE_IN_USE, &state->flags);
        spin_unlock(&state->state_lock);
-       server = state->owner->so_server;
-       if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
-               struct nfs_client *clp = server->nfs_client;
-               clp->cl_mvops->free_lock_state(server, lsp);
-       } else
+       if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags))
+               queue_work(nfsiod_workqueue, &lsp->ls_release);
+       else {
+               server = state->owner->so_server;
                nfs4_free_lock_state(server, lsp);
+       }
  }
  
  static void nfs4_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
@@@ -935,13 -928,7 +928,7 @@@ int nfs4_set_lock_state(struct nfs4_sta
  
        if (fl->fl_ops != NULL)
                return 0;
-       if (fl->fl_flags & FL_POSIX)
-               lsp = nfs4_get_lock_state(state, fl->fl_owner, 0, NFS4_POSIX_LOCK_TYPE);
-       else if (fl->fl_flags & FL_FLOCK)
-               lsp = nfs4_get_lock_state(state, NULL, fl->fl_pid,
-                               NFS4_FLOCK_LOCK_TYPE);
-       else
-               return -EINVAL;
+       lsp = nfs4_get_lock_state(state, fl->fl_owner);
        if (lsp == NULL)
                return -ENOMEM;
        fl->fl_u.nfs4_fl.owner = lsp;
@@@ -955,7 -942,6 +942,6 @@@ static int nfs4_copy_lock_stateid(nfs4_
  {
        struct nfs4_lock_state *lsp;
        fl_owner_t fl_owner;
-       pid_t fl_pid;
        int ret = -ENOENT;
  
  
                goto out;
  
        fl_owner = lockowner->l_owner;
-       fl_pid = lockowner->l_pid;
        spin_lock(&state->state_lock);
-       lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE);
+       lsp = __nfs4_find_lock_state(state, fl_owner);
        if (lsp && test_bit(NFS_LOCK_LOST, &lsp->ls_flags))
                ret = -EIO;
        else if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) {
@@@ -1251,8 -1236,8 +1236,8 @@@ int nfs4_wait_clnt_recover(struct nfs_c
        might_sleep();
  
        atomic_inc(&clp->cl_count);
 -      res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
 -                      nfs_wait_bit_killable, TASK_KILLABLE);
 +      res = wait_on_bit_action(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
 +                               nfs_wait_bit_killable, TASK_KILLABLE);
        if (res)
                goto out;
        if (clp->cl_cons_state < 0)
diff --combined fs/nfs/pagelist.c
index 0be5050638f7c026f14eb8eb728530a6238160d0,9425118e91d71917254567d0c738b42f2eeb8a67..ba491926df5f7df2db1e224c96e7e070bd36dcec
@@@ -115,7 -115,7 +115,7 @@@ __nfs_iocounter_wait(struct nfs_io_coun
                set_bit(NFS_IO_INPROGRESS, &c->flags);
                if (atomic_read(&c->io_count) == 0)
                        break;
 -              ret = nfs_wait_bit_killable(&c->flags);
 +              ret = nfs_wait_bit_killable(&q.key);
        } while (atomic_read(&c->io_count) != 0);
        finish_wait(wq, &q.wait);
        return ret;
@@@ -136,21 -136,36 +136,29 @@@ nfs_iocounter_wait(struct nfs_io_counte
        return __nfs_iocounter_wait(c);
  }
  
 -static int nfs_wait_bit_uninterruptible(void *word)
 -{
 -      io_schedule();
 -      return 0;
 -}
 -
  /*
   * nfs_page_group_lock - lock the head of the page group
   * @req - request in group that is to be locked
   *
   * this lock must be held if modifying the page group list
+  *
+  * returns result from wait_on_bit_lock: 0 on success, < 0 on error
   */
- void
- nfs_page_group_lock(struct nfs_page *req)
+ int
+ nfs_page_group_lock(struct nfs_page *req, bool wait)
  {
        struct nfs_page *head = req->wb_head;
+       int ret;
  
        WARN_ON_ONCE(head != head->wb_head);
  
-       wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK,
+       do {
+               ret = wait_on_bit_lock(&head->wb_flags, PG_HEADLOCK,
 -                      nfs_wait_bit_uninterruptible,
                        TASK_UNINTERRUPTIBLE);
+       } while (wait && ret != 0);
+       WARN_ON_ONCE(ret > 0);
+       return ret;
  }
  
  /*
@@@ -211,7 -226,7 +219,7 @@@ bool nfs_page_group_sync_on_bit(struct 
  {
        bool ret;
  
-       nfs_page_group_lock(req);
+       nfs_page_group_lock(req, true);
        ret = nfs_page_group_sync_on_bit_locked(req, bit);
        nfs_page_group_unlock(req);
  
@@@ -428,8 -443,9 +436,8 @@@ void nfs_release_request(struct nfs_pag
  int
  nfs_wait_on_request(struct nfs_page *req)
  {
 -      return wait_on_bit(&req->wb_flags, PG_BUSY,
 -                      nfs_wait_bit_uninterruptible,
 -                      TASK_UNINTERRUPTIBLE);
 +      return wait_on_bit_io(&req->wb_flags, PG_BUSY,
 +                            TASK_UNINTERRUPTIBLE);
  }
  
  /*
@@@ -454,123 -470,72 +462,72 @@@ size_t nfs_generic_pg_test(struct nfs_p
  }
  EXPORT_SYMBOL_GPL(nfs_generic_pg_test);
  
- static inline struct nfs_rw_header *NFS_RW_HEADER(struct nfs_pgio_header *hdr)
- {
-       return container_of(hdr, struct nfs_rw_header, header);
- }
- /**
-  * nfs_rw_header_alloc - Allocate a header for a read or write
-  * @ops: Read or write function vector
-  */
- struct nfs_rw_header *nfs_rw_header_alloc(const struct nfs_rw_ops *ops)
+ struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *ops)
  {
-       struct nfs_rw_header *header = ops->rw_alloc_header();
-       if (header) {
-               struct nfs_pgio_header *hdr = &header->header;
+       struct nfs_pgio_header *hdr = ops->rw_alloc_header();
  
+       if (hdr) {
                INIT_LIST_HEAD(&hdr->pages);
                spin_lock_init(&hdr->lock);
-               atomic_set(&hdr->refcnt, 0);
                hdr->rw_ops = ops;
        }
-       return header;
+       return hdr;
  }
- EXPORT_SYMBOL_GPL(nfs_rw_header_alloc);
+ EXPORT_SYMBOL_GPL(nfs_pgio_header_alloc);
  
  /*
-  * nfs_rw_header_free - Free a read or write header
+  * nfs_pgio_header_free - Free a read or write header
   * @hdr: The header to free
   */
- void nfs_rw_header_free(struct nfs_pgio_header *hdr)
+ void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
  {
-       hdr->rw_ops->rw_free_header(NFS_RW_HEADER(hdr));
+       hdr->rw_ops->rw_free_header(hdr);
  }
- EXPORT_SYMBOL_GPL(nfs_rw_header_free);
+ EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
  
  /**
-  * nfs_pgio_data_alloc - Allocate pageio data
-  * @hdr: The header making a request
-  * @pagecount: Number of pages to create
-  */
- static struct nfs_pgio_data *nfs_pgio_data_alloc(struct nfs_pgio_header *hdr,
-                                                unsigned int pagecount)
- {
-       struct nfs_pgio_data *data, *prealloc;
-       prealloc = &NFS_RW_HEADER(hdr)->rpc_data;
-       if (prealloc->header == NULL)
-               data = prealloc;
-       else
-               data = kzalloc(sizeof(*data), GFP_KERNEL);
-       if (!data)
-               goto out;
-       if (nfs_pgarray_set(&data->pages, pagecount)) {
-               data->header = hdr;
-               atomic_inc(&hdr->refcnt);
-       } else {
-               if (data != prealloc)
-                       kfree(data);
-               data = NULL;
-       }
- out:
-       return data;
- }
- /**
-  * nfs_pgio_data_release - Properly free pageio data
-  * @data: The data to release
+  * nfs_pgio_data_destroy - make @hdr suitable for reuse
+  *
+  * Frees memory and releases refs from nfs_generic_pgio, so that it may
+  * be called again.
+  *
+  * @hdr: A header that has had nfs_generic_pgio called
   */
- void nfs_pgio_data_release(struct nfs_pgio_data *data)
+ void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = data->header;
-       struct nfs_rw_header *pageio_header = NFS_RW_HEADER(hdr);
-       put_nfs_open_context(data->args.context);
-       if (data->pages.pagevec != data->pages.page_array)
-               kfree(data->pages.pagevec);
-       if (data == &pageio_header->rpc_data) {
-               data->header = NULL;
-               data = NULL;
-       }
-       if (atomic_dec_and_test(&hdr->refcnt))
-               hdr->completion_ops->completion(hdr);
-       /* Note: we only free the rpc_task after callbacks are done.
-        * See the comment in rpc_free_task() for why
-        */
-       kfree(data);
+       put_nfs_open_context(hdr->args.context);
+       if (hdr->page_array.pagevec != hdr->page_array.page_array)
+               kfree(hdr->page_array.pagevec);
  }
- EXPORT_SYMBOL_GPL(nfs_pgio_data_release);
+ EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy);
  
  /**
   * nfs_pgio_rpcsetup - Set up arguments for a pageio call
-  * @data: The pageio data
+  * @hdr: The pageio hdr
   * @count: Number of bytes to read
   * @offset: Initial offset
   * @how: How to commit data (writes only)
   * @cinfo: Commit information for the call (writes only)
   */
- static void nfs_pgio_rpcsetup(struct nfs_pgio_data *data,
+ static void nfs_pgio_rpcsetup(struct nfs_pgio_header *hdr,
                              unsigned int count, unsigned int offset,
                              int how, struct nfs_commit_info *cinfo)
  {
-       struct nfs_page *req = data->header->req;
+       struct nfs_page *req = hdr->req;
  
        /* Set up the RPC argument and reply structs
-        * NB: take care not to mess about with data->commit et al. */
+        * NB: take care not to mess about with hdr->commit et al. */
  
-       data->args.fh     = NFS_FH(data->header->inode);
-       data->args.offset = req_offset(req) + offset;
+       hdr->args.fh     = NFS_FH(hdr->inode);
+       hdr->args.offset = req_offset(req) + offset;
        /* pnfs_set_layoutcommit needs this */
-       data->mds_offset = data->args.offset;
-       data->args.pgbase = req->wb_pgbase + offset;
-       data->args.pages  = data->pages.pagevec;
-       data->args.count  = count;
-       data->args.context = get_nfs_open_context(req->wb_context);
-       data->args.lock_context = req->wb_lock_context;
-       data->args.stable  = NFS_UNSTABLE;
+       hdr->mds_offset = hdr->args.offset;
+       hdr->args.pgbase = req->wb_pgbase + offset;
+       hdr->args.pages  = hdr->page_array.pagevec;
+       hdr->args.count  = count;
+       hdr->args.context = get_nfs_open_context(req->wb_context);
+       hdr->args.lock_context = req->wb_lock_context;
+       hdr->args.stable  = NFS_UNSTABLE;
        switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
        case 0:
                break;
                if (nfs_reqs_to_commit(cinfo))
                        break;
        default:
-               data->args.stable = NFS_FILE_SYNC;
+               hdr->args.stable = NFS_FILE_SYNC;
        }
  
-       data->res.fattr   = &data->fattr;
-       data->res.count   = count;
-       data->res.eof     = 0;
-       data->res.verf    = &data->verf;
-       nfs_fattr_init(&data->fattr);
+       hdr->res.fattr   = &hdr->fattr;
+       hdr->res.count   = count;
+       hdr->res.eof     = 0;
+       hdr->res.verf    = &hdr->verf;
+       nfs_fattr_init(&hdr->fattr);
  }
  
  /**
-  * nfs_pgio_prepare - Prepare pageio data to go over the wire
+  * nfs_pgio_prepare - Prepare pageio hdr to go over the wire
   * @task: The current task
-  * @calldata: pageio data to prepare
+  * @calldata: pageio header to prepare
   */
  static void nfs_pgio_prepare(struct rpc_task *task, void *calldata)
  {
-       struct nfs_pgio_data *data = calldata;
+       struct nfs_pgio_header *hdr = calldata;
        int err;
-       err = NFS_PROTO(data->header->inode)->pgio_rpc_prepare(task, data);
+       err = NFS_PROTO(hdr->inode)->pgio_rpc_prepare(task, hdr);
        if (err)
                rpc_exit(task, err);
  }
  
- int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_data *data,
+ int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
                      const struct rpc_call_ops *call_ops, int how, int flags)
  {
        struct rpc_task *task;
        struct rpc_message msg = {
-               .rpc_argp = &data->args,
-               .rpc_resp = &data->res,
-               .rpc_cred = data->header->cred,
+               .rpc_argp = &hdr->args,
+               .rpc_resp = &hdr->res,
+               .rpc_cred = hdr->cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = clnt,
-               .task = &data->task,
+               .task = &hdr->task,
                .rpc_message = &msg,
                .callback_ops = call_ops,
-               .callback_data = data,
+               .callback_data = hdr,
                .workqueue = nfsiod_workqueue,
                .flags = RPC_TASK_ASYNC | flags,
        };
        int ret = 0;
  
-       data->header->rw_ops->rw_initiate(data, &msg, &task_setup_data, how);
+       hdr->rw_ops->rw_initiate(hdr, &msg, &task_setup_data, how);
  
        dprintk("NFS: %5u initiated pgio call "
                "(req %s/%llu, %u bytes @ offset %llu)\n",
-               data->task.tk_pid,
-               data->header->inode->i_sb->s_id,
-               (unsigned long long)NFS_FILEID(data->header->inode),
-               data->args.count,
-               (unsigned long long)data->args.offset);
+               hdr->task.tk_pid,
+               hdr->inode->i_sb->s_id,
+               (unsigned long long)NFS_FILEID(hdr->inode),
+               hdr->args.count,
+               (unsigned long long)hdr->args.offset);
  
        task = rpc_run_task(&task_setup_data);
        if (IS_ERR(task)) {
@@@ -657,22 -622,23 +614,23 @@@ static int nfs_pgio_error(struct nfs_pa
                          struct nfs_pgio_header *hdr)
  {
        set_bit(NFS_IOHDR_REDO, &hdr->flags);
-       nfs_pgio_data_release(hdr->data);
-       hdr->data = NULL;
+       nfs_pgio_data_destroy(hdr);
+       hdr->completion_ops->completion(hdr);
        desc->pg_completion_ops->error_cleanup(&desc->pg_list);
        return -ENOMEM;
  }
  
  /**
   * nfs_pgio_release - Release pageio data
-  * @calldata: The pageio data to release
+  * @calldata: The pageio header to release
   */
  static void nfs_pgio_release(void *calldata)
  {
-       struct nfs_pgio_data *data = calldata;
-       if (data->header->rw_ops->rw_release)
-               data->header->rw_ops->rw_release(data);
-       nfs_pgio_data_release(data);
+       struct nfs_pgio_header *hdr = calldata;
+       if (hdr->rw_ops->rw_release)
+               hdr->rw_ops->rw_release(hdr);
+       nfs_pgio_data_destroy(hdr);
+       hdr->completion_ops->completion(hdr);
  }
  
  /**
@@@ -713,22 -679,22 +671,22 @@@ EXPORT_SYMBOL_GPL(nfs_pageio_init)
  /**
   * nfs_pgio_result - Basic pageio error handling
   * @task: The task that ran
-  * @calldata: Pageio data to check
+  * @calldata: Pageio header to check
   */
  static void nfs_pgio_result(struct rpc_task *task, void *calldata)
  {
-       struct nfs_pgio_data *data = calldata;
-       struct inode *inode = data->header->inode;
+       struct nfs_pgio_header *hdr = calldata;
+       struct inode *inode = hdr->inode;
  
        dprintk("NFS: %s: %5u, (status %d)\n", __func__,
                task->tk_pid, task->tk_status);
  
-       if (data->header->rw_ops->rw_done(task, data, inode) != 0)
+       if (hdr->rw_ops->rw_done(task, hdr, inode) != 0)
                return;
        if (task->tk_status < 0)
-               nfs_set_pgio_error(data->header, task->tk_status, data->args.offset);
+               nfs_set_pgio_error(hdr, task->tk_status, hdr->args.offset);
        else
-               data->header->rw_ops->rw_result(task, data);
+               hdr->rw_ops->rw_result(task, hdr);
  }
  
  /*
@@@ -744,17 -710,16 +702,16 @@@ int nfs_generic_pgio(struct nfs_pageio_
  {
        struct nfs_page         *req;
        struct page             **pages;
-       struct nfs_pgio_data    *data;
        struct list_head *head = &desc->pg_list;
        struct nfs_commit_info cinfo;
+       unsigned int pagecount;
  
-       data = nfs_pgio_data_alloc(hdr, nfs_page_array_len(desc->pg_base,
-                                                          desc->pg_count));
-       if (!data)
+       pagecount = nfs_page_array_len(desc->pg_base, desc->pg_count);
+       if (!nfs_pgarray_set(&hdr->page_array, pagecount))
                return nfs_pgio_error(desc, hdr);
  
        nfs_init_cinfo(&cinfo, desc->pg_inode, desc->pg_dreq);
-       pages = data->pages.pagevec;
+       pages = hdr->page_array.pagevec;
        while (!list_empty(head)) {
                req = nfs_list_entry(head->next);
                nfs_list_remove_request(req);
                desc->pg_ioflags &= ~FLUSH_COND_STABLE;
  
        /* Set up the argument struct */
-       nfs_pgio_rpcsetup(data, desc->pg_count, 0, desc->pg_ioflags, &cinfo);
-       hdr->data = data;
+       nfs_pgio_rpcsetup(hdr, desc->pg_count, 0, desc->pg_ioflags, &cinfo);
        desc->pg_rpc_callops = &nfs_pgio_common_ops;
        return 0;
  }
@@@ -776,25 -740,20 +732,20 @@@ EXPORT_SYMBOL_GPL(nfs_generic_pgio)
  
  static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
  {
-       struct nfs_rw_header *rw_hdr;
        struct nfs_pgio_header *hdr;
        int ret;
  
-       rw_hdr = nfs_rw_header_alloc(desc->pg_rw_ops);
-       if (!rw_hdr) {
+       hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
+       if (!hdr) {
                desc->pg_completion_ops->error_cleanup(&desc->pg_list);
                return -ENOMEM;
        }
-       hdr = &rw_hdr->header;
-       nfs_pgheader_init(desc, hdr, nfs_rw_header_free);
-       atomic_inc(&hdr->refcnt);
+       nfs_pgheader_init(desc, hdr, nfs_pgio_header_free);
        ret = nfs_generic_pgio(desc, hdr);
        if (ret == 0)
                ret = nfs_initiate_pgio(NFS_CLIENT(hdr->inode),
-                                       hdr->data, desc->pg_rpc_callops,
+                                       hdr, desc->pg_rpc_callops,
                                        desc->pg_ioflags, 0);
-       if (atomic_dec_and_test(&hdr->refcnt))
-               hdr->completion_ops->completion(hdr);
        return ret;
  }
  
@@@ -907,8 -866,13 +858,13 @@@ static int __nfs_pageio_add_request(str
        struct nfs_page *subreq;
        unsigned int bytes_left = 0;
        unsigned int offset, pgbase;
+       int ret;
  
-       nfs_page_group_lock(req);
+       ret = nfs_page_group_lock(req, false);
+       if (ret < 0) {
+               desc->pg_error = ret;
+               return 0;
+       }
  
        subreq = req;
        bytes_left = subreq->wb_bytes;
                        if (desc->pg_recoalesce)
                                return 0;
                        /* retry add_request for this subreq */
-                       nfs_page_group_lock(req);
+                       ret = nfs_page_group_lock(req, false);
+                       if (ret < 0) {
+                               desc->pg_error = ret;
+                               return 0;
+                       }
                        continue;
                }
  
@@@ -1005,7 -973,38 +965,38 @@@ int nfs_pageio_add_request(struct nfs_p
        } while (ret);
        return ret;
  }
- EXPORT_SYMBOL_GPL(nfs_pageio_add_request);
+ /*
+  * nfs_pageio_resend - Transfer requests to new descriptor and resend
+  * @hdr - the pgio header to move request from
+  * @desc - the pageio descriptor to add requests to
+  *
+  * Try to move each request (nfs_page) from @hdr to @desc then attempt
+  * to send them.
+  *
+  * Returns 0 on success and < 0 on error.
+  */
+ int nfs_pageio_resend(struct nfs_pageio_descriptor *desc,
+                     struct nfs_pgio_header *hdr)
+ {
+       LIST_HEAD(failed);
+       desc->pg_dreq = hdr->dreq;
+       while (!list_empty(&hdr->pages)) {
+               struct nfs_page *req = nfs_list_entry(hdr->pages.next);
+               nfs_list_remove_request(req);
+               if (!nfs_pageio_add_request(desc, req))
+                       nfs_list_add_request(req, &failed);
+       }
+       nfs_pageio_complete(desc);
+       if (!list_empty(&failed)) {
+               list_move(&failed, &hdr->pages);
+               return -EIO;
+       }
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(nfs_pageio_resend);
  
  /**
   * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor
@@@ -1021,7 -1020,6 +1012,6 @@@ void nfs_pageio_complete(struct nfs_pag
                        break;
        }
  }
- EXPORT_SYMBOL_GPL(nfs_pageio_complete);
  
  /**
   * nfs_pageio_cond_complete - Conditional I/O completion
diff --combined fs/nfs/pnfs.c
index a8914b3356174a5063369452bc73fab7c0b8db2e,4e853157feccbd30cd50dafe8dff241e3d4a1fc6..a3851debf8a2f481435b3750f5cb1bec678ab4fe
@@@ -361,6 -361,23 +361,23 @@@ pnfs_put_lseg(struct pnfs_layout_segmen
  }
  EXPORT_SYMBOL_GPL(pnfs_put_lseg);
  
+ static void pnfs_put_lseg_async_work(struct work_struct *work)
+ {
+       struct pnfs_layout_segment *lseg;
+       lseg = container_of(work, struct pnfs_layout_segment, pls_work);
+       pnfs_put_lseg(lseg);
+ }
+ void
+ pnfs_put_lseg_async(struct pnfs_layout_segment *lseg)
+ {
+       INIT_WORK(&lseg->pls_work, pnfs_put_lseg_async_work);
+       schedule_work(&lseg->pls_work);
+ }
+ EXPORT_SYMBOL_GPL(pnfs_put_lseg_async);
  static u64
  end_offset(u64 start, u64 len)
  {
@@@ -1470,41 -1487,19 +1487,19 @@@ pnfs_generic_pg_test(struct nfs_pageio_
  }
  EXPORT_SYMBOL_GPL(pnfs_generic_pg_test);
  
- int pnfs_write_done_resend_to_mds(struct inode *inode,
-                               struct list_head *head,
-                               const struct nfs_pgio_completion_ops *compl_ops,
-                               struct nfs_direct_req *dreq)
+ int pnfs_write_done_resend_to_mds(struct nfs_pgio_header *hdr)
  {
        struct nfs_pageio_descriptor pgio;
-       LIST_HEAD(failed);
  
        /* Resend all requests through the MDS */
-       nfs_pageio_init_write(&pgio, inode, FLUSH_STABLE, true, compl_ops);
-       pgio.pg_dreq = dreq;
-       while (!list_empty(head)) {
-               struct nfs_page *req = nfs_list_entry(head->next);
-               nfs_list_remove_request(req);
-               if (!nfs_pageio_add_request(&pgio, req))
-                       nfs_list_add_request(req, &failed);
-       }
-       nfs_pageio_complete(&pgio);
-       if (!list_empty(&failed)) {
-               /* For some reason our attempt to resend pages. Mark the
-                * overall send request as having failed, and let
-                * nfs_writeback_release_full deal with the error.
-                */
-               list_move(&failed, head);
-               return -EIO;
-       }
-       return 0;
+       nfs_pageio_init_write(&pgio, hdr->inode, FLUSH_STABLE, true,
+                             hdr->completion_ops);
+       return nfs_pageio_resend(&pgio, hdr);
  }
  EXPORT_SYMBOL_GPL(pnfs_write_done_resend_to_mds);
  
- static void pnfs_ld_handle_write_error(struct nfs_pgio_data *data)
+ static void pnfs_ld_handle_write_error(struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = data->header;
  
        dprintk("pnfs write error = %d\n", hdr->pnfs_error);
        if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
                pnfs_return_layout(hdr->inode);
        }
        if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
-               data->task.tk_status = pnfs_write_done_resend_to_mds(hdr->inode,
-                                                       &hdr->pages,
-                                                       hdr->completion_ops,
-                                                       hdr->dreq);
+               hdr->task.tk_status = pnfs_write_done_resend_to_mds(hdr);
  }
  
  /*
   * Called by non rpc-based layout drivers
   */
- void pnfs_ld_write_done(struct nfs_pgio_data *data)
+ void pnfs_ld_write_done(struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = data->header;
-       trace_nfs4_pnfs_write(data, hdr->pnfs_error);
+       trace_nfs4_pnfs_write(hdr, hdr->pnfs_error);
        if (!hdr->pnfs_error) {
-               pnfs_set_layoutcommit(data);
-               hdr->mds_ops->rpc_call_done(&data->task, data);
+               pnfs_set_layoutcommit(hdr);
+               hdr->mds_ops->rpc_call_done(&hdr->task, hdr);
        } else
-               pnfs_ld_handle_write_error(data);
-       hdr->mds_ops->rpc_release(data);
+               pnfs_ld_handle_write_error(hdr);
+       hdr->mds_ops->rpc_release(hdr);
  }
  EXPORT_SYMBOL_GPL(pnfs_ld_write_done);
  
  static void
  pnfs_write_through_mds(struct nfs_pageio_descriptor *desc,
-               struct nfs_pgio_data *data)
+               struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = data->header;
        if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
                list_splice_tail_init(&hdr->pages, &desc->pg_list);
                nfs_pageio_reset_write_mds(desc);
                desc->pg_recoalesce = 1;
        }
-       nfs_pgio_data_release(data);
+       nfs_pgio_data_destroy(hdr);
  }
  
  static enum pnfs_try_status
- pnfs_try_to_write_data(struct nfs_pgio_data *wdata,
+ pnfs_try_to_write_data(struct nfs_pgio_header *hdr,
                        const struct rpc_call_ops *call_ops,
                        struct pnfs_layout_segment *lseg,
                        int how)
  {
-       struct nfs_pgio_header *hdr = wdata->header;
        struct inode *inode = hdr->inode;
        enum pnfs_try_status trypnfs;
        struct nfs_server *nfss = NFS_SERVER(inode);
        hdr->mds_ops = call_ops;
  
        dprintk("%s: Writing ino:%lu %u@%llu (how %d)\n", __func__,
-               inode->i_ino, wdata->args.count, wdata->args.offset, how);
-       trypnfs = nfss->pnfs_curr_ld->write_pagelist(wdata, how);
+               inode->i_ino, hdr->args.count, hdr->args.offset, how);
+       trypnfs = nfss->pnfs_curr_ld->write_pagelist(hdr, how);
        if (trypnfs != PNFS_NOT_ATTEMPTED)
                nfs_inc_stats(inode, NFSIOS_PNFS_WRITE);
        dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs);
@@@ -1575,139 -1562,105 +1562,105 @@@ static voi
  pnfs_do_write(struct nfs_pageio_descriptor *desc,
              struct nfs_pgio_header *hdr, int how)
  {
-       struct nfs_pgio_data *data = hdr->data;
        const struct rpc_call_ops *call_ops = desc->pg_rpc_callops;
        struct pnfs_layout_segment *lseg = desc->pg_lseg;
        enum pnfs_try_status trypnfs;
  
        desc->pg_lseg = NULL;
-       trypnfs = pnfs_try_to_write_data(data, call_ops, lseg, how);
+       trypnfs = pnfs_try_to_write_data(hdr, call_ops, lseg, how);
        if (trypnfs == PNFS_NOT_ATTEMPTED)
-               pnfs_write_through_mds(desc, data);
+               pnfs_write_through_mds(desc, hdr);
        pnfs_put_lseg(lseg);
  }
  
  static void pnfs_writehdr_free(struct nfs_pgio_header *hdr)
  {
        pnfs_put_lseg(hdr->lseg);
-       nfs_rw_header_free(hdr);
+       nfs_pgio_header_free(hdr);
  }
  EXPORT_SYMBOL_GPL(pnfs_writehdr_free);
  
  int
  pnfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
  {
-       struct nfs_rw_header *whdr;
        struct nfs_pgio_header *hdr;
        int ret;
  
-       whdr = nfs_rw_header_alloc(desc->pg_rw_ops);
-       if (!whdr) {
+       hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
+       if (!hdr) {
                desc->pg_completion_ops->error_cleanup(&desc->pg_list);
                pnfs_put_lseg(desc->pg_lseg);
                desc->pg_lseg = NULL;
                return -ENOMEM;
        }
-       hdr = &whdr->header;
        nfs_pgheader_init(desc, hdr, pnfs_writehdr_free);
        hdr->lseg = pnfs_get_lseg(desc->pg_lseg);
-       atomic_inc(&hdr->refcnt);
        ret = nfs_generic_pgio(desc, hdr);
        if (ret != 0) {
                pnfs_put_lseg(desc->pg_lseg);
                desc->pg_lseg = NULL;
        } else
                pnfs_do_write(desc, hdr, desc->pg_ioflags);
-       if (atomic_dec_and_test(&hdr->refcnt))
-               hdr->completion_ops->completion(hdr);
        return ret;
  }
  EXPORT_SYMBOL_GPL(pnfs_generic_pg_writepages);
  
- int pnfs_read_done_resend_to_mds(struct inode *inode,
-                               struct list_head *head,
-                               const struct nfs_pgio_completion_ops *compl_ops,
-                               struct nfs_direct_req *dreq)
+ int pnfs_read_done_resend_to_mds(struct nfs_pgio_header *hdr)
  {
        struct nfs_pageio_descriptor pgio;
-       LIST_HEAD(failed);
  
        /* Resend all requests through the MDS */
-       nfs_pageio_init_read(&pgio, inode, true, compl_ops);
-       pgio.pg_dreq = dreq;
-       while (!list_empty(head)) {
-               struct nfs_page *req = nfs_list_entry(head->next);
-               nfs_list_remove_request(req);
-               if (!nfs_pageio_add_request(&pgio, req))
-                       nfs_list_add_request(req, &failed);
-       }
-       nfs_pageio_complete(&pgio);
-       if (!list_empty(&failed)) {
-               list_move(&failed, head);
-               return -EIO;
-       }
-       return 0;
+       nfs_pageio_init_read(&pgio, hdr->inode, true, hdr->completion_ops);
+       return nfs_pageio_resend(&pgio, hdr);
  }
  EXPORT_SYMBOL_GPL(pnfs_read_done_resend_to_mds);
  
- static void pnfs_ld_handle_read_error(struct nfs_pgio_data *data)
+ static void pnfs_ld_handle_read_error(struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = data->header;
        dprintk("pnfs read error = %d\n", hdr->pnfs_error);
        if (NFS_SERVER(hdr->inode)->pnfs_curr_ld->flags &
            PNFS_LAYOUTRET_ON_ERROR) {
                pnfs_return_layout(hdr->inode);
        }
        if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags))
-               data->task.tk_status = pnfs_read_done_resend_to_mds(hdr->inode,
-                                                       &hdr->pages,
-                                                       hdr->completion_ops,
-                                                       hdr->dreq);
+               hdr->task.tk_status = pnfs_read_done_resend_to_mds(hdr);
  }
  
  /*
   * Called by non rpc-based layout drivers
   */
- void pnfs_ld_read_done(struct nfs_pgio_data *data)
+ void pnfs_ld_read_done(struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = data->header;
-       trace_nfs4_pnfs_read(data, hdr->pnfs_error);
+       trace_nfs4_pnfs_read(hdr, hdr->pnfs_error);
        if (likely(!hdr->pnfs_error)) {
-               __nfs4_read_done_cb(data);
-               hdr->mds_ops->rpc_call_done(&data->task, data);
+               __nfs4_read_done_cb(hdr);
+               hdr->mds_ops->rpc_call_done(&hdr->task, hdr);
        } else
-               pnfs_ld_handle_read_error(data);
-       hdr->mds_ops->rpc_release(data);
+               pnfs_ld_handle_read_error(hdr);
+       hdr->mds_ops->rpc_release(hdr);
  }
  EXPORT_SYMBOL_GPL(pnfs_ld_read_done);
  
  static void
  pnfs_read_through_mds(struct nfs_pageio_descriptor *desc,
-               struct nfs_pgio_data *data)
+               struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = data->header;
        if (!test_and_set_bit(NFS_IOHDR_REDO, &hdr->flags)) {
                list_splice_tail_init(&hdr->pages, &desc->pg_list);
                nfs_pageio_reset_read_mds(desc);
                desc->pg_recoalesce = 1;
        }
-       nfs_pgio_data_release(data);
+       nfs_pgio_data_destroy(hdr);
  }
  
  /*
   * Call the appropriate parallel I/O subsystem read function.
   */
  static enum pnfs_try_status
- pnfs_try_to_read_data(struct nfs_pgio_data *rdata,
+ pnfs_try_to_read_data(struct nfs_pgio_header *hdr,
                       const struct rpc_call_ops *call_ops,
                       struct pnfs_layout_segment *lseg)
  {
-       struct nfs_pgio_header *hdr = rdata->header;
        struct inode *inode = hdr->inode;
        struct nfs_server *nfss = NFS_SERVER(inode);
        enum pnfs_try_status trypnfs;
        hdr->mds_ops = call_ops;
  
        dprintk("%s: Reading ino:%lu %u@%llu\n",
-               __func__, inode->i_ino, rdata->args.count, rdata->args.offset);
+               __func__, inode->i_ino, hdr->args.count, hdr->args.offset);
  
-       trypnfs = nfss->pnfs_curr_ld->read_pagelist(rdata);
+       trypnfs = nfss->pnfs_curr_ld->read_pagelist(hdr);
        if (trypnfs != PNFS_NOT_ATTEMPTED)
                nfs_inc_stats(inode, NFSIOS_PNFS_READ);
        dprintk("%s End (trypnfs:%d)\n", __func__, trypnfs);
  static void
  pnfs_do_read(struct nfs_pageio_descriptor *desc, struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_data *data = hdr->data;
        const struct rpc_call_ops *call_ops = desc->pg_rpc_callops;
        struct pnfs_layout_segment *lseg = desc->pg_lseg;
        enum pnfs_try_status trypnfs;
  
        desc->pg_lseg = NULL;
-       trypnfs = pnfs_try_to_read_data(data, call_ops, lseg);
+       trypnfs = pnfs_try_to_read_data(hdr, call_ops, lseg);
        if (trypnfs == PNFS_NOT_ATTEMPTED)
-               pnfs_read_through_mds(desc, data);
+               pnfs_read_through_mds(desc, hdr);
        pnfs_put_lseg(lseg);
  }
  
  static void pnfs_readhdr_free(struct nfs_pgio_header *hdr)
  {
        pnfs_put_lseg(hdr->lseg);
-       nfs_rw_header_free(hdr);
+       nfs_pgio_header_free(hdr);
  }
  EXPORT_SYMBOL_GPL(pnfs_readhdr_free);
  
  int
  pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
  {
-       struct nfs_rw_header *rhdr;
        struct nfs_pgio_header *hdr;
        int ret;
  
-       rhdr = nfs_rw_header_alloc(desc->pg_rw_ops);
-       if (!rhdr) {
+       hdr = nfs_pgio_header_alloc(desc->pg_rw_ops);
+       if (!hdr) {
                desc->pg_completion_ops->error_cleanup(&desc->pg_list);
                ret = -ENOMEM;
                pnfs_put_lseg(desc->pg_lseg);
                desc->pg_lseg = NULL;
                return ret;
        }
-       hdr = &rhdr->header;
        nfs_pgheader_init(desc, hdr, pnfs_readhdr_free);
        hdr->lseg = pnfs_get_lseg(desc->pg_lseg);
-       atomic_inc(&hdr->refcnt);
        ret = nfs_generic_pgio(desc, hdr);
        if (ret != 0) {
                pnfs_put_lseg(desc->pg_lseg);
                desc->pg_lseg = NULL;
        } else
                pnfs_do_read(desc, hdr);
-       if (atomic_dec_and_test(&hdr->refcnt))
-               hdr->completion_ops->completion(hdr);
        return ret;
  }
  EXPORT_SYMBOL_GPL(pnfs_generic_pg_readpages);
@@@ -1820,12 -1767,11 +1767,11 @@@ void pnfs_set_lo_fail(struct pnfs_layou
  EXPORT_SYMBOL_GPL(pnfs_set_lo_fail);
  
  void
- pnfs_set_layoutcommit(struct nfs_pgio_data *wdata)
+ pnfs_set_layoutcommit(struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = wdata->header;
        struct inode *inode = hdr->inode;
        struct nfs_inode *nfsi = NFS_I(inode);
-       loff_t end_pos = wdata->mds_offset + wdata->res.count;
+       loff_t end_pos = hdr->mds_offset + hdr->res.count;
        bool mark_as_dirty = false;
  
        spin_lock(&inode->i_lock);
@@@ -1885,7 -1831,7 +1831,7 @@@ pnfs_layoutcommit_inode(struct inode *i
        if (test_and_set_bit(NFS_INO_LAYOUTCOMMITTING, &nfsi->flags)) {
                if (!sync)
                        goto out;
 -              status = wait_on_bit_lock(&nfsi->flags,
 +              status = wait_on_bit_lock_action(&nfsi->flags,
                                NFS_INO_LAYOUTCOMMITTING,
                                nfs_wait_bit_killable,
                                TASK_KILLABLE);
diff --combined fs/nfs/write.c
index 962c9ee758be30e57141761bd29898a1df129009,1065de26190bd6ad25a370c334c78a7027811d06..e3b5cf28bdc5c2dbfba5d3c16b06b5724afe6c60
@@@ -47,6 -47,8 +47,8 @@@ static const struct nfs_pgio_completion
  static const struct nfs_commit_completion_ops nfs_commit_completion_ops;
  static const struct nfs_rw_ops nfs_rw_write_ops;
  static void nfs_clear_request_commit(struct nfs_page *req);
+ static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo,
+                                     struct inode *inode);
  
  static struct kmem_cache *nfs_wdata_cachep;
  static mempool_t *nfs_wdata_mempool;
@@@ -71,18 -73,18 +73,18 @@@ void nfs_commit_free(struct nfs_commit_
  }
  EXPORT_SYMBOL_GPL(nfs_commit_free);
  
- static struct nfs_rw_header *nfs_writehdr_alloc(void)
+ static struct nfs_pgio_header *nfs_writehdr_alloc(void)
  {
-       struct nfs_rw_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
+       struct nfs_pgio_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
  
        if (p)
                memset(p, 0, sizeof(*p));
        return p;
  }
  
- static void nfs_writehdr_free(struct nfs_rw_header *whdr)
+ static void nfs_writehdr_free(struct nfs_pgio_header *hdr)
  {
-       mempool_free(whdr, nfs_wdata_mempool);
+       mempool_free(hdr, nfs_wdata_mempool);
  }
  
  static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
        set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
  }
  
+ /*
+  * nfs_page_search_commits_for_head_request_locked
+  *
+  * Search through commit lists on @inode for the head request for @page.
+  * Must be called while holding the inode (which is cinfo) lock.
+  *
+  * Returns the head request if found, or NULL if not found.
+  */
+ static struct nfs_page *
+ nfs_page_search_commits_for_head_request_locked(struct nfs_inode *nfsi,
+                                               struct page *page)
+ {
+       struct nfs_page *freq, *t;
+       struct nfs_commit_info cinfo;
+       struct inode *inode = &nfsi->vfs_inode;
+       nfs_init_cinfo_from_inode(&cinfo, inode);
+       /* search through pnfs commit lists */
+       freq = pnfs_search_commit_reqs(inode, &cinfo, page);
+       if (freq)
+               return freq->wb_head;
+       /* Linearly search the commit list for the correct request */
+       list_for_each_entry_safe(freq, t, &cinfo.mds->list, wb_list) {
+               if (freq->wb_page == page)
+                       return freq->wb_head;
+       }
+       return NULL;
+ }
  /*
   * nfs_page_find_head_request_locked - find head request associated with @page
   *
@@@ -106,21 -140,12 +140,12 @@@ nfs_page_find_head_request_locked(struc
  
        if (PagePrivate(page))
                req = (struct nfs_page *)page_private(page);
-       else if (unlikely(PageSwapCache(page))) {
-               struct nfs_page *freq, *t;
-               /* Linearly search the commit list for the correct req */
-               list_for_each_entry_safe(freq, t, &nfsi->commit_info.list, wb_list) {
-                       if (freq->wb_page == page) {
-                               req = freq->wb_head;
-                               break;
-                       }
-               }
-       }
+       else if (unlikely(PageSwapCache(page)))
+               req = nfs_page_search_commits_for_head_request_locked(nfsi,
+                       page);
  
        if (req) {
                WARN_ON_ONCE(req->wb_head != req);
                kref_get(&req->wb_kref);
        }
  
@@@ -216,7 -241,7 +241,7 @@@ static bool nfs_page_group_covers_page(
        unsigned int pos = 0;
        unsigned int len = nfs_page_length(req->wb_page);
  
-       nfs_page_group_lock(req);
+       nfs_page_group_lock(req, true);
  
        do {
                tmp = nfs_page_group_search_locked(req->wb_head, pos);
@@@ -379,8 -404,6 +404,6 @@@ nfs_destroy_unlinked_subrequests(struc
                subreq->wb_head = subreq;
                subreq->wb_this_page = subreq;
  
-               nfs_clear_request_commit(subreq);
                /* subreq is now totally disconnected from page group or any
                 * write / commit lists. last chance to wake any waiters */
                nfs_unlock_request(subreq);
@@@ -456,7 -479,9 +479,9 @@@ try_again
        }
  
        /* lock each request in the page group */
-       nfs_page_group_lock(head);
+       ret = nfs_page_group_lock(head, false);
+       if (ret < 0)
+               return ERR_PTR(ret);
        subreq = head;
        do {
                /*
         * Commit list removal accounting is done after locks are dropped */
        subreq = head;
        do {
-               nfs_list_remove_request(subreq);
+               nfs_clear_request_commit(subreq);
                subreq = subreq->wb_this_page;
        } while (subreq != head);
  
  
        nfs_page_group_unlock(head);
  
-       /* drop lock to clear_request_commit the head req and clean up
-        * requests on destroy list */
+       /* drop lock to clean uprequests on destroy list */
        spin_unlock(&inode->i_lock);
  
        nfs_destroy_unlinked_subrequests(destroy_list, head);
  
-       /* clean up commit list state */
-       nfs_clear_request_commit(head);
        /* still holds ref on head from nfs_page_find_head_request_locked
         * and still has lock on head from lock loop */
        return head;
@@@ -623,7 -644,7 +644,7 @@@ int nfs_writepages(struct address_spac
        int err;
  
        /* Stop dirtying of new pages while we sync */
 -      err = wait_on_bit_lock(bitlock, NFS_INO_FLUSHING,
 +      err = wait_on_bit_lock_action(bitlock, NFS_INO_FLUSHING,
                        nfs_wait_bit_killable, TASK_KILLABLE);
        if (err)
                goto out_err;
@@@ -705,6 -726,8 +726,8 @@@ static void nfs_inode_remove_request(st
  
        if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags))
                nfs_release_request(req);
+       else
+               WARN_ON_ONCE(1);
  }
  
  static void
@@@ -808,6 -831,7 +831,7 @@@ nfs_clear_page_commit(struct page *page
        dec_bdi_stat(page_file_mapping(page)->backing_dev_info, BDI_RECLAIMABLE);
  }
  
+ /* Called holding inode (/cinfo) lock */
  static void
  nfs_clear_request_commit(struct nfs_page *req)
  {
  
                nfs_init_cinfo_from_inode(&cinfo, inode);
                if (!pnfs_clear_request_commit(req, &cinfo)) {
-                       spin_lock(cinfo.lock);
                        nfs_request_remove_commit_list(req, &cinfo);
-                       spin_unlock(cinfo.lock);
                }
                nfs_clear_page_commit(req->wb_page);
        }
  }
  
- static inline
- int nfs_write_need_commit(struct nfs_pgio_data *data)
+ int nfs_write_need_commit(struct nfs_pgio_header *hdr)
  {
-       if (data->verf.committed == NFS_DATA_SYNC)
-               return data->header->lseg == NULL;
-       return data->verf.committed != NFS_FILE_SYNC;
+       if (hdr->verf.committed == NFS_DATA_SYNC)
+               return hdr->lseg == NULL;
+       return hdr->verf.committed != NFS_FILE_SYNC;
  }
  
  #else
@@@ -856,8 -877,7 +877,7 @@@ nfs_clear_request_commit(struct nfs_pag
  {
  }
  
- static inline
- int nfs_write_need_commit(struct nfs_pgio_data *data)
+ int nfs_write_need_commit(struct nfs_pgio_header *hdr)
  {
        return 0;
  }
@@@ -883,11 -903,7 +903,7 @@@ static void nfs_write_completion(struc
                        nfs_context_set_write_error(req->wb_context, hdr->error);
                        goto remove_req;
                }
-               if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags)) {
-                       nfs_mark_request_dirty(req);
-                       goto next;
-               }
-               if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) {
+               if (nfs_write_need_commit(hdr)) {
                        memcpy(&req->wb_verf, &hdr->verf.verifier, sizeof(req->wb_verf));
                        nfs_mark_request_commit(req, hdr->lseg, &cinfo);
                        goto next;
@@@ -1038,9 -1054,9 +1054,9 @@@ static struct nfs_page *nfs_try_to_upda
        else
                req->wb_bytes = rqend - req->wb_offset;
  out_unlock:
-       spin_unlock(&inode->i_lock);
        if (req)
                nfs_clear_request_commit(req);
+       spin_unlock(&inode->i_lock);
        return req;
  out_flushme:
        spin_unlock(&inode->i_lock);
@@@ -1241,17 -1257,18 +1257,18 @@@ static int flush_task_priority(int how
        return RPC_PRIORITY_NORMAL;
  }
  
- static void nfs_initiate_write(struct nfs_pgio_data *data, struct rpc_message *msg,
+ static void nfs_initiate_write(struct nfs_pgio_header *hdr,
+                              struct rpc_message *msg,
                               struct rpc_task_setup *task_setup_data, int how)
  {
-       struct inode *inode = data->header->inode;
+       struct inode *inode = hdr->inode;
        int priority = flush_task_priority(how);
  
        task_setup_data->priority = priority;
-       NFS_PROTO(inode)->write_setup(data, msg);
+       NFS_PROTO(inode)->write_setup(hdr, msg);
  
        nfs4_state_protect_write(NFS_SERVER(inode)->nfs_client,
-                                &task_setup_data->rpc_client, msg, data);
+                                &task_setup_data->rpc_client, msg, hdr);
  }
  
  /* If a nfs_flush_* function fails, it should remove reqs from @head and
@@@ -1313,21 -1330,9 +1330,9 @@@ void nfs_commit_prepare(struct rpc_tas
        NFS_PROTO(data->inode)->commit_rpc_prepare(task, data);
  }
  
- static void nfs_writeback_release_common(struct nfs_pgio_data *data)
+ static void nfs_writeback_release_common(struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_header *hdr = data->header;
-       int status = data->task.tk_status;
-       if ((status >= 0) && nfs_write_need_commit(data)) {
-               spin_lock(&hdr->lock);
-               if (test_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags))
-                       ; /* Do nothing */
-               else if (!test_and_set_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags))
-                       memcpy(&hdr->verf, &data->verf, sizeof(hdr->verf));
-               else if (memcmp(&hdr->verf, &data->verf, sizeof(hdr->verf)))
-                       set_bit(NFS_IOHDR_NEED_RESCHED, &hdr->flags);
-               spin_unlock(&hdr->lock);
-       }
+       /* do nothing! */
  }
  
  /*
@@@ -1358,7 -1363,8 +1363,8 @@@ static int nfs_should_remove_suid(cons
  /*
   * This function is called when the WRITE call is complete.
   */
- static int nfs_writeback_done(struct rpc_task *task, struct nfs_pgio_data *data,
+ static int nfs_writeback_done(struct rpc_task *task,
+                             struct nfs_pgio_header *hdr,
                              struct inode *inode)
  {
        int status;
         * another writer had changed the file, but some applications
         * depend on tighter cache coherency when writing.
         */
-       status = NFS_PROTO(inode)->write_done(task, data);
+       status = NFS_PROTO(inode)->write_done(task, hdr);
        if (status != 0)
                return status;
-       nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, data->res.count);
+       nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, hdr->res.count);
  
  #if IS_ENABLED(CONFIG_NFS_V3) || IS_ENABLED(CONFIG_NFS_V4)
-       if (data->res.verf->committed < data->args.stable && task->tk_status >= 0) {
+       if (hdr->res.verf->committed < hdr->args.stable &&
+           task->tk_status >= 0) {
                /* We tried a write call, but the server did not
                 * commit data to stable storage even though we
                 * requested it.
                        dprintk("NFS:       faulty NFS server %s:"
                                " (committed = %d) != (stable = %d)\n",
                                NFS_SERVER(inode)->nfs_client->cl_hostname,
-                               data->res.verf->committed, data->args.stable);
+                               hdr->res.verf->committed, hdr->args.stable);
                        complain = jiffies + 300 * HZ;
                }
        }
  /*
   * This function is called when the WRITE call is complete.
   */
- static void nfs_writeback_result(struct rpc_task *task, struct nfs_pgio_data *data)
+ static void nfs_writeback_result(struct rpc_task *task,
+                                struct nfs_pgio_header *hdr)
  {
-       struct nfs_pgio_args    *argp = &data->args;
-       struct nfs_pgio_res     *resp = &data->res;
+       struct nfs_pgio_args    *argp = &hdr->args;
+       struct nfs_pgio_res     *resp = &hdr->res;
  
        if (resp->count < argp->count) {
                static unsigned long    complain;
  
                /* This a short write! */
-               nfs_inc_stats(data->header->inode, NFSIOS_SHORTWRITE);
+               nfs_inc_stats(hdr->inode, NFSIOS_SHORTWRITE);
  
                /* Has the server at least made some progress? */
                if (resp->count == 0) {
                                       argp->count);
                                complain = jiffies + 300 * HZ;
                        }
-                       nfs_set_pgio_error(data->header, -EIO, argp->offset);
+                       nfs_set_pgio_error(hdr, -EIO, argp->offset);
                        task->tk_status = -EIO;
                        return;
                }
                /* Was this an NFSv2 write or an NFSv3 stable write? */
                if (resp->verf->committed != NFS_UNSTABLE) {
                        /* Resend from where the server left off */
-                       data->mds_offset += resp->count;
+                       hdr->mds_offset += resp->count;
                        argp->offset += resp->count;
                        argp->pgbase += resp->count;
                        argp->count -= resp->count;
@@@ -1703,7 -1711,7 +1711,7 @@@ int nfs_commit_inode(struct inode *inod
                        return error;
                if (!may_wait)
                        goto out_mark_dirty;
 -              error = wait_on_bit(&NFS_I(inode)->flags,
 +              error = wait_on_bit_action(&NFS_I(inode)->flags,
                                NFS_INO_COMMIT,
                                nfs_wait_bit_killable,
                                TASK_KILLABLE);
@@@ -1884,7 -1892,7 +1892,7 @@@ int nfs_migrate_page(struct address_spa
  int __init nfs_init_writepagecache(void)
  {
        nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
-                                            sizeof(struct nfs_rw_header),
+                                            sizeof(struct nfs_pgio_header),
                                             0, SLAB_HWCACHE_ALIGN,
                                             NULL);
        if (nfs_wdata_cachep == NULL)
diff --combined net/sunrpc/xprt.c
index 51c63165073c08044bf94b081e3cd1f698de4e82,c5b2916298b07106e9fc0da8162cd4cd18fe1281..56e4e150e80ee8931e4f15e0fe4f5d9527f07b1f
@@@ -744,6 -744,7 +744,7 @@@ static void xprt_connect_status(struct 
        case -ECONNABORTED:
        case -ENETUNREACH:
        case -EHOSTUNREACH:
+       case -EPIPE:
        case -EAGAIN:
                dprintk("RPC: %5u xprt_connect_status: retrying\n", task->tk_pid);
                break;
@@@ -1306,7 -1307,7 +1307,7 @@@ struct rpc_xprt *xprt_create_transport(
                }
        }
        spin_unlock(&xprt_list_lock);
 -      printk(KERN_ERR "RPC: transport (%d) not supported\n", args->ident);
 +      dprintk("RPC: transport (%d) not supported\n", args->ident);
        return ERR_PTR(-EIO);
  
  found: