Merge branch 'nfs-for-2.6.39' into nfs-for-next
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Thu, 24 Mar 2011 21:03:14 +0000 (17:03 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Thu, 24 Mar 2011 21:03:14 +0000 (17:03 -0400)
1  2 
fs/nfs/dir.c
fs/nfs/getroot.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4xdr.c
include/linux/nfs4.h
include/linux/nfs_xdr.h

diff --combined fs/nfs/dir.c
index a83cd0d9dfab1df36655ab282dc803c1efe6dab4,db87a7d1109b419582540d49d71205c04014720b..7237672216c804769b945c9b09b1e57e1b3de202
@@@ -44,6 -44,7 +44,7 @@@
  /* #define NFS_DEBUG_VERBOSE 1 */
  
  static int nfs_opendir(struct inode *, struct file *);
+ static int nfs_closedir(struct inode *, struct file *);
  static int nfs_readdir(struct file *, void *, filldir_t);
  static struct dentry *nfs_lookup(struct inode *, struct dentry *, struct nameidata *);
  static int nfs_create(struct inode *, struct dentry *, int, struct nameidata *);
@@@ -64,7 -65,7 +65,7 @@@ const struct file_operations nfs_dir_op
        .read           = generic_read_dir,
        .readdir        = nfs_readdir,
        .open           = nfs_opendir,
-       .release        = nfs_release,
+       .release        = nfs_closedir,
        .fsync          = nfs_fsync_dir,
  };
  
@@@ -133,13 -134,35 +134,35 @@@ const struct inode_operations nfs4_dir_
  
  #endif /* CONFIG_NFS_V4 */
  
+ static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct rpc_cred *cred)
+ {
+       struct nfs_open_dir_context *ctx;
+       ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+       if (ctx != NULL) {
+               ctx->duped = 0;
+               ctx->dir_cookie = 0;
+               ctx->dup_cookie = 0;
+               ctx->cred = get_rpccred(cred);
+       } else
+               ctx = ERR_PTR(-ENOMEM);
+       return ctx;
+ }
+ static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx)
+ {
+       put_rpccred(ctx->cred);
+       kfree(ctx);
+ }
  /*
   * Open file
   */
  static int
  nfs_opendir(struct inode *inode, struct file *filp)
  {
-       int res;
+       int res = 0;
+       struct nfs_open_dir_context *ctx;
+       struct rpc_cred *cred;
  
        dfprintk(FILE, "NFS: open dir(%s/%s)\n",
                        filp->f_path.dentry->d_parent->d_name.name,
  
        nfs_inc_stats(inode, NFSIOS_VFSOPEN);
  
-       /* Call generic open code in order to cache credentials */
-       res = nfs_open(inode, filp);
+       cred = rpc_lookup_cred();
+       if (IS_ERR(cred))
+               return PTR_ERR(cred);
+       ctx = alloc_nfs_open_dir_context(cred);
+       if (IS_ERR(ctx)) {
+               res = PTR_ERR(ctx);
+               goto out;
+       }
+       filp->private_data = ctx;
        if (filp->f_path.dentry == filp->f_path.mnt->mnt_root) {
                /* This is a mountpoint, so d_revalidate will never
                 * have been called, so we need to refresh the
                 */
                __nfs_revalidate_inode(NFS_SERVER(inode), inode);
        }
+ out:
+       put_rpccred(cred);
        return res;
  }
  
+ static int
+ nfs_closedir(struct inode *inode, struct file *filp)
+ {
+       put_nfs_open_dir_context(filp->private_data);
+       return 0;
+ }
  struct nfs_cache_array_entry {
        u64 cookie;
        u64 ino;
@@@ -284,19 -323,20 +323,20 @@@ int nfs_readdir_search_for_pos(struct n
  {
        loff_t diff = desc->file->f_pos - desc->current_index;
        unsigned int index;
+       struct nfs_open_dir_context *ctx = desc->file->private_data;
  
        if (diff < 0)
                goto out_eof;
        if (diff >= array->size) {
                if (array->eof_index >= 0)
                        goto out_eof;
-               desc->current_index += array->size;
                return -EAGAIN;
        }
  
        index = (unsigned int)diff;
        *desc->dir_cookie = array->array[index].cookie;
        desc->cache_entry_index = index;
+       ctx->duped = 0;
        return 0;
  out_eof:
        desc->eof = 1;
@@@ -307,10 -347,18 +347,18 @@@ stati
  int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc)
  {
        int i;
+       loff_t new_pos;
        int status = -EAGAIN;
+       struct nfs_open_dir_context *ctx = desc->file->private_data;
  
        for (i = 0; i < array->size; i++) {
                if (array->array[i].cookie == *desc->dir_cookie) {
+                       new_pos = desc->current_index + i;
+                       if (new_pos < desc->file->f_pos) {
+                               ctx->dup_cookie = *desc->dir_cookie;
+                               ctx->duped = 1;
+                       }
+                       desc->file->f_pos = new_pos;
                        desc->cache_entry_index = i;
                        return 0;
                }
@@@ -342,6 -390,7 +390,7 @@@ int nfs_readdir_search_array(nfs_readdi
  
        if (status == -EAGAIN) {
                desc->last_cookie = array->last_cookie;
+               desc->current_index += array->size;
                desc->page_index++;
        }
        nfs_readdir_release_array(desc->page);
@@@ -354,7 -403,8 +403,8 @@@ stati
  int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
                        struct nfs_entry *entry, struct file *file, struct inode *inode)
  {
-       struct rpc_cred *cred = nfs_file_cred(file);
+       struct nfs_open_dir_context *ctx = file->private_data;
+       struct rpc_cred *cred = ctx->cred;
        unsigned long   timestamp, gencount;
        int             error;
  
@@@ -693,6 -743,20 +743,20 @@@ int nfs_do_filldir(nfs_readdir_descript
        int i = 0;
        int res = 0;
        struct nfs_cache_array *array = NULL;
+       struct nfs_open_dir_context *ctx = file->private_data;
+       if (ctx->duped != 0 && ctx->dup_cookie == *desc->dir_cookie) {
+               if (printk_ratelimit()) {
+                       pr_notice("NFS: directory %s/%s contains a readdir loop.  "
+                               "Please contact your server vendor.  "
+                               "Offending cookie: %llu\n",
+                               file->f_dentry->d_parent->d_name.name,
+                               file->f_dentry->d_name.name,
+                               *desc->dir_cookie);
+               }
+               res = -ELOOP;
+               goto out;
+       }
  
        array = nfs_readdir_get_array(desc->page);
        if (IS_ERR(array)) {
@@@ -785,6 -849,7 +849,7 @@@ static int nfs_readdir(struct file *fil
        struct inode    *inode = dentry->d_inode;
        nfs_readdir_descriptor_t my_desc,
                        *desc = &my_desc;
+       struct nfs_open_dir_context *dir_ctx = filp->private_data;
        int res;
  
        dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
        memset(desc, 0, sizeof(*desc));
  
        desc->file = filp;
-       desc->dir_cookie = &nfs_file_open_context(filp)->dir_cookie;
+       desc->dir_cookie = &dir_ctx->dir_cookie;
        desc->decode = NFS_PROTO(inode)->decode_dirent;
        desc->plus = NFS_USE_READDIRPLUS(inode);
  
@@@ -853,6 -918,7 +918,7 @@@ static loff_t nfs_llseek_dir(struct fil
  {
        struct dentry *dentry = filp->f_path.dentry;
        struct inode *inode = dentry->d_inode;
+       struct nfs_open_dir_context *dir_ctx = filp->private_data;
  
        dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n",
                        dentry->d_parent->d_name.name,
        }
        if (offset != filp->f_pos) {
                filp->f_pos = offset;
-               nfs_file_open_context(filp)->dir_cookie = 0;
+               dir_ctx->dir_cookie = 0;
+               dir_ctx->duped = 0;
        }
  out:
        mutex_unlock(&inode->i_mutex);
@@@ -1068,7 -1135,7 +1135,7 @@@ static int nfs_lookup_revalidate(struc
        if (fhandle == NULL || fattr == NULL)
                goto out_error;
  
 -      error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
 +      error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
        if (error)
                goto out_bad;
        if (nfs_compare_fh(NFS_FH(inode), fhandle))
@@@ -1169,23 -1236,11 +1236,23 @@@ static void nfs_dentry_iput(struct dent
        iput(inode);
  }
  
 +static void nfs_d_release(struct dentry *dentry)
 +{
 +      /* free cached devname value, if it survived that far */
 +      if (unlikely(dentry->d_fsdata)) {
 +              if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
 +                      WARN_ON(1);
 +              else
 +                      kfree(dentry->d_fsdata);
 +      }
 +}
 +
  const struct dentry_operations nfs_dentry_operations = {
        .d_revalidate   = nfs_lookup_revalidate,
        .d_delete       = nfs_dentry_delete,
        .d_iput         = nfs_dentry_iput,
        .d_automount    = nfs_d_automount,
 +      .d_release      = nfs_d_release,
  };
  
  static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
        parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        nfs_block_sillyrename(parent);
 -      error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
 +      error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
        if (error == -ENOENT)
                goto no_entry;
        if (error < 0) {
@@@ -1260,7 -1315,6 +1327,7 @@@ const struct dentry_operations nfs4_den
        .d_delete       = nfs_dentry_delete,
        .d_iput         = nfs_dentry_iput,
        .d_automount    = nfs_d_automount,
 +      .d_release      = nfs_d_release,
  };
  
  /*
@@@ -1562,7 -1616,7 +1629,7 @@@ int nfs_instantiate(struct dentry *dent
        if (dentry->d_inode)
                goto out;
        if (fhandle->size == 0) {
 -              error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
 +              error = NFS_PROTO(dir)->lookup(NFS_SERVER(dir)->client, dir, &dentry->d_name, fhandle, fattr);
                if (error)
                        goto out_error;
        }
diff --combined fs/nfs/getroot.c
index 1084792bc0fec1fd2ecefd4a3cc04e7450c1bf7c,79796521cfdc2dacbf7b721de48322f918a9f393..dcb61548887fdba13fc484c844aeb3a6b14a0fe9
@@@ -75,25 -75,18 +75,25 @@@ static int nfs_superblock_set_dummy_roo
  /*
   * get an NFS2/NFS3 root dentry from the root filehandle
   */
 -struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
 +struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh,
 +                          const char *devname)
  {
        struct nfs_server *server = NFS_SB(sb);
        struct nfs_fsinfo fsinfo;
        struct dentry *ret;
        struct inode *inode;
 +      void *name = kstrdup(devname, GFP_KERNEL);
        int error;
  
 +      if (!name)
 +              return ERR_PTR(-ENOMEM);
 +
        /* get the actual root for this mount */
        fsinfo.fattr = nfs_alloc_fattr();
 -      if (fsinfo.fattr == NULL)
 +      if (fsinfo.fattr == NULL) {
 +              kfree(name);
                return ERR_PTR(-ENOMEM);
 +      }
  
        error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
        if (error < 0) {
        }
  
        security_d_instantiate(ret, inode);
 +      spin_lock(&ret->d_lock);
 +      if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) {
 +              ret->d_fsdata = name;
 +              name = NULL;
 +      }
 +      spin_unlock(&ret->d_lock);
  out:
 +      if (name)
 +              kfree(name);
        nfs_free_fattr(fsinfo.fattr);
        return ret;
  }
@@@ -184,35 -169,27 +184,35 @@@ out
  /*
   * get an NFS4 root dentry from the root filehandle
   */
 -struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
 +struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh,
 +                           const char *devname)
  {
        struct nfs_server *server = NFS_SB(sb);
        struct nfs_fattr *fattr = NULL;
        struct dentry *ret;
        struct inode *inode;
 +      void *name = kstrdup(devname, GFP_KERNEL);
        int error;
  
        dprintk("--> nfs4_get_root()\n");
  
 +      if (!name)
 +              return ERR_PTR(-ENOMEM);
 +
        /* get the info about the server and filesystem */
        error = nfs4_server_capabilities(server, mntfh);
        if (error < 0) {
                dprintk("nfs_get_root: getcaps error = %d\n",
                        -error);
 +              kfree(name);
                return ERR_PTR(error);
        }
  
        fattr = nfs_alloc_fattr();
 -      if (fattr == NULL)
 -              return ERR_PTR(-ENOMEM);;
 +      if (fattr == NULL) {
 +              kfree(name);
 +              return ERR_PTR(-ENOMEM);
 +      }
  
        /* get the actual root for this mount */
        error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr);
                goto out;
        }
  
+       if (fattr->valid & NFS_ATTR_FATTR_FSID &&
+           !nfs_fsid_equal(&server->fsid, &fattr->fsid))
+               memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
        inode = nfs_fhget(sb, mntfh, fattr);
        if (IS_ERR(inode)) {
                dprintk("nfs_get_root: get root inode failed\n");
        }
  
        security_d_instantiate(ret, inode);
 -
 +      spin_lock(&ret->d_lock);
 +      if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) {
 +              ret->d_fsdata = name;
 +              name = NULL;
 +      }
 +      spin_unlock(&ret->d_lock);
  out:
 +      if (name)
 +              kfree(name);
        nfs_free_fattr(fattr);
        dprintk("<-- nfs4_get_root()\n");
        return ret;
diff --combined fs/nfs/inode.c
index 058d7d63e566a8a5a6877b0bd3e874850ee2d926,229e586b1a20e681195d6f33ff0c700f11b22971..57bb31ad7a5ec1c46a5533db711ee6d7d423f68e
@@@ -254,9 -254,7 +254,9 @@@ nfs_fhget(struct super_block *sb, struc
        struct inode *inode = ERR_PTR(-ENOENT);
        unsigned long hash;
  
 -      if ((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0)
 +      nfs_attr_check_mountpoint(sb, fattr);
 +
 +      if ((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0 && (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT) == 0)
                goto out_no_inode;
        if ((fattr->valid & NFS_ATTR_FATTR_TYPE) == 0)
                goto out_no_inode;
                        if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
                                set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
                        /* Deal with crossing mountpoints */
 -                      if ((fattr->valid & NFS_ATTR_FATTR_FSID)
 -                                      && !nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) {
 +                      if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
 +                                      fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
                                if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
                                        inode->i_op = &nfs_referral_inode_operations;
                                else
@@@ -641,7 -639,6 +641,6 @@@ struct nfs_open_context *alloc_nfs_open
                ctx->mode = f_mode;
                ctx->flags = 0;
                ctx->error = 0;
-               ctx->dir_cookie = 0;
                nfs_init_lock_context(&ctx->lock_context);
                ctx->lock_context.open_context = ctx;
                INIT_LIST_HEAD(&ctx->list);
@@@ -1473,6 -1470,7 +1472,7 @@@ static inline void nfs4_init_once(struc
        nfsi->delegation_state = 0;
        init_rwsem(&nfsi->rwsem);
        nfsi->layout = NULL;
+       atomic_set(&nfsi->commits_outstanding, 0);
  #endif
  }
  
@@@ -1520,7 -1518,7 +1520,7 @@@ static int nfsiod_start(void
  {
        struct workqueue_struct *wq;
        dprintk("RPC:       creating workqueue nfsiod\n");
 -      wq = alloc_workqueue("nfsiod", WQ_RESCUER, 0);
 +      wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM, 0);
        if (wq == NULL)
                return -ENOMEM;
        nfsiod_workqueue = wq;
diff --combined fs/nfs/internal.h
index 345d86b90e269dee9a4ff2ffe767e166bf23d08d,708705062216fd7a4366eca9b4ee63890ab80cfd..ce118ce885dd5e20276356011e288d8502c6ef11
@@@ -39,12 -39,6 +39,12 @@@ static inline int nfs4_has_persistent_s
        return 0;
  }
  
 +static inline void nfs_attr_check_mountpoint(struct super_block *parent, struct nfs_fattr *fattr)
 +{
 +      if (!nfs_fsid_equal(&NFS_SB(parent)->fsid, &fattr->fsid))
 +              fattr->valid |= NFS_ATTR_FATTR_MOUNTPOINT;
 +}
 +
  struct nfs_clone_mount {
        const struct super_block *sb;
        const struct dentry *dentry;
@@@ -172,10 -166,10 +172,10 @@@ static inline void nfs_fs_proc_exit(voi
  
  /* nfs4namespace.c */
  #ifdef CONFIG_NFS_V4
 -extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry);
 +extern struct vfsmount *nfs_do_refmount(struct dentry *dentry);
  #else
  static inline
 -struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry)
 +struct vfsmount *nfs_do_refmount(struct dentry *dentry)
  {
        return ERR_PTR(-ENOENT);
  }
@@@ -220,7 -214,6 +220,7 @@@ extern const u32 nfs41_maxwrite_overhea
  /* nfs4proc.c */
  #ifdef CONFIG_NFS_V4
  extern struct rpc_procinfo nfs4_procedures[];
 +void nfs_fixup_secinfo_attributes(struct nfs_fattr *, struct nfs_fh *);
  #endif
  
  extern int nfs4_init_ds_session(struct nfs_client *clp);
@@@ -263,16 -256,16 +263,16 @@@ extern void nfs_sb_active(struct super_
  extern void nfs_sb_deactive(struct super_block *sb);
  
  /* namespace.c */
 -extern char *nfs_path(const char *base,
 -                    const struct dentry *droot,
 -                    const struct dentry *dentry,
 +extern char *nfs_path(char **p, struct dentry *dentry,
                      char *buffer, ssize_t buflen);
  extern struct vfsmount *nfs_d_automount(struct path *path);
  
  /* getroot.c */
 -extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *);
 +extern struct dentry *nfs_get_root(struct super_block *, struct nfs_fh *,
 +                                 const char *);
  #ifdef CONFIG_NFS_V4
 -extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *);
 +extern struct dentry *nfs4_get_root(struct super_block *, struct nfs_fh *,
 +                                  const char *);
  
  extern int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh);
  #endif
@@@ -283,11 -276,25 +283,25 @@@ extern int nfs_initiate_read(struct nfs
  extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
  
  /* write.c */
+ extern void nfs_commit_free(struct nfs_write_data *p);
  extern int nfs_initiate_write(struct nfs_write_data *data,
                              struct rpc_clnt *clnt,
                              const struct rpc_call_ops *call_ops,
                              int how);
  extern void nfs_write_prepare(struct rpc_task *task, void *calldata);
+ extern int nfs_initiate_commit(struct nfs_write_data *data,
+                              struct rpc_clnt *clnt,
+                              const struct rpc_call_ops *call_ops,
+                              int how);
+ extern void nfs_init_commit(struct nfs_write_data *data,
+                           struct list_head *head,
+                           struct pnfs_layout_segment *lseg);
+ void nfs_retry_commit(struct list_head *page_list,
+                     struct pnfs_layout_segment *lseg);
+ void nfs_commit_clear_lock(struct nfs_inode *nfsi);
+ void nfs_commitdata_release(void *data);
+ void nfs_commit_release_pages(struct nfs_write_data *data);
  #ifdef CONFIG_MIGRATION
  extern int nfs_migrate_page(struct address_space *,
                struct page *, struct page *);
@@@ -303,14 -310,12 +317,14 @@@ extern int nfs4_init_client(struct nfs_
                            rpc_authflavor_t authflavour,
                            int noresvport);
  extern void nfs4_reset_write(struct rpc_task *task, struct nfs_write_data *data);
 -extern int _nfs4_call_sync(struct nfs_server *server,
 +extern int _nfs4_call_sync(struct rpc_clnt *clnt,
 +                         struct nfs_server *server,
                           struct rpc_message *msg,
                           struct nfs4_sequence_args *args,
                           struct nfs4_sequence_res *res,
                           int cache_reply);
 -extern int _nfs4_call_sync_session(struct nfs_server *server,
 +extern int _nfs4_call_sync_session(struct rpc_clnt *clnt,
 +                                 struct nfs_server *server,
                                   struct rpc_message *msg,
                                   struct nfs4_sequence_args *args,
                                   struct nfs4_sequence_res *res,
  /*
   * Determine the device name as a string
   */
 -static inline char *nfs_devname(const struct vfsmount *mnt_parent,
 -                              const struct dentry *dentry,
 +static inline char *nfs_devname(struct dentry *dentry,
                                char *buffer, ssize_t buflen)
  {
 -      return nfs_path(mnt_parent->mnt_devname, mnt_parent->mnt_root,
 -                      dentry, buffer, buflen);
 +      char *dummy;
 +      return nfs_path(&dummy, dentry, buffer, buflen);
  }
  
  /*
diff --combined fs/nfs/nfs4_fs.h
index 72e5f1a2883e29b25230d346f9b4b383d9fc7b2b,4414fd7e7d5e52fe417cd4f6cf57f23aa93ee0eb..e1c261ddd65dc591408633469e5e90ec176f476c
@@@ -57,8 -57,7 +57,8 @@@ enum nfs4_session_state 
  struct nfs4_minor_version_ops {
        u32     minor_version;
  
 -      int     (*call_sync)(struct nfs_server *server,
 +      int     (*call_sync)(struct rpc_clnt *clnt,
 +                      struct nfs_server *server,
                        struct rpc_message *msg,
                        struct nfs4_sequence_args *args,
                        struct nfs4_sequence_res *res,
@@@ -263,6 -262,8 +263,8 @@@ extern int nfs4_proc_destroy_session(st
  extern int nfs4_init_session(struct nfs_server *server);
  extern int nfs4_proc_get_lease_time(struct nfs_client *clp,
                struct nfs_fsinfo *fsinfo);
+ extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data,
+                                 bool sync);
  
  static inline bool
  is_ds_only_client(struct nfs_client *clp)
diff --combined fs/nfs/nfs4proc.c
index f9150f03d6400d136f48415daef0f0827f050929,8f071314e94b0762a0527a6db63986acf3d06171..dfd1e6d7e6c3c645886424f2858d27874233566f
@@@ -41,7 -41,6 +41,7 @@@
  #include <linux/string.h>
  #include <linux/slab.h>
  #include <linux/sunrpc/clnt.h>
 +#include <linux/sunrpc/gss_api.h>
  #include <linux/nfs.h>
  #include <linux/nfs4.h>
  #include <linux/nfs_fs.h>
@@@ -72,9 -71,7 +72,9 @@@ static int _nfs4_proc_open(struct nfs4_
  static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
  static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
  static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
 -static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
 +static int _nfs4_proc_lookup(struct rpc_clnt *client, struct inode *dir,
 +                           const struct qstr *name, struct nfs_fh *fhandle,
 +                           struct nfs_fattr *fattr);
  static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
  static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
@@@ -88,8 -85,6 +88,8 @@@ static int nfs4_map_errors(int err
        switch (err) {
        case -NFS4ERR_RESOURCE:
                return -EREMOTEIO;
 +      case -NFS4ERR_WRONGSEC:
 +              return -EPERM;
        case -NFS4ERR_BADOWNER:
        case -NFS4ERR_BADNAME:
                return -EINVAL;
@@@ -662,8 -657,7 +662,8 @@@ struct rpc_call_ops nfs41_call_priv_syn
        .rpc_call_done = nfs41_call_sync_done,
  };
  
 -static int nfs4_call_sync_sequence(struct nfs_server *server,
 +static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
 +                                 struct nfs_server *server,
                                   struct rpc_message *msg,
                                   struct nfs4_sequence_args *args,
                                   struct nfs4_sequence_res *res,
                .cache_reply = cache_reply,
        };
        struct rpc_task_setup task_setup = {
 -              .rpc_client = server->client,
 +              .rpc_client = clnt,
                .rpc_message = msg,
                .callback_ops = &nfs41_call_sync_ops,
                .callback_data = &data
        return ret;
  }
  
 -int _nfs4_call_sync_session(struct nfs_server *server,
 +int _nfs4_call_sync_session(struct rpc_clnt *clnt,
 +                          struct nfs_server *server,
                            struct rpc_message *msg,
                            struct nfs4_sequence_args *args,
                            struct nfs4_sequence_res *res,
                            int cache_reply)
  {
 -      return nfs4_call_sync_sequence(server, msg, args, res, cache_reply, 0);
 +      return nfs4_call_sync_sequence(clnt, server, msg, args, res, cache_reply, 0);
  }
  
  #else
@@@ -716,28 -709,19 +716,28 @@@ static int nfs4_sequence_done(struct rp
  }
  #endif /* CONFIG_NFS_V4_1 */
  
 -int _nfs4_call_sync(struct nfs_server *server,
 +int _nfs4_call_sync(struct rpc_clnt *clnt,
 +                  struct nfs_server *server,
                    struct rpc_message *msg,
                    struct nfs4_sequence_args *args,
                    struct nfs4_sequence_res *res,
                    int cache_reply)
  {
        args->sa_session = res->sr_session = NULL;
 -      return rpc_call_sync(server->client, msg, 0);
 +      return rpc_call_sync(clnt, msg, 0);
  }
  
 -#define nfs4_call_sync(server, msg, args, res, cache_reply) \
 -      (server)->nfs_client->cl_mvops->call_sync((server), (msg), &(args)->seq_args, \
 -                      &(res)->seq_res, (cache_reply))
 +static inline
 +int nfs4_call_sync(struct rpc_clnt *clnt,
 +                 struct nfs_server *server,
 +                 struct rpc_message *msg,
 +                 struct nfs4_sequence_args *args,
 +                 struct nfs4_sequence_res *res,
 +                 int cache_reply)
 +{
 +      return server->nfs_client->cl_mvops->call_sync(clnt, server, msg,
 +                                              args, res, cache_reply);
 +}
  
  static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
  {
@@@ -1847,7 -1831,7 +1847,7 @@@ static int _nfs4_do_setattr(struct inod
        } else
                memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
  
 -      status = nfs4_call_sync(server, &msg, &arg, &res, 1);
 +      status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (status == 0 && state != NULL)
                renew_lease(server, timestamp);
        return status;
@@@ -2106,7 -2090,7 +2106,7 @@@ static int _nfs4_server_capabilities(st
        };
        int status;
  
 -      status = nfs4_call_sync(server, &msg, &args, &res, 0);
 +      status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
        if (status == 0) {
                memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
                server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|
@@@ -2176,7 -2160,7 +2176,7 @@@ static int _nfs4_lookup_root(struct nfs
        };
  
        nfs_fattr_init(info->fattr);
 -      return nfs4_call_sync(server, &msg, &args, &res, 0);
 +      return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
  }
  
  static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
        return err;
  }
  
 +static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
 +                              struct nfs_fsinfo *info, rpc_authflavor_t flavor)
 +{
 +      struct rpc_auth *auth;
 +      int ret;
 +
 +      auth = rpcauth_create(flavor, server->client);
 +      if (!auth) {
 +              ret = -EIO;
 +              goto out;
 +      }
 +      ret = nfs4_lookup_root(server, fhandle, info);
 +      if (ret < 0)
 +              ret = -EAGAIN;
 +out:
 +      return ret;
 +}
 +
  /*
   * get the file handle for the "/" directory on the server
   */
  static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
                              struct nfs_fsinfo *info)
  {
 -      int status;
 +      int i, len, status = 0;
 +      rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS + 2];
  
 -      status = nfs4_lookup_root(server, fhandle, info);
 +      flav_array[0] = RPC_AUTH_UNIX;
 +      len = gss_mech_list_pseudoflavors(&flav_array[1]);
 +      flav_array[1+len] = RPC_AUTH_NULL;
 +      len += 2;
 +
 +      for (i = 0; i < len; i++) {
 +              status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
 +              if (status == 0)
 +                      break;
 +      }
        if (status == 0)
                status = nfs4_server_capabilities(server, fhandle);
        if (status == 0)
@@@ -2293,7 -2249,7 +2293,7 @@@ static int _nfs4_proc_getattr(struct nf
        };
        
        nfs_fattr_init(fattr);
 -      return nfs4_call_sync(server, &msg, &args, &res, 0);
 +      return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
  }
  
  static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
@@@ -2353,9 -2309,9 +2353,9 @@@ nfs4_proc_setattr(struct dentry *dentry
        return status;
  }
  
 -static int _nfs4_proc_lookupfh(struct nfs_server *server, const struct nfs_fh *dirfh,
 -              const struct qstr *name, struct nfs_fh *fhandle,
 -              struct nfs_fattr *fattr)
 +static int _nfs4_proc_lookupfh(struct rpc_clnt *clnt, struct nfs_server *server,
 +              const struct nfs_fh *dirfh, const struct qstr *name,
 +              struct nfs_fh *fhandle, struct nfs_fattr *fattr)
  {
        int                    status;
        struct nfs4_lookup_arg args = {
        nfs_fattr_init(fattr);
  
        dprintk("NFS call  lookupfh %s\n", name->name);
 -      status = nfs4_call_sync(server, &msg, &args, &res, 0);
 +      status = nfs4_call_sync(clnt, server, &msg, &args.seq_args, &res.seq_res, 0);
        dprintk("NFS reply lookupfh: %d\n", status);
        return status;
  }
@@@ -2389,7 -2345,7 +2389,7 @@@ static int nfs4_proc_lookupfh(struct nf
        struct nfs4_exception exception = { };
        int err;
        do {
 -              err = _nfs4_proc_lookupfh(server, dirfh, name, fhandle, fattr);
 +              err = _nfs4_proc_lookupfh(server->client, server, dirfh, name, fhandle, fattr);
                /* FIXME: !!!! */
                if (err == -NFS4ERR_MOVED) {
                        err = -EREMOTE;
        return err;
  }
  
 -static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name,
 -              struct nfs_fh *fhandle, struct nfs_fattr *fattr)
 +static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
 +              const struct qstr *name, struct nfs_fh *fhandle,
 +              struct nfs_fattr *fattr)
  {
        int status;
        
        dprintk("NFS call  lookup %s\n", name->name);
 -      status = _nfs4_proc_lookupfh(NFS_SERVER(dir), NFS_FH(dir), name, fhandle, fattr);
 +      status = _nfs4_proc_lookupfh(clnt, NFS_SERVER(dir), NFS_FH(dir), name, fhandle, fattr);
        if (status == -NFS4ERR_MOVED)
                status = nfs4_get_referral(dir, name, fattr, fhandle);
        dprintk("NFS reply lookup: %d\n", status);
        return status;
  }
  
 -static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
 +void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr, struct nfs_fh *fh)
 +{
 +      memset(fh, 0, sizeof(struct nfs_fh));
 +      fattr->fsid.major = 1;
 +      fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
 +              NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_FSID | NFS_ATTR_FATTR_MOUNTPOINT;
 +      fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
 +      fattr->nlink = 2;
 +}
 +
 +static int nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct qstr *name,
 +                          struct nfs_fh *fhandle, struct nfs_fattr *fattr)
  {
        struct nfs4_exception exception = { };
        int err;
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
 -                              _nfs4_proc_lookup(dir, name, fhandle, fattr),
 +                              _nfs4_proc_lookup(clnt, dir, name, fhandle, fattr),
                                &exception);
 +              if (err == -EPERM)
 +                      nfs_fixup_secinfo_attributes(fattr, fhandle);
        } while (exception.retry);
        return err;
  }
@@@ -2479,7 -2421,7 +2479,7 @@@ static int _nfs4_proc_access(struct ino
        if (res.fattr == NULL)
                return -ENOMEM;
  
 -      status = nfs4_call_sync(server, &msg, &args, &res, 0);
 +      status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
        if (!status) {
                entry->mask = 0;
                if (res.access & NFS4_ACCESS_READ)
@@@ -2546,7 -2488,7 +2546,7 @@@ static int _nfs4_proc_readlink(struct i
                .rpc_resp = &res,
        };
  
 -      return nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0);
 +      return nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
  }
  
  static int nfs4_proc_readlink(struct inode *inode, struct page *page,
@@@ -2635,7 -2577,7 +2635,7 @@@ static int _nfs4_proc_remove(struct ino
        if (res.dir_attr == NULL)
                goto out;
  
 -      status = nfs4_call_sync(server, &msg, &args, &res, 1);
 +      status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
        if (status == 0) {
                update_changeattr(dir, &res.cinfo);
                nfs_post_op_update_inode(dir, res.dir_attr);
@@@ -2736,7 -2678,7 +2736,7 @@@ static int _nfs4_proc_rename(struct ino
        if (res.old_fattr == NULL || res.new_fattr == NULL)
                goto out;
  
 -      status = nfs4_call_sync(server, &msg, &arg, &res, 1);
 +      status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(old_dir, &res.old_cinfo);
                nfs_post_op_update_inode(old_dir, res.old_fattr);
@@@ -2787,7 -2729,7 +2787,7 @@@ static int _nfs4_proc_link(struct inod
        if (res.fattr == NULL || res.dir_attr == NULL)
                goto out;
  
 -      status = nfs4_call_sync(server, &msg, &arg, &res, 1);
 +      status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(dir, &res.cinfo);
                nfs_post_op_update_inode(dir, res.dir_attr);
@@@ -2850,8 -2792,8 +2850,8 @@@ static struct nfs4_createdata *nfs4_all
  
  static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
  {
 -      int status = nfs4_call_sync(NFS_SERVER(dir), &data->msg,
 -                                  &data->arg, &data->res, 1);
 +      int status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &data->msg,
 +                                  &data->arg.seq_args, &data->res.seq_res, 1);
        if (status == 0) {
                update_changeattr(dir, &data->res.dir_cinfo);
                nfs_post_op_update_inode(dir, data->res.dir_fattr);
@@@ -2963,7 -2905,7 +2963,7 @@@ static int _nfs4_proc_readdir(struct de
                        (unsigned long long)cookie);
        nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
        res.pgbase = args.pgbase;
 -      status = nfs4_call_sync(NFS_SERVER(dir), &msg, &args, &res, 0);
 +      status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
        if (status >= 0) {
                memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
                status += args.pgbase;
@@@ -3055,7 -2997,7 +3055,7 @@@ static int _nfs4_proc_statfs(struct nfs
        };
  
        nfs_fattr_init(fsstat->fattr);
 -      return  nfs4_call_sync(server, &msg, &args, &res, 0);
 +      return  nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
  }
  
  static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsstat *fsstat)
@@@ -3086,7 -3028,7 +3086,7 @@@ static int _nfs4_do_fsinfo(struct nfs_s
                .rpc_resp = &res,
        };
  
 -      return nfs4_call_sync(server, &msg, &args, &res, 0);
 +      return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
  }
  
  static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
@@@ -3131,7 -3073,7 +3131,7 @@@ static int _nfs4_proc_pathconf(struct n
        }
  
        nfs_fattr_init(pathconf->fattr);
 -      return nfs4_call_sync(server, &msg, &args, &res, 0);
 +      return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
  }
  
  static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
@@@ -3253,12 -3195,9 +3253,9 @@@ static void nfs4_proc_write_setup(struc
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_WRITE];
  }
  
- static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+ static int nfs4_commit_done_cb(struct rpc_task *task, struct nfs_write_data *data)
  {
        struct inode *inode = data->inode;
-       
-       if (!nfs4_sequence_done(task, &data->res.seq_res))
-               return -EAGAIN;
  
        if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
                nfs_restart_rpc(task, NFS_SERVER(inode)->nfs_client);
        return 0;
  }
  
+ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
+ {
+       if (!nfs4_sequence_done(task, &data->res.seq_res))
+               return -EAGAIN;
+       return data->write_done_cb(task, data);
+ }
  static void nfs4_proc_commit_setup(struct nfs_write_data *data, struct rpc_message *msg)
  {
        struct nfs_server *server = NFS_SERVER(data->inode);
-       
-       data->args.bitmask = server->cache_consistency_bitmask;
+       if (data->lseg) {
+               data->args.bitmask = NULL;
+               data->res.fattr = NULL;
+       } else
+               data->args.bitmask = server->cache_consistency_bitmask;
+       if (!data->write_done_cb)
+               data->write_done_cb = nfs4_commit_done_cb;
        data->res.server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT];
  }
@@@ -3510,7 -3462,7 +3520,7 @@@ static ssize_t __nfs4_get_acl_uncached(
                resp_buf = buf;
                buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
        }
 -      ret = nfs4_call_sync(NFS_SERVER(inode), &msg, &args, &res, 0);
 +      ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
        if (ret)
                goto out_free;
        if (res.acl_len > args.acl_len)
@@@ -3585,7 -3537,7 +3595,7 @@@ static int __nfs4_proc_set_acl(struct i
        if (i < 0)
                return i;
        nfs_inode_return_delegation(inode);
 -      ret = nfs4_call_sync(server, &msg, &arg, &res, 1);
 +      ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
  
        /*
         * Free each page after tx, so the only ref left is
@@@ -3948,7 -3900,7 +3958,7 @@@ static int _nfs4_proc_getlk(struct nfs4
        lsp = request->fl_u.nfs4_fl.owner;
        arg.lock_owner.id = lsp->ls_id.id;
        arg.lock_owner.s_dev = server->s_dev;
 -      status = nfs4_call_sync(server, &msg, &arg, &res, 1);
 +      status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        switch (status) {
                case 0:
                        request->fl_type = F_UNLCK;
@@@ -4676,46 -4628,12 +4686,46 @@@ int nfs4_proc_fs_locations(struct inod
        nfs_fattr_init(&fs_locations->fattr);
        fs_locations->server = server;
        fs_locations->nlocations = 0;
 -      status = nfs4_call_sync(server, &msg, &args, &res, 0);
 +      status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
        nfs_fixup_referral_attributes(&fs_locations->fattr);
        dprintk("%s: returned status = %d\n", __func__, status);
        return status;
  }
  
 +static int _nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
 +{
 +      int status;
 +      struct nfs4_secinfo_arg args = {
 +              .dir_fh = NFS_FH(dir),
 +              .name   = name,
 +      };
 +      struct nfs4_secinfo_res res = {
 +              .flavors     = flavors,
 +      };
 +      struct rpc_message msg = {
 +              .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO],
 +              .rpc_argp = &args,
 +              .rpc_resp = &res,
 +      };
 +
 +      dprintk("NFS call  secinfo %s\n", name->name);
 +      status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
 +      dprintk("NFS reply  secinfo: %d\n", status);
 +      return status;
 +}
 +
 +int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name, struct nfs4_secinfo_flavors *flavors)
 +{
 +      struct nfs4_exception exception = { };
 +      int err;
 +      do {
 +              err = nfs4_handle_exception(NFS_SERVER(dir),
 +                              _nfs4_proc_secinfo(dir, name, flavors),
 +                              &exception);
 +      } while (exception.retry);
 +      return err;
 +}
 +
  #ifdef CONFIG_NFS_V4_1
  /*
   * Check the exchange flags returned by the server for invalid flags, having
@@@ -5608,8 -5526,6 +5618,6 @@@ static void nfs4_layoutget_release(voi
        struct nfs4_layoutget *lgp = calldata;
  
        dprintk("--> %s\n", __func__);
-       if (lgp->res.layout.buf != NULL)
-               free_page((unsigned long) lgp->res.layout.buf);
        put_nfs_open_context(lgp->args.ctx);
        kfree(calldata);
        dprintk("<-- %s\n", __func__);
@@@ -5641,12 -5557,7 +5649,7 @@@ int nfs4_proc_layoutget(struct nfs4_lay
  
        dprintk("--> %s\n", __func__);
  
-       lgp->res.layout.buf = (void *)__get_free_page(GFP_NOFS);
-       if (lgp->res.layout.buf == NULL) {
-               nfs4_layoutget_release(lgp);
-               return -ENOMEM;
-       }
+       lgp->res.layoutp = &lgp->args.layout;
        lgp->res.seq_res.sr_slot = NULL;
        task = rpc_run_task(&task_setup_data);
        if (IS_ERR(task))
@@@ -5678,7 -5589,7 +5681,7 @@@ _nfs4_proc_getdeviceinfo(struct nfs_ser
        int status;
  
        dprintk("--> %s\n", __func__);
 -      status = nfs4_call_sync(server, &msg, &args, &res, 0);
 +      status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
        dprintk("<-- %s status=%d\n", __func__, status);
  
        return status;
@@@ -5698,6 -5609,100 +5701,100 @@@ int nfs4_proc_getdeviceinfo(struct nfs_
  }
  EXPORT_SYMBOL_GPL(nfs4_proc_getdeviceinfo);
  
+ static void nfs4_layoutcommit_prepare(struct rpc_task *task, void *calldata)
+ {
+       struct nfs4_layoutcommit_data *data = calldata;
+       struct nfs_server *server = NFS_SERVER(data->args.inode);
+       if (nfs4_setup_sequence(server, &data->args.seq_args,
+                               &data->res.seq_res, 1, task))
+               return;
+       rpc_call_start(task);
+ }
+ static void
+ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata)
+ {
+       struct nfs4_layoutcommit_data *data = calldata;
+       struct nfs_server *server = NFS_SERVER(data->args.inode);
+       if (!nfs4_sequence_done(task, &data->res.seq_res))
+               return;
+       switch (task->tk_status) { /* Just ignore these failures */
+       case NFS4ERR_DELEG_REVOKED: /* layout was recalled */
+       case NFS4ERR_BADIOMODE:     /* no IOMODE_RW layout for range */
+       case NFS4ERR_BADLAYOUT:     /* no layout */
+       case NFS4ERR_GRACE:         /* loca_recalim always false */
+               task->tk_status = 0;
+       }
+       if (nfs4_async_handle_error(task, server, NULL) == -EAGAIN) {
+               nfs_restart_rpc(task, server->nfs_client);
+               return;
+       }
+       if (task->tk_status == 0)
+               nfs_post_op_update_inode_force_wcc(data->args.inode,
+                                                  data->res.fattr);
+ }
+ static void nfs4_layoutcommit_release(void *calldata)
+ {
+       struct nfs4_layoutcommit_data *data = calldata;
+       /* Matched by references in pnfs_set_layoutcommit */
+       put_lseg(data->lseg);
+       put_rpccred(data->cred);
+       kfree(data);
+ }
+ static const struct rpc_call_ops nfs4_layoutcommit_ops = {
+       .rpc_call_prepare = nfs4_layoutcommit_prepare,
+       .rpc_call_done = nfs4_layoutcommit_done,
+       .rpc_release = nfs4_layoutcommit_release,
+ };
+ int
+ nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync)
+ {
+       struct rpc_message msg = {
+               .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTCOMMIT],
+               .rpc_argp = &data->args,
+               .rpc_resp = &data->res,
+               .rpc_cred = data->cred,
+       };
+       struct rpc_task_setup task_setup_data = {
+               .task = &data->task,
+               .rpc_client = NFS_CLIENT(data->args.inode),
+               .rpc_message = &msg,
+               .callback_ops = &nfs4_layoutcommit_ops,
+               .callback_data = data,
+               .flags = RPC_TASK_ASYNC,
+       };
+       struct rpc_task *task;
+       int status = 0;
+       dprintk("NFS: %4d initiating layoutcommit call. sync %d "
+               "lbw: %llu inode %lu\n",
+               data->task.tk_pid, sync,
+               data->args.lastbytewritten,
+               data->args.inode->i_ino);
+       task = rpc_run_task(&task_setup_data);
+       if (IS_ERR(task))
+               return PTR_ERR(task);
+       if (sync == false)
+               goto out;
+       status = nfs4_wait_for_completion_rpc_task(task);
+       if (status != 0)
+               goto out;
+       status = task->tk_status;
+ out:
+       dprintk("%s: status %d\n", __func__, status);
+       rpc_put_task(task);
+       return status;
+ }
  #endif /* CONFIG_NFS_V4_1 */
  
  struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
@@@ -5833,7 -5838,6 +5930,7 @@@ const struct nfs_rpc_ops nfs_v4_cliento
        .close_context  = nfs4_close_context,
        .open_context   = nfs4_atomic_open,
        .init_client    = nfs4_init_client,
 +      .secinfo        = nfs4_proc_secinfo,
  };
  
  static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
diff --combined fs/nfs/nfs4xdr.c
index 21c3004b72d524c1d5852b04b123586df6863336,40da65e8fa2aa91508b8a22b0ce5186fbb8c79f0..dddfb5795d7b8c67999ebfc819b5cd8021e36cd8
@@@ -46,7 -46,6 +46,7 @@@
  #include <linux/kdev_t.h>
  #include <linux/sunrpc/clnt.h>
  #include <linux/sunrpc/msg_prot.h>
 +#include <linux/sunrpc/gss_api.h>
  #include <linux/nfs.h>
  #include <linux/nfs4.h>
  #include <linux/nfs_fs.h>
@@@ -113,7 -112,7 +113,7 @@@ static int nfs4_stat_to_errno(int)
  #define encode_restorefh_maxsz  (op_encode_hdr_maxsz)
  #define decode_restorefh_maxsz  (op_decode_hdr_maxsz)
  #define encode_fsinfo_maxsz   (encode_getattr_maxsz)
 -#define decode_fsinfo_maxsz   (op_decode_hdr_maxsz + 11)
 +#define decode_fsinfo_maxsz   (op_decode_hdr_maxsz + 15)
  #define encode_renew_maxsz    (op_encode_hdr_maxsz + 3)
  #define decode_renew_maxsz    (op_decode_hdr_maxsz)
  #define encode_setclientid_maxsz \
                                (encode_getattr_maxsz)
  #define decode_fs_locations_maxsz \
                                (0)
 +#define encode_secinfo_maxsz  (op_encode_hdr_maxsz + nfs4_name_maxsz)
 +#define decode_secinfo_maxsz  (op_decode_hdr_maxsz + 4 + (NFS_MAX_SECFLAVORS * (16 + GSS_OID_MAX_LEN)))
  
  #if defined(CONFIG_NFS_V4_1)
  #define NFS4_MAX_MACHINE_NAME_LEN (64)
  #define decode_layoutget_maxsz        (op_decode_hdr_maxsz + 8 + \
                                decode_stateid_maxsz + \
                                XDR_QUADLEN(PNFS_LAYOUT_MAXSIZE))
+ #define encode_layoutcommit_maxsz (op_encode_hdr_maxsz +          \
+                               2 /* offset */ + \
+                               2 /* length */ + \
+                               1 /* reclaim */ + \
+                               encode_stateid_maxsz + \
+                               1 /* new offset (true) */ + \
+                               2 /* last byte written */ + \
+                               1 /* nt_timechanged (false) */ + \
+                               1 /* layoutupdate4 layout type */ + \
+                               1 /* NULL filelayout layoutupdate4 payload */)
+ #define decode_layoutcommit_maxsz (op_decode_hdr_maxsz + 3)
  #else /* CONFIG_NFS_V4_1 */
  #define encode_sequence_maxsz 0
  #define decode_sequence_maxsz 0
                                 decode_putfh_maxsz + \
                                 decode_lookup_maxsz + \
                                 decode_fs_locations_maxsz)
 +#define NFS4_enc_secinfo_sz   (compound_encode_hdr_maxsz + \
 +                              encode_sequence_maxsz + \
 +                              encode_putfh_maxsz + \
 +                              encode_secinfo_maxsz)
 +#define NFS4_dec_secinfo_sz   (compound_decode_hdr_maxsz + \
 +                              decode_sequence_maxsz + \
 +                              decode_putfh_maxsz + \
 +                              decode_secinfo_maxsz)
  #if defined(CONFIG_NFS_V4_1)
  #define NFS4_enc_exchange_id_sz \
                                (compound_encode_hdr_maxsz + \
                                decode_sequence_maxsz + \
                                decode_putfh_maxsz +        \
                                decode_layoutget_maxsz)
+ #define NFS4_enc_layoutcommit_sz (compound_encode_hdr_maxsz + \
+                               encode_sequence_maxsz +\
+                               encode_putfh_maxsz + \
+                               encode_layoutcommit_maxsz + \
+                               encode_getattr_maxsz)
+ #define NFS4_dec_layoutcommit_sz (compound_decode_hdr_maxsz + \
+                               decode_sequence_maxsz + \
+                               decode_putfh_maxsz + \
+                               decode_layoutcommit_maxsz + \
+                               decode_getattr_maxsz)
  
  const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
                                      compound_encode_hdr_maxsz +
@@@ -1631,18 -1643,6 +1654,18 @@@ static void encode_delegreturn(struct x
        hdr->replen += decode_delegreturn_maxsz;
  }
  
 +static void encode_secinfo(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
 +{
 +      int len = name->len;
 +      __be32 *p;
 +
 +      p = reserve_space(xdr, 8 + len);
 +      *p++ = cpu_to_be32(OP_SECINFO);
 +      xdr_encode_opaque(p, name->name, len);
 +      hdr->nops++;
 +      hdr->replen += decode_secinfo_maxsz;
 +}
 +
  #if defined(CONFIG_NFS_V4_1)
  /* NFSv4.1 operations */
  static void encode_exchange_id(struct xdr_stream *xdr,
@@@ -1839,6 -1839,34 +1862,34 @@@ encode_layoutget(struct xdr_stream *xdr
        hdr->nops++;
        hdr->replen += decode_layoutget_maxsz;
  }
+ static int
+ encode_layoutcommit(struct xdr_stream *xdr,
+                   const struct nfs4_layoutcommit_args *args,
+                   struct compound_hdr *hdr)
+ {
+       __be32 *p;
+       dprintk("%s: lbw: %llu type: %d\n", __func__, args->lastbytewritten,
+               NFS_SERVER(args->inode)->pnfs_curr_ld->id);
+       p = reserve_space(xdr, 48 + NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(OP_LAYOUTCOMMIT);
+       /* Only whole file layouts */
+       p = xdr_encode_hyper(p, 0); /* offset */
+       p = xdr_encode_hyper(p, NFS4_MAX_UINT64); /* length */
+       *p++ = cpu_to_be32(0); /* reclaim */
+       p = xdr_encode_opaque_fixed(p, args->stateid.data, NFS4_STATEID_SIZE);
+       *p++ = cpu_to_be32(1); /* newoffset = TRUE */
+       p = xdr_encode_hyper(p, args->lastbytewritten);
+       *p++ = cpu_to_be32(0); /* Never send time_modify_changed */
+       *p++ = cpu_to_be32(NFS_SERVER(args->inode)->pnfs_curr_ld->id);/* type */
+       *p++ = cpu_to_be32(0); /* no file layout payload */
+       hdr->nops++;
+       hdr->replen += decode_layoutcommit_maxsz;
+       return 0;
+ }
  #endif /* CONFIG_NFS_V4_1 */
  
  /*
@@@ -2317,7 -2345,8 +2368,8 @@@ static void nfs4_xdr_enc_commit(struct 
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fh, &hdr);
        encode_commit(xdr, args, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
+       if (args->bitmask)
+               encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
  }
  
@@@ -2488,24 -2517,6 +2540,24 @@@ static void nfs4_xdr_enc_fs_locations(s
        encode_nops(&hdr);
  }
  
 +/*
 + * Encode SECINFO request
 + */
 +static void nfs4_xdr_enc_secinfo(struct rpc_rqst *req,
 +                              struct xdr_stream *xdr,
 +                              struct nfs4_secinfo_arg *args)
 +{
 +      struct compound_hdr hdr = {
 +              .minorversion = nfs4_xdr_minorversion(&args->seq_args),
 +      };
 +
 +      encode_compound_hdr(xdr, req, &hdr);
 +      encode_sequence(xdr, &args->seq_args, &hdr);
 +      encode_putfh(xdr, args->dir_fh, &hdr);
 +      encode_secinfo(xdr, args->name, &hdr);
 +      encode_nops(&hdr);
 +}
 +
  #if defined(CONFIG_NFS_V4_1)
  /*
   * EXCHANGE_ID request
@@@ -2645,7 -2656,31 +2697,31 @@@ static void nfs4_xdr_enc_layoutget(stru
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, NFS_FH(args->inode), &hdr);
        encode_layoutget(xdr, args, &hdr);
+       xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2,
+           args->layout.pages, 0, args->layout.pglen);
+       encode_nops(&hdr);
+ }
+ /*
+  *  Encode LAYOUTCOMMIT request
+  */
+ static int nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
+                                    struct nfs4_layoutcommit_args *args)
+ {
+       struct compound_hdr hdr = {
+               .minorversion = nfs4_xdr_minorversion(&args->seq_args),
+       };
+       encode_compound_hdr(xdr, req, &hdr);
+       encode_sequence(xdr, &args->seq_args, &hdr);
+       encode_putfh(xdr, NFS_FH(args->inode), &hdr);
+       encode_layoutcommit(xdr, args, &hdr);
+       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
+       return 0;
  }
  #endif /* CONFIG_NFS_V4_1 */
  
@@@ -2966,7 -3001,6 +3042,7 @@@ static int decode_attr_error(struct xdr
                if (unlikely(!p))
                        goto out_overflow;
                bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR;
 +              return -be32_to_cpup(p);
        }
        return 0;
  out_overflow:
@@@ -3954,10 -3988,6 +4030,10 @@@ static int decode_getfattr_attrs(struc
        fattr->valid |= status;
  
        status = decode_attr_error(xdr, bitmap);
 +      if (status == -NFS4ERR_WRONGSEC) {
 +              nfs_fixup_secinfo_attributes(fattr, fh);
 +              status = 0;
 +      }
        if (status < 0)
                goto xdr_error;
  
@@@ -4726,73 -4756,6 +4802,73 @@@ static int decode_delegreturn(struct xd
        return decode_op_hdr(xdr, OP_DELEGRETURN);
  }
  
 +static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor *flavor)
 +{
 +      __be32 *p;
 +
 +      p = xdr_inline_decode(xdr, 4);
 +      if (unlikely(!p))
 +              goto out_overflow;
 +      flavor->gss.sec_oid4.len = be32_to_cpup(p);
 +      if (flavor->gss.sec_oid4.len > GSS_OID_MAX_LEN)
 +              goto out_err;
 +
 +      p = xdr_inline_decode(xdr, flavor->gss.sec_oid4.len);
 +      if (unlikely(!p))
 +              goto out_overflow;
 +      memcpy(flavor->gss.sec_oid4.data, p, flavor->gss.sec_oid4.len);
 +
 +      p = xdr_inline_decode(xdr, 8);
 +      if (unlikely(!p))
 +              goto out_overflow;
 +      flavor->gss.qop4 = be32_to_cpup(p++);
 +      flavor->gss.service = be32_to_cpup(p);
 +
 +      return 0;
 +
 +out_overflow:
 +      print_overflow_msg(__func__, xdr);
 +      return -EIO;
 +out_err:
 +      return -EINVAL;
 +}
 +
 +static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
 +{
 +      struct nfs4_secinfo_flavor *sec_flavor;
 +      int status;
 +      __be32 *p;
 +      int i;
 +
 +      status = decode_op_hdr(xdr, OP_SECINFO);
 +      p = xdr_inline_decode(xdr, 4);
 +      if (unlikely(!p))
 +              goto out_overflow;
 +      res->flavors->num_flavors = be32_to_cpup(p);
 +
 +      for (i = 0; i < res->flavors->num_flavors; i++) {
 +              sec_flavor = &res->flavors->flavors[i];
 +              if ((char *)&sec_flavor[1] - (char *)res > PAGE_SIZE)
 +                      break;
 +
 +              p = xdr_inline_decode(xdr, 4);
 +              if (unlikely(!p))
 +                      goto out_overflow;
 +              sec_flavor->flavor = be32_to_cpup(p);
 +
 +              if (sec_flavor->flavor == RPC_AUTH_GSS) {
 +                      if (decode_secinfo_gss(xdr, sec_flavor))
 +                              break;
 +              }
 +      }
 +
 +      return 0;
 +
 +out_overflow:
 +      print_overflow_msg(__func__, xdr);
 +      return -EIO;
 +}
 +
  #if defined(CONFIG_NFS_V4_1)
  static int decode_exchange_id(struct xdr_stream *xdr,
                              struct nfs41_exchange_id_res *res)
@@@ -5063,6 -5026,9 +5139,9 @@@ static int decode_layoutget(struct xdr_
        __be32 *p;
        int status;
        u32 layout_count;
+       struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
+       struct kvec *iov = rcvbuf->head;
+       u32 hdrlen, recvd;
  
        status = decode_op_hdr(xdr, OP_LAYOUTGET);
        if (status)
                return -EINVAL;
        }
  
-       p = xdr_inline_decode(xdr, 24);
+       p = xdr_inline_decode(xdr, 28);
        if (unlikely(!p))
                goto out_overflow;
        p = xdr_decode_hyper(p, &res->range.offset);
        p = xdr_decode_hyper(p, &res->range.length);
        res->range.iomode = be32_to_cpup(p++);
        res->type = be32_to_cpup(p++);
-       status = decode_opaque_inline(xdr, &res->layout.len, (char **)&p);
-       if (unlikely(status))
-               return status;
+       res->layoutp->len = be32_to_cpup(p);
  
        dprintk("%s roff:%lu rlen:%lu riomode:%d, lo_type:0x%x, lo.len:%d\n",
                __func__,
                (unsigned long)res->range.length,
                res->range.iomode,
                res->type,
-               res->layout.len);
+               res->layoutp->len);
+       hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base;
+       recvd = req->rq_rcv_buf.len - hdrlen;
+       if (res->layoutp->len > recvd) {
+               dprintk("NFS: server cheating in layoutget reply: "
+                               "layout len %u > recvd %u\n",
+                               res->layoutp->len, recvd);
+               return -EINVAL;
+       }
  
-       /* nfs4_proc_layoutget allocated a single page */
-       if (res->layout.len > PAGE_SIZE)
-               return -ENOMEM;
-       memcpy(res->layout.buf, p, res->layout.len);
+       xdr_read_pages(xdr, res->layoutp->len);
  
        if (layout_count > 1) {
                /* We only handle a length one array at the moment.  Any
@@@ -5119,6 -5088,35 +5201,35 @@@ out_overflow
        print_overflow_msg(__func__, xdr);
        return -EIO;
  }
+ static int decode_layoutcommit(struct xdr_stream *xdr,
+                              struct rpc_rqst *req,
+                              struct nfs4_layoutcommit_res *res)
+ {
+       __be32 *p;
+       __u32 sizechanged;
+       int status;
+       status = decode_op_hdr(xdr, OP_LAYOUTCOMMIT);
+       if (status)
+               return status;
+       p = xdr_inline_decode(xdr, 4);
+       if (unlikely(!p))
+               goto out_overflow;
+       sizechanged = be32_to_cpup(p);
+       if (sizechanged) {
+               /* throw away new size */
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out_overflow;
+       }
+       return 0;
+ out_overflow:
+       print_overflow_msg(__func__, xdr);
+       return -EIO;
+ }
  #endif /* CONFIG_NFS_V4_1 */
  
  /*
@@@ -5836,8 -5834,9 +5947,9 @@@ static int nfs4_xdr_dec_commit(struct r
        status = decode_commit(xdr, res);
        if (status)
                goto out;
-       decode_getfattr(xdr, res->fattr, res->server,
-                       !RPC_IS_ASYNC(rqstp->rq_task));
+       if (res->fattr)
+               decode_getfattr(xdr, res->fattr, res->server,
+                               !RPC_IS_ASYNC(rqstp->rq_task));
  out:
        return status;
  }
        return status;
  }
  
 +/*
 + * Decode SECINFO response
 + */
 +static int nfs4_xdr_dec_secinfo(struct rpc_rqst *rqstp,
 +                              struct xdr_stream *xdr,
 +                              struct nfs4_secinfo_res *res)
 +{
 +      struct compound_hdr hdr;
 +      int status;
 +
 +      status = decode_compound_hdr(xdr, &hdr);
 +      if (status)
 +              goto out;
 +      status = decode_sequence(xdr, &res->seq_res, rqstp);
 +      if (status)
 +              goto out;
 +      status = decode_putfh(xdr);
 +      if (status)
 +              goto out;
 +      status = decode_secinfo(xdr, res);
 +      if (status)
 +              goto out;
 +out:
 +      return status;
 +}
 +
  #if defined(CONFIG_NFS_V4_1)
  /*
   * Decode EXCHANGE_ID response
@@@ -6205,6 -6178,34 +6317,34 @@@ static int nfs4_xdr_dec_layoutget(struc
  out:
        return status;
  }
+ /*
+  * Decode LAYOUTCOMMIT response
+  */
+ static int nfs4_xdr_dec_layoutcommit(struct rpc_rqst *rqstp,
+                                    struct xdr_stream *xdr,
+                                    struct nfs4_layoutcommit_res *res)
+ {
+       struct compound_hdr hdr;
+       int status;
+       status = decode_compound_hdr(xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_sequence(xdr, &res->seq_res, rqstp);
+       if (status)
+               goto out;
+       status = decode_putfh(xdr);
+       if (status)
+               goto out;
+       status = decode_layoutcommit(xdr, rqstp, res);
+       if (status)
+               goto out;
+       decode_getfattr(xdr, res->fattr, res->server,
+                       !RPC_IS_ASYNC(rqstp->rq_task));
+ out:
+       return status;
+ }
  #endif /* CONFIG_NFS_V4_1 */
  
  /**
@@@ -6319,6 -6320,10 +6459,6 @@@ static struct 
        { NFS4ERR_SYMLINK,      -ELOOP          },
        { NFS4ERR_OP_ILLEGAL,   -EOPNOTSUPP     },
        { NFS4ERR_DEADLOCK,     -EDEADLK        },
 -      { NFS4ERR_WRONGSEC,     -EPERM          }, /* FIXME: this needs
 -                                                  * to be handled by a
 -                                                  * middle-layer.
 -                                                  */
        { -1,                   -EIO            }
  };
  
@@@ -6393,7 -6398,6 +6533,7 @@@ struct rpc_procinfo     nfs4_procedures[] 
        PROC(SETACL,            enc_setacl,             dec_setacl),
        PROC(FS_LOCATIONS,      enc_fs_locations,       dec_fs_locations),
        PROC(RELEASE_LOCKOWNER, enc_release_lockowner,  dec_release_lockowner),
 +      PROC(SECINFO,           enc_secinfo,            dec_secinfo),
  #if defined(CONFIG_NFS_V4_1)
        PROC(EXCHANGE_ID,       enc_exchange_id,        dec_exchange_id),
        PROC(CREATE_SESSION,    enc_create_session,     dec_create_session),
        PROC(RECLAIM_COMPLETE,  enc_reclaim_complete,   dec_reclaim_complete),
        PROC(GETDEVICEINFO,     enc_getdeviceinfo,      dec_getdeviceinfo),
        PROC(LAYOUTGET,         enc_layoutget,          dec_layoutget),
+       PROC(LAYOUTCOMMIT,      enc_layoutcommit,       dec_layoutcommit),
  #endif /* CONFIG_NFS_V4_1 */
  };
  
diff --combined include/linux/nfs4.h
index 7e7f6b7e46b170cf9613ddb2b45049ff8677f87c,bf22cfaaeee5612e8702e3f74213c53ed83ec807..b528f6d4b860d394e2870a02022366a209abcec0
@@@ -550,7 -550,6 +550,7 @@@ enum 
        NFSPROC4_CLNT_SETACL,
        NFSPROC4_CLNT_FS_LOCATIONS,
        NFSPROC4_CLNT_RELEASE_LOCKOWNER,
 +      NFSPROC4_CLNT_SECINFO,
  
        /* nfs41 */
        NFSPROC4_CLNT_EXCHANGE_ID,
        NFSPROC4_CLNT_RECLAIM_COMPLETE,
        NFSPROC4_CLNT_LAYOUTGET,
        NFSPROC4_CLNT_GETDEVICEINFO,
+       NFSPROC4_CLNT_LAYOUTCOMMIT,
  };
  
  /* nfs41 types */
diff --combined include/linux/nfs_xdr.h
index fa1ba78ca2c8eeec082343c1d66c19cf3434d199,a6e21b10f43d53aeaa4590f8107bb5f31edc2e83..78b101e487eac78113ea001974d7e50ebf6b6e8b
@@@ -3,7 -3,6 +3,7 @@@
  
  #include <linux/nfsacl.h>
  #include <linux/nfs3.h>
 +#include <linux/sunrpc/gss_api.h>
  
  /*
   * To change the maximum rsize and wsize supported by the NFS client, adjust
@@@ -15,9 -14,6 +15,9 @@@
  #define NFS_DEF_FILE_IO_SIZE  (4096U)
  #define NFS_MIN_FILE_IO_SIZE  (1024U)
  
 +/* Forward declaration for NFS v3 */
 +struct nfs4_secinfo_flavors;
 +
  struct nfs_fsid {
        uint64_t                major;
        uint64_t                minor;
@@@ -82,7 -78,6 +82,7 @@@ struct nfs_fattr 
  #define NFS_ATTR_FATTR_CHANGE         (1U << 17)
  #define NFS_ATTR_FATTR_PRECHANGE      (1U << 18)
  #define NFS_ATTR_FATTR_V4_REFERRAL    (1U << 19)      /* NFSv4 referral */
 +#define NFS_ATTR_FATTR_MOUNTPOINT     (1U << 20)      /* Treat as mountpoint */
  
  #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \
                | NFS_ATTR_FATTR_MODE \
@@@ -195,8 -190,9 +195,9 @@@ struct nfs4_get_lease_time_res 
  #define PNFS_LAYOUT_MAXSIZE 4096
  
  struct nfs4_layoutdriver_data {
+       struct page **pages;
+       __u32 pglen;
        __u32 len;
-       void *buf;
  };
  
  struct pnfs_layout_range {
@@@ -214,6 -210,7 +215,7 @@@ struct nfs4_layoutget_args 
        struct nfs_open_context *ctx;
        struct nfs4_sequence_args seq_args;
        nfs4_stateid stateid;
+       struct nfs4_layoutdriver_data layout;
  };
  
  struct nfs4_layoutget_res {
        struct pnfs_layout_range range;
        __u32 type;
        nfs4_stateid stateid;
-       struct nfs4_layoutdriver_data layout;
        struct nfs4_sequence_res seq_res;
+       struct nfs4_layoutdriver_data *layoutp;
  };
  
  struct nfs4_layoutget {
@@@ -241,6 -238,29 +243,29 @@@ struct nfs4_getdeviceinfo_res 
        struct nfs4_sequence_res seq_res;
  };
  
+ struct nfs4_layoutcommit_args {
+       nfs4_stateid stateid;
+       __u64 lastbytewritten;
+       struct inode *inode;
+       const u32 *bitmask;
+       struct nfs4_sequence_args seq_args;
+ };
+ struct nfs4_layoutcommit_res {
+       struct nfs_fattr *fattr;
+       const struct nfs_server *server;
+       struct nfs4_sequence_res seq_res;
+ };
+ struct nfs4_layoutcommit_data {
+       struct rpc_task task;
+       struct nfs_fattr fattr;
+       struct pnfs_layout_segment *lseg;
+       struct rpc_cred *cred;
+       struct nfs4_layoutcommit_args args;
+       struct nfs4_layoutcommit_res res;
+ };
  /*
   * Arguments to the open call.
   */
@@@ -941,38 -961,6 +966,38 @@@ struct nfs4_fs_locations_res 
        struct nfs4_sequence_res        seq_res;
  };
  
 +struct nfs4_secinfo_oid {
 +      unsigned int len;
 +      char data[GSS_OID_MAX_LEN];
 +};
 +
 +struct nfs4_secinfo_gss {
 +      struct nfs4_secinfo_oid sec_oid4;
 +      unsigned int qop4;
 +      unsigned int service;
 +};
 +
 +struct nfs4_secinfo_flavor {
 +      unsigned int            flavor;
 +      struct nfs4_secinfo_gss gss;
 +};
 +
 +struct nfs4_secinfo_flavors {
 +      unsigned int num_flavors;
 +      struct nfs4_secinfo_flavor flavors[0];
 +};
 +
 +struct nfs4_secinfo_arg {
 +      const struct nfs_fh             *dir_fh;
 +      const struct qstr               *name;
 +      struct nfs4_sequence_args       seq_args;
 +};
 +
 +struct nfs4_secinfo_res {
 +      struct nfs4_secinfo_flavors     *flavors;
 +      struct nfs4_sequence_res        seq_res;
 +};
 +
  #endif /* CONFIG_NFS_V4 */
  
  struct nfstime4 {
@@@ -1077,6 -1065,7 +1102,7 @@@ struct nfs_write_data 
        struct nfs_writeres     res;            /* result struct */
        struct pnfs_layout_segment *lseg;
        struct nfs_client       *ds_clp;        /* pNFS data server */
+       int                     ds_commit_index;
        const struct rpc_call_ops *mds_ops;
        int (*write_done_cb) (struct rpc_task *task, struct nfs_write_data *data);
  #ifdef CONFIG_NFS_V4
@@@ -1108,7 -1097,7 +1134,7 @@@ struct nfs_rpc_ops 
                            struct nfs_fattr *);
        int     (*setattr) (struct dentry *, struct nfs_fattr *,
                            struct iattr *);
 -      int     (*lookup)  (struct inode *, struct qstr *,
 +      int     (*lookup)  (struct rpc_clnt *clnt, struct inode *, struct qstr *,
                            struct nfs_fh *, struct nfs_fattr *);
        int     (*access)  (struct inode *, struct nfs_access_entry *);
        int     (*readlink)(struct inode *, struct page *, unsigned int,
                                struct iattr *iattr);
        int     (*init_client) (struct nfs_client *, const struct rpc_timeout *,
                                const char *, rpc_authflavor_t, int);
 +      int     (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *);
  };
  
  /*