diff options
-rw-r--r-- | fs/afs/flock.c | 62 | ||||
-rw-r--r-- | fs/afs/internal.h | 1 | ||||
-rw-r--r-- | include/trace/events/afs.h | 7 |
3 files changed, 66 insertions, 4 deletions
diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 3e06a560f66b..742038a21ef7 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c @@ -158,6 +158,28 @@ static void afs_next_locker(struct afs_vnode *vnode, int error) } /* + * 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 */ static int afs_set_lock(struct afs_vnode *vnode, struct key *key, @@ -278,13 +300,19 @@ again: /* 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; @@ -304,12 +332,21 @@ again: 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); @@ -333,6 +370,11 @@ again: 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); @@ -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. @@ -489,6 +535,13 @@ try_to_lock: 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); @@ -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 */ diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 6e680783f59f..5eb6be3f73b2 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -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 */ }; /* diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index 24c058a93e8f..21b896fabb2f 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -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, @@ -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 ") \ |