@@ -735,12 +735,22 @@ int nfs_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int query_flags)
{
struct inode *inode = d_inode(path->dentry);
- int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
+ unsigned long cache_validity;
+ bool force_sync = query_flags & AT_STATX_FORCE_SYNC;
+ bool dont_sync = !force_sync && query_flags & AT_STATX_DONT_SYNC;
+ bool need_atime = !dont_sync;
+ bool need_cmtime = !dont_sync;
+ bool reval = force_sync;
int err = 0;
+ if (!(request_mask & STATX_ATIME))
+ need_atime = false;
+ if (!(request_mask & (STATX_CTIME|STATX_MTIME)))
+ need_cmtime = false;
+
trace_nfs_getattr_enter(inode);
/* Flush out writes to the server in order to update c/mtime. */
- if (S_ISREG(inode->i_mode)) {
+ if (S_ISREG(inode->i_mode) && need_cmtime) {
err = filemap_write_and_wait(inode->i_mapping);
if (err)
goto out;
@@ -757,9 +767,22 @@ int nfs_getattr(const struct path *path, struct kstat *stat,
*/
if ((path->mnt->mnt_flags & MNT_NOATIME) ||
((path->mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
- need_atime = 0;
-
- if (need_atime || nfs_need_revalidate_inode(inode)) {
+ need_atime = false;
+
+ /* Check for whether the cached attributes are invalid */
+ cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
+ if (need_cmtime)
+ reval |= cache_validity & NFS_INO_REVAL_PAGECACHE;
+ if (need_atime)
+ reval |= cache_validity & NFS_INO_INVALID_ATIME;
+ if (request_mask & (STATX_MODE|STATX_NLINK|STATX_UID|STATX_GID|
+ STATX_ATIME|STATX_MTIME|STATX_CTIME|
+ STATX_SIZE|STATX_BLOCKS))
+ reval |= cache_validity & NFS_INO_INVALID_ATTR;
+ if (dont_sync)
+ reval = false;
+
+ if (reval) {
struct nfs_server *server = NFS_SERVER(inode);
if (!(server->flags & NFS_MOUNT_NOAC))
@@ -767,13 +790,18 @@ int nfs_getattr(const struct path *path, struct kstat *stat,
else
nfs_readdirplus_parent_cache_hit(path->dentry);
err = __nfs_revalidate_inode(server, inode);
- } else
+ } else if (!dont_sync)
nfs_readdirplus_parent_cache_hit(path->dentry);
if (!err) {
generic_fillattr(inode, stat);
stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
if (S_ISDIR(inode->i_mode))
stat->blksize = NFS_SERVER(inode)->dtsize;
+ /* Return only the requested attrs if others may be stale */
+ if (!reval && cache_validity & (NFS_INO_REVAL_PAGECACHE|
+ NFS_INO_INVALID_ATIME|
+ NFS_INO_INVALID_ATTR))
+ stat->result_mask &= request_mask;
}
out:
trace_nfs_getattr_exit(inode, err);