Merge branch 'master' into devel and apply fixup from Stephen Rothwell:
authorStephen Rothwell <sfr@canb.auug.org.au>
Mon, 25 Jul 2011 17:59:46 +0000 (13:59 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Mon, 25 Jul 2011 18:53:52 +0000 (14:53 -0400)
vfs/nfs: fixup for nfs_open_context change

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
1  2 
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/pagelist.c
fs/nfs/read.c
fs/nfs/write.c
net/sunrpc/clnt.c

diff --combined fs/nfs/nfs4_fs.h
@@@ -48,7 -48,6 +48,7 @@@ enum nfs4_client_state 
        NFS4CLNT_SESSION_RESET,
        NFS4CLNT_RECALL_SLOT,
        NFS4CLNT_LEASE_CONFIRM,
 +      NFS4CLNT_SERVER_SCOPE_MISMATCH,
  };
  
  enum nfs4_session_state {
@@@ -67,8 -66,6 +67,8 @@@ struct nfs4_minor_version_ops 
                        int cache_reply);
        int     (*validate_stateid)(struct nfs_delegation *,
                        const nfs4_stateid *);
 +      int     (*find_root_sec)(struct nfs_server *, struct nfs_fh *,
 +                      struct nfs_fsinfo *);
        const struct nfs4_state_recovery_ops *reboot_recovery_ops;
        const struct nfs4_state_recovery_ops *nograce_recovery_ops;
        const struct nfs4_state_maintenance_ops *state_renewal_ops;
@@@ -241,7 -238,7 +241,7 @@@ extern int nfs4_proc_async_renew(struc
  extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *);
  extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *);
  extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *);
- extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc);
+ extern int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc);
  extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle);
  extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
                struct nfs4_fs_locations *fs_locations, struct page *page);
@@@ -344,16 -341,14 +344,16 @@@ extern struct nfs4_state_owner * nfs4_g
  extern void nfs4_put_state_owner(struct nfs4_state_owner *);
  extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
  extern void nfs4_put_open_state(struct nfs4_state *);
- extern void nfs4_close_state(struct path *, struct nfs4_state *, fmode_t);
- extern void nfs4_close_sync(struct path *, struct nfs4_state *, fmode_t);
+ extern void nfs4_close_state(struct nfs4_state *, fmode_t);
+ extern void nfs4_close_sync(struct nfs4_state *, fmode_t);
  extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
  extern void nfs4_schedule_lease_recovery(struct nfs_client *);
  extern void nfs4_schedule_state_manager(struct nfs_client *);
  extern void nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
  extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags);
  extern void nfs41_handle_recall_slot(struct nfs_client *clp);
 +extern void nfs41_handle_server_scope(struct nfs_client *,
 +                                    struct server_scope **);
  extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
  extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
  extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t, pid_t);
@@@ -378,8 -373,8 +378,8 @@@ extern struct svc_version nfs4_callback
  
  #else
  
- #define nfs4_close_state(a, b, c) do { } while (0)
- #define nfs4_close_sync(a, b, c) do { } while (0)
+ #define nfs4_close_state(a, b) do { } while (0)
+ #define nfs4_close_sync(a, b) do { } while (0)
  
  #endif /* CONFIG_NFS_V4 */
  #endif /* __LINUX_FS_NFS_NFS4_FS.H */
diff --combined fs/nfs/nfs4proc.c
@@@ -80,10 -80,7 +80,10 @@@ static int _nfs4_proc_getattr(struct nf
  static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
                            struct nfs4_state *state);
 -
 +#ifdef CONFIG_NFS_V4_1
 +static int nfs41_test_stateid(struct nfs_server *, struct nfs4_state *);
 +static int nfs41_free_stateid(struct nfs_server *, struct nfs4_state *);
 +#endif
  /* Prevent leaks of NFSv4 errors into userland */
  static int nfs4_map_errors(int err)
  {
@@@ -766,8 -763,8 +766,8 @@@ struct nfs4_opendata 
        struct nfs_open_confirmres c_res;
        struct nfs_fattr f_attr;
        struct nfs_fattr dir_attr;
-       struct path path;
        struct dentry *dir;
+       struct dentry *dentry;
        struct nfs4_state_owner *owner;
        struct nfs4_state *state;
        struct iattr attrs;
@@@ -789,12 -786,12 +789,12 @@@ static void nfs4_init_opendata_res(stru
        nfs_fattr_init(&p->dir_attr);
  }
  
- static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
+ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
                struct nfs4_state_owner *sp, fmode_t fmode, int flags,
                const struct iattr *attrs,
                gfp_t gfp_mask)
  {
-       struct dentry *parent = dget_parent(path->dentry);
+       struct dentry *parent = dget_parent(dentry);
        struct inode *dir = parent->d_inode;
        struct nfs_server *server = NFS_SERVER(dir);
        struct nfs4_opendata *p;
        p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
        if (p->o_arg.seqid == NULL)
                goto err_free;
-       path_get(path);
-       p->path = *path;
+       nfs_sb_active(dentry->d_sb);
+       p->dentry = dget(dentry);
        p->dir = parent;
        p->owner = sp;
        atomic_inc(&sp->so_count);
        p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
        p->o_arg.clientid = server->nfs_client->cl_clientid;
        p->o_arg.id = sp->so_owner_id.id;
-       p->o_arg.name = &p->path.dentry->d_name;
+       p->o_arg.name = &dentry->d_name;
        p->o_arg.server = server;
        p->o_arg.bitmask = server->attr_bitmask;
        p->o_arg.claim = NFS4_OPEN_CLAIM_NULL;
@@@ -845,13 -842,15 +845,15 @@@ static void nfs4_opendata_free(struct k
  {
        struct nfs4_opendata *p = container_of(kref,
                        struct nfs4_opendata, kref);
+       struct super_block *sb = p->dentry->d_sb;
  
        nfs_free_seqid(p->o_arg.seqid);
        if (p->state != NULL)
                nfs4_put_open_state(p->state);
        nfs4_put_state_owner(p->owner);
        dput(p->dir);
-       path_put(&p->path);
+       dput(p->dentry);
+       nfs_sb_deactive(sb);
        kfree(p);
  }
  
@@@ -1133,7 -1132,7 +1135,7 @@@ static struct nfs4_opendata *nfs4_open_
  {
        struct nfs4_opendata *opendata;
  
-       opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL, GFP_NOFS);
+       opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0, NULL, GFP_NOFS);
        if (opendata == NULL)
                return ERR_PTR(-ENOMEM);
        opendata->state = state;
@@@ -1157,7 -1156,7 +1159,7 @@@ static int nfs4_open_recover_helper(str
        newstate = nfs4_opendata_to_nfs4_state(opendata);
        if (IS_ERR(newstate))
                return PTR_ERR(newstate);
-       nfs4_close_state(&opendata->path, newstate, fmode);
+       nfs4_close_state(newstate, fmode);
        *res = newstate;
        return 0;
  }
@@@ -1355,7 -1354,7 +1357,7 @@@ static void nfs4_open_confirm_release(v
                goto out_free;
        state = nfs4_opendata_to_nfs4_state(data);
        if (!IS_ERR(state))
-               nfs4_close_state(&data->path, state, data->o_arg.fmode);
+               nfs4_close_state(state, data->o_arg.fmode);
  out_free:
        nfs4_opendata_put(data);
  }
@@@ -1500,7 -1499,7 +1502,7 @@@ static void nfs4_open_release(void *cal
                goto out_free;
        state = nfs4_opendata_to_nfs4_state(data);
        if (!IS_ERR(state))
-               nfs4_close_state(&data->path, state, data->o_arg.fmode);
+               nfs4_close_state(state, data->o_arg.fmode);
  out_free:
        nfs4_opendata_put(data);
  }
@@@ -1651,7 -1650,7 +1653,7 @@@ static int _nfs4_open_expired(struct nf
                return PTR_ERR(opendata);
        ret = nfs4_open_recover(opendata, state);
        if (ret == -ESTALE)
-               d_drop(ctx->path.dentry);
+               d_drop(ctx->dentry);
        nfs4_opendata_put(opendata);
        return ret;
  }
@@@ -1690,20 -1689,6 +1692,20 @@@ static int nfs4_open_expired(struct nfs
        return ret;
  }
  
 +#if defined(CONFIG_NFS_V4_1)
 +static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
 +{
 +      int status;
 +      struct nfs_server *server = NFS_SERVER(state->inode);
 +
 +      status = nfs41_test_stateid(server, state);
 +      if (status == NFS_OK)
 +              return 0;
 +      nfs41_free_stateid(server, state);
 +      return nfs4_open_expired(sp, state);
 +}
 +#endif
 +
  /*
   * on an EXCLUSIVE create, the server should send back a bitmask with FATTR4-*
   * fields corresponding to attributes that were used to store the verifier.
@@@ -1723,7 -1708,7 +1725,7 @@@ static inline void nfs4_exclusive_attrs
  /*
   * Returns a referenced nfs4_state
   */
- static int _nfs4_do_open(struct inode *dir, struct path *path, 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_state_owner  *sp;
        struct nfs4_state     *state = NULL;
        status = nfs4_recover_expired_lease(server);
        if (status != 0)
                goto err_put_state_owner;
-       if (path->dentry->d_inode != NULL)
-               nfs4_return_incompatible_delegation(path->dentry->d_inode, fmode);
+       if (dentry->d_inode != NULL)
+               nfs4_return_incompatible_delegation(dentry->d_inode, fmode);
        status = -ENOMEM;
-       opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr, GFP_KERNEL);
+       opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr, GFP_KERNEL);
        if (opendata == NULL)
                goto err_put_state_owner;
  
-       if (path->dentry->d_inode != NULL)
-               opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp);
+       if (dentry->d_inode != NULL)
+               opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
  
        status = _nfs4_proc_open(opendata);
        if (status != 0)
@@@ -1786,14 -1771,14 +1788,14 @@@ out_err
  }
  
  
- static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, 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_exception exception = { };
        struct nfs4_state *res;
        int status;
  
        do {
-               status = _nfs4_do_open(dir, path, fmode, flags, sattr, cred, &res);
+               status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, &res);
                if (status == 0)
                        break;
                /* NOTE: BAD_SEQID means the server and client disagree about the
@@@ -1890,7 -1875,6 +1892,6 @@@ static int nfs4_do_setattr(struct inod
  }
  
  struct nfs4_closedata {
-       struct path path;
        struct inode *inode;
        struct nfs4_state *state;
        struct nfs_closeargs arg;
@@@ -1905,13 -1889,14 +1906,14 @@@ static void nfs4_free_closedata(void *d
  {
        struct nfs4_closedata *calldata = data;
        struct nfs4_state_owner *sp = calldata->state->owner;
+       struct super_block *sb = calldata->state->inode->i_sb;
  
        if (calldata->roc)
                pnfs_roc_release(calldata->state->inode);
        nfs4_put_open_state(calldata->state);
        nfs_free_seqid(calldata->arg.seqid);
        nfs4_put_state_owner(sp);
-       path_put(&calldata->path);
+       nfs_sb_deactive(sb);
        kfree(calldata);
  }
  
@@@ -2031,7 -2016,7 +2033,7 @@@ static const struct rpc_call_ops nfs4_c
   *
   * NOTE: Caller must be holding the sp->so_owner semaphore!
   */
- int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc)
+ int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait, bool roc)
  {
        struct nfs_server *server = NFS_SERVER(state->inode);
        struct nfs4_closedata *calldata;
        calldata->res.seqid = calldata->arg.seqid;
        calldata->res.server = server;
        calldata->roc = roc;
-       path_get(path);
-       calldata->path = *path;
+       nfs_sb_active(calldata->inode->i_sb);
  
        msg.rpc_argp = &calldata->arg;
        msg.rpc_resp = &calldata->res;
@@@ -2097,7 -2081,7 +2098,7 @@@ nfs4_atomic_open(struct inode *dir, str
        struct nfs4_state *state;
  
        /* Protect against concurrent sillydeletes */
-       state = nfs4_do_open(dir, &ctx->path, ctx->mode, open_flags, attr, ctx->cred);
+       state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, ctx->cred);
        if (IS_ERR(state))
                return ERR_CAST(state);
        ctx->state = state;
@@@ -2109,9 -2093,9 +2110,9 @@@ static void nfs4_close_context(struct n
        if (ctx->state == NULL)
                return;
        if (is_sync)
-               nfs4_close_sync(&ctx->path, ctx->state, ctx->mode);
+               nfs4_close_sync(ctx->state, ctx->mode);
        else
-               nfs4_close_state(&ctx->path, ctx->state, ctx->mode);
+               nfs4_close_state(ctx->state, ctx->mode);
  }
  
  static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
@@@ -2268,14 -2252,13 +2269,14 @@@ static int nfs4_find_root_sec(struct nf
  static int nfs4_proc_get_root(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);
        if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR))
                /*
                 * A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM
                 * by nfs4_map_errors() as this function exits.
                 */
 -              status = nfs4_find_root_sec(server, fhandle, info);
 +              status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info);
        if (status == 0)
                status = nfs4_server_capabilities(server, fhandle);
        if (status == 0)
@@@ -2634,10 -2617,7 +2635,7 @@@ static in
  nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                   int flags, struct nfs_open_context *ctx)
  {
-       struct path my_path = {
-               .dentry = dentry,
-       };
-       struct path *path = &my_path;
+       struct dentry *de = dentry;
        struct nfs4_state *state;
        struct rpc_cred *cred = NULL;
        fmode_t fmode = 0;
  
        if (ctx != NULL) {
                cred = ctx->cred;
-               path = &ctx->path;
+               de = ctx->dentry;
                fmode = ctx->mode;
        }
        sattr->ia_mode &= ~current_umask();
-       state = nfs4_do_open(dir, path, fmode, flags, sattr, cred);
+       state = nfs4_do_open(dir, de, fmode, flags, sattr, cred);
        d_drop(dentry);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
        if (ctx != NULL)
                ctx->state = state;
        else
-               nfs4_close_sync(path, state, fmode);
+               nfs4_close_sync(state, fmode);
  out:
        return status;
  }
@@@ -4312,7 -4292,7 +4310,7 @@@ static void nfs4_lock_done(struct rpc_t
                memcpy(data->lsp->ls_stateid.data, data->res.stateid.data,
                                        sizeof(data->lsp->ls_stateid.data));
                data->lsp->ls_flags |= NFS_LOCK_INITIALIZED;
-               renew_lease(NFS_SERVER(data->ctx->path.dentry->d_inode), data->timestamp);
+               renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp);
        }
  out:
        dprintk("%s: done, ret = %d!\n", __func__, data->rpc_status);
        return err;
  }
  
 +#if defined(CONFIG_NFS_V4_1)
 +static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request)
 +{
 +      int status;
 +      struct nfs_server *server = NFS_SERVER(state->inode);
 +
 +      status = nfs41_test_stateid(server, state);
 +      if (status == NFS_OK)
 +              return 0;
 +      nfs41_free_stateid(server, state);
 +      return nfs4_lock_expired(state, request);
 +}
 +#endif
 +
  static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
  {
        struct nfs_inode *nfsi = NFS_I(state->inode);
@@@ -4813,16 -4779,6 +4811,16 @@@ out_inval
        return -NFS4ERR_INVAL;
  }
  
 +static bool
 +nfs41_same_server_scope(struct server_scope *a, struct 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 true;
 +
 +      return false;
 +}
 +
  /*
   * nfs4_proc_exchange_id()
   *
@@@ -4865,31 -4821,9 +4863,31 @@@ int nfs4_proc_exchange_id(struct nfs_cl
                                init_utsname()->domainname,
                                clp->cl_rpcclient->cl_auth->au_flavor);
  
 +      res.server_scope = kzalloc(sizeof(struct server_scope), GFP_KERNEL);
 +      if (unlikely(!res.server_scope))
 +              return -ENOMEM;
 +
        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) {
 +              if (clp->server_scope &&
 +                  !nfs41_same_server_scope(clp->server_scope,
 +                                           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;
 +              }
 +
 +              if (!clp->server_scope)
 +                      clp->server_scope = res.server_scope;
 +              else
 +                      kfree(res.server_scope);
 +      }
 +
        dprintk("<-- %s status= %d\n", __func__, status);
        return status;
  }
@@@ -5770,7 -5704,7 +5768,7 @@@ static void nfs4_layoutreturn_done(stru
  {
        struct nfs4_layoutreturn *lrp = calldata;
        struct nfs_server *server;
 -      struct pnfs_layout_hdr *lo = NFS_I(lrp->args.inode)->layout;
 +      struct pnfs_layout_hdr *lo = lrp->args.layout;
  
        dprintk("--> %s\n", __func__);
  
@@@ -5799,7 -5733,7 +5797,7 @@@ static void nfs4_layoutreturn_release(v
        struct nfs4_layoutreturn *lrp = calldata;
  
        dprintk("--> %s\n", __func__);
 -      put_layout_hdr(NFS_I(lrp->args.inode)->layout);
 +      put_layout_hdr(lrp->args.layout);
        kfree(calldata);
        dprintk("<-- %s\n", __func__);
  }
        rpc_put_task(task);
        return status;
  }
 +
 +static int
 +_nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
 +                  struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
 +{
 +      struct nfs41_secinfo_no_name_args args = {
 +              .style = SECINFO_STYLE_CURRENT_FH,
 +      };
 +      struct nfs4_secinfo_res res = {
 +              .flavors = flavors,
 +      };
 +      struct rpc_message msg = {
 +              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO_NO_NAME],
 +              .rpc_argp = &args,
 +              .rpc_resp = &res,
 +      };
 +      return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
 +}
 +
 +static int
 +nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
 +                         struct nfs_fsinfo *info, struct nfs4_secinfo_flavors *flavors)
 +{
 +      struct nfs4_exception exception = { };
 +      int err;
 +      do {
 +              err = _nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
 +              switch (err) {
 +              case 0:
 +              case -NFS4ERR_WRONGSEC:
 +              case -NFS4ERR_NOTSUPP:
 +                      break;
 +              default:
 +                      err = nfs4_handle_exception(server, err, &exception);
 +              }
 +      } while (exception.retry);
 +      return err;
 +}
 +
 +static int
 +nfs41_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
 +                  struct nfs_fsinfo *info)
 +{
 +      int err;
 +      struct page *page;
 +      rpc_authflavor_t flavor;
 +      struct nfs4_secinfo_flavors *flavors;
 +
 +      page = alloc_page(GFP_KERNEL);
 +      if (!page) {
 +              err = -ENOMEM;
 +              goto out;
 +      }
 +
 +      flavors = page_address(page);
 +      err = nfs41_proc_secinfo_no_name(server, fhandle, info, flavors);
 +
 +      /*
 +       * Fall back on "guess and check" method if
 +       * the server doesn't support SECINFO_NO_NAME
 +       */
 +      if (err == -NFS4ERR_WRONGSEC || err == -NFS4ERR_NOTSUPP) {
 +              err = nfs4_find_root_sec(server, fhandle, info);
 +              goto out_freepage;
 +      }
 +      if (err)
 +              goto out_freepage;
 +
 +      flavor = nfs_find_best_sec(flavors);
 +      if (err == 0)
 +              err = nfs4_lookup_root_sec(server, fhandle, info, flavor);
 +
 +out_freepage:
 +      put_page(page);
 +      if (err == -EACCES)
 +              return -EPERM;
 +out:
 +      return err;
 +}
 +static int _nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *state)
 +{
 +      int status;
 +      struct nfs41_test_stateid_args args = {
 +              .stateid = &state->stateid,
 +      };
 +      struct nfs41_test_stateid_res res;
 +      struct rpc_message msg = {
 +              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID],
 +              .rpc_argp = &args,
 +              .rpc_resp = &res,
 +      };
 +      args.seq_args.sa_session = res.seq_res.sr_session = NULL;
 +      status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
 +      return status;
 +}
 +
 +static int nfs41_test_stateid(struct nfs_server *server, struct nfs4_state *state)
 +{
 +      struct nfs4_exception exception = { };
 +      int err;
 +      do {
 +              err = nfs4_handle_exception(server,
 +                              _nfs41_test_stateid(server, state),
 +                              &exception);
 +      } while (exception.retry);
 +      return err;
 +}
 +
 +static int _nfs4_free_stateid(struct nfs_server *server, struct nfs4_state *state)
 +{
 +      int status;
 +      struct nfs41_free_stateid_args args = {
 +              .stateid = &state->stateid,
 +      };
 +      struct nfs41_free_stateid_res res;
 +      struct rpc_message msg = {
 +              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
 +              .rpc_argp = &args,
 +              .rpc_resp = &res,
 +      };
 +
 +      args.seq_args.sa_session = res.seq_res.sr_session = NULL;
 +      status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 0, 1);
 +      return status;
 +}
 +
 +static int nfs41_free_stateid(struct nfs_server *server, struct nfs4_state *state)
 +{
 +      struct nfs4_exception exception = { };
 +      int err;
 +      do {
 +              err = nfs4_handle_exception(server,
 +                              _nfs4_free_stateid(server, state),
 +                              &exception);
 +      } while (exception.retry);
 +      return err;
 +}
  #endif /* CONFIG_NFS_V4_1 */
  
  struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
@@@ -6140,8 -5937,8 +6138,8 @@@ struct nfs4_state_recovery_ops nfs40_no
  struct nfs4_state_recovery_ops nfs41_nograce_recovery_ops = {
        .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
        .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
 -      .recover_open   = nfs4_open_expired,
 -      .recover_lock   = nfs4_lock_expired,
 +      .recover_open   = nfs41_open_expired,
 +      .recover_lock   = nfs41_lock_expired,
        .establish_clid = nfs41_init_clientid,
        .get_clid_cred  = nfs4_get_exchange_id_cred,
  };
@@@ -6165,7 -5962,6 +6163,7 @@@ static const struct nfs4_minor_version_
        .minor_version = 0,
        .call_sync = _nfs4_call_sync,
        .validate_stateid = nfs4_validate_delegation_stateid,
 +      .find_root_sec = nfs4_find_root_sec,
        .reboot_recovery_ops = &nfs40_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs40_nograce_recovery_ops,
        .state_renewal_ops = &nfs40_state_renewal_ops,
@@@ -6176,7 -5972,6 +6174,7 @@@ static const struct nfs4_minor_version_
        .minor_version = 1,
        .call_sync = _nfs4_call_sync_session,
        .validate_stateid = nfs41_validate_delegation_stateid,
 +      .find_root_sec = nfs41_find_root_sec,
        .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
        .state_renewal_ops = &nfs41_state_renewal_ops,
diff --combined fs/nfs/nfs4state.c
@@@ -641,7 -641,7 +641,7 @@@ void nfs4_put_open_state(struct nfs4_st
  /*
   * Close the current file.
   */
- static void __nfs4_close(struct path *path, struct nfs4_state *state,
+ static void __nfs4_close(struct nfs4_state *state,
                fmode_t fmode, gfp_t gfp_mask, int wait)
  {
        struct nfs4_state_owner *owner = state->owner;
        } else {
                bool roc = pnfs_roc(state->inode);
  
-               nfs4_do_close(path, state, gfp_mask, wait, roc);
+               nfs4_do_close(state, gfp_mask, wait, roc);
        }
  }
  
- void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode)
+ void nfs4_close_state(struct nfs4_state *state, fmode_t fmode)
  {
-       __nfs4_close(path, state, fmode, GFP_NOFS, 0);
+       __nfs4_close(state, fmode, GFP_NOFS, 0);
  }
  
- void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode)
+ void nfs4_close_sync(struct nfs4_state *state, fmode_t fmode)
  {
-       __nfs4_close(path, state, fmode, GFP_KERNEL, 1);
+       __nfs4_close(state, fmode, GFP_KERNEL, 1);
  }
  
  /*
@@@ -1643,14 -1643,7 +1643,14 @@@ static void nfs4_state_manager(struct n
                                goto out_error;
                        }
                        clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
 -                      set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
 +
 +                      if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH,
 +                                             &clp->cl_state))
 +                              nfs4_state_start_reclaim_nograce(clp);
 +                      else
 +                              set_bit(NFS4CLNT_RECLAIM_REBOOT,
 +                                      &clp->cl_state);
 +
                        pnfs_destroy_all_layouts(clp);
                }
  
diff --combined fs/nfs/pagelist.c
@@@ -114,7 -114,7 +114,7 @@@ int nfs_set_page_tag_locked(struct nfs_
        if (!nfs_lock_request_dontget(req))
                return 0;
        if (test_bit(PG_MAPPED, &req->wb_flags))
-               radix_tree_tag_set(&NFS_I(req->wb_context->path.dentry->d_inode)->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
+               radix_tree_tag_set(&NFS_I(req->wb_context->dentry->d_inode)->nfs_page_tree, req->wb_index, NFS_PAGE_TAG_LOCKED);
        return 1;
  }
  
  void nfs_clear_page_tag_locked(struct nfs_page *req)
  {
        if (test_bit(PG_MAPPED, &req->wb_flags)) {
-               struct inode *inode = req->wb_context->path.dentry->d_inode;
+               struct inode *inode = req->wb_context->dentry->d_inode;
                struct nfs_inode *nfsi = NFS_I(inode);
  
                spin_lock(&inode->i_lock);
@@@ -230,7 -230,7 +230,7 @@@ EXPORT_SYMBOL_GPL(nfs_generic_pg_test)
   */
  void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
                     struct inode *inode,
 -                   int (*doio)(struct nfs_pageio_descriptor *),
 +                   const struct nfs_pageio_ops *pg_ops,
                     size_t bsize,
                     int io_flags)
  {
        desc->pg_bsize = bsize;
        desc->pg_base = 0;
        desc->pg_moreio = 0;
 +      desc->pg_recoalesce = 0;
        desc->pg_inode = inode;
 -      desc->pg_doio = doio;
 +      desc->pg_ops = pg_ops;
        desc->pg_ioflags = io_flags;
        desc->pg_error = 0;
        desc->pg_lseg = NULL;
 -      desc->pg_test = nfs_generic_pg_test;
 -      pnfs_pageio_init(desc, inode);
  }
  
  /**
@@@ -275,7 -276,7 +275,7 @@@ static bool nfs_can_coalesce_requests(s
                return false;
        if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
                return false;
 -      return pgio->pg_test(pgio, prev, req);
 +      return pgio->pg_ops->pg_test(pgio, prev, req);
  }
  
  /**
@@@ -296,8 -297,6 +296,8 @@@ static int nfs_pageio_do_add_request(st
                if (!nfs_can_coalesce_requests(prev, req, desc))
                        return 0;
        } else {
 +              if (desc->pg_ops->pg_init)
 +                      desc->pg_ops->pg_init(desc, req);
                desc->pg_base = req->wb_pgbase;
        }
        nfs_list_remove_request(req);
  static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
  {
        if (!list_empty(&desc->pg_list)) {
 -              int error = desc->pg_doio(desc);
 +              int error = desc->pg_ops->pg_doio(desc);
                if (error < 0)
                        desc->pg_error = error;
                else
   * Returns true if the request 'req' was successfully coalesced into the
   * existing list of pages 'desc'.
   */
 -int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
 +static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
                           struct nfs_page *req)
  {
        while (!nfs_pageio_do_add_request(desc, req)) {
                if (desc->pg_error < 0)
                        return 0;
                desc->pg_moreio = 0;
 +              if (desc->pg_recoalesce)
 +                      return 0;
        }
        return 1;
  }
  
 +static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
 +{
 +      LIST_HEAD(head);
 +
 +      do {
 +              list_splice_init(&desc->pg_list, &head);
 +              desc->pg_bytes_written -= desc->pg_count;
 +              desc->pg_count = 0;
 +              desc->pg_base = 0;
 +              desc->pg_recoalesce = 0;
 +
 +              while (!list_empty(&head)) {
 +                      struct nfs_page *req;
 +
 +                      req = list_first_entry(&head, struct nfs_page, wb_list);
 +                      nfs_list_remove_request(req);
 +                      if (__nfs_pageio_add_request(desc, req))
 +                              continue;
 +                      if (desc->pg_error < 0)
 +                              return 0;
 +                      break;
 +              }
 +      } while (desc->pg_recoalesce);
 +      return 1;
 +}
 +
 +int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
 +              struct nfs_page *req)
 +{
 +      int ret;
 +
 +      do {
 +              ret = __nfs_pageio_add_request(desc, req);
 +              if (ret)
 +                      break;
 +              if (desc->pg_error < 0)
 +                      break;
 +              ret = nfs_do_recoalesce(desc);
 +      } while (ret);
 +      return ret;
 +}
 +
  /**
   * nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor
   * @desc: pointer to io descriptor
   */
  void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
  {
 -      nfs_pageio_doio(desc);
 +      for (;;) {
 +              nfs_pageio_doio(desc);
 +              if (!desc->pg_recoalesce)
 +                      break;
 +              if (!nfs_do_recoalesce(desc))
 +                      break;
 +      }
  }
  
  /**
@@@ -420,7 -369,7 +420,7 @@@ void nfs_pageio_cond_complete(struct nf
        if (!list_empty(&desc->pg_list)) {
                struct nfs_page *prev = nfs_list_entry(desc->pg_list.prev);
                if (index != prev->wb_index + 1)
 -                      nfs_pageio_doio(desc);
 +                      nfs_pageio_complete(desc);
        }
  }
  
diff --combined fs/nfs/read.c
@@@ -30,7 -30,8 +30,7 @@@
  
  #define NFSDBG_FACILITY               NFSDBG_PAGECACHE
  
 -static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc);
 -static int nfs_pagein_one(struct nfs_pageio_descriptor *desc);
 +static const struct nfs_pageio_ops nfs_pageio_read_ops;
  static const struct rpc_call_ops nfs_read_partial_ops;
  static const struct rpc_call_ops nfs_read_full_ops;
  
@@@ -67,7 -68,7 +67,7 @@@ void nfs_readdata_free(struct nfs_read_
        mempool_free(p, nfs_rdata_mempool);
  }
  
 -static void nfs_readdata_release(struct nfs_read_data *rdata)
 +void nfs_readdata_release(struct nfs_read_data *rdata)
  {
        put_lseg(rdata->lseg);
        put_nfs_open_context(rdata->args.context);
@@@ -112,27 -113,6 +112,27 @@@ static void nfs_readpage_truncate_unini
        }
  }
  
 +static void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio,
 +              struct inode *inode)
 +{
 +      nfs_pageio_init(pgio, inode, &nfs_pageio_read_ops,
 +                      NFS_SERVER(inode)->rsize, 0);
 +}
 +
 +void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
 +{
 +      pgio->pg_ops = &nfs_pageio_read_ops;
 +      pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->rsize;
 +}
 +EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
 +
 +static void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
 +              struct inode *inode)
 +{
 +      if (!pnfs_pageio_init_read(pgio, inode))
 +              nfs_pageio_init_read_mds(pgio, inode);
 +}
 +
  int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
                       struct page *page)
  {
        if (len < PAGE_CACHE_SIZE)
                zero_user_segment(page, len, PAGE_CACHE_SIZE);
  
 -      nfs_pageio_init(&pgio, inode, NULL, 0, 0);
 -      nfs_list_add_request(new, &pgio.pg_list);
 -      pgio.pg_count = len;
 -
 -      if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
 -              nfs_pagein_multi(&pgio);
 -      else
 -              nfs_pagein_one(&pgio);
 +      nfs_pageio_init_read(&pgio, inode);
 +      nfs_pageio_add_request(&pgio, new);
 +      nfs_pageio_complete(&pgio);
        return 0;
  }
  
  static void nfs_readpage_release(struct nfs_page *req)
  {
-       struct inode *d_inode = req->wb_context->path.dentry->d_inode;
+       struct inode *d_inode = req->wb_context->dentry->d_inode;
  
        if (PageUptodate(req->wb_page))
                nfs_readpage_to_fscache(d_inode, req->wb_page, 0);
        unlock_page(req->wb_page);
  
        dprintk("NFS: read done (%s/%Ld %d@%Ld)\n",
-                       req->wb_context->path.dentry->d_inode->i_sb->s_id,
-                       (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
+                       req->wb_context->dentry->d_inode->i_sb->s_id,
+                       (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
                        req->wb_bytes,
                        (long long)req_offset(req));
        nfs_release_request(req);
@@@ -217,14 -202,17 +217,14 @@@ EXPORT_SYMBOL_GPL(nfs_initiate_read)
  /*
   * Set up the NFS read request struct
   */
 -static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
 -              const struct rpc_call_ops *call_ops,
 -              unsigned int count, unsigned int offset,
 -              struct pnfs_layout_segment *lseg)
 +static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
 +              unsigned int count, unsigned int offset)
  {
-       struct inode *inode = req->wb_context->path.dentry->d_inode;
+       struct inode *inode = req->wb_context->dentry->d_inode;
  
        data->req         = req;
        data->inode       = inode;
        data->cred        = req->wb_context->cred;
 -      data->lseg        = get_lseg(lseg);
  
        data->args.fh     = NFS_FH(inode);
        data->args.offset = req_offset(req) + offset;
        data->res.count   = count;
        data->res.eof     = 0;
        nfs_fattr_init(&data->fattr);
 +}
  
 -      if (data->lseg &&
 -          (pnfs_try_to_read_data(data, call_ops) == PNFS_ATTEMPTED))
 -              return 0;
 +static int nfs_do_read(struct nfs_read_data *data,
 +              const struct rpc_call_ops *call_ops)
 +{
-       struct inode *inode = data->args.context->path.dentry->d_inode;
++      struct inode *inode = data->args.context->dentry->d_inode;
  
        return nfs_initiate_read(data, NFS_CLIENT(inode), call_ops);
  }
  
 +static int
 +nfs_do_multiple_reads(struct list_head *head,
 +              const struct rpc_call_ops *call_ops)
 +{
 +      struct nfs_read_data *data;
 +      int ret = 0;
 +
 +      while (!list_empty(head)) {
 +              int ret2;
 +
 +              data = list_entry(head->next, struct nfs_read_data, list);
 +              list_del_init(&data->list);
 +
 +              ret2 = nfs_do_read(data, call_ops);
 +              if (ret == 0)
 +                      ret = ret2;
 +      }
 +      return ret;
 +}
 +
  static void
  nfs_async_read_error(struct list_head *head)
  {
   * won't see the new data until our attribute cache is updated.  This is more
   * or less conventional NFS client behavior.
   */
 -static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc)
 +static int nfs_pagein_multi(struct nfs_pageio_descriptor *desc, struct list_head *res)
  {
        struct nfs_page *req = nfs_list_entry(desc->pg_list.next);
        struct page *page = req->wb_page;
        struct nfs_read_data *data;
 -      size_t rsize = NFS_SERVER(desc->pg_inode)->rsize, nbytes;
 +      size_t rsize = desc->pg_bsize, nbytes;
        unsigned int offset;
        int requests = 0;
        int ret = 0;
 -      struct pnfs_layout_segment *lseg;
 -      LIST_HEAD(list);
  
        nfs_list_remove_request(req);
  
 +      offset = 0;
        nbytes = desc->pg_count;
        do {
                size_t len = min(nbytes,rsize);
                data = nfs_readdata_alloc(1);
                if (!data)
                        goto out_bad;
 -              list_add(&data->pages, &list);
 +              data->pagevec[0] = page;
 +              nfs_read_rpcsetup(req, data, len, offset);
 +              list_add(&data->list, res);
                requests++;
                nbytes -= len;
 +              offset += len;
        } while(nbytes != 0);
        atomic_set(&req->wb_complete, requests);
 -
 -      BUG_ON(desc->pg_lseg != NULL);
 -      lseg = pnfs_update_layout(desc->pg_inode, req->wb_context,
 -                                req_offset(req), desc->pg_count,
 -                                IOMODE_READ, GFP_KERNEL);
        ClearPageError(page);
 -      offset = 0;
 -      nbytes = desc->pg_count;
 -      do {
 -              int ret2;
 -
 -              data = list_entry(list.next, struct nfs_read_data, pages);
 -              list_del_init(&data->pages);
 -
 -              data->pagevec[0] = page;
 -
 -              if (nbytes < rsize)
 -                      rsize = nbytes;
 -              ret2 = nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
 -                                       rsize, offset, lseg);
 -              if (ret == 0)
 -                      ret = ret2;
 -              offset += rsize;
 -              nbytes -= rsize;
 -      } while (nbytes != 0);
 -      put_lseg(lseg);
 -      desc->pg_lseg = NULL;
 -
 +      desc->pg_rpc_callops = &nfs_read_partial_ops;
        return ret;
 -
  out_bad:
 -      while (!list_empty(&list)) {
 -              data = list_entry(list.next, struct nfs_read_data, pages);
 -              list_del(&data->pages);
 +      while (!list_empty(res)) {
 +              data = list_entry(res->next, struct nfs_read_data, list);
 +              list_del(&data->list);
                nfs_readdata_free(data);
        }
        SetPageError(page);
        return -ENOMEM;
  }
  
 -static int nfs_pagein_one(struct nfs_pageio_descriptor *desc)
 +static int nfs_pagein_one(struct nfs_pageio_descriptor *desc, struct list_head *res)
  {
        struct nfs_page         *req;
        struct page             **pages;
        struct nfs_read_data    *data;
        struct list_head *head = &desc->pg_list;
 -      struct pnfs_layout_segment *lseg = desc->pg_lseg;
 -      int ret = -ENOMEM;
 +      int ret = 0;
  
        data = nfs_readdata_alloc(nfs_page_array_len(desc->pg_base,
                                                     desc->pg_count));
        if (!data) {
                nfs_async_read_error(head);
 +              ret = -ENOMEM;
                goto out;
        }
  
                *pages++ = req->wb_page;
        }
        req = nfs_list_entry(data->pages.next);
 -      if ((!lseg) && list_is_singular(&data->pages))
 -              lseg = pnfs_update_layout(desc->pg_inode, req->wb_context,
 -                                        req_offset(req), desc->pg_count,
 -                                        IOMODE_READ, GFP_KERNEL);
  
 -      ret = nfs_read_rpcsetup(req, data, &nfs_read_full_ops, desc->pg_count,
 -                              0, lseg);
 +      nfs_read_rpcsetup(req, data, desc->pg_count, 0);
 +      list_add(&data->list, res);
 +      desc->pg_rpc_callops = &nfs_read_full_ops;
  out:
 -      put_lseg(lseg);
 -      desc->pg_lseg = NULL;
        return ret;
  }
  
 +int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, struct list_head *head)
 +{
 +      if (desc->pg_bsize < PAGE_CACHE_SIZE)
 +              return nfs_pagein_multi(desc, head);
 +      return nfs_pagein_one(desc, head);
 +}
 +
 +static int nfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc)
 +{
 +      LIST_HEAD(head);
 +      int ret;
 +
 +      ret = nfs_generic_pagein(desc, &head);
 +      if (ret == 0)
 +              ret = nfs_do_multiple_reads(&head, desc->pg_rpc_callops);
 +      return ret;
 +}
 +
 +static const struct nfs_pageio_ops nfs_pageio_read_ops = {
 +      .pg_test = nfs_generic_pg_test,
 +      .pg_doio = nfs_generic_pg_readpages,
 +};
 +
  /*
   * This is the callback from RPC telling us whether a reply was
   * received or some error occurred (timeout or socket shutdown).
@@@ -662,6 -635,8 +662,6 @@@ int nfs_readpages(struct file *filp, st
                .pgio = &pgio,
        };
        struct inode *inode = mapping->host;
 -      struct nfs_server *server = NFS_SERVER(inode);
 -      size_t rsize = server->rsize;
        unsigned long npages;
        int ret = -ESTALE;
  
        if (ret == 0)
                goto read_complete; /* all pages were read */
  
 -      if (rsize < PAGE_CACHE_SIZE)
 -              nfs_pageio_init(&pgio, inode, nfs_pagein_multi, rsize, 0);
 -      else
 -              nfs_pageio_init(&pgio, inode, nfs_pagein_one, rsize, 0);
 +      nfs_pageio_init_read(&pgio, inode);
  
        ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
  
diff --combined fs/nfs/write.c
@@@ -97,7 -97,7 +97,7 @@@ void nfs_writedata_free(struct nfs_writ
        mempool_free(p, nfs_wdata_mempool);
  }
  
 -static void nfs_writedata_release(struct nfs_write_data *wdata)
 +void nfs_writedata_release(struct nfs_write_data *wdata)
  {
        put_lseg(wdata->lseg);
        put_nfs_open_context(wdata->args.context);
@@@ -409,7 -409,7 +409,7 @@@ out
   */
  static void nfs_inode_remove_request(struct nfs_page *req)
  {
-       struct inode *inode = req->wb_context->path.dentry->d_inode;
+       struct inode *inode = req->wb_context->dentry->d_inode;
        struct nfs_inode *nfsi = NFS_I(inode);
  
        BUG_ON (!NFS_WBACK_BUSY(req));
@@@ -438,7 -438,7 +438,7 @@@ nfs_mark_request_dirty(struct nfs_page 
  static void
  nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
  {
-       struct inode *inode = req->wb_context->path.dentry->d_inode;
+       struct inode *inode = req->wb_context->dentry->d_inode;
        struct nfs_inode *nfsi = NFS_I(inode);
  
        spin_lock(&inode->i_lock);
@@@ -845,19 -845,22 +845,19 @@@ EXPORT_SYMBOL_GPL(nfs_initiate_write)
  /*
   * Set up the argument/result storage required for the RPC call.
   */
 -static int nfs_write_rpcsetup(struct nfs_page *req,
 +static void nfs_write_rpcsetup(struct nfs_page *req,
                struct nfs_write_data *data,
 -              const struct rpc_call_ops *call_ops,
                unsigned int count, unsigned int offset,
 -              struct pnfs_layout_segment *lseg,
                int how)
  {
-       struct inode *inode = req->wb_context->path.dentry->d_inode;
+       struct inode *inode = req->wb_context->dentry->d_inode;
  
        /* Set up the RPC argument and reply structs
         * NB: take care not to mess about with data->commit et al. */
  
        data->req = req;
-       data->inode = inode = req->wb_context->path.dentry->d_inode;
+       data->inode = inode = req->wb_context->dentry->d_inode;
        data->cred = req->wb_context->cred;
 -      data->lseg = get_lseg(lseg);
  
        data->args.fh     = NFS_FH(inode);
        data->args.offset = req_offset(req) + offset;
        data->args.context = get_nfs_open_context(req->wb_context);
        data->args.lock_context = req->wb_lock_context;
        data->args.stable  = NFS_UNSTABLE;
 -      if (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
 -              data->args.stable = NFS_DATA_SYNC;
 -              if (!nfs_need_commit(NFS_I(inode)))
 -                      data->args.stable = NFS_FILE_SYNC;
 +      switch (how & (FLUSH_STABLE | FLUSH_COND_STABLE)) {
 +      case 0:
 +              break;
 +      case FLUSH_COND_STABLE:
 +              if (nfs_need_commit(NFS_I(inode)))
 +                      break;
 +      default:
 +              data->args.stable = NFS_FILE_SYNC;
        }
  
        data->res.fattr   = &data->fattr;
        data->res.count   = count;
        data->res.verf    = &data->verf;
        nfs_fattr_init(&data->fattr);
 +}
  
 -      if (data->lseg &&
 -          (pnfs_try_to_write_data(data, call_ops, how) == PNFS_ATTEMPTED))
 -              return 0;
 +static int nfs_do_write(struct nfs_write_data *data,
 +              const struct rpc_call_ops *call_ops,
 +              int how)
 +{
-       struct inode *inode = data->args.context->path.dentry->d_inode;
++      struct inode *inode = data->args.context->dentry->d_inode;
  
        return nfs_initiate_write(data, NFS_CLIENT(inode), call_ops, how);
  }
  
 +static int nfs_do_multiple_writes(struct list_head *head,
 +              const struct rpc_call_ops *call_ops,
 +              int how)
 +{
 +      struct nfs_write_data *data;
 +      int ret = 0;
 +
 +      while (!list_empty(head)) {
 +              int ret2;
 +
 +              data = list_entry(head->next, struct nfs_write_data, list);
 +              list_del_init(&data->list);
 +              
 +              ret2 = nfs_do_write(data, call_ops, how);
 +               if (ret == 0)
 +                       ret = ret2;
 +      }
 +      return ret;
 +}
 +
  /* If a nfs_flush_* function fails, it should remove reqs from @head and
   * call this on each, which will prepare them to be retried on next
   * writeback using standard nfs.
@@@ -931,15 -907,17 +931,15 @@@ static void nfs_redirty_request(struct 
   * Generate multiple small requests to write out a single
   * contiguous dirty area on one page.
   */
 -static int nfs_flush_multi(struct nfs_pageio_descriptor *desc)
 +static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head *res)
  {
        struct nfs_page *req = nfs_list_entry(desc->pg_list.next);
        struct page *page = req->wb_page;
        struct nfs_write_data *data;
 -      size_t wsize = NFS_SERVER(desc->pg_inode)->wsize, nbytes;
 +      size_t wsize = desc->pg_bsize, nbytes;
        unsigned int offset;
        int requests = 0;
        int ret = 0;
 -      struct pnfs_layout_segment *lseg;
 -      LIST_HEAD(list);
  
        nfs_list_remove_request(req);
  
                desc->pg_ioflags &= ~FLUSH_COND_STABLE;
  
  
 +      offset = 0;
        nbytes = desc->pg_count;
        do {
                size_t len = min(nbytes, wsize);
                data = nfs_writedata_alloc(1);
                if (!data)
                        goto out_bad;
 -              list_add(&data->pages, &list);
 +              data->pagevec[0] = page;
 +              nfs_write_rpcsetup(req, data, wsize, offset, desc->pg_ioflags);
 +              list_add(&data->list, res);
                requests++;
                nbytes -= len;
 +              offset += len;
        } while (nbytes != 0);
        atomic_set(&req->wb_complete, requests);
 -
 -      BUG_ON(desc->pg_lseg);
 -      lseg = pnfs_update_layout(desc->pg_inode, req->wb_context,
 -                                req_offset(req), desc->pg_count,
 -                                IOMODE_RW, GFP_NOFS);
 -      ClearPageError(page);
 -      offset = 0;
 -      nbytes = desc->pg_count;
 -      do {
 -              int ret2;
 -
 -              data = list_entry(list.next, struct nfs_write_data, pages);
 -              list_del_init(&data->pages);
 -
 -              data->pagevec[0] = page;
 -
 -              if (nbytes < wsize)
 -                      wsize = nbytes;
 -              ret2 = nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
 -                                        wsize, offset, lseg, desc->pg_ioflags);
 -              if (ret == 0)
 -                      ret = ret2;
 -              offset += wsize;
 -              nbytes -= wsize;
 -      } while (nbytes != 0);
 -
 -      put_lseg(lseg);
 -      desc->pg_lseg = NULL;
 +      desc->pg_rpc_callops = &nfs_write_partial_ops;
        return ret;
  
  out_bad:
 -      while (!list_empty(&list)) {
 -              data = list_entry(list.next, struct nfs_write_data, pages);
 -              list_del(&data->pages);
 +      while (!list_empty(res)) {
 +              data = list_entry(res->next, struct nfs_write_data, list);
 +              list_del(&data->list);
                nfs_writedata_free(data);
        }
        nfs_redirty_request(req);
   * This is the case if nfs_updatepage detects a conflicting request
   * that has been written but not committed.
   */
 -static int nfs_flush_one(struct nfs_pageio_descriptor *desc)
 +static int nfs_flush_one(struct nfs_pageio_descriptor *desc, struct list_head *res)
  {
        struct nfs_page         *req;
        struct page             **pages;
        struct nfs_write_data   *data;
        struct list_head *head = &desc->pg_list;
 -      struct pnfs_layout_segment *lseg = desc->pg_lseg;
 -      int ret;
 +      int ret = 0;
  
        data = nfs_writedata_alloc(nfs_page_array_len(desc->pg_base,
                                                      desc->pg_count));
                *pages++ = req->wb_page;
        }
        req = nfs_list_entry(data->pages.next);
 -      if ((!lseg) && list_is_singular(&data->pages))
 -              lseg = pnfs_update_layout(desc->pg_inode, req->wb_context,
 -                                        req_offset(req), desc->pg_count,
 -                                        IOMODE_RW, GFP_NOFS);
  
        if ((desc->pg_ioflags & FLUSH_COND_STABLE) &&
            (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit))
                desc->pg_ioflags &= ~FLUSH_COND_STABLE;
  
        /* Set up the argument struct */
 -      ret = nfs_write_rpcsetup(req, data, &nfs_write_full_ops, desc->pg_count, 0, lseg, desc->pg_ioflags);
 +      nfs_write_rpcsetup(req, data, desc->pg_count, 0, desc->pg_ioflags);
 +      list_add(&data->list, res);
 +      desc->pg_rpc_callops = &nfs_write_full_ops;
  out:
 -      put_lseg(lseg); /* Cleans any gotten in ->pg_test */
 -      desc->pg_lseg = NULL;
        return ret;
  }
  
 -static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
 +int nfs_generic_flush(struct nfs_pageio_descriptor *desc, struct list_head *head)
 +{
 +      if (desc->pg_bsize < PAGE_CACHE_SIZE)
 +              return nfs_flush_multi(desc, head);
 +      return nfs_flush_one(desc, head);
 +}
 +
 +static int nfs_generic_pg_writepages(struct nfs_pageio_descriptor *desc)
 +{
 +      LIST_HEAD(head);
 +      int ret;
 +
 +      ret = nfs_generic_flush(desc, &head);
 +      if (ret == 0)
 +              ret = nfs_do_multiple_writes(&head, desc->pg_rpc_callops,
 +                              desc->pg_ioflags);
 +      return ret;
 +}
 +
 +static const struct nfs_pageio_ops nfs_pageio_write_ops = {
 +      .pg_test = nfs_generic_pg_test,
 +      .pg_doio = nfs_generic_pg_writepages,
 +};
 +
 +static void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio,
                                  struct inode *inode, int ioflags)
  {
 -      size_t wsize = NFS_SERVER(inode)->wsize;
 +      nfs_pageio_init(pgio, inode, &nfs_pageio_write_ops,
 +                              NFS_SERVER(inode)->wsize, ioflags);
 +}
  
 -      if (wsize < PAGE_CACHE_SIZE)
 -              nfs_pageio_init(pgio, inode, nfs_flush_multi, wsize, ioflags);
 -      else
 -              nfs_pageio_init(pgio, inode, nfs_flush_one, wsize, ioflags);
 +void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
 +{
 +      pgio->pg_ops = &nfs_pageio_write_ops;
 +      pgio->pg_bsize = NFS_SERVER(pgio->pg_inode)->wsize;
 +}
 +EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
 +
 +static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
 +                                struct inode *inode, int ioflags)
 +{
 +      if (!pnfs_pageio_init_write(pgio, inode, ioflags))
 +              nfs_pageio_init_write_mds(pgio, inode, ioflags);
  }
  
  /*
@@@ -1081,9 -1053,9 +1081,9 @@@ static void nfs_writeback_done_partial(
  
        dprintk("NFS: %5u write(%s/%lld %d@%lld)",
                task->tk_pid,
-               data->req->wb_context->path.dentry->d_inode->i_sb->s_id,
+               data->req->wb_context->dentry->d_inode->i_sb->s_id,
                (long long)
-                 NFS_FILEID(data->req->wb_context->path.dentry->d_inode),
+                 NFS_FILEID(data->req->wb_context->dentry->d_inode),
                data->req->wb_bytes, (long long)req_offset(data->req));
  
        nfs_writeback_done(task, data);
@@@ -1176,8 -1148,8 +1176,8 @@@ static void nfs_writeback_release_full(
  
                dprintk("NFS: %5u write (%s/%lld %d@%lld)",
                        data->task.tk_pid,
-                       req->wb_context->path.dentry->d_inode->i_sb->s_id,
-                       (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
+                       req->wb_context->dentry->d_inode->i_sb->s_id,
+                       (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
                        req->wb_bytes,
                        (long long)req_offset(req));
  
@@@ -1375,7 -1347,7 +1375,7 @@@ void nfs_init_commit(struct nfs_write_d
                            struct pnfs_layout_segment *lseg)
  {
        struct nfs_page *first = nfs_list_entry(head->next);
-       struct inode *inode = first->wb_context->path.dentry->d_inode;
+       struct inode *inode = first->wb_context->dentry->d_inode;
  
        /* Set up the RPC argument and reply structs
         * NB: take care not to mess about with data->commit et al. */
@@@ -1463,8 -1435,8 +1463,8 @@@ void nfs_commit_release_pages(struct nf
                nfs_clear_request_commit(req);
  
                dprintk("NFS:       commit (%s/%lld %d@%lld)",
-                       req->wb_context->path.dentry->d_inode->i_sb->s_id,
-                       (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
+                       req->wb_context->dentry->d_sb->s_id,
+                       (long long)NFS_FILEID(req->wb_context->dentry->d_inode),
                        req->wb_bytes,
                        (long long)req_offset(req));
                if (status < 0) {
diff --combined net/sunrpc/clnt.c
@@@ -64,9 -64,9 +64,9 @@@ static void   call_decode(struct rpc_tas
  static void   call_bind(struct rpc_task *task);
  static void   call_bind_status(struct rpc_task *task);
  static void   call_transmit(struct rpc_task *task);
 -#if defined(CONFIG_NFS_V4_1)
 +#if defined(CONFIG_SUNRPC_BACKCHANNEL)
  static void   call_bc_transmit(struct rpc_task *task);
 -#endif /* CONFIG_NFS_V4_1 */
 +#endif /* CONFIG_SUNRPC_BACKCHANNEL */
  static void   call_status(struct rpc_task *task);
  static void   call_transmit_status(struct rpc_task *task);
  static void   call_refresh(struct rpc_task *task);
@@@ -97,8 -97,7 +97,7 @@@ static in
  rpc_setup_pipedir(struct rpc_clnt *clnt, char *dir_name)
  {
        static uint32_t clntid;
-       struct nameidata nd;
-       struct path path;
+       struct path path, dir;
        char name[15];
        struct qstr q = {
                .name = name,
        path.mnt = rpc_get_mount();
        if (IS_ERR(path.mnt))
                return PTR_ERR(path.mnt);
-       error = vfs_path_lookup(path.mnt->mnt_root, path.mnt, dir_name, 0, &nd);
+       error = vfs_path_lookup(path.mnt->mnt_root, path.mnt, dir_name, 0, &dir);
        if (error)
                goto err;
  
                q.len = snprintf(name, sizeof(name), "clnt%x", (unsigned int)clntid++);
                name[sizeof(name) - 1] = '\0';
                q.hash = full_name_hash(q.name, q.len);
-               path.dentry = rpc_create_client_dir(nd.path.dentry, &q, clnt);
+               path.dentry = rpc_create_client_dir(dir.dentry, &q, clnt);
                if (!IS_ERR(path.dentry))
                        break;
                error = PTR_ERR(path.dentry);
                        goto err_path_put;
                }
        }
-       path_put(&nd.path);
+       path_put(&dir);
        clnt->cl_path = path;
        return 0;
  err_path_put:
-       path_put(&nd.path);
+       path_put(&dir);
  err:
        rpc_put_mount();
        return error;
@@@ -716,7 -715,7 +715,7 @@@ rpc_call_async(struct rpc_clnt *clnt, c
  }
  EXPORT_SYMBOL_GPL(rpc_call_async);
  
 -#if defined(CONFIG_NFS_V4_1)
 +#if defined(CONFIG_SUNRPC_BACKCHANNEL)
  /**
   * rpc_run_bc_task - Allocate a new RPC task for backchannel use, then run
   * rpc_execute against it
@@@ -759,7 -758,7 +758,7 @@@ out
        dprintk("RPC: rpc_run_bc_task: task= %p\n", task);
        return task;
  }
 -#endif /* CONFIG_NFS_V4_1 */
 +#endif /* CONFIG_SUNRPC_BACKCHANNEL */
  
  void
  rpc_call_start(struct rpc_task *task)
@@@ -1362,7 -1361,7 +1361,7 @@@ call_transmit_status(struct rpc_task *t
        }
  }
  
 -#if defined(CONFIG_NFS_V4_1)
 +#if defined(CONFIG_SUNRPC_BACKCHANNEL)
  /*
   * 5b.        Send the backchannel RPC reply.  On error, drop the reply.  In
   * addition, disconnect on connectivity errors.
@@@ -1426,7 -1425,7 +1425,7 @@@ call_bc_transmit(struct rpc_task *task
        }
        rpc_wake_up_queued_task(&req->rq_xprt->pending, task);
  }
 -#endif /* CONFIG_NFS_V4_1 */
 +#endif /* CONFIG_SUNRPC_BACKCHANNEL */
  
  /*
   * 6. Sort out the RPC call status
@@@ -1551,7 -1550,8 +1550,7 @@@ call_decode(struct rpc_task *task
        kxdrdproc_t     decode = task->tk_msg.rpc_proc->p_decode;
        __be32          *p;
  
 -      dprintk("RPC: %5u call_decode (status %d)\n",
 -                      task->tk_pid, task->tk_status);
 +      dprint_status(task);
  
        if (task->tk_flags & RPC_CALL_MAJORSEEN) {
                if (clnt->cl_chatty)
@@@ -1665,19 -1665,18 +1664,18 @@@ rpc_verify_header(struct rpc_task *task
                if (--len < 0)
                        goto out_overflow;
                switch ((n = ntohl(*p++))) {
-                       case RPC_AUTH_ERROR:
-                               break;
-                       case RPC_MISMATCH:
-                               dprintk("RPC: %5u %s: RPC call version "
-                                               "mismatch!\n",
-                                               task->tk_pid, __func__);
-                               error = -EPROTONOSUPPORT;
-                               goto out_err;
-                       default:
-                               dprintk("RPC: %5u %s: RPC call rejected, "
-                                               "unknown error: %x\n",
-                                               task->tk_pid, __func__, n);
-                               goto out_eio;
+               case RPC_AUTH_ERROR:
+                       break;
+               case RPC_MISMATCH:
+                       dprintk("RPC: %5u %s: RPC call version mismatch!\n",
+                               task->tk_pid, __func__);
+                       error = -EPROTONOSUPPORT;
+                       goto out_err;
+               default:
+                       dprintk("RPC: %5u %s: RPC call rejected, "
+                               "unknown error: %x\n",
+                               task->tk_pid, __func__, n);
+                       goto out_eio;
                }
                if (--len < 0)
                        goto out_overflow;