NFS: Use information about the change attribute to optimise updates
If the NFSv4.2 server supports the 'change_attr_type' attribute, then allow the client to optimise its attribute cache update strategy. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
parent
7f08a3359a
commit
6f9be83d07
111
fs/nfs/inode.c
111
fs/nfs/inode.c
@ -1657,25 +1657,20 @@ EXPORT_SYMBOL_GPL(_nfs_display_fhandle);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* nfs_inode_attrs_need_update - check if the inode attributes need updating
|
||||
* @inode: pointer to inode
|
||||
* nfs_inode_attrs_cmp_generic - compare attributes
|
||||
* @fattr: attributes
|
||||
* @inode: pointer to inode
|
||||
*
|
||||
* Attempt to divine whether or not an RPC call reply carrying stale
|
||||
* attributes got scheduled after another call carrying updated ones.
|
||||
*
|
||||
* To do so, the function first assumes that a more recent ctime means
|
||||
* that the attributes in fattr are newer, however it also attempt to
|
||||
* catch the case where ctime either didn't change, or went backwards
|
||||
* (if someone reset the clock on the server) by looking at whether
|
||||
* or not this RPC call was started after the inode was last updated.
|
||||
* Note also the check for wraparound of 'attr_gencount'
|
||||
*
|
||||
* The function returns 'true' if it thinks the attributes in 'fattr' are
|
||||
* more recent than the ones cached in the inode.
|
||||
*
|
||||
* The function returns '1' if it thinks the attributes in @fattr are
|
||||
* more recent than the ones cached in @inode. Otherwise it returns
|
||||
* the value '0'.
|
||||
*/
|
||||
static int nfs_inode_attrs_need_update(const struct inode *inode, const struct nfs_fattr *fattr)
|
||||
static int nfs_inode_attrs_cmp_generic(const struct nfs_fattr *fattr,
|
||||
const struct inode *inode)
|
||||
{
|
||||
unsigned long attr_gencount = NFS_I(inode)->attr_gencount;
|
||||
|
||||
@ -1683,15 +1678,93 @@ static int nfs_inode_attrs_need_update(const struct inode *inode, const struct n
|
||||
(long)(attr_gencount - nfs_read_attr_generation_counter()) > 0;
|
||||
}
|
||||
|
||||
static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
|
||||
/**
|
||||
* nfs_inode_attrs_cmp_monotonic - compare attributes
|
||||
* @fattr: attributes
|
||||
* @inode: pointer to inode
|
||||
*
|
||||
* Attempt to divine whether or not an RPC call reply carrying stale
|
||||
* attributes got scheduled after another call carrying updated ones.
|
||||
*
|
||||
* We assume that the server observes monotonic semantics for
|
||||
* the change attribute, so a larger value means that the attributes in
|
||||
* @fattr are more recent, in which case the function returns the
|
||||
* value '1'.
|
||||
* A return value of '0' indicates no measurable change
|
||||
* A return value of '-1' means that the attributes in @inode are
|
||||
* more recent.
|
||||
*/
|
||||
static int nfs_inode_attrs_cmp_monotonic(const struct nfs_fattr *fattr,
|
||||
const struct inode *inode)
|
||||
{
|
||||
int ret;
|
||||
s64 diff = fattr->change_attr - inode_peek_iversion_raw(inode);
|
||||
if (diff > 0)
|
||||
return 1;
|
||||
return diff == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_inode_attrs_cmp_strict_monotonic - compare attributes
|
||||
* @fattr: attributes
|
||||
* @inode: pointer to inode
|
||||
*
|
||||
* Attempt to divine whether or not an RPC call reply carrying stale
|
||||
* attributes got scheduled after another call carrying updated ones.
|
||||
*
|
||||
* We assume that the server observes strictly monotonic semantics for
|
||||
* the change attribute, so a larger value means that the attributes in
|
||||
* @fattr are more recent, in which case the function returns the
|
||||
* value '1'.
|
||||
* A return value of '-1' means that the attributes in @inode are
|
||||
* more recent or unchanged.
|
||||
*/
|
||||
static int nfs_inode_attrs_cmp_strict_monotonic(const struct nfs_fattr *fattr,
|
||||
const struct inode *inode)
|
||||
{
|
||||
return nfs_inode_attrs_cmp_monotonic(fattr, inode) > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_inode_attrs_cmp - compare attributes
|
||||
* @fattr: attributes
|
||||
* @inode: pointer to inode
|
||||
*
|
||||
* This function returns '1' if it thinks the attributes in @fattr are
|
||||
* more recent than the ones cached in @inode. It returns '-1' if
|
||||
* the attributes in @inode are more recent than the ones in @fattr,
|
||||
* and it returns 0 if not sure.
|
||||
*/
|
||||
static int nfs_inode_attrs_cmp(const struct nfs_fattr *fattr,
|
||||
const struct inode *inode)
|
||||
{
|
||||
if (nfs_inode_attrs_cmp_generic(fattr, inode) > 0)
|
||||
return 1;
|
||||
switch (NFS_SERVER(inode)->change_attr_type) {
|
||||
case NFS4_CHANGE_TYPE_IS_UNDEFINED:
|
||||
break;
|
||||
case NFS4_CHANGE_TYPE_IS_TIME_METADATA:
|
||||
if (!(fattr->valid & NFS_ATTR_FATTR_CHANGE))
|
||||
break;
|
||||
return nfs_inode_attrs_cmp_monotonic(fattr, inode);
|
||||
default:
|
||||
if (!(fattr->valid & NFS_ATTR_FATTR_CHANGE))
|
||||
break;
|
||||
return nfs_inode_attrs_cmp_strict_monotonic(fattr, inode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs_refresh_inode_locked(struct inode *inode,
|
||||
struct nfs_fattr *fattr)
|
||||
{
|
||||
int attr_cmp = nfs_inode_attrs_cmp(fattr, inode);
|
||||
int ret = 0;
|
||||
|
||||
trace_nfs_refresh_inode_enter(inode);
|
||||
|
||||
if (nfs_inode_attrs_need_update(inode, fattr))
|
||||
if (attr_cmp > 0)
|
||||
ret = nfs_update_inode(inode, fattr);
|
||||
else
|
||||
else if (attr_cmp == 0)
|
||||
ret = nfs_check_inode_attributes(inode, fattr);
|
||||
|
||||
trace_nfs_refresh_inode_exit(inode, ret);
|
||||
@ -1776,11 +1849,13 @@ EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
|
||||
*/
|
||||
int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr)
|
||||
{
|
||||
int attr_cmp = nfs_inode_attrs_cmp(fattr, inode);
|
||||
int status;
|
||||
|
||||
/* Don't do a WCC update if these attributes are already stale */
|
||||
if ((fattr->valid & NFS_ATTR_FATTR) == 0 ||
|
||||
!nfs_inode_attrs_need_update(inode, fattr)) {
|
||||
if (attr_cmp < 0)
|
||||
return 0;
|
||||
if ((fattr->valid & NFS_ATTR_FATTR) == 0 || !attr_cmp) {
|
||||
fattr->valid &= ~(NFS_ATTR_FATTR_PRECHANGE
|
||||
| NFS_ATTR_FATTR_PRESIZE
|
||||
| NFS_ATTR_FATTR_PREMTIME
|
||||
|
@ -1186,10 +1186,23 @@ nfs4_update_changeattr_locked(struct inode *inode,
|
||||
unsigned long timestamp, unsigned long cache_validity)
|
||||
{
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
u64 change_attr = inode_peek_iversion_raw(inode);
|
||||
|
||||
cache_validity |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
|
||||
|
||||
if (cinfo->atomic && cinfo->before == inode_peek_iversion_raw(inode)) {
|
||||
switch (NFS_SERVER(inode)->change_attr_type) {
|
||||
case NFS4_CHANGE_TYPE_IS_UNDEFINED:
|
||||
break;
|
||||
case NFS4_CHANGE_TYPE_IS_TIME_METADATA:
|
||||
if ((s64)(change_attr - cinfo->after) > 0)
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
if ((s64)(change_attr - cinfo->after) >= 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cinfo->atomic && cinfo->before == change_attr) {
|
||||
nfsi->attrtimeo_timestamp = jiffies;
|
||||
} else {
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
@ -1201,7 +1214,7 @@ nfs4_update_changeattr_locked(struct inode *inode,
|
||||
cache_validity |= NFS_INO_REVAL_PAGECACHE;
|
||||
}
|
||||
|
||||
if (cinfo->before != inode_peek_iversion_raw(inode))
|
||||
if (cinfo->before != change_attr)
|
||||
cache_validity |= NFS_INO_INVALID_ACCESS |
|
||||
NFS_INO_INVALID_ACL |
|
||||
NFS_INO_INVALID_XATTR;
|
||||
@ -1209,8 +1222,9 @@ nfs4_update_changeattr_locked(struct inode *inode,
|
||||
inode_set_iversion_raw(inode, cinfo->after);
|
||||
nfsi->read_cache_jiffies = timestamp;
|
||||
nfsi->attr_gencount = nfs_inc_attr_generation_counter();
|
||||
nfs_set_cache_invalid(inode, cache_validity);
|
||||
nfsi->cache_validity &= ~NFS_INO_INVALID_CHANGE;
|
||||
out:
|
||||
nfs_set_cache_invalid(inode, cache_validity);
|
||||
}
|
||||
|
||||
void
|
||||
|
Loading…
Reference in New Issue
Block a user