diff options
author | Pavel Shilovsky <piastry@etersoft.ru> | 2011-10-22 07:33:29 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2011-10-24 13:27:01 -0400 |
commit | 85160e03a79e0d7f9082e61f6a784abc6f402701 (patch) | |
tree | 30e08edf4a022583a582331144d54d26faf2f734 /fs/cifs | |
parent | 42274bb22afc3e877ae5abed787b0b09d7dede52 (diff) |
CIFS: Implement caching mechanism for mandatory brlocks
If we have an oplock and negotiate mandatory locking style we handle
all brlock requests on the client.
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Acked-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/cifsglob.h | 2 | ||||
-rw-r--r-- | fs/cifs/file.c | 206 |
2 files changed, 197 insertions, 11 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index d153d0b89d39..8238aa13e01c 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -496,6 +496,8 @@ extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb); | |||
496 | */ | 496 | */ |
497 | struct cifsLockInfo { | 497 | struct cifsLockInfo { |
498 | struct list_head llist; /* pointer to next cifsLockInfo */ | 498 | struct list_head llist; /* pointer to next cifsLockInfo */ |
499 | struct list_head blist; /* pointer to locks blocked on this */ | ||
500 | wait_queue_head_t block_q; | ||
499 | __u64 offset; | 501 | __u64 offset; |
500 | __u64 length; | 502 | __u64 length; |
501 | __u32 pid; | 503 | __u32 pid; |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index a3b545ff5250..34cbbee30b18 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
@@ -275,11 +275,14 @@ cifs_new_fileinfo(__u16 fileHandle, struct file *file, | |||
275 | spin_unlock(&cifs_file_list_lock); | 275 | spin_unlock(&cifs_file_list_lock); |
276 | 276 | ||
277 | cifs_set_oplock_level(pCifsInode, oplock); | 277 | cifs_set_oplock_level(pCifsInode, oplock); |
278 | pCifsInode->can_cache_brlcks = pCifsInode->clientCanCacheAll; | ||
278 | 279 | ||
279 | file->private_data = pCifsFile; | 280 | file->private_data = pCifsFile; |
280 | return pCifsFile; | 281 | return pCifsFile; |
281 | } | 282 | } |
282 | 283 | ||
284 | static void cifs_del_lock_waiters(struct cifsLockInfo *lock); | ||
285 | |||
283 | /* | 286 | /* |
284 | * Release a reference on the file private data. This may involve closing | 287 | * Release a reference on the file private data. This may involve closing |
285 | * the filehandle out on the server. Must be called without holding | 288 | * the filehandle out on the server. Must be called without holding |
@@ -335,6 +338,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) | |||
335 | if (li->netfid != cifs_file->netfid) | 338 | if (li->netfid != cifs_file->netfid) |
336 | continue; | 339 | continue; |
337 | list_del(&li->llist); | 340 | list_del(&li->llist); |
341 | cifs_del_lock_waiters(li); | ||
338 | kfree(li); | 342 | kfree(li); |
339 | } | 343 | } |
340 | mutex_unlock(&cifsi->lock_mutex); | 344 | mutex_unlock(&cifsi->lock_mutex); |
@@ -640,24 +644,182 @@ int cifs_closedir(struct inode *inode, struct file *file) | |||
640 | return rc; | 644 | return rc; |
641 | } | 645 | } |
642 | 646 | ||
643 | static int store_file_lock(struct cifsInodeInfo *cinode, __u64 len, | 647 | static struct cifsLockInfo * |
644 | __u64 offset, __u8 type, __u16 netfid) | 648 | cifs_lock_init(__u64 len, __u64 offset, __u8 type, __u16 netfid) |
645 | { | 649 | { |
646 | struct cifsLockInfo *li = | 650 | struct cifsLockInfo *li = |
647 | kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); | 651 | kmalloc(sizeof(struct cifsLockInfo), GFP_KERNEL); |
648 | if (li == NULL) | 652 | if (!li) |
649 | return -ENOMEM; | 653 | return li; |
650 | li->netfid = netfid; | 654 | li->netfid = netfid; |
651 | li->offset = offset; | 655 | li->offset = offset; |
652 | li->length = len; | 656 | li->length = len; |
653 | li->type = type; | 657 | li->type = type; |
654 | li->pid = current->tgid; | 658 | li->pid = current->tgid; |
659 | INIT_LIST_HEAD(&li->blist); | ||
660 | init_waitqueue_head(&li->block_q); | ||
661 | return li; | ||
662 | } | ||
663 | |||
664 | static void | ||
665 | cifs_del_lock_waiters(struct cifsLockInfo *lock) | ||
666 | { | ||
667 | struct cifsLockInfo *li, *tmp; | ||
668 | list_for_each_entry_safe(li, tmp, &lock->blist, blist) { | ||
669 | list_del_init(&li->blist); | ||
670 | wake_up(&li->block_q); | ||
671 | } | ||
672 | } | ||
673 | |||
674 | static bool | ||
675 | cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset, | ||
676 | __u64 length, __u8 type, __u16 netfid, | ||
677 | struct cifsLockInfo **conf_lock) | ||
678 | { | ||
679 | struct cifsLockInfo *li, *tmp; | ||
680 | |||
681 | list_for_each_entry_safe(li, tmp, &cinode->llist, llist) { | ||
682 | if (offset + length <= li->offset || | ||
683 | offset >= li->offset + li->length) | ||
684 | continue; | ||
685 | else if ((type & LOCKING_ANDX_SHARED_LOCK) && | ||
686 | ((netfid == li->netfid && current->tgid == li->pid) || | ||
687 | type == li->type)) | ||
688 | continue; | ||
689 | else { | ||
690 | *conf_lock = li; | ||
691 | return true; | ||
692 | } | ||
693 | } | ||
694 | return false; | ||
695 | } | ||
696 | |||
697 | static int | ||
698 | cifs_lock_test(struct cifsInodeInfo *cinode, __u64 offset, __u64 length, | ||
699 | __u8 type, __u16 netfid, struct file_lock *flock) | ||
700 | { | ||
701 | int rc = 0; | ||
702 | struct cifsLockInfo *conf_lock; | ||
703 | bool exist; | ||
704 | |||
705 | mutex_lock(&cinode->lock_mutex); | ||
706 | |||
707 | exist = cifs_find_lock_conflict(cinode, offset, length, type, netfid, | ||
708 | &conf_lock); | ||
709 | if (exist) { | ||
710 | flock->fl_start = conf_lock->offset; | ||
711 | flock->fl_end = conf_lock->offset + conf_lock->length - 1; | ||
712 | flock->fl_pid = conf_lock->pid; | ||
713 | if (conf_lock->type & LOCKING_ANDX_SHARED_LOCK) | ||
714 | flock->fl_type = F_RDLCK; | ||
715 | else | ||
716 | flock->fl_type = F_WRLCK; | ||
717 | } else if (!cinode->can_cache_brlcks) | ||
718 | rc = 1; | ||
719 | else | ||
720 | flock->fl_type = F_UNLCK; | ||
721 | |||
722 | mutex_unlock(&cinode->lock_mutex); | ||
723 | return rc; | ||
724 | } | ||
725 | |||
726 | static int | ||
727 | cifs_lock_add(struct cifsInodeInfo *cinode, __u64 len, __u64 offset, | ||
728 | __u8 type, __u16 netfid) | ||
729 | { | ||
730 | struct cifsLockInfo *li; | ||
731 | |||
732 | li = cifs_lock_init(len, offset, type, netfid); | ||
733 | if (!li) | ||
734 | return -ENOMEM; | ||
735 | |||
655 | mutex_lock(&cinode->lock_mutex); | 736 | mutex_lock(&cinode->lock_mutex); |
656 | list_add_tail(&li->llist, &cinode->llist); | 737 | list_add_tail(&li->llist, &cinode->llist); |
657 | mutex_unlock(&cinode->lock_mutex); | 738 | mutex_unlock(&cinode->lock_mutex); |
658 | return 0; | 739 | return 0; |
659 | } | 740 | } |
660 | 741 | ||
742 | static int | ||
743 | cifs_lock_add_if(struct cifsInodeInfo *cinode, __u64 offset, __u64 length, | ||
744 | __u8 type, __u16 netfid, bool wait) | ||
745 | { | ||
746 | struct cifsLockInfo *lock, *conf_lock; | ||
747 | bool exist; | ||
748 | int rc = 0; | ||
749 | |||
750 | lock = cifs_lock_init(length, offset, type, netfid); | ||
751 | if (!lock) | ||
752 | return -ENOMEM; | ||
753 | |||
754 | try_again: | ||
755 | exist = false; | ||
756 | mutex_lock(&cinode->lock_mutex); | ||
757 | |||
758 | exist = cifs_find_lock_conflict(cinode, offset, length, type, netfid, | ||
759 | &conf_lock); | ||
760 | if (!exist && cinode->can_cache_brlcks) { | ||
761 | list_add_tail(&lock->llist, &cinode->llist); | ||
762 | mutex_unlock(&cinode->lock_mutex); | ||
763 | return rc; | ||
764 | } | ||
765 | |||
766 | if (!exist) | ||
767 | rc = 1; | ||
768 | else if (!wait) | ||
769 | rc = -EACCES; | ||
770 | else { | ||
771 | list_add_tail(&lock->blist, &conf_lock->blist); | ||
772 | mutex_unlock(&cinode->lock_mutex); | ||
773 | rc = wait_event_interruptible(lock->block_q, | ||
774 | (lock->blist.prev == &lock->blist) && | ||
775 | (lock->blist.next == &lock->blist)); | ||
776 | if (!rc) | ||
777 | goto try_again; | ||
778 | else { | ||
779 | mutex_lock(&cinode->lock_mutex); | ||
780 | list_del_init(&lock->blist); | ||
781 | mutex_unlock(&cinode->lock_mutex); | ||
782 | } | ||
783 | } | ||
784 | |||
785 | kfree(lock); | ||
786 | mutex_unlock(&cinode->lock_mutex); | ||
787 | return rc; | ||
788 | } | ||
789 | |||
790 | static int | ||
791 | cifs_push_locks(struct cifsFileInfo *cfile) | ||
792 | { | ||
793 | int xid, rc = 0, stored_rc; | ||
794 | struct cifsLockInfo *li, *tmp; | ||
795 | struct cifs_tcon *tcon; | ||
796 | struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); | ||
797 | |||
798 | xid = GetXid(); | ||
799 | tcon = tlink_tcon(cfile->tlink); | ||
800 | |||
801 | mutex_lock(&cinode->lock_mutex); | ||
802 | if (!cinode->can_cache_brlcks) { | ||
803 | mutex_unlock(&cinode->lock_mutex); | ||
804 | FreeXid(xid); | ||
805 | return rc; | ||
806 | } | ||
807 | |||
808 | list_for_each_entry_safe(li, tmp, &cinode->llist, llist) { | ||
809 | stored_rc = CIFSSMBLock(xid, tcon, cfile->netfid, | ||
810 | li->pid, li->length, li->offset, | ||
811 | 0, 1, li->type, 0, 0); | ||
812 | if (stored_rc) | ||
813 | rc = stored_rc; | ||
814 | } | ||
815 | |||
816 | cinode->can_cache_brlcks = false; | ||
817 | mutex_unlock(&cinode->lock_mutex); | ||
818 | |||
819 | FreeXid(xid); | ||
820 | return rc; | ||
821 | } | ||
822 | |||
661 | static void | 823 | static void |
662 | cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock, | 824 | cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock, |
663 | bool *wait_flag) | 825 | bool *wait_flag) |
@@ -708,6 +870,7 @@ cifs_getlk(struct cifsFileInfo *cfile, struct file_lock *flock, __u8 type, | |||
708 | { | 870 | { |
709 | int rc = 0; | 871 | int rc = 0; |
710 | __u64 length = 1 + flock->fl_end - flock->fl_start; | 872 | __u64 length = 1 + flock->fl_end - flock->fl_start; |
873 | struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); | ||
711 | __u16 netfid = cfile->netfid; | 874 | __u16 netfid = cfile->netfid; |
712 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); | 875 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); |
713 | 876 | ||
@@ -723,6 +886,11 @@ cifs_getlk(struct cifsFileInfo *cfile, struct file_lock *flock, __u8 type, | |||
723 | return rc; | 886 | return rc; |
724 | } | 887 | } |
725 | 888 | ||
889 | rc = cifs_lock_test(cinode, flock->fl_start, length, type, netfid, | ||
890 | flock); | ||
891 | if (!rc) | ||
892 | return rc; | ||
893 | |||
726 | /* BB we could chain these into one lock request BB */ | 894 | /* BB we could chain these into one lock request BB */ |
727 | rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, | 895 | rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, |
728 | flock->fl_start, 0, 1, type, 0, 0); | 896 | flock->fl_start, 0, 1, type, 0, 0); |
@@ -790,12 +958,19 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type, | |||
790 | } | 958 | } |
791 | 959 | ||
792 | if (lock) { | 960 | if (lock) { |
961 | rc = cifs_lock_add_if(cinode, flock->fl_start, length, | ||
962 | type, netfid, wait_flag); | ||
963 | if (rc < 0) | ||
964 | return rc; | ||
965 | else if (!rc) | ||
966 | goto out; | ||
967 | |||
793 | rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, | 968 | rc = CIFSSMBLock(xid, tcon, netfid, current->tgid, length, |
794 | flock->fl_start, 0, lock, type, wait_flag, 0); | 969 | flock->fl_start, 0, 1, type, wait_flag, 0); |
795 | if (rc == 0) { | 970 | if (rc == 0) { |
796 | /* For Windows locks we must store them. */ | 971 | /* For Windows locks we must store them. */ |
797 | rc = store_file_lock(cinode, length, flock->fl_start, | 972 | rc = cifs_lock_add(cinode, length, flock->fl_start, |
798 | type, netfid); | 973 | type, netfid); |
799 | } | 974 | } |
800 | } else if (unlock) { | 975 | } else if (unlock) { |
801 | /* | 976 | /* |
@@ -816,14 +991,19 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type, | |||
816 | if (cfile->netfid != li->netfid) | 991 | if (cfile->netfid != li->netfid) |
817 | continue; | 992 | continue; |
818 | 993 | ||
819 | stored_rc = CIFSSMBLock(xid, tcon, netfid, | 994 | if (!cinode->can_cache_brlcks) |
820 | current->tgid, li->length, | 995 | stored_rc = CIFSSMBLock(xid, tcon, netfid, |
821 | li->offset, 1, 0, li->type, | 996 | current->tgid, |
822 | 0, 0); | 997 | li->length, li->offset, |
998 | 1, 0, li->type, 0, 0); | ||
999 | else | ||
1000 | stored_rc = 0; | ||
1001 | |||
823 | if (stored_rc) | 1002 | if (stored_rc) |
824 | rc = stored_rc; | 1003 | rc = stored_rc; |
825 | else { | 1004 | else { |
826 | list_del(&li->llist); | 1005 | list_del(&li->llist); |
1006 | cifs_del_lock_waiters(li); | ||
827 | kfree(li); | 1007 | kfree(li); |
828 | } | 1008 | } |
829 | } | 1009 | } |
@@ -2404,6 +2584,10 @@ void cifs_oplock_break(struct work_struct *work) | |||
2404 | cFYI(1, "Oplock flush inode %p rc %d", inode, rc); | 2584 | cFYI(1, "Oplock flush inode %p rc %d", inode, rc); |
2405 | } | 2585 | } |
2406 | 2586 | ||
2587 | rc = cifs_push_locks(cfile); | ||
2588 | if (rc) | ||
2589 | cERROR(1, "Push locks rc = %d", rc); | ||
2590 | |||
2407 | /* | 2591 | /* |
2408 | * releasing stale oplock after recent reconnect of smb session using | 2592 | * releasing stale oplock after recent reconnect of smb session using |
2409 | * a now incorrect file handle is not a data integrity issue but do | 2593 | * a now incorrect file handle is not a data integrity issue but do |