diff options
-rw-r--r-- | fs/lockd/clntproc.c | 26 | ||||
-rw-r--r-- | fs/locks.c | 23 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 74 | ||||
-rw-r--r-- | include/linux/fs.h | 1 |
4 files changed, 80 insertions, 44 deletions
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 5980c45998cc..89ba0df14c22 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c | |||
@@ -454,7 +454,7 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho | |||
454 | fl->fl_ops = &nlmclnt_lock_ops; | 454 | fl->fl_ops = &nlmclnt_lock_ops; |
455 | } | 455 | } |
456 | 456 | ||
457 | static void do_vfs_lock(struct file_lock *fl) | 457 | static int do_vfs_lock(struct file_lock *fl) |
458 | { | 458 | { |
459 | int res = 0; | 459 | int res = 0; |
460 | switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { | 460 | switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { |
@@ -467,9 +467,7 @@ static void do_vfs_lock(struct file_lock *fl) | |||
467 | default: | 467 | default: |
468 | BUG(); | 468 | BUG(); |
469 | } | 469 | } |
470 | if (res < 0) | 470 | return res; |
471 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", | ||
472 | __FUNCTION__); | ||
473 | } | 471 | } |
474 | 472 | ||
475 | /* | 473 | /* |
@@ -498,6 +496,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) | |||
498 | struct nlm_host *host = req->a_host; | 496 | struct nlm_host *host = req->a_host; |
499 | struct nlm_res *resp = &req->a_res; | 497 | struct nlm_res *resp = &req->a_res; |
500 | struct nlm_wait *block = NULL; | 498 | struct nlm_wait *block = NULL; |
499 | unsigned char fl_flags = fl->fl_flags; | ||
501 | int status = -ENOLCK; | 500 | int status = -ENOLCK; |
502 | 501 | ||
503 | if (!host->h_monitored && nsm_monitor(host) < 0) { | 502 | if (!host->h_monitored && nsm_monitor(host) < 0) { |
@@ -505,6 +504,10 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) | |||
505 | host->h_name); | 504 | host->h_name); |
506 | goto out; | 505 | goto out; |
507 | } | 506 | } |
507 | fl->fl_flags |= FL_ACCESS; | ||
508 | status = do_vfs_lock(fl); | ||
509 | if (status < 0) | ||
510 | goto out; | ||
508 | 511 | ||
509 | block = nlmclnt_prepare_block(host, fl); | 512 | block = nlmclnt_prepare_block(host, fl); |
510 | again: | 513 | again: |
@@ -539,9 +542,10 @@ again: | |||
539 | up_read(&host->h_rwsem); | 542 | up_read(&host->h_rwsem); |
540 | goto again; | 543 | goto again; |
541 | } | 544 | } |
542 | fl->fl_flags |= FL_SLEEP; | ||
543 | /* Ensure the resulting lock will get added to granted list */ | 545 | /* Ensure the resulting lock will get added to granted list */ |
544 | do_vfs_lock(fl); | 546 | fl->fl_flags = fl_flags | FL_SLEEP; |
547 | if (do_vfs_lock(fl) < 0) | ||
548 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); | ||
545 | up_read(&host->h_rwsem); | 549 | up_read(&host->h_rwsem); |
546 | } | 550 | } |
547 | status = nlm_stat_to_errno(resp->status); | 551 | status = nlm_stat_to_errno(resp->status); |
@@ -552,6 +556,7 @@ out_unblock: | |||
552 | nlmclnt_cancel(host, req->a_args.block, fl); | 556 | nlmclnt_cancel(host, req->a_args.block, fl); |
553 | out: | 557 | out: |
554 | nlm_release_call(req); | 558 | nlm_release_call(req); |
559 | fl->fl_flags = fl_flags; | ||
555 | return status; | 560 | return status; |
556 | } | 561 | } |
557 | 562 | ||
@@ -606,15 +611,19 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) | |||
606 | { | 611 | { |
607 | struct nlm_host *host = req->a_host; | 612 | struct nlm_host *host = req->a_host; |
608 | struct nlm_res *resp = &req->a_res; | 613 | struct nlm_res *resp = &req->a_res; |
609 | int status; | 614 | int status = 0; |
610 | 615 | ||
611 | /* | 616 | /* |
612 | * Note: the server is supposed to either grant us the unlock | 617 | * Note: the server is supposed to either grant us the unlock |
613 | * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either | 618 | * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either |
614 | * case, we want to unlock. | 619 | * case, we want to unlock. |
615 | */ | 620 | */ |
621 | fl->fl_flags |= FL_EXISTS; | ||
616 | down_read(&host->h_rwsem); | 622 | down_read(&host->h_rwsem); |
617 | do_vfs_lock(fl); | 623 | if (do_vfs_lock(fl) == -ENOENT) { |
624 | up_read(&host->h_rwsem); | ||
625 | goto out; | ||
626 | } | ||
618 | up_read(&host->h_rwsem); | 627 | up_read(&host->h_rwsem); |
619 | 628 | ||
620 | if (req->a_flags & RPC_TASK_ASYNC) | 629 | if (req->a_flags & RPC_TASK_ASYNC) |
@@ -624,7 +633,6 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) | |||
624 | if (status < 0) | 633 | if (status < 0) |
625 | goto out; | 634 | goto out; |
626 | 635 | ||
627 | status = 0; | ||
628 | if (resp->status == NLM_LCK_GRANTED) | 636 | if (resp->status == NLM_LCK_GRANTED) |
629 | goto out; | 637 | goto out; |
630 | 638 | ||
diff --git a/fs/locks.c b/fs/locks.c index 1ad29c9b6252..b0b41a64e10b 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
@@ -725,6 +725,10 @@ next_task: | |||
725 | /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks | 725 | /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks |
726 | * at the head of the list, but that's secret knowledge known only to | 726 | * at the head of the list, but that's secret knowledge known only to |
727 | * flock_lock_file and posix_lock_file. | 727 | * flock_lock_file and posix_lock_file. |
728 | * | ||
729 | * Note that if called with an FL_EXISTS argument, the caller may determine | ||
730 | * whether or not a lock was successfully freed by testing the return | ||
731 | * value for -ENOENT. | ||
728 | */ | 732 | */ |
729 | static int flock_lock_file(struct file *filp, struct file_lock *request) | 733 | static int flock_lock_file(struct file *filp, struct file_lock *request) |
730 | { | 734 | { |
@@ -735,6 +739,8 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
735 | int found = 0; | 739 | int found = 0; |
736 | 740 | ||
737 | lock_kernel(); | 741 | lock_kernel(); |
742 | if (request->fl_flags & FL_ACCESS) | ||
743 | goto find_conflict; | ||
738 | for_each_lock(inode, before) { | 744 | for_each_lock(inode, before) { |
739 | struct file_lock *fl = *before; | 745 | struct file_lock *fl = *before; |
740 | if (IS_POSIX(fl)) | 746 | if (IS_POSIX(fl)) |
@@ -750,8 +756,11 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
750 | break; | 756 | break; |
751 | } | 757 | } |
752 | 758 | ||
753 | if (request->fl_type == F_UNLCK) | 759 | if (request->fl_type == F_UNLCK) { |
760 | if ((request->fl_flags & FL_EXISTS) && !found) | ||
761 | error = -ENOENT; | ||
754 | goto out; | 762 | goto out; |
763 | } | ||
755 | 764 | ||
756 | error = -ENOMEM; | 765 | error = -ENOMEM; |
757 | new_fl = locks_alloc_lock(); | 766 | new_fl = locks_alloc_lock(); |
@@ -764,6 +773,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
764 | if (found) | 773 | if (found) |
765 | cond_resched(); | 774 | cond_resched(); |
766 | 775 | ||
776 | find_conflict: | ||
767 | for_each_lock(inode, before) { | 777 | for_each_lock(inode, before) { |
768 | struct file_lock *fl = *before; | 778 | struct file_lock *fl = *before; |
769 | if (IS_POSIX(fl)) | 779 | if (IS_POSIX(fl)) |
@@ -777,6 +787,8 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) | |||
777 | locks_insert_block(fl, request); | 787 | locks_insert_block(fl, request); |
778 | goto out; | 788 | goto out; |
779 | } | 789 | } |
790 | if (request->fl_flags & FL_ACCESS) | ||
791 | goto out; | ||
780 | locks_copy_lock(new_fl, request); | 792 | locks_copy_lock(new_fl, request); |
781 | locks_insert_lock(&inode->i_flock, new_fl); | 793 | locks_insert_lock(&inode->i_flock, new_fl); |
782 | new_fl = NULL; | 794 | new_fl = NULL; |
@@ -948,8 +960,11 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request | |||
948 | 960 | ||
949 | error = 0; | 961 | error = 0; |
950 | if (!added) { | 962 | if (!added) { |
951 | if (request->fl_type == F_UNLCK) | 963 | if (request->fl_type == F_UNLCK) { |
964 | if (request->fl_flags & FL_EXISTS) | ||
965 | error = -ENOENT; | ||
952 | goto out; | 966 | goto out; |
967 | } | ||
953 | 968 | ||
954 | if (!new_fl) { | 969 | if (!new_fl) { |
955 | error = -ENOLCK; | 970 | error = -ENOLCK; |
@@ -996,6 +1011,10 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request | |||
996 | * Add a POSIX style lock to a file. | 1011 | * Add a POSIX style lock to a file. |
997 | * We merge adjacent & overlapping locks whenever possible. | 1012 | * We merge adjacent & overlapping locks whenever possible. |
998 | * POSIX locks are sorted by owner task, then by starting address | 1013 | * POSIX locks are sorted by owner task, then by starting address |
1014 | * | ||
1015 | * Note that if called with an FL_EXISTS argument, the caller may determine | ||
1016 | * whether or not a lock was successfully freed by testing the return | ||
1017 | * value for -ENOENT. | ||
999 | */ | 1018 | */ |
1000 | int posix_lock_file(struct file *filp, struct file_lock *fl) | 1019 | int posix_lock_file(struct file *filp, struct file_lock *fl) |
1001 | { | 1020 | { |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b4916b092194..e6ee97f19d81 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -3144,9 +3144,6 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl) | |||
3144 | default: | 3144 | default: |
3145 | BUG(); | 3145 | BUG(); |
3146 | } | 3146 | } |
3147 | if (res < 0) | ||
3148 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", | ||
3149 | __FUNCTION__); | ||
3150 | return res; | 3147 | return res; |
3151 | } | 3148 | } |
3152 | 3149 | ||
@@ -3258,8 +3255,6 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, | |||
3258 | return ERR_PTR(-ENOMEM); | 3255 | return ERR_PTR(-ENOMEM); |
3259 | } | 3256 | } |
3260 | 3257 | ||
3261 | /* Unlock _before_ we do the RPC call */ | ||
3262 | do_vfs_lock(fl->fl_file, fl); | ||
3263 | return rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data); | 3258 | return rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data); |
3264 | } | 3259 | } |
3265 | 3260 | ||
@@ -3270,30 +3265,28 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * | |||
3270 | struct rpc_task *task; | 3265 | struct rpc_task *task; |
3271 | int status = 0; | 3266 | int status = 0; |
3272 | 3267 | ||
3273 | /* Is this a delegated lock? */ | ||
3274 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) | ||
3275 | goto out_unlock; | ||
3276 | /* Is this open_owner holding any locks on the server? */ | ||
3277 | if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) | ||
3278 | goto out_unlock; | ||
3279 | |||
3280 | status = nfs4_set_lock_state(state, request); | 3268 | status = nfs4_set_lock_state(state, request); |
3269 | /* Unlock _before_ we do the RPC call */ | ||
3270 | request->fl_flags |= FL_EXISTS; | ||
3271 | if (do_vfs_lock(request->fl_file, request) == -ENOENT) | ||
3272 | goto out; | ||
3281 | if (status != 0) | 3273 | if (status != 0) |
3282 | goto out_unlock; | 3274 | goto out; |
3275 | /* Is this a delegated lock? */ | ||
3276 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) | ||
3277 | goto out; | ||
3283 | lsp = request->fl_u.nfs4_fl.owner; | 3278 | lsp = request->fl_u.nfs4_fl.owner; |
3284 | status = -ENOMEM; | ||
3285 | seqid = nfs_alloc_seqid(&lsp->ls_seqid); | 3279 | seqid = nfs_alloc_seqid(&lsp->ls_seqid); |
3280 | status = -ENOMEM; | ||
3286 | if (seqid == NULL) | 3281 | if (seqid == NULL) |
3287 | goto out_unlock; | 3282 | goto out; |
3288 | task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid); | 3283 | task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid); |
3289 | status = PTR_ERR(task); | 3284 | status = PTR_ERR(task); |
3290 | if (IS_ERR(task)) | 3285 | if (IS_ERR(task)) |
3291 | goto out_unlock; | 3286 | goto out; |
3292 | status = nfs4_wait_for_completion_rpc_task(task); | 3287 | status = nfs4_wait_for_completion_rpc_task(task); |
3293 | rpc_release_task(task); | 3288 | rpc_release_task(task); |
3294 | return status; | 3289 | out: |
3295 | out_unlock: | ||
3296 | do_vfs_lock(request->fl_file, request); | ||
3297 | return status; | 3290 | return status; |
3298 | } | 3291 | } |
3299 | 3292 | ||
@@ -3461,10 +3454,10 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request | |||
3461 | struct nfs4_exception exception = { }; | 3454 | struct nfs4_exception exception = { }; |
3462 | int err; | 3455 | int err; |
3463 | 3456 | ||
3464 | /* Cache the lock if possible... */ | ||
3465 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) | ||
3466 | return 0; | ||
3467 | do { | 3457 | do { |
3458 | /* Cache the lock if possible... */ | ||
3459 | if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) | ||
3460 | return 0; | ||
3468 | err = _nfs4_do_setlk(state, F_SETLK, request, 1); | 3461 | err = _nfs4_do_setlk(state, F_SETLK, request, 1); |
3469 | if (err != -NFS4ERR_DELAY) | 3462 | if (err != -NFS4ERR_DELAY) |
3470 | break; | 3463 | break; |
@@ -3483,6 +3476,8 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request | |||
3483 | if (err != 0) | 3476 | if (err != 0) |
3484 | return err; | 3477 | return err; |
3485 | do { | 3478 | do { |
3479 | if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) | ||
3480 | return 0; | ||
3486 | err = _nfs4_do_setlk(state, F_SETLK, request, 0); | 3481 | err = _nfs4_do_setlk(state, F_SETLK, request, 0); |
3487 | if (err != -NFS4ERR_DELAY) | 3482 | if (err != -NFS4ERR_DELAY) |
3488 | break; | 3483 | break; |
@@ -3494,29 +3489,42 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request | |||
3494 | static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) | 3489 | static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) |
3495 | { | 3490 | { |
3496 | struct nfs4_client *clp = state->owner->so_client; | 3491 | struct nfs4_client *clp = state->owner->so_client; |
3492 | unsigned char fl_flags = request->fl_flags; | ||
3497 | int status; | 3493 | int status; |
3498 | 3494 | ||
3499 | /* Is this a delegated open? */ | 3495 | /* Is this a delegated open? */ |
3500 | if (NFS_I(state->inode)->delegation_state != 0) { | ||
3501 | /* Yes: cache locks! */ | ||
3502 | status = do_vfs_lock(request->fl_file, request); | ||
3503 | /* ...but avoid races with delegation recall... */ | ||
3504 | if (status < 0 || test_bit(NFS_DELEGATED_STATE, &state->flags)) | ||
3505 | return status; | ||
3506 | } | ||
3507 | down_read(&clp->cl_sem); | ||
3508 | status = nfs4_set_lock_state(state, request); | 3496 | status = nfs4_set_lock_state(state, request); |
3509 | if (status != 0) | 3497 | if (status != 0) |
3510 | goto out; | 3498 | goto out; |
3499 | request->fl_flags |= FL_ACCESS; | ||
3500 | status = do_vfs_lock(request->fl_file, request); | ||
3501 | if (status < 0) | ||
3502 | goto out; | ||
3503 | down_read(&clp->cl_sem); | ||
3504 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { | ||
3505 | struct nfs_inode *nfsi = NFS_I(state->inode); | ||
3506 | /* Yes: cache locks! */ | ||
3507 | down_read(&nfsi->rwsem); | ||
3508 | /* ...but avoid races with delegation recall... */ | ||
3509 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { | ||
3510 | request->fl_flags = fl_flags & ~FL_SLEEP; | ||
3511 | status = do_vfs_lock(request->fl_file, request); | ||
3512 | up_read(&nfsi->rwsem); | ||
3513 | goto out_unlock; | ||
3514 | } | ||
3515 | up_read(&nfsi->rwsem); | ||
3516 | } | ||
3511 | status = _nfs4_do_setlk(state, cmd, request, 0); | 3517 | status = _nfs4_do_setlk(state, cmd, request, 0); |
3512 | if (status != 0) | 3518 | if (status != 0) |
3513 | goto out; | 3519 | goto out_unlock; |
3514 | /* Note: we always want to sleep here! */ | 3520 | /* Note: we always want to sleep here! */ |
3515 | request->fl_flags |= FL_SLEEP; | 3521 | request->fl_flags = fl_flags | FL_SLEEP; |
3516 | if (do_vfs_lock(request->fl_file, request) < 0) | 3522 | if (do_vfs_lock(request->fl_file, request) < 0) |
3517 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); | 3523 | printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); |
3518 | out: | 3524 | out_unlock: |
3519 | up_read(&clp->cl_sem); | 3525 | up_read(&clp->cl_sem); |
3526 | out: | ||
3527 | request->fl_flags = fl_flags; | ||
3520 | return status; | 3528 | return status; |
3521 | } | 3529 | } |
3522 | 3530 | ||
diff --git a/include/linux/fs.h b/include/linux/fs.h index 134b32068246..43aef9b230fd 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -716,6 +716,7 @@ extern spinlock_t files_lock; | |||
716 | #define FL_POSIX 1 | 716 | #define FL_POSIX 1 |
717 | #define FL_FLOCK 2 | 717 | #define FL_FLOCK 2 |
718 | #define FL_ACCESS 8 /* not trying to lock, just looking */ | 718 | #define FL_ACCESS 8 /* not trying to lock, just looking */ |
719 | #define FL_EXISTS 16 /* when unlocking, test for existence */ | ||
719 | #define FL_LEASE 32 /* lease held on this file */ | 720 | #define FL_LEASE 32 /* lease held on this file */ |
720 | #define FL_CLOSE 64 /* unlock on close */ | 721 | #define FL_CLOSE 64 /* unlock on close */ |
721 | #define FL_SLEEP 128 /* A blocking lock */ | 722 | #define FL_SLEEP 128 /* A blocking lock */ |