Commit 0fafdc9f authored by David Howells's avatar David Howells

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: 72f98e72 ("locks: turn lock_flocks into a spinlock")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
cc: jlayton@redhat.com
parent cf9b0772
...@@ -170,7 +170,7 @@ void afs_lock_work(struct work_struct *work) ...@@ -170,7 +170,7 @@ void afs_lock_work(struct work_struct *work)
{ {
struct afs_vnode *vnode = struct afs_vnode *vnode =
container_of(work, struct afs_vnode, lock_work.work); container_of(work, struct afs_vnode, lock_work.work);
struct file_lock *fl; struct file_lock *fl, *next;
afs_lock_type_t type; afs_lock_type_t type;
struct key *key; struct key *key;
int ret; int ret;
...@@ -179,117 +179,136 @@ void afs_lock_work(struct work_struct *work) ...@@ -179,117 +179,136 @@ void afs_lock_work(struct work_struct *work)
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
if (test_bit(AFS_VNODE_UNLOCKING, &vnode->flags)) { again:
_debug("wstate %u for %p", vnode->lock_state, vnode);
switch (vnode->lock_state) {
case AFS_VNODE_LOCK_NEED_UNLOCK:
_debug("unlock"); _debug("unlock");
vnode->lock_state = AFS_VNODE_LOCK_UNLOCKING;
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
/* attempt to release the server lock; if it fails, we just /* attempt to release the server lock; if it fails, we just
* wait 5 minutes and it'll time out anyway */ * wait 5 minutes and it'll expire anyway */
ret = afs_release_lock(vnode, vnode->unlock_key); ret = afs_release_lock(vnode, vnode->lock_key);
if (ret < 0) if (ret < 0)
printk(KERN_WARNING "AFS:" printk(KERN_WARNING "AFS:"
" Failed to release lock on {%x:%x} error %d\n", " Failed to release lock on {%x:%x} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret); vnode->fid.vid, vnode->fid.vnode, ret);
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
key_put(vnode->unlock_key); key_put(vnode->lock_key);
vnode->unlock_key = NULL; vnode->lock_key = NULL;
clear_bit(AFS_VNODE_UNLOCKING, &vnode->flags); vnode->lock_state = AFS_VNODE_LOCK_NONE;
}
if (list_empty(&vnode->pending_locks)) {
spin_unlock(&vnode->lock);
return;
}
/* The new front of the queue now owns the state variables. */
next = list_entry(vnode->pending_locks.next,
struct file_lock, fl_u.afs.link);
vnode->lock_key = afs_file_key(next->fl_file);
vnode->lock_type = (next->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
goto again;
/* if we've got a lock, then it must be time to extend that lock as AFS /* If we've already got a lock, then it must be time to extend that
* locks time out after 5 minutes */ * lock as AFS locks time out after 5 minutes.
if (!list_empty(&vnode->granted_locks)) { */
case AFS_VNODE_LOCK_GRANTED:
_debug("extend"); _debug("extend");
if (test_and_set_bit(AFS_VNODE_LOCKING, &vnode->flags)) ASSERT(!list_empty(&vnode->granted_locks));
BUG();
fl = list_entry(vnode->granted_locks.next, key = key_get(vnode->lock_key);
struct file_lock, fl_u.afs.link); vnode->lock_state = AFS_VNODE_LOCK_EXTENDING;
key = key_get(afs_file_key(fl->fl_file));
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
ret = afs_extend_lock(vnode, key); ret = afs_extend_lock(vnode, key); /* RPC */
clear_bit(AFS_VNODE_LOCKING, &vnode->flags);
key_put(key); key_put(key);
switch (ret) {
case 0: if (ret < 0)
pr_warning("AFS: Failed to extend lock on {%x:%x} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret);
spin_lock(&vnode->lock);
if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING)
goto again;
vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
if (ret == 0)
afs_schedule_lock_extension(vnode); afs_schedule_lock_extension(vnode);
break; else
default:
/* ummm... we failed to extend the lock - retry
* extension shortly */
printk(KERN_WARNING "AFS:"
" Failed to extend lock on {%x:%x} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret);
queue_delayed_work(afs_lock_manager, &vnode->lock_work, queue_delayed_work(afs_lock_manager, &vnode->lock_work,
HZ * 10); HZ * 10);
break; spin_unlock(&vnode->lock);
} _leave(" [ext]");
_leave(" [extend]");
return; return;
}
/* if we don't have a granted lock, then we must've been called back by /* If we don't have a granted lock, then we must've been called
* the server, and so if might be possible to get a lock we're * back by the server, and so if might be possible to get a
* currently waiting for */ * lock we're currently waiting for.
if (!list_empty(&vnode->pending_locks)) { */
case AFS_VNODE_LOCK_WAITING_FOR_CB:
_debug("get"); _debug("get");
if (test_and_set_bit(AFS_VNODE_LOCKING, &vnode->flags)) key = key_get(vnode->lock_key);
BUG(); type = vnode->lock_type;
fl = list_entry(vnode->pending_locks.next, vnode->lock_state = AFS_VNODE_LOCK_SETTING;
struct file_lock, fl_u.afs.link);
key = key_get(afs_file_key(fl->fl_file));
type = (fl->fl_type == F_RDLCK) ?
AFS_LOCK_READ : AFS_LOCK_WRITE;
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
ret = afs_set_lock(vnode, key, type); ret = afs_set_lock(vnode, key, type); /* RPC */
clear_bit(AFS_VNODE_LOCKING, &vnode->flags); key_put(key);
spin_lock(&vnode->lock);
switch (ret) { switch (ret) {
case -EWOULDBLOCK: case -EWOULDBLOCK:
_debug("blocked"); _debug("blocked");
break; break;
case 0: case 0:
_debug("acquired"); _debug("acquired");
if (type == AFS_LOCK_READ) vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
set_bit(AFS_VNODE_READLOCKED, &vnode->flags); /* Fall through */
else
set_bit(AFS_VNODE_WRITELOCKED, &vnode->flags);
ret = AFS_LOCK_GRANTED;
default: default:
spin_lock(&vnode->lock); /* Pass the lock or the error onto the first locker in
/* the pending lock may have been withdrawn due to a * the list - if they're looking for this type of lock.
* signal */ * If they're not, we assume that whoever asked for it
if (list_entry(vnode->pending_locks.next, * took a signal.
struct file_lock, fl_u.afs.link) == fl) { */
fl->fl_u.afs.state = ret; if (list_empty(&vnode->pending_locks)) {
if (ret == AFS_LOCK_GRANTED)
afs_grant_locks(vnode, fl);
else
list_del_init(&fl->fl_u.afs.link);
wake_up(&fl->fl_wait);
spin_unlock(&vnode->lock);
} else {
_debug("withdrawn"); _debug("withdrawn");
clear_bit(AFS_VNODE_READLOCKED, &vnode->flags); vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags); goto again;
spin_unlock(&vnode->lock);
afs_release_lock(vnode, key);
if (!list_empty(&vnode->pending_locks))
afs_lock_may_be_available(vnode);
} }
break;
fl = list_entry(vnode->pending_locks.next,
struct file_lock, fl_u.afs.link);
type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
if (vnode->lock_type != type) {
_debug("changed");
vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
goto again;
}
fl->fl_u.afs.state = ret;
if (ret == 0)
afs_grant_locks(vnode, fl);
else
list_del_init(&fl->fl_u.afs.link);
wake_up(&fl->fl_wait);
spin_unlock(&vnode->lock);
_leave(" [granted]");
return;
} }
key_put(key);
_leave(" [pend]"); default:
/* Looks like a lock request was withdrawn. */
spin_unlock(&vnode->lock);
_leave(" [no]");
return; return;
} }
/* looks like the lock request was withdrawn on a signal */
spin_unlock(&vnode->lock);
_leave(" [no locks]");
} }
/* /*
...@@ -298,15 +317,105 @@ void afs_lock_work(struct work_struct *work) ...@@ -298,15 +317,105 @@ void afs_lock_work(struct work_struct *work)
* AF_RXRPC * AF_RXRPC
* - the caller must hold the vnode lock * - the caller must hold the vnode lock
*/ */
static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key) static void afs_defer_unlock(struct afs_vnode *vnode)
{ {
cancel_delayed_work(&vnode->lock_work); _enter("");
if (!test_and_clear_bit(AFS_VNODE_READLOCKED, &vnode->flags) &&
!test_and_clear_bit(AFS_VNODE_WRITELOCKED, &vnode->flags)) if (vnode->lock_state == AFS_VNODE_LOCK_GRANTED ||
BUG(); vnode->lock_state == AFS_VNODE_LOCK_EXTENDING) {
if (test_and_set_bit(AFS_VNODE_UNLOCKING, &vnode->flags)) cancel_delayed_work(&vnode->lock_work);
BUG();
vnode->unlock_key = key_get(key); vnode->lock_state = AFS_VNODE_LOCK_NEED_UNLOCK;
afs_lock_may_be_available(vnode);
}
}
/*
* Check that our view of the file metadata is up to date and check to see
* whether we think that we have a locking permit.
*/
static int afs_do_setlk_check(struct afs_vnode *vnode, struct key *key,
afs_lock_type_t type, bool can_sleep)
{
afs_access_t access;
int ret;
/* Make sure we've got a callback on this file and that our view of the
* data version is up to date.
*/
ret = afs_validate(vnode, key);
if (ret < 0)
return ret;
/* Check the permission set to see if we're actually going to be
* allowed to get a lock on this file.
*/
ret = afs_check_permit(vnode, key, &access);
if (ret < 0)
return ret;
/* At a rough estimation, you need LOCK, WRITE or INSERT perm to
* read-lock a file and WRITE or INSERT perm to write-lock a file.
*
* We can't rely on the server to do this for us since if we want to
* share a read lock that we already have, we won't go the server.
*/
if (type == AFS_LOCK_READ) {
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE | AFS_ACE_LOCK)))
return -EACCES;
if (vnode->status.lock_count == -1 && !can_sleep)
return -EAGAIN; /* Write locked */
} else {
if (!(access & (AFS_ACE_INSERT | AFS_ACE_WRITE)))
return -EACCES;
if (vnode->status.lock_count != 0 && !can_sleep)
return -EAGAIN; /* Locked */
}
return 0;
}
/*
* Remove the front runner from the pending queue.
* - The caller must hold vnode->lock.
*/
static void afs_dequeue_lock(struct afs_vnode *vnode, struct file_lock *fl)
{
struct file_lock *next;
_enter("");
/* ->lock_type, ->lock_key and ->lock_state only belong to this
* file_lock if we're at the front of the pending queue or if we have
* the lock granted or if the lock_state is NEED_UNLOCK or UNLOCKING.
*/
if (vnode->granted_locks.next == &fl->fl_u.afs.link &&
vnode->granted_locks.prev == &fl->fl_u.afs.link) {
list_del_init(&fl->fl_u.afs.link);
afs_defer_unlock(vnode);
return;
}
if (!list_empty(&vnode->granted_locks) ||
vnode->pending_locks.next != &fl->fl_u.afs.link) {
list_del_init(&fl->fl_u.afs.link);
return;
}
list_del_init(&fl->fl_u.afs.link);
key_put(vnode->lock_key);
vnode->lock_key = NULL;
vnode->lock_state = AFS_VNODE_LOCK_NONE;
if (list_empty(&vnode->pending_locks))
return;
/* The new front of the queue now owns the state variables. */
next = list_entry(vnode->pending_locks.next,
struct file_lock, fl_u.afs.link);
vnode->lock_key = afs_file_key(next->fl_file);
vnode->lock_type = (next->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
afs_lock_may_be_available(vnode); afs_lock_may_be_available(vnode);
} }
...@@ -315,7 +424,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key) ...@@ -315,7 +424,7 @@ static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key)
*/ */
static int afs_do_setlk(struct file *file, struct file_lock *fl) static int afs_do_setlk(struct file *file, struct file_lock *fl)
{ {
struct inode *inode = file_inode(file); struct inode *inode = locks_inode(file);
struct afs_vnode *vnode = AFS_FS_I(inode); struct afs_vnode *vnode = AFS_FS_I(inode);
afs_lock_type_t type; afs_lock_type_t type;
struct key *key = afs_file_key(file); struct key *key = afs_file_key(file);
...@@ -333,165 +442,136 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) ...@@ -333,165 +442,136 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE;
spin_lock(&inode->i_lock); ret = afs_do_setlk_check(vnode, key, type, fl->fl_flags & FL_SLEEP);
/* make sure we've got a callback on this file and that our view of the
* data version is up to date */
ret = afs_validate(vnode, key);
if (ret < 0) if (ret < 0)
goto error; return ret;
if (vnode->status.lock_count != 0 && !(fl->fl_flags & FL_SLEEP)) {
ret = -EAGAIN;
goto error;
}
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
/* if we've already got a readlock on the server then we can instantly /* If we've already got a readlock on the server then we instantly
* grant another readlock, irrespective of whether there are any * grant another readlock, irrespective of whether there are any
* pending writelocks */ * pending writelocks.
*/
if (type == AFS_LOCK_READ && if (type == AFS_LOCK_READ &&
vnode->flags & (1 << AFS_VNODE_READLOCKED)) { vnode->lock_state == AFS_VNODE_LOCK_GRANTED &&
vnode->lock_type == AFS_LOCK_READ) {
_debug("instant readlock"); _debug("instant readlock");
ASSERTCMP(vnode->flags &
((1 << AFS_VNODE_LOCKING) |
(1 << AFS_VNODE_WRITELOCKED)), ==, 0);
ASSERT(!list_empty(&vnode->granted_locks)); ASSERT(!list_empty(&vnode->granted_locks));
goto sharing_existing_lock; goto share_existing_lock;
} }
/* if there's no-one else with a lock on this vnode, then we need to list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);
* ask the server for a lock */
if (list_empty(&vnode->pending_locks) &&
list_empty(&vnode->granted_locks)) {
_debug("not locked");
ASSERTCMP(vnode->flags &
((1 << AFS_VNODE_LOCKING) |
(1 << AFS_VNODE_READLOCKED) |
(1 << AFS_VNODE_WRITELOCKED)), ==, 0);
list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);
set_bit(AFS_VNODE_LOCKING, &vnode->flags);
spin_unlock(&vnode->lock);
ret = afs_set_lock(vnode, key, type); if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
clear_bit(AFS_VNODE_LOCKING, &vnode->flags); goto need_to_wait;
switch (ret) {
case 0:
_debug("acquired");
goto acquired_server_lock;
case -EWOULDBLOCK:
_debug("would block");
spin_lock(&vnode->lock);
ASSERT(list_empty(&vnode->granted_locks));
ASSERTCMP(vnode->pending_locks.next, ==,
&fl->fl_u.afs.link);
goto wait;
default:
spin_lock(&vnode->lock);
list_del_init(&fl->fl_u.afs.link);
spin_unlock(&vnode->lock);
goto error;
}
}
/* otherwise, we need to wait for a local lock to become available */ /* We don't have a lock on this vnode and we aren't currently waiting
_debug("wait local"); * for one either, so ask the server for a lock.
list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); *
wait: * Note that we need to be careful if we get interrupted by a signal
if (!(fl->fl_flags & FL_SLEEP)) { * after dispatching the request as we may still get the lock, even
_debug("noblock"); * though we don't wait for the reply (it's not too bad a problem - the
ret = -EAGAIN; * lock will expire in 10 mins anyway).
goto abort_attempt; */
} _debug("not locked");
vnode->lock_key = key_get(key);
vnode->lock_type = type;
vnode->lock_state = AFS_VNODE_LOCK_SETTING;
spin_unlock(&vnode->lock); spin_unlock(&vnode->lock);
/* now we need to sleep and wait for the lock manager thread to get the ret = afs_set_lock(vnode, key, type); /* RPC */
* lock from the server */
_debug("sleep");
ret = wait_event_interruptible(fl->fl_wait,
fl->fl_u.afs.state <= AFS_LOCK_GRANTED);
if (fl->fl_u.afs.state <= AFS_LOCK_GRANTED) {
ret = fl->fl_u.afs.state;
if (ret < 0)
goto error;
spin_lock(&vnode->lock);
goto given_lock;
}
/* we were interrupted, but someone may still be in the throes of
* giving us the lock */
_debug("intr");
ASSERTCMP(ret, ==, -ERESTARTSYS);
spin_lock(&vnode->lock); spin_lock(&vnode->lock);
if (fl->fl_u.afs.state <= AFS_LOCK_GRANTED) { switch (ret) {
ret = fl->fl_u.afs.state; default:
if (ret < 0) { goto abort_attempt;
spin_unlock(&vnode->lock);
goto error;
}
goto given_lock;
}
abort_attempt: case -EWOULDBLOCK:
/* we aren't going to get the lock, either because we're unwilling to /* The server doesn't have a lock-waiting queue, so the client
* wait, or because some signal happened */ * will have to retry. The server will break the outstanding
_debug("abort"); * callbacks on a file when a lock is released.
if (list_empty(&vnode->granted_locks) && */
vnode->pending_locks.next == &fl->fl_u.afs.link) { _debug("would block");
if (vnode->pending_locks.prev != &fl->fl_u.afs.link) { ASSERT(list_empty(&vnode->granted_locks));
/* kick the next pending lock into having a go */ ASSERTCMP(vnode->pending_locks.next, ==, &fl->fl_u.afs.link);
list_del_init(&fl->fl_u.afs.link); vnode->lock_state = AFS_VNODE_LOCK_WAITING_FOR_CB;
afs_lock_may_be_available(vnode); goto need_to_wait;
}
} else { case 0:
list_del_init(&fl->fl_u.afs.link); _debug("acquired");
break;
} }
spin_unlock(&vnode->lock);
goto error;
acquired_server_lock:
/* we've acquired a server lock, but it needs to be renewed after 5 /* we've acquired a server lock, but it needs to be renewed after 5
* mins */ * mins */
spin_lock(&vnode->lock); vnode->lock_state = AFS_VNODE_LOCK_GRANTED;
afs_schedule_lock_extension(vnode); afs_schedule_lock_extension(vnode);
if (type == AFS_LOCK_READ)
set_bit(AFS_VNODE_READLOCKED, &vnode->flags); share_existing_lock:
else
set_bit(AFS_VNODE_WRITELOCKED, &vnode->flags);
sharing_existing_lock:
/* the lock has been granted as far as we're concerned... */ /* the lock has been granted as far as we're concerned... */
fl->fl_u.afs.state = AFS_LOCK_GRANTED; fl->fl_u.afs.state = AFS_LOCK_GRANTED;
list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks); list_move_tail(&fl->fl_u.afs.link, &vnode->granted_locks);
given_lock: given_lock:
/* ... but we do still need to get the VFS's blessing */ /* ... but we do still need to get the VFS's blessing */
ASSERT(!(vnode->flags & (1 << AFS_VNODE_LOCKING))); spin_unlock(&vnode->lock);
ASSERT((vnode->flags & ((1 << AFS_VNODE_READLOCKED) |
(1 << AFS_VNODE_WRITELOCKED))) != 0);
ret = posix_lock_file(file, fl, NULL); ret = posix_lock_file(file, fl, NULL);
if (ret < 0) if (ret < 0)
goto vfs_rejected_lock; goto vfs_rejected_lock;
spin_unlock(&vnode->lock);
/* again, make sure we've got a callback on this file and, again, make /* Again, make sure we've got a callback on this file and, again, make
* sure that our view of the data version is up to date (we ignore * sure that our view of the data version is up to date (we ignore
* errors incurred here and deal with the consequences elsewhere) */ * errors incurred here and deal with the consequences elsewhere).
*/
afs_validate(vnode, key); afs_validate(vnode, key);
_leave(" = 0");
return 0;
error: need_to_wait:
spin_unlock(&inode->i_lock); /* We're going to have to wait. Either this client doesn't have a lock
* on the server yet and we need to wait for a callback to occur, or
* the client does have a lock on the server, but it belongs to some
* other process(es) and is incompatible with the lock we want.
*/
ret = -EAGAIN;
if (fl->fl_flags & FL_SLEEP) {
spin_unlock(&vnode->lock);
_debug("sleep");
ret = wait_event_interruptible(fl->fl_wait,
fl->fl_u.afs.state != AFS_LOCK_PENDING);
spin_lock(&vnode->lock);
}
if (fl->fl_u.afs.state == AFS_LOCK_GRANTED)
goto given_lock;
if (fl->fl_u.afs.state < 0)
ret = fl->fl_u.afs.state;
abort_attempt:
/* we aren't going to get the lock, either because we're unwilling to
* wait, or because some signal happened */
_debug("abort");
afs_dequeue_lock(vnode, fl);
error_unlock:
spin_unlock(&vnode->lock);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
vfs_rejected_lock: vfs_rejected_lock:
/* the VFS rejected the lock we just obtained, so we have to discard /* The VFS rejected the lock we just obtained, so we have to discard
* what we just got */ * what we just got. We defer this to the lock manager work item to
* deal with.
*/
_debug("vfs refused %d", ret); _debug("vfs refused %d", ret);
spin_lock(&vnode->lock);
list_del_init(&fl->fl_u.afs.link); list_del_init(&fl->fl_u.afs.link);
if (list_empty(&vnode->granted_locks)) if (list_empty(&vnode->granted_locks))
afs_defer_unlock(vnode, key); afs_defer_unlock(vnode);
goto abort_attempt; goto error_unlock;
} }
/* /*
...@@ -499,34 +579,21 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) ...@@ -499,34 +579,21 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
*/ */
static int afs_do_unlk(struct file *file, struct file_lock *fl) static int afs_do_unlk(struct file *file, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
struct key *key = afs_file_key(file);
int ret; int ret;
_enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type); _enter("{%x:%u},%u", vnode->fid.vid, vnode->fid.vnode, fl->fl_type);
/* Flush all pending writes before doing anything with locks. */
vfs_fsync(file, 0);
/* only whole-file unlocks are supported */ /* only whole-file unlocks are supported */
if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX) if (fl->fl_start != 0 || fl->fl_end != OFFSET_MAX)
return -EINVAL; return -EINVAL;
fl->fl_ops = &afs_lock_ops;
INIT_LIST_HEAD(&fl->fl_u.afs.link);
fl->fl_u.afs.state = AFS_LOCK_PENDING;
spin_lock(&vnode->lock);
ret = posix_lock_file(file, fl, NULL); ret = posix_lock_file(file, fl, NULL);
if (ret < 0) { _leave(" = %d [%u]", ret, vnode->lock_state);
spin_unlock(&vnode->lock); return ret;
_leave(" = %d [vfs]", ret);
return ret;
}
/* discard the server lock only if all granted locks are gone */
if (list_empty(&vnode->granted_locks))
afs_defer_unlock(vnode, key);
spin_unlock(&vnode->lock);
_leave(" = 0");
return 0;
} }
/* /*
...@@ -534,7 +601,7 @@ static int afs_do_unlk(struct file *file, struct file_lock *fl) ...@@ -534,7 +601,7 @@ static int afs_do_unlk(struct file *file, struct file_lock *fl)
*/ */
static int afs_do_getlk(struct file *file, struct file_lock *fl) static int afs_do_getlk(struct file *file, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
struct key *key = afs_file_key(file); struct key *key = afs_file_key(file);
int ret, lock_count; int ret, lock_count;
...@@ -542,29 +609,25 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl) ...@@ -542,29 +609,25 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
fl->fl_type = F_UNLCK; fl->fl_type = F_UNLCK;
inode_lock(&vnode->vfs_inode);
/* check local lock records first */ /* check local lock records first */
ret = 0;
posix_test_lock(file, fl); posix_test_lock(file, fl);
if (fl->fl_type == F_UNLCK) { if (fl->fl_type == F_UNLCK) {
/* no local locks; consult the server */ /* no local locks; consult the server */
ret = afs_fetch_status(vnode, key); ret = afs_fetch_status(vnode, key);
if (ret < 0) if (ret < 0)
goto error; goto error;
lock_count = vnode->status.lock_count;
if (lock_count) { lock_count = READ_ONCE(vnode->status.lock_count);
if (lock_count > 0) if (lock_count > 0)
fl->fl_type = F_RDLCK; fl->fl_type = F_RDLCK;
else else
fl->fl_type = F_WRLCK; fl->fl_type = F_WRLCK;
fl->fl_start = 0; fl->fl_start = 0;
fl->fl_end = OFFSET_MAX; fl->fl_end = OFFSET_MAX;
}
} }
ret = 0;
error: error:
inode_unlock(&vnode->vfs_inode);
_leave(" = %d [%hd]", ret, fl->fl_type); _leave(" = %d [%hd]", ret, fl->fl_type);
return ret; return ret;
} }
...@@ -574,7 +637,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl) ...@@ -574,7 +637,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
*/ */
int afs_lock(struct file *file, int cmd, struct file_lock *fl) int afs_lock(struct file *file, int cmd, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
_enter("{%x:%u},%d,{t=%x,fl=%x,r=%Ld:%Ld}", _enter("{%x:%u},%d,{t=%x,fl=%x,r=%Ld:%Ld}",
vnode->fid.vid, vnode->fid.vnode, cmd, vnode->fid.vid, vnode->fid.vnode, cmd,
...@@ -597,7 +660,7 @@ int afs_lock(struct file *file, int cmd, struct file_lock *fl) ...@@ -597,7 +660,7 @@ int afs_lock(struct file *file, int cmd, struct file_lock *fl)
*/ */
int afs_flock(struct file *file, int cmd, struct file_lock *fl) int afs_flock(struct file *file, int cmd, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(file_inode(file)); struct afs_vnode *vnode = AFS_FS_I(locks_inode(file));
_enter("{%x:%u},%d,{t=%x,fl=%x}", _enter("{%x:%u},%d,{t=%x,fl=%x}",
vnode->fid.vid, vnode->fid.vnode, cmd, vnode->fid.vid, vnode->fid.vnode, cmd,
...@@ -627,9 +690,13 @@ int afs_flock(struct file *file, int cmd, struct file_lock *fl) ...@@ -627,9 +690,13 @@ int afs_flock(struct file *file, int cmd, struct file_lock *fl)
*/ */
static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl) static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(locks_inode(fl->fl_file));
_enter(""); _enter("");
spin_lock(&vnode->lock);
list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link); list_add(&new->fl_u.afs.link, &fl->fl_u.afs.link);
spin_unlock(&vnode->lock);
} }
/* /*
...@@ -638,7 +705,12 @@ static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl) ...@@ -638,7 +705,12 @@ static void afs_fl_copy_lock(struct file_lock *new, struct file_lock *fl)
*/ */
static void afs_fl_release_private(struct file_lock *fl) static void afs_fl_release_private(struct file_lock *fl)
{ {
struct afs_vnode *vnode = AFS_FS_I(locks_inode(fl->fl_file));
_enter(""); _enter("");
list_del_init(&fl->fl_u.afs.link); spin_lock(&vnode->lock);
afs_dequeue_lock(vnode, fl);
_debug("state %u for %p", vnode->lock_state, vnode);
spin_unlock(&vnode->lock);
} }
...@@ -430,6 +430,16 @@ struct afs_volume { ...@@ -430,6 +430,16 @@ struct afs_volume {
u8 name[AFS_MAXVOLNAME + 1]; /* NUL-padded volume name */ u8 name[AFS_MAXVOLNAME + 1]; /* NUL-padded volume name */
}; };
enum afs_lock_state {
AFS_VNODE_LOCK_NONE, /* The vnode has no lock on the server */
AFS_VNODE_LOCK_WAITING_FOR_CB, /* We're waiting for the server to break the callback */
AFS_VNODE_LOCK_SETTING, /* We're asking the server for a lock */
AFS_VNODE_LOCK_GRANTED, /* We have a lock on the server */
AFS_VNODE_LOCK_EXTENDING, /* We're extending a lock on the server */
AFS_VNODE_LOCK_NEED_UNLOCK, /* We need to unlock on the server */
AFS_VNODE_LOCK_UNLOCKING, /* We're telling the server to unlock */
};
/* /*
* AFS inode private data * AFS inode private data
*/ */
...@@ -454,18 +464,16 @@ struct afs_vnode { ...@@ -454,18 +464,16 @@ struct afs_vnode {
#define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */ #define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */
#define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */ #define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */
#define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ #define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */
#define AFS_VNODE_LOCKING 6 /* set if waiting for lock on vnode */ #define AFS_VNODE_AUTOCELL 6 /* set if Vnode is an auto mount point */
#define AFS_VNODE_READLOCKED 7 /* set if vnode is read-locked on the server */ #define AFS_VNODE_PSEUDODIR 7 /* set if Vnode is a pseudo directory */
#define AFS_VNODE_WRITELOCKED 8 /* set if vnode is write-locked on the server */
#define AFS_VNODE_UNLOCKING 9 /* set if vnode is being unlocked on the server */
#define AFS_VNODE_AUTOCELL 10 /* set if Vnode is an auto mount point */
#define AFS_VNODE_PSEUDODIR 11 /* set if Vnode is a pseudo directory */
struct list_head wb_keys; /* List of keys available for writeback */ struct list_head wb_keys; /* List of keys available for writeback */
struct list_head pending_locks; /* locks waiting to be granted */ struct list_head pending_locks; /* locks waiting to be granted */
struct list_head granted_locks; /* locks granted on this file */ struct list_head granted_locks; /* locks granted on this file */
struct delayed_work lock_work; /* work to be done in locking */ struct delayed_work lock_work; /* work to be done in locking */
struct key *unlock_key; /* key to be used in unlocking */ struct key *lock_key; /* Key to be used in lock ops */
enum afs_lock_state lock_state : 8;
afs_lock_type_t lock_type : 8;
/* outstanding callback notification on this file */ /* outstanding callback notification on this file */
struct afs_cb_interest *cb_interest; /* Server on which this resides */ struct afs_cb_interest *cb_interest; /* Server on which this resides */
...@@ -843,6 +851,7 @@ extern void afs_clear_permits(struct afs_vnode *); ...@@ -843,6 +851,7 @@ extern void afs_clear_permits(struct afs_vnode *);
extern void afs_cache_permit(struct afs_vnode *, struct key *, unsigned int); extern void afs_cache_permit(struct afs_vnode *, struct key *, unsigned int);
extern void afs_zap_permits(struct rcu_head *); extern void afs_zap_permits(struct rcu_head *);
extern struct key *afs_request_key(struct afs_cell *); extern struct key *afs_request_key(struct afs_cell *);
extern int afs_check_permit(struct afs_vnode *, struct key *, afs_access_t *);
extern int afs_permission(struct inode *, int); extern int afs_permission(struct inode *, int);
extern void __exit afs_clean_up_permit_cache(void); extern void __exit afs_clean_up_permit_cache(void);
......
...@@ -46,8 +46,7 @@ bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode ...@@ -46,8 +46,7 @@ bool afs_begin_vnode_operation(struct afs_fs_cursor *fc, struct afs_vnode *vnode
return false; return false;
} }
if (test_bit(AFS_VNODE_READLOCKED, &vnode->flags) || if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
test_bit(AFS_VNODE_WRITELOCKED, &vnode->flags))
fc->flags |= AFS_FS_CURSOR_CUR_ONLY; fc->flags |= AFS_FS_CURSOR_CUR_ONLY;
return true; return true;
} }
...@@ -117,7 +116,7 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code) ...@@ -117,7 +116,7 @@ static void afs_busy(struct afs_volume *volume, u32 abort_code)
case VSALVAGING: m = "being salvaged"; break; case VSALVAGING: m = "being salvaged"; break;
default: m = "busy"; break; default: m = "busy"; break;
} }
pr_notice("kAFS: Volume %u '%s' is %s\n", volume->vid, volume->name, m); 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) ...@@ -438,24 +437,67 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
_enter(""); _enter("");
if (!cbi) { switch (fc->ac.error) {
fc->ac.error = -ESTALE; 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; fc->flags |= AFS_FS_CURSOR_STOP;
_leave(" = f [okay/local %d]", fc->ac.error);
return false; return false;
}
read_lock(&cbi->server->fs_lock); case -ECONNABORTED:
alist = afs_get_addrlist(cbi->server->addresses);
read_unlock(&cbi->server->fs_lock);
if (!alist) {
fc->ac.error = -ESTALE;
fc->flags |= AFS_FS_CURSOR_STOP; fc->flags |= AFS_FS_CURSOR_STOP;
_leave(" = f [abort]");
return false; return false;
case -ENETUNREACH:
case -EHOSTUNREACH:
case -ECONNREFUSED:
case -ETIMEDOUT:
case -ETIME:
_debug("no conn");
goto iterate_address;
} }
fc->ac.alist = alist; iterate_address:
fc->ac.error = 0; /* Iterate over the current server's address list to try and find an
return true; * 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;
} }
/* /*
......
...@@ -284,8 +284,8 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -284,8 +284,8 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
* permitted to be accessed with this authorisation, and if so, what access it * permitted to be accessed with this authorisation, and if so, what access it
* is granted * is granted
*/ */
static int afs_check_permit(struct afs_vnode *vnode, struct key *key, int afs_check_permit(struct afs_vnode *vnode, struct key *key,
afs_access_t *_access) afs_access_t *_access)
{ {
struct afs_permits *permits; struct afs_permits *permits;
bool valid = false; bool valid = false;
......
...@@ -17,7 +17,7 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist) ...@@ -17,7 +17,7 @@ void afs_put_serverlist(struct afs_net *net, struct afs_server_list *slist)
{ {
int i; 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++) { for (i = 0; i < slist->nr_servers; i++) {
afs_put_cb_interest(net, slist->servers[i].cb_interest); afs_put_cb_interest(net, slist->servers[i].cb_interest);
afs_put_server(net, slist->servers[i].server); afs_put_server(net, slist->servers[i].server);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment