aboutsummaryrefslogtreecommitdiffstats
path: root/fs/afs
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2019-04-25 09:26:51 -0400
committerDavid Howells <dhowells@redhat.com>2019-04-25 09:26:51 -0400
commitcdfb26b40dfa51127d22d171cef4fe8993cbfb55 (patch)
treef113dc931a8677a31d9aa70e50953a2a08357cd3 /fs/afs
parent445b10289f766b73527ecb4fb4e388677ad93740 (diff)
afs: Handle lock rpc ops failing on a file that got deleted
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>
Diffstat (limited to 'fs/afs')
-rw-r--r--fs/afs/flock.c62
-rw-r--r--fs/afs/internal.h1
2 files changed, 60 insertions, 3 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)
158} 158}
159 159
160/* 160/*
161 * Kill off all waiters in the the pending lock queue due to the vnode being
162 * deleted.
163 */
164static void afs_kill_lockers_enoent(struct afs_vnode *vnode)
165{
166 struct file_lock *p;
167
168 afs_set_lock_state(vnode, AFS_VNODE_LOCK_DELETED);
169
170 while (!list_empty(&vnode->pending_locks)) {
171 p = list_entry(vnode->pending_locks.next,
172 struct file_lock, fl_u.afs.link);
173 list_del_init(&p->fl_u.afs.link);
174 p->fl_u.afs.state = -ENOENT;
175 wake_up(&p->fl_wait);
176 }
177
178 key_put(vnode->lock_key);
179 vnode->lock_key = NULL;
180}
181
182/*
161 * Get a lock on a file 183 * Get a lock on a file
162 */ 184 */
163static int afs_set_lock(struct afs_vnode *vnode, struct key *key, 185static int afs_set_lock(struct afs_vnode *vnode, struct key *key,
@@ -278,13 +300,19 @@ again:
278 /* attempt to release the server lock; if it fails, we just 300 /* attempt to release the server lock; if it fails, we just
279 * wait 5 minutes and it'll expire anyway */ 301 * wait 5 minutes and it'll expire anyway */
280 ret = afs_release_lock(vnode, vnode->lock_key); 302 ret = afs_release_lock(vnode, vnode->lock_key);
281 if (ret < 0) 303 if (ret < 0) {
304 trace_afs_flock_ev(vnode, NULL, afs_flock_release_fail,
305 ret);
282 printk(KERN_WARNING "AFS:" 306 printk(KERN_WARNING "AFS:"
283 " Failed to release lock on {%llx:%llx} error %d\n", 307 " Failed to release lock on {%llx:%llx} error %d\n",
284 vnode->fid.vid, vnode->fid.vnode, ret); 308 vnode->fid.vid, vnode->fid.vnode, ret);
309 }
285 310
286 spin_lock(&vnode->lock); 311 spin_lock(&vnode->lock);
287 afs_next_locker(vnode, 0); 312 if (ret == -ENOENT)
313 afs_kill_lockers_enoent(vnode);
314 else
315 afs_next_locker(vnode, 0);
288 spin_unlock(&vnode->lock); 316 spin_unlock(&vnode->lock);
289 return; 317 return;
290 318
@@ -304,12 +332,21 @@ again:
304 ret = afs_extend_lock(vnode, key); /* RPC */ 332 ret = afs_extend_lock(vnode, key); /* RPC */
305 key_put(key); 333 key_put(key);
306 334
307 if (ret < 0) 335 if (ret < 0) {
336 trace_afs_flock_ev(vnode, NULL, afs_flock_extend_fail,
337 ret);
308 pr_warning("AFS: Failed to extend lock on {%llx:%llx} error %d\n", 338 pr_warning("AFS: Failed to extend lock on {%llx:%llx} error %d\n",
309 vnode->fid.vid, vnode->fid.vnode, ret); 339 vnode->fid.vid, vnode->fid.vnode, ret);
340 }
310 341
311 spin_lock(&vnode->lock); 342 spin_lock(&vnode->lock);
312 343
344 if (ret == -ENOENT) {
345 afs_kill_lockers_enoent(vnode);
346 spin_unlock(&vnode->lock);
347 return;
348 }
349
313 if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING) 350 if (vnode->lock_state != AFS_VNODE_LOCK_EXTENDING)
314 goto again; 351 goto again;
315 afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED); 352 afs_set_lock_state(vnode, AFS_VNODE_LOCK_GRANTED);
@@ -333,6 +370,11 @@ again:
333 spin_unlock(&vnode->lock); 370 spin_unlock(&vnode->lock);
334 return; 371 return;
335 372
373 case AFS_VNODE_LOCK_DELETED:
374 afs_kill_lockers_enoent(vnode);
375 spin_unlock(&vnode->lock);
376 return;
377
336 default: 378 default:
337 /* Looks like a lock request was withdrawn. */ 379 /* Looks like a lock request was withdrawn. */
338 spin_unlock(&vnode->lock); 380 spin_unlock(&vnode->lock);
@@ -435,6 +477,10 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl)
435 spin_lock(&vnode->lock); 477 spin_lock(&vnode->lock);
436 list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks); 478 list_add_tail(&fl->fl_u.afs.link, &vnode->pending_locks);
437 479
480 ret = -ENOENT;
481 if (vnode->lock_state == AFS_VNODE_LOCK_DELETED)
482 goto error_unlock;
483
438 /* If we've already got a lock on the server then try to move to having 484 /* If we've already got a lock on the server then try to move to having
439 * the VFS grant the requested lock. Note that this means that other 485 * the VFS grant the requested lock. Note that this means that other
440 * clients may get starved out. 486 * clients may get starved out.
@@ -489,6 +535,13 @@ try_to_lock:
489 afs_next_locker(vnode, ret); 535 afs_next_locker(vnode, ret);
490 goto error_unlock; 536 goto error_unlock;
491 537
538 case -ENOENT:
539 fl->fl_u.afs.state = ret;
540 trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret);
541 list_del_init(&fl->fl_u.afs.link);
542 afs_kill_lockers_enoent(vnode);
543 goto error_unlock;
544
492 default: 545 default:
493 fl->fl_u.afs.state = ret; 546 fl->fl_u.afs.state = ret;
494 trace_afs_flock_ev(vnode, fl, afs_flock_fail_other, ret); 547 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)
638 691
639 _enter(""); 692 _enter("");
640 693
694 if (vnode->lock_state == AFS_VNODE_LOCK_DELETED)
695 return -ENOENT;
696
641 fl->fl_type = F_UNLCK; 697 fl->fl_type = F_UNLCK;
642 698
643 /* check local lock records first */ 699 /* 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 {
599 AFS_VNODE_LOCK_EXTENDING, /* We're extending a lock on the server */ 599 AFS_VNODE_LOCK_EXTENDING, /* We're extending a lock on the server */
600 AFS_VNODE_LOCK_NEED_UNLOCK, /* We need to unlock on the server */ 600 AFS_VNODE_LOCK_NEED_UNLOCK, /* We need to unlock on the server */
601 AFS_VNODE_LOCK_UNLOCKING, /* We're telling the server to unlock */ 601 AFS_VNODE_LOCK_UNLOCKING, /* We're telling the server to unlock */
602 AFS_VNODE_LOCK_DELETED, /* The vnode has been deleted whilst we have a lock */
602}; 603};
603 604
604/* 605/*