aboutsummaryrefslogtreecommitdiffstats
path: root/fs/afs/rotate.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-13 11:59:50 -0500
committerDavid Howells <dhowells@redhat.com>2017-11-17 05:06:13 -0500
commit0fafdc9f888b42499001b7ca9d9f371c0b2932f4 (patch)
tree188f5a33b0a043704c0a080519581ce1cf4f4083 /fs/afs/rotate.c
parentcf9b0772f2e410645fece13b749bd56505b998b8 (diff)
afs: Fix file locking
Fix the AFS file locking whereby the use of the big kernel lock (which could be slept with) was replaced by a spinlock (which couldn't). The problem is that the AFS code was doing stuff inside the critical section that might call schedule(), so this is a broken transformation. Fix this by the following means: (1) Use a state machine with a proper state that can only be changed under the spinlock rather than using a collection of bit flags. (2) Cache the key used for the lock and the lock type in the afs_vnode struct so that the manager work function doesn't have to refer to a file_lock struct that's been dequeued. This makes signal handling safer. (4) Move the unlock from afs_do_unlk() to afs_fl_release_private() which means that unlock is achieved in other circumstances too. (5) Unlock the file on the server before taking the next conflicting lock. Also change: (1) Check the permits on a file before actually trying the lock. (2) fsync the file before effecting an explicit unlock operation. We don't fsync if the lock is erased otherwise as we might not be in a context where we can actually do that. Further fixes: (1) Fixed-fileserver address rotation is made to work. It's only used by the locking functions, so couldn't be tested before. Fixes: 72f98e72551f ("locks: turn lock_flocks into a spinlock") Signed-off-by: David Howells <dhowells@redhat.com> cc: jlayton@redhat.com
Diffstat (limited to 'fs/afs/rotate.c')
-rw-r--r--fs/afs/rotate.c70
1 files changed, 56 insertions, 14 deletions
diff --git a/fs/afs/rotate.c b/fs/afs/rotate.c
index e728ca1776c9..d04511fb3879 100644
--- a/fs/afs/rotate.c
+++ b/fs/afs/rotate.c
@@ -46,8 +46,7 @@ bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode
46 return false; 46 return false;
47 } 47 }
48 48
49 if (test_bit(AFS_VNODE_READLOCKED, &vnode->flags) || 49 if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
50 test_bit(AFS_VNODE_WRITELOCKED, &vnode->flags))
51 fc->flags |= AFS_FS_CURSOR_CUR_ONLY; 50 fc->flags |= AFS_FS_CURSOR_CUR_ONLY;
52 return true; 51 return true;
53} 52}
@@ -117,7 +116,7 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code)
117 case VSALVAGING: m = "being salvaged"; break; 116 case VSALVAGING: m = "being salvaged"; break;
118 default: m = "busy"; break; 117 default: m = "busy"; break;
119 } 118 }
120 119
121 pr_notice("kAFS: Volume %u '%s' is %s\n", volume->vid, volume->name, m); 120 pr_notice("kAFS: Volume %u '%s' is %s\n", volume->vid, volume->name, m);
122} 121}
123 122
@@ -438,24 +437,67 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
438 437
439 _enter(""); 438 _enter("");
440 439
441 if (!cbi) { 440 switch (fc->ac.error) {
442 fc->ac.error = -ESTALE; 441 case SHRT_MAX:
442 if (!cbi) {
443 fc->ac.error = -ESTALE;
444 fc->flags |= AFS_FS_CURSOR_STOP;
445 return false;
446 }
447
448 fc->cbi = afs_get_cb_interest(vnode->cb_interest);
449
450 read_lock(&cbi->server->fs_lock);
451 alist = rcu_dereference_protected(cbi->server->addresses,
452 lockdep_is_held(&cbi->server->fs_lock));
453 afs_get_addrlist(alist);
454 read_unlock(&cbi->server->fs_lock);
455 if (!alist) {
456 fc->ac.error = -ESTALE;
457 fc->flags |= AFS_FS_CURSOR_STOP;
458 return false;
459 }
460
461 fc->ac.alist = alist;
462 fc->ac.addr = NULL;
463 fc->ac.start = READ_ONCE(alist->index);
464 fc->ac.index = fc->ac.start;
465 fc->ac.error = 0;
466 fc->ac.begun = false;
467 goto iterate_address;
468
469 case 0:
470 default:
471 /* Success or local failure. Stop. */
443 fc->flags |= AFS_FS_CURSOR_STOP; 472 fc->flags |= AFS_FS_CURSOR_STOP;
473 _leave(" = f [okay/local %d]", fc->ac.error);
444 return false; 474 return false;
445 }
446 475
447 read_lock(&cbi->server->fs_lock); 476 case -ECONNABORTED:
448 alist = afs_get_addrlist(cbi->server->addresses);
449 read_unlock(&cbi->server->fs_lock);
450 if (!alist) {
451 fc->ac.error = -ESTALE;
452 fc->flags |= AFS_FS_CURSOR_STOP; 477 fc->flags |= AFS_FS_CURSOR_STOP;
478 _leave(" = f [abort]");
453 return false; 479 return false;
480
481 case -ENETUNREACH:
482 case -EHOSTUNREACH:
483 case -ECONNREFUSED:
484 case -ETIMEDOUT:
485 case -ETIME:
486 _debug("no conn");
487 goto iterate_address;
454 } 488 }
455 489
456 fc->ac.alist = alist; 490iterate_address:
457 fc->ac.error = 0; 491 /* Iterate over the current server's address list to try and find an
458 return true; 492 * address on which it will respond to us.
493 */
494 if (afs_iterate_addresses(&fc->ac)) {
495 _leave(" = t");
496 return true;
497 }
498
499 afs_end_cursor(&fc->ac);
500 return false;
459} 501}
460 502
461/* 503/*