afs: Handle lock rpc ops failing on a file that got deleted
authorDavid Howells <dhowells@redhat.com>
Thu, 25 Apr 2019 13:26:51 +0000 (14:26 +0100)
committerDavid Howells <dhowells@redhat.com>
Thu, 25 Apr 2019 13:26:51 +0000 (14:26 +0100)
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: 0fafdc9f888b ("afs: Fix file locking")
Reported-by: Jonathan Billings <jsbillin@umich.edu>
Signed-off-by: David Howells <dhowells@redhat.com>
fs/afs/flock.c
fs/afs/internal.h
include/trace/events/afs.h

index 3e06a560f66b7e28800b0feac0cf0d07fdb33423..742038a21ef7e099341a06c6a96000cb4331c1cc 100644 (file)
@@ -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
  */
@@ -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 */
index 6e680783f59fb8b1ab5f2743e964d9e9ebf28ba3..5eb6be3f73b2798d135beb9e17d2612b7f04bc7a 100644 (file)
@@ -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 */
 };
 
 /*
index 24c058a93e8fbeffcd848f2c0b3c762cc1a5d6ac..21b896fabb2f3aa11ce97e6a50792c32c3aca75b 100644 (file)
@@ -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 ")             \