aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilov@microsoft.com>2016-11-29 14:31:23 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-06 04:40:16 -0500
commit46890ffba1d62550c45f4412378daca9586ba51b (patch)
tree84e3b2292524717456f1a3f9887c4e8779a06a15 /fs
parent69d13b69e79cb76413b49a66e43a2be39f14aefe (diff)
CIFS: Fix a possible double locking of mutex during reconnect
commit 96a988ffeb90dba33a71c3826086fe67c897a183 upstream. With the current code it is possible to lock a mutex twice when a subsequent reconnects are triggered. On the 1st reconnect we reconnect sessions and tcons and then persistent file handles. If the 2nd reconnect happens during the reconnecting of persistent file handles then the following sequence of calls is observed: cifs_reopen_file -> SMB2_open -> small_smb2_init -> smb2_reconnect -> cifs_reopen_persistent_file_handles -> cifs_reopen_file (again!). So, we are trying to acquire the same cfile->fh_mutex twice which is wrong. Fix this by moving reconnecting of persistent handles to the delayed work (smb2_reconnect_server) and submitting this work every time we reconnect tcon in SMB2 commands handling codepath. This can also lead to corruption of a temporary file list in cifs_reopen_persistent_file_handles() because we can recursively call this function twice. Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/cifsglob.h1
-rw-r--r--fs/cifs/file.c8
-rw-r--r--fs/cifs/smb2pdu.c14
-rw-r--r--fs/cifs/smb2pdu.h2
4 files changed, 19 insertions, 6 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 64f5d8754fb5..203287f86525 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -925,6 +925,7 @@ struct cifs_tcon {
925 bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ 925 bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */
926 bool broken_sparse_sup; /* if server or share does not support sparse */ 926 bool broken_sparse_sup; /* if server or share does not support sparse */
927 bool need_reconnect:1; /* connection reset, tid now invalid */ 927 bool need_reconnect:1; /* connection reset, tid now invalid */
928 bool need_reopen_files:1; /* need to reopen tcon file handles */
928 bool use_resilient:1; /* use resilient instead of durable handles */ 929 bool use_resilient:1; /* use resilient instead of durable handles */
929 bool use_persistent:1; /* use persistent instead of durable handles */ 930 bool use_persistent:1; /* use persistent instead of durable handles */
930#ifdef CONFIG_CIFS_SMB2 931#ifdef CONFIG_CIFS_SMB2
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 7f5f6176c6f1..18a1e1d6671f 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -777,6 +777,11 @@ cifs_reopen_persistent_handles(struct cifs_tcon *tcon)
777 struct list_head *tmp1; 777 struct list_head *tmp1;
778 struct list_head tmp_list; 778 struct list_head tmp_list;
779 779
780 if (!tcon->use_persistent || !tcon->need_reopen_files)
781 return;
782
783 tcon->need_reopen_files = false;
784
780 cifs_dbg(FYI, "Reopen persistent handles"); 785 cifs_dbg(FYI, "Reopen persistent handles");
781 INIT_LIST_HEAD(&tmp_list); 786 INIT_LIST_HEAD(&tmp_list);
782 787
@@ -793,7 +798,8 @@ cifs_reopen_persistent_handles(struct cifs_tcon *tcon)
793 798
794 list_for_each_safe(tmp, tmp1, &tmp_list) { 799 list_for_each_safe(tmp, tmp1, &tmp_list) {
795 open_file = list_entry(tmp, struct cifsFileInfo, rlist); 800 open_file = list_entry(tmp, struct cifsFileInfo, rlist);
796 cifs_reopen_file(open_file, false /* do not flush */); 801 if (cifs_reopen_file(open_file, false /* do not flush */))
802 tcon->need_reopen_files = true;
797 list_del_init(&open_file->rlist); 803 list_del_init(&open_file->rlist);
798 cifsFileInfo_put(open_file); 804 cifsFileInfo_put(open_file);
799 } 805 }
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 4ba3f68a1766..87457227812c 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -250,16 +250,19 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
250 } 250 }
251 251
252 cifs_mark_open_files_invalid(tcon); 252 cifs_mark_open_files_invalid(tcon);
253 if (tcon->use_persistent)
254 tcon->need_reopen_files = true;
253 255
254 rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage); 256 rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
255 mutex_unlock(&tcon->ses->session_mutex); 257 mutex_unlock(&tcon->ses->session_mutex);
256 258
257 if (tcon->use_persistent)
258 cifs_reopen_persistent_handles(tcon);
259
260 cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); 259 cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
261 if (rc) 260 if (rc)
262 goto out; 261 goto out;
262
263 if (smb2_command != SMB2_INTERNAL_CMD)
264 queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
265
263 atomic_inc(&tconInfoReconnectCount); 266 atomic_inc(&tconInfoReconnectCount);
264out: 267out:
265 /* 268 /*
@@ -1990,7 +1993,7 @@ void smb2_reconnect_server(struct work_struct *work)
1990 spin_lock(&cifs_tcp_ses_lock); 1993 spin_lock(&cifs_tcp_ses_lock);
1991 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 1994 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
1992 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { 1995 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
1993 if (tcon->need_reconnect) { 1996 if (tcon->need_reconnect || tcon->need_reopen_files) {
1994 tcon->tc_count++; 1997 tcon->tc_count++;
1995 list_add_tail(&tcon->rlist, &tmp_list); 1998 list_add_tail(&tcon->rlist, &tmp_list);
1996 tcon_exist = true; 1999 tcon_exist = true;
@@ -2007,7 +2010,8 @@ void smb2_reconnect_server(struct work_struct *work)
2007 spin_unlock(&cifs_tcp_ses_lock); 2010 spin_unlock(&cifs_tcp_ses_lock);
2008 2011
2009 list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) { 2012 list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
2010 smb2_reconnect(SMB2_ECHO, tcon); 2013 if (!smb2_reconnect(SMB2_INTERNAL_CMD, tcon))
2014 cifs_reopen_persistent_handles(tcon);
2011 list_del_init(&tcon->rlist); 2015 list_del_init(&tcon->rlist);
2012 cifs_put_tcon(tcon); 2016 cifs_put_tcon(tcon);
2013 } 2017 }
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index fd3709e8de33..dc0d141f33e2 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -80,6 +80,8 @@
80#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) 80#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE)
81#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) 81#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE)
82 82
83#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF)
84
83#define NUMBER_OF_SMB2_COMMANDS 0x0013 85#define NUMBER_OF_SMB2_COMMANDS 0x0013
84 86
85/* BB FIXME - analyze following length BB */ 87/* BB FIXME - analyze following length BB */