cifs_tcon_reconnect() will also be used in cifs_reconnect_tcon() with the next commit. Signed-off-by: Stefan Metzmacher <metze@xxxxxxxxx> --- fs/cifs/cifsglob.h | 8 +++ fs/cifs/cifsproto.h | 3 + fs/cifs/connect.c | 125 ++++++++++++++++++++++++++++++++++++ fs/cifs/smb2pdu.c | 151 +++++++++----------------------------------- 4 files changed, 166 insertions(+), 121 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index de82cfa44b1a..8393ed7ebf96 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1074,6 +1074,14 @@ struct cached_fid { struct smb2_file_all_info file_all_info; }; +struct cifs_tcon_reconnect_params { + bool skip_reconnect; + bool exit_nodev; + bool early_eagain; + bool late_eagain; + bool start_timer; +}; + /* * there is one of these for each connection to a resource on a particular * session diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4c93007e44c0..64f13affdb15 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -270,6 +270,9 @@ extern int cifs_connect_session_locked(const unsigned int xid, struct cifs_ses *ses, struct nls_table *nls_info, bool retry); +extern int cifs_tcon_reconnect(struct cifs_tcon *tcon, + const struct cifs_tcon_reconnect_params *params); + extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); extern int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index c243c9a1b3d4..67d2ad330f33 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -5398,6 +5398,131 @@ cifs_connect_session_locked(const unsigned int xid, return rc; } +int +cifs_tcon_reconnect(struct cifs_tcon *tcon, + const struct cifs_tcon_reconnect_params *params) +{ + int rc; + struct nls_table *nls_codepage; + struct cifs_ses *ses; + struct TCP_Server_Info *server; + int retries; + + /* + * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so + * check for tcp and smb session status done differently + * for those three - in the calling routine. + */ + if (tcon == NULL) + return 0; + + ses = tcon->ses; + server = ses->server; + + if (params->skip_reconnect) + return 0; + + if (tcon->tidStatus == CifsExiting) { + if (params->exit_nodev) { + cifs_dbg(FYI, "return ENODEV while umounting\n"); + return -ENODEV; + } + } + if ((!ses) || (ses->status == CifsExiting) || (!server)) + return -EIO; + + retries = server->nr_targets; + + /* + * 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) { + if (params->early_eagain) { + 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? */ + if (server->tcpStatus != CifsNeedReconnect) + break; + + 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; + } + + if (!ses->need_reconnect && !tcon->need_reconnect) + return 0; + + nls_codepage = load_nls_default(); + + /* + * need to prevent multiple threads trying to simultaneously reconnect + * the same SMB session + */ + mutex_lock(&ses->session_mutex); + + /* + * Recheck after acquire mutex. If another thread is negotiating + * and the server never sends an answer the socket will be closed + * and tcpStatus set to reconnect. + */ + rc = cifs_connect_session_locked(0, ses, + nls_codepage, + tcon->retry); + /* do we need to reconnect tcon? */ + if (rc || !tcon->need_reconnect) { + mutex_unlock(&ses->session_mutex); + goto out; + } + + cifs_mark_open_files_invalid(tcon); + if (tcon->use_persistent) + tcon->need_reopen_files = true; + + rc = cifs_tree_connect(0, tcon, nls_codepage); + mutex_unlock(&ses->session_mutex); + + cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); + if (rc) { + /* If sess reconnected but tcon didn't, something strange ... */ + printk_once(KERN_WARNING "reconnect tcon failed rc = %d\n", rc); + goto out; + } + + if (params->start_timer) + mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + + atomic_inc(&tconInfoReconnectCount); +out: + if (params->late_eagain) { + rc = -EAGAIN; + } + + unload_nls(nls_codepage); + return rc; +} + static int cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 715a50ffb234..162fe3381f4c 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -158,139 +158,48 @@ smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd, static int smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) { - int rc; - struct nls_table *nls_codepage; - struct cifs_ses *ses; - struct TCP_Server_Info *server; - int retries; - - /* - * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so - * check for tcp and smb session status done differently - * for those three - in the calling routine. - */ - if (tcon == NULL) - return 0; + struct cifs_tcon_reconnect_params params = { + .skip_reconnect = false, + }; - if (smb2_command == SMB2_TREE_CONNECT) - return 0; - - if (tcon->tidStatus == CifsExiting) { - /* - * only tree disconnect, open, and write, - * (and ulogoff which does not have tcon) - * are allowed as we start force umount. - */ - if ((smb2_command != SMB2_WRITE) && - (smb2_command != SMB2_CREATE) && - (smb2_command != SMB2_TREE_DISCONNECT)) { - cifs_dbg(FYI, "can not send cmd %d while umounting\n", - smb2_command); - return -ENODEV; - } + switch (smb2_command) { + case SMB2_TREE_CONNECT: + params.skip_reconnect = true; + break; } - if ((!tcon->ses) || (tcon->ses->status == CifsExiting) || - (!tcon->ses->server)) - return -EIO; - - ses = tcon->ses; - server = ses->server; - - retries = server->nr_targets; /* - * Give demultiplex thread up to 10 seconds to each target available for - * reconnect -- should be greater than cifs socket timeout which is 7 - * seconds. + * only tree disconnect, open, and write, + * (and ulogoff which does not have tcon) + * are allowed as we start force umount. */ - 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? */ - if (server->tcpStatus != CifsNeedReconnect) - break; - - 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; + switch (smb2_command) { + case SMB2_WRITE: + case SMB2_CREATE: + case SMB2_TREE_DISCONNECT: + params.exit_nodev = true; + break; } - if (!tcon->ses->need_reconnect && !tcon->need_reconnect) - return 0; - - nls_codepage = load_nls_default(); - /* - * need to prevent multiple threads trying to simultaneously reconnect - * the same SMB session + * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE + * here since they are implicitly done when session drops. */ - mutex_lock(&tcon->ses->session_mutex); - + switch (smb2_command) { /* - * Recheck after acquire mutex. If another thread is negotiating - * and the server never sends an answer the socket will be closed - * and tcpStatus set to reconnect. + * BB Should we keep oplock break and add flush to exceptions? */ - rc = cifs_connect_session_locked(0, tcon->ses, - nls_codepage, - tcon->retry); - /* do we need to reconnect tcon? */ - if (rc || !tcon->need_reconnect) { - mutex_unlock(&tcon->ses->session_mutex); - goto out; - } - - cifs_mark_open_files_invalid(tcon); - if (tcon->use_persistent) - tcon->need_reopen_files = true; - - rc = cifs_tree_connect(0, tcon, nls_codepage); - mutex_unlock(&tcon->ses->session_mutex); - - cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); - if (rc) { - /* If sess reconnected but tcon didn't, something strange ... */ - printk_once(KERN_WARNING "reconnect tcon failed rc = %d\n", rc); - goto out; + case SMB2_TREE_DISCONNECT: + case SMB2_CANCEL: + case SMB2_CLOSE: + case SMB2_OPLOCK_BREAK: + params.early_eagain = true; + break; } if (smb2_command != SMB2_INTERNAL_CMD) - mod_delayed_work(cifsiod_wq, &server->reconnect, 0); + params.start_timer = true; - atomic_inc(&tconInfoReconnectCount); -out: /* * Check if handle based operation so we know whether we can continue * or not without returning to caller to reset file handle. @@ -309,11 +218,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) case SMB2_CHANGE_NOTIFY: case SMB2_QUERY_INFO: case SMB2_SET_INFO: - rc = -EAGAIN; + params.late_eagain = true; + break; } - unload_nls(nls_codepage); - return rc; + return cifs_tcon_reconnect(tcon, ¶ms); } static void -- 2.17.1