aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorPavel Shilovsky <piastry@etersoft.ru>2012-03-27 07:36:15 -0400
committerSteve French <sfrench@us.ibm.com>2012-03-31 18:30:48 -0400
commitb5efb978469d152c2c7c0a09746fb0bfc6171868 (patch)
tree18d46e1ef3bdf9bbb2df7f8e72b0c1e2ac2919fd /fs/cifs
parentfa2a4519cb6ad94224eb56a1341fff570fd44ea1 (diff)
CIFS: Fix VFS lock usage for oplocked files
We can deadlock if we have a write oplock and two processes use the same file handle. In this case the first process can't unlock its lock if another process blocked on the lock in the same time. Fix this by removing lock_mutex protection from waiting on a blocked lock and protect only posix_lock_file call. Cc: stable@kernel.org Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru> Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/file.c56
1 files changed, 52 insertions, 4 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 460d87b7cda0..0a11dbbbb131 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -671,6 +671,21 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock)
671 } 671 }
672} 672}
673 673
674/*
675 * Copied from fs/locks.c with small changes.
676 * Remove waiter from blocker's block list.
677 * When blocker ends up pointing to itself then the list is empty.
678 */
679static void
680cifs_locks_delete_block(struct file_lock *waiter)
681{
682 lock_flocks();
683 list_del_init(&waiter->fl_block);
684 list_del_init(&waiter->fl_link);
685 waiter->fl_next = NULL;
686 unlock_flocks();
687}
688
674static bool 689static bool
675__cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset, 690__cifs_find_lock_conflict(struct cifsInodeInfo *cinode, __u64 offset,
676 __u64 length, __u8 type, __u16 netfid, 691 __u64 length, __u8 type, __u16 netfid,
@@ -820,6 +835,39 @@ cifs_posix_lock_test(struct file *file, struct file_lock *flock)
820 return rc; 835 return rc;
821} 836}
822 837
838/* Called with locked lock_mutex, return with unlocked. */
839static int
840cifs_posix_lock_file_wait_locked(struct file *file, struct file_lock *flock)
841{
842 struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode);
843 int rc;
844
845 while (true) {
846 rc = posix_lock_file(file, flock, NULL);
847 mutex_unlock(&cinode->lock_mutex);
848 if (rc != FILE_LOCK_DEFERRED)
849 break;
850 rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next);
851 if (!rc) {
852 mutex_lock(&cinode->lock_mutex);
853 continue;
854 }
855 cifs_locks_delete_block(flock);
856 break;
857 }
858 return rc;
859}
860
861static int
862cifs_posix_lock_file_wait(struct file *file, struct file_lock *flock)
863{
864 struct cifsInodeInfo *cinode = CIFS_I(file->f_path.dentry->d_inode);
865
866 mutex_lock(&cinode->lock_mutex);
867 /* lock_mutex will be released by the function below */
868 return cifs_posix_lock_file_wait_locked(file, flock);
869}
870
823/* 871/*
824 * Set the byte-range lock (posix style). Returns: 872 * Set the byte-range lock (posix style). Returns:
825 * 1) 0, if we set the lock and don't need to request to the server; 873 * 1) 0, if we set the lock and don't need to request to the server;
@@ -840,9 +888,9 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
840 mutex_unlock(&cinode->lock_mutex); 888 mutex_unlock(&cinode->lock_mutex);
841 return rc; 889 return rc;
842 } 890 }
843 rc = posix_lock_file_wait(file, flock); 891
844 mutex_unlock(&cinode->lock_mutex); 892 /* lock_mutex will be released by the function below */
845 return rc; 893 return cifs_posix_lock_file_wait_locked(file, flock);
846} 894}
847 895
848static int 896static int
@@ -1338,7 +1386,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u8 type,
1338 1386
1339out: 1387out:
1340 if (flock->fl_flags & FL_POSIX) 1388 if (flock->fl_flags & FL_POSIX)
1341 posix_lock_file_wait(file, flock); 1389 cifs_posix_lock_file_wait(file, flock);
1342 return rc; 1390 return rc;
1343} 1391}
1344 1392