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>
This commit is contained in:
54
fs/afs/dir.c
54
fs/afs/dir.c
@@ -965,6 +965,58 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
return d;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -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));
|
||||
|
Reference in New Issue
Block a user