Commit 4d470070 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.linux-nfs.org/pub/linux/nfs-2.6

* git://git.linux-nfs.org/pub/linux/nfs-2.6: (28 commits)
  NFS: Fix a compile glitch on 64-bit systems
  NFS: Clean up nfs_create_request comments
  spkm3: initialize hash
  spkm3: remove bad kfree, unnecessary export
  spkm3: fix spkm3's use of hmac
  NFS4: invalidate cached acl on setacl
  NFS: Fix directory caching problem - with test case and patch.
  NFS: Set meaningful value for fattr->time_start in readdirplus results.
  NFS: Added support to turn off the NFSv3 READDIRPLUS RPC.
  SUNRPC: RPC client should retry with different versions of rpcbind
  SUNRPC: remove old portmapper
  NFS: switch NFSROOT to use new rpcbind client
  SUNRPC: switch the RPC server to use the new rpcbind registration API
  SUNRPC: switch socket-based RPC transports to use rpcbind
  SUNRPC: introduce rpcbind: replacement for in-kernel portmapper
  SUNRPC: Eliminate side effects from rpc_malloc
  SUNRPC: RPC buffer size estimates are too large
  NLM: Shrink the maximum request size of NLM4 requests
  NFS: Use pgoff_t in structures and functions that pass page cache offsets
  NFS: Clean up nfs_sync_mapping_wait()
  ...
parents 7e20ef03 84dde76c
...@@ -1734,6 +1734,18 @@ config SUNRPC ...@@ -1734,6 +1734,18 @@ config SUNRPC
config SUNRPC_GSS config SUNRPC_GSS
tristate tristate
config SUNRPC_BIND34
bool "Support for rpcbind versions 3 & 4 (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL
help
Provides kernel support for querying rpcbind servers via versions 3
and 4 of the rpcbind protocol. The kernel automatically falls back
to version 2 if a remote rpcbind service does not support versions
3 or 4.
If unsure, say N to get traditional behavior (version 2 rpcbind
requests only).
config RPCSEC_GSS_KRB5 config RPCSEC_GSS_KRB5
tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)"
depends on SUNRPC && EXPERIMENTAL depends on SUNRPC && EXPERIMENTAL
......
...@@ -225,16 +225,13 @@ xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp) ...@@ -225,16 +225,13 @@ xdr_decode_stat(struct rpc_rqst *rqstp, __be32 *p, struct nsm_res *resp)
#define SM_monres_sz 2 #define SM_monres_sz 2
#define SM_unmonres_sz 1 #define SM_unmonres_sz 1
#ifndef MAX
# define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
static struct rpc_procinfo nsm_procedures[] = { static struct rpc_procinfo nsm_procedures[] = {
[SM_MON] = { [SM_MON] = {
.p_proc = SM_MON, .p_proc = SM_MON,
.p_encode = (kxdrproc_t) xdr_encode_mon, .p_encode = (kxdrproc_t) xdr_encode_mon,
.p_decode = (kxdrproc_t) xdr_decode_stat_res, .p_decode = (kxdrproc_t) xdr_decode_stat_res,
.p_bufsiz = MAX(SM_mon_sz, SM_monres_sz) << 2, .p_arglen = SM_mon_sz,
.p_replen = SM_monres_sz,
.p_statidx = SM_MON, .p_statidx = SM_MON,
.p_name = "MONITOR", .p_name = "MONITOR",
}, },
...@@ -242,7 +239,8 @@ static struct rpc_procinfo nsm_procedures[] = { ...@@ -242,7 +239,8 @@ static struct rpc_procinfo nsm_procedures[] = {
.p_proc = SM_UNMON, .p_proc = SM_UNMON,
.p_encode = (kxdrproc_t) xdr_encode_unmon, .p_encode = (kxdrproc_t) xdr_encode_unmon,
.p_decode = (kxdrproc_t) xdr_decode_stat, .p_decode = (kxdrproc_t) xdr_decode_stat,
.p_bufsiz = MAX(SM_mon_id_sz, SM_unmonres_sz) << 2, .p_arglen = SM_mon_id_sz,
.p_replen = SM_unmonres_sz,
.p_statidx = SM_UNMON, .p_statidx = SM_UNMON,
.p_name = "UNMONITOR", .p_name = "UNMONITOR",
}, },
......
...@@ -510,17 +510,20 @@ nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) ...@@ -510,17 +510,20 @@ nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
return 0; return 0;
} }
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
#endif
/* /*
* Buffer requirements for NLM * Buffer requirements for NLM
*/ */
#define NLM_void_sz 0 #define NLM_void_sz 0
#define NLM_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN) #define NLM_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
#define NLM_caller_sz 1+XDR_QUADLEN(sizeof(utsname()->nodename)) #define NLM_caller_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
#define NLM_netobj_sz 1+XDR_QUADLEN(XDR_MAX_NETOBJ) #define NLM_owner_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
/* #define NLM_owner_sz 1+XDR_QUADLEN(NLM_MAXOWNER) */
#define NLM_fhandle_sz 1+XDR_QUADLEN(NFS2_FHSIZE) #define NLM_fhandle_sz 1+XDR_QUADLEN(NFS2_FHSIZE)
#define NLM_lock_sz 3+NLM_caller_sz+NLM_netobj_sz+NLM_fhandle_sz #define NLM_lock_sz 3+NLM_caller_sz+NLM_owner_sz+NLM_fhandle_sz
#define NLM_holder_sz 4+NLM_netobj_sz #define NLM_holder_sz 4+NLM_owner_sz
#define NLM_testargs_sz NLM_cookie_sz+1+NLM_lock_sz #define NLM_testargs_sz NLM_cookie_sz+1+NLM_lock_sz
#define NLM_lockargs_sz NLM_cookie_sz+4+NLM_lock_sz #define NLM_lockargs_sz NLM_cookie_sz+4+NLM_lock_sz
...@@ -531,10 +534,6 @@ nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) ...@@ -531,10 +534,6 @@ nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
#define NLM_res_sz NLM_cookie_sz+1 #define NLM_res_sz NLM_cookie_sz+1
#define NLM_norep_sz 0 #define NLM_norep_sz 0
#ifndef MAX
# define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
/* /*
* For NLM, a void procedure really returns nothing * For NLM, a void procedure really returns nothing
*/ */
...@@ -545,7 +544,8 @@ nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) ...@@ -545,7 +544,8 @@ nlmclt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
.p_proc = NLMPROC_##proc, \ .p_proc = NLMPROC_##proc, \
.p_encode = (kxdrproc_t) nlmclt_encode_##argtype, \ .p_encode = (kxdrproc_t) nlmclt_encode_##argtype, \
.p_decode = (kxdrproc_t) nlmclt_decode_##restype, \ .p_decode = (kxdrproc_t) nlmclt_decode_##restype, \
.p_bufsiz = MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2, \ .p_arglen = NLM_##argtype##_sz, \
.p_replen = NLM_##restype##_sz, \
.p_statidx = NLMPROC_##proc, \ .p_statidx = NLMPROC_##proc, \
.p_name = #proc, \ .p_name = #proc, \
} }
......
...@@ -516,17 +516,24 @@ nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) ...@@ -516,17 +516,24 @@ nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
return 0; return 0;
} }
#if (NLMCLNT_OHSIZE > XDR_MAX_NETOBJ)
# error "NLM host name cannot be larger than XDR_MAX_NETOBJ!"
#endif
#if (NLMCLNT_OHSIZE > NLM_MAXSTRLEN)
# error "NLM host name cannot be larger than NLM's maximum string length!"
#endif
/* /*
* Buffer requirements for NLM * Buffer requirements for NLM
*/ */
#define NLM4_void_sz 0 #define NLM4_void_sz 0
#define NLM4_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN) #define NLM4_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
#define NLM4_caller_sz 1+XDR_QUADLEN(NLM_MAXSTRLEN) #define NLM4_caller_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
#define NLM4_netobj_sz 1+XDR_QUADLEN(XDR_MAX_NETOBJ) #define NLM4_owner_sz 1+XDR_QUADLEN(NLMCLNT_OHSIZE)
/* #define NLM4_owner_sz 1+XDR_QUADLEN(NLM4_MAXOWNER) */
#define NLM4_fhandle_sz 1+XDR_QUADLEN(NFS3_FHSIZE) #define NLM4_fhandle_sz 1+XDR_QUADLEN(NFS3_FHSIZE)
#define NLM4_lock_sz 5+NLM4_caller_sz+NLM4_netobj_sz+NLM4_fhandle_sz #define NLM4_lock_sz 5+NLM4_caller_sz+NLM4_owner_sz+NLM4_fhandle_sz
#define NLM4_holder_sz 6+NLM4_netobj_sz #define NLM4_holder_sz 6+NLM4_owner_sz
#define NLM4_testargs_sz NLM4_cookie_sz+1+NLM4_lock_sz #define NLM4_testargs_sz NLM4_cookie_sz+1+NLM4_lock_sz
#define NLM4_lockargs_sz NLM4_cookie_sz+4+NLM4_lock_sz #define NLM4_lockargs_sz NLM4_cookie_sz+4+NLM4_lock_sz
...@@ -537,10 +544,6 @@ nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) ...@@ -537,10 +544,6 @@ nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
#define NLM4_res_sz NLM4_cookie_sz+1 #define NLM4_res_sz NLM4_cookie_sz+1
#define NLM4_norep_sz 0 #define NLM4_norep_sz 0
#ifndef MAX
# define MAX(a,b) (((a) > (b))? (a) : (b))
#endif
/* /*
* For NLM, a void procedure really returns nothing * For NLM, a void procedure really returns nothing
*/ */
...@@ -551,7 +554,8 @@ nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp) ...@@ -551,7 +554,8 @@ nlm4clt_decode_res(struct rpc_rqst *req, __be32 *p, struct nlm_res *resp)
.p_proc = NLMPROC_##proc, \ .p_proc = NLMPROC_##proc, \
.p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \ .p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \
.p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \ .p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \
.p_bufsiz = MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2, \ .p_arglen = NLM4_##argtype##_sz, \
.p_replen = NLM4_##restype##_sz, \
.p_statidx = NLMPROC_##proc, \ .p_statidx = NLMPROC_##proc, \
.p_name = #proc, \ .p_name = #proc, \
} }
......
...@@ -618,7 +618,8 @@ static int nfs_init_server(struct nfs_server *server, const struct nfs_mount_dat ...@@ -618,7 +618,8 @@ static int nfs_init_server(struct nfs_server *server, const struct nfs_mount_dat
if (clp->cl_nfsversion == 3) { if (clp->cl_nfsversion == 3) {
if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
server->namelen = NFS3_MAXNAMLEN; server->namelen = NFS3_MAXNAMLEN;
server->caps |= NFS_CAP_READDIRPLUS; if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
server->caps |= NFS_CAP_READDIRPLUS;
} else { } else {
if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
server->namelen = NFS2_MAXNAMLEN; server->namelen = NFS2_MAXNAMLEN;
......
...@@ -154,6 +154,8 @@ typedef struct { ...@@ -154,6 +154,8 @@ typedef struct {
decode_dirent_t decode; decode_dirent_t decode;
int plus; int plus;
int error; int error;
unsigned long timestamp;
int timestamp_valid;
} nfs_readdir_descriptor_t; } nfs_readdir_descriptor_t;
/* Now we cache directories properly, by stuffing the dirent /* Now we cache directories properly, by stuffing the dirent
...@@ -195,6 +197,8 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) ...@@ -195,6 +197,8 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
} }
goto error; goto error;
} }
desc->timestamp = timestamp;
desc->timestamp_valid = 1;
SetPageUptodate(page); SetPageUptodate(page);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME; NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME;
...@@ -225,6 +229,10 @@ int dir_decode(nfs_readdir_descriptor_t *desc) ...@@ -225,6 +229,10 @@ int dir_decode(nfs_readdir_descriptor_t *desc)
if (IS_ERR(p)) if (IS_ERR(p))
return PTR_ERR(p); return PTR_ERR(p);
desc->ptr = p; desc->ptr = p;
if (desc->timestamp_valid)
desc->entry->fattr->time_start = desc->timestamp;
else
desc->entry->fattr->valid &= ~NFS_ATTR_FATTR;
return 0; return 0;
} }
...@@ -316,6 +324,10 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc) ...@@ -316,6 +324,10 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
__FUNCTION__, desc->page_index, __FUNCTION__, desc->page_index,
(long long) *desc->dir_cookie); (long long) *desc->dir_cookie);
/* If we find the page in the page_cache, we cannot be sure
* how fresh the data is, so we will ignore readdir_plus attributes.
*/
desc->timestamp_valid = 0;
page = read_cache_page(inode->i_mapping, desc->page_index, page = read_cache_page(inode->i_mapping, desc->page_index,
(filler_t *)nfs_readdir_filler, desc); (filler_t *)nfs_readdir_filler, desc);
if (IS_ERR(page)) { if (IS_ERR(page)) {
...@@ -468,6 +480,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, ...@@ -468,6 +480,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
struct rpc_cred *cred = nfs_file_cred(file); struct rpc_cred *cred = nfs_file_cred(file);
struct page *page = NULL; struct page *page = NULL;
int status; int status;
unsigned long timestamp;
dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
(unsigned long long)*desc->dir_cookie); (unsigned long long)*desc->dir_cookie);
...@@ -477,6 +490,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, ...@@ -477,6 +490,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
status = -ENOMEM; status = -ENOMEM;
goto out; goto out;
} }
timestamp = jiffies;
desc->error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, *desc->dir_cookie, desc->error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, *desc->dir_cookie,
page, page,
NFS_SERVER(inode)->dtsize, NFS_SERVER(inode)->dtsize,
...@@ -487,6 +501,8 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, ...@@ -487,6 +501,8 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
desc->page = page; desc->page = page;
desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */
if (desc->error >= 0) { if (desc->error >= 0) {
desc->timestamp = timestamp;
desc->timestamp_valid = 1;
if ((status = dir_decode(desc)) == 0) if ((status = dir_decode(desc)) == 0)
desc->entry->prev_cookie = *desc->dir_cookie; desc->entry->prev_cookie = *desc->dir_cookie;
} else } else
...@@ -849,6 +865,10 @@ static int nfs_dentry_delete(struct dentry *dentry) ...@@ -849,6 +865,10 @@ static int nfs_dentry_delete(struct dentry *dentry)
static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
{ {
nfs_inode_return_delegation(inode); nfs_inode_return_delegation(inode);
if (S_ISDIR(inode->i_mode))
/* drop any readdir cache as it could easily be old */
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
lock_kernel(); lock_kernel();
drop_nlink(inode); drop_nlink(inode);
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#include "internal.h"
#include "iostat.h" #include "iostat.h"
#define NFSDBG_FACILITY NFSDBG_VFS #define NFSDBG_FACILITY NFSDBG_VFS
...@@ -271,7 +272,7 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo ...@@ -271,7 +272,7 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo
bytes = min(rsize,count); bytes = min(rsize,count);
result = -ENOMEM; result = -ENOMEM;
data = nfs_readdata_alloc(pgbase + bytes); data = nfs_readdata_alloc(nfs_page_array_len(pgbase, bytes));
if (unlikely(!data)) if (unlikely(!data))
break; break;
...@@ -602,7 +603,7 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l ...@@ -602,7 +603,7 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l
bytes = min(wsize,count); bytes = min(wsize,count);
result = -ENOMEM; result = -ENOMEM;
data = nfs_writedata_alloc(pgbase + bytes); data = nfs_writedata_alloc(nfs_page_array_len(pgbase, bytes));
if (unlikely(!data)) if (unlikely(!data))
break; break;
......
...@@ -231,3 +231,15 @@ unsigned int nfs_page_length(struct page *page) ...@@ -231,3 +231,15 @@ unsigned int nfs_page_length(struct page *page)
} }
return 0; return 0;
} }
/*
* Determine the number of pages in an array of length 'len' and
* with a base offset of 'base'
*/
static inline
unsigned int nfs_page_array_len(unsigned int base, size_t len)
{
return ((unsigned long)len + (unsigned long)base +
PAGE_SIZE - 1) >> PAGE_SHIFT;
}
...@@ -133,13 +133,15 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res) ...@@ -133,13 +133,15 @@ xdr_decode_fhstatus3(struct rpc_rqst *req, __be32 *p, struct mnt_fhstatus *res)
#define MNT_dirpath_sz (1 + 256) #define MNT_dirpath_sz (1 + 256)
#define MNT_fhstatus_sz (1 + 8) #define MNT_fhstatus_sz (1 + 8)
#define MNT_fhstatus3_sz (1 + 16)
static struct rpc_procinfo mnt_procedures[] = { static struct rpc_procinfo mnt_procedures[] = {
[MNTPROC_MNT] = { [MNTPROC_MNT] = {
.p_proc = MNTPROC_MNT, .p_proc = MNTPROC_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath, .p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus, .p_decode = (kxdrproc_t) xdr_decode_fhstatus,
.p_bufsiz = MNT_dirpath_sz << 2, .p_arglen = MNT_dirpath_sz,
.p_replen = MNT_fhstatus_sz,
.p_statidx = MNTPROC_MNT, .p_statidx = MNTPROC_MNT,
.p_name = "MOUNT", .p_name = "MOUNT",
}, },
...@@ -150,7 +152,8 @@ static struct rpc_procinfo mnt3_procedures[] = { ...@@ -150,7 +152,8 @@ static struct rpc_procinfo mnt3_procedures[] = {
.p_proc = MOUNTPROC3_MNT, .p_proc = MOUNTPROC3_MNT,
.p_encode = (kxdrproc_t) xdr_encode_dirpath, .p_encode = (kxdrproc_t) xdr_encode_dirpath,
.p_decode = (kxdrproc_t) xdr_decode_fhstatus3, .p_decode = (kxdrproc_t) xdr_decode_fhstatus3,
.p_bufsiz = MNT_dirpath_sz << 2, .p_arglen = MNT_dirpath_sz,
.p_replen = MNT_fhstatus3_sz,
.p_statidx = MOUNTPROC3_MNT, .p_statidx = MOUNTPROC3_MNT,
.p_name = "MOUNT", .p_name = "MOUNT",
}, },
......
...@@ -687,16 +687,13 @@ nfs_stat_to_errno(int stat) ...@@ -687,16 +687,13 @@ nfs_stat_to_errno(int stat)
return nfs_errtbl[i].errno; return nfs_errtbl[i].errno;
} }
#ifndef MAX
# define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
#define PROC(proc, argtype, restype, timer) \ #define PROC(proc, argtype, restype, timer) \
[NFSPROC_##proc] = { \ [NFSPROC_##proc] = { \
.p_proc = NFSPROC_##proc, \ .p_proc = NFSPROC_##proc, \
.p_encode = (kxdrproc_t) nfs_xdr_##argtype, \ .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs_xdr_##restype, \ .p_decode = (kxdrproc_t) nfs_xdr_##restype, \
.p_bufsiz = MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \ .p_arglen = NFS_##argtype##_sz, \
.p_replen = NFS_##restype##_sz, \
.p_timer = timer, \ .p_timer = timer, \
.p_statidx = NFSPROC_##proc, \ .p_statidx = NFSPROC_##proc, \
.p_name = #proc, \ .p_name = #proc, \
......
...@@ -1102,16 +1102,13 @@ nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) ...@@ -1102,16 +1102,13 @@ nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
} }
#endif /* CONFIG_NFS_V3_ACL */ #endif /* CONFIG_NFS_V3_ACL */
#ifndef MAX
# define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
#define PROC(proc, argtype, restype, timer) \ #define PROC(proc, argtype, restype, timer) \
[NFS3PROC_##proc] = { \ [NFS3PROC_##proc] = { \
.p_proc = NFS3PROC_##proc, \ .p_proc = NFS3PROC_##proc, \
.p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \ .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs3_xdr_##restype, \ .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
.p_bufsiz = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \ .p_arglen = NFS3_##argtype##_sz, \
.p_replen = NFS3_##restype##_sz, \
.p_timer = timer, \ .p_timer = timer, \
.p_statidx = NFS3PROC_##proc, \ .p_statidx = NFS3PROC_##proc, \
.p_name = #proc, \ .p_name = #proc, \
...@@ -1153,7 +1150,8 @@ static struct rpc_procinfo nfs3_acl_procedures[] = { ...@@ -1153,7 +1150,8 @@ static struct rpc_procinfo nfs3_acl_procedures[] = {
.p_proc = ACLPROC3_GETACL, .p_proc = ACLPROC3_GETACL,
.p_encode = (kxdrproc_t) nfs3_xdr_getaclargs, .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
.p_decode = (kxdrproc_t) nfs3_xdr_getaclres, .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
.p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2, .p_arglen = ACL3_getaclargs_sz,
.p_replen = ACL3_getaclres_sz,
.p_timer = 1, .p_timer = 1,
.p_name = "GETACL", .p_name = "GETACL",
}, },
...@@ -1161,7 +1159,8 @@ static struct rpc_procinfo nfs3_acl_procedures[] = { ...@@ -1161,7 +1159,8 @@ static struct rpc_procinfo nfs3_acl_procedures[] = {
.p_proc = ACLPROC3_SETACL, .p_proc = ACLPROC3_SETACL,
.p_encode = (kxdrproc_t) nfs3_xdr_setaclargs, .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
.p_decode = (kxdrproc_t) nfs3_xdr_setaclres, .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
.p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2, .p_arglen = ACL3_setaclargs_sz,
.p_replen = ACL3_setaclres_sz,
.p_timer = 0, .p_timer = 0,
.p_name = "SETACL", .p_name = "SETACL",
}, },
......
...@@ -2647,8 +2647,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl ...@@ -2647,8 +2647,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
nfs_inode_return_delegation(inode); nfs_inode_return_delegation(inode);
buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase); buf_to_pages(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); ret = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (ret == 0) nfs_zap_caches(inode);
nfs4_write_cached_acl(inode, buf, buflen);
return ret; return ret;
} }
......
...@@ -4546,16 +4546,13 @@ nfs4_stat_to_errno(int stat) ...@@ -4546,16 +4546,13 @@ nfs4_stat_to_errno(int stat)
return stat; return stat;
} }
#ifndef MAX
# define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
#define PROC(proc, argtype, restype) \ #define PROC(proc, argtype, restype) \
[NFSPROC4_CLNT_##proc] = { \ [NFSPROC4_CLNT_##proc] = { \
.p_proc = NFSPROC4_COMPOUND, \ .p_proc = NFSPROC4_COMPOUND, \
.p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \ .p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs4_xdr_##restype, \ .p_decode = (kxdrproc_t) nfs4_xdr_##restype, \
.p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \ .p_arglen = NFS4_##argtype##_sz, \
.p_replen = NFS4_##restype##_sz, \
.p_statidx = NFSPROC4_CLNT_##proc, \ .p_statidx = NFSPROC4_CLNT_##proc, \
.p_name = #proc, \ .p_name = #proc, \
} }
......
...@@ -428,7 +428,7 @@ static int __init root_nfs_getport(int program, int version, int proto) ...@@ -428,7 +428,7 @@ static int __init root_nfs_getport(int program, int version, int proto)
printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n", printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n",
program, version, NIPQUAD(servaddr)); program, version, NIPQUAD(servaddr));
set_sockaddr(&sin, servaddr, 0); set_sockaddr(&sin, servaddr, 0);
return rpc_getport_external(&sin, program, version, proto); return rpcb_getport_external(&sin, program, version, proto);
} }
......
...@@ -17,7 +17,8 @@ ...@@ -17,7 +17,8 @@
#include <linux/nfs_page.h> #include <linux/nfs_page.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_mount.h> #include <linux/nfs_mount.h>
#include <linux/writeback.h>
#include "internal.h"
#define NFS_PARANOIA 1 #define NFS_PARANOIA 1
...@@ -50,9 +51,7 @@ nfs_page_free(struct nfs_page *p) ...@@ -50,9 +51,7 @@ nfs_page_free(struct nfs_page *p)
* @count: number of bytes to read/write * @count: number of bytes to read/write
* *
* The page must be locked by the caller. This makes sure we never * The page must be locked by the caller. This makes sure we never
* create two different requests for the same page, and avoids * create two different requests for the same page.
* a possible deadlock when we reach the hard limit on the number
* of dirty pages.
* User should ensure it is safe to sleep in this function. * User should ensure it is safe to sleep in this function.
*/ */
struct nfs_page * struct nfs_page *
...@@ -63,16 +62,12 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode, ...@@ -63,16 +62,12 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs_page *req; struct nfs_page *req;
/* Deal with hard limits. */
for (;;) { for (;;) {
/* try to allocate the request struct */ /* try to allocate the request struct */
req = nfs_page_alloc(); req = nfs_page_alloc();
if (req != NULL) if (req != NULL)
break; break;
/* Try to free up at least one request in order to stay
* below the hard limit
*/
if (signalled() && (server->flags & NFS_MOUNT_INTR)) if (signalled() && (server->flags & NFS_MOUNT_INTR))
return ERR_PTR(-ERESTARTSYS); return ERR_PTR(-ERESTARTSYS);
yield(); yield();
...@@ -223,123 +218,150 @@ nfs_wait_on_request(struct nfs_page *req) ...@@ -223,123 +218,150 @@ nfs_wait_on_request(struct nfs_page *req)
} }
/** /**
* nfs_coalesce_requests - Split coalesced requests out from a list. * nfs_pageio_init - initialise a page io descriptor
* @head: source list * @desc: pointer to descriptor
* @dst: destination list * @inode: pointer to inode
* @nmax: maximum number of requests to coalesce * @doio: pointer to io function
* * @bsize: io block size
* Moves a maximum of 'nmax' elements from one list to another. * @io_flags: extra parameters for the io function
* The elements are checked to ensure that they form a contiguous set
* of pages, and that the RPC credentials are the same.
*/ */
int void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
nfs_coalesce_requests(struct list_head *head, struct list_head *dst, struct inode *inode,
unsigned int nmax) int (*doio)(struct inode *, struct list_head *, unsigned int, size_t, int),
size_t bsize,
int io_flags)
{ {
struct nfs_page *req = NULL; INIT_LIST_HEAD(&desc->pg_list);
unsigned int npages = 0; desc->pg_bytes_written = 0;
desc->pg_count = 0;
while (!list_empty(head)) { desc->pg_bsize = bsize;
struct nfs_page *prev = req; desc->pg_base = 0;
desc->pg_inode = inode;
req = nfs_list_entry(head->next); desc->pg_doio = doio;
if (prev) { desc->pg_ioflags = io_flags;
if (req->wb_context->cred != prev->wb_context->cred) desc->pg_error = 0;
break;
if (req->wb_context->lockowner != prev->wb_context->lockowner)
break;
if (req->wb_context->state != prev->wb_context->state)
break;
if (req->wb_index != (prev->wb_index + 1))
break;
if (req->wb_pgbase != 0)
break;
}
nfs_list_remove_request(req);
nfs_list_add_request(req, dst);
npages++;
if (req->wb_pgbase + req->wb_bytes != PAGE_CACHE_SIZE)
break;
if (npages >= nmax)
break;
}
return npages;
} }
#define NFS_SCAN_MAXENTRIES 16
/** /**
* nfs_scan_dirty - Scan the radix tree for dirty requests * nfs_can_coalesce_requests - test two requests for compatibility
* @mapping: pointer to address space * @prev: pointer to nfs_page
* @wbc: writeback_control structure * @req: pointer to nfs_page
* @dst: Destination list
* *
* Moves elements from one of the inode request lists. * The nfs_page structures 'prev' and 'req' are compared to ensure that the
* If the number of requests is set to 0, the entire address_space * page data area they describe is contiguous, and that their RPC
* starting at index idx_start, is scanned. * credentials, NFSv4 open state, and lockowners are the same.
* The requests are *not* checked to ensure that they form a contiguous set. *
* You must be holding the inode's req_lock when calling this function * Return 'true' if this is the case, else return 'false'.
*/ */
long nfs_scan_dirty(struct address_space *mapping, static int nfs_can_coalesce_requests(struct nfs_page *prev,
struct writeback_control *wbc, struct nfs_page *req)
struct list_head *dst)
{ {
struct nfs_inode *nfsi = NFS_I(mapping->host); if (req->wb_context->cred != prev->wb_context->cred)
struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES];
struct nfs_page *req;
pgoff_t idx_start, idx_end;
long res = 0;
int found, i;
if (nfsi->ndirty == 0)
return 0; return 0;
if (wbc->range_cyclic) { if (req->wb_context->lockowner != prev->wb_context->lockowner)
idx_start = 0; return 0;
idx_end = ULONG_MAX; if (req->wb_context->state != prev->wb_context->state)
} else if (wbc->range_end == 0) { return 0;
idx_start = wbc->range_start >> PAGE_CACHE_SHIFT; if (req->wb_index != (prev->wb_index + 1))
idx_end = ULONG_MAX; return 0;
} else { if (req->wb_pgbase != 0)
idx_start = wbc->range_start >> PAGE_CACHE_SHIFT; return 0;
idx_end = wbc->range_end >> PAGE_CACHE_SHIFT; if (prev->wb_pgbase + prev->wb_bytes != PAGE_CACHE_SIZE)
} return 0;
return 1;
}
for (;;) { /**
unsigned int toscan = NFS_SCAN_MAXENTRIES; * nfs_pageio_do_add_request - Attempt to coalesce a request into a page list.
* @desc: destination io descriptor
* @req: request
*
* Returns true if the request 'req' was successfully coalesced into the
* existing list of pages 'desc'.
*/
static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
struct nfs_page *req)
{
size_t newlen = req->wb_bytes;
found = radix_tree_gang_lookup_tag(&nfsi->nfs_page_tree, if (desc->pg_count != 0) {
(void **)&pgvec[0], idx_start, toscan, struct nfs_page *prev;
NFS_PAGE_TAG_DIRTY);
/* Did we make progress? */ /*
if (found <= 0) * FIXME: ideally we should be able to coalesce all requests
break; * that are not block boundary aligned, but currently this
* is problematic for the case of bsize < PAGE_CACHE_SIZE,
* since nfs_flush_multi and nfs_pagein_multi assume you
* can have only one struct nfs_page.
*/
if (desc->pg_bsize < PAGE_SIZE)
return 0;
newlen += desc->pg_count;
if (newlen > desc->pg_bsize)
return 0;
prev = nfs_list_entry(desc->pg_list.prev);
if (!nfs_can_coalesce_requests(prev, req))
return 0;
} else
desc->pg_base = req->wb_pgbase;
nfs_list_remove_request(req);
nfs_list_add_request(req, &desc->pg_list);
desc->pg_count = newlen;
return 1;
}
for (i = 0; i < found; i++) { /*
req = pgvec[i]; * Helper for nfs_pageio_add_request and nfs_pageio_complete
if (!wbc->range_cyclic && req->wb_index > idx_end) */
goto out; static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
{
if (!list_empty(&desc->pg_list)) {
int error = desc->pg_doio(desc->pg_inode,
&desc->pg_list,
nfs_page_array_len(desc->pg_base,
desc->pg_count),
desc->pg_count,
desc->pg_ioflags);
if (error < 0)
desc->pg_error = error;
else
desc->pg_bytes_written += desc->pg_count;
}
if (list_empty(&desc->pg_list)) {
desc->pg_count = 0;
desc->pg_base = 0;
}
}
/* Try to lock request and mark it for writeback */ /**
if (!nfs_set_page_writeback_locked(req)) * nfs_pageio_add_request - Attempt to coalesce a request into a page list.
goto next; * @desc: destination io descriptor
radix_tree_tag_clear(&nfsi->nfs_page_tree, * @req: request
req->wb_index, NFS_PAGE_TAG_DIRTY); *
nfsi->ndirty--; * Returns true if the request 'req' was successfully coalesced into the
nfs_list_remove_request(req); * existing list of pages 'desc'.
nfs_list_add_request(req, dst); */
res++; int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
if (res == LONG_MAX) struct nfs_page *req)
goto out; {
next: while (!nfs_pageio_do_add_request(desc, req)) {
idx_start = req->wb_index + 1; nfs_pageio_doio(desc);
} if (desc->pg_error < 0)
return 0;
} }
out: return 1;
WARN_ON ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty));
return res;
} }
/**
* nfs_pageio_complete - Complete I/O on an nfs_pageio_descriptor
* @desc: pointer to io descriptor
*/
void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
{
nfs_pageio_doio(desc);
}
#define NFS_SCAN_MAXENTRIES 16
/** /**
* nfs_scan_list - Scan a list for matching requests * nfs_scan_list - Scan a list for matching requests
* @nfsi: NFS inode * @nfsi: NFS inode
...@@ -355,12 +377,12 @@ long nfs_scan_dirty(struct address_space *mapping, ...@@ -355,12 +377,12 @@ long nfs_scan_dirty(struct address_space *mapping,
* You must be holding the inode's req_lock when calling this function * You must be holding the inode's req_lock when calling this function
*/ */
int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head,
struct list_head *dst, unsigned long idx_start, struct list_head *dst, pgoff_t idx_start,
unsigned int npages) unsigned int npages)
{ {
struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES]; struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES];
struct nfs_page *req; struct nfs_page *req;
unsigned long idx_end; pgoff_t idx_end;
int found, i; int found, i;
int res; int res;
......
...@@ -27,7 +27,8 @@ ...@@ -27,7 +27,8 @@
#define NFSDBG_FACILITY NFSDBG_PAGECACHE #define NFSDBG_FACILITY NFSDBG_PAGECACHE
static int nfs_pagein_one(struct list_head *, struct inode *); static int nfs_pagein_multi(struct inode *, struct list_head *, unsigned int, size_t, int);
static int nfs_pagein_one(struct inode *, struct list_head *, unsigned int, size_t, int);
static const struct rpc_call_ops nfs_read_partial_ops; static const struct rpc_call_ops nfs_read_partial_ops;
static const struct rpc_call_ops nfs_read_full_ops; static const struct rpc_call_ops nfs_read_full_ops;
...@@ -36,9 +37,8 @@ static mempool_t *nfs_rdata_mempool; ...@@ -36,9 +37,8 @@ static mempool_t *nfs_rdata_mempool;
#define MIN_POOL_READ (32) #define MIN_POOL_READ (32)
struct nfs_read_data *nfs_readdata_alloc(size_t len) struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
{ {
unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS); struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS);
if (p) { if (p) {
...@@ -133,7 +133,10 @@ static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, ...@@ -133,7 +133,10 @@ static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len); memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len);
nfs_list_add_request(new, &one_request); nfs_list_add_request(new, &one_request);
nfs_pagein_one(&one_request, inode); if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
nfs_pagein_multi(inode, &one_request, 1, len, 0);
else
nfs_pagein_one(inode, &one_request, 1, len, 0);
return 0; return 0;
} }
...@@ -230,7 +233,7 @@ static void nfs_execute_read(struct nfs_read_data *data) ...@@ -230,7 +233,7 @@ static void nfs_execute_read(struct nfs_read_data *data)
* won't see the new data until our attribute cache is updated. This is more * won't see the new data until our attribute cache is updated. This is more
* or less conventional NFS client behavior. * or less conventional NFS client behavior.
*/ */
static int nfs_pagein_multi(struct list_head *head, struct inode *inode) static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags)
{ {
struct nfs_page *req = nfs_list_entry(head->next); struct nfs_page *req = nfs_list_entry(head->next);
struct page *page = req->wb_page; struct page *page = req->wb_page;
...@@ -242,11 +245,11 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode) ...@@ -242,11 +245,11 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
nfs_list_remove_request(req); nfs_list_remove_request(req);
nbytes = req->wb_bytes; nbytes = count;
do { do {
size_t len = min(nbytes,rsize); size_t len = min(nbytes,rsize);
data = nfs_readdata_alloc(len); data = nfs_readdata_alloc(1);
if (!data) if (!data)
goto out_bad; goto out_bad;
INIT_LIST_HEAD(&data->pages); INIT_LIST_HEAD(&data->pages);
...@@ -258,23 +261,19 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode) ...@@ -258,23 +261,19 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
ClearPageError(page); ClearPageError(page);
offset = 0; offset = 0;
nbytes = req->wb_bytes; nbytes = count;
do { do {
data = list_entry(list.next, struct nfs_read_data, pages); data = list_entry(list.next, struct nfs_read_data, pages);
list_del_init(&data->pages); list_del_init(&data->pages);
data->pagevec[0] = page; data->pagevec[0] = page;
if (nbytes > rsize) { if (nbytes < rsize)
nfs_read_rpcsetup(req, data, &nfs_read_partial_ops, rsize = nbytes;
rsize, offset); nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
offset += rsize; rsize, offset);
nbytes -= rsize; offset += rsize;
} else { nbytes -= rsize;
nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
nbytes, offset);
nbytes = 0;
}
nfs_execute_read(data); nfs_execute_read(data);
} while (nbytes != 0); } while (nbytes != 0);
...@@ -291,30 +290,24 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode) ...@@ -291,30 +290,24 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
return -ENOMEM; return -ENOMEM;
} }
static int nfs_pagein_one(struct list_head *head, struct inode *inode) static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags)
{ {
struct nfs_page *req; struct nfs_page *req;
struct page **pages; struct page **pages;
struct nfs_read_data *data; struct nfs_read_data *data;
unsigned int count;
if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE) data = nfs_readdata_alloc(npages);
return nfs_pagein_multi(head, inode);
data = nfs_readdata_alloc(NFS_SERVER(inode)->rsize);
if (!data) if (!data)
goto out_bad; goto out_bad;
INIT_LIST_HEAD(&data->pages); INIT_LIST_HEAD(&data->pages);
pages = data->pagevec; pages = data->pagevec;
count = 0;
while (!list_empty(head)) { while (!list_empty(head)) {
req = nfs_list_entry(head->next); req = nfs_list_entry(head->next);
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_list_add_request(req, &data->pages); nfs_list_add_request(req, &data->pages);
ClearPageError(req->wb_page); ClearPageError(req->wb_page);
*pages++ = req->wb_page; *pages++ = req->wb_page;
count += req->wb_bytes;
} }
req = nfs_list_entry(data->pages.next); req = nfs_list_entry(data->pages.next);
...@@ -327,28 +320,6 @@ static int nfs_pagein_one(struct list_head *head, struct inode *inode) ...@@ -327,28 +320,6 @@ static int nfs_pagein_one(struct list_head *head, struct inode *inode)
return -ENOMEM; return -ENOMEM;
} }
static int
nfs_pagein_list(struct list_head *head, int rpages)
{
LIST_HEAD(one_request);
struct nfs_page *req;
int error = 0;
unsigned int pages = 0;
while (!list_empty(head)) {
pages += nfs_coalesce_requests(head, &one_request, rpages);
req = nfs_list_entry(one_request.next);
error = nfs_pagein_one(&one_request, req->wb_context->dentry->d_inode);
if (error < 0)
break;
}
if (error >= 0)
return pages;
nfs_async_read_error(head);
return error;
}
/* /*
* This is the callback from RPC telling us whether a reply was * This is the callback from RPC telling us whether a reply was
* received or some error occurred (timeout or socket shutdown). * received or some error occurred (timeout or socket shutdown).
...@@ -538,7 +509,7 @@ int nfs_readpage(struct file *file, struct page *page) ...@@ -538,7 +509,7 @@ int nfs_readpage(struct file *file, struct page *page)
} }
struct nfs_readdesc { struct nfs_readdesc {
struct list_head *head; struct nfs_pageio_descriptor *pgio;
struct nfs_open_context *ctx; struct nfs_open_context *ctx;
}; };
...@@ -562,19 +533,21 @@ readpage_async_filler(void *data, struct page *page) ...@@ -562,19 +533,21 @@ readpage_async_filler(void *data, struct page *page)
} }
if (len < PAGE_CACHE_SIZE) if (len < PAGE_CACHE_SIZE)
memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len); memclear_highpage_flush(page, len, PAGE_CACHE_SIZE - len);
nfs_list_add_request(new, desc->head); nfs_pageio_add_request(desc->pgio, new);
return 0; return 0;
} }
int nfs_readpages(struct file *filp, struct address_space *mapping, int nfs_readpages(struct file *filp, struct address_space *mapping,
struct list_head *pages, unsigned nr_pages) struct list_head *pages, unsigned nr_pages)
{ {
LIST_HEAD(head); struct nfs_pageio_descriptor pgio;
struct nfs_readdesc desc = { struct nfs_readdesc desc = {
.head = &head, .pgio = &pgio,
}; };
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
size_t rsize = server->rsize;
unsigned long npages;
int ret = -ESTALE; int ret = -ESTALE;
dprintk("NFS: nfs_readpages (%s/%Ld %d)\n", dprintk("NFS: nfs_readpages (%s/%Ld %d)\n",
...@@ -593,13 +566,16 @@ int nfs_readpages(struct file *filp, struct address_space *mapping, ...@@ -593,13 +566,16 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
} else } else
desc.ctx = get_nfs_open_context((struct nfs_open_context *) desc.ctx = get_nfs_open_context((struct nfs_open_context *)
filp->private_data); filp->private_data);
if (rsize < PAGE_CACHE_SIZE)
nfs_pageio_init(&pgio, inode, nfs_pagein_multi, rsize, 0);
else
nfs_pageio_init(&pgio, inode, nfs_pagein_one, rsize, 0);
ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);
if (!list_empty(&head)) {
int err = nfs_pagein_list(&head, server->rpages); nfs_pageio_complete(&pgio);
if (!ret) npages = (pgio.pg_bytes_written + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
nfs_add_stats(inode, NFSIOS_READPAGES, err); nfs_add_stats(inode, NFSIOS_READPAGES, npages);
ret = err;
}
put_nfs_open_context(desc.ctx); put_nfs_open_context(desc.ctx);
out: out:
return ret; return ret;
......
...@@ -204,9 +204,9 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -204,9 +204,9 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
lock_kernel(); lock_kernel();
error = server->nfs_client->rpc_ops->statfs(server, fh, &res); error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
buf->f_type = NFS_SUPER_MAGIC;
if (error < 0) if (error < 0)
goto out_err; goto out_err;
buf->f_type = NFS_SUPER_MAGIC;
/* /*
* Current versions of glibc do not correctly handle the * Current versions of glibc do not correctly handle the
...@@ -233,15 +233,14 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -233,15 +233,14 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_ffree = res.afiles; buf->f_ffree = res.afiles;
buf->f_namelen = server->namelen; buf->f_namelen = server->namelen;
out:
unlock_kernel(); unlock_kernel();
return 0; return 0;
out_err: out_err:
dprintk("%s: statfs error = %d\n", __FUNCTION__, -error); dprintk("%s: statfs error = %d\n", __FUNCTION__, -error);
buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; unlock_kernel();
goto out; return error;
} }
/* /*
...@@ -291,6 +290,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, ...@@ -291,6 +290,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
{ NFS_MOUNT_NOAC, ",noac", "" }, { NFS_MOUNT_NOAC, ",noac", "" },
{ NFS_MOUNT_NONLM, ",nolock", "" }, { NFS_MOUNT_NONLM, ",nolock", "" },
{ NFS_MOUNT_NOACL, ",noacl", "" }, { NFS_MOUNT_NOACL, ",noacl", "" },
{ NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
{ 0, NULL, NULL } { 0, NULL, NULL }
}; };
const struct proc_nfs_info *nfs_infop; const struct proc_nfs_info *nfs_infop;
......
...@@ -38,7 +38,8 @@ ...@@ -38,7 +38,8 @@
static struct nfs_page * nfs_update_request(struct nfs_open_context*, static struct nfs_page * nfs_update_request(struct nfs_open_context*,
struct page *, struct page *,
unsigned int, unsigned int); unsigned int, unsigned int);
static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how); static void nfs_pageio_init_write(struct nfs_pageio_descriptor *desc,
struct inode *inode, int ioflags);
static const struct rpc_call_ops nfs_write_partial_ops; static const struct rpc_call_ops nfs_write_partial_ops;
static const struct rpc_call_ops nfs_write_full_ops; static const struct rpc_call_ops nfs_write_full_ops;
static const struct rpc_call_ops nfs_commit_ops; static const struct rpc_call_ops nfs_commit_ops;
...@@ -71,9 +72,8 @@ void nfs_commit_free(struct nfs_write_data *wdata) ...@@ -71,9 +72,8 @@ void nfs_commit_free(struct nfs_write_data *wdata)
call_rcu_bh(&wdata->task.u.tk_rcu, nfs_commit_rcu_free); call_rcu_bh(&wdata->task.u.tk_rcu, nfs_commit_rcu_free);
} }
struct nfs_write_data *nfs_writedata_alloc(size_t len) struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
{ {
unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
if (p) { if (p) {
...@@ -139,7 +139,7 @@ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int c ...@@ -139,7 +139,7 @@ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int c
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
loff_t end, i_size = i_size_read(inode); loff_t end, i_size = i_size_read(inode);
unsigned long end_index = (i_size - 1) >> PAGE_CACHE_SHIFT; pgoff_t end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
if (i_size > 0 && page->index < end_index) if (i_size > 0 && page->index < end_index)
return; return;
...@@ -201,7 +201,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page, ...@@ -201,7 +201,7 @@ static int nfs_writepage_setup(struct nfs_open_context *ctx, struct page *page,
static int wb_priority(struct writeback_control *wbc) static int wb_priority(struct writeback_control *wbc)
{ {
if (wbc->for_reclaim) if (wbc->for_reclaim)
return FLUSH_HIGHPRI; return FLUSH_HIGHPRI | FLUSH_STABLE;
if (wbc->for_kupdate) if (wbc->for_kupdate)
return FLUSH_LOWPRI; return FLUSH_LOWPRI;
return 0; return 0;
...@@ -251,7 +251,8 @@ static void nfs_end_page_writeback(struct page *page) ...@@ -251,7 +251,8 @@ static void nfs_end_page_writeback(struct page *page)
* was not tagged. * was not tagged.
* May also return an error if the user signalled nfs_wait_on_request(). * May also return an error if the user signalled nfs_wait_on_request().
*/ */
static int nfs_page_mark_flush(struct page *page) static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
struct page *page)
{ {
struct nfs_page *req; struct nfs_page *req;
struct nfs_inode *nfsi = NFS_I(page->mapping->host); struct nfs_inode *nfsi = NFS_I(page->mapping->host);
...@@ -273,6 +274,8 @@ static int nfs_page_mark_flush(struct page *page) ...@@ -273,6 +274,8 @@ static int nfs_page_mark_flush(struct page *page)
* request as dirty (in which case we don't care). * request as dirty (in which case we don't care).
*/ */
spin_unlock(req_lock); spin_unlock(req_lock);
/* Prevent deadlock! */
nfs_pageio_complete(pgio);
ret = nfs_wait_on_request(req); ret = nfs_wait_on_request(req);
nfs_release_request(req); nfs_release_request(req);
if (ret != 0) if (ret != 0)
...@@ -283,21 +286,18 @@ static int nfs_page_mark_flush(struct page *page) ...@@ -283,21 +286,18 @@ static int nfs_page_mark_flush(struct page *page)
/* This request is marked for commit */ /* This request is marked for commit */
spin_unlock(req_lock); spin_unlock(req_lock);
nfs_unlock_request(req); nfs_unlock_request(req);
nfs_pageio_complete(pgio);
return 1; return 1;
} }
if (nfs_set_page_writeback(page) == 0) { if (nfs_set_page_writeback(page) != 0) {
nfs_list_remove_request(req);
/* add the request to the inode's dirty list. */
radix_tree_tag_set(&nfsi->nfs_page_tree,
req->wb_index, NFS_PAGE_TAG_DIRTY);
nfs_list_add_request(req, &nfsi->dirty);
nfsi->ndirty++;
spin_unlock(req_lock);
__mark_inode_dirty(page->mapping->host, I_DIRTY_PAGES);
} else
spin_unlock(req_lock); spin_unlock(req_lock);
BUG();
}
radix_tree_tag_set(&nfsi->nfs_page_tree, req->wb_index,
NFS_PAGE_TAG_WRITEBACK);
ret = test_bit(PG_NEED_FLUSH, &req->wb_flags); ret = test_bit(PG_NEED_FLUSH, &req->wb_flags);
nfs_unlock_request(req); spin_unlock(req_lock);
nfs_pageio_add_request(pgio, req);
return ret; return ret;
} }
...@@ -306,6 +306,7 @@ static int nfs_page_mark_flush(struct page *page) ...@@ -306,6 +306,7 @@ static int nfs_page_mark_flush(struct page *page)
*/ */
static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc) static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc)
{ {
struct nfs_pageio_descriptor mypgio, *pgio;
struct nfs_open_context *ctx; struct nfs_open_context *ctx;
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
unsigned offset; unsigned offset;
...@@ -314,7 +315,14 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc ...@@ -314,7 +315,14 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE); nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1); nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1);
err = nfs_page_mark_flush(page); if (wbc->for_writepages)
pgio = wbc->fs_private;
else {
nfs_pageio_init_write(&mypgio, inode, wb_priority(wbc));
pgio = &mypgio;
}
err = nfs_page_async_flush(pgio, page);
if (err <= 0) if (err <= 0)
goto out; goto out;
err = 0; err = 0;
...@@ -331,12 +339,12 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc ...@@ -331,12 +339,12 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
put_nfs_open_context(ctx); put_nfs_open_context(ctx);
if (err != 0) if (err != 0)
goto out; goto out;
err = nfs_page_mark_flush(page); err = nfs_page_async_flush(pgio, page);
if (err > 0) if (err > 0)
err = 0; err = 0;
out: out:
if (!wbc->for_writepages) if (!wbc->for_writepages)
nfs_flush_mapping(page->mapping, wbc, FLUSH_STABLE|wb_priority(wbc)); nfs_pageio_complete(pgio);
return err; return err;
} }
...@@ -352,20 +360,20 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -352,20 +360,20 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc) int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
{ {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct nfs_pageio_descriptor pgio;
int err; int err;
nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES); nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
nfs_pageio_init_write(&pgio, inode, wb_priority(wbc));
wbc->fs_private = &pgio;
err = generic_writepages(mapping, wbc); err = generic_writepages(mapping, wbc);
nfs_pageio_complete(&pgio);
if (err) if (err)
return err; return err;
err = nfs_flush_mapping(mapping, wbc, wb_priority(wbc)); if (pgio.pg_error)
if (err < 0) return pgio.pg_error;
goto out; return 0;
nfs_add_stats(inode, NFSIOS_WRITEPAGES, err);
err = 0;
out:
return err;
} }
/* /*
...@@ -503,11 +511,11 @@ int nfs_reschedule_unstable_write(struct nfs_page *req) ...@@ -503,11 +511,11 @@ int nfs_reschedule_unstable_write(struct nfs_page *req)
* *
* Interruptible by signals only if mounted with intr flag. * Interruptible by signals only if mounted with intr flag.
*/ */
static int nfs_wait_on_requests_locked(struct inode *inode, unsigned long idx_start, unsigned int npages) static int nfs_wait_on_requests_locked(struct inode *inode, pgoff_t idx_start, unsigned int npages)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req; struct nfs_page *req;
unsigned long idx_end, next; pgoff_t idx_end, next;
unsigned int res = 0; unsigned int res = 0;
int error; int error;
...@@ -536,18 +544,6 @@ static int nfs_wait_on_requests_locked(struct inode *inode, unsigned long idx_st ...@@ -536,18 +544,6 @@ static int nfs_wait_on_requests_locked(struct inode *inode, unsigned long idx_st
return res; return res;
} }
static void nfs_cancel_dirty_list(struct list_head *head)
{
struct nfs_page *req;
while(!list_empty(head)) {
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_end_page_writeback(req->wb_page);
nfs_inode_remove_request(req);
nfs_clear_page_writeback(req);
}
}
static void nfs_cancel_commit_list(struct list_head *head) static void nfs_cancel_commit_list(struct list_head *head)
{ {
struct nfs_page *req; struct nfs_page *req;
...@@ -574,7 +570,7 @@ static void nfs_cancel_commit_list(struct list_head *head) ...@@ -574,7 +570,7 @@ static void nfs_cancel_commit_list(struct list_head *head)
* The requests are *not* checked to ensure that they form a contiguous set. * The requests are *not* checked to ensure that they form a contiguous set.
*/ */
static int static int
nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
int res = 0; int res = 0;
...@@ -588,40 +584,12 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_st ...@@ -588,40 +584,12 @@ nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_st
return res; return res;
} }
#else #else
static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages) static inline int nfs_scan_commit(struct inode *inode, struct list_head *dst, pgoff_t idx_start, unsigned int npages)
{ {
return 0; return 0;
} }
#endif #endif
static int nfs_wait_on_write_congestion(struct address_space *mapping)
{
struct inode *inode = mapping->host;
struct backing_dev_info *bdi = mapping->backing_dev_info;
int ret = 0;
might_sleep();
if (!bdi_write_congested(bdi))
return 0;
nfs_inc_stats(inode, NFSIOS_CONGESTIONWAIT);
do {
struct rpc_clnt *clnt = NFS_CLIENT(inode);
sigset_t oldset;
rpc_clnt_sigmask(clnt, &oldset);
ret = congestion_wait_interruptible(WRITE, HZ/10);
rpc_clnt_sigunmask(clnt, &oldset);
if (ret == -ERESTARTSYS)
break;
ret = 0;
} while (bdi_write_congested(bdi));
return ret;
}
/* /*
* Try to update any existing write request, or create one if there is none. * Try to update any existing write request, or create one if there is none.
* In order to match, the request's credentials must match those of * In order to match, the request's credentials must match those of
...@@ -636,12 +604,10 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, ...@@ -636,12 +604,10 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx,
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_page *req, *new = NULL; struct nfs_page *req, *new = NULL;
unsigned long rqend, end; pgoff_t rqend, end;
end = offset + bytes; end = offset + bytes;
if (nfs_wait_on_write_congestion(mapping))
return ERR_PTR(-ERESTARTSYS);
for (;;) { for (;;) {
/* Loop over all inode entries and see if we find /* Loop over all inode entries and see if we find
* A request for the page we wish to update * A request for the page we wish to update
...@@ -865,7 +831,7 @@ static void nfs_execute_write(struct nfs_write_data *data) ...@@ -865,7 +831,7 @@ static void nfs_execute_write(struct nfs_write_data *data)
* Generate multiple small requests to write out a single * Generate multiple small requests to write out a single
* contiguous dirty area on one page. * contiguous dirty area on one page.
*/ */
static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how) static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how)
{ {
struct nfs_page *req = nfs_list_entry(head->next); struct nfs_page *req = nfs_list_entry(head->next);
struct page *page = req->wb_page; struct page *page = req->wb_page;
...@@ -877,11 +843,11 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how) ...@@ -877,11 +843,11 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
nfs_list_remove_request(req); nfs_list_remove_request(req);
nbytes = req->wb_bytes; nbytes = count;
do { do {
size_t len = min(nbytes, wsize); size_t len = min(nbytes, wsize);
data = nfs_writedata_alloc(len); data = nfs_writedata_alloc(1);
if (!data) if (!data)
goto out_bad; goto out_bad;
list_add(&data->pages, &list); list_add(&data->pages, &list);
...@@ -892,23 +858,19 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how) ...@@ -892,23 +858,19 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
ClearPageError(page); ClearPageError(page);
offset = 0; offset = 0;
nbytes = req->wb_bytes; nbytes = count;
do { do {
data = list_entry(list.next, struct nfs_write_data, pages); data = list_entry(list.next, struct nfs_write_data, pages);
list_del_init(&data->pages); list_del_init(&data->pages);
data->pagevec[0] = page; data->pagevec[0] = page;
if (nbytes > wsize) { if (nbytes < wsize)
nfs_write_rpcsetup(req, data, &nfs_write_partial_ops, wsize = nbytes;
wsize, offset, how); nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
offset += wsize; wsize, offset, how);
nbytes -= wsize; offset += wsize;
} else { nbytes -= wsize;
nfs_write_rpcsetup(req, data, &nfs_write_partial_ops,
nbytes, offset, how);
nbytes = 0;
}
nfs_execute_write(data); nfs_execute_write(data);
} while (nbytes != 0); } while (nbytes != 0);
...@@ -934,26 +896,23 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how) ...@@ -934,26 +896,23 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
* This is the case if nfs_updatepage detects a conflicting request * This is the case if nfs_updatepage detects a conflicting request
* that has been written but not committed. * that has been written but not committed.
*/ */
static int nfs_flush_one(struct inode *inode, struct list_head *head, int how) static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how)
{ {
struct nfs_page *req; struct nfs_page *req;
struct page **pages; struct page **pages;
struct nfs_write_data *data; struct nfs_write_data *data;
unsigned int count;
data = nfs_writedata_alloc(NFS_SERVER(inode)->wsize); data = nfs_writedata_alloc(npages);
if (!data) if (!data)
goto out_bad; goto out_bad;
pages = data->pagevec; pages = data->pagevec;
count = 0;
while (!list_empty(head)) { while (!list_empty(head)) {
req = nfs_list_entry(head->next); req = nfs_list_entry(head->next);
nfs_list_remove_request(req); nfs_list_remove_request(req);
nfs_list_add_request(req, &data->pages); nfs_list_add_request(req, &data->pages);
ClearPageError(req->wb_page); ClearPageError(req->wb_page);
*pages++ = req->wb_page; *pages++ = req->wb_page;
count += req->wb_bytes;
} }
req = nfs_list_entry(data->pages.next); req = nfs_list_entry(data->pages.next);
...@@ -973,40 +932,15 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how) ...@@ -973,40 +932,15 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how)
return -ENOMEM; return -ENOMEM;
} }
static int nfs_flush_list(struct inode *inode, struct list_head *head, int npages, int how) static void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
struct inode *inode, int ioflags)
{ {
LIST_HEAD(one_request);
int (*flush_one)(struct inode *, struct list_head *, int);
struct nfs_page *req;
int wpages = NFS_SERVER(inode)->wpages;
int wsize = NFS_SERVER(inode)->wsize; int wsize = NFS_SERVER(inode)->wsize;
int error;
flush_one = nfs_flush_one;
if (wsize < PAGE_CACHE_SIZE) if (wsize < PAGE_CACHE_SIZE)
flush_one = nfs_flush_multi; nfs_pageio_init(pgio, inode, nfs_flush_multi, wsize, ioflags);
/* For single writes, FLUSH_STABLE is more efficient */ else
if (npages <= wpages && npages == NFS_I(inode)->npages nfs_pageio_init(pgio, inode, nfs_flush_one, wsize, ioflags);
&& nfs_list_entry(head->next)->wb_bytes <= wsize)
how |= FLUSH_STABLE;
do {
nfs_coalesce_requests(head, &one_request, wpages);
req = nfs_list_entry(one_request.next);
error = flush_one(inode, &one_request, how);
if (error < 0)
goto out_err;
} while (!list_empty(head));
return 0;
out_err:
while (!list_empty(head)) {
req = nfs_list_entry(head->next);
nfs_list_remove_request(req);
nfs_redirty_request(req);
nfs_end_page_writeback(req->wb_page);
nfs_clear_page_writeback(req);
}
return error;
} }
/* /*
...@@ -1330,31 +1264,7 @@ static const struct rpc_call_ops nfs_commit_ops = { ...@@ -1330,31 +1264,7 @@ static const struct rpc_call_ops nfs_commit_ops = {
.rpc_call_done = nfs_commit_done, .rpc_call_done = nfs_commit_done,
.rpc_release = nfs_commit_release, .rpc_release = nfs_commit_release,
}; };
#else
static inline int nfs_commit_list(struct inode *inode, struct list_head *head, int how)
{
return 0;
}
#endif
static long nfs_flush_mapping(struct address_space *mapping, struct writeback_control *wbc, int how)
{
struct nfs_inode *nfsi = NFS_I(mapping->host);
LIST_HEAD(head);
long res;
spin_lock(&nfsi->req_lock);
res = nfs_scan_dirty(mapping, wbc, &head);
spin_unlock(&nfsi->req_lock);
if (res) {
int error = nfs_flush_list(mapping->host, &head, res, how);
if (error < 0)
return error;
}
return res;
}
#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
int nfs_commit_inode(struct inode *inode, int how) int nfs_commit_inode(struct inode *inode, int how)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
...@@ -1371,13 +1281,18 @@ int nfs_commit_inode(struct inode *inode, int how) ...@@ -1371,13 +1281,18 @@ int nfs_commit_inode(struct inode *inode, int how)
} }
return res; return res;
} }
#else
static inline int nfs_commit_list(struct inode *inode, struct list_head *head, int how)
{
return 0;
}
#endif #endif
long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how) long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_control *wbc, int how)
{ {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
unsigned long idx_start, idx_end; pgoff_t idx_start, idx_end;
unsigned int npages = 0; unsigned int npages = 0;
LIST_HEAD(head); LIST_HEAD(head);
int nocommit = how & FLUSH_NOCOMMIT; int nocommit = how & FLUSH_NOCOMMIT;
...@@ -1390,41 +1305,24 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr ...@@ -1390,41 +1305,24 @@ long nfs_sync_mapping_wait(struct address_space *mapping, struct writeback_contr
idx_start = wbc->range_start >> PAGE_CACHE_SHIFT; idx_start = wbc->range_start >> PAGE_CACHE_SHIFT;
idx_end = wbc->range_end >> PAGE_CACHE_SHIFT; idx_end = wbc->range_end >> PAGE_CACHE_SHIFT;
if (idx_end > idx_start) { if (idx_end > idx_start) {
unsigned long l_npages = 1 + idx_end - idx_start; pgoff_t l_npages = 1 + idx_end - idx_start;
npages = l_npages; npages = l_npages;
if (sizeof(npages) != sizeof(l_npages) && if (sizeof(npages) != sizeof(l_npages) &&
(unsigned long)npages != l_npages) (pgoff_t)npages != l_npages)
npages = 0; npages = 0;
} }
} }
how &= ~FLUSH_NOCOMMIT; how &= ~FLUSH_NOCOMMIT;
spin_lock(&nfsi->req_lock); spin_lock(&nfsi->req_lock);
do { do {
wbc->pages_skipped = 0;
ret = nfs_wait_on_requests_locked(inode, idx_start, npages); ret = nfs_wait_on_requests_locked(inode, idx_start, npages);
if (ret != 0) if (ret != 0)
continue; continue;
pages = nfs_scan_dirty(mapping, wbc, &head);
if (pages != 0) {
spin_unlock(&nfsi->req_lock);
if (how & FLUSH_INVALIDATE) {
nfs_cancel_dirty_list(&head);
ret = pages;
} else
ret = nfs_flush_list(inode, &head, pages, how);
spin_lock(&nfsi->req_lock);
continue;
}
if (wbc->pages_skipped != 0)
continue;
if (nocommit) if (nocommit)
break; break;
pages = nfs_scan_commit(inode, &head, idx_start, npages); pages = nfs_scan_commit(inode, &head, idx_start, npages);
if (pages == 0) { if (pages == 0)
if (wbc->pages_skipped != 0)
continue;
break; break;
}
if (how & FLUSH_INVALIDATE) { if (how & FLUSH_INVALIDATE) {
spin_unlock(&nfsi->req_lock); spin_unlock(&nfsi->req_lock);
nfs_cancel_commit_list(&head); nfs_cancel_commit_list(&head);
...@@ -1456,7 +1354,7 @@ int nfs_wb_all(struct inode *inode) ...@@ -1456,7 +1354,7 @@ int nfs_wb_all(struct inode *inode)
}; };
int ret; int ret;
ret = generic_writepages(mapping, &wbc); ret = nfs_writepages(mapping, &wbc);
if (ret < 0) if (ret < 0)
goto out; goto out;
ret = nfs_sync_mapping_wait(mapping, &wbc, 0); ret = nfs_sync_mapping_wait(mapping, &wbc, 0);
...@@ -1479,11 +1377,9 @@ int nfs_sync_mapping_range(struct address_space *mapping, loff_t range_start, lo ...@@ -1479,11 +1377,9 @@ int nfs_sync_mapping_range(struct address_space *mapping, loff_t range_start, lo
}; };
int ret; int ret;
if (!(how & FLUSH_NOWRITEPAGE)) { ret = nfs_writepages(mapping, &wbc);
ret = generic_writepages(mapping, &wbc); if (ret < 0)
if (ret < 0) goto out;
goto out;
}
ret = nfs_sync_mapping_wait(mapping, &wbc, how); ret = nfs_sync_mapping_wait(mapping, &wbc, how);
if (ret >= 0) if (ret >= 0)
return 0; return 0;
...@@ -1506,7 +1402,7 @@ int nfs_wb_page_priority(struct inode *inode, struct page *page, int how) ...@@ -1506,7 +1402,7 @@ int nfs_wb_page_priority(struct inode *inode, struct page *page, int how)
int ret; int ret;
BUG_ON(!PageLocked(page)); BUG_ON(!PageLocked(page));
if (!(how & FLUSH_NOWRITEPAGE) && clear_page_dirty_for_io(page)) { if (clear_page_dirty_for_io(page)) {
ret = nfs_writepage_locked(page, &wbc); ret = nfs_writepage_locked(page, &wbc);
if (ret < 0) if (ret < 0)
goto out; goto out;
...@@ -1531,10 +1427,18 @@ int nfs_wb_page(struct inode *inode, struct page* page) ...@@ -1531,10 +1427,18 @@ int nfs_wb_page(struct inode *inode, struct page* page)
int nfs_set_page_dirty(struct page *page) int nfs_set_page_dirty(struct page *page)
{ {
spinlock_t *req_lock = &NFS_I(page->mapping->host)->req_lock; struct address_space *mapping = page->mapping;
struct inode *inode;
spinlock_t *req_lock;
struct nfs_page *req; struct nfs_page *req;
int ret; int ret;
if (!mapping)
goto out_raced;
inode = mapping->host;
if (!inode)
goto out_raced;
req_lock = &NFS_I(inode)->req_lock;
spin_lock(req_lock); spin_lock(req_lock);
req = nfs_page_find_request_locked(page); req = nfs_page_find_request_locked(page);
if (req != NULL) { if (req != NULL) {
...@@ -1547,6 +1451,8 @@ int nfs_set_page_dirty(struct page *page) ...@@ -1547,6 +1451,8 @@ int nfs_set_page_dirty(struct page *page)
ret = __set_page_dirty_nobuffers(page); ret = __set_page_dirty_nobuffers(page);
spin_unlock(req_lock); spin_unlock(req_lock);
return ret; return ret;
out_raced:
return !TestSetPageDirty(page);
} }
......
...@@ -315,16 +315,13 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p) ...@@ -315,16 +315,13 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p)
/* /*
* RPC procedure tables * RPC procedure tables
*/ */
#ifndef MAX
# define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
#define PROC(proc, call, argtype, restype) \ #define PROC(proc, call, argtype, restype) \
[NFSPROC4_CLNT_##proc] = { \ [NFSPROC4_CLNT_##proc] = { \
.p_proc = NFSPROC4_CB_##call, \ .p_proc = NFSPROC4_CB_##call, \
.p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \ .p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \
.p_decode = (kxdrproc_t) nfs4_xdr_##restype, \ .p_decode = (kxdrproc_t) nfs4_xdr_##restype, \
.p_bufsiz = MAX(NFS4_##argtype##_sz,NFS4_##restype##_sz) << 2, \ .p_arglen = NFS4_##argtype##_sz, \
.p_replen = NFS4_##restype##_sz, \
.p_statidx = NFSPROC4_CB_##call, \ .p_statidx = NFSPROC4_CB_##call, \
.p_name = #proc, \ .p_name = #proc, \
} }
......
...@@ -88,7 +88,7 @@ struct nlm_wait; ...@@ -88,7 +88,7 @@ struct nlm_wait;
/* /*
* Memory chunk for NLM client RPC request. * Memory chunk for NLM client RPC request.
*/ */
#define NLMCLNT_OHSIZE (sizeof(utsname()->nodename)+10) #define NLMCLNT_OHSIZE ((__NEW_UTS_LEN) + 10u)
struct nlm_rqst { struct nlm_rqst {
unsigned int a_flags; /* initial RPC task flags */ unsigned int a_flags; /* initial RPC task flags */
struct nlm_host * a_host; /* host handle */ struct nlm_host * a_host; /* host handle */
......
...@@ -455,7 +455,7 @@ nfs_have_writebacks(struct inode *inode) ...@@ -455,7 +455,7 @@ nfs_have_writebacks(struct inode *inode)
/* /*
* Allocate nfs_write_data structures * Allocate nfs_write_data structures
*/ */
extern struct nfs_write_data *nfs_writedata_alloc(size_t len); extern struct nfs_write_data *nfs_writedata_alloc(unsigned int npages);
/* /*
* linux/fs/nfs/read.c * linux/fs/nfs/read.c
...@@ -469,7 +469,7 @@ extern void nfs_readdata_release(void *data); ...@@ -469,7 +469,7 @@ extern void nfs_readdata_release(void *data);
/* /*
* Allocate nfs_read_data structures * Allocate nfs_read_data structures
*/ */
extern struct nfs_read_data *nfs_readdata_alloc(size_t len); extern struct nfs_read_data *nfs_readdata_alloc(unsigned int npages);
/* /*
* linux/fs/nfs3proc.c * linux/fs/nfs3proc.c
......
...@@ -61,6 +61,7 @@ struct nfs_mount_data { ...@@ -61,6 +61,7 @@ struct nfs_mount_data {
#define NFS_MOUNT_NOACL 0x0800 /* 4 */ #define NFS_MOUNT_NOACL 0x0800 /* 4 */
#define NFS_MOUNT_STRICTLOCK 0x1000 /* reserved for NFSv4 */ #define NFS_MOUNT_STRICTLOCK 0x1000 /* reserved for NFSv4 */
#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */ #define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */
#define NFS_MOUNT_NORDIRPLUS 0x4000 /* 5 */
#define NFS_MOUNT_FLAGMASK 0xFFFF #define NFS_MOUNT_FLAGMASK 0xFFFF
#endif #endif
...@@ -21,8 +21,7 @@ ...@@ -21,8 +21,7 @@
/* /*
* Valid flags for the radix tree * Valid flags for the radix tree
*/ */
#define NFS_PAGE_TAG_DIRTY 0 #define NFS_PAGE_TAG_WRITEBACK 0
#define NFS_PAGE_TAG_WRITEBACK 1
/* /*
* Valid flags for a dirty buffer * Valid flags for a dirty buffer
...@@ -39,7 +38,7 @@ struct nfs_page { ...@@ -39,7 +38,7 @@ struct nfs_page {
struct page *wb_page; /* page to read in/write out */ struct page *wb_page; /* page to read in/write out */
struct nfs_open_context *wb_context; /* File state context info */ struct nfs_open_context *wb_context; /* File state context info */
atomic_t wb_complete; /* i/os we're waiting for */ atomic_t wb_complete; /* i/os we're waiting for */
unsigned long wb_index; /* Offset >> PAGE_CACHE_SHIFT */ pgoff_t wb_index; /* Offset >> PAGE_CACHE_SHIFT */
unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */ unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */
wb_pgbase, /* Start of page data */ wb_pgbase, /* Start of page data */
wb_bytes; /* Length of request */ wb_bytes; /* Length of request */
...@@ -48,6 +47,19 @@ struct nfs_page { ...@@ -48,6 +47,19 @@ struct nfs_page {
struct nfs_writeverf wb_verf; /* Commit cookie */ struct nfs_writeverf wb_verf; /* Commit cookie */
}; };
struct nfs_pageio_descriptor {
struct list_head pg_list;
unsigned long pg_bytes_written;
size_t pg_count;
size_t pg_bsize;
unsigned int pg_base;
struct inode *pg_inode;
int (*pg_doio)(struct inode *, struct list_head *, unsigned int, size_t, int);
int pg_ioflags;
int pg_error;
};
#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags)) #define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))
extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx, extern struct nfs_page *nfs_create_request(struct nfs_open_context *ctx,
...@@ -59,13 +71,16 @@ extern void nfs_clear_request(struct nfs_page *req); ...@@ -59,13 +71,16 @@ extern void nfs_clear_request(struct nfs_page *req);
extern void nfs_release_request(struct nfs_page *req); extern void nfs_release_request(struct nfs_page *req);
extern long nfs_scan_dirty(struct address_space *mapping,
struct writeback_control *wbc,
struct list_head *dst);
extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst,
unsigned long idx_start, unsigned int npages); pgoff_t idx_start, unsigned int npages);
extern int nfs_coalesce_requests(struct list_head *, struct list_head *, extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
unsigned int); struct inode *inode,
int (*doio)(struct inode *, struct list_head *, unsigned int, size_t, int),
size_t bsize,
int how);
extern int nfs_pageio_add_request(struct nfs_pageio_descriptor *,
struct nfs_page *);
extern void nfs_pageio_complete(struct nfs_pageio_descriptor *desc);
extern int nfs_wait_on_request(struct nfs_page *); extern int nfs_wait_on_request(struct nfs_page *);
extern void nfs_unlock_request(struct nfs_page *req); extern void nfs_unlock_request(struct nfs_page *req);
extern int nfs_set_page_writeback_locked(struct nfs_page *req); extern int nfs_set_page_writeback_locked(struct nfs_page *req);
......
...@@ -84,7 +84,8 @@ struct rpc_procinfo { ...@@ -84,7 +84,8 @@ struct rpc_procinfo {
u32 p_proc; /* RPC procedure number */ u32 p_proc; /* RPC procedure number */
kxdrproc_t p_encode; /* XDR encode function */ kxdrproc_t p_encode; /* XDR encode function */
kxdrproc_t p_decode; /* XDR decode function */ kxdrproc_t p_decode; /* XDR decode function */
unsigned int p_bufsiz; /* req. buffer size */ unsigned int p_arglen; /* argument hdr length (u32) */
unsigned int p_replen; /* reply hdr length (u32) */
unsigned int p_count; /* call count */ unsigned int p_count; /* call count */
unsigned int p_timer; /* Which RTT timer to use */ unsigned int p_timer; /* Which RTT timer to use */
u32 p_statidx; /* Which procedure to account */ u32 p_statidx; /* Which procedure to account */
...@@ -121,8 +122,8 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); ...@@ -121,8 +122,8 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
int rpc_shutdown_client(struct rpc_clnt *); int rpc_shutdown_client(struct rpc_clnt *);
int rpc_destroy_client(struct rpc_clnt *); int rpc_destroy_client(struct rpc_clnt *);
void rpc_release_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *);
void rpc_getport(struct rpc_task *); int rpcb_register(u32, u32, int, unsigned short, int *);
int rpc_register(u32, u32, int, unsigned short, int *); void rpcb_getport(struct rpc_task *);
void rpc_call_setup(struct rpc_task *, struct rpc_message *, int); void rpc_call_setup(struct rpc_task *, struct rpc_message *, int);
...@@ -144,7 +145,7 @@ char * rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t); ...@@ -144,7 +145,7 @@ char * rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t);
/* /*
* Helper function for NFSroot support * Helper function for NFSroot support
*/ */
int rpc_getport_external(struct sockaddr_in *, __u32, __u32, int); int rpcb_getport_external(struct sockaddr_in *, __u32, __u32, int);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_CLNT_H */ #endif /* _LINUX_SUNRPC_CLNT_H */
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
#define RPCDBG_DEBUG 0x0004 #define RPCDBG_DEBUG 0x0004
#define RPCDBG_NFS 0x0008 #define RPCDBG_NFS 0x0008
#define RPCDBG_AUTH 0x0010 #define RPCDBG_AUTH 0x0010
#define RPCDBG_PMAP 0x0020 #define RPCDBG_BIND 0x0020
#define RPCDBG_SCHED 0x0040 #define RPCDBG_SCHED 0x0040
#define RPCDBG_TRANS 0x0080 #define RPCDBG_TRANS 0x0080
#define RPCDBG_SVCSOCK 0x0100 #define RPCDBG_SVCSOCK 0x0100
......
...@@ -78,10 +78,6 @@ enum rpc_auth_stat { ...@@ -78,10 +78,6 @@ enum rpc_auth_stat {
RPCSEC_GSS_CTXPROBLEM = 14 RPCSEC_GSS_CTXPROBLEM = 14
}; };
#define RPC_PMAP_PROGRAM 100000
#define RPC_PMAP_VERSION 2
#define RPC_PMAP_PORT 111
#define RPC_MAXNETNAMELEN 256 #define RPC_MAXNETNAMELEN 256
/* /*
......
...@@ -264,7 +264,7 @@ struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *); ...@@ -264,7 +264,7 @@ struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *);
void rpc_wake_up_status(struct rpc_wait_queue *, int); void rpc_wake_up_status(struct rpc_wait_queue *, int);
void rpc_delay(struct rpc_task *, unsigned long); void rpc_delay(struct rpc_task *, unsigned long);
void * rpc_malloc(struct rpc_task *, size_t); void * rpc_malloc(struct rpc_task *, size_t);
void rpc_free(struct rpc_task *); void rpc_free(void *);
int rpciod_up(void); int rpciod_up(void);
void rpciod_down(void); void rpciod_down(void);
int __rpc_wait_for_completion_task(struct rpc_task *task, int (*)(void *)); int __rpc_wait_for_completion_task(struct rpc_task *task, int (*)(void *));
......
...@@ -84,7 +84,9 @@ struct rpc_rqst { ...@@ -84,7 +84,9 @@ struct rpc_rqst {
struct list_head rq_list; struct list_head rq_list;
__u32 * rq_buffer; /* XDR encode buffer */ __u32 * rq_buffer; /* XDR encode buffer */
size_t rq_bufsize; size_t rq_bufsize,
rq_callsize,
rq_rcvsize;
struct xdr_buf rq_private_buf; /* The receive buffer struct xdr_buf rq_private_buf; /* The receive buffer
* used in the softirq. * used in the softirq.
...@@ -112,7 +114,7 @@ struct rpc_xprt_ops { ...@@ -112,7 +114,7 @@ struct rpc_xprt_ops {
void (*set_port)(struct rpc_xprt *xprt, unsigned short port); void (*set_port)(struct rpc_xprt *xprt, unsigned short port);
void (*connect)(struct rpc_task *task); void (*connect)(struct rpc_task *task);
void * (*buf_alloc)(struct rpc_task *task, size_t size); void * (*buf_alloc)(struct rpc_task *task, size_t size);
void (*buf_free)(struct rpc_task *task); void (*buf_free)(void *buffer);
int (*send_request)(struct rpc_task *task); int (*send_request)(struct rpc_task *task);
void (*set_retrans_timeout)(struct rpc_task *task); void (*set_retrans_timeout)(struct rpc_task *task);
void (*timer)(struct rpc_task *task); void (*timer)(struct rpc_task *task);
...@@ -150,6 +152,7 @@ struct rpc_xprt { ...@@ -150,6 +152,7 @@ struct rpc_xprt {
unsigned long state; /* transport state */ unsigned long state; /* transport state */
unsigned char shutdown : 1, /* being shut down */ unsigned char shutdown : 1, /* being shut down */
resvport : 1; /* use a reserved port */ resvport : 1; /* use a reserved port */
unsigned int bind_index; /* bind function index */
/* /*
* Connection of transports * Connection of transports
......
...@@ -59,6 +59,8 @@ struct writeback_control { ...@@ -59,6 +59,8 @@ struct writeback_control {
unsigned for_reclaim:1; /* Invoked from the page allocator */ unsigned for_reclaim:1; /* Invoked from the page allocator */
unsigned for_writepages:1; /* This is a writepages() call */ unsigned for_writepages:1; /* This is a writepages() call */
unsigned range_cyclic:1; /* range_start is cyclic */ unsigned range_cyclic:1; /* range_start is cyclic */
void *fs_private; /* For use by ->writepages() */
}; };
/* /*
......
...@@ -9,7 +9,7 @@ obj-$(CONFIG_SUNRPC_GSS) += auth_gss/ ...@@ -9,7 +9,7 @@ obj-$(CONFIG_SUNRPC_GSS) += auth_gss/
sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
auth.o auth_null.o auth_unix.o \ auth.o auth_null.o auth_unix.o \
svc.o svcsock.o svcauth.o svcauth_unix.o \ svc.o svcsock.o svcauth.o svcauth_unix.o \
pmap_clnt.o timer.o xdr.o \ rpcb_clnt.o timer.o xdr.o \
sunrpc_syms.o cache.o rpc_pipe.o sunrpc_syms.o cache.o rpc_pipe.o
sunrpc-$(CONFIG_PROC_FS) += stats.o sunrpc-$(CONFIG_PROC_FS) += stats.o
sunrpc-$(CONFIG_SYSCTL) += sysctl.o sunrpc-$(CONFIG_SYSCTL) += sysctl.o
...@@ -123,9 +123,6 @@ spkm3_make_token(struct spkm3_ctx *ctx, ...@@ -123,9 +123,6 @@ spkm3_make_token(struct spkm3_ctx *ctx,
return GSS_S_COMPLETE; return GSS_S_COMPLETE;
out_err: out_err:
if (md5cksum.data)
kfree(md5cksum.data);
token->data = NULL; token->data = NULL;
token->len = 0; token->len = 0;
return GSS_S_FAILURE; return GSS_S_FAILURE;
...@@ -152,7 +149,7 @@ make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header, ...@@ -152,7 +149,7 @@ make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
switch (cksumtype) { switch (cksumtype) {
case CKSUMTYPE_HMAC_MD5: case CKSUMTYPE_HMAC_MD5:
cksumname = "md5"; cksumname = "hmac(md5)";
break; break;
default: default:
dprintk("RPC: spkm3_make_checksum:" dprintk("RPC: spkm3_make_checksum:"
...@@ -172,8 +169,12 @@ make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header, ...@@ -172,8 +169,12 @@ make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
if (err) if (err)
goto out; goto out;
err = crypto_hash_init(&desc);
if (err)
goto out;
sg_set_buf(sg, header, hdrlen); sg_set_buf(sg, header, hdrlen);
crypto_hash_update(&desc, sg, 1); crypto_hash_update(&desc, sg, sg->length);
xdr_process_buf(body, body_offset, body->len - body_offset, xdr_process_buf(body, body_offset, body->len - body_offset,
spkm3_checksummer, &desc); spkm3_checksummer, &desc);
...@@ -184,5 +185,3 @@ make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header, ...@@ -184,5 +185,3 @@ make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
return err ? GSS_S_FAILURE : 0; return err ? GSS_S_FAILURE : 0;
} }
EXPORT_SYMBOL(make_spkm3_checksum);
...@@ -36,8 +36,6 @@ ...@@ -36,8 +36,6 @@
#include <linux/sunrpc/metrics.h> #include <linux/sunrpc/metrics.h>
#define RPC_SLACK_SPACE (1024) /* total overkill */
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_CALL # define RPCDBG_FACILITY RPCDBG_CALL
#endif #endif
...@@ -747,21 +745,38 @@ call_reserveresult(struct rpc_task *task) ...@@ -747,21 +745,38 @@ call_reserveresult(struct rpc_task *task)
static void static void
call_allocate(struct rpc_task *task) call_allocate(struct rpc_task *task)
{ {
unsigned int slack = task->tk_auth->au_cslack;
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
struct rpc_xprt *xprt = task->tk_xprt; struct rpc_xprt *xprt = task->tk_xprt;
unsigned int bufsiz; struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
dprint_status(task); dprint_status(task);
task->tk_status = 0;
task->tk_action = call_bind; task->tk_action = call_bind;
if (req->rq_buffer) if (req->rq_buffer)
return; return;
/* FIXME: compute buffer requirements more exactly using if (proc->p_proc != 0) {
* auth->au_wslack */ BUG_ON(proc->p_arglen == 0);
bufsiz = task->tk_msg.rpc_proc->p_bufsiz + RPC_SLACK_SPACE; if (proc->p_decode != NULL)
BUG_ON(proc->p_replen == 0);
}
if (xprt->ops->buf_alloc(task, bufsiz << 1) != NULL) /*
* Calculate the size (in quads) of the RPC call
* and reply headers, and convert both values
* to byte sizes.
*/
req->rq_callsize = RPC_CALLHDRSIZE + (slack << 1) + proc->p_arglen;
req->rq_callsize <<= 2;
req->rq_rcvsize = RPC_REPHDRSIZE + slack + proc->p_replen;
req->rq_rcvsize <<= 2;
req->rq_buffer = xprt->ops->buf_alloc(task,
req->rq_callsize + req->rq_rcvsize);
if (req->rq_buffer != NULL)
return; return;
dprintk("RPC: %5u rpc_buffer allocation failed\n", task->tk_pid); dprintk("RPC: %5u rpc_buffer allocation failed\n", task->tk_pid);
...@@ -788,6 +803,17 @@ rpc_task_force_reencode(struct rpc_task *task) ...@@ -788,6 +803,17 @@ rpc_task_force_reencode(struct rpc_task *task)
task->tk_rqstp->rq_snd_buf.len = 0; task->tk_rqstp->rq_snd_buf.len = 0;
} }
static inline void
rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
{
buf->head[0].iov_base = start;
buf->head[0].iov_len = len;
buf->tail[0].iov_len = 0;
buf->page_len = 0;
buf->len = 0;
buf->buflen = len;
}
/* /*
* 3. Encode arguments of an RPC call * 3. Encode arguments of an RPC call
*/ */
...@@ -795,28 +821,17 @@ static void ...@@ -795,28 +821,17 @@ static void
call_encode(struct rpc_task *task) call_encode(struct rpc_task *task)
{ {
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
struct xdr_buf *sndbuf = &req->rq_snd_buf;
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
unsigned int bufsiz;
kxdrproc_t encode; kxdrproc_t encode;
__be32 *p; __be32 *p;
dprint_status(task); dprint_status(task);
/* Default buffer setup */ rpc_xdr_buf_init(&req->rq_snd_buf,
bufsiz = req->rq_bufsize >> 1; req->rq_buffer,
sndbuf->head[0].iov_base = (void *)req->rq_buffer; req->rq_callsize);
sndbuf->head[0].iov_len = bufsiz; rpc_xdr_buf_init(&req->rq_rcv_buf,
sndbuf->tail[0].iov_len = 0; (char *)req->rq_buffer + req->rq_callsize,
sndbuf->page_len = 0; req->rq_rcvsize);
sndbuf->len = 0;
sndbuf->buflen = bufsiz;
rcvbuf->head[0].iov_base = (void *)((char *)req->rq_buffer + bufsiz);
rcvbuf->head[0].iov_len = bufsiz;
rcvbuf->tail[0].iov_len = 0;
rcvbuf->page_len = 0;
rcvbuf->len = 0;
rcvbuf->buflen = bufsiz;
/* Encode header and provided arguments */ /* Encode header and provided arguments */
encode = task->tk_msg.rpc_proc->p_encode; encode = task->tk_msg.rpc_proc->p_encode;
...@@ -887,9 +902,11 @@ call_bind_status(struct rpc_task *task) ...@@ -887,9 +902,11 @@ call_bind_status(struct rpc_task *task)
task->tk_pid); task->tk_pid);
break; break;
case -EPROTONOSUPPORT: case -EPROTONOSUPPORT:
dprintk("RPC: %5u remote rpcbind version 2 unavailable\n", dprintk("RPC: %5u remote rpcbind version unavailable, retrying\n",
task->tk_pid); task->tk_pid);
break; task->tk_status = 0;
task->tk_action = call_bind;
return;
default: default:
dprintk("RPC: %5u unrecognized rpcbind error (%d)\n", dprintk("RPC: %5u unrecognized rpcbind error (%d)\n",
task->tk_pid, -task->tk_status); task->tk_pid, -task->tk_status);
......
/*
* linux/net/sunrpc/pmap_clnt.c
*
* In-kernel RPC portmapper client.
*
* Portmapper supports version 2 of the rpcbind protocol (RFC 1833).
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/uio.h>
#include <linux/in.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/sched.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_PMAP
#endif
#define PMAP_SET 1
#define PMAP_UNSET 2
#define PMAP_GETPORT 3
struct portmap_args {
u32 pm_prog;
u32 pm_vers;
u32 pm_prot;
unsigned short pm_port;
struct rpc_xprt * pm_xprt;
};
static struct rpc_procinfo pmap_procedures[];
static struct rpc_clnt * pmap_create(char *, struct sockaddr_in *, int, int);
static void pmap_getport_done(struct rpc_task *, void *);
static struct rpc_program pmap_program;
static void pmap_getport_prepare(struct rpc_task *task, void *calldata)
{
struct portmap_args *map = calldata;
struct rpc_message msg = {
.rpc_proc = &pmap_procedures[PMAP_GETPORT],
.rpc_argp = map,
.rpc_resp = &map->pm_port,
};
rpc_call_setup(task, &msg, 0);
}
static inline struct portmap_args *pmap_map_alloc(void)
{
return kmalloc(sizeof(struct portmap_args), GFP_NOFS);
}
static inline void pmap_map_free(struct portmap_args *map)
{
kfree(map);
}
static void pmap_map_release(void *data)
{
struct portmap_args *map = data;
xprt_put(map->pm_xprt);
pmap_map_free(map);
}
static const struct rpc_call_ops pmap_getport_ops = {
.rpc_call_prepare = pmap_getport_prepare,
.rpc_call_done = pmap_getport_done,
.rpc_release = pmap_map_release,
};
static inline void pmap_wake_portmap_waiters(struct rpc_xprt *xprt, int status)
{
xprt_clear_binding(xprt);
rpc_wake_up_status(&xprt->binding, status);
}
/**
* rpc_getport - obtain the port for a given RPC service on a given host
* @task: task that is waiting for portmapper request
*
* This one can be called for an ongoing RPC request, and can be used in
* an async (rpciod) context.
*/
void rpc_getport(struct rpc_task *task)
{
struct rpc_clnt *clnt = task->tk_client;
struct rpc_xprt *xprt = task->tk_xprt;
struct sockaddr_in addr;
struct portmap_args *map;
struct rpc_clnt *pmap_clnt;
struct rpc_task *child;
int status;
dprintk("RPC: %5u rpc_getport(%s, %u, %u, %d)\n",
task->tk_pid, clnt->cl_server,
clnt->cl_prog, clnt->cl_vers, xprt->prot);
/* Autobind on cloned rpc clients is discouraged */
BUG_ON(clnt->cl_parent != clnt);
status = -EACCES; /* tell caller to check again */
if (xprt_test_and_set_binding(xprt))
goto bailout_nowake;
/* Put self on queue before sending rpcbind request, in case
* pmap_getport_done completes before we return from rpc_run_task */
rpc_sleep_on(&xprt->binding, task, NULL, NULL);
/* Someone else may have bound if we slept */
status = 0;
if (xprt_bound(xprt))
goto bailout_nofree;
status = -ENOMEM;
map = pmap_map_alloc();
if (!map)
goto bailout_nofree;
map->pm_prog = clnt->cl_prog;
map->pm_vers = clnt->cl_vers;
map->pm_prot = xprt->prot;
map->pm_port = 0;
map->pm_xprt = xprt_get(xprt);
rpc_peeraddr(clnt, (struct sockaddr *) &addr, sizeof(addr));
pmap_clnt = pmap_create(clnt->cl_server, &addr, map->pm_prot, 0);
status = PTR_ERR(pmap_clnt);
if (IS_ERR(pmap_clnt))
goto bailout;
status = -EIO;
child = rpc_run_task(pmap_clnt, RPC_TASK_ASYNC, &pmap_getport_ops, map);
if (IS_ERR(child))
goto bailout_nofree;
rpc_put_task(child);
task->tk_xprt->stat.bind_count++;
return;
bailout:
pmap_map_free(map);
xprt_put(xprt);
bailout_nofree:
pmap_wake_portmap_waiters(xprt, status);
bailout_nowake:
task->tk_status = status;
}
#ifdef CONFIG_ROOT_NFS
/**
* rpc_getport_external - obtain the port for a given RPC service on a given host
* @sin: address of remote peer
* @prog: RPC program number to bind
* @vers: RPC version number to bind
* @prot: transport protocol to use to make this request
*
* This one is called from outside the RPC client in a synchronous task context.
*/
int rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
{
struct portmap_args map = {
.pm_prog = prog,
.pm_vers = vers,
.pm_prot = prot,
.pm_port = 0
};
struct rpc_message msg = {
.rpc_proc = &pmap_procedures[PMAP_GETPORT],
.rpc_argp = &map,
.rpc_resp = &map.pm_port,
};
struct rpc_clnt *pmap_clnt;
char hostname[32];
int status;
dprintk("RPC: rpc_getport_external(%u.%u.%u.%u, %u, %u, %d)\n",
NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr));
pmap_clnt = pmap_create(hostname, sin, prot, 0);
if (IS_ERR(pmap_clnt))
return PTR_ERR(pmap_clnt);
/* Setup the call info struct */
status = rpc_call_sync(pmap_clnt, &msg, 0);
if (status >= 0) {
if (map.pm_port != 0)
return map.pm_port;
status = -EACCES;
}
return status;
}
#endif
/*
* Portmapper child task invokes this callback via tk_exit.
*/
static void pmap_getport_done(struct rpc_task *child, void *data)
{
struct portmap_args *map = data;
struct rpc_xprt *xprt = map->pm_xprt;
int status = child->tk_status;
if (status < 0) {
/* Portmapper not available */
xprt->ops->set_port(xprt, 0);
} else if (map->pm_port == 0) {
/* Requested RPC service wasn't registered */
xprt->ops->set_port(xprt, 0);
status = -EACCES;
} else {
/* Succeeded */
xprt->ops->set_port(xprt, map->pm_port);
xprt_set_bound(xprt);
status = 0;
}
dprintk("RPC: %5u pmap_getport_done(status %d, port %u)\n",
child->tk_pid, status, map->pm_port);
pmap_wake_portmap_waiters(xprt, status);
}
/**
* rpc_register - set or unset a port registration with the local portmapper
* @prog: RPC program number to bind
* @vers: RPC version number to bind
* @prot: transport protocol to use to make this request
* @port: port value to register
* @okay: result code
*
* port == 0 means unregister, port != 0 means register.
*/
int rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
{
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct portmap_args map = {
.pm_prog = prog,
.pm_vers = vers,
.pm_prot = prot,
.pm_port = port,
};
struct rpc_message msg = {
.rpc_proc = &pmap_procedures[port ? PMAP_SET : PMAP_UNSET],
.rpc_argp = &map,
.rpc_resp = okay,
};
struct rpc_clnt *pmap_clnt;
int error = 0;
dprintk("RPC: registering (%u, %u, %d, %u) with portmapper.\n",
prog, vers, prot, port);
pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP, 1);
if (IS_ERR(pmap_clnt)) {
error = PTR_ERR(pmap_clnt);
dprintk("RPC: couldn't create pmap client. Error = %d\n",
error);
return error;
}
error = rpc_call_sync(pmap_clnt, &msg, 0);
if (error < 0) {
printk(KERN_WARNING
"RPC: failed to contact portmap (errno %d).\n",
error);
}
dprintk("RPC: registration status %d/%d\n", error, *okay);
/* Client deleted automatically because cl_oneshot == 1 */
return error;
}
static struct rpc_clnt *pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto, int privileged)
{
struct rpc_create_args args = {
.protocol = proto,
.address = (struct sockaddr *)srvaddr,
.addrsize = sizeof(*srvaddr),
.servername = hostname,
.program = &pmap_program,
.version = RPC_PMAP_VERSION,
.authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_ONESHOT |
RPC_CLNT_CREATE_NOPING),
};
srvaddr->sin_port = htons(RPC_PMAP_PORT);
if (!privileged)
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
return rpc_create(&args);
}
/*
* XDR encode/decode functions for PMAP
*/
static int xdr_encode_mapping(struct rpc_rqst *req, __be32 *p, struct portmap_args *map)
{
dprintk("RPC: xdr_encode_mapping(%u, %u, %u, %u)\n",
map->pm_prog, map->pm_vers,
map->pm_prot, map->pm_port);
*p++ = htonl(map->pm_prog);
*p++ = htonl(map->pm_vers);
*p++ = htonl(map->pm_prot);
*p++ = htonl(map->pm_port);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int xdr_decode_port(struct rpc_rqst *req, __be32 *p, unsigned short *portp)
{
*portp = (unsigned short) ntohl(*p++);
return 0;
}
static int xdr_decode_bool(struct rpc_rqst *req, __be32 *p, unsigned int *boolp)
{
*boolp = (unsigned int) ntohl(*p++);
return 0;
}
static struct rpc_procinfo pmap_procedures[] = {
[PMAP_SET] = {
.p_proc = PMAP_SET,
.p_encode = (kxdrproc_t) xdr_encode_mapping,
.p_decode = (kxdrproc_t) xdr_decode_bool,
.p_bufsiz = 4,
.p_count = 1,
.p_statidx = PMAP_SET,
.p_name = "SET",
},
[PMAP_UNSET] = {
.p_proc = PMAP_UNSET,
.p_encode = (kxdrproc_t) xdr_encode_mapping,
.p_decode = (kxdrproc_t) xdr_decode_bool,
.p_bufsiz = 4,
.p_count = 1,
.p_statidx = PMAP_UNSET,
.p_name = "UNSET",
},
[PMAP_GETPORT] = {
.p_proc = PMAP_GETPORT,
.p_encode = (kxdrproc_t) xdr_encode_mapping,
.p_decode = (kxdrproc_t) xdr_decode_port,
.p_bufsiz = 4,
.p_count = 1,
.p_statidx = PMAP_GETPORT,
.p_name = "GETPORT",
},
};
static struct rpc_version pmap_version2 = {
.number = 2,
.nrprocs = 4,
.procs = pmap_procedures
};
static struct rpc_version * pmap_version[] = {
NULL,
NULL,
&pmap_version2
};
static struct rpc_stat pmap_stats;
static struct rpc_program pmap_program = {
.name = "portmap",
.number = RPC_PMAP_PROGRAM,
.nrvers = ARRAY_SIZE(pmap_version),
.version = pmap_version,
.stats = &pmap_stats,
};
/*
* In-kernel rpcbind client supporting versions 2, 3, and 4 of the rpcbind
* protocol
*
* Based on RFC 1833: "Binding Protocols for ONC RPC Version 2" and
* RFC 3530: "Network File System (NFS) version 4 Protocol"
*
* Original: Gilles Quillard, Bull Open Source, 2005 <gilles.quillard@bull.net>
* Updated: Chuck Lever, Oracle Corporation, 2007 <chuck.lever@oracle.com>
*
* Descended from net/sunrpc/pmap_clnt.c,
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/sched.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_BIND
#endif
#define RPCBIND_PROGRAM (100000u)
#define RPCBIND_PORT (111u)
enum {
RPCBPROC_NULL,
RPCBPROC_SET,
RPCBPROC_UNSET,
RPCBPROC_GETPORT,
RPCBPROC_GETADDR = 3, /* alias for GETPORT */
RPCBPROC_DUMP,
RPCBPROC_CALLIT,
RPCBPROC_BCAST = 5, /* alias for CALLIT */
RPCBPROC_GETTIME,
RPCBPROC_UADDR2TADDR,
RPCBPROC_TADDR2UADDR,
RPCBPROC_GETVERSADDR,
RPCBPROC_INDIRECT,
RPCBPROC_GETADDRLIST,
RPCBPROC_GETSTAT,
};
#define RPCB_HIGHPROC_2 RPCBPROC_CALLIT
#define RPCB_HIGHPROC_3 RPCBPROC_TADDR2UADDR
#define RPCB_HIGHPROC_4 RPCBPROC_GETSTAT
/*
* r_addr
*
* Quoting RFC 3530, section 2.2:
*
* For TCP over IPv4 and for UDP over IPv4, the format of r_addr is the
* US-ASCII string:
*
* h1.h2.h3.h4.p1.p2
*
* The prefix, "h1.h2.h3.h4", is the standard textual form for
* representing an IPv4 address, which is always four octets long.
* Assuming big-endian ordering, h1, h2, h3, and h4, are respectively,
* the first through fourth octets each converted to ASCII-decimal.
* Assuming big-endian ordering, p1 and p2 are, respectively, the first
* and second octets each converted to ASCII-decimal. For example, if a
* host, in big-endian order, has an address of 0x0A010307 and there is
* a service listening on, in big endian order, port 0x020F (decimal
* 527), then the complete universal address is "10.1.3.7.2.15".
*
* ...
*
* For TCP over IPv6 and for UDP over IPv6, the format of r_addr is the
* US-ASCII string:
*
* x1:x2:x3:x4:x5:x6:x7:x8.p1.p2
*
* The suffix "p1.p2" is the service port, and is computed the same way
* as with universal addresses for TCP and UDP over IPv4. The prefix,
* "x1:x2:x3:x4:x5:x6:x7:x8", is the standard textual form for
* representing an IPv6 address as defined in Section 2.2 of [RFC2373].
* Additionally, the two alternative forms specified in Section 2.2 of
* [RFC2373] are also acceptable.
*
* XXX: Currently this implementation does not explicitly convert the
* stored address to US-ASCII on non-ASCII systems.
*/
#define RPCB_MAXADDRLEN (128u)
/*
* r_netid
*
* Quoting RFC 3530, section 2.2:
*
* For TCP over IPv4 the value of r_netid is the string "tcp". For UDP
* over IPv4 the value of r_netid is the string "udp".
*
* ...
*
* For TCP over IPv6 the value of r_netid is the string "tcp6". For UDP
* over IPv6 the value of r_netid is the string "udp6".
*/
#define RPCB_NETID_UDP "\165\144\160" /* "udp" */
#define RPCB_NETID_TCP "\164\143\160" /* "tcp" */
#define RPCB_NETID_UDP6 "\165\144\160\066" /* "udp6" */
#define RPCB_NETID_TCP6 "\164\143\160\066" /* "tcp6" */
#define RPCB_MAXNETIDLEN (4u)
/*
* r_owner
*
* The "owner" is allowed to unset a service in the rpcbind database.
* We always use the following (arbitrary) fixed string.
*/
#define RPCB_OWNER_STRING "rpcb"
#define RPCB_MAXOWNERLEN sizeof(RPCB_OWNER_STRING)
static void rpcb_getport_done(struct rpc_task *, void *);
extern struct rpc_program rpcb_program;
struct rpcbind_args {
struct rpc_xprt * r_xprt;
u32 r_prog;
u32 r_vers;
u32 r_prot;
unsigned short r_port;
char * r_netid;
char r_addr[RPCB_MAXADDRLEN];
char * r_owner;
};
static struct rpc_procinfo rpcb_procedures2[];
static struct rpc_procinfo rpcb_procedures3[];
static struct rpcb_info {
int rpc_vers;
struct rpc_procinfo * rpc_proc;
} rpcb_next_version[];
static void rpcb_getport_prepare(struct rpc_task *task, void *calldata)
{
struct rpcbind_args *map = calldata;
struct rpc_xprt *xprt = map->r_xprt;
struct rpc_message msg = {
.rpc_proc = rpcb_next_version[xprt->bind_index].rpc_proc,
.rpc_argp = map,
.rpc_resp = &map->r_port,
};
rpc_call_setup(task, &msg, 0);
}
static void rpcb_map_release(void *data)
{
struct rpcbind_args *map = data;
xprt_put(map->r_xprt);
kfree(map);
}
static const struct rpc_call_ops rpcb_getport_ops = {
.rpc_call_prepare = rpcb_getport_prepare,
.rpc_call_done = rpcb_getport_done,
.rpc_release = rpcb_map_release,
};
static void rpcb_wake_rpcbind_waiters(struct rpc_xprt *xprt, int status)
{
xprt_clear_binding(xprt);
rpc_wake_up_status(&xprt->binding, status);
}
static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
int proto, int version, int privileged)
{
struct rpc_create_args args = {
.protocol = proto,
.address = srvaddr,
.addrsize = sizeof(struct sockaddr_in),
.servername = hostname,
.program = &rpcb_program,
.version = version,
.authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_ONESHOT |
RPC_CLNT_CREATE_NOPING),
};
((struct sockaddr_in *)srvaddr)->sin_port = htons(RPCBIND_PORT);
if (!privileged)
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
return rpc_create(&args);
}
/**
* rpcb_register - set or unset a port registration with the local rpcbind svc
* @prog: RPC program number to bind
* @vers: RPC version number to bind
* @prot: transport protocol to use to make this request
* @port: port value to register
* @okay: result code
*
* port == 0 means unregister, port != 0 means register.
*
* This routine supports only rpcbind version 2.
*/
int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
{
struct sockaddr_in sin = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct rpcbind_args map = {
.r_prog = prog,
.r_vers = vers,
.r_prot = prot,
.r_port = port,
};
struct rpc_message msg = {
.rpc_proc = &rpcb_procedures2[port ?
RPCBPROC_SET : RPCBPROC_UNSET],
.rpc_argp = &map,
.rpc_resp = okay,
};
struct rpc_clnt *rpcb_clnt;
int error = 0;
dprintk("RPC: %sregistering (%u, %u, %d, %u) with local "
"rpcbind\n", (port ? "" : "un"),
prog, vers, prot, port);
rpcb_clnt = rpcb_create("localhost", (struct sockaddr *) &sin,
IPPROTO_UDP, 2, 1);
if (IS_ERR(rpcb_clnt))
return PTR_ERR(rpcb_clnt);
error = rpc_call_sync(rpcb_clnt, &msg, 0);
if (error < 0)
printk(KERN_WARNING "RPC: failed to contact local rpcbind "
"server (errno %d).\n", -error);
dprintk("RPC: registration status %d/%d\n", error, *okay);
return error;
}
#ifdef CONFIG_ROOT_NFS
/**
* rpcb_getport_external - obtain the port for an RPC service on a given host
* @sin: address of remote peer
* @prog: RPC program number to bind
* @vers: RPC version number to bind
* @prot: transport protocol to use to make this request
*
* Called from outside the RPC client in a synchronous task context.
*
* For now, this supports only version 2 queries, but is used only by
* mount_clnt for NFS_ROOT.
*/
int rpcb_getport_external(struct sockaddr_in *sin, __u32 prog,
__u32 vers, int prot)
{
struct rpcbind_args map = {
.r_prog = prog,
.r_vers = vers,
.r_prot = prot,
.r_port = 0,
};
struct rpc_message msg = {
.rpc_proc = &rpcb_procedures2[RPCBPROC_GETPORT],
.rpc_argp = &map,
.rpc_resp = &map.r_port,
};
struct rpc_clnt *rpcb_clnt;
char hostname[40];
int status;
dprintk("RPC: rpcb_getport_external(%u.%u.%u.%u, %u, %u, %d)\n",
NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr));
rpcb_clnt = rpcb_create(hostname, (struct sockaddr *)sin, prot, 2, 0);
if (IS_ERR(rpcb_clnt))
return PTR_ERR(rpcb_clnt);
status = rpc_call_sync(rpcb_clnt, &msg, 0);
if (status >= 0) {
if (map.r_port != 0)
return map.r_port;
status = -EACCES;
}
return status;
}
#endif
/**
* rpcb_getport - obtain the port for a given RPC service on a given host
* @task: task that is waiting for portmapper request
*
* This one can be called for an ongoing RPC request, and can be used in
* an async (rpciod) context.
*/
void rpcb_getport(struct rpc_task *task)
{
struct rpc_clnt *clnt = task->tk_client;
int bind_version;
struct rpc_xprt *xprt = task->tk_xprt;
struct rpc_clnt *rpcb_clnt;
static struct rpcbind_args *map;
struct rpc_task *child;
struct sockaddr addr;
int status;
dprintk("RPC: %5u rpcb_getport(%s, %u, %u, %d)\n",
task->tk_pid, clnt->cl_server,
clnt->cl_prog, clnt->cl_vers, xprt->prot);
/* Autobind on cloned rpc clients is discouraged */
BUG_ON(clnt->cl_parent != clnt);
if (xprt_test_and_set_binding(xprt)) {
status = -EACCES; /* tell caller to check again */
dprintk("RPC: %5u rpcb_getport waiting for another binder\n",
task->tk_pid);
goto bailout_nowake;
}
/* Put self on queue before sending rpcbind request, in case
* rpcb_getport_done completes before we return from rpc_run_task */
rpc_sleep_on(&xprt->binding, task, NULL, NULL);
/* Someone else may have bound if we slept */
if (xprt_bound(xprt)) {
status = 0;
dprintk("RPC: %5u rpcb_getport already bound\n", task->tk_pid);
goto bailout_nofree;
}
if (rpcb_next_version[xprt->bind_index].rpc_proc == NULL) {
xprt->bind_index = 0;
status = -EACCES; /* tell caller to try again later */
dprintk("RPC: %5u rpcb_getport no more getport versions "
"available\n", task->tk_pid);
goto bailout_nofree;
}
bind_version = rpcb_next_version[xprt->bind_index].rpc_vers;
dprintk("RPC: %5u rpcb_getport trying rpcbind version %u\n",
task->tk_pid, bind_version);
map = kzalloc(sizeof(struct rpcbind_args), GFP_ATOMIC);
if (!map) {
status = -ENOMEM;
dprintk("RPC: %5u rpcb_getport no memory available\n",
task->tk_pid);
goto bailout_nofree;
}
map->r_prog = clnt->cl_prog;
map->r_vers = clnt->cl_vers;
map->r_prot = xprt->prot;
map->r_port = 0;
map->r_xprt = xprt_get(xprt);
map->r_netid = (xprt->prot == IPPROTO_TCP) ? RPCB_NETID_TCP :
RPCB_NETID_UDP;
memcpy(&map->r_addr, rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR),
sizeof(map->r_addr));
map->r_owner = RPCB_OWNER_STRING; /* ignored for GETADDR */
rpc_peeraddr(clnt, (void *)&addr, sizeof(addr));
rpcb_clnt = rpcb_create(clnt->cl_server, &addr, xprt->prot, bind_version, 0);
if (IS_ERR(rpcb_clnt)) {
status = PTR_ERR(rpcb_clnt);
dprintk("RPC: %5u rpcb_getport rpcb_create failed, error %ld\n",
task->tk_pid, PTR_ERR(rpcb_clnt));
goto bailout;
}
child = rpc_run_task(rpcb_clnt, RPC_TASK_ASYNC, &rpcb_getport_ops, map);
if (IS_ERR(child)) {
status = -EIO;
dprintk("RPC: %5u rpcb_getport rpc_run_task failed\n",
task->tk_pid);
goto bailout_nofree;
}
rpc_put_task(child);
task->tk_xprt->stat.bind_count++;
return;
bailout:
kfree(map);
xprt_put(xprt);
bailout_nofree:
rpcb_wake_rpcbind_waiters(xprt, status);
bailout_nowake:
task->tk_status = status;
}
/*
* Rpcbind child task calls this callback via tk_exit.
*/
static void rpcb_getport_done(struct rpc_task *child, void *data)
{
struct rpcbind_args *map = data;
struct rpc_xprt *xprt = map->r_xprt;
int status = child->tk_status;
/* rpcbind server doesn't support this rpcbind protocol version */
if (status == -EPROTONOSUPPORT)
xprt->bind_index++;
if (status < 0) {
/* rpcbind server not available on remote host? */
xprt->ops->set_port(xprt, 0);
} else if (map->r_port == 0) {
/* Requested RPC service wasn't registered on remote host */
xprt->ops->set_port(xprt, 0);
status = -EACCES;
} else {
/* Succeeded */
xprt->ops->set_port(xprt, map->r_port);
xprt_set_bound(xprt);
status = 0;
}
dprintk("RPC: %5u rpcb_getport_done(status %d, port %u)\n",
child->tk_pid, status, map->r_port);
rpcb_wake_rpcbind_waiters(xprt, status);
}
static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p,
struct rpcbind_args *rpcb)
{
dprintk("RPC: rpcb_encode_mapping(%u, %u, %d, %u)\n",
rpcb->r_prog, rpcb->r_vers, rpcb->r_prot, rpcb->r_port);
*p++ = htonl(rpcb->r_prog);
*p++ = htonl(rpcb->r_vers);
*p++ = htonl(rpcb->r_prot);
*p++ = htonl(rpcb->r_port);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int rpcb_decode_getport(struct rpc_rqst *req, __be32 *p,
unsigned short *portp)
{
*portp = (unsigned short) ntohl(*p++);
dprintk("RPC: rpcb_decode_getport result %u\n",
*portp);
return 0;
}
static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p,
unsigned int *boolp)
{
*boolp = (unsigned int) ntohl(*p++);
dprintk("RPC: rpcb_decode_set result %u\n",
*boolp);
return 0;
}
static int rpcb_encode_getaddr(struct rpc_rqst *req, __be32 *p,
struct rpcbind_args *rpcb)
{
dprintk("RPC: rpcb_encode_getaddr(%u, %u, %s)\n",
rpcb->r_prog, rpcb->r_vers, rpcb->r_addr);
*p++ = htonl(rpcb->r_prog);
*p++ = htonl(rpcb->r_vers);
p = xdr_encode_string(p, rpcb->r_netid);
p = xdr_encode_string(p, rpcb->r_addr);
p = xdr_encode_string(p, rpcb->r_owner);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int rpcb_decode_getaddr(struct rpc_rqst *req, __be32 *p,
unsigned short *portp)
{
char *addr;
int addr_len, c, i, f, first, val;
*portp = 0;
addr_len = (unsigned int) ntohl(*p++);
if (addr_len > RPCB_MAXADDRLEN) /* sanity */
return -EINVAL;
dprintk("RPC: rpcb_decode_getaddr returned string: '%s'\n",
(char *) p);
addr = (char *)p;
val = 0;
first = 1;
f = 1;
for (i = addr_len - 1; i > 0; i--) {
c = addr[i];
if (c >= '0' && c <= '9') {
val += (c - '0') * f;
f *= 10;
} else if (c == '.') {
if (first) {
*portp = val;
val = first = 0;
f = 1;
} else {
*portp |= (val << 8);
break;
}
}
}
dprintk("RPC: rpcb_decode_getaddr port=%u\n", *portp);
return 0;
}
#define RPCB_program_sz (1u)
#define RPCB_version_sz (1u)
#define RPCB_protocol_sz (1u)
#define RPCB_port_sz (1u)
#define RPCB_boolean_sz (1u)
#define RPCB_netid_sz (1+XDR_QUADLEN(RPCB_MAXNETIDLEN))
#define RPCB_addr_sz (1+XDR_QUADLEN(RPCB_MAXADDRLEN))
#define RPCB_ownerstring_sz (1+XDR_QUADLEN(RPCB_MAXOWNERLEN))
#define RPCB_mappingargs_sz RPCB_program_sz+RPCB_version_sz+ \
RPCB_protocol_sz+RPCB_port_sz
#define RPCB_getaddrargs_sz RPCB_program_sz+RPCB_version_sz+ \
RPCB_netid_sz+RPCB_addr_sz+ \
RPCB_ownerstring_sz
#define RPCB_setres_sz RPCB_boolean_sz
#define RPCB_getportres_sz RPCB_port_sz
/*
* Note that RFC 1833 does not put any size restrictions on the
* address string returned by the remote rpcbind database.
*/
#define RPCB_getaddrres_sz RPCB_addr_sz
#define PROC(proc, argtype, restype) \
[RPCBPROC_##proc] = { \
.p_proc = RPCBPROC_##proc, \
.p_encode = (kxdrproc_t) rpcb_encode_##argtype, \
.p_decode = (kxdrproc_t) rpcb_decode_##restype, \
.p_arglen = RPCB_##argtype##args_sz, \
.p_replen = RPCB_##restype##res_sz, \
.p_statidx = RPCBPROC_##proc, \
.p_timer = 0, \
.p_name = #proc, \
}
/*
* Not all rpcbind procedures described in RFC 1833 are implemented
* since the Linux kernel RPC code requires only these.
*/
static struct rpc_procinfo rpcb_procedures2[] = {
PROC(SET, mapping, set),
PROC(UNSET, mapping, set),
PROC(GETADDR, mapping, getport),
};
static struct rpc_procinfo rpcb_procedures3[] = {
PROC(SET, mapping, set),
PROC(UNSET, mapping, set),
PROC(GETADDR, getaddr, getaddr),
};
static struct rpc_procinfo rpcb_procedures4[] = {
PROC(SET, mapping, set),
PROC(UNSET, mapping, set),
PROC(GETVERSADDR, getaddr, getaddr),
};
static struct rpcb_info rpcb_next_version[] = {
#ifdef CONFIG_SUNRPC_BIND34
{ 4, &rpcb_procedures4[RPCBPROC_GETVERSADDR] },
{ 3, &rpcb_procedures3[RPCBPROC_GETADDR] },
#endif
{ 2, &rpcb_procedures2[RPCBPROC_GETPORT] },
{ 0, NULL },
};
static struct rpc_version rpcb_version2 = {
.number = 2,
.nrprocs = RPCB_HIGHPROC_2,
.procs = rpcb_procedures2
};
static struct rpc_version rpcb_version3 = {
.number = 3,
.nrprocs = RPCB_HIGHPROC_3,
.procs = rpcb_procedures3
};
static struct rpc_version rpcb_version4 = {
.number = 4,
.nrprocs = RPCB_HIGHPROC_4,
.procs = rpcb_procedures4
};
static struct rpc_version *rpcb_version[] = {
NULL,
NULL,
&rpcb_version2,
&rpcb_version3,
&rpcb_version4
};
static struct rpc_stat rpcb_stats;
struct rpc_program rpcb_program = {
.name = "rpcbind",
.number = RPCBIND_PROGRAM,
.nrvers = ARRAY_SIZE(rpcb_version),
.version = rpcb_version,
.stats = &rpcb_stats,
};
...@@ -741,50 +741,53 @@ static void rpc_async_schedule(struct work_struct *work) ...@@ -741,50 +741,53 @@ static void rpc_async_schedule(struct work_struct *work)
* @task: RPC task that will use this buffer * @task: RPC task that will use this buffer
* @size: requested byte size * @size: requested byte size
* *
* We try to ensure that some NFS reads and writes can always proceed * To prevent rpciod from hanging, this allocator never sleeps,
* by using a mempool when allocating 'small' buffers. * returning NULL if the request cannot be serviced immediately.
* The caller can arrange to sleep in a way that is safe for rpciod.
*
* Most requests are 'small' (under 2KiB) and can be serviced from a
* mempool, ensuring that NFS reads and writes can always proceed,
* and that there is good locality of reference for these buffers.
*
* In order to avoid memory starvation triggering more writebacks of * In order to avoid memory starvation triggering more writebacks of
* NFS requests, we use GFP_NOFS rather than GFP_KERNEL. * NFS requests, we avoid using GFP_KERNEL.
*/ */
void * rpc_malloc(struct rpc_task *task, size_t size) void *rpc_malloc(struct rpc_task *task, size_t size)
{ {
struct rpc_rqst *req = task->tk_rqstp; size_t *buf;
gfp_t gfp; gfp_t gfp = RPC_IS_SWAPPER(task) ? GFP_ATOMIC : GFP_NOWAIT;
if (task->tk_flags & RPC_TASK_SWAPPER) size += sizeof(size_t);
gfp = GFP_ATOMIC; if (size <= RPC_BUFFER_MAXSIZE)
buf = mempool_alloc(rpc_buffer_mempool, gfp);
else else
gfp = GFP_NOFS; buf = kmalloc(size, gfp);
*buf = size;
if (size > RPC_BUFFER_MAXSIZE) { dprintk("RPC: %5u allocated buffer of size %u at %p\n",
req->rq_buffer = kmalloc(size, gfp); task->tk_pid, size, buf);
if (req->rq_buffer) return (void *) ++buf;
req->rq_bufsize = size;
} else {
req->rq_buffer = mempool_alloc(rpc_buffer_mempool, gfp);
if (req->rq_buffer)
req->rq_bufsize = RPC_BUFFER_MAXSIZE;
}
return req->rq_buffer;
} }
/** /**
* rpc_free - free buffer allocated via rpc_malloc * rpc_free - free buffer allocated via rpc_malloc
* @task: RPC task with a buffer to be freed * @buffer: buffer to free
* *
*/ */
void rpc_free(struct rpc_task *task) void rpc_free(void *buffer)
{ {
struct rpc_rqst *req = task->tk_rqstp; size_t size, *buf = (size_t *) buffer;
if (req->rq_buffer) { if (!buffer)
if (req->rq_bufsize == RPC_BUFFER_MAXSIZE) return;
mempool_free(req->rq_buffer, rpc_buffer_mempool); size = *buf;
else buf--;
kfree(req->rq_buffer);
req->rq_buffer = NULL; dprintk("RPC: freeing buffer of size %u at %p\n",
req->rq_bufsize = 0; size, buf);
} if (size <= RPC_BUFFER_MAXSIZE)
mempool_free(buf, rpc_buffer_mempool);
else
kfree(buf);
} }
/* /*
......
...@@ -757,7 +757,7 @@ svc_register(struct svc_serv *serv, int proto, unsigned short port) ...@@ -757,7 +757,7 @@ svc_register(struct svc_serv *serv, int proto, unsigned short port)
if (progp->pg_vers[i]->vs_hidden) if (progp->pg_vers[i]->vs_hidden)
continue; continue;
error = rpc_register(progp->pg_prog, i, proto, port, &dummy); error = rpcb_register(progp->pg_prog, i, proto, port, &dummy);
if (error < 0) if (error < 0)
break; break;
if (port && !dummy) { if (port && !dummy) {
......
...@@ -823,7 +823,6 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) ...@@ -823,7 +823,6 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
req->rq_task = task; req->rq_task = task;
req->rq_xprt = xprt; req->rq_xprt = xprt;
req->rq_buffer = NULL; req->rq_buffer = NULL;
req->rq_bufsize = 0;
req->rq_xid = xprt_alloc_xid(xprt); req->rq_xid = xprt_alloc_xid(xprt);
req->rq_release_snd_buf = NULL; req->rq_release_snd_buf = NULL;
xprt_reset_majortimeo(req); xprt_reset_majortimeo(req);
...@@ -855,7 +854,7 @@ void xprt_release(struct rpc_task *task) ...@@ -855,7 +854,7 @@ void xprt_release(struct rpc_task *task)
mod_timer(&xprt->timer, mod_timer(&xprt->timer,
xprt->last_used + xprt->idle_timeout); xprt->last_used + xprt->idle_timeout);
spin_unlock_bh(&xprt->transport_lock); spin_unlock_bh(&xprt->transport_lock);
xprt->ops->buf_free(task); xprt->ops->buf_free(req->rq_buffer);
task->tk_rqstp = NULL; task->tk_rqstp = NULL;
if (req->rq_release_snd_buf) if (req->rq_release_snd_buf)
req->rq_release_snd_buf(req); req->rq_release_snd_buf(req);
...@@ -928,6 +927,7 @@ struct rpc_xprt *xprt_create_transport(int proto, struct sockaddr *ap, size_t si ...@@ -928,6 +927,7 @@ struct rpc_xprt *xprt_create_transport(int proto, struct sockaddr *ap, size_t si
xprt->timer.data = (unsigned long) xprt; xprt->timer.data = (unsigned long) xprt;
xprt->last_used = jiffies; xprt->last_used = jiffies;
xprt->cwnd = RPC_INITCWND; xprt->cwnd = RPC_INITCWND;
xprt->bind_index = 0;
rpc_init_wait_queue(&xprt->binding, "xprt_binding"); rpc_init_wait_queue(&xprt->binding, "xprt_binding");
rpc_init_wait_queue(&xprt->pending, "xprt_pending"); rpc_init_wait_queue(&xprt->pending, "xprt_pending");
......
...@@ -1476,7 +1476,7 @@ static struct rpc_xprt_ops xs_udp_ops = { ...@@ -1476,7 +1476,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
.set_buffer_size = xs_udp_set_buffer_size, .set_buffer_size = xs_udp_set_buffer_size,
.reserve_xprt = xprt_reserve_xprt_cong, .reserve_xprt = xprt_reserve_xprt_cong,
.release_xprt = xprt_release_xprt_cong, .release_xprt = xprt_release_xprt_cong,
.rpcbind = rpc_getport, .rpcbind = rpcb_getport,
.set_port = xs_set_port, .set_port = xs_set_port,
.connect = xs_connect, .connect = xs_connect,
.buf_alloc = rpc_malloc, .buf_alloc = rpc_malloc,
...@@ -1493,7 +1493,7 @@ static struct rpc_xprt_ops xs_udp_ops = { ...@@ -1493,7 +1493,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
static struct rpc_xprt_ops xs_tcp_ops = { static struct rpc_xprt_ops xs_tcp_ops = {
.reserve_xprt = xprt_reserve_xprt, .reserve_xprt = xprt_reserve_xprt,
.release_xprt = xs_tcp_release_xprt, .release_xprt = xs_tcp_release_xprt,
.rpcbind = rpc_getport, .rpcbind = rpcb_getport,
.set_port = xs_set_port, .set_port = xs_set_port,
.connect = xs_connect, .connect = xs_connect,
.buf_alloc = rpc_malloc, .buf_alloc = rpc_malloc,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment