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:
David Howells
2019-05-20 08:48:46 +01:00
parent 8b6a666a97
commit a0753c2900
2 changed files with 113 additions and 18 deletions

View File

@@ -303,6 +303,40 @@ someone_else_changed_it:
return;
}
static bool afs_check_permit_rcu(struct afs_vnode *vnode, struct key *key,
afs_access_t *_access)
{
const struct afs_permits *permits;
int i;
_enter("{%llx:%llu},%x",
vnode->fid.vid, vnode->fid.vnode, key_serial(key));
/* check the permits to see if we've got one yet */
if (key == vnode->volume->cell->anonymous_key) {
*_access = vnode->status.anon_access;
_leave(" = t [anon %x]", *_access);
return true;
}
permits = rcu_dereference(vnode->permit_cache);
if (permits) {
for (i = 0; i < permits->nr_permits; i++) {
if (permits->permits[i].key < key)
continue;
if (permits->permits[i].key > key)
break;
*_access = permits->permits[i].access;
_leave(" = %u [perm %x]", !permits->invalidated, *_access);
return !permits->invalidated;
}
}
_leave(" = f");
return false;
}
/*
* check with the fileserver to see if the directory or parent directory is
* permitted to be accessed with this authorisation, and if so, what access it
@@ -369,33 +403,42 @@ int afs_permission(struct inode *inode, int mask)
struct afs_vnode *vnode = AFS_FS_I(inode);
afs_access_t uninitialized_var(access);
struct key *key;
int ret;
if (mask & MAY_NOT_BLOCK)
return -ECHILD;
int ret = 0;
_enter("{{%llx:%llu},%lx},%x,",
vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
_leave(" = %ld [key]", PTR_ERR(key));
return PTR_ERR(key);
if (mask & MAY_NOT_BLOCK) {
key = afs_request_key_rcu(vnode->volume->cell);
if (IS_ERR(key))
return -ECHILD;
ret = -ECHILD;
if (!afs_check_validity(vnode) ||
!afs_check_permit_rcu(vnode, key, &access))
goto error;
} else {
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
_leave(" = %ld [key]", PTR_ERR(key));
return PTR_ERR(key);
}
ret = afs_validate(vnode, key);
if (ret < 0)
goto error;
/* check the permits to see if we've got one yet */
ret = afs_check_permit(vnode, key, &access);
if (ret < 0)
goto error;
}
ret = afs_validate(vnode, key);
if (ret < 0)
goto error;
/* check the permits to see if we've got one yet */
ret = afs_check_permit(vnode, key, &access);
if (ret < 0)
goto error;
/* interpret the access mask */
_debug("REQ %x ACC %x on %s",
mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
ret = 0;
if (S_ISDIR(inode->i_mode)) {
if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) {
if (!(access & AFS_ACE_LOOKUP))