Skip to content

Commit 1bcd548

Browse files
Paulo AlcantaraSteve French
authored andcommitted
cifs: prevent data race in cifs_reconnect_tcon()
Make sure to get an up-to-date TCP_Server_Info::nr_targets value prior to waiting the server to be reconnected in cifs_reconnect_tcon(). It is set in cifs_tcp_ses_needs_reconnect() and protected by TCP_Server_Info::srv_lock. Create a new cifs_wait_for_server_reconnect() helper that can be used by both SMB2+ and CIFS reconnect code. Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent b9ee2e3 commit 1bcd548

File tree

4 files changed

+69
-101
lines changed

4 files changed

+69
-101
lines changed

fs/cifs/cifsproto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,5 +694,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
694694

695695
struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
696696
void cifs_put_tcon_super(struct super_block *sb);
697+
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
697698

698699
#endif /* _CIFSPROTO_H */

fs/cifs/cifssmb.c

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
7272
struct cifs_ses *ses;
7373
struct TCP_Server_Info *server;
7474
struct nls_table *nls_codepage;
75-
int retries;
7675

7776
/*
7877
* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
@@ -102,45 +101,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
102101
}
103102
spin_unlock(&tcon->tc_lock);
104103

105-
retries = server->nr_targets;
106-
107-
/*
108-
* Give demultiplex thread up to 10 seconds to each target available for
109-
* reconnect -- should be greater than cifs socket timeout which is 7
110-
* seconds.
111-
*/
112-
while (server->tcpStatus == CifsNeedReconnect) {
113-
rc = wait_event_interruptible_timeout(server->response_q,
114-
(server->tcpStatus != CifsNeedReconnect),
115-
10 * HZ);
116-
if (rc < 0) {
117-
cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n",
118-
__func__);
119-
return -ERESTARTSYS;
120-
}
121-
122-
/* are we still trying to reconnect? */
123-
spin_lock(&server->srv_lock);
124-
if (server->tcpStatus != CifsNeedReconnect) {
125-
spin_unlock(&server->srv_lock);
126-
break;
127-
}
128-
spin_unlock(&server->srv_lock);
129-
130-
if (retries && --retries)
131-
continue;
132-
133-
/*
134-
* on "soft" mounts we wait once. Hard mounts keep
135-
* retrying until process is killed or server comes
136-
* back on-line
137-
*/
138-
if (!tcon->retry) {
139-
cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
140-
return -EHOSTDOWN;
141-
}
142-
retries = server->nr_targets;
143-
}
104+
rc = cifs_wait_for_server_reconnect(server, tcon->retry);
105+
if (rc)
106+
return rc;
144107

145108
spin_lock(&ses->chan_lock);
146109
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {

fs/cifs/misc.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,3 +1266,47 @@ int cifs_inval_name_dfs_link_error(const unsigned int xid,
12661266
return 0;
12671267
}
12681268
#endif
1269+
1270+
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry)
1271+
{
1272+
int timeout = 10;
1273+
int rc;
1274+
1275+
spin_lock(&server->srv_lock);
1276+
if (server->tcpStatus != CifsNeedReconnect) {
1277+
spin_unlock(&server->srv_lock);
1278+
return 0;
1279+
}
1280+
timeout *= server->nr_targets;
1281+
spin_unlock(&server->srv_lock);
1282+
1283+
/*
1284+
* Give demultiplex thread up to 10 seconds to each target available for
1285+
* reconnect -- should be greater than cifs socket timeout which is 7
1286+
* seconds.
1287+
*
1288+
* On "soft" mounts we wait once. Hard mounts keep retrying until
1289+
* process is killed or server comes back on-line.
1290+
*/
1291+
do {
1292+
rc = wait_event_interruptible_timeout(server->response_q,
1293+
(server->tcpStatus != CifsNeedReconnect),
1294+
timeout * HZ);
1295+
if (rc < 0) {
1296+
cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
1297+
__func__);
1298+
return -ERESTARTSYS;
1299+
}
1300+
1301+
/* are we still trying to reconnect? */
1302+
spin_lock(&server->srv_lock);
1303+
if (server->tcpStatus != CifsNeedReconnect) {
1304+
spin_unlock(&server->srv_lock);
1305+
return 0;
1306+
}
1307+
spin_unlock(&server->srv_lock);
1308+
} while (retry);
1309+
1310+
cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
1311+
return -EHOSTDOWN;
1312+
}

fs/cifs/smb2pdu.c

Lines changed: 21 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -139,66 +139,6 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
139139
return;
140140
}
141141

142-
static int wait_for_server_reconnect(struct TCP_Server_Info *server,
143-
__le16 smb2_command, bool retry)
144-
{
145-
int timeout = 10;
146-
int rc;
147-
148-
spin_lock(&server->srv_lock);
149-
if (server->tcpStatus != CifsNeedReconnect) {
150-
spin_unlock(&server->srv_lock);
151-
return 0;
152-
}
153-
timeout *= server->nr_targets;
154-
spin_unlock(&server->srv_lock);
155-
156-
/*
157-
* Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
158-
* here since they are implicitly done when session drops.
159-
*/
160-
switch (smb2_command) {
161-
/*
162-
* BB Should we keep oplock break and add flush to exceptions?
163-
*/
164-
case SMB2_TREE_DISCONNECT:
165-
case SMB2_CANCEL:
166-
case SMB2_CLOSE:
167-
case SMB2_OPLOCK_BREAK:
168-
return -EAGAIN;
169-
}
170-
171-
/*
172-
* Give demultiplex thread up to 10 seconds to each target available for
173-
* reconnect -- should be greater than cifs socket timeout which is 7
174-
* seconds.
175-
*
176-
* On "soft" mounts we wait once. Hard mounts keep retrying until
177-
* process is killed or server comes back on-line.
178-
*/
179-
do {
180-
rc = wait_event_interruptible_timeout(server->response_q,
181-
(server->tcpStatus != CifsNeedReconnect),
182-
timeout * HZ);
183-
if (rc < 0) {
184-
cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
185-
__func__);
186-
return -ERESTARTSYS;
187-
}
188-
189-
/* are we still trying to reconnect? */
190-
spin_lock(&server->srv_lock);
191-
if (server->tcpStatus != CifsNeedReconnect) {
192-
spin_unlock(&server->srv_lock);
193-
return 0;
194-
}
195-
spin_unlock(&server->srv_lock);
196-
} while (retry);
197-
198-
cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
199-
return -EHOSTDOWN;
200-
}
201-
202142
static int
203143
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
204144
struct TCP_Server_Info *server)
@@ -243,7 +183,27 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
243183
(!tcon->ses->server) || !server)
244184
return -EIO;
245185

246-
rc = wait_for_server_reconnect(server, smb2_command, tcon->retry);
186+
spin_lock(&server->srv_lock);
187+
if (server->tcpStatus == CifsNeedReconnect) {
188+
/*
189+
* Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
190+
* here since they are implicitly done when session drops.
191+
*/
192+
switch (smb2_command) {
193+
/*
194+
* BB Should we keep oplock break and add flush to exceptions?
195+
*/
196+
case SMB2_TREE_DISCONNECT:
197+
case SMB2_CANCEL:
198+
case SMB2_CLOSE:
199+
case SMB2_OPLOCK_BREAK:
200+
spin_unlock(&server->srv_lock);
201+
return -EAGAIN;
202+
}
203+
}
204+
spin_unlock(&server->srv_lock);
205+
206+
rc = cifs_wait_for_server_reconnect(server, tcon->retry);
247207
if (rc)
248208
return rc;
249209

0 commit comments

Comments
 (0)