提交 5b174fd6 编写于 作者: L Linus Torvalds

Merge branch 'for-3.16' of git://linux-nfs.org/~bfields/linux

Pull nfsd updates from Bruce Fields:
 "The largest piece is a long-overdue rewrite of the xdr code to remove
  some annoying limitations: for example, there was no way to return
  ACLs larger than 4K, and readdir results were returned only in 4k
  chunks, limiting performance on large directories.

  Also:
        - part of Neil Brown's work to make NFS work reliably over the
          loopback interface (so client and server can run on the same
          machine without deadlocks).  The rest of it is coming through
          other trees.
        - cleanup and bugfixes for some of the server RDMA code, from
          Steve Wise.
        - Various cleanup of NFSv4 state code in preparation for an
          overhaul of the locking, from Jeff, Trond, and Benny.
        - smaller bugfixes and cleanup from Christoph Hellwig and
          Kinglong Mee.

  Thanks to everyone!

  This summer looks likely to be busier than usual for knfsd.  Hopefully
  we won't break it too badly; testing definitely welcomed"

* 'for-3.16' of git://linux-nfs.org/~bfields/linux: (100 commits)
  nfsd4: fix FREE_STATEID lockowner leak
  svcrdma: Fence LOCAL_INV work requests
  svcrdma: refactor marshalling logic
  nfsd: don't halt scanning the DRC LRU list when there's an RC_INPROG entry
  nfs4: remove unused CHANGE_SECURITY_LABEL
  nfsd4: kill READ64
  nfsd4: kill READ32
  nfsd4: simplify server xdr->next_page use
  nfsd4: hash deleg stateid only on successful nfs4_set_delegation
  nfsd4: rename recall_lock to state_lock
  nfsd: remove unneeded zeroing of fields in nfsd4_proc_compound
  nfsd: fix setting of NFS4_OO_CONFIRMED in nfsd4_open
  nfsd4: use recall_lock for delegation hashing
  nfsd: fix laundromat next-run-time calculation
  nfsd: make nfsd4_encode_fattr static
  SUNRPC/NFSD: Remove using of dprintk with KERN_WARNING
  nfsd: remove unused function nfsd_read_file
  nfsd: getattr for FATTR4_WORD0_FILES_AVAIL needs the statfs buffer
  NFSD: Error out when getting more than one fsloc/secinfo/uuid
  NFSD: Using type of uint32_t for ex_nflavors instead of int
  ...
...@@ -176,7 +176,5 @@ Nonstandard compound limitations: ...@@ -176,7 +176,5 @@ Nonstandard compound limitations:
ca_maxrequestsize request and a ca_maxresponsesize reply, so we may ca_maxrequestsize request and a ca_maxresponsesize reply, so we may
fail to live up to the promise we made in CREATE_SESSION fore channel fail to live up to the promise we made in CREATE_SESSION fore channel
negotiation. negotiation.
* No more than one read-like operation allowed per compound; encoding
replies that cross page boundaries (except for read data) not handled.
See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues. See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues.
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <uapi/linux/nfs3.h>
#define NLMDBG_FACILITY NLMDBG_XDR #define NLMDBG_FACILITY NLMDBG_XDR
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ) #if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <uapi/linux/nfs2.h>
#define NLMDBG_FACILITY NLMDBG_XDR #define NLMDBG_FACILITY NLMDBG_XDR
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ) #if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
......
...@@ -622,8 +622,8 @@ static int __init init_nlm(void) ...@@ -622,8 +622,8 @@ static int __init init_nlm(void)
err_pernet: err_pernet:
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
unregister_sysctl_table(nlm_sysctl_table); unregister_sysctl_table(nlm_sysctl_table);
#endif
err_sysctl: err_sysctl:
#endif
return err; return err;
} }
......
...@@ -14,12 +14,11 @@ ...@@ -14,12 +14,11 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/sunrpc/addr.h> #include <linux/sunrpc/addr.h>
#include <linux/nfsd/nfsfh.h>
#include <linux/nfsd/export.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <linux/lockd/share.h> #include <linux/lockd/share.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <uapi/linux/nfs2.h>
#define NLMDBG_FACILITY NLMDBG_SVCSUBS #define NLMDBG_FACILITY NLMDBG_SVCSUBS
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/lockd/lockd.h> #include <linux/lockd/lockd.h>
#include <uapi/linux/nfs2.h>
#define NLMDBG_FACILITY NLMDBG_XDR #define NLMDBG_FACILITY NLMDBG_XDR
......
...@@ -2750,7 +2750,7 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync) ...@@ -2750,7 +2750,7 @@ static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
#define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_MOUNTED_ON_FILEID - 1UL) #define FATTR4_WORD1_NFS40_MASK (2*FATTR4_WORD1_MOUNTED_ON_FILEID - 1UL)
#define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL) #define FATTR4_WORD2_NFS41_MASK (2*FATTR4_WORD2_SUPPATTR_EXCLCREAT - 1UL)
#define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_CHANGE_SECURITY_LABEL - 1UL) #define FATTR4_WORD2_NFS42_MASK (2*FATTR4_WORD2_SECURITY_LABEL - 1UL)
static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
{ {
......
...@@ -49,7 +49,7 @@ struct svc_rqst; ...@@ -49,7 +49,7 @@ struct svc_rqst;
struct nfs4_acl *nfs4_acl_new(int); struct nfs4_acl *nfs4_acl_new(int);
int nfs4_acl_get_whotype(char *, u32); int nfs4_acl_get_whotype(char *, u32);
__be32 nfs4_acl_write_who(int who, __be32 **p, int *len); __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
struct nfs4_acl **acl); struct nfs4_acl **acl);
......
/* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */ /* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/user_namespace.h>
#include "nfsd.h" #include "nfsd.h"
#include "auth.h" #include "auth.h"
...@@ -25,7 +24,6 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) ...@@ -25,7 +24,6 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
struct cred *new; struct cred *new;
int i; int i;
int flags = nfsexp_flags(rqstp, exp); int flags = nfsexp_flags(rqstp, exp);
int ret;
validate_process_creds(); validate_process_creds();
...@@ -86,8 +84,7 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) ...@@ -86,8 +84,7 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
return 0; return 0;
oom: oom:
ret = -ENOMEM;
abort_creds(new); abort_creds(new);
return ret; return -ENOMEM;
} }
...@@ -17,17 +17,12 @@ ...@@ -17,17 +17,12 @@
#include <linux/exportfs.h> #include <linux/exportfs.h>
#include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/svc_xprt.h>
#include <net/ipv6.h>
#include "nfsd.h" #include "nfsd.h"
#include "nfsfh.h" #include "nfsfh.h"
#include "netns.h" #include "netns.h"
#define NFSDDBG_FACILITY NFSDDBG_EXPORT #define NFSDDBG_FACILITY NFSDDBG_EXPORT
typedef struct auth_domain svc_client;
typedef struct svc_export svc_export;
/* /*
* We have two caches. * We have two caches.
* One maps client+vfsmnt+dentry to export options - the export map * One maps client+vfsmnt+dentry to export options - the export map
...@@ -73,7 +68,7 @@ static struct svc_expkey *svc_expkey_lookup(struct cache_detail *cd, struct svc_ ...@@ -73,7 +68,7 @@ static struct svc_expkey *svc_expkey_lookup(struct cache_detail *cd, struct svc_
static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen)
{ {
/* client fsidtype fsid [path] */ /* client fsidtype fsid expiry [path] */
char *buf; char *buf;
int len; int len;
struct auth_domain *dom = NULL; struct auth_domain *dom = NULL;
...@@ -295,13 +290,19 @@ svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new, ...@@ -295,13 +290,19 @@ svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new,
static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc) static void nfsd4_fslocs_free(struct nfsd4_fs_locations *fsloc)
{ {
struct nfsd4_fs_location *locations = fsloc->locations;
int i; int i;
if (!locations)
return;
for (i = 0; i < fsloc->locations_count; i++) { for (i = 0; i < fsloc->locations_count; i++) {
kfree(fsloc->locations[i].path); kfree(locations[i].path);
kfree(fsloc->locations[i].hosts); kfree(locations[i].hosts);
} }
kfree(fsloc->locations);
kfree(locations);
fsloc->locations = NULL;
} }
static void svc_export_put(struct kref *ref) static void svc_export_put(struct kref *ref)
...@@ -388,6 +389,10 @@ fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) ...@@ -388,6 +389,10 @@ fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc)
int len; int len;
int migrated, i, err; int migrated, i, err;
/* more than one fsloc */
if (fsloc->locations)
return -EINVAL;
/* listsize */ /* listsize */
err = get_uint(mesg, &fsloc->locations_count); err = get_uint(mesg, &fsloc->locations_count);
if (err) if (err)
...@@ -437,13 +442,18 @@ fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc) ...@@ -437,13 +442,18 @@ fsloc_parse(char **mesg, char *buf, struct nfsd4_fs_locations *fsloc)
static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp) static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
{ {
int listsize, err;
struct exp_flavor_info *f; struct exp_flavor_info *f;
u32 listsize;
int err;
/* more than one secinfo */
if (exp->ex_nflavors)
return -EINVAL;
err = get_int(mesg, &listsize); err = get_uint(mesg, &listsize);
if (err) if (err)
return err; return err;
if (listsize < 0 || listsize > MAX_SECINFO_LIST) if (listsize > MAX_SECINFO_LIST)
return -EINVAL; return -EINVAL;
for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) { for (f = exp->ex_flavors; f < exp->ex_flavors + listsize; f++) {
...@@ -474,6 +484,27 @@ static inline int ...@@ -474,6 +484,27 @@ static inline int
secinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; } secinfo_parse(char **mesg, char *buf, struct svc_export *exp) { return 0; }
#endif #endif
static inline int
uuid_parse(char **mesg, char *buf, unsigned char **puuid)
{
int len;
/* more than one uuid */
if (*puuid)
return -EINVAL;
/* expect a 16 byte uuid encoded as \xXXXX... */
len = qword_get(mesg, buf, PAGE_SIZE);
if (len != EX_UUID_LEN)
return -EINVAL;
*puuid = kmemdup(buf, EX_UUID_LEN, GFP_KERNEL);
if (*puuid == NULL)
return -ENOMEM;
return 0;
}
static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
{ {
/* client path expiry [flags anonuid anongid fsid] */ /* client path expiry [flags anonuid anongid fsid] */
...@@ -552,18 +583,9 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) ...@@ -552,18 +583,9 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
while ((len = qword_get(&mesg, buf, PAGE_SIZE)) > 0) { while ((len = qword_get(&mesg, buf, PAGE_SIZE)) > 0) {
if (strcmp(buf, "fsloc") == 0) if (strcmp(buf, "fsloc") == 0)
err = fsloc_parse(&mesg, buf, &exp.ex_fslocs); err = fsloc_parse(&mesg, buf, &exp.ex_fslocs);
else if (strcmp(buf, "uuid") == 0) { else if (strcmp(buf, "uuid") == 0)
/* expect a 16 byte uuid encoded as \xXXXX... */ err = uuid_parse(&mesg, buf, &exp.ex_uuid);
len = qword_get(&mesg, buf, PAGE_SIZE); else if (strcmp(buf, "secinfo") == 0)
if (len != 16)
err = -EINVAL;
else {
exp.ex_uuid =
kmemdup(buf, 16, GFP_KERNEL);
if (exp.ex_uuid == NULL)
err = -ENOMEM;
}
} else if (strcmp(buf, "secinfo") == 0)
err = secinfo_parse(&mesg, buf, &exp); err = secinfo_parse(&mesg, buf, &exp);
else else
/* quietly ignore unknown words and anything /* quietly ignore unknown words and anything
...@@ -649,7 +671,7 @@ static int svc_export_show(struct seq_file *m, ...@@ -649,7 +671,7 @@ static int svc_export_show(struct seq_file *m,
if (exp->ex_uuid) { if (exp->ex_uuid) {
int i; int i;
seq_puts(m, ",uuid="); seq_puts(m, ",uuid=");
for (i=0; i<16; i++) { for (i = 0; i < EX_UUID_LEN; i++) {
if ((i&3) == 0 && i) if ((i&3) == 0 && i)
seq_putc(m, ':'); seq_putc(m, ':');
seq_printf(m, "%02x", exp->ex_uuid[i]); seq_printf(m, "%02x", exp->ex_uuid[i]);
...@@ -771,7 +793,7 @@ svc_export_update(struct svc_export *new, struct svc_export *old) ...@@ -771,7 +793,7 @@ svc_export_update(struct svc_export *new, struct svc_export *old)
static struct svc_expkey * static struct svc_expkey *
exp_find_key(struct cache_detail *cd, svc_client *clp, int fsid_type, exp_find_key(struct cache_detail *cd, struct auth_domain *clp, int fsid_type,
u32 *fsidv, struct cache_req *reqp) u32 *fsidv, struct cache_req *reqp)
{ {
struct svc_expkey key, *ek; struct svc_expkey key, *ek;
...@@ -793,9 +815,9 @@ exp_find_key(struct cache_detail *cd, svc_client *clp, int fsid_type, ...@@ -793,9 +815,9 @@ exp_find_key(struct cache_detail *cd, svc_client *clp, int fsid_type,
return ek; return ek;
} }
static struct svc_export *
static svc_export *exp_get_by_name(struct cache_detail *cd, svc_client *clp, exp_get_by_name(struct cache_detail *cd, struct auth_domain *clp,
const struct path *path, struct cache_req *reqp) const struct path *path, struct cache_req *reqp)
{ {
struct svc_export *exp, key; struct svc_export *exp, key;
int err; int err;
...@@ -819,11 +841,11 @@ static svc_export *exp_get_by_name(struct cache_detail *cd, svc_client *clp, ...@@ -819,11 +841,11 @@ static svc_export *exp_get_by_name(struct cache_detail *cd, svc_client *clp,
/* /*
* Find the export entry for a given dentry. * Find the export entry for a given dentry.
*/ */
static struct svc_export *exp_parent(struct cache_detail *cd, svc_client *clp, static struct svc_export *
struct path *path) exp_parent(struct cache_detail *cd, struct auth_domain *clp, struct path *path)
{ {
struct dentry *saved = dget(path->dentry); struct dentry *saved = dget(path->dentry);
svc_export *exp = exp_get_by_name(cd, clp, path, NULL); struct svc_export *exp = exp_get_by_name(cd, clp, path, NULL);
while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(path->dentry)) { while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(path->dentry)) {
struct dentry *parent = dget_parent(path->dentry); struct dentry *parent = dget_parent(path->dentry);
...@@ -844,7 +866,7 @@ static struct svc_export *exp_parent(struct cache_detail *cd, svc_client *clp, ...@@ -844,7 +866,7 @@ static struct svc_export *exp_parent(struct cache_detail *cd, svc_client *clp,
* since its harder to fool a kernel module than a user space program. * since its harder to fool a kernel module than a user space program.
*/ */
int int
exp_rootfh(struct net *net, svc_client *clp, char *name, exp_rootfh(struct net *net, struct auth_domain *clp, char *name,
struct knfsd_fh *f, int maxsize) struct knfsd_fh *f, int maxsize)
{ {
struct svc_export *exp; struct svc_export *exp;
......
/* /*
* include/linux/nfsd/export.h
*
* Public declarations for NFS exports. The definitions for the
* syscall interface are in nfsctl.h
*
* Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de> * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
*/ */
#ifndef NFSD_EXPORT_H #ifndef NFSD_EXPORT_H
#define NFSD_EXPORT_H #define NFSD_EXPORT_H
# include <linux/nfsd/nfsfh.h> #include <linux/sunrpc/cache.h>
#include <uapi/linux/nfsd/export.h> #include <uapi/linux/nfsd/export.h>
struct knfsd_fh;
struct svc_fh;
struct svc_rqst;
/* /*
* FS Locations * FS Locations
*/ */
...@@ -38,6 +37,7 @@ struct nfsd4_fs_locations { ...@@ -38,6 +37,7 @@ struct nfsd4_fs_locations {
* spkm3i, and spkm3p (and using all 8 at once should be rare). * spkm3i, and spkm3p (and using all 8 at once should be rare).
*/ */
#define MAX_SECINFO_LIST 8 #define MAX_SECINFO_LIST 8
#define EX_UUID_LEN 16
struct exp_flavor_info { struct exp_flavor_info {
u32 pseudoflavor; u32 pseudoflavor;
...@@ -54,7 +54,7 @@ struct svc_export { ...@@ -54,7 +54,7 @@ struct svc_export {
int ex_fsid; int ex_fsid;
unsigned char * ex_uuid; /* 16 byte fsid */ unsigned char * ex_uuid; /* 16 byte fsid */
struct nfsd4_fs_locations ex_fslocs; struct nfsd4_fs_locations ex_fslocs;
int ex_nflavors; uint32_t ex_nflavors;
struct exp_flavor_info ex_flavors[MAX_SECINFO_LIST]; struct exp_flavor_info ex_flavors[MAX_SECINFO_LIST];
struct cache_detail *cd; struct cache_detail *cd;
}; };
......
...@@ -97,25 +97,14 @@ static ssize_t fault_inject_read(struct file *file, char __user *buf, ...@@ -97,25 +97,14 @@ static ssize_t fault_inject_read(struct file *file, char __user *buf,
{ {
static u64 val; static u64 val;
char read_buf[25]; char read_buf[25];
size_t size, ret; size_t size;
loff_t pos = *ppos; loff_t pos = *ppos;
if (!pos) if (!pos)
nfsd_inject_get(file_inode(file)->i_private, &val); nfsd_inject_get(file_inode(file)->i_private, &val);
size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val); size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val);
if (pos < 0) return simple_read_from_buffer(buf, len, ppos, read_buf, size);
return -EINVAL;
if (pos >= size || !len)
return 0;
if (len > size - pos)
len = size - pos;
ret = copy_to_user(buf, read_buf + pos, len);
if (ret == len)
return -EFAULT;
len -= ret;
*ppos = pos + len;
return len;
} }
static ssize_t fault_inject_write(struct file *file, const char __user *buf, static ssize_t fault_inject_write(struct file *file, const char __user *buf,
......
...@@ -56,7 +56,7 @@ static inline void nfsd_idmap_shutdown(struct net *net) ...@@ -56,7 +56,7 @@ static inline void nfsd_idmap_shutdown(struct net *net)
__be32 nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, kuid_t *); __be32 nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, kuid_t *);
__be32 nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, kgid_t *); __be32 nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, kgid_t *);
__be32 nfsd4_encode_user(struct svc_rqst *, kuid_t, __be32 **, int *); __be32 nfsd4_encode_user(struct xdr_stream *, struct svc_rqst *, kuid_t);
__be32 nfsd4_encode_group(struct svc_rqst *, kgid_t, __be32 **, int *); __be32 nfsd4_encode_group(struct xdr_stream *, struct svc_rqst *, kgid_t);
#endif /* LINUX_NFSD_IDMAP_H */ #endif /* LINUX_NFSD_IDMAP_H */
...@@ -182,7 +182,8 @@ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessarg ...@@ -182,7 +182,8 @@ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessarg
static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p, static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_getaclargs *argp) struct nfsd3_getaclargs *argp)
{ {
if (!(p = nfs2svc_decode_fh(p, &argp->fh))) p = nfs2svc_decode_fh(p, &argp->fh);
if (!p)
return 0; return 0;
argp->mask = ntohl(*p); p++; argp->mask = ntohl(*p); p++;
...@@ -197,7 +198,8 @@ static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -197,7 +198,8 @@ static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p,
unsigned int base; unsigned int base;
int n; int n;
if (!(p = nfs2svc_decode_fh(p, &argp->fh))) p = nfs2svc_decode_fh(p, &argp->fh);
if (!p)
return 0; return 0;
argp->mask = ntohl(*p++); argp->mask = ntohl(*p++);
if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) || if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
...@@ -218,7 +220,8 @@ static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -218,7 +220,8 @@ static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p,
static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p, static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_fhandle *argp) struct nfsd_fhandle *argp)
{ {
if (!(p = nfs2svc_decode_fh(p, &argp->fh))) p = nfs2svc_decode_fh(p, &argp->fh);
if (!p)
return 0; return 0;
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
...@@ -226,7 +229,8 @@ static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -226,7 +229,8 @@ static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p,
static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p, static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_accessargs *argp) struct nfsd3_accessargs *argp)
{ {
if (!(p = nfs2svc_decode_fh(p, &argp->fh))) p = nfs2svc_decode_fh(p, &argp->fh);
if (!p)
return 0; return 0;
argp->access = ntohl(*p++); argp->access = ntohl(*p++);
......
...@@ -128,7 +128,8 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst * rqstp, ...@@ -128,7 +128,8 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst * rqstp,
static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p, static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_getaclargs *args) struct nfsd3_getaclargs *args)
{ {
if (!(p = nfs3svc_decode_fh(p, &args->fh))) p = nfs3svc_decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
args->mask = ntohl(*p); p++; args->mask = ntohl(*p); p++;
...@@ -143,7 +144,8 @@ static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -143,7 +144,8 @@ static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p,
unsigned int base; unsigned int base;
int n; int n;
if (!(p = nfs3svc_decode_fh(p, &args->fh))) p = nfs3svc_decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
args->mask = ntohl(*p++); args->mask = ntohl(*p++);
if (args->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) || if (args->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) ||
......
...@@ -278,7 +278,8 @@ void fill_post_wcc(struct svc_fh *fhp) ...@@ -278,7 +278,8 @@ void fill_post_wcc(struct svc_fh *fhp)
int int
nfs3svc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p, struct nfsd_fhandle *args) nfs3svc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p, struct nfsd_fhandle *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
...@@ -287,7 +288,8 @@ int ...@@ -287,7 +288,8 @@ int
nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p, nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_sattrargs *args) struct nfsd3_sattrargs *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
p = decode_sattr3(p, &args->attrs); p = decode_sattr3(p, &args->attrs);
...@@ -315,7 +317,8 @@ int ...@@ -315,7 +317,8 @@ int
nfs3svc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p, nfs3svc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_accessargs *args) struct nfsd3_accessargs *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
args->access = ntohl(*p++); args->access = ntohl(*p++);
...@@ -330,7 +333,8 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -330,7 +333,8 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
int v; int v;
u32 max_blocksize = svc_max_payload(rqstp); u32 max_blocksize = svc_max_payload(rqstp);
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
p = xdr_decode_hyper(p, &args->offset); p = xdr_decode_hyper(p, &args->offset);
...@@ -360,7 +364,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -360,7 +364,8 @@ nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
unsigned int len, v, hdr, dlen; unsigned int len, v, hdr, dlen;
u32 max_blocksize = svc_max_payload(rqstp); u32 max_blocksize = svc_max_payload(rqstp);
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
p = xdr_decode_hyper(p, &args->offset); p = xdr_decode_hyper(p, &args->offset);
...@@ -535,7 +540,8 @@ int ...@@ -535,7 +540,8 @@ int
nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_readlinkargs *args) struct nfsd3_readlinkargs *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
args->buffer = page_address(*(rqstp->rq_next_page++)); args->buffer = page_address(*(rqstp->rq_next_page++));
...@@ -558,7 +564,8 @@ int ...@@ -558,7 +564,8 @@ int
nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p, nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_readdirargs *args) struct nfsd3_readdirargs *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
p = xdr_decode_hyper(p, &args->cookie); p = xdr_decode_hyper(p, &args->cookie);
args->verf = p; p += 2; args->verf = p; p += 2;
...@@ -580,7 +587,8 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -580,7 +587,8 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p,
int len; int len;
u32 max_blocksize = svc_max_payload(rqstp); u32 max_blocksize = svc_max_payload(rqstp);
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
p = xdr_decode_hyper(p, &args->cookie); p = xdr_decode_hyper(p, &args->cookie);
args->verf = p; p += 2; args->verf = p; p += 2;
...@@ -605,7 +613,8 @@ int ...@@ -605,7 +613,8 @@ int
nfs3svc_decode_commitargs(struct svc_rqst *rqstp, __be32 *p, nfs3svc_decode_commitargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_commitargs *args) struct nfsd3_commitargs *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
p = xdr_decode_hyper(p, &args->offset); p = xdr_decode_hyper(p, &args->offset);
args->count = ntohl(*p++); args->count = ntohl(*p++);
......
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/export.h>
#include "nfsfh.h" #include "nfsfh.h"
#include "nfsd.h" #include "nfsd.h"
#include "acl.h" #include "acl.h"
...@@ -920,20 +919,19 @@ nfs4_acl_get_whotype(char *p, u32 len) ...@@ -920,20 +919,19 @@ nfs4_acl_get_whotype(char *p, u32 len)
return NFS4_ACL_WHO_NAMED; return NFS4_ACL_WHO_NAMED;
} }
__be32 nfs4_acl_write_who(int who, __be32 **p, int *len) __be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who)
{ {
__be32 *p;
int i; int i;
int bytes;
for (i = 0; i < ARRAY_SIZE(s2t_map); i++) { for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
if (s2t_map[i].type != who) if (s2t_map[i].type != who)
continue; continue;
bytes = 4 + (XDR_QUADLEN(s2t_map[i].stringlen) << 2); p = xdr_reserve_space(xdr, s2t_map[i].stringlen + 4);
if (bytes > *len) if (!p)
return nfserr_resource; return nfserr_resource;
*p = xdr_encode_opaque(*p, s2t_map[i].string, p = xdr_encode_opaque(p, s2t_map[i].string,
s2t_map[i].stringlen); s2t_map[i].stringlen);
*len -= bytes;
return 0; return 0;
} }
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
......
...@@ -551,44 +551,43 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen ...@@ -551,44 +551,43 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen
return 0; return 0;
} }
static __be32 encode_ascii_id(u32 id, __be32 **p, int *buflen) static __be32 encode_ascii_id(struct xdr_stream *xdr, u32 id)
{ {
char buf[11]; char buf[11];
int len; int len;
int bytes; __be32 *p;
len = sprintf(buf, "%u", id); len = sprintf(buf, "%u", id);
bytes = 4 + (XDR_QUADLEN(len) << 2); p = xdr_reserve_space(xdr, len + 4);
if (bytes > *buflen) if (!p)
return nfserr_resource; return nfserr_resource;
*p = xdr_encode_opaque(*p, buf, len); p = xdr_encode_opaque(p, buf, len);
*buflen -= bytes;
return 0; return 0;
} }
static __be32 idmap_id_to_name(struct svc_rqst *rqstp, int type, u32 id, __be32 **p, int *buflen) static __be32 idmap_id_to_name(struct xdr_stream *xdr,
struct svc_rqst *rqstp, int type, u32 id)
{ {
struct ent *item, key = { struct ent *item, key = {
.id = id, .id = id,
.type = type, .type = type,
}; };
__be32 *p;
int ret; int ret;
int bytes;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname)); strlcpy(key.authname, rqst_authname(rqstp), sizeof(key.authname));
ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item); ret = idmap_lookup(rqstp, idtoname_lookup, &key, nn->idtoname_cache, &item);
if (ret == -ENOENT) if (ret == -ENOENT)
return encode_ascii_id(id, p, buflen); return encode_ascii_id(xdr, id);
if (ret) if (ret)
return nfserrno(ret); return nfserrno(ret);
ret = strlen(item->name); ret = strlen(item->name);
WARN_ON_ONCE(ret > IDMAP_NAMESZ); WARN_ON_ONCE(ret > IDMAP_NAMESZ);
bytes = 4 + (XDR_QUADLEN(ret) << 2); p = xdr_reserve_space(xdr, ret + 4);
if (bytes > *buflen) if (!p)
return nfserr_resource; return nfserr_resource;
*p = xdr_encode_opaque(*p, item->name, ret); p = xdr_encode_opaque(p, item->name, ret);
*buflen -= bytes;
cache_put(&item->h, nn->idtoname_cache); cache_put(&item->h, nn->idtoname_cache);
return 0; return 0;
} }
...@@ -622,11 +621,12 @@ do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u ...@@ -622,11 +621,12 @@ do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u
return idmap_name_to_id(rqstp, type, name, namelen, id); return idmap_name_to_id(rqstp, type, name, namelen, id);
} }
static __be32 encode_name_from_id(struct svc_rqst *rqstp, int type, u32 id, __be32 **p, int *buflen) static __be32 encode_name_from_id(struct xdr_stream *xdr,
struct svc_rqst *rqstp, int type, u32 id)
{ {
if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
return encode_ascii_id(id, p, buflen); return encode_ascii_id(xdr, id);
return idmap_id_to_name(rqstp, type, id, p, buflen); return idmap_id_to_name(xdr, rqstp, type, id);
} }
__be32 __be32
...@@ -655,14 +655,16 @@ nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, ...@@ -655,14 +655,16 @@ nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen,
return status; return status;
} }
__be32 nfsd4_encode_user(struct svc_rqst *rqstp, kuid_t uid, __be32 **p, int *buflen) __be32 nfsd4_encode_user(struct xdr_stream *xdr, struct svc_rqst *rqstp,
kuid_t uid)
{ {
u32 id = from_kuid(&init_user_ns, uid); u32 id = from_kuid(&init_user_ns, uid);
return encode_name_from_id(rqstp, IDMAP_TYPE_USER, id, p, buflen); return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_USER, id);
} }
__be32 nfsd4_encode_group(struct svc_rqst *rqstp, kgid_t gid, __be32 **p, int *buflen) __be32 nfsd4_encode_group(struct xdr_stream *xdr, struct svc_rqst *rqstp,
kgid_t gid)
{ {
u32 id = from_kgid(&init_user_ns, gid); u32 id = from_kgid(&init_user_ns, gid);
return encode_name_from_id(rqstp, IDMAP_TYPE_GROUP, id, p, buflen); return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_GROUP, id);
} }
...@@ -430,12 +430,12 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -430,12 +430,12 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
goto out; goto out;
break; break;
case NFS4_OPEN_CLAIM_PREVIOUS: case NFS4_OPEN_CLAIM_PREVIOUS:
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
status = nfs4_check_open_reclaim(&open->op_clientid, status = nfs4_check_open_reclaim(&open->op_clientid,
cstate->minorversion, cstate->minorversion,
nn); nn);
if (status) if (status)
goto out; goto out;
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
case NFS4_OPEN_CLAIM_FH: case NFS4_OPEN_CLAIM_FH:
case NFS4_OPEN_CLAIM_DELEG_CUR_FH: case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
status = do_open_fhandle(rqstp, cstate, open); status = do_open_fhandle(rqstp, cstate, open);
...@@ -445,7 +445,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -445,7 +445,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
break; break;
case NFS4_OPEN_CLAIM_DELEG_PREV_FH: case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
case NFS4_OPEN_CLAIM_DELEGATE_PREV: case NFS4_OPEN_CLAIM_DELEGATE_PREV:
open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
dprintk("NFSD: unsupported OPEN claim type %d\n", dprintk("NFSD: unsupported OPEN claim type %d\n",
open->op_claim_type); open->op_claim_type);
status = nfserr_notsupp; status = nfserr_notsupp;
...@@ -786,7 +785,6 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -786,7 +785,6 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (!nfsd4_last_compound_op(rqstp)) if (!nfsd4_last_compound_op(rqstp))
rqstp->rq_splice_ok = false; rqstp->rq_splice_ok = false;
nfs4_lock_state();
/* check stateid */ /* check stateid */
if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp),
cstate, &read->rd_stateid, cstate, &read->rd_stateid,
...@@ -794,11 +792,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -794,11 +792,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
goto out; goto out;
} }
if (read->rd_filp)
get_file(read->rd_filp);
status = nfs_ok; status = nfs_ok;
out: out:
nfs4_unlock_state();
read->rd_rqstp = rqstp; read->rd_rqstp = rqstp;
read->rd_fhp = &cstate->current_fh; read->rd_fhp = &cstate->current_fh;
return status; return status;
...@@ -937,10 +932,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -937,10 +932,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
int err; int err;
if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
nfs4_lock_state();
status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate,
&setattr->sa_stateid, WR_STATE, NULL); &setattr->sa_stateid, WR_STATE, NULL);
nfs4_unlock_state();
if (status) { if (status) {
dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n"); dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
return status; return status;
...@@ -1006,17 +999,12 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -1006,17 +999,12 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
if (write->wr_offset >= OFFSET_MAX) if (write->wr_offset >= OFFSET_MAX)
return nfserr_inval; return nfserr_inval;
nfs4_lock_state();
status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), status = nfs4_preprocess_stateid_op(SVC_NET(rqstp),
cstate, stateid, WR_STATE, &filp); cstate, stateid, WR_STATE, &filp);
if (status) { if (status) {
nfs4_unlock_state();
dprintk("NFSD: nfsd4_write: couldn't process stateid!\n"); dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
return status; return status;
} }
if (filp)
get_file(filp);
nfs4_unlock_state();
cnt = write->wr_buflen; cnt = write->wr_buflen;
write->wr_how_written = write->wr_stable_how; write->wr_how_written = write->wr_stable_how;
...@@ -1072,10 +1060,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -1072,10 +1060,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return nfserr_jukebox; return nfserr_jukebox;
p = buf; p = buf;
status = nfsd4_encode_fattr(&cstate->current_fh, status = nfsd4_encode_fattr_to_buf(&p, count, &cstate->current_fh,
cstate->current_fh.fh_export, cstate->current_fh.fh_export,
cstate->current_fh.fh_dentry, &p, cstate->current_fh.fh_dentry,
count, verify->ve_bmval, verify->ve_bmval,
rqstp, 0); rqstp, 0);
/* /*
* If nfsd4_encode_fattr() ran out of space, assume that's because * If nfsd4_encode_fattr() ran out of space, assume that's because
...@@ -1182,9 +1170,7 @@ struct nfsd4_operation { ...@@ -1182,9 +1170,7 @@ struct nfsd4_operation {
static struct nfsd4_operation nfsd4_ops[]; static struct nfsd4_operation nfsd4_ops[];
#ifdef NFSD_DEBUG
static const char *nfsd4_op_name(unsigned opnum); static const char *nfsd4_op_name(unsigned opnum);
#endif
/* /*
* Enforce NFSv4.1 COMPOUND ordering rules: * Enforce NFSv4.1 COMPOUND ordering rules:
...@@ -1226,6 +1212,8 @@ static inline struct nfsd4_operation *OPDESC(struct nfsd4_op *op) ...@@ -1226,6 +1212,8 @@ static inline struct nfsd4_operation *OPDESC(struct nfsd4_op *op)
bool nfsd4_cache_this_op(struct nfsd4_op *op) bool nfsd4_cache_this_op(struct nfsd4_op *op)
{ {
if (op->opnum == OP_ILLEGAL)
return false;
return OPDESC(op)->op_flags & OP_CACHEME; return OPDESC(op)->op_flags & OP_CACHEME;
} }
...@@ -1262,6 +1250,25 @@ static bool need_wrongsec_check(struct svc_rqst *rqstp) ...@@ -1262,6 +1250,25 @@ static bool need_wrongsec_check(struct svc_rqst *rqstp)
return !(nextd->op_flags & OP_HANDLES_WRONGSEC); return !(nextd->op_flags & OP_HANDLES_WRONGSEC);
} }
static void svcxdr_init_encode(struct svc_rqst *rqstp,
struct nfsd4_compoundres *resp)
{
struct xdr_stream *xdr = &resp->xdr;
struct xdr_buf *buf = &rqstp->rq_res;
struct kvec *head = buf->head;
xdr->buf = buf;
xdr->iov = head;
xdr->p = head->iov_base + head->iov_len;
xdr->end = head->iov_base + PAGE_SIZE - rqstp->rq_auth_slack;
/* Tail and page_len should be zero at this point: */
buf->len = buf->head[0].iov_len;
xdr->scratch.iov_len = 0;
xdr->page_ptr = buf->pages - 1;
buf->buflen = PAGE_SIZE * (1 + rqstp->rq_page_end - buf->pages)
- rqstp->rq_auth_slack;
}
/* /*
* COMPOUND call. * COMPOUND call.
*/ */
...@@ -1275,24 +1282,16 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -1275,24 +1282,16 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate = &resp->cstate; struct nfsd4_compound_state *cstate = &resp->cstate;
struct svc_fh *current_fh = &cstate->current_fh; struct svc_fh *current_fh = &cstate->current_fh;
struct svc_fh *save_fh = &cstate->save_fh; struct svc_fh *save_fh = &cstate->save_fh;
int slack_bytes;
u32 plen = 0;
__be32 status; __be32 status;
resp->xbuf = &rqstp->rq_res; svcxdr_init_encode(rqstp, resp);
resp->p = rqstp->rq_res.head[0].iov_base + resp->tagp = resp->xdr.p;
rqstp->rq_res.head[0].iov_len;
resp->tagp = resp->p;
/* reserve space for: taglen, tag, and opcnt */ /* reserve space for: taglen, tag, and opcnt */
resp->p += 2 + XDR_QUADLEN(args->taglen); xdr_reserve_space(&resp->xdr, 8 + args->taglen);
resp->end = rqstp->rq_res.head[0].iov_base + PAGE_SIZE;
resp->taglen = args->taglen; resp->taglen = args->taglen;
resp->tag = args->tag; resp->tag = args->tag;
resp->opcnt = 0;
resp->rqstp = rqstp; resp->rqstp = rqstp;
cstate->minorversion = args->minorversion; cstate->minorversion = args->minorversion;
cstate->replay_owner = NULL;
cstate->session = NULL;
fh_init(current_fh, NFS4_FHSIZE); fh_init(current_fh, NFS4_FHSIZE);
fh_init(save_fh, NFS4_FHSIZE); fh_init(save_fh, NFS4_FHSIZE);
/* /*
...@@ -1332,19 +1331,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -1332,19 +1331,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
goto encode_op; goto encode_op;
} }
/* We must be able to encode a successful response to
* this operation, with enough room left over to encode a
* failed response to the next operation. If we don't
* have enough room, fail with ERR_RESOURCE.
*/
slack_bytes = (char *)resp->end - (char *)resp->p;
if (slack_bytes < COMPOUND_SLACK_SPACE
+ COMPOUND_ERR_SLACK_SPACE) {
BUG_ON(slack_bytes < COMPOUND_ERR_SLACK_SPACE);
op->status = nfserr_resource;
goto encode_op;
}
opdesc = OPDESC(op); opdesc = OPDESC(op);
if (!current_fh->fh_dentry) { if (!current_fh->fh_dentry) {
...@@ -1362,9 +1348,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -1362,9 +1348,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
/* If op is non-idempotent */ /* If op is non-idempotent */
if (opdesc->op_flags & OP_MODIFIES_SOMETHING) { if (opdesc->op_flags & OP_MODIFIES_SOMETHING) {
plen = opdesc->op_rsize_bop(rqstp, op);
/* /*
* If there's still another operation, make sure * Don't execute this op if we couldn't encode a
* succesful reply:
*/
u32 plen = opdesc->op_rsize_bop(rqstp, op);
/*
* Plus if there's another operation, make sure
* we'll have space to at least encode an error: * we'll have space to at least encode an error:
*/ */
if (resp->opcnt < args->opcnt) if (resp->opcnt < args->opcnt)
...@@ -1399,7 +1389,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -1399,7 +1389,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
} }
if (op->status == nfserr_replay_me) { if (op->status == nfserr_replay_me) {
op->replay = &cstate->replay_owner->so_replay; op->replay = &cstate->replay_owner->so_replay;
nfsd4_encode_replay(resp, op); nfsd4_encode_replay(&resp->xdr, op);
status = op->status = op->replay->rp_status; status = op->status = op->replay->rp_status;
} else { } else {
nfsd4_encode_operation(resp, op); nfsd4_encode_operation(resp, op);
...@@ -1438,7 +1428,8 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, ...@@ -1438,7 +1428,8 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
#define op_encode_change_info_maxsz (5) #define op_encode_change_info_maxsz (5)
#define nfs4_fattr_bitmap_maxsz (4) #define nfs4_fattr_bitmap_maxsz (4)
#define op_encode_lockowner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) /* We'll fall back on returning no lockowner if run out of space: */
#define op_encode_lockowner_maxsz (0)
#define op_encode_lock_denied_maxsz (8 + op_encode_lockowner_maxsz) #define op_encode_lock_denied_maxsz (8 + op_encode_lockowner_maxsz)
#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) #define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
...@@ -1470,6 +1461,49 @@ static inline u32 nfsd4_create_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op ...@@ -1470,6 +1461,49 @@ static inline u32 nfsd4_create_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op
+ nfs4_fattr_bitmap_maxsz) * sizeof(__be32); + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
} }
/*
* Note since this is an idempotent operation we won't insist on failing
* the op prematurely if the estimate is too large. We may turn off splice
* reads unnecessarily.
*/
static inline u32 nfsd4_getattr_rsize(struct svc_rqst *rqstp,
struct nfsd4_op *op)
{
u32 *bmap = op->u.getattr.ga_bmval;
u32 bmap0 = bmap[0], bmap1 = bmap[1], bmap2 = bmap[2];
u32 ret = 0;
if (bmap0 & FATTR4_WORD0_ACL)
return svc_max_payload(rqstp);
if (bmap0 & FATTR4_WORD0_FS_LOCATIONS)
return svc_max_payload(rqstp);
if (bmap1 & FATTR4_WORD1_OWNER) {
ret += IDMAP_NAMESZ + 4;
bmap1 &= ~FATTR4_WORD1_OWNER;
}
if (bmap1 & FATTR4_WORD1_OWNER_GROUP) {
ret += IDMAP_NAMESZ + 4;
bmap1 &= ~FATTR4_WORD1_OWNER_GROUP;
}
if (bmap0 & FATTR4_WORD0_FILEHANDLE) {
ret += NFS4_FHSIZE + 4;
bmap0 &= ~FATTR4_WORD0_FILEHANDLE;
}
if (bmap2 & FATTR4_WORD2_SECURITY_LABEL) {
ret += NFSD4_MAX_SEC_LABEL_LEN + 12;
bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL;
}
/*
* Largest of remaining attributes are 16 bytes (e.g.,
* supported_attributes)
*/
ret += 16 * (hweight32(bmap0) + hweight32(bmap1) + hweight32(bmap2));
/* bitmask, length */
ret += 20;
return ret;
}
static inline u32 nfsd4_link_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) static inline u32 nfsd4_link_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{ {
return (op_encode_hdr_size + op_encode_change_info_maxsz) return (op_encode_hdr_size + op_encode_change_info_maxsz)
...@@ -1500,18 +1534,19 @@ static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) ...@@ -1500,18 +1534,19 @@ static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
if (rlen > maxcount) if (rlen > maxcount)
rlen = maxcount; rlen = maxcount;
return (op_encode_hdr_size + 2) * sizeof(__be32) + rlen; return (op_encode_hdr_size + 2 + XDR_QUADLEN(rlen)) * sizeof(__be32);
} }
static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{ {
u32 maxcount = svc_max_payload(rqstp);
u32 rlen = op->u.readdir.rd_maxcount; u32 rlen = op->u.readdir.rd_maxcount;
if (rlen > PAGE_SIZE) if (rlen > maxcount)
rlen = PAGE_SIZE; rlen = maxcount;
return (op_encode_hdr_size + op_encode_verifier_maxsz) return (op_encode_hdr_size + op_encode_verifier_maxsz +
* sizeof(__be32) + rlen; XDR_QUADLEN(rlen)) * sizeof(__be32);
} }
static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
...@@ -1526,6 +1561,12 @@ static inline u32 nfsd4_rename_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op ...@@ -1526,6 +1561,12 @@ static inline u32 nfsd4_rename_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op
+ op_encode_change_info_maxsz) * sizeof(__be32); + op_encode_change_info_maxsz) * sizeof(__be32);
} }
static inline u32 nfsd4_sequence_rsize(struct svc_rqst *rqstp,
struct nfsd4_op *op)
{
return NFS4_MAX_SESSIONID_LEN + 20;
}
static inline u32 nfsd4_setattr_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) static inline u32 nfsd4_setattr_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{ {
return (op_encode_hdr_size + nfs4_fattr_bitmap_maxsz) * sizeof(__be32); return (op_encode_hdr_size + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
...@@ -1539,7 +1580,7 @@ static inline u32 nfsd4_setclientid_rsize(struct svc_rqst *rqstp, struct nfsd4_o ...@@ -1539,7 +1580,7 @@ static inline u32 nfsd4_setclientid_rsize(struct svc_rqst *rqstp, struct nfsd4_o
static inline u32 nfsd4_write_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) static inline u32 nfsd4_write_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
{ {
return (op_encode_hdr_size + op_encode_verifier_maxsz) * sizeof(__be32); return (op_encode_hdr_size + 2 + op_encode_verifier_maxsz) * sizeof(__be32);
} }
static inline u32 nfsd4_exchange_id_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op) static inline u32 nfsd4_exchange_id_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
...@@ -1607,6 +1648,7 @@ static struct nfsd4_operation nfsd4_ops[] = { ...@@ -1607,6 +1648,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
[OP_GETATTR] = { [OP_GETATTR] = {
.op_func = (nfsd4op_func)nfsd4_getattr, .op_func = (nfsd4op_func)nfsd4_getattr,
.op_flags = ALLOWED_ON_ABSENT_FS, .op_flags = ALLOWED_ON_ABSENT_FS,
.op_rsize_bop = nfsd4_getattr_rsize,
.op_name = "OP_GETATTR", .op_name = "OP_GETATTR",
}, },
[OP_GETFH] = { [OP_GETFH] = {
...@@ -1676,37 +1718,32 @@ static struct nfsd4_operation nfsd4_ops[] = { ...@@ -1676,37 +1718,32 @@ static struct nfsd4_operation nfsd4_ops[] = {
[OP_PUTFH] = { [OP_PUTFH] = {
.op_func = (nfsd4op_func)nfsd4_putfh, .op_func = (nfsd4op_func)nfsd4_putfh,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
| OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID,
| OP_CLEAR_STATEID,
.op_name = "OP_PUTFH", .op_name = "OP_PUTFH",
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
}, },
[OP_PUTPUBFH] = { [OP_PUTPUBFH] = {
.op_func = (nfsd4op_func)nfsd4_putrootfh, .op_func = (nfsd4op_func)nfsd4_putrootfh,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
| OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID,
| OP_CLEAR_STATEID,
.op_name = "OP_PUTPUBFH", .op_name = "OP_PUTPUBFH",
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
}, },
[OP_PUTROOTFH] = { [OP_PUTROOTFH] = {
.op_func = (nfsd4op_func)nfsd4_putrootfh, .op_func = (nfsd4op_func)nfsd4_putrootfh,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
| OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING | OP_IS_PUTFH_LIKE | OP_CLEAR_STATEID,
| OP_CLEAR_STATEID,
.op_name = "OP_PUTROOTFH", .op_name = "OP_PUTROOTFH",
.op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
}, },
[OP_READ] = { [OP_READ] = {
.op_func = (nfsd4op_func)nfsd4_read, .op_func = (nfsd4op_func)nfsd4_read,
.op_flags = OP_MODIFIES_SOMETHING,
.op_name = "OP_READ", .op_name = "OP_READ",
.op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize, .op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize,
.op_get_currentstateid = (stateid_getter)nfsd4_get_readstateid, .op_get_currentstateid = (stateid_getter)nfsd4_get_readstateid,
}, },
[OP_READDIR] = { [OP_READDIR] = {
.op_func = (nfsd4op_func)nfsd4_readdir, .op_func = (nfsd4op_func)nfsd4_readdir,
.op_flags = OP_MODIFIES_SOMETHING,
.op_name = "OP_READDIR", .op_name = "OP_READDIR",
.op_rsize_bop = (nfsd4op_rsize)nfsd4_readdir_rsize, .op_rsize_bop = (nfsd4op_rsize)nfsd4_readdir_rsize,
}, },
...@@ -1864,14 +1901,33 @@ static struct nfsd4_operation nfsd4_ops[] = { ...@@ -1864,14 +1901,33 @@ static struct nfsd4_operation nfsd4_ops[] = {
}, },
}; };
#ifdef NFSD_DEBUG int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op)
{
struct nfsd4_operation *opdesc;
nfsd4op_rsize estimator;
if (op->opnum == OP_ILLEGAL)
return op_encode_hdr_size * sizeof(__be32);
opdesc = OPDESC(op);
estimator = opdesc->op_rsize_bop;
return estimator ? estimator(rqstp, op) : PAGE_SIZE;
}
void warn_on_nonidempotent_op(struct nfsd4_op *op)
{
if (OPDESC(op)->op_flags & OP_MODIFIES_SOMETHING) {
pr_err("unable to encode reply to nonidempotent op %d (%s)\n",
op->opnum, nfsd4_op_name(op->opnum));
WARN_ON_ONCE(1);
}
}
static const char *nfsd4_op_name(unsigned opnum) static const char *nfsd4_op_name(unsigned opnum)
{ {
if (opnum < ARRAY_SIZE(nfsd4_ops)) if (opnum < ARRAY_SIZE(nfsd4_ops))
return nfsd4_ops[opnum].op_name; return nfsd4_ops[opnum].op_name;
return "unknown_operation"; return "unknown_operation";
} }
#endif
#define nfsd4_voidres nfsd4_voidargs #define nfsd4_voidres nfsd4_voidargs
struct nfsd4_voidargs { int dummy; }; struct nfsd4_voidargs { int dummy; };
......
...@@ -81,13 +81,13 @@ static DEFINE_MUTEX(client_mutex); ...@@ -81,13 +81,13 @@ static DEFINE_MUTEX(client_mutex);
* effort to decrease the scope of the client_mutex, this spinlock may * effort to decrease the scope of the client_mutex, this spinlock may
* eventually cover more: * eventually cover more:
*/ */
static DEFINE_SPINLOCK(recall_lock); static DEFINE_SPINLOCK(state_lock);
static struct kmem_cache *openowner_slab = NULL; static struct kmem_cache *openowner_slab;
static struct kmem_cache *lockowner_slab = NULL; static struct kmem_cache *lockowner_slab;
static struct kmem_cache *file_slab = NULL; static struct kmem_cache *file_slab;
static struct kmem_cache *stateid_slab = NULL; static struct kmem_cache *stateid_slab;
static struct kmem_cache *deleg_slab = NULL; static struct kmem_cache *deleg_slab;
void void
nfs4_lock_state(void) nfs4_lock_state(void)
...@@ -235,9 +235,9 @@ static void nfsd4_free_file(struct nfs4_file *f) ...@@ -235,9 +235,9 @@ static void nfsd4_free_file(struct nfs4_file *f)
static inline void static inline void
put_nfs4_file(struct nfs4_file *fi) put_nfs4_file(struct nfs4_file *fi)
{ {
if (atomic_dec_and_lock(&fi->fi_ref, &recall_lock)) { if (atomic_dec_and_lock(&fi->fi_ref, &state_lock)) {
hlist_del(&fi->fi_hash); hlist_del(&fi->fi_hash);
spin_unlock(&recall_lock); spin_unlock(&state_lock);
iput(fi->fi_inode); iput(fi->fi_inode);
nfsd4_free_file(fi); nfsd4_free_file(fi);
} }
...@@ -375,7 +375,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv ...@@ -375,7 +375,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab)); dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
if (dp == NULL) if (dp == NULL)
return dp; return dp;
dp->dl_stid.sc_type = NFS4_DELEG_STID;
/* /*
* delegation seqid's are never incremented. The 4.1 special * delegation seqid's are never incremented. The 4.1 special
* meaning of seqid 0 isn't meaningful, really, but let's avoid * meaning of seqid 0 isn't meaningful, really, but let's avoid
...@@ -418,6 +417,8 @@ nfs4_put_delegation(struct nfs4_delegation *dp) ...@@ -418,6 +417,8 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
static void nfs4_put_deleg_lease(struct nfs4_file *fp) static void nfs4_put_deleg_lease(struct nfs4_file *fp)
{ {
if (!fp->fi_lease)
return;
if (atomic_dec_and_test(&fp->fi_delegees)) { if (atomic_dec_and_test(&fp->fi_delegees)) {
vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease); vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease);
fp->fi_lease = NULL; fp->fi_lease = NULL;
...@@ -431,18 +432,30 @@ static void unhash_stid(struct nfs4_stid *s) ...@@ -431,18 +432,30 @@ static void unhash_stid(struct nfs4_stid *s)
s->sc_type = 0; s->sc_type = 0;
} }
static void
hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
{
lockdep_assert_held(&state_lock);
dp->dl_stid.sc_type = NFS4_DELEG_STID;
list_add(&dp->dl_perfile, &fp->fi_delegations);
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
}
/* Called under the state lock. */ /* Called under the state lock. */
static void static void
unhash_delegation(struct nfs4_delegation *dp) unhash_delegation(struct nfs4_delegation *dp)
{ {
spin_lock(&state_lock);
list_del_init(&dp->dl_perclnt); list_del_init(&dp->dl_perclnt);
spin_lock(&recall_lock);
list_del_init(&dp->dl_perfile); list_del_init(&dp->dl_perfile);
list_del_init(&dp->dl_recall_lru); list_del_init(&dp->dl_recall_lru);
spin_unlock(&recall_lock); spin_unlock(&state_lock);
nfs4_put_deleg_lease(dp->dl_file); if (dp->dl_file) {
put_nfs4_file(dp->dl_file); nfs4_put_deleg_lease(dp->dl_file);
dp->dl_file = NULL; put_nfs4_file(dp->dl_file);
dp->dl_file = NULL;
}
} }
...@@ -645,6 +658,12 @@ static void unhash_lockowner(struct nfs4_lockowner *lo) ...@@ -645,6 +658,12 @@ static void unhash_lockowner(struct nfs4_lockowner *lo)
} }
} }
static void nfs4_free_lockowner(struct nfs4_lockowner *lo)
{
kfree(lo->lo_owner.so_owner.data);
kmem_cache_free(lockowner_slab, lo);
}
static void release_lockowner(struct nfs4_lockowner *lo) static void release_lockowner(struct nfs4_lockowner *lo)
{ {
unhash_lockowner(lo); unhash_lockowner(lo);
...@@ -699,6 +718,12 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo) ...@@ -699,6 +718,12 @@ static void release_last_closed_stateid(struct nfs4_openowner *oo)
} }
} }
static void nfs4_free_openowner(struct nfs4_openowner *oo)
{
kfree(oo->oo_owner.so_owner.data);
kmem_cache_free(openowner_slab, oo);
}
static void release_openowner(struct nfs4_openowner *oo) static void release_openowner(struct nfs4_openowner *oo)
{ {
unhash_openowner(oo); unhash_openowner(oo);
...@@ -1093,7 +1118,7 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) ...@@ -1093,7 +1118,7 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
return clp; return clp;
} }
static inline void static void
free_client(struct nfs4_client *clp) free_client(struct nfs4_client *clp)
{ {
struct nfsd_net __maybe_unused *nn = net_generic(clp->net, nfsd_net_id); struct nfsd_net __maybe_unused *nn = net_generic(clp->net, nfsd_net_id);
...@@ -1136,13 +1161,13 @@ destroy_client(struct nfs4_client *clp) ...@@ -1136,13 +1161,13 @@ destroy_client(struct nfs4_client *clp)
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
INIT_LIST_HEAD(&reaplist); INIT_LIST_HEAD(&reaplist);
spin_lock(&recall_lock); spin_lock(&state_lock);
while (!list_empty(&clp->cl_delegations)) { while (!list_empty(&clp->cl_delegations)) {
dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt); dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
list_del_init(&dp->dl_perclnt); list_del_init(&dp->dl_perclnt);
list_move(&dp->dl_recall_lru, &reaplist); list_move(&dp->dl_recall_lru, &reaplist);
} }
spin_unlock(&recall_lock); spin_unlock(&state_lock);
while (!list_empty(&reaplist)) { while (!list_empty(&reaplist)) {
dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
destroy_delegation(dp); destroy_delegation(dp);
...@@ -1544,6 +1569,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_r ...@@ -1544,6 +1569,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_r
void void
nfsd4_store_cache_entry(struct nfsd4_compoundres *resp) nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
{ {
struct xdr_buf *buf = resp->xdr.buf;
struct nfsd4_slot *slot = resp->cstate.slot; struct nfsd4_slot *slot = resp->cstate.slot;
unsigned int base; unsigned int base;
...@@ -1557,11 +1583,9 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp) ...@@ -1557,11 +1583,9 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
slot->sl_datalen = 0; slot->sl_datalen = 0;
return; return;
} }
slot->sl_datalen = (char *)resp->p - (char *)resp->cstate.datap; base = resp->cstate.data_offset;
base = (char *)resp->cstate.datap - slot->sl_datalen = buf->len - base;
(char *)resp->xbuf->head[0].iov_base; if (read_bytes_from_xdr_buf(buf, base, slot->sl_data, slot->sl_datalen))
if (read_bytes_from_xdr_buf(resp->xbuf, base, slot->sl_data,
slot->sl_datalen))
WARN("%s: sessions DRC could not cache compound\n", __func__); WARN("%s: sessions DRC could not cache compound\n", __func__);
return; return;
} }
...@@ -1602,6 +1626,8 @@ nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, ...@@ -1602,6 +1626,8 @@ nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp,
struct nfsd4_sequence *seq) struct nfsd4_sequence *seq)
{ {
struct nfsd4_slot *slot = resp->cstate.slot; struct nfsd4_slot *slot = resp->cstate.slot;
struct xdr_stream *xdr = &resp->xdr;
__be32 *p;
__be32 status; __be32 status;
dprintk("--> %s slot %p\n", __func__, slot); dprintk("--> %s slot %p\n", __func__, slot);
...@@ -1610,14 +1636,16 @@ nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, ...@@ -1610,14 +1636,16 @@ nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp,
if (status) if (status)
return status; return status;
/* The sequence operation has been encoded, cstate->datap set. */ p = xdr_reserve_space(xdr, slot->sl_datalen);
memcpy(resp->cstate.datap, slot->sl_data, slot->sl_datalen); if (!p) {
WARN_ON_ONCE(1);
return nfserr_serverfault;
}
xdr_encode_opaque_fixed(p, slot->sl_data, slot->sl_datalen);
xdr_commit_encode(xdr);
resp->opcnt = slot->sl_opcnt; resp->opcnt = slot->sl_opcnt;
resp->p = resp->cstate.datap + XDR_QUADLEN(slot->sl_datalen); return slot->sl_status;
status = slot->sl_status;
return status;
} }
/* /*
...@@ -2189,11 +2217,13 @@ nfsd4_sequence(struct svc_rqst *rqstp, ...@@ -2189,11 +2217,13 @@ nfsd4_sequence(struct svc_rqst *rqstp,
struct nfsd4_sequence *seq) struct nfsd4_sequence *seq)
{ {
struct nfsd4_compoundres *resp = rqstp->rq_resp; struct nfsd4_compoundres *resp = rqstp->rq_resp;
struct xdr_stream *xdr = &resp->xdr;
struct nfsd4_session *session; struct nfsd4_session *session;
struct nfs4_client *clp; struct nfs4_client *clp;
struct nfsd4_slot *slot; struct nfsd4_slot *slot;
struct nfsd4_conn *conn; struct nfsd4_conn *conn;
__be32 status; __be32 status;
int buflen;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
if (resp->opcnt != 1) if (resp->opcnt != 1)
...@@ -2262,6 +2292,16 @@ nfsd4_sequence(struct svc_rqst *rqstp, ...@@ -2262,6 +2292,16 @@ nfsd4_sequence(struct svc_rqst *rqstp,
if (status) if (status)
goto out_put_session; goto out_put_session;
buflen = (seq->cachethis) ?
session->se_fchannel.maxresp_cached :
session->se_fchannel.maxresp_sz;
status = (seq->cachethis) ? nfserr_rep_too_big_to_cache :
nfserr_rep_too_big;
if (xdr_restrict_buflen(xdr, buflen - rqstp->rq_auth_slack))
goto out_put_session;
svc_reserve(rqstp, buflen);
status = nfs_ok;
/* Success! bump slot seqid */ /* Success! bump slot seqid */
slot->sl_seqid = seq->seqid; slot->sl_seqid = seq->seqid;
slot->sl_flags |= NFSD4_SLOT_INUSE; slot->sl_flags |= NFSD4_SLOT_INUSE;
...@@ -2499,28 +2539,19 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino) ...@@ -2499,28 +2539,19 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct inode *ino)
fp->fi_lease = NULL; fp->fi_lease = NULL;
memset(fp->fi_fds, 0, sizeof(fp->fi_fds)); memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
memset(fp->fi_access, 0, sizeof(fp->fi_access)); memset(fp->fi_access, 0, sizeof(fp->fi_access));
spin_lock(&recall_lock); spin_lock(&state_lock);
hlist_add_head(&fp->fi_hash, &file_hashtbl[hashval]); hlist_add_head(&fp->fi_hash, &file_hashtbl[hashval]);
spin_unlock(&recall_lock); spin_unlock(&state_lock);
}
static void
nfsd4_free_slab(struct kmem_cache **slab)
{
if (*slab == NULL)
return;
kmem_cache_destroy(*slab);
*slab = NULL;
} }
void void
nfsd4_free_slabs(void) nfsd4_free_slabs(void)
{ {
nfsd4_free_slab(&openowner_slab); kmem_cache_destroy(openowner_slab);
nfsd4_free_slab(&lockowner_slab); kmem_cache_destroy(lockowner_slab);
nfsd4_free_slab(&file_slab); kmem_cache_destroy(file_slab);
nfsd4_free_slab(&stateid_slab); kmem_cache_destroy(stateid_slab);
nfsd4_free_slab(&deleg_slab); kmem_cache_destroy(deleg_slab);
} }
int int
...@@ -2529,42 +2560,38 @@ nfsd4_init_slabs(void) ...@@ -2529,42 +2560,38 @@ nfsd4_init_slabs(void)
openowner_slab = kmem_cache_create("nfsd4_openowners", openowner_slab = kmem_cache_create("nfsd4_openowners",
sizeof(struct nfs4_openowner), 0, 0, NULL); sizeof(struct nfs4_openowner), 0, 0, NULL);
if (openowner_slab == NULL) if (openowner_slab == NULL)
goto out_nomem; goto out;
lockowner_slab = kmem_cache_create("nfsd4_lockowners", lockowner_slab = kmem_cache_create("nfsd4_lockowners",
sizeof(struct nfs4_lockowner), 0, 0, NULL); sizeof(struct nfs4_lockowner), 0, 0, NULL);
if (lockowner_slab == NULL) if (lockowner_slab == NULL)
goto out_nomem; goto out_free_openowner_slab;
file_slab = kmem_cache_create("nfsd4_files", file_slab = kmem_cache_create("nfsd4_files",
sizeof(struct nfs4_file), 0, 0, NULL); sizeof(struct nfs4_file), 0, 0, NULL);
if (file_slab == NULL) if (file_slab == NULL)
goto out_nomem; goto out_free_lockowner_slab;
stateid_slab = kmem_cache_create("nfsd4_stateids", stateid_slab = kmem_cache_create("nfsd4_stateids",
sizeof(struct nfs4_ol_stateid), 0, 0, NULL); sizeof(struct nfs4_ol_stateid), 0, 0, NULL);
if (stateid_slab == NULL) if (stateid_slab == NULL)
goto out_nomem; goto out_free_file_slab;
deleg_slab = kmem_cache_create("nfsd4_delegations", deleg_slab = kmem_cache_create("nfsd4_delegations",
sizeof(struct nfs4_delegation), 0, 0, NULL); sizeof(struct nfs4_delegation), 0, 0, NULL);
if (deleg_slab == NULL) if (deleg_slab == NULL)
goto out_nomem; goto out_free_stateid_slab;
return 0; return 0;
out_nomem:
nfsd4_free_slabs(); out_free_stateid_slab:
kmem_cache_destroy(stateid_slab);
out_free_file_slab:
kmem_cache_destroy(file_slab);
out_free_lockowner_slab:
kmem_cache_destroy(lockowner_slab);
out_free_openowner_slab:
kmem_cache_destroy(openowner_slab);
out:
dprintk("nfsd4: out of memory while initializing nfsv4\n"); dprintk("nfsd4: out of memory while initializing nfsv4\n");
return -ENOMEM; return -ENOMEM;
} }
void nfs4_free_openowner(struct nfs4_openowner *oo)
{
kfree(oo->oo_owner.so_owner.data);
kmem_cache_free(openowner_slab, oo);
}
void nfs4_free_lockowner(struct nfs4_lockowner *lo)
{
kfree(lo->lo_owner.so_owner.data);
kmem_cache_free(lockowner_slab, lo);
}
static void init_nfs4_replay(struct nfs4_replay *rp) static void init_nfs4_replay(struct nfs4_replay *rp)
{ {
rp->rp_status = nfserr_serverfault; rp->rp_status = nfserr_serverfault;
...@@ -2685,15 +2712,15 @@ find_file(struct inode *ino) ...@@ -2685,15 +2712,15 @@ find_file(struct inode *ino)
unsigned int hashval = file_hashval(ino); unsigned int hashval = file_hashval(ino);
struct nfs4_file *fp; struct nfs4_file *fp;
spin_lock(&recall_lock); spin_lock(&state_lock);
hlist_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) { hlist_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) {
if (fp->fi_inode == ino) { if (fp->fi_inode == ino) {
get_nfs4_file(fp); get_nfs4_file(fp);
spin_unlock(&recall_lock); spin_unlock(&state_lock);
return fp; return fp;
} }
} }
spin_unlock(&recall_lock); spin_unlock(&state_lock);
return NULL; return NULL;
} }
...@@ -2730,6 +2757,7 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp) ...@@ -2730,6 +2757,7 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
struct nfs4_client *clp = dp->dl_stid.sc_client; struct nfs4_client *clp = dp->dl_stid.sc_client;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
lockdep_assert_held(&state_lock);
/* We're assuming the state code never drops its reference /* We're assuming the state code never drops its reference
* without first removing the lease. Since we're in this lease * without first removing the lease. Since we're in this lease
* callback (and since the lease code is serialized by the kernel * callback (and since the lease code is serialized by the kernel
...@@ -2766,11 +2794,11 @@ static void nfsd_break_deleg_cb(struct file_lock *fl) ...@@ -2766,11 +2794,11 @@ static void nfsd_break_deleg_cb(struct file_lock *fl)
*/ */
fl->fl_break_time = 0; fl->fl_break_time = 0;
spin_lock(&recall_lock); spin_lock(&state_lock);
fp->fi_had_conflict = true; fp->fi_had_conflict = true;
list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
nfsd_break_one_deleg(dp); nfsd_break_one_deleg(dp);
spin_unlock(&recall_lock); spin_unlock(&state_lock);
} }
static static
...@@ -3047,11 +3075,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp) ...@@ -3047,11 +3075,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
status = vfs_setlease(fl->fl_file, fl->fl_type, &fl); status = vfs_setlease(fl->fl_file, fl->fl_type, &fl);
if (status) if (status)
goto out_free; goto out_free;
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
fp->fi_lease = fl; fp->fi_lease = fl;
fp->fi_deleg_file = get_file(fl->fl_file); fp->fi_deleg_file = get_file(fl->fl_file);
atomic_set(&fp->fi_delegees, 1); atomic_set(&fp->fi_delegees, 1);
list_add(&dp->dl_perfile, &fp->fi_delegations); spin_lock(&state_lock);
hash_delegation_locked(dp, fp);
spin_unlock(&state_lock);
return 0; return 0;
out_free: out_free:
locks_free_lock(fl); locks_free_lock(fl);
...@@ -3060,33 +3089,21 @@ static int nfs4_setlease(struct nfs4_delegation *dp) ...@@ -3060,33 +3089,21 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp) static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
{ {
int status;
if (fp->fi_had_conflict) if (fp->fi_had_conflict)
return -EAGAIN; return -EAGAIN;
get_nfs4_file(fp); get_nfs4_file(fp);
dp->dl_file = fp; dp->dl_file = fp;
if (!fp->fi_lease) { if (!fp->fi_lease)
status = nfs4_setlease(dp); return nfs4_setlease(dp);
if (status) spin_lock(&state_lock);
goto out_free; atomic_inc(&fp->fi_delegees);
return 0;
}
spin_lock(&recall_lock);
if (fp->fi_had_conflict) { if (fp->fi_had_conflict) {
spin_unlock(&recall_lock); spin_unlock(&state_lock);
status = -EAGAIN; return -EAGAIN;
goto out_free;
} }
atomic_inc(&fp->fi_delegees); hash_delegation_locked(dp, fp);
list_add(&dp->dl_perfile, &fp->fi_delegations); spin_unlock(&state_lock);
spin_unlock(&recall_lock);
list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
return 0; return 0;
out_free:
put_nfs4_file(fp);
dp->dl_file = fp;
return status;
} }
static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
...@@ -3173,8 +3190,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh, ...@@ -3173,8 +3190,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ; open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
return; return;
out_free: out_free:
remove_stid(&dp->dl_stid); destroy_delegation(dp);
nfs4_put_delegation(dp);
out_no_deleg: out_no_deleg:
open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE; open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS && if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&
...@@ -3391,8 +3407,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -3391,8 +3407,7 @@ nfs4_laundromat(struct nfsd_net *nn)
struct nfs4_delegation *dp; struct nfs4_delegation *dp;
struct list_head *pos, *next, reaplist; struct list_head *pos, *next, reaplist;
time_t cutoff = get_seconds() - nn->nfsd4_lease; time_t cutoff = get_seconds() - nn->nfsd4_lease;
time_t t, clientid_val = nn->nfsd4_lease; time_t t, new_timeo = nn->nfsd4_lease;
time_t u, test_val = nn->nfsd4_lease;
nfs4_lock_state(); nfs4_lock_state();
...@@ -3404,8 +3419,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -3404,8 +3419,7 @@ nfs4_laundromat(struct nfsd_net *nn)
clp = list_entry(pos, struct nfs4_client, cl_lru); clp = list_entry(pos, struct nfs4_client, cl_lru);
if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) {
t = clp->cl_time - cutoff; t = clp->cl_time - cutoff;
if (clientid_val > t) new_timeo = min(new_timeo, t);
clientid_val = t;
break; break;
} }
if (mark_client_expired_locked(clp)) { if (mark_client_expired_locked(clp)) {
...@@ -3422,39 +3436,35 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -3422,39 +3436,35 @@ nfs4_laundromat(struct nfsd_net *nn)
clp->cl_clientid.cl_id); clp->cl_clientid.cl_id);
expire_client(clp); expire_client(clp);
} }
spin_lock(&recall_lock); spin_lock(&state_lock);
list_for_each_safe(pos, next, &nn->del_recall_lru) { list_for_each_safe(pos, next, &nn->del_recall_lru) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
if (net_generic(dp->dl_stid.sc_client->net, nfsd_net_id) != nn) if (net_generic(dp->dl_stid.sc_client->net, nfsd_net_id) != nn)
continue; continue;
if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) {
u = dp->dl_time - cutoff; t = dp->dl_time - cutoff;
if (test_val > u) new_timeo = min(new_timeo, t);
test_val = u;
break; break;
} }
list_move(&dp->dl_recall_lru, &reaplist); list_move(&dp->dl_recall_lru, &reaplist);
} }
spin_unlock(&recall_lock); spin_unlock(&state_lock);
list_for_each_safe(pos, next, &reaplist) { list_for_each_safe(pos, next, &reaplist) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
revoke_delegation(dp); revoke_delegation(dp);
} }
test_val = nn->nfsd4_lease;
list_for_each_safe(pos, next, &nn->close_lru) { list_for_each_safe(pos, next, &nn->close_lru) {
oo = container_of(pos, struct nfs4_openowner, oo_close_lru); oo = container_of(pos, struct nfs4_openowner, oo_close_lru);
if (time_after((unsigned long)oo->oo_time, (unsigned long)cutoff)) { if (time_after((unsigned long)oo->oo_time, (unsigned long)cutoff)) {
u = oo->oo_time - cutoff; t = oo->oo_time - cutoff;
if (test_val > u) new_timeo = min(new_timeo, t);
test_val = u;
break; break;
} }
release_openowner(oo); release_openowner(oo);
} }
if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT) new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT;
nfs4_unlock_state(); nfs4_unlock_state();
return clientid_val; return new_timeo;
} }
static struct workqueue_struct *laundry_wq; static struct workqueue_struct *laundry_wq;
...@@ -3654,6 +3664,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, ...@@ -3654,6 +3664,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
struct svc_fh *current_fh = &cstate->current_fh; struct svc_fh *current_fh = &cstate->current_fh;
struct inode *ino = current_fh->fh_dentry->d_inode; struct inode *ino = current_fh->fh_dentry->d_inode;
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct file *file = NULL;
__be32 status; __be32 status;
if (filpp) if (filpp)
...@@ -3665,10 +3676,12 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, ...@@ -3665,10 +3676,12 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return check_special_stateids(net, current_fh, stateid, flags); return check_special_stateids(net, current_fh, stateid, flags);
nfs4_lock_state();
status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID,
&s, cstate->minorversion, nn); &s, cstate->minorversion, nn);
if (status) if (status)
return status; goto out;
status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate));
if (status) if (status)
goto out; goto out;
...@@ -3679,8 +3692,8 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, ...@@ -3679,8 +3692,8 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
if (status) if (status)
goto out; goto out;
if (filpp) { if (filpp) {
*filpp = dp->dl_file->fi_deleg_file; file = dp->dl_file->fi_deleg_file;
if (!*filpp) { if (!file) {
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
status = nfserr_serverfault; status = nfserr_serverfault;
goto out; goto out;
...@@ -3701,16 +3714,20 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, ...@@ -3701,16 +3714,20 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate,
goto out; goto out;
if (filpp) { if (filpp) {
if (flags & RD_STATE) if (flags & RD_STATE)
*filpp = find_readable_file(stp->st_file); file = find_readable_file(stp->st_file);
else else
*filpp = find_writeable_file(stp->st_file); file = find_writeable_file(stp->st_file);
} }
break; break;
default: default:
return nfserr_bad_stateid; status = nfserr_bad_stateid;
goto out;
} }
status = nfs_ok; status = nfs_ok;
if (file)
*filpp = get_file(file);
out: out:
nfs4_unlock_state();
return status; return status;
} }
...@@ -3726,7 +3743,7 @@ nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp) ...@@ -3726,7 +3743,7 @@ nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp)
* correspondance, and we have to delete the lockowner when we * correspondance, and we have to delete the lockowner when we
* delete the lock stateid: * delete the lock stateid:
*/ */
unhash_lockowner(lo); release_lockowner(lo);
return nfs_ok; return nfs_ok;
} }
...@@ -4896,6 +4913,7 @@ static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max, ...@@ -4896,6 +4913,7 @@ static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max,
struct nfs4_delegation *dp, *next; struct nfs4_delegation *dp, *next;
u64 count = 0; u64 count = 0;
lockdep_assert_held(&state_lock);
list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) { list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) {
if (victims) if (victims)
list_move(&dp->dl_recall_lru, victims); list_move(&dp->dl_recall_lru, victims);
...@@ -4911,9 +4929,9 @@ u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max) ...@@ -4911,9 +4929,9 @@ u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max)
LIST_HEAD(victims); LIST_HEAD(victims);
u64 count; u64 count;
spin_lock(&recall_lock); spin_lock(&state_lock);
count = nfsd_find_all_delegations(clp, max, &victims); count = nfsd_find_all_delegations(clp, max, &victims);
spin_unlock(&recall_lock); spin_unlock(&state_lock);
list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)
revoke_delegation(dp); revoke_delegation(dp);
...@@ -4927,11 +4945,11 @@ u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max) ...@@ -4927,11 +4945,11 @@ u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max)
LIST_HEAD(victims); LIST_HEAD(victims);
u64 count; u64 count;
spin_lock(&recall_lock); spin_lock(&state_lock);
count = nfsd_find_all_delegations(clp, max, &victims); count = nfsd_find_all_delegations(clp, max, &victims);
list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) list_for_each_entry_safe(dp, next, &victims, dl_recall_lru)
nfsd_break_one_deleg(dp); nfsd_break_one_deleg(dp);
spin_unlock(&recall_lock); spin_unlock(&state_lock);
return count; return count;
} }
...@@ -4940,9 +4958,9 @@ u64 nfsd_print_client_delegations(struct nfs4_client *clp, u64 max) ...@@ -4940,9 +4958,9 @@ u64 nfsd_print_client_delegations(struct nfs4_client *clp, u64 max)
{ {
u64 count = 0; u64 count = 0;
spin_lock(&recall_lock); spin_lock(&state_lock);
count = nfsd_find_all_delegations(clp, max, NULL); count = nfsd_find_all_delegations(clp, max, NULL);
spin_unlock(&recall_lock); spin_unlock(&state_lock);
nfsd_print_count(clp, count, "delegations"); nfsd_print_count(clp, count, "delegations");
return count; return count;
...@@ -4983,13 +5001,6 @@ struct nfs4_client *nfsd_find_client(struct sockaddr_storage *addr, size_t addr_ ...@@ -4983,13 +5001,6 @@ struct nfs4_client *nfsd_find_client(struct sockaddr_storage *addr, size_t addr_
#endif /* CONFIG_NFSD_FAULT_INJECTION */ #endif /* CONFIG_NFSD_FAULT_INJECTION */
/* initialization to perform at module load time: */
void
nfs4_state_init(void)
{
}
/* /*
* Since the lifetime of a delegation isn't limited to that of an open, a * Since the lifetime of a delegation isn't limited to that of an open, a
* client may quite reasonably hang on to a delegation as long as it has * client may quite reasonably hang on to a delegation as long as it has
...@@ -5160,12 +5171,12 @@ nfs4_state_shutdown_net(struct net *net) ...@@ -5160,12 +5171,12 @@ nfs4_state_shutdown_net(struct net *net)
nfs4_lock_state(); nfs4_lock_state();
INIT_LIST_HEAD(&reaplist); INIT_LIST_HEAD(&reaplist);
spin_lock(&recall_lock); spin_lock(&state_lock);
list_for_each_safe(pos, next, &nn->del_recall_lru) { list_for_each_safe(pos, next, &nn->del_recall_lru) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
list_move(&dp->dl_recall_lru, &reaplist); list_move(&dp->dl_recall_lru, &reaplist);
} }
spin_unlock(&recall_lock); spin_unlock(&state_lock);
list_for_each_safe(pos, next, &reaplist) { list_for_each_safe(pos, next, &reaplist) {
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
destroy_delegation(dp); destroy_delegation(dp);
......
...@@ -98,11 +98,6 @@ xdr_error: \ ...@@ -98,11 +98,6 @@ xdr_error: \
status = nfserr_bad_xdr; \ status = nfserr_bad_xdr; \
goto out goto out
#define READ32(x) (x) = ntohl(*p++)
#define READ64(x) do { \
(x) = (u64)ntohl(*p++) << 32; \
(x) |= ntohl(*p++); \
} while (0)
#define READMEM(x,nbytes) do { \ #define READMEM(x,nbytes) do { \
x = (char *)p; \ x = (char *)p; \
p += XDR_QUADLEN(nbytes); \ p += XDR_QUADLEN(nbytes); \
...@@ -248,17 +243,17 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval) ...@@ -248,17 +243,17 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
bmval[2] = 0; bmval[2] = 0;
READ_BUF(4); READ_BUF(4);
READ32(bmlen); bmlen = be32_to_cpup(p++);
if (bmlen > 1000) if (bmlen > 1000)
goto xdr_error; goto xdr_error;
READ_BUF(bmlen << 2); READ_BUF(bmlen << 2);
if (bmlen > 0) if (bmlen > 0)
READ32(bmval[0]); bmval[0] = be32_to_cpup(p++);
if (bmlen > 1) if (bmlen > 1)
READ32(bmval[1]); bmval[1] = be32_to_cpup(p++);
if (bmlen > 2) if (bmlen > 2)
READ32(bmval[2]); bmval[2] = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -270,6 +265,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -270,6 +265,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
{ {
int expected_len, len = 0; int expected_len, len = 0;
u32 dummy32; u32 dummy32;
u64 sec;
char *buf; char *buf;
DECODE_HEAD; DECODE_HEAD;
...@@ -278,12 +274,12 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -278,12 +274,12 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
return status; return status;
READ_BUF(4); READ_BUF(4);
READ32(expected_len); expected_len = be32_to_cpup(p++);
if (bmval[0] & FATTR4_WORD0_SIZE) { if (bmval[0] & FATTR4_WORD0_SIZE) {
READ_BUF(8); READ_BUF(8);
len += 8; len += 8;
READ64(iattr->ia_size); p = xdr_decode_hyper(p, &iattr->ia_size);
iattr->ia_valid |= ATTR_SIZE; iattr->ia_valid |= ATTR_SIZE;
} }
if (bmval[0] & FATTR4_WORD0_ACL) { if (bmval[0] & FATTR4_WORD0_ACL) {
...@@ -291,7 +287,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -291,7 +287,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
struct nfs4_ace *ace; struct nfs4_ace *ace;
READ_BUF(4); len += 4; READ_BUF(4); len += 4;
READ32(nace); nace = be32_to_cpup(p++);
if (nace > NFS4_ACL_MAX) if (nace > NFS4_ACL_MAX)
return nfserr_fbig; return nfserr_fbig;
...@@ -305,10 +301,10 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -305,10 +301,10 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
(*acl)->naces = nace; (*acl)->naces = nace;
for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) { for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
READ_BUF(16); len += 16; READ_BUF(16); len += 16;
READ32(ace->type); ace->type = be32_to_cpup(p++);
READ32(ace->flag); ace->flag = be32_to_cpup(p++);
READ32(ace->access_mask); ace->access_mask = be32_to_cpup(p++);
READ32(dummy32); dummy32 = be32_to_cpup(p++);
READ_BUF(dummy32); READ_BUF(dummy32);
len += XDR_QUADLEN(dummy32) << 2; len += XDR_QUADLEN(dummy32) << 2;
READMEM(buf, dummy32); READMEM(buf, dummy32);
...@@ -330,14 +326,14 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -330,14 +326,14 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
if (bmval[1] & FATTR4_WORD1_MODE) { if (bmval[1] & FATTR4_WORD1_MODE) {
READ_BUF(4); READ_BUF(4);
len += 4; len += 4;
READ32(iattr->ia_mode); iattr->ia_mode = be32_to_cpup(p++);
iattr->ia_mode &= (S_IFMT | S_IALLUGO); iattr->ia_mode &= (S_IFMT | S_IALLUGO);
iattr->ia_valid |= ATTR_MODE; iattr->ia_valid |= ATTR_MODE;
} }
if (bmval[1] & FATTR4_WORD1_OWNER) { if (bmval[1] & FATTR4_WORD1_OWNER) {
READ_BUF(4); READ_BUF(4);
len += 4; len += 4;
READ32(dummy32); dummy32 = be32_to_cpup(p++);
READ_BUF(dummy32); READ_BUF(dummy32);
len += (XDR_QUADLEN(dummy32) << 2); len += (XDR_QUADLEN(dummy32) << 2);
READMEM(buf, dummy32); READMEM(buf, dummy32);
...@@ -348,7 +344,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -348,7 +344,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) { if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) {
READ_BUF(4); READ_BUF(4);
len += 4; len += 4;
READ32(dummy32); dummy32 = be32_to_cpup(p++);
READ_BUF(dummy32); READ_BUF(dummy32);
len += (XDR_QUADLEN(dummy32) << 2); len += (XDR_QUADLEN(dummy32) << 2);
READMEM(buf, dummy32); READMEM(buf, dummy32);
...@@ -359,15 +355,16 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -359,15 +355,16 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) { if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
READ_BUF(4); READ_BUF(4);
len += 4; len += 4;
READ32(dummy32); dummy32 = be32_to_cpup(p++);
switch (dummy32) { switch (dummy32) {
case NFS4_SET_TO_CLIENT_TIME: case NFS4_SET_TO_CLIENT_TIME:
/* We require the high 32 bits of 'seconds' to be 0, and we ignore /* We require the high 32 bits of 'seconds' to be 0, and we ignore
all 32 bits of 'nseconds'. */ all 32 bits of 'nseconds'. */
READ_BUF(12); READ_BUF(12);
len += 12; len += 12;
READ64(iattr->ia_atime.tv_sec); p = xdr_decode_hyper(p, &sec);
READ32(iattr->ia_atime.tv_nsec); iattr->ia_atime.tv_sec = (time_t)sec;
iattr->ia_atime.tv_nsec = be32_to_cpup(p++);
if (iattr->ia_atime.tv_nsec >= (u32)1000000000) if (iattr->ia_atime.tv_nsec >= (u32)1000000000)
return nfserr_inval; return nfserr_inval;
iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET);
...@@ -382,15 +379,16 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -382,15 +379,16 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
READ_BUF(4); READ_BUF(4);
len += 4; len += 4;
READ32(dummy32); dummy32 = be32_to_cpup(p++);
switch (dummy32) { switch (dummy32) {
case NFS4_SET_TO_CLIENT_TIME: case NFS4_SET_TO_CLIENT_TIME:
/* We require the high 32 bits of 'seconds' to be 0, and we ignore /* We require the high 32 bits of 'seconds' to be 0, and we ignore
all 32 bits of 'nseconds'. */ all 32 bits of 'nseconds'. */
READ_BUF(12); READ_BUF(12);
len += 12; len += 12;
READ64(iattr->ia_mtime.tv_sec); p = xdr_decode_hyper(p, &sec);
READ32(iattr->ia_mtime.tv_nsec); iattr->ia_mtime.tv_sec = sec;
iattr->ia_mtime.tv_nsec = be32_to_cpup(p++);
if (iattr->ia_mtime.tv_nsec >= (u32)1000000000) if (iattr->ia_mtime.tv_nsec >= (u32)1000000000)
return nfserr_inval; return nfserr_inval;
iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET);
...@@ -408,13 +406,13 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ...@@ -408,13 +406,13 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) { if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
READ_BUF(4); READ_BUF(4);
len += 4; len += 4;
READ32(dummy32); /* lfs: we don't use it */ dummy32 = be32_to_cpup(p++); /* lfs: we don't use it */
READ_BUF(4); READ_BUF(4);
len += 4; len += 4;
READ32(dummy32); /* pi: we don't use it either */ dummy32 = be32_to_cpup(p++); /* pi: we don't use it either */
READ_BUF(4); READ_BUF(4);
len += 4; len += 4;
READ32(dummy32); dummy32 = be32_to_cpup(p++);
READ_BUF(dummy32); READ_BUF(dummy32);
if (dummy32 > NFSD4_MAX_SEC_LABEL_LEN) if (dummy32 > NFSD4_MAX_SEC_LABEL_LEN)
return nfserr_badlabel; return nfserr_badlabel;
...@@ -445,7 +443,7 @@ nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid) ...@@ -445,7 +443,7 @@ nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid)
DECODE_HEAD; DECODE_HEAD;
READ_BUF(sizeof(stateid_t)); READ_BUF(sizeof(stateid_t));
READ32(sid->si_generation); sid->si_generation = be32_to_cpup(p++);
COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t)); COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
DECODE_TAIL; DECODE_TAIL;
...@@ -457,7 +455,7 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access ...@@ -457,7 +455,7 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(access->ac_req_access); access->ac_req_access = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -472,7 +470,7 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ ...@@ -472,7 +470,7 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_
/* callback_sec_params4 */ /* callback_sec_params4 */
READ_BUF(4); READ_BUF(4);
READ32(nr_secflavs); nr_secflavs = be32_to_cpup(p++);
if (nr_secflavs) if (nr_secflavs)
cbs->flavor = (u32)(-1); cbs->flavor = (u32)(-1);
else else
...@@ -480,7 +478,7 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ ...@@ -480,7 +478,7 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_
cbs->flavor = 0; cbs->flavor = 0;
for (i = 0; i < nr_secflavs; ++i) { for (i = 0; i < nr_secflavs; ++i) {
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
switch (dummy) { switch (dummy) {
case RPC_AUTH_NULL: case RPC_AUTH_NULL:
/* Nothing to read */ /* Nothing to read */
...@@ -490,21 +488,21 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ ...@@ -490,21 +488,21 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_
case RPC_AUTH_UNIX: case RPC_AUTH_UNIX:
READ_BUF(8); READ_BUF(8);
/* stamp */ /* stamp */
READ32(dummy); dummy = be32_to_cpup(p++);
/* machine name */ /* machine name */
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy); READ_BUF(dummy);
SAVEMEM(machine_name, dummy); SAVEMEM(machine_name, dummy);
/* uid, gid */ /* uid, gid */
READ_BUF(8); READ_BUF(8);
READ32(uid); uid = be32_to_cpup(p++);
READ32(gid); gid = be32_to_cpup(p++);
/* more gids */ /* more gids */
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy * 4); READ_BUF(dummy * 4);
if (cbs->flavor == (u32)(-1)) { if (cbs->flavor == (u32)(-1)) {
kuid_t kuid = make_kuid(&init_user_ns, uid); kuid_t kuid = make_kuid(&init_user_ns, uid);
...@@ -524,14 +522,14 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ ...@@ -524,14 +522,14 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_
"not supported!\n"); "not supported!\n");
READ_BUF(8); READ_BUF(8);
/* gcbp_service */ /* gcbp_service */
READ32(dummy); dummy = be32_to_cpup(p++);
/* gcbp_handle_from_server */ /* gcbp_handle_from_server */
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy); READ_BUF(dummy);
p += XDR_QUADLEN(dummy); p += XDR_QUADLEN(dummy);
/* gcbp_handle_from_client */ /* gcbp_handle_from_client */
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy); READ_BUF(dummy);
break; break;
default: default:
...@@ -547,7 +545,7 @@ static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, stru ...@@ -547,7 +545,7 @@ static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, stru
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(bc->bc_cb_program); bc->bc_cb_program = be32_to_cpup(p++);
nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec); nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec);
DECODE_TAIL; DECODE_TAIL;
...@@ -559,7 +557,7 @@ static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, ...@@ -559,7 +557,7 @@ static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp,
READ_BUF(NFS4_MAX_SESSIONID_LEN + 8); READ_BUF(NFS4_MAX_SESSIONID_LEN + 8);
COPYMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN); COPYMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
READ32(bcts->dir); bcts->dir = be32_to_cpup(p++);
/* XXX: skipping ctsa_use_conn_in_rdma_mode. Perhaps Tom Tucker /* XXX: skipping ctsa_use_conn_in_rdma_mode. Perhaps Tom Tucker
* could help us figure out we should be using it. */ * could help us figure out we should be using it. */
DECODE_TAIL; DECODE_TAIL;
...@@ -571,7 +569,7 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close) ...@@ -571,7 +569,7 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(close->cl_seqid); close->cl_seqid = be32_to_cpup(p++);
return nfsd4_decode_stateid(argp, &close->cl_stateid); return nfsd4_decode_stateid(argp, &close->cl_stateid);
DECODE_TAIL; DECODE_TAIL;
...@@ -584,8 +582,8 @@ nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit ...@@ -584,8 +582,8 @@ nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit
DECODE_HEAD; DECODE_HEAD;
READ_BUF(12); READ_BUF(12);
READ64(commit->co_offset); p = xdr_decode_hyper(p, &commit->co_offset);
READ32(commit->co_count); commit->co_count = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -596,19 +594,19 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create ...@@ -596,19 +594,19 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(create->cr_type); create->cr_type = be32_to_cpup(p++);
switch (create->cr_type) { switch (create->cr_type) {
case NF4LNK: case NF4LNK:
READ_BUF(4); READ_BUF(4);
READ32(create->cr_linklen); create->cr_linklen = be32_to_cpup(p++);
READ_BUF(create->cr_linklen); READ_BUF(create->cr_linklen);
SAVEMEM(create->cr_linkname, create->cr_linklen); SAVEMEM(create->cr_linkname, create->cr_linklen);
break; break;
case NF4BLK: case NF4BLK:
case NF4CHR: case NF4CHR:
READ_BUF(8); READ_BUF(8);
READ32(create->cr_specdata1); create->cr_specdata1 = be32_to_cpup(p++);
READ32(create->cr_specdata2); create->cr_specdata2 = be32_to_cpup(p++);
break; break;
case NF4SOCK: case NF4SOCK:
case NF4FIFO: case NF4FIFO:
...@@ -618,7 +616,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create ...@@ -618,7 +616,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
} }
READ_BUF(4); READ_BUF(4);
READ32(create->cr_namelen); create->cr_namelen = be32_to_cpup(p++);
READ_BUF(create->cr_namelen); READ_BUF(create->cr_namelen);
SAVEMEM(create->cr_name, create->cr_namelen); SAVEMEM(create->cr_name, create->cr_namelen);
if ((status = check_filename(create->cr_name, create->cr_namelen))) if ((status = check_filename(create->cr_name, create->cr_namelen)))
...@@ -650,7 +648,7 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) ...@@ -650,7 +648,7 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(link->li_namelen); link->li_namelen = be32_to_cpup(p++);
READ_BUF(link->li_namelen); READ_BUF(link->li_namelen);
SAVEMEM(link->li_name, link->li_namelen); SAVEMEM(link->li_name, link->li_namelen);
if ((status = check_filename(link->li_name, link->li_namelen))) if ((status = check_filename(link->li_name, link->li_namelen)))
...@@ -668,24 +666,24 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) ...@@ -668,24 +666,24 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
* type, reclaim(boolean), offset, length, new_lock_owner(boolean) * type, reclaim(boolean), offset, length, new_lock_owner(boolean)
*/ */
READ_BUF(28); READ_BUF(28);
READ32(lock->lk_type); lock->lk_type = be32_to_cpup(p++);
if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT)) if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT))
goto xdr_error; goto xdr_error;
READ32(lock->lk_reclaim); lock->lk_reclaim = be32_to_cpup(p++);
READ64(lock->lk_offset); p = xdr_decode_hyper(p, &lock->lk_offset);
READ64(lock->lk_length); p = xdr_decode_hyper(p, &lock->lk_length);
READ32(lock->lk_is_new); lock->lk_is_new = be32_to_cpup(p++);
if (lock->lk_is_new) { if (lock->lk_is_new) {
READ_BUF(4); READ_BUF(4);
READ32(lock->lk_new_open_seqid); lock->lk_new_open_seqid = be32_to_cpup(p++);
status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid); status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid);
if (status) if (status)
return status; return status;
READ_BUF(8 + sizeof(clientid_t)); READ_BUF(8 + sizeof(clientid_t));
READ32(lock->lk_new_lock_seqid); lock->lk_new_lock_seqid = be32_to_cpup(p++);
COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t)); COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t));
READ32(lock->lk_new_owner.len); lock->lk_new_owner.len = be32_to_cpup(p++);
READ_BUF(lock->lk_new_owner.len); READ_BUF(lock->lk_new_owner.len);
READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len); READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len);
} else { } else {
...@@ -693,7 +691,7 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) ...@@ -693,7 +691,7 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
if (status) if (status)
return status; return status;
READ_BUF(4); READ_BUF(4);
READ32(lock->lk_old_lock_seqid); lock->lk_old_lock_seqid = be32_to_cpup(p++);
} }
DECODE_TAIL; DECODE_TAIL;
...@@ -705,13 +703,13 @@ nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt) ...@@ -705,13 +703,13 @@ nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
DECODE_HEAD; DECODE_HEAD;
READ_BUF(32); READ_BUF(32);
READ32(lockt->lt_type); lockt->lt_type = be32_to_cpup(p++);
if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT)) if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT))
goto xdr_error; goto xdr_error;
READ64(lockt->lt_offset); p = xdr_decode_hyper(p, &lockt->lt_offset);
READ64(lockt->lt_length); p = xdr_decode_hyper(p, &lockt->lt_length);
COPYMEM(&lockt->lt_clientid, 8); COPYMEM(&lockt->lt_clientid, 8);
READ32(lockt->lt_owner.len); lockt->lt_owner.len = be32_to_cpup(p++);
READ_BUF(lockt->lt_owner.len); READ_BUF(lockt->lt_owner.len);
READMEM(lockt->lt_owner.data, lockt->lt_owner.len); READMEM(lockt->lt_owner.data, lockt->lt_owner.len);
...@@ -724,16 +722,16 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku) ...@@ -724,16 +722,16 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
DECODE_HEAD; DECODE_HEAD;
READ_BUF(8); READ_BUF(8);
READ32(locku->lu_type); locku->lu_type = be32_to_cpup(p++);
if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT)) if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
goto xdr_error; goto xdr_error;
READ32(locku->lu_seqid); locku->lu_seqid = be32_to_cpup(p++);
status = nfsd4_decode_stateid(argp, &locku->lu_stateid); status = nfsd4_decode_stateid(argp, &locku->lu_stateid);
if (status) if (status)
return status; return status;
READ_BUF(16); READ_BUF(16);
READ64(locku->lu_offset); p = xdr_decode_hyper(p, &locku->lu_offset);
READ64(locku->lu_length); p = xdr_decode_hyper(p, &locku->lu_length);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -744,7 +742,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup ...@@ -744,7 +742,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(lookup->lo_len); lookup->lo_len = be32_to_cpup(p++);
READ_BUF(lookup->lo_len); READ_BUF(lookup->lo_len);
SAVEMEM(lookup->lo_name, lookup->lo_len); SAVEMEM(lookup->lo_name, lookup->lo_len);
if ((status = check_filename(lookup->lo_name, lookup->lo_len))) if ((status = check_filename(lookup->lo_name, lookup->lo_len)))
...@@ -759,7 +757,7 @@ static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *sh ...@@ -759,7 +757,7 @@ static __be32 nfsd4_decode_share_access(struct nfsd4_compoundargs *argp, u32 *sh
u32 w; u32 w;
READ_BUF(4); READ_BUF(4);
READ32(w); w = be32_to_cpup(p++);
*share_access = w & NFS4_SHARE_ACCESS_MASK; *share_access = w & NFS4_SHARE_ACCESS_MASK;
*deleg_want = w & NFS4_SHARE_WANT_MASK; *deleg_want = w & NFS4_SHARE_WANT_MASK;
if (deleg_when) if (deleg_when)
...@@ -811,7 +809,7 @@ static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x) ...@@ -811,7 +809,7 @@ static __be32 nfsd4_decode_share_deny(struct nfsd4_compoundargs *argp, u32 *x)
__be32 *p; __be32 *p;
READ_BUF(4); READ_BUF(4);
READ32(*x); *x = be32_to_cpup(p++);
/* Note: unlinke access bits, deny bits may be zero. */ /* Note: unlinke access bits, deny bits may be zero. */
if (*x & ~NFS4_SHARE_DENY_BOTH) if (*x & ~NFS4_SHARE_DENY_BOTH)
return nfserr_bad_xdr; return nfserr_bad_xdr;
...@@ -825,7 +823,7 @@ static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_ne ...@@ -825,7 +823,7 @@ static __be32 nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_ne
__be32 *p; __be32 *p;
READ_BUF(4); READ_BUF(4);
READ32(o->len); o->len = be32_to_cpup(p++);
if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT) if (o->len == 0 || o->len > NFS4_OPAQUE_LIMIT)
return nfserr_bad_xdr; return nfserr_bad_xdr;
...@@ -850,7 +848,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) ...@@ -850,7 +848,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
open->op_xdr_error = 0; open->op_xdr_error = 0;
/* seqid, share_access, share_deny, clientid, ownerlen */ /* seqid, share_access, share_deny, clientid, ownerlen */
READ_BUF(4); READ_BUF(4);
READ32(open->op_seqid); open->op_seqid = be32_to_cpup(p++);
/* decode, yet ignore deleg_when until supported */ /* decode, yet ignore deleg_when until supported */
status = nfsd4_decode_share_access(argp, &open->op_share_access, status = nfsd4_decode_share_access(argp, &open->op_share_access,
&open->op_deleg_want, &dummy); &open->op_deleg_want, &dummy);
...@@ -865,13 +863,13 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) ...@@ -865,13 +863,13 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
if (status) if (status)
goto xdr_error; goto xdr_error;
READ_BUF(4); READ_BUF(4);
READ32(open->op_create); open->op_create = be32_to_cpup(p++);
switch (open->op_create) { switch (open->op_create) {
case NFS4_OPEN_NOCREATE: case NFS4_OPEN_NOCREATE:
break; break;
case NFS4_OPEN_CREATE: case NFS4_OPEN_CREATE:
READ_BUF(4); READ_BUF(4);
READ32(open->op_createmode); open->op_createmode = be32_to_cpup(p++);
switch (open->op_createmode) { switch (open->op_createmode) {
case NFS4_CREATE_UNCHECKED: case NFS4_CREATE_UNCHECKED:
case NFS4_CREATE_GUARDED: case NFS4_CREATE_GUARDED:
...@@ -904,12 +902,12 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) ...@@ -904,12 +902,12 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
/* open_claim */ /* open_claim */
READ_BUF(4); READ_BUF(4);
READ32(open->op_claim_type); open->op_claim_type = be32_to_cpup(p++);
switch (open->op_claim_type) { switch (open->op_claim_type) {
case NFS4_OPEN_CLAIM_NULL: case NFS4_OPEN_CLAIM_NULL:
case NFS4_OPEN_CLAIM_DELEGATE_PREV: case NFS4_OPEN_CLAIM_DELEGATE_PREV:
READ_BUF(4); READ_BUF(4);
READ32(open->op_fname.len); open->op_fname.len = be32_to_cpup(p++);
READ_BUF(open->op_fname.len); READ_BUF(open->op_fname.len);
SAVEMEM(open->op_fname.data, open->op_fname.len); SAVEMEM(open->op_fname.data, open->op_fname.len);
if ((status = check_filename(open->op_fname.data, open->op_fname.len))) if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
...@@ -917,14 +915,14 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) ...@@ -917,14 +915,14 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
break; break;
case NFS4_OPEN_CLAIM_PREVIOUS: case NFS4_OPEN_CLAIM_PREVIOUS:
READ_BUF(4); READ_BUF(4);
READ32(open->op_delegate_type); open->op_delegate_type = be32_to_cpup(p++);
break; break;
case NFS4_OPEN_CLAIM_DELEGATE_CUR: case NFS4_OPEN_CLAIM_DELEGATE_CUR:
status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid); status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid);
if (status) if (status)
return status; return status;
READ_BUF(4); READ_BUF(4);
READ32(open->op_fname.len); open->op_fname.len = be32_to_cpup(p++);
READ_BUF(open->op_fname.len); READ_BUF(open->op_fname.len);
SAVEMEM(open->op_fname.data, open->op_fname.len); SAVEMEM(open->op_fname.data, open->op_fname.len);
if ((status = check_filename(open->op_fname.data, open->op_fname.len))) if ((status = check_filename(open->op_fname.data, open->op_fname.len)))
...@@ -962,7 +960,7 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con ...@@ -962,7 +960,7 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con
if (status) if (status)
return status; return status;
READ_BUF(4); READ_BUF(4);
READ32(open_conf->oc_seqid); open_conf->oc_seqid = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -976,7 +974,7 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d ...@@ -976,7 +974,7 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d
if (status) if (status)
return status; return status;
READ_BUF(4); READ_BUF(4);
READ32(open_down->od_seqid); open_down->od_seqid = be32_to_cpup(p++);
status = nfsd4_decode_share_access(argp, &open_down->od_share_access, status = nfsd4_decode_share_access(argp, &open_down->od_share_access,
&open_down->od_deleg_want, NULL); &open_down->od_deleg_want, NULL);
if (status) if (status)
...@@ -993,7 +991,7 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh) ...@@ -993,7 +991,7 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(putfh->pf_fhlen); putfh->pf_fhlen = be32_to_cpup(p++);
if (putfh->pf_fhlen > NFS4_FHSIZE) if (putfh->pf_fhlen > NFS4_FHSIZE)
goto xdr_error; goto xdr_error;
READ_BUF(putfh->pf_fhlen); READ_BUF(putfh->pf_fhlen);
...@@ -1019,8 +1017,8 @@ nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read) ...@@ -1019,8 +1017,8 @@ nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
if (status) if (status)
return status; return status;
READ_BUF(12); READ_BUF(12);
READ64(read->rd_offset); p = xdr_decode_hyper(p, &read->rd_offset);
READ32(read->rd_length); read->rd_length = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -1031,10 +1029,10 @@ nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *read ...@@ -1031,10 +1029,10 @@ nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *read
DECODE_HEAD; DECODE_HEAD;
READ_BUF(24); READ_BUF(24);
READ64(readdir->rd_cookie); p = xdr_decode_hyper(p, &readdir->rd_cookie);
COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data)); COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data));
READ32(readdir->rd_dircount); /* just in case you needed a useless field... */ readdir->rd_dircount = be32_to_cpup(p++);
READ32(readdir->rd_maxcount); readdir->rd_maxcount = be32_to_cpup(p++);
if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval))) if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval)))
goto out; goto out;
...@@ -1047,7 +1045,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove ...@@ -1047,7 +1045,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(remove->rm_namelen); remove->rm_namelen = be32_to_cpup(p++);
READ_BUF(remove->rm_namelen); READ_BUF(remove->rm_namelen);
SAVEMEM(remove->rm_name, remove->rm_namelen); SAVEMEM(remove->rm_name, remove->rm_namelen);
if ((status = check_filename(remove->rm_name, remove->rm_namelen))) if ((status = check_filename(remove->rm_name, remove->rm_namelen)))
...@@ -1062,10 +1060,10 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename ...@@ -1062,10 +1060,10 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(rename->rn_snamelen); rename->rn_snamelen = be32_to_cpup(p++);
READ_BUF(rename->rn_snamelen + 4); READ_BUF(rename->rn_snamelen + 4);
SAVEMEM(rename->rn_sname, rename->rn_snamelen); SAVEMEM(rename->rn_sname, rename->rn_snamelen);
READ32(rename->rn_tnamelen); rename->rn_tnamelen = be32_to_cpup(p++);
READ_BUF(rename->rn_tnamelen); READ_BUF(rename->rn_tnamelen);
SAVEMEM(rename->rn_tname, rename->rn_tnamelen); SAVEMEM(rename->rn_tname, rename->rn_tnamelen);
if ((status = check_filename(rename->rn_sname, rename->rn_snamelen))) if ((status = check_filename(rename->rn_sname, rename->rn_snamelen)))
...@@ -1097,7 +1095,7 @@ nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, ...@@ -1097,7 +1095,7 @@ nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp,
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(secinfo->si_namelen); secinfo->si_namelen = be32_to_cpup(p++);
READ_BUF(secinfo->si_namelen); READ_BUF(secinfo->si_namelen);
SAVEMEM(secinfo->si_name, secinfo->si_namelen); SAVEMEM(secinfo->si_name, secinfo->si_namelen);
status = check_filename(secinfo->si_name, secinfo->si_namelen); status = check_filename(secinfo->si_name, secinfo->si_namelen);
...@@ -1113,7 +1111,7 @@ nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp, ...@@ -1113,7 +1111,7 @@ nfsd4_decode_secinfo_no_name(struct nfsd4_compoundargs *argp,
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(sin->sin_style); sin->sin_style = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -1144,16 +1142,16 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient ...@@ -1144,16 +1142,16 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
if (status) if (status)
return nfserr_bad_xdr; return nfserr_bad_xdr;
READ_BUF(8); READ_BUF(8);
READ32(setclientid->se_callback_prog); setclientid->se_callback_prog = be32_to_cpup(p++);
READ32(setclientid->se_callback_netid_len); setclientid->se_callback_netid_len = be32_to_cpup(p++);
READ_BUF(setclientid->se_callback_netid_len + 4); READ_BUF(setclientid->se_callback_netid_len + 4);
SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len); SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len);
READ32(setclientid->se_callback_addr_len); setclientid->se_callback_addr_len = be32_to_cpup(p++);
READ_BUF(setclientid->se_callback_addr_len + 4); READ_BUF(setclientid->se_callback_addr_len + 4);
SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len); SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len);
READ32(setclientid->se_callback_ident); setclientid->se_callback_ident = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -1186,7 +1184,7 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify ...@@ -1186,7 +1184,7 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
* nfsd4_proc_verify */ * nfsd4_proc_verify */
READ_BUF(4); READ_BUF(4);
READ32(verify->ve_attrlen); verify->ve_attrlen = be32_to_cpup(p++);
READ_BUF(verify->ve_attrlen); READ_BUF(verify->ve_attrlen);
SAVEMEM(verify->ve_attrval, verify->ve_attrlen); SAVEMEM(verify->ve_attrval, verify->ve_attrlen);
...@@ -1204,11 +1202,11 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) ...@@ -1204,11 +1202,11 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
if (status) if (status)
return status; return status;
READ_BUF(16); READ_BUF(16);
READ64(write->wr_offset); p = xdr_decode_hyper(p, &write->wr_offset);
READ32(write->wr_stable_how); write->wr_stable_how = be32_to_cpup(p++);
if (write->wr_stable_how > 2) if (write->wr_stable_how > 2)
goto xdr_error; goto xdr_error;
READ32(write->wr_buflen); write->wr_buflen = be32_to_cpup(p++);
/* Sorry .. no magic macros for this.. * /* Sorry .. no magic macros for this.. *
* READ_BUF(write->wr_buflen); * READ_BUF(write->wr_buflen);
...@@ -1254,7 +1252,7 @@ nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_rel ...@@ -1254,7 +1252,7 @@ nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_rel
READ_BUF(12); READ_BUF(12);
COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t)); COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
READ32(rlockowner->rl_owner.len); rlockowner->rl_owner.len = be32_to_cpup(p++);
READ_BUF(rlockowner->rl_owner.len); READ_BUF(rlockowner->rl_owner.len);
READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len); READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len);
...@@ -1278,63 +1276,63 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, ...@@ -1278,63 +1276,63 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
return nfserr_bad_xdr; return nfserr_bad_xdr;
READ_BUF(4); READ_BUF(4);
READ32(exid->flags); exid->flags = be32_to_cpup(p++);
/* Ignore state_protect4_a */ /* Ignore state_protect4_a */
READ_BUF(4); READ_BUF(4);
READ32(exid->spa_how); exid->spa_how = be32_to_cpup(p++);
switch (exid->spa_how) { switch (exid->spa_how) {
case SP4_NONE: case SP4_NONE:
break; break;
case SP4_MACH_CRED: case SP4_MACH_CRED:
/* spo_must_enforce */ /* spo_must_enforce */
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy * 4); READ_BUF(dummy * 4);
p += dummy; p += dummy;
/* spo_must_allow */ /* spo_must_allow */
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy * 4); READ_BUF(dummy * 4);
p += dummy; p += dummy;
break; break;
case SP4_SSV: case SP4_SSV:
/* ssp_ops */ /* ssp_ops */
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy * 4); READ_BUF(dummy * 4);
p += dummy; p += dummy;
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy * 4); READ_BUF(dummy * 4);
p += dummy; p += dummy;
/* ssp_hash_algs<> */ /* ssp_hash_algs<> */
READ_BUF(4); READ_BUF(4);
READ32(tmp); tmp = be32_to_cpup(p++);
while (tmp--) { while (tmp--) {
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy); READ_BUF(dummy);
p += XDR_QUADLEN(dummy); p += XDR_QUADLEN(dummy);
} }
/* ssp_encr_algs<> */ /* ssp_encr_algs<> */
READ_BUF(4); READ_BUF(4);
READ32(tmp); tmp = be32_to_cpup(p++);
while (tmp--) { while (tmp--) {
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy); READ_BUF(dummy);
p += XDR_QUADLEN(dummy); p += XDR_QUADLEN(dummy);
} }
/* ssp_window and ssp_num_gss_handles */ /* ssp_window and ssp_num_gss_handles */
READ_BUF(8); READ_BUF(8);
READ32(dummy); dummy = be32_to_cpup(p++);
READ32(dummy); dummy = be32_to_cpup(p++);
break; break;
default: default:
goto xdr_error; goto xdr_error;
...@@ -1342,7 +1340,7 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, ...@@ -1342,7 +1340,7 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
/* Ignore Implementation ID */ /* Ignore Implementation ID */
READ_BUF(4); /* nfs_impl_id4 array length */ READ_BUF(4); /* nfs_impl_id4 array length */
READ32(dummy); dummy = be32_to_cpup(p++);
if (dummy > 1) if (dummy > 1)
goto xdr_error; goto xdr_error;
...@@ -1350,13 +1348,13 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, ...@@ -1350,13 +1348,13 @@ nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp,
if (dummy == 1) { if (dummy == 1) {
/* nii_domain */ /* nii_domain */
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy); READ_BUF(dummy);
p += XDR_QUADLEN(dummy); p += XDR_QUADLEN(dummy);
/* nii_name */ /* nii_name */
READ_BUF(4); READ_BUF(4);
READ32(dummy); dummy = be32_to_cpup(p++);
READ_BUF(dummy); READ_BUF(dummy);
p += XDR_QUADLEN(dummy); p += XDR_QUADLEN(dummy);
...@@ -1376,21 +1374,21 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, ...@@ -1376,21 +1374,21 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
READ_BUF(16); READ_BUF(16);
COPYMEM(&sess->clientid, 8); COPYMEM(&sess->clientid, 8);
READ32(sess->seqid); sess->seqid = be32_to_cpup(p++);
READ32(sess->flags); sess->flags = be32_to_cpup(p++);
/* Fore channel attrs */ /* Fore channel attrs */
READ_BUF(28); READ_BUF(28);
READ32(dummy); /* headerpadsz is always 0 */ dummy = be32_to_cpup(p++); /* headerpadsz is always 0 */
READ32(sess->fore_channel.maxreq_sz); sess->fore_channel.maxreq_sz = be32_to_cpup(p++);
READ32(sess->fore_channel.maxresp_sz); sess->fore_channel.maxresp_sz = be32_to_cpup(p++);
READ32(sess->fore_channel.maxresp_cached); sess->fore_channel.maxresp_cached = be32_to_cpup(p++);
READ32(sess->fore_channel.maxops); sess->fore_channel.maxops = be32_to_cpup(p++);
READ32(sess->fore_channel.maxreqs); sess->fore_channel.maxreqs = be32_to_cpup(p++);
READ32(sess->fore_channel.nr_rdma_attrs); sess->fore_channel.nr_rdma_attrs = be32_to_cpup(p++);
if (sess->fore_channel.nr_rdma_attrs == 1) { if (sess->fore_channel.nr_rdma_attrs == 1) {
READ_BUF(4); READ_BUF(4);
READ32(sess->fore_channel.rdma_attrs); sess->fore_channel.rdma_attrs = be32_to_cpup(p++);
} else if (sess->fore_channel.nr_rdma_attrs > 1) { } else if (sess->fore_channel.nr_rdma_attrs > 1) {
dprintk("Too many fore channel attr bitmaps!\n"); dprintk("Too many fore channel attr bitmaps!\n");
goto xdr_error; goto xdr_error;
...@@ -1398,23 +1396,23 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, ...@@ -1398,23 +1396,23 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp,
/* Back channel attrs */ /* Back channel attrs */
READ_BUF(28); READ_BUF(28);
READ32(dummy); /* headerpadsz is always 0 */ dummy = be32_to_cpup(p++); /* headerpadsz is always 0 */
READ32(sess->back_channel.maxreq_sz); sess->back_channel.maxreq_sz = be32_to_cpup(p++);
READ32(sess->back_channel.maxresp_sz); sess->back_channel.maxresp_sz = be32_to_cpup(p++);
READ32(sess->back_channel.maxresp_cached); sess->back_channel.maxresp_cached = be32_to_cpup(p++);
READ32(sess->back_channel.maxops); sess->back_channel.maxops = be32_to_cpup(p++);
READ32(sess->back_channel.maxreqs); sess->back_channel.maxreqs = be32_to_cpup(p++);
READ32(sess->back_channel.nr_rdma_attrs); sess->back_channel.nr_rdma_attrs = be32_to_cpup(p++);
if (sess->back_channel.nr_rdma_attrs == 1) { if (sess->back_channel.nr_rdma_attrs == 1) {
READ_BUF(4); READ_BUF(4);
READ32(sess->back_channel.rdma_attrs); sess->back_channel.rdma_attrs = be32_to_cpup(p++);
} else if (sess->back_channel.nr_rdma_attrs > 1) { } else if (sess->back_channel.nr_rdma_attrs > 1) {
dprintk("Too many back channel attr bitmaps!\n"); dprintk("Too many back channel attr bitmaps!\n");
goto xdr_error; goto xdr_error;
} }
READ_BUF(4); READ_BUF(4);
READ32(sess->callback_prog); sess->callback_prog = be32_to_cpup(p++);
nfsd4_decode_cb_sec(argp, &sess->cb_sec); nfsd4_decode_cb_sec(argp, &sess->cb_sec);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -1437,7 +1435,7 @@ nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp, ...@@ -1437,7 +1435,7 @@ nfsd4_decode_free_stateid(struct nfsd4_compoundargs *argp,
DECODE_HEAD; DECODE_HEAD;
READ_BUF(sizeof(stateid_t)); READ_BUF(sizeof(stateid_t));
READ32(free_stateid->fr_stateid.si_generation); free_stateid->fr_stateid.si_generation = be32_to_cpup(p++);
COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t)); COPYMEM(&free_stateid->fr_stateid.si_opaque, sizeof(stateid_opaque_t));
DECODE_TAIL; DECODE_TAIL;
...@@ -1451,10 +1449,10 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp, ...@@ -1451,10 +1449,10 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp,
READ_BUF(NFS4_MAX_SESSIONID_LEN + 16); READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN); COPYMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
READ32(seq->seqid); seq->seqid = be32_to_cpup(p++);
READ32(seq->slotid); seq->slotid = be32_to_cpup(p++);
READ32(seq->maxslots); seq->maxslots = be32_to_cpup(p++);
READ32(seq->cachethis); seq->cachethis = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -1511,7 +1509,7 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str ...@@ -1511,7 +1509,7 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str
DECODE_HEAD; DECODE_HEAD;
READ_BUF(4); READ_BUF(4);
READ32(rc->rca_one_fs); rc->rca_one_fs = be32_to_cpup(p++);
DECODE_TAIL; DECODE_TAIL;
} }
...@@ -1605,47 +1603,25 @@ nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op) ...@@ -1605,47 +1603,25 @@ nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
return true; return true;
} }
/*
* Return a rough estimate of the maximum possible reply size. Note the
* estimate includes rpc headers so is meant to be passed to
* svc_reserve, not svc_reserve_auth.
*
* Also note the current compound encoding permits only one operation to
* use pages beyond the first one, so the maximum possible length is the
* maximum over these values, not the sum.
*/
static int nfsd4_max_reply(u32 opnum)
{
switch (opnum) {
case OP_READLINK:
case OP_READDIR:
/*
* Both of these ops take a single page for data and put
* the head and tail in another page:
*/
return 2 * PAGE_SIZE;
case OP_READ:
return INT_MAX;
default:
return PAGE_SIZE;
}
}
static __be32 static __be32
nfsd4_decode_compound(struct nfsd4_compoundargs *argp) nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
{ {
DECODE_HEAD; DECODE_HEAD;
struct nfsd4_op *op; struct nfsd4_op *op;
bool cachethis = false; bool cachethis = false;
int max_reply = PAGE_SIZE; int auth_slack= argp->rqstp->rq_auth_slack;
int max_reply = auth_slack + 8; /* opcnt, status */
int readcount = 0;
int readbytes = 0;
int i; int i;
READ_BUF(4); READ_BUF(4);
READ32(argp->taglen); argp->taglen = be32_to_cpup(p++);
READ_BUF(argp->taglen + 8); READ_BUF(argp->taglen + 8);
SAVEMEM(argp->tag, argp->taglen); SAVEMEM(argp->tag, argp->taglen);
READ32(argp->minorversion); argp->minorversion = be32_to_cpup(p++);
READ32(argp->opcnt); argp->opcnt = be32_to_cpup(p++);
max_reply += 4 + (XDR_QUADLEN(argp->taglen) << 2);
if (argp->taglen > NFSD4_MAX_TAGLEN) if (argp->taglen > NFSD4_MAX_TAGLEN)
goto xdr_error; goto xdr_error;
...@@ -1669,7 +1645,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) ...@@ -1669,7 +1645,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
op->replay = NULL; op->replay = NULL;
READ_BUF(4); READ_BUF(4);
READ32(op->opnum); op->opnum = be32_to_cpup(p++);
if (nfsd4_opnum_in_range(argp, op)) if (nfsd4_opnum_in_range(argp, op))
op->status = nfsd4_dec_ops[op->opnum](argp, &op->u); op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
...@@ -1677,97 +1653,82 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) ...@@ -1677,97 +1653,82 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
op->opnum = OP_ILLEGAL; op->opnum = OP_ILLEGAL;
op->status = nfserr_op_illegal; op->status = nfserr_op_illegal;
} }
if (op->status) {
argp->opcnt = i+1;
break;
}
/* /*
* We'll try to cache the result in the DRC if any one * We'll try to cache the result in the DRC if any one
* op in the compound wants to be cached: * op in the compound wants to be cached:
*/ */
cachethis |= nfsd4_cache_this_op(op); cachethis |= nfsd4_cache_this_op(op);
max_reply = max(max_reply, nfsd4_max_reply(op->opnum)); if (op->opnum == OP_READ) {
readcount++;
readbytes += nfsd4_max_reply(argp->rqstp, op);
} else
max_reply += nfsd4_max_reply(argp->rqstp, op);
if (op->status) {
argp->opcnt = i+1;
break;
}
} }
/* Sessions make the DRC unnecessary: */ /* Sessions make the DRC unnecessary: */
if (argp->minorversion) if (argp->minorversion)
cachethis = false; cachethis = false;
if (max_reply != INT_MAX) svc_reserve(argp->rqstp, max_reply + readbytes);
svc_reserve(argp->rqstp, max_reply);
argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE; argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
DECODE_TAIL; if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack)
} argp->rqstp->rq_splice_ok = false;
#define WRITE32(n) *p++ = htonl(n)
#define WRITE64(n) do { \
*p++ = htonl((u32)((n) >> 32)); \
*p++ = htonl((u32)(n)); \
} while (0)
#define WRITEMEM(ptr,nbytes) do { if (nbytes > 0) { \
*(p + XDR_QUADLEN(nbytes) -1) = 0; \
memcpy(p, ptr, nbytes); \
p += XDR_QUADLEN(nbytes); \
}} while (0)
static void write32(__be32 **p, u32 n)
{
*(*p)++ = htonl(n);
}
static void write64(__be32 **p, u64 n) DECODE_TAIL;
{
write32(p, (n >> 32));
write32(p, (u32)n);
} }
static void write_change(__be32 **p, struct kstat *stat, struct inode *inode) static __be32 *encode_change(__be32 *p, struct kstat *stat, struct inode *inode)
{ {
if (IS_I_VERSION(inode)) { if (IS_I_VERSION(inode)) {
write64(p, inode->i_version); p = xdr_encode_hyper(p, inode->i_version);
} else { } else {
write32(p, stat->ctime.tv_sec); *p++ = cpu_to_be32(stat->ctime.tv_sec);
write32(p, stat->ctime.tv_nsec); *p++ = cpu_to_be32(stat->ctime.tv_nsec);
} }
return p;
} }
static void write_cinfo(__be32 **p, struct nfsd4_change_info *c) static __be32 *encode_cinfo(__be32 *p, struct nfsd4_change_info *c)
{ {
write32(p, c->atomic); *p++ = cpu_to_be32(c->atomic);
if (c->change_supported) { if (c->change_supported) {
write64(p, c->before_change); p = xdr_encode_hyper(p, c->before_change);
write64(p, c->after_change); p = xdr_encode_hyper(p, c->after_change);
} else { } else {
write32(p, c->before_ctime_sec); *p++ = cpu_to_be32(c->before_ctime_sec);
write32(p, c->before_ctime_nsec); *p++ = cpu_to_be32(c->before_ctime_nsec);
write32(p, c->after_ctime_sec); *p++ = cpu_to_be32(c->after_ctime_sec);
write32(p, c->after_ctime_nsec); *p++ = cpu_to_be32(c->after_ctime_nsec);
} }
return p;
} }
#define RESERVE_SPACE(nbytes) do { \
p = resp->p; \
BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end); \
} while (0)
#define ADJUST_ARGS() resp->p = p
/* Encode as an array of strings the string given with components /* Encode as an array of strings the string given with components
* separated @sep, escaped with esc_enter and esc_exit. * separated @sep, escaped with esc_enter and esc_exit.
*/ */
static __be32 nfsd4_encode_components_esc(char sep, char *components, static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
__be32 **pp, int *buflen, char *components, char esc_enter,
char esc_enter, char esc_exit) char esc_exit)
{ {
__be32 *p = *pp; __be32 *p;
__be32 *countp = p; __be32 pathlen;
int pathlen_offset;
int strlen, count=0; int strlen, count=0;
char *str, *end, *next; char *str, *end, *next;
dprintk("nfsd4_encode_components(%s)\n", components); dprintk("nfsd4_encode_components(%s)\n", components);
if ((*buflen -= 4) < 0)
pathlen_offset = xdr->buf->len;
p = xdr_reserve_space(xdr, 4);
if (!p)
return nfserr_resource; return nfserr_resource;
WRITE32(0); /* We will fill this in with @count later */ p++; /* We will fill this in with @count later */
end = str = components; end = str = components;
while (*end) { while (*end) {
bool found_esc = false; bool found_esc = false;
...@@ -1789,59 +1750,57 @@ static __be32 nfsd4_encode_components_esc(char sep, char *components, ...@@ -1789,59 +1750,57 @@ static __be32 nfsd4_encode_components_esc(char sep, char *components,
strlen = end - str; strlen = end - str;
if (strlen) { if (strlen) {
if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0) p = xdr_reserve_space(xdr, strlen + 4);
if (!p)
return nfserr_resource; return nfserr_resource;
WRITE32(strlen); p = xdr_encode_opaque(p, str, strlen);
WRITEMEM(str, strlen);
count++; count++;
} }
else else
end++; end++;
str = end; str = end;
} }
*pp = p; pathlen = htonl(xdr->buf->len - pathlen_offset);
p = countp; write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4);
WRITE32(count);
return 0; return 0;
} }
/* Encode as an array of strings the string given with components /* Encode as an array of strings the string given with components
* separated @sep. * separated @sep.
*/ */
static __be32 nfsd4_encode_components(char sep, char *components, static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep,
__be32 **pp, int *buflen) char *components)
{ {
return nfsd4_encode_components_esc(sep, components, pp, buflen, 0, 0); return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
} }
/* /*
* encode a location element of a fs_locations structure * encode a location element of a fs_locations structure
*/ */
static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location, static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr,
__be32 **pp, int *buflen) struct nfsd4_fs_location *location)
{ {
__be32 status; __be32 status;
__be32 *p = *pp;
status = nfsd4_encode_components_esc(':', location->hosts, &p, buflen, status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
'[', ']'); '[', ']');
if (status) if (status)
return status; return status;
status = nfsd4_encode_components('/', location->path, &p, buflen); status = nfsd4_encode_components(xdr, '/', location->path);
if (status) if (status)
return status; return status;
*pp = p;
return 0; return 0;
} }
/* /*
* Encode a path in RFC3530 'pathname4' format * Encode a path in RFC3530 'pathname4' format
*/ */
static __be32 nfsd4_encode_path(const struct path *root, static __be32 nfsd4_encode_path(struct xdr_stream *xdr,
const struct path *path, __be32 **pp, int *buflen) const struct path *root,
const struct path *path)
{ {
struct path cur = *path; struct path cur = *path;
__be32 *p = *pp; __be32 *p;
struct dentry **components = NULL; struct dentry **components = NULL;
unsigned int ncomponents = 0; unsigned int ncomponents = 0;
__be32 err = nfserr_jukebox; __be32 err = nfserr_jukebox;
...@@ -1872,11 +1831,11 @@ static __be32 nfsd4_encode_path(const struct path *root, ...@@ -1872,11 +1831,11 @@ static __be32 nfsd4_encode_path(const struct path *root,
components[ncomponents++] = cur.dentry; components[ncomponents++] = cur.dentry;
cur.dentry = dget_parent(cur.dentry); cur.dentry = dget_parent(cur.dentry);
} }
err = nfserr_resource;
*buflen -= 4; p = xdr_reserve_space(xdr, 4);
if (*buflen < 0) if (!p)
goto out_free; goto out_free;
WRITE32(ncomponents); *p++ = cpu_to_be32(ncomponents);
while (ncomponents) { while (ncomponents) {
struct dentry *dentry = components[ncomponents - 1]; struct dentry *dentry = components[ncomponents - 1];
...@@ -1884,20 +1843,18 @@ static __be32 nfsd4_encode_path(const struct path *root, ...@@ -1884,20 +1843,18 @@ static __be32 nfsd4_encode_path(const struct path *root,
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
len = dentry->d_name.len; len = dentry->d_name.len;
*buflen -= 4 + (XDR_QUADLEN(len) << 2); p = xdr_reserve_space(xdr, len + 4);
if (*buflen < 0) { if (!p) {
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
goto out_free; goto out_free;
} }
WRITE32(len); p = xdr_encode_opaque(p, dentry->d_name.name, len);
WRITEMEM(dentry->d_name.name, len);
dprintk("/%s", dentry->d_name.name); dprintk("/%s", dentry->d_name.name);
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
dput(dentry); dput(dentry);
ncomponents--; ncomponents--;
} }
*pp = p;
err = 0; err = 0;
out_free: out_free:
dprintk(")\n"); dprintk(")\n");
...@@ -1908,8 +1865,8 @@ static __be32 nfsd4_encode_path(const struct path *root, ...@@ -1908,8 +1865,8 @@ static __be32 nfsd4_encode_path(const struct path *root,
return err; return err;
} }
static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp, static __be32 nfsd4_encode_fsloc_fsroot(struct xdr_stream *xdr,
const struct path *path, __be32 **pp, int *buflen) struct svc_rqst *rqstp, const struct path *path)
{ {
struct svc_export *exp_ps; struct svc_export *exp_ps;
__be32 res; __be32 res;
...@@ -1917,7 +1874,7 @@ static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp, ...@@ -1917,7 +1874,7 @@ static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
exp_ps = rqst_find_fsidzero_export(rqstp); exp_ps = rqst_find_fsidzero_export(rqstp);
if (IS_ERR(exp_ps)) if (IS_ERR(exp_ps))
return nfserrno(PTR_ERR(exp_ps)); return nfserrno(PTR_ERR(exp_ps));
res = nfsd4_encode_path(&exp_ps->ex_path, path, pp, buflen); res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path);
exp_put(exp_ps); exp_put(exp_ps);
return res; return res;
} }
...@@ -1925,28 +1882,26 @@ static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp, ...@@ -1925,28 +1882,26 @@ static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
/* /*
* encode a fs_locations structure * encode a fs_locations structure
*/ */
static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp, static __be32 nfsd4_encode_fs_locations(struct xdr_stream *xdr,
struct svc_export *exp, struct svc_rqst *rqstp, struct svc_export *exp)
__be32 **pp, int *buflen)
{ {
__be32 status; __be32 status;
int i; int i;
__be32 *p = *pp; __be32 *p;
struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs; struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
status = nfsd4_encode_fsloc_fsroot(rqstp, &exp->ex_path, &p, buflen); status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path);
if (status) if (status)
return status; return status;
if ((*buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
return nfserr_resource; return nfserr_resource;
WRITE32(fslocs->locations_count); *p++ = cpu_to_be32(fslocs->locations_count);
for (i=0; i<fslocs->locations_count; i++) { for (i=0; i<fslocs->locations_count; i++) {
status = nfsd4_encode_fs_location4(&fslocs->locations[i], status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
&p, buflen);
if (status) if (status)
return status; return status;
} }
*pp = p;
return 0; return 0;
} }
...@@ -1965,15 +1920,15 @@ static u32 nfs4_file_type(umode_t mode) ...@@ -1965,15 +1920,15 @@ static u32 nfs4_file_type(umode_t mode)
} }
static inline __be32 static inline __be32
nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace, nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
__be32 **p, int *buflen) struct nfs4_ace *ace)
{ {
if (ace->whotype != NFS4_ACL_WHO_NAMED) if (ace->whotype != NFS4_ACL_WHO_NAMED)
return nfs4_acl_write_who(ace->whotype, p, buflen); return nfs4_acl_write_who(xdr, ace->whotype);
else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
return nfsd4_encode_group(rqstp, ace->who_gid, p, buflen); return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
else else
return nfsd4_encode_user(rqstp, ace->who_uid, p, buflen); return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
} }
#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
...@@ -1982,31 +1937,28 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace, ...@@ -1982,31 +1937,28 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace,
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
static inline __be32 static inline __be32
nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen) nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
void *context, int len)
{ {
__be32 *p = *pp; __be32 *p;
if (*buflen < ((XDR_QUADLEN(len) << 2) + 4 + 4 + 4)) p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
if (!p)
return nfserr_resource; return nfserr_resource;
/* /*
* For now we use a 0 here to indicate the null translation; in * For now we use a 0 here to indicate the null translation; in
* the future we may place a call to translation code here. * the future we may place a call to translation code here.
*/ */
if ((*buflen -= 8) < 0) *p++ = cpu_to_be32(0); /* lfs */
return nfserr_resource; *p++ = cpu_to_be32(0); /* pi */
WRITE32(0); /* lfs */
WRITE32(0); /* pi */
p = xdr_encode_opaque(p, context, len); p = xdr_encode_opaque(p, context, len);
*buflen -= (XDR_QUADLEN(len) << 2) + 4;
*pp = p;
return 0; return 0;
} }
#else #else
static inline __be32 static inline __be32
nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen) nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
void *context, int len)
{ return 0; } { return 0; }
#endif #endif
...@@ -2045,12 +1997,11 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat) ...@@ -2045,12 +1997,11 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
/* /*
* Note: @fhp can be NULL; in this case, we might have to compose the filehandle * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
* ourselves. * ourselves.
*
* countp is the buffer size in _words_
*/ */
__be32 static __be32
nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
struct dentry *dentry, __be32 **buffer, int count, u32 *bmval, struct svc_export *exp,
struct dentry *dentry, u32 *bmval,
struct svc_rqst *rqstp, int ignore_crossmnt) struct svc_rqst *rqstp, int ignore_crossmnt)
{ {
u32 bmval0 = bmval[0]; u32 bmval0 = bmval[0];
...@@ -2059,12 +2010,13 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, ...@@ -2059,12 +2010,13 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
struct kstat stat; struct kstat stat;
struct svc_fh *tempfh = NULL; struct svc_fh *tempfh = NULL;
struct kstatfs statfs; struct kstatfs statfs;
int buflen = count << 2; __be32 *p;
__be32 *attrlenp; int starting_len = xdr->buf->len;
int attrlen_offset;
__be32 attrlen;
u32 dummy; u32 dummy;
u64 dummy64; u64 dummy64;
u32 rdattr_err = 0; u32 rdattr_err = 0;
__be32 *p = *buffer;
__be32 status; __be32 status;
int err; int err;
int aclsupport = 0; int aclsupport = 0;
...@@ -2095,8 +2047,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, ...@@ -2095,8 +2047,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
err = vfs_getattr(&path, &stat); err = vfs_getattr(&path, &stat);
if (err) if (err)
goto out_nfserr; goto out_nfserr;
if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL | if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
FATTR4_WORD0_MAXNAME)) || FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
(bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
FATTR4_WORD1_SPACE_TOTAL))) { FATTR4_WORD1_SPACE_TOTAL))) {
err = vfs_statfs(&path, &statfs); err = vfs_statfs(&path, &statfs);
...@@ -2145,25 +2097,33 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, ...@@ -2145,25 +2097,33 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
if (bmval2) { if (bmval2) {
if ((buflen -= 16) < 0) p = xdr_reserve_space(xdr, 16);
if (!p)
goto out_resource; goto out_resource;
WRITE32(3); *p++ = cpu_to_be32(3);
WRITE32(bmval0); *p++ = cpu_to_be32(bmval0);
WRITE32(bmval1); *p++ = cpu_to_be32(bmval1);
WRITE32(bmval2); *p++ = cpu_to_be32(bmval2);
} else if (bmval1) { } else if (bmval1) {
if ((buflen -= 12) < 0) p = xdr_reserve_space(xdr, 12);
if (!p)
goto out_resource; goto out_resource;
WRITE32(2); *p++ = cpu_to_be32(2);
WRITE32(bmval0); *p++ = cpu_to_be32(bmval0);
WRITE32(bmval1); *p++ = cpu_to_be32(bmval1);
} else { } else {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE32(1); *p++ = cpu_to_be32(1);
WRITE32(bmval0); *p++ = cpu_to_be32(bmval0);
} }
attrlenp = p++; /* to be backfilled later */
attrlen_offset = xdr->buf->len;
p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource;
p++; /* to be backfilled later */
if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
u32 word0 = nfsd_suppattrs0(minorversion); u32 word0 = nfsd_suppattrs0(minorversion);
...@@ -2175,296 +2135,343 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, ...@@ -2175,296 +2135,343 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
if (!contextsupport) if (!contextsupport)
word2 &= ~FATTR4_WORD2_SECURITY_LABEL; word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
if (!word2) { if (!word2) {
if ((buflen -= 12) < 0) p = xdr_reserve_space(xdr, 12);
if (!p)
goto out_resource; goto out_resource;
WRITE32(2); *p++ = cpu_to_be32(2);
WRITE32(word0); *p++ = cpu_to_be32(word0);
WRITE32(word1); *p++ = cpu_to_be32(word1);
} else { } else {
if ((buflen -= 16) < 0) p = xdr_reserve_space(xdr, 16);
if (!p)
goto out_resource; goto out_resource;
WRITE32(3); *p++ = cpu_to_be32(3);
WRITE32(word0); *p++ = cpu_to_be32(word0);
WRITE32(word1); *p++ = cpu_to_be32(word1);
WRITE32(word2); *p++ = cpu_to_be32(word2);
} }
} }
if (bmval0 & FATTR4_WORD0_TYPE) { if (bmval0 & FATTR4_WORD0_TYPE) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
dummy = nfs4_file_type(stat.mode); dummy = nfs4_file_type(stat.mode);
if (dummy == NF4BAD) { if (dummy == NF4BAD) {
status = nfserr_serverfault; status = nfserr_serverfault;
goto out; goto out;
} }
WRITE32(dummy); *p++ = cpu_to_be32(dummy);
} }
if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) { if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
WRITE32(NFS4_FH_PERSISTENT); *p++ = cpu_to_be32(NFS4_FH_PERSISTENT);
else else
WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME); *p++ = cpu_to_be32(NFS4_FH_PERSISTENT|
NFS4_FH_VOL_RENAME);
} }
if (bmval0 & FATTR4_WORD0_CHANGE) { if (bmval0 & FATTR4_WORD0_CHANGE) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
write_change(&p, &stat, dentry->d_inode); p = encode_change(p, &stat, dentry->d_inode);
} }
if (bmval0 & FATTR4_WORD0_SIZE) { if (bmval0 & FATTR4_WORD0_SIZE) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE64(stat.size); p = xdr_encode_hyper(p, stat.size);
} }
if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) { if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(1); *p++ = cpu_to_be32(1);
} }
if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) { if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(1); *p++ = cpu_to_be32(1);
} }
if (bmval0 & FATTR4_WORD0_NAMED_ATTR) { if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(0); *p++ = cpu_to_be32(0);
} }
if (bmval0 & FATTR4_WORD0_FSID) { if (bmval0 & FATTR4_WORD0_FSID) {
if ((buflen -= 16) < 0) p = xdr_reserve_space(xdr, 16);
if (!p)
goto out_resource; goto out_resource;
if (exp->ex_fslocs.migrated) { if (exp->ex_fslocs.migrated) {
WRITE64(NFS4_REFERRAL_FSID_MAJOR); p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MAJOR);
WRITE64(NFS4_REFERRAL_FSID_MINOR); p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
} else switch(fsid_source(fhp)) { } else switch(fsid_source(fhp)) {
case FSIDSOURCE_FSID: case FSIDSOURCE_FSID:
WRITE64((u64)exp->ex_fsid); p = xdr_encode_hyper(p, (u64)exp->ex_fsid);
WRITE64((u64)0); p = xdr_encode_hyper(p, (u64)0);
break; break;
case FSIDSOURCE_DEV: case FSIDSOURCE_DEV:
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(MAJOR(stat.dev)); *p++ = cpu_to_be32(MAJOR(stat.dev));
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(MINOR(stat.dev)); *p++ = cpu_to_be32(MINOR(stat.dev));
break; break;
case FSIDSOURCE_UUID: case FSIDSOURCE_UUID:
WRITEMEM(exp->ex_uuid, 16); p = xdr_encode_opaque_fixed(p, exp->ex_uuid,
EX_UUID_LEN);
break; break;
} }
} }
if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) { if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(0); *p++ = cpu_to_be32(0);
} }
if (bmval0 & FATTR4_WORD0_LEASE_TIME) { if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(nn->nfsd4_lease); *p++ = cpu_to_be32(nn->nfsd4_lease);
} }
if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(rdattr_err); *p++ = cpu_to_be32(rdattr_err);
} }
if (bmval0 & FATTR4_WORD0_ACL) { if (bmval0 & FATTR4_WORD0_ACL) {
struct nfs4_ace *ace; struct nfs4_ace *ace;
if (acl == NULL) { if (acl == NULL) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(0); *p++ = cpu_to_be32(0);
goto out_acl; goto out_acl;
} }
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(acl->naces); *p++ = cpu_to_be32(acl->naces);
for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) { for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
if ((buflen -= 4*3) < 0) p = xdr_reserve_space(xdr, 4*3);
if (!p)
goto out_resource; goto out_resource;
WRITE32(ace->type); *p++ = cpu_to_be32(ace->type);
WRITE32(ace->flag); *p++ = cpu_to_be32(ace->flag);
WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL); *p++ = cpu_to_be32(ace->access_mask &
status = nfsd4_encode_aclname(rqstp, ace, &p, &buflen); NFS4_ACE_MASK_ALL);
status = nfsd4_encode_aclname(xdr, rqstp, ace);
if (status) if (status)
goto out; goto out;
} }
} }
out_acl: out_acl:
if (bmval0 & FATTR4_WORD0_ACLSUPPORT) { if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(aclsupport ? *p++ = cpu_to_be32(aclsupport ?
ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0); ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
} }
if (bmval0 & FATTR4_WORD0_CANSETTIME) { if (bmval0 & FATTR4_WORD0_CANSETTIME) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(1); *p++ = cpu_to_be32(1);
} }
if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) { if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(0); *p++ = cpu_to_be32(0);
} }
if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) { if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(1); *p++ = cpu_to_be32(1);
} }
if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) { if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(1); *p++ = cpu_to_be32(1);
} }
if (bmval0 & FATTR4_WORD0_FILEHANDLE) { if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4; p = xdr_reserve_space(xdr, fhp->fh_handle.fh_size + 4);
if (buflen < 0) if (!p)
goto out_resource; goto out_resource;
WRITE32(fhp->fh_handle.fh_size); p = xdr_encode_opaque(p, &fhp->fh_handle.fh_base,
WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size); fhp->fh_handle.fh_size);
} }
if (bmval0 & FATTR4_WORD0_FILEID) { if (bmval0 & FATTR4_WORD0_FILEID) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE64(stat.ino); p = xdr_encode_hyper(p, stat.ino);
} }
if (bmval0 & FATTR4_WORD0_FILES_AVAIL) { if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE64((u64) statfs.f_ffree); p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
} }
if (bmval0 & FATTR4_WORD0_FILES_FREE) { if (bmval0 & FATTR4_WORD0_FILES_FREE) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE64((u64) statfs.f_ffree); p = xdr_encode_hyper(p, (u64) statfs.f_ffree);
} }
if (bmval0 & FATTR4_WORD0_FILES_TOTAL) { if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE64((u64) statfs.f_files); p = xdr_encode_hyper(p, (u64) statfs.f_files);
} }
if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) { if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen); status = nfsd4_encode_fs_locations(xdr, rqstp, exp);
if (status) if (status)
goto out; goto out;
} }
if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) { if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(1); *p++ = cpu_to_be32(1);
} }
if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE64(exp->ex_path.mnt->mnt_sb->s_maxbytes); p = xdr_encode_hyper(p, exp->ex_path.mnt->mnt_sb->s_maxbytes);
} }
if (bmval0 & FATTR4_WORD0_MAXLINK) { if (bmval0 & FATTR4_WORD0_MAXLINK) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(255); *p++ = cpu_to_be32(255);
} }
if (bmval0 & FATTR4_WORD0_MAXNAME) { if (bmval0 & FATTR4_WORD0_MAXNAME) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(statfs.f_namelen); *p++ = cpu_to_be32(statfs.f_namelen);
} }
if (bmval0 & FATTR4_WORD0_MAXREAD) { if (bmval0 & FATTR4_WORD0_MAXREAD) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE64((u64) svc_max_payload(rqstp)); p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
} }
if (bmval0 & FATTR4_WORD0_MAXWRITE) { if (bmval0 & FATTR4_WORD0_MAXWRITE) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE64((u64) svc_max_payload(rqstp)); p = xdr_encode_hyper(p, (u64) svc_max_payload(rqstp));
} }
if (bmval1 & FATTR4_WORD1_MODE) { if (bmval1 & FATTR4_WORD1_MODE) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(stat.mode & S_IALLUGO); *p++ = cpu_to_be32(stat.mode & S_IALLUGO);
} }
if (bmval1 & FATTR4_WORD1_NO_TRUNC) { if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(1); *p++ = cpu_to_be32(1);
} }
if (bmval1 & FATTR4_WORD1_NUMLINKS) { if (bmval1 & FATTR4_WORD1_NUMLINKS) {
if ((buflen -= 4) < 0) p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource; goto out_resource;
WRITE32(stat.nlink); *p++ = cpu_to_be32(stat.nlink);
} }
if (bmval1 & FATTR4_WORD1_OWNER) { if (bmval1 & FATTR4_WORD1_OWNER) {
status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen); status = nfsd4_encode_user(xdr, rqstp, stat.uid);
if (status) if (status)
goto out; goto out;
} }
if (bmval1 & FATTR4_WORD1_OWNER_GROUP) { if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen); status = nfsd4_encode_group(xdr, rqstp, stat.gid);
if (status) if (status)
goto out; goto out;
} }
if (bmval1 & FATTR4_WORD1_RAWDEV) { if (bmval1 & FATTR4_WORD1_RAWDEV) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
WRITE32((u32) MAJOR(stat.rdev)); *p++ = cpu_to_be32((u32) MAJOR(stat.rdev));
WRITE32((u32) MINOR(stat.rdev)); *p++ = cpu_to_be32((u32) MINOR(stat.rdev));
} }
if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) { if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize; dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
WRITE64(dummy64); p = xdr_encode_hyper(p, dummy64);
} }
if (bmval1 & FATTR4_WORD1_SPACE_FREE) { if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize; dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
WRITE64(dummy64); p = xdr_encode_hyper(p, dummy64);
} }
if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) { if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize; dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
WRITE64(dummy64); p = xdr_encode_hyper(p, dummy64);
} }
if (bmval1 & FATTR4_WORD1_SPACE_USED) { if (bmval1 & FATTR4_WORD1_SPACE_USED) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
dummy64 = (u64)stat.blocks << 9; dummy64 = (u64)stat.blocks << 9;
WRITE64(dummy64); p = xdr_encode_hyper(p, dummy64);
} }
if (bmval1 & FATTR4_WORD1_TIME_ACCESS) { if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
if ((buflen -= 12) < 0) p = xdr_reserve_space(xdr, 12);
if (!p)
goto out_resource; goto out_resource;
WRITE64((s64)stat.atime.tv_sec); p = xdr_encode_hyper(p, (s64)stat.atime.tv_sec);
WRITE32(stat.atime.tv_nsec); *p++ = cpu_to_be32(stat.atime.tv_nsec);
} }
if (bmval1 & FATTR4_WORD1_TIME_DELTA) { if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
if ((buflen -= 12) < 0) p = xdr_reserve_space(xdr, 12);
if (!p)
goto out_resource; goto out_resource;
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(1); *p++ = cpu_to_be32(1);
WRITE32(0); *p++ = cpu_to_be32(0);
} }
if (bmval1 & FATTR4_WORD1_TIME_METADATA) { if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
if ((buflen -= 12) < 0) p = xdr_reserve_space(xdr, 12);
if (!p)
goto out_resource; goto out_resource;
WRITE64((s64)stat.ctime.tv_sec); p = xdr_encode_hyper(p, (s64)stat.ctime.tv_sec);
WRITE32(stat.ctime.tv_nsec); *p++ = cpu_to_be32(stat.ctime.tv_nsec);
} }
if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
if ((buflen -= 12) < 0) p = xdr_reserve_space(xdr, 12);
if (!p)
goto out_resource; goto out_resource;
WRITE64((s64)stat.mtime.tv_sec); p = xdr_encode_hyper(p, (s64)stat.mtime.tv_sec);
WRITE32(stat.mtime.tv_nsec); *p++ = cpu_to_be32(stat.mtime.tv_nsec);
} }
if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) { if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
if ((buflen -= 8) < 0) p = xdr_reserve_space(xdr, 8);
if (!p)
goto out_resource; goto out_resource;
/* /*
* Get parent's attributes if not ignoring crossmount * Get parent's attributes if not ignoring crossmount
...@@ -2473,25 +2480,26 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, ...@@ -2473,25 +2480,26 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
if (ignore_crossmnt == 0 && if (ignore_crossmnt == 0 &&
dentry == exp->ex_path.mnt->mnt_root) dentry == exp->ex_path.mnt->mnt_root)
get_parent_attributes(exp, &stat); get_parent_attributes(exp, &stat);
WRITE64(stat.ino); p = xdr_encode_hyper(p, stat.ino);
} }
if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) { if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
status = nfsd4_encode_security_label(rqstp, context, status = nfsd4_encode_security_label(xdr, rqstp, context,
contextlen, &p, &buflen); contextlen);
if (status) if (status)
goto out; goto out;
} }
if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) { if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
if ((buflen -= 16) < 0) p = xdr_reserve_space(xdr, 16);
if (!p)
goto out_resource; goto out_resource;
WRITE32(3); *p++ = cpu_to_be32(3);
WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0); *p++ = cpu_to_be32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD1); *p++ = cpu_to_be32(NFSD_SUPPATTR_EXCLCREAT_WORD1);
WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD2); *p++ = cpu_to_be32(NFSD_SUPPATTR_EXCLCREAT_WORD2);
} }
*attrlenp = htonl((char *)p - (char *)attrlenp - 4); attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
*buffer = p; write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
status = nfs_ok; status = nfs_ok;
out: out:
...@@ -2504,6 +2512,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, ...@@ -2504,6 +2512,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
fh_put(tempfh); fh_put(tempfh);
kfree(tempfh); kfree(tempfh);
} }
if (status)
xdr_truncate_encode(xdr, starting_len);
return status; return status;
out_nfserr: out_nfserr:
status = nfserrno(err); status = nfserrno(err);
...@@ -2513,6 +2523,37 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, ...@@ -2513,6 +2523,37 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
goto out; goto out;
} }
static void svcxdr_init_encode_from_buffer(struct xdr_stream *xdr,
struct xdr_buf *buf, __be32 *p, int bytes)
{
xdr->scratch.iov_len = 0;
memset(buf, 0, sizeof(struct xdr_buf));
buf->head[0].iov_base = p;
buf->head[0].iov_len = 0;
buf->len = 0;
xdr->buf = buf;
xdr->iov = buf->head;
xdr->p = p;
xdr->end = (void *)p + bytes;
buf->buflen = bytes;
}
__be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
struct svc_fh *fhp, struct svc_export *exp,
struct dentry *dentry, u32 *bmval,
struct svc_rqst *rqstp, int ignore_crossmnt)
{
struct xdr_buf dummy;
struct xdr_stream xdr;
__be32 ret;
svcxdr_init_encode_from_buffer(&xdr, &dummy, *p, words << 2);
ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp,
ignore_crossmnt);
*p = xdr.p;
return ret;
}
static inline int attributes_need_mount(u32 *bmval) static inline int attributes_need_mount(u32 *bmval)
{ {
if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME)) if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
...@@ -2523,8 +2564,8 @@ static inline int attributes_need_mount(u32 *bmval) ...@@ -2523,8 +2564,8 @@ static inline int attributes_need_mount(u32 *bmval)
} }
static __be32 static __be32
nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
const char *name, int namlen, __be32 **p, int buflen) const char *name, int namlen)
{ {
struct svc_export *exp = cd->rd_fhp->fh_export; struct svc_export *exp = cd->rd_fhp->fh_export;
struct dentry *dentry; struct dentry *dentry;
...@@ -2576,7 +2617,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, ...@@ -2576,7 +2617,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
} }
out_encode: out_encode:
nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval, nfserr = nfsd4_encode_fattr(xdr, NULL, exp, dentry, cd->rd_bmval,
cd->rd_rqstp, ignore_crossmnt); cd->rd_rqstp, ignore_crossmnt);
out_put: out_put:
dput(dentry); dput(dentry);
...@@ -2585,9 +2626,12 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, ...@@ -2585,9 +2626,12 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
} }
static __be32 * static __be32 *
nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr) nfsd4_encode_rdattr_error(struct xdr_stream *xdr, __be32 nfserr)
{ {
if (buflen < 6) __be32 *p;
p = xdr_reserve_space(xdr, 6);
if (!p)
return NULL; return NULL;
*p++ = htonl(2); *p++ = htonl(2);
*p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
...@@ -2604,10 +2648,13 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, ...@@ -2604,10 +2648,13 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
{ {
struct readdir_cd *ccd = ccdv; struct readdir_cd *ccd = ccdv;
struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
int buflen; struct xdr_stream *xdr = cd->xdr;
__be32 *p = cd->buffer; int start_offset = xdr->buf->len;
__be32 *cookiep; int cookie_offset;
int entry_bytes;
__be32 nfserr = nfserr_toosmall; __be32 nfserr = nfserr_toosmall;
__be64 wire_offset;
__be32 *p;
/* In nfsv4, "." and ".." never make it onto the wire.. */ /* In nfsv4, "." and ".." never make it onto the wire.. */
if (name && isdotent(name, namlen)) { if (name && isdotent(name, namlen)) {
...@@ -2615,19 +2662,24 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, ...@@ -2615,19 +2662,24 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
return 0; return 0;
} }
if (cd->offset) if (cd->cookie_offset) {
xdr_encode_hyper(cd->offset, (u64) offset); wire_offset = cpu_to_be64(offset);
write_bytes_to_xdr_buf(xdr->buf, cd->cookie_offset,
&wire_offset, 8);
}
buflen = cd->buflen - 4 - XDR_QUADLEN(namlen); p = xdr_reserve_space(xdr, 4);
if (buflen < 0) if (!p)
goto fail; goto fail;
*p++ = xdr_one; /* mark entry present */ *p++ = xdr_one; /* mark entry present */
cookiep = p; cookie_offset = xdr->buf->len;
p = xdr_reserve_space(xdr, 3*4 + namlen);
if (!p)
goto fail;
p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */
p = xdr_encode_array(p, name, namlen); /* name length & name */ p = xdr_encode_array(p, name, namlen); /* name length & name */
nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, &p, buflen); nfserr = nfsd4_encode_dirent_fattr(xdr, cd, name, namlen);
switch (nfserr) { switch (nfserr) {
case nfs_ok: case nfs_ok:
break; break;
...@@ -2646,59 +2698,74 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, ...@@ -2646,59 +2698,74 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
*/ */
if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
goto fail; goto fail;
p = nfsd4_encode_rdattr_error(p, buflen, nfserr); p = nfsd4_encode_rdattr_error(xdr, nfserr);
if (p == NULL) { if (p == NULL) {
nfserr = nfserr_toosmall; nfserr = nfserr_toosmall;
goto fail; goto fail;
} }
} }
cd->buflen -= (p - cd->buffer); nfserr = nfserr_toosmall;
cd->buffer = p; entry_bytes = xdr->buf->len - start_offset;
cd->offset = cookiep; if (entry_bytes > cd->rd_maxcount)
goto fail;
cd->rd_maxcount -= entry_bytes;
if (!cd->rd_dircount)
goto fail;
cd->rd_dircount--;
cd->cookie_offset = cookie_offset;
skip_entry: skip_entry:
cd->common.err = nfs_ok; cd->common.err = nfs_ok;
return 0; return 0;
fail: fail:
xdr_truncate_encode(xdr, start_offset);
cd->common.err = nfserr; cd->common.err = nfserr;
return -EINVAL; return -EINVAL;
} }
static void static __be32
nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid) nfsd4_encode_stateid(struct xdr_stream *xdr, stateid_t *sid)
{ {
__be32 *p; __be32 *p;
RESERVE_SPACE(sizeof(stateid_t)); p = xdr_reserve_space(xdr, sizeof(stateid_t));
WRITE32(sid->si_generation); if (!p)
WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t)); return nfserr_resource;
ADJUST_ARGS(); *p++ = cpu_to_be32(sid->si_generation);
p = xdr_encode_opaque_fixed(p, &sid->si_opaque,
sizeof(stateid_opaque_t));
return 0;
} }
static __be32 static __be32
nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access) nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(8); p = xdr_reserve_space(xdr, 8);
WRITE32(access->ac_supported); if (!p)
WRITE32(access->ac_resp_access); return nfserr_resource;
ADJUST_ARGS(); *p++ = cpu_to_be32(access->ac_supported);
*p++ = cpu_to_be32(access->ac_resp_access);
} }
return nfserr; return nfserr;
} }
static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_bind_conn_to_session *bcts) static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_bind_conn_to_session *bcts)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 8); p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 8);
WRITEMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN); if (!p)
WRITE32(bcts->dir); return nfserr_resource;
p = xdr_encode_opaque_fixed(p, bcts->sessionid.data,
NFS4_MAX_SESSIONID_LEN);
*p++ = cpu_to_be32(bcts->dir);
/* Sorry, we do not yet support RDMA over 4.1: */ /* Sorry, we do not yet support RDMA over 4.1: */
WRITE32(0); *p++ = cpu_to_be32(0);
ADJUST_ARGS();
} }
return nfserr; return nfserr;
} }
...@@ -2706,8 +2773,10 @@ static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, ...@@ -2706,8 +2773,10 @@ static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp,
static __be32 static __be32
nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close) nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
{ {
struct xdr_stream *xdr = &resp->xdr;
if (!nfserr) if (!nfserr)
nfsd4_encode_stateid(resp, &close->cl_stateid); nfserr = nfsd4_encode_stateid(xdr, &close->cl_stateid);
return nfserr; return nfserr;
} }
...@@ -2716,12 +2785,15 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c ...@@ -2716,12 +2785,15 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c
static __be32 static __be32
nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit) nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(NFS4_VERIFIER_SIZE); p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
WRITEMEM(commit->co_verf.data, NFS4_VERIFIER_SIZE); if (!p)
ADJUST_ARGS(); return nfserr_resource;
p = xdr_encode_opaque_fixed(p, commit->co_verf.data,
NFS4_VERIFIER_SIZE);
} }
return nfserr; return nfserr;
} }
...@@ -2729,15 +2801,17 @@ nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ ...@@ -2729,15 +2801,17 @@ nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
static __be32 static __be32
nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create) nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(32); p = xdr_reserve_space(xdr, 32);
write_cinfo(&p, &create->cr_cinfo); if (!p)
WRITE32(2); return nfserr_resource;
WRITE32(create->cr_bmval[0]); p = encode_cinfo(p, &create->cr_cinfo);
WRITE32(create->cr_bmval[1]); *p++ = cpu_to_be32(2);
ADJUST_ARGS(); *p++ = cpu_to_be32(create->cr_bmval[0]);
*p++ = cpu_to_be32(create->cr_bmval[1]);
} }
return nfserr; return nfserr;
} }
...@@ -2746,14 +2820,13 @@ static __be32 ...@@ -2746,14 +2820,13 @@ static __be32
nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr) nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
{ {
struct svc_fh *fhp = getattr->ga_fhp; struct svc_fh *fhp = getattr->ga_fhp;
int buflen; struct xdr_stream *xdr = &resp->xdr;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2); nfserr = nfsd4_encode_fattr(xdr, fhp, fhp->fh_export, fhp->fh_dentry,
nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry, getattr->ga_bmval,
&resp->p, buflen, getattr->ga_bmval,
resp->rqstp, 0); resp->rqstp, 0);
return nfserr; return nfserr;
} }
...@@ -2761,16 +2834,17 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 ...@@ -2761,16 +2834,17 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
static __be32 static __be32
nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp) nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp)
{ {
struct xdr_stream *xdr = &resp->xdr;
struct svc_fh *fhp = *fhpp; struct svc_fh *fhp = *fhpp;
unsigned int len; unsigned int len;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
len = fhp->fh_handle.fh_size; len = fhp->fh_handle.fh_size;
RESERVE_SPACE(len + 4); p = xdr_reserve_space(xdr, len + 4);
WRITE32(len); if (!p)
WRITEMEM(&fhp->fh_handle.fh_base, len); return nfserr_resource;
ADJUST_ARGS(); p = xdr_encode_opaque(p, &fhp->fh_handle.fh_base, len);
} }
return nfserr; return nfserr;
} }
...@@ -2779,52 +2853,69 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh ...@@ -2779,52 +2853,69 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
* Including all fields other than the name, a LOCK4denied structure requires * Including all fields other than the name, a LOCK4denied structure requires
* 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes. * 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
*/ */
static void static __be32
nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld) nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld)
{ {
struct xdr_netobj *conf = &ld->ld_owner; struct xdr_netobj *conf = &ld->ld_owner;
__be32 *p; __be32 *p;
RESERVE_SPACE(32 + XDR_LEN(conf->len)); again:
WRITE64(ld->ld_start); p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len));
WRITE64(ld->ld_length); if (!p) {
WRITE32(ld->ld_type); /*
* Don't fail to return the result just because we can't
* return the conflicting open:
*/
if (conf->len) {
conf->len = 0;
conf->data = NULL;
goto again;
}
return nfserr_resource;
}
p = xdr_encode_hyper(p, ld->ld_start);
p = xdr_encode_hyper(p, ld->ld_length);
*p++ = cpu_to_be32(ld->ld_type);
if (conf->len) { if (conf->len) {
WRITEMEM(&ld->ld_clientid, 8); p = xdr_encode_opaque_fixed(p, &ld->ld_clientid, 8);
WRITE32(conf->len); p = xdr_encode_opaque(p, conf->data, conf->len);
WRITEMEM(conf->data, conf->len);
kfree(conf->data);
} else { /* non - nfsv4 lock in conflict, no clientid nor owner */ } else { /* non - nfsv4 lock in conflict, no clientid nor owner */
WRITE64((u64)0); /* clientid */ p = xdr_encode_hyper(p, (u64)0); /* clientid */
WRITE32(0); /* length of owner name */ *p++ = cpu_to_be32(0); /* length of owner name */
} }
ADJUST_ARGS(); return nfserr_denied;
} }
static __be32 static __be32
nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock) nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock)
{ {
struct xdr_stream *xdr = &resp->xdr;
if (!nfserr) if (!nfserr)
nfsd4_encode_stateid(resp, &lock->lk_resp_stateid); nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid);
else if (nfserr == nfserr_denied) else if (nfserr == nfserr_denied)
nfsd4_encode_lock_denied(resp, &lock->lk_denied); nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied);
kfree(lock->lk_denied.ld_owner.data);
return nfserr; return nfserr;
} }
static __be32 static __be32
nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt) nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt)
{ {
struct xdr_stream *xdr = &resp->xdr;
if (nfserr == nfserr_denied) if (nfserr == nfserr_denied)
nfsd4_encode_lock_denied(resp, &lockt->lt_denied); nfsd4_encode_lock_denied(xdr, &lockt->lt_denied);
return nfserr; return nfserr;
} }
static __be32 static __be32
nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku) nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku)
{ {
struct xdr_stream *xdr = &resp->xdr;
if (!nfserr) if (!nfserr)
nfsd4_encode_stateid(resp, &locku->lu_stateid); nfserr = nfsd4_encode_stateid(xdr, &locku->lu_stateid);
return nfserr; return nfserr;
} }
...@@ -2833,12 +2924,14 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l ...@@ -2833,12 +2924,14 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l
static __be32 static __be32
nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link) nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(20); p = xdr_reserve_space(xdr, 20);
write_cinfo(&p, &link->li_cinfo); if (!p)
ADJUST_ARGS(); return nfserr_resource;
p = encode_cinfo(p, &link->li_cinfo);
} }
return nfserr; return nfserr;
} }
...@@ -2847,72 +2940,86 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_li ...@@ -2847,72 +2940,86 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_li
static __be32 static __be32
nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open) nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (nfserr) if (nfserr)
goto out; goto out;
nfsd4_encode_stateid(resp, &open->op_stateid); nfserr = nfsd4_encode_stateid(xdr, &open->op_stateid);
RESERVE_SPACE(40); if (nfserr)
write_cinfo(&p, &open->op_cinfo); goto out;
WRITE32(open->op_rflags); p = xdr_reserve_space(xdr, 40);
WRITE32(2); if (!p)
WRITE32(open->op_bmval[0]); return nfserr_resource;
WRITE32(open->op_bmval[1]); p = encode_cinfo(p, &open->op_cinfo);
WRITE32(open->op_delegate_type); *p++ = cpu_to_be32(open->op_rflags);
ADJUST_ARGS(); *p++ = cpu_to_be32(2);
*p++ = cpu_to_be32(open->op_bmval[0]);
*p++ = cpu_to_be32(open->op_bmval[1]);
*p++ = cpu_to_be32(open->op_delegate_type);
switch (open->op_delegate_type) { switch (open->op_delegate_type) {
case NFS4_OPEN_DELEGATE_NONE: case NFS4_OPEN_DELEGATE_NONE:
break; break;
case NFS4_OPEN_DELEGATE_READ: case NFS4_OPEN_DELEGATE_READ:
nfsd4_encode_stateid(resp, &open->op_delegate_stateid); nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
RESERVE_SPACE(20); if (nfserr)
WRITE32(open->op_recall); return nfserr;
p = xdr_reserve_space(xdr, 20);
if (!p)
return nfserr_resource;
*p++ = cpu_to_be32(open->op_recall);
/* /*
* TODO: ACE's in delegations * TODO: ACE's in delegations
*/ */
WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); *p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(0); /* XXX: is NULL principal ok? */ *p++ = cpu_to_be32(0); /* XXX: is NULL principal ok? */
ADJUST_ARGS();
break; break;
case NFS4_OPEN_DELEGATE_WRITE: case NFS4_OPEN_DELEGATE_WRITE:
nfsd4_encode_stateid(resp, &open->op_delegate_stateid); nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
RESERVE_SPACE(32); if (nfserr)
WRITE32(0); return nfserr;
p = xdr_reserve_space(xdr, 32);
if (!p)
return nfserr_resource;
*p++ = cpu_to_be32(0);
/* /*
* TODO: space_limit's in delegations * TODO: space_limit's in delegations
*/ */
WRITE32(NFS4_LIMIT_SIZE); *p++ = cpu_to_be32(NFS4_LIMIT_SIZE);
WRITE32(~(u32)0); *p++ = cpu_to_be32(~(u32)0);
WRITE32(~(u32)0); *p++ = cpu_to_be32(~(u32)0);
/* /*
* TODO: ACE's in delegations * TODO: ACE's in delegations
*/ */
WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); *p++ = cpu_to_be32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE);
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(0); /* XXX: is NULL principal ok? */ *p++ = cpu_to_be32(0); /* XXX: is NULL principal ok? */
ADJUST_ARGS();
break; break;
case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */ case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */
switch (open->op_why_no_deleg) { switch (open->op_why_no_deleg) {
case WND4_CONTENTION: case WND4_CONTENTION:
case WND4_RESOURCE: case WND4_RESOURCE:
RESERVE_SPACE(8); p = xdr_reserve_space(xdr, 8);
WRITE32(open->op_why_no_deleg); if (!p)
WRITE32(0); /* deleg signaling not supported yet */ return nfserr_resource;
*p++ = cpu_to_be32(open->op_why_no_deleg);
/* deleg signaling not supported yet: */
*p++ = cpu_to_be32(0);
break; break;
default: default:
RESERVE_SPACE(4); p = xdr_reserve_space(xdr, 4);
WRITE32(open->op_why_no_deleg); if (!p)
return nfserr_resource;
*p++ = cpu_to_be32(open->op_why_no_deleg);
} }
ADJUST_ARGS();
break; break;
default: default:
BUG(); BUG();
...@@ -2925,8 +3032,10 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op ...@@ -2925,8 +3032,10 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
static __be32 static __be32
nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc) nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
{ {
struct xdr_stream *xdr = &resp->xdr;
if (!nfserr) if (!nfserr)
nfsd4_encode_stateid(resp, &oc->oc_resp_stateid); nfserr = nfsd4_encode_stateid(xdr, &oc->oc_resp_stateid);
return nfserr; return nfserr;
} }
...@@ -2934,127 +3043,233 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct ...@@ -2934,127 +3043,233 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct
static __be32 static __be32
nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od) nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
{ {
struct xdr_stream *xdr = &resp->xdr;
if (!nfserr) if (!nfserr)
nfsd4_encode_stateid(resp, &od->od_stateid); nfserr = nfsd4_encode_stateid(xdr, &od->od_stateid);
return nfserr; return nfserr;
} }
static __be32 static __be32 nfsd4_encode_splice_read(
nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_compoundres *resp,
struct nfsd4_read *read) struct nfsd4_read *read,
struct file *file, unsigned long maxcount)
{ {
struct xdr_stream *xdr = &resp->xdr;
struct xdr_buf *buf = xdr->buf;
u32 eof; u32 eof;
int v; int space_left;
struct page *page; __be32 nfserr;
unsigned long maxcount; __be32 *p = xdr->p - 2;
long len;
__be32 *p;
if (nfserr) /*
return nfserr; * Don't inline pages unless we know there's room for eof,
if (resp->xbuf->page_len) * count, and possible padding:
*/
if (xdr->end - xdr->p < 3)
return nfserr_resource; return nfserr_resource;
RESERVE_SPACE(8); /* eof flag and byte count */ nfserr = nfsd_splice_read(read->rd_rqstp, file,
read->rd_offset, &maxcount);
if (nfserr) {
/*
* nfsd_splice_actor may have already messed with the
* page length; reset it so as not to confuse
* xdr_truncate_encode:
*/
buf->page_len = 0;
return nfserr;
}
maxcount = svc_max_payload(resp->rqstp); eof = (read->rd_offset + maxcount >=
if (maxcount > read->rd_length) read->rd_fhp->fh_dentry->d_inode->i_size);
maxcount = read->rd_length;
*(p++) = htonl(eof);
*(p++) = htonl(maxcount);
buf->page_len = maxcount;
buf->len += maxcount;
xdr->page_ptr += (maxcount + PAGE_SIZE - 1) / PAGE_SIZE;
/* Use rest of head for padding and remaining ops: */
buf->tail[0].iov_base = xdr->p;
buf->tail[0].iov_len = 0;
xdr->iov = buf->tail;
if (maxcount&3) {
int pad = 4 - (maxcount&3);
*(xdr->p++) = 0;
buf->tail[0].iov_base += maxcount&3;
buf->tail[0].iov_len = pad;
buf->len += pad;
}
space_left = min_t(int, (void *)xdr->end - (void *)xdr->p,
buf->buflen - buf->len);
buf->buflen = buf->len + space_left;
xdr->end = (__be32 *)((void *)xdr->end + space_left);
return 0;
}
static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
struct nfsd4_read *read,
struct file *file, unsigned long maxcount)
{
struct xdr_stream *xdr = &resp->xdr;
u32 eof;
int v;
int starting_len = xdr->buf->len - 8;
long len;
int thislen;
__be32 nfserr;
__be32 tmp;
__be32 *p;
u32 zzz = 0;
int pad;
len = maxcount; len = maxcount;
v = 0; v = 0;
while (len > 0) {
page = *(resp->rqstp->rq_next_page); thislen = (void *)xdr->end - (void *)xdr->p;
if (!page) { /* ran out of pages */ if (len < thislen)
maxcount -= len; thislen = len;
break; p = xdr_reserve_space(xdr, (thislen+3)&~3);
} WARN_ON_ONCE(!p);
resp->rqstp->rq_vec[v].iov_base = page_address(page); resp->rqstp->rq_vec[v].iov_base = p;
resp->rqstp->rq_vec[v].iov_len = resp->rqstp->rq_vec[v].iov_len = thislen;
len < PAGE_SIZE ? len : PAGE_SIZE; v++;
resp->rqstp->rq_next_page++; len -= thislen;
while (len) {
thislen = min_t(long, len, PAGE_SIZE);
p = xdr_reserve_space(xdr, (thislen+3)&~3);
WARN_ON_ONCE(!p);
resp->rqstp->rq_vec[v].iov_base = p;
resp->rqstp->rq_vec[v].iov_len = thislen;
v++; v++;
len -= PAGE_SIZE; len -= thislen;
} }
read->rd_vlen = v; read->rd_vlen = v;
nfserr = nfsd_read_file(read->rd_rqstp, read->rd_fhp, read->rd_filp, nfserr = nfsd_readv(file, read->rd_offset, resp->rqstp->rq_vec,
read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen, read->rd_vlen, &maxcount);
&maxcount);
if (nfserr) if (nfserr)
return nfserr; return nfserr;
xdr_truncate_encode(xdr, starting_len + 8 + ((maxcount+3)&~3));
eof = (read->rd_offset + maxcount >= eof = (read->rd_offset + maxcount >=
read->rd_fhp->fh_dentry->d_inode->i_size); read->rd_fhp->fh_dentry->d_inode->i_size);
WRITE32(eof); tmp = htonl(eof);
WRITE32(maxcount); write_bytes_to_xdr_buf(xdr->buf, starting_len , &tmp, 4);
ADJUST_ARGS(); tmp = htonl(maxcount);
resp->xbuf->head[0].iov_len = (char*)p write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4);
- (char*)resp->xbuf->head[0].iov_base;
resp->xbuf->page_len = maxcount;
/* Use rest of head for padding and remaining ops: */ pad = (maxcount&3) ? 4 - (maxcount&3) : 0;
resp->xbuf->tail[0].iov_base = p; write_bytes_to_xdr_buf(xdr->buf, starting_len + 8 + maxcount,
resp->xbuf->tail[0].iov_len = 0; &zzz, pad);
if (maxcount&3) {
RESERVE_SPACE(4);
WRITE32(0);
resp->xbuf->tail[0].iov_base += maxcount&3;
resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
ADJUST_ARGS();
}
return 0; return 0;
} }
static __be32 static __be32
nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink) nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_read *read)
{ {
int maxcount; unsigned long maxcount;
char *page; struct xdr_stream *xdr = &resp->xdr;
struct file *file = read->rd_filp;
int starting_len = xdr->buf->len;
struct raparms *ra;
__be32 *p; __be32 *p;
__be32 err;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
if (resp->xbuf->page_len)
p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
if (!p) {
WARN_ON_ONCE(resp->rqstp->rq_splice_ok);
return nfserr_resource; return nfserr_resource;
if (!*resp->rqstp->rq_next_page) }
if (resp->xdr.buf->page_len && resp->rqstp->rq_splice_ok) {
WARN_ON_ONCE(1);
return nfserr_resource; return nfserr_resource;
}
xdr_commit_encode(xdr);
maxcount = svc_max_payload(resp->rqstp);
if (maxcount > xdr->buf->buflen - xdr->buf->len)
maxcount = xdr->buf->buflen - xdr->buf->len;
if (maxcount > read->rd_length)
maxcount = read->rd_length;
if (!read->rd_filp) {
err = nfsd_get_tmp_read_open(resp->rqstp, read->rd_fhp,
&file, &ra);
if (err)
goto err_truncate;
}
if (file->f_op->splice_read && resp->rqstp->rq_splice_ok)
err = nfsd4_encode_splice_read(resp, read, file, maxcount);
else
err = nfsd4_encode_readv(resp, read, file, maxcount);
if (!read->rd_filp)
nfsd_put_tmp_read_open(file, ra);
err_truncate:
if (err)
xdr_truncate_encode(xdr, starting_len);
return err;
}
static __be32
nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
{
int maxcount;
__be32 wire_count;
int zero = 0;
struct xdr_stream *xdr = &resp->xdr;
int length_offset = xdr->buf->len;
__be32 *p;
page = page_address(*(resp->rqstp->rq_next_page++)); if (nfserr)
return nfserr;
p = xdr_reserve_space(xdr, 4);
if (!p)
return nfserr_resource;
maxcount = PAGE_SIZE; maxcount = PAGE_SIZE;
RESERVE_SPACE(4);
p = xdr_reserve_space(xdr, maxcount);
if (!p)
return nfserr_resource;
/* /*
* XXX: By default, the ->readlink() VFS op will truncate symlinks * XXX: By default, the ->readlink() VFS op will truncate symlinks
* if they would overflow the buffer. Is this kosher in NFSv4? If * if they would overflow the buffer. Is this kosher in NFSv4? If
* not, one easy fix is: if ->readlink() precisely fills the buffer, * not, one easy fix is: if ->readlink() precisely fills the buffer,
* assume that truncation occurred, and return NFS4ERR_RESOURCE. * assume that truncation occurred, and return NFS4ERR_RESOURCE.
*/ */
nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount); nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
(char *)p, &maxcount);
if (nfserr == nfserr_isdir) if (nfserr == nfserr_isdir)
return nfserr_inval; nfserr = nfserr_inval;
if (nfserr) if (nfserr) {
xdr_truncate_encode(xdr, length_offset);
return nfserr; return nfserr;
WRITE32(maxcount);
ADJUST_ARGS();
resp->xbuf->head[0].iov_len = (char*)p
- (char*)resp->xbuf->head[0].iov_base;
resp->xbuf->page_len = maxcount;
/* Use rest of head for padding and remaining ops: */
resp->xbuf->tail[0].iov_base = p;
resp->xbuf->tail[0].iov_len = 0;
if (maxcount&3) {
RESERVE_SPACE(4);
WRITE32(0);
resp->xbuf->tail[0].iov_base += maxcount&3;
resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
ADJUST_ARGS();
} }
wire_count = htonl(maxcount);
write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, 4);
xdr_truncate_encode(xdr, length_offset + 4 + maxcount);
if (maxcount & 3)
write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount,
&zero, 4 - (maxcount&3));
return 0; return 0;
} }
...@@ -3062,47 +3277,52 @@ static __be32 ...@@ -3062,47 +3277,52 @@ static __be32
nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir) nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir)
{ {
int maxcount; int maxcount;
int bytes_left;
loff_t offset; loff_t offset;
__be32 *page, *savep, *tailbase; __be64 wire_offset;
struct xdr_stream *xdr = &resp->xdr;
int starting_len = xdr->buf->len;
__be32 *p; __be32 *p;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
if (resp->xbuf->page_len)
return nfserr_resource;
if (!*resp->rqstp->rq_next_page)
return nfserr_resource;
RESERVE_SPACE(NFS4_VERIFIER_SIZE); p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
savep = p; if (!p)
return nfserr_resource;
/* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(0); *p++ = cpu_to_be32(0);
ADJUST_ARGS(); resp->xdr.buf->head[0].iov_len = ((char *)resp->xdr.p)
resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; - (char *)resp->xdr.buf->head[0].iov_base;
tailbase = p;
maxcount = PAGE_SIZE;
if (maxcount > readdir->rd_maxcount)
maxcount = readdir->rd_maxcount;
/* /*
* Convert from bytes to words, account for the two words already * Number of bytes left for directory entries allowing for the
* written, make sure to leave two words at the end for the next * final 8 bytes of the readdir and a following failed op:
* pointer and eof field. */
bytes_left = xdr->buf->buflen - xdr->buf->len
- COMPOUND_ERR_SLACK_SPACE - 8;
if (bytes_left < 0) {
nfserr = nfserr_resource;
goto err_no_verf;
}
maxcount = min_t(u32, readdir->rd_maxcount, INT_MAX);
/*
* Note the rfc defines rd_maxcount as the size of the
* READDIR4resok structure, which includes the verifier above
* and the 8 bytes encoded at the end of this function:
*/ */
maxcount = (maxcount >> 2) - 4; if (maxcount < 16) {
if (maxcount < 0) { nfserr = nfserr_toosmall;
nfserr = nfserr_toosmall;
goto err_no_verf; goto err_no_verf;
} }
maxcount = min_t(int, maxcount-16, bytes_left);
page = page_address(*(resp->rqstp->rq_next_page++)); readdir->xdr = xdr;
readdir->rd_maxcount = maxcount;
readdir->common.err = 0; readdir->common.err = 0;
readdir->buflen = maxcount; readdir->cookie_offset = 0;
readdir->buffer = page;
readdir->offset = NULL;
offset = readdir->rd_cookie; offset = readdir->rd_cookie;
nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp,
...@@ -3110,42 +3330,49 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 ...@@ -3110,42 +3330,49 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
&readdir->common, nfsd4_encode_dirent); &readdir->common, nfsd4_encode_dirent);
if (nfserr == nfs_ok && if (nfserr == nfs_ok &&
readdir->common.err == nfserr_toosmall && readdir->common.err == nfserr_toosmall &&
readdir->buffer == page) xdr->buf->len == starting_len + 8) {
nfserr = nfserr_toosmall; /* nothing encoded; which limit did we hit?: */
if (maxcount - 16 < bytes_left)
/* It was the fault of rd_maxcount: */
nfserr = nfserr_toosmall;
else
/* We ran out of buffer space: */
nfserr = nfserr_resource;
}
if (nfserr) if (nfserr)
goto err_no_verf; goto err_no_verf;
if (readdir->offset) if (readdir->cookie_offset) {
xdr_encode_hyper(readdir->offset, offset); wire_offset = cpu_to_be64(offset);
write_bytes_to_xdr_buf(xdr->buf, readdir->cookie_offset,
&wire_offset, 8);
}
p = readdir->buffer; p = xdr_reserve_space(xdr, 8);
if (!p) {
WARN_ON_ONCE(1);
goto err_no_verf;
}
*p++ = 0; /* no more entries */ *p++ = 0; /* no more entries */
*p++ = htonl(readdir->common.err == nfserr_eof); *p++ = htonl(readdir->common.err == nfserr_eof);
resp->xbuf->page_len = ((char*)p) -
(char*)page_address(*(resp->rqstp->rq_next_page-1));
/* Use rest of head for padding and remaining ops: */
resp->xbuf->tail[0].iov_base = tailbase;
resp->xbuf->tail[0].iov_len = 0;
resp->p = resp->xbuf->tail[0].iov_base;
resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4;
return 0; return 0;
err_no_verf: err_no_verf:
p = savep; xdr_truncate_encode(xdr, starting_len);
ADJUST_ARGS();
return nfserr; return nfserr;
} }
static __be32 static __be32
nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove) nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(20); p = xdr_reserve_space(xdr, 20);
write_cinfo(&p, &remove->rm_cinfo); if (!p)
ADJUST_ARGS(); return nfserr_resource;
p = encode_cinfo(p, &remove->rm_cinfo);
} }
return nfserr; return nfserr;
} }
...@@ -3153,19 +3380,21 @@ nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ ...@@ -3153,19 +3380,21 @@ nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
static __be32 static __be32
nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename) nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(40); p = xdr_reserve_space(xdr, 40);
write_cinfo(&p, &rename->rn_sinfo); if (!p)
write_cinfo(&p, &rename->rn_tinfo); return nfserr_resource;
ADJUST_ARGS(); p = encode_cinfo(p, &rename->rn_sinfo);
p = encode_cinfo(p, &rename->rn_tinfo);
} }
return nfserr; return nfserr;
} }
static __be32 static __be32
nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp, nfsd4_do_encode_secinfo(struct xdr_stream *xdr,
__be32 nfserr, struct svc_export *exp) __be32 nfserr, struct svc_export *exp)
{ {
u32 i, nflavs, supported; u32 i, nflavs, supported;
...@@ -3176,6 +3405,7 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp, ...@@ -3176,6 +3405,7 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
if (nfserr) if (nfserr)
goto out; goto out;
nfserr = nfserr_resource;
if (exp->ex_nflavors) { if (exp->ex_nflavors) {
flavs = exp->ex_flavors; flavs = exp->ex_flavors;
nflavs = exp->ex_nflavors; nflavs = exp->ex_nflavors;
...@@ -3197,9 +3427,10 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp, ...@@ -3197,9 +3427,10 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
} }
supported = 0; supported = 0;
RESERVE_SPACE(4); p = xdr_reserve_space(xdr, 4);
if (!p)
goto out;
flavorsp = p++; /* to be backfilled later */ flavorsp = p++; /* to be backfilled later */
ADJUST_ARGS();
for (i = 0; i < nflavs; i++) { for (i = 0; i < nflavs; i++) {
rpc_authflavor_t pf = flavs[i].pseudoflavor; rpc_authflavor_t pf = flavs[i].pseudoflavor;
...@@ -3207,18 +3438,20 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp, ...@@ -3207,18 +3438,20 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
if (rpcauth_get_gssinfo(pf, &info) == 0) { if (rpcauth_get_gssinfo(pf, &info) == 0) {
supported++; supported++;
RESERVE_SPACE(4 + 4 + XDR_LEN(info.oid.len) + 4 + 4); p = xdr_reserve_space(xdr, 4 + 4 +
WRITE32(RPC_AUTH_GSS); XDR_LEN(info.oid.len) + 4 + 4);
WRITE32(info.oid.len); if (!p)
WRITEMEM(info.oid.data, info.oid.len); goto out;
WRITE32(info.qop); *p++ = cpu_to_be32(RPC_AUTH_GSS);
WRITE32(info.service); p = xdr_encode_opaque(p, info.oid.data, info.oid.len);
ADJUST_ARGS(); *p++ = cpu_to_be32(info.qop);
*p++ = cpu_to_be32(info.service);
} else if (pf < RPC_AUTH_MAXFLAVOR) { } else if (pf < RPC_AUTH_MAXFLAVOR) {
supported++; supported++;
RESERVE_SPACE(4); p = xdr_reserve_space(xdr, 4);
WRITE32(pf); if (!p)
ADJUST_ARGS(); goto out;
*p++ = cpu_to_be32(pf);
} else { } else {
if (report) if (report)
pr_warn("NFS: SECINFO: security flavor %u " pr_warn("NFS: SECINFO: security flavor %u "
...@@ -3229,7 +3462,7 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp, ...@@ -3229,7 +3462,7 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
if (nflavs != supported) if (nflavs != supported)
report = false; report = false;
*flavorsp = htonl(supported); *flavorsp = htonl(supported);
nfserr = 0;
out: out:
if (exp) if (exp)
exp_put(exp); exp_put(exp);
...@@ -3240,14 +3473,18 @@ static __be32 ...@@ -3240,14 +3473,18 @@ static __be32
nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr, nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_secinfo *secinfo) struct nfsd4_secinfo *secinfo)
{ {
return nfsd4_do_encode_secinfo(resp, nfserr, secinfo->si_exp); struct xdr_stream *xdr = &resp->xdr;
return nfsd4_do_encode_secinfo(xdr, nfserr, secinfo->si_exp);
} }
static __be32 static __be32
nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr, nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_secinfo_no_name *secinfo) struct nfsd4_secinfo_no_name *secinfo)
{ {
return nfsd4_do_encode_secinfo(resp, nfserr, secinfo->sin_exp); struct xdr_stream *xdr = &resp->xdr;
return nfsd4_do_encode_secinfo(xdr, nfserr, secinfo->sin_exp);
} }
/* /*
...@@ -3257,41 +3494,47 @@ nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr, ...@@ -3257,41 +3494,47 @@ nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
static __be32 static __be32
nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr) nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
RESERVE_SPACE(16); p = xdr_reserve_space(xdr, 16);
if (!p)
return nfserr_resource;
if (nfserr) { if (nfserr) {
WRITE32(3); *p++ = cpu_to_be32(3);
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(0); *p++ = cpu_to_be32(0);
WRITE32(0); *p++ = cpu_to_be32(0);
} }
else { else {
WRITE32(3); *p++ = cpu_to_be32(3);
WRITE32(setattr->sa_bmval[0]); *p++ = cpu_to_be32(setattr->sa_bmval[0]);
WRITE32(setattr->sa_bmval[1]); *p++ = cpu_to_be32(setattr->sa_bmval[1]);
WRITE32(setattr->sa_bmval[2]); *p++ = cpu_to_be32(setattr->sa_bmval[2]);
} }
ADJUST_ARGS();
return nfserr; return nfserr;
} }
static __be32 static __be32
nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd) nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(8 + NFS4_VERIFIER_SIZE); p = xdr_reserve_space(xdr, 8 + NFS4_VERIFIER_SIZE);
WRITEMEM(&scd->se_clientid, 8); if (!p)
WRITEMEM(&scd->se_confirm, NFS4_VERIFIER_SIZE); return nfserr_resource;
ADJUST_ARGS(); p = xdr_encode_opaque_fixed(p, &scd->se_clientid, 8);
p = xdr_encode_opaque_fixed(p, &scd->se_confirm,
NFS4_VERIFIER_SIZE);
} }
else if (nfserr == nfserr_clid_inuse) { else if (nfserr == nfserr_clid_inuse) {
RESERVE_SPACE(8); p = xdr_reserve_space(xdr, 8);
WRITE32(0); if (!p)
WRITE32(0); return nfserr_resource;
ADJUST_ARGS(); *p++ = cpu_to_be32(0);
*p++ = cpu_to_be32(0);
} }
return nfserr; return nfserr;
} }
...@@ -3299,14 +3542,17 @@ nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct n ...@@ -3299,14 +3542,17 @@ nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct n
static __be32 static __be32
nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write) nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (!nfserr) { if (!nfserr) {
RESERVE_SPACE(16); p = xdr_reserve_space(xdr, 16);
WRITE32(write->wr_bytes_written); if (!p)
WRITE32(write->wr_how_written); return nfserr_resource;
WRITEMEM(write->wr_verifier.data, NFS4_VERIFIER_SIZE); *p++ = cpu_to_be32(write->wr_bytes_written);
ADJUST_ARGS(); *p++ = cpu_to_be32(write->wr_how_written);
p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
NFS4_VERIFIER_SIZE);
} }
return nfserr; return nfserr;
} }
...@@ -3323,6 +3569,7 @@ static __be32 ...@@ -3323,6 +3569,7 @@ static __be32
nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_exchange_id *exid) struct nfsd4_exchange_id *exid)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
char *major_id; char *major_id;
char *server_scope; char *server_scope;
...@@ -3338,60 +3585,61 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, ...@@ -3338,60 +3585,61 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
server_scope = utsname()->nodename; server_scope = utsname()->nodename;
server_scope_sz = strlen(server_scope); server_scope_sz = strlen(server_scope);
RESERVE_SPACE( p = xdr_reserve_space(xdr,
8 /* eir_clientid */ + 8 /* eir_clientid */ +
4 /* eir_sequenceid */ + 4 /* eir_sequenceid */ +
4 /* eir_flags */ + 4 /* eir_flags */ +
4 /* spr_how */); 4 /* spr_how */);
if (!p)
return nfserr_resource;
WRITEMEM(&exid->clientid, 8); p = xdr_encode_opaque_fixed(p, &exid->clientid, 8);
WRITE32(exid->seqid); *p++ = cpu_to_be32(exid->seqid);
WRITE32(exid->flags); *p++ = cpu_to_be32(exid->flags);
WRITE32(exid->spa_how); *p++ = cpu_to_be32(exid->spa_how);
ADJUST_ARGS();
switch (exid->spa_how) { switch (exid->spa_how) {
case SP4_NONE: case SP4_NONE:
break; break;
case SP4_MACH_CRED: case SP4_MACH_CRED:
/* spo_must_enforce, spo_must_allow */ /* spo_must_enforce, spo_must_allow */
RESERVE_SPACE(16); p = xdr_reserve_space(xdr, 16);
if (!p)
return nfserr_resource;
/* spo_must_enforce bitmap: */ /* spo_must_enforce bitmap: */
WRITE32(2); *p++ = cpu_to_be32(2);
WRITE32(nfs4_minimal_spo_must_enforce[0]); *p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[0]);
WRITE32(nfs4_minimal_spo_must_enforce[1]); *p++ = cpu_to_be32(nfs4_minimal_spo_must_enforce[1]);
/* empty spo_must_allow bitmap: */ /* empty spo_must_allow bitmap: */
WRITE32(0); *p++ = cpu_to_be32(0);
ADJUST_ARGS();
break; break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
RESERVE_SPACE( p = xdr_reserve_space(xdr,
8 /* so_minor_id */ + 8 /* so_minor_id */ +
4 /* so_major_id.len */ + 4 /* so_major_id.len */ +
(XDR_QUADLEN(major_id_sz) * 4) + (XDR_QUADLEN(major_id_sz) * 4) +
4 /* eir_server_scope.len */ + 4 /* eir_server_scope.len */ +
(XDR_QUADLEN(server_scope_sz) * 4) + (XDR_QUADLEN(server_scope_sz) * 4) +
4 /* eir_server_impl_id.count (0) */); 4 /* eir_server_impl_id.count (0) */);
if (!p)
return nfserr_resource;
/* The server_owner struct */ /* The server_owner struct */
WRITE64(minor_id); /* Minor id */ p = xdr_encode_hyper(p, minor_id); /* Minor id */
/* major id */ /* major id */
WRITE32(major_id_sz); p = xdr_encode_opaque(p, major_id, major_id_sz);
WRITEMEM(major_id, major_id_sz);
/* Server scope */ /* Server scope */
WRITE32(server_scope_sz); p = xdr_encode_opaque(p, server_scope, server_scope_sz);
WRITEMEM(server_scope, server_scope_sz);
/* Implementation id */ /* Implementation id */
WRITE32(0); /* zero length nfs_impl_id4 array */ *p++ = cpu_to_be32(0); /* zero length nfs_impl_id4 array */
ADJUST_ARGS();
return 0; return 0;
} }
...@@ -3399,47 +3647,54 @@ static __be32 ...@@ -3399,47 +3647,54 @@ static __be32
nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr, nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_create_session *sess) struct nfsd4_create_session *sess)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
RESERVE_SPACE(24); p = xdr_reserve_space(xdr, 24);
WRITEMEM(sess->sessionid.data, NFS4_MAX_SESSIONID_LEN); if (!p)
WRITE32(sess->seqid); return nfserr_resource;
WRITE32(sess->flags); p = xdr_encode_opaque_fixed(p, sess->sessionid.data,
ADJUST_ARGS(); NFS4_MAX_SESSIONID_LEN);
*p++ = cpu_to_be32(sess->seqid);
RESERVE_SPACE(28); *p++ = cpu_to_be32(sess->flags);
WRITE32(0); /* headerpadsz */
WRITE32(sess->fore_channel.maxreq_sz); p = xdr_reserve_space(xdr, 28);
WRITE32(sess->fore_channel.maxresp_sz); if (!p)
WRITE32(sess->fore_channel.maxresp_cached); return nfserr_resource;
WRITE32(sess->fore_channel.maxops); *p++ = cpu_to_be32(0); /* headerpadsz */
WRITE32(sess->fore_channel.maxreqs); *p++ = cpu_to_be32(sess->fore_channel.maxreq_sz);
WRITE32(sess->fore_channel.nr_rdma_attrs); *p++ = cpu_to_be32(sess->fore_channel.maxresp_sz);
ADJUST_ARGS(); *p++ = cpu_to_be32(sess->fore_channel.maxresp_cached);
*p++ = cpu_to_be32(sess->fore_channel.maxops);
*p++ = cpu_to_be32(sess->fore_channel.maxreqs);
*p++ = cpu_to_be32(sess->fore_channel.nr_rdma_attrs);
if (sess->fore_channel.nr_rdma_attrs) { if (sess->fore_channel.nr_rdma_attrs) {
RESERVE_SPACE(4); p = xdr_reserve_space(xdr, 4);
WRITE32(sess->fore_channel.rdma_attrs); if (!p)
ADJUST_ARGS(); return nfserr_resource;
*p++ = cpu_to_be32(sess->fore_channel.rdma_attrs);
} }
RESERVE_SPACE(28); p = xdr_reserve_space(xdr, 28);
WRITE32(0); /* headerpadsz */ if (!p)
WRITE32(sess->back_channel.maxreq_sz); return nfserr_resource;
WRITE32(sess->back_channel.maxresp_sz); *p++ = cpu_to_be32(0); /* headerpadsz */
WRITE32(sess->back_channel.maxresp_cached); *p++ = cpu_to_be32(sess->back_channel.maxreq_sz);
WRITE32(sess->back_channel.maxops); *p++ = cpu_to_be32(sess->back_channel.maxresp_sz);
WRITE32(sess->back_channel.maxreqs); *p++ = cpu_to_be32(sess->back_channel.maxresp_cached);
WRITE32(sess->back_channel.nr_rdma_attrs); *p++ = cpu_to_be32(sess->back_channel.maxops);
ADJUST_ARGS(); *p++ = cpu_to_be32(sess->back_channel.maxreqs);
*p++ = cpu_to_be32(sess->back_channel.nr_rdma_attrs);
if (sess->back_channel.nr_rdma_attrs) { if (sess->back_channel.nr_rdma_attrs) {
RESERVE_SPACE(4); p = xdr_reserve_space(xdr, 4);
WRITE32(sess->back_channel.rdma_attrs); if (!p)
ADJUST_ARGS(); return nfserr_resource;
*p++ = cpu_to_be32(sess->back_channel.rdma_attrs);
} }
return 0; return 0;
} }
...@@ -3448,22 +3703,25 @@ static __be32 ...@@ -3448,22 +3703,25 @@ static __be32
nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr, nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_sequence *seq) struct nfsd4_sequence *seq)
{ {
struct xdr_stream *xdr = &resp->xdr;
__be32 *p; __be32 *p;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 20); p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 20);
WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN); if (!p)
WRITE32(seq->seqid); return nfserr_resource;
WRITE32(seq->slotid); p = xdr_encode_opaque_fixed(p, seq->sessionid.data,
NFS4_MAX_SESSIONID_LEN);
*p++ = cpu_to_be32(seq->seqid);
*p++ = cpu_to_be32(seq->slotid);
/* Note slotid's are numbered from zero: */ /* Note slotid's are numbered from zero: */
WRITE32(seq->maxslots - 1); /* sr_highest_slotid */ *p++ = cpu_to_be32(seq->maxslots - 1); /* sr_highest_slotid */
WRITE32(seq->maxslots - 1); /* sr_target_highest_slotid */ *p++ = cpu_to_be32(seq->maxslots - 1); /* sr_target_highest_slotid */
WRITE32(seq->status_flags); *p++ = cpu_to_be32(seq->status_flags);
ADJUST_ARGS(); resp->cstate.data_offset = xdr->buf->len; /* DRC cache data pointer */
resp->cstate.datap = p; /* DRC cache data pointer */
return 0; return 0;
} }
...@@ -3471,20 +3729,22 @@ static __be32 ...@@ -3471,20 +3729,22 @@ static __be32
nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr, nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_test_stateid *test_stateid) struct nfsd4_test_stateid *test_stateid)
{ {
struct xdr_stream *xdr = &resp->xdr;
struct nfsd4_test_stateid_id *stateid, *next; struct nfsd4_test_stateid_id *stateid, *next;
__be32 *p; __be32 *p;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
RESERVE_SPACE(4 + (4 * test_stateid->ts_num_ids)); p = xdr_reserve_space(xdr, 4 + (4 * test_stateid->ts_num_ids));
if (!p)
return nfserr_resource;
*p++ = htonl(test_stateid->ts_num_ids); *p++ = htonl(test_stateid->ts_num_ids);
list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) { list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) {
*p++ = stateid->ts_id_status; *p++ = stateid->ts_id_status;
} }
ADJUST_ARGS();
return nfserr; return nfserr;
} }
...@@ -3563,81 +3823,99 @@ static nfsd4_enc nfsd4_enc_ops[] = { ...@@ -3563,81 +3823,99 @@ static nfsd4_enc nfsd4_enc_ops[] = {
}; };
/* /*
* Calculate the total amount of memory that the compound response has taken * Calculate whether we still have space to encode repsize bytes.
* after encoding the current operation with pad. * There are two considerations:
* * - For NFS versions >=4.1, the size of the reply must stay within
* pad: if operation is non-idempotent, pad was calculate by op_rsize_bop() * session limits
* which was specified at nfsd4_operation, else pad is zero. * - For all NFS versions, we must stay within limited preallocated
* * buffer space.
* Compare this length to the session se_fmaxresp_sz and se_fmaxresp_cached.
* *
* Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so * This is called before the operation is processed, so can only provide
* will be at least a page and will therefore hold the xdr_buf head. * an upper estimate. For some nonidempotent operations (such as
* getattr), it's not necessarily a problem if that estimate is wrong,
* as we can fail it after processing without significant side effects.
*/ */
__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad) __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
{ {
struct xdr_buf *xb = &resp->rqstp->rq_res; struct xdr_buf *buf = &resp->rqstp->rq_res;
struct nfsd4_session *session = NULL;
struct nfsd4_slot *slot = resp->cstate.slot; struct nfsd4_slot *slot = resp->cstate.slot;
u32 length, tlen = 0;
if (buf->len + respsize <= buf->buflen)
return nfs_ok;
if (!nfsd4_has_session(&resp->cstate)) if (!nfsd4_has_session(&resp->cstate))
return 0; return nfserr_resource;
if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) {
session = resp->cstate.session; WARN_ON_ONCE(1);
if (xb->page_len == 0) {
length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
} else {
if (xb->tail[0].iov_base && xb->tail[0].iov_len > 0)
tlen = (char *)resp->p - (char *)xb->tail[0].iov_base;
length = xb->head[0].iov_len + xb->page_len + tlen + pad;
}
dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
length, xb->page_len, tlen, pad);
if (length > session->se_fchannel.maxresp_sz)
return nfserr_rep_too_big;
if ((slot->sl_flags & NFSD4_SLOT_CACHETHIS) &&
length > session->se_fchannel.maxresp_cached)
return nfserr_rep_too_big_to_cache; return nfserr_rep_too_big_to_cache;
}
return 0; return nfserr_rep_too_big;
} }
void void
nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
{ {
struct xdr_stream *xdr = &resp->xdr;
struct nfs4_stateowner *so = resp->cstate.replay_owner; struct nfs4_stateowner *so = resp->cstate.replay_owner;
__be32 *statp; struct svc_rqst *rqstp = resp->rqstp;
int post_err_offset;
nfsd4_enc encoder;
__be32 *p; __be32 *p;
RESERVE_SPACE(8); p = xdr_reserve_space(xdr, 8);
WRITE32(op->opnum); if (!p) {
statp = p++; /* to be backfilled at the end */ WARN_ON_ONCE(1);
ADJUST_ARGS(); return;
}
*p++ = cpu_to_be32(op->opnum);
post_err_offset = xdr->buf->len;
if (op->opnum == OP_ILLEGAL) if (op->opnum == OP_ILLEGAL)
goto status; goto status;
BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) || BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
!nfsd4_enc_ops[op->opnum]); !nfsd4_enc_ops[op->opnum]);
op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u); encoder = nfsd4_enc_ops[op->opnum];
op->status = encoder(resp, op->status, &op->u);
xdr_commit_encode(xdr);
/* nfsd4_check_resp_size guarantees enough room for error status */ /* nfsd4_check_resp_size guarantees enough room for error status */
if (!op->status) if (!op->status) {
op->status = nfsd4_check_resp_size(resp, 0); int space_needed = 0;
if (!nfsd4_last_compound_op(rqstp))
space_needed = COMPOUND_ERR_SLACK_SPACE;
op->status = nfsd4_check_resp_size(resp, space_needed);
}
if (op->status == nfserr_resource && nfsd4_has_session(&resp->cstate)) {
struct nfsd4_slot *slot = resp->cstate.slot;
if (slot->sl_flags & NFSD4_SLOT_CACHETHIS)
op->status = nfserr_rep_too_big_to_cache;
else
op->status = nfserr_rep_too_big;
}
if (op->status == nfserr_resource ||
op->status == nfserr_rep_too_big ||
op->status == nfserr_rep_too_big_to_cache) {
/*
* The operation may have already been encoded or
* partially encoded. No op returns anything additional
* in the case of one of these three errors, so we can
* just truncate back to after the status. But it's a
* bug if we had to do this on a non-idempotent op:
*/
warn_on_nonidempotent_op(op);
xdr_truncate_encode(xdr, post_err_offset);
}
if (so) { if (so) {
int len = xdr->buf->len - post_err_offset;
so->so_replay.rp_status = op->status; so->so_replay.rp_status = op->status;
so->so_replay.rp_buflen = (char *)resp->p - (char *)(statp+1); so->so_replay.rp_buflen = len;
memcpy(so->so_replay.rp_buf, statp+1, so->so_replay.rp_buflen); read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
so->so_replay.rp_buf, len);
} }
status: status:
/* /* Note that op->status is already in network byte order: */
* Note: We write the status directly, instead of using WRITE32(), write_bytes_to_xdr_buf(xdr->buf, post_err_offset - 4, &op->status, 4);
* since it is already in network byte order.
*/
*statp = op->status;
} }
/* /*
...@@ -3649,21 +3927,22 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) ...@@ -3649,21 +3927,22 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
* called with nfs4_lock_state() held * called with nfs4_lock_state() held
*/ */
void void
nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op) nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
{ {
__be32 *p; __be32 *p;
struct nfs4_replay *rp = op->replay; struct nfs4_replay *rp = op->replay;
BUG_ON(!rp); BUG_ON(!rp);
RESERVE_SPACE(8); p = xdr_reserve_space(xdr, 8 + rp->rp_buflen);
WRITE32(op->opnum); if (!p) {
WARN_ON_ONCE(1);
return;
}
*p++ = cpu_to_be32(op->opnum);
*p++ = rp->rp_status; /* already xdr'ed */ *p++ = rp->rp_status; /* already xdr'ed */
ADJUST_ARGS();
RESERVE_SPACE(rp->rp_buflen); p = xdr_encode_opaque_fixed(p, rp->rp_buf, rp->rp_buflen);
WRITEMEM(rp->rp_buf, rp->rp_buflen);
ADJUST_ARGS();
} }
int int
...@@ -3720,19 +3999,19 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo ...@@ -3720,19 +3999,19 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
* All that remains is to write the tag and operation count... * All that remains is to write the tag and operation count...
*/ */
struct nfsd4_compound_state *cs = &resp->cstate; struct nfsd4_compound_state *cs = &resp->cstate;
struct kvec *iov; struct xdr_buf *buf = resp->xdr.buf;
WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len +
buf->tail[0].iov_len);
rqstp->rq_next_page = resp->xdr.page_ptr + 1;
p = resp->tagp; p = resp->tagp;
*p++ = htonl(resp->taglen); *p++ = htonl(resp->taglen);
memcpy(p, resp->tag, resp->taglen); memcpy(p, resp->tag, resp->taglen);
p += XDR_QUADLEN(resp->taglen); p += XDR_QUADLEN(resp->taglen);
*p++ = htonl(resp->opcnt); *p++ = htonl(resp->opcnt);
if (rqstp->rq_res.page_len)
iov = &rqstp->rq_res.tail[0];
else
iov = &rqstp->rq_res.head[0];
iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
BUG_ON(iov->iov_len > PAGE_SIZE);
if (nfsd4_has_session(cs)) { if (nfsd4_has_session(cs)) {
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
struct nfs4_client *clp = cs->session->se_client; struct nfs4_client *clp = cs->session->se_client;
......
...@@ -224,13 +224,6 @@ hash_refile(struct svc_cacherep *rp) ...@@ -224,13 +224,6 @@ hash_refile(struct svc_cacherep *rp)
hlist_add_head(&rp->c_hash, cache_hash + hash_32(rp->c_xid, maskbits)); hlist_add_head(&rp->c_hash, cache_hash + hash_32(rp->c_xid, maskbits));
} }
static inline bool
nfsd_cache_entry_expired(struct svc_cacherep *rp)
{
return rp->c_state != RC_INPROG &&
time_after(jiffies, rp->c_timestamp + RC_EXPIRE);
}
/* /*
* Walk the LRU list and prune off entries that are older than RC_EXPIRE. * Walk the LRU list and prune off entries that are older than RC_EXPIRE.
* Also prune the oldest ones when the total exceeds the max number of entries. * Also prune the oldest ones when the total exceeds the max number of entries.
...@@ -242,8 +235,14 @@ prune_cache_entries(void) ...@@ -242,8 +235,14 @@ prune_cache_entries(void)
long freed = 0; long freed = 0;
list_for_each_entry_safe(rp, tmp, &lru_head, c_lru) { list_for_each_entry_safe(rp, tmp, &lru_head, c_lru) {
if (!nfsd_cache_entry_expired(rp) && /*
num_drc_entries <= max_drc_entries) * Don't free entries attached to calls that are still
* in-progress, but do keep scanning the list.
*/
if (rp->c_state == RC_INPROG)
continue;
if (num_drc_entries <= max_drc_entries &&
time_before(jiffies, rp->c_timestamp + RC_EXPIRE))
break; break;
nfsd_reply_cache_free_locked(rp); nfsd_reply_cache_free_locked(rp);
freed++; freed++;
......
...@@ -1179,7 +1179,6 @@ static int __init init_nfsd(void) ...@@ -1179,7 +1179,6 @@ static int __init init_nfsd(void)
retval = nfsd4_init_slabs(); retval = nfsd4_init_slabs();
if (retval) if (retval)
goto out_unregister_pernet; goto out_unregister_pernet;
nfs4_state_init();
retval = nfsd_fault_inject_init(); /* nfsd fault injection controls */ retval = nfsd_fault_inject_init(); /* nfsd fault injection controls */
if (retval) if (retval)
goto out_free_slabs; goto out_free_slabs;
......
...@@ -15,11 +15,20 @@ ...@@ -15,11 +15,20 @@
#include <linux/nfs2.h> #include <linux/nfs2.h>
#include <linux/nfs3.h> #include <linux/nfs3.h>
#include <linux/nfs4.h> #include <linux/nfs4.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/msg_prot.h> #include <linux/sunrpc/msg_prot.h>
#include <linux/nfsd/debug.h> #include <uapi/linux/nfsd/debug.h>
#include <linux/nfsd/export.h>
#include <linux/nfsd/stats.h> #include "stats.h"
#include "export.h"
#undef ifdebug
#ifdef NFSD_DEBUG
# define ifdebug(flag) if (nfsd_debug & NFSDDBG_##flag)
#else
# define ifdebug(flag) if (0)
#endif
/* /*
* nfsd version * nfsd version
...@@ -106,7 +115,6 @@ static inline int nfsd_v4client(struct svc_rqst *rq) ...@@ -106,7 +115,6 @@ static inline int nfsd_v4client(struct svc_rqst *rq)
*/ */
#ifdef CONFIG_NFSD_V4 #ifdef CONFIG_NFSD_V4
extern unsigned long max_delegations; extern unsigned long max_delegations;
void nfs4_state_init(void);
int nfsd4_init_slabs(void); int nfsd4_init_slabs(void);
void nfsd4_free_slabs(void); void nfsd4_free_slabs(void);
int nfs4_state_start(void); int nfs4_state_start(void);
...@@ -117,7 +125,6 @@ void nfs4_reset_lease(time_t leasetime); ...@@ -117,7 +125,6 @@ void nfs4_reset_lease(time_t leasetime);
int nfs4_reset_recoverydir(char *recdir); int nfs4_reset_recoverydir(char *recdir);
char * nfs4_recoverydir(void); char * nfs4_recoverydir(void);
#else #else
static inline void nfs4_state_init(void) { }
static inline int nfsd4_init_slabs(void) { return 0; } static inline int nfsd4_init_slabs(void) { return 0; }
static inline void nfsd4_free_slabs(void) { } static inline void nfsd4_free_slabs(void) { }
static inline int nfs4_state_start(void) { return 0; } static inline int nfs4_state_start(void) { return 0; }
......
...@@ -88,9 +88,8 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, ...@@ -88,9 +88,8 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
/* Check if the request originated from a secure port. */ /* Check if the request originated from a secure port. */
if (!rqstp->rq_secure && !(flags & NFSEXP_INSECURE_PORT)) { if (!rqstp->rq_secure && !(flags & NFSEXP_INSECURE_PORT)) {
RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
dprintk(KERN_WARNING dprintk("nfsd: request from insecure port %s!\n",
"nfsd: request from insecure port %s!\n", svc_print_addr(rqstp, buf, sizeof(buf)));
svc_print_addr(rqstp, buf, sizeof(buf)));
return nfserr_perm; return nfserr_perm;
} }
...@@ -169,8 +168,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) ...@@ -169,8 +168,8 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
data_left -= len; data_left -= len;
if (data_left < 0) if (data_left < 0)
return error; return error;
exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_auth); exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_fsid);
fid = (struct fid *)(fh->fh_auth + len); fid = (struct fid *)(fh->fh_fsid + len);
} else { } else {
__u32 tfh[2]; __u32 tfh[2];
dev_t xdev; dev_t xdev;
...@@ -385,7 +384,7 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp, ...@@ -385,7 +384,7 @@ static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
{ {
if (dentry != exp->ex_path.dentry) { if (dentry != exp->ex_path.dentry) {
struct fid *fid = (struct fid *) struct fid *fid = (struct fid *)
(fhp->fh_handle.fh_auth + fhp->fh_handle.fh_size/4 - 1); (fhp->fh_handle.fh_fsid + fhp->fh_handle.fh_size/4 - 1);
int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4;
int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK); int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
...@@ -513,7 +512,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, ...@@ -513,7 +512,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
*/ */
struct inode * inode = dentry->d_inode; struct inode * inode = dentry->d_inode;
__u32 *datap;
dev_t ex_dev = exp_sb(exp)->s_dev; dev_t ex_dev = exp_sb(exp)->s_dev;
dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %pd2, ino=%ld)\n", dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %pd2, ino=%ld)\n",
...@@ -557,17 +555,16 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, ...@@ -557,17 +555,16 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
if (inode) if (inode)
_fh_update_old(dentry, exp, &fhp->fh_handle); _fh_update_old(dentry, exp, &fhp->fh_handle);
} else { } else {
int len; fhp->fh_handle.fh_size =
key_len(fhp->fh_handle.fh_fsid_type) + 4;
fhp->fh_handle.fh_auth_type = 0; fhp->fh_handle.fh_auth_type = 0;
datap = fhp->fh_handle.fh_auth+0;
mk_fsid(fhp->fh_handle.fh_fsid_type, datap, ex_dev, mk_fsid(fhp->fh_handle.fh_fsid_type,
fhp->fh_handle.fh_fsid,
ex_dev,
exp->ex_path.dentry->d_inode->i_ino, exp->ex_path.dentry->d_inode->i_ino,
exp->ex_fsid, exp->ex_uuid); exp->ex_fsid, exp->ex_uuid);
len = key_len(fhp->fh_handle.fh_fsid_type);
datap += len/4;
fhp->fh_handle.fh_size = 4 + len;
if (inode) if (inode)
_fh_update(fhp, exp, dentry); _fh_update(fhp, exp, dentry);
if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) { if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) {
......
/* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> */ /*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*
* This file describes the layout of the file handles as passed
* over the wire.
*/
#ifndef _LINUX_NFSD_NFSFH_H
#define _LINUX_NFSD_NFSFH_H
#include <linux/sunrpc/svc.h>
#include <uapi/linux/nfsd/nfsfh.h>
static inline __u32 ino_t_to_u32(ino_t ino)
{
return (__u32) ino;
}
static inline ino_t u32_to_ino_t(__u32 uino)
{
return (ino_t) uino;
}
#ifndef _LINUX_NFSD_FH_INT_H /*
#define _LINUX_NFSD_FH_INT_H * This is the internal representation of an NFS handle used in knfsd.
* pre_mtime/post_version will be used to support wcc_attr's in NFSv3.
*/
typedef struct svc_fh {
struct knfsd_fh fh_handle; /* FH data */
struct dentry * fh_dentry; /* validated dentry */
struct svc_export * fh_export; /* export pointer */
int fh_maxsize; /* max size for fh_handle */
unsigned char fh_locked; /* inode locked by us */
unsigned char fh_want_write; /* remount protection taken */
#ifdef CONFIG_NFSD_V3
unsigned char fh_post_saved; /* post-op attrs saved */
unsigned char fh_pre_saved; /* pre-op attrs saved */
/* Pre-op attributes saved during fh_lock */
__u64 fh_pre_size; /* size before operation */
struct timespec fh_pre_mtime; /* mtime before oper */
struct timespec fh_pre_ctime; /* ctime before oper */
/*
* pre-op nfsv4 change attr: note must check IS_I_VERSION(inode)
* to find out if it is valid.
*/
u64 fh_pre_change;
/* Post-op attributes saved in fh_unlock */
struct kstat fh_post_attr; /* full attrs after operation */
u64 fh_post_change; /* nfsv4 change; see above */
#endif /* CONFIG_NFSD_V3 */
#include <linux/nfsd/nfsfh.h> } svc_fh;
enum nfsd_fsid { enum nfsd_fsid {
FSID_DEV = 0, FSID_DEV = 0,
...@@ -215,4 +264,4 @@ fh_unlock(struct svc_fh *fhp) ...@@ -215,4 +264,4 @@ fh_unlock(struct svc_fh *fhp)
} }
} }
#endif /* _LINUX_NFSD_FH_INT_H */ #endif /* _LINUX_NFSD_NFSFH_H */
...@@ -591,12 +591,6 @@ nfsd(void *vrqstp) ...@@ -591,12 +591,6 @@ nfsd(void *vrqstp)
nfsdstats.th_cnt++; nfsdstats.th_cnt++;
mutex_unlock(&nfsd_mutex); mutex_unlock(&nfsd_mutex);
/*
* We want less throttling in balance_dirty_pages() so that nfs to
* localhost doesn't cause nfsd to lock up due to all the client's
* dirty pages.
*/
current->flags |= PF_LESS_THROTTLE;
set_freezable(); set_freezable();
/* /*
......
...@@ -214,7 +214,8 @@ nfssvc_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy) ...@@ -214,7 +214,8 @@ nfssvc_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
int int
nfssvc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p, struct nfsd_fhandle *args) nfssvc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p, struct nfsd_fhandle *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
...@@ -248,7 +249,8 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -248,7 +249,8 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
{ {
unsigned int len; unsigned int len;
int v; int v;
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
args->offset = ntohl(*p++); args->offset = ntohl(*p++);
...@@ -281,7 +283,8 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -281,7 +283,8 @@ nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p,
unsigned int len, hdr, dlen; unsigned int len, hdr, dlen;
int v; int v;
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
p++; /* beginoffset */ p++; /* beginoffset */
...@@ -355,7 +358,8 @@ nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p, ...@@ -355,7 +358,8 @@ nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p,
int int
nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_readlinkargs *args) nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_readlinkargs *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
args->buffer = page_address(*(rqstp->rq_next_page++)); args->buffer = page_address(*(rqstp->rq_next_page++));
...@@ -391,7 +395,8 @@ int ...@@ -391,7 +395,8 @@ int
nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p, nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd_readdirargs *args) struct nfsd_readdirargs *args)
{ {
if (!(p = decode_fh(p, &args->fh))) p = decode_fh(p, &args->fh);
if (!p)
return 0; return 0;
args->cookie = ntohl(*p++); args->cookie = ntohl(*p++);
args->count = ntohl(*p++); args->count = ntohl(*p++);
......
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/svc_xprt.h>
#include <linux/nfsd/nfsfh.h>
#include "nfsfh.h" #include "nfsfh.h"
typedef struct { typedef struct {
...@@ -123,7 +122,7 @@ static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s) ...@@ -123,7 +122,7 @@ static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s)
/* Maximum number of operations per session compound */ /* Maximum number of operations per session compound */
#define NFSD_MAX_OPS_PER_COMPOUND 16 #define NFSD_MAX_OPS_PER_COMPOUND 16
/* Maximum session per slot cache size */ /* Maximum session per slot cache size */
#define NFSD_SLOT_CACHE_SIZE 1024 #define NFSD_SLOT_CACHE_SIZE 2048
/* Maximum number of NFSD_SLOT_CACHE_SIZE slots per session */ /* Maximum number of NFSD_SLOT_CACHE_SIZE slots per session */
#define NFSD_CACHE_SIZE_SLOTS_PER_SESSION 32 #define NFSD_CACHE_SIZE_SLOTS_PER_SESSION 32
#define NFSD_MAX_MEM_PER_SESSION \ #define NFSD_MAX_MEM_PER_SESSION \
...@@ -464,8 +463,6 @@ extern void nfs4_release_reclaim(struct nfsd_net *); ...@@ -464,8 +463,6 @@ extern void nfs4_release_reclaim(struct nfsd_net *);
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir, extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir,
struct nfsd_net *nn); struct nfsd_net *nn);
extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn); extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn);
extern void nfs4_free_openowner(struct nfs4_openowner *);
extern void nfs4_free_lockowner(struct nfs4_lockowner *);
extern int set_callback_cred(void); extern int set_callback_cred(void);
extern void nfsd4_init_callback(struct nfsd4_callback *); extern void nfsd4_init_callback(struct nfsd4_callback *);
extern void nfsd4_probe_callback(struct nfs4_client *clp); extern void nfsd4_probe_callback(struct nfs4_client *clp);
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/nfsd/stats.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include "nfsd.h" #include "nfsd.h"
......
/* /*
* linux/include/linux/nfsd/stats.h
*
* Statistics for NFS server. * Statistics for NFS server.
* *
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/ */
#ifndef LINUX_NFSD_STATS_H #ifndef _NFSD_STATS_H
#define LINUX_NFSD_STATS_H #define _NFSD_STATS_H
#include <uapi/linux/nfsd/stats.h> #include <uapi/linux/nfsd/stats.h>
...@@ -42,4 +40,4 @@ extern struct svc_stat nfsd_svcstats; ...@@ -42,4 +40,4 @@ extern struct svc_stat nfsd_svcstats;
void nfsd_stat_init(void); void nfsd_stat_init(void);
void nfsd_stat_shutdown(void); void nfsd_stat_shutdown(void);
#endif /* LINUX_NFSD_STATS_H */ #endif /* _NFSD_STATS_H */
...@@ -820,55 +820,54 @@ static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe, ...@@ -820,55 +820,54 @@ static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe,
return __splice_from_pipe(pipe, sd, nfsd_splice_actor); return __splice_from_pipe(pipe, sd, nfsd_splice_actor);
} }
static __be32 __be32 nfsd_finish_read(struct file *file, unsigned long *count, int host_err)
nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
{ {
mm_segment_t oldfs;
__be32 err;
int host_err;
err = nfserr_perm;
if (file->f_op->splice_read && rqstp->rq_splice_ok) {
struct splice_desc sd = {
.len = 0,
.total_len = *count,
.pos = offset,
.u.data = rqstp,
};
rqstp->rq_next_page = rqstp->rq_respages + 1;
host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor);
} else {
oldfs = get_fs();
set_fs(KERNEL_DS);
host_err = vfs_readv(file, (struct iovec __user *)vec, vlen, &offset);
set_fs(oldfs);
}
if (host_err >= 0) { if (host_err >= 0) {
nfsdstats.io_read += host_err; nfsdstats.io_read += host_err;
*count = host_err; *count = host_err;
err = 0;
fsnotify_access(file); fsnotify_access(file);
return 0;
} else } else
err = nfserrno(host_err); return nfserrno(host_err);
return err; }
int nfsd_splice_read(struct svc_rqst *rqstp,
struct file *file, loff_t offset, unsigned long *count)
{
struct splice_desc sd = {
.len = 0,
.total_len = *count,
.pos = offset,
.u.data = rqstp,
};
int host_err;
rqstp->rq_next_page = rqstp->rq_respages + 1;
host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor);
return nfsd_finish_read(file, count, host_err);
} }
static void kill_suid(struct dentry *dentry) int nfsd_readv(struct file *file, loff_t offset, struct kvec *vec, int vlen,
unsigned long *count)
{ {
struct iattr ia; mm_segment_t oldfs;
ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; int host_err;
mutex_lock(&dentry->d_inode->i_mutex); oldfs = get_fs();
/* set_fs(KERNEL_DS);
* Note we call this on write, so notify_change will not host_err = vfs_readv(file, (struct iovec __user *)vec, vlen, &offset);
* encounter any conflicting delegations: set_fs(oldfs);
*/ return nfsd_finish_read(file, count, host_err);
notify_change(dentry, &ia, NULL); }
mutex_unlock(&dentry->d_inode->i_mutex);
static __be32
nfsd_vfs_read(struct svc_rqst *rqstp, struct file *file,
loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
{
if (file->f_op->splice_read && rqstp->rq_splice_ok)
return nfsd_splice_read(rqstp, file, offset, count);
else
return nfsd_readv(file, offset, vec, vlen, count);
} }
/* /*
...@@ -922,6 +921,16 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -922,6 +921,16 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
int stable = *stablep; int stable = *stablep;
int use_wgather; int use_wgather;
loff_t pos = offset; loff_t pos = offset;
unsigned int pflags = current->flags;
if (rqstp->rq_local)
/*
* We want less throttling in balance_dirty_pages()
* and shrink_inactive_list() so that nfs to
* localhost doesn't cause nfsd to lock up due to all
* the client's dirty pages or its congested queue.
*/
current->flags |= PF_LESS_THROTTLE;
dentry = file->f_path.dentry; dentry = file->f_path.dentry;
inode = dentry->d_inode; inode = dentry->d_inode;
...@@ -942,10 +951,6 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -942,10 +951,6 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
nfsdstats.io_write += host_err; nfsdstats.io_write += host_err;
fsnotify_modify(file); fsnotify_modify(file);
/* clear setuid/setgid flag after write */
if (inode->i_mode & (S_ISUID | S_ISGID))
kill_suid(dentry);
if (stable) { if (stable) {
if (use_wgather) if (use_wgather)
host_err = wait_for_concurrent_writes(file); host_err = wait_for_concurrent_writes(file);
...@@ -959,36 +964,33 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -959,36 +964,33 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
err = 0; err = 0;
else else
err = nfserrno(host_err); err = nfserrno(host_err);
if (rqstp->rq_local)
tsk_restore_flags(current, pflags, PF_LESS_THROTTLE);
return err; return err;
} }
/* __be32 nfsd_get_tmp_read_open(struct svc_rqst *rqstp, struct svc_fh *fhp,
* Read data from a file. count must contain the requested read count struct file **file, struct raparms **ra)
* on entry. On return, *count contains the number of bytes actually read.
* N.B. After this call fhp needs an fh_put
*/
__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
{ {
struct file *file;
struct inode *inode; struct inode *inode;
struct raparms *ra;
__be32 err; __be32 err;
err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, file);
if (err) if (err)
return err; return err;
inode = file_inode(file); inode = file_inode(*file);
/* Get readahead parameters */ /* Get readahead parameters */
ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino); *ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino);
if (ra && ra->p_set) if (*ra && (*ra)->p_set)
file->f_ra = ra->p_ra; (*file)->f_ra = (*ra)->p_ra;
return nfs_ok;
err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); }
void nfsd_put_tmp_read_open(struct file *file, struct raparms *ra)
{
/* Write back readahead params */ /* Write back readahead params */
if (ra) { if (ra) {
struct raparm_hbucket *rab = &raparm_hash[ra->p_hindex]; struct raparm_hbucket *rab = &raparm_hash[ra->p_hindex];
...@@ -998,28 +1000,29 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -998,28 +1000,29 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
ra->p_count--; ra->p_count--;
spin_unlock(&rab->pb_lock); spin_unlock(&rab->pb_lock);
} }
nfsd_close(file); nfsd_close(file);
return err;
} }
/* As above, but use the provided file descriptor. */ /*
__be32 * Read data from a file. count must contain the requested read count
nfsd_read_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, * on entry. On return, *count contains the number of bytes actually read.
loff_t offset, struct kvec *vec, int vlen, * N.B. After this call fhp needs an fh_put
unsigned long *count) */
__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t offset, struct kvec *vec, int vlen, unsigned long *count)
{ {
__be32 err; struct file *file;
struct raparms *ra;
__be32 err;
err = nfsd_get_tmp_read_open(rqstp, fhp, &file, &ra);
if (err)
return err;
err = nfsd_vfs_read(rqstp, file, offset, vec, vlen, count);
nfsd_put_tmp_read_open(file, ra);
if (file) {
err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry,
NFSD_MAY_READ|NFSD_MAY_OWNER_OVERRIDE);
if (err)
goto out;
err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count);
} else /* Note file may still be NULL in NFSv4 special stateid case: */
err = nfsd_read(rqstp, fhp, offset, vec, vlen, count);
out:
return err; return err;
} }
......
...@@ -70,10 +70,16 @@ __be32 nfsd_commit(struct svc_rqst *, struct svc_fh *, ...@@ -70,10 +70,16 @@ __be32 nfsd_commit(struct svc_rqst *, struct svc_fh *,
__be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t, __be32 nfsd_open(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **); int, struct file **);
void nfsd_close(struct file *); void nfsd_close(struct file *);
struct raparms;
__be32 nfsd_get_tmp_read_open(struct svc_rqst *, struct svc_fh *,
struct file **, struct raparms **);
void nfsd_put_tmp_read_open(struct file *, struct raparms *);
int nfsd_splice_read(struct svc_rqst *,
struct file *, loff_t, unsigned long *);
int nfsd_readv(struct file *, loff_t, struct kvec *, int,
unsigned long *);
__be32 nfsd_read(struct svc_rqst *, struct svc_fh *, __be32 nfsd_read(struct svc_rqst *, struct svc_fh *,
loff_t, struct kvec *, int, unsigned long *); loff_t, struct kvec *, int, unsigned long *);
__be32 nfsd_read_file(struct svc_rqst *, struct svc_fh *, struct file *,
loff_t, struct kvec *, int, unsigned long *);
__be32 nfsd_write(struct svc_rqst *, struct svc_fh *,struct file *, __be32 nfsd_write(struct svc_rqst *, struct svc_fh *,struct file *,
loff_t, struct kvec *,int, unsigned long *, int *); loff_t, struct kvec *,int, unsigned long *, int *);
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *, __be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
......
...@@ -58,7 +58,7 @@ struct nfsd4_compound_state { ...@@ -58,7 +58,7 @@ struct nfsd4_compound_state {
/* For sessions DRC */ /* For sessions DRC */
struct nfsd4_session *session; struct nfsd4_session *session;
struct nfsd4_slot *slot; struct nfsd4_slot *slot;
__be32 *datap; int data_offset;
size_t iovlen; size_t iovlen;
u32 minorversion; u32 minorversion;
__be32 status; __be32 status;
...@@ -287,9 +287,8 @@ struct nfsd4_readdir { ...@@ -287,9 +287,8 @@ struct nfsd4_readdir {
struct svc_fh * rd_fhp; /* response */ struct svc_fh * rd_fhp; /* response */
struct readdir_cd common; struct readdir_cd common;
__be32 * buffer; struct xdr_stream *xdr;
int buflen; int cookie_offset;
__be32 * offset;
}; };
struct nfsd4_release_lockowner { struct nfsd4_release_lockowner {
...@@ -506,9 +505,7 @@ struct nfsd4_compoundargs { ...@@ -506,9 +505,7 @@ struct nfsd4_compoundargs {
struct nfsd4_compoundres { struct nfsd4_compoundres {
/* scratch variables for XDR encode */ /* scratch variables for XDR encode */
__be32 * p; struct xdr_stream xdr;
__be32 * end;
struct xdr_buf * xbuf;
struct svc_rqst * rqstp; struct svc_rqst * rqstp;
u32 taglen; u32 taglen;
...@@ -538,6 +535,9 @@ static inline bool nfsd4_last_compound_op(struct svc_rqst *rqstp) ...@@ -538,6 +535,9 @@ static inline bool nfsd4_last_compound_op(struct svc_rqst *rqstp)
return argp->opcnt == resp->opcnt; return argp->opcnt == resp->opcnt;
} }
int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op);
void warn_on_nonidempotent_op(struct nfsd4_op *op);
#define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs)
static inline void static inline void
...@@ -563,10 +563,11 @@ int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *, ...@@ -563,10 +563,11 @@ int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *,
struct nfsd4_compoundres *); struct nfsd4_compoundres *);
__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32); __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *); void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op); void nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op);
__be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, __be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
struct dentry *dentry, __be32 **buffer, int countp, struct svc_fh *fhp, struct svc_export *exp,
u32 *bmval, struct svc_rqst *, int ignore_crossmnt); struct dentry *dentry,
u32 *bmval, struct svc_rqst *, int ignore_crossmnt);
extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp, extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp,
struct nfsd4_compound_state *, struct nfsd4_compound_state *,
struct nfsd4_setclientid *setclid); struct nfsd4_setclientid *setclid);
......
...@@ -17,13 +17,13 @@ ...@@ -17,13 +17,13 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/nfsd/nfsfh.h>
#include <linux/lockd/bind.h> #include <linux/lockd/bind.h>
#include <linux/lockd/xdr.h> #include <linux/lockd/xdr.h>
#ifdef CONFIG_LOCKD_V4 #ifdef CONFIG_LOCKD_V4
#include <linux/lockd/xdr4.h> #include <linux/lockd/xdr4.h>
#endif #endif
#include <linux/lockd/debug.h> #include <linux/lockd/debug.h>
#include <linux/sunrpc/svc.h>
/* /*
* Version string * Version string
......
...@@ -399,8 +399,6 @@ enum lock_type4 { ...@@ -399,8 +399,6 @@ enum lock_type4 {
#define FATTR4_WORD2_LAYOUT_BLKSIZE (1UL << 1) #define FATTR4_WORD2_LAYOUT_BLKSIZE (1UL << 1)
#define FATTR4_WORD2_MDSTHRESHOLD (1UL << 4) #define FATTR4_WORD2_MDSTHRESHOLD (1UL << 4)
#define FATTR4_WORD2_SECURITY_LABEL (1UL << 16) #define FATTR4_WORD2_SECURITY_LABEL (1UL << 16)
#define FATTR4_WORD2_CHANGE_SECURITY_LABEL \
(1UL << 17)
/* MDS threshold bitmap bits */ /* MDS threshold bitmap bits */
#define THRESHOLD_RD (1UL << 0) #define THRESHOLD_RD (1UL << 0)
......
/*
* linux/include/linux/nfsd/debug.h
*
* Debugging-related stuff for nfsd
*
* Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef LINUX_NFSD_DEBUG_H
#define LINUX_NFSD_DEBUG_H
#include <uapi/linux/nfsd/debug.h>
# undef ifdebug
# ifdef NFSD_DEBUG
# define ifdebug(flag) if (nfsd_debug & NFSDDBG_##flag)
# else
# define ifdebug(flag) if (0)
# endif
#endif /* LINUX_NFSD_DEBUG_H */
/*
* include/linux/nfsd/nfsfh.h
*
* This file describes the layout of the file handles as passed
* over the wire.
*
* Earlier versions of knfsd used to sign file handles using keyed MD5
* or SHA. I've removed this code, because it doesn't give you more
* security than blocking external access to port 2049 on your firewall.
*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#ifndef _LINUX_NFSD_FH_H
#define _LINUX_NFSD_FH_H
# include <linux/sunrpc/svc.h>
#include <uapi/linux/nfsd/nfsfh.h>
static inline __u32 ino_t_to_u32(ino_t ino)
{
return (__u32) ino;
}
static inline ino_t u32_to_ino_t(__u32 uino)
{
return (ino_t) uino;
}
/*
* This is the internal representation of an NFS handle used in knfsd.
* pre_mtime/post_version will be used to support wcc_attr's in NFSv3.
*/
typedef struct svc_fh {
struct knfsd_fh fh_handle; /* FH data */
struct dentry * fh_dentry; /* validated dentry */
struct svc_export * fh_export; /* export pointer */
int fh_maxsize; /* max size for fh_handle */
unsigned char fh_locked; /* inode locked by us */
unsigned char fh_want_write; /* remount protection taken */
#ifdef CONFIG_NFSD_V3
unsigned char fh_post_saved; /* post-op attrs saved */
unsigned char fh_pre_saved; /* pre-op attrs saved */
/* Pre-op attributes saved during fh_lock */
__u64 fh_pre_size; /* size before operation */
struct timespec fh_pre_mtime; /* mtime before oper */
struct timespec fh_pre_ctime; /* ctime before oper */
/*
* pre-op nfsv4 change attr: note must check IS_I_VERSION(inode)
* to find out if it is valid.
*/
u64 fh_pre_change;
/* Post-op attributes saved in fh_unlock */
struct kstat fh_post_attr; /* full attrs after operation */
u64 fh_post_change; /* nfsv4 change; see above */
#endif /* CONFIG_NFSD_V3 */
} svc_fh;
#endif /* _LINUX_NFSD_FH_H */
...@@ -244,6 +244,7 @@ struct svc_rqst { ...@@ -244,6 +244,7 @@ struct svc_rqst {
struct page * rq_pages[RPCSVC_MAXPAGES]; struct page * rq_pages[RPCSVC_MAXPAGES];
struct page * *rq_respages; /* points into rq_pages */ struct page * *rq_respages; /* points into rq_pages */
struct page * *rq_next_page; /* next reply page to use */ struct page * *rq_next_page; /* next reply page to use */
struct page * *rq_page_end; /* one past the last page */
struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */ struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */
...@@ -254,11 +255,15 @@ struct svc_rqst { ...@@ -254,11 +255,15 @@ struct svc_rqst {
u32 rq_prot; /* IP protocol */ u32 rq_prot; /* IP protocol */
unsigned short unsigned short
rq_secure : 1; /* secure port */ rq_secure : 1; /* secure port */
unsigned short rq_local : 1; /* local request */
void * rq_argp; /* decoded arguments */ void * rq_argp; /* decoded arguments */
void * rq_resp; /* xdr'd results */ void * rq_resp; /* xdr'd results */
void * rq_auth_data; /* flavor-specific data */ void * rq_auth_data; /* flavor-specific data */
int rq_auth_slack; /* extra space xdr code
* should leave in head
* for krb5i, krb5p.
*/
int rq_reserved; /* space on socket outq int rq_reserved; /* space on socket outq
* reserved for this request * reserved for this request
*/ */
...@@ -454,11 +459,7 @@ char * svc_print_addr(struct svc_rqst *, char *, size_t); ...@@ -454,11 +459,7 @@ char * svc_print_addr(struct svc_rqst *, char *, size_t);
*/ */
static inline void svc_reserve_auth(struct svc_rqst *rqstp, int space) static inline void svc_reserve_auth(struct svc_rqst *rqstp, int space)
{ {
int added_space = 0; svc_reserve(rqstp, space + rqstp->rq_auth_slack);
if (rqstp->rq_authop->flavour)
added_space = RPC_MAX_AUTH_SIZE;
svc_reserve(rqstp, space + added_space);
} }
#endif /* SUNRPC_SVC_H */ #endif /* SUNRPC_SVC_H */
...@@ -115,14 +115,13 @@ struct svc_rdma_fastreg_mr { ...@@ -115,14 +115,13 @@ struct svc_rdma_fastreg_mr {
struct list_head frmr_list; struct list_head frmr_list;
}; };
struct svc_rdma_req_map { struct svc_rdma_req_map {
struct svc_rdma_fastreg_mr *frmr;
unsigned long count; unsigned long count;
union { union {
struct kvec sge[RPCSVC_MAXPAGES]; struct kvec sge[RPCSVC_MAXPAGES];
struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES]; struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES];
unsigned long lkey[RPCSVC_MAXPAGES];
}; };
}; };
#define RDMACTXT_F_FAST_UNREG 1
#define RDMACTXT_F_LAST_CTXT 2 #define RDMACTXT_F_LAST_CTXT 2
#define SVCRDMA_DEVCAP_FAST_REG 1 /* fast mr registration */ #define SVCRDMA_DEVCAP_FAST_REG 1 /* fast mr registration */
......
...@@ -24,6 +24,7 @@ struct svc_xprt_ops { ...@@ -24,6 +24,7 @@ struct svc_xprt_ops {
void (*xpo_release_rqst)(struct svc_rqst *); void (*xpo_release_rqst)(struct svc_rqst *);
void (*xpo_detach)(struct svc_xprt *); void (*xpo_detach)(struct svc_xprt *);
void (*xpo_free)(struct svc_xprt *); void (*xpo_free)(struct svc_xprt *);
int (*xpo_secure_port)(struct svc_rqst *);
}; };
struct svc_xprt_class { struct svc_xprt_class {
...@@ -63,6 +64,7 @@ struct svc_xprt { ...@@ -63,6 +64,7 @@ struct svc_xprt {
#define XPT_DETACHED 10 /* detached from tempsocks list */ #define XPT_DETACHED 10 /* detached from tempsocks list */
#define XPT_LISTENER 11 /* listening endpoint */ #define XPT_LISTENER 11 /* listening endpoint */
#define XPT_CACHE_AUTH 12 /* cache auth info */ #define XPT_CACHE_AUTH 12 /* cache auth info */
#define XPT_LOCAL 13 /* connection from loopback interface */
struct svc_serv *xpt_server; /* service for transport */ struct svc_serv *xpt_server; /* service for transport */
atomic_t xpt_reserved; /* space on outq that is rsvd */ atomic_t xpt_reserved; /* space on outq that is rsvd */
......
...@@ -215,6 +215,9 @@ typedef int (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj); ...@@ -215,6 +215,9 @@ typedef int (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_commit_encode(struct xdr_stream *xdr);
extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
extern int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen);
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
unsigned int base, unsigned int len); unsigned int base, unsigned int len);
extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr); extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr);
......
/* /*
* include/linux/nfsd/nfsfh.h
*
* This file describes the layout of the file handles as passed * This file describes the layout of the file handles as passed
* over the wire. * over the wire.
* *
* Earlier versions of knfsd used to sign file handles using keyed MD5
* or SHA. I've removed this code, because it doesn't give you more
* security than blocking external access to port 2049 on your firewall.
*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/ */
...@@ -37,7 +31,7 @@ struct nfs_fhbase_old { ...@@ -37,7 +31,7 @@ struct nfs_fhbase_old {
}; };
/* /*
* This is the new flexible, extensible style NFSv2/v3 file handle. * This is the new flexible, extensible style NFSv2/v3/v4 file handle.
* by Neil Brown <neilb@cse.unsw.edu.au> - March 2000 * by Neil Brown <neilb@cse.unsw.edu.au> - March 2000
* *
* The file handle starts with a sequence of four-byte words. * The file handle starts with a sequence of four-byte words.
...@@ -47,14 +41,7 @@ struct nfs_fhbase_old { ...@@ -47,14 +41,7 @@ struct nfs_fhbase_old {
* *
* All four-byte values are in host-byte-order. * All four-byte values are in host-byte-order.
* *
* The auth_type field specifies how the filehandle can be authenticated * The auth_type field is deprecated and must be set to 0.
* This might allow a file to be confirmed to be in a writable part of a
* filetree without checking the path from it up to the root.
* Current values:
* 0 - No authentication. fb_auth is 0 bytes long
* Possible future values:
* 1 - 4 bytes taken from MD5 hash of the remainer of the file handle
* prefixed by a secret and with the important export flags.
* *
* The fsid_type identifies how the filesystem (or export point) is * The fsid_type identifies how the filesystem (or export point) is
* encoded. * encoded.
...@@ -71,14 +58,9 @@ struct nfs_fhbase_old { ...@@ -71,14 +58,9 @@ struct nfs_fhbase_old {
* 7 - 8 byte inode number and 16 byte uuid * 7 - 8 byte inode number and 16 byte uuid
* *
* The fileid_type identified how the file within the filesystem is encoded. * The fileid_type identified how the file within the filesystem is encoded.
* This is (will be) passed to, and set by, the underlying filesystem if it supports * The values for this field are filesystem specific, exccept that
* filehandle operations. The filesystem must not use the value '0' or '0xff' and may * filesystems must not use the values '0' or '0xff'. 'See enum fid_type'
* only use the values 1 and 2 as defined below: * in include/linux/exportfs.h for currently registered values.
* Current values:
* 0 - The root, or export point, of the filesystem. fb_fileid is 0 bytes.
* 1 - 32bit inode number, 32 bit generation number.
* 2 - 32bit inode number, 32 bit generation number, 32 bit parent directory inode number.
*
*/ */
struct nfs_fhbase_new { struct nfs_fhbase_new {
__u8 fb_version; /* == 1, even => nfs_fhbase_old */ __u8 fb_version; /* == 1, even => nfs_fhbase_old */
...@@ -114,9 +96,9 @@ struct knfsd_fh { ...@@ -114,9 +96,9 @@ struct knfsd_fh {
#define fh_fsid_type fh_base.fh_new.fb_fsid_type #define fh_fsid_type fh_base.fh_new.fb_fsid_type
#define fh_auth_type fh_base.fh_new.fb_auth_type #define fh_auth_type fh_base.fh_new.fb_auth_type
#define fh_fileid_type fh_base.fh_new.fb_fileid_type #define fh_fileid_type fh_base.fh_new.fb_fileid_type
#define fh_auth fh_base.fh_new.fb_auth
#define fh_fsid fh_base.fh_new.fb_auth #define fh_fsid fh_base.fh_new.fb_auth
/* Do not use, provided for userspace compatiblity. */
#define fh_auth fh_base.fh_new.fb_auth
#endif /* _UAPI_LINUX_NFSD_FH_H */ #endif /* _UAPI_LINUX_NFSD_FH_H */
...@@ -1503,6 +1503,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) ...@@ -1503,6 +1503,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
if (unwrap_integ_data(rqstp, &rqstp->rq_arg, if (unwrap_integ_data(rqstp, &rqstp->rq_arg,
gc->gc_seq, rsci->mechctx)) gc->gc_seq, rsci->mechctx))
goto garbage_args; goto garbage_args;
rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE;
break; break;
case RPC_GSS_SVC_PRIVACY: case RPC_GSS_SVC_PRIVACY:
/* placeholders for length and seq. number: */ /* placeholders for length and seq. number: */
...@@ -1511,6 +1512,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) ...@@ -1511,6 +1512,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
if (unwrap_priv_data(rqstp, &rqstp->rq_arg, if (unwrap_priv_data(rqstp, &rqstp->rq_arg,
gc->gc_seq, rsci->mechctx)) gc->gc_seq, rsci->mechctx))
goto garbage_args; goto garbage_args;
rqstp->rq_auth_slack = RPC_MAX_AUTH_SIZE * 2;
break; break;
default: default:
goto auth_err; goto auth_err;
......
...@@ -374,7 +374,7 @@ void sunrpc_destroy_cache_detail(struct cache_detail *cd) ...@@ -374,7 +374,7 @@ void sunrpc_destroy_cache_detail(struct cache_detail *cd)
} }
return; return;
out: out:
printk(KERN_ERR "nfsd: failed to unregister %s cache\n", cd->name); printk(KERN_ERR "RPC: failed to unregister %s cache\n", cd->name);
} }
EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail); EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail);
......
...@@ -43,6 +43,19 @@ static inline int rpc_reply_expected(struct rpc_task *task) ...@@ -43,6 +43,19 @@ static inline int rpc_reply_expected(struct rpc_task *task)
(task->tk_msg.rpc_proc->p_decode != NULL); (task->tk_msg.rpc_proc->p_decode != NULL);
} }
static inline int sock_is_loopback(struct sock *sk)
{
struct dst_entry *dst;
int loopback = 0;
rcu_read_lock();
dst = rcu_dereference(sk->sk_dst_cache);
if (dst && dst->dev &&
(dst->dev->features & NETIF_F_LOOPBACK))
loopback = 1;
rcu_read_unlock();
return loopback;
}
int svc_send_common(struct socket *sock, struct xdr_buf *xdr, int svc_send_common(struct socket *sock, struct xdr_buf *xdr,
struct page *headpage, unsigned long headoffset, struct page *headpage, unsigned long headoffset,
struct page *tailpage, unsigned long tailoffset); struct page *tailpage, unsigned long tailoffset);
......
...@@ -597,6 +597,7 @@ static int svc_alloc_arg(struct svc_rqst *rqstp) ...@@ -597,6 +597,7 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)
} }
rqstp->rq_pages[i] = p; rqstp->rq_pages[i] = p;
} }
rqstp->rq_page_end = &rqstp->rq_pages[i];
rqstp->rq_pages[i++] = NULL; /* this might be seen in nfs_read_actor */ rqstp->rq_pages[i++] = NULL; /* this might be seen in nfs_read_actor */
/* Make arg->head point to first page and arg->pages point to rest */ /* Make arg->head point to first page and arg->pages point to rest */
...@@ -730,6 +731,8 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt) ...@@ -730,6 +731,8 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)
newxpt = xprt->xpt_ops->xpo_accept(xprt); newxpt = xprt->xpt_ops->xpo_accept(xprt);
if (newxpt) if (newxpt)
svc_add_new_temp_xprt(serv, newxpt); svc_add_new_temp_xprt(serv, newxpt);
else
module_put(xprt->xpt_class->xcl_owner);
} else if (xprt->xpt_ops->xpo_has_wspace(xprt)) { } else if (xprt->xpt_ops->xpo_has_wspace(xprt)) {
/* XPT_DATA|XPT_DEFERRED case: */ /* XPT_DATA|XPT_DEFERRED case: */
dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n", dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n",
...@@ -793,7 +796,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) ...@@ -793,7 +796,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
clear_bit(XPT_OLD, &xprt->xpt_flags); clear_bit(XPT_OLD, &xprt->xpt_flags);
rqstp->rq_secure = svc_port_is_privileged(svc_addr(rqstp)); rqstp->rq_secure = xprt->xpt_ops->xpo_secure_port(rqstp);
rqstp->rq_chandle.defer = svc_defer; rqstp->rq_chandle.defer = svc_defer;
if (serv->sv_stats) if (serv->sv_stats)
......
...@@ -54,6 +54,8 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp) ...@@ -54,6 +54,8 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp)
} }
spin_unlock(&authtab_lock); spin_unlock(&authtab_lock);
rqstp->rq_auth_slack = 0;
rqstp->rq_authop = aops; rqstp->rq_authop = aops;
return aops->accept(rqstp, authp); return aops->accept(rqstp, authp);
} }
......
...@@ -400,6 +400,12 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd, ...@@ -400,6 +400,12 @@ static void svc_sock_setbufsize(struct socket *sock, unsigned int snd,
release_sock(sock->sk); release_sock(sock->sk);
#endif #endif
} }
static int svc_sock_secure_port(struct svc_rqst *rqstp)
{
return svc_port_is_privileged(svc_addr(rqstp));
}
/* /*
* INET callback when data has been received on the socket. * INET callback when data has been received on the socket.
*/ */
...@@ -678,6 +684,7 @@ static struct svc_xprt_ops svc_udp_ops = { ...@@ -678,6 +684,7 @@ static struct svc_xprt_ops svc_udp_ops = {
.xpo_prep_reply_hdr = svc_udp_prep_reply_hdr, .xpo_prep_reply_hdr = svc_udp_prep_reply_hdr,
.xpo_has_wspace = svc_udp_has_wspace, .xpo_has_wspace = svc_udp_has_wspace,
.xpo_accept = svc_udp_accept, .xpo_accept = svc_udp_accept,
.xpo_secure_port = svc_sock_secure_port,
}; };
static struct svc_xprt_class svc_udp_class = { static struct svc_xprt_class svc_udp_class = {
...@@ -842,8 +849,7 @@ static struct svc_xprt *svc_tcp_accept(struct svc_xprt *xprt) ...@@ -842,8 +849,7 @@ static struct svc_xprt *svc_tcp_accept(struct svc_xprt *xprt)
* tell us anything. For now just warn about unpriv connections. * tell us anything. For now just warn about unpriv connections.
*/ */
if (!svc_port_is_privileged(sin)) { if (!svc_port_is_privileged(sin)) {
dprintk(KERN_WARNING dprintk("%s: connect from unprivileged port: %s\n",
"%s: connect from unprivileged port: %s\n",
serv->sv_name, serv->sv_name,
__svc_print_addr(sin, buf, sizeof(buf))); __svc_print_addr(sin, buf, sizeof(buf)));
} }
...@@ -867,6 +873,10 @@ static struct svc_xprt *svc_tcp_accept(struct svc_xprt *xprt) ...@@ -867,6 +873,10 @@ static struct svc_xprt *svc_tcp_accept(struct svc_xprt *xprt)
} }
svc_xprt_set_local(&newsvsk->sk_xprt, sin, slen); svc_xprt_set_local(&newsvsk->sk_xprt, sin, slen);
if (sock_is_loopback(newsock->sk))
set_bit(XPT_LOCAL, &newsvsk->sk_xprt.xpt_flags);
else
clear_bit(XPT_LOCAL, &newsvsk->sk_xprt.xpt_flags);
if (serv->sv_stats) if (serv->sv_stats)
serv->sv_stats->nettcpconn++; serv->sv_stats->nettcpconn++;
...@@ -1112,6 +1122,7 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) ...@@ -1112,6 +1122,7 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
rqstp->rq_xprt_ctxt = NULL; rqstp->rq_xprt_ctxt = NULL;
rqstp->rq_prot = IPPROTO_TCP; rqstp->rq_prot = IPPROTO_TCP;
rqstp->rq_local = !!test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags);
p = (__be32 *)rqstp->rq_arg.head[0].iov_base; p = (__be32 *)rqstp->rq_arg.head[0].iov_base;
calldir = p[1]; calldir = p[1];
...@@ -1234,6 +1245,7 @@ static struct svc_xprt_ops svc_tcp_bc_ops = { ...@@ -1234,6 +1245,7 @@ static struct svc_xprt_ops svc_tcp_bc_ops = {
.xpo_detach = svc_bc_tcp_sock_detach, .xpo_detach = svc_bc_tcp_sock_detach,
.xpo_free = svc_bc_sock_free, .xpo_free = svc_bc_sock_free,
.xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr, .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr,
.xpo_secure_port = svc_sock_secure_port,
}; };
static struct svc_xprt_class svc_tcp_bc_class = { static struct svc_xprt_class svc_tcp_bc_class = {
...@@ -1272,6 +1284,7 @@ static struct svc_xprt_ops svc_tcp_ops = { ...@@ -1272,6 +1284,7 @@ static struct svc_xprt_ops svc_tcp_ops = {
.xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr, .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr,
.xpo_has_wspace = svc_tcp_has_wspace, .xpo_has_wspace = svc_tcp_has_wspace,
.xpo_accept = svc_tcp_accept, .xpo_accept = svc_tcp_accept,
.xpo_secure_port = svc_sock_secure_port,
}; };
static struct svc_xprt_class svc_tcp_class = { static struct svc_xprt_class svc_tcp_class = {
......
...@@ -462,6 +462,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) ...@@ -462,6 +462,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
struct kvec *iov = buf->head; struct kvec *iov = buf->head;
int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len; int scratch_len = buf->buflen - buf->page_len - buf->tail[0].iov_len;
xdr_set_scratch_buffer(xdr, NULL, 0);
BUG_ON(scratch_len < 0); BUG_ON(scratch_len < 0);
xdr->buf = buf; xdr->buf = buf;
xdr->iov = iov; xdr->iov = iov;
...@@ -481,6 +482,73 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) ...@@ -481,6 +482,73 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
} }
EXPORT_SYMBOL_GPL(xdr_init_encode); EXPORT_SYMBOL_GPL(xdr_init_encode);
/**
* xdr_commit_encode - Ensure all data is written to buffer
* @xdr: pointer to xdr_stream
*
* We handle encoding across page boundaries by giving the caller a
* temporary location to write to, then later copying the data into
* place; xdr_commit_encode does that copying.
*
* Normally the caller doesn't need to call this directly, as the
* following xdr_reserve_space will do it. But an explicit call may be
* required at the end of encoding, or any other time when the xdr_buf
* data might be read.
*/
void xdr_commit_encode(struct xdr_stream *xdr)
{
int shift = xdr->scratch.iov_len;
void *page;
if (shift == 0)
return;
page = page_address(*xdr->page_ptr);
memcpy(xdr->scratch.iov_base, page, shift);
memmove(page, page + shift, (void *)xdr->p - page);
xdr->scratch.iov_len = 0;
}
EXPORT_SYMBOL_GPL(xdr_commit_encode);
__be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr, size_t nbytes)
{
static __be32 *p;
int space_left;
int frag1bytes, frag2bytes;
if (nbytes > PAGE_SIZE)
return NULL; /* Bigger buffers require special handling */
if (xdr->buf->len + nbytes > xdr->buf->buflen)
return NULL; /* Sorry, we're totally out of space */
frag1bytes = (xdr->end - xdr->p) << 2;
frag2bytes = nbytes - frag1bytes;
if (xdr->iov)
xdr->iov->iov_len += frag1bytes;
else
xdr->buf->page_len += frag1bytes;
xdr->page_ptr++;
xdr->iov = NULL;
/*
* If the last encode didn't end exactly on a page boundary, the
* next one will straddle boundaries. Encode into the next
* page, then copy it back later in xdr_commit_encode. We use
* the "scratch" iov to track any temporarily unused fragment of
* space at the end of the previous buffer:
*/
xdr->scratch.iov_base = xdr->p;
xdr->scratch.iov_len = frag1bytes;
p = page_address(*xdr->page_ptr);
/*
* Note this is where the next encode will start after we've
* shifted this one back:
*/
xdr->p = (void *)p + frag2bytes;
space_left = xdr->buf->buflen - xdr->buf->len;
xdr->end = (void *)p + min_t(int, space_left, PAGE_SIZE);
xdr->buf->page_len += frag2bytes;
xdr->buf->len += nbytes;
return p;
}
/** /**
* xdr_reserve_space - Reserve buffer space for sending * xdr_reserve_space - Reserve buffer space for sending
* @xdr: pointer to xdr_stream * @xdr: pointer to xdr_stream
...@@ -495,19 +563,121 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes) ...@@ -495,19 +563,121 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
__be32 *p = xdr->p; __be32 *p = xdr->p;
__be32 *q; __be32 *q;
xdr_commit_encode(xdr);
/* align nbytes on the next 32-bit boundary */ /* align nbytes on the next 32-bit boundary */
nbytes += 3; nbytes += 3;
nbytes &= ~3; nbytes &= ~3;
q = p + (nbytes >> 2); q = p + (nbytes >> 2);
if (unlikely(q > xdr->end || q < p)) if (unlikely(q > xdr->end || q < p))
return NULL; return xdr_get_next_encode_buffer(xdr, nbytes);
xdr->p = q; xdr->p = q;
xdr->iov->iov_len += nbytes; if (xdr->iov)
xdr->iov->iov_len += nbytes;
else
xdr->buf->page_len += nbytes;
xdr->buf->len += nbytes; xdr->buf->len += nbytes;
return p; return p;
} }
EXPORT_SYMBOL_GPL(xdr_reserve_space); EXPORT_SYMBOL_GPL(xdr_reserve_space);
/**
* xdr_truncate_encode - truncate an encode buffer
* @xdr: pointer to xdr_stream
* @len: new length of buffer
*
* Truncates the xdr stream, so that xdr->buf->len == len,
* and xdr->p points at offset len from the start of the buffer, and
* head, tail, and page lengths are adjusted to correspond.
*
* If this means moving xdr->p to a different buffer, we assume that
* that the end pointer should be set to the end of the current page,
* except in the case of the head buffer when we assume the head
* buffer's current length represents the end of the available buffer.
*
* This is *not* safe to use on a buffer that already has inlined page
* cache pages (as in a zero-copy server read reply), except for the
* simple case of truncating from one position in the tail to another.
*
*/
void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
{
struct xdr_buf *buf = xdr->buf;
struct kvec *head = buf->head;
struct kvec *tail = buf->tail;
int fraglen;
int new, old;
if (len > buf->len) {
WARN_ON_ONCE(1);
return;
}
xdr_commit_encode(xdr);
fraglen = min_t(int, buf->len - len, tail->iov_len);
tail->iov_len -= fraglen;
buf->len -= fraglen;
if (tail->iov_len && buf->len == len) {
xdr->p = tail->iov_base + tail->iov_len;
/* xdr->end, xdr->iov should be set already */
return;
}
WARN_ON_ONCE(fraglen);
fraglen = min_t(int, buf->len - len, buf->page_len);
buf->page_len -= fraglen;
buf->len -= fraglen;
new = buf->page_base + buf->page_len;
old = new + fraglen;
xdr->page_ptr -= (old >> PAGE_SHIFT) - (new >> PAGE_SHIFT);
if (buf->page_len && buf->len == len) {
xdr->p = page_address(*xdr->page_ptr);
xdr->end = (void *)xdr->p + PAGE_SIZE;
xdr->p = (void *)xdr->p + (new % PAGE_SIZE);
/* xdr->iov should already be NULL */
return;
}
if (fraglen) {
xdr->end = head->iov_base + head->iov_len;
xdr->page_ptr--;
}
/* (otherwise assume xdr->end is already set) */
head->iov_len = len;
buf->len = len;
xdr->p = head->iov_base + head->iov_len;
xdr->iov = buf->head;
}
EXPORT_SYMBOL(xdr_truncate_encode);
/**
* xdr_restrict_buflen - decrease available buffer space
* @xdr: pointer to xdr_stream
* @newbuflen: new maximum number of bytes available
*
* Adjust our idea of how much space is available in the buffer.
* If we've already used too much space in the buffer, returns -1.
* If the available space is already smaller than newbuflen, returns 0
* and does nothing. Otherwise, adjusts xdr->buf->buflen to newbuflen
* and ensures xdr->end is set at most offset newbuflen from the start
* of the buffer.
*/
int xdr_restrict_buflen(struct xdr_stream *xdr, int newbuflen)
{
struct xdr_buf *buf = xdr->buf;
int left_in_this_buf = (void *)xdr->end - (void *)xdr->p;
int end_offset = buf->len + left_in_this_buf;
if (newbuflen < 0 || newbuflen < buf->len)
return -1;
if (newbuflen > buf->buflen)
return 0;
if (newbuflen < end_offset)
xdr->end = (void *)xdr->end + newbuflen - end_offset;
buf->buflen = newbuflen;
return 0;
}
EXPORT_SYMBOL(xdr_restrict_buflen);
/** /**
* xdr_write_pages - Insert a list of pages into an XDR buffer for sending * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
* @xdr: pointer to xdr_stream * @xdr: pointer to xdr_stream
......
/* /*
* Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved.
* Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved. * Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
...@@ -69,7 +70,8 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, ...@@ -69,7 +70,8 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp,
/* Set up the XDR head */ /* Set up the XDR head */
rqstp->rq_arg.head[0].iov_base = page_address(page); rqstp->rq_arg.head[0].iov_base = page_address(page);
rqstp->rq_arg.head[0].iov_len = min(byte_count, ctxt->sge[0].length); rqstp->rq_arg.head[0].iov_len =
min_t(size_t, byte_count, ctxt->sge[0].length);
rqstp->rq_arg.len = byte_count; rqstp->rq_arg.len = byte_count;
rqstp->rq_arg.buflen = byte_count; rqstp->rq_arg.buflen = byte_count;
...@@ -85,7 +87,7 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, ...@@ -85,7 +87,7 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp,
page = ctxt->pages[sge_no]; page = ctxt->pages[sge_no];
put_page(rqstp->rq_pages[sge_no]); put_page(rqstp->rq_pages[sge_no]);
rqstp->rq_pages[sge_no] = page; rqstp->rq_pages[sge_no] = page;
bc -= min(bc, ctxt->sge[sge_no].length); bc -= min_t(u32, bc, ctxt->sge[sge_no].length);
rqstp->rq_arg.buflen += ctxt->sge[sge_no].length; rqstp->rq_arg.buflen += ctxt->sge[sge_no].length;
sge_no++; sge_no++;
} }
...@@ -113,291 +115,265 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, ...@@ -113,291 +115,265 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp,
rqstp->rq_arg.tail[0].iov_len = 0; rqstp->rq_arg.tail[0].iov_len = 0;
} }
/* Encode a read-chunk-list as an array of IB SGE static int rdma_read_max_sge(struct svcxprt_rdma *xprt, int sge_count)
*
* Assumptions:
* - chunk[0]->position points to pages[0] at an offset of 0
* - pages[] is not physically or virtually contiguous and consists of
* PAGE_SIZE elements.
*
* Output:
* - sge array pointing into pages[] array.
* - chunk_sge array specifying sge index and count for each
* chunk in the read list
*
*/
static int map_read_chunks(struct svcxprt_rdma *xprt,
struct svc_rqst *rqstp,
struct svc_rdma_op_ctxt *head,
struct rpcrdma_msg *rmsgp,
struct svc_rdma_req_map *rpl_map,
struct svc_rdma_req_map *chl_map,
int ch_count,
int byte_count)
{ {
int sge_no; if (rdma_node_get_transport(xprt->sc_cm_id->device->node_type) ==
int sge_bytes; RDMA_TRANSPORT_IWARP)
int page_off; return 1;
int page_no; else
int ch_bytes; return min_t(int, sge_count, xprt->sc_max_sge);
int ch_no; }
struct rpcrdma_read_chunk *ch;
sge_no = 0; typedef int (*rdma_reader_fn)(struct svcxprt_rdma *xprt,
page_no = 0; struct svc_rqst *rqstp,
page_off = 0; struct svc_rdma_op_ctxt *head,
ch = (struct rpcrdma_read_chunk *)&rmsgp->rm_body.rm_chunks[0]; int *page_no,
ch_no = 0; u32 *page_offset,
ch_bytes = ntohl(ch->rc_target.rs_length); u32 rs_handle,
head->arg.head[0] = rqstp->rq_arg.head[0]; u32 rs_length,
head->arg.tail[0] = rqstp->rq_arg.tail[0]; u64 rs_offset,
head->arg.pages = &head->pages[head->count]; int last);
head->hdr_count = head->count; /* save count of hdr pages */
head->arg.page_base = 0; /* Issue an RDMA_READ using the local lkey to map the data sink */
head->arg.page_len = ch_bytes; static int rdma_read_chunk_lcl(struct svcxprt_rdma *xprt,
head->arg.len = rqstp->rq_arg.len + ch_bytes; struct svc_rqst *rqstp,
head->arg.buflen = rqstp->rq_arg.buflen + ch_bytes; struct svc_rdma_op_ctxt *head,
head->count++; int *page_no,
chl_map->ch[0].start = 0; u32 *page_offset,
while (byte_count) { u32 rs_handle,
rpl_map->sge[sge_no].iov_base = u32 rs_length,
page_address(rqstp->rq_arg.pages[page_no]) + page_off; u64 rs_offset,
sge_bytes = min_t(int, PAGE_SIZE-page_off, ch_bytes); int last)
rpl_map->sge[sge_no].iov_len = sge_bytes; {
/* struct ib_send_wr read_wr;
* Don't bump head->count here because the same page int pages_needed = PAGE_ALIGN(*page_offset + rs_length) >> PAGE_SHIFT;
* may be used by multiple SGE. struct svc_rdma_op_ctxt *ctxt = svc_rdma_get_context(xprt);
*/ int ret, read, pno;
head->arg.pages[page_no] = rqstp->rq_arg.pages[page_no]; u32 pg_off = *page_offset;
rqstp->rq_respages = &rqstp->rq_arg.pages[page_no+1]; u32 pg_no = *page_no;
ctxt->direction = DMA_FROM_DEVICE;
ctxt->read_hdr = head;
pages_needed =
min_t(int, pages_needed, rdma_read_max_sge(xprt, pages_needed));
read = min_t(int, pages_needed << PAGE_SHIFT, rs_length);
for (pno = 0; pno < pages_needed; pno++) {
int len = min_t(int, rs_length, PAGE_SIZE - pg_off);
head->arg.pages[pg_no] = rqstp->rq_arg.pages[pg_no];
head->arg.page_len += len;
head->arg.len += len;
if (!pg_off)
head->count++;
rqstp->rq_respages = &rqstp->rq_arg.pages[pg_no+1];
rqstp->rq_next_page = rqstp->rq_respages + 1; rqstp->rq_next_page = rqstp->rq_respages + 1;
ctxt->sge[pno].addr =
ib_dma_map_page(xprt->sc_cm_id->device,
head->arg.pages[pg_no], pg_off,
PAGE_SIZE - pg_off,
DMA_FROM_DEVICE);
ret = ib_dma_mapping_error(xprt->sc_cm_id->device,
ctxt->sge[pno].addr);
if (ret)
goto err;
atomic_inc(&xprt->sc_dma_used);
byte_count -= sge_bytes; /* The lkey here is either a local dma lkey or a dma_mr lkey */
ch_bytes -= sge_bytes; ctxt->sge[pno].lkey = xprt->sc_dma_lkey;
sge_no++; ctxt->sge[pno].length = len;
/* ctxt->count++;
* If all bytes for this chunk have been mapped to an
* SGE, move to the next SGE /* adjust offset and wrap to next page if needed */
*/ pg_off += len;
if (ch_bytes == 0) { if (pg_off == PAGE_SIZE) {
chl_map->ch[ch_no].count = pg_off = 0;
sge_no - chl_map->ch[ch_no].start; pg_no++;
ch_no++;
ch++;
chl_map->ch[ch_no].start = sge_no;
ch_bytes = ntohl(ch->rc_target.rs_length);
/* If bytes remaining account for next chunk */
if (byte_count) {
head->arg.page_len += ch_bytes;
head->arg.len += ch_bytes;
head->arg.buflen += ch_bytes;
}
} }
/* rs_length -= len;
* If this SGE consumed all of the page, move to the
* next page
*/
if ((sge_bytes + page_off) == PAGE_SIZE) {
page_no++;
page_off = 0;
/*
* If there are still bytes left to map, bump
* the page count
*/
if (byte_count)
head->count++;
} else
page_off += sge_bytes;
} }
BUG_ON(byte_count != 0);
return sge_no; if (last && rs_length == 0)
set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
else
clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
memset(&read_wr, 0, sizeof(read_wr));
read_wr.wr_id = (unsigned long)ctxt;
read_wr.opcode = IB_WR_RDMA_READ;
ctxt->wr_op = read_wr.opcode;
read_wr.send_flags = IB_SEND_SIGNALED;
read_wr.wr.rdma.rkey = rs_handle;
read_wr.wr.rdma.remote_addr = rs_offset;
read_wr.sg_list = ctxt->sge;
read_wr.num_sge = pages_needed;
ret = svc_rdma_send(xprt, &read_wr);
if (ret) {
pr_err("svcrdma: Error %d posting RDMA_READ\n", ret);
set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
goto err;
}
/* return current location in page array */
*page_no = pg_no;
*page_offset = pg_off;
ret = read;
atomic_inc(&rdma_stat_read);
return ret;
err:
svc_rdma_unmap_dma(ctxt);
svc_rdma_put_context(ctxt, 0);
return ret;
} }
/* Map a read-chunk-list to an XDR and fast register the page-list. /* Issue an RDMA_READ using an FRMR to map the data sink */
* static int rdma_read_chunk_frmr(struct svcxprt_rdma *xprt,
* Assumptions:
* - chunk[0] position points to pages[0] at an offset of 0
* - pages[] will be made physically contiguous by creating a one-off memory
* region using the fastreg verb.
* - byte_count is # of bytes in read-chunk-list
* - ch_count is # of chunks in read-chunk-list
*
* Output:
* - sge array pointing into pages[] array.
* - chunk_sge array specifying sge index and count for each
* chunk in the read list
*/
static int fast_reg_read_chunks(struct svcxprt_rdma *xprt,
struct svc_rqst *rqstp, struct svc_rqst *rqstp,
struct svc_rdma_op_ctxt *head, struct svc_rdma_op_ctxt *head,
struct rpcrdma_msg *rmsgp, int *page_no,
struct svc_rdma_req_map *rpl_map, u32 *page_offset,
struct svc_rdma_req_map *chl_map, u32 rs_handle,
int ch_count, u32 rs_length,
int byte_count) u64 rs_offset,
int last)
{ {
int page_no; struct ib_send_wr read_wr;
int ch_no; struct ib_send_wr inv_wr;
u32 offset; struct ib_send_wr fastreg_wr;
struct rpcrdma_read_chunk *ch; u8 key;
struct svc_rdma_fastreg_mr *frmr; int pages_needed = PAGE_ALIGN(*page_offset + rs_length) >> PAGE_SHIFT;
int ret = 0; struct svc_rdma_op_ctxt *ctxt = svc_rdma_get_context(xprt);
struct svc_rdma_fastreg_mr *frmr = svc_rdma_get_frmr(xprt);
int ret, read, pno;
u32 pg_off = *page_offset;
u32 pg_no = *page_no;
frmr = svc_rdma_get_frmr(xprt);
if (IS_ERR(frmr)) if (IS_ERR(frmr))
return -ENOMEM; return -ENOMEM;
head->frmr = frmr; ctxt->direction = DMA_FROM_DEVICE;
head->arg.head[0] = rqstp->rq_arg.head[0]; ctxt->frmr = frmr;
head->arg.tail[0] = rqstp->rq_arg.tail[0]; pages_needed = min_t(int, pages_needed, xprt->sc_frmr_pg_list_len);
head->arg.pages = &head->pages[head->count]; read = min_t(int, pages_needed << PAGE_SHIFT, rs_length);
head->hdr_count = head->count; /* save count of hdr pages */
head->arg.page_base = 0;
head->arg.page_len = byte_count;
head->arg.len = rqstp->rq_arg.len + byte_count;
head->arg.buflen = rqstp->rq_arg.buflen + byte_count;
/* Fast register the page list */ frmr->kva = page_address(rqstp->rq_arg.pages[pg_no]);
frmr->kva = page_address(rqstp->rq_arg.pages[0]);
frmr->direction = DMA_FROM_DEVICE; frmr->direction = DMA_FROM_DEVICE;
frmr->access_flags = (IB_ACCESS_LOCAL_WRITE|IB_ACCESS_REMOTE_WRITE); frmr->access_flags = (IB_ACCESS_LOCAL_WRITE|IB_ACCESS_REMOTE_WRITE);
frmr->map_len = byte_count; frmr->map_len = pages_needed << PAGE_SHIFT;
frmr->page_list_len = PAGE_ALIGN(byte_count) >> PAGE_SHIFT; frmr->page_list_len = pages_needed;
for (page_no = 0; page_no < frmr->page_list_len; page_no++) {
frmr->page_list->page_list[page_no] = for (pno = 0; pno < pages_needed; pno++) {
int len = min_t(int, rs_length, PAGE_SIZE - pg_off);
head->arg.pages[pg_no] = rqstp->rq_arg.pages[pg_no];
head->arg.page_len += len;
head->arg.len += len;
if (!pg_off)
head->count++;
rqstp->rq_respages = &rqstp->rq_arg.pages[pg_no+1];
rqstp->rq_next_page = rqstp->rq_respages + 1;
frmr->page_list->page_list[pno] =
ib_dma_map_page(xprt->sc_cm_id->device, ib_dma_map_page(xprt->sc_cm_id->device,
rqstp->rq_arg.pages[page_no], 0, head->arg.pages[pg_no], 0,
PAGE_SIZE, DMA_FROM_DEVICE); PAGE_SIZE, DMA_FROM_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device, ret = ib_dma_mapping_error(xprt->sc_cm_id->device,
frmr->page_list->page_list[page_no])) frmr->page_list->page_list[pno]);
goto fatal_err; if (ret)
goto err;
atomic_inc(&xprt->sc_dma_used); atomic_inc(&xprt->sc_dma_used);
head->arg.pages[page_no] = rqstp->rq_arg.pages[page_no];
}
head->count += page_no;
/* rq_respages points one past arg pages */
rqstp->rq_respages = &rqstp->rq_arg.pages[page_no];
rqstp->rq_next_page = rqstp->rq_respages + 1;
/* Create the reply and chunk maps */ /* adjust offset and wrap to next page if needed */
offset = 0; pg_off += len;
ch = (struct rpcrdma_read_chunk *)&rmsgp->rm_body.rm_chunks[0]; if (pg_off == PAGE_SIZE) {
for (ch_no = 0; ch_no < ch_count; ch_no++) { pg_off = 0;
int len = ntohl(ch->rc_target.rs_length); pg_no++;
rpl_map->sge[ch_no].iov_base = frmr->kva + offset; }
rpl_map->sge[ch_no].iov_len = len; rs_length -= len;
chl_map->ch[ch_no].count = 1;
chl_map->ch[ch_no].start = ch_no;
offset += len;
ch++;
} }
ret = svc_rdma_fastreg(xprt, frmr); if (last && rs_length == 0)
if (ret) set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
goto fatal_err; else
clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
return ch_no;
fatal_err:
printk("svcrdma: error fast registering xdr for xprt %p", xprt);
svc_rdma_put_frmr(xprt, frmr);
return -EIO;
}
static int rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
struct svc_rdma_op_ctxt *ctxt,
struct svc_rdma_fastreg_mr *frmr,
struct kvec *vec,
u64 *sgl_offset,
int count)
{
int i;
unsigned long off;
ctxt->count = count; /* Bump the key */
ctxt->direction = DMA_FROM_DEVICE; key = (u8)(frmr->mr->lkey & 0x000000FF);
for (i = 0; i < count; i++) { ib_update_fast_reg_key(frmr->mr, ++key);
ctxt->sge[i].length = 0; /* in case map fails */
if (!frmr) { ctxt->sge[0].addr = (unsigned long)frmr->kva + *page_offset;
BUG_ON(!virt_to_page(vec[i].iov_base)); ctxt->sge[0].lkey = frmr->mr->lkey;
off = (unsigned long)vec[i].iov_base & ~PAGE_MASK; ctxt->sge[0].length = read;
ctxt->sge[i].addr = ctxt->count = 1;
ib_dma_map_page(xprt->sc_cm_id->device, ctxt->read_hdr = head;
virt_to_page(vec[i].iov_base),
off, /* Prepare FASTREG WR */
vec[i].iov_len, memset(&fastreg_wr, 0, sizeof(fastreg_wr));
DMA_FROM_DEVICE); fastreg_wr.opcode = IB_WR_FAST_REG_MR;
if (ib_dma_mapping_error(xprt->sc_cm_id->device, fastreg_wr.send_flags = IB_SEND_SIGNALED;
ctxt->sge[i].addr)) fastreg_wr.wr.fast_reg.iova_start = (unsigned long)frmr->kva;
return -EINVAL; fastreg_wr.wr.fast_reg.page_list = frmr->page_list;
ctxt->sge[i].lkey = xprt->sc_dma_lkey; fastreg_wr.wr.fast_reg.page_list_len = frmr->page_list_len;
atomic_inc(&xprt->sc_dma_used); fastreg_wr.wr.fast_reg.page_shift = PAGE_SHIFT;
} else { fastreg_wr.wr.fast_reg.length = frmr->map_len;
ctxt->sge[i].addr = (unsigned long)vec[i].iov_base; fastreg_wr.wr.fast_reg.access_flags = frmr->access_flags;
ctxt->sge[i].lkey = frmr->mr->lkey; fastreg_wr.wr.fast_reg.rkey = frmr->mr->lkey;
} fastreg_wr.next = &read_wr;
ctxt->sge[i].length = vec[i].iov_len;
*sgl_offset = *sgl_offset + vec[i].iov_len; /* Prepare RDMA_READ */
memset(&read_wr, 0, sizeof(read_wr));
read_wr.send_flags = IB_SEND_SIGNALED;
read_wr.wr.rdma.rkey = rs_handle;
read_wr.wr.rdma.remote_addr = rs_offset;
read_wr.sg_list = ctxt->sge;
read_wr.num_sge = 1;
if (xprt->sc_dev_caps & SVCRDMA_DEVCAP_READ_W_INV) {
read_wr.opcode = IB_WR_RDMA_READ_WITH_INV;
read_wr.wr_id = (unsigned long)ctxt;
read_wr.ex.invalidate_rkey = ctxt->frmr->mr->lkey;
} else {
read_wr.opcode = IB_WR_RDMA_READ;
read_wr.next = &inv_wr;
/* Prepare invalidate */
memset(&inv_wr, 0, sizeof(inv_wr));
inv_wr.wr_id = (unsigned long)ctxt;
inv_wr.opcode = IB_WR_LOCAL_INV;
inv_wr.send_flags = IB_SEND_SIGNALED | IB_SEND_FENCE;
inv_wr.ex.invalidate_rkey = frmr->mr->lkey;
}
ctxt->wr_op = read_wr.opcode;
/* Post the chain */
ret = svc_rdma_send(xprt, &fastreg_wr);
if (ret) {
pr_err("svcrdma: Error %d posting RDMA_READ\n", ret);
set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
goto err;
} }
return 0;
}
static int rdma_read_max_sge(struct svcxprt_rdma *xprt, int sge_count) /* return current location in page array */
{ *page_no = pg_no;
if ((rdma_node_get_transport(xprt->sc_cm_id->device->node_type) == *page_offset = pg_off;
RDMA_TRANSPORT_IWARP) && ret = read;
sge_count > 1) atomic_inc(&rdma_stat_read);
return 1; return ret;
else err:
return min_t(int, sge_count, xprt->sc_max_sge); svc_rdma_unmap_dma(ctxt);
svc_rdma_put_context(ctxt, 0);
svc_rdma_put_frmr(xprt, frmr);
return ret;
} }
/* static int rdma_read_chunks(struct svcxprt_rdma *xprt,
* Use RDMA_READ to read data from the advertised client buffer into the struct rpcrdma_msg *rmsgp,
* XDR stream starting at rq_arg.head[0].iov_base. struct svc_rqst *rqstp,
* Each chunk in the array struct svc_rdma_op_ctxt *head)
* contains the following fields:
* discrim - '1', This isn't used for data placement
* position - The xdr stream offset (the same for every chunk)
* handle - RMR for client memory region
* length - data transfer length
* offset - 64 bit tagged offset in remote memory region
*
* On our side, we need to read into a pagelist. The first page immediately
* follows the RPC header.
*
* This function returns:
* 0 - No error and no read-list found.
*
* 1 - Successful read-list processing. The data is not yet in
* the pagelist and therefore the RPC request must be deferred. The
* I/O completion will enqueue the transport again and
* svc_rdma_recvfrom will complete the request.
*
* <0 - Error processing/posting read-list.
*
* NOTE: The ctxt must not be touched after the last WR has been posted
* because the I/O completion processing may occur on another
* processor and free / modify the context. Ne touche pas!
*/
static int rdma_read_xdr(struct svcxprt_rdma *xprt,
struct rpcrdma_msg *rmsgp,
struct svc_rqst *rqstp,
struct svc_rdma_op_ctxt *hdr_ctxt)
{ {
struct ib_send_wr read_wr; int page_no, ch_count, ret;
struct ib_send_wr inv_wr;
int err = 0;
int ch_no;
int ch_count;
int byte_count;
int sge_count;
u64 sgl_offset;
struct rpcrdma_read_chunk *ch; struct rpcrdma_read_chunk *ch;
struct svc_rdma_op_ctxt *ctxt = NULL; u32 page_offset, byte_count;
struct svc_rdma_req_map *rpl_map; u64 rs_offset;
struct svc_rdma_req_map *chl_map; rdma_reader_fn reader;
/* If no read list is present, return 0 */ /* If no read list is present, return 0 */
ch = svc_rdma_get_read_chunk(rmsgp); ch = svc_rdma_get_read_chunk(rmsgp);
...@@ -408,122 +384,55 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, ...@@ -408,122 +384,55 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt,
if (ch_count > RPCSVC_MAXPAGES) if (ch_count > RPCSVC_MAXPAGES)
return -EINVAL; return -EINVAL;
/* Allocate temporary reply and chunk maps */ /* The request is completed when the RDMA_READs complete. The
rpl_map = svc_rdma_get_req_map(); * head context keeps all the pages that comprise the
chl_map = svc_rdma_get_req_map(); * request.
*/
head->arg.head[0] = rqstp->rq_arg.head[0];
head->arg.tail[0] = rqstp->rq_arg.tail[0];
head->arg.pages = &head->pages[head->count];
head->hdr_count = head->count;
head->arg.page_base = 0;
head->arg.page_len = 0;
head->arg.len = rqstp->rq_arg.len;
head->arg.buflen = rqstp->rq_arg.buflen;
if (!xprt->sc_frmr_pg_list_len) /* Use FRMR if supported */
sge_count = map_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp, if (xprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG)
rpl_map, chl_map, ch_count, reader = rdma_read_chunk_frmr;
byte_count);
else else
sge_count = fast_reg_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp, reader = rdma_read_chunk_lcl;
rpl_map, chl_map, ch_count,
byte_count);
if (sge_count < 0) {
err = -EIO;
goto out;
}
sgl_offset = 0;
ch_no = 0;
page_no = 0; page_offset = 0;
for (ch = (struct rpcrdma_read_chunk *)&rmsgp->rm_body.rm_chunks[0]; for (ch = (struct rpcrdma_read_chunk *)&rmsgp->rm_body.rm_chunks[0];
ch->rc_discrim != 0; ch++, ch_no++) { ch->rc_discrim != 0; ch++) {
u64 rs_offset;
next_sge:
ctxt = svc_rdma_get_context(xprt);
ctxt->direction = DMA_FROM_DEVICE;
ctxt->frmr = hdr_ctxt->frmr;
ctxt->read_hdr = NULL;
clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
/* Prepare READ WR */
memset(&read_wr, 0, sizeof read_wr);
read_wr.wr_id = (unsigned long)ctxt;
read_wr.opcode = IB_WR_RDMA_READ;
ctxt->wr_op = read_wr.opcode;
read_wr.send_flags = IB_SEND_SIGNALED;
read_wr.wr.rdma.rkey = ntohl(ch->rc_target.rs_handle);
xdr_decode_hyper((__be32 *)&ch->rc_target.rs_offset, xdr_decode_hyper((__be32 *)&ch->rc_target.rs_offset,
&rs_offset); &rs_offset);
read_wr.wr.rdma.remote_addr = rs_offset + sgl_offset; byte_count = ntohl(ch->rc_target.rs_length);
read_wr.sg_list = ctxt->sge;
read_wr.num_sge = while (byte_count > 0) {
rdma_read_max_sge(xprt, chl_map->ch[ch_no].count); ret = reader(xprt, rqstp, head,
err = rdma_set_ctxt_sge(xprt, ctxt, hdr_ctxt->frmr, &page_no, &page_offset,
&rpl_map->sge[chl_map->ch[ch_no].start], ntohl(ch->rc_target.rs_handle),
&sgl_offset, byte_count, rs_offset,
read_wr.num_sge); ((ch+1)->rc_discrim == 0) /* last */
if (err) { );
svc_rdma_unmap_dma(ctxt); if (ret < 0)
svc_rdma_put_context(ctxt, 0); goto err;
goto out; byte_count -= ret;
} rs_offset += ret;
if (((ch+1)->rc_discrim == 0) && head->arg.buflen += ret;
(read_wr.num_sge == chl_map->ch[ch_no].count)) {
/*
* Mark the last RDMA_READ with a bit to
* indicate all RPC data has been fetched from
* the client and the RPC needs to be enqueued.
*/
set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags);
if (hdr_ctxt->frmr) {
set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
/*
* Invalidate the local MR used to map the data
* sink.
*/
if (xprt->sc_dev_caps &
SVCRDMA_DEVCAP_READ_W_INV) {
read_wr.opcode =
IB_WR_RDMA_READ_WITH_INV;
ctxt->wr_op = read_wr.opcode;
read_wr.ex.invalidate_rkey =
ctxt->frmr->mr->lkey;
} else {
/* Prepare INVALIDATE WR */
memset(&inv_wr, 0, sizeof inv_wr);
inv_wr.opcode = IB_WR_LOCAL_INV;
inv_wr.send_flags = IB_SEND_SIGNALED;
inv_wr.ex.invalidate_rkey =
hdr_ctxt->frmr->mr->lkey;
read_wr.next = &inv_wr;
}
}
ctxt->read_hdr = hdr_ctxt;
}
/* Post the read */
err = svc_rdma_send(xprt, &read_wr);
if (err) {
printk(KERN_ERR "svcrdma: Error %d posting RDMA_READ\n",
err);
set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
svc_rdma_unmap_dma(ctxt);
svc_rdma_put_context(ctxt, 0);
goto out;
} }
atomic_inc(&rdma_stat_read);
if (read_wr.num_sge < chl_map->ch[ch_no].count) {
chl_map->ch[ch_no].count -= read_wr.num_sge;
chl_map->ch[ch_no].start += read_wr.num_sge;
goto next_sge;
}
sgl_offset = 0;
err = 1;
} }
ret = 1;
out: err:
svc_rdma_put_req_map(rpl_map);
svc_rdma_put_req_map(chl_map);
/* Detach arg pages. svc_recv will replenish them */ /* Detach arg pages. svc_recv will replenish them */
for (ch_no = 0; &rqstp->rq_pages[ch_no] < rqstp->rq_respages; ch_no++) for (page_no = 0;
rqstp->rq_pages[ch_no] = NULL; &rqstp->rq_pages[page_no] < rqstp->rq_respages; page_no++)
rqstp->rq_pages[page_no] = NULL;
return err; return ret;
} }
static int rdma_read_complete(struct svc_rqst *rqstp, static int rdma_read_complete(struct svc_rqst *rqstp,
...@@ -595,13 +504,9 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) ...@@ -595,13 +504,9 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
struct svc_rdma_op_ctxt, struct svc_rdma_op_ctxt,
dto_q); dto_q);
list_del_init(&ctxt->dto_q); list_del_init(&ctxt->dto_q);
}
if (ctxt) {
spin_unlock_bh(&rdma_xprt->sc_rq_dto_lock); spin_unlock_bh(&rdma_xprt->sc_rq_dto_lock);
return rdma_read_complete(rqstp, ctxt); return rdma_read_complete(rqstp, ctxt);
} } else if (!list_empty(&rdma_xprt->sc_rq_dto_q)) {
if (!list_empty(&rdma_xprt->sc_rq_dto_q)) {
ctxt = list_entry(rdma_xprt->sc_rq_dto_q.next, ctxt = list_entry(rdma_xprt->sc_rq_dto_q.next,
struct svc_rdma_op_ctxt, struct svc_rdma_op_ctxt,
dto_q); dto_q);
...@@ -621,7 +526,6 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) ...@@ -621,7 +526,6 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) if (test_bit(XPT_CLOSE, &xprt->xpt_flags))
goto close_out; goto close_out;
BUG_ON(ret);
goto out; goto out;
} }
dprintk("svcrdma: processing ctxt=%p on xprt=%p, rqstp=%p, status=%d\n", dprintk("svcrdma: processing ctxt=%p on xprt=%p, rqstp=%p, status=%d\n",
...@@ -644,12 +548,11 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) ...@@ -644,12 +548,11 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp)
} }
/* Read read-list data. */ /* Read read-list data. */
ret = rdma_read_xdr(rdma_xprt, rmsgp, rqstp, ctxt); ret = rdma_read_chunks(rdma_xprt, rmsgp, rqstp, ctxt);
if (ret > 0) { if (ret > 0) {
/* read-list posted, defer until data received from client. */ /* read-list posted, defer until data received from client. */
goto defer; goto defer;
} } else if (ret < 0) {
if (ret < 0) {
/* Post of read-list failed, free context. */ /* Post of read-list failed, free context. */
svc_rdma_put_context(ctxt, 1); svc_rdma_put_context(ctxt, 1);
return 0; return 0;
......
/* /*
* Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved.
* Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved. * Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
...@@ -49,152 +50,6 @@ ...@@ -49,152 +50,6 @@
#define RPCDBG_FACILITY RPCDBG_SVCXPRT #define RPCDBG_FACILITY RPCDBG_SVCXPRT
/* Encode an XDR as an array of IB SGE
*
* Assumptions:
* - head[0] is physically contiguous.
* - tail[0] is physically contiguous.
* - pages[] is not physically or virtually contiguous and consists of
* PAGE_SIZE elements.
*
* Output:
* SGE[0] reserved for RCPRDMA header
* SGE[1] data from xdr->head[]
* SGE[2..sge_count-2] data from xdr->pages[]
* SGE[sge_count-1] data from xdr->tail.
*
* The max SGE we need is the length of the XDR / pagesize + one for
* head + one for tail + one for RPCRDMA header. Since RPCSVC_MAXPAGES
* reserves a page for both the request and the reply header, and this
* array is only concerned with the reply we are assured that we have
* on extra page for the RPCRMDA header.
*/
static int fast_reg_xdr(struct svcxprt_rdma *xprt,
struct xdr_buf *xdr,
struct svc_rdma_req_map *vec)
{
int sge_no;
u32 sge_bytes;
u32 page_bytes;
u32 page_off;
int page_no = 0;
u8 *frva;
struct svc_rdma_fastreg_mr *frmr;
frmr = svc_rdma_get_frmr(xprt);
if (IS_ERR(frmr))
return -ENOMEM;
vec->frmr = frmr;
/* Skip the RPCRDMA header */
sge_no = 1;
/* Map the head. */
frva = (void *)((unsigned long)(xdr->head[0].iov_base) & PAGE_MASK);
vec->sge[sge_no].iov_base = xdr->head[0].iov_base;
vec->sge[sge_no].iov_len = xdr->head[0].iov_len;
vec->count = 2;
sge_no++;
/* Map the XDR head */
frmr->kva = frva;
frmr->direction = DMA_TO_DEVICE;
frmr->access_flags = 0;
frmr->map_len = PAGE_SIZE;
frmr->page_list_len = 1;
page_off = (unsigned long)xdr->head[0].iov_base & ~PAGE_MASK;
frmr->page_list->page_list[page_no] =
ib_dma_map_page(xprt->sc_cm_id->device,
virt_to_page(xdr->head[0].iov_base),
page_off,
PAGE_SIZE - page_off,
DMA_TO_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
frmr->page_list->page_list[page_no]))
goto fatal_err;
atomic_inc(&xprt->sc_dma_used);
/* Map the XDR page list */
page_off = xdr->page_base;
page_bytes = xdr->page_len + page_off;
if (!page_bytes)
goto encode_tail;
/* Map the pages */
vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
vec->sge[sge_no].iov_len = page_bytes;
sge_no++;
while (page_bytes) {
struct page *page;
page = xdr->pages[page_no++];
sge_bytes = min_t(u32, page_bytes, (PAGE_SIZE - page_off));
page_bytes -= sge_bytes;
frmr->page_list->page_list[page_no] =
ib_dma_map_page(xprt->sc_cm_id->device,
page, page_off,
sge_bytes, DMA_TO_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
frmr->page_list->page_list[page_no]))
goto fatal_err;
atomic_inc(&xprt->sc_dma_used);
page_off = 0; /* reset for next time through loop */
frmr->map_len += PAGE_SIZE;
frmr->page_list_len++;
}
vec->count++;
encode_tail:
/* Map tail */
if (0 == xdr->tail[0].iov_len)
goto done;
vec->count++;
vec->sge[sge_no].iov_len = xdr->tail[0].iov_len;
if (((unsigned long)xdr->tail[0].iov_base & PAGE_MASK) ==
((unsigned long)xdr->head[0].iov_base & PAGE_MASK)) {
/*
* If head and tail use the same page, we don't need
* to map it again.
*/
vec->sge[sge_no].iov_base = xdr->tail[0].iov_base;
} else {
void *va;
/* Map another page for the tail */
page_off = (unsigned long)xdr->tail[0].iov_base & ~PAGE_MASK;
va = (void *)((unsigned long)xdr->tail[0].iov_base & PAGE_MASK);
vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
frmr->page_list->page_list[page_no] =
ib_dma_map_page(xprt->sc_cm_id->device, virt_to_page(va),
page_off,
PAGE_SIZE,
DMA_TO_DEVICE);
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
frmr->page_list->page_list[page_no]))
goto fatal_err;
atomic_inc(&xprt->sc_dma_used);
frmr->map_len += PAGE_SIZE;
frmr->page_list_len++;
}
done:
if (svc_rdma_fastreg(xprt, frmr))
goto fatal_err;
return 0;
fatal_err:
printk("svcrdma: Error fast registering memory for xprt %p\n", xprt);
vec->frmr = NULL;
svc_rdma_put_frmr(xprt, frmr);
return -EIO;
}
static int map_xdr(struct svcxprt_rdma *xprt, static int map_xdr(struct svcxprt_rdma *xprt,
struct xdr_buf *xdr, struct xdr_buf *xdr,
struct svc_rdma_req_map *vec) struct svc_rdma_req_map *vec)
...@@ -208,9 +63,6 @@ static int map_xdr(struct svcxprt_rdma *xprt, ...@@ -208,9 +63,6 @@ static int map_xdr(struct svcxprt_rdma *xprt,
BUG_ON(xdr->len != BUG_ON(xdr->len !=
(xdr->head[0].iov_len + xdr->page_len + xdr->tail[0].iov_len)); (xdr->head[0].iov_len + xdr->page_len + xdr->tail[0].iov_len));
if (xprt->sc_frmr_pg_list_len)
return fast_reg_xdr(xprt, xdr, vec);
/* Skip the first sge, this is for the RPCRDMA header */ /* Skip the first sge, this is for the RPCRDMA header */
sge_no = 1; sge_no = 1;
...@@ -282,8 +134,6 @@ static dma_addr_t dma_map_xdr(struct svcxprt_rdma *xprt, ...@@ -282,8 +134,6 @@ static dma_addr_t dma_map_xdr(struct svcxprt_rdma *xprt,
} }
/* Assumptions: /* Assumptions:
* - We are using FRMR
* - or -
* - The specified write_len can be represented in sc_max_sge * PAGE_SIZE * - The specified write_len can be represented in sc_max_sge * PAGE_SIZE
*/ */
static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
...@@ -327,23 +177,16 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, ...@@ -327,23 +177,16 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
sge_bytes = min_t(size_t, sge_bytes = min_t(size_t,
bc, vec->sge[xdr_sge_no].iov_len-sge_off); bc, vec->sge[xdr_sge_no].iov_len-sge_off);
sge[sge_no].length = sge_bytes; sge[sge_no].length = sge_bytes;
if (!vec->frmr) { sge[sge_no].addr =
sge[sge_no].addr = dma_map_xdr(xprt, &rqstp->rq_res, xdr_off,
dma_map_xdr(xprt, &rqstp->rq_res, xdr_off, sge_bytes, DMA_TO_DEVICE);
sge_bytes, DMA_TO_DEVICE); xdr_off += sge_bytes;
xdr_off += sge_bytes; if (ib_dma_mapping_error(xprt->sc_cm_id->device,
if (ib_dma_mapping_error(xprt->sc_cm_id->device, sge[sge_no].addr))
sge[sge_no].addr)) goto err;
goto err; atomic_inc(&xprt->sc_dma_used);
atomic_inc(&xprt->sc_dma_used); sge[sge_no].lkey = xprt->sc_dma_lkey;
sge[sge_no].lkey = xprt->sc_dma_lkey;
} else {
sge[sge_no].addr = (unsigned long)
vec->sge[xdr_sge_no].iov_base + sge_off;
sge[sge_no].lkey = vec->frmr->mr->lkey;
}
ctxt->count++; ctxt->count++;
ctxt->frmr = vec->frmr;
sge_off = 0; sge_off = 0;
sge_no++; sge_no++;
xdr_sge_no++; xdr_sge_no++;
...@@ -369,7 +212,6 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, ...@@ -369,7 +212,6 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
return 0; return 0;
err: err:
svc_rdma_unmap_dma(ctxt); svc_rdma_unmap_dma(ctxt);
svc_rdma_put_frmr(xprt, vec->frmr);
svc_rdma_put_context(ctxt, 0); svc_rdma_put_context(ctxt, 0);
/* Fatal error, close transport */ /* Fatal error, close transport */
return -EIO; return -EIO;
...@@ -397,10 +239,7 @@ static int send_write_chunks(struct svcxprt_rdma *xprt, ...@@ -397,10 +239,7 @@ static int send_write_chunks(struct svcxprt_rdma *xprt,
res_ary = (struct rpcrdma_write_array *) res_ary = (struct rpcrdma_write_array *)
&rdma_resp->rm_body.rm_chunks[1]; &rdma_resp->rm_body.rm_chunks[1];
if (vec->frmr) max_write = xprt->sc_max_sge * PAGE_SIZE;
max_write = vec->frmr->map_len;
else
max_write = xprt->sc_max_sge * PAGE_SIZE;
/* Write chunks start at the pagelist */ /* Write chunks start at the pagelist */
for (xdr_off = rqstp->rq_res.head[0].iov_len, chunk_no = 0; for (xdr_off = rqstp->rq_res.head[0].iov_len, chunk_no = 0;
...@@ -472,10 +311,7 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt, ...@@ -472,10 +311,7 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt,
res_ary = (struct rpcrdma_write_array *) res_ary = (struct rpcrdma_write_array *)
&rdma_resp->rm_body.rm_chunks[2]; &rdma_resp->rm_body.rm_chunks[2];
if (vec->frmr) max_write = xprt->sc_max_sge * PAGE_SIZE;
max_write = vec->frmr->map_len;
else
max_write = xprt->sc_max_sge * PAGE_SIZE;
/* xdr offset starts at RPC message */ /* xdr offset starts at RPC message */
nchunks = ntohl(arg_ary->wc_nchunks); nchunks = ntohl(arg_ary->wc_nchunks);
...@@ -545,7 +381,6 @@ static int send_reply(struct svcxprt_rdma *rdma, ...@@ -545,7 +381,6 @@ static int send_reply(struct svcxprt_rdma *rdma,
int byte_count) int byte_count)
{ {
struct ib_send_wr send_wr; struct ib_send_wr send_wr;
struct ib_send_wr inv_wr;
int sge_no; int sge_no;
int sge_bytes; int sge_bytes;
int page_no; int page_no;
...@@ -559,7 +394,6 @@ static int send_reply(struct svcxprt_rdma *rdma, ...@@ -559,7 +394,6 @@ static int send_reply(struct svcxprt_rdma *rdma,
"svcrdma: could not post a receive buffer, err=%d." "svcrdma: could not post a receive buffer, err=%d."
"Closing transport %p.\n", ret, rdma); "Closing transport %p.\n", ret, rdma);
set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags);
svc_rdma_put_frmr(rdma, vec->frmr);
svc_rdma_put_context(ctxt, 0); svc_rdma_put_context(ctxt, 0);
return -ENOTCONN; return -ENOTCONN;
} }
...@@ -567,11 +401,6 @@ static int send_reply(struct svcxprt_rdma *rdma, ...@@ -567,11 +401,6 @@ static int send_reply(struct svcxprt_rdma *rdma,
/* Prepare the context */ /* Prepare the context */
ctxt->pages[0] = page; ctxt->pages[0] = page;
ctxt->count = 1; ctxt->count = 1;
ctxt->frmr = vec->frmr;
if (vec->frmr)
set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
else
clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags);
/* Prepare the SGE for the RPCRDMA Header */ /* Prepare the SGE for the RPCRDMA Header */
ctxt->sge[0].lkey = rdma->sc_dma_lkey; ctxt->sge[0].lkey = rdma->sc_dma_lkey;
...@@ -590,21 +419,15 @@ static int send_reply(struct svcxprt_rdma *rdma, ...@@ -590,21 +419,15 @@ static int send_reply(struct svcxprt_rdma *rdma,
int xdr_off = 0; int xdr_off = 0;
sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count); sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count);
byte_count -= sge_bytes; byte_count -= sge_bytes;
if (!vec->frmr) { ctxt->sge[sge_no].addr =
ctxt->sge[sge_no].addr = dma_map_xdr(rdma, &rqstp->rq_res, xdr_off,
dma_map_xdr(rdma, &rqstp->rq_res, xdr_off, sge_bytes, DMA_TO_DEVICE);
sge_bytes, DMA_TO_DEVICE); xdr_off += sge_bytes;
xdr_off += sge_bytes; if (ib_dma_mapping_error(rdma->sc_cm_id->device,
if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[sge_no].addr))
ctxt->sge[sge_no].addr)) goto err;
goto err; atomic_inc(&rdma->sc_dma_used);
atomic_inc(&rdma->sc_dma_used); ctxt->sge[sge_no].lkey = rdma->sc_dma_lkey;
ctxt->sge[sge_no].lkey = rdma->sc_dma_lkey;
} else {
ctxt->sge[sge_no].addr = (unsigned long)
vec->sge[sge_no].iov_base;
ctxt->sge[sge_no].lkey = vec->frmr->mr->lkey;
}
ctxt->sge[sge_no].length = sge_bytes; ctxt->sge[sge_no].length = sge_bytes;
} }
BUG_ON(byte_count != 0); BUG_ON(byte_count != 0);
...@@ -627,6 +450,7 @@ static int send_reply(struct svcxprt_rdma *rdma, ...@@ -627,6 +450,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
ctxt->sge[page_no+1].length = 0; ctxt->sge[page_no+1].length = 0;
} }
rqstp->rq_next_page = rqstp->rq_respages + 1; rqstp->rq_next_page = rqstp->rq_respages + 1;
BUG_ON(sge_no > rdma->sc_max_sge); BUG_ON(sge_no > rdma->sc_max_sge);
memset(&send_wr, 0, sizeof send_wr); memset(&send_wr, 0, sizeof send_wr);
ctxt->wr_op = IB_WR_SEND; ctxt->wr_op = IB_WR_SEND;
...@@ -635,15 +459,6 @@ static int send_reply(struct svcxprt_rdma *rdma, ...@@ -635,15 +459,6 @@ static int send_reply(struct svcxprt_rdma *rdma,
send_wr.num_sge = sge_no; send_wr.num_sge = sge_no;
send_wr.opcode = IB_WR_SEND; send_wr.opcode = IB_WR_SEND;
send_wr.send_flags = IB_SEND_SIGNALED; send_wr.send_flags = IB_SEND_SIGNALED;
if (vec->frmr) {
/* Prepare INVALIDATE WR */
memset(&inv_wr, 0, sizeof inv_wr);
inv_wr.opcode = IB_WR_LOCAL_INV;
inv_wr.send_flags = IB_SEND_SIGNALED;
inv_wr.ex.invalidate_rkey =
vec->frmr->mr->lkey;
send_wr.next = &inv_wr;
}
ret = svc_rdma_send(rdma, &send_wr); ret = svc_rdma_send(rdma, &send_wr);
if (ret) if (ret)
...@@ -653,7 +468,6 @@ static int send_reply(struct svcxprt_rdma *rdma, ...@@ -653,7 +468,6 @@ static int send_reply(struct svcxprt_rdma *rdma,
err: err:
svc_rdma_unmap_dma(ctxt); svc_rdma_unmap_dma(ctxt);
svc_rdma_put_frmr(rdma, vec->frmr);
svc_rdma_put_context(ctxt, 1); svc_rdma_put_context(ctxt, 1);
return -EIO; return -EIO;
} }
......
/* /*
* Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved.
* Copyright (c) 2005-2007 Network Appliance, Inc. All rights reserved. * Copyright (c) 2005-2007 Network Appliance, Inc. All rights reserved.
* *
* This software is available to you under a choice of one of two * This software is available to you under a choice of one of two
...@@ -65,6 +66,7 @@ static void dto_tasklet_func(unsigned long data); ...@@ -65,6 +66,7 @@ static void dto_tasklet_func(unsigned long data);
static void svc_rdma_detach(struct svc_xprt *xprt); static void svc_rdma_detach(struct svc_xprt *xprt);
static void svc_rdma_free(struct svc_xprt *xprt); static void svc_rdma_free(struct svc_xprt *xprt);
static int svc_rdma_has_wspace(struct svc_xprt *xprt); static int svc_rdma_has_wspace(struct svc_xprt *xprt);
static int svc_rdma_secure_port(struct svc_rqst *);
static void rq_cq_reap(struct svcxprt_rdma *xprt); static void rq_cq_reap(struct svcxprt_rdma *xprt);
static void sq_cq_reap(struct svcxprt_rdma *xprt); static void sq_cq_reap(struct svcxprt_rdma *xprt);
...@@ -82,6 +84,7 @@ static struct svc_xprt_ops svc_rdma_ops = { ...@@ -82,6 +84,7 @@ static struct svc_xprt_ops svc_rdma_ops = {
.xpo_prep_reply_hdr = svc_rdma_prep_reply_hdr, .xpo_prep_reply_hdr = svc_rdma_prep_reply_hdr,
.xpo_has_wspace = svc_rdma_has_wspace, .xpo_has_wspace = svc_rdma_has_wspace,
.xpo_accept = svc_rdma_accept, .xpo_accept = svc_rdma_accept,
.xpo_secure_port = svc_rdma_secure_port,
}; };
struct svc_xprt_class svc_rdma_class = { struct svc_xprt_class svc_rdma_class = {
...@@ -160,7 +163,6 @@ struct svc_rdma_req_map *svc_rdma_get_req_map(void) ...@@ -160,7 +163,6 @@ struct svc_rdma_req_map *svc_rdma_get_req_map(void)
schedule_timeout_uninterruptible(msecs_to_jiffies(500)); schedule_timeout_uninterruptible(msecs_to_jiffies(500));
} }
map->count = 0; map->count = 0;
map->frmr = NULL;
return map; return map;
} }
...@@ -336,22 +338,21 @@ static void process_context(struct svcxprt_rdma *xprt, ...@@ -336,22 +338,21 @@ static void process_context(struct svcxprt_rdma *xprt,
switch (ctxt->wr_op) { switch (ctxt->wr_op) {
case IB_WR_SEND: case IB_WR_SEND:
if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags)) BUG_ON(ctxt->frmr);
svc_rdma_put_frmr(xprt, ctxt->frmr);
svc_rdma_put_context(ctxt, 1); svc_rdma_put_context(ctxt, 1);
break; break;
case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE:
BUG_ON(ctxt->frmr);
svc_rdma_put_context(ctxt, 0); svc_rdma_put_context(ctxt, 0);
break; break;
case IB_WR_RDMA_READ: case IB_WR_RDMA_READ:
case IB_WR_RDMA_READ_WITH_INV: case IB_WR_RDMA_READ_WITH_INV:
svc_rdma_put_frmr(xprt, ctxt->frmr);
if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) {
struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr;
BUG_ON(!read_hdr); BUG_ON(!read_hdr);
if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags))
svc_rdma_put_frmr(xprt, ctxt->frmr);
spin_lock_bh(&xprt->sc_rq_dto_lock); spin_lock_bh(&xprt->sc_rq_dto_lock);
set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags);
list_add_tail(&read_hdr->dto_q, list_add_tail(&read_hdr->dto_q,
...@@ -363,6 +364,7 @@ static void process_context(struct svcxprt_rdma *xprt, ...@@ -363,6 +364,7 @@ static void process_context(struct svcxprt_rdma *xprt,
break; break;
default: default:
BUG_ON(1);
printk(KERN_ERR "svcrdma: unexpected completion type, " printk(KERN_ERR "svcrdma: unexpected completion type, "
"opcode=%d\n", "opcode=%d\n",
ctxt->wr_op); ctxt->wr_op);
...@@ -378,29 +380,42 @@ static void process_context(struct svcxprt_rdma *xprt, ...@@ -378,29 +380,42 @@ static void process_context(struct svcxprt_rdma *xprt,
static void sq_cq_reap(struct svcxprt_rdma *xprt) static void sq_cq_reap(struct svcxprt_rdma *xprt)
{ {
struct svc_rdma_op_ctxt *ctxt = NULL; struct svc_rdma_op_ctxt *ctxt = NULL;
struct ib_wc wc; struct ib_wc wc_a[6];
struct ib_wc *wc;
struct ib_cq *cq = xprt->sc_sq_cq; struct ib_cq *cq = xprt->sc_sq_cq;
int ret; int ret;
memset(wc_a, 0, sizeof(wc_a));
if (!test_and_clear_bit(RDMAXPRT_SQ_PENDING, &xprt->sc_flags)) if (!test_and_clear_bit(RDMAXPRT_SQ_PENDING, &xprt->sc_flags))
return; return;
ib_req_notify_cq(xprt->sc_sq_cq, IB_CQ_NEXT_COMP); ib_req_notify_cq(xprt->sc_sq_cq, IB_CQ_NEXT_COMP);
atomic_inc(&rdma_stat_sq_poll); atomic_inc(&rdma_stat_sq_poll);
while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) { while ((ret = ib_poll_cq(cq, ARRAY_SIZE(wc_a), wc_a)) > 0) {
if (wc.status != IB_WC_SUCCESS) int i;
/* Close the transport */
set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
/* Decrement used SQ WR count */ for (i = 0; i < ret; i++) {
atomic_dec(&xprt->sc_sq_count); wc = &wc_a[i];
wake_up(&xprt->sc_send_wait); if (wc->status != IB_WC_SUCCESS) {
dprintk("svcrdma: sq wc err status %d\n",
wc->status);
ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id; /* Close the transport */
if (ctxt) set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
process_context(xprt, ctxt); }
svc_xprt_put(&xprt->sc_xprt); /* Decrement used SQ WR count */
atomic_dec(&xprt->sc_sq_count);
wake_up(&xprt->sc_send_wait);
ctxt = (struct svc_rdma_op_ctxt *)
(unsigned long)wc->wr_id;
if (ctxt)
process_context(xprt, ctxt);
svc_xprt_put(&xprt->sc_xprt);
}
} }
if (ctxt) if (ctxt)
...@@ -993,7 +1008,11 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) ...@@ -993,7 +1008,11 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
need_dma_mr = 0; need_dma_mr = 0;
break; break;
case RDMA_TRANSPORT_IB: case RDMA_TRANSPORT_IB:
if (!(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) { if (!(newxprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG)) {
need_dma_mr = 1;
dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
} else if (!(devattr.device_cap_flags &
IB_DEVICE_LOCAL_DMA_LKEY)) {
need_dma_mr = 1; need_dma_mr = 1;
dma_mr_acc = IB_ACCESS_LOCAL_WRITE; dma_mr_acc = IB_ACCESS_LOCAL_WRITE;
} else } else
...@@ -1190,14 +1209,7 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt) ...@@ -1190,14 +1209,7 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt)
container_of(xprt, struct svcxprt_rdma, sc_xprt); container_of(xprt, struct svcxprt_rdma, sc_xprt);
/* /*
* If there are fewer SQ WR available than required to send a * If there are already waiters on the SQ,
* simple response, return false.
*/
if ((rdma->sc_sq_depth - atomic_read(&rdma->sc_sq_count) < 3))
return 0;
/*
* ...or there are already waiters on the SQ,
* return false. * return false.
*/ */
if (waitqueue_active(&rdma->sc_send_wait)) if (waitqueue_active(&rdma->sc_send_wait))
...@@ -1207,6 +1219,11 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt) ...@@ -1207,6 +1219,11 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt)
return 1; return 1;
} }
static int svc_rdma_secure_port(struct svc_rqst *rqstp)
{
return 1;
}
/* /*
* Attempt to register the kvec representing the RPC memory with the * Attempt to register the kvec representing the RPC memory with the
* device. * device.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册