summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2019-05-20 03:48:46 -0400
committerDavid Howells <dhowells@redhat.com>2019-09-02 06:43:54 -0400
commita0753c29004f4983e303abce019f29e183b1ee48 (patch)
tree0227fe9742afaf92caddb7e5f086cf5f474becf0
parent8b6a666a97544bf307190a05947742b8357aa962 (diff)
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>
-rw-r--r--fs/afs/dir.c54
-rw-r--r--fs/afs/security.c75
2 files changed, 112 insertions, 17 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,
966} 966}
967 967
968/* 968/*
969 * Check the validity of a dentry under RCU conditions.
970 */
971static int afs_d_revalidate_rcu(struct dentry *dentry)
972{
973 struct afs_vnode *dvnode, *vnode;
974 struct dentry *parent;
975 struct inode *dir, *inode;
976 long dir_version, de_version;
977
978 _enter("%p", dentry);
979
980 /* Check the parent directory is still valid first. */
981 parent = READ_ONCE(dentry->d_parent);
982 dir = d_inode_rcu(parent);
983 if (!dir)
984 return -ECHILD;
985 dvnode = AFS_FS_I(dir);
986 if (test_bit(AFS_VNODE_DELETED, &dvnode->flags))
987 return -ECHILD;
988
989 if (!afs_check_validity(dvnode))
990 return -ECHILD;
991
992 /* We only need to invalidate a dentry if the server's copy changed
993 * behind our back. If we made the change, it's no problem. Note that
994 * on a 32-bit system, we only have 32 bits in the dentry to store the
995 * version.
996 */
997 dir_version = (long)READ_ONCE(dvnode->status.data_version);
998 de_version = (long)READ_ONCE(dentry->d_fsdata);
999 if (de_version != dir_version) {
1000 dir_version = (long)READ_ONCE(dvnode->invalid_before);
1001 if (de_version - dir_version < 0)
1002 return -ECHILD;
1003 }
1004
1005 /* Check to see if the vnode referred to by the dentry still
1006 * has a callback.
1007 */
1008 if (d_really_is_positive(dentry)) {
1009 inode = d_inode_rcu(dentry);
1010 if (inode) {
1011 vnode = AFS_FS_I(inode);
1012 if (!afs_check_validity(vnode))
1013 return -ECHILD;
1014 }
1015 }
1016
1017 return 1; /* Still valid */
1018}
1019
1020/*
969 * check that a dentry lookup hit has found a valid entry 1021 * check that a dentry lookup hit has found a valid entry
970 * - NOTE! the hit can be a negative hit too, so we can't assume we have an 1022 * - NOTE! the hit can be a negative hit too, so we can't assume we have an
971 * inode 1023 * inode
@@ -982,7 +1034,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
982 int ret; 1034 int ret;
983 1035
984 if (flags & LOOKUP_RCU) 1036 if (flags & LOOKUP_RCU)
985 return -ECHILD; 1037 return afs_d_revalidate_rcu(dentry);
986 1038
987 if (d_really_is_positive(dentry)) { 1039 if (d_really_is_positive(dentry)) {
988 vnode = AFS_FS_I(d_inode(dentry)); 1040 vnode = AFS_FS_I(d_inode(dentry));
diff --git a/fs/afs/security.c b/fs/afs/security.c
index ef2fd34ba282..ce9de1e6742b 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -303,6 +303,40 @@ someone_else_changed_it:
303 return; 303 return;
304} 304}
305 305
306static bool afs_check_permit_rcu(struct afs_vnode *vnode, struct key *key,
307 afs_access_t *_access)
308{
309 const struct afs_permits *permits;
310 int i;
311
312 _enter("{%llx:%llu},%x",
313 vnode->fid.vid, vnode->fid.vnode, key_serial(key));
314
315 /* check the permits to see if we've got one yet */
316 if (key == vnode->volume->cell->anonymous_key) {
317 *_access = vnode->status.anon_access;
318 _leave(" = t [anon %x]", *_access);
319 return true;
320 }
321
322 permits = rcu_dereference(vnode->permit_cache);
323 if (permits) {
324 for (i = 0; i < permits->nr_permits; i++) {
325 if (permits->permits[i].key < key)
326 continue;
327 if (permits->permits[i].key > key)
328 break;
329
330 *_access = permits->permits[i].access;
331 _leave(" = %u [perm %x]", !permits->invalidated, *_access);
332 return !permits->invalidated;
333 }
334 }
335
336 _leave(" = f");
337 return false;
338}
339
306/* 340/*
307 * check with the fileserver to see if the directory or parent directory is 341 * check with the fileserver to see if the directory or parent directory is
308 * permitted to be accessed with this authorisation, and if so, what access it 342 * 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)
369 struct afs_vnode *vnode = AFS_FS_I(inode); 403 struct afs_vnode *vnode = AFS_FS_I(inode);
370 afs_access_t uninitialized_var(access); 404 afs_access_t uninitialized_var(access);
371 struct key *key; 405 struct key *key;
372 int ret; 406 int ret = 0;
373
374 if (mask & MAY_NOT_BLOCK)
375 return -ECHILD;
376 407
377 _enter("{{%llx:%llu},%lx},%x,", 408 _enter("{{%llx:%llu},%lx},%x,",
378 vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask); 409 vnode->fid.vid, vnode->fid.vnode, vnode->flags, mask);
379 410
380 key = afs_request_key(vnode->volume->cell); 411 if (mask & MAY_NOT_BLOCK) {
381 if (IS_ERR(key)) { 412 key = afs_request_key_rcu(vnode->volume->cell);
382 _leave(" = %ld [key]", PTR_ERR(key)); 413 if (IS_ERR(key))
383 return PTR_ERR(key); 414 return -ECHILD;
384 }
385 415
386 ret = afs_validate(vnode, key); 416 ret = -ECHILD;
387 if (ret < 0) 417 if (!afs_check_validity(vnode) ||
388 goto error; 418 !afs_check_permit_rcu(vnode, key, &access))
419 goto error;
420 } else {
421 key = afs_request_key(vnode->volume->cell);
422 if (IS_ERR(key)) {
423 _leave(" = %ld [key]", PTR_ERR(key));
424 return PTR_ERR(key);
425 }
389 426
390 /* check the permits to see if we've got one yet */ 427 ret = afs_validate(vnode, key);
391 ret = afs_check_permit(vnode, key, &access); 428 if (ret < 0)
392 if (ret < 0) 429 goto error;
393 goto error; 430
431 /* check the permits to see if we've got one yet */
432 ret = afs_check_permit(vnode, key, &access);
433 if (ret < 0)
434 goto error;
435 }
394 436
395 /* interpret the access mask */ 437 /* interpret the access mask */
396 _debug("REQ %x ACC %x on %s", 438 _debug("REQ %x ACC %x on %s",
397 mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file"); 439 mask, access, S_ISDIR(inode->i_mode) ? "dir" : "file");
398 440
441 ret = 0;
399 if (S_ISDIR(inode->i_mode)) { 442 if (S_ISDIR(inode->i_mode)) {
400 if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) { 443 if (mask & (MAY_EXEC | MAY_READ | MAY_CHDIR)) {
401 if (!(access & AFS_ACE_LOOKUP)) 444 if (!(access & AFS_ACE_LOOKUP))