diff options
author | David Howells | 2017-11-13 16:59:50 +0000 |
---|---|---|
committer | David Howells | 2017-11-17 10:06:13 +0000 |
commit | 0fafdc9f888b42499001b7ca9d9f371c0b2932f4 (patch) | |
tree | 188f5a33b0a043704c0a080519581ce1cf4f4083 /fs/afs/server_list.c | |
parent | cf9b0772f2e410645fece13b749bd56505b998b8 (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/server_list.c')
-rw-r--r-- | fs/afs/server_list.c | 2 |
1 files changed, 1 insertions, 1 deletions
diff --git a/fs/afs/server_list.c b/fs/afs/server_list.c index 26bad7032bba..0ab3f8457839 100644 --- a/fs/afs/server_list.c +++ b/fs/afs/server_list.c @@ -17,7 +17,7 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist) { int i; - if (refcount_dec_and_test(&slist->usage)) { + if (slist && refcount_dec_and_test(&slist->usage)) { for (i = 0; i < slist->nr_servers; i++) { afs_put_cb_interest(net, slist->servers[i].cb_interest); afs_put_server(net, slist->servers[i].server); |