2018-03-20 20:53:56

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 0/6] Finer grained attribute checking for NFSv4.x

This patch series allows the NFS client to track attribute information
on a more fine grained basis, so that we do not end up worrying about
the validity of the mode, owner, group etc information when asked about
the timestamps.
Basically, this means that the NFSv4 cache consistency data is tracked
separately from the other attribute data, allowing it to be revalidated
by the cache consistency GETATTR operations.

Trond Myklebust (6):
NFS: Convert NFS_INO_INVALID flags to unsigned long
NFS: Don't force a revalidation of all attributes if change is missing
NFS: Don't redirty the attribute cache in nfs_wcc_update_inode()
NFS: Don't force unnecessary cache invalidation in nfs_update_inode()
NFS: More fine grained attribute tracking
NFSv4: Ignore change attribute invalidations if we hold a delegation

fs/nfs/dir.c | 4 +-
fs/nfs/inode.c | 132 ++++++++++++++++++++++++++++---------------------
fs/nfs/nfs4proc.c | 7 ++-
fs/nfs/write.c | 7 ++-
include/linux/nfs_fs.h | 35 ++++++++-----
5 files changed, 111 insertions(+), 74 deletions(-)

--
2.14.3



2018-03-20 20:53:59

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 3/6] NFS: Don't redirty the attribute cache in nfs_wcc_update_inode()

If we received weak cache consistency data from the server, then those
attributes are up to date, and there is no reason to mark them as
dirty in the attribute cache.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/inode.c | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 5a721c3f4eb0..d8e8bf9da211 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1303,24 +1303,20 @@ static bool nfs_file_has_buffered_writers(struct nfs_inode *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)
&& (fattr->valid & NFS_ATTR_FATTR_CHANGE)
&& inode_eq_iversion_raw(inode, fattr->pre_change_attr)) {
inode_set_iversion_raw(inode, fattr->change_attr);
if (S_ISDIR(inode->i_mode))
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 ((fattr->valid & NFS_ATTR_FATTR_PRECTIME)
&& (fattr->valid & NFS_ATTR_FATTR_CTIME)
&& timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) {
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
- ret |= NFS_INO_INVALID_ATTR;
}

if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME)
@@ -1329,17 +1325,13 @@ static unsigned long nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
if (S_ISDIR(inode->i_mode))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_DATA);
- ret |= NFS_INO_INVALID_ATTR;
}
if ((fattr->valid & NFS_ATTR_FATTR_PRESIZE)
&& (fattr->valid & NFS_ATTR_FATTR_SIZE)
&& i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size)
&& !nfs_have_writebacks(inode)) {
i_size_write(inode, nfs_size_to_loff_t(fattr->size));
- ret |= NFS_INO_INVALID_ATTR;
}
-
- return ret;
}

/**
@@ -1789,7 +1781,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
| NFS_INO_REVAL_PAGECACHE);

/* Do atomic weak cache consistency updates */
- invalid |= nfs_wcc_update_inode(inode, fattr);
+ nfs_wcc_update_inode(inode, fattr);

if (pnfs_layoutcommit_outstanding(inode)) {
nfsi->cache_validity |= save_cache_validity & NFS_INO_INVALID_ATTR;
--
2.14.3


2018-03-20 20:53:58

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 2/6] NFS: Don't force a revalidation of all attributes if change is missing

Even if the change attribute is missing, it is still OK to mark the other
attributes as being up to date.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/inode.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 9da00b2e26a1..5a721c3f4eb0 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1813,7 +1813,10 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
inode_set_iversion_raw(inode, fattr->change_attr);
}
} else {
- nfsi->cache_validity |= save_cache_validity;
+ nfsi->cache_validity |= save_cache_validity &
+ (NFS_INO_INVALID_ATTR
+ | NFS_INO_REVAL_PAGECACHE
+ | NFS_INO_REVAL_FORCED);
cache_revalidated = false;
}

--
2.14.3


2018-03-20 20:54:01

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 6/6] NFSv4: Ignore change attribute invalidations if we hold a delegation

Don't bother even recording an invalid change attribute if we hold a
delegation since we already know the state of our attribute cache.
We can rely on the fact that we will pick up a copy from the server
when we return the delegation.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/inode.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 23880f45c1e4..a2c5d4ad4535 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -200,7 +200,10 @@ bool nfs_check_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);
+ 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)
flags &= ~NFS_INO_INVALID_DATA;
nfsi->cache_validity |= flags;
--
2.14.3


2018-03-20 20:53:57

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 1/6] NFS: Convert NFS_INO_INVALID flags to unsigned long

The cache validity attribute is unsigned long, so make sure that
the flags are too.

Signed-off-by: Trond Myklebust <[email protected]>
---
include/linux/nfs_fs.h | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 38187c68063d..4afb11be73f4 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -198,14 +198,14 @@ struct nfs_inode {
/*
* Cache validity bit flags
*/
-#define NFS_INO_INVALID_ATTR 0x0001 /* cached attrs are invalid */
-#define NFS_INO_INVALID_DATA 0x0002 /* cached data is invalid */
-#define NFS_INO_INVALID_ATIME 0x0004 /* cached atime is invalid */
-#define NFS_INO_INVALID_ACCESS 0x0008 /* cached access cred invalid */
-#define NFS_INO_INVALID_ACL 0x0010 /* cached acls are invalid */
-#define NFS_INO_REVAL_PAGECACHE 0x0020 /* must revalidate pagecache */
-#define NFS_INO_REVAL_FORCED 0x0040 /* force revalidation ignoring a delegation */
-#define NFS_INO_INVALID_LABEL 0x0080 /* cached label is invalid */
+#define NFS_INO_INVALID_ATTR BIT(0) /* cached attrs are invalid */
+#define NFS_INO_INVALID_DATA BIT(1) /* cached data is invalid */
+#define NFS_INO_INVALID_ATIME BIT(2) /* cached atime is invalid */
+#define NFS_INO_INVALID_ACCESS BIT(3) /* cached access cred invalid */
+#define NFS_INO_INVALID_ACL BIT(4) /* cached acls are invalid */
+#define NFS_INO_REVAL_PAGECACHE BIT(5) /* must revalidate pagecache */
+#define NFS_INO_REVAL_FORCED BIT(6) /* force revalidation ignoring a delegation */
+#define NFS_INO_INVALID_LABEL BIT(7) /* cached label is invalid */

/*
* Bit offsets in flags field
--
2.14.3


2018-03-20 20:54:00

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 4/6] NFS: Don't force unnecessary cache invalidation in nfs_update_inode()

If we managed to revalidate all the attributes, then there is no reason
to mark them as invalid again. We do, however want to ensure that we
set nfsi->attrtimeo correctly.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/inode.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index d8e8bf9da211..6e5a96e2e9a0 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1799,6 +1799,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
| NFS_INO_INVALID_DATA
| NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL;
+ /* Force revalidate of all attributes */
+ save_cache_validity |= NFS_INO_INVALID_ATTR;
if (S_ISDIR(inode->i_mode))
nfs_force_lookup_revalidate(inode);
}
@@ -1937,6 +1939,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)

/* Update attrtimeo value if we're out of the unstable period */
if (invalid & NFS_INO_INVALID_ATTR) {
+ invalid &= ~NFS_INO_INVALID_ATTR;
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now;
@@ -1957,10 +1960,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
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 */
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode)))
--
2.14.3


2018-03-20 20:54:01

by Trond Myklebust

[permalink] [raw]
Subject: [PATCH 5/6] NFS: More fine grained attribute tracking

Currently, if the NFS_INO_INVALID_ATTR flag is set, for instance by
a call to nfs_post_op_update_inode_locked(), then it will not be cleared
until all the attributes have been revalidated. This means, for instance,
that NFSv4 writes will always force a full attribute revalidation.

Track the ctime, mtime, size and change attribute separately from the
other attributes so that we can have nfs_post_op_update_inode_locked()
set them correctly, and later have the cache consistency bitmask be
able to clear them.

Signed-off-by: Trond Myklebust <[email protected]>
---
fs/nfs/dir.c | 4 +-
fs/nfs/inode.c | 109 +++++++++++++++++++++++++++++--------------------
fs/nfs/nfs4proc.c | 7 +++-
fs/nfs/write.c | 7 +++-
include/linux/nfs_fs.h | 21 +++++++---
5 files changed, 94 insertions(+), 54 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 8001f8c7ad0e..73f8b43d988c 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -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 */
if (inode->i_nlink == 1)
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);
}

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 6e5a96e2e9a0..23880f45c1e4 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -452,7 +452,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st
inode->i_mode = fattr->mode;
if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
&& 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
* that's precisely what we have in nfs_file_inode_operations.
*/
@@ -498,37 +498,35 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
inode->i_atime = fattr->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)
inode->i_mtime = fattr->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)
inode->i_ctime = fattr->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)
inode_set_iversion_raw(inode, fattr->change_attr);
else
- nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR
- | NFS_INO_REVAL_PAGECACHE);
+ nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE);
if (fattr->valid & NFS_ATTR_FATTR_SIZE)
inode->i_size = nfs_size_to_loff_t(fattr->size);
else
- nfs_set_cache_invalid(inode, NFS_INO_INVALID_ATTR
- | NFS_INO_REVAL_PAGECACHE);
+ nfs_set_cache_invalid(inode, NFS_INO_INVALID_SIZE);
if (fattr->valid & NFS_ATTR_FATTR_NLINK)
set_nlink(inode, fattr->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)
inode->i_uid = fattr->uid;
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)
inode->i_gid = fattr->gid;
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)
inode->i_blocks = fattr->du.nfs2.blocks;
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
@@ -657,6 +655,7 @@ static int nfs_vmtruncate(struct inode * inode, loff_t offset)
* nfs_setattr_update_inode - Update inode metadata after a setattr call.
* @inode: pointer to struct inode
* @attr: pointer to struct iattr
+ * @fattr: pointer to struct nfs_fattr
*
* Note: we do this in the *proc.c in order to ensure that
* it works for things like exclusive creates too.
@@ -669,6 +668,8 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,

spin_lock(&inode->i_lock);
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) != 0) {
int mode = attr->ia_mode & S_IALLUGO;
@@ -683,13 +684,12 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
| NFS_INO_INVALID_ACL);
}
if ((attr->ia_valid & ATTR_SIZE) != 0) {
+ nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
nfs_vmtruncate(inode, attr->ia_size);
}
if (fattr->valid)
nfs_update_inode(inode, fattr);
- else
- NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR;
spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
@@ -1361,33 +1361,41 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
if (!nfs_file_has_buffered_writers(nfsi)) {
/* Verify a few of the more important attributes */
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))
- invalid |= NFS_INO_INVALID_ATTR;
+ invalid |= NFS_INO_INVALID_MTIME;

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) {
cur_size = i_size_read(inode);
new_isize = nfs_size_to_loff_t(fattr->size);
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? */
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))
- 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))
- 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? */
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))
invalid |= NFS_INO_INVALID_ATIME;
@@ -1589,10 +1597,9 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
}
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))
invalid |= NFS_INO_INVALID_DATA;
nfs_set_cache_invalid(inode, invalid);
@@ -1621,7 +1628,9 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)

spin_lock(&inode->i_lock);
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);

return status;
@@ -1673,7 +1682,10 @@ int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fa
fattr->valid |= NFS_ATTR_FATTR_PRESIZE;
}
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;
}

@@ -1795,12 +1807,15 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
inode->i_sb->s_id, inode->i_ino);
/* Could it be a race with writeback? */
if (!have_writers) {
- invalid |= NFS_INO_INVALID_ATTR
+ invalid |= NFS_INO_INVALID_CHANGE
| NFS_INO_INVALID_DATA
| NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL;
/* Force revalidate of all attributes */
- save_cache_validity |= NFS_INO_INVALID_ATTR;
+ 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))
nfs_force_lookup_revalidate(inode);
}
@@ -1808,7 +1823,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
}
} else {
nfsi->cache_validity |= save_cache_validity &
- (NFS_INO_INVALID_ATTR
+ (NFS_INO_INVALID_CHANGE
| NFS_INO_REVAL_PAGECACHE
| NFS_INO_REVAL_FORCED);
cache_revalidated = false;
@@ -1818,7 +1833,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
} else if (server->caps & NFS_CAP_MTIME) {
nfsi->cache_validity |= save_cache_validity &
- (NFS_INO_INVALID_ATTR
+ (NFS_INO_INVALID_MTIME
| NFS_INO_REVAL_FORCED);
cache_revalidated = false;
}
@@ -1827,7 +1842,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
} else if (server->caps & NFS_CAP_CTIME) {
nfsi->cache_validity |= save_cache_validity &
- (NFS_INO_INVALID_ATTR
+ (NFS_INO_INVALID_CTIME
| NFS_INO_REVAL_FORCED);
cache_revalidated = false;
}
@@ -1842,7 +1857,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
if (!nfs_have_writebacks(inode) || new_isize > cur_isize) {
i_size_write(inode, new_isize);
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 "
"(%Ld to %Ld)\n",
@@ -1853,7 +1868,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
}
} else {
nfsi->cache_validity |= save_cache_validity &
- (NFS_INO_INVALID_ATTR
+ (NFS_INO_INVALID_SIZE
| NFS_INO_REVAL_PAGECACHE
| NFS_INO_REVAL_FORCED);
cache_revalidated = false;
@@ -1874,55 +1889,61 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
umode_t newmode = inode->i_mode & S_IFMT;
newmode |= fattr->mode & S_IALLUGO;
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) {
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_OTHER
| NFS_INO_REVAL_FORCED);
cache_revalidated = false;
}

if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
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;
}
} else if (server->caps & NFS_CAP_OWNER) {
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_OTHER
| NFS_INO_REVAL_FORCED);
cache_revalidated = false;
}

if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
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;
}
} else if (server->caps & NFS_CAP_OWNER_GROUP) {
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_OTHER
| NFS_INO_REVAL_FORCED);
cache_revalidated = false;
}

if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
if (inode->i_nlink != fattr->nlink) {
- invalid |= NFS_INO_INVALID_ATTR;
+ invalid |= NFS_INO_INVALID_OTHER;
if (S_ISDIR(inode->i_mode))
invalid |= NFS_INO_INVALID_DATA;
set_nlink(inode, fattr->nlink);
}
} else if (server->caps & NFS_CAP_NLINK) {
nfsi->cache_validity |= save_cache_validity &
- (NFS_INO_INVALID_ATTR
+ (NFS_INO_INVALID_OTHER
| NFS_INO_REVAL_FORCED);
cache_revalidated = false;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index b4ebee12fe28..acb3350c7571 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1045,7 +1045,9 @@ static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo,
struct nfs_inode *nfsi = NFS_I(dir);

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)) {
nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE;
nfsi->attrtimeo_timestamp = jiffies;
@@ -5364,7 +5366,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
* so mark the attribute cache invalid.
*/
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);
nfs_access_zap_cache(inode);
nfs_zap_acl_cache(inode);
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index e7d8ceae8f26..485ce488eb0b 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1562,8 +1562,11 @@ static int nfs_writeback_done(struct rpc_task *task,
}

/* Deal with the suid/sgid bit corner case */
- if (nfs_should_remove_suid(inode))
- nfs_mark_for_revalidate(inode);
+ if (nfs_should_remove_suid(inode)) {
+ spin_lock(&inode->i_lock);
+ NFS_I(inode)->cache_validity |= NFS_INO_INVALID_OTHER;
+ spin_unlock(&inode->i_lock);
+ }
return 0;
}

diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 4afb11be73f4..2f129bbfaae8 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -198,7 +198,6 @@ struct nfs_inode {
/*
* Cache validity bit flags
*/
-#define NFS_INO_INVALID_ATTR BIT(0) /* cached attrs are invalid */
#define NFS_INO_INVALID_DATA BIT(1) /* cached data is invalid */
#define NFS_INO_INVALID_ATIME BIT(2) /* cached atime is invalid */
#define NFS_INO_INVALID_ACCESS BIT(3) /* cached access cred invalid */
@@ -206,6 +205,17 @@ struct nfs_inode {
#define NFS_INO_REVAL_PAGECACHE BIT(5) /* must revalidate pagecache */
#define NFS_INO_REVAL_FORCED BIT(6) /* force revalidation ignoring a delegation */
#define NFS_INO_INVALID_LABEL BIT(7) /* 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
@@ -292,10 +302,11 @@ static inline void nfs_mark_for_revalidate(struct inode *inode)
struct nfs_inode *nfsi = NFS_I(inode);

spin_lock(&inode->i_lock);
- nfsi->cache_validity |= NFS_INO_INVALID_ATTR |
- NFS_INO_REVAL_PAGECACHE |
- NFS_INO_INVALID_ACCESS |
- NFS_INO_INVALID_ACL;
+ nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE
+ | NFS_INO_INVALID_ACCESS
+ | NFS_INO_INVALID_ACL
+ | NFS_INO_INVALID_CHANGE
+ | NFS_INO_INVALID_CTIME;
if (S_ISDIR(inode->i_mode))
nfsi->cache_validity |= NFS_INO_INVALID_DATA;
spin_unlock(&inode->i_lock);
--
2.14.3