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:
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
|
||||
|
||||
Reference in New Issue
Block a user