summaryrefslogtreecommitdiffstats
path: root/fs/afs/rotate.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-11-13 16:59:50 +0000
committerDavid Howells <dhowells@redhat.com>2017-11-17 10:06:13 +0000
commit0fafdc9f888b42499001b7ca9d9f371c0b2932f4 (patch)
tree188f5a33b0a043704c0a080519581ce1cf4f4083 /fs/afs/rotate.c
parentcf9b0772f2e410645fece13b749bd56505b998b8 (diff)
downloadlinux-0fafdc9f888b42499001b7ca9d9f371c0b2932f4.tar.gz
linux-0fafdc9f888b42499001b7ca9d9f371c0b2932f4.tar.bz2
linux-0fafdc9f888b42499001b7ca9d9f371c0b2932f4.zip
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
return false;
}
- if (test_bit(AFS_VNODE_READLOCKED, &vnode->flags) ||
- test_bit(AFS_VNODE_WRITELOCKED, &vnode->flags))
+ if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
fc->flags |= AFS_FS_CURSOR_CUR_ONLY;
return true;
}
@@ -117,7 +116,7 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code)
case VSALVAGING: m = "being salvaged"; break;
default: m = "busy"; break;
}
-
+
pr_notice("kAFS: Volume %u '%s' is %s\n", volume->vid, volume->name, m);
}
@@ -438,24 +437,67 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
_enter("");
- if (!cbi) {
- fc->ac.error = -ESTALE;
+ switch (fc->ac.error) {
+ case SHRT_MAX:
+ if (!cbi) {
+ fc->ac.error = -ESTALE;
+ fc->flags |= AFS_FS_CURSOR_STOP;
+ return false;
+ }
+
+ fc->cbi = afs_get_cb_interest(vnode->cb_interest);
+
+ read_lock(&cbi->server->fs_lock);
+ alist = rcu_dereference_protected(cbi->server->addresses,
+ lockdep_is_held(&cbi->server->fs_lock));
+ afs_get_addrlist(alist);
+ read_unlock(&cbi->server->fs_lock);
+ if (!alist) {
+ fc->ac.error = -ESTALE;
+ fc->flags |= AFS_FS_CURSOR_STOP;
+ return false;
+ }
+
+ fc->ac.alist = alist;
+ fc->ac.addr = NULL;
+ fc->ac.start = READ_ONCE(alist->index);
+ fc->ac.index = fc->ac.start;
+ fc->ac.error = 0;
+ fc->ac.begun = false;
+ goto iterate_address;
+
+ case 0:
+ default:
+ /* Success or local failure. Stop. */
fc->flags |= AFS_FS_CURSOR_STOP;
+ _leave(" = f [okay/local %d]", fc->ac.error);
return false;
- }
- read_lock(&cbi->server->fs_lock);
- alist = afs_get_addrlist(cbi->server->addresses);
- read_unlock(&cbi->server->fs_lock);
- if (!alist) {
- fc->ac.error = -ESTALE;
+ case -ECONNABORTED:
fc->flags |= AFS_FS_CURSOR_STOP;
+ _leave(" = f [abort]");
return false;
+
+ case -ENETUNREACH:
+ case -EHOSTUNREACH:
+ case -ECONNREFUSED:
+ case -ETIMEDOUT:
+ case -ETIME:
+ _debug("no conn");
+ goto iterate_address;
}
- fc->ac.alist = alist;
- fc->ac.error = 0;
- return true;
+iterate_address:
+ /* Iterate over the current server's address list to try and find an
+ * address on which it will respond to us.
+ */
+ if (afs_iterate_addresses(&fc->ac)) {
+ _leave(" = t");
+ return true;
+ }
+
+ afs_end_cursor(&fc->ac);
+ return false;
}
/*