diff options
author | David Howells <dhowells@redhat.com> | 2019-05-20 09:48:46 +0200 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2019-09-02 12:43:54 +0200 |
commit | a0753c29004f4983e303abce019f29e183b1ee48 (patch) | |
tree | 0227fe9742afaf92caddb7e5f086cf5f474becf0 /fs/afs/dir.c | |
parent | afs: Provide an RCU-capable key lookup (diff) | |
download | linux-a0753c29004f4983e303abce019f29e183b1ee48.tar.xz linux-a0753c29004f4983e303abce019f29e183b1ee48.zip |
afs: Support RCU pathwalk
Make afs_permission() and afs_d_revalidate() do initial checks in RCU-mode
pathwalk to reduce latency in pathwalk elements that get done multiple
times. We don't need to query the server unless we've received a
notification from it that something has changed or the callback has
expired.
This requires that we can request a key and check permits under RCU
conditions if we need to.
Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'fs/afs/dir.c')
-rw-r--r-- | fs/afs/dir.c | 54 |
1 files changed, 53 insertions, 1 deletions
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 139b4e3cc946..cc12772d0a4d 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -966,6 +966,58 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, } /* + * Check the validity of a dentry under RCU conditions. + */ +static int afs_d_revalidate_rcu(struct dentry *dentry) +{ + struct afs_vnode *dvnode, *vnode; + struct dentry *parent; + struct inode *dir, *inode; + long dir_version, de_version; + + _enter("%p", dentry); + + /* Check the parent directory is still valid first. */ + parent = READ_ONCE(dentry->d_parent); + dir = d_inode_rcu(parent); + if (!dir) + return -ECHILD; + dvnode = AFS_FS_I(dir); + if (test_bit(AFS_VNODE_DELETED, &dvnode->flags)) + return -ECHILD; + + if (!afs_check_validity(dvnode)) + return -ECHILD; + + /* 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)READ_ONCE(dvnode->status.data_version); + de_version = (long)READ_ONCE(dentry->d_fsdata); + if (de_version != dir_version) { + dir_version = (long)READ_ONCE(dvnode->invalid_before); + if (de_version - dir_version < 0) + return -ECHILD; + } + + /* Check to see if the vnode referred to by the dentry still + * has a callback. + */ + if (d_really_is_positive(dentry)) { + inode = d_inode_rcu(dentry); + if (inode) { + vnode = AFS_FS_I(inode); + if (!afs_check_validity(vnode)) + return -ECHILD; + } + } + + return 1; /* Still valid */ +} + +/* * check that a dentry lookup hit has found a valid entry * - NOTE! the hit can be a negative hit too, so we can't assume we have an * inode @@ -982,7 +1034,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) int ret; if (flags & LOOKUP_RCU) - return -ECHILD; + return afs_d_revalidate_rcu(dentry); if (d_really_is_positive(dentry)) { vnode = AFS_FS_I(d_inode(dentry)); |