NFS client updates for Linux 4.17

Stable bugfixes:
 - xprtrdma: Fix corner cases when handling device removal # v4.12+
 - xprtrdma: Fix latency regression on NUMA NFS/RDMA clients # v4.15+
 
 Features:
 - New sunrpc tracepoint for RPC pings
 - Finer grained NFSv4 attribute checking
 - Don't unnecessarily return NFS v4 delegations
 
 Other bugfixes and cleanups:
 - Several other small NFSoRDMA cleanups
 - Improvements to the sunrpc RTT measurements
 - A few sunrpc tracepoint cleanups
 - Various fixes for NFS v4 lock notifications
 - Various sunrpc and NFS v4 XDR encoding cleanups
 - Switch to the ida_simple API
 - Fix NFSv4.1 exclusive create
 - Forget acl cache after setattr operation
 - Don't advance the nfs_entry readdir cookie if xdr decoding fails
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAlrNG1IACgkQ18tUv7Cl
 QOvotw//fQoUgQ/AOJGlZo/4ws2mGJN3dfwwKM8xYOnHaxppOYubZRHwvswK8d22
 +XR/Q6IVbUxI3mJluv1L0d9CJT06s3c9CO90McIJbk4CWihGP19bNIY4JiPlzrbv
 4FDiyOvMBej2UXbHX5EzKj0srxyBoEVf3iUAIa6DaHi3c6EIUo6fP3d2eRNJStqd
 WMyZs+nqr2W9biyClxntT7l/Sk+o+4I7M3Oo9pjjS+PiePYdaMrL5T1kPeHaJshF
 GMGXkbvVdqpDRiXX84R9+2/nuSiA15eEnaR94UNvs84oLR3qob3ZhxhudqFdSPrX
 RS6E7m34gY/EaQm/wbB26PZm+3jHd4Pqm5SKLbyFfoCmG6oMwBvXNRJZas1DFaHM
 CMOECvfAr6kixVLkAN0MNQ2Ku/FuJ52OLP1dRLmxsblocnhEPujc6RSz6Ju/v3a0
 adbpmJMA2IoSGgXMu3g1VGnjHfMj7ZmjtpigXVvlcUqQGCL7t4ngh23cpeTQeJ76
 bMwSHUQu18NbmtJjBTE+PIm7mdCrpQD7ZuOPWpK62zxLYUnnv7nm75m84DrDru7d
 XAmrCmdUJNrVWQs6BAtCXgO4PZ6xNGLosb0xTQXTAQYftc+DRJ9SW/VGc0Mp1L9m
 0G0iz++b8cy4Pih5UCDJcCkpjCIvHLcn72zn1kbufWqG3xr2koc=
 =IlWo
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-4.17-1' of git://git.linux-nfs.org/projects/anna/linux-nfs

Pull NFS client updates from Anna Schumaker:
 "Stable bugfixes:
   - xprtrdma: Fix corner cases when handling device removal # v4.12+
   - xprtrdma: Fix latency regression on NUMA NFS/RDMA clients # v4.15+

  Features:
   - New sunrpc tracepoint for RPC pings
   - Finer grained NFSv4 attribute checking
   - Don't unnecessarily return NFS v4 delegations

  Other bugfixes and cleanups:
   - Several other small NFSoRDMA cleanups
   - Improvements to the sunrpc RTT measurements
   - A few sunrpc tracepoint cleanups
   - Various fixes for NFS v4 lock notifications
   - Various sunrpc and NFS v4 XDR encoding cleanups
   - Switch to the ida_simple API
   - Fix NFSv4.1 exclusive create
   - Forget acl cache after setattr operation
   - Don't advance the nfs_entry readdir cookie if xdr decoding fails"

* tag 'nfs-for-4.17-1' of git://git.linux-nfs.org/projects/anna/linux-nfs: (47 commits)
  NFS: advance nfs_entry cookie only after decoding completes successfully
  NFSv3/acl: forget acl cache after setattr
  NFSv4.1: Fix exclusive create
  NFSv4: Declare the size up to date after it was set.
  nfs: Use ida_simple API
  NFSv4: Fix the nfs_inode_set_delegation() arguments
  NFSv4: Clean up CB_GETATTR encoding
  NFSv4: Don't ask for attributes when ACCESS is protected by a delegation
  NFSv4: Add a helper to encode/decode struct timespec
  NFSv4: Clean up encode_attrs
  NFSv4; Clean up XDR encoding of type bitmap4
  NFSv4: Allow GFP_NOIO sleeps in decode_attr_owner/decode_attr_group
  SUNRPC: Add a helper for encoding opaque data inline
  SUNRPC: Add helpers for decoding opaque and string types
  NFSv4: Ignore change attribute invalidations if we hold a delegation
  NFS: More fine grained attribute tracking
  NFS: Don't force unnecessary cache invalidation in nfs_update_inode()
  NFS: Don't redirty the attribute cache in nfs_wcc_update_inode()
  NFS: Don't force a revalidation of all attributes if change is missing
  NFS: Convert NFS_INO_INVALID flags to unsigned long
  ...
This commit is contained in:
Linus Torvalds 2018-04-12 12:55:50 -07:00
commit a1bf4c7da6
33 changed files with 866 additions and 493 deletions

View File

@ -535,35 +535,10 @@ static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char
return 0; return 0;
} }
#define CB_SUPPORTED_ATTR0 (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE) static __be32 encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, size_t sz)
#define CB_SUPPORTED_ATTR1 (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY)
static __be32 encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, __be32 **savep)
{ {
__be32 bm[2]; if (xdr_stream_encode_uint32_array(xdr, bitmap, sz) < 0)
__be32 *p; return cpu_to_be32(NFS4ERR_RESOURCE);
bm[0] = htonl(bitmap[0] & CB_SUPPORTED_ATTR0);
bm[1] = htonl(bitmap[1] & CB_SUPPORTED_ATTR1);
if (bm[1] != 0) {
p = xdr_reserve_space(xdr, 16);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*p++ = htonl(2);
*p++ = bm[0];
*p++ = bm[1];
} else if (bm[0] != 0) {
p = xdr_reserve_space(xdr, 12);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*p++ = htonl(1);
*p++ = bm[0];
} else {
p = xdr_reserve_space(xdr, 8);
if (unlikely(p == NULL))
return htonl(NFS4ERR_RESOURCE);
*p++ = htonl(0);
}
*savep = p;
return 0; return 0;
} }
@ -656,9 +631,13 @@ static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr,
if (unlikely(status != 0)) if (unlikely(status != 0))
goto out; goto out;
status = encode_attr_bitmap(xdr, res->bitmap, &savep); status = encode_attr_bitmap(xdr, res->bitmap, ARRAY_SIZE(res->bitmap));
if (unlikely(status != 0)) if (unlikely(status != 0))
goto out; goto out;
status = cpu_to_be32(NFS4ERR_RESOURCE);
savep = xdr_reserve_space(xdr, sizeof(*savep));
if (unlikely(!savep))
goto out;
status = encode_attr_change(xdr, res->bitmap, res->change_attr); status = encode_attr_change(xdr, res->bitmap, res->change_attr);
if (unlikely(status != 0)) if (unlikely(status != 0))
goto out; goto out;

View File

@ -19,6 +19,7 @@
#include <linux/nfs_xdr.h> #include <linux/nfs_xdr.h>
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "nfs4session.h"
#include "delegation.h" #include "delegation.h"
#include "internal.h" #include "internal.h"
#include "nfs4trace.h" #include "nfs4trace.h"
@ -171,11 +172,15 @@ again:
* nfs_inode_reclaim_delegation - process a delegation reclaim request * nfs_inode_reclaim_delegation - process a delegation reclaim request
* @inode: inode to process * @inode: inode to process
* @cred: credential to use for request * @cred: credential to use for request
* @res: new delegation state from server * @type: delegation type
* @stateid: delegation stateid
* @pagemod_limit: write delegation "space_limit"
* *
*/ */
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
struct nfs_openres *res) fmode_t type,
const nfs4_stateid *stateid,
unsigned long pagemod_limit)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
struct rpc_cred *oldcred = NULL; struct rpc_cred *oldcred = NULL;
@ -185,9 +190,9 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
if (delegation != NULL) { if (delegation != NULL) {
spin_lock(&delegation->lock); spin_lock(&delegation->lock);
if (delegation->inode != NULL) { if (delegation->inode != NULL) {
nfs4_stateid_copy(&delegation->stateid, &res->delegation); nfs4_stateid_copy(&delegation->stateid, stateid);
delegation->type = res->delegation_type; delegation->type = type;
delegation->pagemod_limit = res->pagemod_limit; delegation->pagemod_limit = pagemod_limit;
oldcred = delegation->cred; oldcred = delegation->cred;
delegation->cred = get_rpccred(cred); delegation->cred = get_rpccred(cred);
clear_bit(NFS_DELEGATION_NEED_RECLAIM, clear_bit(NFS_DELEGATION_NEED_RECLAIM,
@ -195,14 +200,14 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
rcu_read_unlock(); rcu_read_unlock();
put_rpccred(oldcred); put_rpccred(oldcred);
trace_nfs4_reclaim_delegation(inode, res->delegation_type); trace_nfs4_reclaim_delegation(inode, type);
return; return;
} }
/* We appear to have raced with a delegation return. */ /* We appear to have raced with a delegation return. */
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
} }
rcu_read_unlock(); rcu_read_unlock();
nfs_inode_set_delegation(inode, cred, res); nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit);
} }
static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
@ -329,11 +334,16 @@ nfs_update_inplace_delegation(struct nfs_delegation *delegation,
* nfs_inode_set_delegation - set up a delegation on an inode * nfs_inode_set_delegation - set up a delegation on an inode
* @inode: inode to which delegation applies * @inode: inode to which delegation applies
* @cred: cred to use for subsequent delegation processing * @cred: cred to use for subsequent delegation processing
* @res: new delegation state from server * @type: delegation type
* @stateid: delegation stateid
* @pagemod_limit: write delegation "space_limit"
* *
* Returns zero on success, or a negative errno value. * Returns zero on success, or a negative errno value.
*/ */
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred,
fmode_t type,
const nfs4_stateid *stateid,
unsigned long pagemod_limit)
{ {
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
@ -345,9 +355,9 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
delegation = kmalloc(sizeof(*delegation), GFP_NOFS); delegation = kmalloc(sizeof(*delegation), GFP_NOFS);
if (delegation == NULL) if (delegation == NULL)
return -ENOMEM; return -ENOMEM;
nfs4_stateid_copy(&delegation->stateid, &res->delegation); nfs4_stateid_copy(&delegation->stateid, stateid);
delegation->type = res->delegation_type; delegation->type = type;
delegation->pagemod_limit = res->pagemod_limit; delegation->pagemod_limit = pagemod_limit;
delegation->change_attr = inode_peek_iversion_raw(inode); delegation->change_attr = inode_peek_iversion_raw(inode);
delegation->cred = get_rpccred(cred); delegation->cred = get_rpccred(cred);
delegation->inode = inode; delegation->inode = inode;
@ -392,7 +402,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
rcu_assign_pointer(nfsi->delegation, delegation); rcu_assign_pointer(nfsi->delegation, delegation);
delegation = NULL; delegation = NULL;
trace_nfs4_set_delegation(inode, res->delegation_type); trace_nfs4_set_delegation(inode, type);
out: out:
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
@ -547,6 +557,22 @@ int nfs4_inode_return_delegation(struct inode *inode)
return err; return err;
} }
/**
* nfs4_inode_make_writeable
* @inode: pointer to inode
*
* Make the inode writeable by returning the delegation if necessary
*
* Returns zero on success, or a negative errno value.
*/
int nfs4_inode_make_writeable(struct inode *inode)
{
if (!nfs4_has_session(NFS_SERVER(inode)->nfs_client) ||
!nfs4_check_delegation(inode, FMODE_WRITE))
return nfs4_inode_return_delegation(inode);
return 0;
}
static void nfs_mark_return_if_closed_delegation(struct nfs_server *server, static void nfs_mark_return_if_closed_delegation(struct nfs_server *server,
struct nfs_delegation *delegation) struct nfs_delegation *delegation)
{ {

View File

@ -36,8 +36,10 @@ enum {
NFS_DELEGATION_TEST_EXPIRED, NFS_DELEGATION_TEST_EXPIRED,
}; };
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred,
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
fmode_t type, const nfs4_stateid *stateid, unsigned long pagemod_limit);
int nfs4_inode_return_delegation(struct inode *inode); int nfs4_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
void nfs_inode_return_delegation_noreclaim(struct inode *inode); void nfs_inode_return_delegation_noreclaim(struct inode *inode);
@ -70,6 +72,7 @@ int nfs4_check_delegation(struct inode *inode, fmode_t flags);
bool nfs4_delegation_flush_on_close(const struct inode *inode); bool nfs4_delegation_flush_on_close(const struct inode *inode);
void nfs_inode_find_delegation_state_and_recover(struct inode *inode, void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
const nfs4_stateid *stateid); const nfs4_stateid *stateid);
int nfs4_inode_make_writeable(struct inode *inode);
#endif #endif

View File

@ -1272,7 +1272,9 @@ static void nfs_drop_nlink(struct inode *inode)
/* drop the inode if we're reasonably sure this is the last link */ /* drop the inode if we're reasonably sure this is the last link */
if (inode->i_nlink == 1) if (inode->i_nlink == 1)
clear_nlink(inode); clear_nlink(inode);
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR; NFS_I(inode)->cache_validity |= NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME
| NFS_INO_INVALID_OTHER;
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
} }
@ -1798,12 +1800,11 @@ static int nfs_safe_remove(struct dentry *dentry)
trace_nfs_remove_enter(dir, dentry); trace_nfs_remove_enter(dir, dentry);
if (inode != NULL) { if (inode != NULL) {
NFS_PROTO(inode)->return_delegation(inode); error = NFS_PROTO(dir)->remove(dir, dentry);
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
if (error == 0) if (error == 0)
nfs_drop_nlink(inode); nfs_drop_nlink(inode);
} else } else
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); error = NFS_PROTO(dir)->remove(dir, dentry);
if (error == -ENOENT) if (error == -ENOENT)
nfs_dentry_handle_enoent(dentry); nfs_dentry_handle_enoent(dentry);
trace_nfs_remove_exit(dir, dentry, error); trace_nfs_remove_exit(dir, dentry, error);
@ -1932,8 +1933,6 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
old_dentry, dentry); old_dentry, dentry);
trace_nfs_link_enter(inode, dir, dentry); trace_nfs_link_enter(inode, dir, dentry);
NFS_PROTO(inode)->return_delegation(inode);
d_drop(dentry); d_drop(dentry);
error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
if (error == 0) { if (error == 0) {
@ -2023,10 +2022,6 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
} }
} }
NFS_PROTO(old_inode)->return_delegation(old_inode);
if (new_inode != NULL)
NFS_PROTO(new_inode)->return_delegation(new_inode);
task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL); task = nfs_async_rename(old_dir, new_dir, old_dentry, new_dentry, NULL);
if (IS_ERR(task)) { if (IS_ERR(task)) {
error = PTR_ERR(task); error = PTR_ERR(task);

View File

@ -195,7 +195,10 @@ bool nfs_check_cache_invalid(struct inode *inode, unsigned long flags)
static void nfs_set_cache_invalid(struct inode *inode, unsigned long flags) static void nfs_set_cache_invalid(struct inode *inode, unsigned long flags)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
bool have_delegation = nfs_have_delegated_attributes(inode);
if (have_delegation)
flags &= ~(NFS_INO_INVALID_CHANGE|NFS_INO_REVAL_PAGECACHE);
if (inode->i_mapping->nrpages == 0) if (inode->i_mapping->nrpages == 0)
flags &= ~NFS_INO_INVALID_DATA; flags &= ~NFS_INO_INVALID_DATA;
nfsi->cache_validity |= flags; nfsi->cache_validity |= flags;
@ -447,7 +450,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st
inode->i_mode = fattr->mode; inode->i_mode = fattr->mode;
if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0 if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
&& nfs_server_capable(inode, NFS_CAP_MODE)) && nfs_server_capable(inode, NFS_CAP_MODE))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); nfs_set_cache_invalid(inode, NFS_INO_INVALID_OTHER);
/* Why so? Because we want revalidate for devices/FIFOs, and /* Why so? Because we want revalidate for devices/FIFOs, and
* that's precisely what we have in nfs_file_inode_operations. * that's precisely what we have in nfs_file_inode_operations.
*/ */
@ -493,37 +496,35 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st
if (fattr->valid & NFS_ATTR_FATTR_ATIME) if (fattr->valid & NFS_ATTR_FATTR_ATIME)
inode->i_atime = fattr->atime; inode->i_atime = fattr->atime;
else if (nfs_server_capable(inode, NFS_CAP_ATIME)) else if (nfs_server_capable(inode, NFS_CAP_ATIME))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATIME);
if (fattr->valid & NFS_ATTR_FATTR_MTIME) if (fattr->valid & NFS_ATTR_FATTR_MTIME)
inode->i_mtime = fattr->mtime; inode->i_mtime = fattr->mtime;
else if (nfs_server_capable(inode, NFS_CAP_MTIME)) else if (nfs_server_capable(inode, NFS_CAP_MTIME))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
if (fattr->valid & NFS_ATTR_FATTR_CTIME) if (fattr->valid & NFS_ATTR_FATTR_CTIME)
inode->i_ctime = fattr->ctime; inode->i_ctime = fattr->ctime;
else if (nfs_server_capable(inode, NFS_CAP_CTIME)) else if (nfs_server_capable(inode, NFS_CAP_CTIME))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); nfs_set_cache_invalid(inode, NFS_INO_INVALID_CTIME);
if (fattr->valid & NFS_ATTR_FATTR_CHANGE) if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
inode_set_iversion_raw(inode, fattr->change_attr); inode_set_iversion_raw(inode, fattr->change_attr);
else else
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE);
| NFS_INO_REVAL_PAGECACHE);
if (fattr->valid & NFS_ATTR_FATTR_SIZE) if (fattr->valid & NFS_ATTR_FATTR_SIZE)
inode->i_size = nfs_size_to_loff_t(fattr->size); inode->i_size = nfs_size_to_loff_t(fattr->size);
else else
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR nfs_set_cache_invalid(inode, NFS_INO_INVALID_SIZE);
| NFS_INO_REVAL_PAGECACHE);
if (fattr->valid & NFS_ATTR_FATTR_NLINK) if (fattr->valid & NFS_ATTR_FATTR_NLINK)
set_nlink(inode, fattr->nlink); set_nlink(inode, fattr->nlink);
else if (nfs_server_capable(inode, NFS_CAP_NLINK)) else if (nfs_server_capable(inode, NFS_CAP_NLINK))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); nfs_set_cache_invalid(inode, NFS_INO_INVALID_OTHER);
if (fattr->valid & NFS_ATTR_FATTR_OWNER) if (fattr->valid & NFS_ATTR_FATTR_OWNER)
inode->i_uid = fattr->uid; inode->i_uid = fattr->uid;
else if (nfs_server_capable(inode, NFS_CAP_OWNER)) else if (nfs_server_capable(inode, NFS_CAP_OWNER))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); nfs_set_cache_invalid(inode, NFS_INO_INVALID_OTHER);
if (fattr->valid & NFS_ATTR_FATTR_GROUP) if (fattr->valid & NFS_ATTR_FATTR_GROUP)
inode->i_gid = fattr->gid; inode->i_gid = fattr->gid;
else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP)) else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR); nfs_set_cache_invalid(inode, NFS_INO_INVALID_OTHER);
if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED) if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
inode->i_blocks = fattr->du.nfs2.blocks; inode->i_blocks = fattr->du.nfs2.blocks;
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) { if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
@ -608,11 +609,6 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
goto out; goto out;
} }
/*
* Return any delegations if we're going to change ACLs
*/
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
NFS_PROTO(inode)->return_delegation(inode);
error = NFS_PROTO(inode)->setattr(dentry, fattr, attr); error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
if (error == 0) if (error == 0)
error = nfs_refresh_inode(inode, fattr); error = nfs_refresh_inode(inode, fattr);
@ -645,6 +641,7 @@ static int nfs_vmtruncate(struct inode * inode, loff_t offset)
/* Optimisation */ /* Optimisation */
if (offset == 0) if (offset == 0)
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_DATA; NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_DATA;
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE;
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
truncate_pagecache(inode, offset); truncate_pagecache(inode, offset);
@ -657,6 +654,7 @@ out:
* nfs_setattr_update_inode - Update inode metadata after a setattr call. * nfs_setattr_update_inode - Update inode metadata after a setattr call.
* @inode: pointer to struct inode * @inode: pointer to struct inode
* @attr: pointer to struct iattr * @attr: pointer to struct iattr
* @fattr: pointer to struct nfs_fattr
* *
* Note: we do this in the *proc.c in order to ensure that * Note: we do this in the *proc.c in order to ensure that
* it works for things like exclusive creates too. * it works for things like exclusive creates too.
@ -669,6 +667,8 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
NFS_I(inode)->attr_gencount = fattr->gencount; NFS_I(inode)->attr_gencount = fattr->gencount;
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME);
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) { if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
if ((attr->ia_valid & ATTR_MODE) != 0) { if ((attr->ia_valid & ATTR_MODE) != 0) {
int mode = attr->ia_mode & S_IALLUGO; int mode = attr->ia_mode & S_IALLUGO;
@ -683,13 +683,12 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
| NFS_INO_INVALID_ACL); | NFS_INO_INVALID_ACL);
} }
if ((attr->ia_valid & ATTR_SIZE) != 0) { if ((attr->ia_valid & ATTR_SIZE) != 0) {
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC); nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
nfs_vmtruncate(inode, attr->ia_size); nfs_vmtruncate(inode, attr->ia_size);
} }
if (fattr->valid) if (fattr->valid)
nfs_update_inode(inode, fattr); nfs_update_inode(inode, fattr);
else
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR;
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
} }
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode); EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
@ -1303,24 +1302,20 @@ static bool nfs_file_has_buffered_writers(struct nfs_inode *nfsi)
return nfs_file_has_writers(nfsi) && nfs_file_io_is_buffered(nfsi); return nfs_file_has_writers(nfsi) && nfs_file_io_is_buffered(nfsi);
} }
static unsigned long nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr) static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
{ {
unsigned long ret = 0;
if ((fattr->valid & NFS_ATTR_FATTR_PRECHANGE) if ((fattr->valid & NFS_ATTR_FATTR_PRECHANGE)
&& (fattr->valid & NFS_ATTR_FATTR_CHANGE) && (fattr->valid & NFS_ATTR_FATTR_CHANGE)
&& inode_eq_iversion_raw(inode, fattr->pre_change_attr)) { && inode_eq_iversion_raw(inode, fattr->pre_change_attr)) {
inode_set_iversion_raw(inode, fattr->change_attr); inode_set_iversion_raw(inode, fattr->change_attr);
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA); nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
ret |= NFS_INO_INVALID_ATTR;
} }
/* If we have atomic WCC data, we may update some attributes */ /* If we have atomic WCC data, we may update some attributes */
if ((fattr->valid & NFS_ATTR_FATTR_PRECTIME) if ((fattr->valid & NFS_ATTR_FATTR_PRECTIME)
&& (fattr->valid & NFS_ATTR_FATTR_CTIME) && (fattr->valid & NFS_ATTR_FATTR_CTIME)
&& timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) { && timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) {
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
ret |= NFS_INO_INVALID_ATTR;
} }
if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME) if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME)
@ -1329,17 +1324,13 @@ static unsigned long nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA); nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
ret |= NFS_INO_INVALID_ATTR;
} }
if ((fattr->valid & NFS_ATTR_FATTR_PRESIZE) if ((fattr->valid & NFS_ATTR_FATTR_PRESIZE)
&& (fattr->valid & NFS_ATTR_FATTR_SIZE) && (fattr->valid & NFS_ATTR_FATTR_SIZE)
&& i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size) && i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size)
&& !nfs_have_writebacks(inode)) { && !nfs_have_writebacks(inode)) {
i_size_write(inode, nfs_size_to_loff_t(fattr->size)); i_size_write(inode, nfs_size_to_loff_t(fattr->size));
ret |= NFS_INO_INVALID_ATTR;
} }
return ret;
} }
/** /**
@ -1369,33 +1360,41 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
if (!nfs_file_has_buffered_writers(nfsi)) { if (!nfs_file_has_buffered_writers(nfsi)) {
/* Verify a few of the more important attributes */ /* Verify a few of the more important attributes */
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && !inode_eq_iversion_raw(inode, fattr->change_attr)) if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 && !inode_eq_iversion_raw(inode, fattr->change_attr))
invalid |= NFS_INO_INVALID_ATTR | NFS_INO_REVAL_PAGECACHE; invalid |= NFS_INO_INVALID_CHANGE
| NFS_INO_REVAL_PAGECACHE;
if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime)) if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime))
invalid |= NFS_INO_INVALID_ATTR; invalid |= NFS_INO_INVALID_MTIME;
if ((fattr->valid & NFS_ATTR_FATTR_CTIME) && !timespec_equal(&inode->i_ctime, &fattr->ctime)) if ((fattr->valid & NFS_ATTR_FATTR_CTIME) && !timespec_equal(&inode->i_ctime, &fattr->ctime))
invalid |= NFS_INO_INVALID_ATTR; invalid |= NFS_INO_INVALID_CTIME;
if (fattr->valid & NFS_ATTR_FATTR_SIZE) { if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
cur_size = i_size_read(inode); cur_size = i_size_read(inode);
new_isize = nfs_size_to_loff_t(fattr->size); new_isize = nfs_size_to_loff_t(fattr->size);
if (cur_size != new_isize) if (cur_size != new_isize)
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; invalid |= NFS_INO_INVALID_SIZE
| NFS_INO_REVAL_PAGECACHE;
} }
} }
/* Have any file permissions changed? */ /* Have any file permissions changed? */
if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO))
invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; invalid |= NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER;
if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && !uid_eq(inode->i_uid, fattr->uid)) if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && !uid_eq(inode->i_uid, fattr->uid))
invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; invalid |= NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER;
if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && !gid_eq(inode->i_gid, fattr->gid)) if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && !gid_eq(inode->i_gid, fattr->gid))
invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; invalid |= NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER;
/* Has the link count changed? */ /* Has the link count changed? */
if ((fattr->valid & NFS_ATTR_FATTR_NLINK) && inode->i_nlink != fattr->nlink) if ((fattr->valid & NFS_ATTR_FATTR_NLINK) && inode->i_nlink != fattr->nlink)
invalid |= NFS_INO_INVALID_ATTR; invalid |= NFS_INO_INVALID_OTHER;
if ((fattr->valid & NFS_ATTR_FATTR_ATIME) && !timespec_equal(&inode->i_atime, &fattr->atime)) if ((fattr->valid & NFS_ATTR_FATTR_ATIME) && !timespec_equal(&inode->i_atime, &fattr->atime))
invalid |= NFS_INO_INVALID_ATIME; invalid |= NFS_INO_INVALID_ATIME;
@ -1597,10 +1596,9 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
} }
EXPORT_SYMBOL_GPL(nfs_refresh_inode); EXPORT_SYMBOL_GPL(nfs_refresh_inode);
static int nfs_post_op_update_inode_locked(struct inode *inode, struct nfs_fattr *fattr) static int nfs_post_op_update_inode_locked(struct inode *inode,
struct nfs_fattr *fattr, unsigned int invalid)
{ {
unsigned long invalid = NFS_INO_INVALID_ATTR;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
invalid |= NFS_INO_INVALID_DATA; invalid |= NFS_INO_INVALID_DATA;
nfs_set_cache_invalid(inode, invalid); nfs_set_cache_invalid(inode, invalid);
@ -1629,7 +1627,9 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
nfs_fattr_set_barrier(fattr); nfs_fattr_set_barrier(fattr);
status = nfs_post_op_update_inode_locked(inode, fattr); status = nfs_post_op_update_inode_locked(inode, fattr,
NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
return status; return status;
@ -1681,7 +1681,10 @@ int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fa
fattr->valid |= NFS_ATTR_FATTR_PRESIZE; fattr->valid |= NFS_ATTR_FATTR_PRESIZE;
} }
out_noforce: out_noforce:
status = nfs_post_op_update_inode_locked(inode, fattr); status = nfs_post_op_update_inode_locked(inode, fattr,
NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME
| NFS_INO_INVALID_MTIME);
return status; return status;
} }
@ -1789,7 +1792,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
| NFS_INO_REVAL_PAGECACHE); | NFS_INO_REVAL_PAGECACHE);
/* Do atomic weak cache consistency updates */ /* Do atomic weak cache consistency updates */
invalid |= nfs_wcc_update_inode(inode, fattr); nfs_wcc_update_inode(inode, fattr);
if (pnfs_layoutcommit_outstanding(inode)) { if (pnfs_layoutcommit_outstanding(inode)) {
nfsi->cache_validity |= save_cache_validity & NFS_INO_INVALID_ATTR; nfsi->cache_validity |= save_cache_validity & NFS_INO_INVALID_ATTR;
@ -1803,17 +1806,25 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_sb->s_id, inode->i_ino); inode->i_sb->s_id, inode->i_ino);
/* Could it be a race with writeback? */ /* Could it be a race with writeback? */
if (!have_writers) { if (!have_writers) {
invalid |= NFS_INO_INVALID_ATTR invalid |= NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_DATA | NFS_INO_INVALID_DATA
| NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL; | NFS_INO_INVALID_ACL;
/* Force revalidate of all attributes */
save_cache_validity |= NFS_INO_INVALID_CTIME
| NFS_INO_INVALID_MTIME
| NFS_INO_INVALID_SIZE
| NFS_INO_INVALID_OTHER;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
nfs_force_lookup_revalidate(inode); nfs_force_lookup_revalidate(inode);
} }
inode_set_iversion_raw(inode, fattr->change_attr); inode_set_iversion_raw(inode, fattr->change_attr);
} }
} else { } else {
nfsi->cache_validity |= save_cache_validity; nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_CHANGE
| NFS_INO_REVAL_PAGECACHE
| NFS_INO_REVAL_FORCED);
cache_revalidated = false; cache_revalidated = false;
} }
@ -1821,7 +1832,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
} else if (server->caps & NFS_CAP_MTIME) { } else if (server->caps & NFS_CAP_MTIME) {
nfsi->cache_validity |= save_cache_validity & nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR (NFS_INO_INVALID_MTIME
| NFS_INO_REVAL_FORCED); | NFS_INO_REVAL_FORCED);
cache_revalidated = false; cache_revalidated = false;
} }
@ -1830,7 +1841,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
} else if (server->caps & NFS_CAP_CTIME) { } else if (server->caps & NFS_CAP_CTIME) {
nfsi->cache_validity |= save_cache_validity & nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR (NFS_INO_INVALID_CTIME
| NFS_INO_REVAL_FORCED); | NFS_INO_REVAL_FORCED);
cache_revalidated = false; cache_revalidated = false;
} }
@ -1845,7 +1856,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
if (!nfs_have_writebacks(inode) || new_isize > cur_isize) { if (!nfs_have_writebacks(inode) || new_isize > cur_isize) {
i_size_write(inode, new_isize); i_size_write(inode, new_isize);
if (!have_writers) if (!have_writers)
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; invalid |= NFS_INO_INVALID_DATA;
} }
dprintk("NFS: isize change on server for file %s/%ld " dprintk("NFS: isize change on server for file %s/%ld "
"(%Ld to %Ld)\n", "(%Ld to %Ld)\n",
@ -1856,7 +1867,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
} }
} else { } else {
nfsi->cache_validity |= save_cache_validity & nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR (NFS_INO_INVALID_SIZE
| NFS_INO_REVAL_PAGECACHE | NFS_INO_REVAL_PAGECACHE
| NFS_INO_REVAL_FORCED); | NFS_INO_REVAL_FORCED);
cache_revalidated = false; cache_revalidated = false;
@ -1877,55 +1888,61 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
umode_t newmode = inode->i_mode & S_IFMT; umode_t newmode = inode->i_mode & S_IFMT;
newmode |= fattr->mode & S_IALLUGO; newmode |= fattr->mode & S_IALLUGO;
inode->i_mode = newmode; inode->i_mode = newmode;
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; invalid |= NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER;
} }
} else if (server->caps & NFS_CAP_MODE) { } else if (server->caps & NFS_CAP_MODE) {
nfsi->cache_validity |= save_cache_validity & nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR (NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL | NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER
| NFS_INO_REVAL_FORCED); | NFS_INO_REVAL_FORCED);
cache_revalidated = false; cache_revalidated = false;
} }
if (fattr->valid & NFS_ATTR_FATTR_OWNER) { if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
if (!uid_eq(inode->i_uid, fattr->uid)) { if (!uid_eq(inode->i_uid, fattr->uid)) {
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; invalid |= NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER;
inode->i_uid = fattr->uid; inode->i_uid = fattr->uid;
} }
} else if (server->caps & NFS_CAP_OWNER) { } else if (server->caps & NFS_CAP_OWNER) {
nfsi->cache_validity |= save_cache_validity & nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR (NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL | NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER
| NFS_INO_REVAL_FORCED); | NFS_INO_REVAL_FORCED);
cache_revalidated = false; cache_revalidated = false;
} }
if (fattr->valid & NFS_ATTR_FATTR_GROUP) { if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
if (!gid_eq(inode->i_gid, fattr->gid)) { if (!gid_eq(inode->i_gid, fattr->gid)) {
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; invalid |= NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER;
inode->i_gid = fattr->gid; inode->i_gid = fattr->gid;
} }
} else if (server->caps & NFS_CAP_OWNER_GROUP) { } else if (server->caps & NFS_CAP_OWNER_GROUP) {
nfsi->cache_validity |= save_cache_validity & nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR (NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL | NFS_INO_INVALID_ACL
| NFS_INO_INVALID_OTHER
| NFS_INO_REVAL_FORCED); | NFS_INO_REVAL_FORCED);
cache_revalidated = false; cache_revalidated = false;
} }
if (fattr->valid & NFS_ATTR_FATTR_NLINK) { if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
if (inode->i_nlink != fattr->nlink) { if (inode->i_nlink != fattr->nlink) {
invalid |= NFS_INO_INVALID_ATTR; invalid |= NFS_INO_INVALID_OTHER;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
invalid |= NFS_INO_INVALID_DATA; invalid |= NFS_INO_INVALID_DATA;
set_nlink(inode, fattr->nlink); set_nlink(inode, fattr->nlink);
} }
} else if (server->caps & NFS_CAP_NLINK) { } else if (server->caps & NFS_CAP_NLINK) {
nfsi->cache_validity |= save_cache_validity & nfsi->cache_validity |= save_cache_validity &
(NFS_INO_INVALID_ATTR (NFS_INO_INVALID_OTHER
| NFS_INO_REVAL_FORCED); | NFS_INO_REVAL_FORCED);
cache_revalidated = false; cache_revalidated = false;
} }
@ -1942,6 +1959,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
/* Update attrtimeo value if we're out of the unstable period */ /* Update attrtimeo value if we're out of the unstable period */
if (invalid & NFS_INO_INVALID_ATTR) { if (invalid & NFS_INO_INVALID_ATTR) {
invalid &= ~NFS_INO_INVALID_ATTR;
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now; nfsi->attrtimeo_timestamp = now;
@ -1962,10 +1980,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
nfsi->attr_gencount = fattr->gencount; nfsi->attr_gencount = fattr->gencount;
} }
/* Don't declare attrcache up to date if there were no attrs! */
if (cache_revalidated)
invalid &= ~NFS_INO_INVALID_ATTR;
/* Don't invalidate the data if we were to blame */ /* Don't invalidate the data if we were to blame */
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode))) || S_ISLNK(inode->i_mode)))

View File

@ -138,8 +138,11 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
msg.rpc_cred = nfs_file_cred(sattr->ia_file); msg.rpc_cred = nfs_file_cred(sattr->ia_file);
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status == 0) if (status == 0) {
if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
nfs_zap_acl_cache(inode);
nfs_setattr_update_inode(inode, sattr, fattr); nfs_setattr_update_inode(inode, sattr, fattr);
}
dprintk("NFS reply setattr: %d\n", status); dprintk("NFS reply setattr: %d\n", status);
return status; return status;
} }
@ -383,11 +386,11 @@ out:
} }
static int static int
nfs3_proc_remove(struct inode *dir, const struct qstr *name) nfs3_proc_remove(struct inode *dir, struct dentry *dentry)
{ {
struct nfs_removeargs arg = { struct nfs_removeargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.name = *name, .name = dentry->d_name,
}; };
struct nfs_removeres res; struct nfs_removeres res;
struct rpc_message msg = { struct rpc_message msg = {
@ -397,7 +400,7 @@ nfs3_proc_remove(struct inode *dir, const struct qstr *name)
}; };
int status = -ENOMEM; int status = -ENOMEM;
dprintk("NFS call remove %s\n", name->name); dprintk("NFS call remove %pd2\n", dentry);
res.dir_attr = nfs_alloc_fattr(); res.dir_attr = nfs_alloc_fattr();
if (res.dir_attr == NULL) if (res.dir_attr == NULL)
goto out; goto out;
@ -411,7 +414,7 @@ out:
} }
static void static void
nfs3_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dentry)
{ {
msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE]; msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE];
} }
@ -433,7 +436,9 @@ nfs3_proc_unlink_done(struct rpc_task *task, struct inode *dir)
} }
static void static void
nfs3_proc_rename_setup(struct rpc_message *msg, struct inode *dir) nfs3_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
struct dentry *new_dentry)
{ {
msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME]; msg->rpc_proc = &nfs3_procedures[NFS3PROC_RENAME];
} }
@ -908,12 +913,6 @@ static int nfs3_have_delegation(struct inode *inode, fmode_t flags)
return 0; return 0;
} }
static int nfs3_return_delegation(struct inode *inode)
{
nfs_wb_all(inode);
return 0;
}
static const struct inode_operations nfs3_dir_inode_operations = { static const struct inode_operations nfs3_dir_inode_operations = {
.create = nfs_create, .create = nfs_create,
.lookup = nfs_lookup, .lookup = nfs_lookup,
@ -990,7 +989,6 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.clear_acl_cache = forget_all_cached_acls, .clear_acl_cache = forget_all_cached_acls,
.close_context = nfs_close_context, .close_context = nfs_close_context,
.have_delegation = nfs3_have_delegation, .have_delegation = nfs3_have_delegation,
.return_delegation = nfs3_return_delegation,
.alloc_client = nfs_alloc_client, .alloc_client = nfs_alloc_client,
.init_client = nfs_init_client, .init_client = nfs_init_client,
.free_client = nfs_free_client, .free_client = nfs_free_client,

View File

@ -1997,6 +1997,7 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
struct nfs_entry old = *entry; struct nfs_entry old = *entry;
__be32 *p; __be32 *p;
int error; int error;
u64 new_cookie;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
@ -2019,8 +2020,7 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
if (unlikely(error)) if (unlikely(error))
return error; return error;
entry->prev_cookie = entry->cookie; error = decode_cookie3(xdr, &new_cookie);
error = decode_cookie3(xdr, &entry->cookie);
if (unlikely(error)) if (unlikely(error))
return error; return error;
@ -2054,6 +2054,9 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
zero_nfs_fh3(entry->fh); zero_nfs_fh3(entry->fh);
} }
entry->prev_cookie = entry->cookie;
entry->cookie = new_cookie;
return 0; return 0;
out_overflow: out_overflow:

View File

@ -1045,7 +1045,9 @@ static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo,
struct nfs_inode *nfsi = NFS_I(dir); struct nfs_inode *nfsi = NFS_I(dir);
spin_lock(&dir->i_lock); spin_lock(&dir->i_lock);
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; nfsi->cache_validity |= NFS_INO_INVALID_CTIME
| NFS_INO_INVALID_MTIME
| NFS_INO_INVALID_DATA;
if (cinfo->atomic && cinfo->before == inode_peek_iversion_raw(dir)) { if (cinfo->atomic && cinfo->before == inode_peek_iversion_raw(dir)) {
nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE; nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE;
nfsi->attrtimeo_timestamp = jiffies; nfsi->attrtimeo_timestamp = jiffies;
@ -1669,6 +1671,7 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
fmode &= FMODE_READ|FMODE_WRITE;
rcu_read_lock(); rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation); delegation = rcu_dereference(NFS_I(inode)->delegation);
if (delegation == NULL || (delegation->type & fmode) == fmode) { if (delegation == NULL || (delegation->type & fmode) == fmode) {
@ -1751,12 +1754,16 @@ nfs4_opendata_check_deleg(struct nfs4_opendata *data, struct nfs4_state *state)
} }
if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0) if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0)
nfs_inode_set_delegation(state->inode, nfs_inode_set_delegation(state->inode,
data->owner->so_cred, data->owner->so_cred,
&data->o_res); data->o_res.delegation_type,
&data->o_res.delegation,
data->o_res.pagemod_limit);
else else
nfs_inode_reclaim_delegation(state->inode, nfs_inode_reclaim_delegation(state->inode,
data->owner->so_cred, data->owner->so_cred,
&data->o_res); data->o_res.delegation_type,
&data->o_res.delegation,
data->o_res.pagemod_limit);
} }
/* /*
@ -2743,27 +2750,40 @@ static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
* fields corresponding to attributes that were used to store the verifier. * fields corresponding to attributes that were used to store the verifier.
* Make sure we clobber those fields in the later setattr call * Make sure we clobber those fields in the later setattr call
*/ */
static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, static unsigned nfs4_exclusive_attrset(struct nfs4_opendata *opendata,
struct iattr *sattr, struct nfs4_label **label) struct iattr *sattr, struct nfs4_label **label)
{ {
const u32 *attrset = opendata->o_res.attrset; const __u32 *bitmask = opendata->o_arg.server->exclcreat_bitmask;
__u32 attrset[3];
unsigned ret;
unsigned i;
if ((attrset[1] & FATTR4_WORD1_TIME_ACCESS) && for (i = 0; i < ARRAY_SIZE(attrset); i++) {
!(sattr->ia_valid & ATTR_ATIME_SET)) attrset[i] = opendata->o_res.attrset[i];
sattr->ia_valid |= ATTR_ATIME; if (opendata->o_arg.createmode == NFS4_CREATE_EXCLUSIVE4_1)
attrset[i] &= ~bitmask[i];
}
if ((attrset[1] & FATTR4_WORD1_TIME_MODIFY) && ret = (opendata->o_arg.createmode == NFS4_CREATE_EXCLUSIVE) ?
!(sattr->ia_valid & ATTR_MTIME_SET)) sattr->ia_valid : 0;
sattr->ia_valid |= ATTR_MTIME;
/* Except MODE, it seems harmless of setting twice. */ if ((attrset[1] & (FATTR4_WORD1_TIME_ACCESS|FATTR4_WORD1_TIME_ACCESS_SET))) {
if (opendata->o_arg.createmode != NFS4_CREATE_EXCLUSIVE && if (sattr->ia_valid & ATTR_ATIME_SET)
(attrset[1] & FATTR4_WORD1_MODE || ret |= ATTR_ATIME_SET;
attrset[2] & FATTR4_WORD2_MODE_UMASK)) else
sattr->ia_valid &= ~ATTR_MODE; ret |= ATTR_ATIME;
}
if (attrset[2] & FATTR4_WORD2_SECURITY_LABEL) if ((attrset[1] & (FATTR4_WORD1_TIME_MODIFY|FATTR4_WORD1_TIME_MODIFY_SET))) {
if (sattr->ia_valid & ATTR_MTIME_SET)
ret |= ATTR_MTIME_SET;
else
ret |= ATTR_MTIME;
}
if (!(attrset[2] & FATTR4_WORD2_SECURITY_LABEL))
*label = NULL; *label = NULL;
return ret;
} }
static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
@ -2892,12 +2912,15 @@ static int _nfs4_do_open(struct inode *dir,
if ((opendata->o_arg.open_flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) && if ((opendata->o_arg.open_flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) &&
(opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) { (opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) {
nfs4_exclusive_attrset(opendata, sattr, &label); unsigned attrs = nfs4_exclusive_attrset(opendata, sattr, &label);
/* /*
* send create attributes which was not set by open * send create attributes which was not set by open
* with an extra setattr. * with an extra setattr.
*/ */
if (sattr->ia_valid & NFS4_VALID_ATTRS) { if (attrs || label) {
unsigned ia_old = sattr->ia_valid;
sattr->ia_valid = attrs;
nfs_fattr_init(opendata->o_res.f_attr); nfs_fattr_init(opendata->o_res.f_attr);
status = nfs4_do_setattr(state->inode, cred, status = nfs4_do_setattr(state->inode, cred,
opendata->o_res.f_attr, sattr, opendata->o_res.f_attr, sattr,
@ -2907,6 +2930,7 @@ static int _nfs4_do_open(struct inode *dir,
opendata->o_res.f_attr); opendata->o_res.f_attr);
nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel); nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel);
} }
sattr->ia_valid = ia_old;
} }
} }
if (opened && opendata->file_created) if (opened && opendata->file_created)
@ -3874,6 +3898,10 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
if (IS_ERR(label)) if (IS_ERR(label))
return PTR_ERR(label); return PTR_ERR(label);
/* Return any delegations if we're going to change ACLs */
if ((sattr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
nfs4_inode_make_writeable(inode);
status = nfs4_do_setattr(inode, cred, fattr, sattr, ctx, NULL, label); status = nfs4_do_setattr(inode, cred, fattr, sattr, ctx, NULL, label);
if (status == 0) { if (status == 0) {
nfs_setattr_update_inode(inode, sattr, fattr); nfs_setattr_update_inode(inode, sattr, fattr);
@ -4048,7 +4076,6 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_accessargs args = { struct nfs4_accessargs args = {
.fh = NFS_FH(inode), .fh = NFS_FH(inode),
.bitmask = server->cache_consistency_bitmask,
.access = entry->mask, .access = entry->mask,
}; };
struct nfs4_accessres res = { struct nfs4_accessres res = {
@ -4062,14 +4089,18 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
}; };
int status = 0; int status = 0;
res.fattr = nfs_alloc_fattr(); if (!nfs_have_delegated_attributes(inode)) {
if (res.fattr == NULL) res.fattr = nfs_alloc_fattr();
return -ENOMEM; if (res.fattr == NULL)
return -ENOMEM;
args.bitmask = server->cache_consistency_bitmask;
}
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
if (!status) { if (!status) {
nfs_access_set_mask(entry, res.access); nfs_access_set_mask(entry, res.access);
nfs_refresh_inode(inode, res.fattr); if (res.fattr)
nfs_refresh_inode(inode, res.fattr);
} }
nfs_free_fattr(res.fattr); nfs_free_fattr(res.fattr);
return status; return status;
@ -4199,10 +4230,32 @@ static int _nfs4_proc_remove(struct inode *dir, const struct qstr *name)
return status; return status;
} }
static int nfs4_proc_remove(struct inode *dir, const struct qstr *name) static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)
{
struct nfs4_exception exception = { };
struct inode *inode = d_inode(dentry);
int err;
if (inode) {
if (inode->i_nlink == 1)
nfs4_inode_return_delegation(inode);
else
nfs4_inode_make_writeable(inode);
}
do {
err = _nfs4_proc_remove(dir, &dentry->d_name);
trace_nfs4_remove(dir, &dentry->d_name, err);
err = nfs4_handle_exception(NFS_SERVER(dir), err,
&exception);
} while (exception.retry);
return err;
}
static int nfs4_proc_rmdir(struct inode *dir, const struct qstr *name)
{ {
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
int err; int err;
do { do {
err = _nfs4_proc_remove(dir, name); err = _nfs4_proc_remove(dir, name);
trace_nfs4_remove(dir, name, err); trace_nfs4_remove(dir, name, err);
@ -4212,17 +4265,20 @@ static int nfs4_proc_remove(struct inode *dir, const struct qstr *name)
return err; return err;
} }
static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct dentry *dentry)
{ {
struct nfs_server *server = NFS_SERVER(dir);
struct nfs_removeargs *args = msg->rpc_argp; struct nfs_removeargs *args = msg->rpc_argp;
struct nfs_removeres *res = msg->rpc_resp; struct nfs_removeres *res = msg->rpc_resp;
struct inode *inode = d_inode(dentry);
res->server = server; res->server = NFS_SB(dentry->d_sb);
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
nfs4_init_sequence(&args->seq_args, &res->seq_res, 1); nfs4_init_sequence(&args->seq_args, &res->seq_res, 1);
nfs_fattr_init(res->dir_attr); nfs_fattr_init(res->dir_attr);
if (inode)
nfs4_inode_return_delegation(inode);
} }
static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data)
@ -4248,14 +4304,21 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
return 1; return 1;
} }
static void nfs4_proc_rename_setup(struct rpc_message *msg, struct inode *dir) static void nfs4_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
struct dentry *new_dentry)
{ {
struct nfs_server *server = NFS_SERVER(dir);
struct nfs_renameargs *arg = msg->rpc_argp; struct nfs_renameargs *arg = msg->rpc_argp;
struct nfs_renameres *res = msg->rpc_resp; struct nfs_renameres *res = msg->rpc_resp;
struct inode *old_inode = d_inode(old_dentry);
struct inode *new_inode = d_inode(new_dentry);
if (old_inode)
nfs4_inode_make_writeable(old_inode);
if (new_inode)
nfs4_inode_return_delegation(new_inode);
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME]; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENAME];
res->server = server; res->server = NFS_SB(old_dentry->d_sb);
nfs4_init_sequence(&arg->seq_args, &res->seq_res, 1); nfs4_init_sequence(&arg->seq_args, &res->seq_res, 1);
} }
@ -4317,6 +4380,8 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, const struct
} }
arg.bitmask = nfs4_bitmask(server, res.label); arg.bitmask = nfs4_bitmask(server, res.label);
nfs4_inode_make_writeable(inode);
status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
if (!status) { if (!status) {
update_changeattr(dir, &res.cinfo, res.fattr->time_start); update_changeattr(dir, &res.cinfo, res.fattr->time_start);
@ -5310,7 +5375,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
i = buf_to_pages_noslab(buf, buflen, arg.acl_pages); i = buf_to_pages_noslab(buf, buflen, arg.acl_pages);
if (i < 0) if (i < 0)
return i; return i;
nfs4_inode_return_delegation(inode); nfs4_inode_make_writeable(inode);
ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
/* /*
@ -5325,7 +5390,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
* so mark the attribute cache invalid. * so mark the attribute cache invalid.
*/ */
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR; NFS_I(inode)->cache_validity |= NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME;
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
nfs_access_zap_cache(inode); nfs_access_zap_cache(inode);
nfs_zap_acl_cache(inode); nfs_zap_acl_cache(inode);
@ -6621,22 +6687,24 @@ static int
nfs4_wake_lock_waiter(wait_queue_entry_t *wait, unsigned int mode, int flags, void *key) nfs4_wake_lock_waiter(wait_queue_entry_t *wait, unsigned int mode, int flags, void *key)
{ {
int ret; int ret;
struct cb_notify_lock_args *cbnl = key;
struct nfs4_lock_waiter *waiter = wait->private; struct nfs4_lock_waiter *waiter = wait->private;
struct nfs_lowner *lowner = &cbnl->cbnl_owner,
*wowner = waiter->owner;
/* Only wake if the callback was for the same owner */ /* NULL key means to wake up everyone */
if (lowner->clientid != wowner->clientid || if (key) {
lowner->id != wowner->id || struct cb_notify_lock_args *cbnl = key;
lowner->s_dev != wowner->s_dev) struct nfs_lowner *lowner = &cbnl->cbnl_owner,
return 0; *wowner = waiter->owner;
/* Make sure it's for the right inode */ /* Only wake if the callback was for the same owner. */
if (nfs_compare_fh(NFS_FH(waiter->inode), &cbnl->cbnl_fh)) if (lowner->id != wowner->id || lowner->s_dev != wowner->s_dev)
return 0; return 0;
waiter->notified = true; /* Make sure it's for the right inode */
if (nfs_compare_fh(NFS_FH(waiter->inode), &cbnl->cbnl_fh))
return 0;
waiter->notified = true;
}
/* override "private" so we can use default_wake_function */ /* override "private" so we can use default_wake_function */
wait->private = waiter->task; wait->private = waiter->task;
@ -6673,6 +6741,7 @@ nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
add_wait_queue(q, &wait); add_wait_queue(q, &wait);
while(!signalled()) { while(!signalled()) {
waiter.notified = false;
status = nfs4_proc_setlk(state, cmd, request); status = nfs4_proc_setlk(state, cmd, request);
if ((status != -EAGAIN) || IS_SETLK(cmd)) if ((status != -EAGAIN) || IS_SETLK(cmd))
break; break;
@ -8414,6 +8483,8 @@ static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nf
{ {
switch(task->tk_status) { switch(task->tk_status) {
case 0: case 0:
wake_up_all(&clp->cl_lock_waitq);
/* Fallthrough */
case -NFS4ERR_COMPLETE_ALREADY: case -NFS4ERR_COMPLETE_ALREADY:
case -NFS4ERR_WRONG_CRED: /* What to do here? */ case -NFS4ERR_WRONG_CRED: /* What to do here? */
break; break;
@ -9593,7 +9664,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.link = nfs4_proc_link, .link = nfs4_proc_link,
.symlink = nfs4_proc_symlink, .symlink = nfs4_proc_symlink,
.mkdir = nfs4_proc_mkdir, .mkdir = nfs4_proc_mkdir,
.rmdir = nfs4_proc_remove, .rmdir = nfs4_proc_rmdir,
.readdir = nfs4_proc_readdir, .readdir = nfs4_proc_readdir,
.mknod = nfs4_proc_mknod, .mknod = nfs4_proc_mknod,
.statfs = nfs4_proc_statfs, .statfs = nfs4_proc_statfs,
@ -9614,7 +9685,6 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.close_context = nfs4_close_context, .close_context = nfs4_close_context,
.open_context = nfs4_atomic_open, .open_context = nfs4_atomic_open,
.have_delegation = nfs4_have_delegation, .have_delegation = nfs4_have_delegation,
.return_delegation = nfs4_inode_return_delegation,
.alloc_client = nfs4_alloc_client, .alloc_client = nfs4_alloc_client,
.init_client = nfs4_init_client, .init_client = nfs4_init_client,
.free_client = nfs4_free_client, .free_client = nfs4_free_client,

View File

@ -428,7 +428,6 @@ nfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
struct rb_node **p = &server->state_owners.rb_node, struct rb_node **p = &server->state_owners.rb_node,
*parent = NULL; *parent = NULL;
struct nfs4_state_owner *sp; struct nfs4_state_owner *sp;
int err;
while (*p != NULL) { while (*p != NULL) {
parent = *p; parent = *p;
@ -445,9 +444,6 @@ nfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
return sp; return sp;
} }
} }
err = ida_get_new(&server->openowner_id, &new->so_seqid.owner_id);
if (err)
return ERR_PTR(err);
rb_link_node(&new->so_server_node, parent, p); rb_link_node(&new->so_server_node, parent, p);
rb_insert_color(&new->so_server_node, &server->state_owners); rb_insert_color(&new->so_server_node, &server->state_owners);
return new; return new;
@ -460,7 +456,6 @@ nfs4_remove_state_owner_locked(struct nfs4_state_owner *sp)
if (!RB_EMPTY_NODE(&sp->so_server_node)) if (!RB_EMPTY_NODE(&sp->so_server_node))
rb_erase(&sp->so_server_node, &server->state_owners); rb_erase(&sp->so_server_node, &server->state_owners);
ida_remove(&server->openowner_id, sp->so_seqid.owner_id);
} }
static void static void
@ -495,6 +490,12 @@ nfs4_alloc_state_owner(struct nfs_server *server,
sp = kzalloc(sizeof(*sp), gfp_flags); sp = kzalloc(sizeof(*sp), gfp_flags);
if (!sp) if (!sp)
return NULL; return NULL;
sp->so_seqid.owner_id = ida_simple_get(&server->openowner_id, 0, 0,
gfp_flags);
if (sp->so_seqid.owner_id < 0) {
kfree(sp);
return NULL;
}
sp->so_server = server; sp->so_server = server;
sp->so_cred = get_rpccred(cred); sp->so_cred = get_rpccred(cred);
spin_lock_init(&sp->so_lock); spin_lock_init(&sp->so_lock);
@ -526,6 +527,7 @@ static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
{ {
nfs4_destroy_seqid_counter(&sp->so_seqid); nfs4_destroy_seqid_counter(&sp->so_seqid);
put_rpccred(sp->so_cred); put_rpccred(sp->so_cred);
ida_simple_remove(&sp->so_server->openowner_id, sp->so_seqid.owner_id);
kfree(sp); kfree(sp);
} }
@ -576,13 +578,9 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
new = nfs4_alloc_state_owner(server, cred, gfp_flags); new = nfs4_alloc_state_owner(server, cred, gfp_flags);
if (new == NULL) if (new == NULL)
goto out; goto out;
do { spin_lock(&clp->cl_lock);
if (ida_pre_get(&server->openowner_id, gfp_flags) == 0) sp = nfs4_insert_state_owner_locked(new);
break; spin_unlock(&clp->cl_lock);
spin_lock(&clp->cl_lock);
sp = nfs4_insert_state_owner_locked(new);
spin_unlock(&clp->cl_lock);
} while (sp == ERR_PTR(-EAGAIN));
if (sp != new) if (sp != new)
nfs4_free_state_owner(new); nfs4_free_state_owner(new);
out: out:

View File

@ -98,6 +98,7 @@ static int nfs4_stat_to_errno(int);
((3+NFS4_FHSIZE) >> 2)) ((3+NFS4_FHSIZE) >> 2))
#define nfs4_fattr_bitmap_maxsz 4 #define nfs4_fattr_bitmap_maxsz 4
#define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) #define encode_getattr_maxsz (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz)
#define nfstime4_maxsz (3)
#define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2)) #define nfs4_name_maxsz (1 + ((3 + NFS4_MAXNAMLEN) >> 2))
#define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2)) #define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2))
#define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) #define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ))
@ -112,7 +113,8 @@ static int nfs4_stat_to_errno(int);
#define decode_mdsthreshold_maxsz (1 + 1 + nfs4_fattr_bitmap_maxsz + 1 + 8) #define decode_mdsthreshold_maxsz (1 + 1 + nfs4_fattr_bitmap_maxsz + 1 + 8)
/* This is based on getfattr, which uses the most attributes: */ /* This is based on getfattr, which uses the most attributes: */
#define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \ #define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \
3 + 3 + 3 + nfs4_owner_maxsz + \ 3*nfstime4_maxsz + \
nfs4_owner_maxsz + \
nfs4_group_maxsz + nfs4_label_maxsz + \ nfs4_group_maxsz + nfs4_label_maxsz + \
decode_mdsthreshold_maxsz)) decode_mdsthreshold_maxsz))
#define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \ #define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \
@ -123,7 +125,8 @@ static int nfs4_stat_to_errno(int);
nfs4_owner_maxsz + \ nfs4_owner_maxsz + \
nfs4_group_maxsz + \ nfs4_group_maxsz + \
nfs4_label_maxsz + \ nfs4_label_maxsz + \
4 + 4) 1 + nfstime4_maxsz + \
1 + nfstime4_maxsz)
#define encode_savefh_maxsz (op_encode_hdr_maxsz) #define encode_savefh_maxsz (op_encode_hdr_maxsz)
#define decode_savefh_maxsz (op_decode_hdr_maxsz) #define decode_savefh_maxsz (op_decode_hdr_maxsz)
#define encode_restorefh_maxsz (op_encode_hdr_maxsz) #define encode_restorefh_maxsz (op_encode_hdr_maxsz)
@ -957,6 +960,35 @@ static void encode_uint64(struct xdr_stream *xdr, u64 n)
WARN_ON_ONCE(xdr_stream_encode_u64(xdr, n) < 0); WARN_ON_ONCE(xdr_stream_encode_u64(xdr, n) < 0);
} }
static ssize_t xdr_encode_bitmap4(struct xdr_stream *xdr,
const __u32 *bitmap, size_t len)
{
ssize_t ret;
/* Trim empty words */
while (len > 0 && bitmap[len-1] == 0)
len--;
ret = xdr_stream_encode_uint32_array(xdr, bitmap, len);
if (WARN_ON_ONCE(ret < 0))
return ret;
return len;
}
static size_t mask_bitmap4(const __u32 *bitmap, const __u32 *mask,
__u32 *res, size_t len)
{
size_t i;
__u32 tmp;
while (len > 0 && (bitmap[len-1] == 0 || mask[len-1] == 0))
len--;
for (i = len; i-- > 0;) {
tmp = bitmap[i] & mask[i];
res[i] = tmp;
}
return len;
}
static void encode_nfs4_seqid(struct xdr_stream *xdr, static void encode_nfs4_seqid(struct xdr_stream *xdr,
const struct nfs_seqid *seqid) const struct nfs_seqid *seqid)
{ {
@ -1011,6 +1043,14 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
encode_opaque_fixed(xdr, verf->data, NFS4_VERIFIER_SIZE); encode_opaque_fixed(xdr, verf->data, NFS4_VERIFIER_SIZE);
} }
static __be32 *
xdr_encode_nfstime4(__be32 *p, const struct timespec *t)
{
p = xdr_encode_hyper(p, (__s64)t->tv_sec);
*p++ = cpu_to_be32(t->tv_nsec);
return p;
}
static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
const struct nfs4_label *label, const struct nfs4_label *label,
const umode_t *umask, const umode_t *umask,
@ -1022,9 +1062,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
int owner_namelen = 0; int owner_namelen = 0;
int owner_grouplen = 0; int owner_grouplen = 0;
__be32 *p; __be32 *p;
unsigned i;
uint32_t len = 0; uint32_t len = 0;
uint32_t bmval_len;
uint32_t bmval[3] = { 0 }; uint32_t bmval[3] = { 0 };
/* /*
@ -1072,7 +1110,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
if (attrmask[1] & FATTR4_WORD1_TIME_ACCESS_SET) { if (attrmask[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
if (iap->ia_valid & ATTR_ATIME_SET) { if (iap->ia_valid & ATTR_ATIME_SET) {
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET; bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
len += 16; len += 4 + (nfstime4_maxsz << 2);
} else if (iap->ia_valid & ATTR_ATIME) { } else if (iap->ia_valid & ATTR_ATIME) {
bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET; bmval[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
len += 4; len += 4;
@ -1081,7 +1119,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
if (attrmask[1] & FATTR4_WORD1_TIME_MODIFY_SET) { if (attrmask[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
if (iap->ia_valid & ATTR_MTIME_SET) { if (iap->ia_valid & ATTR_MTIME_SET) {
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET; bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
len += 16; len += 4 + (nfstime4_maxsz << 2);
} else if (iap->ia_valid & ATTR_MTIME) { } else if (iap->ia_valid & ATTR_MTIME) {
bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET; bmval[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
len += 4; len += 4;
@ -1093,19 +1131,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
bmval[2] |= FATTR4_WORD2_SECURITY_LABEL; bmval[2] |= FATTR4_WORD2_SECURITY_LABEL;
} }
if (bmval[2] != 0) xdr_encode_bitmap4(xdr, bmval, ARRAY_SIZE(bmval));
bmval_len = 3; xdr_stream_encode_opaque_inline(xdr, (void **)&p, len);
else if (bmval[1] != 0)
bmval_len = 2;
else
bmval_len = 1;
p = reserve_space(xdr, 4 + (bmval_len << 2) + 4 + len);
*p++ = cpu_to_be32(bmval_len);
for (i = 0; i < bmval_len; i++)
*p++ = cpu_to_be32(bmval[i]);
*p++ = cpu_to_be32(len);
if (bmval[0] & FATTR4_WORD0_SIZE) if (bmval[0] & FATTR4_WORD0_SIZE)
p = xdr_encode_hyper(p, iap->ia_size); p = xdr_encode_hyper(p, iap->ia_size);
@ -1118,16 +1145,14 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) { if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
if (iap->ia_valid & ATTR_ATIME_SET) { if (iap->ia_valid & ATTR_ATIME_SET) {
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME); *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
p = xdr_encode_hyper(p, (s64)iap->ia_atime.tv_sec); p = xdr_encode_nfstime4(p, &iap->ia_atime);
*p++ = cpu_to_be32(iap->ia_atime.tv_nsec);
} else } else
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME); *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
} }
if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
if (iap->ia_valid & ATTR_MTIME_SET) { if (iap->ia_valid & ATTR_MTIME_SET) {
*p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME); *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME);
p = xdr_encode_hyper(p, (s64)iap->ia_mtime.tv_sec); p = xdr_encode_nfstime4(p, &iap->ia_mtime);
*p++ = cpu_to_be32(iap->ia_mtime.tv_nsec);
} else } else
*p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME); *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME);
} }
@ -1199,85 +1224,45 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
create->server, create->server->attr_bitmask); create->server, create->server->attr_bitmask);
} }
static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr) static void encode_getattr(struct xdr_stream *xdr,
const __u32 *bitmap, const __u32 *mask, size_t len,
struct compound_hdr *hdr)
{ {
__be32 *p; __u32 masked_bitmap[nfs4_fattr_bitmap_maxsz];
encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr); encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr);
p = reserve_space(xdr, 8); if (mask) {
*p++ = cpu_to_be32(1); if (WARN_ON_ONCE(len > ARRAY_SIZE(masked_bitmap)))
*p = cpu_to_be32(bitmap); len = ARRAY_SIZE(masked_bitmap);
} len = mask_bitmap4(bitmap, mask, masked_bitmap, len);
bitmap = masked_bitmap;
static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1, struct compound_hdr *hdr)
{
__be32 *p;
encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr);
p = reserve_space(xdr, 12);
*p++ = cpu_to_be32(2);
*p++ = cpu_to_be32(bm0);
*p = cpu_to_be32(bm1);
}
static void
encode_getattr_three(struct xdr_stream *xdr,
uint32_t bm0, uint32_t bm1, uint32_t bm2,
struct compound_hdr *hdr)
{
__be32 *p;
encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr);
if (bm2) {
p = reserve_space(xdr, 16);
*p++ = cpu_to_be32(3);
*p++ = cpu_to_be32(bm0);
*p++ = cpu_to_be32(bm1);
*p = cpu_to_be32(bm2);
} else if (bm1) {
p = reserve_space(xdr, 12);
*p++ = cpu_to_be32(2);
*p++ = cpu_to_be32(bm0);
*p = cpu_to_be32(bm1);
} else {
p = reserve_space(xdr, 8);
*p++ = cpu_to_be32(1);
*p = cpu_to_be32(bm0);
} }
xdr_encode_bitmap4(xdr, bitmap, len);
} }
static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{ {
encode_getattr_three(xdr, bitmask[0] & nfs4_fattr_bitmap[0], encode_getattr(xdr, nfs4_fattr_bitmap, bitmask,
bitmask[1] & nfs4_fattr_bitmap[1], ARRAY_SIZE(nfs4_fattr_bitmap), hdr);
bitmask[2] & nfs4_fattr_bitmap[2],
hdr);
} }
static void encode_getfattr_open(struct xdr_stream *xdr, const u32 *bitmask, static void encode_getfattr_open(struct xdr_stream *xdr, const u32 *bitmask,
const u32 *open_bitmap, const u32 *open_bitmap,
struct compound_hdr *hdr) struct compound_hdr *hdr)
{ {
encode_getattr_three(xdr, encode_getattr(xdr, open_bitmap, bitmask, 3, hdr);
bitmask[0] & open_bitmap[0],
bitmask[1] & open_bitmap[1],
bitmask[2] & open_bitmap[2],
hdr);
} }
static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{ {
encode_getattr_three(xdr, encode_getattr(xdr, nfs4_fsinfo_bitmap, bitmask,
bitmask[0] & nfs4_fsinfo_bitmap[0], ARRAY_SIZE(nfs4_fsinfo_bitmap), hdr);
bitmask[1] & nfs4_fsinfo_bitmap[1],
bitmask[2] & nfs4_fsinfo_bitmap[2],
hdr);
} }
static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{ {
encode_getattr_two(xdr, bitmask[0] & nfs4_fs_locations_bitmap[0], encode_getattr(xdr, nfs4_fs_locations_bitmap, bitmask,
bitmask[1] & nfs4_fs_locations_bitmap[1], hdr); ARRAY_SIZE(nfs4_fs_locations_bitmap), hdr);
} }
static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr) static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
@ -2116,7 +2101,8 @@ static void nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_sequence(xdr, &args->seq_args, &hdr); encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr); encode_putfh(xdr, args->fh, &hdr);
encode_access(xdr, args->access, &hdr); encode_access(xdr, args->access, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr); if (args->bitmask)
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr); encode_nops(&hdr);
} }
@ -2558,13 +2544,17 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
struct compound_hdr hdr = { struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args), .minorversion = nfs4_xdr_minorversion(&args->seq_args),
}; };
const __u32 nfs4_acl_bitmap[1] = {
[0] = FATTR4_WORD0_ACL,
};
uint32_t replen; uint32_t replen;
encode_compound_hdr(xdr, req, &hdr); encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr); encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr); encode_putfh(xdr, args->fh, &hdr);
replen = hdr.replen + op_decode_hdr_maxsz; replen = hdr.replen + op_decode_hdr_maxsz;
encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr); encode_getattr(xdr, nfs4_acl_bitmap, NULL,
ARRAY_SIZE(nfs4_acl_bitmap), &hdr);
xdr_inline_pages(&req->rq_rcv_buf, replen << 2, xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
args->acl_pages, 0, args->acl_len); args->acl_pages, 0, args->acl_len);
@ -2643,8 +2633,8 @@ static void nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr); encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr); encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr); encode_putfh(xdr, args->fh, &hdr);
encode_getattr_one(xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0], encode_getattr(xdr, nfs4_pathconf_bitmap, args->bitmask,
&hdr); ARRAY_SIZE(nfs4_pathconf_bitmap), &hdr);
encode_nops(&hdr); encode_nops(&hdr);
} }
@ -2662,8 +2652,8 @@ static void nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr); encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr); encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr); encode_putfh(xdr, args->fh, &hdr);
encode_getattr_two(xdr, args->bitmask[0] & nfs4_statfs_bitmap[0], encode_getattr(xdr, nfs4_statfs_bitmap, args->bitmask,
args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr); ARRAY_SIZE(nfs4_statfs_bitmap), &hdr);
encode_nops(&hdr); encode_nops(&hdr);
} }
@ -2683,7 +2673,7 @@ static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr); encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr); encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fhandle, &hdr); encode_putfh(xdr, args->fhandle, &hdr);
encode_getattr_three(xdr, bitmask[0], bitmask[1], bitmask[2], &hdr); encode_getattr(xdr, bitmask, NULL, 3, &hdr);
encode_nops(&hdr); encode_nops(&hdr);
} }
@ -3217,34 +3207,27 @@ static int decode_ace(struct xdr_stream *xdr, void *ace)
return -EIO; return -EIO;
} }
static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap) static ssize_t
decode_bitmap4(struct xdr_stream *xdr, uint32_t *bitmap, size_t sz)
{ {
uint32_t bmlen; ssize_t ret;
__be32 *p;
p = xdr_inline_decode(xdr, 4); ret = xdr_stream_decode_uint32_array(xdr, bitmap, sz);
if (unlikely(!p)) if (likely(ret >= 0))
goto out_overflow; return ret;
bmlen = be32_to_cpup(p); if (ret == -EMSGSIZE)
return sz;
bitmap[0] = bitmap[1] = bitmap[2] = 0;
p = xdr_inline_decode(xdr, (bmlen << 2));
if (unlikely(!p))
goto out_overflow;
if (bmlen > 0) {
bitmap[0] = be32_to_cpup(p++);
if (bmlen > 1) {
bitmap[1] = be32_to_cpup(p++);
if (bmlen > 2)
bitmap[2] = be32_to_cpup(p);
}
}
return 0;
out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
} }
static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
{
ssize_t ret;
ret = decode_bitmap4(xdr, bitmap, 3);
return ret < 0 ? ret : 0;
}
static int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, unsigned int *savep) static int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, unsigned int *savep)
{ {
__be32 *p; __be32 *p;
@ -3980,7 +3963,7 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap,
bitmap[1] &= ~FATTR4_WORD1_OWNER; bitmap[1] &= ~FATTR4_WORD1_OWNER;
if (owner_name != NULL) { if (owner_name != NULL) {
len = decode_nfs4_string(xdr, owner_name, GFP_NOWAIT); len = decode_nfs4_string(xdr, owner_name, GFP_NOIO);
if (len <= 0) if (len <= 0)
goto out; goto out;
dprintk("%s: name=%s\n", __func__, owner_name->data); dprintk("%s: name=%s\n", __func__, owner_name->data);
@ -4015,7 +3998,7 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap,
bitmap[1] &= ~FATTR4_WORD1_OWNER_GROUP; bitmap[1] &= ~FATTR4_WORD1_OWNER_GROUP;
if (group_name != NULL) { if (group_name != NULL) {
len = decode_nfs4_string(xdr, group_name, GFP_NOWAIT); len = decode_nfs4_string(xdr, group_name, GFP_NOIO);
if (len <= 0) if (len <= 0)
goto out; goto out;
dprintk("%s: name=%s\n", __func__, group_name->data); dprintk("%s: name=%s\n", __func__, group_name->data);
@ -4155,19 +4138,25 @@ out_overflow:
return -EIO; return -EIO;
} }
static __be32 *
xdr_decode_nfstime4(__be32 *p, struct timespec *t)
{
__u64 sec;
p = xdr_decode_hyper(p, &sec);
t-> tv_sec = (time_t)sec;
t->tv_nsec = be32_to_cpup(p++);
return p;
}
static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time) static int decode_attr_time(struct xdr_stream *xdr, struct timespec *time)
{ {
__be32 *p; __be32 *p;
uint64_t sec;
uint32_t nsec;
p = xdr_inline_decode(xdr, 12); p = xdr_inline_decode(xdr, nfstime4_maxsz << 2);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
p = xdr_decode_hyper(p, &sec); xdr_decode_nfstime4(p, time);
nsec = be32_to_cpup(p);
time->tv_sec = (time_t)sec;
time->tv_nsec = (long)nsec;
return 0; return 0;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
@ -5470,21 +5459,13 @@ decode_savefh(struct xdr_stream *xdr)
static int decode_setattr(struct xdr_stream *xdr) static int decode_setattr(struct xdr_stream *xdr)
{ {
__be32 *p;
uint32_t bmlen;
int status; int status;
status = decode_op_hdr(xdr, OP_SETATTR); status = decode_op_hdr(xdr, OP_SETATTR);
if (status) if (status)
return status; return status;
p = xdr_inline_decode(xdr, 4); if (decode_bitmap4(xdr, NULL, 0) >= 0)
if (unlikely(!p))
goto out_overflow;
bmlen = be32_to_cpup(p);
p = xdr_inline_decode(xdr, bmlen << 2);
if (likely(p))
return 0; return 0;
out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
} }
@ -6255,7 +6236,8 @@ static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
status = decode_access(xdr, &res->supported, &res->access); status = decode_access(xdr, &res->supported, &res->access);
if (status != 0) if (status != 0)
goto out; goto out;
decode_getfattr(xdr, res->fattr, res->server); if (res->fattr)
decode_getfattr(xdr, res->fattr, res->server);
out: out:
return status; return status;
} }
@ -7535,6 +7517,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
unsigned int savep; unsigned int savep;
uint32_t bitmap[3] = {0}; uint32_t bitmap[3] = {0};
uint32_t len; uint32_t len;
uint64_t new_cookie;
__be32 *p = xdr_inline_decode(xdr, 4); __be32 *p = xdr_inline_decode(xdr, 4);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
@ -7551,8 +7534,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
p = xdr_inline_decode(xdr, 12); p = xdr_inline_decode(xdr, 12);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
entry->prev_cookie = entry->cookie; p = xdr_decode_hyper(p, &new_cookie);
p = xdr_decode_hyper(p, &entry->cookie);
entry->len = be32_to_cpup(p); entry->len = be32_to_cpup(p);
p = xdr_inline_decode(xdr, entry->len); p = xdr_inline_decode(xdr, entry->len);
@ -7586,6 +7568,9 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
if (entry->fattr->valid & NFS_ATTR_FATTR_TYPE) if (entry->fattr->valid & NFS_ATTR_FATTR_TYPE)
entry->d_type = nfs_umode_to_dtype(entry->fattr->mode); entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
entry->prev_cookie = entry->cookie;
entry->cookie = new_cookie;
return 0; return 0;
out_overflow: out_overflow:

View File

@ -300,11 +300,11 @@ out:
} }
static int static int
nfs_proc_remove(struct inode *dir, const struct qstr *name) nfs_proc_remove(struct inode *dir, struct dentry *dentry)
{ {
struct nfs_removeargs arg = { struct nfs_removeargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.name = *name, .name = dentry->d_name,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs_procedures[NFSPROC_REMOVE], .rpc_proc = &nfs_procedures[NFSPROC_REMOVE],
@ -312,7 +312,7 @@ nfs_proc_remove(struct inode *dir, const struct qstr *name)
}; };
int status; int status;
dprintk("NFS call remove %s\n", name->name); dprintk("NFS call remove %pd2\n",dentry);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nfs_mark_for_revalidate(dir); nfs_mark_for_revalidate(dir);
@ -321,7 +321,7 @@ nfs_proc_remove(struct inode *dir, const struct qstr *name)
} }
static void static void
nfs_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dentry)
{ {
msg->rpc_proc = &nfs_procedures[NFSPROC_REMOVE]; msg->rpc_proc = &nfs_procedures[NFSPROC_REMOVE];
} }
@ -338,7 +338,9 @@ static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
} }
static void static void
nfs_proc_rename_setup(struct rpc_message *msg, struct inode *dir) nfs_proc_rename_setup(struct rpc_message *msg,
struct dentry *old_dentry,
struct dentry *new_dentry)
{ {
msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME]; msg->rpc_proc = &nfs_procedures[NFSPROC_RENAME];
} }
@ -671,12 +673,6 @@ static int nfs_have_delegation(struct inode *inode, fmode_t flags)
return 0; return 0;
} }
static int nfs_return_delegation(struct inode *inode)
{
nfs_wb_all(inode);
return 0;
}
static const struct inode_operations nfs_dir_inode_operations = { static const struct inode_operations nfs_dir_inode_operations = {
.create = nfs_create, .create = nfs_create,
.lookup = nfs_lookup, .lookup = nfs_lookup,
@ -741,7 +737,6 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.lock_check_bounds = nfs_lock_check_bounds, .lock_check_bounds = nfs_lock_check_bounds,
.close_context = nfs_close_context, .close_context = nfs_close_context,
.have_delegation = nfs_have_delegation, .have_delegation = nfs_have_delegation,
.return_delegation = nfs_return_delegation,
.alloc_client = nfs_alloc_client, .alloc_client = nfs_alloc_client,
.init_client = nfs_init_client, .init_client = nfs_init_client,
.free_client = nfs_free_client, .free_client = nfs_free_client,

View File

@ -105,7 +105,7 @@ static void nfs_do_call_unlink(struct nfs_unlinkdata *data)
data->args.fh = NFS_FH(dir); data->args.fh = NFS_FH(dir);
nfs_fattr_init(data->res.dir_attr); nfs_fattr_init(data->res.dir_attr);
NFS_PROTO(dir)->unlink_setup(&msg, dir); NFS_PROTO(dir)->unlink_setup(&msg, data->dentry);
task_setup_data.rpc_client = NFS_CLIENT(dir); task_setup_data.rpc_client = NFS_CLIENT(dir);
task = rpc_run_task(&task_setup_data); task = rpc_run_task(&task_setup_data);
@ -386,7 +386,7 @@ nfs_async_rename(struct inode *old_dir, struct inode *new_dir,
nfs_sb_active(old_dir->i_sb); nfs_sb_active(old_dir->i_sb);
NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dir); NFS_PROTO(data->old_dir)->rename_setup(&msg, old_dentry, new_dentry);
return rpc_run_task(&task_setup_data); return rpc_run_task(&task_setup_data);
} }
@ -463,9 +463,6 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
fileid = NFS_FILEID(d_inode(dentry)); fileid = NFS_FILEID(d_inode(dentry));
/* Return delegation in anticipation of the rename */
NFS_PROTO(d_inode(dentry))->return_delegation(d_inode(dentry));
sdentry = NULL; sdentry = NULL;
do { do {
int slen; int slen;

View File

@ -231,6 +231,7 @@ static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int c
if (i_size >= end) if (i_size >= end)
goto out; goto out;
i_size_write(inode, end); i_size_write(inode, end);
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_SIZE;
nfs_inc_stats(inode, NFSIOS_EXTENDWRITE); nfs_inc_stats(inode, NFSIOS_EXTENDWRITE);
out: out:
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
@ -1562,8 +1563,11 @@ static int nfs_writeback_done(struct rpc_task *task,
} }
/* Deal with the suid/sgid bit corner case */ /* Deal with the suid/sgid bit corner case */
if (nfs_should_remove_suid(inode)) if (nfs_should_remove_suid(inode)) {
nfs_mark_for_revalidate(inode); spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_OTHER;
spin_unlock(&inode->i_lock);
}
return 0; return 0;
} }

View File

@ -198,14 +198,24 @@ struct nfs_inode {
/* /*
* Cache validity bit flags * Cache validity bit flags
*/ */
#define NFS_INO_INVALID_ATTR 0x0001 /* cached attrs are invalid */ #define NFS_INO_INVALID_DATA BIT(1) /* cached data is invalid */
#define NFS_INO_INVALID_DATA 0x0002 /* cached data is invalid */ #define NFS_INO_INVALID_ATIME BIT(2) /* cached atime is invalid */
#define NFS_INO_INVALID_ATIME 0x0004 /* cached atime is invalid */ #define NFS_INO_INVALID_ACCESS BIT(3) /* cached access cred invalid */
#define NFS_INO_INVALID_ACCESS 0x0008 /* cached access cred invalid */ #define NFS_INO_INVALID_ACL BIT(4) /* cached acls are invalid */
#define NFS_INO_INVALID_ACL 0x0010 /* cached acls are invalid */ #define NFS_INO_REVAL_PAGECACHE BIT(5) /* must revalidate pagecache */
#define NFS_INO_REVAL_PAGECACHE 0x0020 /* must revalidate pagecache */ #define NFS_INO_REVAL_FORCED BIT(6) /* force revalidation ignoring a delegation */
#define NFS_INO_REVAL_FORCED 0x0040 /* force revalidation ignoring a delegation */ #define NFS_INO_INVALID_LABEL BIT(7) /* cached label is invalid */
#define NFS_INO_INVALID_LABEL 0x0080 /* cached label is invalid */ #define NFS_INO_INVALID_CHANGE BIT(8) /* cached change is invalid */
#define NFS_INO_INVALID_CTIME BIT(9) /* cached ctime is invalid */
#define NFS_INO_INVALID_MTIME BIT(10) /* cached mtime is invalid */
#define NFS_INO_INVALID_SIZE BIT(11) /* cached size is invalid */
#define NFS_INO_INVALID_OTHER BIT(12) /* other attrs are invalid */
#define NFS_INO_INVALID_ATTR (NFS_INO_INVALID_CHANGE \
| NFS_INO_INVALID_CTIME \
| NFS_INO_INVALID_MTIME \
| NFS_INO_INVALID_SIZE \
| NFS_INO_INVALID_OTHER) /* inode metadata is invalid */
/* /*
* Bit offsets in flags field * Bit offsets in flags field
@ -292,10 +302,11 @@ static inline void nfs_mark_for_revalidate(struct inode *inode)
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
nfsi->cache_validity |= NFS_INO_INVALID_ATTR | nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE
NFS_INO_REVAL_PAGECACHE | | NFS_INO_INVALID_ACCESS
NFS_INO_INVALID_ACCESS | | NFS_INO_INVALID_ACL
NFS_INO_INVALID_ACL; | NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_CTIME;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
nfsi->cache_validity |= NFS_INO_INVALID_DATA; nfsi->cache_validity |= NFS_INO_INVALID_DATA;
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);

View File

@ -1590,11 +1590,13 @@ struct nfs_rpc_ops {
unsigned int); unsigned int);
int (*create) (struct inode *, struct dentry *, int (*create) (struct inode *, struct dentry *,
struct iattr *, int); struct iattr *, int);
int (*remove) (struct inode *, const struct qstr *); int (*remove) (struct inode *, struct dentry *);
void (*unlink_setup) (struct rpc_message *, struct inode *dir); void (*unlink_setup) (struct rpc_message *, struct dentry *);
void (*unlink_rpc_prepare) (struct rpc_task *, struct nfs_unlinkdata *); void (*unlink_rpc_prepare) (struct rpc_task *, struct nfs_unlinkdata *);
int (*unlink_done) (struct rpc_task *, struct inode *); int (*unlink_done) (struct rpc_task *, struct inode *);
void (*rename_setup) (struct rpc_message *msg, struct inode *dir); void (*rename_setup) (struct rpc_message *msg,
struct dentry *old_dentry,
struct dentry *new_dentry);
void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *); void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *);
int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir); int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir);
int (*link) (struct inode *, struct inode *, const struct qstr *); int (*link) (struct inode *, struct inode *, const struct qstr *);
@ -1633,7 +1635,6 @@ struct nfs_rpc_ops {
struct iattr *iattr, struct iattr *iattr,
int *); int *);
int (*have_delegation)(struct inode *, fmode_t); int (*have_delegation)(struct inode *, fmode_t);
int (*return_delegation)(struct inode *);
struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *); struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *);
struct nfs_client *(*init_client) (struct nfs_client *, struct nfs_client *(*init_client) (struct nfs_client *,
const struct nfs_client_initdata *); const struct nfs_client_initdata *);

View File

@ -217,5 +217,12 @@ void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *, struct rpc_xprt *);
bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt, bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
const struct sockaddr *sap); const struct sockaddr *sap);
void rpc_cleanup_clids(void); void rpc_cleanup_clids(void);
static inline int rpc_reply_expected(struct rpc_task *task)
{
return (task->tk_msg.rpc_proc != NULL) &&
(task->tk_msg.rpc_proc->p_decode != NULL);
}
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_CLNT_H */ #endif /* _LINUX_SUNRPC_CLNT_H */

View File

@ -253,6 +253,12 @@ xdr_stream_remaining(const struct xdr_stream *xdr)
return xdr->nwords << 2; return xdr->nwords << 2;
} }
ssize_t xdr_stream_decode_opaque(struct xdr_stream *xdr, void *ptr,
size_t size);
ssize_t xdr_stream_decode_opaque_dup(struct xdr_stream *xdr, void **ptr,
size_t maxlen, gfp_t gfp_flags);
ssize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str,
size_t size);
ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str, ssize_t xdr_stream_decode_string_dup(struct xdr_stream *xdr, char **str,
size_t maxlen, gfp_t gfp_flags); size_t maxlen, gfp_t gfp_flags);
/** /**
@ -312,6 +318,31 @@ xdr_stream_encode_u64(struct xdr_stream *xdr, __u64 n)
return len; return len;
} }
/**
* xdr_stream_encode_opaque_inline - Encode opaque xdr data
* @xdr: pointer to xdr_stream
* @ptr: pointer to void pointer
* @len: size of object
*
* Return values:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
static inline ssize_t
xdr_stream_encode_opaque_inline(struct xdr_stream *xdr, void **ptr, size_t len)
{
size_t count = sizeof(__u32) + xdr_align_size(len);
__be32 *p = xdr_reserve_space(xdr, count);
if (unlikely(!p)) {
*ptr = NULL;
return -EMSGSIZE;
}
xdr_encode_opaque(p, NULL, len);
*ptr = ++p;
return count;
}
/** /**
* xdr_stream_encode_opaque_fixed - Encode fixed length opaque xdr data * xdr_stream_encode_opaque_fixed - Encode fixed length opaque xdr data
* @xdr: pointer to xdr_stream * @xdr: pointer to xdr_stream
@ -355,6 +386,31 @@ xdr_stream_encode_opaque(struct xdr_stream *xdr, const void *ptr, size_t len)
return count; return count;
} }
/**
* xdr_stream_encode_uint32_array - Encode variable length array of integers
* @xdr: pointer to xdr_stream
* @array: array of integers
* @array_size: number of elements in @array
*
* Return values:
* On success, returns length in bytes of XDR buffer consumed
* %-EMSGSIZE on XDR buffer overflow
*/
static inline ssize_t
xdr_stream_encode_uint32_array(struct xdr_stream *xdr,
const __u32 *array, size_t array_size)
{
ssize_t ret = (array_size+1) * sizeof(__u32);
__be32 *p = xdr_reserve_space(xdr, ret);
if (unlikely(!p))
return -EMSGSIZE;
*p++ = cpu_to_be32(array_size);
for (; array_size > 0; p++, array++, array_size--)
*p = cpu_to_be32p(array);
return ret;
}
/** /**
* xdr_stream_decode_u32 - Decode a 32-bit integer * xdr_stream_decode_u32 - Decode a 32-bit integer
* @xdr: pointer to xdr_stream * @xdr: pointer to xdr_stream
@ -432,6 +488,44 @@ xdr_stream_decode_opaque_inline(struct xdr_stream *xdr, void **ptr, size_t maxle
} }
return len; return len;
} }
/**
* xdr_stream_decode_uint32_array - Decode variable length array of integers
* @xdr: pointer to xdr_stream
* @array: location to store the integer array or NULL
* @array_size: number of elements to store
*
* Return values:
* On success, returns number of elements stored in @array
* %-EBADMSG on XDR buffer overflow
* %-EMSGSIZE if the size of the array exceeds @array_size
*/
static inline ssize_t
xdr_stream_decode_uint32_array(struct xdr_stream *xdr,
__u32 *array, size_t array_size)
{
__be32 *p;
__u32 len;
ssize_t retval;
if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0))
return -EBADMSG;
p = xdr_inline_decode(xdr, len * sizeof(*p));
if (unlikely(!p))
return -EBADMSG;
if (array == NULL)
return len;
if (len <= array_size) {
if (len < array_size)
memset(array+len, 0, (array_size-len)*sizeof(*array));
array_size = len;
retval = len;
} else
retval = -EMSGSIZE;
for (; array_size > 0; p++, array++, array_size--)
*array = be32_to_cpup(p);
return retval;
}
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _SUNRPC_XDR_H_ */ #endif /* _SUNRPC_XDR_H_ */

View File

@ -197,7 +197,7 @@ struct rpc_xprt {
struct list_head free; /* free slots */ struct list_head free; /* free slots */
unsigned int max_reqs; /* max number of slots */ unsigned int max_reqs; /* max number of slots */
unsigned int min_reqs; /* min number of slots */ unsigned int min_reqs; /* min number of slots */
atomic_t num_reqs; /* total slots */ unsigned int num_reqs; /* total slots */
unsigned long state; /* transport state */ unsigned long state; /* transport state */
unsigned char resvport : 1; /* use a reserved port */ unsigned char resvport : 1; /* use a reserved port */
atomic_t swapper; /* we're swapping over this atomic_t swapper; /* we're swapping over this
@ -373,6 +373,7 @@ void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action);
void xprt_write_space(struct rpc_xprt *xprt); void xprt_write_space(struct rpc_xprt *xprt);
void xprt_adjust_cwnd(struct rpc_xprt *xprt, struct rpc_task *task, int result); void xprt_adjust_cwnd(struct rpc_xprt *xprt, struct rpc_task *task, int result);
struct rpc_rqst * xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid); struct rpc_rqst * xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid);
void xprt_update_rtt(struct rpc_task *task);
void xprt_complete_rqst(struct rpc_task *task, int copied); void xprt_complete_rqst(struct rpc_task *task, int copied);
void xprt_pin_rqst(struct rpc_rqst *req); void xprt_pin_rqst(struct rpc_rqst *req);
void xprt_unpin_rqst(struct rpc_rqst *req); void xprt_unpin_rqst(struct rpc_rqst *req);

View File

@ -50,9 +50,9 @@ DEFINE_EVENT(rpc_task_status, rpc_bind_status,
); );
TRACE_EVENT(rpc_connect_status, TRACE_EVENT(rpc_connect_status,
TP_PROTO(struct rpc_task *task, int status), TP_PROTO(const struct rpc_task *task),
TP_ARGS(task, status), TP_ARGS(task),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(unsigned int, task_id) __field(unsigned int, task_id)
@ -63,7 +63,7 @@ TRACE_EVENT(rpc_connect_status,
TP_fast_assign( TP_fast_assign(
__entry->task_id = task->tk_pid; __entry->task_id = task->tk_pid;
__entry->client_id = task->tk_client->cl_clid; __entry->client_id = task->tk_client->cl_clid;
__entry->status = status; __entry->status = task->tk_status;
), ),
TP_printk("task:%u@%u status=%d", TP_printk("task:%u@%u status=%d",
@ -103,9 +103,9 @@ TRACE_EVENT(rpc_request,
DECLARE_EVENT_CLASS(rpc_task_running, DECLARE_EVENT_CLASS(rpc_task_running,
TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const void *action), TP_PROTO(const struct rpc_task *task, const void *action),
TP_ARGS(clnt, task, action), TP_ARGS(task, action),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(unsigned int, task_id) __field(unsigned int, task_id)
@ -117,7 +117,8 @@ DECLARE_EVENT_CLASS(rpc_task_running,
), ),
TP_fast_assign( TP_fast_assign(
__entry->client_id = clnt ? clnt->cl_clid : -1; __entry->client_id = task->tk_client ?
task->tk_client->cl_clid : -1;
__entry->task_id = task->tk_pid; __entry->task_id = task->tk_pid;
__entry->action = action; __entry->action = action;
__entry->runstate = task->tk_runstate; __entry->runstate = task->tk_runstate;
@ -136,33 +137,33 @@ DECLARE_EVENT_CLASS(rpc_task_running,
DEFINE_EVENT(rpc_task_running, rpc_task_begin, DEFINE_EVENT(rpc_task_running, rpc_task_begin,
TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const void *action), TP_PROTO(const struct rpc_task *task, const void *action),
TP_ARGS(clnt, task, action) TP_ARGS(task, action)
); );
DEFINE_EVENT(rpc_task_running, rpc_task_run_action, DEFINE_EVENT(rpc_task_running, rpc_task_run_action,
TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const void *action), TP_PROTO(const struct rpc_task *task, const void *action),
TP_ARGS(clnt, task, action) TP_ARGS(task, action)
); );
DEFINE_EVENT(rpc_task_running, rpc_task_complete, DEFINE_EVENT(rpc_task_running, rpc_task_complete,
TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const void *action), TP_PROTO(const struct rpc_task *task, const void *action),
TP_ARGS(clnt, task, action) TP_ARGS(task, action)
); );
DECLARE_EVENT_CLASS(rpc_task_queued, DECLARE_EVENT_CLASS(rpc_task_queued,
TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const struct rpc_wait_queue *q), TP_PROTO(const struct rpc_task *task, const struct rpc_wait_queue *q),
TP_ARGS(clnt, task, q), TP_ARGS(task, q),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(unsigned int, task_id) __field(unsigned int, task_id)
@ -175,7 +176,8 @@ DECLARE_EVENT_CLASS(rpc_task_queued,
), ),
TP_fast_assign( TP_fast_assign(
__entry->client_id = clnt ? clnt->cl_clid : -1; __entry->client_id = task->tk_client ?
task->tk_client->cl_clid : -1;
__entry->task_id = task->tk_pid; __entry->task_id = task->tk_pid;
__entry->timeout = task->tk_timeout; __entry->timeout = task->tk_timeout;
__entry->runstate = task->tk_runstate; __entry->runstate = task->tk_runstate;
@ -196,20 +198,65 @@ DECLARE_EVENT_CLASS(rpc_task_queued,
DEFINE_EVENT(rpc_task_queued, rpc_task_sleep, DEFINE_EVENT(rpc_task_queued, rpc_task_sleep,
TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const struct rpc_wait_queue *q), TP_PROTO(const struct rpc_task *task, const struct rpc_wait_queue *q),
TP_ARGS(clnt, task, q) TP_ARGS(task, q)
); );
DEFINE_EVENT(rpc_task_queued, rpc_task_wakeup, DEFINE_EVENT(rpc_task_queued, rpc_task_wakeup,
TP_PROTO(const struct rpc_clnt *clnt, const struct rpc_task *task, const struct rpc_wait_queue *q), TP_PROTO(const struct rpc_task *task, const struct rpc_wait_queue *q),
TP_ARGS(clnt, task, q) TP_ARGS(task, q)
); );
TRACE_EVENT(rpc_stats_latency,
TP_PROTO(
const struct rpc_task *task,
ktime_t backlog,
ktime_t rtt,
ktime_t execute
),
TP_ARGS(task, backlog, rtt, execute),
TP_STRUCT__entry(
__field(u32, xid)
__field(int, version)
__string(progname, task->tk_client->cl_program->name)
__string(procname, rpc_proc_name(task))
__field(unsigned long, backlog)
__field(unsigned long, rtt)
__field(unsigned long, execute)
__string(addr,
task->tk_xprt->address_strings[RPC_DISPLAY_ADDR])
__string(port,
task->tk_xprt->address_strings[RPC_DISPLAY_PORT])
),
TP_fast_assign(
__entry->xid = be32_to_cpu(task->tk_rqstp->rq_xid);
__entry->version = task->tk_client->cl_vers;
__assign_str(progname, task->tk_client->cl_program->name)
__assign_str(procname, rpc_proc_name(task))
__entry->backlog = ktime_to_us(backlog);
__entry->rtt = ktime_to_us(rtt);
__entry->execute = ktime_to_us(execute);
__assign_str(addr,
task->tk_xprt->address_strings[RPC_DISPLAY_ADDR]);
__assign_str(port,
task->tk_xprt->address_strings[RPC_DISPLAY_PORT]);
),
TP_printk("peer=[%s]:%s xid=0x%08x %sv%d %s backlog=%lu rtt=%lu execute=%lu",
__get_str(addr), __get_str(port), __entry->xid,
__get_str(progname), __entry->version, __get_str(procname),
__entry->backlog, __entry->rtt, __entry->execute)
);
/* /*
* First define the enums in the below macros to be exported to userspace * First define the enums in the below macros to be exported to userspace
* via TRACE_DEFINE_ENUM(). * via TRACE_DEFINE_ENUM().
@ -406,6 +453,27 @@ DEFINE_EVENT(rpc_xprt_event, xprt_complete_rqst,
TP_PROTO(struct rpc_xprt *xprt, __be32 xid, int status), TP_PROTO(struct rpc_xprt *xprt, __be32 xid, int status),
TP_ARGS(xprt, xid, status)); TP_ARGS(xprt, xid, status));
TRACE_EVENT(xprt_ping,
TP_PROTO(const struct rpc_xprt *xprt, int status),
TP_ARGS(xprt, status),
TP_STRUCT__entry(
__field(int, status)
__string(addr, xprt->address_strings[RPC_DISPLAY_ADDR])
__string(port, xprt->address_strings[RPC_DISPLAY_PORT])
),
TP_fast_assign(
__entry->status = status;
__assign_str(addr, xprt->address_strings[RPC_DISPLAY_ADDR]);
__assign_str(port, xprt->address_strings[RPC_DISPLAY_PORT]);
),
TP_printk("peer=[%s]:%s status=%d",
__get_str(addr), __get_str(port), __entry->status)
);
TRACE_EVENT(xs_tcp_data_ready, TRACE_EVENT(xs_tcp_data_ready,
TP_PROTO(struct rpc_xprt *xprt, int err, unsigned int total), TP_PROTO(struct rpc_xprt *xprt, int err, unsigned int total),

View File

@ -1887,7 +1887,7 @@ call_connect_status(struct rpc_task *task)
dprint_status(task); dprint_status(task);
trace_rpc_connect_status(task, status); trace_rpc_connect_status(task);
task->tk_status = 0; task->tk_status = 0;
switch (status) { switch (status) {
case -ECONNREFUSED: case -ECONNREFUSED:
@ -2014,6 +2014,9 @@ call_transmit_status(struct rpc_task *task)
case -EPERM: case -EPERM:
if (RPC_IS_SOFTCONN(task)) { if (RPC_IS_SOFTCONN(task)) {
xprt_end_transmit(task); xprt_end_transmit(task);
if (!task->tk_msg.rpc_proc->p_proc)
trace_xprt_ping(task->tk_xprt,
task->tk_status);
rpc_exit(task, task->tk_status); rpc_exit(task, task->tk_status);
break; break;
} }
@ -2112,6 +2115,9 @@ call_status(struct rpc_task *task)
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
int status; int status;
if (!task->tk_msg.rpc_proc->p_proc)
trace_xprt_ping(task->tk_xprt, task->tk_status);
if (req->rq_reply_bytes_recvd > 0 && !req->rq_bytes_sent) if (req->rq_reply_bytes_recvd > 0 && !req->rq_bytes_sent)
task->tk_status = req->rq_reply_bytes_recvd; task->tk_status = req->rq_reply_bytes_recvd;

View File

@ -276,7 +276,7 @@ static void rpc_set_active(struct rpc_task *task)
{ {
rpc_task_set_debuginfo(task); rpc_task_set_debuginfo(task);
set_bit(RPC_TASK_ACTIVE, &task->tk_runstate); set_bit(RPC_TASK_ACTIVE, &task->tk_runstate);
trace_rpc_task_begin(task->tk_client, task, NULL); trace_rpc_task_begin(task, NULL);
} }
/* /*
@ -291,7 +291,7 @@ static int rpc_complete_task(struct rpc_task *task)
unsigned long flags; unsigned long flags;
int ret; int ret;
trace_rpc_task_complete(task->tk_client, task, NULL); trace_rpc_task_complete(task, NULL);
spin_lock_irqsave(&wq->lock, flags); spin_lock_irqsave(&wq->lock, flags);
clear_bit(RPC_TASK_ACTIVE, &task->tk_runstate); clear_bit(RPC_TASK_ACTIVE, &task->tk_runstate);
@ -358,7 +358,7 @@ static void __rpc_sleep_on_priority(struct rpc_wait_queue *q,
dprintk("RPC: %5u sleep_on(queue \"%s\" time %lu)\n", dprintk("RPC: %5u sleep_on(queue \"%s\" time %lu)\n",
task->tk_pid, rpc_qname(q), jiffies); task->tk_pid, rpc_qname(q), jiffies);
trace_rpc_task_sleep(task->tk_client, task, q); trace_rpc_task_sleep(task, q);
__rpc_add_wait_queue(q, task, queue_priority); __rpc_add_wait_queue(q, task, queue_priority);
@ -428,7 +428,7 @@ static void __rpc_do_wake_up_task_on_wq(struct workqueue_struct *wq,
return; return;
} }
trace_rpc_task_wakeup(task->tk_client, task, queue); trace_rpc_task_wakeup(task, queue);
__rpc_remove_wait_queue(queue, task); __rpc_remove_wait_queue(queue, task);
@ -780,7 +780,7 @@ static void __rpc_execute(struct rpc_task *task)
} }
if (!do_action) if (!do_action)
break; break;
trace_rpc_task_run_action(task->tk_client, task, do_action); trace_rpc_task_run_action(task, do_action);
do_action(task); do_action(task);
/* /*

View File

@ -24,6 +24,8 @@
#include <linux/sunrpc/metrics.h> #include <linux/sunrpc/metrics.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <trace/events/sunrpc.h>
#include "netns.h" #include "netns.h"
#define RPCDBG_FACILITY RPCDBG_MISC #define RPCDBG_FACILITY RPCDBG_MISC
@ -148,7 +150,7 @@ void rpc_count_iostats_metrics(const struct rpc_task *task,
struct rpc_iostats *op_metrics) struct rpc_iostats *op_metrics)
{ {
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
ktime_t delta, now; ktime_t backlog, execute, now;
if (!op_metrics || !req) if (!op_metrics || !req)
return; return;
@ -164,16 +166,20 @@ void rpc_count_iostats_metrics(const struct rpc_task *task,
op_metrics->om_bytes_sent += req->rq_xmit_bytes_sent; op_metrics->om_bytes_sent += req->rq_xmit_bytes_sent;
op_metrics->om_bytes_recv += req->rq_reply_bytes_recvd; op_metrics->om_bytes_recv += req->rq_reply_bytes_recvd;
backlog = 0;
if (ktime_to_ns(req->rq_xtime)) { if (ktime_to_ns(req->rq_xtime)) {
delta = ktime_sub(req->rq_xtime, task->tk_start); backlog = ktime_sub(req->rq_xtime, task->tk_start);
op_metrics->om_queue = ktime_add(op_metrics->om_queue, delta); op_metrics->om_queue = ktime_add(op_metrics->om_queue, backlog);
} }
op_metrics->om_rtt = ktime_add(op_metrics->om_rtt, req->rq_rtt); op_metrics->om_rtt = ktime_add(op_metrics->om_rtt, req->rq_rtt);
delta = ktime_sub(now, task->tk_start); execute = ktime_sub(now, task->tk_start);
op_metrics->om_execute = ktime_add(op_metrics->om_execute, delta); op_metrics->om_execute = ktime_add(op_metrics->om_execute, execute);
spin_unlock(&op_metrics->om_lock); spin_unlock(&op_metrics->om_lock);
trace_rpc_stats_latency(req->rq_task, backlog, req->rq_rtt, execute);
} }
EXPORT_SYMBOL_GPL(rpc_count_iostats_metrics); EXPORT_SYMBOL_GPL(rpc_count_iostats_metrics);

View File

@ -37,12 +37,6 @@ struct rpc_buffer {
char data[]; char data[];
}; };
static inline int rpc_reply_expected(struct rpc_task *task)
{
return (task->tk_msg.rpc_proc != NULL) &&
(task->tk_msg.rpc_proc->p_decode != NULL);
}
static inline int sock_is_loopback(struct sock *sk) static inline int sock_is_loopback(struct sock *sk)
{ {
struct dst_entry *dst; struct dst_entry *dst;

View File

@ -1518,6 +1518,88 @@ out:
} }
EXPORT_SYMBOL_GPL(xdr_process_buf); EXPORT_SYMBOL_GPL(xdr_process_buf);
/**
* xdr_stream_decode_opaque - Decode variable length opaque
* @xdr: pointer to xdr_stream
* @ptr: location to store opaque data
* @size: size of storage buffer @ptr
*
* Return values:
* On success, returns size of object stored in *@ptr
* %-EBADMSG on XDR buffer overflow
* %-EMSGSIZE on overflow of storage buffer @ptr
*/
ssize_t xdr_stream_decode_opaque(struct xdr_stream *xdr, void *ptr, size_t size)
{
ssize_t ret;
void *p;
ret = xdr_stream_decode_opaque_inline(xdr, &p, size);
if (ret <= 0)
return ret;
memcpy(ptr, p, ret);
return ret;
}
EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque);
/**
* xdr_stream_decode_opaque_dup - Decode and duplicate variable length opaque
* @xdr: pointer to xdr_stream
* @ptr: location to store pointer to opaque data
* @maxlen: maximum acceptable object size
* @gfp_flags: GFP mask to use
*
* Return values:
* On success, returns size of object stored in *@ptr
* %-EBADMSG on XDR buffer overflow
* %-EMSGSIZE if the size of the object would exceed @maxlen
* %-ENOMEM on memory allocation failure
*/
ssize_t xdr_stream_decode_opaque_dup(struct xdr_stream *xdr, void **ptr,
size_t maxlen, gfp_t gfp_flags)
{
ssize_t ret;
void *p;
ret = xdr_stream_decode_opaque_inline(xdr, &p, maxlen);
if (ret > 0) {
*ptr = kmemdup(p, ret, gfp_flags);
if (*ptr != NULL)
return ret;
ret = -ENOMEM;
}
*ptr = NULL;
return ret;
}
EXPORT_SYMBOL_GPL(xdr_stream_decode_opaque_dup);
/**
* xdr_stream_decode_string - Decode variable length string
* @xdr: pointer to xdr_stream
* @str: location to store string
* @size: size of storage buffer @str
*
* Return values:
* On success, returns length of NUL-terminated string stored in *@str
* %-EBADMSG on XDR buffer overflow
* %-EMSGSIZE on overflow of storage buffer @str
*/
ssize_t xdr_stream_decode_string(struct xdr_stream *xdr, char *str, size_t size)
{
ssize_t ret;
void *p;
ret = xdr_stream_decode_opaque_inline(xdr, &p, size);
if (ret > 0) {
memcpy(str, p, ret);
str[ret] = '\0';
return strlen(str);
}
*str = '\0';
return ret;
}
EXPORT_SYMBOL_GPL(xdr_stream_decode_string);
/** /**
* xdr_stream_decode_string_dup - Decode and duplicate variable length string * xdr_stream_decode_string_dup - Decode and duplicate variable length string
* @xdr: pointer to xdr_stream * @xdr: pointer to xdr_stream

View File

@ -826,6 +826,7 @@ static void xprt_connect_status(struct rpc_task *task)
* @xprt: transport on which the original request was transmitted * @xprt: transport on which the original request was transmitted
* @xid: RPC XID of incoming reply * @xid: RPC XID of incoming reply
* *
* Caller holds xprt->recv_lock.
*/ */
struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid) struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid)
{ {
@ -834,6 +835,7 @@ struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid)
list_for_each_entry(entry, &xprt->recv, rq_list) list_for_each_entry(entry, &xprt->recv, rq_list)
if (entry->rq_xid == xid) { if (entry->rq_xid == xid) {
trace_xprt_lookup_rqst(xprt, xid, 0); trace_xprt_lookup_rqst(xprt, xid, 0);
entry->rq_rtt = ktime_sub(ktime_get(), entry->rq_xtime);
return entry; return entry;
} }
@ -889,7 +891,13 @@ __must_hold(&req->rq_xprt->recv_lock)
} }
} }
static void xprt_update_rtt(struct rpc_task *task) /**
* xprt_update_rtt - Update RPC RTT statistics
* @task: RPC request that recently completed
*
* Caller holds xprt->recv_lock.
*/
void xprt_update_rtt(struct rpc_task *task)
{ {
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
struct rpc_rtt *rtt = task->tk_client->cl_rtt; struct rpc_rtt *rtt = task->tk_client->cl_rtt;
@ -902,13 +910,14 @@ static void xprt_update_rtt(struct rpc_task *task)
rpc_set_timeo(rtt, timer, req->rq_ntrans - 1); rpc_set_timeo(rtt, timer, req->rq_ntrans - 1);
} }
} }
EXPORT_SYMBOL_GPL(xprt_update_rtt);
/** /**
* xprt_complete_rqst - called when reply processing is complete * xprt_complete_rqst - called when reply processing is complete
* @task: RPC request that recently completed * @task: RPC request that recently completed
* @copied: actual number of bytes received from the transport * @copied: actual number of bytes received from the transport
* *
* Caller holds transport lock. * Caller holds xprt->recv_lock.
*/ */
void xprt_complete_rqst(struct rpc_task *task, int copied) void xprt_complete_rqst(struct rpc_task *task, int copied)
{ {
@ -920,9 +929,6 @@ void xprt_complete_rqst(struct rpc_task *task, int copied)
trace_xprt_complete_rqst(xprt, req->rq_xid, copied); trace_xprt_complete_rqst(xprt, req->rq_xid, copied);
xprt->stat.recvs++; xprt->stat.recvs++;
req->rq_rtt = ktime_sub(ktime_get(), req->rq_xtime);
if (xprt->ops->timer != NULL)
xprt_update_rtt(task);
list_del_init(&req->rq_list); list_del_init(&req->rq_list);
req->rq_private_buf.len = copied; req->rq_private_buf.len = copied;
@ -1003,7 +1009,7 @@ void xprt_transmit(struct rpc_task *task)
struct rpc_rqst *req = task->tk_rqstp; struct rpc_rqst *req = task->tk_rqstp;
struct rpc_xprt *xprt = req->rq_xprt; struct rpc_xprt *xprt = req->rq_xprt;
unsigned int connect_cookie; unsigned int connect_cookie;
int status, numreqs; int status;
dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen); dprintk("RPC: %5u xprt_transmit(%u)\n", task->tk_pid, req->rq_slen);
@ -1027,7 +1033,6 @@ void xprt_transmit(struct rpc_task *task)
return; return;
connect_cookie = xprt->connect_cookie; connect_cookie = xprt->connect_cookie;
req->rq_xtime = ktime_get();
status = xprt->ops->send_request(task); status = xprt->ops->send_request(task);
trace_xprt_transmit(xprt, req->rq_xid, status); trace_xprt_transmit(xprt, req->rq_xid, status);
if (status != 0) { if (status != 0) {
@ -1042,9 +1047,6 @@ void xprt_transmit(struct rpc_task *task)
xprt->ops->set_retrans_timeout(task); xprt->ops->set_retrans_timeout(task);
numreqs = atomic_read(&xprt->num_reqs);
if (numreqs > xprt->stat.max_slots)
xprt->stat.max_slots = numreqs;
xprt->stat.sends++; xprt->stat.sends++;
xprt->stat.req_u += xprt->stat.sends - xprt->stat.recvs; xprt->stat.req_u += xprt->stat.sends - xprt->stat.recvs;
xprt->stat.bklog_u += xprt->backlog.qlen; xprt->stat.bklog_u += xprt->backlog.qlen;
@ -1106,14 +1108,15 @@ static struct rpc_rqst *xprt_dynamic_alloc_slot(struct rpc_xprt *xprt)
{ {
struct rpc_rqst *req = ERR_PTR(-EAGAIN); struct rpc_rqst *req = ERR_PTR(-EAGAIN);
if (!atomic_add_unless(&xprt->num_reqs, 1, xprt->max_reqs)) if (xprt->num_reqs >= xprt->max_reqs)
goto out; goto out;
++xprt->num_reqs;
spin_unlock(&xprt->reserve_lock); spin_unlock(&xprt->reserve_lock);
req = kzalloc(sizeof(struct rpc_rqst), GFP_NOFS); req = kzalloc(sizeof(struct rpc_rqst), GFP_NOFS);
spin_lock(&xprt->reserve_lock); spin_lock(&xprt->reserve_lock);
if (req != NULL) if (req != NULL)
goto out; goto out;
atomic_dec(&xprt->num_reqs); --xprt->num_reqs;
req = ERR_PTR(-ENOMEM); req = ERR_PTR(-ENOMEM);
out: out:
return req; return req;
@ -1121,7 +1124,8 @@ out:
static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
{ {
if (atomic_add_unless(&xprt->num_reqs, -1, xprt->min_reqs)) { if (xprt->num_reqs > xprt->min_reqs) {
--xprt->num_reqs;
kfree(req); kfree(req);
return true; return true;
} }
@ -1157,6 +1161,8 @@ void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
spin_unlock(&xprt->reserve_lock); spin_unlock(&xprt->reserve_lock);
return; return;
out_init_req: out_init_req:
xprt->stat.max_slots = max_t(unsigned int, xprt->stat.max_slots,
xprt->num_reqs);
task->tk_status = 0; task->tk_status = 0;
task->tk_rqstp = req; task->tk_rqstp = req;
xprt_request_init(task, xprt); xprt_request_init(task, xprt);
@ -1224,7 +1230,7 @@ struct rpc_xprt *xprt_alloc(struct net *net, size_t size,
else else
xprt->max_reqs = num_prealloc; xprt->max_reqs = num_prealloc;
xprt->min_reqs = num_prealloc; xprt->min_reqs = num_prealloc;
atomic_set(&xprt->num_reqs, num_prealloc); xprt->num_reqs = num_prealloc;
return xprt; return xprt;

View File

@ -44,13 +44,6 @@ static int rpcrdma_bc_setup_rqst(struct rpcrdma_xprt *r_xprt,
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
rb = rpcrdma_alloc_regbuf(RPCRDMA_HDRBUF_SIZE,
DMA_TO_DEVICE, GFP_KERNEL);
if (IS_ERR(rb))
goto out_fail;
req->rl_rdmabuf = rb;
xdr_buf_init(&req->rl_hdrbuf, rb->rg_base, rdmab_length(rb));
size = r_xprt->rx_data.inline_rsize; size = r_xprt->rx_data.inline_rsize;
rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, GFP_KERNEL); rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, GFP_KERNEL);
if (IS_ERR(rb)) if (IS_ERR(rb))

View File

@ -191,7 +191,7 @@ fmr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
mr = rpcrdma_mr_get(r_xprt); mr = rpcrdma_mr_get(r_xprt);
if (!mr) if (!mr)
return ERR_PTR(-ENOBUFS); return ERR_PTR(-EAGAIN);
pageoff = offset_in_page(seg1->mr_offset); pageoff = offset_in_page(seg1->mr_offset);
seg1->mr_offset -= pageoff; /* start of page */ seg1->mr_offset -= pageoff; /* start of page */
@ -251,6 +251,16 @@ out_maperr:
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
} }
/* Post Send WR containing the RPC Call message.
*/
static int
fmr_op_send(struct rpcrdma_ia *ia, struct rpcrdma_req *req)
{
struct ib_send_wr *bad_wr;
return ib_post_send(ia->ri_id->qp, &req->rl_sendctx->sc_wr, &bad_wr);
}
/* Invalidate all memory regions that were registered for "req". /* Invalidate all memory regions that were registered for "req".
* *
* Sleeps until it is safe for the host CPU to access the * Sleeps until it is safe for the host CPU to access the
@ -305,6 +315,7 @@ out_reset:
const struct rpcrdma_memreg_ops rpcrdma_fmr_memreg_ops = { const struct rpcrdma_memreg_ops rpcrdma_fmr_memreg_ops = {
.ro_map = fmr_op_map, .ro_map = fmr_op_map,
.ro_send = fmr_op_send,
.ro_unmap_sync = fmr_op_unmap_sync, .ro_unmap_sync = fmr_op_unmap_sync,
.ro_recover_mr = fmr_op_recover_mr, .ro_recover_mr = fmr_op_recover_mr,
.ro_open = fmr_op_open, .ro_open = fmr_op_open,

View File

@ -357,8 +357,7 @@ frwr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
struct rpcrdma_mr *mr; struct rpcrdma_mr *mr;
struct ib_mr *ibmr; struct ib_mr *ibmr;
struct ib_reg_wr *reg_wr; struct ib_reg_wr *reg_wr;
struct ib_send_wr *bad_wr; int i, n;
int rc, i, n;
u8 key; u8 key;
mr = NULL; mr = NULL;
@ -367,7 +366,7 @@ frwr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
rpcrdma_mr_defer_recovery(mr); rpcrdma_mr_defer_recovery(mr);
mr = rpcrdma_mr_get(r_xprt); mr = rpcrdma_mr_get(r_xprt);
if (!mr) if (!mr)
return ERR_PTR(-ENOBUFS); return ERR_PTR(-EAGAIN);
} while (mr->frwr.fr_state != FRWR_IS_INVALID); } while (mr->frwr.fr_state != FRWR_IS_INVALID);
frwr = &mr->frwr; frwr = &mr->frwr;
frwr->fr_state = FRWR_IS_VALID; frwr->fr_state = FRWR_IS_VALID;
@ -407,22 +406,12 @@ frwr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
ib_update_fast_reg_key(ibmr, ++key); ib_update_fast_reg_key(ibmr, ++key);
reg_wr = &frwr->fr_regwr; reg_wr = &frwr->fr_regwr;
reg_wr->wr.next = NULL;
reg_wr->wr.opcode = IB_WR_REG_MR;
frwr->fr_cqe.done = frwr_wc_fastreg;
reg_wr->wr.wr_cqe = &frwr->fr_cqe;
reg_wr->wr.num_sge = 0;
reg_wr->wr.send_flags = 0;
reg_wr->mr = ibmr; reg_wr->mr = ibmr;
reg_wr->key = ibmr->rkey; reg_wr->key = ibmr->rkey;
reg_wr->access = writing ? reg_wr->access = writing ?
IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE : IB_ACCESS_REMOTE_WRITE | IB_ACCESS_LOCAL_WRITE :
IB_ACCESS_REMOTE_READ; IB_ACCESS_REMOTE_READ;
rc = ib_post_send(ia->ri_id->qp, &reg_wr->wr, &bad_wr);
if (rc)
goto out_senderr;
mr->mr_handle = ibmr->rkey; mr->mr_handle = ibmr->rkey;
mr->mr_length = ibmr->length; mr->mr_length = ibmr->length;
mr->mr_offset = ibmr->iova; mr->mr_offset = ibmr->iova;
@ -442,11 +431,40 @@ out_mapmr_err:
frwr->fr_mr, n, mr->mr_nents); frwr->fr_mr, n, mr->mr_nents);
rpcrdma_mr_defer_recovery(mr); rpcrdma_mr_defer_recovery(mr);
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
}
out_senderr: /* Post Send WR containing the RPC Call message.
pr_err("rpcrdma: FRWR registration ib_post_send returned %i\n", rc); *
rpcrdma_mr_defer_recovery(mr); * For FRMR, chain any FastReg WRs to the Send WR. Only a
return ERR_PTR(-ENOTCONN); * single ib_post_send call is needed to register memory
* and then post the Send WR.
*/
static int
frwr_op_send(struct rpcrdma_ia *ia, struct rpcrdma_req *req)
{
struct ib_send_wr *post_wr, *bad_wr;
struct rpcrdma_mr *mr;
post_wr = &req->rl_sendctx->sc_wr;
list_for_each_entry(mr, &req->rl_registered, mr_list) {
struct rpcrdma_frwr *frwr;
frwr = &mr->frwr;
frwr->fr_cqe.done = frwr_wc_fastreg;
frwr->fr_regwr.wr.next = post_wr;
frwr->fr_regwr.wr.wr_cqe = &frwr->fr_cqe;
frwr->fr_regwr.wr.num_sge = 0;
frwr->fr_regwr.wr.opcode = IB_WR_REG_MR;
frwr->fr_regwr.wr.send_flags = 0;
post_wr = &frwr->fr_regwr.wr;
}
/* If ib_post_send fails, the next ->send_request for
* @req will queue these MWs for recovery.
*/
return ib_post_send(ia->ri_id->qp, post_wr, &bad_wr);
} }
/* Handle a remotely invalidated mr on the @mrs list /* Handle a remotely invalidated mr on the @mrs list
@ -561,6 +579,7 @@ reset_mrs:
const struct rpcrdma_memreg_ops rpcrdma_frwr_memreg_ops = { const struct rpcrdma_memreg_ops rpcrdma_frwr_memreg_ops = {
.ro_map = frwr_op_map, .ro_map = frwr_op_map,
.ro_send = frwr_op_send,
.ro_reminv = frwr_op_reminv, .ro_reminv = frwr_op_reminv,
.ro_unmap_sync = frwr_op_unmap_sync, .ro_unmap_sync = frwr_op_unmap_sync,
.ro_recover_mr = frwr_op_recover_mr, .ro_recover_mr = frwr_op_recover_mr,

View File

@ -365,7 +365,7 @@ rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
seg = r_xprt->rx_ia.ri_ops->ro_map(r_xprt, seg, nsegs, seg = r_xprt->rx_ia.ri_ops->ro_map(r_xprt, seg, nsegs,
false, &mr); false, &mr);
if (IS_ERR(seg)) if (IS_ERR(seg))
return PTR_ERR(seg); goto out_maperr;
rpcrdma_mr_push(mr, &req->rl_registered); rpcrdma_mr_push(mr, &req->rl_registered);
if (encode_read_segment(xdr, mr, pos) < 0) if (encode_read_segment(xdr, mr, pos) < 0)
@ -377,6 +377,11 @@ rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
} while (nsegs); } while (nsegs);
return 0; return 0;
out_maperr:
if (PTR_ERR(seg) == -EAGAIN)
xprt_wait_for_buffer_space(rqst->rq_task, NULL);
return PTR_ERR(seg);
} }
/* Register and XDR encode the Write list. Supports encoding a list /* Register and XDR encode the Write list. Supports encoding a list
@ -423,7 +428,7 @@ rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
seg = r_xprt->rx_ia.ri_ops->ro_map(r_xprt, seg, nsegs, seg = r_xprt->rx_ia.ri_ops->ro_map(r_xprt, seg, nsegs,
true, &mr); true, &mr);
if (IS_ERR(seg)) if (IS_ERR(seg))
return PTR_ERR(seg); goto out_maperr;
rpcrdma_mr_push(mr, &req->rl_registered); rpcrdma_mr_push(mr, &req->rl_registered);
if (encode_rdma_segment(xdr, mr) < 0) if (encode_rdma_segment(xdr, mr) < 0)
@ -440,6 +445,11 @@ rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
*segcount = cpu_to_be32(nchunks); *segcount = cpu_to_be32(nchunks);
return 0; return 0;
out_maperr:
if (PTR_ERR(seg) == -EAGAIN)
xprt_wait_for_buffer_space(rqst->rq_task, NULL);
return PTR_ERR(seg);
} }
/* Register and XDR encode the Reply chunk. Supports encoding an array /* Register and XDR encode the Reply chunk. Supports encoding an array
@ -481,7 +491,7 @@ rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
seg = r_xprt->rx_ia.ri_ops->ro_map(r_xprt, seg, nsegs, seg = r_xprt->rx_ia.ri_ops->ro_map(r_xprt, seg, nsegs,
true, &mr); true, &mr);
if (IS_ERR(seg)) if (IS_ERR(seg))
return PTR_ERR(seg); goto out_maperr;
rpcrdma_mr_push(mr, &req->rl_registered); rpcrdma_mr_push(mr, &req->rl_registered);
if (encode_rdma_segment(xdr, mr) < 0) if (encode_rdma_segment(xdr, mr) < 0)
@ -498,6 +508,11 @@ rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
*segcount = cpu_to_be32(nchunks); *segcount = cpu_to_be32(nchunks);
return 0; return 0;
out_maperr:
if (PTR_ERR(seg) == -EAGAIN)
xprt_wait_for_buffer_space(rqst->rq_task, NULL);
return PTR_ERR(seg);
} }
/** /**
@ -724,8 +739,8 @@ rpcrdma_prepare_send_sges(struct rpcrdma_xprt *r_xprt,
* Returns: * Returns:
* %0 if the RPC was sent successfully, * %0 if the RPC was sent successfully,
* %-ENOTCONN if the connection was lost, * %-ENOTCONN if the connection was lost,
* %-EAGAIN if not enough pages are available for on-demand reply buffer, * %-EAGAIN if the caller should call again with the same arguments,
* %-ENOBUFS if no MRs are available to register chunks, * %-ENOBUFS if the caller should call again after a delay,
* %-EMSGSIZE if the transport header is too small, * %-EMSGSIZE if the transport header is too small,
* %-EIO if a permanent problem occurred while marshaling. * %-EIO if a permanent problem occurred while marshaling.
*/ */
@ -868,10 +883,7 @@ rpcrdma_marshal_req(struct rpcrdma_xprt *r_xprt, struct rpc_rqst *rqst)
return 0; return 0;
out_err: out_err:
if (ret != -ENOBUFS) { r_xprt->rx_stats.failed_marshal_count++;
pr_err("rpcrdma: header marshaling failed (%d)\n", ret);
r_xprt->rx_stats.failed_marshal_count++;
}
return ret; return ret;
} }
@ -1366,7 +1378,7 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
trace_xprtrdma_reply(rqst->rq_task, rep, req, credits); trace_xprtrdma_reply(rqst->rq_task, rep, req, credits);
queue_work_on(req->rl_cpu, rpcrdma_receive_wq, &rep->rr_work); queue_work(rpcrdma_receive_wq, &rep->rr_work);
return; return;
out_badstatus: out_badstatus:

View File

@ -52,7 +52,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/sunrpc/addr.h> #include <linux/sunrpc/addr.h>
#include <linux/smp.h>
#include "xprt_rdma.h" #include "xprt_rdma.h"
@ -237,8 +236,6 @@ rpcrdma_connect_worker(struct work_struct *work)
struct rpc_xprt *xprt = &r_xprt->rx_xprt; struct rpc_xprt *xprt = &r_xprt->rx_xprt;
spin_lock_bh(&xprt->transport_lock); spin_lock_bh(&xprt->transport_lock);
if (++xprt->connect_cookie == 0) /* maintain a reserved value */
++xprt->connect_cookie;
if (ep->rep_connected > 0) { if (ep->rep_connected > 0) {
if (!xprt_test_and_set_connected(xprt)) if (!xprt_test_and_set_connected(xprt))
xprt_wake_pending_tasks(xprt, 0); xprt_wake_pending_tasks(xprt, 0);
@ -540,29 +537,6 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task)
} }
} }
/* Allocate a fixed-size buffer in which to construct and send the
* RPC-over-RDMA header for this request.
*/
static bool
rpcrdma_get_rdmabuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
gfp_t flags)
{
size_t size = RPCRDMA_HDRBUF_SIZE;
struct rpcrdma_regbuf *rb;
if (req->rl_rdmabuf)
return true;
rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, flags);
if (IS_ERR(rb))
return false;
r_xprt->rx_stats.hardway_register_count += size;
req->rl_rdmabuf = rb;
xdr_buf_init(&req->rl_hdrbuf, rb->rg_base, rdmab_length(rb));
return true;
}
static bool static bool
rpcrdma_get_sendbuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req, rpcrdma_get_sendbuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
size_t size, gfp_t flags) size_t size, gfp_t flags)
@ -644,15 +618,11 @@ xprt_rdma_allocate(struct rpc_task *task)
if (RPC_IS_SWAPPER(task)) if (RPC_IS_SWAPPER(task))
flags = __GFP_MEMALLOC | GFP_NOWAIT | __GFP_NOWARN; flags = __GFP_MEMALLOC | GFP_NOWAIT | __GFP_NOWARN;
if (!rpcrdma_get_rdmabuf(r_xprt, req, flags))
goto out_fail;
if (!rpcrdma_get_sendbuf(r_xprt, req, rqst->rq_callsize, flags)) if (!rpcrdma_get_sendbuf(r_xprt, req, rqst->rq_callsize, flags))
goto out_fail; goto out_fail;
if (!rpcrdma_get_recvbuf(r_xprt, req, rqst->rq_rcvsize, flags)) if (!rpcrdma_get_recvbuf(r_xprt, req, rqst->rq_rcvsize, flags))
goto out_fail; goto out_fail;
req->rl_cpu = smp_processor_id();
req->rl_connect_cookie = 0; /* our reserved value */
rpcrdma_set_xprtdata(rqst, req); rpcrdma_set_xprtdata(rqst, req);
rqst->rq_buffer = req->rl_sendbuf->rg_base; rqst->rq_buffer = req->rl_sendbuf->rg_base;
rqst->rq_rbuffer = req->rl_recvbuf->rg_base; rqst->rq_rbuffer = req->rl_recvbuf->rg_base;
@ -694,7 +664,8 @@ xprt_rdma_free(struct rpc_task *task)
* Returns: * Returns:
* %0 if the RPC message has been sent * %0 if the RPC message has been sent
* %-ENOTCONN if the caller should reconnect and call again * %-ENOTCONN if the caller should reconnect and call again
* %-ENOBUFS if the caller should call again later * %-EAGAIN if the caller should call again
* %-ENOBUFS if the caller should call again after a delay
* %-EIO if a permanent error occurred and the request was not * %-EIO if a permanent error occurred and the request was not
* sent. Do not try to send this message again. * sent. Do not try to send this message again.
*/ */
@ -723,9 +694,9 @@ xprt_rdma_send_request(struct rpc_task *task)
rpcrdma_recv_buffer_get(req); rpcrdma_recv_buffer_get(req);
/* Must suppress retransmit to maintain credits */ /* Must suppress retransmit to maintain credits */
if (req->rl_connect_cookie == xprt->connect_cookie) if (rqst->rq_connect_cookie == xprt->connect_cookie)
goto drop_connection; goto drop_connection;
req->rl_connect_cookie = xprt->connect_cookie; rqst->rq_xtime = ktime_get();
__set_bit(RPCRDMA_REQ_F_PENDING, &req->rl_flags); __set_bit(RPCRDMA_REQ_F_PENDING, &req->rl_flags);
if (rpcrdma_ep_post(&r_xprt->rx_ia, &r_xprt->rx_ep, req)) if (rpcrdma_ep_post(&r_xprt->rx_ia, &r_xprt->rx_ep, req))
@ -733,6 +704,12 @@ xprt_rdma_send_request(struct rpc_task *task)
rqst->rq_xmit_bytes_sent += rqst->rq_snd_buf.len; rqst->rq_xmit_bytes_sent += rqst->rq_snd_buf.len;
rqst->rq_bytes_sent = 0; rqst->rq_bytes_sent = 0;
/* An RPC with no reply will throw off credit accounting,
* so drop the connection to reset the credit grant.
*/
if (!rpc_reply_expected(task))
goto drop_connection;
return 0; return 0;
failed_marshal: failed_marshal:

View File

@ -250,11 +250,11 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
wait_for_completion(&ia->ri_remove_done); wait_for_completion(&ia->ri_remove_done);
ia->ri_id = NULL; ia->ri_id = NULL;
ia->ri_pd = NULL;
ia->ri_device = NULL; ia->ri_device = NULL;
/* Return 1 to ensure the core destroys the id. */ /* Return 1 to ensure the core destroys the id. */
return 1; return 1;
case RDMA_CM_EVENT_ESTABLISHED: case RDMA_CM_EVENT_ESTABLISHED:
++xprt->rx_xprt.connect_cookie;
connstate = 1; connstate = 1;
rpcrdma_update_connect_private(xprt, &event->param.conn); rpcrdma_update_connect_private(xprt, &event->param.conn);
goto connected; goto connected;
@ -273,6 +273,7 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
connstate = -EAGAIN; connstate = -EAGAIN;
goto connected; goto connected;
case RDMA_CM_EVENT_DISCONNECTED: case RDMA_CM_EVENT_DISCONNECTED:
++xprt->rx_xprt.connect_cookie;
connstate = -ECONNABORTED; connstate = -ECONNABORTED;
connected: connected:
xprt->rx_buf.rb_credits = 1; xprt->rx_buf.rb_credits = 1;
@ -445,7 +446,9 @@ rpcrdma_ia_remove(struct rpcrdma_ia *ia)
ia->ri_id->qp = NULL; ia->ri_id->qp = NULL;
} }
ib_free_cq(ep->rep_attr.recv_cq); ib_free_cq(ep->rep_attr.recv_cq);
ep->rep_attr.recv_cq = NULL;
ib_free_cq(ep->rep_attr.send_cq); ib_free_cq(ep->rep_attr.send_cq);
ep->rep_attr.send_cq = NULL;
/* The ULP is responsible for ensuring all DMA /* The ULP is responsible for ensuring all DMA
* mappings and MRs are gone. * mappings and MRs are gone.
@ -458,6 +461,8 @@ rpcrdma_ia_remove(struct rpcrdma_ia *ia)
rpcrdma_dma_unmap_regbuf(req->rl_recvbuf); rpcrdma_dma_unmap_regbuf(req->rl_recvbuf);
} }
rpcrdma_mrs_destroy(buf); rpcrdma_mrs_destroy(buf);
ib_dealloc_pd(ia->ri_pd);
ia->ri_pd = NULL;
/* Allow waiters to continue */ /* Allow waiters to continue */
complete(&ia->ri_remove_done); complete(&ia->ri_remove_done);
@ -589,11 +594,8 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
/* Client offers RDMA Read but does not initiate */ /* Client offers RDMA Read but does not initiate */
ep->rep_remote_cma.initiator_depth = 0; ep->rep_remote_cma.initiator_depth = 0;
if (ia->ri_device->attrs.max_qp_rd_atom > 32) /* arbitrary but <= 255 */ ep->rep_remote_cma.responder_resources =
ep->rep_remote_cma.responder_resources = 32; min_t(int, U8_MAX, ia->ri_device->attrs.max_qp_rd_atom);
else
ep->rep_remote_cma.responder_resources =
ia->ri_device->attrs.max_qp_rd_atom;
/* Limit transport retries so client can detect server /* Limit transport retries so client can detect server
* GID changes quickly. RPC layer handles re-establishing * GID changes quickly. RPC layer handles re-establishing
@ -628,14 +630,16 @@ rpcrdma_ep_destroy(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia)
{ {
cancel_delayed_work_sync(&ep->rep_connect_worker); cancel_delayed_work_sync(&ep->rep_connect_worker);
if (ia->ri_id->qp) { if (ia->ri_id && ia->ri_id->qp) {
rpcrdma_ep_disconnect(ep, ia); rpcrdma_ep_disconnect(ep, ia);
rdma_destroy_qp(ia->ri_id); rdma_destroy_qp(ia->ri_id);
ia->ri_id->qp = NULL; ia->ri_id->qp = NULL;
} }
ib_free_cq(ep->rep_attr.recv_cq); if (ep->rep_attr.recv_cq)
ib_free_cq(ep->rep_attr.send_cq); ib_free_cq(ep->rep_attr.recv_cq);
if (ep->rep_attr.send_cq)
ib_free_cq(ep->rep_attr.send_cq);
} }
/* Re-establish a connection after a device removal event. /* Re-establish a connection after a device removal event.
@ -1024,7 +1028,7 @@ rpcrdma_mrs_create(struct rpcrdma_xprt *r_xprt)
LIST_HEAD(free); LIST_HEAD(free);
LIST_HEAD(all); LIST_HEAD(all);
for (count = 0; count < 32; count++) { for (count = 0; count < 3; count++) {
struct rpcrdma_mr *mr; struct rpcrdma_mr *mr;
int rc; int rc;
@ -1049,8 +1053,9 @@ rpcrdma_mrs_create(struct rpcrdma_xprt *r_xprt)
list_splice(&all, &buf->rb_all); list_splice(&all, &buf->rb_all);
r_xprt->rx_stats.mrs_allocated += count; r_xprt->rx_stats.mrs_allocated += count;
spin_unlock(&buf->rb_mrlock); spin_unlock(&buf->rb_mrlock);
trace_xprtrdma_createmrs(r_xprt, count); trace_xprtrdma_createmrs(r_xprt, count);
xprt_write_space(&r_xprt->rx_xprt);
} }
static void static void
@ -1068,17 +1073,27 @@ struct rpcrdma_req *
rpcrdma_create_req(struct rpcrdma_xprt *r_xprt) rpcrdma_create_req(struct rpcrdma_xprt *r_xprt)
{ {
struct rpcrdma_buffer *buffer = &r_xprt->rx_buf; struct rpcrdma_buffer *buffer = &r_xprt->rx_buf;
struct rpcrdma_regbuf *rb;
struct rpcrdma_req *req; struct rpcrdma_req *req;
req = kzalloc(sizeof(*req), GFP_KERNEL); req = kzalloc(sizeof(*req), GFP_KERNEL);
if (req == NULL) if (req == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
rb = rpcrdma_alloc_regbuf(RPCRDMA_HDRBUF_SIZE,
DMA_TO_DEVICE, GFP_KERNEL);
if (IS_ERR(rb)) {
kfree(req);
return ERR_PTR(-ENOMEM);
}
req->rl_rdmabuf = rb;
xdr_buf_init(&req->rl_hdrbuf, rb->rg_base, rdmab_length(rb));
req->rl_buffer = buffer;
INIT_LIST_HEAD(&req->rl_registered);
spin_lock(&buffer->rb_reqslock); spin_lock(&buffer->rb_reqslock);
list_add(&req->rl_all, &buffer->rb_allreqs); list_add(&req->rl_all, &buffer->rb_allreqs);
spin_unlock(&buffer->rb_reqslock); spin_unlock(&buffer->rb_reqslock);
req->rl_buffer = &r_xprt->rx_buf;
INIT_LIST_HEAD(&req->rl_registered);
return req; return req;
} }
@ -1535,7 +1550,6 @@ rpcrdma_ep_post(struct rpcrdma_ia *ia,
struct rpcrdma_req *req) struct rpcrdma_req *req)
{ {
struct ib_send_wr *send_wr = &req->rl_sendctx->sc_wr; struct ib_send_wr *send_wr = &req->rl_sendctx->sc_wr;
struct ib_send_wr *send_wr_fail;
int rc; int rc;
if (req->rl_reply) { if (req->rl_reply) {
@ -1554,7 +1568,7 @@ rpcrdma_ep_post(struct rpcrdma_ia *ia,
--ep->rep_send_count; --ep->rep_send_count;
} }
rc = ib_post_send(ia->ri_id->qp, send_wr, &send_wr_fail); rc = ia->ri_ops->ro_send(ia, req);
trace_xprtrdma_post_send(req, rc); trace_xprtrdma_post_send(req, rc);
if (rc) if (rc)
return -ENOTCONN; return -ENOTCONN;

View File

@ -334,8 +334,6 @@ enum {
struct rpcrdma_buffer; struct rpcrdma_buffer;
struct rpcrdma_req { struct rpcrdma_req {
struct list_head rl_list; struct list_head rl_list;
int rl_cpu;
unsigned int rl_connect_cookie;
struct rpcrdma_buffer *rl_buffer; struct rpcrdma_buffer *rl_buffer;
struct rpcrdma_rep *rl_reply; struct rpcrdma_rep *rl_reply;
struct xdr_stream rl_stream; struct xdr_stream rl_stream;
@ -474,6 +472,8 @@ struct rpcrdma_memreg_ops {
(*ro_map)(struct rpcrdma_xprt *, (*ro_map)(struct rpcrdma_xprt *,
struct rpcrdma_mr_seg *, int, bool, struct rpcrdma_mr_seg *, int, bool,
struct rpcrdma_mr **); struct rpcrdma_mr **);
int (*ro_send)(struct rpcrdma_ia *ia,
struct rpcrdma_req *req);
void (*ro_reminv)(struct rpcrdma_rep *rep, void (*ro_reminv)(struct rpcrdma_rep *rep,
struct list_head *mrs); struct list_head *mrs);
void (*ro_unmap_sync)(struct rpcrdma_xprt *, void (*ro_unmap_sync)(struct rpcrdma_xprt *,

View File

@ -527,6 +527,7 @@ static int xs_local_send_request(struct rpc_task *task)
xs_pktdump("packet data:", xs_pktdump("packet data:",
req->rq_svec->iov_base, req->rq_svec->iov_len); req->rq_svec->iov_base, req->rq_svec->iov_len);
req->rq_xtime = ktime_get();
status = xs_sendpages(transport->sock, NULL, 0, xdr, req->rq_bytes_sent, status = xs_sendpages(transport->sock, NULL, 0, xdr, req->rq_bytes_sent,
true, &sent); true, &sent);
dprintk("RPC: %s(%u) = %d\n", dprintk("RPC: %s(%u) = %d\n",
@ -589,6 +590,7 @@ static int xs_udp_send_request(struct rpc_task *task)
if (!xprt_bound(xprt)) if (!xprt_bound(xprt))
return -ENOTCONN; return -ENOTCONN;
req->rq_xtime = ktime_get();
status = xs_sendpages(transport->sock, xs_addr(xprt), xprt->addrlen, status = xs_sendpages(transport->sock, xs_addr(xprt), xprt->addrlen,
xdr, req->rq_bytes_sent, true, &sent); xdr, req->rq_bytes_sent, true, &sent);
@ -678,6 +680,7 @@ static int xs_tcp_send_request(struct rpc_task *task)
/* Continue transmitting the packet/record. We must be careful /* Continue transmitting the packet/record. We must be careful
* to cope with writespace callbacks arriving _after_ we have * to cope with writespace callbacks arriving _after_ we have
* called sendmsg(). */ * called sendmsg(). */
req->rq_xtime = ktime_get();
while (1) { while (1) {
sent = 0; sent = 0;
status = xs_sendpages(transport->sock, NULL, 0, xdr, status = xs_sendpages(transport->sock, NULL, 0, xdr,
@ -1060,6 +1063,7 @@ static void xs_udp_data_read_skb(struct rpc_xprt *xprt,
if (!rovr) if (!rovr)
goto out_unlock; goto out_unlock;
xprt_pin_rqst(rovr); xprt_pin_rqst(rovr);
xprt_update_rtt(rovr->rq_task);
spin_unlock(&xprt->recv_lock); spin_unlock(&xprt->recv_lock);
task = rovr->rq_task; task = rovr->rq_task;