mirror of
https://github.com/torvalds/linux.git
synced 2025-01-01 15:51:46 +00:00
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) <pc@manguebit.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
b9ee2e307c
commit
1bcd548d93
@ -694,5 +694,6 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
|
|||||||
|
|
||||||
struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
|
struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
|
||||||
void cifs_put_tcon_super(struct super_block *sb);
|
void cifs_put_tcon_super(struct super_block *sb);
|
||||||
|
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
|
||||||
|
|
||||||
#endif /* _CIFSPROTO_H */
|
#endif /* _CIFSPROTO_H */
|
||||||
|
@ -72,7 +72,6 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
|
|||||||
struct cifs_ses *ses;
|
struct cifs_ses *ses;
|
||||||
struct TCP_Server_Info *server;
|
struct TCP_Server_Info *server;
|
||||||
struct nls_table *nls_codepage;
|
struct nls_table *nls_codepage;
|
||||||
int retries;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
|
* 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)
|
|||||||
}
|
}
|
||||||
spin_unlock(&tcon->tc_lock);
|
spin_unlock(&tcon->tc_lock);
|
||||||
|
|
||||||
retries = server->nr_targets;
|
rc = cifs_wait_for_server_reconnect(server, tcon->retry);
|
||||||
|
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) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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) {
|
||||||
|
@ -1266,3 +1266,47 @@ int cifs_inval_name_dfs_link_error(const unsigned int xid,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, 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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
@ -139,66 +139,6 @@ out:
|
|||||||
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)
|
||||||
@ -243,7 +183,27 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
|||||||
(!tcon->ses->server) || !server)
|
(!tcon->ses->server) || !server)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
rc = wait_for_server_reconnect(server, smb2_command, tcon->retry);
|
spin_lock(&server->srv_lock);
|
||||||
|
if (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:
|
||||||
|
spin_unlock(&server->srv_lock);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spin_unlock(&server->srv_lock);
|
||||||
|
|
||||||
|
rc = cifs_wait_for_server_reconnect(server, tcon->retry);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user