afs: Keep track of invalid-before version for dentry coherency
authorDavid Howells <dhowells@redhat.com>
Fri, 6 Apr 2018 13:17:24 +0000 (14:17 +0100)
committerDavid Howells <dhowells@redhat.com>
Mon, 9 Apr 2018 20:53:59 +0000 (21:53 +0100)
Each afs dentry is tagged with the version that the parent directory was at
last time it was validated and, currently, if this differs, the directory
is scanned and the dentry is refreshed.

However, this leads to an excessive amount of revalidation on directories
that get modified on the client without conflict with another client.  We
know there's no conflict because the parent directory's data version number
got incremented by exactly 1 on any create, mkdir, unlink, etc., therefore
we can trust the current state of the unaffected dentries when we perform a
local directory modification.

Optimise by keeping track of the last version of the parent directory that
was changed outside of the client in the parent directory's vnode and using
that to validate the dentries rather than the current version.

Signed-off-by: David Howells <dhowells@redhat.com>
fs/afs/dir.c
fs/afs/fsclient.c
fs/afs/inode.c
fs/afs/internal.h

index 538ca18efe0d8217725a05d873d566c61a8d4d87..08b499504f6325d3b016cb528a48ea71a757ea4d 100644 (file)
@@ -1015,7 +1015,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
        struct dentry *parent;
        struct inode *inode;
        struct key *key;
-       void *dir_version;
+       long dir_version, de_version;
        int ret;
 
        if (flags & LOOKUP_RCU)
@@ -1059,9 +1059,19 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
                goto out_bad_parent;
        }
 
-       dir_version = (void *) (unsigned long) dir->status.data_version;
-       if (dentry->d_fsdata == dir_version)
-               goto out_valid; /* the dir contents are unchanged */
+       /* We only need to invalidate a dentry if the server's copy changed
+        * behind our back.  If we made the change, it's no problem.  Note that
+        * on a 32-bit system, we only have 32 bits in the dentry to store the
+        * version.
+        */
+       dir_version = (long)dir->status.data_version;
+       de_version = (long)dentry->d_fsdata;
+       if (de_version == dir_version)
+               goto out_valid;
+
+       dir_version = (long)dir->invalid_before;
+       if (de_version - dir_version >= 0)
+               goto out_valid;
 
        _debug("dir modified");
        afs_stat_v(dir, n_reval);
@@ -1120,7 +1130,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
        }
 
 out_valid:
-       dentry->d_fsdata = dir_version;
+       dentry->d_fsdata = (void *)dir_version;
        dput(parent);
        key_put(key);
        _leave(" = 1 [valid]");
index ff87fc6bb27fbbc32dd25d2ff0c419307056684b..f7570d229dcc4d8f304913b23d59e0fcdcd3563f 100644 (file)
@@ -100,6 +100,7 @@ void afs_update_inode_from_status(struct afs_vnode *vnode,
                               (unsigned long long) status->data_version,
                               vnode->fid.vid, vnode->fid.vnode,
                               (unsigned long long) *expected_version);
+                       vnode->invalid_before = status->data_version;
                        set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
                        set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
                }
index 2e32d475ec11f583eab1a7d6e55e61be8834532c..07f450513f3e95a389f1fd24fa39676df3c4018f 100644 (file)
@@ -83,6 +83,7 @@ static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key)
 
        inode->i_blocks         = 0;
        inode->i_mapping->a_ops = &afs_fs_aops;
+       vnode->invalid_before   = vnode->status.data_version;
 
        read_sequnlock_excl(&vnode->cb_lock);
 
index ac3076c2a8e827d0e77df5ecf31c63b7d4ae4b0b..adf9b17d328c1b28d12b5e930b2ed763dcc6a1a9 100644 (file)
@@ -479,6 +479,7 @@ struct afs_vnode {
        struct afs_volume       *volume;        /* volume on which vnode resides */
        struct afs_fid          fid;            /* the file identifier for this inode */
        struct afs_file_status  status;         /* AFS status info for this file */
+       afs_dataversion_t       invalid_before; /* Child dentries are invalid before this */
 #ifdef CONFIG_AFS_FSCACHE
        struct fscache_cookie   *cache;         /* caching cookie */
 #endif