Commit 3c0070f5 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French

cifs: prevent data race in smb2_reconnect()

Make sure to get an up-to-date TCP_Server_Info::nr_targets value prior
to waiting the server to be reconnected in smb2_reconnect().  It is
set in cifs_tcp_ses_needs_reconnect() and protected by
TCP_Server_Info::srv_lock.
Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 77e3f338
...@@ -139,6 +139,66 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, ...@@ -139,6 +139,66 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
return; return;
} }
static int wait_for_server_reconnect(struct TCP_Server_Info *server,
__le16 smb2_command, bool retry)
{
int timeout = 10;
int rc;
spin_lock(&server->srv_lock);
if (server->tcpStatus != CifsNeedReconnect) {
spin_unlock(&server->srv_lock);
return 0;
}
timeout *= server->nr_targets;
spin_unlock(&server->srv_lock);
/*
* Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
* here since they are implicitly done when session drops.
*/
switch (smb2_command) {
/*
* BB Should we keep oplock break and add flush to exceptions?
*/
case SMB2_TREE_DISCONNECT:
case SMB2_CANCEL:
case SMB2_CLOSE:
case SMB2_OPLOCK_BREAK:
return -EAGAIN;
}
/*
* Give demultiplex thread up to 10 seconds to each target available for
* reconnect -- should be greater than cifs socket timeout which is 7
* seconds.
*
* On "soft" mounts we wait once. Hard mounts keep retrying until
* process is killed or server comes back on-line.
*/
do {
rc = wait_event_interruptible_timeout(server->response_q,
(server->tcpStatus != CifsNeedReconnect),
timeout * HZ);
if (rc < 0) {
cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n",
__func__);
return -ERESTARTSYS;
}
/* are we still trying to reconnect? */
spin_lock(&server->srv_lock);
if (server->tcpStatus != CifsNeedReconnect) {
spin_unlock(&server->srv_lock);
return 0;
}
spin_unlock(&server->srv_lock);
} while (retry);
cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__);
return -EHOSTDOWN;
}
static int static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
struct TCP_Server_Info *server) struct TCP_Server_Info *server)
...@@ -146,7 +206,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -146,7 +206,6 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
int rc = 0; int rc = 0;
struct nls_table *nls_codepage; struct nls_table *nls_codepage;
struct cifs_ses *ses; struct cifs_ses *ses;
int retries;
/* /*
* SMB2s NegProt, SessSetup, Logoff do not have tcon yet so * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
...@@ -184,61 +243,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -184,61 +243,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
(!tcon->ses->server) || !server) (!tcon->ses->server) || !server)
return -EIO; return -EIO;
ses = tcon->ses; rc = wait_for_server_reconnect(server, smb2_command, tcon->retry);
retries = server->nr_targets; if (rc)
return rc;
/*
* Give demultiplex thread up to 10 seconds to each target available for
* reconnect -- should be greater than cifs socket timeout which is 7
* seconds.
*/
while (server->tcpStatus == CifsNeedReconnect) {
/*
* Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
* here since they are implicitly done when session drops.
*/
switch (smb2_command) {
/*
* BB Should we keep oplock break and add flush to exceptions?
*/
case SMB2_TREE_DISCONNECT:
case SMB2_CANCEL:
case SMB2_CLOSE:
case SMB2_OPLOCK_BREAK:
return -EAGAIN;
}
rc = wait_event_interruptible_timeout(server->response_q,
(server->tcpStatus != CifsNeedReconnect),
10 * HZ);
if (rc < 0) {
cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n",
__func__);
return -ERESTARTSYS;
}
/* are we still trying to reconnect? */
spin_lock(&server->srv_lock);
if (server->tcpStatus != CifsNeedReconnect) {
spin_unlock(&server->srv_lock);
break;
}
spin_unlock(&server->srv_lock);
if (retries && --retries)
continue;
/* ses = tcon->ses;
* on "soft" mounts we wait once. Hard mounts keep
* retrying until process is killed or server comes
* back on-line
*/
if (!tcon->retry) {
cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
return -EHOSTDOWN;
}
retries = server->nr_targets;
}
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) { if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment