Message ID | 20200224131510.20608-7-metze@samba.org |
---|---|
State | New |
Headers | show |
Series | Avoid reconnects of failed session setups on soft mounts | expand |
Any updates on this (rebase) on the remaining 10? On Mon, Feb 24, 2020 at 7:16 AM Stefan Metzmacher <metze@samba.org> wrote: > > cifs_tcon_reconnect() will also be used in cifs_reconnect_tcon() > with the next commit. > > Signed-off-by: Stefan Metzmacher <metze@samba.org> > --- > 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 > -- Thanks, Steve
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
cifs_tcon_reconnect() will also be used in cifs_reconnect_tcon() with the next commit. Signed-off-by: Stefan Metzmacher <metze@samba.org> --- 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(-)