aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorPavel Shilovsky <piastry@etersoft.ru>2011-10-22 07:33:29 -0400
committerSteve French <smfrench@gmail.com>2011-10-24 13:27:01 -0400
commit85160e03a79e0d7f9082e61f6a784abc6f402701 (patch)
tree30e08edf4a022583a582331144d54d26faf2f734 /fs/cifs
parent42274bb22afc3e877ae5abed787b0b09d7dede52 (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.h2
-rw-r--r--fs/cifs/file.c206
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 */
497struct cifsLockInfo { 497struct 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
284static 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
643static int store_file_lock(struct cifsInodeInfo *cinode, __u64 len, 647static struct cifsLockInfo *
644 __u64 offset, __u8 type, __u16 netfid) 648cifs_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
664static void
665cifs_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
674static bool
675cifs_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
697static int
698cifs_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
726static int
727cifs_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
742static int
743cifs_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
754try_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
790static int
791cifs_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
661static void 823static void
662cifs_read_flock(struct file_lock *flock, __u8 *type, int *lock, int *unlock, 824cifs_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