Skip to content

Commit

Permalink
afs: Handle lock rpc ops failing on a file that got deleted
Browse files Browse the repository at this point in the history
Holding a file lock on an AFS file does not prevent it from being deleted
on the server, so we need to handle an error resulting from that when we
try setting, extending or releasing a lock.

Fix this by adding a "deleted" lock state and cancelling the lock extension
process for that file and aborting all waiters for the lock.

Fixes: 0fafdc9 ("afs: Fix file locking")
Reported-by: Jonathan Billings <jsbillin@umich.edu>
Signed-off-by: David Howells <dhowells@redhat.com>
  • Loading branch information
David Howells committed Apr 25, 2019
1 parent 445b102 commit cdfb26b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 4 deletions.
62 changes: 59 additions & 3 deletions fs/afs/flock.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,28 @@ static void afs_next_locker(struct afs_vnode *vnode, int error)
_leave("");
}

/*
* Kill off all waiters in the the pending lock queue due to the vnode being
* deleted.
*/
static void afs_kill_lockers_enoent(struct afs_vnode *vnode)
{
struct file_lock *p;

afs_set_lock_state(vnode, AFS_VNODE_LOCK_DELETED);

while (!list_empty(&vnode->pending_locks)) {
p = list_entry(vnode->pending_locks.next,
struct file_lock, fl_u.afs.link);
list_del_init(&p->fl_u.afs.link);
p->fl_u.afs.state = -ENOENT;
wake_up(&p->fl_wait);
}

key_put(vnode->lock_key);
vnode->lock_key = NULL;
}

/*
* Get a lock on a file
*/
Expand Down Expand Up @@ -278,13 +300,19 @@ void afs_lock_work(struct work_struct *work)
/* attempt to release the server lock; if it fails, we just
* wait 5 minutes and it'll expire anyway */
ret = afs_release_lock(vnode, vnode->lock_key);
if (ret < 0)
if (ret < 0) {
trace_afs_flock_ev(vnode, NULL, afs_flock_release_fail,
ret);
printk(KERN_WARNING "AFS:"
" Failed to release lock on {%llx:%llx} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret);
}

spin_lock(&vnode->lock);
afs_next_locker(vnode, 0);
if (ret == -ENOENT)
afs_kill_lockers_enoent(vnode);
else
afs_next_locker(vnode, 0);
spin_unlock(&vnode->lock);
return;

Expand All @@ -304,12 +332,21 @@ void afs_lock_work(struct work_struct *work)
ret = afs_extend_lock(vnode, key); /* RPC */
key_put(key);

if (ret < 0)
if (ret < 0) {
trace_afs_flock_ev(vnode, NULL, afs_flock_extend_fail,
ret);
pr_warning("AFS: Failed to extend lock on {%llx:%llx} error %d\n",
vnode->fid.vid, vnode->fid.vnode, ret);
}

spin_lock(&vnode->lock);

if (ret == -ENOENT) {
afs_kill_lockers_enoent(vnode);
spin_unlock(&vnode->lock);
return;
}

if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING)
goto again;
afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED);
Expand All @@ -333,6 +370,11 @@ void afs_lock_work(struct work_struct *work)
spin_unlock(&vnode->lock);
return;

case AFS_VNODE_LOCK_DELETED:
afs_kill_lockers_enoent(vnode);
spin_unlock(&vnode->lock);
return;

default:
/* Looks like a lock request was withdrawn. */
spin_unlock(&vnode->lock);
Expand Down Expand Up @@ -435,6 +477,10 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
spin_lock(&vnode->lock);
list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);

ret = -ENOENT;
if (vnode->lock_state == AFS_VNODE_LOCK_DELETED)
goto error_unlock;

/* If we've already got a lock on the server then try to move to having
* the VFS grant the requested lock. Note that this means that other
* clients may get starved out.
Expand Down Expand Up @@ -489,6 +535,13 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
afs_next_locker(vnode, ret);
goto error_unlock;

case -ENOENT:
fl->fl_u.afs.state = ret;
trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret);
list_del_init(&fl->fl_u.afs.link);
afs_kill_lockers_enoent(vnode);
goto error_unlock;

default:
fl->fl_u.afs.state = ret;
trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret);
Expand Down Expand Up @@ -638,6 +691,9 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)

_enter("");

if (vnode->lock_state == AFS_VNODE_LOCK_DELETED)
return -ENOENT;

fl->fl_type = F_UNLCK;

/* check local lock records first */
Expand Down
1 change: 1 addition & 0 deletions fs/afs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ enum afs_lock_state {
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_VNODE_LOCK_DELETED, /* The vnode has been deleted whilst we have a lock */
};

/*
Expand Down
7 changes: 6 additions & 1 deletion include/trace/events/afs.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ enum afs_flock_event {
afs_flock_acquired,
afs_flock_callback_break,
afs_flock_defer_unlock,
afs_flock_extend_fail,
afs_flock_fail_other,
afs_flock_fail_perm,
afs_flock_no_lockers,
afs_flock_release_fail,
afs_flock_timestamp,
afs_flock_try_to_lock,
afs_flock_vfs_lock,
Expand Down Expand Up @@ -323,15 +325,18 @@ enum afs_flock_operation {
EM(AFS_VNODE_LOCK_GRANTED, "GRANTED") \
EM(AFS_VNODE_LOCK_EXTENDING, "EXTENDING") \
EM(AFS_VNODE_LOCK_NEED_UNLOCK, "NEED_UNLOCK") \
E_(AFS_VNODE_LOCK_UNLOCKING, "UNLOCKING") \
EM(AFS_VNODE_LOCK_UNLOCKING, "UNLOCKING") \
E_(AFS_VNODE_LOCK_DELETED, "DELETED")

#define afs_flock_events \
EM(afs_flock_acquired, "Acquired") \
EM(afs_flock_callback_break, "Callback") \
EM(afs_flock_defer_unlock, "D-Unlock") \
EM(afs_flock_extend_fail, "Ext_Fail") \
EM(afs_flock_fail_other, "ErrOther") \
EM(afs_flock_fail_perm, "ErrPerm ") \
EM(afs_flock_no_lockers, "NoLocker") \
EM(afs_flock_release_fail, "Rel_Fail") \
EM(afs_flock_timestamp, "Timestmp") \
EM(afs_flock_try_to_lock, "TryToLck") \
EM(afs_flock_vfs_lock, "VFSLock ") \
Expand Down

0 comments on commit cdfb26b

Please sign in to comment.