aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2008-04-01 20:26:22 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2008-04-19 16:53:49 -0400
commit5f50c0c6d644d6c8180d9079c13c5d9de3adeb34 (patch)
tree8cc145c4c3fafc1ea23e0e20929238e6318a44a5
parent6b4b3a752b3464f2fd9fe2837fb19270c23c1d6b (diff)
NLM/lockd: Fix a race when cancelling a blocking lock
We shouldn't remove the lock from the list of blocked locks until the CANCEL call has completed since we may be racing with a GRANTED callback. Also ensure that we send an UNLOCK if the CANCEL request failed. Normally that should only happen if the process gets hit with a fatal signal. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/lockd/clntproc.c43
1 files changed, 34 insertions, 9 deletions
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index ea1a6940af22..37d1aa20a607 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -510,6 +510,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
510 struct nlm_res *resp = &req->a_res; 510 struct nlm_res *resp = &req->a_res;
511 struct nlm_wait *block = NULL; 511 struct nlm_wait *block = NULL;
512 unsigned char fl_flags = fl->fl_flags; 512 unsigned char fl_flags = fl->fl_flags;
513 unsigned char fl_type;
513 int status = -ENOLCK; 514 int status = -ENOLCK;
514 515
515 if (nsm_monitor(host) < 0) { 516 if (nsm_monitor(host) < 0) {
@@ -525,13 +526,16 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
525 526
526 block = nlmclnt_prepare_block(host, fl); 527 block = nlmclnt_prepare_block(host, fl);
527again: 528again:
529 /*
530 * Initialise resp->status to a valid non-zero value,
531 * since 0 == nlm_lck_granted
532 */
533 resp->status = nlm_lck_blocked;
528 for(;;) { 534 for(;;) {
529 /* Reboot protection */ 535 /* Reboot protection */
530 fl->fl_u.nfs_fl.state = host->h_state; 536 fl->fl_u.nfs_fl.state = host->h_state;
531 status = nlmclnt_call(req, NLMPROC_LOCK); 537 status = nlmclnt_call(req, NLMPROC_LOCK);
532 if (status < 0) 538 if (status < 0)
533 goto out_unblock;
534 if (!req->a_args.block)
535 break; 539 break;
536 /* Did a reclaimer thread notify us of a server reboot? */ 540 /* Did a reclaimer thread notify us of a server reboot? */
537 if (resp->status == nlm_lck_denied_grace_period) 541 if (resp->status == nlm_lck_denied_grace_period)
@@ -540,15 +544,22 @@ again:
540 break; 544 break;
541 /* Wait on an NLM blocking lock */ 545 /* Wait on an NLM blocking lock */
542 status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT); 546 status = nlmclnt_block(block, req, NLMCLNT_POLL_TIMEOUT);
543 /* if we were interrupted. Send a CANCEL request to the server
544 * and exit
545 */
546 if (status < 0) 547 if (status < 0)
547 goto out_unblock; 548 break;
548 if (resp->status != nlm_lck_blocked) 549 if (resp->status != nlm_lck_blocked)
549 break; 550 break;
550 } 551 }
551 552
553 /* if we were interrupted while blocking, then cancel the lock request
554 * and exit
555 */
556 if (resp->status == nlm_lck_blocked) {
557 if (!req->a_args.block)
558 goto out_unlock;
559 if (nlmclnt_cancel(host, req->a_args.block, fl) == 0)
560 goto out_unblock;
561 }
562
552 if (resp->status == nlm_granted) { 563 if (resp->status == nlm_granted) {
553 down_read(&host->h_rwsem); 564 down_read(&host->h_rwsem);
554 /* Check whether or not the server has rebooted */ 565 /* Check whether or not the server has rebooted */
@@ -562,16 +573,30 @@ again:
562 printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); 573 printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__);
563 up_read(&host->h_rwsem); 574 up_read(&host->h_rwsem);
564 fl->fl_flags = fl_flags; 575 fl->fl_flags = fl_flags;
576 status = 0;
565 } 577 }
578 if (status < 0)
579 goto out_unlock;
566 status = nlm_stat_to_errno(resp->status); 580 status = nlm_stat_to_errno(resp->status);
567out_unblock: 581out_unblock:
568 nlmclnt_finish_block(block); 582 nlmclnt_finish_block(block);
569 /* Cancel the blocked request if it is still pending */
570 if (resp->status == nlm_lck_blocked)
571 nlmclnt_cancel(host, req->a_args.block, fl);
572out: 583out:
573 nlm_release_call(req); 584 nlm_release_call(req);
574 return status; 585 return status;
586out_unlock:
587 /* Fatal error: ensure that we remove the lock altogether */
588 dprintk("lockd: lock attempt ended in fatal error.\n"
589 " Attempting to unlock.\n");
590 nlmclnt_finish_block(block);
591 fl_type = fl->fl_type;
592 fl->fl_type = F_UNLCK;
593 down_read(&host->h_rwsem);
594 do_vfs_lock(fl);
595 up_read(&host->h_rwsem);
596 fl->fl_type = fl_type;
597 fl->fl_flags = fl_flags;
598 nlmclnt_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops);
599 return status;
575} 600}
576 601
577/* 602/*