aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDave Wysochanski <dwysocha@redhat.com>2019-10-23 05:02:33 -0400
committerSteve French <stfrench@microsoft.com>2019-10-24 22:35:04 -0400
commitd46b0da7a33dd8c99d969834f682267a45444ab3 (patch)
treef8db814544321af48a968439747c6c39a334de60 /fs
parent1a67c415965752879e2e9fad407bc44fc7f25f23 (diff)
cifs: Fix cifsInodeInfo lock_sem deadlock when reconnect occurs
There's a deadlock that is possible and can easily be seen with a test where multiple readers open/read/close of the same file and a disruption occurs causing reconnect. The deadlock is due a reader thread inside cifs_strict_readv calling down_read and obtaining lock_sem, and then after reconnect inside cifs_reopen_file calling down_read a second time. If in between the two down_read calls, a down_write comes from another process, deadlock occurs. CPU0 CPU1 ---- ---- cifs_strict_readv() down_read(&cifsi->lock_sem); _cifsFileInfo_put OR cifs_new_fileinfo down_write(&cifsi->lock_sem); cifs_reopen_file() down_read(&cifsi->lock_sem); Fix the above by changing all down_write(lock_sem) calls to down_write_trylock(lock_sem)/msleep() loop, which in turn makes the second down_read call benign since it will never block behind the writer while holding lock_sem. Signed-off-by: Dave Wysochanski <dwysocha@redhat.com> Suggested-by: Ronnie Sahlberg <lsahlber@redhat.com> Reviewed--by: Ronnie Sahlberg <lsahlber@redhat.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/cifsglob.h5
-rw-r--r--fs/cifs/cifsproto.h1
-rw-r--r--fs/cifs/file.c23
-rw-r--r--fs/cifs/smb2file.c2
4 files changed, 22 insertions, 9 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 50dfd9049370..d78bfcc19156 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1391,6 +1391,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
1391struct cifsInodeInfo { 1391struct cifsInodeInfo {
1392 bool can_cache_brlcks; 1392 bool can_cache_brlcks;
1393 struct list_head llist; /* locks helb by this inode */ 1393 struct list_head llist; /* locks helb by this inode */
1394 /*
1395 * NOTE: Some code paths call down_read(lock_sem) twice, so
1396 * we must always use use cifs_down_write() instead of down_write()
1397 * for this semaphore to avoid deadlocks.
1398 */
1394 struct rw_semaphore lock_sem; /* protect the fields above */ 1399 struct rw_semaphore lock_sem; /* protect the fields above */
1395 /* BB add in lists for dirty pages i.e. write caching info for oplock */ 1400 /* BB add in lists for dirty pages i.e. write caching info for oplock */
1396 struct list_head openFileList; 1401 struct list_head openFileList;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index e53e9f62b87b..fe597d3d5208 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -170,6 +170,7 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
170 struct file_lock *flock, const unsigned int xid); 170 struct file_lock *flock, const unsigned int xid);
171extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); 171extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
172 172
173extern void cifs_down_write(struct rw_semaphore *sem);
173extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, 174extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
174 struct file *file, 175 struct file *file,
175 struct tcon_link *tlink, 176 struct tcon_link *tlink,
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 64827938ecf7..fa7b0fa72bb3 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -281,6 +281,13 @@ cifs_has_mand_locks(struct cifsInodeInfo *cinode)
281 return has_locks; 281 return has_locks;
282} 282}
283 283
284void
285cifs_down_write(struct rw_semaphore *sem)
286{
287 while (!down_write_trylock(sem))
288 msleep(10);
289}
290
284struct cifsFileInfo * 291struct cifsFileInfo *
285cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, 292cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
286 struct tcon_link *tlink, __u32 oplock) 293 struct tcon_link *tlink, __u32 oplock)
@@ -306,7 +313,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
306 INIT_LIST_HEAD(&fdlocks->locks); 313 INIT_LIST_HEAD(&fdlocks->locks);
307 fdlocks->cfile = cfile; 314 fdlocks->cfile = cfile;
308 cfile->llist = fdlocks; 315 cfile->llist = fdlocks;
309 down_write(&cinode->lock_sem); 316 cifs_down_write(&cinode->lock_sem);
310 list_add(&fdlocks->llist, &cinode->llist); 317 list_add(&fdlocks->llist, &cinode->llist);
311 up_write(&cinode->lock_sem); 318 up_write(&cinode->lock_sem);
312 319
@@ -464,7 +471,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
464 * Delete any outstanding lock records. We'll lose them when the file 471 * Delete any outstanding lock records. We'll lose them when the file
465 * is closed anyway. 472 * is closed anyway.
466 */ 473 */
467 down_write(&cifsi->lock_sem); 474 cifs_down_write(&cifsi->lock_sem);
468 list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) { 475 list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
469 list_del(&li->llist); 476 list_del(&li->llist);
470 cifs_del_lock_waiters(li); 477 cifs_del_lock_waiters(li);
@@ -1027,7 +1034,7 @@ static void
1027cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock) 1034cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
1028{ 1035{
1029 struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry)); 1036 struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
1030 down_write(&cinode->lock_sem); 1037 cifs_down_write(&cinode->lock_sem);
1031 list_add_tail(&lock->llist, &cfile->llist->locks); 1038 list_add_tail(&lock->llist, &cfile->llist->locks);
1032 up_write(&cinode->lock_sem); 1039 up_write(&cinode->lock_sem);
1033} 1040}
@@ -1049,7 +1056,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
1049 1056
1050try_again: 1057try_again:
1051 exist = false; 1058 exist = false;
1052 down_write(&cinode->lock_sem); 1059 cifs_down_write(&cinode->lock_sem);
1053 1060
1054 exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length, 1061 exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
1055 lock->type, lock->flags, &conf_lock, 1062 lock->type, lock->flags, &conf_lock,
@@ -1072,7 +1079,7 @@ try_again:
1072 (lock->blist.next == &lock->blist)); 1079 (lock->blist.next == &lock->blist));
1073 if (!rc) 1080 if (!rc)
1074 goto try_again; 1081 goto try_again;
1075 down_write(&cinode->lock_sem); 1082 cifs_down_write(&cinode->lock_sem);
1076 list_del_init(&lock->blist); 1083 list_del_init(&lock->blist);
1077 } 1084 }
1078 1085
@@ -1125,7 +1132,7 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
1125 return rc; 1132 return rc;
1126 1133
1127try_again: 1134try_again:
1128 down_write(&cinode->lock_sem); 1135 cifs_down_write(&cinode->lock_sem);
1129 if (!cinode->can_cache_brlcks) { 1136 if (!cinode->can_cache_brlcks) {
1130 up_write(&cinode->lock_sem); 1137 up_write(&cinode->lock_sem);
1131 return rc; 1138 return rc;
@@ -1331,7 +1338,7 @@ cifs_push_locks(struct cifsFileInfo *cfile)
1331 int rc = 0; 1338 int rc = 0;
1332 1339
1333 /* we are going to update can_cache_brlcks here - need a write access */ 1340 /* we are going to update can_cache_brlcks here - need a write access */
1334 down_write(&cinode->lock_sem); 1341 cifs_down_write(&cinode->lock_sem);
1335 if (!cinode->can_cache_brlcks) { 1342 if (!cinode->can_cache_brlcks) {
1336 up_write(&cinode->lock_sem); 1343 up_write(&cinode->lock_sem);
1337 return rc; 1344 return rc;
@@ -1522,7 +1529,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
1522 if (!buf) 1529 if (!buf)
1523 return -ENOMEM; 1530 return -ENOMEM;
1524 1531
1525 down_write(&cinode->lock_sem); 1532 cifs_down_write(&cinode->lock_sem);
1526 for (i = 0; i < 2; i++) { 1533 for (i = 0; i < 2; i++) {
1527 cur = buf; 1534 cur = buf;
1528 num = 0; 1535 num = 0;
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index e6a1fc72018f..8b0b512c5792 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -145,7 +145,7 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
145 145
146 cur = buf; 146 cur = buf;
147 147
148 down_write(&cinode->lock_sem); 148 cifs_down_write(&cinode->lock_sem);
149 list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { 149 list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
150 if (flock->fl_start > li->offset || 150 if (flock->fl_start > li->offset ||
151 (flock->fl_start + length) < 151 (flock->fl_start + length) <